Exemple #1
0
/* 
 * Write to a file (through the page cache).
 */
static ssize_t
nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
	struct dentry * dentry = file->f_dentry;
	struct inode * inode = dentry->d_inode;
	ssize_t result;

	dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,
		inode->i_ino, (unsigned long) count, (unsigned long) *ppos);

	result = -EBUSY;
	if (IS_SWAPFILE(inode))
		goto out_swapfile;
	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
	if (result)
		goto out;

	result = count;
	if (!count)
		goto out;

	result = generic_file_write(file, buf, count, ppos);
out:
	return result;

out_swapfile:
	printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
	goto out;
}
Exemple #2
0
static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
	struct inode *inode = dentry->d_inode;
	struct page *page;
	void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode));
	if (err)
		goto read_failed;
	page = read_cache_page(&inode->i_data, 0,
				(filler_t *)nfs_symlink_filler, inode);
	if (IS_ERR(page)) {
		err = page;
		goto read_failed;
	}
	if (!PageUptodate(page)) {
		err = ERR_PTR(-EIO);
		goto getlink_read_error;
	}
	nd_set_link(nd, kmap(page));
	return page;

getlink_read_error:
	page_cache_release(page);
read_failed:
	nd_set_link(nd, err);
	return NULL;
}
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
	struct inode *inode = NULL;
	int error = 0;

	/* Check that we are indeed trying to open this file */
	if (!is_atomic_open(dir, nd))
		goto no_open;

	if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
		error = -ENAMETOOLONG;
		goto out;
	}
	dentry->d_op = NFS_PROTO(dir)->dentry_ops;

	/* Let vfs_create() deal with O_EXCL */
	if (nd->intent.open.flags & O_EXCL)
		goto no_entry;

	/* Open the file on the server */
	lock_kernel();
	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	if (nd->intent.open.flags & O_CREAT) {
		nfs_begin_data_update(dir);
		inode = nfs4_atomic_open(dir, dentry, nd);
		nfs_end_data_update(dir);
	} else
		inode = nfs4_atomic_open(dir, dentry, nd);
	unlock_kernel();
	if (IS_ERR(inode)) {
		error = PTR_ERR(inode);
		switch (error) {
			/* Make a negative dentry */
			case -ENOENT:
				inode = NULL;
				break;
			/* This turned out not to be a regular file */
			case -ELOOP:
				if (!(nd->intent.open.flags & O_NOFOLLOW))
					goto no_open;
			/* case -EISDIR: */
			/* case -EINVAL: */
			default:
				goto out;
		}
	}
no_entry:
	d_add(dentry, inode);
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out:
	BUG_ON(error > 0);
	return ERR_PTR(error);
no_open:
	return nfs_lookup(dir, dentry, nd);
}
/*
 * A check for whether or not the parent directory has changed.
 * In the case it has, we assume that the dentries are untrustworthy
 * and may need to be looked up again.
 */
static inline
int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
{
	if (IS_ROOT(dentry))
		return 1;
	if (nfs_revalidate_inode(NFS_SERVER(dir), dir))
		return 0;
	return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir));
}
static inline
int nfs_lookup_verify_inode(struct inode *inode, int isopen)
{
	struct nfs_server *server = NFS_SERVER(inode);

	if (isopen && !(server->flags & NFS_MOUNT_NOCTO))
		return __nfs_revalidate_inode(server, inode);
	return nfs_revalidate_inode(server, inode);
}
static inline
int nfs_lookup_verify_inode(struct inode *inode, int flags)
{
	struct nfs_server *server = NFS_SERVER(inode);
	/*
	 * If we're interested in close-to-open cache consistency,
	 * then we revalidate the inode upon lookup.
	 */
	if (!(server->flags & NFS_MOUNT_NOCTO) && !(flags & LOOKUP_CONTINUE))
		NFS_CACHEINV(inode);
	return nfs_revalidate_inode(server, inode);
}
Exemple #7
0
static int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{
	struct dentry *dentry = file->f_dentry;
	int	status;

	dfprintk(VFS, "nfs: mmap(%s/%s)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name);

	status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
	if (!status)
		status = generic_file_mmap(file, vma);
	return status;
}
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
	struct inode *inode = NULL;
	int error;
	struct nfs_fh fhandle;
	struct nfs_fattr fattr;

	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name);

	error = -ENAMETOOLONG;
	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
		goto out;

	error = -ENOMEM;
	dentry->d_op = NFS_PROTO(dir)->dentry_ops;

	lock_kernel();
	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	/* If we're doing an exclusive create, optimize away the lookup */
	if (nfs_is_exclusive_create(dir, nd))
		goto no_entry;

	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
	if (error != 0) {
		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name,
				&fhandle, &fattr);
		if (error == -ENOENT)
			goto no_entry;
		if (error != 0)
			goto out_unlock;
	}
	error = -EACCES;
	inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
	if (!inode)
		goto out_unlock;
