nfs-ganesha 1.4

nfs4_op_readdir.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  02110-1301  USA
00022  *
00023  * --------------------------------------- */
00024 #ifdef HAVE_CONFIG_H
00025 #include "config.h"
00026 #endif
00027 #ifdef _SOLARIS
00028 #include "solaris_port.h"
00029 #endif
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <pthread.h>
00033 #include <fcntl.h>
00034 #include <sys/file.h>           /* for having FNDELAY */
00035 #include "HashData.h"
00036 #include "HashTable.h"
00037 #include "log.h"
00038 #include "ganesha_rpc.h"
00039 #include "nfs23.h"
00040 #include "nfs4.h"
00041 #include "mount.h"
00042 #include "nfs_core.h"
00043 #include "cache_inode.h"
00044 #include "nfs_exports.h"
00045 #include "nfs_creds.h"
00046 #include "nfs_proto_functions.h"
00047 #include "nfs_proto_tools.h"
00048 #include "nfs_file_handle.h"
00049 
00050 #define arg_READDIR4 op->nfs_argop4_u.opreaddir
00051 #define res_READDIR4 resp->nfs_resop4_u.opreaddir
00052 
00053 static bool_t nfs4_readdir_callback(void* opaque,
00054                                     char *name,
00055                                     fsal_handle_t *handle,
00056                                     fsal_attrib_list_t *attrs,
00057                                     uint64_t cookie);
00058 static void free_entries(entry4 *entries);
00059 
00060 static const bitmap4 RdAttrErrorBitmap = {1, (uint32_t *) "\0\0\0\b"};
00061 static const attrlist4 RdAttrErrorVals = {0, NULL};
00062 
00070 struct nfs4_readdir_cb_data
00071 {
00072      entry4 *entries; /*< The array holding individual entries */
00073      size_t mem_left; /*< The amount of memory remaining before we
00074                           hit maxcount */
00075      size_t count; /*< The count of complete entries stored in the
00076                        buffer */
00077      size_t total_entries; /*< The total number of entries available
00078                                in the array*/
00079      nfsstat4 error; /*< Set to a value other than NFS4_OK if the
00080                          callback function finds a fatal error. */
00081      bitmap4 req_attr; /*< The requested attributes */
00082      compound_data_t *data; /*< The compound data, so we can produce
00083                                 nfs_fh4s. */
00084 };
00085 
00099 int
00100 nfs4_op_readdir(struct nfs_argop4 *op,
00101                 compound_data_t * data,
00102                 struct nfs_resop4 *resp)
00103 {
00104      cache_entry_t *dir_entry = NULL;
00105      bool_t eod_met = FALSE;
00106      char __attribute__ ((__unused__)) funcname[] = "nfs4_op_readdir";
00107      unsigned long dircount = 0;
00108      unsigned long maxcount = 0;
00109      entry4 *entries = NULL;
00110      verifier4 cookie_verifier;
00111      uint64_t cookie = 0;
00112      unsigned int estimated_num_entries = 0;
00113      unsigned int num_entries = 0;
00114      struct nfs4_readdir_cb_data cb_data;
00115      cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
00116 
00117      resp->resop = NFS4_OP_READDIR;
00118      res_READDIR4.status = NFS4_OK;
00119 
00120      if ((res_READDIR4.status
00121           = nfs4_sanity_check_FH(data, DIRECTORY)) != NFS4_OK) {
00122           goto out;
00123      }
00124 
00125      /* Pseudo Fs management */
00126      if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) {
00127           res_READDIR4.status = nfs4_op_readdir_pseudo(op, data, resp);
00128           goto out;
00129      }
00130 
00131      /* Xattrs management */
00132      if(nfs4_Is_Fh_Xattr(&(data->currentFH))) {
00133           res_READDIR4.status = nfs4_op_readdir_xattr(op, data, resp);
00134           goto out;
00135      }
00136 
00137      dir_entry = data->current_entry;
00138 
00139      /* get the characteristic value for readdir operation */
00140      dircount = arg_READDIR4.dircount;
00141      maxcount = (arg_READDIR4.maxcount * 9) / 10;
00142      cookie = arg_READDIR4.cookie;
00143 
00144      /* Dircount is considered meaningless by many nfsv4 client (like the CITI
00145         one).  we use maxcount instead. */
00146 
00147      /* The Linux 3.0, 3.1.0 clients vs. TCP Ganesha comes out 10x slower
00148         with 500 max entries */
00149 
00150      estimated_num_entries = 50;
00151      cb_data.total_entries = estimated_num_entries;
00152 
00153      LogFullDebug(COMPONENT_NFS_V4,
00154                   "--- nfs4_op_readdir ---> dircount=%lu maxcount=%lu "
00155                   "cookie=%"PRIu64" estimated_num_entries=%u",
00156                   dircount, maxcount, cookie, estimated_num_entries);
00157 
00158      /* Since we never send a cookie of 1 or 2, we shouldn't ever get
00159         them back. */
00160      if (cookie == 1 || cookie == 2) {
00161           res_READDIR4.status = NFS4ERR_BAD_COOKIE;
00162           goto out;
00163      }
00164 
00165      /* Get only attributes that are allowed to be read */
00166      if (!nfs4_Fattr_Check_Access_Bitmap(&arg_READDIR4.attr_request,
00167                                         FATTR4_ATTR_READ)) {
00168           res_READDIR4.status = NFS4ERR_INVAL;
00169           goto out;
00170      }
00171 
00172      /* If maxcount is too short (14 should be enough for an empty directory)
00173           return NFS4ERR_TOOSMALL */
00174      if (maxcount < 14 || estimated_num_entries == 0) {
00175           res_READDIR4.status = NFS4ERR_TOOSMALL;
00176           goto out;
00177      }
00178 
00179      /* If a cookie verifier is used, then a non-trivial value is
00180         returned to the client.  This value is the mtime of the
00181         directory.  If verifier is unused (as in many NFS Servers)
00182         then only a set of zeros is returned (trivial value) */
00183      memset(cookie_verifier, 0, NFS4_VERIFIER_SIZE);
00184 
00185      /* Cookie delivered by the server and used by the client SHOULD
00186         not be 0, 1 or 2 because these values are reserved (see RFC
00187         3530, p. 192/RFC 5661, p468).
00188 
00189              0 - cookie for first READDIR
00190              1 - reserved for . on client
00191              2 - reserved for .. on client
00192 
00193         '.' and '..' are not returned, so all cookies will be offset
00194         by 2 */
00195 
00196      if ((cookie != 0) && (data->pexport->UseCookieVerifier == 1)) {
00197           if(memcmp(cookie_verifier, arg_READDIR4.cookieverf,
00198                     NFS4_VERIFIER_SIZE) != 0) {
00199                res_READDIR4.status = NFS4ERR_BAD_COOKIE;
00200                goto out;
00201           }
00202      }
00203 
00204      /* Prepare to read the entries */
00205 
00206      entries = gsh_calloc(estimated_num_entries,
00207                           sizeof(entry4));
00208      cb_data.entries = entries;
00209      cb_data.mem_left = maxcount - sizeof(READDIR4resok);
00210      cb_data.count = 0;
00211      cb_data.error = NFS4_OK;
00212      cb_data.req_attr = arg_READDIR4.attr_request;
00213      cb_data.data = data;
00214 
00215      /* Perform the readdir operation */
00216      if (cache_inode_readdir(dir_entry,
00217                              cookie,
00218                              &num_entries,
00219                              &eod_met,
00220                              data->pcontext,
00221                              nfs4_readdir_callback,
00222                              &cb_data,
00223                              &cache_status) != CACHE_INODE_SUCCESS) {
00224           res_READDIR4.status = nfs4_Errno(cache_status);
00225           goto out;
00226      }
00227 
00228      if ((res_READDIR4.status = cb_data.error) != NFS4_OK) {
00229           goto out;
00230      }
00231 
00232      if (cb_data.count != 0) {
00233           /* Put the entry's list in the READDIR reply if there were any. */
00234           res_READDIR4.READDIR4res_u.resok4.reply.entries = entries;
00235      } else {
00236           gsh_free(entries);
00237           res_READDIR4.READDIR4res_u.resok4.reply.entries
00238                = entries = NULL;
00239      }
00240 
00241      res_READDIR4.READDIR4res_u.resok4.reply.eof = eod_met;
00242 
00243      /* Do not forget to set the verifier */
00244      memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf,
00245             cookie_verifier, NFS4_VERIFIER_SIZE);
00246 
00247 
00248      res_READDIR4.status = NFS4_OK;
00249 
00250 out:
00251      if (res_READDIR4.status != NFS4_OK) {
00252           if (entries) {
00253                free_entries(entries);
00254           }
00255      }
00256 
00257   return res_READDIR4.status;
00258 }                               /* nfs4_op_readdir */
00259 
00270 void nfs4_op_readdir_Free(READDIR4res *resp)
00271 {
00272      free_entries(resp->READDIR4res_u.resok4.reply.entries);
00273 } /* nfs4_op_readdir_Free */
00274 
00291 static bool_t
00292 nfs4_readdir_callback(void* opaque,
00293                       char *name,
00294                       fsal_handle_t *handle,
00295                       fsal_attrib_list_t *attrs,
00296                       uint64_t cookie)
00297 {
00298      struct nfs4_readdir_cb_data *tracker =
00299           (struct nfs4_readdir_cb_data *) opaque;
00300      size_t namelen = 0;
00301      char val_fh[NFS4_FHSIZE];
00302      nfs_fh4 entryFH = {
00303           .nfs_fh4_len = 0,
00304           .nfs_fh4_val = val_fh
00305      };
00306 
00307      if (tracker->total_entries == tracker->count) {
00308           return FALSE;
00309      }
00310      memset(val_fh, 0, NFS4_FHSIZE);
00311      /* Bits that don't require allocation */
00312      if (tracker->mem_left < sizeof(entry4)) {
00313           if (tracker->count == 0) {
00314                tracker->error = NFS4ERR_TOOSMALL;
00315           }
00316           return FALSE;
00317      }
00318      tracker->mem_left -= sizeof(entry4);
00319      tracker->entries[tracker->count].cookie = cookie;
00320      tracker->entries[tracker->count].nextentry = NULL;
00321 
00322      /* The filename.  We don't use str2utf8 because that has an
00323         additional copy into a buffer before copying into the
00324         destination. */
00325 
00326      namelen = strlen(name);
00327      if (tracker->mem_left < (namelen + 1)) {
00328           if (tracker->count == 0) {
00329                tracker->error = NFS4ERR_TOOSMALL;
00330           }
00331           return FALSE;
00332      }
00333      tracker->mem_left -= (namelen + 1);
00334      tracker->entries[tracker->count].name.utf8string_len = namelen;
00335      tracker->entries[tracker->count].name.utf8string_val
00336           = gsh_malloc(namelen + 1);
00337      strcpy(tracker->entries[tracker->count].name.utf8string_val,
00338             name);
00339 
00340      if ((tracker->req_attr.bitmap4_val != NULL) &&
00341          (tracker->req_attr.bitmap4_val[0] & FATTR4_FILEHANDLE)) {
00342           if (!nfs4_FSALToFhandle(&entryFH, handle, tracker->data)) {
00343                tracker->error = NFS4ERR_SERVERFAULT;
00344                gsh_free(tracker->entries[tracker->count].name.utf8string_val);
00345                return FALSE;
00346           }
00347      }
00348 
00349      if (nfs4_FSALattr_To_Fattr(tracker->data->pexport,
00350                                 attrs,
00351                                 &tracker->entries[tracker->count].attrs,
00352                                 tracker->data,
00353                                 &entryFH,
00354                                 &tracker->req_attr) != 0) {
00355           /* Return the fattr4_rdattr_error, see RFC 3530, p. 192/RFC
00356              5661 p. 112. */
00357           tracker->entries[tracker->count]
00358                .attrs.attrmask = RdAttrErrorBitmap;
00359           tracker->entries[tracker->count]
00360                .attrs.attr_vals = RdAttrErrorVals;
00361      }
00362 
00363      if (tracker->mem_left <
00364          ((tracker->entries[tracker->count].attrs.attrmask.bitmap4_len *
00365            sizeof(uint32_t)) +
00366           (tracker->entries[tracker->count]
00367            .attrs.attr_vals.attrlist4_len))) {
00368           gsh_free(tracker->entries[tracker->count]
00369                    .attrs.attrmask.bitmap4_val);
00370           gsh_free(tracker->entries[tracker->count]
00371                    .attrs.attr_vals.attrlist4_val);
00372           gsh_free(tracker->entries[tracker->count].name.utf8string_val);
00373           if (tracker->count == 0) {
00374                tracker->error = NFS4ERR_TOOSMALL;
00375           }
00376           return FALSE;
00377      }
00378      tracker->mem_left -=
00379           (tracker->entries[tracker->count].attrs.attrmask.bitmap4_len *
00380            sizeof(uint32_t));
00381      tracker->mem_left -=
00382           (tracker->entries[tracker->count].attrs.attr_vals.attrlist4_len);
00383 
00384      if (tracker->count != 0) {
00385           tracker->entries[tracker->count - 1].nextentry =
00386                &tracker->entries[tracker->count];
00387      }
00388 
00389      ++(tracker->count);
00390      return TRUE;
00391 }
00392 
00393 static void
00394 free_entries(entry4 *entries)
00395 {
00396      entry4 *entry = NULL;
00397 
00398      for (entry = entries;
00399           entry != NULL;
00400           entry = entry->nextentry) {
00401           if (entry->attrs.attrmask.bitmap4_val !=
00402               RdAttrErrorBitmap.bitmap4_val) {
00403                gsh_free(entry->attrs.attrmask.bitmap4_val);
00404           }
00405           if (entry->attrs.attr_vals.attrlist4_val !=
00406               RdAttrErrorVals.attrlist4_val) {
00407                gsh_free(entry->attrs.attr_vals.attrlist4_val);
00408           }
00409           gsh_free(entry->name.utf8string_val);
00410      }
00411      gsh_free(entries);
00412 
00413      return;
00414 } /* free_entries */