int nfs4_op_putrootfh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { fsal_status_t status = {0, 0}; struct fsal_obj_handle *file_obj; PUTROOTFH4res * const res_PUTROOTFH4 = &resp->nfs_resop4_u.opputrootfh; /* First of all, set the reply to zero to make sure * it contains no parasite information */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_PUTROOTFH; /* Release any old export reference */ if (op_ctx->ctx_export != NULL) put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; /* Clear out current entry for now */ set_current_entry(data, NULL); /* Get the root export of the Pseudo FS */ op_ctx->ctx_export = get_gsh_export_by_pseudo("/", true); if (op_ctx->ctx_export == NULL) { LogCrit(COMPONENT_EXPORT, "Could not get export for Pseudo Root"); res_PUTROOTFH4->status = NFS4ERR_NOENT; return res_PUTROOTFH4->status; } op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Build credentials */ res_PUTROOTFH4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_PUTROOTFH4->status == NFS4ERR_ACCESS) { /* Client has no access at all */ LogDebug(COMPONENT_EXPORT, "Client doesn't have access to Pseudo Root"); return res_PUTROOTFH4->status; } if (res_PUTROOTFH4->status != NFS4_OK) { LogMajor(COMPONENT_EXPORT, "Failed to get FSAL credentials Pseudo Root"); return res_PUTROOTFH4->status; } /* Get the Pesudo Root inode of the mounted on export */ status = nfs_export_get_root_entry(op_ctx->ctx_export, &file_obj); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_EXPORT, "Could not get root inode for Pseudo Root"); res_PUTROOTFH4->status = nfs4_Errno_status(status); return res_PUTROOTFH4->status; } LogMidDebug(COMPONENT_EXPORT, "Root node %p", data->current_obj); set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops.put_ref(file_obj); /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(data->currentFH.nfs_fh4_val == NULL, &data->currentFH, data->current_obj, op_ctx->ctx_export)) { LogCrit(COMPONENT_EXPORT, "Could not get handle for Pseudo Root"); res_PUTROOTFH4->status = NFS4ERR_SERVERFAULT; return res_PUTROOTFH4->status; } LogHandleNFS4("NFS4 PUTROOTFH CURRENT FH: ", &data->currentFH); res_PUTROOTFH4->status = NFS4_OK; return res_PUTROOTFH4->status; } /* nfs4_op_putrootfh */
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 */
/** * @brief NFS4_OP_LOOKUPP * * This function implements the NFS4_OP_LOOKUPP operation, which looks * up the parent of the supplied directory. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 369 * */ int nfs4_op_lookupp(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LOOKUPP4res * const res_LOOKUPP4 = &resp->nfs_resop4_u.oplookupp; struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *file_obj; struct fsal_obj_handle *root_obj; fsal_status_t status; struct gsh_export *original_export = op_ctx->ctx_export; resp->resop = NFS4_OP_LOOKUPP; res_LOOKUPP4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LOOKUPP4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LOOKUPP4->status != NFS4_OK) return res_LOOKUPP4->status; /* Preparing for cache_inode_lookup ".." */ file_obj = NULL; dir_obj = data->current_obj; /* If Filehandle points to the root of the current export, then backup * through junction into the containing export. */ if (data->current_obj->type != DIRECTORY) goto not_junction; PTHREAD_RWLOCK_rdlock(&original_export->lock); status = nfs_export_get_root_entry(original_export, &root_obj); if (FSAL_IS_ERROR(status)) { res_LOOKUPP4->status = nfs4_Errno_status(status); PTHREAD_RWLOCK_unlock(&original_export->lock); return res_LOOKUPP4->status; } if (data->current_obj == root_obj) { struct gsh_export *parent_exp = NULL; /* Handle reverse junction */ LogDebug(COMPONENT_EXPORT, "Handling reverse junction from Export_Id %d Path %s Parent=%p", original_export->export_id, original_export->fullpath, original_export->exp_parent_exp); if (original_export->exp_parent_exp == NULL) { /* lookupp on the root on the pseudofs should return * NFS4ERR_NOENT (RFC3530, page 166) */ PTHREAD_RWLOCK_unlock(&original_export->lock); res_LOOKUPP4->status = NFS4ERR_NOENT; return res_LOOKUPP4->status; } PTHREAD_RWLOCK_unlock(&original_export->lock); /* Clear out data->current entry outside lock * so if it cascades into cleanup, we aren't holding * an export lock that would cause trouble. */ set_current_entry(data, NULL); /* We need to protect accessing the parent information * with the export lock. We use the current export's lock * which is plenty, the parent can't go away without * grabbing the current export's lock to clean out the * parent information. */ PTHREAD_RWLOCK_rdlock(&original_export->lock); /* Get the junction inode into dir_obj and parent_exp * for reference. */ dir_obj = original_export->exp_junction_obj; parent_exp = original_export->exp_parent_exp; /* Check if there is a problem with the export and try and * get a reference to the parent export. */ if (dir_obj == NULL || parent_exp == NULL || !export_ready(parent_exp)) { /* Export is in the process of dying */ PTHREAD_RWLOCK_unlock(&original_export->lock); LogCrit(COMPONENT_EXPORT, "Reverse junction from Export_Id %d Path %s Parent=%p is stale", original_export->export_id, original_export->fullpath, parent_exp); res_LOOKUPP4->status = NFS4ERR_STALE; return res_LOOKUPP4->status; } get_gsh_export_ref(parent_exp); dir_obj->obj_ops.get_ref(dir_obj); /* Set up dir_obj as current obj with an LRU reference * while still holding the lock. */ set_current_entry(data, dir_obj); /* Put our ref */ dir_obj->obj_ops.put_ref(dir_obj); /* Stash parent export in opctx while still holding the lock. */ op_ctx->ctx_export = parent_exp; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Now we are safely transitioned to the parent export and can * release the lock. */ PTHREAD_RWLOCK_unlock(&original_export->lock); /* Release old export reference that was held by opctx. */ put_gsh_export(original_export); /* Build credentials */ res_LOOKUPP4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_LOOKUPP4->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 Path %s with NFS4ERR_NOENT", parent_exp->export_id, parent_exp->fullpath); res_LOOKUPP4->status = NFS4ERR_NOENT; return res_LOOKUPP4->status; } } else { /* Release the lock taken above */ PTHREAD_RWLOCK_unlock(&original_export->lock); } /* Return our ref from above */ root_obj->obj_ops.put_ref(root_obj); not_junction: status = fsal_lookupp(dir_obj, &file_obj, NULL); if (file_obj != NULL) { /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(false, &data->currentFH, file_obj, op_ctx->ctx_export)) { res_LOOKUPP4->status = NFS4ERR_SERVERFAULT; file_obj->obj_ops.put_ref(file_obj); return res_LOOKUPP4->status; } /* Keep the pointer within the compound data */ set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops.put_ref(file_obj); /* Return successfully */ res_LOOKUPP4->status = NFS4_OK; } else { /* Unable to look up parent for some reason. * Return error. */ set_current_entry(data, NULL); res_LOOKUPP4->status = nfs4_Errno_status(status); } return res_LOOKUPP4->status; } /* nfs4_op_lookupp */