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 */
/** * @brief The NFS4_OP_LISTXATTR operation. * * This functions handles the NFS4_OP_LISTXATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_listxattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LISTXATTR4args * const arg_LISTXATTR4 = &op->nfs_argop4_u.oplistxattr; LISTXATTR4res * const res_LISTXATTR4 = &resp->nfs_resop4_u.oplistxattr; fsal_status_t fsal_status; struct fsal_obj_handle *obj_handle = data->current_obj; xattrlist4 list; nfs_cookie4 la_cookie; verifier4 la_cookieverf; bool_t lr_eof; component4 *entry; int i; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); resp->resop = NFS4_OP_LISTXATTR; res_LISTXATTR4->status = NFS4_OK; LogDebug(COMPONENT_NFS_V4, "SetXattr max count %d cookie %" PRIu64, arg_LISTXATTR4->la_maxcount, arg_LISTXATTR4->la_cookie); /* Do basic checks on a filehandle */ res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_LISTXATTR4->status != NFS4_OK) return res_LISTXATTR4->status; /* Do basic checks on a filehandle */ res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_LISTXATTR4->status != NFS4_OK) return res_LISTXATTR4->status; /* Double buf size, one half for compound and on half for names. */ list.entries = (component4 *)gsh_malloc(2*arg_LISTXATTR4->la_maxcount); la_cookie = arg_LISTXATTR4->la_cookie; memset(la_cookieverf, 0, NFS4_VERIFIER_SIZE); if (la_cookie == 0 && use_cookie_verifier) { if (memcmp(la_cookieverf, arg_LISTXATTR4->la_cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_LISTXATTR4->status = NFS4ERR_BAD_COOKIE; LogFullDebug(COMPONENT_NFS_V4, "Bad cookie"); return res_LISTXATTR4->status; } } fsal_status = obj_handle->obj_ops->listxattrs(obj_handle, arg_LISTXATTR4->la_maxcount, &la_cookie, &la_cookieverf, &lr_eof, &list); if (FSAL_IS_ERROR(fsal_status)) { res_LISTXATTR4->status = nfs4_Errno_state(state_error_convert(fsal_status)); gsh_free(list.entries); res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names.entries = NULL; return res_LISTXATTR4->status; } res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookie = la_cookie; res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_eof = lr_eof; memcpy(res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookieverf, la_cookieverf, NFS4_VERIFIER_SIZE); res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names = list; entry = list.entries; for (i = 0; i < list.entryCount; i++) { LogFullDebug(COMPONENT_FSAL, "entry %d at %p len %d at %p name %s", i, entry, entry->utf8string_len, entry->utf8string_val, entry->utf8string_val); entry += 1; } return res_LISTXATTR4->status; }