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 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 */