nfs-ganesha 1.4

fsal_dirs.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  */
00004 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #include "fsal.h"
00019 #include "fsal_internal.h"
00020 #include "fsal_convert.h"
00021 #include "namespace.h"
00022 #include <string.h>
00023 
00048 fsal_status_t FUSEFSAL_opendir(fsal_handle_t * dir_hdl,  /* IN */
00049                                fsal_op_context_t * p_context,       /* IN */
00050                                fsal_dir_t * dir_desc,     /* OUT */
00051                                fsal_attrib_list_t * dir_attributes      /* [ IN/OUT ] */
00052     )
00053 {
00054   int rc;
00055   char object_path[FSAL_MAX_PATH_LEN];
00056   fusefsal_dir_t * dir_descriptor = (fusefsal_dir_t *)dir_desc;
00057   fusefsal_handle_t * dir_handle = (fusefsal_handle_t *)dir_hdl;
00058 
00059   /* sanity checks
00060    * note : dir_attributes is optionnal.
00061    */
00062   if(!dir_handle || !p_context || !dir_descriptor)
00063     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_opendir);
00064 
00065   /* get the full path for this directory */
00066   rc = NamespacePath(dir_handle->data.inode, dir_handle->data.device, dir_handle->data.validator,
00067                      object_path);
00068   if(rc)
00069     Return(ERR_FSAL_STALE, rc, INDEX_FSAL_opendir);
00070 
00071   memset(dir_descriptor, 0, sizeof(fsal_dir_t));
00072 
00073   /* set context for the next operation, so it can be retrieved by FS thread */
00074   fsal_set_thread_context(p_context);
00075 
00076   /* check opendir call */
00077 
00078   if(p_fs_ops->opendir)
00079     {
00080       TakeTokenFSCall();
00081       rc = p_fs_ops->opendir(object_path, &(dir_descriptor->dir_info));
00082       ReleaseTokenFSCall();
00083 
00084       if(rc)
00085         Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_opendir);
00086     }
00087   else
00088     {
00089       /* ignoring opendir */
00090       memset(&(dir_descriptor->dir_info), 0, sizeof(struct ganefuse_file_info));
00091     }
00092 
00093   /* fill the dir descriptor structure */
00094   dir_descriptor->dir_handle = *dir_handle;
00095 
00096   /* backup context */
00097   dir_descriptor->context = *(fusefsal_op_context_t *)p_context;
00098 
00099   /* optionaly get attributes */
00100   if(dir_attributes)
00101     {
00102       fsal_status_t status;
00103 
00104       status = FUSEFSAL_getattrs(dir_hdl, p_context, dir_attributes);
00105 
00106       /* on error, we set a special bit in the mask. */
00107       if(FSAL_IS_ERROR(status))
00108         {
00109           FSAL_CLEAR_MASK(dir_attributes->asked_attributes);
00110           FSAL_SET_MASK(dir_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR);
00111         }
00112     }
00113 
00114   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_opendir);
00115 
00116 }
00117 
00118 typedef struct __fsal_dirbuff
00119 {
00120   fsal_attrib_mask_t getattr_mask;
00121   fsal_count_t nb_entries;
00122   fsal_count_t max_entries;
00123   fsal_dirent_t *p_entries;
00124   fsal_status_t status;
00125 
00126 /* for filesystems that do not support readdir offset */
00127   fsal_off_t begin_off;
00128   fsal_off_t curr_off;
00129 
00130 } fsal_dirbuff_t;
00131 
00132 #define INODE_TO_BE_COMPLETED   ((ino_t)(-1))
00133 
00134 static void fill_dirent(fsal_dirent_t * to_be_filled,
00135                         fsal_attrib_mask_t getattr_mask,
00136                         const char *name, const struct stat *stbuf, off_t off)
00137 {
00138   fsal_status_t status;
00139   struct stat tmp_statbuff;
00140   int err = FALSE;
00141   fusefsal_handle_t *fill_handle = (fusefsal_handle_t *) &to_be_filled->handle;
00142 
00143   if(stbuf)
00144     {
00145       if(stbuf->st_ino == 0)
00146         {
00147           LogDebug(COMPONENT_FSAL,
00148                    "WARNING in fill_dirent: Filesystem doesn't provide inode numbers !!!");
00149         }
00150 
00151       fill_handle->data.inode = stbuf->st_ino;
00152       fill_handle->data.device = stbuf->st_dev;
00153       FSAL_str2name(name, strlen(name) + 1, &(to_be_filled->name));
00154       ((fusefsal_cookie_t *) &to_be_filled->cookie)->data = off;
00155 
00156       /* local copy for non "const" calls */
00157       tmp_statbuff = *stbuf;
00158 
00159       /* set attributes */
00160       to_be_filled->attributes.asked_attributes = getattr_mask;
00161       status = posix2fsal_attributes(&tmp_statbuff, &to_be_filled->attributes);
00162 
00163       LogFullDebug(COMPONENT_FSAL,
00164            "getattr_mask = %X, recupere = %X, status=%d, inode=%llX.%llu, type=%d, posixmode=%#o, mode=%#o",
00165            (unsigned int)getattr_mask, (unsigned int)to_be_filled->attributes.asked_attributes, status.major,
00166            to_be_filled->attributes.fsid.major, to_be_filled->attributes.fileid,
00167            to_be_filled->attributes.type, tmp_statbuff.st_mode,
00168            to_be_filled->attributes.mode);
00169 
00170       if(FSAL_IS_ERROR(status))
00171         {
00172           FSAL_CLEAR_MASK(to_be_filled->attributes.asked_attributes);
00173           /* set getattr error bit in attr mask */
00174           FSAL_SET_MASK(to_be_filled->attributes.asked_attributes, FSAL_ATTR_RDATTR_ERR);
00175           err = TRUE;
00176         }
00177     }
00178 
00179   /* if any error occured during conversion,
00180    * or if values seem to be inconsistent,
00181    * proceed a lookup afterward */
00182 
00183   if(!stbuf
00184      || (stbuf->st_ino == 0)
00185      || err
00186      || to_be_filled->attributes.type == (fsal_nodetype_t) - 1
00187      || to_be_filled->attributes.mode == 0 || to_be_filled->attributes.numlinks == 0)
00188     {
00189       FSAL_CLEAR_MASK(to_be_filled->attributes.asked_attributes);
00190       /* we only known entry name, we tag it for a later lookup.
00191        */
00192       fill_handle->data.inode = INODE_TO_BE_COMPLETED;
00193       FSAL_str2name(name, strlen(name) + 1, &(to_be_filled->name));
00194       ((fusefsal_cookie_t *) &to_be_filled->cookie)->data = off;
00195     }
00196 
00197 }                               /* fill_dirent */
00198 
00199 /* this function is used by FUSE dir reader to fill the output buffer */
00200 
00201 static int ganefuse_fill_dir(void *buf, const char *name,
00202                              const struct stat *stbuf, off_t off)
00203 {
00204   fsal_dirbuff_t *dirbuff = (fsal_dirbuff_t *) buf;
00205   fsal_dirent_t *tab;
00206   unsigned int i;
00207 
00208   if(!dirbuff)
00209     return 1;
00210 
00211   /* missing name parameter */
00212   if(!name)
00213     {
00214       dirbuff->status.major = ERR_FSAL_INVAL;
00215       dirbuff->status.minor = 0;
00216       return 1;
00217     }
00218 
00219   /* if this is . or .., ignore */
00220   if(!strcmp(name, ".") || !strcmp(name, ".."))
00221     return 0;
00222 
00223   /* full output buffer */
00224   if(dirbuff->nb_entries == dirbuff->max_entries)
00225     {
00226       /* should not have been called */
00227       dirbuff->status.major = ERR_FSAL_SERVERFAULT;
00228       dirbuff->status.minor = 0;
00229       return 1;
00230     }
00231 
00232   tab = dirbuff->p_entries;
00233 
00234   /* offset is provided */
00235   if(off)
00236     {
00237       i = dirbuff->nb_entries;
00238       fill_dirent(&(tab[i]), dirbuff->getattr_mask, name, stbuf, off);
00239     }
00240   else
00241     {
00242       /* no offset is provided, we must skip some entries */
00243 
00244       if(dirbuff->curr_off < dirbuff->begin_off)
00245         {
00246           /* skip entry and go to the next */
00247           dirbuff->curr_off++;
00248           return 0;
00249         }
00250 
00251       /* entry is to be added */
00252       dirbuff->curr_off++;
00253 
00254       i = dirbuff->nb_entries;
00255       fill_dirent(&(tab[i]), dirbuff->getattr_mask, name, stbuf, dirbuff->curr_off);
00256     }
00257 
00258   dirbuff->nb_entries++;
00259 
00260   if(dirbuff->nb_entries == dirbuff->max_entries)
00261     return 1;
00262   else
00263     return 0;
00264 }
00265 
00266 /* this function is used by filesystems binded to old version of FUSE
00267  * that use getdir() instead of readdir().
00268  */
00269 static int ganefuse_dirfil_old(ganefuse_dirh_t h, const char *name, int type, ino_t ino)
00270 {
00271   return ganefuse_fill_dir((void *)h, name, NULL, 0);
00272 }
00273 
00309 fsal_status_t FUSEFSAL_readdir(fsal_dir_t * dir_desc,     /* IN */
00310                                fsal_cookie_t start_position,        /* IN */
00311                                fsal_attrib_mask_t get_attr_mask,        /* IN */
00312                                fsal_mdsize_t buffersize,        /* IN */
00313                                fsal_dirent_t * pdirent, /* OUT */
00314                                fsal_cookie_t * end_position,        /* OUT */
00315                                fsal_count_t * nb_entries,       /* OUT */
00316                                fsal_boolean_t * end_of_dir      /* OUT */
00317     )
00318 {
00319   int rc;
00320   char dir_path[FSAL_MAX_PATH_LEN];
00321   fsal_dirbuff_t reqbuff;
00322   unsigned int i;
00323   fsal_status_t st;
00324   fusefsal_dir_t * dir_descriptor = (fusefsal_dir_t *)dir_desc;
00325 
00326   /* sanity checks */
00327 
00328   if(!dir_descriptor || !pdirent || !end_position || !nb_entries || !end_of_dir)
00329     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_readdir);
00330 
00331   /* get the full path for dir inode */
00332   rc = NamespacePath(dir_descriptor->dir_handle.data.inode,
00333                      dir_descriptor->dir_handle.data.device,
00334                      dir_descriptor->dir_handle.data.validator, dir_path);
00335   if(rc)
00336     Return(ERR_FSAL_STALE, rc, INDEX_FSAL_readdir);
00337 
00338   if(!p_fs_ops->readdir && !p_fs_ops->getdir)
00339     Return(ERR_FSAL_NOTSUPP, 0, INDEX_FSAL_readdir);
00340 
00341   /* set context so it can be retrieved by FS */
00342   fsal_set_thread_context((fsal_op_context_t *) &dir_descriptor->context);
00343 
00344   /* prepare reaadir structure */
00345 
00346   reqbuff.getattr_mask = get_attr_mask;
00347   reqbuff.nb_entries = 0;
00348   reqbuff.max_entries = (buffersize / sizeof(fsal_dirent_t));
00349   reqbuff.p_entries = pdirent;
00350   reqbuff.status.major = 0;
00351   reqbuff.status.minor = 0;
00352   memcpy( (char *)&reqbuff.begin_off, (char *)&start_position.data, sizeof( off_t ) ) ;
00353   reqbuff.curr_off = 0 ;
00354 
00355   TakeTokenFSCall();
00356 
00357   if(p_fs_ops->readdir)
00358     rc = p_fs_ops->readdir(dir_path, (void *)&reqbuff, ganefuse_fill_dir,
00359                            (off_t)start_position.data, &dir_descriptor->dir_info);
00360   else
00361     rc = p_fs_ops->getdir(dir_path, (ganefuse_dirh_t) & reqbuff, ganefuse_dirfil_old);
00362 
00363   ReleaseTokenFSCall();
00364 
00365   if(rc)
00366     Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_readdir);
00367   else if(FSAL_IS_ERROR(reqbuff.status))
00368     Return(reqbuff.status.major, reqbuff.status.minor, INDEX_FSAL_readdir);
00369 
00370   /* if no entry found */
00371 
00372   if(reqbuff.nb_entries == 0)
00373     {
00374       *end_position = start_position;
00375       *end_of_dir = TRUE;
00376       *nb_entries = 0;
00377 
00378       LogFullDebug(COMPONENT_FSAL, "No entries found");
00379 
00380       Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_readdir);
00381     }
00382 
00383   /* at least 1 entry found */
00384 
00385   /* we must do some operations on the final dirent array:
00386    * - chaining entries together
00387    * - if the filesystem did not provide stat buffers,
00388    *   we must do lookup operations.
00389    * - adding dir entries to namespace
00390    */
00391 
00392   for(i = 0; i < reqbuff.nb_entries; i++)
00393     {
00394       /* 1) chaining entries together */
00395 
00396       if(i == reqbuff.nb_entries - 1)
00397         {                       /* last entry */
00398           pdirent[i].nextentry = NULL;
00399           *end_position = pdirent[i].cookie;
00400         }
00401       else
00402         pdirent[i].nextentry = &(pdirent[i + 1]);
00403 
00404       /* 2) check weither the filesystem provided stat buff */
00405 
00406       if(((fusefsal_handle_t *) &pdirent[i].handle)->data.inode == INODE_TO_BE_COMPLETED)
00407         {
00408           /* If not, make a lookup operation for this entry.
00409            * (this with automatically add it to namespace) */
00410 
00411           pdirent[i].attributes.asked_attributes = get_attr_mask;
00412 
00413           LogFullDebug(COMPONENT_FSAL, "Inode to be completed");
00414 
00415           st = FUSEFSAL_lookup((fsal_handle_t *) &dir_descriptor->dir_handle,
00416                                &pdirent[i].name,
00417                                (fsal_op_context_t *) &dir_descriptor->context,
00418                                &pdirent[i].handle, &pdirent[i].attributes);
00419 
00420           if(FSAL_IS_ERROR(st))
00421             Return(st.major, st.minor, INDEX_FSAL_readdir);
00422         }
00423       else
00424         {
00425           /* 3) just add entry to namespace except for '.' and '..'
00426            *    Also set a validator for this entry.
00427            */
00428 
00429           if(strcmp(pdirent[i].name.name, ".") && strcmp(pdirent[i].name.name, ".."))
00430             {
00431               LogFullDebug(COMPONENT_FSAL, "adding entry to namespace: %lX.%ld %s",
00432                      ((fusefsal_handle_t *) &pdirent[i].handle)->data.device,
00433                      ((fusefsal_handle_t *) &pdirent[i].handle)->data.inode, pdirent[i].name.name);
00434 
00435               ((fusefsal_handle_t *) &pdirent[i].handle)->data.validator = pdirent[i].attributes.ctime.seconds;
00436 
00437               NamespaceAdd(dir_descriptor->dir_handle.data.inode,
00438                            dir_descriptor->dir_handle.data.device,
00439                            dir_descriptor->dir_handle.data.validator,
00440                            pdirent[i].name.name,
00441                            ((fusefsal_handle_t *) &pdirent[i].handle)->data.inode,
00442                            ((fusefsal_handle_t *) &pdirent[i].handle)->data.device,
00443                            &(((fusefsal_handle_t *) &pdirent[i].handle)->data.validator));
00444             }
00445         }
00446 
00447     }
00448 
00449   /* end of dir was reached if not enough entries were provided */
00450   *end_of_dir = (reqbuff.nb_entries < reqbuff.max_entries);
00451   *nb_entries = reqbuff.nb_entries;
00452 
00453   LogFullDebug(COMPONENT_FSAL, "EOD = %d", *end_of_dir);
00454 
00455   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_readdir);
00456 
00457 }
00458 
00472 fsal_status_t FUSEFSAL_closedir(fsal_dir_t * dir_desc     /* IN */
00473     )
00474 {
00475 
00476   int rc;
00477   char dir_path[FSAL_MAX_PATH_LEN];
00478   fusefsal_dir_t * dir_descriptor = (fusefsal_dir_t *)dir_desc;
00479 
00480   /* sanity checks */
00481   if(!dir_descriptor)
00482     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_closedir);
00483 
00484   /* get the full path for dir inode */
00485   rc = NamespacePath(dir_descriptor->dir_handle.data.inode,
00486                      dir_descriptor->dir_handle.data.device,
00487                      dir_descriptor->dir_handle.data.validator, dir_path);
00488   if(rc)
00489     Return(ERR_FSAL_STALE, rc, INDEX_FSAL_closedir);
00490 
00491   if(!p_fs_ops->releasedir)
00492     /* ignore this call */
00493     Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_closedir);
00494 
00495   /* set context so it can be retrieved by FS */
00496   fsal_set_thread_context((fsal_op_context_t *) &dir_descriptor->context);
00497 
00498   /* release the resources used for reading directory */
00499 
00500   TakeTokenFSCall();
00501 
00502   rc = p_fs_ops->releasedir(dir_path, &dir_descriptor->dir_info);
00503 
00504   ReleaseTokenFSCall();
00505 
00506   if(rc)
00507     Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_closedir);
00508 
00509   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_closedir);
00510 
00511 }