no_entry:
	error = 0;
	d_add(dentry, inode);
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unlock:
	unlock_kernel();
out:
	BUG_ON(error > 0);
	return ERR_PTR(error);
}
Exemple #9
0
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
	struct dentry * dentry = file->f_dentry;
	ssize_t result;

	dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,
		(unsigned long) count, (unsigned long) *ppos);

	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
	if (!result)
		result = generic_file_read(file, buf, count, ppos);
	return result;
}
Exemple #10
0
/*
 * Flush all dirty pages, and check for write errors.
 *
 */
static int
nfs_file_flush(struct file *file, fl_owner_t id)
{
    struct nfs_open_context *ctx = nfs_file_open_context(file);
    struct inode    *inode = file->f_path.dentry->d_inode;
    int        status;

    dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);

    if ((file->f_mode & FMODE_WRITE) == 0)
        return 0;
    nfs_inc_stats(inode, NFSIOS_VFSFLUSH);

    /* Ensure that data+attribute caches are up to date after close() */
    status = nfs_do_fsync(ctx, inode);
    if (!status)
        nfs_revalidate_inode(NFS_SERVER(inode), inode);
    return status;
}
Exemple #11
0
/*
 * Flush all dirty pages, and check for write errors.
 *
 */
static int
nfs_file_flush(struct file *file)
{
	struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
	struct inode	*inode = file->f_dentry->d_inode;
	int		status;

	dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);

	if ((file->f_mode & FMODE_WRITE) == 0)
		return 0;
	lock_kernel();
	/* Ensure that data+attribute caches are up to date after close() */
	status = nfs_wb_all(inode);
	if (!status) {
		status = ctx->error;
		ctx->error = 0;
		if (!status)
			nfs_revalidate_inode(NFS_SERVER(inode), inode);
	}
	unlock_kernel();
	return status;
}
int
nfs_permission(struct inode *inode, int mask)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
	struct rpc_cred *cred;
	int mode = inode->i_mode;
	int error;

	if (mask & MAY_WRITE) {
		/*
		 *
		 * Nobody gets write access to a read-only fs.
		 *
		 */
		if (IS_RDONLY(inode) &&
		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
			return -EROFS;

		/*
		 *
		 * Nobody gets write access to an immutable file.
		 *
		 */
		if (IS_IMMUTABLE(inode))
			return -EACCES;
	}

	if ((server->flags & NFS_MOUNT_NOACL) || !NFS_PROTO(inode)->access)
		goto out_notsup;
	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
	if (cache->cred == cred
	    && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) {
		if (!cache->err) {
			/* Is the mask a subset of an accepted mask? */
			if ((cache->mask & mask) == mask)
				goto out_cached;
	       	} else {
			/* ...or is it a superset of a rejected mask? */
			if ((cache->mask & mask) == cache->mask)
				goto out_cached;
		}
	}
	error = NFS_PROTO(inode)->access(inode, cred, mask);
	if (!error || error == -EACCES) {
		cache->jiffies = jiffies;
		if (cache->cred)
			put_rpccred(cache->cred);
		cache->cred = cred;
		cache->mask = mask;
		cache->err = error;
		return error;
	}
	put_rpccred(cred);
out_notsup:
	nfs_revalidate_inode(NFS_SERVER(inode), inode);
	return vfs_permission(inode, mask);
out_cached:
	put_rpccred(cred);
	return cache->err;
}
Exemple #13
0
struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_fattr fattr;
	struct page *pages[NFSACL_MAXPAGES] = { };
	struct nfs3_getaclargs args = {
		.fh = NFS_FH(inode),
		/* The xdr layer may allocate pages here. */
		.pages = pages,
	};
	struct nfs3_getaclres res = {
		.fattr =	&fattr,
	};
	struct rpc_message msg = {
		.rpc_argp	= &args,
		.rpc_resp	= &res,
	};
	struct posix_acl *acl;
	int status, count;

	if (!nfs_server_capable(inode, NFS_CAP_ACLS))
		return ERR_PTR(-EOPNOTSUPP);

	status = nfs_revalidate_inode(server, inode);
	if (status < 0)
		return ERR_PTR(status);
	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
		nfs_zap_acl_cache(inode);
	acl = nfs3_get_cached_acl(inode, type);
	if (acl != ERR_PTR(-EAGAIN))
		return acl;
	acl = NULL;

	/*
	 * Only get the access acl when explicitly requested: We don't
	 * need it for access decisions, and only some applications use
	 * it. Applications which request the access acl first are not
	 * penalized from this optimization.
	 */
	if (type == ACL_TYPE_ACCESS)
		args.mask |= NFS_ACLCNT|NFS_ACL;
	if (S_ISDIR(inode->i_mode))
		args.mask |= NFS_DFACLCNT|NFS_DFACL;
	if (args.mask == 0)
		return NULL;

	dprintk("NFS call getacl\n");
	msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL];
	nfs_fattr_init(&fattr);
	status = rpc_call_sync(server->client_acl, &msg, 0);
	dprintk("NFS reply getacl: %d\n", status);

	/* pages may have been allocated at the xdr layer. */
	for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
		__free_page(args.pages[count]);

	switch (status) {
		case 0:
			status = nfs_refresh_inode(inode, &fattr);
			break;
		case -EPFNOSUPPORT:
		case -EPROTONOSUPPORT:
			dprintk("NFS_V3_ACL extension not supported; disabling\n");
			server->caps &= ~NFS_CAP_ACLS;
		case -ENOTSUPP:
			status = -EOPNOTSUPP;
		default:
			goto getout;
	}
	if ((args.mask & res.mask) != args.mask) {
		status = -EIO;
		goto getout;
	}

	if (res.acl_access != NULL) {
		if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
			posix_acl_release(res.acl_access);
			res.acl_access = NULL;
		}
	}
	nfs3_cache_acls(inode,
		(res.mask & NFS_ACL)   ? res.acl_access  : ERR_PTR(-EINVAL),
		(res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL));

	switch(type) {
		case ACL_TYPE_ACCESS:
			acl = res.acl_access;
			res.acl_access = NULL;
			break;

		case ACL_TYPE_DEFAULT:
			acl = res.acl_default;
			res.acl_default = NULL;
	}

