Beispiel #1
0
/**
 *
 * @brief The NFSPROC3_LINK
 *
 * The NFSPROC3_LINK.
 *
 * @param[in]  arg     NFS argument union
 * @param[in]  req     SVC request related to this call
 * @param[out] res     Structure to contain the result of the call
 *
 * @retval NFS_REQ_OK if successful
 * @retval NFS_REQ_DROP if failed but retryable
 * @retval NFS_REQ_FAILED if failed and not retryable
 *
 */
int nfs3_link(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res)
{
	struct LINK3args *l3_arg = &arg->arg_link3;
	struct LINK3res *l3_res = &res->res_link3;
	const char *link_name = l3_arg->link.name;
	struct fsal_obj_handle *target_obj = NULL;
	struct fsal_obj_handle *parent_obj = NULL;
	pre_op_attr pre_parent = {0};
	fsal_status_t fsal_status = {0, 0};
	int rc = NFS_REQ_OK;

	if (isDebug(COMPONENT_NFSPROTO)) {
		char strto[LEN_FH_STR], strfrom[LEN_FH_STR];

		nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->file,
				 NULL, strfrom);
		nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->link.dir,
				 NULL, strto);

		LogDebug(COMPONENT_NFSPROTO,
			 "REQUEST PROCESSING: Calling nfs3_link handle: %s to handle: %s name: %s",
			 strfrom, strto, link_name);
	}

	/* to avoid setting it on each error case */
	l3_res->LINK3res_u.resfail.file_attributes.attributes_follow = FALSE;
	l3_res->LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE;
	l3_res->LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE;

	l3_res->status = nfs3_verify_exportid(l3_arg, req);
	if (l3_res->status != NFS3_OK)
		return rc;

	parent_obj = nfs3_FhandleToCache(&l3_arg->link.dir, &l3_res->status,
					 &rc);
	if (parent_obj == NULL)
		return rc;  /* Status and rc are set by nfs3_FhandleToCache */

	nfs_SetPreOpAttr(parent_obj, &pre_parent);

	target_obj = nfs3_FhandleToCache(&l3_arg->file, &l3_res->status, &rc);
	if (target_obj == NULL) {
		parent_obj->obj_ops.put_ref(parent_obj);
		return rc;  /* Status and rc are set by nfs3_FhandleToCache */
	}

	if (parent_obj->type != DIRECTORY) {
		l3_res->status = NFS3ERR_NOTDIR;
		goto out;
	}

	if (link_name == NULL || *link_name == '\0') {
		l3_res->status = NFS3ERR_INVAL;
		goto out;
	}

	fsal_status = fsal_link(target_obj, parent_obj, link_name);

	if (FSAL_IS_ERROR(fsal_status)) {
		/* If we are here, there was an error */
		LogFullDebug(COMPONENT_NFSPROTO,
			     "failed link: fsal_status=%s",
			     fsal_err_txt(fsal_status));

		if (nfs_RetryableError(fsal_status.major)) {
			rc = NFS_REQ_DROP;
			goto out;
		}

		l3_res->status = nfs3_Errno_status(fsal_status);
		nfs_SetPostOpAttr(target_obj,
				  &l3_res->LINK3res_u.resfail.file_attributes,
				  NULL);

		nfs_SetWccData(&pre_parent, parent_obj,
			       &l3_res->LINK3res_u.resfail.linkdir_wcc);
	} else {
		nfs_SetPostOpAttr(target_obj,
				  &l3_res->LINK3res_u.resok.file_attributes,
				  NULL);

		nfs_SetWccData(&pre_parent, parent_obj,
			       &l3_res->LINK3res_u.resok.linkdir_wcc);
		l3_res->status = NFS3_OK;
	}

 out:
	/* return references */
	target_obj->obj_ops.put_ref(target_obj);
	parent_obj->obj_ops.put_ref(parent_obj);

	return rc;
}				/* nfs3_link */
Beispiel #2
0
/**
 * @brief Set attributes on an object
 *
 * This function sets attributes on an object.  Which attributes are
 * set is determined by attrib_set->mask. The FSAL must manage bypass
 * or not of share reservations, and a state may be passed.
 *
 * @param[in] obj_hdl    File on which to operate
 * @param[in] state      state_t to use for this operation
 * @param[in] attrib_set Attributes to set
 *
 * @return FSAL status.
 */
fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl,
			   bool bypass,
			   struct state_t *state,
			   struct attrlist *attrib_set)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status = {0, 0};
	int retval = 0;
	fsal_openflags_t openflags = FSAL_O_ANY;
	bool has_lock = false;
	bool need_fsync = false;
	bool closefd = false;
	int my_fd;
	const char *func;

	/* apply umask, if mode attribute is to be changed */
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE))
		attrib_set->mode &=
		    ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export);

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name,
			 obj_hdl->fs->fsal != NULL
				? obj_hdl->fs->fsal->name
				: "(none)");
		return fsalstat(posix2fsal_error(EXDEV), EXDEV);
	}

