diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 75 | ||||
-rw-r--r-- | fs/nfs/inode.c | 16 | ||||
-rw-r--r-- | fs/nfs/write.c | 7 |
3 files changed, 64 insertions, 34 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c74c73243..aaf17187b 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -471,11 +471,11 @@ void nfs_renew_times(struct dentry * dentry) static int nfs_lookup(struct inode *dir, struct dentry * dentry) { + int len = dentry->d_name.len; struct inode *inode; + int error; struct nfs_fh fhandle; struct nfs_fattr fattr; - int len = dentry->d_name.len; - int error; dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", dir->i_dev, dir->i_ino, len, dentry->d_name.name); @@ -516,10 +516,38 @@ out: } /* + * Attempt to patch up certain errors following a create or + * mkdir operation. We clear the original error if the new + * lookup succeeds and has the correct mode. + */ +static int nfs_fixup(struct inode *dir, struct dentry *dentry, int mode, + struct nfs_fh *fhandle, struct nfs_fattr *fattr, int error) +{ + int newerr; + +#ifdef NFS_PARANOIA +printk("nfs_fixup: %s/%s, error=%d, mode=%x\n", +dentry->d_parent->d_name.name, dentry->d_name.name, error, mode); +#endif + if (error == -EEXIST) { + newerr = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, fhandle, fattr); + if (!newerr) { +#ifdef NFS_PARANOIA +printk("nfs_fixup: lookup OK, got mode=%x, want mode=%x\n", fattr->mode, mode); +#endif + if ((fattr->mode & S_IFMT) == (mode & S_IFMT)) + error = 0; + } + } + return error; +} + +/* * Code common to create, mkdir, and mknod. */ -static int nfs_instantiate(struct dentry *dentry, struct nfs_fattr *fattr, - struct nfs_fh *fhandle) +static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) { struct inode *inode; int error = -EACCES; @@ -545,12 +573,12 @@ inode->i_ino, inode->i_count, inode->i_nlink); * that the operation succeeded on the server, but an error in the * reply path made it appear to have failed. */ -static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) +static int nfs_create(struct inode *dir, struct dentry *dentry, int mode) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -574,9 +602,11 @@ static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) - error = nfs_instantiate(dentry, &fattr, &fhandle); - else + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error) d_drop(dentry); out: return error; @@ -587,10 +617,10 @@ out: */ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -612,9 +642,11 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde nfs_invalidate_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) - error = nfs_instantiate(dentry, &fattr, &fhandle); - else + error = nfs_instantiate(dentry, &fhandle, &fattr); + if (error) d_drop(dentry); return error; } @@ -624,10 +656,10 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde */ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { + int error; struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", dir->i_dev, dir->i_ino, dentry->d_name.name); @@ -640,6 +672,8 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; + /* For some reason mode doesn't have the S_IFDIR flag ... */ + mode |= S_IFDIR; sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; @@ -647,6 +681,8 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) nfs_invalidate_dircache(dir); error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &sattr, &fhandle, &fattr); + if (error) + error = nfs_fixup(dir, dentry, mode, &fhandle, &fattr, error); if (!error) { /* * Some AIX servers reportedly fail to fill out the fattr. @@ -660,8 +696,9 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) fattr.mode); goto drop; } - error = nfs_instantiate(dentry, &fattr, &fhandle); - } else { + error = nfs_instantiate(dentry, &fhandle, &fattr); + } + if (error) { drop: d_drop(dentry); } @@ -858,11 +895,8 @@ out: * Remove a file after making sure there are no pending writes, * and after checking that the file has only one user. * - * Updating inode->i_nlink here rather than waiting for the next - * nfs_refresh_inode() is not merely cosmetic; once an object has - * been deleted, we want to get rid of the inode locally. The NFS - * server may reuse the fileid for a new inode, and we don't want - * that to be confused with this inode. + * We update inode->i_nlink and free the inode prior to the operation + * to avoid possible races if the server reuses the inode. */ static int nfs_safe_remove(struct dentry *dentry) { @@ -870,6 +904,7 @@ static int nfs_safe_remove(struct dentry *dentry) struct inode *inode = dentry->d_inode; int error, rehash = 0; + /* N.B. not needed now that d_delete is done in advance? */ error = -EBUSY; if (inode) { if (NFS_WRITEBACK(inode)) { @@ -897,7 +932,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); goto out; } #ifdef NFS_PARANOIA -if (inode && inode->i_count > 1) +if (inode && inode->i_count > inode->i_nlink) printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d, i_nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count, inode->i_nlink); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c070d130b..eb56950eb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -35,9 +35,6 @@ #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_PARANOIA 1 -extern void nfs_invalidate_dircache_sb(struct super_block *); -extern int check_failed_request(struct inode *); - static void nfs_read_inode(struct inode *); static void nfs_put_inode(struct inode *); static void nfs_delete_inode(struct inode *); @@ -99,8 +96,9 @@ nfs_delete_inode(struct inode * inode) */ if (NFS_WRITEBACK(inode) != NULL) { unsigned long timeout = jiffies + 5*HZ; - printk("NFS: inode %ld, invalidating pending RPC requests\n", - inode->i_ino); +#ifdef NFS_DEBUG_VERBOSE +printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); +#endif nfs_invalidate_pages(inode); while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) { current->state = TASK_INTERRUPTIBLE; @@ -112,7 +110,7 @@ nfs_delete_inode(struct inode * inode) printk("NFS: Arghhh, stuck RPC requests!\n"); } - failed = check_failed_request(inode); + failed = nfs_check_failed_request(inode); if (failed) printk("NFS: inode %ld had %d failed requests\n", inode->i_ino, failed); @@ -355,8 +353,6 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) * instead of inode number. We use this technique instead of using * the vfs read_inode function because there is no way to pass the * file handle or current attributes into the read_inode function. - * We just have to be careful not to subvert iget's special handling - * of mount points. */ struct inode * nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, @@ -422,8 +418,10 @@ printk("nfs_fhget: impossible\n"); inode->i_size = fattr->size; inode->i_mtime = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds; + *NFS_FH(inode) = *fhandle; } - *NFS_FH(inode) = *fhandle; + if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh))) + printk("nfs_fhget: fhandle changed!\n"); nfs_refresh_inode(inode, fattr); dprintk("NFS: fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 97663cc11..53c227e58 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -68,8 +68,6 @@ #define NFSDBG_FACILITY NFSDBG_PAGECACHE -int check_failed_request(struct inode *); - static void nfs_wback_lock(struct rpc_task *task); static void nfs_wback_result(struct rpc_task *task); @@ -331,7 +329,7 @@ remove_failed_request(struct nfs_wreq * req) * Find and release all failed requests for this inode. */ int -check_failed_request(struct inode * inode) +nfs_check_failed_request(struct inode * inode) { struct nfs_wreq * req; int found = 0; @@ -496,8 +494,7 @@ wait_on_write_request(struct nfs_wreq *req) } remove_wait_queue(&page->wait, &wait); current->state = TASK_RUNNING; - if (atomic_read(&page->count) == 1) - printk("NFS: page unused while waiting\n"); + /* N.B. page may have been unused, so we must use free_page() */ free_page(page_address(page)); return retval; } |