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

Commit 980802e3 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds
Browse files

[PATCH] NFS: Ensure that we revalidate the cached file length for llseek(SEEK_END)



This fixes a data corruption error for mail delivery applications that
expect to be able to do posix locking and then append writes on NFS.

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c22fa3ac
Loading
Loading
Loading
Loading
+41 −1
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@


static int nfs_file_open(struct inode *, struct file *);
static int nfs_file_open(struct inode *, struct file *);
static int nfs_file_release(struct inode *, struct file *);
static int nfs_file_release(struct inode *, struct file *);
static loff_t nfs_file_llseek(struct file *file, loff_t offset, int origin);
static int  nfs_file_mmap(struct file *, struct vm_area_struct *);
static int  nfs_file_mmap(struct file *, struct vm_area_struct *);
static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *);
static ssize_t nfs_file_read(struct kiocb *, char __user *, size_t, loff_t);
static ssize_t nfs_file_read(struct kiocb *, char __user *, size_t, loff_t);
@@ -48,7 +49,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl);
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl);
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl);


struct file_operations nfs_file_operations = {
struct file_operations nfs_file_operations = {
	.llseek		= remote_llseek,
	.llseek		= nfs_file_llseek,
	.read		= do_sync_read,
	.read		= do_sync_read,
	.write		= do_sync_write,
	.write		= do_sync_write,
	.aio_read		= nfs_file_read,
	.aio_read		= nfs_file_read,
@@ -114,6 +115,45 @@ nfs_file_release(struct inode *inode, struct file *filp)
	return NFS_PROTO(inode)->file_release(inode, filp);
	return NFS_PROTO(inode)->file_release(inode, filp);
}
}


/**
 * nfs_revalidate_size - Revalidate the file size
 * @inode - pointer to inode struct
 * @file - pointer to struct file
 *
 * Revalidates the file length. This is basically a wrapper around
 * nfs_revalidate_inode() that takes into account the fact that we may
 * have cached writes (in which case we don't care about the server's
 * idea of what the file length is), or O_DIRECT (in which case we
 * shouldn't trust the cache).
 */
static int nfs_revalidate_file_size(struct inode *inode, struct file *filp)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_inode *nfsi = NFS_I(inode);

	if (server->flags & NFS_MOUNT_NOAC)
		goto force_reval;
	if (filp->f_flags & O_DIRECT)
		goto force_reval;
	if (nfsi->npages != 0)
		return 0;
	return nfs_revalidate_inode(server, inode);
force_reval:
	return __nfs_revalidate_inode(server, inode);
}

static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
{
	/* origin == SEEK_END => we must revalidate the cached file length */
	if (origin == 2) {
		struct inode *inode = filp->f_mapping->host;
		int retval = nfs_revalidate_file_size(inode, filp);
		if (retval < 0)
			return (loff_t)retval;
	}
	return remote_llseek(filp, offset, origin);
}

/*
/*
 * Flush all dirty pages, and check for write errors.
 * Flush all dirty pages, and check for write errors.
 *
 *