getout:
	posix_acl_release(res.acl_access);
	posix_acl_release(res.acl_default);

	if (status != 0) {
		posix_acl_release(acl);
		acl = ERR_PTR(status);
	}
	return acl;
}
/*
 * This is called every time the dcache has a lookup hit,
 * and we should check whether we can really trust that
 * lookup.
 *
 * NOTE! The hit can be a negative hit too, don't assume
 * we have an inode!
 *
 * If the parent directory is seen to have changed, we throw out the
 * cached dentry and do a new lookup.
 */
static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
{
	struct inode *dir;
	struct inode *inode;
	struct dentry *parent;
	int error;
	struct nfs_fh fhandle;
	struct nfs_fattr fattr;
	unsigned long verifier;
	int isopen = 0;

	parent = dget_parent(dentry);
	lock_kernel();
	dir = parent->d_inode;
	inode = dentry->d_inode;

	if (nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_OPEN))
		isopen = 1;

	if (!inode) {
		if (nfs_neg_need_reval(dir, dentry, nd))
			goto out_bad;
		goto out_valid;
	}

	if (is_bad_inode(inode)) {
		dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
			dentry->d_parent->d_name.name, dentry->d_name.name);
		goto out_bad;
	}

	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	/* Force a full look up iff the parent directory has changed */
	if (nfs_check_verifier(dir, dentry)) {
		if (nfs_lookup_verify_inode(inode, isopen))
			goto out_zap_parent;
		goto out_valid;
	}

	/*
	 * Note: we're not holding inode->i_sem and so may be racing with
	 * operations that change the directory. We therefore save the
	 * change attribute *before* we do the RPC call.
	 */
	verifier = nfs_save_change_attribute(dir);
	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
	if (!error) {
		if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
			goto out_bad;
		if (nfs_lookup_verify_inode(inode, isopen))
			goto out_zap_parent;
		goto out_valid_renew;
	}

	if (NFS_STALE(inode))
		goto out_bad;

	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
	if (error)
		goto out_bad;
	if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
		goto out_bad;
	if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
		goto out_bad;

 out_valid_renew:
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, verifier);
 out_valid:
	unlock_kernel();
	dput(parent);
	return 1;