#ifdef ENABLE_VFS_DEBUG_ACL
#ifdef ENABLE_RFC_ACL
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE) &&
	    !FSAL_TEST_MASK(attrib_set->mask, ATTR_ACL)) {
		/* Set ACL from MODE */
		struct attrlist attrs;

		fsal_prepare_attrs(&attrs, ATTR_ACL);

		status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs);

		if (FSAL_IS_ERROR(status))
			return status;

		status = fsal_mode_to_acl(attrib_set, attrs.acl);

		/* Done with the attrs */
		fsal_release_attrs(&attrs);
	} else {
		/* If ATTR_ACL is set, mode needs to be adjusted no matter what.
		 * See 7530 s 6.4.1.3 */
		if (!FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE))
			attrib_set->mode = myself->mode;
		status = fsal_acl_to_mode(attrib_set);
	}

	if (FSAL_IS_ERROR(status))
		return status;
#endif /* ENABLE_RFC_ACL */
#endif

	/* This is yet another "you can't get there from here".  If this object
	 * is a socket (AF_UNIX), an fd on the socket s useless _period_.
	 * If it is for a symlink, without O_PATH, you will get an ELOOP error
	 * and (f)chmod doesn't work for a symlink anyway - not that it matters
	 * because access checking is not done on the symlink but the final
	 * target.
	 * AF_UNIX sockets are also ozone material.  If the socket is already
	 * active listeners et al, you can manipulate the mode etc.  If it is
	 * just sitting there as in you made it with a mknod.
	 * (one of those leaky abstractions...)
	 * or the listener forgot to unlink it, it is lame duck.
	 */

	/* Test if size is being set, make sure file is regular and if so,
	 * require a read/write file descriptor.
	 */
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) {
		if (obj_hdl->type != REGULAR_FILE) {
			LogFullDebug(COMPONENT_FSAL,
				     "Setting size on non-regular file");
			return fsalstat(ERR_FSAL_INVAL, EINVAL);
		}
		openflags = FSAL_O_RDWR;
	}

	/* Get a usable file descriptor. Share conflict is only possible if
	 * size is being set.
	 */
	status = find_fd(&my_fd, obj_hdl, bypass, state, openflags,
			 &has_lock, &need_fsync, &closefd, false);

	if (FSAL_IS_ERROR(status)) {
		if (obj_hdl->type == SYMBOLIC_LINK &&
		    status.major == ERR_FSAL_PERM) {
			/* You cannot open_by_handle (XFS) a symlink and it
			 * throws an EPERM error for it.  open_by_handle_at
			 * does not throw that error for symlinks so we play a
			 * game here.  Since there is not much we can do with
			 * symlinks anyway, say that we did it
			 * but don't actually do anything.
			 * If you *really* want to tweek things
			 * like owners, get a modern linux kernel...
			 */
			status = fsalstat(ERR_FSAL_NO_ERROR, 0);
		}
		LogFullDebug(COMPONENT_FSAL,
			     "find_fd status=%s",
			     fsal_err_txt(status));
		goto out;
	}

	/** TRUNCATE **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) {
		retval = ftruncate(my_fd, attrib_set->filesize);
		if (retval != 0) {
			/** @todo FSF: is this still necessary?
			 *
			 * XXX ESXi volume creation pattern reliably
			 * reached this point in the past, however now that we
			 * only use the already open file descriptor if it is
			 * open read/write, this may no longer fail.
			 * If there is some other error from ftruncate, then
			 * we will needlessly retry, but without more detail
			 * of the original failure, we can't be sure.
			 * Fortunately permission checking is done by
			 * Ganesha before calling here, so we won't get an
			 * EACCES since this call is done as root. We could
			 * get EFBIG, EPERM, or EINVAL.
			 */
			/** @todo FSF: re-open if we really still need this
			 */

			retval = ftruncate(my_fd, attrib_set->filesize);
			if (retval != 0) {
				func = "truncate";
				goto fileerr;
			}
		}
	}

	/** CHMOD **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) {
		/* The POSIX chmod call doesn't affect the symlink object, but
		 * the entry it points to. So we must ignore it.
		 */
		if (obj_hdl->type != SYMBOLIC_LINK) {
			if (vfs_unopenable_type(obj_hdl->type))
				retval = fchmodat(
					my_fd,
					myself->u.unopenable.name,
					fsal2unix_mode(attrib_set->mode),
					0);
			else
				retval = fchmod(
					my_fd,
					fsal2unix_mode(attrib_set->mode));

			if (retval != 0) {
				func = "chmod";
				goto fileerr;
			}
		}
	}

	/**  CHOWN  **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER | ATTR_GROUP)) {
		uid_t user = FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER)
		    ? (int)attrib_set->owner : -1;
		gid_t group = FSAL_TEST_MASK(attrib_set->mask, ATTR_GROUP)
		    ? (int)attrib_set->group : -1;

		if (vfs_unopenable_type(obj_hdl->type))
			retval = fchownat(my_fd, myself->u.unopenable.name,
					  user, group, AT_SYMLINK_NOFOLLOW);
		else if (obj_hdl->type == SYMBOLIC_LINK)
			retval = fchownat(my_fd, "", user, group,
					  AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);
		else
			retval = fchown(my_fd, user, group);

		if (retval) {
			func = "chown";
			goto fileerr;
		}
	}

	/**  UTIME  **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTRS_SET_TIME)) {
		struct timespec timebuf[2];

		if (obj_hdl->type == SYMBOLIC_LINK)
			goto out; /* Setting time on symlinks is illegal */
		/* Atime */
		if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME_SERVER)) {
			timebuf[0].tv_sec = 0;
			timebuf[0].tv_nsec = UTIME_NOW;
		} else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME)) {
			timebuf[0] = attrib_set->atime;
		} else {
			timebuf[0].tv_sec = 0;
			timebuf[0].tv_nsec = UTIME_OMIT;
		}

		/* Mtime */
		if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME_SERVER)) {
			timebuf[1].tv_sec = 0;
			timebuf[1].tv_nsec = UTIME_NOW;
		} else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME)) {
			timebuf[1] = attrib_set->mtime;
		} else {
			timebuf[1].tv_sec = 0;
			timebuf[1].tv_nsec = UTIME_OMIT;
		}
		if (vfs_unopenable_type(obj_hdl->type))
			retval = vfs_utimesat(my_fd, myself->u.unopenable.name,
					      timebuf, AT_SYMLINK_NOFOLLOW);
		else
			retval = vfs_utimes(my_fd, timebuf);
		if (retval != 0) {
			func = "utimes";
			goto fileerr;
		}
	}

	/** SUBFSAL **/
	if (myself->sub_ops && myself->sub_ops->setattrs) {
		status = myself->sub_ops->setattrs(
					myself,
					my_fd,
					attrib_set->mask, attrib_set);
		if (FSAL_IS_ERROR(status))
			goto out;
	}

	errno = 0;

 fileerr:

	retval = errno;

	if (retval != 0) {
		LogDebug(COMPONENT_FSAL,
			 "%s returned %s",
			 func, strerror(retval));
	}

	status = fsalstat(posix2fsal_error(retval), retval);

 out:

	if (closefd)
		close(my_fd);

	if (has_lock)
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}
Beispiel #3
0
/**
 * @brief Up Thread
 *
 * @param Arg reference to void
 *
 */
