nfs-ganesha 1.4

nfs3_Readdirplus.c

Go to the documentation of this file.
00001 /*
00002  * vim: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
00022  * 02110-1301 USA
00023  *
00024  * ---------------------------------------
00025  */
00026 
00038 #ifdef HAVE_CONFIG_H
00039 #include "config.h"
00040 #endif
00041 
00042 #ifdef _SOLARIS
00043 #include "solaris_port.h"
00044 #endif
00045 
00046 #include <stdio.h>
00047 #include <string.h>
00048 #include <pthread.h>
00049 #include <fcntl.h>
00050 #include <sys/file.h>           /* for having FNDELAY */
00051 #include "HashData.h"
00052 #include "HashTable.h"
00053 #include "log.h"
00054 #include "nfs23.h"
00055 #include "nfs4.h"
00056 #include "mount.h"
00057 #include "nfs_core.h"
00058 #include "cache_inode.h"
00059 #include "cache_inode_lru.h"
00060 #include "cache_inode_weakref.h"
00061 #include "nfs_exports.h"
00062 #include "nfs_creds.h"
00063 #include "nfs_proto_functions.h"
00064 #include "nfs_tools.h"
00065 #include "nfs_file_handle.h"
00066 #include "nfs_proto_tools.h"
00067 #include <assert.h>
00068 
00069 static bool_t nfs3_readdirplus_callback(void* opaque,
00070                                         char *name,
00071                                         fsal_handle_t *handle,
00072                                         fsal_attrib_list_t *attrs,
00073                                         uint64_t cookie);
00074 static void free_entryplus3s(entryplus3 *entryplus3s);
00075 
00083 struct nfs3_readdirplus_cb_data
00084 {
00085      entryplus3 *entries; /*< The array holding individual entries */
00086      size_t mem_left; /*< The amount of memory remaining before we
00087                           hit maxcount */
00088      size_t count; /*< The count of complete entries stored in the
00089                        buffer */
00090      size_t total_entries; /*< The number of entires we allocated for
00091                                the array. */
00092      exportlist_t *export; /*< Pointer to the entry for the supplied
00093                                handle's export */
00094      fsal_op_context_t *context; /*< FSAL operation context */
00095      nfsstat3 error; /*< Set to a value other than NFS_OK if the
00096                          callback function finds a fatal error. */
00097 };
00098 
00115 int
00116 nfs3_Readdirplus(nfs_arg_t *arg,
00117                  exportlist_t *export,
00118                  fsal_op_context_t *context,
00119                  nfs_worker_data_t *pworker,
00120                  struct svc_req *req,
00121                  nfs_res_t *res)
00122 {
00123      cache_entry_t *dir_entry = NULL;
00124      fsal_attrib_list_t dir_attr;
00125      uint64_t begin_cookie = 0;
00126      uint64_t cache_inode_cookie = 0;
00127      cookieverf3 cookie_verifier;
00128      unsigned int num_entries = 0;
00129      unsigned long estimated_num_entries = 0;
00130      cache_inode_file_type_t dir_filetype = 0;
00131      bool_t eod_met = FALSE;
00132      cache_inode_status_t cache_status = 0;
00133      cache_inode_status_t cache_status_gethandle = 0;
00134      int rc = NFS_REQ_OK;
00135      struct nfs3_readdirplus_cb_data cb_opaque = {.entries = NULL,
00136                                                   .mem_left = 0,
00137                                                   .count = 0,
00138                                                   .export = export,
00139                                                   .context = context,
00140                                                   .error = NFS3_OK};
00141 
00142      if (isDebug(COMPONENT_NFSPROTO) ||
00143          isDebug(COMPONENT_NFS_READDIR)) {
00144           char str[LEN_FH_STR];
00145           log_components_t component;
00146           sprint_fhandle3(str, &(arg->arg_readdirplus3.dir));
00147           if (isDebug(COMPONENT_NFSPROTO)) {
00148                component = COMPONENT_NFSPROTO;
00149           } else {
00150                component = COMPONENT_NFS_READDIR;
00151                LogDebug(component,
00152                         "REQUEST PROCESSING: Calling nfs3_Readdirplus "
00153                         " handle: %s", str);
00154           }
00155      }
00156 
00157      /* to avoid setting it on each error case */
00158      res->res_readdir3.READDIR3res_u.resfail
00159           .dir_attributes.attributes_follow = FALSE;
00160 
00161      cb_opaque.mem_left = (arg->arg_readdirplus3.maxcount * 9) / 10;
00162      begin_cookie = arg->arg_readdirplus3.cookie;
00163 
00164      cb_opaque.mem_left -= sizeof(READDIRPLUS3resok);
00165 
00166      /* Estimate assuming that we're going to send no names and no handles.
00167       * Don't count space for pointers for nextentry or 
00168       * name_handle.data.data_val in entryplus3 */
00169      estimated_num_entries =
00170           MIN((cb_opaque.mem_left + sizeof(entryplus3 *))
00171               / (sizeof(entryplus3) - sizeof(char *)*2), 50);
00172 
00173      cb_opaque.total_entries = estimated_num_entries;
00174      LogFullDebug(COMPONENT_NFS_READDIR,
00175                   "nfs3_Readdirplus: dircount=%u "
00176                   "begin_cookie=%"PRIu64" "
00177                   "estimated_num_entries=%lu, mem_left=%zd",
00178                   arg->arg_readdirplus3.dircount,
00179                   begin_cookie,
00180                   estimated_num_entries, cb_opaque.mem_left);
00181 
00182      /* Is this a xattr FH ? */
00183      if (nfs3_Is_Fh_Xattr(&(arg->arg_readdirplus3.dir))) {
00184           rc = nfs3_Readdirplus_Xattr(arg, export, context,
00185                                       req, res);
00186           goto out;
00187      }
00188 
00189      /* Convert file handle into a vnode */
00190      if ((dir_entry
00191           = nfs_FhandleToCache(req->rq_vers,
00192                                NULL,
00193                                &(arg->arg_readdirplus3.dir),
00194                                NULL,
00195                                NULL,
00196                                &(res->res_readdirplus3.status),
00197                                NULL,
00198                                &dir_attr, context, &rc)) == NULL) {
00199           rc = NFS_REQ_DROP;
00200           goto out;
00201      }
00202 
00203      /* Extract the filetype */
00204      dir_filetype = cache_inode_fsal_type_convert(dir_attr.type);
00205 
00206      /* Sanity checks -- must be a directory */
00207 
00208      if (dir_filetype != DIRECTORY) {
00209           res->res_readdirplus3.status = NFS3ERR_NOTDIR;
00210           rc = NFS_REQ_OK;
00211           goto out;
00212     }
00213 
00214      memset(cookie_verifier, 0, sizeof(cookieverf3));
00215 
00216      /* If cookie verifier is used, then an non-trivial value is
00217         returned to the client This value is the mtime of the
00218         directory. If verifier is unused (as in many NFS Servers) then
00219         only a set of zeros is returned (trivial value) */
00220 
00221      if (export->UseCookieVerifier) {
00222           memcpy(cookie_verifier, &(dir_attr.mtime), sizeof(dir_attr.mtime));
00223      }
00224 
00225      if (export->UseCookieVerifier && (begin_cookie != 0)) {
00226           /* Not the first call, so we have to check the cookie
00227              verifier */
00228           if (memcmp(cookie_verifier, arg->arg_readdirplus3.cookieverf,
00229                      NFS3_COOKIEVERFSIZE) != 0) {
00230                res->res_readdirplus3.status = NFS3ERR_BAD_COOKIE;
00231                rc = NFS_REQ_OK;
00232                goto out;
00233           }
00234      }
00235 
00236      res->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL;
00237      res->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE;
00238 
00239      /* Fudge cookie for "." and "..", if necessary */
00240      if (begin_cookie > 1) {
00241           cache_inode_cookie = begin_cookie;
00242      } else {
00243           cache_inode_cookie = 0;
00244      }
00245 
00246      /* A definition that will be very useful to avoid very long names
00247         for variables */
00248 #define RES_READDIRPLUS_REPLY pres->res_readdirplus3.READDIRPLUS3res_u  \
00249           .resok.reply
00250 
00251      /* Allocate space for entries */
00252      cb_opaque.entries = gsh_calloc(estimated_num_entries, sizeof(entryplus3));
00253      if (cb_opaque.entries == NULL) {
00254           rc = NFS_REQ_DROP;
00255           goto out;
00256      }
00257 
00258      if (begin_cookie == 0) {
00259           /* Fill in "." */
00260           if (!(nfs3_readdirplus_callback(&cb_opaque,
00261                                           ".",
00262                                           &dir_entry->handle,
00263                                           &dir_attr,
00264                                           1))) {
00265                res->res_readdirplus3.status = cb_opaque.error;
00266                rc = NFS_REQ_OK;
00267                goto out;
00268           }
00269      }
00270 
00271      /* Fill in ".." */
00272      if (begin_cookie <= 1) {
00273           fsal_attrib_list_t parent_dir_attr;
00274           cache_entry_t *parent_dir_entry
00275                = cache_inode_lookupp(dir_entry,
00276                                      context,
00277                                      &cache_status_gethandle);
00278           if (parent_dir_entry == NULL) {
00279                res->res_readdirplus3.status
00280                     = nfs3_Errno(cache_status_gethandle);
00281                rc = NFS_REQ_OK;
00282                goto out;
00283           }
00284 
00285           if ((cache_inode_getattr(parent_dir_entry,
00286                                    &parent_dir_attr,
00287                                    context,
00288                                    &cache_status_gethandle))
00289               != CACHE_INODE_SUCCESS) {
00290                res->res_readdirplus3.status
00291                     = nfs3_Errno(cache_status_gethandle);
00292                cache_inode_lru_unref(parent_dir_entry, 0);
00293                rc = NFS_REQ_OK;
00294                goto out;
00295           }
00296           if (!(nfs3_readdirplus_callback(&cb_opaque,
00297                                           "..",
00298                                           &parent_dir_entry->handle,
00299                                           &parent_dir_attr,
00300                                           2))) {
00301                res->res_readdirplus3.status = cb_opaque.error;
00302                cache_inode_lru_unref(parent_dir_entry, 0);
00303                rc = NFS_REQ_OK;
00304                goto out;
00305           }
00306           cache_inode_lru_unref(parent_dir_entry, 0);
00307      }
00308 
00309      /* Call readdir */
00310      if (cache_inode_readdir(dir_entry,
00311                              cache_inode_cookie,
00312                              &num_entries,
00313                              &eod_met,
00314                              context,
00315                              nfs3_readdirplus_callback,
00316                              &cb_opaque,
00317                              &cache_status) != CACHE_INODE_SUCCESS) {
00318           /* Is this a retryable error */
00319           if (nfs_RetryableError(cache_status)) {
00320                rc = NFS_REQ_DROP;
00321                goto out;
00322           }
00323 
00324           /* Set failed status */
00325           nfs_SetFailedStatus(context,
00326                               export,
00327                               NFS_V3,
00328                               cache_status,
00329                               NULL,
00330                               &res->res_readdirplus3.status,
00331                               dir_entry,
00332                               &(res->res_readdirplus3.READDIRPLUS3res_u
00333                                 .resfail.dir_attributes),
00334                               NULL, NULL, NULL, NULL, NULL, NULL);
00335           goto out;
00336      }
00337      LogFullDebug(COMPONENT_NFS_READDIR,
00338                   "Readdirplus3 -> Call to cache_inode_readdir( cookie=%"
00339                   PRIu64") -> num_entries = %u",
00340                   cache_inode_cookie, num_entries);
00341 
00342      if ((num_entries == 0) && (begin_cookie > 1)) {
00343           res->res_readdirplus3.status = NFS3_OK;
00344           res->res_readdirplus3.READDIRPLUS3res_u
00345                .resok.reply.entries = NULL;
00346           res->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE;
00347 
00348           nfs_SetPostOpAttr(export,
00349                             NULL,
00350                             &(res->res_readdirplus3.READDIRPLUS3res_u
00351                               .resok.dir_attributes));
00352 
00353           memcpy(res->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf,
00354                  cookie_verifier, sizeof(cookieverf3));
00355      } else {
00356           res->res_readdirplus3.READDIRPLUS3res_u
00357                .resok.reply.entries = cb_opaque.entries;
00358           res->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof
00359                = eod_met;
00360      }
00361 
00362      nfs_SetPostOpAttr(export,
00363                        &dir_attr,
00364                        &(res->res_readdirplus3.READDIRPLUS3res_u
00365                          .resok.dir_attributes));
00366 
00367      memcpy(res->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf,
00368             cookie_verifier, sizeof(cookieverf3));
00369 
00370      res->res_readdirplus3.status = NFS3_OK;
00371 
00372      memcpy(res->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf,
00373             cookie_verifier, sizeof(cookieverf3));
00374 
00375      rc = NFS_REQ_OK;
00376 
00377 out:
00378      if (dir_entry)
00379           cache_inode_put(dir_entry);
00380 
00381      if (((res->res_readdir3.status != NFS3_OK) ||
00382           (rc != NFS_REQ_OK)) &&
00383          (cb_opaque.entries != NULL)) {
00384           free_entryplus3s(cb_opaque.entries);
00385      }
00386 
00387      return rc;
00388 }                               /* nfs3_Readdirplus */
00389 
00398 void nfs3_Readdirplus_Free(nfs_res_t *resp)
00399 {
00400 #define RESREADDIRPLUSREPLY resp->res_readdirplus3.READDIRPLUS3res_u.resok.reply
00401      if ((resp->res_readdirplus3.status == NFS3_OK) &&
00402          (RESREADDIRPLUSREPLY.entries != NULL)) {
00403           free_entryplus3s(RESREADDIRPLUSREPLY.entries);
00404      }
00405 } /*  nfs3_Readdirplus_Free */
00406 
00423 static bool_t
00424 nfs3_readdirplus_callback(void* opaque,
00425                           char *name,
00426                           fsal_handle_t *handle,
00427                           fsal_attrib_list_t *attrs,
00428                           uint64_t cookie)
00429 {
00430      /* Not-so-opaque pointer to callback data`*/
00431      struct nfs3_readdirplus_cb_data *tracker =
00432           (struct nfs3_readdirplus_cb_data *) opaque;
00433      /* Length of the current filename */
00434      size_t namelen = strlen(name);
00435      /* Fileid buffer descriptor */
00436      entryplus3 *ep3 = tracker->entries + tracker->count;
00437      struct fsal_handle_desc id_descriptor
00438           = {sizeof(ep3->fileid), (caddr_t) &ep3->fileid};
00439 
00440      if (tracker->count == tracker->total_entries) {
00441           return FALSE;
00442      }
00443      /* This is a pessimistic check, which assumes that we're going
00444       * to send attributes and full size handle - if it fails then 
00445       * we're close enough to the buffer size limit and t's time to 
00446       * stop anyway */
00447      if ((tracker->mem_left < (sizeof(entryplus3) + namelen + NFS3_FHSIZE))) {
00448           if (tracker->count == 0) {
00449                tracker->error = NFS3ERR_TOOSMALL;
00450           }
00451           return FALSE;
00452      }
00453 
00454      FSAL_DigestHandle(FSAL_GET_EXP_CTX(tracker->context),
00455                        FSAL_DIGEST_FILEID3,
00456                        handle,
00457                        &id_descriptor);
00458 
00459      ep3->name = gsh_malloc(namelen + 1);
00460      if (ep3->name == NULL) {
00461           tracker->error = NFS3ERR_IO;
00462           return FALSE;
00463      }
00464      strcpy(ep3->name, name);
00465      ep3->cookie = cookie;
00466 
00467      /* Account for file name + length + cookie */
00468      tracker->mem_left -= sizeof(ep3->cookie) + ((namelen + 3) & ~3) + 4;
00469 
00470      ep3->name_handle.handle_follows = TRUE;
00471      ep3->name_handle.post_op_fh3_u.handle.data.data_val
00472           = gsh_malloc(NFS3_FHSIZE);
00473      if (ep3->name_handle.post_op_fh3_u .handle.data.data_val == NULL) {
00474           tracker->error = NFS3ERR_SERVERFAULT;
00475           gsh_free(ep3->name);
00476           return FALSE;
00477      }
00478 
00479      if (nfs3_FSALToFhandle(&ep3->name_handle.post_op_fh3_u.handle,
00480                             handle,
00481                             tracker->export) == 0) {
00482           tracker->error = NFS3ERR_BADHANDLE;
00483           gsh_free(ep3->name);
00484           gsh_free(ep3->name_handle.post_op_fh3_u.handle.data.data_val);
00485           return FALSE;
00486      }
00487 
00488      /* Account for filehande + length + follows + nextentry */
00489      tracker->mem_left -= ep3->name_handle.post_op_fh3_u.handle.data.data_len + 12;
00490      if (tracker->count > 0) {
00491           tracker->entries[tracker->count - 1].nextentry = ep3;
00492      }
00493      ep3->name_attributes.attributes_follow = FALSE;
00494 
00495      nfs_SetPostOpAttr(tracker->export,
00496                        attrs,
00497                        &ep3->name_attributes);
00498      if (ep3->name_attributes.attributes_follow) {
00499           tracker->mem_left -= sizeof(ep3->name_attributes);
00500      } else {
00501           tracker->mem_left -= sizeof(ep3->name_attributes.attributes_follow);
00502      }
00503      ++(tracker->count);
00504      return TRUE;
00505 } /* nfs3_readdirplus_callback */
00506 
00516 static void
00517 free_entryplus3s(entryplus3 *entryplus3s)
00518 {
00519      entryplus3 *entry = NULL;
00520 
00521      for (entry = entryplus3s;
00522           entry != NULL;
00523           entry = entry->nextentry) {
00524           gsh_free(entry->name);
00525           gsh_free(entry->name_handle.post_op_fh3_u.handle.data.data_val);
00526      }
00527      gsh_free(entryplus3s);
00528 
00529      return;
00530 } /* free_entryplus3s */