diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-26 09:52:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-26 09:52:14 -0700 |
commit | 32e51f141fd8d880f57b6a2eb53ce72856254d4a (patch) | |
tree | d8d7a0d503533a03fb07b2ebd5eccd9043f2d228 | |
parent | ca16d140af91febe25daeb9e032bf8bd46b8c31f (diff) | |
parent | b6ff24a333267a6810e28ee5b9fc539d149c52f0 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (25 commits)
cifs: remove unnecessary dentry_unhash on rmdir/rename_dir
ocfs2: remove unnecessary dentry_unhash on rmdir/rename_dir
exofs: remove unnecessary dentry_unhash on rmdir/rename_dir
nfs: remove unnecessary dentry_unhash on rmdir/rename_dir
ext2: remove unnecessary dentry_unhash on rmdir/rename_dir
ext3: remove unnecessary dentry_unhash on rmdir/rename_dir
ext4: remove unnecessary dentry_unhash on rmdir/rename_dir
btrfs: remove unnecessary dentry_unhash in rmdir/rename_dir
ceph: remove unnecessary dentry_unhash calls
vfs: clean up vfs_rename_other
vfs: clean up vfs_rename_dir
vfs: clean up vfs_rmdir
vfs: fix vfs_rename_dir for FS_RENAME_DOES_D_MOVE filesystems
libfs: drop unneeded dentry_unhash
vfs: update dentry_unhash() comment
vfs: push dentry_unhash on rename_dir into file systems
vfs: push dentry_unhash on rmdir into file systems
vfs: remove dget() from dentry_unhash()
vfs: dentry_unhash immediately prior to rmdir
vfs: Block mmapped writes while the fs is frozen
...
-rw-r--r-- | fs/9p/vfs_inode.c | 4 | ||||
-rw-r--r-- | fs/Kconfig | 2 | ||||
-rw-r--r-- | fs/affs/namei.c | 5 | ||||
-rw-r--r-- | fs/afs/dir.c | 5 | ||||
-rw-r--r-- | fs/autofs4/root.c | 2 | ||||
-rw-r--r-- | fs/bfs/dir.c | 3 | ||||
-rw-r--r-- | fs/buffer.c | 59 | ||||
-rw-r--r-- | fs/coda/dir.c | 5 | ||||
-rw-r--r-- | fs/configfs/dir.c | 2 | ||||
-rw-r--r-- | fs/ecryptfs/inode.c | 5 | ||||
-rw-r--r-- | fs/fat/namei_msdos.c | 5 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 5 | ||||
-rw-r--r-- | fs/fuse/dir.c | 6 | ||||
-rw-r--r-- | fs/hfs/dir.c | 6 | ||||
-rw-r--r-- | fs/hfsplus/dir.c | 8 | ||||
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 5 | ||||
-rw-r--r-- | fs/hpfs/namei.c | 9 | ||||
-rw-r--r-- | fs/jffs2/dir.c | 5 | ||||
-rw-r--r-- | fs/jfs/namei.c | 5 | ||||
-rw-r--r-- | fs/logfs/dir.c | 5 | ||||
-rw-r--r-- | fs/minix/namei.c | 5 | ||||
-rw-r--r-- | fs/namei.c | 380 | ||||
-rw-r--r-- | fs/namespace.c | 2 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 5 | ||||
-rw-r--r-- | fs/nilfs2/namei.c | 5 | ||||
-rw-r--r-- | fs/omfs/dir.c | 11 | ||||
-rw-r--r-- | fs/reiserfs/namei.c | 5 | ||||
-rw-r--r-- | fs/reiserfs/xattr.c | 1 | ||||
-rw-r--r-- | fs/sysv/namei.c | 5 | ||||
-rw-r--r-- | fs/ubifs/dir.c | 5 | ||||
-rw-r--r-- | fs/udf/namei.c | 5 | ||||
-rw-r--r-- | fs/ufs/namei.c | 5 | ||||
-rw-r--r-- | include/linux/buffer_head.h | 16 |
33 files changed, 332 insertions, 269 deletions
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 7f6c6770319..8d7f3e69ae2 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -814,6 +814,7 @@ int v9fs_vfs_unlink(struct inode *i, struct dentry *d) int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) { + dentry_unhash(d); return v9fs_remove(i, d, 1); } @@ -839,6 +840,9 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct p9_fid *newdirfid; struct p9_wstat wstat; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + P9_DPRINTK(P9_DEBUG_VFS, "\n"); retval = 0; old_inode = old_dentry->d_inode; diff --git a/fs/Kconfig b/fs/Kconfig index f6edba2e069..19891aab9c6 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -47,7 +47,7 @@ config FS_POSIX_ACL def_bool n config EXPORTFS - bool + tristate config FILE_LOCKING bool "Enable POSIX file locking API" if EXPERT diff --git a/fs/affs/namei.c b/fs/affs/namei.c index e3e9efc1fdd..03330e2e390 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -320,6 +320,8 @@ affs_rmdir(struct inode *dir, struct dentry *dentry) dentry->d_inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name); + dentry_unhash(dentry); + return affs_remove_header(dentry); } @@ -417,6 +419,9 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct buffer_head *bh = NULL; int retval; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n", (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name, (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name); diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 20c106f2492..2c4e0516004 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -845,6 +845,8 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) _enter("{%x:%u},{%s}", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); + dentry_unhash(dentry); + ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; @@ -1146,6 +1148,9 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, struct key *key; int ret; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + vnode = AFS_FS_I(old_dentry->d_inode); orig_dvnode = AFS_FS_I(old_dir); new_dvnode = AFS_FS_I(new_dir); diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index f55ae23b137..87d95a8cddb 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -583,6 +583,8 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) if (!autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN)) return -EACCES; + dentry_unhash(dentry); + if (atomic_dec_and_test(&ino->count)) { p_ino = autofs4_dentry_ino(dentry->d_parent); if (p_ino && dentry->d_parent != dentry) diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index b14cebfd904..c7d1d06b048 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -224,6 +224,9 @@ static int bfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct bfs_sb_info *info; int error = -ENOENT; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + old_bh = new_bh = NULL; old_inode = old_dentry->d_inode; if (S_ISDIR(old_inode->i_mode)) diff --git a/fs/buffer.c b/fs/buffer.c index a08bb8e61c6..b0675bfe820 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2331,24 +2331,26 @@ EXPORT_SYMBOL(block_commit_write); * page lock we can determine safely if the page is beyond EOF. If it is not * beyond EOF, then the page is guaranteed safe against truncation until we * unlock the page. + * + * Direct callers of this function should call vfs_check_frozen() so that page + * fault does not busyloop until the fs is thawed. */ -int -block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, - get_block_t get_block) +int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, + get_block_t get_block) { struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; unsigned long end; loff_t size; - int ret = VM_FAULT_NOPAGE; /* make the VM retry the fault */ + int ret; lock_page(page); size = i_size_read(inode); if ((page->mapping != inode->i_mapping) || (page_offset(page) > size)) { - /* page got truncated out from underneath us */ - unlock_page(page); - goto out; + /* We overload EFAULT to mean page got truncated */ + ret = -EFAULT; + goto out_unlock; } /* page is wholly or partially inside EOF */ @@ -2361,18 +2363,41 @@ block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, if (!ret) ret = block_commit_write(page, 0, end); - if (unlikely(ret)) { - unlock_page(page); - if (ret == -ENOMEM) - ret = VM_FAULT_OOM; - else /* -ENOSPC, -EIO, etc */ - ret = VM_FAULT_SIGBUS; - } else - ret = VM_FAULT_LOCKED; - -out: + if (unlikely(ret < 0)) + goto out_unlock; + /* + * Freezing in progress? We check after the page is marked dirty and + * with page lock held so if the test here fails, we are sure freezing + * code will wait during syncing until the page fault is done - at that + * point page will be dirty and unlocked so freezing code will write it + * and writeprotect it again. + */ + set_page_dirty(page); + if (inode->i_sb->s_frozen != SB_UNFROZEN) { + ret = -EAGAIN; + goto out_unlock; + } + return 0; +out_unlock: + unlock_page(page); return ret; } +EXPORT_SYMBOL(__block_page_mkwrite); + +int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, + get_block_t get_block) +{ + int ret; + struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb; + + /* + * This check is racy but catches the common case. The check in + * __block_page_mkwrite() is reliable. + */ + vfs_check_frozen(sb, SB_FREEZE_WRITE); + ret = __block_page_mkwrite(vma, vmf, get_block); + return block_page_mkwrite_return(ret); +} EXPORT_SYMBOL(block_page_mkwrite); /* diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 2b8dae4d121..a46126fd573 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -336,6 +336,8 @@ static int coda_rmdir(struct inode *dir, struct dentry *de) int len = de->d_name.len; int error; + dentry_unhash(de); + error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); if (!error) { /* VFS may delete the child */ @@ -359,6 +361,9 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, int new_length = new_dentry->d_name.len; int error; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), coda_i2f(new_dir), old_length, new_length, (const char *) old_name, (const char *)new_name); diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 9a37a9b6de3..9d17d350abc 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1359,6 +1359,8 @@ static int configfs_rmdir(struct inode *dir, struct dentry *dentry) struct module *subsys_owner = NULL, *dead_item_owner = NULL; int ret; + dentry_unhash(dentry); + if (dentry->d_parent == configfs_sb->s_root) return -EPERM; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 4d4cc6a90cd..227b409b840 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -521,6 +521,8 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; int rc; + dentry_unhash(dentry); + lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); @@ -571,6 +573,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dir_dentry; struct dentry *trap = NULL; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry); lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry); dget(lower_old_dentry); diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 3b222dafd15..be15437c272 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -326,6 +326,8 @@ static int msdos_rmdir(struct inode *dir, struct dentry *dentry) struct fat_slot_info sinfo; int err; + dentry_unhash(dentry); + lock_super(sb); /* * Check whether the directory is not in use, then check @@ -457,6 +459,9 @@ static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; + if (new_inode && S_ISDIR(new_inode->i_mode)) + dentry_unhash(new_dentry); + err = fat_scan(old_dir, old_name, &old_sinfo); if (err) { err = -EIO; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 20b4ea53fdc..c61a6789f36 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -824,6 +824,8 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry) struct fat_slot_info sinfo; int err; + dentry_unhash(dentry); + lock_super(sb); err = fat_dir_empty(inode); @@ -931,6 +933,9 @@ static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, int err, is_dir, update_dotdot, corrupt = 0; struct super_block *sb = old_dir->i_sb; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index b32eb29a4e6..0d0e3faddcf 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -667,6 +667,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) if (IS_ERR(req)) return PTR_ERR(req); + dentry_unhash(entry); + req->in.h.opcode = FUSE_RMDIR; req->in.h.nodeid = get_node_id(dir); req->in.numargs = 1; @@ -691,6 +693,10 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, struct fuse_rename_in inarg; struct fuse_conn *fc = get_fuse_conn(olddir); struct fuse_req *req = fuse_get_req(fc); + + if (newent->d_inode && S_ISDIR(newent->d_inode->i_mode)) + dentry_unhash(newent); + if (IS_ERR(req)) return PTR_ERR(req); diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index b4d70b13be9..1cb70cdba2c 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -253,6 +253,9 @@ static int hfs_remove(struct inode *dir, struct dentry *dentry) struct inode *inode = dentry->d_inode; int res; + if (S_ISDIR(inode->i_mode)) + dentry_unhash(dentry); + if (S_ISDIR(inode->i_mode) && inode->i_size != 2) return -ENOTEMPTY; res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); @@ -283,6 +286,9 @@ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* Unlink destination if it already exists */ if (new_dentry->d_inode) { + if (S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + res = hfs_remove(new_dir, new_dentry); if (res) return res; diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 4df5059c25d..b28835091dd 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -370,6 +370,8 @@ static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) struct inode *inode = dentry->d_inode; int res; + dentry_unhash(dentry); + if (inode->i_size != 2) return -ENOTEMPTY; @@ -467,10 +469,12 @@ static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, /* Unlink destination if it already exists */ if (new_dentry->d_inode) { - if (S_ISDIR(new_dentry->d_inode->i_mode)) + if (S_ISDIR(new_dentry->d_inode->i_mode)) { + dentry_unhash(new_dentry); res = hfsplus_rmdir(new_dir, new_dentry); - else + } else { res = hfsplus_unlink(new_dir, new_dentry); + } if (res) return res; } diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 2638c834ed2..e6816b9e690 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -683,6 +683,8 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry) char *file; int err; + dentry_unhash(dentry); + if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = do_rmdir(file); @@ -736,6 +738,9 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, char *from_name, *to_name; int err; + if (to->d_inode && S_ISDIR(to->d_inode->i_mode)) + dentry_unhash(to); + if ((from_name = dentry_name(from)) == NULL) return -ENOMEM; if ((to_name = dentry_name(to)) == NULL) { diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index 1f05839c27a..ff0ce21c086 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -395,7 +395,6 @@ again: dentry_unhash(dentry); if (!d_unhashed(dentry)) { - dput(dentry); hpfs_unlock(dir->i_sb); return -ENOSPC; } @@ -403,7 +402,6 @@ again: !S_ISREG(inode->i_mode) || get_write_access(inode)) { d_rehash(dentry); - dput(dentry); } else { struct iattr newattrs; /*printk("HPFS: truncating file before delete.\n");*/ @@ -411,7 +409,6 @@ again: newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; err = notify_change(dentry, &newattrs); put_write_access(inode); - dput(dentry); if (!err) goto again; } @@ -442,6 +439,8 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry) int err; int r; + dentry_unhash(dentry); + hpfs_adjust_length(name, &len); hpfs_lock(dir->i_sb); err = -ENOENT; @@ -535,6 +534,10 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct buffer_head *bh; struct fnode *fnode; int err; + + if (new_inode && S_ISDIR(new_inode->i_mode)) + dentry_unhash(new_dentry); + if ((err = hpfs_chk_name(new_name, &new_len))) return err; err = 0; hpfs_adjust_length(old_name, &old_len); diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 82faddd1f32..05f73328b28 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -609,6 +609,8 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) int ret; uint32_t now = get_seconds(); + dentry_unhash(dentry); + for (fd = f->dents ; fd; fd = fd->next) { if (fd->ino) return -ENOTEMPTY; @@ -784,6 +786,9 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, uint8_t type; uint32_t now; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index eaaf2b511e8..865df16a6cf 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -360,6 +360,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry) jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name); + dentry_unhash(dentry); + /* Init inode for quota operations. */ dquot_initialize(dip); dquot_initialize(ip); @@ -1095,6 +1097,9 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, jfs_info("jfs_rename: %s %s", old_dentry->d_name.name, new_dentry->d_name.name); + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + dquot_initialize(old_dir); dquot_initialize(new_dir); diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 9ed89d1663f..f34c9cde9e9 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -273,6 +273,8 @@ static int logfs_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; + dentry_unhash(dentry); + if (!logfs_empty_dir(inode)) return -ENOTEMPTY; @@ -622,6 +624,9 @@ static int logfs_rename_cross(struct inode *old_dir, struct dentry *old_dentry, loff_t pos; int err; + if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) + dentry_unhash(new_dentry); + /* 1. locate source dd */ err = logfs_get_dd(old_dir, old_dentry, &dd, &pos); if (err) diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 6e6777f1b4b..f60aed8db9c 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -168,6 +168,8 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry) struct inode * inode = dentry->d_inode; int err = -ENOTEMPTY; + dentry_unhash(dentry); + if (minix_empty_dir(inode)) { err = minix_unlink(dir, dentry); if (!err) { @@ -190,6 +192,9 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, struct minix_dir_entry * old_de; int err = -ENOENT; + if (new_inode && S_ISDIR(new_inode->i_mode)) + dentry_unhash(new_dentry); + old_de = minix_find_entry(old_dentry, &old_page); if (!old_de) goto out; diff --git a/fs/namei.c b/fs/namei.c index 6ff858c049c..2358b326b22 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -391,79 +391,28 @@ void path_put(struct path *path) } EXPORT_SYMBOL(path_put); -/** - * nameidata_drop_rcu - drop this nameidata out of rcu-walk - * @nd: nameidata pathwalk data to drop - * Returns: 0 on success, -ECHILD on failure - * +/* * Path walking has 2 modes, rcu-walk and ref-walk (see - * Documentation/filesystems/path-lookup.txt). __drop_rcu* functions attempt - * to drop out of rcu-walk mode and take normal reference counts on dentries - * and vfsmounts to transition to rcu-walk mode. __drop_rcu* functions take - * refcounts at the last known good point before rcu-walk got stuck, so - * ref-walk may continue from there. If this is not successful (eg. a seqcount - * has changed), then failure is returned and path walk restarts from the - * beginning in ref-walk mode. - * - * nameidata_drop_rcu attempts to drop the current nd->path and nd->root into - * ref-walk. Must be called from rcu-walk context. + * Documentation/filesystems/path-lookup.txt). In situations when we can't + * continue in RCU mode, we attempt to drop out of rcu-walk mode and grab + * normal reference counts on dentries and vfsmounts to transition to rcu-walk + * mode. Refcounts are grabbed at the last known good point before rcu-walk + * got stuck, so ref-walk may continue from there. If this is not successful + * (eg. a seqcount has changed), then failure is returned and it's up to caller + * to restart the path walk from the beginning in ref-walk mode. */ -static int nameidata_drop_rcu(struct nameidata *nd) -{ - struct fs_struct *fs = current->fs; - struct dentry *dentry = nd->path.dentry; - int want_root = 0; - - BUG_ON(!(nd->flags & LOOKUP_RCU)); - if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { - want_root = 1; - spin_lock(&fs->lock); - if (nd->root.mnt != fs->root.mnt || - nd->root.dentry != fs->root.dentry) - goto err_root; - } - spin_lock(&dentry->d_lock); - if (!__d_rcu_to_refcount(dentry, nd->seq)) - goto err; - BUG_ON(nd->inode != dentry->d_inode); - spin_unlock(&dentry->d_lock); - if (want_root) { - path_get(&nd->root); - spin_unlock(&fs->lock); - } - mntget(nd->path.mnt); - - rcu_read_unlock(); - br_read_unlock(vfsmount_lock); - nd->flags &= ~LOOKUP_RCU; - return 0; -err: - spin_unlock(&dentry->d_lock); -err_root: - if (want_root) - spin_unlock(&fs->lock); - return -ECHILD; -} - -/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */ -static inline int nameidata_drop_rcu_maybe(struct nameidata *nd) -{ - if (nd->flags & LOOKUP_RCU) - return nameidata_drop_rcu(nd); - return 0; -} /** - * nameidata_dentry_drop_rcu - drop nameidata and dentry out of rcu-walk - * @nd: nameidata pathwalk data to drop - * @dentry: dentry to drop + * unlazy_walk - try to switch to ref-walk mode. + * @nd: nameidata pathwalk data + * @dentry: child of nd->path.dentry or NULL * Returns: 0 on success, -ECHILD on failure * - * nameidata_dentry_drop_rcu attempts to drop the current nd->path and nd->root, - * and dentry into ref-walk. @dentry must be a path found by a do_lookup call on - * @nd. Must be called from rcu-walk context. + * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry + * for ref-walk mode. @dentry must be a path found by a do_lookup call on + * @nd or NULL. Must be called from rcu-walk context. */ -static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry) +static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) { struct fs_struct *fs = current->fs; struct dentry *parent = nd->path.dentry; @@ -478,18 +427,25 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry goto err_root; } spin_lock(&parent->d_lock); - spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); - if (!__d_rcu_to_refcount(dentry, nd->seq)) - goto err; - /* - * If the sequence check on the child dentry passed, then the child has - * not been removed from its parent. This means the parent dentry must - * be valid and able to take a reference at this point. - */ - BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent); - BUG_ON(!parent->d_count); - parent->d_count++; - spin_unlock(&dentry->d_lock); + if (!dentry) { + if (!__d_rcu_to_refcount(parent, nd->seq)) + goto err_parent; + BUG_ON(nd->inode != parent->d_inode); + } else { + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + if (!__d_rcu_to_refcount(dentry, nd->seq)) + goto err_child; + /* + * If the sequence check on the child dentry passed, then + * the child has not been removed from its parent. This + * means the parent dentry must be valid and able to take + * a reference at this point. + */ + BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent); + BUG_ON(!parent->d_count); + parent->d_count++; + spin_unlock(&dentry->d_lock); + } spin_unlock(&parent->d_lock); if (want_root) { path_get(&nd->root); @@ -501,8 +457,10 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry br_read_unlock(vfsmount_lock); nd->flags &= ~LOOKUP_RCU; return 0; -err: + +err_child: spin_unlock(&dentry->d_lock); +err_parent: spin_unlock(&parent->d_lock); err_root: if (want_root) @@ -510,59 +468,6 @@ err_root: return -ECHILD; } -/* Try to drop out of rcu-walk mode if we were in it, otherwise do nothing. */ -static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct dentry *dentry) -{ - if (nd->flags & LOOKUP_RCU) { - if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) { - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - rcu_read_unlock(); - br_read_unlock(vfsmount_lock); - return -ECHILD; - } - } - return 0; -} - -/** - * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk - * @nd: nameidata pathwalk data to drop - * Returns: 0 on success, -ECHILD on failure - * - * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk. - * nd->path should be the final element of the lookup, so nd->root is discarded. - * Must be called from rcu-walk context. - */ -static int nameidata_drop_rcu_last(struct nameidata *nd) -{ - struct dentry *dentry = nd->path.dentry; - - BUG_ON(!(nd->flags & LOOKUP_RCU)); - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - spin_lock(&dentry->d_lock); - if (!__d_rcu_to_refcount(dentry, nd->seq)) - goto err_unlock; - BUG_ON(nd->inode != dentry->d_inode); - spin_unlock(&dentry->d_lock); - - mntget(nd->path.mnt); - - rcu_read_unlock(); - br_read_unlock(vfsmount_lock); - - return 0; - -err_unlock: - spin_unlock(&dentry->d_lock); - rcu_read_unlock(); - br_read_unlock(vfsmount_lock); - return -ECHILD; -} - /** * release_open_intent - free up open intent resources * @nd: pointer to nameidata @@ -606,26 +511,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd) return dentry; } -/* - * handle_reval_path - force revalidation of a dentry - * - * In some situations the path walking code will trust dentries without - * revalidating them. This causes problems for filesystems that depend on - * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set - * (which indicates that it's possible for the dentry to go stale), force - * a d_revalidate call before proceeding. +/** + * complete_walk - successful completion of path walk + * @nd: pointer nameidata * - * Returns 0 if the revalidation was successful. If the revalidation fails, - * either return the error returned by d_r |