Beispiel #1
0
int nfs4_op_nverify(struct nfs_argop4 *op, compound_data_t *data,
		    struct nfs_resop4 *resp)
{
	NVERIFY4args * const arg_NVERIFY4 = &op->nfs_argop4_u.opnverify;
	NVERIFY4res * const res_NVERIFY4 = &resp->nfs_resop4_u.opnverify;
	fattr4 file_attr4;
	int rc = 0;

	resp->resop = NFS4_OP_NVERIFY;
	res_NVERIFY4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_NVERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_NVERIFY4->status != NFS4_OK)
		return res_NVERIFY4->status;

	/* operation is always permitted on pseudofs */
	if (nfs4_Is_Fh_Pseudo(&(data->currentFH))) {
		res_NVERIFY4->status = NFS4_OK;
		return res_NVERIFY4->status;
	}

	/* Get only attributes that are allowed to be read */
	if (!nfs4_Fattr_Check_Access(&arg_NVERIFY4->obj_attributes,
				     FATTR4_ATTR_READ)) {
		res_NVERIFY4->status = NFS4ERR_INVAL;
		return res_NVERIFY4->status;
	}

	/* Ask only for supported attributes */
	if (!nfs4_Fattr_Supported(&arg_NVERIFY4->obj_attributes)) {
		res_NVERIFY4->status = NFS4ERR_ATTRNOTSUPP;
		return res_NVERIFY4->status;
	}

	res_NVERIFY4->status =
	    cache_entry_To_Fattr(data->current_entry,
				 &file_attr4,
				 data,
				 &(data->currentFH),
				 &(arg_NVERIFY4->obj_attributes.attrmask));

	if (res_NVERIFY4->status != NFS4_OK)
		return res_NVERIFY4->status;

	rc = nfs4_Fattr_cmp(&arg_NVERIFY4->obj_attributes, &file_attr4);

	if (rc == false) {
		res_NVERIFY4->status = NFS4_OK;
	} else {
		if (rc == -1)
			res_NVERIFY4->status = NFS4ERR_INVAL;
		else
			res_NVERIFY4->status = NFS4ERR_SAME;
	}

	nfs4_Fattr_Free(&file_attr4);
	return res_NVERIFY4->status;
}				/* nfs4_op_nverify */
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;
	cache_inode_status_t cache_status;
	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;

	/* Using cache_inode_readlink */
	cache_status = cache_inode_readlink(data->current_entry, &link_buffer);

	if (cache_status != CACHE_INODE_SUCCESS) {
		res_READLINK4->status = nfs4_Errno(cache_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);
	return;
}				/* nfs4_op_readlink_Free */
int nfs41_op_free_stateid(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs41_op_free_stateid";

  /* Lock are not supported */
  resp->resop = NFS4_OP_FREE_STATEID;
  res_FREE_STATEID4.fsr_status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_FREE_STATEID4.fsr_status = nfs4_sanity_check_FH(data,0LL);
  /* If nfs4_sanity_check_FH is OK it'll return NFS4_OK, an error otherwise */

  return res_FREE_STATEID4.fsr_status;
}                               /* nfs41_op_lock */
Beispiel #4
0
/**
 * @brief The NFS4_OP_REMOVEXATTR operation.
 *
 * This functions handles the NFS4_OP_REMOVEXATTR 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
 *
 * @return per RFC5661, p. 373-4
 */
int nfs4_op_removexattr(struct nfs_argop4 *op, compound_data_t *data,
			struct nfs_resop4 *resp)
{
	REMOVEXATTR4args * const arg_REMOVEXATTR4 =
					&op->nfs_argop4_u.opremovexattr;
	REMOVEXATTR4res * const res_REMOVEXATTR4 =
					&resp->nfs_resop4_u.opremovexattr;
	fsal_status_t fsal_status;
	struct fsal_obj_handle *obj_handle = data->current_obj;

	resp->resop = NFS4_OP_REMOVEXATTR;
	res_REMOVEXATTR4->status = NFS4_OK;

	LogDebug(COMPONENT_NFS_V4,
		 "RemoveXattr len %d name: %s",
		 arg_REMOVEXATTR4->ra_name.utf8string_len,
		 arg_REMOVEXATTR4->ra_name.utf8string_val);

	/* Do basic checks on a filehandle */
	res_REMOVEXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE,
							false);

	if (res_REMOVEXATTR4->status != NFS4_OK)
		return res_REMOVEXATTR4->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_REMOVEXATTR4->status = NFS4ERR_GRACE;
		return res_REMOVEXATTR4->status;
	}
	res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.atomic = false;
	res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.before =
				fsal_get_changeid4(data->current_obj);
	fsal_status = obj_handle->obj_ops->removexattrs(obj_handle,
					&arg_REMOVEXATTR4->ra_name);
	if (FSAL_IS_ERROR(fsal_status))
		return res_REMOVEXATTR4->status = nfs4_Errno_state(
					state_error_convert(fsal_status));
	res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.after =
				fsal_get_changeid4(data->current_obj);
	return res_REMOVEXATTR4->status;
}
Beispiel #5
0
/**
 * @brief Gets attributes for an entry in the FSAL.
 *
 * Impelments the NFS4_OP_GETATTR operation, which gets attributes for
 * an entry in the FSAL.
 *
 * @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. 365
 *
 */
int nfs4_op_getattr(struct nfs_argop4 *op, compound_data_t *data,
		    struct nfs_resop4 *resp)
{
	GETATTR4args * const arg_GETATTR4 = &op->nfs_argop4_u.opgetattr;
	GETATTR4res * const res_GETATTR4 = &resp->nfs_resop4_u.opgetattr;

	/* This is a NFS4_OP_GETTAR */
	resp->resop = NFS4_OP_GETATTR;
	res_GETATTR4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_GETATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_GETATTR4->status != NFS4_OK)
		return res_GETATTR4->status;

	/* Sanity check: if no attributes are wanted, nothing is to be
	 * done.  In this case NFS4_OK is to be returned */
	if (arg_GETATTR4->attr_request.bitmap4_len == 0) {
		res_GETATTR4->status = NFS4_OK;
		return res_GETATTR4->status;
	}

	/* Get only attributes that are allowed to be read */
	if (!nfs4_Fattr_Check_Access_Bitmap(&arg_GETATTR4->attr_request,
					    FATTR4_ATTR_READ)) {
		res_GETATTR4->status = NFS4ERR_INVAL;
		return res_GETATTR4->status;
	}

	nfs4_bitmap4_Remove_Unsupported(&arg_GETATTR4->attr_request);

	res_GETATTR4->status =
		   cache_entry_To_Fattr(data->current_entry,
					&res_GETATTR4->GETATTR4res_u.resok4.
					obj_attributes,
					data,
					&data->currentFH,
					&arg_GETATTR4->attr_request);

	return res_GETATTR4->status;
}				/* nfs4_op_getattr */
Beispiel #6
0
/**
 * @brief NFS4_OP_ACCESS, checks for file's accessibility.
 *
 * This function impelments the NFS4_OP_ACCESS operation, which checks
 * for file's accessibility.
 *
 * @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. 362
 *
 */
int nfs4_op_access(struct nfs_argop4 *op, compound_data_t *data,
		   struct nfs_resop4 *resp)
{
	ACCESS4args * const arg_ACCESS4 = &op->nfs_argop4_u.opaccess;
	ACCESS4res * const res_ACCESS4 = &resp->nfs_resop4_u.opaccess;
	cache_inode_status_t cache_status;
	uint32_t max_access = (ACCESS4_READ | ACCESS4_LOOKUP |
			       ACCESS4_MODIFY | ACCESS4_EXTEND |
			       ACCESS4_DELETE | ACCESS4_EXECUTE);

	/* initialize output */
	res_ACCESS4->ACCESS4res_u.resok4.supported = 0;
	res_ACCESS4->ACCESS4res_u.resok4.access = 0;
	resp->resop = NFS4_OP_ACCESS;
	res_ACCESS4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_ACCESS4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_ACCESS4->status != NFS4_OK)
		return res_ACCESS4->status;

	/* Check for input parameter's sanity */
	if (arg_ACCESS4->access > max_access) {
		res_ACCESS4->status = NFS4ERR_INVAL;
		return res_ACCESS4->status;
	}

	/* Perform the 'access' call */
	cache_status =
	    nfs_access_op(data->current_entry, arg_ACCESS4->access,
			  &res_ACCESS4->ACCESS4res_u.resok4.access,
			  &res_ACCESS4->ACCESS4res_u.resok4.supported);

	if (cache_status == CACHE_INODE_SUCCESS
	    || cache_status == CACHE_INODE_FSAL_EACCESS)
		res_ACCESS4->status = NFS4_OK;
	else
		res_ACCESS4->status = nfs4_Errno(cache_status);

	return res_ACCESS4->status;
}				/* nfs4_op_access */
Beispiel #7
0
int nfs4_op_io_advise(struct nfs_argop4 *op, compound_data_t *data,
		      struct nfs_resop4 *resp)
{
	IO_ADVISE4args * const arg_IO_ADVISE = &op->nfs_argop4_u.opio_advise;
	IO_ADVISE4res * const res_IO_ADVISE = &resp->nfs_resop4_u.opio_advise;
	fsal_status_t fsal_status = { 0, 0 };
	struct io_hints hints;
	state_t *state_found = NULL;
	struct fsal_obj_handle *obj = NULL;

	/* Say we are managing NFS4_OP_IO_ADVISE */
	resp->resop = NFS4_OP_IO_ADVISE;
	res_IO_ADVISE->iaa_status = NFS4_OK;

	hints.hints = 0;
	hints.offset = 0;
	hints.count = 0;

	if (data->minorversion < 2) {
		res_IO_ADVISE->iaa_status = NFS4ERR_NOTSUPP;
		goto done;
	}

	/* Do basic checks on a filehandle Only files can be set */

	res_IO_ADVISE->iaa_status = nfs4_sanity_check_FH(data, REGULAR_FILE,
							 true);
	if (res_IO_ADVISE->iaa_status != NFS4_OK)
		goto done;

	obj = data->current_obj;
	/* Check stateid correctness and get pointer to state (also
	   checks for special stateids) */

	res_IO_ADVISE->iaa_status =
	    nfs4_Check_Stateid(&arg_IO_ADVISE->iaa_stateid, obj,
				&state_found, data,  STATEID_SPECIAL_ANY,
				0, false, "IO_ADVISE");
	if (res_IO_ADVISE->iaa_status != NFS4_OK)
		goto done;

	if (state_found && obj) {
		hints.hints = arg_IO_ADVISE->iaa_hints.map[0];
		hints.offset = arg_IO_ADVISE->iaa_offset;
		hints.count = arg_IO_ADVISE->iaa_count;

		fsal_status = obj->obj_ops.io_advise(obj, &hints);
		if (FSAL_IS_ERROR(fsal_status)) {
			res_IO_ADVISE->iaa_status = NFS4ERR_NOTSUPP;
			goto done;
		}
		/* save hints to use with other operations */
		state_found->state_data.io_advise = hints.hints;

		res_IO_ADVISE->iaa_status = NFS4_OK;
		res_IO_ADVISE->iaa_hints.bitmap4_len = 1;
		res_IO_ADVISE->iaa_hints.map[0] = hints.hints;
	}
done:
	LogDebug(COMPONENT_NFS_V4,
		 "Status  %s hints 0x%X offset %" PRIu64 " count %" PRIu64,
		 nfsstat4_to_str(res_IO_ADVISE->iaa_status),
		 hints.hints, hints.offset, hints.count);

	if (state_found != NULL)
		dec_state_t_ref(state_found);

	return res_IO_ADVISE->iaa_status;
}				/* nfs4_op_io_advise */
Beispiel #8
0
int nfs4_op_open(struct nfs_argop4 *op, compound_data_t *data,
		 struct nfs_resop4 *resp)
{
	/* Shorter alias for OPEN4 arguments */
	OPEN4args * const arg_OPEN4 = &(op->nfs_argop4_u.opopen);
	/* Shorter alias for OPEN4 response */
	OPEN4res * const res_OPEN4 = &(resp->nfs_resop4_u.opopen);
	/* The handle from which the change_info4 is to be
	 * generated.  Every mention of change_info4 in RFC5661
	 * speaks of the parent directory of the file being opened.
	 * However, with CLAIM_FH, CLAIM_DELEG_CUR_FH, and
	 * CLAIM_DELEG_PREV_FH, there is no way to derive the parent
	 * directory from the file handle.  It is Unclear what the
	 * correct behavior is.  In our implementation, we take the
	 * change_info4 of whatever filehandle is current when the
	 * OPEN operation is invoked.
	 */
	struct fsal_obj_handle *obj_change = NULL;
	/* The found client record */
	nfs_client_id_t *clientid = NULL;
	/* The found or created state owner for this open */
	state_owner_t *owner = NULL;
	/* The supplied calim type */
	open_claim_type4 claim = arg_OPEN4->claim.claim;
	/* The open state for the file */
	state_t *file_state = NULL;
	/* True if the state was newly created */
	bool new_state = false;
	int retval;

	LogDebug(COMPONENT_STATE,
		 "Entering NFS v4 OPEN handler -----------------------------");

	/* What kind of open is it ? */
	LogFullDebug(COMPONENT_STATE,
		     "OPEN: Claim type = %d, Open Type = %d, Share Deny = %d, Share Access = %d ",
		     arg_OPEN4->claim.claim,
		     arg_OPEN4->openhow.opentype,
		     arg_OPEN4->share_deny,
		     arg_OPEN4->share_access);

	resp->resop = NFS4_OP_OPEN;
	res_OPEN4->status = NFS4_OK;
	res_OPEN4->OPEN4res_u.resok4.rflags = 0;

	/* Check export permissions if OPEN4_CREATE */
	if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE) &&
	    ((op_ctx->export_perms->options &
	      EXPORT_OPTION_MD_WRITE_ACCESS) == 0)) {
		res_OPEN4->status = NFS4ERR_ROFS;

		LogDebug(COMPONENT_NFS_V4,
			 "Status of OP_OPEN due to export permissions = %s",
			 nfsstat4_to_str(res_OPEN4->status));
		return res_OPEN4->status;
	}

	/* Check export permissions if OPEN4_SHARE_ACCESS_WRITE */
	if (((arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) &&
	    ((op_ctx->export_perms->options &
	      EXPORT_OPTION_WRITE_ACCESS) == 0)) {
		res_OPEN4->status = NFS4ERR_ROFS;

		LogDebug(COMPONENT_NFS_V4,
			 "Status of OP_OPEN due to export permissions = %s",
			 nfsstat4_to_str(res_OPEN4->status));

		return res_OPEN4->status;
	}

	/* Do basic checks on a filehandle */
	res_OPEN4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_OPEN4->status != NFS4_OK)
		return res_OPEN4->status;

	if (data->current_obj == NULL) {
		/* This should be impossible, as PUTFH fills in the
		 * current entry and previous checks weed out handles
		 * in the PseudoFS and DS handles.
		 */
		res_OPEN4->status = NFS4ERR_SERVERFAULT;
		LogCrit(COMPONENT_NFS_V4,
			"Impossible condition in compound data at %s:%u.",
			__FILE__, __LINE__);
		goto out3;
	}

	/* It this a known client id? */
	LogDebug(COMPONENT_STATE,
		 "OPEN Client id = %" PRIx64,
		 arg_OPEN4->owner.clientid);

	retval = nfs_client_id_get_confirmed(
		data->minorversion == 0 ?
			arg_OPEN4->owner.clientid :
			data->session->clientid,
		&clientid);

	if (retval != CLIENT_ID_SUCCESS) {
		res_OPEN4->status = clientid_error_to_nfsstat(retval);
		LogDebug(COMPONENT_NFS_V4,
			 "nfs_client_id_get_confirmed failed");
		return res_OPEN4->status;
	}

	/* Check if lease is expired and reserve it */
	PTHREAD_MUTEX_lock(&clientid->cid_mutex);

	if (data->minorversion == 0 && !reserve_lease(clientid)) {
		PTHREAD_MUTEX_unlock(&clientid->cid_mutex);
		res_OPEN4->status = NFS4ERR_EXPIRED;
		LogDebug(COMPONENT_NFS_V4, "Lease expired");
		goto out3;
	}

	PTHREAD_MUTEX_unlock(&clientid->cid_mutex);

	/* Get the open owner */
	if (!open4_open_owner(op, data, resp, clientid, &owner)) {
		LogDebug(COMPONENT_NFS_V4, "open4_open_owner failed");
		goto out2;
	}

	/* Do the claim check here, so we can save the result in the
	 * owner for NFSv4.0.
	 */
	res_OPEN4->status = open4_validate_claim(data, claim, clientid);

	if (res_OPEN4->status != NFS4_OK) {
		LogDebug(COMPONENT_NFS_V4, "open4_validate_claim failed");
		goto out;
	}

	/* After this point we know we have only CLAIM_NULL,
	 * CLAIM_FH, or CLAIM_PREVIOUS, and that our grace period and
	 * minor version are appropriate for the claim specified.
	 */
	if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE)
	    && (claim != CLAIM_NULL)) {
		res_OPEN4->status = NFS4ERR_INVAL;
		LogDebug(COMPONENT_NFS_V4, "OPEN4_CREATE but not CLAIM_NULL");
		goto out2;
	}

	/* So we still have a reference even after we repalce the
	 * current FH.
	 */
	obj_change = data->current_obj;
	obj_change->obj_ops->get_ref(obj_change);

	/* Update the change info for entry_change. */
	res_OPEN4->OPEN4res_u.resok4.cinfo.before =
		fsal_get_changeid4(obj_change);

	/* Check if share_access does not have any access set, or has
	 * invalid bits that are set.  check that share_deny doesn't
	 * have any invalid bits set.
	 */
	if (!(arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_BOTH)
	    || (data->minorversion == 0
		&& arg_OPEN4->share_access & ~OPEN4_SHARE_ACCESS_BOTH)
	    || (arg_OPEN4->share_access & (~OPEN4_SHARE_ACCESS_WANT_DELEG_MASK &
					   ~OPEN4_SHARE_ACCESS_BOTH))
	    || (arg_OPEN4->share_deny & ~OPEN4_SHARE_DENY_BOTH)) {
		res_OPEN4->status = NFS4ERR_INVAL;
		LogDebug(COMPONENT_NFS_V4,
			 "Invalid SHARE_ACCESS or SHARE_DENY");
		goto out;
	}

	/* Utilize the extended FSAL APU functionality to perform the open. */
	open4_ex(arg_OPEN4, data, res_OPEN4, clientid,
		 owner, &file_state, &new_state);

	if (res_OPEN4->status != NFS4_OK)
		goto out;

	memset(&res_OPEN4->OPEN4res_u.resok4.attrset,
	       0,
	       sizeof(struct bitmap4));

	if (arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4 ||
	    arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4_1) {
		struct bitmap4 *bits = &res_OPEN4->OPEN4res_u.resok4.attrset;

		set_attribute_in_bitmap(bits, FATTR4_TIME_ACCESS);
		set_attribute_in_bitmap(bits, FATTR4_TIME_MODIFY);
	}

	/* If server use OPEN_CONFIRM4, set the correct flag,
	 * but not for 4.1 */
	if (owner->so_owner.so_nfs4_owner.so_confirmed == false)
		res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_CONFIRM;

	res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_LOCKTYPE_POSIX;

	LogFullDebug(COMPONENT_STATE, "NFS4 OPEN returning NFS4_OK");

	/* regular exit */
	res_OPEN4->status = NFS4_OK;

	/* Update change_info4 */
	res_OPEN4->OPEN4res_u.resok4.cinfo.after =
		fsal_get_changeid4(obj_change);
	res_OPEN4->OPEN4res_u.resok4.cinfo.atomic = FALSE;

	/* Handle open stateid/seqid for success */
	update_stateid(file_state,
		       &res_OPEN4->OPEN4res_u.resok4.stateid,
		       data,
		       open_tag);

 out:

	if (res_OPEN4->status != NFS4_OK) {
		LogDebug(COMPONENT_STATE, "failed with status %s",
			 nfsstat4_to_str(res_OPEN4->status));
	}

	/* Save the response in the open owner.
	 * obj_change is either the parent directory or for a CLAIM_PREV is
	 * the entry itself. In either case, it's the right entry to use in
	 * saving the request results.
	 */
	if (data->minorversion == 0) {
		Copy_nfs4_state_req(owner,
				    arg_OPEN4->seqid,
				    op,
				    obj_change,
				    resp,
				    open_tag);
	}

 out2:

	/* Update the lease before exit */
	if (data->minorversion == 0) {
		PTHREAD_MUTEX_lock(&clientid->cid_mutex);
		update_lease(clientid);
		PTHREAD_MUTEX_unlock(&clientid->cid_mutex);
	}

	if (file_state != NULL)
		dec_state_t_ref(file_state);

	/* Clean up if we have an error exit */
	if ((file_state != NULL) && new_state &&
	    (res_OPEN4->status != NFS4_OK)) {
		/* Need to destroy open owner and state */
		state_del(file_state);
	}

	if (obj_change)
		obj_change->obj_ops->put_ref(obj_change);

	if (owner != NULL) {
		/* Need to release the open owner for this call */
		dec_state_owner_ref(owner);
	}

 out3:

	dec_client_id_ref(clientid);

	return res_OPEN4->status;
}				/* nfs4_op_open */
Beispiel #9
0
int nfs4_op_rename(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_rename";

  cache_entry_t        * dst_entry = NULL;
  cache_entry_t        * src_entry = NULL;
  cache_entry_t        * tst_entry_dst = NULL;
  cache_entry_t        * tst_entry_src = NULL;
  fsal_attrib_list_t     attr_dst;
  fsal_attrib_list_t     attr_src;
  fsal_attrib_list_t     attr_tst_dst;
  fsal_attrib_list_t     attr_tst_src;
  cache_inode_status_t   cache_status;
  fsal_status_t          fsal_status;
  fsal_handle_t        * handlenew = NULL;
  fsal_handle_t        * handleold = NULL;
  fsal_name_t            oldname;
  fsal_name_t            newname;

  resp->resop = NFS4_OP_RENAME;
  res_RENAME4.status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_RENAME4.status = nfs4_sanity_check_FH(data, 0LL);
  if(res_RENAME4.status != NFS4_OK)
    return res_RENAME4.status;

  /* Do basic checks on saved filehandle */

  /* If there is no FH */
  if(nfs4_Is_Fh_Empty(&(data->savedFH)))
    {
      res_RENAME4.status = NFS4ERR_NOFILEHANDLE;
      return res_RENAME4.status;
    }

  /* If the filehandle is invalid */
  if(nfs4_Is_Fh_Invalid(&(data->savedFH)))
    {
      res_RENAME4.status = NFS4ERR_BADHANDLE;
      return res_RENAME4.status;
    }

  /* Tests if the Filehandle is expired (for volatile filehandle) */
  if(nfs4_Is_Fh_Expired(&(data->savedFH)))
    {
      res_RENAME4.status = NFS4ERR_FHEXPIRED;
      return res_RENAME4.status;
    }

  /* Pseudo Fs is explictely a Read-Only File system */
  if(nfs4_Is_Fh_Pseudo(&(data->savedFH)))
    {
      res_RENAME4.status = NFS4ERR_ROFS;
      return res_RENAME4.status;
    }

  if (nfs_in_grace())
    {
      res_RENAME4.status = NFS4ERR_GRACE;
      return res_RENAME4.status;
    }

  /* get the names from the RPC input */
  cache_status = utf8_to_name(&arg_RENAME4.oldname, &oldname);

  if(cache_status != CACHE_INODE_SUCCESS)
    {
      res_RENAME4.status = nfs4_Errno(cache_status);
      return res_RENAME4.status;
    }

  cache_status = utf8_to_name(&arg_RENAME4.newname, &newname);

  if(cache_status != CACHE_INODE_SUCCESS)
    {
      res_RENAME4.status = nfs4_Errno(cache_status);
      return res_RENAME4.status;
    }

  /* Sanuty check: never rename to '.' or '..' */
  if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT)
     || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      res_RENAME4.status = NFS4ERR_BADNAME;
      return res_RENAME4.status;
    }

  /* Sanuty check: never rename to '.' or '..' */
  if(!FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT)
     || !FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      res_RENAME4.status = NFS4ERR_BADNAME;
      return res_RENAME4.status;
    }

  /*
   * This operation renames 
   *             - the object in directory pointed by savedFH, named arg_RENAME4.oldname
   *       into
   *             - an object in directory pointed by currentFH, named arg_RENAME4.newname
   *
   * Because of this, we will use 2 entry and we have verified both currentFH and savedFH */

  /* No Cross Device */
  if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid !=
     ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid)
    {
      res_RENAME4.status = NFS4ERR_XDEV;
      return res_RENAME4.status;
    }

  /* destination must be a directory */
  dst_entry = data->current_entry;

  if(data->current_filetype != DIRECTORY)
    {
      res_RENAME4.status = NFS4ERR_NOTDIR;
      return res_RENAME4.status;
    }

  /* Convert saved FH into a vnode */
  src_entry = data->saved_entry;

  /* Source must be a directory */
  if(data->saved_filetype != DIRECTORY)
    {
      res_RENAME4.status = NFS4ERR_NOTDIR;
      return res_RENAME4.status;
    }

  /* Renaming a file to himself is allowed, returns NFS4_OK */
  if(src_entry == dst_entry)
    {
      if(!FSAL_namecmp(&oldname, &newname))
        {
          res_RENAME4.status = NFS4_OK;
          return res_RENAME4.status;
        }
    }

  /* For the change_info4, get the 'change' attributes for both directories */
  if((cache_status = cache_inode_getattr(src_entry,
                                         &attr_src,
                                         data->pcontext,
                                         &cache_status)) != CACHE_INODE_SUCCESS)
    {
      res_RENAME4.status = nfs4_Errno(cache_status);
      return res_RENAME4.status;
    }
#ifdef BUGAZOMEU
  /* Ne devrait pas se produire dans le cas de exportid differents */

  /* Both object must resides on the same filesystem, return NFS4ERR_XDEV if not */
  if(attr_src.va_rdev != attr_dst.va_rdev)
    {
      res_RENAME4.status = NFS4ERR_XDEV;
      return res_RENAME4.status;
    }
#endif
  /* Lookup oldfile to see if it exists (refcount +1) */
  if((tst_entry_src = cache_inode_lookup(src_entry,
                                         &oldname,
                                         &attr_tst_src,
                                         data->pcontext,
                                         &cache_status)) == NULL)
    {
      res_RENAME4.status = nfs4_Errno(cache_status);
      goto release;
    }

  /* Lookup file with new name to see if it already exists (refcount +1),
   * I expect to get NO_ERROR or ENOENT, anything else means an error */
  tst_entry_dst = cache_inode_lookup(dst_entry,
                                     &newname,
                                     &attr_tst_dst,
                                     data->pcontext,
                                     &cache_status);
  if((cache_status != CACHE_INODE_SUCCESS) &&
     (cache_status != CACHE_INODE_NOT_FOUND))
    {
        /* Unexpected status at this step, exit with an error */
        res_RENAME4.status = nfs4_Errno(cache_status);
        goto release;
    }

  if(cache_status == CACHE_INODE_NOT_FOUND)
    tst_entry_dst = NULL;       /* Just to make sure */

  /* Renaming a file to one of its own hardlink is allowed, return NFS4_OK */
  if(tst_entry_src == tst_entry_dst) {
      res_RENAME4.status = NFS4_OK;
      goto release;
    }

  /* Renaming dir into existing file should return NFS4ERR_EXIST */
  if ((tst_entry_src->type == DIRECTORY) &&
      ((tst_entry_dst != NULL) &&
       (tst_entry_dst->type == REGULAR_FILE)))
    {
      res_RENAME4.status = NFS4ERR_EXIST;
      goto release;
    }

  /* Renaming file into existing dir should return NFS4ERR_EXIST */
  if(tst_entry_src->type == REGULAR_FILE)
    {
      if(tst_entry_dst != NULL)
        {
          if(tst_entry_dst->type == DIRECTORY)
            {
              res_RENAME4.status = NFS4ERR_EXIST;
              goto release;
            }
        }
    }

  /* Renaming dir1 into existing, nonempty dir2 should return NFS4ERR_EXIST
   * Renaming file into existing, nonempty dir should return NFS4ERR_EXIST */
  if(tst_entry_dst != NULL)
    {
      if((tst_entry_dst->type == DIRECTORY)
         && ((tst_entry_src->type == DIRECTORY)
             || (tst_entry_src->type == REGULAR_FILE)))
        {
          if(cache_inode_is_dir_empty_WithLock(tst_entry_dst) ==
             CACHE_INODE_DIR_NOT_EMPTY)
            {
              res_RENAME4.status = NFS4ERR_EXIST;
              goto release;
            }
        }
    }

  res_RENAME4.RENAME4res_u.resok4.source_cinfo.before
       = cache_inode_get_changeid4(src_entry);
  res_RENAME4.RENAME4res_u.resok4.target_cinfo.before
       = cache_inode_get_changeid4(dst_entry);

  if(cache_status == CACHE_INODE_SUCCESS)
    {
      /* New entry already exists, its attributes are in attr_tst_*,
         check for old entry to see if types are compatible */
      handlenew = &tst_entry_dst->handle;
      handleold = &tst_entry_src->handle;

      if(!FSAL_handlecmp(handlenew, handleold, &fsal_status))
        {
          /* For the change_info4, get the 'change' attributes for both directories */
          res_RENAME4.RENAME4res_u.resok4.source_cinfo.before
            = cache_inode_get_changeid4(src_entry);
          res_RENAME4.RENAME4res_u.resok4.target_cinfo.before
            = cache_inode_get_changeid4(dst_entry);
          res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE;
          res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE;

          res_RENAME4.status = NFS4_OK;
          goto release;
        }
      else
        {
          /* Destination exists and is something different from source */
          if(( tst_entry_src->type == REGULAR_FILE &&
              tst_entry_dst->type == REGULAR_FILE ) ||
              ( tst_entry_src->type == DIRECTORY &&
              tst_entry_dst->type == DIRECTORY ))
            {
              if(cache_inode_rename(src_entry,
                                    &oldname,
                                    dst_entry,
                                    &newname,
                                    &attr_src,
                                    &attr_dst,
                                    data->pcontext, &cache_status) != CACHE_INODE_SUCCESS)
               {

                 res_RENAME4.status = nfs4_Errno(cache_status);
                 goto release;
               }
            }
          else
            { 
              res_RENAME4.status = NFS4ERR_EXIST;
              goto release;
            }
        }
    }
  else
    {
      /* New entry does not already exist, call cache_entry_rename */
      if(cache_inode_rename(src_entry,
                            &oldname,
                            dst_entry,
                            &newname,
                            &attr_src,
                            &attr_dst,
                            data->pcontext, &cache_status) != CACHE_INODE_SUCCESS)
        {
          res_RENAME4.status = nfs4_Errno(cache_status);
          goto release;
        }
    }

  /* If you reach this point, then everything was alright */
  /* For the change_info4, get the 'change' attributes for both directories */

  res_RENAME4.RENAME4res_u.resok4.source_cinfo.after =
       cache_inode_get_changeid4(src_entry);
  res_RENAME4.RENAME4res_u.resok4.target_cinfo.after =
       cache_inode_get_changeid4(dst_entry);
  res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE;
  res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE;
  res_RENAME4.status = nfs4_Errno(cache_status);

release:
  if (tst_entry_src)
      (void) cache_inode_put(tst_entry_src);
  if (tst_entry_dst)
      (void) cache_inode_put(tst_entry_dst);

  return (res_RENAME4.status);
}                               /* nfs4_op_rename */
Beispiel #10
0
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 */
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
	};
	uint32_t resp_size;

	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;

	/* Response size is space for nfsstat4, length, pointer, and the
	 * link itself.
	 */
	resp_size = RNDUP(link_buffer.len) + 3 * sizeof(uint32_t);

	res_READLINK4->status = check_resp_room(data, resp_size);

	if (res_READLINK4->status != NFS4_OK) {
		/* No room for response, free link. */
		gsh_free(
		    res_READLINK4->READLINK4res_u.resok4.link.utf8string_val);
	}

	data->op_resp_size = resp_size;

	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)
		gsh_free(resp->READLINK4res_u.resok4.link.utf8string_val);
}
int nfs4_op_open_downgrade(struct nfs_argop4 *op, compound_data_t *data,
			   struct nfs_resop4 *resp)
{
	OPEN_DOWNGRADE4args * const arg_OPEN_DOWNGRADE4 =
		&op->nfs_argop4_u.opopen_downgrade;
	OPEN_DOWNGRADE4res * const res_OPEN_DOWNGRADE4 =
		&resp->nfs_resop4_u.opopen_downgrade;
	OPEN_DOWNGRADE4resok *resok =
		&res_OPEN_DOWNGRADE4->OPEN_DOWNGRADE4res_u.resok4;
	state_t *state_found = NULL;
	state_owner_t *open_owner;
	int rc;
	const char *tag = "OPEN_DOWNGRADE";
	char *cause = "";

	resp->resop = NFS4_OP_OPEN_DOWNGRADE;
	res_OPEN_DOWNGRADE4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_OPEN_DOWNGRADE4->status =
	    nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_OPEN_DOWNGRADE4->status != NFS4_OK)
		return res_OPEN_DOWNGRADE4->status;

	/* Open downgrade is done only on a file */
	if (data->current_filetype != REGULAR_FILE) {
		res_OPEN_DOWNGRADE4->status = NFS4ERR_INVAL;
		return res_OPEN_DOWNGRADE4->status;
	}

	/* Check stateid correctness and get pointer to state */
	rc = nfs4_Check_Stateid(&arg_OPEN_DOWNGRADE4->open_stateid,
				data->current_obj,
				&state_found,
				data,
				STATEID_SPECIAL_FOR_LOCK,
				arg_OPEN_DOWNGRADE4->seqid,
				data->minorversion == 0,
				tag);

	if (rc != NFS4_OK && rc != NFS4ERR_REPLAY) {
		res_OPEN_DOWNGRADE4->status = rc;
		LogDebug(COMPONENT_STATE,
			 "OPEN_DOWNGRADE failed nfs4_Check_Stateid");
		return res_OPEN_DOWNGRADE4->status;
	}

	open_owner = get_state_owner_ref(state_found);

	if (open_owner == NULL) {
		/* Unexpected, but something just went stale. */
		res_OPEN_DOWNGRADE4->status = NFS4ERR_STALE;
		goto out2;
	}

	PTHREAD_MUTEX_lock(&open_owner->so_mutex);

	/* Check seqid */
	if (data->minorversion == 0 &&
	    !Check_nfs4_seqid(open_owner,
			      arg_OPEN_DOWNGRADE4->seqid,
			      op,
			      data->current_obj,
			      resp,
			      tag)) {
		/* Response is all setup for us and LogDebug told what was wrong
		 */
		PTHREAD_MUTEX_unlock(&open_owner->so_mutex);
		goto out;
	}

	PTHREAD_MUTEX_unlock(&open_owner->so_mutex);

	/* What kind of open is it ? */
	LogFullDebug(COMPONENT_STATE,
		     "OPEN_DOWNGRADE: Share Deny = %d Share Access = %d ",
		     arg_OPEN_DOWNGRADE4->share_deny,
		     arg_OPEN_DOWNGRADE4->share_access);

	res_OPEN_DOWNGRADE4->status = nfs4_do_open_downgrade(op,
							     data,
							     open_owner,
							     state_found,
							     &cause);

	if (res_OPEN_DOWNGRADE4->status != NFS4_OK) {
		LogEvent(COMPONENT_STATE,
			 "Failed to open downgrade: %s",
			 cause);
		goto out;
	}

	/* Successful exit */
	res_OPEN_DOWNGRADE4->status = NFS4_OK;

	/* Handle stateid/seqid for success */
	update_stateid(state_found, &resok->open_stateid, data, tag);

	/* Save the response in the open owner */
	if (data->minorversion == 0) {
		Copy_nfs4_state_req(open_owner,
				    arg_OPEN_DOWNGRADE4->seqid,
				    op,
				    data->current_obj,
				    resp,
				    tag);
	}

 out:

	dec_state_owner_ref(open_owner);

 out2:

	dec_state_t_ref(state_found);

	return res_OPEN_DOWNGRADE4->status;
}				/* nfs4_op_opendowngrade */
Beispiel #13
0
int nfs4_op_savefh(struct nfs_argop4 *op, compound_data_t *data,
		   struct nfs_resop4 *resp)
{
	SAVEFH4res * const res_SAVEFH = &resp->nfs_resop4_u.opsavefh;

	/* 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_SAVEFH;
	res_SAVEFH->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_SAVEFH->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, true);

	if (res_SAVEFH->status != NFS4_OK)
		return res_SAVEFH->status;

	/* If the savefh is not allocated, do it now */
	if (data->savedFH.nfs_fh4_val == NULL)
		nfs4_AllocateFH(&data->savedFH);

	/* Determine if we can get a new export reference. If there is
	 * no op_ctx->ctx_export, don't get a reference.
	 */
	if (op_ctx->ctx_export != NULL) {
		if (!export_ready(op_ctx->ctx_export)) {
			/* The CurrentFH export has gone bad. */
			res_SAVEFH->status = NFS4ERR_STALE;
			return res_SAVEFH->status;
		}
		get_gsh_export_ref(op_ctx->ctx_export);
	}

	/* Copy the data from current FH to saved FH */
	memcpy(data->savedFH.nfs_fh4_val,
	       data->currentFH.nfs_fh4_val,
	       data->currentFH.nfs_fh4_len);

	data->savedFH.nfs_fh4_len = data->currentFH.nfs_fh4_len;

	/* If saved and current entry are equal, skip the following. */
	if (data->saved_obj != data->current_obj) {

		set_saved_entry(data, data->current_obj);

		/* Make SAVEFH work right for DS handle */
		if (data->current_ds != NULL) {
			data->saved_ds = data->current_ds;
			ds_handle_get_ref(data->saved_ds);
		}
	}

	/* Save the current stateid */
	data->saved_stateid = data->current_stateid;
	data->saved_stateid_valid = data->current_stateid_valid;

	/* If old SavedFH had a related export, release reference. */
	if (data->saved_export != NULL)
		put_gsh_export(data->saved_export);

	/* Save the export information (reference already taken above). */
	data->saved_export = op_ctx->ctx_export;
	data->saved_export_perms = *op_ctx->export_perms;

	if (isFullDebug(COMPONENT_NFS_V4)) {
		char str[LEN_FH_STR];

		sprint_fhandle4(str, &data->savedFH);
		LogFullDebug(COMPONENT_NFS_V4, "SAVE FH: Saved FH %s", str);
	}

	res_SAVEFH->status = NFS4_OK;

	return NFS4_OK;
}				/* nfs4_op_savefh */
int nfs4_op_remove(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_remove";

  cache_entry_t        * parent_entry = NULL;
  fsal_attrib_list_t     attr_parent;
  fsal_name_t            name;
  cache_inode_status_t   cache_status;

  resp->resop = NFS4_OP_REMOVE;
  res_REMOVE4.status = NFS4_OK;

  /*
   * Do basic checks on a filehandle
   * Delete arg_REMOVE4.target in directory pointed by currentFH
   * Make sure the currentFH is pointed a directory
   */
  res_REMOVE4.status = nfs4_sanity_check_FH(data, DIRECTORY);
  if(res_REMOVE4.status != NFS4_OK)
    return res_REMOVE4.status;

  /* Pseudo Fs is explictely a Read-Only File system */
  if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
    {
      res_REMOVE4.status = NFS4ERR_ROFS;
      return res_REMOVE4.status;
    }

  if (nfs_in_grace())
    {
      res_REMOVE4.status = NFS4ERR_GRACE;
      return res_REMOVE4.status;
    }

  /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
  if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
    return nfs4_op_remove_xattr(op, data, resp);

  /* Get the parent entry (aka the current one in the compound data) */
  parent_entry = data->current_entry;

  /* We have to keep track of the 'change' file attribute for reply structure */
  memset(&(res_REMOVE4.REMOVE4res_u.resok4.cinfo.before), 0, sizeof(changeid4));
  res_REMOVE4.REMOVE4res_u.resok4.cinfo.before =
       cache_inode_get_changeid4(parent_entry);

  /* Check for name length */
  if(arg_REMOVE4.target.utf8string_len > FSAL_MAX_NAME_LEN)
    {
      res_REMOVE4.status = NFS4ERR_NAMETOOLONG;
      return res_REMOVE4.status;
    }

  /* get the filename from the argument, it should not be empty */
  if(arg_REMOVE4.target.utf8string_len == 0)
    {
      res_REMOVE4.status = NFS4ERR_INVAL;
      return res_REMOVE4.status;
    }

  /* NFS4_OP_REMOVE can delete files as well as directory, it replaces NFS3_RMDIR and NFS3_REMOVE
   * because of this, we have to know if object is a directory or not */
  if((cache_status =
      cache_inode_error_convert(FSAL_buffdesc2name
                                ((fsal_buffdesc_t *) & arg_REMOVE4.target,
                                 &name))) != CACHE_INODE_SUCCESS)
    {
      res_REMOVE4.status = nfs4_Errno(cache_status);
      return res_REMOVE4.status;
    }

  /* Test RM7: remiving '.' should return NFS4ERR_BADNAME */
  if(!FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT)
     || !FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      res_REMOVE4.status = NFS4ERR_BADNAME;
      return res_REMOVE4.status;
    }

  if((cache_status = cache_inode_remove(parent_entry,
                                        &name,
                                        &attr_parent,
                                        data->pcontext,
                                        &cache_status)) != CACHE_INODE_SUCCESS)
    {
      res_REMOVE4.status = nfs4_Errno(cache_status);
      return res_REMOVE4.status;
    }

  res_REMOVE4.REMOVE4res_u.resok4.cinfo.after
       = cache_inode_get_changeid4(parent_entry);

  /* Operation was not atomic .... */
  res_REMOVE4.REMOVE4res_u.resok4.cinfo.atomic = FALSE;

  /* If you reach this point, everything was ok */

  res_REMOVE4.status = NFS4_OK;

  return NFS4_OK;
}                               /* nfs4_op_remove */
Beispiel #15
0
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 */
Beispiel #16
0
int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t *data,
		   struct nfs_resop4 *resp)
{
	VERIFY4args * const arg_VERIFY4 = &op->nfs_argop4_u.opverify;
	VERIFY4res * const res_VERIFY4 = &resp->nfs_resop4_u.opverify;
	fattr4 file_attr4;
	int rc = 0;
	struct attrlist attrs;

	resp->resop = NFS4_OP_VERIFY;
	res_VERIFY4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_VERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_VERIFY4->status != NFS4_OK)
		return res_VERIFY4->status;

	/* Get only attributes that are allowed to be read */
	if (!nfs4_Fattr_Check_Access
	    (&arg_VERIFY4->obj_attributes, FATTR4_ATTR_READ)) {
		res_VERIFY4->status = NFS4ERR_INVAL;
		return res_VERIFY4->status;
	}

	/* Ask only for supported attributes */
	if (!nfs4_Fattr_Supported(&arg_VERIFY4->obj_attributes)) {
		res_VERIFY4->status = NFS4ERR_ATTRNOTSUPP;
		return res_VERIFY4->status;
	}

	fsal_prepare_attrs(&attrs, 0);

	res_VERIFY4->status =
		bitmap4_to_attrmask_t(&arg_VERIFY4->obj_attributes.attrmask,
				      &attrs.mask);

	if (res_VERIFY4->status != NFS4_OK)
		return res_VERIFY4->status;

	res_VERIFY4->status =
		file_To_Fattr(data, attrs.mask, &attrs, &file_attr4,
			      &arg_VERIFY4->obj_attributes.attrmask);

	if (res_VERIFY4->status != NFS4_OK)
		return res_VERIFY4->status;

	/* Done with the attrs */
	fsal_release_attrs(&attrs);

	rc = nfs4_Fattr_cmp(&(arg_VERIFY4->obj_attributes), &file_attr4);

	if (rc == true)
		res_VERIFY4->status = NFS4_OK;
	else if (rc == -1)
		res_VERIFY4->status = NFS4ERR_INVAL;
	else
		res_VERIFY4->status = NFS4ERR_NOT_SAME;

	nfs4_Fattr_Free(&file_attr4);
	return res_VERIFY4->status;
}				/* nfs4_op_verify */
int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_verify";

  fsal_attrib_list_t   file_attr;
  cache_inode_status_t cache_status;
  fattr4               file_attr4;
  int                  rc = 0;

  resp->resop = NFS4_OP_VERIFY;
  res_VERIFY4.status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_VERIFY4.status = nfs4_sanity_check_FH(data, 0LL);
  if(res_VERIFY4.status != NFS4_OK)
    return res_VERIFY4.status;

  /* operation is always permitted on pseudofs */
  if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
    {
      res_VERIFY4.status = NFS4_OK;
      return res_VERIFY4.status;
    }

  /* Get only attributes that are allowed to be read */
  if(!nfs4_Fattr_Check_Access(&arg_VERIFY4.obj_attributes, FATTR4_ATTR_READ))
    {
      res_VERIFY4.status = NFS4ERR_INVAL;
      return res_VERIFY4.status;
    }

  /* Ask only for supported attributes */
  if(!nfs4_Fattr_Supported(&arg_VERIFY4.obj_attributes))
    {
      res_VERIFY4.status = NFS4ERR_ATTRNOTSUPP;
      return res_VERIFY4.status;
    }

  /* Get the cache inode attribute */
  if((cache_status = cache_inode_getattr(data->current_entry,
                                         &file_attr,
                                         data->pcontext,
                                         &cache_status)) != CACHE_INODE_SUCCESS)
    {
      res_VERIFY4.status = NFS4ERR_INVAL;
      return res_VERIFY4.status;
    }

  if(nfs4_FSALattr_To_Fattr(data->pexport,
                            &file_attr,
                            &file_attr4,
                            data,
                            &(data->currentFH),
                            &(arg_VERIFY4.obj_attributes.attrmask)) != 0)
    {
      res_VERIFY4.status = NFS4ERR_SERVERFAULT;
      return res_VERIFY4.status;
    }

  if((rc = nfs4_Fattr_cmp(&(arg_VERIFY4.obj_attributes), &file_attr4)) == TRUE)
    res_VERIFY4.status = NFS4_OK;
  else
    {
      if(rc == -1)
        res_VERIFY4.status = NFS4ERR_INVAL;
      else
        res_VERIFY4.status = NFS4ERR_NOT_SAME;
    }

  nfs4_Fattr_Free(&file_attr4);
  return res_VERIFY4.status;
}                               /* nfs4_op_verify */
Beispiel #18
0
int nfs4_op_seek(struct nfs_argop4 *op, compound_data_t *data,
		  struct nfs_resop4 *resp)
{
	SEEK4args * const arg_SEEK = &op->nfs_argop4_u.opseek;
	SEEK4res * const res_SEEK = &resp->nfs_resop4_u.opseek;
	fsal_status_t fsal_status = { 0, 0 };
	state_t *state_found = NULL;
	struct fsal_obj_handle *obj = NULL;
	struct io_info info;

	/* Say we are managing NFS4_OP_SEEK */
	resp->resop = NFS4_OP_SEEK;

	if (data->minorversion < 2) {
		res_SEEK->sr_status = NFS4ERR_NOTSUPP;
		goto done;
	}
	res_SEEK->sr_status = NFS4_OK;

	/* Do basic checks on a filehandle Only files can be set */
	res_SEEK->sr_status = nfs4_sanity_check_FH(data, REGULAR_FILE, true);
	if (res_SEEK->sr_status != NFS4_OK)
		goto done;

	obj = data->current_obj;
	/* Check stateid correctness and get pointer to state (also
	   checks for special stateids) */

	res_SEEK->sr_status =
	    nfs4_Check_Stateid(&arg_SEEK->sa_stateid, obj,
				&state_found, data,  STATEID_SPECIAL_ANY,
				0, false, "SEEK");
	if (res_SEEK->sr_status != NFS4_OK)
		goto done;

	if (state_found != NULL) {
		info.io_advise = state_found->state_data.io_advise;
		info.io_content.what = arg_SEEK->sa_what;

		if (arg_SEEK->sa_what == NFS4_CONTENT_DATA ||
				arg_SEEK->sa_what == NFS4_CONTENT_HOLE) {
			info.io_content.hole.di_offset = arg_SEEK->sa_offset;
}
		else
			info.io_content.adb.adb_offset = arg_SEEK->sa_offset;

		fsal_status = obj->obj_ops.seek(obj, &info);
		if (FSAL_IS_ERROR(fsal_status)) {
			res_SEEK->sr_status = NFS4ERR_NXIO;
			goto done;
		}
		res_SEEK->sr_resok4.sr_eof = info.io_eof;
		res_SEEK->sr_resok4.sr_offset = info.io_content.hole.di_offset;
	}
done:
	LogDebug(COMPONENT_NFS_V4,
		 "Status  %s type %d offset %" PRIu64,
		 nfsstat4_to_str(res_SEEK->sr_status), arg_SEEK->sa_what,
		 arg_SEEK->sa_offset);

	if (state_found != NULL)
		dec_state_t_ref(state_found);

	return res_SEEK->sr_status;
}
Beispiel #19
0
int nfs4_op_link(struct nfs_argop4 *op, compound_data_t *data,
		 struct nfs_resop4 *resp)
{
	LINK4args * const arg_LINK4 = &op->nfs_argop4_u.oplink;
	LINK4res * const res_LINK4 = &resp->nfs_resop4_u.oplink;
	cache_entry_t *dir_entry = NULL;
	cache_entry_t *file_entry = NULL;
	cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
	char *newname = NULL;

	resp->resop = NFS4_OP_LINK;
	res_LINK4->status = NFS4_OK;

	/* Do basic checks on a filehandle */
	res_LINK4->status = nfs4_sanity_check_FH(data, DIRECTORY, false);

	if (res_LINK4->status != NFS4_OK)
		goto out;

	res_LINK4->status = nfs4_sanity_check_saved_FH(data, -DIRECTORY, false);

	if (res_LINK4->status != NFS4_OK)
		goto out;

	/*
	 * This operation creates a hard link, for the file
	 * represented by the saved FH, in directory represented by
	 * currentFH under the name arg_LINK4.target
	 */

	/* Validate and convert the UFT8 objname to a regular string */
	res_LINK4->status = nfs4_utf8string2dynamic(&arg_LINK4->newname,
						    UTF8_SCAN_ALL,
						    &newname);

	if (res_LINK4->status != NFS4_OK)
		goto out;

	/* get info from compound data */
	dir_entry = data->current_entry;

	res_LINK4->LINK4res_u.resok4.cinfo.before =
	    cache_inode_get_changeid4(dir_entry);

	file_entry = data->saved_entry;

	/* make the link */
	cache_status =
	    cache_inode_link(file_entry, dir_entry, newname);

	if (cache_status != CACHE_INODE_SUCCESS) {
		res_LINK4->status = nfs4_Errno(cache_status);
		goto out;
	}

	res_LINK4->LINK4res_u.resok4.cinfo.after =
	    cache_inode_get_changeid4(dir_entry);
	res_LINK4->LINK4res_u.resok4.cinfo.atomic = FALSE;

	res_LINK4->status = NFS4_OK;

 out:

	if (newname)
		gsh_free(newname);

	return res_LINK4->status;
}				/* nfs4_op_link */
Beispiel #20
0
int nfs4_op_lockt(struct nfs_argop4 *op, compound_data_t *data,
		  struct nfs_resop4 *resp)
{
	/* Alias for arguments */
	LOCKT4args * const arg_LOCKT4 = &op->nfs_argop4_u.oplockt;
	/* Alias for response */
	LOCKT4res * const res_LOCKT4 = &resp->nfs_resop4_u.oplockt;
	/* Return code from state calls */
	state_status_t state_status = STATE_SUCCESS;
	/* Client id record */
	nfs_client_id_t *clientid = NULL;
	/* Lock owner name */
	state_nfs4_owner_name_t owner_name;
	/* Lock owner record */
	state_owner_t *lock_owner = NULL;
	/* Owner of conflicting lock */
	state_owner_t *conflict_owner = NULL;
	/* Description of lock to test */
	fsal_lock_param_t lock_desc = { FSAL_NO_LOCK, 0, 0 };
	/* Description of conflicting lock */
	fsal_lock_param_t conflict_desc;
	/* return code from id confirm calls */
	int rc;

	LogDebug(COMPONENT_NFS_V4_LOCK,
		 "Entering NFS v4 LOCKT handler ----------------------------");

	/* Initialize to sane default */
	resp->resop = NFS4_OP_LOCKT;

	res_LOCKT4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false);

	if (res_LOCKT4->status != NFS4_OK)
		return res_LOCKT4->status;


	/* Lock length should not be 0 */
	if (arg_LOCKT4->length == 0LL) {
		res_LOCKT4->status = NFS4ERR_INVAL;
		return res_LOCKT4->status;
	}

	if (nfs_in_grace()) {
		res_LOCKT4->status = NFS4ERR_GRACE;
		return res_LOCKT4->status;
	}

	/* Convert lock parameters to internal types */
	switch (arg_LOCKT4->locktype) {
	case READ_LT:
	case READW_LT:
		lock_desc.lock_type = FSAL_LOCK_R;
		break;

	case WRITE_LT:
	case WRITEW_LT:
		lock_desc.lock_type = FSAL_LOCK_W;
		break;
	default:
		LogDebug(COMPONENT_NFS_V4_LOCK,
			 "Invalid lock type");
		res_LOCKT4->status = NFS4ERR_INVAL;
		return res_LOCKT4->status;
	}

	lock_desc.lock_start = arg_LOCKT4->offset;

	if (arg_LOCKT4->length != STATE_LOCK_OFFSET_EOF)
		lock_desc.lock_length = arg_LOCKT4->length;
	else
		lock_desc.lock_length = 0;

	/* Check for range overflow.  Comparing beyond 2^64 is not
	 * possible in 64 bit precision, but off+len > 2^64-1 is
	 * equivalent to len > 2^64-1 - off
	 */

	if (lock_desc.lock_length >
	    (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) {
		res_LOCKT4->status = NFS4ERR_INVAL;
		return res_LOCKT4->status;
	}

	/* Check clientid */
	rc = nfs_client_id_get_confirmed(data->minorversion == 0 ?
						arg_LOCKT4->owner.clientid :
						data->session->clientid,
					 &clientid);

	if (rc != CLIENT_ID_SUCCESS) {
		res_LOCKT4->status = clientid_error_to_nfsstat(rc);
		return res_LOCKT4->status;
	}

	PTHREAD_MUTEX_lock(&clientid->cid_mutex);

	if (data->minorversion == 0 && !reserve_lease(clientid)) {
		PTHREAD_MUTEX_unlock(&clientid->cid_mutex);
		dec_client_id_ref(clientid);
		res_LOCKT4->status = NFS4ERR_EXPIRED;
		return res_LOCKT4->status;
	}

	PTHREAD_MUTEX_unlock(&clientid->cid_mutex);

	/* Is this lock_owner known ? */
	convert_nfs4_lock_owner(&arg_LOCKT4->owner, &owner_name);

	/* This lock owner is not known yet, allocated and set up a new one */
	lock_owner = create_nfs4_owner(&owner_name,
				       clientid,
				       STATE_LOCK_OWNER_NFSV4,
				       NULL,
				       0,
				       NULL,
				       CARE_ALWAYS);

	LogStateOwner("Lock: ", lock_owner);

	if (lock_owner == NULL) {
		LogEvent(COMPONENT_NFS_V4_LOCK,
			 "LOCKT unable to create lock owner");
		res_LOCKT4->status = NFS4ERR_SERVERFAULT;
		goto out;
	}

	LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCKT",
		data->current_entry, lock_owner, &lock_desc);

	if (data->minorversion == 0) {
		op_ctx->clientid =
		    &lock_owner->so_owner.so_nfs4_owner.so_clientid;
	}

	/* Now we have a lock owner and a stateid.  Go ahead and test
	 * the lock in SAL (and FSAL).
	 */

	state_status = state_test(data->current_entry,
				  lock_owner,
				  &lock_desc,
				  &conflict_owner,
				  &conflict_desc);

	if (state_status == STATE_LOCK_CONFLICT) {
		/* A conflicting lock from a different lock_owner,
		 * returns NFS4ERR_DENIED
		 */
		LogStateOwner("Conflict: ", conflict_owner);

		Process_nfs4_conflict(&res_LOCKT4->LOCKT4res_u.denied,
				      conflict_owner,
				      &conflict_desc);
	}

	if (data->minorversion == 0)
		op_ctx->clientid = NULL;

	/* Release NFS4 Open Owner reference */
	dec_state_owner_ref(lock_owner);

	/* Return result */
	res_LOCKT4->status = nfs4_Errno_state(state_status);

 out:

	/* Update the lease before exit */
	if (data->minorversion == 0) {
		PTHREAD_MUTEX_lock(&clientid->cid_mutex);
		update_lease(clientid);
		PTHREAD_MUTEX_unlock(&clientid->cid_mutex);
	}

	dec_client_id_ref(clientid);

	return res_LOCKT4->status;
}				/* nfs4_op_lockt */
int nfs4_op_getdevicelist(struct nfs_argop4 *op, compound_data_t *data,
			  struct nfs_resop4 *resp)
{
	/* Convenience alias for arguments */
	GETDEVICELIST4args * const arg_GETDEVICELIST4 =
	    &op->nfs_argop4_u.opgetdevicelist;
	/* Convenience alias for response */
	GETDEVICELIST4res * const res_GETDEVICELIST4 =
	    &resp->nfs_resop4_u.opgetdevicelist;
	/* NFS4 return code */
	nfsstat4 nfs_status = 0;
	/* Input/output and output parameters of FSAL function */
	struct fsal_getdevicelist_res res;
	/* Structure for callback */
	struct cb_data cb_opaque;

	resp->resop = NFS4_OP_GETDEVICELIST;

	if (data->minorversion == 0)
		return res_GETDEVICELIST4->gdlr_status = NFS4ERR_INVAL;

	nfs_status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (nfs_status != NFS4_OK)
		goto out;

	memset(&res, 0, sizeof(struct fsal_getdevicelist_res));

	res.cookie = arg_GETDEVICELIST4->gdla_cookie;
	memcpy(&res.cookieverf,
	       arg_GETDEVICELIST4->gdla_cookieverf,
	       NFS4_VERIFIER_SIZE);

	cb_opaque.count = 0;
	cb_opaque.max = 32;
	cb_opaque.swexport = nfs_htonl64(op_ctx->export->export_id);

	res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.
	     gdlr_deviceid_list.gdlr_deviceid_list_val =
	     gsh_malloc(cb_opaque.max * sizeof(deviceid4));

	if (res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.
	     gdlr_deviceid_list.gdlr_deviceid_list_val == NULL) {
		nfs_status = NFS4ERR_SERVERFAULT;
		goto out;
	}

	cb_opaque.buffer =
	    res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.
	    gdlr_deviceid_list.gdlr_deviceid_list_val;

	nfs_status = op_ctx->fsal_export->ops->getdevicelist(
					op_ctx->fsal_export,
					arg_GETDEVICELIST4->gdla_layout_type,
					&cb_opaque, cb,
					&res);

	if (nfs_status != NFS4_OK) {
		gsh_free(cb_opaque.buffer);
		goto out;
	}

	res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.gdlr_cookie =
	    res.cookie;

	memcpy(res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.
	       gdlr_cookieverf, &res.cookieverf,
	       NFS4_VERIFIER_SIZE);

	res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.gdlr_deviceid_list.
	    gdlr_deviceid_list_len = cb_opaque.count;

	res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4.gdlr_eof = res.eof;

	nfs_status = NFS4_OK;

 out:
	res_GETDEVICELIST4->gdlr_status = nfs_status;

