nfs-ganesha 1.4

nfs_Readdir.c

Go to the documentation of this file.
00001  /*
00002  * vimf:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright CEA/DAM/DIF  (2008)
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 
00036 #ifdef HAVE_CONFIG_H
00037 #include "config.h"
00038 #endif
00039 
00040 #ifdef _SOLARIS
00041 #include "solaris_port.h"
00042 #endif
00043 #include <stdio.h>
00044 #include <string.h>
00045 #include <pthread.h>
00046 #include <fcntl.h>
00047 #include <sys/file.h>           /* for having FNDELAY */
00048 #include "HashData.h"
00049 #include "HashTable.h"
00050 #include "log.h"
00051 #include "ganesha_rpc.h"
00052 #include "nfs23.h"
00053 #include "nfs4.h"
00054 #include "mount.h"
00055 #include "nfs_core.h"
00056 #include "cache_inode.h"
00057 #include "cache_inode_lru.h"
00058 #include "cache_inode_weakref.h"
00059 #include "nfs_exports.h"
00060 #include "nfs_creds.h"
00061 #include "nfs_proto_functions.h"
00062 #include "nfs_tools.h"
00063 #include "nfs_proto_tools.h"
00064 #include <assert.h>
00065 
00066 /* This has a tremendous amount of code duplication, but it's very
00067    difficult to refactor since the differences between NFSv2 and
00068    NFSv3 are more a matter of data types than functionality. */
00069 
00070 static bool_t nfs2_readdir_callback(void* opaque,
00071                                     char *name,
00072                                     fsal_handle_t *handle,
00073                                     fsal_attrib_list_t *attrs,
00074                                     uint64_t cookie);
00075 static bool_t nfs3_readdir_callback(void* opaque,
00076                                     char *name,
00077                                     fsal_handle_t *handle,
00078                                     fsal_attrib_list_t *attrs,
00079                                     uint64_t cookie);
00080 static void free_entry2s(entry2 *entry2s);
00081 static void free_entry3s(entry3 *entry3s);
00082 
00090 struct nfs2_readdir_cb_data
00091 {
00092      entry2 *entries; /*< The array holding individual entries */
00093      size_t mem_left; /*< The amount of memory remaining before we
00094                           hit maxcount */
00095      size_t count; /*< The count of complete entries stored in the
00096                        buffer */
00097      size_t total_entries; /*< The total number of entries in the
00098                               array */
00099      fsal_op_context_t *context; /*< FSAL operation context */
00100      nfsstat2 error; /*< Set to a value other than NFS_OK if the
00101                          callback function finds a fatal error. */
00102 };
00103 
00111 struct nfs3_readdir_cb_data
00112 {
00113      entry3 *entries; /*< The array holding individual entries */
00114      size_t mem_left; /*< The amount of memory remaining before we
00115                           hit maxcount */
00116      size_t count; /*< The count of complete entries stored in the
00117                        buffer */
00118      size_t total_entries; /*< The total number of entries in the
00119                               array */
00120      fsal_op_context_t *context; /*< FSAL operation context */
00121      nfsstat3 error; /*< Set to a value other than NFS_OK if the
00122                          callback function finds a fatal error. */
00123 };
00124 
00125 
00145 int
00146 nfs_Readdir(nfs_arg_t *arg,
00147             exportlist_t *export,
00148             fsal_op_context_t *context,
00149             nfs_worker_data_t *worker,
00150             struct svc_req *req,
00151             nfs_res_t *res)
00152 {
00153      cache_entry_t *dir_entry = NULL;
00154      unsigned long count = 0;
00155      fsal_attrib_list_t dir_attr;
00156      uint64_t cookie = 0;
00157      uint64_t cache_inode_cookie = 0;
00158      cookieverf3 cookie_verifier;
00159      unsigned int num_entries;
00160      unsigned long estimated_num_entries = 0;
00161      cache_inode_file_type_t dir_filetype = 0;
00162      bool_t eod_met = FALSE;
00163      cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
00164      cache_inode_status_t cache_status_gethandle = CACHE_INODE_SUCCESS;
00165      int rc = NFS_REQ_OK;
00166      struct nfs2_readdir_cb_data cb2 = {NULL};
00167      struct nfs3_readdir_cb_data cb3 = {NULL};
00168      cache_inode_readdir_cb_t cbfunc;
00169      void *cbdata;
00170      cache_entry_t *parent_dir_entry = NULL;
00171 
00172      if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) {
00173           char str[LEN_FH_STR];
00174           log_components_t component;
00175           nfs_FhandleToStr(req->rq_vers,
00176                            &(arg->arg_readdir2.dir),
00177                            &(arg->arg_readdir3.dir),
00178                            NULL,
00179                            str);
00180           if(isDebug(COMPONENT_NFSPROTO)) {
00181                component = COMPONENT_NFSPROTO;
00182           } else {
00183                component = COMPONENT_NFS_READDIR;
00184           }
00185           LogDebug(component,
00186                    "REQUEST PROCESSING: Calling nfs_Readdir handle: %s", str);
00187      }
00188 
00189      if (req->rq_vers == NFS_V3) {
00190           /* to avoid setting it on each error case */
00191           res->res_readdir3.READDIR3res_u.resfail.dir_attributes
00192                .attributes_follow = FALSE;
00193      }
00194 
00195      /* Look up cache entry for filehandle */
00196      if ((dir_entry
00197           = nfs_FhandleToCache(req->rq_vers,
00198                                &(arg->arg_readdir2.dir),
00199                                &(arg->arg_readdir3.dir),
00200                                NULL,
00201                                &(res->res_readdir2.status),
00202                                &(res->res_readdir3.status),
00203                                NULL,
00204                                &dir_attr,
00205                                context,
00206                                &rc)) == NULL) {
00207           /* Stale NFS FH? */
00208           goto out;
00209      }
00210 
00211      if ((req->rq_vers == NFS_V3) &&
00212          (nfs3_Is_Fh_Xattr(&(arg->arg_readdir3.dir)))) {
00213           rc = nfs3_Readdir_Xattr(arg, export, context, req, res);
00214           goto out;
00215      }
00216 
00217      /* Extract the filetype */
00218      dir_filetype = cache_inode_fsal_type_convert(dir_attr.type);
00219      /* Sanity checks -- must be a directory */
00220      if (dir_filetype != DIRECTORY) {
00221           if (req->rq_vers == NFS_V2) {
00222                /* In the RFC tell it not good but it does not tell
00223                   what to do ... */
00224                res->res_readdir2.status = NFSERR_NOTDIR;
00225           } else if (req->rq_vers == NFS_V3) {
00226                res->res_readdir3.status = NFS3ERR_NOTDIR;
00227           }
00228           rc = NFS_REQ_OK;
00229           goto out;
00230      }
00231 
00232      /* Parse out request arguments and decide how many entries we
00233         want.  For NFSv3, deal with the cookie verifier. */
00234 
00235      if (req->rq_vers == NFS_V2) {
00236           count = (arg->arg_readdir2.count * 9) / 10;
00237           memcpy(&cookie, arg->arg_readdir2.cookie, NFS2_COOKIESIZE);
00238           estimated_num_entries = MIN(50, count / sizeof(entry2));
00239           LogFullDebug(COMPONENT_NFS_READDIR,
00240                        "-- Readdir2 -> count=%lu  cookie = %"PRIu64"  "
00241                        "estimated_num_entries=%lu", count, cookie,
00242                        estimated_num_entries);
00243           if (estimated_num_entries == 0) {
00244                res->res_readdir2.status = NFSERR_IO;
00245                rc = NFS_REQ_OK;
00246                goto out;
00247           }
00248 
00249           cb2.entries = gsh_calloc(estimated_num_entries,
00250                                    sizeof(entry2));
00251           if (cb2.entries == NULL) {
00252                rc = NFS_REQ_DROP;
00253                goto out;
00254           }
00255 
00256           cb2.total_entries = estimated_num_entries;
00257           cb2.mem_left = count - sizeof(READDIR2resok);
00258           cb2.count = 0;
00259           cb2.context = context;
00260           cb2.error = NFS_OK;
00261           cbfunc = nfs2_readdir_callback;
00262           cbdata = &cb2;
00263      } else {
00264           count = (arg->arg_readdir3.count * 9 / 10);
00265           cookie = arg->arg_readdir3.cookie;
00266           estimated_num_entries = MIN(count / sizeof(entry3), 50);
00267           LogFullDebug(COMPONENT_NFS_READDIR,
00268                        "---> nfs3_Readdir: count=%lu  cookie=%"PRIu64"  "
00269                        "estimated_num_entries=%lu",
00270                        count, cookie, estimated_num_entries);
00271           if (estimated_num_entries == 0) {
00272                res->res_readdir3.status = NFS3ERR_TOOSMALL;
00273                rc = NFS_REQ_OK;
00274                goto out;
00275           }
00276 
00277           /* To make or check the cookie verifier */
00278           memset(cookie_verifier, 0, sizeof(cookieverf3));
00279           /* If cookie verifier is used, then a
00280              non-trivial value is returned to the
00281              client.
00282 
00283              This value is the mtime of the directory. If verifier is
00284              unused (as in many NFS Servers) then only a set of zeros
00285              is returned (trivial value). */
00286           if (export->UseCookieVerifier)
00287                memcpy(cookie_verifier,
00288                       &(dir_attr.mtime),
00289                       sizeof(dir_attr.mtime));
00290           /* Nothing to do if != 0 because the area is already full of
00291              zero */
00292           if ((cookie != 0) &&
00293               (export->UseCookieVerifier)) {
00294                /* Not the first call, so we have to check the cookie
00295                   verifier */
00296                if (memcmp(cookie_verifier,
00297                           arg->arg_readdir3.cookieverf,
00298                           NFS3_COOKIEVERFSIZE) != 0) {
00299                     res->res_readdir3.status = NFS3ERR_BAD_COOKIE;
00300                     rc = NFS_REQ_OK;
00301                     goto out;
00302                }
00303           }
00304 
00305           cb3.entries = gsh_calloc(estimated_num_entries,
00306                                    sizeof(entry3));
00307           if (cb3.entries == NULL) {
00308                rc = NFS_REQ_DROP;
00309                goto out;
00310           }
00311           cb3.total_entries = estimated_num_entries;
00312           cb3.mem_left = count - sizeof(READDIR3resok);
00313           cb3.count = 0;
00314           cb3.context = context;
00315           cb3.error = NFS_OK;
00316           cbfunc = nfs3_readdir_callback;
00317           cbdata = &cb3;
00318      }
00319 
00320      /* Adjust the cookie we supply to cache_inode */
00321      if (cookie > 2)  /* it is not the cookie for "." nor ".." */ {
00322           cache_inode_cookie = cookie;
00323      } else {
00324           cache_inode_cookie = 0;
00325      }
00326 
00327      /* Fills "."  */
00328      if (cookie == 0) {
00329           if (!cbfunc(cbdata, ".", &dir_entry->handle, &dir_attr, 1))
00330                 goto outerr;
00331      }
00332 
00333      /* Fills ".." */
00334      if ((cookie <= 1) && (estimated_num_entries > 1)) {
00335           fsal_attrib_list_t parent_dir_attr;
00336           /* Get parent pentry */
00337           parent_dir_entry = cache_inode_lookupp(dir_entry,
00338                                                  context,
00339                                                  &cache_status_gethandle);
00340           if (parent_dir_entry == NULL) {
00341                if (req->rq_vers == NFS_V2) {
00342                     res->res_readdir2.status
00343                          = nfs2_Errno(cache_status_gethandle);
00344                } else if (req->rq_vers == NFS_V3) {
00345                     res->res_readdir3.status
00346                          = nfs3_Errno(cache_status_gethandle);
00347                }
00348                rc = NFS_REQ_OK;
00349                goto out;
00350           }
00351           if ((cache_inode_getattr(parent_dir_entry,
00352                                    &parent_dir_attr,
00353                                    context,
00354                                    &cache_status_gethandle))
00355               != CACHE_INODE_SUCCESS) {
00356                if (req->rq_vers == NFS_V2) {
00357                     res->res_readdir2.status
00358                          = nfs2_Errno(cache_status_gethandle);
00359                } else if (req->rq_vers == NFS_V3) {
00360                     res->res_readdir3.status
00361                          = nfs3_Errno(cache_status_gethandle);
00362                }
00363                rc = NFS_REQ_OK;
00364                goto out;
00365           }
00366           if (!cbfunc(cbdata, "..", &parent_dir_entry->handle,
00367                       &parent_dir_attr, 2))
00368                 goto outerr;
00369           cache_inode_put(parent_dir_entry);
00370           parent_dir_entry = NULL;
00371      }
00372 
00373 /* Some definitions that will be very useful to avoid very long names
00374    for variables */
00375 #define RES_READDIR2_OK   res->res_readdir2.READDIR2res_u.readdirok
00376 #define RES_READDIR3_OK   res->res_readdir3.READDIR3res_u.resok
00377 #define RES_READDIR3_FAIL res->res_readdir3.READDIR3res_u.resfail
00378 
00379      /* Call readdir */
00380      if (cache_inode_readdir(dir_entry,
00381                              cache_inode_cookie,
00382                              &num_entries,
00383                              &eod_met,
00384                              context,
00385                              cbfunc,
00386                              cbdata,
00387                              &cache_status) != CACHE_INODE_SUCCESS) {
00388           if (nfs_RetryableError(cache_status)) {
00389                rc = NFS_REQ_DROP;
00390                goto out;
00391           }
00392 
00393           nfs_SetFailedStatus(context, export,
00394                               req->rq_vers,
00395                               cache_status,
00396                               &res->res_readdir2.status,
00397                               &res->res_readdir3.status,
00398                               dir_entry,
00399                               &(res->res_readdir3.READDIR3res_u
00400                                 .resfail.dir_attributes),
00401                               NULL, NULL, NULL, NULL, NULL, NULL);
00402           goto out;
00403      }
00404 
00405      LogFullDebug(COMPONENT_NFS_READDIR,
00406                   "-- Readdir -> Call to "
00407                   "cache_inode_readdir(cookie=%"PRIu64
00408                   " -> num_entries = %u",
00409                   cache_inode_cookie,
00410                   num_entries);
00411 
00412      if (req->rq_vers == NFS_V2) {
00413           RES_READDIR2_OK.entries = cb2.entries;
00414           RES_READDIR2_OK.eof = eod_met;
00415      } else if (req->rq_vers == NFS_V3) {
00416           RES_READDIR3_OK.reply.entries = cb3.entries;
00417           RES_READDIR3_OK.reply.eof = eod_met;
00418           nfs_SetPostOpAttr(export,
00419                             &dir_attr,
00420                             &(RES_READDIR3_OK.dir_attributes));
00421           memcpy(RES_READDIR3_OK.cookieverf,
00422                  cookie_verifier,
00423                  sizeof(cookieverf3));
00424           res->res_readdir3.status = NFS3_OK;
00425      }
00426 
00427      rc = NFS_REQ_OK;
00428 
00429 out:
00430      /* return references */
00431      if (dir_entry) {
00432           cache_inode_put(dir_entry);
00433      }
00434 
00435      if (parent_dir_entry) {
00436           cache_inode_put(parent_dir_entry);
00437      }
00438 
00439      /* Deallocate memory in the event of an error */
00440      if (req->rq_vers == NFS_V2) {
00441           if (((res->res_readdir2.status != NFS_OK) ||
00442                (rc != NFS_REQ_OK)) &&
00443               (cb2.entries != NULL)) {
00444                free_entry2s(cb2.entries);
00445                RES_READDIR2_OK.entries = NULL;
00446           }
00447      } else if (req->rq_vers == NFS_V3) {
00448           if (((res->res_readdir3.status != NFS3_OK) ||
00449                (rc != NFS_REQ_OK)) &&
00450               (cb3.entries != NULL)) {
00451                free_entry3s(cb3.entries);
00452                RES_READDIR3_OK.reply.entries = NULL;
00453           }
00454      }
00455 
00456      return rc;
00457 
00458 outerr:
00459      if (req->rq_vers == NFS_V2) {
00460           assert(cbdata == &cb2);
00461           res->res_readdir2.status = cb2.error;
00462      } else if (req->rq_vers == NFS_V3) {
00463           assert(cbdata == &cb3);
00464           res->res_readdir3.status = cb3.error;
00465      }
00466 
00467      goto out;
00468 } /* nfs_Readdir */
00469 
00478 void nfs2_Readdir_Free(nfs_res_t * resp)
00479 {
00480      if ((resp->res_readdir2.status == NFS_OK) &&
00481          (resp->res_readdir2.READDIR2res_u.readdirok.entries != NULL)) {
00482           free_entry2s(resp->res_readdir2.READDIR2res_u.readdirok.entries);
00483      }
00484 } /* nfs2_Readdir_Free */
00485 
00494 void nfs3_Readdir_Free(nfs_res_t * resp)
00495 {
00496      if ((resp->res_readdir3.status == NFS3_OK) &&
00497          (resp->res_readdir3.READDIR3res_u.resok.reply.entries != NULL)) {
00498           free_entry3s(resp->res_readdir3.READDIR3res_u.resok.reply.entries);
00499      }
00500 } /* nfs3_Readdir_Free */
00501 
00518 static bool_t
00519 nfs2_readdir_callback(void* opaque,
00520                       char *name,
00521                       fsal_handle_t *handle,
00522                       fsal_attrib_list_t *attrs,
00523                       uint64_t cookie)
00524 {
00525      /* Not-so-opaque pointer to callback data`*/
00526      struct nfs2_readdir_cb_data *tracker =
00527           (struct nfs2_readdir_cb_data *) opaque;
00528      /* Space needed for this entry's filename */
00529      unsigned long namelen = strlen(name);
00530      /* A big-endian representation of the least significant
00531         thirty-to bits of the cookie. */
00532      uint32_t truncookie = htonl((uint32_t) cookie);
00533      entry2 *e2 = tracker->entries + tracker->count;
00534      /* Fileid descriptor */
00535      struct fsal_handle_desc id_descriptor
00536           = {sizeof(e2->fileid), (caddr_t) &e2->fileid};
00537      size_t need = sizeof(entry2) + ((namelen + 3) & ~3) + 4;
00538 
00539      if (tracker->count == tracker->total_entries) {
00540           return FALSE;
00541      }
00542      if (tracker->mem_left < (sizeof(entry2) + namelen)) {
00543           if (tracker->count == 0) {
00544                tracker->error = NFSERR_IO;
00545           }
00546           return FALSE;
00547      }
00548      FSAL_DigestHandle(FSAL_GET_EXP_CTX(tracker->context),
00549                        FSAL_DIGEST_FILEID2,
00550                        handle,
00551                        &id_descriptor);
00552      e2->name = gsh_malloc(namelen + 1);
00553      if (e2->name == NULL) {
00554           tracker->error = NFSERR_IO;
00555           return FALSE;
00556      }
00557      strcpy(e2->name, name);
00558      memcpy(e2->cookie, &truncookie, NFS2_COOKIESIZE);
00559      if (tracker->count != 0) {
00560           tracker->entries[tracker->count - 1].nextentry = e2;
00561      }
00562      tracker->mem_left -= need;
00563      ++(tracker->count);
00564      return TRUE;
00565 } /* nfs2_readdir_callback */
00566 
00583 static bool_t
00584 nfs3_readdir_callback(void* opaque,
00585                       char *name,
00586                       fsal_handle_t *handle,
00587                       fsal_attrib_list_t *attrs,
00588                       uint64_t cookie)
00589 {
00590      /* Not-so-opaque pointer to callback data`*/
00591      struct nfs3_readdir_cb_data *tracker =
00592           (struct nfs3_readdir_cb_data *) opaque;
00593      /* Length of the current filename */
00594      size_t namelen = strlen(name);
00595      entry3 *e3 = tracker->entries + tracker->count;
00596      /* Fileid descriptor */
00597      struct fsal_handle_desc id_descriptor
00598           = {sizeof(e3->fileid), (caddr_t) &e3->fileid};
00599      size_t need = sizeof(entry3) + ((namelen + 3) & ~3) + 4;
00600 
00601      if (tracker->count == tracker->total_entries) {
00602           return FALSE;
00603      }
00604      if ((tracker->mem_left < need)) {
00605           if (tracker->count == 0) {
00606                tracker->error = NFS3ERR_TOOSMALL;
00607           }
00608           return FALSE;
00609      }
00610      FSAL_DigestHandle(FSAL_GET_EXP_CTX(tracker->context),
00611                        FSAL_DIGEST_FILEID3,
00612                        handle,
00613                        &id_descriptor);
00614 
00615      e3->name = gsh_malloc(namelen + 1);
00616      if (e3->name == NULL) {
00617           tracker->error = NFS3ERR_IO;
00618           return FALSE;
00619      }
00620      strcpy(e3->name, name);
00621      e3->cookie = cookie;
00622 
00623      if (tracker->count > 0) {
00624           tracker->entries[tracker->count - 1].nextentry = e3;
00625      }
00626      tracker->mem_left -= need;
00627      ++(tracker->count);
00628      return TRUE;
00629 } /* */
00630 
00640 static void
00641 free_entry2s(entry2 *entry2s)
00642 {
00643      entry2 *entry = NULL;
00644 
00645      for (entry = entry2s;
00646           entry != NULL;
00647           entry = entry->nextentry) {
00648           gsh_free(entry->name);
00649      }
00650      gsh_free(entry2s);
00651 
00652      return;
00653 } /* free_entry2s */
00654 
00664 static void
00665 free_entry3s(entry3 *entry3s)
00666 {
00667      entry3 *entry = NULL;
00668 
00669      for (entry = entry3s;
00670           entry != NULL;
00671           entry = entry->nextentry) {
00672           gsh_free(entry->name);
00673      }
00674      gsh_free(entry3s);
00675 
00676      return;
00677 } /* free_entry3s */