static nfsstat3 nfs_readdir_dot_entry(struct fsal_obj_handle *obj, const char *name, uint64_t cookie, fsal_getattr_cb_t cb, struct nfs3_readdir_cb_data *tracker) { struct fsal_readdir_cb_parms cb_parms; fsal_status_t fsal_status; cb_parms.opaque = tracker; cb_parms.name = name; cb_parms.attr_allowed = true; cb_parms.in_result = true; /* NFS v3 READDIR does not use attributes, so pass NULL */ fsal_status.major = cb(&cb_parms, obj, NULL, 0, cookie, CB_ORIGINAL); if (FSAL_IS_ERROR(fsal_status)) return nfs3_Errno_status(fsal_status); else return tracker->error; }
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_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_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; 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 */