nfs-ganesha 1.4
|
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 */