Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5db11c21 authored by Mike Marshall's avatar Mike Marshall
Browse files

Orangefs: kernel client part 2

parent f7ab093f
Loading
Loading
Loading
Loading

fs/orangefs/acl.c

0 → 100644
+175 −0
Original line number Diff line number Diff line
/*
 * (C) 2001 Clemson University and The University of Chicago
 *
 * See COPYING in top-level directory.
 */

#include "protocol.h"
#include "pvfs2-kernel.h"
#include "pvfs2-bufmap.h"
#include <linux/posix_acl_xattr.h>
#include <linux/fs_struct.h>

struct posix_acl *pvfs2_get_acl(struct inode *inode, int type)
{
	struct posix_acl *acl;
	int ret;
	char *key = NULL, *value = NULL;

	switch (type) {
	case ACL_TYPE_ACCESS:
		key = PVFS2_XATTR_NAME_ACL_ACCESS;
		break;
	case ACL_TYPE_DEFAULT:
		key = PVFS2_XATTR_NAME_ACL_DEFAULT;
		break;
	default:
		gossip_err("pvfs2_get_acl: bogus value of type %d\n", type);
		return ERR_PTR(-EINVAL);
	}
	/*
	 * Rather than incurring a network call just to determine the exact
	 * length of the attribute, I just allocate a max length to save on
	 * the network call. Conceivably, we could pass NULL to
	 * pvfs2_inode_getxattr() to probe the length of the value, but
	 * I don't do that for now.
	 */
	value = kmalloc(PVFS_MAX_XATTR_VALUELEN, GFP_KERNEL);
	if (value == NULL)
		return ERR_PTR(-ENOMEM);

	gossip_debug(GOSSIP_ACL_DEBUG,
		     "inode %pU, key %s, type %d\n",
		     get_khandle_from_ino(inode),
		     key,
		     type);
	ret = pvfs2_inode_getxattr(inode,
				   "",
				   key,
				   value,
				   PVFS_MAX_XATTR_VALUELEN);
	/* if the key exists, convert it to an in-memory rep */
	if (ret > 0) {
		acl = posix_acl_from_xattr(&init_user_ns, value, ret);
	} else if (ret == -ENODATA || ret == -ENOSYS) {
		acl = NULL;
	} else {
		gossip_err("inode %pU retrieving acl's failed with error %d\n",
			   get_khandle_from_ino(inode),
			   ret);
		acl = ERR_PTR(ret);
	}
	/* kfree(NULL) is safe, so don't worry if value ever got used */
	kfree(value);
	return acl;
}

int pvfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
	struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(inode);
	int error = 0;
	void *value = NULL;
	size_t size = 0;
	const char *name = NULL;

	switch (type) {
	case ACL_TYPE_ACCESS:
		name = PVFS2_XATTR_NAME_ACL_ACCESS;
		if (acl) {
			umode_t mode = inode->i_mode;
			/*
			 * can we represent this with the traditional file
			 * mode permission bits?
			 */
			error = posix_acl_equiv_mode(acl, &mode);
			if (error < 0) {
				gossip_err("%s: posix_acl_equiv_mode err: %d\n",
					   __func__,
					   error);
				return error;
			}

			if (inode->i_mode != mode)
				SetModeFlag(pvfs2_inode);
			inode->i_mode = mode;
			mark_inode_dirty_sync(inode);
			if (error == 0)
				acl = NULL;
		}
		break;
	case ACL_TYPE_DEFAULT:
		name = PVFS2_XATTR_NAME_ACL_DEFAULT;
		break;
	default:
		gossip_err("%s: invalid type %d!\n", __func__, type);
		return -EINVAL;
	}

	gossip_debug(GOSSIP_ACL_DEBUG,
		     "%s: inode %pU, key %s type %d\n",
		     __func__, get_khandle_from_ino(inode),
		     name,
		     type);

	if (acl) {
		size = posix_acl_xattr_size(acl->a_count);
		value = kmalloc(size, GFP_KERNEL);
		if (!value)
			return -ENOMEM;

		error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
		if (error < 0)
			goto out;
	}

	gossip_debug(GOSSIP_ACL_DEBUG,
		     "%s: name %s, value %p, size %zd, acl %p\n",
		     __func__, name, value, size, acl);
	/*
	 * Go ahead and set the extended attribute now. NOTE: Suppose acl
	 * was NULL, then value will be NULL and size will be 0 and that
	 * will xlate to a removexattr. However, we don't want removexattr
	 * complain if attributes does not exist.
	 */
	error = pvfs2_inode_setxattr(inode, "", name, value, size, 0);

