int nfs4_op_readlink(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { READLINK4res * const res_READLINK4 = &resp->nfs_resop4_u.opreadlink; fsal_status_t fsal_status = {0, 0}; struct gsh_buffdesc link_buffer = {.addr = NULL, .len = 0 }; resp->resop = NFS4_OP_READLINK; res_READLINK4->status = NFS4_OK; /* * Do basic checks on a filehandle You can readlink only on a link * ... */ res_READLINK4->status = nfs4_sanity_check_FH(data, SYMBOLIC_LINK, false); if (res_READLINK4->status != NFS4_OK) return res_READLINK4->status; fsal_status = fsal_readlink(data->current_obj, &link_buffer); if (FSAL_IS_ERROR(fsal_status)) { res_READLINK4->status = nfs4_Errno_status(fsal_status); return res_READLINK4->status; } res_READLINK4->READLINK4res_u.resok4.link.utf8string_val = link_buffer.addr; /* NFSv4 does not require the \NUL terminator. */ res_READLINK4->READLINK4res_u.resok4.link.utf8string_len = link_buffer.len - 1; res_READLINK4->status = NFS4_OK; return res_READLINK4->status; } /* nfs4_op_readlink */ /** * @brief Free memory allocated for READLINK result * * This function frees the memory allocated for the resutl of the * NFS4_OP_READLINK operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_readlink_Free(nfs_resop4 *res) { READLINK4res *resp = &res->nfs_resop4_u.opreadlink; if (resp->status == NFS4_OK && resp->READLINK4res_u.resok4.link.utf8string_val) gsh_free(resp->READLINK4res_u.resok4.link.utf8string_val); }
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; }
static void open4_ex(OPEN4args *arg, compound_data_t *data, OPEN4res *res_OPEN4, nfs_client_id_t *clientid, state_owner_t *owner, state_t **file_state, bool *new_state) { /* Parent directory in which to open the file. */ struct fsal_obj_handle *parent = NULL; /* The entry we associated with the desired file before open. */ struct fsal_obj_handle *file_obj = NULL; /* Indicator that file_obj came from lookup. */ bool looked_up_file_obj = false; /* The in_obj to pass to fsal_open2. */ struct fsal_obj_handle *in_obj = NULL; /* The entry open associated with the file. */ struct fsal_obj_handle *out_obj = NULL; fsal_openflags_t openflags = 0; fsal_openflags_t old_openflags = 0; enum fsal_create_mode createmode = FSAL_NO_CREATE; /* The filename to create */ char *filename = NULL; /* The supplied calim type */ open_claim_type4 claim = arg->claim.claim; fsal_verifier_t verifier; struct attrlist sattr; /* Status for fsal calls */ fsal_status_t status = {0, 0}; /* The open state for the file */ bool state_lock_held = false; /* Make sure the attributes are initialized */ memset(&sattr, 0, sizeof(sattr)); /* Make sure... */ *file_state = NULL; *new_state = false; /* Pre-process the claim type */ switch (claim) { case CLAIM_NULL: /* Check parent */ parent = data->current_obj; in_obj = parent; /* Parent must be a directory */ if (parent->type != DIRECTORY) { if (parent->type == SYMBOLIC_LINK) { res_OPEN4->status = NFS4ERR_SYMLINK; goto out; } else { res_OPEN4->status = NFS4ERR_NOTDIR; goto out; } } /* Validate and convert the utf8 filename */ res_OPEN4->status = nfs4_utf8string2dynamic(&arg->claim.open_claim4_u.file, UTF8_SCAN_ALL, &filename); if (res_OPEN4->status != NFS4_OK) goto out; /* Set the createmode if appropriate) */ if (arg->openhow.opentype == OPEN4_CREATE) { open4_ex_create_args(arg, data, res_OPEN4, verifier, &createmode, &sattr); if (res_OPEN4->status != NFS4_OK) goto out; } status = fsal_lookup(parent, filename, &file_obj, NULL); if (!FSAL_IS_ERROR(status)) { /* Check create situations. */ if (arg->openhow.opentype == OPEN4_CREATE) { if (createmode >= FSAL_EXCLUSIVE) { /* Could be a replay, need to continue. */ LogFullDebug(COMPONENT_STATE, "EXCLUSIVE open with existing file %s", filename); } else if (createmode == FSAL_GUARDED) { /* This will be a failure no matter' * what. */ looked_up_file_obj = true; res_OPEN4->status = NFS4ERR_EXIST; goto out; } else { /* FSAL_UNCHECKED, may be a truncate * and we need to pass in the case * of fsal_reopen2 case. */ if (FSAL_TEST_MASK(sattr.valid_mask, ATTR_SIZE) && sattr.filesize == 0) { LogFullDebug(COMPONENT_STATE, "Truncate"); openflags |= FSAL_O_TRUNC; } } } /* We found the file by lookup, discard the filename * and remember that we found the entry by lookup. */ looked_up_file_obj = true; gsh_free(filename); filename = NULL; } else if (status.major != ERR_FSAL_NOENT || arg->openhow.opentype != OPEN4_CREATE) { /* A real error occurred */ res_OPEN4->status = nfs4_Errno_status(status); goto out; } break; /* Both of these just use the current filehandle. */ case CLAIM_PREVIOUS: owner->so_owner.so_nfs4_owner.so_confirmed = true; if (!nfs4_check_deleg_reclaim(clientid, &data->currentFH)) { /* It must have been revoked. Can't reclaim.*/ LogInfo(COMPONENT_NFS_V4, "Can't reclaim delegation"); res_OPEN4->status = NFS4ERR_RECLAIM_BAD; goto out; } openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; case CLAIM_FH: file_obj = data->current_obj; break; case CLAIM_DELEGATE_PREV: /* FIXME: Remove this when we have full support * for CLAIM_DELEGATE_PREV and delegpurge operations */ res_OPEN4->status = NFS4ERR_NOTSUPP; goto out; case CLAIM_DELEGATE_CUR: res_OPEN4->status = open4_claim_deleg(arg, data); if (res_OPEN4->status != NFS4_OK) goto out; openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; default: LogFatal(COMPONENT_STATE, "Programming error. Invalid claim after check."); } if ((arg->share_access & OPEN4_SHARE_ACCESS_READ) != 0) openflags |= FSAL_O_READ; if ((arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) openflags |= FSAL_O_WRITE; if ((arg->share_deny & OPEN4_SHARE_DENY_READ) != 0) openflags |= FSAL_O_DENY_READ; if ((arg->share_deny & OPEN4_SHARE_DENY_WRITE) != 0) openflags |= FSAL_O_DENY_WRITE_MAND; /* Check if file_obj a REGULAR_FILE */ if (file_obj != NULL && file_obj->type != REGULAR_FILE) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected REGULAR_FILE actual %s", object_file_type_to_str(file_obj->type)); if (file_obj->type == DIRECTORY) { res_OPEN4->status = NFS4ERR_ISDIR; } else { /* All special nodes must return NFS4ERR_SYMLINK for * proper client behavior per this linux-nfs post: * http://marc.info/?l=linux-nfs&m=131342421825436&w=2 */ res_OPEN4->status = NFS4ERR_SYMLINK; } goto out; } if (file_obj != NULL) { /* Go ahead and take the state lock now. */ PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; in_obj = file_obj; /* Check if any existing delegations conflict with this open. * Delegation recalls will be scheduled if there is a conflict. */ if (state_deleg_conflict(file_obj, (arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0)) { res_OPEN4->status = NFS4ERR_DELAY; goto out; } /* Check if there is already a state for this entry and owner. */ *file_state = nfs4_State_Get_Obj(file_obj, owner); if (isFullDebug(COMPONENT_STATE) && *file_state != NULL) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, *file_state); LogFullDebug(COMPONENT_STATE, "Found existing state %s", str); } /* Check if open from another export */ if (*file_state != NULL && !state_same_export(*file_state, op_ctx->ctx_export)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %" PRIu16" request for export %"PRIu16, state_export_id(*file_state), op_ctx->ctx_export->export_id); res_OPEN4->status = NFS4ERR_INVAL; goto out; } } /* If that did not succeed, allocate a state from the FSAL. */ if (*file_state == NULL) { *file_state = op_ctx->fsal_export->exp_ops.alloc_state( op_ctx->fsal_export, STATE_TYPE_SHARE, NULL); /* Remember we allocated a new state */ *new_state = true; /* We are ready to perform the open (with possible create). * in_obj has been set to the file itself or the parent. * filename is NULL if in_obj is the file itself. * * Permission check has been done on directory if appropriate, * otherwise fsal_open2 will do a directory permission * check. * * fsal_open2 handles the permission check on the file * itself and also handles all the share reservation stuff. * * fsal_open2 returns with a ref on out_obj, which should be * passed to the state. */ LogFullDebug(COMPONENT_STATE, "Calling open2 for %s", filename); status = fsal_open2(in_obj, *file_state, openflags, createmode, filename, &sattr, verifier, &out_obj, NULL); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } } else if (createmode >= FSAL_EXCLUSIVE) { /* We have an EXCLUSIVE create with an existing * state. We still need to verify it, but no need * to call reopen2. */ LogFullDebug(COMPONENT_STATE, "Calling verify2 "); status = fsal_verify2(file_obj, verifier); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops->get_ref(file_obj); } else { old_openflags = file_obj->obj_ops->status2(file_obj, *file_state); /* Open upgrade */ LogFullDebug(COMPONENT_STATE, "Calling reopen2"); status = fsal_reopen2(file_obj, *file_state, openflags | old_openflags, false); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops->get_ref(file_obj); } if (file_obj == NULL) { /* We have a new cache inode entry, take the state lock. */ file_obj = out_obj; PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; } /* Now the state_lock is held for sure and we have an extra LRU * reference to file_obj, which is the opened file. */ if (*new_state) { /* The state data to be added */ union state_data candidate_data; /* Tracking data for the open state */ struct state_refer refer, *p_refer = NULL; state_status_t state_status; candidate_data.share.share_access = arg->share_access & OPEN4_SHARE_ACCESS_BOTH; candidate_data.share.share_deny = arg->share_deny; candidate_data.share.share_access_prev = (1 << candidate_data.share.share_access); candidate_data.share.share_deny_prev = (1 << candidate_data.share.share_deny); LogFullDebug(COMPONENT_STATE, "Creating new state access=%x deny=%x access_prev=%x deny_prev=%x", candidate_data.share.share_access, candidate_data.share.share_deny, candidate_data.share.share_access_prev, candidate_data.share.share_deny_prev); /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; p_refer = &refer; } /* We need to register this state now. */ state_status = state_add_impl(file_obj, STATE_TYPE_SHARE, &candidate_data, owner, file_state, p_refer); if (state_status != STATE_SUCCESS) { /* state_add_impl failure closed and freed state. * file_state will also be NULL at this point. Also * release the ref on file_obj, since the state add * failed. */ file_obj->obj_ops->put_ref(file_obj); res_OPEN4->status = nfs4_Errno_state(state_status); *new_state = false; goto out; } glist_init(&(*file_state)->state_data.share.share_lockstates); } res_OPEN4->status = open4_create_fh(data, file_obj, true); if (res_OPEN4->status != NFS4_OK) { if (*new_state) { /* state_del_locked will close the file. */ state_del_locked(*file_state); *file_state = NULL; *new_state = false; } else { /*Do an open downgrade to the old open flags */ status = file_obj->obj_ops->reopen2(file_obj, *file_state, old_openflags); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_NFS_V4, "Failed to allocate handle, reopen2 failed with %s", fsal_err_txt(status)); } /* Need to release the state_lock before the put_ref * call. */ PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); state_lock_held = false; /* Release the extra LRU reference on file_obj. */ file_obj->obj_ops->put_ref(file_obj); goto out; } } /* Since open4_create_fh succeeded the LRU reference to file_obj was * consumed by data->current_obj. */ if (!(*new_state)) { LogFullDebug(COMPONENT_STATE, "Open upgrade old access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); LogFullDebug(COMPONENT_STATE, "Open upgrade to access=%x deny=%x", arg->share_access, arg->share_deny); /* Update share_access and share_deny */ (*file_state)->state_data.share.share_access |= arg->share_access & OPEN4_SHARE_ACCESS_BOTH; (*file_state)->state_data.share.share_deny |= arg->share_deny; /* Update share_access_prev and share_deny_prev */ (*file_state)->state_data.share.share_access_prev |= (1 << (arg->share_access & OPEN4_SHARE_ACCESS_BOTH)); (*file_state)->state_data.share.share_deny_prev |= (1 << arg->share_deny); LogFullDebug(COMPONENT_STATE, "Open upgrade new access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); } do_delegation(arg, res_OPEN4, data, owner, *file_state, clientid); out: /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (state_lock_held) PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); if (filename) gsh_free(filename); if (res_OPEN4->status != NFS4_OK) { /* Cleanup state on error */ if (*new_state) (*file_state) ->state_exp->exp_ops.free_state( (*file_state)->state_exp, *file_state); else if (*file_state != NULL) dec_state_t_ref(*file_state); *file_state = NULL; } if (looked_up_file_obj) { /* We got file_obj via lookup, we need to unref it. */ file_obj->obj_ops->put_ref(file_obj); } }
/** * @brief Check delegation claims while opening a file * * This function implements the CLAIM_DELEGATE_CUR claim. * * @param[in] arg OPEN4 arguments * @param[in,out] data Comopund's data */ static nfsstat4 open4_claim_deleg(OPEN4args *arg, compound_data_t *data) { open_claim_type4 claim = arg->claim.claim; stateid4 *rcurr_state; struct fsal_obj_handle *obj_lookup; const utf8string *utfname; char *filename; fsal_status_t fsal_status; nfsstat4 status; state_t *found_state = NULL; if (!(op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_delegations_w) || op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_delegations_r)) ) { LogDebug(COMPONENT_STATE, "NFS4 OPEN returning NFS4ERR_NOTSUPP for CLAIM_DELEGATE"); return NFS4ERR_NOTSUPP; } assert(claim == CLAIM_DELEGATE_CUR); utfname = &arg->claim.open_claim4_u.delegate_cur_info.file; rcurr_state = &arg->claim.open_claim4_u.delegate_cur_info.delegate_stateid; LogDebug(COMPONENT_NFS_V4, "file name: %.*s", utfname->utf8string_len, utfname->utf8string_val); /* Check if filename is correct */ status = nfs4_utf8string2dynamic(utfname, UTF8_SCAN_ALL, &filename); if (status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "Invalid filename"); return status; } /* Does a file with this name already exist ? */ fsal_status = fsal_lookup(data->current_obj, filename, &obj_lookup, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_V4, "%s lookup failed.", filename); gsh_free(filename); return nfs4_Errno_status(fsal_status); } gsh_free(filename); status = open4_create_fh(data, obj_lookup, false); if (status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "open4_create_fh failed"); return status; } found_state = nfs4_State_Get_Pointer(rcurr_state->other); if (found_state == NULL) { LogDebug(COMPONENT_NFS_V4, "state not found with CLAIM_DELEGATE_CUR"); return NFS4ERR_BAD_STATEID; } else { if (isFullDebug(COMPONENT_NFS_V4)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, found_state); LogFullDebug(COMPONENT_NFS_V4, "found matching %s", str); } dec_state_t_ref(found_state); } LogFullDebug(COMPONENT_NFS_V4, "done with CLAIM_DELEGATE_CUR"); return NFS4_OK; }
bool open4_open_owner(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *res, nfs_client_id_t *clientid, state_owner_t **owner) { /* Shortcut to open args */ OPEN4args * const arg_OPEN4 = &(op->nfs_argop4_u.opopen); /* Shortcut to open args */ OPEN4res * const res_OPEN4 = &(res->nfs_resop4_u.opopen); /* The parsed-out name of the open owner */ state_nfs4_owner_name_t owner_name; /* Indicates if the owner is new */ bool_t isnew; /* Return value of FSAL operations */ fsal_status_t status = {0, 0}; struct fsal_obj_handle *obj_lookup = NULL; /* Is this open_owner known? If so, get it so we can use * replay cache */ convert_nfs4_open_owner(&arg_OPEN4->owner, &owner_name); /* If this open owner is not known yet, allocate and set up a new one */ *owner = create_nfs4_owner(&owner_name, clientid, STATE_OPEN_OWNER_NFSV4, NULL, 0, &isnew, CARE_ALWAYS, data->minorversion != 0); LogStateOwner("Open: ", *owner); if (*owner == NULL) { res_OPEN4->status = NFS4ERR_RESOURCE; LogEvent(COMPONENT_STATE, "NFS4 OPEN returning NFS4ERR_RESOURCE for CLAIM_NULL (could not create NFS4 Owner"); return false; } /* Seqid checking is only proper for reused NFSv4.0 owner */ if (isnew || (data->minorversion != 0)) return true; if (arg_OPEN4->seqid == 0) { LogDebug(COMPONENT_STATE, "Previously known open_owner is used with seqid=0, ask the client to confirm it again"); (*owner)->so_owner.so_nfs4_owner.so_confirmed = false; return true; } /* Check for replay */ if (Check_nfs4_seqid(*owner, arg_OPEN4->seqid, op, data->current_obj, res, open_tag)) { /* No replay */ return true; } /* Response is setup for us and LogDebug told what was * wrong. * * Or if this is a seqid replay, find the file entry * and update currentFH */ if (res_OPEN4->status == NFS4_OK) { utf8string *utfile; open_claim4 *oc = &arg_OPEN4->claim; char *filename; /* Load up CLAIM_DELEGATE_CUR file */ switch (oc->claim) { case CLAIM_NULL: utfile = &oc->open_claim4_u.file; break; case CLAIM_DELEGATE_CUR: utfile = &oc->open_claim4_u.delegate_cur_info.file; break; case CLAIM_DELEGATE_PREV: default: return false; } /* Check if filename is correct */ res_OPEN4->status = nfs4_utf8string2dynamic( utfile, UTF8_SCAN_ALL, &filename); if (res_OPEN4->status != NFS4_OK) return false; status = fsal_lookup(data->current_obj, filename, &obj_lookup, NULL); gsh_free(filename); if (obj_lookup == NULL) { res_OPEN4->status = nfs4_Errno_status(status); return false; } res_OPEN4->status = open4_create_fh(data, obj_lookup, false); } return false; }
int nfs4_op_setattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETATTR4args * const arg_SETATTR4 = &op->nfs_argop4_u.opsetattr; SETATTR4res * const res_SETATTR4 = &resp->nfs_resop4_u.opsetattr; struct attrlist sattr; fsal_status_t fsal_status = {0, 0}; const char *tag = "SETATTR"; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; resp->resop = NFS4_OP_SETATTR; res_SETATTR4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_SETATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. */ if (nfs_in_grace()) { res_SETATTR4->status = NFS4ERR_GRACE; return res_SETATTR4->status; } /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access (&arg_SETATTR4->obj_attributes, FATTR4_ATTR_WRITE)) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_SETATTR4->obj_attributes)) { res_SETATTR4->status = NFS4ERR_ATTRNOTSUPP; return res_SETATTR4->status; } /* Convert the fattr4 in the request to a fsal sattr structure */ res_SETATTR4->status = nfs4_Fattr_To_FSAL_attr(&sattr, &arg_SETATTR4->obj_attributes, data); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Trunc may change Xtime so we have to start with trunc and * finish by the mtime and atime */ if ((FSAL_TEST_MASK(sattr.mask, ATTR_SIZE)) || (FSAL_TEST_MASK(sattr.mask, ATTR4_SPACE_RESERVED))) { /* Setting the size of a directory is prohibited */ if (data->current_filetype == DIRECTORY) { res_SETATTR4->status = NFS4ERR_ISDIR; return res_SETATTR4->status; } /* Object should be a file */ if (data->current_obj->type != REGULAR_FILE) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } obj = data->current_obj; /* Check stateid correctness and get pointer to state */ res_SETATTR4->status = nfs4_Check_Stateid(&arg_SETATTR4->stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, tag); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* NB: After this point, if state_found == NULL, then * the stateid is all-0 or all-1 */ if (state_found != NULL) { switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); break; case STATE_TYPE_DELEG: state_open = NULL; break; default: res_SETATTR4->status = NFS4ERR_BAD_STATEID; return res_SETATTR4->status; } /* This is a size operation, this means that * the file MUST have been opened for writing */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_SETATTR4->status = NFS4ERR_OPENMODE; return res_SETATTR4->status; } } else { /* Special stateid, no open state, check to * see if any share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if * any share conflicts The stateid is all-0 or all-1 */ res_SETATTR4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_WRITE, SHARE_BYPASS_NONE)); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; anonymous_started = true; } } const time_t S_NSECS = 1000000000UL; /* Set the atime and mtime (ctime is not setable) */ /* A carry into seconds considered invalid */ if (sattr.atime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } if (sattr.mtime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&sattr); /* If a SETATTR comes with an open stateid, and size is being * set, then the open MUST be for write (checked above), so * is_open_write is simple at this stage, it's just a check that * we have an open owner. */ fsal_status = fsal_setattr(data->current_obj, false, state_found, &sattr); /* Release the attributes (may release an explicit or inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) { res_SETATTR4->status = nfs4_Errno_status(fsal_status); goto done; } /* Set the replyed structure */ res_SETATTR4->attrsset = arg_SETATTR4->obj_attributes.attrmask; /* Exit with no error */ res_SETATTR4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_WRITE); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_SETATTR4->status; } /* nfs4_op_setattr */
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 */
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 */
/** * @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 */
static int nfs4_read(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { READ4args * const arg_READ4 = &op->nfs_argop4_u.opread; READ4res * const res_READ4 = &resp->nfs_resop4_u.opread; uint64_t size = 0; size_t read_size = 0; uint64_t offset = 0; bool eof_met = false; void *bufferdata = NULL; fsal_status_t fsal_status = {0, 0}; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; bool sync = false; bool anonymous_started = false; state_owner_t *owner = NULL; bool bypass = false; uint64_t MaxRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); uint64_t MaxOffsetRead = atomic_fetch_uint64_t( &op_ctx->ctx_export->MaxOffsetRead); /* Say we are managing NFS4_OP_READ */ resp->resop = NFS4_OP_READ; res_READ4->status = NFS4_OK; /* Do basic checks on a filehandle Only files can be read */ if ((data->minorversion > 0) && nfs4_Is_Fh_DSHandle(&data->currentFH)) { if (io == FSAL_IO_READ) return op_dsread(op, data, resp); else return op_dsread_plus(op, data, resp, info); } res_READ4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_READ4->status != NFS4_OK) return res_READ4->status; obj = data->current_obj; /* Check stateid correctness and get pointer to state (also checks for special stateids) */ res_READ4->status = nfs4_Check_Stateid(&arg_READ4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "READ"); if (res_READ4->status != NFS4_OK) return res_READ4->status; /* NB: After this point, if state_found == NULL, then the stateid is all-0 or all-1 */ if (state_found != NULL) { struct state_deleg *sdeleg; if (info) info->io_advise = state_found->state_data.io_advise; switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); /** * @todo FSF: need to check against existing locks */ break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); /** * @todo FSF: should check that write is in * range of an byte range lock... */ break; case STATE_TYPE_DELEG: /* Check if the delegation state allows READ */ sdeleg = &state_found->state_data.deleg; if (!(sdeleg->sd_type & OPEN_DELEGATE_READ) || (sdeleg->sd_state != DELEG_GRANTED)) { /* Invalid delegation for this operation. */ LogDebug(COMPONENT_STATE, "Delegation type:%d state:%d", sdeleg->sd_type, sdeleg->sd_state); res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } state_open = NULL; break; default: res_READ4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "READ with invalid statid of type %d", state_found->state_type); goto out; } /* This is a read operation, this means that the file MUST have been opened for reading */ if (state_open != NULL && (state_open->state_data.share. share_access & OPEN4_SHARE_ACCESS_READ) == 0) { /* Even if file is open for write, the client * may do accidently read operation (caching). * Because of this, READ is allowed if not * explicitly denied. See page 112 in RFC 7530 * for more details. */ if (state_open->state_data.share. share_deny & OPEN4_SHARE_DENY_READ) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_READ4->status = NFS4ERR_OPENMODE; if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_stateid(&dspbuf, state_found); LogDebug(COMPONENT_NFS_V4_LOCK, "READ %s doesn't have OPEN4_SHARE_ACCESS_READ", str); } goto out; } } /** * @todo : this piece of code looks a bit suspicious * (see Rong's mail) * * @todo: ACE: This works for now. How do we want to * handle owner confirmation across NFSv4.0/NFSv4.1? * Do we want to mark every NFSv4.1 owner * pre-confirmed, or make the check conditional on * minorversion like we do here? */ switch (state_found->state_type) { case STATE_TYPE_SHARE: if (data->minorversion == 0 && !state_owner_confirmed(state_found)) { res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } break; case STATE_TYPE_LOCK: case STATE_TYPE_DELEG: break; default: /* Sanity check: all other types are illegal. * we should not got that place (similar check * above), anyway it costs nothing to add this * test */ res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } } else { /* Special stateid, no open state, check to see if any share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if any share * conflicts The stateid is all-0 or all-1 */ bypass = arg_READ4->stateid.seqid != 0; res_READ4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_READ, arg_READ4->stateid.seqid != 0 ? SHARE_BYPASS_READ : SHARE_BYPASS_NONE)); if (res_READ4->status != NFS4_OK) goto out; anonymous_started = true; } /* Need to permission check the read. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_READ_ACCESS, NULL, NULL, true); if (fsal_status.major == ERR_FSAL_ACCESS) { /* Test for execute permission */ fsal_status = fsal_access(obj, FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET (FSAL_ACE_PERM_EXECUTE)); } if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the size and offset of the read operation */ offset = arg_READ4->offset; size = arg_READ4->count; if (MaxOffsetRead < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Read offset=%" PRIu64 " size=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetRead); if ((offset + size) > MaxOffsetRead) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetRead, op_ctx->ctx_export->export_id); res_READ4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxRead) { /* the client asked for too much data, this should normally not happen because client will get FATTR4_MAXREAD value at mount time */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "read requested size = %"PRIu64 " read allowed size = %" PRIu64, size, MaxRead); size = MaxRead; } } /* If size == 0, no I/O is to be made and everything is alright */ if (size == 0) { /* A size = 0 can not lead to EOF */ res_READ4->READ4res_u.resok4.eof = false; res_READ4->READ4res_u.resok4.data.data_len = 0; res_READ4->READ4res_u.resok4.data.data_val = NULL; res_READ4->status = NFS4_OK; goto done; } /* Some work is to be done */ bufferdata = gsh_malloc_aligned(4096, size); if (!anonymous_started && data->minorversion == 0) { owner = get_state_owner_ref(state_found); if (owner != NULL) { op_ctx->clientid = &owner->so_owner.so_nfs4_owner.so_clientid; } } if (obj->fsal->m_ops.support_ex(obj)) { /* Call the new fsal_read2 */ fsal_status = fsal_read2(obj, bypass, state_found, offset, size, &read_size, bufferdata, &eof_met, info); } else { /* Call legacy fsal_rdwr */ fsal_status = fsal_rdwr(obj, io, offset, size, &read_size, bufferdata, &eof_met, &sync, info); } if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); gsh_free(bufferdata); res_READ4->READ4res_u.resok4.data.data_val = NULL; goto done; } if (!eof_met) { /** @todo FSF: add a config option for this behavior? */ /* Need to check against filesize for ESXi clients */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_SIZE); if (!FSAL_IS_ERROR(obj->obj_ops.getattrs(obj, &attrs))) eof_met = (offset + read_size) >= attrs.filesize; /* Done with the attrs */ fsal_release_attrs(&attrs); } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; res_READ4->READ4res_u.resok4.data.data_len = read_size; res_READ4->READ4res_u.resok4.data.data_val = bufferdata; LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_READ: offset = %" PRIu64 " read length = %zu eof=%u", offset, read_size, eof_met); /* Is EOF met or not ? */ res_READ4->READ4res_u.resok4.eof = eof_met; /* Say it is ok */ res_READ4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_READ); server_stats_io_done(size, read_size, (res_READ4->status == NFS4_OK) ? true : false, false); out: if (owner != NULL) dec_state_owner_ref(owner); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_READ4->status; } /* nfs4_op_read */
static int nfs4_write(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite; WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite; uint64_t size = 0; size_t written_size = 0; uint64_t offset; bool eof_met; bool sync = false; void *bufferdata; stable_how4 stable_how; state_t *state_found = NULL; state_t *state_open = NULL; fsal_status_t fsal_status = {0, 0}; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; struct gsh_buffdesc verf_desc; state_owner_t *owner = NULL; uint64_t MaxWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); uint64_t MaxOffsetWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetWrite); /* Lock are not supported */ resp->resop = NFS4_OP_WRITE; res_WRITE4->status = NFS4_OK; if ((data->minorversion > 0) && (nfs4_Is_Fh_DSHandle(&data->currentFH))) { if (io == FSAL_IO_WRITE) return op_dswrite(op, data, resp); else return op_dswrite_plus(op, data, resp, info); } /* * Do basic checks on a filehandle * Only files can be written */ res_WRITE4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota( op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = NFS4ERR_DQUOT; return res_WRITE4->status; } /* vnode to manage is the current one */ obj = data->current_obj; /* Check stateid correctness and get pointer to state * (also checks for special stateids) */ res_WRITE4->status = nfs4_Check_Stateid(&arg_WRITE4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "WRITE"); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* NB: After this points, if state_found == NULL, then * the stateid is all-0 or all-1 */ if (state_found != NULL) { struct state_deleg *sdeleg; if (info) info->io_advise = state_found->state_data.io_advise; switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); /** @todo FSF: need to check against existing locks */ break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); /** * @todo FSF: should check that write is in range of an * exclusive lock... */ break; case STATE_TYPE_DELEG: /* Check if the delegation state allows READ */ sdeleg = &state_found->state_data.deleg; if (!(sdeleg->sd_type & OPEN_DELEGATE_WRITE) || (sdeleg->sd_state != DELEG_GRANTED)) { /* Invalid delegation for this operation. */ LogDebug(COMPONENT_STATE, "Delegation type:%d state:%d", sdeleg->sd_type, sdeleg->sd_state); res_WRITE4->status = NFS4ERR_BAD_STATEID; return res_WRITE4->status; } state_open = NULL; break; case STATE_TYPE_LAYOUT: state_open = NULL; break; default: res_WRITE4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "WRITE with invalid stateid of type %d", (int)state_found->state_type); return res_WRITE4->status; } /* This is a write operation, this means that the file * MUST have been opened for writing */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_WRITE4->status = NFS4ERR_OPENMODE; if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_stateid(&dspbuf, state_found); LogDebug(COMPONENT_NFS_V4_LOCK, "WRITE %s doesn't have OPEN4_SHARE_ACCESS_WRITE", str); } goto out; } } else { /* Special stateid, no open state, check to see if any * share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if any share * conflicts The stateid is all-0 or all-1 */ res_WRITE4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_WRITE, SHARE_BYPASS_NONE)); if (res_WRITE4->status != NFS4_OK) goto out; anonymous_started = true; } /* Need to permission check the write. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_WRITE_ACCESS, NULL, NULL, true); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the characteristics of the I/O to be made */ offset = arg_WRITE4->offset; size = arg_WRITE4->data.data_len; stable_how = arg_WRITE4->stable; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64 " stable = %d", offset, size, stable_how); if (MaxOffsetWrite < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Write offset=%" PRIu64 " count=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetWrite); if ((offset + size) > MaxOffsetWrite) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetWrite, op_ctx->ctx_export->export_id); res_WRITE4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxWrite) { /* * The client asked for too much data, we * must restrict him */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "write requested size = %" PRIu64 " write allowed size = %" PRIu64, size, MaxWrite); size = MaxWrite; } } /* Where are the data ? */ bufferdata = arg_WRITE4->data.data_val; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64, offset, size); /* if size == 0 , no I/O) are actually made and everything is alright */ if (size == 0) { res_WRITE4->WRITE4res_u.resok4.count = 0; res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier( op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; goto done; } if (arg_WRITE4->stable == UNSTABLE4) sync = false; else sync = true; if (!anonymous_started && data->minorversion == 0) { owner = get_state_owner_ref(state_found); if (owner != NULL) { op_ctx->clientid = &owner->so_owner.so_nfs4_owner.so_clientid; } } if (obj->fsal->m_ops.support_ex(obj)) { /* Call the new fsal_write */ fsal_status = fsal_write2(obj, false, state_found, offset, size, &written_size, bufferdata, &sync, info); } else { /* Call legacy fsal_rdwr */ fsal_status = fsal_rdwr(obj, io, offset, size, &written_size, bufferdata, &eof_met, &sync, info); } if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_V4, "write returned %s", fsal_err_txt(fsal_status)); res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; /* Set the returned value */ if (sync) res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; else res_WRITE4->WRITE4res_u.resok4.committed = UNSTABLE4; res_WRITE4->WRITE4res_u.resok4.count = written_size; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier(op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_WRITE); server_stats_io_done(size, written_size, (res_WRITE4->status == NFS4_OK) ? true : false, true); out: if (owner != NULL) dec_state_owner_ref(owner); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_WRITE4->status; } /* nfs4_op_write */