nfs-ganesha 1.4

9p_readdir.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright CEA/DAM/DIF  (2011)
00005  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00006  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00007  *
00008  *
00009  * This program is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 3 of the License, or (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022  *
00023  * ---------------------------------------
00024  */
00025 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif
00038 
00039 #ifdef _SOLARIS
00040 #include "solaris_port.h"
00041 #endif
00042 
00043 #include <stdio.h>
00044 #include <string.h>
00045 #include <pthread.h>
00046 #include <sys/stat.h>
00047 #include "nfs_core.h"
00048 #include "log.h"
00049 #include "cache_inode.h"
00050 #include "fsal.h"
00051 #include "9p.h"
00052 #include "abstract_mem.h"
00053 
00054 u8 qid_type_file = _9P_QTFILE ;
00055 u8 qid_type_symlink = _9P_QTSYMLINK ;
00056 u8 qid_type_dir = _9P_QTDIR ;
00057 char pathdot[] = "." ;
00058 char pathdotdot[] = ".." ;
00059 
00060 typedef struct _9p_cb_entry
00061 {
00062    u64    qid_path ;
00063    u8   * qid_type ;
00064    char * name_str ;
00065    u16    name_len ;
00066 } _9p_cb_entry_t ;
00067 
00068 typedef struct _9p_cb_data 
00069 {
00070    _9p_cb_entry_t * entries ;
00071    size_t count ;
00072    size_t max ;
00073 } _9p_cb_data_t ;
00074 
00075 static bool_t _9p_readdir_callback( void* opaque,
00076                                     char *name,
00077                                     fsal_handle_t *handle,
00078                                     fsal_attrib_list_t * pattrs,
00079                                     uint64_t cookie)
00080 {
00081    _9p_cb_data_t * cb_data = opaque ;
00082 
00083   if( cb_data == NULL )
00084    return FALSE ;
00085 
00086   if( cb_data->count > cb_data->max )
00087    return FALSE ;
00088 
00089   cb_data->entries[cb_data->count].qid_path = pattrs->fileid ;
00090   cb_data->entries[cb_data->count].name_str = name ;
00091   cb_data->entries[cb_data->count].name_len = strlen( name ) ;
00092  
00093   switch( pattrs->type ) 
00094    {
00095       case FSAL_TYPE_FIFO:
00096       case FSAL_TYPE_CHR:
00097       case FSAL_TYPE_BLK:
00098       case FSAL_TYPE_FILE:
00099       case FSAL_TYPE_SOCK:
00100         cb_data->entries[cb_data->count].qid_type = &qid_type_file ;
00101         break ;
00102 
00103       case FSAL_TYPE_JUNCTION:
00104       case FSAL_TYPE_DIR:
00105         cb_data->entries[cb_data->count].qid_type = &qid_type_dir ;
00106         break ;
00107 
00108       case FSAL_TYPE_LNK:
00109         cb_data->entries[cb_data->count].qid_type = &qid_type_symlink ;
00110         break ;
00111     
00112       default:
00113         return FALSE;  
00114    }
00115  
00116   cb_data->count += 1 ; 
00117   return TRUE ;
00118 
00119 }
00120 
00121 int _9p_readdir( _9p_request_data_t * preq9p,
00122                  void  * pworker_data,
00123                  u32 * plenout,
00124                  char * preply)
00125 {
00126   char * cursor = preq9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE ;
00127 
00128   _9p_cb_data_t cb_data ;
00129 
00130   u16 * msgtag = NULL ;
00131   u32 * fid    = NULL ;
00132   u64 * offset = NULL ;
00133   u32 * count  = NULL ;
00134 
00135   u32  dcount      = 0 ;
00136   u32  recsize     = 0 ;
00137   u16  name_len    = 0 ;
00138   
00139   char * name_str = NULL ;
00140 
00141   u8  * qid_type    = NULL ;
00142   u64 * qid_path    = NULL ;
00143 
00144   char * dcount_pos = NULL ;
00145 
00146   cache_inode_status_t cache_status;
00147   bool_t eod_met;
00148   cache_entry_t * pentry_dot_dot = NULL ;
00149 
00150   unsigned int cookie = 0;
00151   unsigned int estimated_num_entries = 0 ;
00152   unsigned int num_entries = 0 ;
00153   unsigned int delta = 0 ;
00154   u64 i = 0LL ;
00155 
00156   if ( !preq9p || !pworker_data || !plenout || !preply )
00157    return -1 ;
00158 
00159   _9p_fid_t * pfid = NULL ;
00160 
00161   /* Get data */
00162   _9p_getptr( cursor, msgtag, u16 ) ; 
00163   _9p_getptr( cursor, fid,    u32 ) ; 
00164   _9p_getptr( cursor, offset, u64 ) ; 
00165   _9p_getptr( cursor, count,  u32 ) ; 
00166   
00167   LogDebug( COMPONENT_9P, "TREADDIR: tag=%u fid=%u offset=%llu count=%u",
00168             (u32)*msgtag, *fid, (unsigned long long)*offset, *count  ) ;
00169 
00170   if( *fid >= _9P_FID_PER_CONN )
00171    return _9p_rerror( preq9p, msgtag, ERANGE, plenout, preply ) ;
00172 
00173    pfid = &preq9p->pconn->fids[*fid] ;
00174 
00175   /* Use Cache Inode to read the directory's content */
00176   cookie = (unsigned int)*offset ;
00177 
00178   /* For each entry, returns:
00179    * qid     = 13 bytes
00180    * offset  = 8 bytes
00181    * type    = 1 byte
00182    * namelen = 2 bytes
00183    * namestr = ~16 bytes (average size)
00184    * -------------------
00185    * total   = ~40 bytes (average size) per dentry */
00186   estimated_num_entries = (unsigned int)( *count / 40 ) ;
00187 
00188   if((cb_data.entries = gsh_calloc(estimated_num_entries,
00189                                    sizeof(_9p_cb_entry_t))) == NULL)
00190     return _9p_rerror( preq9p, msgtag, EIO, plenout, preply ) ;
00191 
00192    /* Is this the first request ? */
00193   if( *offset == 0 )
00194    {
00195       /* compute the parent entry */
00196       if( ( pentry_dot_dot = cache_inode_lookupp( pfid->pentry,
00197                                                   &pfid->fsal_op_context,
00198                                                   &cache_status ) ) == NULL )
00199         return _9p_rerror( preq9p, msgtag, _9p_tools_errno( cache_status ), plenout, preply ) ;
00200 
00201       /* Deal with "." and ".." */
00202       cb_data.entries[0].qid_path =  pfid->pentry->attributes.fileid ;
00203       cb_data.entries[0].qid_type =  &qid_type_dir ;
00204       cb_data.entries[0].name_str =  pathdot ;
00205       cb_data.entries[0].name_len =  strlen( pathdot ) ;
00206 
00207 
00208       cb_data.entries[1].qid_path =  pentry_dot_dot->attributes.fileid ;
00209       cb_data.entries[1].qid_type =  &qid_type_dir ;
00210       cb_data.entries[1].name_str =  pathdotdot ;
00211       cb_data.entries[1].name_len =  strlen( pathdotdot ) ;
00212 
00213       delta = 2 ;
00214    }
00215   else
00216    delta = 0 ;
00217 
00218   if( *offset == 2 )
00219    {
00220       /* offset == 2 as an input as one and only reason:
00221        *   - a former call with offset=0 was made and the dir was empty
00222        *   - '.' and '..' were returned and nothing else
00223        *   - the client makes a new call, expecting it to have empty return
00224        */
00225       num_entries = 0 ; /* Empty return */
00226    }
00227   else
00228    {
00229      cb_data.count = delta ;
00230      cb_data.max = _9P_MAXDIRCOUNT - delta ;
00231 
00232      if(cache_inode_readdir( pfid->pentry,
00233                              cookie,
00234                              &num_entries,
00235                              &eod_met,
00236                              &pfid->fsal_op_context, 
00237                              _9p_readdir_callback,
00238                              &cb_data,
00239                              &cache_status) != CACHE_INODE_SUCCESS)
00240         return _9p_rerror( preq9p, msgtag, _9p_tools_errno( cache_status ), plenout, preply ) ;
00241    }
00242   /* Never go behind _9P_MAXDIRCOUNT */
00243   if( num_entries > _9P_MAXDIRCOUNT ) num_entries = _9P_MAXDIRCOUNT ;
00244 
00245 
00246   /* Build the reply */
00247   _9p_setinitptr( cursor, preply, _9P_RREADDIR ) ;
00248   _9p_setptr( cursor, msgtag, u16 ) ;
00249 
00250   /* Remember dcount position for later use */
00251   _9p_savepos( cursor, dcount_pos, u32 ) ;
00252 
00253   /* fills in the dentry in 9P marshalling */
00254   for( i = 0 ; i < num_entries + delta ; i++ )
00255    {
00256      recsize = 0 ;
00257 
00258      /* Build qid */
00259      qid_path = &cb_data.entries[i].qid_path ;
00260      qid_type = cb_data.entries[i].qid_type ;
00261 
00262      /* Get dirent name information */
00263      name_str = cb_data.entries[i].name_str ;
00264      name_len = cb_data.entries[i].name_len ;
00265 
00266      /* Add 13 bytes in recsize for qid + 8 bytes for offset + 1 for type + 2 for strlen = 24 bytes*/
00267      recsize = 24 + name_len  ;
00268 
00269      /* Check if there is room left for another dentry */
00270      if( dcount + recsize > *count )
00271        break ; /* exit for loop */
00272      else
00273        dcount += recsize ;
00274 
00275      /* qid in 3 parts */
00276      _9p_setptr( cursor, qid_type, u8 ) ;
00277      _9p_setvalue( cursor, 0, u32 ) ; /* qid_version set to 0 to prevent the client from caching */
00278      _9p_setptr( cursor, qid_path, u64 ) ;
00279      
00280      /* offset */
00281      _9p_setvalue( cursor, i+cookie+1, u64 ) ;   
00282 
00283      /* Type (again ?) */
00284      _9p_setptr( cursor, qid_type, u8 ) ;
00285 
00286      /* name */
00287      _9p_setstr( cursor, name_len, name_str ) ;
00288   
00289      LogDebug( COMPONENT_9P, "RREADDIR dentry: recsize=%u dentry={ off=%llu,qid=(type=%u,version=%u,path=%llu),type=%u,name=%s",
00290                recsize, (unsigned long long)i+cookie+1, *qid_type, 0, (unsigned long long)*qid_path, 
00291                *qid_type, name_str ) ;
00292    } /* for( i = 0 , ... ) */
00293 
00294   gsh_free( cb_data.entries ) ;
00295   /* Set buffsize in previously saved position */
00296   _9p_setvalue( dcount_pos, dcount, u32 ) ;
00297 
00298   _9p_setendptr( cursor, preply ) ;
00299   _9p_checkbound( cursor, preply, plenout ) ;
00300 
00301   LogDebug( COMPONENT_9P, "RREADDIR: tag=%u fid=%u dcount=%u",
00302             (u32)*msgtag, *fid , dcount ) ;
00303 
00304   return 1 ;
00305 }
00306