out:
	kfree(value);
	if (!error)
		set_cached_acl(inode, type, acl);
	return error;
}

int pvfs2_init_acl(struct inode *inode, struct inode *dir)
{
	struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(inode);
	struct posix_acl *default_acl, *acl;
	umode_t mode = inode->i_mode;
	int error = 0;

	ClearModeFlag(pvfs2_inode);

	error = posix_acl_create(dir, &mode, &default_acl, &acl);
	if (error)
		return error;

	if (default_acl) {
		error = pvfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
		posix_acl_release(default_acl);
	}

	if (acl) {
		if (!error)
			error = pvfs2_set_acl(inode, acl, ACL_TYPE_ACCESS);
		posix_acl_release(acl);
	}

	/* If mode of the inode was changed, then do a forcible ->setattr */
	if (mode != inode->i_mode) {
		SetModeFlag(pvfs2_inode);
		inode->i_mode = mode;
		pvfs2_flush_inode(inode);
	}

	return error;
}

fs/orangefs/dcache.c

0 → 100644
+142 −0
Original line number Diff line number Diff line
/*
 * (C) 2001 Clemson University and The University of Chicago
 *
 * See COPYING in top-level directory.
 */

/*
 *  Implementation of dentry (directory cache) functions.
 */

#include "protocol.h"
#include "pvfs2-kernel.h"

/* Returns 1 if dentry can still be trusted, else 0. */
static int pvfs2_revalidate_lookup(struct dentry *dentry)
{
	struct dentry *parent_dentry = dget_parent(dentry);
	struct inode *parent_inode = parent_dentry->d_inode;
	struct pvfs2_inode_s *parent = PVFS2_I(parent_inode);
	struct inode *inode = dentry->d_inode;
	struct pvfs2_kernel_op_s *new_op;
	int ret = 0;
	int err = 0;

	gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__);

	new_op = op_alloc(PVFS2_VFS_OP_LOOKUP);
	if (!new_op)
		goto out_put_parent;

	new_op->upcall.req.lookup.sym_follow = PVFS2_LOOKUP_LINK_NO_FOLLOW;
	new_op->upcall.req.lookup.parent_refn = parent->refn;
	strncpy(new_op->upcall.req.lookup.d_name,
		dentry->d_name.name,
		PVFS2_NAME_LEN);

	gossip_debug(GOSSIP_DCACHE_DEBUG,
		     "%s:%s:%d interrupt flag [%d]\n",
		     __FILE__,
		     __func__,
		     __LINE__,
		     get_interruptible_flag(parent_inode));

	err = service_operation(new_op, "pvfs2_lookup",
			get_interruptible_flag(parent_inode));
	if (err)
		goto out_drop;

	if (new_op->downcall.status != 0 ||
	    !match_handle(new_op->downcall.resp.lookup.refn.khandle, inode)) {
		gossip_debug(GOSSIP_DCACHE_DEBUG,
			"%s:%s:%d "
			"lookup failure |%s| or no match |%s|.\n",
			__FILE__,
			__func__,
			__LINE__,
			new_op->downcall.status ? "true" : "false",
			match_handle(new_op->downcall.resp.lookup.refn.khandle,
					inode) ? "false" : "true");
		gossip_debug(GOSSIP_DCACHE_DEBUG,
			     "%s:%s:%d revalidate failed\n",
			     __FILE__, __func__, __LINE__);
		goto out_drop;
	}

	ret = 1;
out_release_op:
	op_release(new_op);
out_put_parent:
	dput(parent_dentry);
	return ret;
