summaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/dir.c223
-rw-r--r--fs/nfs/file.c26
-rw-r--r--fs/nfs/inode.c15
-rw-r--r--fs/nfs/mount_clnt.c1
-rw-r--r--fs/nfs/write.c61
5 files changed, 183 insertions, 143 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 66b33161f..7cc5ae160 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -29,11 +29,6 @@
#include <asm/segment.h> /* for fs functions */
-#define NFS_MAX_AGE 10*HZ /* max age for dentry validation */
-
-/* needed by smbfs as well ... move to dcache? */
-extern void nfs_renew_times(struct dentry *);
-
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
@@ -79,6 +74,7 @@ static struct file_operations nfs_dir_operations = {
NULL, /* ioctl - default */
NULL, /* mmap */
nfs_dir_open, /* open - revalidate */
+ NULL, /* flush */
NULL, /* no special release code */
NULL /* fsync */
};
@@ -375,6 +371,7 @@ nfs_free_dircache(void)
nfs_invalidate_dircache_sb(NULL);
}
+#define NFS_REVALIDATE_INTERVAL (5*HZ)
/*
* This is called every time the dcache has a lookup hit,
* and we should check whether we can really trust that
@@ -383,47 +380,80 @@ nfs_free_dircache(void)
* NOTE! The hit can be a negative hit too, don't assume
* we have an inode!
*
- * The decision to drop the dentry should probably be
- * smarter than this. Right now we believe in directories
- * for 10 seconds, and in normal files for five..
+ * If the dentry is older than the revalidation interval,
+ * we do a new lookup and verify that the dentry is still
+ * correct.
*/
static int nfs_lookup_revalidate(struct dentry * dentry)
{
+ struct dentry * parent = dentry->d_parent;
+ struct inode * inode = dentry->d_inode;
unsigned long time = jiffies - dentry->d_time;
- unsigned long max = 5*HZ;
+ int error;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
- if (dentry->d_inode) {
- if (is_bad_inode(dentry->d_inode)) {
+ if (inode && is_bad_inode(inode)) {
#ifdef NFS_PARANOIA
printk("nfs_lookup_validate: %s/%s has dud inode\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
+parent->d_name.name, dentry->d_name.name);
#endif
- goto bad;
- }
- if (S_ISDIR(dentry->d_inode->i_mode))
- max = NFS_MAX_AGE;
+ goto out_bad;
+ }
+
+ if (time < NFS_REVALIDATE_INTERVAL)
+ goto out_valid;
+ /*
+ * Don't bother looking up a negative dentry ...
+ */
+ if (!inode)
+ goto out_bad;
+
+ if (IS_ROOT(dentry))
+ goto out_valid;
+ /*
+ * Do a new lookup and check the dentry attributes.
+ */
+ error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent),
+ dentry->d_name.name, &fhandle, &fattr);
+ if (error) {
+printk("nfs_lookup_revalidate: error=%d\n", error);
+ goto out_bad;
}
- return (time < max) || IS_ROOT(dentry);
-bad:
+ /* Inode number matches? */
+ if (fattr.fileid != inode->i_ino) {
+printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n",
+parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid);
+ goto out_bad;
+ }
+ /* Filehandle matches? */
+ if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
+printk("nfs_lookup_revalidate: %s/%s fh changed\n",
+parent->d_name.name, dentry->d_name.name);
+ goto out_bad;
+ }
+
+out_valid:
+ return 1;
+out_bad:
return 0;
}
/*
* This is called from dput() when d_count is going to 0.
- * We use it to clean up silly-renamed files, and to check
- * for dentries that have already expired.
+ * We use it to clean up silly-renamed files.
*/
static void nfs_dentry_delete(struct dentry *dentry)
{
+ dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_flags);
+
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
int error;
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_dentry_delete: unlinking %s/%s\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-#endif
/* Unhash it first */
d_drop(dentry);
error = nfs_safe_remove(dentry);
@@ -432,14 +462,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
dentry->d_parent->d_name.name,
dentry->d_name.name, error);
}
- /*
- * Check whether to expire the dentry ...
- */
- else {
- unsigned long age = jiffies - dentry->d_time;
- if (age > NFS_MAX_AGE)
- d_drop(dentry);
- }
#ifdef NFS_PARANOIA
/*
@@ -464,6 +486,10 @@ inode->i_ino, inode->i_count, inode->i_nlink);
*/
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{
+ dfprintk(VFS, "NFS: dentry_iput(%s/%s, cnt=%d, ino=%ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_count, inode->i_ino);
+
if (NFS_WRITEBACK(inode)) {
#ifdef NFS_PARANOIA
printk("nfs_dentry_iput: pending writes for %s/%s, i_count=%d\n",
@@ -522,17 +548,12 @@ static void show_dentry(struct list_head * dlist)
#endif
/*
- * Whenever a lookup succeeds, we know the parent directories
- * are all valid, so we want to update the dentry timestamps.
+ * Whenever an NFS operation succeeds, we know that the dentry
+ * is valid, so we update the revalidation timestamp.
*/
-void nfs_renew_times(struct dentry * dentry)
+static inline void nfs_renew_times(struct dentry * dentry)
{
- for (;;) {
- dentry->d_time = jiffies;
- if (dentry == dentry->d_parent)
- break;
- dentry = dentry->d_parent;
- }
+ dentry->d_time = jiffies;
}
static int nfs_lookup(struct inode *dir, struct dentry * dentry)
@@ -545,11 +566,6 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
dfprintk(VFS, "NFS: lookup(%s/%s)\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_lookup: inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
@@ -634,12 +650,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
struct nfs_fh fhandle;
dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_create: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -674,12 +685,7 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
struct nfs_fh fhandle;
dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_mknod: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
@@ -711,12 +717,7 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
struct nfs_fh fhandle;
dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_mkdir: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -753,12 +754,7 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
int error, rehash = 0;
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_rmdir: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -867,6 +863,10 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
struct dentry *sdentry;
int error = -EIO;
+ dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_count);
+
/*
* Note that a silly-renamed file can be deleted once it's
* no longer in use -- it's just an ordinary file now.
@@ -939,6 +939,10 @@ static int nfs_safe_remove(struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int error, rehash = 0;
+ dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino);
+
/* N.B. not needed now that d_delete is done in advance? */
error = -EBUSY;
if (inode) {
@@ -1009,12 +1013,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
int error;
dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_unlink: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -1038,12 +1037,7 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
int error;
dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
- dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
-
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_symlink: inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
@@ -1095,11 +1089,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
dentry->d_parent->d_name.name, dentry->d_name.name);
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("nfs_link: dir is NULL or not a directory\n");
- return -ENOENT;
- }
-
error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
goto out;
@@ -1153,22 +1142,13 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
{
struct inode *old_inode = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode;
+ struct dentry *dentry = NULL;
int error, rehash = 0, update = 1;
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n",
-old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count,
-new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
-#endif
- if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
- printk("nfs_rename: old inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
- if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
- printk("nfs_rename: new inode is NULL or not a directory\n");
- return -ENOENT;
- }
+ dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+ new_dentry->d_count);
error = -ENAMETOOLONG;
if (old_dentry->d_name.len > NFS_MAXNAMLEN ||
@@ -1178,16 +1158,43 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
/*
* First check whether the target is busy ... we can't
* safely do _any_ rename if the target is in use.
+ *
+ * For files, make a copy of the dentry and then do a
+ * silly-rename. If the silly-rename succeeds, the
+ * copied dentry is hashed and becomes the new target.
+ *
+ * For directories, prune any unused children.
*/
- if (new_dentry->d_count > 1 && !list_empty(&new_dentry->d_subdirs))
- shrink_dcache_parent(new_dentry);
error = -EBUSY;
- if (new_dentry->d_count > 1) {
+ if (new_dentry->d_count > 1 && new_inode) {
+ if (S_ISREG(new_inode->i_mode)) {
+ int err;
+ /* copy the target dentry's name */
+ dentry = d_alloc(new_dentry->d_parent,
+ &new_dentry->d_name);
+ if (!dentry)
+ goto out;
+
+ /* silly-rename the existing target ... */
+ err = nfs_sillyrename(new_dir, new_dentry);
+ if (!err) {
+ new_dentry = dentry;
+ new_inode = NULL;
+ /* hash the replacement target */
+ d_add(new_dentry, NULL);
+ }
+ } else if (!list_empty(&new_dentry->d_subdirs)) {
+ shrink_dcache_parent(new_dentry);
+ }
+
+ /* dentry still busy? */
+ if (new_dentry->d_count > 1) {
#ifdef NFS_PARANOIA
printk("nfs_rename: target %s/%s busy, d_count=%d\n",
new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
#endif
- goto out;
+ goto out;
+ }
}
/*
@@ -1231,7 +1238,7 @@ old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count);
#endif
goto out;
}
- if (new_dentry->d_count > 1) {
+ if (new_dentry->d_count > 1 && new_inode) {
#ifdef NFS_PARANOIA
printk("nfs_rename: new dentry %s/%s busy, d_count=%d\n",
new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
@@ -1274,7 +1281,11 @@ new_inode->i_count, new_inode->i_nlink);
if (update)
d_move(old_dentry, new_dentry);
}
+
out:
+ /* new dentry created? */
+ if (dentry)
+ dput(dentry);
return error;
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 9ec883aff..973ad52ec 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -35,6 +35,7 @@
static int nfs_file_mmap(struct file *, struct vm_area_struct *);
static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *);
static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
+static int nfs_file_flush(struct file *);
static int nfs_file_close(struct inode *, struct file *);
static int nfs_fsync(struct file *, struct dentry *dentry);
@@ -47,6 +48,7 @@ static struct file_operations nfs_file_operations = {
NULL, /* ioctl - default */
nfs_file_mmap, /* mmap */
NULL, /* no special open is needed */
+ nfs_file_flush, /* flush */
nfs_file_close, /* release */
nfs_fsync, /* fsync */
NULL, /* fasync */
@@ -84,13 +86,7 @@ struct inode_operations nfs_file_inode_operations = {
#endif
/*
- * Flush all dirty pages, and check for write errors.
- *
- * Note that since the file close operation is called only by the
- * _last_ process to close the file, we need to flush _all_ dirty
- * pages. This also means that there is little sense in checking
- * for errors for this specific process -- we should probably just
- * clear all errors.
+ * Sync the file..
*/
static int
nfs_file_close(struct inode *inode, struct file *file)
@@ -106,6 +102,22 @@ nfs_file_close(struct inode *inode, struct file *file)
return status;
}
+/*
+ * Flush all dirty pages, and check for write errors.
+ *
+ * We should probably do this better - this does get called at every
+ * close, so we should probably just flush the changes that "this"
+ * file has done and report on only those.
+ *
+ * Right now we use the "flush everything" approach. Overkill, but
+ * works.
+ */
+static int
+nfs_file_flush(struct file *file)
+{
+ return nfs_file_close(file->f_dentry->d_inode, file);
+}
+
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index e89abdbce..597821270 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -652,6 +652,7 @@ _nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
int status = 0;
struct nfs_fattr fattr;
+ /* Don't bother revalidating if we've done it recently */
if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
goto out;
@@ -746,6 +747,20 @@ nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
goto out_changed;
/*
+ * If we have pending write-back entries, we don't want
+ * to look at the size the server sends us too closely..
+ * In particular, ignore the server if it tells us that
+ * the file is smaller or older than we locally think it
+ * is..
+ */
+ if (NFS_WRITEBACK(inode)) {
+ if (inode->i_size > fattr->size)
+ fattr->size = inode->i_size;
+ if (inode->i_mtime > fattr->mtime.seconds)
+ fattr->mtime.seconds = inode->i_mtime;
+ }
+
+ /*
* If the size or mtime changed from outside, we want
* to invalidate the local caches immediately.
*/
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 81da8f996..3ecb9bfa6 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -78,6 +78,7 @@ mnt_create(char *hostname, struct sockaddr_in *srvaddr)
clnt->cl_softrtry = 1;
clnt->cl_chatty = 1;
clnt->cl_oneshot = 1;
+ clnt->cl_intr = 1;
}
return clnt;
}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index c317f330b..f8658f37f 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -56,21 +56,12 @@
#include <linux/nfs_fs.h>
#include <asm/uaccess.h>
-/*
- * NOTE! We must NOT default to soft-mounting: that breaks too many
- * programs that depend on POSIX behaviour of uninterruptible reads
- * and writes.
- *
- * Until we have a per-mount soft/hard mount policy that we can honour
- * we must default to hard mounting!
- */
-#define IS_SOFT 0
-
#define NFS_PARANOIA 1
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
static void nfs_wback_lock(struct rpc_task *task);
static void nfs_wback_result(struct rpc_task *task);
+static void nfs_cancel_request(struct nfs_wreq *req);
/*
* Cache parameters
@@ -258,7 +249,7 @@ nfs_find_dentry_request(struct inode *inode, struct dentry *dentry)
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- if (req->wb_dentry == dentry) {
+ if (req->wb_dentry == dentry && !WB_CANCELLED(req)) {
found = 1;
break;
}
@@ -442,11 +433,15 @@ schedule_write_request(struct nfs_wreq *req, int sync)
sync = 1;
if (sync) {
+ sigset_t oldmask;
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
dprintk("NFS: %4d schedule_write_request (sync)\n",
task->tk_pid);
/* Page is already locked */
req->wb_flags |= NFS_WRITE_LOCKED;
+ rpc_clnt_sigmask(clnt, &oldmask);
rpc_execute(task);
+ rpc_clnt_sigunmask(clnt, &oldmask);
} else {
dprintk("NFS: %4d schedule_write_request (async)\n",
task->tk_pid);
@@ -467,17 +462,20 @@ wait_on_write_request(struct nfs_wreq *req)
{
struct wait_queue wait = { current, NULL };
struct page *page = req->wb_page;
- int retval;
+ int retval;
+ sigset_t oldmask;
+ struct rpc_clnt *clnt = NFS_CLIENT(req->wb_inode);
+ rpc_clnt_sigmask(clnt, &oldmask);
add_wait_queue(&page->wait, &wait);
atomic_inc(&page->count);
for (;;) {
- current->state = IS_SOFT ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
retval = 0;
if (!PageLocked(page))
break;
retval = -ERESTARTSYS;
- if (IS_SOFT && signalled())
+ if (signalled())
break;
schedule();
}
@@ -485,6 +483,7 @@ wait_on_write_request(struct nfs_wreq *req)
current->state = TASK_RUNNING;
/* N.B. page may have been unused, so we must use free_page() */
free_page(page_address(page));
+ rpc_clnt_sigunmask(clnt, &oldmask);
return retval;
}
@@ -534,6 +533,10 @@ nfs_updatepage(struct file *file, struct page *page, const char *buffer,
if ((req = find_write_request(inode, page)) != NULL) {
if (update_write_request(req, offset, count)) {
/* N.B. check for a fault here and cancel the req */
+ /*
+ * SECURITY - copy_from_user must zero the
+ * rest of the data after a fault!
+ */
copy_from_user(page_addr + offset, buffer, count);
goto updated;
}
@@ -583,8 +586,11 @@ done:
transfer_page_lock(req);
/* rpc_execute(&req->wb_task); */
if (sync) {
- /* N.B. if signalled, result not ready? */
- wait_on_write_request(req);
+ /* if signalled, ensure request is cancelled */
+ if ((count = wait_on_write_request(req)) != 0) {
+ nfs_cancel_request(req);
+ status = count;
+ }
if ((count = nfs_write_error(inode)) < 0)
status = count;
}
@@ -716,29 +722,21 @@ int
nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
{
struct nfs_wreq *last = NULL;
- int result = 0, cancel = 0;
+ int result = 0;
dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
inode->i_dev, inode->i_ino, current->pid, offset, len);
- if (IS_SOFT && signalled()) {
- nfs_cancel_dirty(inode, pid);
- cancel = 1;
- }
-
for (;;) {
- if (IS_SOFT && signalled()) {
- if (!cancel)
- nfs_cancel_dirty(inode, pid);
- result = -ERESTARTSYS;
- break;
- }
-
/* Flush all pending writes for the pid and file region */
last = nfs_flush_pages(inode, pid, offset, len, 0);
if (last == NULL)
break;
- wait_on_write_request(last);
+ result = wait_on_write_request(last);
+ if (result) {
+ nfs_cancel_dirty(inode,pid);
+ break;
+ }
}
return result;
@@ -889,6 +887,9 @@ nfs_wback_result(struct rpc_task *task)
/* Update attributes as result of writeback.
* Beware: when UDP replies arrive out of order, we
* may end up overwriting a previous, bigger file size.
+ *
+ * When the file size shrinks we cancel all pending
+ * writebacks.
*/
if (fattr->mtime.seconds >= inode->i_mtime) {
if (fattr->size < inode->i_size)