fsal_status_t vfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { ssize_t nb_written; fsal_status_t status; int retval = 0; int my_fd = -1; bool has_lock = false; bool need_fsync = false; bool closefd = false; fsal_openflags_t openflags = FSAL_O_WRITE; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } if (*fsal_stable) openflags |= FSAL_O_SYNC; /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &need_fsync, &closefd, false); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); goto out; } fsal_set_credentials(op_ctx->creds); nb_written = pwrite(my_fd, buffer, buffer_size, offset); if (nb_written == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto out; } *wrote_amount = nb_written; /* attempt stability if we aren't using an O_SYNC fd */ if (need_fsync) { retval = fsync(my_fd); if (retval == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); } } out: if (closefd) close(my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->lock); fsal_restore_ganesha_credentials(); return status; }
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 nfs4_op_lookup(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenient alias for the arguments */ LOOKUP4args * const arg_LOOKUP4 = &op->nfs_argop4_u.oplookup; /* Convenient alias for the response */ LOOKUP4res * const res_LOOKUP4 = &resp->nfs_resop4_u.oplookup; /* The name to look up */ char *name = NULL; /* The directory in which to look up the name */ struct fsal_obj_handle *dir_obj = NULL; /* The name found */ struct fsal_obj_handle *file_obj = NULL; /* Status code from fsal */ fsal_status_t status = {0, 0}; resp->resop = NFS4_OP_LOOKUP; res_LOOKUP4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LOOKUP4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LOOKUP4->status != NFS4_OK) { /* for some reason lookup is picky. Just not being * dir is not enough. We want to know it is a symlink */ if (res_LOOKUP4->status == NFS4ERR_NOTDIR && data->current_filetype == SYMBOLIC_LINK) res_LOOKUP4->status = NFS4ERR_SYMLINK; goto out; } /* Validate and convert the UFT8 objname to a regular string */ res_LOOKUP4->status = nfs4_utf8string2dynamic(&arg_LOOKUP4->objname, UTF8_SCAN_ALL, &name); if (res_LOOKUP4->status != NFS4_OK) goto out; LogDebug(COMPONENT_NFS_V4, "name=%s", name); /* Do the lookup in the FSAL */ file_obj = NULL; dir_obj = data->current_obj; /* Sanity check: dir_obj should be ACTUALLY a directory */ status = fsal_lookup(dir_obj, name, &file_obj, NULL); if (FSAL_IS_ERROR(status)) { res_LOOKUP4->status = nfs4_Errno_status(status); goto out; } if (file_obj->type == DIRECTORY) { PTHREAD_RWLOCK_rdlock(&file_obj->state_hdl->state_lock); if (file_obj->state_hdl->dir.junction_export != NULL) { /* Handle junction */ struct fsal_obj_handle *obj = NULL; /* Attempt to get a reference to the export across the * junction. */ if (!export_ready( file_obj->state_hdl->dir.junction_export)) { /* If we could not get a reference, return * stale. Release state_lock */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_STALE on LOOKUP of %s", name); res_LOOKUP4->status = NFS4ERR_STALE; PTHREAD_RWLOCK_unlock( &file_obj->state_hdl->state_lock); goto out; } get_gsh_export_ref( file_obj->state_hdl->dir.junction_export); /* Release any old export reference */ if (op_ctx->ctx_export != NULL) put_gsh_export(op_ctx->ctx_export); /* Stash the new export in the compound data. */ op_ctx->ctx_export = file_obj->state_hdl->dir.junction_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); /* Build credentials */ res_LOOKUP4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_LOOKUP4->status == NFS4ERR_ACCESS) { /* If return is NFS4ERR_ACCESS then this client * doesn't have access to this export, return * NFS4ERR_NOENT to hide it. It was not visible * in READDIR response. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_ACCESS Hiding Export_Id %d Pseudo %s with NFS4ERR_NOENT", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); res_LOOKUP4->status = NFS4ERR_NOENT; goto out; } if (res_LOOKUP4->status == NFS4ERR_WRONGSEC) { /* LogInfo already documents why */ goto out; } if (res_LOOKUP4->status != NFS4_OK) { /* Should never get here, * nfs4_export_check_access can only return * NFS4_OK, NFS4ERR_ACCESS or NFS4ERR_WRONGSEC. */ LogMajor(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Failed with %s for %s, id=%d", nfsstat4_to_str(res_LOOKUP4->status), op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id); goto out; } status = nfs_export_get_root_entry(op_ctx->ctx_export, &obj); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Failed to get root for %s, id=%d, status = %s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, msg_fsal_err(status.major)); res_LOOKUP4->status = nfs4_Errno_status(status); goto out; } LogDebug(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Crossed to %s, id=%d for name=%s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, name); file_obj->obj_ops->put_ref(file_obj); file_obj = obj; } else { PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); } } /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(false, &data->currentFH, file_obj, op_ctx->ctx_export)) { res_LOOKUP4->status = NFS4ERR_SERVERFAULT; goto out; } /* Keep the pointer within the compound data */ set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops->put_ref(file_obj); file_obj = NULL; /* Return successfully */ res_LOOKUP4->status = NFS4_OK; out: /* Release reference on file_obj if we didn't utilze it. */ if (file_obj) file_obj->obj_ops->put_ref(file_obj); gsh_free(name); return res_LOOKUP4->status; } /* nfs4_op_lookup */
static int nfs4_mds_putfh(compound_data_t *data) { struct file_handle_v4 *v4_handle = (struct file_handle_v4 *)data->currentFH.nfs_fh4_val; struct gsh_export *exporting; struct fsal_export *export; struct gsh_buffdesc fh_desc; struct fsal_obj_handle *new_hdl; fsal_status_t fsal_status = { 0, 0 }; bool changed = true; LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle flags 0x%X export id %d", v4_handle->fhflags1, ntohs(v4_handle->id.exports)); LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 FSAL Handle %s", LEN_FH_STR, v4_handle->fsopaque, v4_handle->fs_len); /* Find any existing export by the "id" from the handle, * before releasing the old export (to prevent thrashing). */ exporting = get_gsh_export(ntohs(v4_handle->id.exports)); if (exporting == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NFS4 Request from client (%s) has invalid export identifier %d", op_ctx->client ? op_ctx->client->hostaddr_str : "unknown", ntohs(v4_handle->id.exports)); return NFS4ERR_STALE; } /* If old CurrentFH had a related export, release reference. */ if (op_ctx->ctx_export != NULL) { changed = ntohs(v4_handle->id.exports) != op_ctx->ctx_export->export_id; put_gsh_export(op_ctx->ctx_export); } /* If old CurrentFH had a related server, release reference. */ if (op_ctx->fsal_pnfs_ds != NULL) { pnfs_ds_put(op_ctx->fsal_pnfs_ds); op_ctx->fsal_pnfs_ds = NULL; } /* Clear out current entry for now */ set_current_entry(data, NULL); /* update _ctx fields needed by nfs4_export_check_access */ op_ctx->ctx_export = exporting; op_ctx->fsal_export = export = exporting->fsal_export; if (changed) { int status; status = nfs4_export_check_access(data->req); if (status != NFS4_OK) { LogFullDebug(COMPONENT_FILEHANDLE, "Export check access failed %s", nfsstat4_to_str(status)); return status; } } fh_desc.len = v4_handle->fs_len; fh_desc.addr = &v4_handle->fsopaque; /* adjust the handle opaque into a cache key */ fsal_status = export->exp_ops.wire_to_host(export, FSAL_DIGEST_NFSV4, &fh_desc, v4_handle->fhflags1); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FILEHANDLE, "wire_to_host failed %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } fsal_status = export->exp_ops.create_handle(export, &fh_desc, &new_hdl, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_FILEHANDLE, "could not get create_handle object error %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } /* Set the current entry using the ref from get */ set_current_entry(data, new_hdl); /* Put our ref */ new_hdl->obj_ops->put_ref(new_hdl); LogFullDebug(COMPONENT_FILEHANDLE, "File handle is of type %s(%d)", object_file_type_to_str(data->current_filetype), data->current_filetype); return NFS4_OK; }
/** * @brief Delete the unecessary directories from pseudo FS * * @param pseudopath [IN] full path of the node * @param entry [IN] cache entry for the last directory in the path * * If this entry is present is pseudo FSAL, and is unnecessary, then remove it. * Check recursively if the parent entry is needed. * * The pseudopath is deconstructed in place to create the subsequently shorter * pseudo paths. * * When called the first time, entry is the mount point of an export that has * been unmounted from the PseudoFS. By definition, it is NOT the root of a * PseudoFS. Also, the PseudoFS root filesystem is NOT mounted and thus this * function will not be called for it. The req_op_context references the * export for the PseudoFS entry is within. Note that the caller is * responsible for checking if it is an FSAL_PSEUDO export (we only clean up * directories in FSAL_PSEUDO filesystems). */ void cleanup_pseudofs_node(char *pseudopath, struct fsal_obj_handle *obj) { struct fsal_obj_handle *parent_obj; char *pos = pseudopath + strlen(pseudopath) - 1; char *name; fsal_status_t fsal_status; /* Strip trailing / from pseudopath */ while (*pos == '/') pos--; /* Replace first trailing / if any with NUL */ pos[1] = '\0'; /* Find the previous slash. * We will NEVER back up PAST the root, so no need to check * for walking off the beginning of the string. */ while (*pos != '/') pos--; /* Remember the element name for remove */ name = pos + 1; LogDebug(COMPONENT_EXPORT, "Checking if pseudo node %s is needed", pseudopath); fsal_status = fsal_lookupp(obj, &parent_obj, NULL); if (FSAL_IS_ERROR(fsal_status)) { /* Truncate the pseudopath to be the path to the parent */ *pos = '\0'; LogCrit(COMPONENT_EXPORT, "Could not find cache entry for parent directory %s", pseudopath); return; } fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) { LogCrit(COMPONENT_EXPORT, "Removing pseudo node %s failed with %s", pseudopath, msg_fsal_err(fsal_status.major)); goto out; } /* Before recursing the check the parent, get export lock for looking at * exp_root_obj so we can check if we have reached the root of * the mounted on export. */ PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock); if (parent_obj == op_ctx->ctx_export->exp_root_obj) { LogDebug(COMPONENT_EXPORT, "Reached root of PseudoFS %s", op_ctx->ctx_export->pseudopath); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); goto out; } PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Truncate the pseudopath to be the path to the parent */ *pos = '\0'; /* check if the parent directory is needed */ cleanup_pseudofs_node(pseudopath, parent_obj); out: parent_obj->obj_ops.put_ref(parent_obj); return; }