out_zap_parent:
	nfs_zap_caches(dir);
 out_bad:
	NFS_CACHEINV(dir);
	if (inode && S_ISDIR(inode->i_mode)) {
		/* Purge readdir caches. */
		nfs_zap_caches(inode);
		/* If we have submounts, don't unhash ! */
		if (have_submounts(dentry))
			goto out_valid;
		shrink_dcache_parent(dentry);
	}
	d_drop(dentry);
	unlock_kernel();
	dput(parent);
	return 0;
}
/* The file offset position is now represented as a true offset into the
 * page cache as is the case in most of the other filesystems.
 */
static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	struct dentry	*dentry = filp->f_dentry;
	struct inode	*inode = dentry->d_inode;
	nfs_readdir_descriptor_t my_desc,
			*desc = &my_desc;
	struct nfs_entry my_entry;
	struct nfs_fh	 fh;
	struct nfs_fattr fattr;
	long		res;

	lock_kernel();

	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
	if (res < 0) {
		unlock_kernel();
		return res;
	}

	/*
	 * filp->f_pos points to the file offset in the page cache.
	 * but if the cache has meanwhile been zapped, we need to
	 * read from the last dirent to revalidate f_pos
	 * itself.
	 */
	memset(desc, 0, sizeof(*desc));

	desc->file = filp;
	desc->target = filp->f_pos;
	desc->decode = NFS_PROTO(inode)->decode_dirent;
	desc->plus = NFS_USE_READDIRPLUS(inode);

	my_entry.cookie = my_entry.prev_cookie = 0;
	my_entry.eof = 0;
	my_entry.fh = &fh;
	my_entry.fattr = &fattr;
	desc->entry = &my_entry;

	while(!desc->entry->eof) {
		res = readdir_search_pagecache(desc);
		if (res == -EBADCOOKIE) {
			/* This means either end of directory */
			if (desc->entry->cookie != desc->target) {
				/* Or that the server has 'lost' a cookie */
				res = uncached_readdir(desc, dirent, filldir);
				if (res >= 0)
					continue;
			}
			res = 0;
			break;
		}
		if (res == -ETOOSMALL && desc->plus) {
			NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
			nfs_zap_caches(inode);
			desc->plus = 0;
			desc->entry->eof = 0;
			continue;
		}
		if (res < 0)
			break;

		res = nfs_do_filldir(desc, dirent, filldir);
		if (res < 0) {
			res = 0;
			break;
		}
	}
	unlock_kernel();
	if (desc->error < 0)
		return desc->error;
	if (res < 0)
		return res;
	return 0;
}
int
nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{
	struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
	struct rpc_cred *cred;
	int mode = inode->i_mode;
	int res;

	if (mask == 0)
		return 0;
	if (mask & MAY_WRITE) {
		/*
		 *
		 * Nobody gets write access to a read-only fs.
		 *
		 */
		if (IS_RDONLY(inode) &&
		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
			return -EROFS;

		/*
		 *
		 * Nobody gets write access to an immutable file.
		 *
		 */
		if (IS_IMMUTABLE(inode))
			return -EACCES;
	}
	/* Are we checking permissions on anything other than lookup/execute? */
	if ((mask & MAY_EXEC) == 0) {
		/* We only need to check permissions on file open() and access() */
		if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS)))
			return 0;
		/* NFSv4 has atomic_open... */
		if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN))
			return 0;
	}

	lock_kernel();

	if (!NFS_PROTO(inode)->access)
		goto out_notsup;

	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
	if (cache->cred == cred
	    && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
	    && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
		if (!(res = cache->err)) {
			/* Is the mask a subset of an accepted mask? */
			if ((cache->mask & mask) == mask)
				goto out;
		} else {
			/* ...or is it a superset of a rejected mask? */
			if ((cache->mask & mask) == cache->mask)
				goto out;
		}
	}

	res = NFS_PROTO(inode)->access(inode, cred, mask);
	if (!res || res == -EACCES)
		goto add_cache;
out:
	put_rpccred(cred);
	unlock_kernel();
	return res;
out_notsup:
	nfs_revalidate_inode(NFS_SERVER(inode), inode);
	res = vfs_permission(inode, mask);
	unlock_kernel();
	return res;
add_cache:
	cache->jiffies = jiffies;
	if (cache->cred)
		put_rpccred(cache->cred);
	cache->cred = cred;
	cache->mask = mask;
	cache->err = res;
	unlock_kernel();
	return res;
}