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 *arg, nfs_worker_data_t *worker, struct svc_req *req, nfs_res_t *res) { cache_entry_t *entry = NULL; int rc = NFS_REQ_OK; struct fsal_export *exp_hdl = op_ctx->fsal_export; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_pathconf3.object)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_pathconf handle: " "%s", str); } /* to avoid setting it on each error case */ res->res_pathconf3.PATHCONF3res_u.resfail.obj_attributes. attributes_follow = FALSE; /* Convert file handle into a fsal_handle */ entry = nfs3_FhandleToCache(&arg->arg_pathconf3.object, &res->res_pathconf3.status, &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } res->res_pathconf3.PATHCONF3res_u.resok.linkmax = exp_hdl->ops->fs_maxlink(exp_hdl); res->res_pathconf3.PATHCONF3res_u.resok.name_max = exp_hdl->ops->fs_maxnamelen(exp_hdl); res->res_pathconf3.PATHCONF3res_u.resok.no_trunc = exp_hdl->ops->fs_supports(exp_hdl, fso_no_trunc); res->res_pathconf3.PATHCONF3res_u.resok.chown_restricted = exp_hdl->ops->fs_supports(exp_hdl, fso_chown_restricted); res->res_pathconf3.PATHCONF3res_u.resok.case_insensitive = exp_hdl->ops->fs_supports(exp_hdl, fso_case_insensitive); res->res_pathconf3.PATHCONF3res_u.resok.case_preserving = exp_hdl->ops->fs_supports(exp_hdl, fso_case_preserving); /* Build post op file attributes */ nfs_SetPostOpAttr(entry, &(res->res_pathconf3.PATHCONF3res_u.resok. obj_attributes)); out: if (entry) cache_inode_put(entry); return rc; } /* 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_remove(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *parent_obj = NULL; struct fsal_obj_handle *child_obj = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; fsal_status_t fsal_status; const char *name = arg->arg_remove3.object.name; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_create3.where.dir, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Remove handle: %s name: %s", str, name); } /* Convert file handle into a pentry */ /* to avoid setting it on each error case */ res->res_remove3.REMOVE3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; res->res_remove3.REMOVE3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; parent_obj = nfs3_FhandleToCache(&arg->arg_remove3.object.dir, &res->res_remove3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Sanity checks: file name must be non-null; parent must be a * directory. */ if (parent_obj->type != DIRECTORY) { res->res_remove3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if (name == NULL || *name == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } /* Lookup the child entry to verify that it is not a directory */ fsal_status = fsal_lookup(parent_obj, name, &child_obj, NULL); if (!FSAL_IS_ERROR(fsal_status)) { /* Sanity check: make sure we are not removing a * directory */ if (child_obj->type == DIRECTORY) { res->res_remove3.status = NFS3ERR_ISDIR; rc = NFS_REQ_OK; goto out; } } LogFullDebug(COMPONENT_NFSPROTO, "Trying to remove file %s", name); /* Remove the entry. */ fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; /* Build Weak Cache Coherency data */ nfs_SetWccData(&pre_parent, parent_obj, &res->res_remove3.REMOVE3res_u.resok.dir_wcc); res->res_remove3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_remove3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &res->res_remove3.REMOVE3res_u.resfail.dir_wcc); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* return references */ if (child_obj) child_obj->obj_ops.put_ref(child_obj); if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_remove */ /** * @brief Free the result structure allocated for nfs3_remove. * * This function frees the result structure allocated for nfs3_remove. * * @param[in,out] res Result structure * */ void nfs3_remove_free(nfs_res_t *res) { /* Nothing to do here */ }
/** * * @brief The NFSPROC3_LINK * * The NFSPROC3_LINK. * * @param[in] arg NFS argument union * @param[in] req SVC request related to this call * @param[out] res Structure to contain the result of the call * * @retval NFS_REQ_OK if successful * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_link(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct LINK3args *l3_arg = &arg->arg_link3; struct LINK3res *l3_res = &res->res_link3; const char *link_name = l3_arg->link.name; struct fsal_obj_handle *target_obj = NULL; struct fsal_obj_handle *parent_obj = NULL; pre_op_attr pre_parent = {0}; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->file, NULL, strfrom); nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->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 */ l3_res->LINK3res_u.resfail.file_attributes.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE; l3_res->status = nfs3_verify_exportid(l3_arg, req); if (l3_res->status != NFS3_OK) return rc; parent_obj = nfs3_FhandleToCache(&l3_arg->link.dir, &l3_res->status, &rc); if (parent_obj == NULL) return rc; /* Status and rc are set by nfs3_FhandleToCache */ nfs_SetPreOpAttr(parent_obj, &pre_parent); target_obj = nfs3_FhandleToCache(&l3_arg->file, &l3_res->status, &rc); if (target_obj == NULL) { parent_obj->obj_ops.put_ref(parent_obj); return rc; /* Status and rc are set by nfs3_FhandleToCache */ } if (parent_obj->type != DIRECTORY) { l3_res->status = NFS3ERR_NOTDIR; goto out; } if (link_name == NULL || *link_name == '\0') { l3_res->status = NFS3ERR_INVAL; goto out; } fsal_status = fsal_link(target_obj, parent_obj, link_name); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed link: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } l3_res->status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resfail.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resfail.linkdir_wcc); } else { nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resok.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resok.linkdir_wcc); l3_res->status = NFS3_OK; } out: /* return references */ target_obj->obj_ops.put_ref(target_obj); parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_link */
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_readdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *parent_dir_obj = NULL; unsigned long count = 0; uint64_t cookie = 0; uint64_t fsal_cookie = 0; cookieverf3 cookie_verifier; unsigned int num_entries = 0; unsigned long estimated_num_entries = 0; object_file_type_t dir_filetype = 0; bool eod_met = false; fsal_status_t fsal_status = {0, 0}; fsal_status_t fsal_status_gethandle = {0, 0}; int rc = NFS_REQ_OK; struct nfs3_readdir_cb_data tracker = { NULL }; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; log_components_t component; nfs_FhandleToStr(req->rq_vers, &(arg->arg_readdir3.dir), NULL, str); if (isDebug(COMPONENT_NFSPROTO)) component = COMPONENT_NFSPROTO; else component = COMPONENT_NFS_READDIR; LogDebug(component, "REQUEST PROCESSING: Calling nfs_Readdir handle: %s", str); } READDIR3resok * const RES_READDIR3_OK = &res->res_readdir3.READDIR3res_u.resok; /* to avoid setting it on each error case */ res->res_readdir3.READDIR3res_u.resfail.dir_attributes. attributes_follow = FALSE; /* Look up object for filehandle */ dir_obj = nfs3_FhandleToCache(&(arg->arg_readdir3.dir), &(res->res_readdir3.status), &rc); if (dir_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Extract the filetype */ dir_filetype = dir_obj->type; /* Sanity checks -- must be a directory */ if (dir_filetype != DIRECTORY) { res->res_readdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* Parse out request arguments and decide how many entries we * want. For NFSv3, deal with the cookie verifier. */ count = arg->arg_readdir3.count; cookie = arg->arg_readdir3.cookie; estimated_num_entries = MIN(count / (sizeof(entry3) - sizeof(char *)), 120); LogFullDebug(COMPONENT_NFS_READDIR, "---> nfs3_readdir: count=%lu cookie=%" PRIu64 " estimated_num_entries=%lu", count, cookie, estimated_num_entries); if (estimated_num_entries == 0) { res->res_readdir3.status = NFS3ERR_TOOSMALL; rc = NFS_REQ_OK; goto out; } /* To make or check the cookie verifier */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* If cookie verifier is used, then a * non-trivial value is returned to the * client. * * This value is the ctime of the directory. If verifier is * unused (as in many NFS Servers) then only a set of zeros * is returned (trivial value). */ if (use_cookie_verifier) { struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_CTIME); fsal_status = dir_obj->obj_ops.getattrs(dir_obj, &attrs); if (FSAL_IS_ERROR(fsal_status)) { res->res_readdir3.status = nfs3_Errno_status(fsal_status); LogFullDebug(COMPONENT_NFS_READDIR, "getattrs returned %s", msg_fsal_err(fsal_status.major)); goto out; } memcpy(cookie_verifier, &attrs.ctime.tv_sec, sizeof(attrs.ctime.tv_sec)); /* Done with the attrs */ fsal_release_attrs(&attrs); } if (cookie != 0 && use_cookie_verifier) { /* Not the first call, so we have to check the cookie * verifier */ if (memcmp(cookie_verifier, arg->arg_readdir3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { res->res_readdir3.status = NFS3ERR_BAD_COOKIE; rc = NFS_REQ_OK; goto out; } } tracker.entries = gsh_calloc(estimated_num_entries, sizeof(entry3)); tracker.total_entries = estimated_num_entries; tracker.mem_left = count - sizeof(READDIR3resok); tracker.count = 0; tracker.error = NFS3_OK; /* Adjust the cookie we supply to fsal */ if (cookie > 2) { /* it is not the cookie for "." nor ".." */ fsal_cookie = cookie; } else { fsal_cookie = 0; } /* Fills "." */ if (cookie == 0) { res->res_readdir3.status = nfs_readdir_dot_entry(dir_obj, ".", 1, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } } /* Fills ".." */ if ((cookie <= 1) && (estimated_num_entries > 1)) { /* Get parent pentry */ fsal_status_gethandle = fsal_lookupp(dir_obj, &parent_dir_obj, NULL); if (parent_dir_obj == NULL) { res->res_readdir3.status = nfs3_Errno_status(fsal_status_gethandle); rc = NFS_REQ_OK; goto out; } res->res_readdir3.status = nfs_readdir_dot_entry(parent_dir_obj, "..", 2, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } parent_dir_obj->obj_ops.put_ref(parent_dir_obj); parent_dir_obj = NULL; } /* Call readdir */ fsal_status = fsal_readdir(dir_obj, fsal_cookie, &num_entries, &eod_met, 0, /* no attr */ nfs3_readdir_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_readdir3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(dir_obj, &res->res_readdir3.READDIR3res_u.resfail. dir_attributes, NULL); goto out; } if (tracker.error != NFS3_OK) { res->res_readdir3.status = tracker.error; nfs_SetPostOpAttr(dir_obj, &res->res_readdir3.READDIR3res_u.resfail. dir_attributes, NULL); goto out; } LogFullDebug(COMPONENT_NFS_READDIR, "-- Readdir -> Call to fsal_readdir(cookie=%" PRIu64 ")", fsal_cookie); if ((num_entries == 0) && (cookie > 1)) { RES_READDIR3_OK->reply.entries = NULL; RES_READDIR3_OK->reply.eof = TRUE; } else { RES_READDIR3_OK->reply.entries = tracker.entries; RES_READDIR3_OK->reply.eof = eod_met; } nfs_SetPostOpAttr(dir_obj, &RES_READDIR3_OK->dir_attributes, NULL); memcpy(RES_READDIR3_OK->cookieverf, cookie_verifier, sizeof(cookieverf3)); res->res_readdir3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (dir_obj) dir_obj->obj_ops.put_ref(dir_obj); if (parent_dir_obj) parent_dir_obj->obj_ops.put_ref(parent_dir_obj); /* Deallocate memory in the event of an error */ if (((res->res_readdir3.status != NFS3_OK) || (rc != NFS_REQ_OK) || ((num_entries == 0) && (cookie > 1))) && (tracker.entries != NULL)) { free_entry3s(tracker.entries); RES_READDIR3_OK->reply.entries = NULL; } return rc; } /* nfs3_readdir */
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_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 nfs3_lookup(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj_dir = NULL; struct fsal_obj_handle *obj_file = NULL; fsal_status_t fsal_status; char *name = NULL; int rc = NFS_REQ_OK; struct attrlist attrs; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; name = arg->arg_lookup3.what.name; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_lookup3.what.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Lookup handle: %s name: %s", str, name); } /* to avoid setting it on each error case */ res->res_lookup3.LOOKUP3res_u.resfail.dir_attributes.attributes_follow = FALSE; obj_dir = nfs3_FhandleToCache(&arg->arg_lookup3.what.dir, &res->res_lookup3.status, &rc); if (obj_dir == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } name = arg->arg_lookup3.what.name; fsal_status = fsal_lookup(obj_dir, name, &obj_file, &attrs); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_lookup3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(obj_dir, &res->res_lookup3.LOOKUP3res_u.resfail. dir_attributes, NULL); } else { /* Build FH */ if (nfs3_FSALToFhandle( true, &res->res_lookup3.LOOKUP3res_u.resok.object, obj_file, op_ctx->ctx_export)) { /* Build entry attributes */ nfs_SetPostOpAttr(obj_file, &res->res_lookup3.LOOKUP3res_u. resok.obj_attributes, &attrs); /* Build directory attributes */ nfs_SetPostOpAttr(obj_dir, &res->res_lookup3. LOOKUP3res_u.resok.dir_attributes, NULL); res->res_lookup3.status = NFS3_OK; } else { res->res_lookup3.status = NFS3ERR_BADHANDLE; } } rc = NFS_REQ_OK; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (obj_dir) obj_dir->obj_ops.put_ref(obj_dir); if (obj_file) obj_file->obj_ops.put_ref(obj_file); return rc; } /* nfs3_lookup */
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_fsinfo(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_fsinfo3.fsroot)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_fsinfo handle: %s", str); } /* To avoid setting it on each error case */ res->res_fsinfo3.FSINFO3res_u.resfail.obj_attributes.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_fsinfo3.fsroot, &res->res_fsinfo3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* New fields were added to nfs_config_t to handle this value. We use them */ FSINFO3resok * const FSINFO_FIELD = &res->res_fsinfo3.FSINFO3res_u.resok; FSINFO_FIELD->rtmax = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); FSINFO_FIELD->rtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefRead); /* This field is generally unused, it will be removed in V4 */ FSINFO_FIELD->rtmult = DEV_BSIZE; FSINFO_FIELD->wtmax = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); FSINFO_FIELD->wtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefWrite); /* This field is generally unused, it will be removed in V4 */ FSINFO_FIELD->wtmult = DEV_BSIZE; FSINFO_FIELD->dtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefReaddir); FSINFO_FIELD->maxfilesize = op_ctx->fsal_export->exp_ops.fs_maxfilesize(op_ctx->fsal_export); FSINFO_FIELD->time_delta.tv_sec = 1; FSINFO_FIELD->time_delta.tv_nsec = 0; LogFullDebug(COMPONENT_NFSPROTO, "rtmax = %d | rtpref = %d | trmult = %d", FSINFO_FIELD->rtmax, FSINFO_FIELD->rtpref, FSINFO_FIELD->rtmult); LogFullDebug(COMPONENT_NFSPROTO, "wtmax = %d | wtpref = %d | wrmult = %d", FSINFO_FIELD->wtmax, FSINFO_FIELD->wtpref, FSINFO_FIELD->wtmult); LogFullDebug(COMPONENT_NFSPROTO, "dtpref = %d | maxfilesize = %llu ", FSINFO_FIELD->dtpref, FSINFO_FIELD->maxfilesize); /* Allow all kinds of operations to be performed on the server through NFS v3 */ FSINFO_FIELD->properties = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME; nfs_SetPostOpAttr(obj, &res->res_fsinfo3.FSINFO3res_u.resok. obj_attributes, NULL); res->res_fsinfo3.status = NFS3_OK; out: if (obj) obj->obj_ops.put_ref(obj); return rc; } /* nfs3_fsinfo */
int nfs3_fsstat(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_dynamicfsinfo_t dynamicinfo; fsal_status_t fsal_status; struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_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; obj = nfs3_FhandleToCache(&arg->arg_fsstat3.fsroot, &res->res_fsstat3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ return rc; } /* Get statistics and convert from FSAL */ fsal_status = fsal_statfs(obj, &dynamicinfo); if (FSAL_IS_ERROR(fsal_status)) { /* At this point we met an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed statfs: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { /* Drop retryable errors. */ rc = NFS_REQ_DROP; } else { res->res_fsstat3.status = nfs3_Errno_status(fsal_status); rc = NFS_REQ_OK; } goto out; } 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(obj, &res->res_fsstat3.FSSTAT3res_u.resok.obj_attributes, NULL); 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; out: /* return references */ obj->obj_ops.put_ref(obj); return rc; } /* nfs3_fsstat */