void *GPFSFSAL_UP_Thread(void *Arg)
{
	struct gpfs_filesystem *gpfs_fs = Arg;
	struct fsal_up_vector *event_func;
	char thr_name[16];
	int rc = 0;
	struct pnfs_deviceid devid;
	struct stat buf;
	struct glock fl;
	struct callback_arg callback;
	struct gpfs_file_handle handle;
	int reason = 0;
	int flags = 0;
	unsigned int *fhP;
	int retry = 0;
	struct gsh_buffdesc key;
	uint32_t expire_time_attr = 0;
	uint32_t upflags;
	int errsv = 0;
	fsal_status_t fsal_status = {0,};

#ifdef _VALGRIND_MEMCHECK
		memset(&handle, 0, sizeof(handle));
		memset(&buf, 0, sizeof(buf));
		memset(&fl, 0, sizeof(fl));
		memset(&devid, 0, sizeof(devid));
#endif

	snprintf(thr_name, sizeof(thr_name),
		 "fsal_up_%"PRIu64".%"PRIu64,
		 gpfs_fs->fs->dev.major, gpfs_fs->fs->dev.minor);
	SetNameFunction(thr_name);

	LogFullDebug(COMPONENT_FSAL_UP,
		     "Initializing FSAL Callback context for %d.",
		     gpfs_fs->root_fd);

	/* wait for nfs init completion to get general_fridge
	 * initialized which is needed for processing some upcall events
	 */
	nfs_init_wait();

	/* Start querying for events and processing. */
	while (1) {
		LogFullDebug(COMPONENT_FSAL_UP,
			     "Requesting event from FSAL Callback interface for %d.",
			     gpfs_fs->root_fd);

		handle.handle_size = GPFS_MAX_FH_SIZE;
		handle.handle_key_size = OPENHANDLE_KEY_LEN;
		handle.handle_version = OPENHANDLE_VERSION;

		callback.interface_version =
		    GPFS_INTERFACE_VERSION + GPFS_INTERFACE_SUB_VER;

		callback.mountdirfd = gpfs_fs->root_fd;
		callback.handle = &handle;
		callback.reason = &reason;
		callback.flags = &flags;
		callback.buf = &buf;
		callback.fl = &fl;
		callback.dev_id = &devid;
		callback.expire_attr = &expire_time_attr;

		rc = gpfs_ganesha(OPENHANDLE_INODE_UPDATE, &callback);
		errsv = errno;

		if (rc != 0) {
			rc = -(rc);
			if (rc > GPFS_INTERFACE_VERSION) {
				LogFatal(COMPONENT_FSAL_UP,
					 "Ganesha version %d mismatch GPFS version %d.",
					 callback.interface_version, rc);
				return NULL;
			}

			if (errsv == EINTR)
				continue;

			LogCrit(COMPONENT_FSAL_UP,
				"OPENHANDLE_INODE_UPDATE failed for %d. rc %d, errno %d (%s) reason %d",
				gpfs_fs->root_fd, rc, errsv,
				strerror(errsv), reason);

			/* @todo 1000 retry logic will go away once the
			 * OPENHANDLE_INODE_UPDATE ioctl separates EINTR
			 * and EUNATCH.
			 */
			if (errsv == EUNATCH && ++retry > 1000)
				LogFatal(COMPONENT_FSAL_UP,
					 "GPFS file system %d has gone away.",
					 gpfs_fs->root_fd);

			continue;
		}

		retry = 0;

		/* flags is int, but only the least significant 2 bytes
		 * are valid.  We are getting random bits into the upper
		 * 2 bytes! Workaround this until the kernel module
		 * gets fixed.
		 */
		flags = flags & 0xffff;

		LogDebug(COMPONENT_FSAL_UP,
			 "inode update: rc %d reason %d update ino %"
			 PRId64 " flags:%x",
			 rc, reason, callback.buf->st_ino, flags);

		LogFullDebug(COMPONENT_FSAL_UP,
			     "inode update: flags:%x callback.handle:%p handle size = %u handle_type:%d handle_version:%d key_size = %u handle_fsid=%X.%X f_handle:%p expire: %d",
			     *callback.flags, callback.handle,
			     callback.handle->handle_size,
			     callback.handle->handle_type,
			     callback.handle->handle_version,
			     callback.handle->handle_key_size,
			     callback.handle->handle_fsid[0],
			     callback.handle->handle_fsid[1],
			     callback.handle->f_handle, expire_time_attr);

		callback.handle->handle_version = OPENHANDLE_VERSION;

		fhP = (int *)&(callback.handle->f_handle[0]);
		LogFullDebug(COMPONENT_FSAL_UP,
			     " inode update: handle %08x %08x %08x %08x %08x %08x %08x",
			     fhP[0], fhP[1], fhP[2], fhP[3], fhP[4], fhP[5],
			     fhP[6]);

		/* Here is where we decide what type of event this is
		 * ... open,close,read,...,invalidate? */
		key.addr = &handle;
		key.len = handle.handle_key_size;

		LogDebug(COMPONENT_FSAL_UP, "Received event to process for %d",
			 gpfs_fs->root_fd);

		/* We need valid up_vector while processing some of the
		 * events below. Setup up vector and hold the mutex while
		 * processing the event for the entire duration.
		 */
		PTHREAD_MUTEX_lock(&gpfs_fs->upvector_mutex);
		if (!setup_up_vector(gpfs_fs)) {
			PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex);
			return NULL;
		}
		event_func = gpfs_fs->up_vector;

		switch (reason) {
		case INODE_LOCK_GRANTED:	/* Lock Event */
		case INODE_LOCK_AGAIN:	/* Lock Event */
			{
				LogMidDebug(COMPONENT_FSAL_UP,
					    "%s: owner %p pid %d type %d start %lld len %lld",
					    reason ==
					    INODE_LOCK_GRANTED ?
					    "inode lock granted" :
					    "inode lock again", fl.lock_owner,
					    fl.flock.l_pid, fl.flock.l_type,
					    (long long)fl.flock.l_start,
					    (long long)fl.flock.l_len);

				fsal_lock_param_t lockdesc = {
					.lock_sle_type = FSAL_POSIX_LOCK,
					.lock_type = fl.flock.l_type,
					.lock_start = fl.flock.l_start,
					.lock_length = fl.flock.l_len
				};
				if (reason == INODE_LOCK_AGAIN)
					fsal_status = up_async_lock_avail(
							 general_fridge,
							 event_func,
							 &key,
							 fl.lock_owner,
							 &lockdesc, NULL, NULL);
				else
					fsal_status = up_async_lock_grant(
							 general_fridge,
							 event_func,
							 &key,
							 fl.lock_owner,
							 &lockdesc, NULL, NULL);
			}
			break;

		case BREAK_DELEGATION:	/* Delegation Event */
			LogDebug(COMPONENT_FSAL_UP,
				 "delegation recall: flags:%x ino %" PRId64,
				 flags, callback.buf->st_ino);
			fsal_status = up_async_delegrecall(general_fridge,
						  event_func,
						  &key, NULL, NULL);
			break;

		case LAYOUT_FILE_RECALL:	/* Layout file recall Event */
			{
				struct pnfs_segment segment = {
					.offset = 0,
					.length = UINT64_MAX,
					.io_mode = LAYOUTIOMODE4_ANY
				};
				LogDebug(COMPONENT_FSAL_UP,
					 "layout file recall: flags:%x ino %"
					 PRId64, flags, callback.buf->st_ino);

				fsal_status = up_async_layoutrecall(
							general_fridge,
							event_func,
							&key,
							LAYOUT4_NFSV4_1_FILES,
							false, &segment,
							NULL, NULL, NULL,
							NULL);
			}
			break;

		case LAYOUT_RECALL_ANY:	/* Recall all layouts Event */
			LogDebug(COMPONENT_FSAL_UP,
				 "layout recall any: flags:%x ino %" PRId64,
				 flags, callback.buf->st_ino);

	    /**
	     * @todo This functionality needs to be implemented as a
	     * bulk FSID CB_LAYOUTRECALL.  RECALL_ANY isn't suitable
	     * since it can't be restricted to just one FSAL.  Also
	     * an FSID LAYOUTRECALL lets you have multiplke
	     * filesystems exported from one FSAL and not yank layouts
	     * on all of them when you only need to recall them for one.
	     */
			break;

		case LAYOUT_NOTIFY_DEVICEID:	/* Device update Event */
			LogDebug(COMPONENT_FSAL_UP,
				 "layout dev update: flags:%x ino %"
				 PRId64 " seq %d fd %d fsid 0x%" PRIx64,
				 flags,
				callback.buf->st_ino,
				devid.device_id2,
				devid.device_id4,
				devid.devid);

			memset(&devid, 0, sizeof(devid));
			devid.fsal_id = FSAL_ID_GPFS;

			fsal_status = up_async_notify_device(general_fridge,
						event_func,
						NOTIFY_DEVICEID4_DELETE_MASK,
						LAYOUT4_NFSV4_1_FILES,
						&devid,
						true, NULL,
						NULL);
			break;

		case INODE_UPDATE:	/* Update Event */
			{
				struct attrlist attr;

				LogMidDebug(COMPONENT_FSAL_UP,
					    "inode update: flags:%x update ino %"
					    PRId64 " n_link:%d",
					    flags, callback.buf->st_ino,
					    (int)callback.buf->st_nlink);

				/** @todo: This notification is completely
				 * asynchronous.  If we happen to change some
				 * of the attributes later, we end up over
				 * writing those with these possibly stale
				 * values as we don't know when we get to
				 * update with these up call values. We should
				 * probably use time stamp or let the up call
				 * always provide UP_TIMES flag in which case
				 * we can compare the current ctime vs up call
				 * provided ctime before updating the
				 * attributes.
				 *
				 * For now, we think size attribute is more
				 * important than others, so invalidate the
				 * attributes and let ganesha fetch attributes
				 * as needed if this update includes a size
				 * change. We are careless for other attribute
				 * changes, and we may end up with stale values
				 * until this gets fixed!
				 */
				if (flags & (UP_SIZE | UP_SIZE_BIG)) {
					fsal_status = event_func->invalidate(
						event_func, &key,
						FSAL_UP_INVALIDATE_CACHE);
					break;
				}

				/* Check for accepted flags, any other changes
				   just invalidate. */
				if (flags &
				    ~(UP_SIZE | UP_NLINK | UP_MODE | UP_OWN |
				     UP_TIMES | UP_ATIME | UP_SIZE_BIG)) {
					fsal_status = event_func->invalidate(
						event_func, &key,
						FSAL_UP_INVALIDATE_CACHE);
				} else {
					/* buf may not have all attributes set.
					 * Set the mask to what is changed
					 */
					attr.valid_mask = 0;
					attr.acl = NULL;
					upflags = 0;
					if (flags & UP_SIZE)
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_SIZE | ATTR_SPACEUSED;
					if (flags & UP_SIZE_BIG) {
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_SIZE | ATTR_SPACEUSED;
						upflags |=
						   fsal_up_update_filesize_inc |
						   fsal_up_update_spaceused_inc;
					}
					if (flags & UP_MODE)
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_MODE;
					if (flags & UP_OWN)
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_OWNER | ATTR_GROUP |
						   ATTR_MODE;
					if (flags & UP_TIMES)
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_ATIME | ATTR_CTIME |
						    ATTR_MTIME;
					if (flags & UP_ATIME)
						attr.valid_mask |=
						   ATTR_CHGTIME | ATTR_CHANGE |
						   ATTR_ATIME;
					if (flags & UP_NLINK)
						attr.valid_mask |=
							ATTR_NUMLINKS;
					attr.request_mask = attr.valid_mask;

					attr.expire_time_attr =
					    expire_time_attr;

					posix2fsal_attributes(&buf, &attr);
					fsal_status = event_func->update(
							event_func, &key,
							&attr, upflags);

					if ((flags & UP_NLINK)
					    && (attr.numlinks == 0)) {
						upflags = fsal_up_nlink;
						attr.valid_mask = 0;
						attr.request_mask = 0;
						fsal_status = up_async_update
						    (general_fridge,
						     event_func,
						     &key, &attr,
						     upflags, NULL, NULL);
					}
				}
			}
			break;

		case THREAD_STOP:  /* We wanted to terminate this thread */
			LogDebug(COMPONENT_FSAL_UP,
				"Terminating the GPFS up call thread for %d",
				gpfs_fs->root_fd);
			PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex);
			return NULL;

		case INODE_INVALIDATE:
			LogMidDebug(COMPONENT_FSAL_UP,
				    "inode invalidate: flags:%x update ino %"
				    PRId64, flags, callback.buf->st_ino);

			upflags = FSAL_UP_INVALIDATE_CACHE;
			fsal_status = event_func->invalidate_close(
						event_func,
						&key,
						upflags);
			break;

		case THREAD_PAUSE:
			/* File system image is probably going away, but
			 * we don't need to do anything here as we
			 * eventually get other errors that stop this
			 * thread.
			 */
			PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex);
			continue; /* get next event */

		default:
			PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex);
			LogWarn(COMPONENT_FSAL_UP, "Unknown event: %d", reason);
			continue;
		}

		PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex);

		if (FSAL_IS_ERROR(fsal_status) &&
		    fsal_status.major != ERR_FSAL_NOENT) {
			LogWarn(COMPONENT_FSAL_UP,
				"Event %d could not be processed for fd %d rc %s",
				reason, gpfs_fs->root_fd,
				fsal_err_txt(fsal_status));
		}
	}

	return NULL;
}				/* GPFSFSAL_UP_Thread */
Beispiel #4
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);
	}
}
Beispiel #5
0
int nfs3_fsstat(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res)
{
	fsal_dynamicfsinfo_t dynamicinfo;
	fsal_status_t fsal_status;
	struct fsal_obj_handle *obj = NULL;
	int rc = NFS_REQ_OK;

	if (isDebug(COMPONENT_NFSPROTO)) {
		char str[LEN_FH_STR];

		nfs_FhandleToStr(req->rq_msg.cb_vers,
				 &(arg->arg_fsstat3.fsroot),
				 NULL, str);
		LogDebug(COMPONENT_NFSPROTO,
			 "REQUEST PROCESSING: Calling nfs3_fsstat handle: %s",
			 str);
	}

	/* to avoid setting it on each error case */
	res->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow =
	    FALSE;

	obj = nfs3_FhandleToCache(&arg->arg_fsstat3.fsroot,
				    &res->res_fsstat3.status,
				    &rc);

	if (obj == NULL) {
		/* Status and rc have been set by nfs3_FhandleToCache */
		return rc;
	}

	/* Get statistics and convert from FSAL */
	fsal_status = fsal_statfs(obj, &dynamicinfo);

	if (FSAL_IS_ERROR(fsal_status)) {
		/* At this point we met an error */
		LogFullDebug(COMPONENT_NFSPROTO,
			     "failed statfs: fsal_status=%s",
			     fsal_err_txt(fsal_status));

		if (nfs_RetryableError(fsal_status.major)) {
			/* Drop retryable errors. */
			rc = NFS_REQ_DROP;
		} else {
			res->res_fsstat3.status =
						nfs3_Errno_status(fsal_status);
			rc = NFS_REQ_OK;
		}

		goto out;
	}

	LogFullDebug(COMPONENT_NFSPROTO,
		     "nfs_Fsstat --> dynamicinfo.total_bytes=%" PRIu64
		     " dynamicinfo.free_bytes=%" PRIu64
		     " dynamicinfo.avail_bytes=%" PRIu64,
		     dynamicinfo.total_bytes, dynamicinfo.free_bytes,
		     dynamicinfo.avail_bytes);
	LogFullDebug(COMPONENT_NFSPROTO,
		     "nfs_Fsstat --> dynamicinfo.total_files=%" PRIu64
		     " dynamicinfo.free_files=%" PRIu64
		     " dynamicinfo.avail_files=%" PRIu64,
		     dynamicinfo.total_files, dynamicinfo.free_files,
		     dynamicinfo.avail_files);

	nfs_SetPostOpAttr(obj,
			  &res->res_fsstat3.FSSTAT3res_u.resok.obj_attributes,
			  NULL);

	res->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes;
	res->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes;
	res->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes;
	res->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files;
	res->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files;
	res->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files;
	/* volatile FS */
	res->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0;

	res->res_fsstat3.status = NFS3_OK;

	LogFullDebug(COMPONENT_NFSPROTO,
		     "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu",
		     res->res_fsstat3.FSSTAT3res_u.resok.tbytes,
		     res->res_fsstat3.FSSTAT3res_u.resok.fbytes,
		     res->res_fsstat3.FSSTAT3res_u.resok.abytes);

	LogFullDebug(COMPONENT_NFSPROTO,
		     "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu",
		     res->res_fsstat3.FSSTAT3res_u.resok.tfiles,
		     res->res_fsstat3.FSSTAT3res_u.resok.ffiles,
		     res->res_fsstat3.FSSTAT3res_u.resok.afiles);

	rc = NFS_REQ_OK;

 out:
	/* return references */
	obj->obj_ops.put_ref(obj);

	return rc;
}				/* nfs3_fsstat */
Beispiel #6
0
static int nfs4_write(struct nfs_argop4 *op, compound_data_t *data,
		     struct nfs_resop4 *resp, fsal_io_direction_t io,
		     struct io_info *info)
{
	WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite;
	WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite;
	uint64_t size = 0;
	size_t written_size = 0;
	uint64_t offset;
	bool eof_met;
	bool sync = false;
	void *bufferdata;
	stable_how4 stable_how;
	state_t *state_found = NULL;
	state_t *state_open = NULL;
	fsal_status_t fsal_status = {0, 0};
	struct fsal_obj_handle *obj = NULL;
	bool anonymous_started = false;
	struct gsh_buffdesc verf_desc;
	state_owner_t *owner = NULL;
	uint64_t MaxWrite =
		atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite);
	uint64_t MaxOffsetWrite =
		atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetWrite);

	/* Lock are not supported */
	resp->resop = NFS4_OP_WRITE;
	res_WRITE4->status = NFS4_OK;

	if ((data->minorversion > 0)
	     && (nfs4_Is_Fh_DSHandle(&data->currentFH))) {
		if (io == FSAL_IO_WRITE)
			return op_dswrite(op, data, resp);
		else
			return op_dswrite_plus(op, data, resp, info);
	}

	/*
	 * Do basic checks on a filehandle
	 * Only files can be written
	 */
	res_WRITE4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true);
	if (res_WRITE4->status != NFS4_OK)
		return res_WRITE4->status;

	/* if quota support is active, then we should check is the FSAL
	   allows inode creation or not */
	fsal_status = op_ctx->fsal_export->exp_ops.check_quota(
						op_ctx->fsal_export,
						op_ctx->ctx_export->fullpath,
						FSAL_QUOTA_INODES);

	if (FSAL_IS_ERROR(fsal_status)) {
		res_WRITE4->status = NFS4ERR_DQUOT;
		return res_WRITE4->status;
	}


	/* vnode to manage is the current one */
	obj = data->current_obj;

	/* Check stateid correctness and get pointer to state
	 * (also checks for special stateids)
	 */
	res_WRITE4->status = nfs4_Check_Stateid(&arg_WRITE4->stateid,
						obj,
						&state_found,
						data,
						STATEID_SPECIAL_ANY,
						0,
						false,
						"WRITE");

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

	/* NB: After this points, if state_found == NULL, then
	 * the stateid is all-0 or all-1
	 */
	if (state_found != NULL) {
		struct state_deleg *sdeleg;

		if (info)
			info->io_advise = state_found->state_data.io_advise;
		switch (state_found->state_type) {
		case STATE_TYPE_SHARE:
			state_open = state_found;
			/* Note this causes an extra refcount, but it
			 * simplifies logic below.
			 */
			inc_state_t_ref(state_open);
			/** @todo FSF: need to check against existing locks */
			break;

		case STATE_TYPE_LOCK:
			state_open = state_found->state_data.lock.openstate;
			inc_state_t_ref(state_open);
			/**
			 * @todo FSF: should check that write is in range of an
			 * exclusive lock...
			 */
			break;

		case STATE_TYPE_DELEG:
			/* Check if the delegation state allows READ */
			sdeleg = &state_found->state_data.deleg;
			if (!(sdeleg->sd_type & OPEN_DELEGATE_WRITE) ||
				(sdeleg->sd_state != DELEG_GRANTED)) {
				/* Invalid delegation for this operation. */
				LogDebug(COMPONENT_STATE,
					"Delegation type:%d state:%d",
					sdeleg->sd_type,
					sdeleg->sd_state);
				res_WRITE4->status = NFS4ERR_BAD_STATEID;
				return res_WRITE4->status;
			}

			state_open = NULL;
			break;

		case STATE_TYPE_LAYOUT:
			state_open = NULL;
			break;

		default:
			res_WRITE4->status = NFS4ERR_BAD_STATEID;
			LogDebug(COMPONENT_NFS_V4_LOCK,
				 "WRITE with invalid stateid of type %d",
				 (int)state_found->state_type);
			return res_WRITE4->status;
		}

		/* This is a write operation, this means that the file
		 * MUST have been opened for writing
		 */
		if (state_open != NULL &&
		    (state_open->state_data.share.share_access &
		     OPEN4_SHARE_ACCESS_WRITE) == 0) {
			/* Bad open mode, return NFS4ERR_OPENMODE */
			res_WRITE4->status = NFS4ERR_OPENMODE;
				if (isDebug(COMPONENT_NFS_V4_LOCK)) {
					char str[LOG_BUFF_LEN] = "\0";
					struct display_buffer dspbuf = {
							sizeof(str), str, str};
					display_stateid(&dspbuf, state_found);
					LogDebug(COMPONENT_NFS_V4_LOCK,
						 "WRITE %s doesn't have OPEN4_SHARE_ACCESS_WRITE",
						 str);
				}
			goto out;
		}
	} else {
		/* Special stateid, no open state, check to see if any
		 * share conflicts
		 */
		state_open = NULL;

		/* Special stateid, no open state, check to see if any share
		 * conflicts The stateid is all-0 or all-1
		 */
		res_WRITE4->status = nfs4_Errno_state(
				state_share_anonymous_io_start(
					obj,
					OPEN4_SHARE_ACCESS_WRITE,
					SHARE_BYPASS_NONE));

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

		anonymous_started = true;
	}

	/* Need to permission check the write. */
	fsal_status = obj->obj_ops.test_access(obj, FSAL_WRITE_ACCESS,
					       NULL, NULL, true);

	if (FSAL_IS_ERROR(fsal_status)) {
		res_WRITE4->status = nfs4_Errno_status(fsal_status);
		goto done;
	}

	/* Get the characteristics of the I/O to be made */
	offset = arg_WRITE4->offset;
	size = arg_WRITE4->data.data_len;
	stable_how = arg_WRITE4->stable;
	LogFullDebug(COMPONENT_NFS_V4,
		     "offset = %" PRIu64 "  length = %" PRIu64 "  stable = %d",
		     offset, size, stable_how);

	if (MaxOffsetWrite < UINT64_MAX) {
		LogFullDebug(COMPONENT_NFS_V4,
			     "Write offset=%" PRIu64 " count=%" PRIu64
			     " MaxOffSet=%" PRIu64, offset, size,
			     MaxOffsetWrite);

		if ((offset + size) > MaxOffsetWrite) {
			LogEvent(COMPONENT_NFS_V4,
				 "A client tryed to violate max file size %"
				 PRIu64 " for exportid #%hu",
				 MaxOffsetWrite,
				 op_ctx->ctx_export->export_id);
			res_WRITE4->status = NFS4ERR_FBIG;
			goto done;
		}
	}

	if (size > MaxWrite) {
		/*
		 * The client asked for too much data, we
		 * must restrict him
		 */

		if (info == NULL ||
		    info->io_content.what != NFS4_CONTENT_HOLE) {
			LogFullDebug(COMPONENT_NFS_V4,
				     "write requested size = %" PRIu64
				     " write allowed size = %" PRIu64,
				     size, MaxWrite);
			size = MaxWrite;
		}
	}

	/* Where are the data ? */
	bufferdata = arg_WRITE4->data.data_val;

	LogFullDebug(COMPONENT_NFS_V4,
		     "offset = %" PRIu64 " length = %" PRIu64,
		     offset, size);

	/* if size == 0 , no I/O) are actually made and everything is alright */
	if (size == 0) {
		res_WRITE4->WRITE4res_u.resok4.count = 0;
		res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4;

		verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf;
		verf_desc.len = sizeof(verifier4);
		op_ctx->fsal_export->exp_ops.get_write_verifier(
					op_ctx->fsal_export, &verf_desc);

		res_WRITE4->status = NFS4_OK;
		goto done;
	}

	if (arg_WRITE4->stable == UNSTABLE4)
		sync = false;
	else
		sync = true;

	if (!anonymous_started && data->minorversion == 0) {
		owner = get_state_owner_ref(state_found);
		if (owner != NULL) {
			op_ctx->clientid =
				&owner->so_owner.so_nfs4_owner.so_clientid;
		}
	}

	if (obj->fsal->m_ops.support_ex(obj)) {
		/* Call the new fsal_write */
		fsal_status = fsal_write2(obj, false, state_found, offset, size,
					  &written_size, bufferdata, &sync,
					  info);
	} else {
		/* Call legacy fsal_rdwr */
		fsal_status = fsal_rdwr(obj, io, offset, size, &written_size,
					bufferdata, &eof_met, &sync, info);
	}

	if (FSAL_IS_ERROR(fsal_status)) {
		LogDebug(COMPONENT_NFS_V4, "write returned %s",
			 fsal_err_txt(fsal_status));
		res_WRITE4->status = nfs4_Errno_status(fsal_status);
		goto done;
	}

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

	/* Set the returned value */
	if (sync)
		res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4;
	else
		res_WRITE4->WRITE4res_u.resok4.committed = UNSTABLE4;

	res_WRITE4->WRITE4res_u.resok4.count = written_size;

	verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf;
	verf_desc.len = sizeof(verifier4);
	op_ctx->fsal_export->exp_ops.get_write_verifier(op_ctx->fsal_export,
							&verf_desc);

	res_WRITE4->status = NFS4_OK;

 done:

	if (anonymous_started)
		state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_WRITE);

	server_stats_io_done(size, written_size,
			     (res_WRITE4->status == NFS4_OK) ? true : false,
			     true);

 out:

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

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

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

	return res_WRITE4->status;
}				/* nfs4_op_write */
Beispiel #7
0
/** makesymlink
 *  Note that we do not set mode bits on symlinks for Linux/POSIX
 *  They are not really settable in the kernel and are not checked
 *  anyway (default is 0777) because open uses that target's mode
 */
