int nfs3_commit(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { cache_inode_status_t cache_status; cache_entry_t *entry = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_commit3.file)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_commit handle: %s", str); } /* To avoid setting it on each error case */ res->res_commit3.COMMIT3res_u.resfail.file_wcc.before. attributes_follow = FALSE; res->res_commit3.COMMIT3res_u.resfail.file_wcc.after.attributes_follow = FALSE; entry = nfs3_FhandleToCache(&arg->arg_commit3.file, &res->res_commit3.status, &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } cache_status = cache_inode_commit(entry, arg->arg_commit3.offset, arg->arg_commit3.count); if (cache_status != CACHE_INODE_SUCCESS) { res->res_commit3.status = nfs3_Errno(cache_status); nfs_SetWccData(NULL, entry, &(res->res_commit3.COMMIT3res_u.resfail. file_wcc)); rc = NFS_REQ_OK; goto out; } nfs_SetWccData(NULL, entry, &(res->res_commit3.COMMIT3res_u.resok.file_wcc)); /* Set the write verifier */ memcpy(res->res_commit3.COMMIT3res_u.resok.verf, NFS3_write_verifier, sizeof(writeverf3)); res->res_commit3.status = NFS3_OK; out: if (entry) cache_inode_put(entry); return rc; } /* nfs3_commit */
int nfs3_Pathconf(nfs_arg_t * parg, exportlist_t * pexport, fsal_op_context_t * pcontext, cache_inode_client_t * pclient, hash_table_t * ht, struct svc_req *preq, nfs_res_t * pres) { static char __attribute__ ((__unused__)) funcName[] = "nfs3_Pathconf"; cache_inode_status_t cache_status; cache_entry_t *pentry = NULL; cache_inode_fsal_data_t fsal_data; fsal_attrib_list_t attr; fsal_staticfsinfo_t staticinfo; fsal_dynamicfsinfo_t dynamicinfo; /* to avoid setting it on each error case */ pres->res_pathconf3.PATHCONF3res_u.resfail.obj_attributes.attributes_follow = FALSE; /* Convert file handle into a fsal_handle */ if(nfs3_FhandleToFSAL(&(parg->arg_access3.object), &fsal_data.handle, pcontext) == 0) return NFS_REQ_DROP; /* Set cookie to zero */ fsal_data.cookie = DIR_START; /* Get the entry in the cache_inode */ if((pentry = cache_inode_get(&fsal_data, &attr, ht, pclient, pcontext, &cache_status)) == NULL) { /* Stale NFS FH ? */ pres->res_pathconf3.status = NFS3ERR_STALE; return NFS_REQ_OK; } /* Get the filesystem information */ if(cache_inode_statfs(pentry, &staticinfo, &dynamicinfo, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { pres->res_pathconf3.status = nfs3_Errno(cache_status); return NFS_REQ_OK; } /* Build post op file attributes */ nfs_SetPostOpAttr(pcontext, pexport, pentry, &attr, &(pres->res_pathconf3.PATHCONF3res_u.resok.obj_attributes)); pres->res_pathconf3.PATHCONF3res_u.resok.linkmax = staticinfo.maxlink; pres->res_pathconf3.PATHCONF3res_u.resok.name_max = staticinfo.maxnamelen; pres->res_pathconf3.PATHCONF3res_u.resok.no_trunc = staticinfo.no_trunc; pres->res_pathconf3.PATHCONF3res_u.resok.chown_restricted = staticinfo.chown_restricted; pres->res_pathconf3.PATHCONF3res_u.resok.case_insensitive = staticinfo.case_insensitive; pres->res_pathconf3.PATHCONF3res_u.resok.case_preserving = staticinfo.case_preserving; return NFS_REQ_OK; } /* nfs3_Pathconf */
int nfs3_getattr(nfs_arg_t *arg, nfs_worker_data_t *worker, struct svc_req *req, nfs_res_t *res) { cache_entry_t *entry = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &(arg->arg_getattr3.object), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_getattr handle: %s", str); } entry = nfs3_FhandleToCache(&arg->arg_getattr3.object, &res->res_getattr3.status, &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr returning %d", rc); goto out; } if (!cache_entry_to_nfs3_Fattr( entry, &res->res_getattr3.GETATTR3res_u.resok.obj_attributes)) { res->res_getattr3.status = nfs3_Errno(CACHE_INODE_INVALID_ARGUMENT); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr set failed status v3"); rc = NFS_REQ_OK; goto out; } res->res_getattr3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr succeeded"); rc = NFS_REQ_OK; out: /* return references */ if (entry) cache_inode_put(entry); return rc; }
int nfs3_access(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { cache_inode_status_t cache_status; cache_entry_t *entry = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_access3.object)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_access handle: %s", str); } /* to avoid setting it on each error case */ res->res_access3.ACCESS3res_u.resfail.obj_attributes.attributes_follow = FALSE; /* Convert file handle into a vnode */ entry = nfs3_FhandleToCache(&(arg->arg_access3.object), &(res->res_access3.status), &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Perform the 'access' call */ cache_status = nfs_access_op(entry, arg->arg_access3.access, &res->res_access3.ACCESS3res_u.resok.access, NULL); if (cache_status == CACHE_INODE_SUCCESS || cache_status == CACHE_INODE_FSAL_EACCESS) { /* Build Post Op Attributes */ nfs_SetPostOpAttr(entry, &(res->res_access3.ACCESS3res_u.resok. obj_attributes)); res->res_access3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } /* If we are here, there was an error */ if (nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } res->res_access3.status = nfs3_Errno(cache_status); nfs_SetPostOpAttr(entry, &(res->res_access3.ACCESS3res_u.resfail. obj_attributes)); out: if (entry) cache_inode_put(entry); return rc; } /* nfs3_access */
int nfs3_link(nfs_arg_t *arg, nfs_worker_data_t *worker, struct svc_req *req, nfs_res_t *res) { const char *link_name = arg->arg_link3.link.name; cache_entry_t *target_entry = NULL; cache_entry_t *parent_entry = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; short to_exportid = 0; short from_exportid = 0; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &(arg->arg_link3.file), NULL, strfrom); nfs_FhandleToStr(req->rq_vers, &(arg->arg_link3.link.dir), NULL, strto); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_link handle: %s to " "handle: %s name: %s", strfrom, strto, link_name); } /* to avoid setting it on each error case */ res->res_link3.LINK3res_u.resfail.file_attributes.attributes_follow = FALSE; res->res_link3.LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE; res->res_link3.LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE; /* Get the exportids for the two handles. */ to_exportid = nfs3_FhandleToExportId(&(arg->arg_link3.link.dir)); from_exportid = nfs3_FhandleToExportId(&(arg->arg_link3.file)); /* Validate the to_exportid */ if (to_exportid < 0 || from_exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS%d LINK Request from client %s has badly formed handle for link dir", req->rq_vers, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); /* Bad handle, report to client */ res->res_link3.status = NFS3ERR_BADHANDLE; goto out; } /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) { res->res_link3.status = NFS3ERR_XDEV; goto out; } /* Get entry for parent directory */ parent_entry = nfs3_FhandleToCache(&arg->arg_link3.link.dir, &res->res_link3.status, &rc); if (parent_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_entry, &pre_parent); target_entry = nfs3_FhandleToCache(&arg->arg_link3.file, &res->res_link3.status, &rc); if (target_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Sanity checks: */ if (parent_entry->type != DIRECTORY) { res->res_link3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if (link_name == NULL || *link_name == '\0') res->res_link3.status = NFS3ERR_INVAL; else { /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) res->res_link3.status = NFS3ERR_XDEV; else { cache_status = cache_inode_link(target_entry, parent_entry, link_name); if (cache_status == CACHE_INODE_SUCCESS) { nfs_SetPostOpAttr(target_entry, &(res->res_link3.LINK3res_u. resok.file_attributes)); nfs_SetWccData(&pre_parent, parent_entry, &(res->res_link3.LINK3res_u. resok.linkdir_wcc)); res->res_link3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } } /* else */ } /* If we are here, there was an error */ if (nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } res->res_link3.status = nfs3_Errno(cache_status); nfs_SetPostOpAttr(target_entry, &(res->res_link3.LINK3res_u.resfail.file_attributes)); nfs_SetWccData(&pre_parent, parent_entry, &res->res_link3.LINK3res_u.resfail.linkdir_wcc); rc = NFS_REQ_OK; out: /* return references */ if (target_entry) cache_inode_put(target_entry); if (parent_entry) cache_inode_put(parent_entry); return rc; } /* nfs3_link */ /** * @brief Free the result structure allocated for nfs3_link * * This function frees the result structure allocated for nfs3_link. * * @param[in,out] resp Result structure * */ void nfs3_link_free(nfs_res_t *resp) { return; }
int nfs3_Mknod(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t * pres) { cache_entry_t *parent_pentry = NULL; fsal_attrib_list_t parent_attr; fsal_attrib_list_t *ppre_attr; fsal_attrib_list_t attr_parent_after; cache_inode_file_type_t parent_filetype; cache_inode_file_type_t nodetype; char *str_file_name = NULL; fsal_name_t file_name; cache_inode_status_t cache_status; cache_inode_status_t cache_status_lookup; fsal_accessmode_t mode = 0; cache_entry_t *node_pentry = NULL; fsal_attrib_list_t attr; cache_inode_create_arg_t create_arg; fsal_handle_t *pfsal_handle; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif memset(&create_arg, 0, sizeof(create_arg)); if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(parg->arg_mknod3.where.dir)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_Mknod handle: %s name: %s", str, parg->arg_mknod3.where.name); } /* to avoid setting them on each error case */ pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; /* retrieve parent entry */ if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, NULL, &(parg->arg_mknod3.where.dir), NULL, NULL, &(pres->res_mknod3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ return rc; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: new node name must be non-null; parent must be a * directory. */ if(parent_filetype != DIRECTORY) { pres->res_mknod3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } str_file_name = parg->arg_mknod3.where.name; switch (parg->arg_mknod3.what.type) { case NF3CHR: case NF3BLK: if(parg->arg_mknod3.what.mknoddata3_u.device.dev_attributes.mode.set_it) mode = (fsal_accessmode_t) parg->arg_mknod3.what.mknoddata3_u.device.dev_attributes. mode.set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; create_arg.dev_spec.major = parg->arg_mknod3.what.mknoddata3_u.device.spec.specdata1; create_arg.dev_spec.minor = parg->arg_mknod3.what.mknoddata3_u.device.spec.specdata2; break; case NF3FIFO: case NF3SOCK: if(parg->arg_mknod3.what.mknoddata3_u.pipe_attributes.mode.set_it) mode = (fsal_accessmode_t) parg->arg_mknod3.what.mknoddata3_u.pipe_attributes.mode. set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; create_arg.dev_spec.major = 0; create_arg.dev_spec.minor = 0; break; default: pres->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } switch (parg->arg_mknod3.what.type) { case NF3CHR: nodetype = CHARACTER_FILE; break; case NF3BLK: nodetype = BLOCK_FILE; break; case NF3FIFO: nodetype = FIFO_FILE; break; case NF3SOCK: nodetype = SOCKET_FILE; break; default: pres->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } //if(str_file_name == NULL || strlen(str_file_name) == 0) if(str_file_name == NULL || *str_file_name == '\0' ) { pres->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { pres->res_mknod3.status = NFS3ERR_DQUOT; return NFS_REQ_OK; } #endif /* _USE_QUOTA */ /* convert node name */ if((cache_status = cache_inode_error_convert(FSAL_str2name(str_file_name, 0, &file_name))) == CACHE_INODE_SUCCESS) { /* * Lookup node to see if it exists. If so, use it. Otherwise * create a new one. */ node_pentry = cache_inode_lookup(parent_pentry, &file_name, &attr, pcontext, &cache_status_lookup); if(cache_status_lookup == CACHE_INODE_NOT_FOUND) { /* Create the node */ if((node_pentry = cache_inode_create(parent_pentry, &file_name, nodetype, mode, &create_arg, &attr, pcontext, &cache_status)) != NULL) { MKNOD3resok *rok = &pres->res_mknod3.MKNOD3res_u.resok; /* * Get the FSAL handle for this entry */ pfsal_handle = &node_pentry->handle; /* Build file handle */ pres->res_mknod3.status = nfs3_AllocateFH(&rok->obj.post_op_fh3_u.handle); if(pres->res_mknod3.status != NFS3_OK) return NFS_REQ_OK; if(nfs3_FSALToFhandle(&rok->obj.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { gsh_free(rok->obj.post_op_fh3_u.handle.data.data_val); pres->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ rok->obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr, &rok->obj_attributes); /* Get the attributes of the parent after the operation */ if(cache_inode_getattr(parent_pentry, &attr_parent_after, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { pres->res_mknod3.status = nfs3_Errno(cache_status); rc = NFS_REQ_OK; goto out; } /* Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &rok->dir_wcc); pres->res_mknod3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } /* mknod sucess */ } /* not found */ else { /* object already exists or failure during lookup */ if(cache_status_lookup == CACHE_INODE_SUCCESS) { /* Trying to create an entry that already exists */ pres->res_mknod3.status = NFS3ERR_EXIST; } else { /* Server fault */ pres->res_mknod3.status = NFS3ERR_INVAL; } nfs_SetWccData(pexport, NULL, NULL, &(pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc)); rc = NFS_REQ_OK; goto out; } } /* If we are here, there was an error */ rc = nfs_SetFailedStatus(pexport, preq->rq_vers, cache_status, NULL, &pres->res_mknod3.status, NULL, ppre_attr, &(pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc), NULL, NULL); out: /* return references */ if (parent_pentry) cache_inode_put(parent_pentry); if (node_pentry) cache_inode_put(node_pentry); return (rc); } /* nfs3_Mknod */
int nfs3_Readdirplus(nfs_arg_t * parg, exportlist_t * pexport, fsal_op_context_t * pcontext, cache_inode_client_t * pclient, hash_table_t * ht, struct svc_req *preq, nfs_res_t * pres) { static char __attribute__ ((__unused__)) funcName[] = "nfs3_Readdirplus"; typedef char entry_name_array_item_t[FSAL_MAX_NAME_LEN]; typedef char fh3_buffer_item_t[NFS3_FHSIZE]; unsigned int delta = 0; cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry_dot_dot = NULL; unsigned long dircount; unsigned long maxcount; fsal_attrib_list_t dir_attr; fsal_attrib_list_t entry_attr; uint64_t begin_cookie; uint64_t end_cookie; uint64_t cache_inode_cookie; cache_inode_dir_entry_t **dirent_array = NULL; cookieverf3 cookie_verifier; int rc; unsigned int i = 0; unsigned int num_entries; unsigned long space_used; unsigned long estimated_num_entries; unsigned long asked_num_entries; cache_inode_file_type_t dir_filetype; cache_inode_endofdir_t eod_met = UNASSIGNED_EOD; cache_inode_status_t cache_status; cache_inode_status_t cache_status_gethandle; fsal_handle_t *pfsal_handle = NULL; entry_name_array_item_t *entry_name_array = NULL; fh3_buffer_item_t *fh3_array = NULL; entryplus3 reference_entry; READDIRPLUS3resok reference_reply; int dir_pentry_unlock = FALSE; if(isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; log_components_t component; sprint_fhandle3(str, &(parg->arg_readdirplus3.dir)); if(isDebug(COMPONENT_NFSPROTO)) component = COMPONENT_NFSPROTO; else component = COMPONENT_NFS_READDIR; LogDebug(component, "REQUEST PROCESSING: Calling nfs3_Readdirplus handle: %s", str); } /* to avoid setting it on each error case */ pres->res_readdir3.READDIR3res_u.resfail.dir_attributes.attributes_follow = FALSE; dircount = parg->arg_readdirplus3.dircount; maxcount = parg->arg_readdirplus3.maxcount; begin_cookie = (unsigned int)parg->arg_readdirplus3.cookie; /* FIXME: This calculation over estimates the number of bytes that * READDIRPLUS3resok will use on the wire by 4 bytes on x86_64. */ space_used = sizeof(reference_reply.dir_attributes.attributes_follow) + sizeof(reference_reply.dir_attributes.post_op_attr_u.attributes) + sizeof(reference_reply.cookieverf) + sizeof(reference_reply.reply.eof); estimated_num_entries = (dircount - space_used + sizeof(entry3 *)) / (sizeof(entry3) - sizeof(char *)*2); // estimated_num_entries *= 4; LogFullDebug(COMPONENT_NFS_READDIR, "nfs3_Readdirplus: dircount=%lu maxcount=%lu begin_cookie=%" PRIu64" space_used=%lu estimated_num_entries=%lu", dircount, maxcount, begin_cookie, space_used, estimated_num_entries); /* Is this a xattr FH ? */ if(nfs3_Is_Fh_Xattr(&(parg->arg_readdirplus3.dir))) return nfs3_Readdirplus_Xattr(parg, pexport, pcontext, pclient, ht, preq, pres); /* Convert file handle into a vnode */ if((dir_pentry = nfs_FhandleToCache(preq->rq_vers, NULL, &(parg->arg_readdirplus3.dir), NULL, NULL, &(pres->res_readdirplus3.status), NULL, &dir_attr, pcontext, pclient, ht, &rc)) == NULL) { /* return NFS_REQ_DROP ; */ return rc; } /* Extract the filetype */ dir_filetype = cache_inode_fsal_type_convert(dir_attr.type); /* Sanity checks -- must be a directory */ if(dir_filetype != DIRECTORY) { pres->res_readdirplus3.status = NFS3ERR_NOTDIR; return NFS_REQ_OK; } /* switch */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the directory. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ if(pexport->UseCookieVerifier) memcpy(cookie_verifier, &(dir_attr.mtime), sizeof(dir_attr.mtime)); /* * nothing to do if != 0 because the area is already full of * zero */ if(pexport->UseCookieVerifier && (begin_cookie != 0)) { /* * Not the first call, so we have to check the cookie * verifier */ if(memcmp(cookie_verifier, parg->arg_readdirplus3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { pres->res_readdirplus3.status = NFS3ERR_BAD_COOKIE; return NFS_REQ_OK; } } if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc_Label( estimated_num_entries * sizeof(cache_inode_dir_entry_t*), "cache_inode_dir_entry_t in nfs3_Readdirplus")) == NULL) { pres->res_readdirplus3.status = NFS3ERR_IO; return NFS_REQ_DROP; } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; /** @todo XXXX fix this--compare nfs4_op_readdir */ /* How many entries will we retry from cache_inode ? */ if(begin_cookie > 1) { asked_num_entries = estimated_num_entries; cache_inode_cookie = begin_cookie; } else { asked_num_entries = ((estimated_num_entries > 2) ? estimated_num_entries - 2 : 0); /* Keep space for '.' and '..' */ cache_inode_cookie = 0; } /* A definition that will be very useful to avoid very long names for variables */ #define RES_READDIRPLUS_REPLY pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply /* Call readdir */ if(cache_inode_readdir(dir_pentry, pexport->cache_inode_policy, cache_inode_cookie, asked_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, ht, &dir_pentry_unlock, pclient, pcontext, &cache_status) == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> Call to cache_inode_readdir( cookie=%" PRIu64", asked=%lu ) -> num_entries = %u", cache_inode_cookie, asked_num_entries, num_entries); if(eod_met == END_OF_DIR) { LogFullDebug(COMPONENT_NFS_READDIR, "+++++++++++++++++++++++++++++++++++++++++> EOD MET "); } /* If nothing was found, return nothing, but if cookie=0, we should return . and .. */ if((num_entries == 0) && (asked_num_entries != 0) && (begin_cookie > 1)) { pres->res_readdirplus3.status = NFS3_OK; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, NULL, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); } else { /* Allocation of the structure for reply */ entry_name_array = (entry_name_array_item_t *) Mem_Alloc_Label(estimated_num_entries * (FSAL_MAX_NAME_LEN + 1), "entry_name_array in nfs3_Readdirplus"); if(entry_name_array == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); return NFS_REQ_DROP; } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = (entryplus3 *) Mem_Alloc_Label(estimated_num_entries * sizeof(entryplus3), "READDIRPLUS3res_u.resok.reply.entries"); if(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } /* Allocation of the file handles */ fh3_array = (fh3_buffer_item_t *) Mem_Alloc_Label(estimated_num_entries * NFS3_FHSIZE, "Filehandle V3 in nfs3_Readdirplus"); if(fh3_array == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } delta = 0; /* manage . and .. */ if(begin_cookie == 0) { /* Fill in '.' */ if(estimated_num_entries > 0) { if((pfsal_handle = cache_inode_get_fsal_handle(dir_pentry, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry * may be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[0]. fileid)); RES_READDIRPLUS_REPLY.entries[0].name = entry_name_array[0]; strcpy(RES_READDIRPLUS_REPLY.entries[0].name, "."); RES_READDIRPLUS_REPLY.entries[0].cookie = 1; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[0]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[0].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[0].name_handle.handle_follows = FALSE; entry_attr = dir_pentry->attributes; /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[0].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=0 num_entries=%d space_used=%lu maxcount=%lu Name=. FileId=%016llx Cookie=%llu", num_entries, space_used, maxcount, RES_READDIRPLUS_REPLY.entries[0].fileid, RES_READDIRPLUS_REPLY.entries[0].cookie); delta += 1; } } /* Fill in '..' */ if(begin_cookie <= 1) { if(estimated_num_entries > delta) { if((pentry_dot_dot = cache_inode_lookupp_sw(dir_pentry, ht, pclient, pcontext, &cache_status_gethandle, !dir_pentry_unlock)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } if((pfsal_handle = cache_inode_get_fsal_handle(pentry_dot_dot, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[delta]. fileid)); RES_READDIRPLUS_REPLY.entries[delta].name = entry_name_array[delta]; strcpy(RES_READDIRPLUS_REPLY.entries[delta].name, ".."); /* Getting a file handle */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[delta]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[delta].cookie = 2; RES_READDIRPLUS_REPLY.entries[delta].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[delta].name_handle.handle_follows = FALSE; entry_attr = pentry_dot_dot->attributes; /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, pentry_dot_dot, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[delta].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%d num_entries=%d space_used=%lu maxcount=%lu Name=.. FileId=%016llx Cookie=%llu", delta, num_entries, space_used, maxcount, RES_READDIRPLUS_REPLY.entries[delta].fileid, RES_READDIRPLUS_REPLY.entries[delta].cookie); } RES_READDIRPLUS_REPLY.entries[0].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta]); if(num_entries > delta + 1) /* not 0 ??? */ RES_READDIRPLUS_REPLY.entries[delta].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta + 1]); else RES_READDIRPLUS_REPLY.entries[delta].nextentry = NULL; delta += 1; } /* if( begin_cookie == 0 ) */ for(i = delta; i < num_entries + delta; i++) { unsigned long needed; /* maxcount is the size with the FH and attributes overhead, * so entryplus3 is used instead of entry3. The data structures * in nfs23.h have funny padding depending on the arch (32 or 64). * We can't get an accurate estimate by simply using * sizeof(entryplus3). */ /* FIXME: There is still a 4 byte over estimate here on x86_64. */ /** @todo Remove cookie offset calculation in readdir and readdirplus (obsoleted) */ needed = sizeof(reference_entry) + NFS3_FHSIZE + ((strlen(dirent_array[i - delta]->name.name) + 3) & ~3); /* if delta == 1 or 2, then "." and ".." have already been added * to the readdirplus reply. */ if (i == delta) { needed += needed*delta /* size of a dir entry in reply */ - ((strlen(dirent_array[i - delta]->name.name) + 3) & ~3)*delta /* size of filename for current entry */ + 4*delta; /* size of "." and ".." filenames in reply */ } if((space_used += needed) > maxcount) { /* If delta != 0, then we already added "." or ".." to the reply. */ if(i == delta && delta == 0) { /* Not enough room to make even a single reply */ /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_TOOSMALL; return NFS_REQ_OK; } break; /* Make post traitement */ } /* * Get information specific to this entry */ if((pfsal_handle = cache_inode_get_fsal_handle(dirent_array[i - delta]->pentry, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } /* Now fill in the replyed entryplus3 list */ FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[i].fileid)); FSAL_name2str(&dirent_array[i - delta]->name, entry_name_array[i], FSAL_MAX_NAME_LEN); RES_READDIRPLUS_REPLY.entries[i].name = entry_name_array[i]; LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%u num_entries=%u delta=%u " "num_entries + delta - 1=%u end_cookie=%"PRIu64, i, num_entries, delta, num_entries + delta - 1, end_cookie); if(i != num_entries + delta - 1) RES_READDIRPLUS_REPLY.entries[i].cookie = dirent_array[i - delta]->cookie; else RES_READDIRPLUS_REPLY.entries[i].cookie = end_cookie; RES_READDIRPLUS_REPLY.entries[i].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[i].name_handle.handle_follows = FALSE; entry_attr = dirent_array[i - delta]->pentry->attributes; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[i]; /* Compute the NFSv3 file handle */ if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dirent_array[i - delta]->pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%d, num_entries=%d needed=%lu space_used=%lu maxcount=%lu Name=%s FileId=%016llx Cookie=%llu", i, num_entries, needed, space_used, maxcount, dirent_array[i - delta]->name.name, RES_READDIRPLUS_REPLY.entries[i].fileid, RES_READDIRPLUS_REPLY.entries[i].cookie); RES_READDIRPLUS_REPLY.entries[i].nextentry = NULL; if(i != 0) RES_READDIRPLUS_REPLY.entries[i - 1].nextentry = &(RES_READDIRPLUS_REPLY.entries[i]); } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; } nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); pres->res_readdirplus3.status = NFS3_OK; if((eod_met == END_OF_DIR) && (i == num_entries + delta)) { /* End of directory */ LogFullDebug(COMPONENT_NFS_READDIR, "============================================================> EOD MET !!!!!!"); pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; } else pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); LogFullDebug(COMPONENT_NFS_READDIR, "============================================================"); /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); /* Free the memory */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); return NFS_REQ_OK; } /* If we are here, there was an error */ /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); /* Free the memory */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); /* Is this a retryable error */ if(nfs_RetryableError(cache_status)) return NFS_REQ_DROP; /* Set failed status */ nfs_SetFailedStatus(pcontext, pexport, NFS_V3, cache_status, NULL, &pres->res_readdirplus3.status, dir_pentry, &(pres->res_readdirplus3.READDIRPLUS3res_u.resfail.dir_attributes), NULL, NULL, NULL, NULL, NULL, NULL); return NFS_REQ_OK; } /* nfs3_Readdirplus */
int nfs_Mkdir(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { char *str_dir_name = NULL; fsal_accessmode_t mode = 0; cache_entry_t *dir_pentry = NULL; cache_entry_t *parent_pentry = NULL; fsal_attrib_list_t parent_attr; fsal_attrib_list_t attr; fsal_attrib_list_t *ppre_attr; fsal_attrib_list_t attr_parent_after; cache_inode_file_type_t parent_filetype; fsal_handle_t *pfsal_handle; fsal_name_t dir_name; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; cache_inode_status_t cache_status_lookup; cache_inode_create_arg_t create_arg; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif memset(&create_arg, 0, sizeof(create_arg)); if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: str_dir_name = parg->arg_mkdir2.where.name; break; case NFS_V3: str_dir_name = parg->arg_mkdir3.where.name; break; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_mkdir2.where.dir), &(parg->arg_mkdir3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Mkdir handle: %s name: %s", str, str_dir_name); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; } if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_mkdir2.where.dir), &(parg->arg_mkdir3.where.dir), NULL, &(pres->res_dirop2.status), &(pres->res_mkdir3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: */ if(parent_filetype != DIRECTORY) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_NOTDIR; break; case NFS_V3: pres->res_mkdir3.status = NFS3ERR_NOTDIR; break; } rc = NFS_REQ_OK; goto out; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_mkdir3.status = NFS3ERR_DQUOT; break; } rc = NFS_REQ_OK ; goto out; } #endif /* _USE_QUOTA */ switch (preq->rq_vers) { case NFS_V2: str_dir_name = parg->arg_mkdir2.where.name; if(parg->arg_mkdir2.attributes.mode != (unsigned int)-1) { mode = (fsal_accessmode_t) parg->arg_mkdir2.attributes.mode; } else { mode = (fsal_accessmode_t) 0; } break; case NFS_V3: str_dir_name = parg->arg_mkdir3.where.name; if(parg->arg_mkdir3.attributes.mode.set_it == TRUE) mode = (fsal_accessmode_t) parg->arg_mkdir3.attributes.mode.set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; break; } //if(str_dir_name == NULL || strlen(str_dir_name) == 0) if(str_dir_name == NULL || *str_dir_name == '\0' ) { if(preq->rq_vers == NFS_V2) pres->res_dirop2.status = NFSERR_IO; if(preq->rq_vers == NFS_V3) pres->res_mkdir3.status = NFS3ERR_INVAL; } else { /* Make the directory */ if((cache_status = cache_inode_error_convert(FSAL_str2name(str_dir_name, 0, &dir_name))) == CACHE_INODE_SUCCESS) { /* * Lookup file to see if it exists. If so, use it. Otherwise * create a new one. */ dir_pentry = cache_inode_lookup(parent_pentry, &dir_name, &attr, pcontext, &cache_status_lookup); if(cache_status_lookup == CACHE_INODE_NOT_FOUND) { /* The create_arg structure contains the information "newly created directory" * to be passed to cache_inode_new_entry from cache_inode_create */ create_arg.newly_created_dir = TRUE; /* Create the directory */ if((dir_pentry = cache_inode_create(parent_pentry, &dir_name, DIRECTORY, mode, &create_arg, &attr, pcontext, &cache_status)) != NULL) { /* * Get the FSAL handle for this entry */ pfsal_handle = &dir_pentry->handle; if(preq->rq_vers == NFS_V2) { DIROP2resok *d2ok = &pres->res_dirop2.DIROP2res_u.diropok; /* Build file handle */ if(!nfs2_FSALToFhandle(&d2ok->file, pfsal_handle, pexport)) pres->res_dirop2.status = NFSERR_IO; else { /* * Build entry * attributes */ if(nfs2_FSALattr_To_Fattr(pexport, &attr, &d2ok->attributes) == 0) pres->res_dirop2.status = NFSERR_IO; else pres->res_dirop2.status = NFS_OK; } } else { MKDIR3resok *d3ok = &pres->res_mkdir3.MKDIR3res_u.resok; /* Build file handle */ pres->res_mkdir3.status = nfs3_AllocateFH(&d3ok->obj.post_op_fh3_u.handle); if(pres->res_mkdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } if(nfs3_FSALToFhandle(&d3ok->obj.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { gsh_free(d3ok->obj.post_op_fh3_u.handle.data.data_val); pres->res_mkdir3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ d3ok->obj.handle_follows = TRUE; /* * Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr, &d3ok->obj_attributes); /* Get the attributes of the parent after the operation */ if(cache_inode_getattr(parent_pentry, &attr_parent_after, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { pres->res_mkdir3.status = nfs3_Errno(cache_status); rc = NFS_REQ_OK; goto out; } /* * Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &d3ok->dir_wcc); pres->res_mkdir3.status = NFS3_OK; } rc = NFS_REQ_OK; goto out; } } /* If( cache_status_lookup == CACHE_INODE_NOT_FOUND ) */ else { switch (preq->rq_vers) { case NFS_V2: if(cache_status_lookup == CACHE_INODE_SUCCESS) pres->res_dirop2.status = NFSERR_EXIST; else pres->res_dirop2.status = NFSERR_IO; break; case NFS_V3: if(cache_status_lookup == CACHE_INODE_SUCCESS) pres->res_mkdir3.status = NFS3ERR_EXIST; else pres->res_mkdir3.status = NFS3ERR_INVAL; nfs_SetWccData(pexport, ppre_attr, NULL, &(pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc)); break; } rc = NFS_REQ_OK; goto out; } } } /* If we are here, there was an error */ rc = nfs_SetFailedStatus(pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_mkdir3.status, NULL, ppre_attr, &(pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc), NULL, NULL); out: /* return references */ if (dir_pentry) cache_inode_put(dir_pentry); if (parent_pentry) cache_inode_put(parent_pentry); return (rc); }
int nfs3_rmdir(nfs_arg_t *arg, nfs_worker_data_t *worker, struct svc_req *req, nfs_res_t *res) { cache_entry_t *parent_entry = NULL; cache_entry_t *child_entry = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; cache_inode_status_t cache_status; const char *name = arg->arg_rmdir3.object.name; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &arg->arg_rmdir3.object.dir, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_rmdir handle: %s " "name: %s", str, name); } /* Convert file handle into a pentry */ /* to avoid setting it on each error case */ res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; parent_entry = nfs3_FhandleToCache(&arg->arg_rmdir3.object.dir, &res->res_rmdir3.status, &rc); if (parent_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_entry, &pre_parent); /* Sanity checks: directory name must be non-null; parent * must be a directory. */ if (parent_entry->type != DIRECTORY) { res->res_rmdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if ((name == NULL) || (*name == '\0')) { cache_status = CACHE_INODE_INVALID_ARGUMENT; goto out_fail; } /* Lookup to the entry to be removed to check that it is a * directory */ cache_status = cache_inode_lookup(parent_entry, name, &child_entry); if (child_entry != NULL) { /* Sanity check: make sure we are about to remove a * directory */ if (child_entry->type != DIRECTORY) { res->res_rmdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } } cache_status = cache_inode_remove(parent_entry, name); if (cache_status != CACHE_INODE_SUCCESS) goto out_fail; nfs_SetWccData(&pre_parent, parent_entry, &res->res_rmdir3.RMDIR3res_u.resok.dir_wcc); res->res_rmdir3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_rmdir3.status = nfs3_Errno(cache_status); nfs_SetWccData(&pre_parent, parent_entry, &res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc); /* If we are here, there was an error */ if (nfs_RetryableError(cache_status)) rc = NFS_REQ_DROP; out: /* return references */ if (child_entry) cache_inode_put(child_entry); if (parent_entry) cache_inode_put(parent_entry); return rc; } /* nfs3_rmdir */ /** * @brief Free the result structure allocated for nfs3_rmdir * * This function frees the result structure allocated for nfs3_rmdir. * * @param[in,out] res Result structure * */ void nfs3_rmdir_free(nfs_res_t *res) { return; }
int nfs_Read(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { cache_entry_t *pentry; fsal_attrib_list_t attr; fsal_attrib_list_t pre_attr; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; size_t size = 0; size_t read_size = 0; fsal_off_t offset = 0; void *data = NULL; cache_inode_file_type_t filetype; fsal_boolean_t eof_met=FALSE; int rc = NFS_REQ_OK; if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: offset = parg->arg_read2.offset; size = parg->arg_read2.count; break; case NFS_V3: offset = parg->arg_read3.offset; size = parg->arg_read3.count; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_read2.file), &(parg->arg_read3.file), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Read handle: %s start: %llx len: %llx", str, (unsigned long long) offset, (unsigned long long) size); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_read3.READ3res_u.resfail.file_attributes.attributes_follow = FALSE; /* initialize for read of size 0 */ pres->res_read3.READ3res_u.resok.eof = FALSE; pres->res_read3.READ3res_u.resok.count = 0; pres->res_read3.READ3res_u.resok.data.data_val = NULL; pres->res_read3.READ3res_u.resok.data.data_len = 0; pres->res_read3.status = NFS3_OK; } else if(preq->rq_vers == NFS_V2) { /* initialize for read of size 0 */ pres->res_read2.READ2res_u.readok.data.nfsdata2_val = NULL; pres->res_read2.READ2res_u.readok.data.nfsdata2_len = 0; pres->res_attr2.status = NFS_OK; } /* Convert file handle into a cache entry */ if((pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_read2.file), &(parg->arg_read3.file), NULL, &(pres->res_read2.status), &(pres->res_read3.status), NULL, &pre_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out; } if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_read3.file)))) { rc = nfs3_Read_Xattr(parg, pexport, pcontext, preq, pres); goto out; } if(cache_inode_access(pentry, FSAL_READ_ACCESS, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = nfs2_Errno(cache_status); break; case NFS_V3: pres->res_read3.status = nfs3_Errno(cache_status); break; } rc = NFS_REQ_OK; goto out; } /* Extract the filetype */ filetype = cache_inode_fsal_type_convert(pre_attr.type); /* Sanity check: read only from a regular file */ if(filetype != REGULAR_FILE) { switch (preq->rq_vers) { case NFS_V2: /* * In the RFC tell it not good but it does * not tell what to do ... */ pres->res_attr2.status = NFSERR_ISDIR; break; case NFS_V3: if(filetype == DIRECTORY) pres->res_read3.status = NFS3ERR_ISDIR; else pres->res_read3.status = NFS3ERR_INVAL; break; } rc = NFS_REQ_OK; goto out; } /* For MDONLY export, reject write operation */ /* Request of type MDONLY_RO were rejected at the nfs_rpc_dispatcher level */ /* This is done by replying EDQUOT (this error is known for not disturbing the client's requests cache */ if(pexport->access_type == ACCESSTYPE_MDONLY || pexport->access_type == ACCESSTYPE_MDONLY_RO) { switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_read3.status = NFS3ERR_DQUOT; break; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; goto out; } /* Extract the argument from the request */ switch (preq->rq_vers) { case NFS_V2: offset = parg->arg_read2.offset; /* beginoffset is obsolete */ size = parg->arg_read2.count; /* totalcount is obsolete */ break; case NFS_V3: offset = parg->arg_read3.offset; size = parg->arg_read3.count; break; } /* * do not exceed maxium READ offset if set */ if((pexport->options & EXPORT_OPTION_MAXOFFSETREAD) == EXPORT_OPTION_MAXOFFSETREAD) { LogFullDebug(COMPONENT_NFSPROTO, "-----> Read offset=%llu count=%llu MaxOffSet=%llu", (unsigned long long) offset, (unsigned long long) size, (unsigned long long) pexport->MaxOffsetRead); if((fsal_off_t) (offset + size) > pexport->MaxOffsetRead) { LogEvent(COMPONENT_NFSPROTO, "NFS READ: A client tryed to violate max file size %llu for exportid #%hu", (unsigned long long) pexport->MaxOffsetRead, pexport->id); switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_read3.status = NFS3ERR_INVAL; break; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; goto out; } } /* * We should not exceed the FSINFO rtmax field for * the size */ if(((pexport->options & EXPORT_OPTION_MAXREAD) == EXPORT_OPTION_MAXREAD) && size > pexport->MaxRead) { /* * The client asked for too much, normally * this should not happen because the client * is calling nfs_Fsinfo at mount time and so * is aware of the server maximum write size */ size = pexport->MaxRead; } if(size == 0) { nfs_read_ok(pexport, preq, pres, NULL, 0, &pre_attr, 0); rc = NFS_REQ_OK; goto out; } else { data = gsh_malloc(size); if(data == NULL) { rc = NFS_REQ_DROP; goto out; } if((cache_inode_rdwr(pentry, CACHE_INODE_READ, offset, size, &read_size, data, &eof_met, pcontext, CACHE_INODE_SAFE_WRITE_TO_FS, &cache_status) == CACHE_INODE_SUCCESS) && (cache_inode_getattr(pentry, &attr, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { nfs_read_ok(pexport, preq, pres, data, read_size, &attr, ((offset + read_size) >= attr.filesize)); rc = NFS_REQ_OK; goto out; } gsh_free(data); } /* If we are here, there was an error */ if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; out: /* return references */ if (pentry) cache_inode_put(pentry); return (rc); } /* nfs_Read */
int nfs3_fsstat(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_dynamicfsinfo_t dynamicinfo; cache_inode_status_t cache_status; cache_entry_t *entry = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &(arg->arg_fsstat3.fsroot), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_fsstat handle: %s", str); } /* to avoid setting it on each error case */ res->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow = FALSE; entry = nfs3_FhandleToCache(&arg->arg_fsstat3.fsroot, &res->res_fsstat3.status, &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Get statistics and convert from cache */ cache_status = cache_inode_statfs(entry, &dynamicinfo); if (cache_status == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_bytes=%" PRIu64 " dynamicinfo.free_bytes=%" PRIu64 " dynamicinfo.avail_bytes=%" PRIu64, dynamicinfo.total_bytes, dynamicinfo.free_bytes, dynamicinfo.avail_bytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_files=%" PRIu64 " dynamicinfo.free_files=%" PRIu64 " dynamicinfo.avail_files=%" PRIu64, dynamicinfo.total_files, dynamicinfo.free_files, dynamicinfo.avail_files); nfs_SetPostOpAttr(entry, &(res->res_fsstat3.FSSTAT3res_u.resok. obj_attributes)); res->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes; res->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes; res->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes; res->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files; res->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files; res->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files; /* volatile FS */ res->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0; res->res_fsstat3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tbytes, res->res_fsstat3.FSSTAT3res_u.resok.fbytes, res->res_fsstat3.FSSTAT3res_u.resok.abytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tfiles, res->res_fsstat3.FSSTAT3res_u.resok.ffiles, res->res_fsstat3.FSSTAT3res_u.resok.afiles); rc = NFS_REQ_OK; goto out; } /* At this point we met an error */ if (nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } res->res_fsstat3.status = nfs3_Errno(cache_status); rc = NFS_REQ_OK; out: /* return references */ if (entry) cache_inode_put(entry); return rc; } /* nfs3_fsstat */
int nfs3_readlink(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { cache_entry_t *entry = NULL; cache_inode_status_t cache_status; struct gsh_buffdesc link_buffer = { .addr = NULL, .len = 0 }; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &(arg->arg_readlink3.symlink), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Readlink handle: %s", str); } /* to avoid setting it on each error case */ res->res_readlink3.READLINK3res_u.resfail.symlink_attributes. attributes_follow = false; entry = nfs3_FhandleToCache(&arg->arg_readlink3.symlink, &res->res_readlink3.status, &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Sanity Check: the entry must be a link */ if (entry->type != SYMBOLIC_LINK) { res->res_readlink3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } cache_status = cache_inode_readlink(entry, &link_buffer); if (cache_status != CACHE_INODE_SUCCESS) { res->res_readlink3.status = nfs3_Errno(cache_status); nfs_SetPostOpAttr(entry, &res->res_readlink3.READLINK3res_u.resfail. symlink_attributes); if (nfs_RetryableError(cache_status)) rc = NFS_REQ_DROP; goto out; } /* Reply to the client */ res->res_readlink3.READLINK3res_u.resok.data = link_buffer.addr; nfs_SetPostOpAttr(entry, &res->res_readlink3.READLINK3res_u. resok.symlink_attributes); res->res_readlink3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (entry) cache_inode_put(entry); return rc; } /* nfs3_readlink */ /** * @brief Free the result structure allocated for nfs3_readlink. * * This function frees the result structure allocated for * nfs3_readlink. * * @param[in,out] res Result structure * */ void nfs3_readlink_free(nfs_res_t *res) { if (res->res_readlink3.status == NFS3_OK) gsh_free(res->res_readlink3.READLINK3res_u.resok.data); }
int nfs3_rename(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { const char *entry_name = arg->arg_rename3.from.name; const char *new_entry_name = arg->arg_rename3.to.name; cache_entry_t *parent_entry = NULL; cache_entry_t *new_parent_entry = NULL; cache_inode_status_t cache_status; short to_exportid = 0; short from_exportid = 0; int rc = NFS_REQ_OK; pre_op_attr pre_parent = { .attributes_follow = false }; pre_op_attr pre_new_parent = { .attributes_follow = false }; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &arg->arg_rename3.from.dir, NULL, strfrom); nfs_FhandleToStr(req->rq_vers, &arg->arg_rename3.to.dir, NULL, strto); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Rename from handle: %s name %s to handle: %s name: %s", strfrom, entry_name, strto, new_entry_name); } /* to avoid setting it on each error case */ res->res_rename3.RENAME3res_u.resfail.fromdir_wcc.before. attributes_follow = FALSE; res->res_rename3.RENAME3res_u.resfail.fromdir_wcc.after. attributes_follow = FALSE; res->res_rename3.RENAME3res_u.resfail.todir_wcc.before. attributes_follow = FALSE; res->res_rename3.RENAME3res_u.resfail.todir_wcc.after. attributes_follow = FALSE; /* Get the exportids for the two handles. */ to_exportid = nfs3_FhandleToExportId(&(arg->arg_rename3.to.dir)); from_exportid = nfs3_FhandleToExportId(&(arg->arg_rename3.from.dir)); /* Validate the to_exportid */ if (to_exportid < 0 || from_exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS%d RENAME Request from client %s has badly formed handle for to dir", req->rq_vers, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); /* Bad handle, report to client */ res->res_rename3.status = NFS3ERR_BADHANDLE; goto out; } /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) { res->res_rename3.status = NFS3ERR_XDEV; goto out; } /* Convert fromdir file handle into a cache_entry */ parent_entry = nfs3_FhandleToCache(&arg->arg_rename3.from.dir, &res->res_create3.status, &rc); if (parent_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_entry, &pre_parent); /* Convert todir file handle into a cache_entry */ new_parent_entry = nfs3_FhandleToCache(&arg->arg_rename3.to.dir, &res->res_create3.status, &rc); if (new_parent_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(new_parent_entry, &pre_new_parent); if (entry_name == NULL || *entry_name == '\0' || new_entry_name == NULL || *new_entry_name == '\0') { cache_status = CACHE_INODE_INVALID_ARGUMENT; goto out_fail; } cache_status = cache_inode_rename(parent_entry, entry_name, new_parent_entry, new_entry_name); if (cache_status != CACHE_INODE_SUCCESS) goto out_fail; res->res_rename3.status = NFS3_OK; nfs_SetWccData(&pre_parent, parent_entry, &res->res_rename3.RENAME3res_u.resok.fromdir_wcc); nfs_SetWccData(&pre_new_parent, new_parent_entry, &res->res_rename3.RENAME3res_u.resok.todir_wcc); rc = NFS_REQ_OK; goto out; out_fail: res->res_rename3.status = nfs3_Errno(cache_status); nfs_SetWccData(&pre_parent, parent_entry, &res->res_rename3.RENAME3res_u.resfail.fromdir_wcc); nfs_SetWccData(&pre_new_parent, new_parent_entry, &res->res_rename3.RENAME3res_u.resfail.todir_wcc); /* If we are here, there was an error */ if (nfs_RetryableError(cache_status)) rc = NFS_REQ_DROP; out: if (parent_entry) cache_inode_put(parent_entry); if (new_parent_entry) cache_inode_put(new_parent_entry); return rc; } /** * @brief Free the result structure allocated for nfs3_rename. * * This function frees the result structure allocated for nfs3_rename. * * @param[in,out] res Result structure * */ void nfs3_rename_free(nfs_res_t *res) { /* Nothing to do here */ }
int nfs3_Readdirplus(nfs_arg_t * parg, exportlist_t * pexport, fsal_op_context_t * pcontext, cache_inode_client_t * pclient, hash_table_t * ht, struct svc_req *preq, nfs_res_t * pres) { static char __attribute__ ((__unused__)) funcName[] = "nfs3_Readdirplus"; typedef char entry_name_array_item_t[FSAL_MAX_NAME_LEN]; typedef char fh3_buffer_item_t[NFS3_FHSIZE]; unsigned int delta = 0; cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry_dot_dot = NULL; unsigned long dircount; unsigned long maxcount; fsal_attrib_list_t dir_attr; fsal_attrib_list_t entry_attr; unsigned int begin_cookie; unsigned int end_cookie; unsigned int cache_inode_cookie; cache_inode_dir_entry_t *dirent_array = NULL; unsigned int *cookie_array = NULL; cookieverf3 cookie_verifier; int rc; unsigned int i = 0; unsigned int num_entries; unsigned long space_used; unsigned long estimated_num_entries; unsigned long asked_num_entries; cache_inode_file_type_t dir_filetype; cache_inode_endofdir_t eod_met = UNASSIGNED_EOD; cache_inode_status_t cache_status; cache_inode_status_t cache_status_gethandle; fsal_handle_t *pfsal_handle = NULL; entry_name_array_item_t *entry_name_array = NULL; fh3_buffer_item_t *fh3_array = NULL; /* to avoid setting it on each error case */ pres->res_readdir3.READDIR3res_u.resfail.dir_attributes.attributes_follow = FALSE; dircount = parg->arg_readdirplus3.dircount; maxcount = parg->arg_readdirplus3.maxcount; begin_cookie = (unsigned int)parg->arg_readdirplus3.cookie; space_used = sizeof(READDIRPLUS3resok); estimated_num_entries = dircount / sizeof(entryplus3); LogFullDebug(COMPONENT_NFS_READDIR, "---> nfs3_Readdirplus: dircount=%d maxcount=%d begin_cookie=%d space_used=%d estimated_num_entries=%d\n", dircount, maxcount, begin_cookie, space_used, estimated_num_entries); /* Is this a xattr FH ? */ if(nfs3_Is_Fh_Xattr(&(parg->arg_readdirplus3.dir))) return nfs3_Readdirplus_Xattr(parg, pexport, pcontext, pclient, ht, preq, pres); /* Convert file handle into a vnode */ /* BUGAZOMEU : rajouter acces direct au DIR_CONTINUE */ if((dir_pentry = nfs_FhandleToCache(preq->rq_vers, NULL, &(parg->arg_readdirplus3.dir), NULL, NULL, &(pres->res_readdirplus3.status), NULL, &dir_attr, pcontext, pclient, ht, &rc)) == NULL) { /* return NFS_REQ_DROP ; */ return rc; } /* Extract the filetype */ dir_filetype = cache_inode_fsal_type_convert(dir_attr.type); /* Sanity checks -- must be a directory */ if((dir_filetype != DIR_BEGINNING) && (dir_filetype != DIR_CONTINUE)) { pres->res_readdirplus3.status = NFS3ERR_NOTDIR; return NFS_REQ_OK; } /* switch */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the directory. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ if(pexport->UseCookieVerifier) memcpy(cookie_verifier, &(dir_attr.mtime), sizeof(dir_attr.mtime)); /* * nothing to do if != 0 because the area is already full of * zero */ if(pexport->UseCookieVerifier && (begin_cookie != 0)) { /* * Not the first call, so we have to check the cookie * verifier */ if(memcmp(cookie_verifier, parg->arg_readdirplus3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { pres->res_readdirplus3.status = NFS3ERR_BAD_COOKIE; return NFS_REQ_OK; } } #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("cache_inode_dir_entry_t in nfs3_Readdirplus"); #endif if((dirent_array = (cache_inode_dir_entry_t *) Mem_Alloc(estimated_num_entries * sizeof(cache_inode_dir_entry_t))) == NULL) { pres->res_readdirplus3.status = NFS3ERR_IO; return NFS_REQ_DROP; } #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("cookie array in nfs3_Readdirplus"); #endif if((cookie_array = (unsigned int *)Mem_Alloc(estimated_num_entries * sizeof(unsigned int))) == NULL) { Mem_Free((char *)dirent_array); pres->res_readdirplus3.status = NFS3ERR_IO; return NFS_REQ_DROP; } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; /* How many entries will we retry from cache_inode ? */ if(begin_cookie > 1) { asked_num_entries = estimated_num_entries; cache_inode_cookie = begin_cookie - 2; } else { asked_num_entries = ((estimated_num_entries > 2) ? estimated_num_entries - 2 : 0); /* Keep space for '.' and '..' */ cache_inode_cookie = 0; } /* A definition that will be very useful to avoid very long names for variables */ #define RES_READDIRPLUS_REPLY pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply /* Call readdir */ if(cache_inode_readdir(dir_pentry, cache_inode_cookie, asked_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, cookie_array, ht, pclient, pcontext, &cache_status) == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "-- Readdirplus3 -> Call to cache_inode_readdir( cookie=%d, asked=%d ) -> num_entries = %d\n", cache_inode_cookie, asked_num_entries, num_entries); if(eod_met == END_OF_DIR) { LogFullDebug(COMPONENT_NFS_READDIR, "+++++++++++++++++++++++++++++++++++++++++> EOD MET \n"); } /* If nothing was found, return nothing, but if cookie=0, we should return . and .. */ if((num_entries == 0) && (asked_num_entries != 0) && (begin_cookie > 1)) { pres->res_readdirplus3.status = NFS3_OK; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, NULL, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); } else { #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("entry_name_array in nfs3_Readdirplus"); #endif /* Allocation of the structure for reply */ entry_name_array = (entry_name_array_item_t *) Mem_Alloc(estimated_num_entries * (FSAL_MAX_NAME_LEN + 1)); if(entry_name_array == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); return NFS_REQ_DROP; } #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("READDIRPLUS3res_u.resok.reply.entries"); #endif pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = (entryplus3 *) Mem_Alloc(estimated_num_entries * sizeof(entryplus3)); if(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } /* Allocation of the file handles */ #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("Filehandle V3 in nfs3_Readdirplus"); #endif fh3_array = (fh3_buffer_item_t *) Mem_Alloc(estimated_num_entries * NFS3_FHSIZE); #ifdef _DEBUG_MEMLEAKS /* For debugging memory leaks */ BuddySetDebugLabel("N/A"); #endif if(fh3_array == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } delta = 0; /* manage . and .. */ if(begin_cookie == 0) { /* Fill in '.' */ if(estimated_num_entries > 0) { if((pfsal_handle = cache_inode_get_fsal_handle(dir_pentry, &cache_status_gethandle)) == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[0]. fileid)); RES_READDIRPLUS_REPLY.entries[0].name = entry_name_array[0]; strcpy(RES_READDIRPLUS_REPLY.entries[0].name, "."); RES_READDIRPLUS_REPLY.entries[0].cookie = 1; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[0]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[0].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[0].name_handle.handle_follows = FALSE; cache_inode_get_attributes(dir_pentry, &entry_attr); /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[0].name_attributes)); delta += 1; } } /* Fill in '..' */ if(begin_cookie <= 1) { if(estimated_num_entries > delta) { if((pentry_dot_dot = cache_inode_lookupp(dir_pentry, ht, pclient, pcontext, &cache_status_gethandle)) == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } if((pfsal_handle = cache_inode_get_fsal_handle(pentry_dot_dot, &cache_status_gethandle)) == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[delta]. fileid)); RES_READDIRPLUS_REPLY.entries[delta].name = entry_name_array[delta]; strcpy(RES_READDIRPLUS_REPLY.entries[delta].name, ".."); /* Getting a file handle */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[delta]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[delta].cookie = 2; RES_READDIRPLUS_REPLY.entries[delta].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[delta].name_handle.handle_follows = FALSE; cache_inode_get_attributes(pentry_dot_dot, &entry_attr); /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, pentry_dot_dot, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[delta].name_attributes)); } RES_READDIRPLUS_REPLY.entries[0].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta]); if(num_entries > delta + 1) /* not 0 ??? */ RES_READDIRPLUS_REPLY.entries[delta].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta + 1]); else RES_READDIRPLUS_REPLY.entries[delta].nextentry = NULL; delta += 1; } /* if( begin_cookie == 0 ) */ for(i = delta; i < num_entries + delta; i++) { unsigned long needed; /* dircount is the size without the FH and attributes overhead, so entry3 is used intead of entryplus3 */ needed = sizeof(entry3) + ((strlen(dirent_array[i - delta].name.name) + 3) & ~3); /* LogFullDebug(COMPONENT_NFS_READDIR, "==============> i=%d sizeof(entryplus3)=%d needed=%d space_used=%d maxcount=%d num_entries=%d asked_num_entries=%d\n", i, sizeof( entryplus3 ), needed, space_used, maxcount, num_entries, asked_num_entries ) ; */ if((space_used += needed) > maxcount) { if(i == delta) { /* * Not enough room to make even a single reply */ Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_TOOSMALL; return NFS_REQ_OK; } break; /* Make post traitement */ } /* * Get information specific to this entry */ if((pfsal_handle = cache_inode_get_fsal_handle(dirent_array[i - delta].pentry, &cache_status_gethandle)) == NULL) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } /* Now fill in the replyed entryplus3 list */ FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[i].fileid)); FSAL_name2str(&dirent_array[i - delta].name, entry_name_array[i], FSAL_MAX_NAME_LEN); RES_READDIRPLUS_REPLY.entries[i].name = entry_name_array[i]; if(i != num_entries + delta - 1) RES_READDIRPLUS_REPLY.entries[i].cookie = cookie_array[i + 1 - delta] + 2; else RES_READDIRPLUS_REPLY.entries[i].cookie = end_cookie + 2; RES_READDIRPLUS_REPLY.entries[i].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[i].name_handle.handle_follows = FALSE; cache_inode_get_attributes(dirent_array[i - delta].pentry, &entry_attr); pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle. post_op_fh3_u.handle.data.data_val = (char *)fh3_array[i]; /* Compute the NFSv3 file handle */ if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } LogFullDebug(COMPONENT_NFS_READDIR, "-- Readdirplus3 -> i=%d num_entries=%d needed=%d space_used=%lu maxcount=%lu Name=%s FileId=%llu Cookie=%llu\n", i, num_entries, needed, space_used, maxcount, dirent_array[i - delta].name.name, RES_READDIRPLUS_REPLY.entries[i].fileid, RES_READDIRPLUS_REPLY.entries[i].cookie); /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle. handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle. post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dirent_array[i - delta].pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply. entries[i].name_attributes)); RES_READDIRPLUS_REPLY.entries[i].nextentry = NULL; if(i != 0) RES_READDIRPLUS_REPLY.entries[i - 1].nextentry = &(RES_READDIRPLUS_REPLY.entries[i]); } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; } nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); pres->res_readdirplus3.status = NFS3_OK; if((eod_met == END_OF_DIR) && (i == num_entries + delta)) { /* End of directory */ LogFullDebug(COMPONENT_NFS_READDIR, "============================================================> EOD MET !!!!!!\n"); pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; } else pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); LogFullDebug(COMPONENT_NFS_READDIR,"============================================================\n"); /* Free the memory */ Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); return NFS_REQ_OK; } /* If we are here, there was an error */ /* Free the memory */ Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); /* Is this a retryable error */ if(nfs_RetryableError(cache_status)) return NFS_REQ_DROP; /* Set failed status */ nfs_SetFailedStatus(pcontext, pexport, NFS_V3, cache_status, NULL, &pres->res_readdirplus3.status, dir_pentry, &(pres->res_readdirplus3.READDIRPLUS3res_u.resfail.dir_attributes), NULL, NULL, NULL, NULL, NULL, NULL); return NFS_REQ_OK; } /* nfs3_Readdirplus */