out_drop:
	d_drop(dentry);
	goto out_release_op;
}

/*
 * Verify that dentry is valid.
 *
 * Should return 1 if dentry can still be trusted, else 0
 */
static int pvfs2_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct inode *inode;
	int ret = 0;

	if (flags & LOOKUP_RCU)
		return -ECHILD;

	gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: called on dentry %p.\n",
		     __func__, dentry);

	/* find inode from dentry */
	if (!dentry->d_inode) {
		gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: negative dentry.\n",
			     __func__);
		goto invalid_exit;
	}

	gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: inode valid.\n", __func__);
	inode = dentry->d_inode;

	/*
	 * first perform a lookup to make sure that the object not only
	 * exists, but is still in the expected place in the name space
	 */
	if (!is_root_handle(inode)) {
		if (!pvfs2_revalidate_lookup(dentry))
			goto invalid_exit;
	} else {
		gossip_debug(GOSSIP_DCACHE_DEBUG,
			     "%s: root handle, lookup skipped.\n",
			     __func__);
	}

	/* now perform getattr */
	gossip_debug(GOSSIP_DCACHE_DEBUG,
		     "%s: doing getattr: inode: %p, handle: %pU\n",
		     __func__,
		     inode,
		     get_khandle_from_ino(inode));
	ret = pvfs2_inode_getattr(inode, PVFS_ATTR_SYS_ALL_NOHINT);
	gossip_debug(GOSSIP_DCACHE_DEBUG,
		     "%s: getattr %s (ret = %d), returning %s for dentry i_count=%d\n",
		     __func__,
		     (ret == 0 ? "succeeded" : "failed"),
		     ret,
		     (ret == 0 ? "valid" : "INVALID"),
		     atomic_read(&inode->i_count));
	if (ret != 0)
		goto invalid_exit;

	/* dentry is valid! */
	return 1;

invalid_exit:
	return 0;
}

const struct dentry_operations pvfs2_dentry_operations = {
	.d_revalidate = pvfs2_d_revalidate,
};
+997 −0

File added.

Preview size limit exceeded, changes collapsed.

fs/orangefs/dir.c

0 → 100644
+394 −0
Original line number Diff line number Diff line
/*
 * (C) 2001 Clemson University and The University of Chicago
 *
 * See COPYING in top-level directory.
 */

#include "protocol.h"
#include "pvfs2-kernel.h"
#include "pvfs2-bufmap.h"

struct readdir_handle_s {
	int buffer_index;
	struct pvfs2_readdir_response_s readdir_response;
	void *dents_buf;
};

/*
 * decode routine needed by kmod to make sense of the shared page for readdirs.
 */
static long decode_dirents(char *ptr, struct pvfs2_readdir_response_s *readdir)
{
	int i;
	struct pvfs2_readdir_response_s *rd =
		(struct pvfs2_readdir_response_s *) ptr;
	char *buf = ptr;
	char **pptr = &buf;

	readdir->token = rd->token;
	readdir->pvfs_dirent_outcount = rd->pvfs_dirent_outcount;
	readdir->dirent_array = kmalloc(readdir->pvfs_dirent_outcount *
					sizeof(*readdir->dirent_array),
					GFP_KERNEL);
	if (readdir->dirent_array == NULL)
		return -ENOMEM;
	*pptr += offsetof(struct pvfs2_readdir_response_s, dirent_array);
	for (i = 0; i < readdir->pvfs_dirent_outcount; i++) {
		dec_string(pptr, &readdir->dirent_array[i].d_name,
			   &readdir->dirent_array[i].d_length);
		readdir->dirent_array[i].khandle =
			*(struct pvfs2_khandle *) *pptr;
		*pptr += 16;
	}
	return (unsigned long)*pptr - (unsigned long)ptr;
}