static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl,
				 const char *name, const char *link_path,
				 struct attrlist *attr_in,
				 struct fsal_obj_handle **handle,
				 struct attrlist *attrs_out)
{
	fsal_status_t status;
	struct gpfs_fsal_obj_handle *hdl;
	struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle));
	/* Use a separate attrlist to getch the actual attributes into */
	struct attrlist attrib;

	*handle = NULL;		/* poison it first */
	if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) {
		LogCrit(COMPONENT_FSAL,
			"Parent handle is not a directory. hdl = 0x%p",
			dir_hdl);
		return fsalstat(ERR_FSAL_NOTDIR, 0);
	}
	memset(fh, 0, sizeof(struct gpfs_file_handle));
	fh->handle_size = GPFS_MAX_FH_SIZE;

	fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE);

	if (attrs_out != NULL)
		attrib.mask |= attrs_out->mask;

	status = GPFSFSAL_symlink(dir_hdl, name, link_path, op_ctx,
				  attr_in->mode, fh, &attrib);
	if (FSAL_IS_ERROR(status))
		return status;

	/* allocate an obj_handle and fill it up */
	hdl = alloc_handle(fh, dir_hdl->fs, &attrib, link_path,
			   op_ctx->fsal_export);

	if (attrs_out != NULL) {
		/* Copy the attributes to caller, passing ACL ref. */
		fsal_copy_attrs(attrs_out, &attrib, true);
	} else {
		/* Done with the attrs */
		fsal_release_attrs(&attrib);
	}
	*handle = &hdl->obj_handle;

	/* We handled the mode above. */
	FSAL_UNSET_MASK(attr_in->mask, ATTR_MODE);

	if (attr_in->mask) {
		/* Now per support_ex API, if there are any other attributes
		 * set, go ahead and get them set now.
		 */
		status = (*handle)->obj_ops.setattr2(*handle, false, NULL,
						     attr_in);
		if (FSAL_IS_ERROR(status)) {
			/* Release the handle we just allocated. */
			LogFullDebug(COMPONENT_FSAL,
				     "setattr2 status=%s",
				      fsal_err_txt(status));
			(*handle)->obj_ops.release(*handle);
			*handle = NULL;
		}
	} else {
		status = fsalstat(ERR_FSAL_NO_ERROR, 0);

		if (attrs_out != NULL) {
			/* Make sure ATTR_RDATTR_ERR is cleared on success. */
			attrs_out->mask &= ~ATTR_RDATTR_ERR;
		}
	}
	FSAL_SET_MASK(attr_in->mask, ATTR_MODE);

	return status;
}