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