static long readdir_handle_ctor(struct readdir_handle_s *rhandle, void *buf,
				int buffer_index)
{
	long ret;

	if (buf == NULL) {
		gossip_err
		    ("Invalid NULL buffer specified in readdir_handle_ctor\n");
		return -ENOMEM;
	}
	if (buffer_index < 0) {
		gossip_err
		    ("Invalid buffer index specified in readdir_handle_ctor\n");
		return -EINVAL;
	}
	rhandle->buffer_index = buffer_index;
	rhandle->dents_buf = buf;
	ret = decode_dirents(buf, &rhandle->readdir_response);
	if (ret < 0) {
		gossip_err("Could not decode readdir from buffer %ld\n", ret);
		rhandle->buffer_index = -1;
		gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", buf);
		vfree(buf);
		rhandle->dents_buf = NULL;
	}
	return ret;
}

static void readdir_handle_dtor(struct pvfs2_bufmap *bufmap,
		struct readdir_handle_s *rhandle)
{
	if (rhandle == NULL)
		return;

	/* kfree(NULL) is safe */
	kfree(rhandle->readdir_response.dirent_array);
	rhandle->readdir_response.dirent_array = NULL;

	if (rhandle->buffer_index >= 0) {
		readdir_index_put(bufmap, rhandle->buffer_index);
		rhandle->buffer_index = -1;
	}
	if (rhandle->dents_buf) {
		gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n",
			     rhandle->dents_buf);
		vfree(rhandle->dents_buf);
		rhandle->dents_buf = NULL;
	}
}

/*
 * Read directory entries from an instance of an open directory.
 *
 * \note This routine was converted for the readdir to iterate change
 *       in "struct file_operations". "converted" mostly amounts to
 *       changing occurrences of "readdir" and "filldir" in the
 *       comments to "iterate" and "dir_emit". Also filldir calls
 *       were changed to dir_emit calls.
 *
 * \param dir_emit callback function called for each entry read.
 *
 * \retval <0 on error
 * \retval 0  when directory has been completely traversed
 * \retval >0 if we don't call dir_emit for all entries
 *
 * \note If the dir_emit call-back returns non-zero, then iterate should
 *       assume that it has had enough, and should return as well.
 */
