示例#1
0
static int nfs4_mds_putfh(compound_data_t *data)
{
	struct file_handle_v4 *v4_handle =
		(struct file_handle_v4 *)data->currentFH.nfs_fh4_val;
	struct gsh_export *exporting;
	struct fsal_export *export;
	struct gsh_buffdesc fh_desc;
	struct fsal_obj_handle *new_hdl;
	fsal_status_t fsal_status = { 0, 0 };
	bool changed = true;

	LogFullDebug(COMPONENT_FILEHANDLE,
		     "NFS4 Handle flags 0x%X export id %d",
		v4_handle->fhflags1, ntohs(v4_handle->id.exports));
	LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 FSAL Handle %s",
			   LEN_FH_STR, v4_handle->fsopaque, v4_handle->fs_len);

	/* Find any existing export by the "id" from the handle,
	 * before releasing the old export (to prevent thrashing).
	 */
	exporting = get_gsh_export(ntohs(v4_handle->id.exports));
	if (exporting == NULL) {
		LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT,
			   "NFS4 Request from client (%s) has invalid export identifier %d",
			   op_ctx->client
				?  op_ctx->client->hostaddr_str
				: "unknown",
			   ntohs(v4_handle->id.exports));

		return NFS4ERR_STALE;
	}

	/* If old CurrentFH had a related export, release reference. */
	if (op_ctx->ctx_export != NULL) {
		changed = ntohs(v4_handle->id.exports) !=
				 op_ctx->ctx_export->export_id;
		put_gsh_export(op_ctx->ctx_export);
	}

	/* If old CurrentFH had a related server, release reference. */
	if (op_ctx->fsal_pnfs_ds != NULL) {
		pnfs_ds_put(op_ctx->fsal_pnfs_ds);
		op_ctx->fsal_pnfs_ds = NULL;
	}

	/* Clear out current entry for now */
	set_current_entry(data, NULL);

	/* update _ctx fields needed by nfs4_export_check_access */
	op_ctx->ctx_export = exporting;
	op_ctx->fsal_export = export = exporting->fsal_export;

	if (changed) {
		int status;

		status = nfs4_export_check_access(data->req);
		if (status != NFS4_OK) {
			LogFullDebug(COMPONENT_FILEHANDLE,
				     "Export check access failed %s",
				     nfsstat4_to_str(status));
			return status;
		}
	}

	fh_desc.len = v4_handle->fs_len;
	fh_desc.addr = &v4_handle->fsopaque;

	/* adjust the handle opaque into a cache key */
	fsal_status = export->exp_ops.wire_to_host(export,
						   FSAL_DIGEST_NFSV4,
						   &fh_desc,
						   v4_handle->fhflags1);
	if (FSAL_IS_ERROR(fsal_status)) {
		LogFullDebug(COMPONENT_FILEHANDLE,
			     "wire_to_host failed %s",
			     msg_fsal_err(fsal_status.major));
		return nfs4_Errno_status(fsal_status);
	}

	fsal_status = export->exp_ops.create_handle(export, &fh_desc,
						    &new_hdl, NULL);
	if (FSAL_IS_ERROR(fsal_status)) {
		LogDebug(COMPONENT_FILEHANDLE,
			 "could not get create_handle object error %s",
			 msg_fsal_err(fsal_status.major));
		return nfs4_Errno_status(fsal_status);
	}

	/* Set the current entry using the ref from get */
	set_current_entry(data, new_hdl);

	/* Put our ref */
	new_hdl->obj_ops->put_ref(new_hdl);

	LogFullDebug(COMPONENT_FILEHANDLE,
		     "File handle is of type %s(%d)",
		     object_file_type_to_str(data->current_filetype),
		     data->current_filetype);

	return NFS4_OK;
}
示例#2
0
fsal_status_t find_fd(int *fd,
		      struct fsal_obj_handle *obj_hdl,
		      bool bypass,
		      struct state_t *state,
		      fsal_openflags_t openflags,
		      bool *has_lock,
		      bool *need_fsync,
		      bool *closefd,
		      bool open_for_locks)
{
	struct vfs_fsal_obj_handle *myself;
	struct vfs_filesystem *vfs_fs;
	struct vfs_fd temp_fd = {0, -1}, *out_fd = &temp_fd;
	fsal_status_t status = {ERR_FSAL_NO_ERROR, 0};
	int rc, posix_flags;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);
	vfs_fs = myself->obj_handle.fs->private_data;

	fsal2posix_openflags(openflags, &posix_flags);

	/* Handle nom-regular files */
	switch (obj_hdl->type) {
	case SOCKET_FILE:
	case CHARACTER_FILE:
	case BLOCK_FILE:
		rc = vfs_open_by_handle(vfs_fs,
					myself->u.unopenable.dir,
					O_PATH | O_NOACCESS,
					&status.major);
		if (rc < 0) {
			LogDebug(COMPONENT_FSAL,
				 "Failed with %s openflags 0x%08x",
				 strerror(-rc), O_PATH | O_NOACCESS);
			return fsalstat(posix2fsal_error(-rc), -rc);
		}
		*fd = rc;
		*closefd = true;
		return status;

	case REGULAR_FILE:
		status = fsal_find_fd((struct fsal_fd **)&out_fd, obj_hdl,
				      (struct fsal_fd *)&myself->u.file.fd,
				      &myself->u.file.share,
				      bypass, state, openflags,
				      vfs_open_func, vfs_close_func,
				      has_lock, need_fsync,
				      closefd, open_for_locks);

		*fd = out_fd->fd;
		return status;

	case SYMBOLIC_LINK:
		posix_flags |= (O_PATH | O_RDWR | O_NOFOLLOW);
		break;

	case FIFO_FILE:
		posix_flags |= O_NONBLOCK;
		break;

	case DIRECTORY:
		break;

	case NO_FILE_TYPE:
	case EXTENDED_ATTR:
		return fsalstat(posix2fsal_error(EINVAL), EINVAL);
	}

	/* Open file descriptor for non-regular files. */
	rc = vfs_fsal_open(myself, posix_flags, &status.major);

	if (rc < 0) {
		LogDebug(COMPONENT_FSAL,
			 "Failed with %s openflags 0x%08x",
			 strerror(-rc), openflags);
		return fsalstat(posix2fsal_error(-rc), -rc);
	}

	LogFullDebug(COMPONENT_FSAL,
		     "Opened fd=%d for file of type %s",
		     rc, object_file_type_to_str(obj_hdl->type));

	*fd = rc;
	*closefd = true;

	return status;
}
示例#3
0
static void open4_ex(OPEN4args *arg,
		     compound_data_t *data,
		     OPEN4res *res_OPEN4,
		     nfs_client_id_t *clientid,
		     state_owner_t *owner,
		     state_t **file_state,
		     bool *new_state)
{
	/* Parent directory in which to open the file. */
	struct fsal_obj_handle *parent = NULL;
	/* The entry we associated with the desired file before open. */
	struct fsal_obj_handle *file_obj = NULL;
	/* Indicator that file_obj came from lookup. */
	bool looked_up_file_obj = false;
	/* The in_obj to pass to fsal_open2. */
	struct fsal_obj_handle *in_obj = NULL;
	/* The entry open associated with the file. */
	struct fsal_obj_handle *out_obj = NULL;
	fsal_openflags_t openflags = 0;
	fsal_openflags_t old_openflags = 0;
	enum fsal_create_mode createmode = FSAL_NO_CREATE;
	/* The filename to create */
	char *filename = NULL;
	/* The supplied calim type */
	open_claim_type4 claim = arg->claim.claim;
	fsal_verifier_t verifier;
	struct attrlist sattr;
	/* Status for fsal calls */
	fsal_status_t status = {0, 0};
	/* The open state for the file */
	bool state_lock_held = false;

	/* Make sure the attributes are initialized */
	memset(&sattr, 0, sizeof(sattr));

	/* Make sure... */
	*file_state = NULL;
	*new_state = false;

	/* Pre-process the claim type */
	switch (claim) {
	case CLAIM_NULL:
		/* Check parent */
		parent = data->current_obj;
		in_obj = parent;

		/* Parent must be a directory */
		if (parent->type != DIRECTORY) {
			if (parent->type == SYMBOLIC_LINK) {
				res_OPEN4->status = NFS4ERR_SYMLINK;
				goto out;
			} else {
				res_OPEN4->status = NFS4ERR_NOTDIR;
				goto out;
			}
		}

		/* Validate and convert the utf8 filename */
		res_OPEN4->status =
		    nfs4_utf8string2dynamic(&arg->claim.open_claim4_u.file,
					    UTF8_SCAN_ALL, &filename);

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

		/* Set the createmode if appropriate) */
		if (arg->openhow.opentype == OPEN4_CREATE) {
			open4_ex_create_args(arg, data, res_OPEN4, verifier,
					     &createmode, &sattr);

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

		status = fsal_lookup(parent, filename, &file_obj, NULL);

		if (!FSAL_IS_ERROR(status)) {
			/* Check create situations. */
			if (arg->openhow.opentype == OPEN4_CREATE) {
				if (createmode >= FSAL_EXCLUSIVE) {
					/* Could be a replay, need to continue.
					 */
					LogFullDebug(COMPONENT_STATE,
						     "EXCLUSIVE open with existing file %s",
						     filename);
				} else if (createmode == FSAL_GUARDED) {
					/* This will be a failure no matter'
					 * what.
					 */
					looked_up_file_obj = true;
					res_OPEN4->status = NFS4ERR_EXIST;
					goto out;
				} else {
					/* FSAL_UNCHECKED, may be a truncate
					 * and we need to pass in the case
					 * of fsal_reopen2 case.
					 */
					if (FSAL_TEST_MASK(sattr.valid_mask,
							   ATTR_SIZE) &&
					    sattr.filesize == 0) {
						LogFullDebug(COMPONENT_STATE,
							     "Truncate");
						openflags |= FSAL_O_TRUNC;
					}
				}
			}

			/* We found the file by lookup, discard the filename
			 * and remember that we found the entry by lookup.
			 */
			looked_up_file_obj = true;
			gsh_free(filename);
			filename = NULL;
		} else if (status.major != ERR_FSAL_NOENT ||
			   arg->openhow.opentype != OPEN4_CREATE) {
			/* A real error occurred */
			res_OPEN4->status = nfs4_Errno_status(status);
			goto out;
		}

		break;

		/* Both of these just use the current filehandle. */
	case CLAIM_PREVIOUS:
		owner->so_owner.so_nfs4_owner.so_confirmed = true;
		if (!nfs4_check_deleg_reclaim(clientid, &data->currentFH)) {
			/* It must have been revoked. Can't reclaim.*/
			LogInfo(COMPONENT_NFS_V4, "Can't reclaim delegation");
			res_OPEN4->status = NFS4ERR_RECLAIM_BAD;
			goto out;
		}
		openflags |= FSAL_O_RECLAIM;
		file_obj = data->current_obj;
		break;

	case CLAIM_FH:
		file_obj = data->current_obj;
		break;

	case CLAIM_DELEGATE_PREV:
		/* FIXME: Remove this when we have full support
		 * for CLAIM_DELEGATE_PREV and delegpurge operations
		 */
		res_OPEN4->status = NFS4ERR_NOTSUPP;
		goto out;

	case CLAIM_DELEGATE_CUR:
		res_OPEN4->status = open4_claim_deleg(arg, data);
		if (res_OPEN4->status != NFS4_OK)
			goto out;
		openflags |= FSAL_O_RECLAIM;
		file_obj = data->current_obj;
		break;

	default:
		LogFatal(COMPONENT_STATE,
			 "Programming error.  Invalid claim after check.");
	}

	if ((arg->share_access & OPEN4_SHARE_ACCESS_READ) != 0)
		openflags |= FSAL_O_READ;

	if ((arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0)
		openflags |= FSAL_O_WRITE;

	if ((arg->share_deny & OPEN4_SHARE_DENY_READ) != 0)
		openflags |= FSAL_O_DENY_READ;

	if ((arg->share_deny & OPEN4_SHARE_DENY_WRITE) != 0)
		openflags |= FSAL_O_DENY_WRITE_MAND;

	/* Check if file_obj a REGULAR_FILE */
	if (file_obj != NULL && file_obj->type != REGULAR_FILE) {
		LogDebug(COMPONENT_NFS_V4,
			 "Wrong file type expected REGULAR_FILE actual %s",
			 object_file_type_to_str(file_obj->type));

		if (file_obj->type == DIRECTORY) {
			res_OPEN4->status = NFS4ERR_ISDIR;
		} else {
			/* All special nodes must return NFS4ERR_SYMLINK for
			 * proper client behavior per this linux-nfs post:
			 * http://marc.info/?l=linux-nfs&m=131342421825436&w=2
			 */
			res_OPEN4->status = NFS4ERR_SYMLINK;
		}

		goto out;
	}

	if (file_obj != NULL) {
		/* Go ahead and take the state lock now. */
		PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock);
		state_lock_held = true;
		in_obj = file_obj;

		/* Check if any existing delegations conflict with this open.
		 * Delegation recalls will be scheduled if there is a conflict.
		 */
		if (state_deleg_conflict(file_obj,
					  (arg->share_access &
					   OPEN4_SHARE_ACCESS_WRITE) != 0)) {
			res_OPEN4->status = NFS4ERR_DELAY;
			goto out;
		}

		/* Check if there is already a state for this entry and owner.
		 */
		*file_state = nfs4_State_Get_Obj(file_obj, owner);

		if (isFullDebug(COMPONENT_STATE) && *file_state != NULL) {
			char str[LOG_BUFF_LEN] = "\0";
			struct display_buffer dspbuf = {sizeof(str), str, str};

			display_stateid(&dspbuf, *file_state);

			LogFullDebug(COMPONENT_STATE,
				     "Found existing state %s",
				     str);
		}

		/* Check if open from another export */
		if (*file_state != NULL &&
		    !state_same_export(*file_state, op_ctx->ctx_export)) {
			LogEvent(COMPONENT_STATE,
				 "Lock Owner Export Conflict, Lock held for export %"
				 PRIu16" request for export %"PRIu16,
				 state_export_id(*file_state),
				 op_ctx->ctx_export->export_id);
			res_OPEN4->status = NFS4ERR_INVAL;
			goto out;
		}
	}

	/* If that did not succeed, allocate a state from the FSAL. */
	if (*file_state == NULL) {
		*file_state = op_ctx->fsal_export->exp_ops.alloc_state(
							op_ctx->fsal_export,
							STATE_TYPE_SHARE,
							NULL);

		/* Remember we allocated a new state */
		*new_state = true;

		/* We are ready to perform the open (with possible create).
		 * in_obj has been set to the file itself or the parent.
		 * filename is NULL if in_obj is the file itself.
		 *
		 * Permission check has been done on directory if appropriate,
		 * otherwise fsal_open2 will do a directory permission
		 * check.
		 *
		 * fsal_open2 handles the permission check on the file
		 * itself and also handles all the share reservation stuff.
		 *
		 * fsal_open2 returns with a ref on out_obj, which should be
		 * passed to the state.
		 */
		LogFullDebug(COMPONENT_STATE,
			     "Calling open2 for %s", filename);

		status = fsal_open2(in_obj,
				    *file_state,
				    openflags,
				    createmode,
				    filename,
				    &sattr,
				    verifier,
				    &out_obj,
				    NULL);

		if (FSAL_IS_ERROR(status)) {
			res_OPEN4->status = nfs4_Errno_status(status);
			goto out;
		}
	} else if (createmode >= FSAL_EXCLUSIVE) {
		/* We have an EXCLUSIVE create with an existing
		 * state. We still need to verify it, but no need
		 * to call reopen2.
		 */
		LogFullDebug(COMPONENT_STATE, "Calling verify2 ");

		status = fsal_verify2(file_obj, verifier);

		if (FSAL_IS_ERROR(status)) {
			res_OPEN4->status = nfs4_Errno_status(status);
			goto out;
		}

		/* We need an extra reference below. */
		file_obj->obj_ops->get_ref(file_obj);
	} else {
		old_openflags =
			file_obj->obj_ops->status2(file_obj, *file_state);

		/* Open upgrade */
		LogFullDebug(COMPONENT_STATE, "Calling reopen2");

		status = fsal_reopen2(file_obj, *file_state,
				      openflags | old_openflags,
				      false);

		if (FSAL_IS_ERROR(status)) {
			res_OPEN4->status = nfs4_Errno_status(status);
			goto out;
		}

		/* We need an extra reference below. */
		file_obj->obj_ops->get_ref(file_obj);
	}

	if (file_obj == NULL) {
		/* We have a new cache inode entry, take the state lock. */
		file_obj = out_obj;
		PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock);
		state_lock_held = true;
	}

	/* Now the state_lock is held for sure and we have an extra LRU
	 * reference to file_obj, which is the opened file.
	 */

	if (*new_state) {
		/* The state data to be added */
		union state_data candidate_data;
		/* Tracking data for the open state */
		struct state_refer refer, *p_refer = NULL;
		state_status_t state_status;

		candidate_data.share.share_access =
		    arg->share_access & OPEN4_SHARE_ACCESS_BOTH;
		candidate_data.share.share_deny = arg->share_deny;
		candidate_data.share.share_access_prev =
			(1 << candidate_data.share.share_access);
		candidate_data.share.share_deny_prev =
			(1 << candidate_data.share.share_deny);

		LogFullDebug(COMPONENT_STATE,
			     "Creating new state access=%x deny=%x access_prev=%x deny_prev=%x",
			     candidate_data.share.share_access,
			     candidate_data.share.share_deny,
			     candidate_data.share.share_access_prev,
			     candidate_data.share.share_deny_prev);

		/* Record the sequence info */
		if (data->minorversion > 0) {
			memcpy(refer.session,
			       data->session->session_id,
			       sizeof(sessionid4));
			refer.sequence = data->sequence;
			refer.slot = data->slot;
			p_refer = &refer;
		}

		/* We need to register this state now. */
		state_status = state_add_impl(file_obj,
					      STATE_TYPE_SHARE,
					      &candidate_data,
					      owner,
					      file_state,
					      p_refer);

		if (state_status != STATE_SUCCESS) {
			/* state_add_impl failure closed and freed state.
			 * file_state will also be NULL at this point. Also
			 * release the ref on file_obj, since the state add
			 * failed.
			 */
			file_obj->obj_ops->put_ref(file_obj);
			res_OPEN4->status = nfs4_Errno_state(state_status);
			*new_state = false;
			goto out;
		}

		glist_init(&(*file_state)->state_data.share.share_lockstates);
	}

	res_OPEN4->status = open4_create_fh(data, file_obj, true);

	if (res_OPEN4->status != NFS4_OK) {
		if (*new_state) {
			/* state_del_locked will close the file. */
			state_del_locked(*file_state);
			*file_state = NULL;
			*new_state = false;
		} else {
			/*Do an open downgrade to the old open flags */
			status = file_obj->obj_ops->reopen2(file_obj,
							   *file_state,
							   old_openflags);
			if (FSAL_IS_ERROR(status)) {
				LogCrit(COMPONENT_NFS_V4,
					"Failed to allocate handle, reopen2 failed with %s",
					fsal_err_txt(status));
			}

			/* Need to release the state_lock before the put_ref
			 * call.
			 */
			PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock);
			state_lock_held = false;

			/* Release the extra LRU reference on file_obj. */
			file_obj->obj_ops->put_ref(file_obj);
			goto out;
		}
	}

	/* Since open4_create_fh succeeded the LRU reference to file_obj was
	 * consumed by data->current_obj.
	 */

	if (!(*new_state)) {
		LogFullDebug(COMPONENT_STATE,
			     "Open upgrade old access=%x deny=%x access_prev=%x deny_prev=%x",
			     (*file_state)->state_data.share.share_access,
			     (*file_state)->state_data.share.share_deny,
			     (*file_state)->state_data.share.share_access_prev,
			     (*file_state)->state_data.share.share_deny_prev);

		LogFullDebug(COMPONENT_STATE,
			     "Open upgrade to access=%x deny=%x",
			     arg->share_access,
			     arg->share_deny);

		/* Update share_access and share_deny */
		(*file_state)->state_data.share.share_access |=
			arg->share_access & OPEN4_SHARE_ACCESS_BOTH;

		(*file_state)->state_data.share.share_deny |=
			arg->share_deny;

		/* Update share_access_prev and share_deny_prev */
		(*file_state)->state_data.share.share_access_prev |=
			(1 << (arg->share_access & OPEN4_SHARE_ACCESS_BOTH));

		(*file_state)->state_data.share.share_deny_prev |=
			(1 << arg->share_deny);

		LogFullDebug(COMPONENT_STATE,
			     "Open upgrade new access=%x deny=%x access_prev=%x deny_prev=%x",
			     (*file_state)->state_data.share.share_access,
			     (*file_state)->state_data.share.share_deny,
			     (*file_state)->state_data.share.share_access_prev,
			     (*file_state)->state_data.share.share_deny_prev);
	}

	do_delegation(arg, res_OPEN4, data, owner, *file_state, clientid);
 out:

	/* Release the attributes (may release an inherited ACL) */
	fsal_release_attrs(&sattr);

	if (state_lock_held)
		PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock);

	if (filename)
		gsh_free(filename);

	if (res_OPEN4->status != NFS4_OK) {
		/* Cleanup state on error */
		if (*new_state)
			(*file_state)
				->state_exp->exp_ops.free_state(
					(*file_state)->state_exp, *file_state);
		else if (*file_state != NULL)
			dec_state_t_ref(*file_state);
		*file_state = NULL;
	}

	if (looked_up_file_obj) {
		/* We got file_obj via lookup, we need to unref it. */
		file_obj->obj_ops->put_ref(file_obj);
	}
}