	return res_GETDEVICELIST4->gdlr_status;
}
Beispiel #22
0
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;
	cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
	const char *tag = "SETATTR";
	state_t *state_found = NULL;
	state_t *state_open = NULL;
	cache_entry_t *entry = 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_entry->type != REGULAR_FILE) {
			res_SETATTR4->status = NFS4ERR_INVAL;
			return res_SETATTR4->status;
		}

		entry = data->current_entry;

		/* Check stateid correctness and get pointer to state */
		res_SETATTR4->status =
		    nfs4_Check_Stateid(&arg_SETATTR4->stateid,
				       data->current_entry,
				       &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(
					entry,
					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.
	 */
	cache_status = cache_inode_setattr(data->current_entry,
					   &sattr,
					   state_open != NULL);

	if (cache_status != CACHE_INODE_SUCCESS) {
		res_SETATTR4->status = nfs4_Errno(cache_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(entry, 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 */
Beispiel #23
0
int nfs4_op_lock(struct nfs_argop4 *op, compound_data_t *data,
		 struct nfs_resop4 *resp)
{
	/* Shorter alias for arguments */
	LOCK4args * const arg_LOCK4 = &op->nfs_argop4_u.oplock;
	/* Shorter alias for response */
	LOCK4res * const res_LOCK4 = &resp->nfs_resop4_u.oplock;
	/* Status code from state calls */
	state_status_t state_status = STATE_SUCCESS;
	/* Data for lock state to be created */
	union state_data candidate_data;
	/* Status code for protocol functions */
	nfsstat4 nfs_status = 0;
	/* Created or found lock state */
	state_t *lock_state = NULL;
	/* Associated open state */
	state_t *state_open = NULL;
	/* The lock owner */
	state_owner_t *lock_owner = NULL;
	/* The open owner */
	state_owner_t *open_owner = NULL;
	/* The owner of a conflicting lock */
	state_owner_t *conflict_owner = NULL;
	/* The owner in which to store the response for NFSv4.0 */
	state_owner_t *resp_owner = NULL;
	/* Sequence ID, for NFSv4.0 */
	seqid4 seqid = 0;
	/* The client performing these operations */
	nfs_client_id_t *clientid = NULL;
	/* Name for the lock owner */
	state_nfs4_owner_name_t owner_name;
	/* Description of requrested lock */
	fsal_lock_param_t lock_desc;
	/* Description of conflicting lock */
	fsal_lock_param_t conflict_desc;
	/* Whether to block */
	state_blocking_t blocking = STATE_NON_BLOCKING;
	/* Tracking data for the lock state */
	struct state_refer refer;
	/* Indicate if we let FSAL to handle requests during grace. */
	bool_t fsal_grace = false;
	int rc;

	LogDebug(COMPONENT_NFS_V4_LOCK,
		 "Entering NFS v4 LOCK handler ----------------------");

	/* Initialize to sane starting values */
	resp->resop = NFS4_OP_LOCK;
	res_LOCK4->status = NFS4_OK;

	/* 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;
	}

	res_LOCK4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false);

	if (res_LOCK4->status != NFS4_OK)
		return res_LOCK4->status;

	/* Convert lock parameters to internal types */
	switch (arg_LOCK4->locktype) {
	case READW_LT:
		blocking = STATE_NFSV4_BLOCKING;
		/* Fall through */

	case READ_LT:
		lock_desc.lock_type = FSAL_LOCK_R;
		break;

	case WRITEW_LT:
		blocking = STATE_NFSV4_BLOCKING;
		/* Fall through */

	case WRITE_LT:
		lock_desc.lock_type = FSAL_LOCK_W;
		break;

	default:
		LogDebug(COMPONENT_NFS_V4_LOCK,
			 "Invalid lock type");
		res_LOCK4->status = NFS4ERR_INVAL;
		return res_LOCK4->status;
	}

	lock_desc.lock_start = arg_LOCK4->offset;
	lock_desc.lock_sle_type = FSAL_POSIX_LOCK;
	lock_desc.lock_reclaim = arg_LOCK4->reclaim;

	if (arg_LOCK4->length != STATE_LOCK_OFFSET_EOF)
		lock_desc.lock_length = arg_LOCK4->length;
	else
		lock_desc.lock_length = 0;

	if (arg_LOCK4->locker.new_lock_owner) {
		/* Check stateid correctness and get pointer to state */
		nfs_status = nfs4_Check_Stateid(
			&arg_LOCK4->locker.locker4_u.open_owner.open_stateid,
			data->current_obj,
			&state_open,
			data,
			STATEID_SPECIAL_FOR_LOCK,
			arg_LOCK4->locker.locker4_u.open_owner.open_seqid,
			data->minorversion == 0,
			lock_tag);

		if (nfs_status != NFS4_OK) {
			if (nfs_status == NFS4ERR_REPLAY) {
				open_owner = get_state_owner_ref(state_open);

				LogStateOwner("Open: ", open_owner);

				if (open_owner != NULL) {
					resp_owner = open_owner;
					seqid = arg_LOCK4->locker.locker4_u
						.open_owner.open_seqid;
					goto check_seqid;
				}
			}

			res_LOCK4->status = nfs_status;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed nfs4_Check_Stateid for open owner");
			return res_LOCK4->status;
		}

		open_owner = get_state_owner_ref(state_open);

		LogStateOwner("Open: ", open_owner);

		if (open_owner == NULL) {
			/* State is going stale. */
			res_LOCK4->status = NFS4ERR_STALE;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed nfs4_Check_Stateid, stale open owner");
			goto out2;
		}

		lock_state = NULL;
		lock_owner = NULL;
		resp_owner = open_owner;
		seqid = arg_LOCK4->locker.locker4_u.open_owner.open_seqid;

		LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG,
			"LOCK New lock owner from open owner",
			data->current_obj, open_owner, &lock_desc);

		/* Check is the clientid is known or not */
		rc = nfs_client_id_get_confirmed(
			data->minorversion == 0 ? arg_LOCK4->locker.
				  locker4_u.open_owner.lock_owner.clientid
				: data->session->clientid,
			&clientid);

		if (rc != CLIENT_ID_SUCCESS) {
			res_LOCK4->status = clientid_error_to_nfsstat(rc);
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed nfs_client_id_get");
			goto out2;
		}

		if (isDebug(COMPONENT_CLIENTID) && (clientid !=
		    open_owner->so_owner.so_nfs4_owner.so_clientrec)) {
			char str_open[LOG_BUFF_LEN / 2];
			struct display_buffer dspbuf_open = {
				sizeof(str_open), str_open, str_open};
			char str_lock[LOG_BUFF_LEN / 2];
			struct display_buffer dspbuf_lock = {
				sizeof(str_lock), str_lock, str_lock};

			display_client_id_rec(&dspbuf_open,
					      open_owner->so_owner
					      .so_nfs4_owner.so_clientrec);
			display_client_id_rec(&dspbuf_lock, clientid);

			LogDebug(COMPONENT_CLIENTID,
				 "Unexpected, new lock owner clientid {%s} doesn't match open owner clientid {%s}",
				 str_lock, str_open);
		}

		/* The related stateid is already stored in state_open */

		/* An open state has been found. Check its type */
		if (state_open->state_type != STATE_TYPE_SHARE) {
			res_LOCK4->status = NFS4ERR_BAD_STATEID;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed open stateid is not a SHARE");
			goto out2;
		}

		/* Is this lock_owner known ? */
		convert_nfs4_lock_owner(&arg_LOCK4->locker.locker4_u.open_owner.
					lock_owner, &owner_name);
		LogStateOwner("Lock: ", lock_owner);
	} else {
		/* Existing lock owner Find the lock stateid From
		 * that, get the open_owner
		 *
		 * There was code here before to handle all-0 stateid,
		 * but that really doesn't apply - when we handle
		 * temporary locks for I/O operations (which is where
		 * we will see all-0 or all-1 stateid, those will not
		 * come in through nfs4_op_lock.
		 *
		 * Check stateid correctness and get pointer to state
		 */
		nfs_status = nfs4_Check_Stateid(
			&arg_LOCK4->locker.locker4_u.lock_owner.lock_stateid,
			data->current_obj,
			&lock_state,
			data,
			STATEID_SPECIAL_FOR_LOCK,
			arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid,
			data->minorversion == 0,
			lock_tag);

		if (nfs_status != NFS4_OK) {
			if (nfs_status == NFS4ERR_REPLAY) {
				lock_owner = get_state_owner_ref(lock_state);

				LogStateOwner("Lock: ", lock_owner);

				if (lock_owner != NULL) {
					open_owner = lock_owner->so_owner
						.so_nfs4_owner.so_related_owner;
					inc_state_owner_ref(open_owner);
					resp_owner = lock_owner;
					seqid = arg_LOCK4->locker.locker4_u
						.lock_owner.lock_seqid;
					goto check_seqid;
				}
			}

			res_LOCK4->status = nfs_status;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed nfs4_Check_Stateid for existing lock owner");
			return res_LOCK4->status;
		}

		/* Check if lock state belongs to same export */
		if (!state_same_export(lock_state, op_ctx->ctx_export)) {
			LogEvent(COMPONENT_STATE,
				 "Lock Owner Export Conflict, Lock held for export %"
				 PRIu16" request for export %"PRIu16,
				 state_export_id(lock_state),
				 op_ctx->ctx_export->export_id);
			res_LOCK4->status = NFS4ERR_INVAL;
			goto out2;
		}

		/* A lock state has been found. Check its type */
		if (lock_state->state_type != STATE_TYPE_LOCK) {
			res_LOCK4->status = NFS4ERR_BAD_STATEID;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed existing lock owner,  state type is not LOCK");
			goto out2;
		}

		/* Get the old lockowner. We can do the following
		 * 'cast', in NFSv4 lock_owner4 and open_owner4 are
		 * different types but with the same definition
		 */
		lock_owner = get_state_owner_ref(lock_state);

		LogStateOwner("Lock: ", lock_owner);

		if (lock_owner == NULL) {
			/* State is going stale. */
			res_LOCK4->status = NFS4ERR_STALE;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "LOCK failed nfs4_Check_Stateid, stale open owner");
			goto out2;
		}

		open_owner =
			lock_owner->so_owner.so_nfs4_owner.so_related_owner;
		LogStateOwner("Open: ", open_owner);
		inc_state_owner_ref(open_owner);
		state_open = lock_state->state_data.lock.openstate;
		inc_state_t_ref(state_open);
		resp_owner = lock_owner;
		seqid = arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid;

		LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG,
			"LOCK Existing lock owner", data->current_obj,
			lock_owner, &lock_desc);

		/* Get the client for this open owner */
		clientid = open_owner->so_owner.so_nfs4_owner.so_clientrec;
		inc_client_id_ref(clientid);
	}

 check_seqid:

	/* Check seqid (lock_seqid or open_seqid) */
	if (data->minorversion == 0) {
		if (!Check_nfs4_seqid(resp_owner,
				      seqid,
				      op,
				      data->current_obj,
				      resp,
				      lock_tag)) {
			/* Response is all setup for us and LogDebug
			 * told what was wrong
			 */
			goto out2;
		}
	}

	/* Lock length should not be 0 */
	if (arg_LOCK4->length == 0LL) {
		res_LOCK4->status = NFS4ERR_INVAL;
		LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length == 0");
		goto out;
	}

	/* Check for range overflow.  Comparing beyond 2^64 is not
	 * possible int 64 bits precision, but off+len > 2^64-1 is
	 * equivalent to len > 2^64-1 - off
	 */

	if (lock_desc.lock_length >
	    (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) {
		res_LOCK4->status = NFS4ERR_INVAL;
		LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length overflow");
		goto out;
	}

	/* Check if open state has correct access for type of lock.
	 *
	 * Don't need to check for conflicting states since this open
	 * state assures there are no conflicting states.
	 */
	if (((arg_LOCK4->locktype == WRITE_LT ||
	      arg_LOCK4->locktype == WRITEW_LT)
	      &&
	      ((state_open->state_data.share.share_access &
		OPEN4_SHARE_ACCESS_WRITE) == 0))
	    ||
	    ((arg_LOCK4->locktype == READ_LT ||
	      arg_LOCK4->locktype == READW_LT)
	      &&
	      ((state_open->state_data.share.share_access &
		OPEN4_SHARE_ACCESS_READ) == 0))) {
		/* The open state doesn't allow access based on the
		 * type of lock
		 */
		LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
			"LOCK failed, SHARE doesn't allow access",
			data->current_obj, lock_owner, &lock_desc);

		res_LOCK4->status = NFS4ERR_OPENMODE;

		goto out;
	}

	/* Do grace period checking (use resp_owner below since a new
	 * lock request with a new lock owner doesn't have a lock owner
	 * yet, but does have an open owner - resp_owner is always one or
	 * the other and non-NULL at this point - so makes for a better log).
	 */
	if (nfs_in_grace()) {
		if (op_ctx->fsal_export->exp_ops.
			fs_supports(op_ctx->fsal_export, fso_grace_method))
				fsal_grace = true;

		if (!fsal_grace && !arg_LOCK4->reclaim) {
			LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
			"LOCK failed, non-reclaim while in grace",
				data->current_obj, resp_owner, &lock_desc);
			res_LOCK4->status = NFS4ERR_GRACE;
			goto out;
		}
		if (!fsal_grace && arg_LOCK4->reclaim
		    && !clientid->cid_allow_reclaim) {
			LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
				"LOCK failed, invalid reclaim while in grace",
				data->current_obj, resp_owner, &lock_desc);
			res_LOCK4->status = NFS4ERR_NO_GRACE;
			goto out;
		}
	} else {
		if (arg_LOCK4->reclaim) {
			LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
				"LOCK failed, reclaim while not in grace",
				data->current_obj, resp_owner, &lock_desc);
			res_LOCK4->status = NFS4ERR_NO_GRACE;
			goto out;
		}
	}

	/* Test if this request is attempting to create a new lock owner */
	if (arg_LOCK4->locker.new_lock_owner) {
		bool_t isnew;

		/* A lock owner is always associated with a previously
		   made open which has itself a previously made
		   stateid */

		/* This lock owner is not known yet, allocated and set
		   up a new one */
		lock_owner = create_nfs4_owner(&owner_name,
					       clientid,
					       STATE_LOCK_OWNER_NFSV4,
					       open_owner,
					       0,
					       &isnew,
					       CARE_ALWAYS);

		LogStateOwner("Lock: ", lock_owner);

		if (lock_owner == NULL) {
			res_LOCK4->status = NFS4ERR_RESOURCE;
			LogLock(COMPONENT_NFS_V4_LOCK, NIV_EVENT,
				"LOCK failed to create new lock owner",
				data->current_obj, open_owner, &lock_desc);
			goto out2;
		}

		if (!isnew) {
			PTHREAD_MUTEX_lock(&lock_owner->so_mutex);
			/* Check lock_seqid if it has attached locks. */
			if (!glist_empty(&lock_owner->so_lock_list)
			    && (data->minorversion == 0)
			    && !Check_nfs4_seqid(lock_owner,
						 arg_LOCK4->locker.locker4_u.
						     open_owner.lock_seqid,
						 op,
						 data->current_obj,
						 resp,
						 lock_tag)) {
				LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
					"LOCK failed to create new lock owner, re-use",
					data->current_obj,
					open_owner, &lock_desc);
				dump_all_locks(
					"All locks (re-use of lock owner)");

				PTHREAD_MUTEX_unlock(&lock_owner->so_mutex);
				/* Response is all setup for us and
				 * LogDebug told what was wrong
				 */
				goto out2;
			}

			PTHREAD_MUTEX_unlock(&lock_owner->so_mutex);

			/* Lock owner is known, see if we also already have
			 * a stateid. Do this here since it's impossible for
			 * there to be such a state if the lock owner was
			 * previously unknown.
			 */
			lock_state = nfs4_State_Get_Obj(data->current_obj,
							  lock_owner);
		}

		if (lock_state == NULL) {
			/* Prepare state management structure */
			memset(&candidate_data, 0, sizeof(candidate_data));
			candidate_data.lock.openstate = state_open;

			/* Add the lock state to the lock table */
			state_status = state_add(data->current_obj,
						 STATE_TYPE_LOCK,
						 &candidate_data,
						 lock_owner,
						 &lock_state,
						 data->minorversion > 0 ?
							&refer : NULL);

			if (state_status != STATE_SUCCESS) {
				res_LOCK4->status = NFS4ERR_RESOURCE;

				LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG,
					"LOCK failed to add new stateid",
					data->current_obj, lock_owner,
					&lock_desc);

				goto out2;
			}

			glist_init(&lock_state->state_data.lock.state_locklist);

			/* Add lock state to the list of lock states belonging
			   to the open state */
			glist_add_tail(
				&state_open->state_data.share.share_lockstates,
				&lock_state->state_data.lock.state_sharelist);

		}
	}

	if (data->minorversion == 0) {
		op_ctx->clientid =
		    &lock_owner->so_owner.so_nfs4_owner.so_clientid;
	}

	/* Now we have a lock owner and a stateid.  Go ahead and push
	 * lock into SAL (and FSAL). */
	state_status = state_lock(data->current_obj,
				  lock_owner,
				  lock_state,
				  blocking,
				  NULL,	/* No block data for now */
				  &lock_desc,
				  &conflict_owner,
				  &conflict_desc);

	if (state_status != STATE_SUCCESS) {
		if (state_status == STATE_LOCK_CONFLICT) {
			/* A conflicting lock from a different
			   lock_owner, returns NFS4ERR_DENIED */
			Process_nfs4_conflict(&res_LOCK4->LOCK4res_u.denied,
					      conflict_owner,
					      &conflict_desc);
		}

		LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed with status %s",
			 state_err_str(state_status));

		res_LOCK4->status = nfs4_Errno_state(state_status);

		/* Save the response in the lock or open owner */
		if (res_LOCK4->status != NFS4ERR_RESOURCE
		    && res_LOCK4->status != NFS4ERR_BAD_STATEID
		    && data->minorversion == 0) {
			Copy_nfs4_state_req(resp_owner,
					    seqid,
					    op,
					    data->current_obj,
					    resp,
					    lock_tag);
		}

		if (arg_LOCK4->locker.new_lock_owner) {
			/* Need to destroy new state */
			state_del(lock_state);
		}
		goto out2;
	}

	if (data->minorversion == 0)
		op_ctx->clientid = NULL;

	res_LOCK4->status = NFS4_OK;

	/* Handle stateid/seqid for success */
	update_stateid(lock_state,
		       &res_LOCK4->LOCK4res_u.resok4.lock_stateid,
		       data,
		       lock_tag);

	if (arg_LOCK4->locker.new_lock_owner) {
		/* Also save the response in the lock owner */
		Copy_nfs4_state_req(lock_owner,
				    arg_LOCK4->locker.locker4_u.open_owner.
				    lock_seqid,
				    op,
				    data->current_obj,
				    resp,
				    lock_tag);

	}

	if (isFullDebug(COMPONENT_NFS_V4_LOCK)) {
		char str[LOG_BUFF_LEN];
		struct display_buffer dspbuf = {sizeof(str), str, str};

		display_stateid(&dspbuf, lock_state);

		LogFullDebug(COMPONENT_NFS_V4_LOCK, "LOCK stateid %s", str);
	}

	LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK applied",
		data->current_obj, lock_owner, &lock_desc);

 out:

	if (data->minorversion == 0) {
		/* Save the response in the lock or open owner */
		Copy_nfs4_state_req(resp_owner,
				    seqid,
				    op,
				    data->current_obj,
				    resp,
				    lock_tag);
	}

 out2:

	if (state_open != NULL)
		dec_state_t_ref(state_open);

	if (lock_state != NULL)
		dec_state_t_ref(lock_state);

	LogStateOwner("Open: ", open_owner);
	LogStateOwner("Lock: ", lock_owner);

	if (open_owner != NULL)
		dec_state_owner_ref(open_owner);

	if (lock_owner != NULL)
		dec_state_owner_ref(lock_owner);

	if (clientid != NULL)
		dec_client_id_ref(clientid);

	return res_LOCK4->status;
}				/* nfs4_op_lock */
int nfs4_op_access(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_access";

  fsal_attrib_list_t   attr;
  fsal_accessflags_t   access_mask = 0;
  cache_inode_status_t cache_status;
  uint32_t max_access = (ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY |
                         ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE);

  /* initialize output */
  res_ACCESS4.ACCESS4res_u.resok4.supported = 0;
  res_ACCESS4.ACCESS4res_u.resok4.access = 0;

  resp->resop = NFS4_OP_ACCESS;
  res_ACCESS4.status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_ACCESS4.status = nfs4_sanity_check_FH(data, 0LL);
  if(res_ACCESS4.status != NFS4_OK)
    return res_ACCESS4.status;

  /* If Filehandle points to a pseudo fs entry, manage it via pseudofs specific functions */
  if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
    return nfs4_op_access_pseudo(op, data, resp);

  /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
  if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
    return nfs4_op_access_xattr(op, data, resp);

  /* Check for input parameter's sanity */
  if(arg_ACCESS4.access > max_access)
    {
      res_ACCESS4.status = NFS4ERR_INVAL;
      return res_ACCESS4.status;
    }

  /* Get the attributes for the object */
  attr = data->current_entry->attributes;

  /* determine the rights to be tested in FSAL */

  if(arg_ACCESS4.access & ACCESS4_READ)
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_READ;
      access_mask |= nfs_get_access_mask(ACCESS4_READ, &attr);
    }

  if((arg_ACCESS4.access & ACCESS4_LOOKUP) && (attr.type == FSAL_TYPE_DIR))
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_LOOKUP;
      access_mask |= nfs_get_access_mask(ACCESS4_LOOKUP, &attr);
    }

  if(arg_ACCESS4.access & ACCESS4_MODIFY)
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_MODIFY;
      access_mask |= nfs_get_access_mask(ACCESS4_MODIFY, &attr);
    }

  if(arg_ACCESS4.access & ACCESS4_EXTEND)
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_EXTEND;
      access_mask |= nfs_get_access_mask(ACCESS4_EXTEND, &attr);
    }

  if((arg_ACCESS4.access & ACCESS4_DELETE) && (attr.type == FSAL_TYPE_DIR))
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_DELETE;
      access_mask |= nfs_get_access_mask(ACCESS4_DELETE, &attr);
    }

  if((arg_ACCESS4.access & ACCESS4_EXECUTE) && (attr.type != FSAL_TYPE_DIR))
    {
      res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_EXECUTE;
      access_mask |= nfs_get_access_mask(ACCESS4_EXECUTE, &attr);
    }

  nfs4_access_debug("requested access", arg_ACCESS4.access, FSAL_ACE4_MASK(access_mask));

  /* Perform the 'access' call */
  if(cache_inode_access(data->current_entry,
                        access_mask,
                        data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
        {
      res_ACCESS4.ACCESS4res_u.resok4.access = res_ACCESS4.ACCESS4res_u.resok4.supported;
      nfs4_access_debug("granted access", arg_ACCESS4.access, 0);
    }

  if(cache_status == CACHE_INODE_FSAL_EACCESS)
        {
      /*
       * We have to determine which access bits are good one by one
       */
      res_ACCESS4.ACCESS4res_u.resok4.access = 0;

      access_mask = nfs_get_access_mask(ACCESS4_READ, &attr);
      if(cache_inode_access(data->current_entry,
                            access_mask,
                            data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
        res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_READ;

      if(attr.type == FSAL_TYPE_DIR)
        {
          access_mask = nfs_get_access_mask(ACCESS4_LOOKUP, &attr);
          if(cache_inode_access(data->current_entry,
                                access_mask,
                                data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
            res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_LOOKUP;
    }

      access_mask = nfs_get_access_mask(ACCESS4_MODIFY, &attr);
      if(cache_inode_access(data->current_entry,
                            access_mask,
                            data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
        res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_MODIFY;

      access_mask = nfs_get_access_mask(ACCESS4_EXTEND, &attr);
      if(cache_inode_access(data->current_entry,
                            access_mask,
                            data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
        res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_EXTEND;

      if(attr.type == FSAL_TYPE_DIR)
        {
          access_mask = nfs_get_access_mask(ACCESS4_DELETE, &attr);
          if(cache_inode_access(data->current_entry,
                                access_mask,
                                data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
            res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_DELETE;
        }

      if(attr.type != FSAL_TYPE_DIR)
        {
          access_mask = nfs_get_access_mask(ACCESS4_EXECUTE, &attr);
          if(cache_inode_access(data->current_entry,
                                access_mask,
                                data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
            res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_EXECUTE;
        }

      nfs4_access_debug("reduced access", res_ACCESS4.ACCESS4res_u.resok4.access, 0);
    }

  res_ACCESS4.status = NFS4_OK;

  return res_ACCESS4.status;
}                               /* nfs4_op_access */
Beispiel #25
0
/**
 * @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;
}
Beispiel #26
0
/**
 * @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 */
Beispiel #27
0
int nfs4_op_link(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_link";

  cache_entry_t        * dir_pentry = NULL;
  cache_entry_t        * file_pentry = NULL;
  cache_inode_status_t   cache_status;
  fsal_attrib_list_t     attr;
  fsal_name_t            newname;

  resp->resop = NFS4_OP_LINK;
  res_LINK4.status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_LINK4.status = nfs4_sanity_check_FH(data, 0LL);
  if(res_LINK4.status != NFS4_OK)
    return res_LINK4.status;

  /* If there is no FH */
  if(nfs4_Is_Fh_Empty(&(data->savedFH)))
    {
      res_LINK4.status = NFS4ERR_NOFILEHANDLE;
      return res_LINK4.status;
    }

  /* If the filehandle is invalid */
  if(nfs4_Is_Fh_Invalid(&(data->savedFH)))
    {
      res_LINK4.status = NFS4ERR_BADHANDLE;
      return res_LINK4.status;
    }

  /* Tests if the Filehandle is expired (for volatile filehandle) */
  if(nfs4_Is_Fh_Expired(&(data->savedFH)))
    {
      res_LINK4.status = NFS4ERR_FHEXPIRED;
      return res_LINK4.status;
    }

  /* Pseudo Fs is explictely a Read-Only File system */
  if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
    {
      res_LINK4.status = NFS4ERR_ROFS;
      return res_LINK4.status;
    }

  /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */
  if(data->pexport == NULL)
    {
      res_LINK4.status = nfs4_SetCompoundExport(data);
      if(res_LINK4.status != NFS4_OK)
        return res_LINK4.status;
    }

  /*
   * This operation creates a hard link, for the file represented by the saved FH, in directory represented by currentFH under the 
   * name arg_LINK4.target 
   */

  /* Crossing device is not allowed */
  if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid !=
     ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid)
    {
      res_LINK4.status = NFS4ERR_XDEV;
      return res_LINK4.status;
    }

  /* If name is empty, return EINVAL */
  if(arg_LINK4.newname.utf8string_len == 0)
    {
      res_LINK4.status = NFS4ERR_INVAL;
      return res_LINK4.status;
    }

  /* Check for name to long */
  if(arg_LINK4.newname.utf8string_len > FSAL_MAX_NAME_LEN)
    {
      res_LINK4.status = NFS4ERR_NAMETOOLONG;
      return res_LINK4.status;
    }

  /* Convert the UFT8 objname to a regular string */
  if((cache_status =
      cache_inode_error_convert(FSAL_buffdesc2name
                                ((fsal_buffdesc_t *) & arg_LINK4.newname,
                                 &newname))) != CACHE_INODE_SUCCESS)
    {
      res_LINK4.status = nfs4_Errno(cache_status);
      return res_LINK4.status;
    }

  /* Sanity check: never create a link named '.' or '..' */
  if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT)
     || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      res_LINK4.status = NFS4ERR_BADNAME;
      return res_LINK4.status;
    }

  /* get info from compound data */
  dir_pentry = data->current_entry;

  /* Destination FH (the currentFH) must be a directory */
  if(data->current_filetype != DIRECTORY)
    {
      res_LINK4.status = NFS4ERR_NOTDIR;
      return res_LINK4.status;
    }

  /* Target object (the savedFH) must not be a directory */
  if(data->saved_filetype == DIRECTORY)
    {
      res_LINK4.status = NFS4ERR_ISDIR;
      return res_LINK4.status;
    }

  /* We have to keep track of the 'change' file attribute for reply structure */
  if((cache_status = cache_inode_getattr(dir_pentry,
                                         &attr,
                                         data->pcontext,
                                         &cache_status)) != CACHE_INODE_SUCCESS)
    {
      res_LINK4.status = nfs4_Errno(cache_status);
      return res_LINK4.status;
    }
  res_LINK4.LINK4res_u.resok4.cinfo.before
       = cache_inode_get_changeid4(dir_pentry);

  /* Convert savedFH into a vnode */
  file_pentry = data->saved_entry;

  /* make the link */
  if(cache_inode_link(file_pentry,
                      dir_pentry,
                      &newname,
                      &attr,
                      data->pcontext, &cache_status) != CACHE_INODE_SUCCESS)
    {
      res_LINK4.status = nfs4_Errno(cache_status);
      return res_LINK4.status;
    }

  res_LINK4.LINK4res_u.resok4.cinfo.after
       = cache_inode_get_changeid4(dir_pentry);
  res_LINK4.LINK4res_u.resok4.cinfo.atomic = FALSE;

  res_LINK4.status = NFS4_OK;
  return NFS4_OK;
}                               /* nfs4_op_link */
Beispiel #28
0
/**
 * @brief The NFS4_OP_GETXATTR operation.
 *
 * This functions handles the NFS4_OP_GETXATTR 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_getxattr(struct nfs_argop4 *op, compound_data_t *data,
		     struct nfs_resop4 *resp)
{
	GETXATTR4args * const arg_GETXATTR4 = &op->nfs_argop4_u.opgetxattr;
	GETXATTR4res * const res_GETXATTR4 = &resp->nfs_resop4_u.opgetxattr;
	xattrvalue4 gr_value;
	fsal_status_t fsal_status;
	struct fsal_obj_handle *obj_handle = data->current_obj;

	resp->resop = NFS4_OP_GETXATTR;
	res_GETXATTR4->status = NFS4_OK;

	LogDebug(COMPONENT_NFS_V4,
		 "GetXattr len %d name: %s",
		 arg_GETXATTR4->ga_name.utf8string_len,
		 arg_GETXATTR4->ga_name.utf8string_val);

	res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_len = 0;
	res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_val = NULL;

	gr_value.utf8string_len = XATTR_VALUE_SIZE;
	gr_value.utf8string_val = gsh_malloc(gr_value.utf8string_len);

	/* Do basic checks on a filehandle */
	res_GETXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false);

	if (res_GETXATTR4->status != NFS4_OK)
		return res_GETXATTR4->status;

	fsal_status = obj_handle->obj_ops->getxattrs(obj_handle,
						    &arg_GETXATTR4->ga_name,
						    &gr_value);
	if (FSAL_IS_ERROR(fsal_status)) {
		if (fsal_status.major == ERR_FSAL_TOOSMALL) {
			LogDebug(COMPONENT_NFS_V4,
				 "FSAL buffer len %d too small",
				  XATTR_VALUE_SIZE);
			/* Get size of xattr value  */
			gsh_free(gr_value.utf8string_val);
			gr_value.utf8string_len = 0;
			gr_value.utf8string_val = NULL;
			fsal_status = obj_handle->obj_ops->getxattrs(obj_handle,
						    &arg_GETXATTR4->ga_name,
						    &gr_value);
			if (FSAL_IS_ERROR(fsal_status))
				return res_GETXATTR4->status = nfs4_Errno_state(
					state_error_convert(fsal_status));
			LogDebug(COMPONENT_NFS_V4,
				 "FSAL buffer new len %d",
				  gr_value.utf8string_len);
			/* Try again with a bigger buffer */
			gr_value.utf8string_val = gsh_malloc(
						      gr_value.utf8string_len);
			fsal_status = obj_handle->obj_ops->getxattrs(obj_handle,
						    &arg_GETXATTR4->ga_name,
						    &gr_value);
			if (FSAL_IS_ERROR(fsal_status))
				return res_GETXATTR4->status = nfs4_Errno_state(
					state_error_convert(fsal_status));
		} else
			return res_GETXATTR4->status = nfs4_Errno_state(
					state_error_convert(fsal_status));
	}
	res_GETXATTR4->status = NFS4_OK;
	res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_len =
							gr_value.utf8string_len;
	res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_val =
							gr_value.utf8string_val;
	return res_GETXATTR4->status;
}
/**
 *
 * nfs4_op_getattr: Gets attributes for an entry in the FSAL.
 *
 * Gets attributes for an entry in the FSAL.
 *
 * @param op    [IN]    pointer to nfs4_op arguments
 * @param data  [INOUT] Pointer to the compound request's data
 * @param resp  [IN]    Pointer to nfs4_op results
 *
 * @return NFS4_OK
 *
 */
int nfs4_op_getattr(struct nfs_argop4 *op,
                    compound_data_t * data, struct nfs_resop4 *resp)
{
  char __attribute__ ((__unused__)) funcname[] = "nfs4_op_getattr";

  fsal_attrib_list_t   attr;
  cache_inode_status_t cache_status;

  /* This is a NFS4_OP_GETTAR */
  resp->resop = NFS4_OP_GETATTR;
  res_GETATTR4.status = NFS4_OK;

  /* Do basic checks on a filehandle */
  res_GETATTR4.status = nfs4_sanity_check_FH(data, 0LL);
  if(res_GETATTR4.status != NFS4_OK)
    return res_GETATTR4.status;

  /* Pseudo Fs management */
  if(nfs4_Is_Fh_Pseudo(&(data->currentFH)))
    return nfs4_op_getattr_pseudo(op, data, resp);

  if (nfs_export_check_security(data->reqp, data->pexport) == FALSE)
    {
      res_GETATTR4.status = NFS4ERR_PERM;
      return res_GETATTR4.status;
    }

  /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */
  if(nfs4_Is_Fh_Xattr(&(data->currentFH)))
    return nfs4_op_getattr_xattr(op, data, resp);

  if(isFullDebug(COMPONENT_NFS_V4))
    {
      char str[LEN_FH_STR];
      sprint_fhandle4(str, &data->currentFH);
      LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_GETATTR: Current FH %s", str);
    }

  /* Sanity check: if no attributes are wanted, nothing is to be done.
   * In this case NFS4_OK is to be returned */
  if(arg_GETATTR4.attr_request.bitmap4_len == 0)
    {
      res_GETATTR4.status = NFS4_OK;
      return res_GETATTR4.status;
    }

  /* Get only attributes that are allowed to be read */
  if(!nfs4_Fattr_Check_Access_Bitmap(&arg_GETATTR4.attr_request, FATTR4_ATTR_READ))
    {
      res_GETATTR4.status = NFS4ERR_INVAL;
      return res_GETATTR4.status;
    }

  if( !nfs4_bitmap4_Remove_Unsupported( &arg_GETATTR4.attr_request ) )
    {
      res_GETATTR4.status = NFS4ERR_SERVERFAULT ;
      return res_GETATTR4.status;
    }


  /*
   * Get attributes.
   */
  if(cache_inode_getattr(data->current_entry,
                         &attr,
                         data->pcontext, &cache_status) == CACHE_INODE_SUCCESS)
    {
      if(nfs4_FSALattr_To_Fattr(data->pexport,
                                &attr,
                                &(res_GETATTR4.GETATTR4res_u.resok4.obj_attributes),
                                data,
                                &(data->currentFH), &(arg_GETATTR4.attr_request)) != 0)
        res_GETATTR4.status = NFS4ERR_SERVERFAULT;
      else
        res_GETATTR4.status = NFS4_OK;

      return res_GETATTR4.status;
    }
  res_GETATTR4.status = nfs4_Errno(cache_status);

  return res_GETATTR4.status;
}                               /* nfs4_op_getattr */
Beispiel #30
0
int nfs4_op_remove(struct nfs_argop4 *op, compound_data_t *data,
		   struct nfs_resop4 *resp)
{
	REMOVE4args * const arg_REMOVE4 = &op->nfs_argop4_u.opremove;
	REMOVE4res * const res_REMOVE4 = &resp->nfs_resop4_u.opremove;
	cache_entry_t *parent_entry = NULL;
	char *name = NULL;
	cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;

	resp->resop = NFS4_OP_REMOVE;
	res_REMOVE4->status = NFS4_OK;

	/* Do basic checks on a filehandle
	 * Delete arg_REMOVE4.target in directory pointed by currentFH
	 * Make sure the currentFH is pointed a directory
	 */
	res_REMOVE4->status = nfs4_sanity_check_FH(data, DIRECTORY, false);

	if (res_REMOVE4->status != NFS4_OK)
		goto out;

	/* Validate and convert the UFT8 target to a regular string */
	res_REMOVE4->status =
	    nfs4_utf8string2dynamic(&arg_REMOVE4->target, UTF8_SCAN_ALL, &name);

	if (res_REMOVE4->status != NFS4_OK)
		goto out;

	if (nfs_in_grace()) {
		res_REMOVE4->status = NFS4ERR_GRACE;
		goto out;
	}

	/* Get the parent entry (aka the current one in the compound data) */
	parent_entry = data->current_entry;

	/* We have to keep track of the 'change' file attribute
	 * for reply structure
	 */
	memset(&res_REMOVE4->REMOVE4res_u.resok4.cinfo.before,
	       0,
	       sizeof(changeid4));

	res_REMOVE4->REMOVE4res_u.resok4.cinfo.before =
	    cache_inode_get_changeid4(parent_entry, data->req_ctx);

	cache_status = cache_inode_remove(parent_entry, name, data->req_ctx);

	if (cache_status != CACHE_INODE_SUCCESS) {
		res_REMOVE4->status = nfs4_Errno(cache_status);
		goto out;
	}

	res_REMOVE4->REMOVE4res_u.resok4.cinfo.after =
	    cache_inode_get_changeid4(parent_entry, data->req_ctx);

	/* Operation was not atomic .... */
	res_REMOVE4->REMOVE4res_u.resok4.cinfo.atomic = FALSE;

	/* If you reach this point, everything was ok */

	res_REMOVE4->status = NFS4_OK;

 out:

	if (name)
		gsh_free(name);

	return res_REMOVE4->status;
}				/* nfs4_op_remove */