static int pvfs2_readdir(struct file *file, struct dir_context *ctx)
{
	struct pvfs2_bufmap *bufmap = NULL;
	int ret = 0;
	int buffer_index;
	__u64 *ptoken = file->private_data;
	__u64 pos = 0;
	ino_t ino = 0;
	struct dentry *dentry = file->f_path.dentry;
	struct pvfs2_kernel_op_s *new_op = NULL;
	struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(dentry->d_inode);
	int buffer_full = 0;
	struct readdir_handle_s rhandle;
	int i = 0;
	int len = 0;
	ino_t current_ino = 0;
	char *current_entry = NULL;
	long bytes_decoded;

	gossip_ldebug(GOSSIP_DIR_DEBUG,
		      "%s: ctx->pos:%lld, token = %llu\n",
		      __func__,
		      lld(ctx->pos),
		      llu(*ptoken));

	pos = (__u64) ctx->pos;

	/* are we done? */
	if (pos == PVFS_READDIR_END) {
		gossip_debug(GOSSIP_DIR_DEBUG,
			     "Skipping to termination path\n");
		return 0;
	}

	gossip_debug(GOSSIP_DIR_DEBUG,
		     "pvfs2_readdir called on %s (pos=%llu)\n",
		     dentry->d_name.name, llu(pos));

	rhandle.buffer_index = -1;
	rhandle.dents_buf = NULL;
	memset(&rhandle.readdir_response, 0, sizeof(rhandle.readdir_response));

	new_op = op_alloc(PVFS2_VFS_OP_READDIR);
	if (!new_op)
		return -ENOMEM;

	new_op->uses_shared_memory = 1;
	new_op->upcall.req.readdir.refn = pvfs2_inode->refn;
	new_op->upcall.req.readdir.max_dirent_count = MAX_DIRENT_COUNT_READDIR;

	gossip_debug(GOSSIP_DIR_DEBUG,
		     "%s: upcall.req.readdir.refn.khandle: %pU\n",
		     __func__,
		     &new_op->upcall.req.readdir.refn.khandle);

	/*
	 * NOTE: the position we send to the readdir upcall is out of
	 * sync with ctx->pos since:
	 * 1. pvfs2 doesn't include the "." and ".." entries that are
	 *    added below.
	 * 2. the introduction of distributed directory logic makes token no
	 *    longer be related to f_pos and pos. Instead an independent
	 *    variable is used inside the function and stored in the
	 *    private_data of the file structure.
	 */
	new_op->upcall.req.readdir.token = *ptoken;

get_new_buffer_index:
	ret = readdir_index_get(&bufmap, &buffer_index);
	if (ret < 0) {
		gossip_lerr("pvfs2_readdir: readdir_index_get() failure (%d)\n",
			    ret);
		goto out_free_op;
	}
	new_op->upcall.req.readdir.buf_index = buffer_index;

	ret = service_operation(new_op,
				"pvfs2_readdir",
				get_interruptible_flag(dentry->d_inode));

	gossip_debug(GOSSIP_DIR_DEBUG,
		     "Readdir downcall status is %d.  ret:%d\n",
		     new_op->downcall.status,
		     ret);

	if (ret == -EAGAIN && op_state_purged(new_op)) {
		/*
		 * readdir shared memory aread has been wiped due to
		 * pvfs2-client-core restarting, so we must get a new
		 * index into the shared memory.
		 */
		gossip_debug(GOSSIP_DIR_DEBUG,
			"%s: Getting new buffer_index for retry of readdir..\n",
			 __func__);
		readdir_index_put(bufmap, buffer_index);
		goto get_new_buffer_index;
	}

	if (ret == -EIO && op_state_purged(new_op)) {
		gossip_err("%s: Client is down. Aborting readdir call.\n",
			__func__);
		readdir_index_put(bufmap, buffer_index);
		goto out_free_op;
	}

	if (ret < 0 || new_op->downcall.status != 0) {
		gossip_debug(GOSSIP_DIR_DEBUG,
			     "Readdir request failed.  Status:%d\n",
			     new_op->downcall.status);
		readdir_index_put(bufmap, buffer_index);
		if (ret >= 0)
			ret = new_op->downcall.status;
		goto out_free_op;
	}

	bytes_decoded =
		readdir_handle_ctor(&rhandle,
				    new_op->downcall.trailer_buf,
				    buffer_index);
	if (bytes_decoded < 0) {
		gossip_err("pvfs2_readdir: Could not decode trailer buffer into a readdir response %d\n",
			ret);
		ret = bytes_decoded;
		readdir_index_put(bufmap, buffer_index);
		goto out_free_op;
	}

	if (bytes_decoded != new_op->downcall.trailer_size) {
		gossip_err("pvfs2_readdir: # bytes decoded (%ld) != trailer size (%ld)\n",
			bytes_decoded,
			(long)new_op->downcall.trailer_size);
		ret = -EINVAL;
		goto out_destroy_handle;
	}

	if (pos == 0) {
		ino = get_ino_from_khandle(dentry->d_inode);
		gossip_debug(GOSSIP_DIR_DEBUG,
			     "%s: calling dir_emit of \".\" with pos = %llu\n",
			     __func__,
			     llu(pos));
		ret = dir_emit(ctx, ".", 1, ino, DT_DIR);
		if (ret < 0)
			goto out_destroy_handle;
		ctx->pos++;
		gossip_ldebug(GOSSIP_DIR_DEBUG,
			      "%s: ctx->pos:%lld\n",
			      __func__,
			      lld(ctx->pos));
		pos++;
	}

	if (pos == 1) {
		ino = get_parent_ino_from_dentry(dentry);
		gossip_debug(GOSSIP_DIR_DEBUG,
			     "%s: calling dir_emit of \"..\" with pos = %llu\n",
			     __func__,
			     llu(pos));
		ret = dir_emit(ctx, "..", 2, ino, DT_DIR);
		if (ret < 0)
			goto out_destroy_handle;
		ctx->pos++;
		gossip_ldebug(GOSSIP_DIR_DEBUG,
			      "%s: ctx->pos:%lld\n",
			      __func__,
			      lld(ctx->pos));
		pos++;
	}

	for (i = 0; i < rhandle.readdir_response.pvfs_dirent_outcount; i++) {
		len = rhandle.readdir_response.dirent_array[i].d_length;
		current_entry = rhandle.readdir_response.dirent_array[i].d_name;
		current_ino = pvfs2_khandle_to_ino(
			&(rhandle.readdir_response.dirent_array[i].khandle));

		gossip_debug(GOSSIP_DIR_DEBUG,
			     "calling dir_emit for %s with len %d, pos %ld\n",
			     current_entry,
			     len,
			     (unsigned long)pos);
		ret =
		    dir_emit(ctx, current_entry, len, current_ino, DT_UNKNOWN);
		if (ret < 0) {
			gossip_debug(GOSSIP_DIR_DEBUG,
				     "dir_emit() failed. ret:%d\n",
				     ret);
			if (i < 2) {
				gossip_err("dir_emit failed on one of the first two true PVFS directory entries.\n");
				gossip_err("Duplicate entries may appear.\n");
			}
			buffer_full = 1;
			break;
		}
		ctx->pos++;
		gossip_ldebug(GOSSIP_DIR_DEBUG,
			      "%s: ctx->pos:%lld\n",
			      __func__,
			      lld(ctx->pos));

		pos++;
	}

	/* this means that all of the dir_emit calls succeeded */
	if (i == rhandle.readdir_response.pvfs_dirent_outcount) {
		/* update token */
		*ptoken = rhandle.readdir_response.token;
	} else {
		/* this means a dir_emit call failed */
		if (rhandle.readdir_response.token == PVFS_READDIR_END) {
			/*
			 * If PVFS hit end of directory, then there
			 * is no way to do math on the token that it
			 * returned. Instead we go by ctx->pos but
			 * back up to account for the artificial .
			 * and .. entries.
			 */
			ctx->pos -= 3;
		} else {
			/*
			 * this means a dir_emit call failed. !!! need to set
			 * back to previous ctx->pos, no middle value allowed
			 */
			pos -= (i - 1);
			ctx->pos -= (i - 1);
		}
		gossip_debug(GOSSIP_DIR_DEBUG,
			"at least one dir_emit call failed. Setting ctx->pos to: %lld\n",
			lld(ctx->pos));
	}

	/*
	 * Did we hit the end of the directory?
	 */
	if (rhandle.readdir_response.token == PVFS_READDIR_END &&
	    !buffer_full) {
		gossip_debug(GOSSIP_DIR_DEBUG, "End of dir detected; setting ctx->pos to PVFS_READDIR_END.\n");
		ctx->pos = PVFS_READDIR_END;
	}

	gossip_debug(GOSSIP_DIR_DEBUG,
		     "pos = %llu, token = %llu"
		     ", ctx->pos should have been %lld\n",
		     llu(pos),
		     llu(*ptoken),
		     lld(ctx->pos));

out_destroy_handle:
	readdir_handle_dtor(bufmap, &rhandle);
out_free_op:
	op_release(new_op);
	gossip_debug(GOSSIP_DIR_DEBUG, "pvfs2_readdir returning %d\n", ret);
	return ret;
}

static int pvfs2_dir_open(struct inode *inode, struct file *file)
{
	__u64 *ptoken;

	file->private_data = kmalloc(sizeof(__u64), GFP_KERNEL);
	if (!file->private_data)
		return -ENOMEM;

	ptoken = file->private_data;
	*ptoken = PVFS_READDIR_START;
	return 0;
}

static int pvfs2_dir_release(struct inode *inode, struct file *file)
{
	pvfs2_flush_inode(inode);
	kfree(file->private_data);
	return 0;
}

/** PVFS2 implementation of VFS directory operations */
const struct file_operations pvfs2_dir_operations = {
	.read = generic_read_dir,
	.iterate = pvfs2_readdir,
	.open = pvfs2_dir_open,
	.release = pvfs2_dir_release,
};

fs/orangefs/file.c

0 → 100644
+1019 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading