From d181146572c4fa9af2a068b967cb53dcac7da944 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 00:49:18 -0400 Subject: [PATCH] new helper - kern_path() Analog of lookup_path(), takes struct path *. Signed-off-by: Al Viro --- fs/namei.c | 10 ++++++++++ include/linux/namei.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 4ea63ed5e791..4a56f9b59e8c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1106,6 +1106,15 @@ int path_lookup(const char *name, unsigned int flags, return do_path_lookup(AT_FDCWD, name, flags, nd); } +int kern_path(const char *name, unsigned int flags, struct path *path) +{ + struct nameidata nd; + int res = do_path_lookup(AT_FDCWD, name, flags, &nd); + if (!res) + *path = nd.path; + return res; +} + /** * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair * @dentry: pointer to dentry of the base directory @@ -2855,6 +2864,7 @@ EXPORT_SYMBOL(__page_symlink); EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); +EXPORT_SYMBOL(kern_path); EXPORT_SYMBOL(vfs_path_lookup); EXPORT_SYMBOL(inode_permission); EXPORT_SYMBOL(vfs_permission); diff --git a/include/linux/namei.h b/include/linux/namei.h index 68f8c3203c89..221e8bc894ba 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -61,6 +61,8 @@ extern int user_path_at(int, const char __user *, unsigned, struct path *); #define user_path_dir(name, path) \ user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path) +extern int kern_path(const char *, unsigned, struct path *); + extern int path_lookup(const char *, unsigned, struct nameidata *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct nameidata *); -- cgit v1.2.3-59-g8ed1b From 2d92ab3c6279f8423b20cf91574d0ad6696d2b44 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 00:51:11 -0400 Subject: [PATCH] finally get rid of nameidata in namespace.c Signed-off-by: Al Viro --- fs/namespace.c | 119 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 6e283c93b50d..9f6005e55862 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1167,19 +1167,19 @@ asmlinkage long sys_oldumount(char __user * name) #endif -static int mount_is_safe(struct nameidata *nd) +static int mount_is_safe(struct path *path) { if (capable(CAP_SYS_ADMIN)) return 0; return -EPERM; #ifdef notyet - if (S_ISLNK(nd->path.dentry->d_inode->i_mode)) + if (S_ISLNK(path->dentry->d_inode->i_mode)) return -EPERM; - if (nd->path.dentry->d_inode->i_mode & S_ISVTX) { - if (current->uid != nd->path.dentry->d_inode->i_uid) + if (path->dentry->d_inode->i_mode & S_ISVTX) { + if (current->uid != path->dentry->d_inode->i_uid) return -EPERM; } - if (vfs_permission(nd, MAY_WRITE)) + if (inode_permission(path->dentry->d_inode, MAY_WRITE)) return -EPERM; return 0; #endif @@ -1427,9 +1427,9 @@ out_unlock: * recursively change the type of the mountpoint. * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_change_type(struct nameidata *nd, int flag) +static noinline int do_change_type(struct path *path, int flag) { - struct vfsmount *m, *mnt = nd->path.mnt; + struct vfsmount *m, *mnt = path->mnt; int recurse = flag & MS_REC; int type = flag & ~MS_REC; int err = 0; @@ -1437,7 +1437,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (nd->path.dentry != nd->path.mnt->mnt_root) + if (path->dentry != path->mnt->mnt_root) return -EINVAL; down_write(&namespace_sem); @@ -1461,38 +1461,38 @@ static noinline int do_change_type(struct nameidata *nd, int flag) * do loopback mount. * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_loopback(struct nameidata *nd, char *old_name, +static noinline int do_loopback(struct path *path, char *old_name, int recurse) { - struct nameidata old_nd; + struct path old_path; struct vfsmount *mnt = NULL; - int err = mount_is_safe(nd); + int err = mount_is_safe(path); if (err) return err; if (!old_name || !*old_name) return -EINVAL; - err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); + err = kern_path(old_name, LOOKUP_FOLLOW, &old_path); if (err) return err; down_write(&namespace_sem); err = -EINVAL; - if (IS_MNT_UNBINDABLE(old_nd.path.mnt)) + if (IS_MNT_UNBINDABLE(old_path.mnt)) goto out; - if (!check_mnt(nd->path.mnt) || !check_mnt(old_nd.path.mnt)) + if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) goto out; err = -ENOMEM; if (recurse) - mnt = copy_tree(old_nd.path.mnt, old_nd.path.dentry, 0); + mnt = copy_tree(old_path.mnt, old_path.dentry, 0); else - mnt = clone_mnt(old_nd.path.mnt, old_nd.path.dentry, 0); + mnt = clone_mnt(old_path.mnt, old_path.dentry, 0); if (!mnt) goto out; - err = graft_tree(mnt, &nd->path); + err = graft_tree(mnt, path); if (err) { LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); @@ -1503,7 +1503,7 @@ static noinline int do_loopback(struct nameidata *nd, char *old_name, out: up_write(&namespace_sem); - path_put(&old_nd.path); + path_put(&old_path); return err; } @@ -1530,31 +1530,31 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags) * on it - tough luck. * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_remount(struct nameidata *nd, int flags, int mnt_flags, +static noinline int do_remount(struct path *path, int flags, int mnt_flags, void *data) { int err; - struct super_block *sb = nd->path.mnt->mnt_sb; + struct super_block *sb = path->mnt->mnt_sb; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!check_mnt(nd->path.mnt)) + if (!check_mnt(path->mnt)) return -EINVAL; - if (nd->path.dentry != nd->path.mnt->mnt_root) + if (path->dentry != path->mnt->mnt_root) return -EINVAL; down_write(&sb->s_umount); if (flags & MS_BIND) - err = change_mount_flags(nd->path.mnt, flags); + err = change_mount_flags(path->mnt, flags); else err = do_remount_sb(sb, flags, data, 0); if (!err) - nd->path.mnt->mnt_flags = mnt_flags; + path->mnt->mnt_flags = mnt_flags; up_write(&sb->s_umount); if (!err) - security_sb_post_remount(nd->path.mnt, flags, data); + security_sb_post_remount(path->mnt, flags, data); return err; } @@ -1571,78 +1571,77 @@ static inline int tree_contains_unbindable(struct vfsmount *mnt) /* * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_move_mount(struct nameidata *nd, char *old_name) +static noinline int do_move_mount(struct path *path, char *old_name) { - struct nameidata old_nd; - struct path parent_path; + struct path old_path, parent_path; struct vfsmount *p; int err = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (!old_name || !*old_name) return -EINVAL; - err = path_lookup(old_name, LOOKUP_FOLLOW, &old_nd); + err = kern_path(old_name, LOOKUP_FOLLOW, &old_path); if (err) return err; down_write(&namespace_sem); - while (d_mountpoint(nd->path.dentry) && - follow_down(&nd->path.mnt, &nd->path.dentry)) + while (d_mountpoint(path->dentry) && + follow_down(&path->mnt, &path->dentry)) ; err = -EINVAL; - if (!check_mnt(nd->path.mnt) || !check_mnt(old_nd.path.mnt)) + if (!check_mnt(path->mnt) || !check_mnt(old_path.mnt)) goto out; err = -ENOENT; - mutex_lock(&nd->path.dentry->d_inode->i_mutex); - if (IS_DEADDIR(nd->path.dentry->d_inode)) + mutex_lock(&path->dentry->d_inode->i_mutex); + if (IS_DEADDIR(path->dentry->d_inode)) goto out1; - if (!IS_ROOT(nd->path.dentry) && d_unhashed(nd->path.dentry)) + if (!IS_ROOT(path->dentry) && d_unhashed(path->dentry)) goto out1; err = -EINVAL; - if (old_nd.path.dentry != old_nd.path.mnt->mnt_root) + if (old_path.dentry != old_path.mnt->mnt_root) goto out1; - if (old_nd.path.mnt == old_nd.path.mnt->mnt_parent) + if (old_path.mnt == old_path.mnt->mnt_parent) goto out1; - if (S_ISDIR(nd->path.dentry->d_inode->i_mode) != - S_ISDIR(old_nd.path.dentry->d_inode->i_mode)) + if (S_ISDIR(path->dentry->d_inode->i_mode) != + S_ISDIR(old_path.dentry->d_inode->i_mode)) goto out1; /* * Don't move a mount residing in a shared parent. */ - if (old_nd.path.mnt->mnt_parent && - IS_MNT_SHARED(old_nd.path.mnt->mnt_parent)) + if (old_path.mnt->mnt_parent && + IS_MNT_SHARED(old_path.mnt->mnt_parent)) goto out1; /* * Don't move a mount tree containing unbindable mounts to a destination * mount which is shared. */ - if (IS_MNT_SHARED(nd->path.mnt) && - tree_contains_unbindable(old_nd.path.mnt)) + if (IS_MNT_SHARED(path->mnt) && + tree_contains_unbindable(old_path.mnt)) goto out1; err = -ELOOP; - for (p = nd->path.mnt; p->mnt_parent != p; p = p->mnt_parent) - if (p == old_nd.path.mnt) + for (p = path->mnt; p->mnt_parent != p; p = p->mnt_parent) + if (p == old_path.mnt) goto out1; - err = attach_recursive_mnt(old_nd.path.mnt, &nd->path, &parent_path); + err = attach_recursive_mnt(old_path.mnt, path, &parent_path); if (err) goto out1; /* if the mount is moved, it should no longer be expire * automatically */ - list_del_init(&old_nd.path.mnt->mnt_expire); + list_del_init(&old_path.mnt->mnt_expire); out1: - mutex_unlock(&nd->path.dentry->d_inode->i_mutex); + mutex_unlock(&path->dentry->d_inode->i_mutex); out: up_write(&namespace_sem); if (!err) path_put(&parent_path); - path_put(&old_nd.path); + path_put(&old_path); return err; } @@ -1651,7 +1650,7 @@ out: * namespace's tree * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_new_mount(struct nameidata *nd, char *type, int flags, +static noinline int do_new_mount(struct path *path, char *type, int flags, int mnt_flags, char *name, void *data) { struct vfsmount *mnt; @@ -1667,7 +1666,7 @@ static noinline int do_new_mount(struct nameidata *nd, char *type, int flags, if (IS_ERR(mnt)) return PTR_ERR(mnt); - return do_add_mount(mnt, &nd->path, mnt_flags, NULL); + return do_add_mount(mnt, path, mnt_flags, NULL); } /* @@ -1902,7 +1901,7 @@ int copy_mount_options(const void __user * data, unsigned long *where) long do_mount(char *dev_name, char *dir_name, char *type_page, unsigned long flags, void *data_page) { - struct nameidata nd; + struct path path; int retval = 0; int mnt_flags = 0; @@ -1940,29 +1939,29 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT); /* ... and get the mountpoint */ - retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd); + retval = kern_path(dir_name, LOOKUP_FOLLOW, &path); if (retval) return retval; - retval = security_sb_mount(dev_name, &nd.path, + retval = security_sb_mount(dev_name, &path, type_page, flags, data_page); if (retval) goto dput_out; if (flags & MS_REMOUNT) - retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags, + retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags, data_page); else if (flags & MS_BIND) - retval = do_loopback(&nd, dev_name, flags & MS_REC); + retval = do_loopback(&path, dev_name, flags & MS_REC); else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) - retval = do_change_type(&nd, flags); + retval = do_change_type(&path, flags); else if (flags & MS_MOVE) - retval = do_move_mount(&nd, dev_name); + retval = do_move_mount(&path, dev_name); else - retval = do_new_mount(&nd, type_page, flags, mnt_flags, + retval = do_new_mount(&path, type_page, flags, mnt_flags, dev_name, data_page); dput_out: - path_put(&nd.path); + path_put(&path); return retval; } -- cgit v1.2.3-59-g8ed1b From 0a0d8a46757e2063433c8cd52b7d654e02b4682b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 00:55:27 -0400 Subject: [PATCH] no need for noinline stuff in fs/namespace.c anymore Stack footprint from hell had been due to many struct nameidata in there. No more. Signed-off-by: Al Viro --- fs/namespace.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 9f6005e55862..f527a0d6c64d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1425,9 +1425,8 @@ out_unlock: /* * recursively change the type of the mountpoint. - * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_change_type(struct path *path, int flag) +static int do_change_type(struct path *path, int flag) { struct vfsmount *m, *mnt = path->mnt; int recurse = flag & MS_REC; @@ -1459,9 +1458,8 @@ static noinline int do_change_type(struct path *path, int flag) /* * do loopback mount. - * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_loopback(struct path *path, char *old_name, +static int do_loopback(struct path *path, char *old_name, int recurse) { struct path old_path; @@ -1528,9 +1526,8 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags) * change filesystem flags. dir should be a physical root of filesystem. * If you've mounted a non-root directory somewhere and want to do remount * on it - tough luck. - * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_remount(struct path *path, int flags, int mnt_flags, +static int do_remount(struct path *path, int flags, int mnt_flags, void *data) { int err; @@ -1568,10 +1565,7 @@ static inline int tree_contains_unbindable(struct vfsmount *mnt) return 0; } -/* - * noinline this do_mount helper to save do_mount stack space. - */ -static noinline int do_move_mount(struct path *path, char *old_name) +static int do_move_mount(struct path *path, char *old_name) { struct path old_path, parent_path; struct vfsmount *p; @@ -1648,9 +1642,8 @@ out: /* * create a new mount for userspace and request it to be added into the * namespace's tree - * noinline this do_mount helper to save do_mount stack space. */ -static noinline int do_new_mount(struct path *path, char *type, int flags, +static int do_new_mount(struct path *path, char *type, int flags, int mnt_flags, char *name, void *data) { struct vfsmount *mnt; -- cgit v1.2.3-59-g8ed1b From 8264613def2e5c4f12bc3167713090fd172e6055 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 00:57:06 -0400 Subject: [PATCH] switch quota_on-related stuff to kern_path() Signed-off-by: Al Viro --- fs/dquot.c | 10 +++++----- fs/ext3/super.c | 22 +++++++++++----------- fs/ext4/super.c | 24 ++++++++++++------------ fs/reiserfs/super.c | 18 +++++++++--------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/fs/dquot.c b/fs/dquot.c index da30a27f2242..5e95261005b2 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -1805,19 +1805,19 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id, } /* Actual function called from quotactl() */ -int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, +int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name, int remount) { - struct nameidata nd; + struct path path; int error; if (remount) return vfs_quota_on_remount(sb, type); - error = path_lookup(path, LOOKUP_FOLLOW, &nd); + error = kern_path(name, LOOKUP_FOLLOW, &path); if (!error) { - error = vfs_quota_on_path(sb, type, format_id, &nd.path); - path_put(&nd.path); + error = vfs_quota_on_path(sb, type, format_id, &path); + path_put(&path); } return error; } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 3a260af5544d..5b7fee10566f 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2783,30 +2783,30 @@ static int ext3_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int ext3_quota_on(struct super_block *sb, int type, int format_id, - char *path, int remount) + char *name, int remount) { int err; - struct nameidata nd; + struct path path; if (!test_opt(sb, QUOTA)) return -EINVAL; - /* When remounting, no checks are needed and in fact, path is NULL */ + /* When remounting, no checks are needed and in fact, name is NULL */ if (remount) - return vfs_quota_on(sb, type, format_id, path, remount); + return vfs_quota_on(sb, type, format_id, name, remount); - err = path_lookup(path, LOOKUP_FOLLOW, &nd); + err = kern_path(name, LOOKUP_FOLLOW, &path); if (err) return err; /* Quotafile not on the same filesystem? */ - if (nd.path.mnt->mnt_sb != sb) { - path_put(&nd.path); + if (path.mnt->mnt_sb != sb) { + path_put(&path); return -EXDEV; } /* Journaling quota? */ if (EXT3_SB(sb)->s_qf_names[type]) { /* Quotafile not of fs root? */ - if (nd.path.dentry->d_parent->d_inode != sb->s_root->d_inode) + if (path.dentry->d_parent != sb->s_root) printk(KERN_WARNING "EXT3-fs: Quota file not on filesystem root. " "Journaled quota will not work.\n"); @@ -2816,7 +2816,7 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, * When we journal data on quota file, we have to flush journal to see * all updates to the file when we bypass pagecache... */ - if (ext3_should_journal_data(nd.path.dentry->d_inode)) { + if (ext3_should_journal_data(path.dentry->d_inode)) { /* * We don't need to lock updates but journal_flush() could * otherwise be livelocked... @@ -2826,8 +2826,8 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, journal_unlock_updates(EXT3_SB(sb)->s_journal); } - err = vfs_quota_on_path(sb, type, format_id, &nd.path); - path_put(&nd.path); + err = vfs_quota_on_path(sb, type, format_id, &path); + path_put(&path); return err; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 9b2b2bc4ec17..ae35f176b697 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3328,30 +3328,30 @@ static int ext4_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int ext4_quota_on(struct super_block *sb, int type, int format_id, - char *path, int remount) + char *name, int remount) { int err; - struct nameidata nd; + struct path path; if (!test_opt(sb, QUOTA)) return -EINVAL; - /* When remounting, no checks are needed and in fact, path is NULL */ + /* When remounting, no checks are needed and in fact, name is NULL */ if (remount) - return vfs_quota_on(sb, type, format_id, path, remount); + return vfs_quota_on(sb, type, format_id, name, remount); - err = path_lookup(path, LOOKUP_FOLLOW, &nd); + err = kern_path(name, LOOKUP_FOLLOW, &path); if (err) return err; /* Quotafile not on the same filesystem? */ - if (nd.path.mnt->mnt_sb != sb) { - path_put(&nd.path); + if (path.mnt->mnt_sb != sb) { + path_put(&path); return -EXDEV; } /* Journaling quota? */ if (EXT4_SB(sb)->s_qf_names[type]) { /* Quotafile not in fs root? */ - if (nd.path.dentry->d_parent->d_inode != sb->s_root->d_inode) + if (path.dentry->d_parent != sb->s_root) printk(KERN_WARNING "EXT4-fs: Quota file not on filesystem root. " "Journaled quota will not work.\n"); @@ -3361,7 +3361,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, * When we journal data on quota file, we have to flush journal to see * all updates to the file when we bypass pagecache... */ - if (ext4_should_journal_data(nd.path.dentry->d_inode)) { + if (ext4_should_journal_data(path.dentry->d_inode)) { /* * We don't need to lock updates but journal_flush() could * otherwise be livelocked... @@ -3370,13 +3370,13 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, err = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); if (err) { - path_put(&nd.path); + path_put(&path); return err; } } - err = vfs_quota_on_path(sb, type, format_id, &nd.path); - path_put(&nd.path); + err = vfs_quota_on_path(sb, type, format_id, &path); + path_put(&path); return err; } diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index d318c7e663fa..663a91f5dce8 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -2058,10 +2058,10 @@ static int reiserfs_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, - char *path, int remount) + char *name, int remount) { int err; - struct nameidata nd; + struct path path; struct inode *inode; struct reiserfs_transaction_handle th; @@ -2069,16 +2069,16 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, return -EINVAL; /* No more checks needed? Path and format_id are bogus anyway... */ if (remount) - return vfs_quota_on(sb, type, format_id, path, 1); - err = path_lookup(path, LOOKUP_FOLLOW, &nd); + return vfs_quota_on(sb, type, format_id, name, 1); + err = kern_path(name, LOOKUP_FOLLOW, &path); if (err) return err; /* Quotafile not on the same filesystem? */ - if (nd.path.mnt->mnt_sb != sb) { + if (path.mnt->mnt_sb != sb) { err = -EXDEV; goto out; } - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; /* We must not pack tails for quota files on reiserfs for quota IO to work */ if (!(REISERFS_I(inode)->i_flags & i_nopack_mask)) { err = reiserfs_unpack(inode, NULL); @@ -2094,7 +2094,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, /* Journaling quota? */ if (REISERFS_SB(sb)->s_qf_names[type]) { /* Quotafile not of fs root? */ - if (nd.path.dentry->d_parent->d_inode != sb->s_root->d_inode) + if (path.dentry->d_parent != sb->s_root) reiserfs_warning(sb, "reiserfs: Quota file not on filesystem root. " "Journalled quota will not work."); @@ -2113,9 +2113,9 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, if (err) goto out; } - err = vfs_quota_on_path(sb, type, format_id, &nd.path); + err = vfs_quota_on_path(sb, type, format_id, &path); out: - path_put(&nd.path); + path_put(&path); return err; } -- cgit v1.2.3-59-g8ed1b From c1a2a4756df01d72b6f7a0563218b23b63a6d4ed Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 01:01:02 -0400 Subject: [PATCH] sanitize svc_export_parse() clean up the exit paths, get rid of nameidata Signed-off-by: Al Viro --- fs/nfsd/export.c | 77 ++++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 9dc036f18356..2fa61f003fff 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -500,35 +500,22 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) int len; int err; struct auth_domain *dom = NULL; - struct nameidata nd; - struct svc_export exp, *expp; + struct svc_export exp = {}, *expp; int an_int; - nd.path.dentry = NULL; - exp.ex_pathname = NULL; - - /* fs locations */ - exp.ex_fslocs.locations = NULL; - exp.ex_fslocs.locations_count = 0; - exp.ex_fslocs.migrated = 0; - - exp.ex_uuid = NULL; - - /* secinfo */ - exp.ex_nflavors = 0; - if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - err = -ENOMEM; - if (!buf) goto out; + if (!buf) + return -ENOMEM; /* client */ - len = qword_get(&mesg, buf, PAGE_SIZE); err = -EINVAL; - if (len <= 0) goto out; + len = qword_get(&mesg, buf, PAGE_SIZE); + if (len <= 0) + goto out; err = -ENOENT; dom = auth_domain_find(buf); @@ -537,25 +524,25 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) /* path */ err = -EINVAL; - if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) - goto out; - err = path_lookup(buf, 0, &nd); - if (err) goto out_no_path; + if ((len = qword_get(&mesg, buf, PAGE_SIZE)) <= 0) + goto out1; + + err = kern_path(buf, 0, &exp.ex_path); + if (err) + goto out1; - exp.h.flags = 0; exp.ex_client = dom; - exp.ex_path.mnt = nd.path.mnt; - exp.ex_path.dentry = nd.path.dentry; - exp.ex_pathname = kstrdup(buf, GFP_KERNEL); + err = -ENOMEM; + exp.ex_pathname = kstrdup(buf, GFP_KERNEL); if (!exp.ex_pathname) - goto out; + goto out2; /* expiry */ err = -EINVAL; exp.h.expiry_time = get_expiry(&mesg); if (exp.h.expiry_time == 0) - goto out; + goto out3; /* flags */ err = get_int(&mesg, &an_int); @@ -563,22 +550,26 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = 0; set_bit(CACHE_NEGATIVE, &exp.h.flags); } else { - if (err || an_int < 0) goto out; + if (err || an_int < 0) + goto out3; exp.ex_flags= an_int; /* anon uid */ err = get_int(&mesg, &an_int); - if (err) goto out; + if (err) + goto out3; exp.ex_anon_uid= an_int; /* anon gid */ err = get_int(&mesg, &an_int); - if (err) goto out; + if (err) + goto out3; exp.ex_anon_gid= an_int; /* fsid */ err = get_int(&mesg, &an_int); - if (err) goto out; + if (err) + goto out3; exp.ex_fsid = an_int; while ((len = qword_get(&mesg, buf, PAGE_SIZE)) > 0) { @@ -604,12 +595,13 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) */ break; if (err) - goto out; + goto out4; } - err = check_export(nd.path.dentry->d_inode, exp.ex_flags, + err = check_export(exp.ex_path.dentry->d_inode, exp.ex_flags, exp.ex_uuid); - if (err) goto out; + if (err) + goto out4; } expp = svc_export_lookup(&exp); @@ -622,15 +614,16 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = -ENOMEM; else exp_put(expp); - out: +out4: nfsd4_fslocs_free(&exp.ex_fslocs); kfree(exp.ex_uuid); +out3: kfree(exp.ex_pathname); - if (nd.path.dentry) - path_put(&nd.path); - out_no_path: - if (dom) - auth_domain_put(dom); +out2: + path_put(&exp.ex_path); +out1: + auth_domain_put(dom); +out: kfree(buf); return err; } -- cgit v1.2.3-59-g8ed1b From a63bb99660d82dfe7c51588e1f9aadefb756ba51 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 01:03:36 -0400 Subject: [PATCH] switch nfsd to kern_path() Signed-off-by: Al Viro --- fs/nfsd/export.c | 48 +++++++++++++++++++++++------------------------- fs/nfsd/nfs4recover.c | 50 +++++++++++++++++++++++++------------------------- fs/nfsd/nfs4state.c | 8 ++++---- fs/nfsd/nfsctl.c | 8 ++++---- 4 files changed, 56 insertions(+), 58 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 2fa61f003fff..676201dbdf84 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -162,20 +162,18 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; } else { - struct nameidata nd; - err = path_lookup(buf, 0, &nd); + err = kern_path(buf, 0, &key.ek_path); if (err) goto out; dprintk("Found the path %s\n", buf); - key.ek_path = nd.path; ek = svc_expkey_update(&key, ek); if (ek) cache_put(&ek->h, &svc_expkey_cache); else err = -ENOMEM; - path_put(&nd.path); + path_put(&key.ek_path); } cache_flush(); out: @@ -991,7 +989,7 @@ exp_export(struct nfsctl_export *nxp) struct svc_export *exp = NULL; struct svc_export new; struct svc_expkey *fsid_key = NULL; - struct nameidata nd; + struct path path; int err; /* Consistency check */ @@ -1014,12 +1012,12 @@ exp_export(struct nfsctl_export *nxp) /* Look up the dentry */ - err = path_lookup(nxp->ex_path, 0, &nd); + err = kern_path(nxp->ex_path, 0, &path); if (err) goto out_put_clp; err = -EINVAL; - exp = exp_get_by_name(clp, nd.path.mnt, nd.path.dentry, NULL); + exp = exp_get_by_name(clp, path.mnt, path.dentry, NULL); memset(&new, 0, sizeof(new)); @@ -1027,8 +1025,8 @@ exp_export(struct nfsctl_export *nxp) if ((nxp->ex_flags & NFSEXP_FSID) && (!IS_ERR(fsid_key = exp_get_fsid_key(clp, nxp->ex_dev))) && fsid_key->ek_path.mnt && - (fsid_key->ek_path.mnt != nd.path.mnt || - fsid_key->ek_path.dentry != nd.path.dentry)) + (fsid_key->ek_path.mnt != path.mnt || + fsid_key->ek_path.dentry != path.dentry)) goto finish; if (!IS_ERR(exp)) { @@ -1044,7 +1042,7 @@ exp_export(struct nfsctl_export *nxp) goto finish; } - err = check_export(nd.path.dentry->d_inode, nxp->ex_flags, NULL); + err = check_export(path.dentry->d_inode, nxp->ex_flags, NULL); if (err) goto finish; err = -ENOMEM; @@ -1057,7 +1055,7 @@ exp_export(struct nfsctl_export *nxp) if (!new.ex_pathname) goto finish; new.ex_client = clp; - new.ex_path = nd.path; + new.ex_path = path; new.ex_flags = nxp->ex_flags; new.ex_anon_uid = nxp->ex_anon_uid; new.ex_anon_gid = nxp->ex_anon_gid; @@ -1083,7 +1081,7 @@ finish: exp_put(exp); if (fsid_key && !IS_ERR(fsid_key)) cache_put(&fsid_key->h, &svc_expkey_cache); - path_put(&nd.path); + path_put(&path); out_put_clp: auth_domain_put(clp); out_unlock: @@ -1114,7 +1112,7 @@ exp_unexport(struct nfsctl_export *nxp) { struct auth_domain *dom; svc_export *exp; - struct nameidata nd; + struct path path; int err; /* Consistency check */ @@ -1131,13 +1129,13 @@ exp_unexport(struct nfsctl_export *nxp) goto out_unlock; } - err = path_lookup(nxp->ex_path, 0, &nd); + err = kern_path(nxp->ex_path, 0, &path); if (err) goto out_domain; err = -EINVAL; - exp = exp_get_by_name(dom, nd.path.mnt, nd.path.dentry, NULL); - path_put(&nd.path); + exp = exp_get_by_name(dom, path.mnt, path.dentry, NULL); + path_put(&path); if (IS_ERR(exp)) goto out_domain; @@ -1159,26 +1157,26 @@ out_unlock: * since its harder to fool a kernel module than a user space program. */ int -exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) +exp_rootfh(svc_client *clp, char *name, struct knfsd_fh *f, int maxsize) { struct svc_export *exp; - struct nameidata nd; + struct path path; struct inode *inode; struct svc_fh fh; int err; err = -EPERM; /* NB: we probably ought to check that it's NUL-terminated */ - if (path_lookup(path, 0, &nd)) { - printk("nfsd: exp_rootfh path not found %s", path); + if (kern_path(name, 0, &path)) { + printk("nfsd: exp_rootfh path not found %s", name); return err; } - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n", - path, nd.path.dentry, clp->name, + name, path.dentry, clp->name, inode->i_sb->s_id, inode->i_ino); - exp = exp_parent(clp, nd.path.mnt, nd.path.dentry, NULL); + exp = exp_parent(clp, path.mnt, path.dentry, NULL); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto out; @@ -1188,7 +1186,7 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) * fh must be initialized before calling fh_compose */ fh_init(&fh, maxsize); - if (fh_compose(&fh, exp, nd.path.dentry, NULL)) + if (fh_compose(&fh, exp, path.dentry, NULL)) err = -EINVAL; else err = 0; @@ -1196,7 +1194,7 @@ exp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize) fh_put(&fh); exp_put(exp); out: - path_put(&nd.path); + path_put(&path); return err; } diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 145b3c877a27..bb93946ace22 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -51,7 +51,7 @@ #define NFSDDBG_FACILITY NFSDDBG_PROC /* Globals */ -static struct nameidata rec_dir; +static struct path rec_dir; static int rec_dir_init = 0; static void @@ -121,9 +121,9 @@ out_no_tfm: static void nfsd4_sync_rec_dir(void) { - mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex); - nfsd_sync_dir(rec_dir.path.dentry); - mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex); + mutex_lock(&rec_dir.dentry->d_inode->i_mutex); + nfsd_sync_dir(rec_dir.dentry); + mutex_unlock(&rec_dir.dentry->d_inode->i_mutex); } int @@ -143,9 +143,9 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) nfs4_save_user(&uid, &gid); /* lock the parent */ - mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex); + mutex_lock(&rec_dir.dentry->d_inode->i_mutex); - dentry = lookup_one_len(dname, rec_dir.path.dentry, HEXDIR_LEN-1); + dentry = lookup_one_len(dname, rec_dir.dentry, HEXDIR_LEN-1); if (IS_ERR(dentry)) { status = PTR_ERR(dentry); goto out_unlock; @@ -155,15 +155,15 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n"); goto out_put; } - status = mnt_want_write(rec_dir.path.mnt); + status = mnt_want_write(rec_dir.mnt); if (status) goto out_put; - status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, S_IRWXU); - mnt_drop_write(rec_dir.path.mnt); + status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU); + mnt_drop_write(rec_dir.mnt); out_put: dput(dentry); out_unlock: - mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex); + mutex_unlock(&rec_dir.dentry->d_inode->i_mutex); if (status == 0) { clp->cl_firststate = 1; nfsd4_sync_rec_dir(); @@ -226,7 +226,7 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f) nfs4_save_user(&uid, &gid); - filp = dentry_open(dget(dir), mntget(rec_dir.path.mnt), O_RDONLY); + filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY); status = PTR_ERR(filp); if (IS_ERR(filp)) goto out; @@ -291,9 +291,9 @@ nfsd4_unlink_clid_dir(char *name, int namlen) dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); - mutex_lock(&rec_dir.path.dentry->d_inode->i_mutex); - dentry = lookup_one_len(name, rec_dir.path.dentry, namlen); - mutex_unlock(&rec_dir.path.dentry->d_inode->i_mutex); + mutex_lock(&rec_dir.dentry->d_inode->i_mutex); + dentry = lookup_one_len(name, rec_dir.dentry, namlen); + mutex_unlock(&rec_dir.dentry->d_inode->i_mutex); if (IS_ERR(dentry)) { status = PTR_ERR(dentry); return status; @@ -302,7 +302,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen) if (!dentry->d_inode) goto out; - status = nfsd4_clear_clid_dir(rec_dir.path.dentry, dentry); + status = nfsd4_clear_clid_dir(rec_dir.dentry, dentry); out: dput(dentry); return status; @@ -318,7 +318,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) if (!rec_dir_init || !clp->cl_firststate) return; - status = mnt_want_write(rec_dir.path.mnt); + status = mnt_want_write(rec_dir.mnt); if (status) goto out; clp->cl_firststate = 0; @@ -327,7 +327,7 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp) nfs4_reset_user(uid, gid); if (status == 0) nfsd4_sync_rec_dir(); - mnt_drop_write(rec_dir.path.mnt); + mnt_drop_write(rec_dir.mnt); out: if (status) printk("NFSD: Failed to remove expired client state directory" @@ -357,17 +357,17 @@ nfsd4_recdir_purge_old(void) { if (!rec_dir_init) return; - status = mnt_want_write(rec_dir.path.mnt); + status = mnt_want_write(rec_dir.mnt); if (status) goto out; - status = nfsd4_list_rec_dir(rec_dir.path.dentry, purge_old); + status = nfsd4_list_rec_dir(rec_dir.dentry, purge_old); if (status == 0) nfsd4_sync_rec_dir(); - mnt_drop_write(rec_dir.path.mnt); + mnt_drop_write(rec_dir.mnt); out: if (status) printk("nfsd4: failed to purge old clients from recovery" - " directory %s\n", rec_dir.path.dentry->d_name.name); + " directory %s\n", rec_dir.dentry->d_name.name); } static int @@ -387,10 +387,10 @@ int nfsd4_recdir_load(void) { int status; - status = nfsd4_list_rec_dir(rec_dir.path.dentry, load_recdir); + status = nfsd4_list_rec_dir(rec_dir.dentry, load_recdir); if (status) printk("nfsd4: failed loading clients from recovery" - " directory %s\n", rec_dir.path.dentry->d_name.name); + " directory %s\n", rec_dir.dentry->d_name.name); return status; } @@ -412,7 +412,7 @@ nfsd4_init_recdir(char *rec_dirname) nfs4_save_user(&uid, &gid); - status = path_lookup(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + status = kern_path(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &rec_dir); if (status) printk("NFSD: unable to find recovery directory %s\n", @@ -429,5 +429,5 @@ nfsd4_shutdown_recdir(void) if (!rec_dir_init) return; rec_dir_init = 0; - path_put(&rec_dir.path); + path_put(&rec_dir); } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0cc7ff5d5ab5..b0bebc552a11 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3284,17 +3284,17 @@ int nfs4_reset_recoverydir(char *recdir) { int status; - struct nameidata nd; + struct path path; - status = path_lookup(recdir, LOOKUP_FOLLOW, &nd); + status = kern_path(recdir, LOOKUP_FOLLOW, &path); if (status) return status; status = -ENOTDIR; - if (S_ISDIR(nd.path.dentry->d_inode->i_mode)) { + if (S_ISDIR(path.dentry->d_inode->i_mode)) { nfs4_set_recdir(recdir); status = 0; } - path_put(&nd.path); + path_put(&path); return status; } diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 97543df58242..e3f9783fdcf7 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -341,7 +341,7 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) { - struct nameidata nd; + struct path path; char *fo_path; int error; @@ -356,13 +356,13 @@ static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) if (qword_get(&buf, fo_path, size) < 0) return -EINVAL; - error = path_lookup(fo_path, 0, &nd); + error = kern_path(fo_path, 0, &path); if (error) return error; - error = nlmsvc_unlock_all_by_sb(nd.path.mnt->mnt_sb); + error = nlmsvc_unlock_all_by_sb(path.mnt->mnt_sb); - path_put(&nd.path); + path_put(&path); return error; } -- cgit v1.2.3-59-g8ed1b From 421748ecde8e69a6364e5ae66eb3bf87e1f995c0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 01:04:36 -0400 Subject: [PATCH] assorted path_lookup() -> kern_path() conversions more nameidata eviction Signed-off-by: Al Viro --- fs/block_dev.c | 14 +++++++------- fs/configfs/symlink.c | 16 ++++++++-------- fs/ecryptfs/main.c | 23 +++++++++-------------- net/unix/af_unix.c | 18 ++++++++++-------- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 218408eed1bb..d06fe3c3dd3f 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1268,33 +1268,33 @@ EXPORT_SYMBOL(ioctl_by_bdev); * namespace if possible and return it. Return ERR_PTR(error) * otherwise. */ -struct block_device *lookup_bdev(const char *path) +struct block_device *lookup_bdev(const char *pathname) { struct block_device *bdev; struct inode *inode; - struct nameidata nd; + struct path path; int error; - if (!path || !*path) + if (!pathname || !*pathname) return ERR_PTR(-EINVAL); - error = path_lookup(path, LOOKUP_FOLLOW, &nd); + error = kern_path(pathname, LOOKUP_FOLLOW, &path); if (error) return ERR_PTR(error); - inode = nd.path.dentry->d_inode; + inode = path.dentry->d_inode; error = -ENOTBLK; if (!S_ISBLK(inode->i_mode)) goto fail; error = -EACCES; - if (nd.path.mnt->mnt_flags & MNT_NODEV) + if (path.mnt->mnt_flags & MNT_NODEV) goto fail; error = -ENOMEM; bdev = bd_acquire(inode); if (!bdev) goto fail; out: - path_put(&nd.path); + path_put(&path); return bdev; fail: bdev = ERR_PTR(error); diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index bf74973b0492..932a92b31483 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -108,18 +108,18 @@ out: } -static int get_target(const char *symname, struct nameidata *nd, +static int get_target(const char *symname, struct path *path, struct config_item **target) { int ret; - ret = path_lookup(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, nd); + ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path); if (!ret) { - if (nd->path.dentry->d_sb == configfs_sb) { - *target = configfs_get_config_item(nd->path.dentry); + if (path->dentry->d_sb == configfs_sb) { + *target = configfs_get_config_item(path->dentry); if (!*target) { ret = -ENOENT; - path_put(&nd->path); + path_put(path); } } else ret = -EPERM; @@ -132,7 +132,7 @@ static int get_target(const char *symname, struct nameidata *nd, int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int ret; - struct nameidata nd; + struct path path; struct configfs_dirent *sd; struct config_item *parent_item; struct config_item *target_item; @@ -159,7 +159,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna !type->ct_item_ops->allow_link) goto out_put; - ret = get_target(symname, &nd, &target_item); + ret = get_target(symname, &path, &target_item); if (ret) goto out_put; @@ -174,7 +174,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna } config_item_put(target_item); - path_put(&nd.path); + path_put(&path); out_put: config_item_put(parent_item); diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 046e027a4cb1..64d2ba980df4 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -471,31 +471,26 @@ out: */ static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) { + struct path path; int rc; - struct nameidata nd; - struct dentry *lower_root; - struct vfsmount *lower_mnt; - memset(&nd, 0, sizeof(struct nameidata)); - rc = path_lookup(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); + rc = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); if (rc) { ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n"); goto out; } - lower_root = nd.path.dentry; - lower_mnt = nd.path.mnt; - ecryptfs_set_superblock_lower(sb, lower_root->d_sb); - sb->s_maxbytes = lower_root->d_sb->s_maxbytes; - sb->s_blocksize = lower_root->d_sb->s_blocksize; - ecryptfs_set_dentry_lower(sb->s_root, lower_root); - ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt); - rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0); + ecryptfs_set_superblock_lower(sb, path.dentry->d_sb); + sb->s_maxbytes = path.dentry->d_sb->s_maxbytes; + sb->s_blocksize = path.dentry->d_sb->s_blocksize; + ecryptfs_set_dentry_lower(sb->s_root, path.dentry); + ecryptfs_set_dentry_lower_mnt(sb->s_root, path.mnt); + rc = ecryptfs_interpose(path.dentry, sb->s_root, sb, 0); if (rc) goto out_free; rc = 0; goto out; out_free: - path_put(&nd.path); + path_put(&path); out: return rc; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c647aab8d418..dc504d308ec0 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -711,28 +711,30 @@ static struct sock *unix_find_other(struct net *net, int type, unsigned hash, int *error) { struct sock *u; - struct nameidata nd; + struct path path; int err = 0; if (sunname->sun_path[0]) { - err = path_lookup(sunname->sun_path, LOOKUP_FOLLOW, &nd); + struct inode *inode; + err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path); if (err) goto fail; - err = vfs_permission(&nd, MAY_WRITE); + inode = path.dentry->d_inode; + err = inode_permission(inode, MAY_WRITE); if (err) goto put_fail; err = -ECONNREFUSED; - if (!S_ISSOCK(nd.path.dentry->d_inode->i_mode)) + if (!S_ISSOCK(inode->i_mode)) goto put_fail; - u = unix_find_socket_byinode(net, nd.path.dentry->d_inode); + u = unix_find_socket_byinode(net, inode); if (!u) goto put_fail; if (u->sk_type == type) - touch_atime(nd.path.mnt, nd.path.dentry); + touch_atime(path.mnt, path.dentry); - path_put(&nd.path); + path_put(&path); err=-EPROTOTYPE; if (u->sk_type != type) { @@ -753,7 +755,7 @@ static struct sock *unix_find_other(struct net *net, return u; put_fail: - path_put(&nd.path); + path_put(&path); fail: *error=err; return NULL; -- cgit v1.2.3-59-g8ed1b From 98bc993f99e51467057ef699e47fec020f24d233 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 01:06:21 -0400 Subject: [PATCH] get rid of nameidata in audit_tree Signed-off-by: Al Viro --- kernel/audit_tree.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index f7921a2ecf16..8ba0e0d934f2 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -532,7 +532,7 @@ void audit_trim_trees(void) list_add(&cursor, &tree_list); while (cursor.next != &tree_list) { struct audit_tree *tree; - struct nameidata nd; + struct path path; struct vfsmount *root_mnt; struct node *node; struct list_head list; @@ -544,12 +544,12 @@ void audit_trim_trees(void) list_add(&cursor, &tree->list); mutex_unlock(&audit_filter_mutex); - err = path_lookup(tree->pathname, 0, &nd); + err = kern_path(tree->pathname, 0, &path); if (err) goto skip_it; - root_mnt = collect_mounts(nd.path.mnt, nd.path.dentry); - path_put(&nd.path); + root_mnt = collect_mounts(path.mnt, path.dentry); + path_put(&path); if (!root_mnt) goto skip_it; @@ -580,19 +580,19 @@ skip_it: } static int is_under(struct vfsmount *mnt, struct dentry *dentry, - struct nameidata *nd) + struct path *path) { - if (mnt != nd->path.mnt) { + if (mnt != path->mnt) { for (;;) { if (mnt->mnt_parent == mnt) return 0; - if (mnt->mnt_parent == nd->path.mnt) + if (mnt->mnt_parent == path->mnt) break; mnt = mnt->mnt_parent; } dentry = mnt->mnt_mountpoint; } - return is_subdir(dentry, nd->path.dentry); + return is_subdir(dentry, path->dentry); } int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op) @@ -618,7 +618,7 @@ void audit_put_tree(struct audit_tree *tree) int audit_add_tree_rule(struct audit_krule *rule) { struct audit_tree *seed = rule->tree, *tree; - struct nameidata nd; + struct path path; struct vfsmount *mnt, *p; struct list_head list; int err; @@ -637,11 +637,11 @@ int audit_add_tree_rule(struct audit_krule *rule) /* do not set rule->tree yet */ mutex_unlock(&audit_filter_mutex); - err = path_lookup(tree->pathname, 0, &nd); + err = kern_path(tree->pathname, 0, &path); if (err) goto Err; - mnt = collect_mounts(nd.path.mnt, nd.path.dentry); - path_put(&nd.path); + mnt = collect_mounts(path.mnt, path.dentry); + path_put(&path); if (!mnt) { err = -ENOMEM; goto Err; @@ -690,29 +690,29 @@ int audit_tag_tree(char *old, char *new) { struct list_head cursor, barrier; int failed = 0; - struct nameidata nd; + struct path path; struct vfsmount *tagged; struct list_head list; struct vfsmount *mnt; struct dentry *dentry; int err; - err = path_lookup(new, 0, &nd); + err = kern_path(new, 0, &path); if (err) return err; - tagged = collect_mounts(nd.path.mnt, nd.path.dentry); - path_put(&nd.path); + tagged = collect_mounts(path.mnt, path.dentry); + path_put(&path); if (!tagged) return -ENOMEM; - err = path_lookup(old, 0, &nd); + err = kern_path(old, 0, &path); if (err) { drop_collected_mounts(tagged); return err; } - mnt = mntget(nd.path.mnt); - dentry = dget(nd.path.dentry); - path_put(&nd.path); + mnt = mntget(path.mnt); + dentry = dget(path.dentry); + path_put(&path); if (dentry == tagged->mnt_root && dentry == mnt->mnt_root) follow_up(&mnt, &dentry); @@ -733,7 +733,7 @@ int audit_tag_tree(char *old, char *new) list_add(&cursor, &tree->list); mutex_unlock(&audit_filter_mutex); - err = path_lookup(tree->pathname, 0, &nd); + err = kern_path(tree->pathname, 0, &path); if (err) { put_tree(tree); mutex_lock(&audit_filter_mutex); @@ -741,15 +741,15 @@ int audit_tag_tree(char *old, char *new) } spin_lock(&vfsmount_lock); - if (!is_under(mnt, dentry, &nd)) { + if (!is_under(mnt, dentry, &path)) { spin_unlock(&vfsmount_lock); - path_put(&nd.path); + path_put(&path); put_tree(tree); mutex_lock(&audit_filter_mutex); continue; } spin_unlock(&vfsmount_lock); - path_put(&nd.path); + path_put(&path); list_for_each_entry(p, &list, mnt_list) { failed = tag_chunk(p->mnt_root->d_inode, tree); -- cgit v1.2.3-59-g8ed1b From 8737f3a1b3c6a38a2a064552d4536633a5a16cd3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 22:36:57 -0400 Subject: [PATCH] get rid of path_lookup_create() ... and don't pass bogus flags when we are just looking for parent. Fold __path_lookup_intent_open() into path_lookup_open() while we are at it; that's the only remaining caller. Signed-off-by: Al Viro --- fs/namei.c | 61 ++++++++++++++++++++++--------------------------------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 4a56f9b59e8c..e584f04745b5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1147,9 +1147,16 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } -static int __path_lookup_intent_open(int dfd, const char *name, - unsigned int lookup_flags, struct nameidata *nd, - int open_flags, int create_mode) +/** + * path_lookup_open - lookup a file path with open intent + * @dfd: the directory to use as base, or AT_FDCWD + * @name: pointer to file name + * @lookup_flags: lookup intent flags + * @nd: pointer to nameidata + * @open_flags: open intent flags + */ +int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags, + struct nameidata *nd, int open_flags) { struct file *filp = get_empty_filp(); int err; @@ -1158,7 +1165,7 @@ static int __path_lookup_intent_open(int dfd, const char *name, return -ENFILE; nd->intent.open.file = filp; nd->intent.open.flags = open_flags; - nd->intent.open.create_mode = create_mode; + nd->intent.open.create_mode = 0; err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd); if (IS_ERR(nd->intent.open.file)) { if (err == 0) { @@ -1170,38 +1177,6 @@ static int __path_lookup_intent_open(int dfd, const char *name, return err; } -/** - * path_lookup_open - lookup a file path with open intent - * @dfd: the directory to use as base, or AT_FDCWD - * @name: pointer to file name - * @lookup_flags: lookup intent flags - * @nd: pointer to nameidata - * @open_flags: open intent flags - */ -int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags, - struct nameidata *nd, int open_flags) -{ - return __path_lookup_intent_open(dfd, name, lookup_flags, nd, - open_flags, 0); -} - -/** - * path_lookup_create - lookup a file path with open + create intent - * @dfd: the directory to use as base, or AT_FDCWD - * @name: pointer to file name - * @lookup_flags: lookup intent flags - * @nd: pointer to nameidata - * @open_flags: open intent flags - * @create_mode: create intent flags - */ -static int path_lookup_create(int dfd, const char *name, - unsigned int lookup_flags, struct nameidata *nd, - int open_flags, int create_mode) -{ - return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE, - nd, open_flags, create_mode); -} - static struct dentry *__lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) { @@ -1711,8 +1686,7 @@ struct file *do_filp_open(int dfd, const char *pathname, /* * Create - we need to know the parent. */ - error = path_lookup_create(dfd, pathname, LOOKUP_PARENT, - &nd, flag, mode); + error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd); if (error) return ERR_PTR(error); @@ -1723,10 +1697,18 @@ struct file *do_filp_open(int dfd, const char *pathname, */ error = -EISDIR; if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len]) - goto exit; + goto exit_parent; + error = -ENFILE; + filp = get_empty_filp(); + if (filp == NULL) + goto exit_parent; + nd.intent.open.file = filp; + nd.intent.open.flags = flag; + nd.intent.open.create_mode = mode; dir = nd.path.dentry; nd.flags &= ~LOOKUP_PARENT; + nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; mutex_lock(&dir->d_inode->i_mutex); path.dentry = lookup_hash(&nd); path.mnt = nd.path.mnt; @@ -1831,6 +1813,7 @@ exit_dput: exit: if (!IS_ERR(nd.intent.open.file)) release_open_intent(&nd); +exit_parent: path_put(&nd.path); return ERR_PTR(error); -- cgit v1.2.3-59-g8ed1b From 2c552d81363e0dac66d478046cc8a3948a67eae9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 Aug 2008 22:40:42 -0400 Subject: [PATCH] don't pass bogus flags to LOOKUP_PARENT lookup in spufs Signed-off-by: Al Viro --- arch/powerpc/platforms/cell/spufs/syscalls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c index 49c87769b1f8..c23617c6baf3 100644 --- a/arch/powerpc/platforms/cell/spufs/syscalls.c +++ b/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -69,9 +69,9 @@ static long do_spu_create(const char __user *pathname, unsigned int flags, if (!IS_ERR(tmp)) { struct nameidata nd; - ret = path_lookup(tmp, LOOKUP_PARENT| - LOOKUP_OPEN|LOOKUP_CREATE, &nd); + ret = path_lookup(tmp, LOOKUP_PARENT, &nd); if (!ret) { + nd.flags |= LOOKUP_OPEN | LOOKUP_CREATE; ret = spufs_create(&nd, flags, mode, neighbor); path_put(&nd.path); } -- cgit v1.2.3-59-g8ed1b From 3516586a424ea5727be089da6541cbd5644f0497 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 Aug 2008 03:00:49 -0400 Subject: [PATCH] make O_EXCL in nd->intent.flags visible in nd->flags New flag: LOOKUP_EXCL. Set before doing the final step of pathname resolution on the paths that have LOOKUP_CREATE and O_EXCL. Signed-off-by: Al Viro --- fs/gfs2/ops_inode.c | 2 +- fs/namei.c | 4 +++- fs/nfs/dir.c | 6 ++---- include/linux/namei.h | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 534e1e2c65ca..d232991b9046 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -69,7 +69,7 @@ static int gfs2_create(struct inode *dir, struct dentry *dentry, mark_inode_dirty(inode); break; } else if (PTR_ERR(inode) != -EEXIST || - (nd && (nd->intent.open.flags & O_EXCL))) { + (nd && nd->flags & LOOKUP_EXCL)) { gfs2_holder_uninit(ghs); return PTR_ERR(inode); } diff --git a/fs/namei.c b/fs/namei.c index e584f04745b5..2b8f823eda44 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1709,6 +1709,8 @@ struct file *do_filp_open(int dfd, const char *pathname, dir = nd.path.dentry; nd.flags &= ~LOOKUP_PARENT; nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN; + if (flag & O_EXCL) + nd.flags |= LOOKUP_EXCL; mutex_lock(&dir->d_inode->i_mutex); path.dentry = lookup_hash(&nd); path.mnt = nd.path.mnt; @@ -1906,7 +1908,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir) if (nd->last_type != LAST_NORM) goto fail; nd->flags &= ~LOOKUP_PARENT; - nd->flags |= LOOKUP_CREATE; + nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL; nd->intent.open.flags = O_EXCL; /* diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index efdba2e802d7..c216c8786c51 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -707,9 +707,7 @@ static int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd) { if (NFS_PROTO(dir)->version == 2) return 0; - if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0) - return 0; - return (nd->intent.open.flags & O_EXCL) != 0; + return nd && nfs_lookup_check_intent(nd, LOOKUP_EXCL); } /* @@ -1009,7 +1007,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash * the dentry. */ - if (nd->intent.open.flags & O_EXCL) { + if (nd->flags & LOOKUP_EXCL) { d_instantiate(dentry, NULL); goto out; } diff --git a/include/linux/namei.h b/include/linux/namei.h index 221e8bc894ba..6b5627afd2eb 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -51,8 +51,9 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; /* * Intent data */ -#define LOOKUP_OPEN (0x0100) -#define LOOKUP_CREATE (0x0200) +#define LOOKUP_OPEN 0x0100 +#define LOOKUP_CREATE 0x0200 +#define LOOKUP_EXCL 0x0400 extern int user_path_at(int, const char __user *, unsigned, struct path *); -- cgit v1.2.3-59-g8ed1b From 72e8264eda02b6ba85a222b9620802ebf23c6a10 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 00:24:08 +0200 Subject: [PATCH] dm: kill lookup_device wrapper Now that lookup_bdev is exported and used by dm just use it directly instead of through a trivial wrapper. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- drivers/md/dm-table.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index a740a6950f59..1407eb96f1a4 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -312,19 +312,6 @@ static inline int check_space(struct dm_table *t) return 0; } -/* - * Convert a device path to a dev_t. - */ -static int lookup_device(const char *path, dev_t *dev) -{ - struct block_device *bdev = lookup_bdev(path); - if (IS_ERR(bdev)) - return PTR_ERR(bdev); - *dev = bdev->bd_dev; - bdput(bdev); - return 0; -} - /* * See if we've already got a device in the list. */ @@ -437,8 +424,12 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti, return -EOVERFLOW; } else { /* convert the path to a device */ - if ((r = lookup_device(path, &dev))) - return r; + struct block_device *bdev = lookup_bdev(path); + + if (IS_ERR(bdev)) + return PTR_ERR(bdev); + dev = bdev->bd_dev; + bdput(bdev); } dd = find_device(&t->devices, dev); -- cgit v1.2.3-59-g8ed1b From ca30bc99527ab968707bafc09e38807de7e70c4a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 00:27:59 +0200 Subject: [PATCH] hpfs: cleanup ->setattr Reformat hpfs_notify_change to standard kernel style to make it readable and rename it to hpfs_setattr as that's what the method is called. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/hpfs/file.c | 2 +- fs/hpfs/hpfs_fn.h | 2 +- fs/hpfs/inode.c | 29 +++++++++++++++++++---------- fs/hpfs/namei.c | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index be8be5040e07..64ab52259204 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -143,5 +143,5 @@ const struct file_operations hpfs_file_ops = const struct inode_operations hpfs_file_iops = { .truncate = hpfs_truncate, - .setattr = hpfs_notify_change, + .setattr = hpfs_setattr, }; diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 42ff60ccf2a9..c2ea31bae313 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -275,7 +275,7 @@ void hpfs_init_inode(struct inode *); void hpfs_read_inode(struct inode *); void hpfs_write_inode(struct inode *); void hpfs_write_inode_nolock(struct inode *); -int hpfs_notify_change(struct dentry *, struct iattr *); +int hpfs_setattr(struct dentry *, struct iattr *); void hpfs_write_if_changed(struct inode *); void hpfs_delete_inode(struct inode *); diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 85d3e1d9ac00..39a1bfbea312 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -260,19 +260,28 @@ void hpfs_write_inode_nolock(struct inode *i) brelse(bh); } -int hpfs_notify_change(struct dentry *dentry, struct iattr *attr) +int hpfs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; - int error=0; + int error = -EINVAL; + lock_kernel(); - if ( ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) || - (hpfs_sb(inode->i_sb)->sb_root == inode->i_ino) ) { - error = -EINVAL; - } else if ((error = inode_change_ok(inode, attr))) { - } else if ((error = inode_setattr(inode, attr))) { - } else { - hpfs_write_inode(inode); - } + if (inode->i_ino == hpfs_sb(inode->i_sb)->sb_root) + goto out_unlock; + if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) + goto out_unlock; + + error = inode_change_ok(inode, attr); + if (error) + goto out_unlock; + + error = inode_setattr(inode, attr); + if (error) + goto out_unlock; + + hpfs_write_inode(inode); + + out_unlock: unlock_kernel(); return error; } diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index d9c59a775449..10783f3d265a 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -669,5 +669,5 @@ const struct inode_operations hpfs_dir_iops = .rmdir = hpfs_rmdir, .mknod = hpfs_mknod, .rename = hpfs_rename, - .setattr = hpfs_notify_change, + .setattr = hpfs_setattr, }; -- cgit v1.2.3-59-g8ed1b From a518ab9329041411526ab8e05edfda7e2715244f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 15:34:22 +0200 Subject: [PATCH] tidy up chrdev_open Use a single goto label for chrdev_put + return error cases. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/char_dev.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/char_dev.c b/fs/char_dev.c index 262fa10e213d..700697a72618 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -386,15 +386,22 @@ static int chrdev_open(struct inode *inode, struct file *filp) cdev_put(new); if (ret) return ret; + + ret = -ENXIO; filp->f_op = fops_get(p->ops); - if (!filp->f_op) { - cdev_put(p); - return -ENXIO; - } - if (filp->f_op->open) + if (!filp->f_op) + goto out_cdev_put; + + if (filp->f_op->open) { ret = filp->f_op->open(inode,filp); - if (ret) - cdev_put(p); + if (ret) + goto out_cdev_put; + } + + return 0; + + out_cdev_put: + cdev_put(p); return ret; } -- cgit v1.2.3-59-g8ed1b From 3a8cff4f026c0b98bee6291eb28d4df42feb76dc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 15:37:17 +0200 Subject: [PATCH] generic_file_llseek tidyups Add kerneldoc for generic_file_llseek and generic_file_llseek_unlocked, use sane variable names and unclutter the code. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/read_write.c | 58 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 9ba495d5a29b..969a6d9c020b 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -31,39 +31,61 @@ const struct file_operations generic_ro_fops = { EXPORT_SYMBOL(generic_ro_fops); +/** + * generic_file_llseek_unlocked - lockless generic llseek implementation + * @file: file structure to seek on + * @offset: file offset to seek to + * @origin: type of seek + * + * Updates the file offset to the value specified by @offset and @origin. + * Locking must be provided by the caller. + */ loff_t generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin) { - loff_t retval; struct inode *inode = file->f_mapping->host; switch (origin) { - case SEEK_END: - offset += inode->i_size; - break; - case SEEK_CUR: - offset += file->f_pos; + case SEEK_END: + offset += inode->i_size; + break; + case SEEK_CUR: + offset += file->f_pos; + break; } - retval = -EINVAL; - if (offset>=0 && offset<=inode->i_sb->s_maxbytes) { - /* Special lock needed here? */ - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } - retval = offset; + + if (offset < 0 || offset > inode->i_sb->s_maxbytes) + return -EINVAL; + + /* Special lock needed here? */ + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; } - return retval; + + return offset; } EXPORT_SYMBOL(generic_file_llseek_unlocked); +/** + * generic_file_llseek - generic llseek implementation for regular files + * @file: file structure to seek on + * @offset: file offset to seek to + * @origin: type of seek + * + * This is a generic implemenation of ->llseek useable for all normal local + * filesystems. It just updates the file offset to the value specified by + * @offset and @origin under i_mutex. + */ loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) { - loff_t n; + loff_t rval; + mutex_lock(&file->f_dentry->d_inode->i_mutex); - n = generic_file_llseek_unlocked(file, offset, origin); + rval = generic_file_llseek_unlocked(file, offset, origin); mutex_unlock(&file->f_dentry->d_inode->i_mutex); - return n; + + return rval; } EXPORT_SYMBOL(generic_file_llseek); -- cgit v1.2.3-59-g8ed1b From 4ea3ada2955e4519befa98ff55dd62d6dfbd1705 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 15:48:57 +0200 Subject: [PATCH] new helper: d_obtain_alias The calling conventions of d_alloc_anon are rather unfortunate for all users, and it's name is not very descriptive either. Add d_obtain_alias as a new exported helper that drops the inode reference in the failure case, too and allows to pass-through NULL pointers and inodes to allow for tail-calls in the export operations. Incidentally this helper already existed as a private function in libfs.c as exportfs_d_alloc so kill that one and switch the callers to d_obtain_alias. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/dcache.c | 35 +++++++++++++++++++++++++++++++++++ fs/libfs.c | 26 ++------------------------ include/linux/dcache.h | 1 + 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index e7a1a99b7464..46fc78206782 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1174,6 +1174,41 @@ struct dentry * d_alloc_anon(struct inode *inode) return res; } +/** + * d_obtain_alias - find or allocate a dentry for a given inode + * @inode: inode to allocate the dentry for + * + * Obtain a dentry for an inode resulting from NFS filehandle conversion or + * similar open by handle operations. The returned dentry may be anonymous, + * or may have a full name (if the inode was already in the cache). + * + * When called on a directory inode, we must ensure that the inode only ever + * has one dentry. If a dentry is found, that is returned instead of + * allocating a new one. + * + * On successful return, the reference to the inode has been transferred + * to the dentry. If %NULL is returned (indicating kmalloc failure), + * the reference on the inode has been released. To make it easier + * to use in export operations a NULL or IS_ERR inode may be passed in + * and will be casted to the corresponding NULL or IS_ERR dentry. + */ +struct dentry *d_obtain_alias(struct inode *inode) +{ + struct dentry *dentry; + + if (!inode) + return NULL; + if (IS_ERR(inode)) + return ERR_CAST(inode); + + dentry = d_alloc_anon(inode); + if (!dentry) { + iput(inode); + dentry = ERR_PTR(-ENOMEM); + } + return dentry; +} +EXPORT_SYMBOL_GPL(d_obtain_alias); /** * d_splice_alias - splice a disconnected dentry into the tree if one exists diff --git a/fs/libfs.c b/fs/libfs.c index 1add676a19df..74688598bcf7 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -732,28 +732,6 @@ out: return ret; } -/* - * This is what d_alloc_anon should have been. Once the exportfs - * argument transition has been finished I will update d_alloc_anon - * to this prototype and this wrapper will go away. --hch - */ -static struct dentry *exportfs_d_alloc(struct inode *inode) -{ - struct dentry *dentry; - - if (!inode) - return NULL; - if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); - - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - dentry = ERR_PTR(-ENOMEM); - } - return dentry; -} - /** * generic_fh_to_dentry - generic helper for the fh_to_dentry export operation * @sb: filesystem to do the file handle conversion on @@ -782,7 +760,7 @@ struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid, break; } - return exportfs_d_alloc(inode); + return d_obtain_alias(inode); } EXPORT_SYMBOL_GPL(generic_fh_to_dentry); @@ -815,7 +793,7 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, break; } - return exportfs_d_alloc(inode); + return d_obtain_alias(inode); } EXPORT_SYMBOL_GPL(generic_fh_to_parent); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index efba1de629ac..2404257d6c67 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -231,6 +231,7 @@ extern struct dentry * d_alloc(struct dentry *, const struct qstr *); extern struct dentry * d_alloc_anon(struct inode *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); +extern struct dentry * d_obtain_alias(struct inode *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); extern void shrink_dcache_for_umount(struct super_block *); -- cgit v1.2.3-59-g8ed1b From 440037287c5ebb07033ab927ca16bb68c291d309 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 15:49:04 +0200 Subject: [PATCH] switch all filesystems over to d_obtain_alias Switch all users of d_alloc_anon to d_obtain_alias. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/dcache.c | 10 ++++----- fs/efs/namei.c | 29 ++++-------------------- fs/exportfs/expfs.c | 4 ---- fs/ext2/namei.c | 13 +---------- fs/ext3/namei.c | 14 +----------- fs/ext4/namei.c | 11 +-------- fs/fat/inode.c | 52 +++++++++++++++---------------------------- fs/fuse/inode.c | 23 +++++++------------ fs/gfs2/ops_export.c | 33 ++++++++------------------- fs/isofs/export.c | 33 +++++---------------------- fs/jfs/namei.c | 15 +------------ fs/nfs/getroot.c | 14 +++++------- fs/ntfs/namei.c | 22 ++---------------- fs/ocfs2/export.c | 30 +++++-------------------- fs/reiserfs/inode.c | 13 ++--------- fs/reiserfs/namei.c | 11 +-------- fs/udf/namei.c | 17 ++------------ fs/xfs/linux-2.6/xfs_export.c | 32 +++----------------------- fs/xfs/linux-2.6/xfs_ioctl.c | 7 +++--- 19 files changed, 78 insertions(+), 305 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 46fc78206782..d45ff7f5ecc2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1187,17 +1187,17 @@ struct dentry * d_alloc_anon(struct inode *inode) * allocating a new one. * * On successful return, the reference to the inode has been transferred - * to the dentry. If %NULL is returned (indicating kmalloc failure), - * the reference on the inode has been released. To make it easier - * to use in export operations a NULL or IS_ERR inode may be passed in - * and will be casted to the corresponding NULL or IS_ERR dentry. + * to the dentry. In case of an error the reference on the inode is released. + * To make it easier to use in export operations a %NULL or IS_ERR inode may + * be passed in and will be the error will be propagate to the return value, + * with a %NULL @inode replaced by ERR_PTR(-ESTALE). */ struct dentry *d_obtain_alias(struct inode *inode) { struct dentry *dentry; if (!inode) - return NULL; + return ERR_PTR(-ESTALE); if (IS_ERR(inode)) return ERR_CAST(inode); diff --git a/fs/efs/namei.c b/fs/efs/namei.c index 291abb11e20e..c3fb5f9c4a44 100644 --- a/fs/efs/namei.c +++ b/fs/efs/namei.c @@ -112,35 +112,14 @@ struct dentry *efs_fh_to_parent(struct super_block *sb, struct fid *fid, struct dentry *efs_get_parent(struct dentry *child) { - struct dentry *parent; - struct inode *inode; + struct dentry *parent = ERR_PTR(-ENOENT); efs_ino_t ino; - long error; lock_kernel(); - - error = -ENOENT; ino = efs_find_entry(child->d_inode, "..", 2); - if (!ino) - goto fail; - - inode = efs_iget(child->d_inode->i_sb, ino); - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - goto fail; - } - - error = -ENOMEM; - parent = d_alloc_anon(inode); - if (!parent) - goto fail_iput; - + if (ino) + parent = d_obtain_alias(efs_iget(child->d_inode->i_sb, ino)); unlock_kernel(); - return parent; - fail_iput: - iput(inode); - fail: - unlock_kernel(); - return ERR_PTR(error); + return parent; } diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index cc91227d3bb8..7b0f75dcf800 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -366,8 +366,6 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, * Try to get any dentry for the given file handle from the filesystem. */ result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); - if (!result) - result = ERR_PTR(-ESTALE); if (IS_ERR(result)) return result; @@ -422,8 +420,6 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, target_dir = nop->fh_to_parent(mnt->mnt_sb, fid, fh_len, fileid_type); - if (!target_dir) - goto err_result; err = PTR_ERR(target_dir); if (IS_ERR(target_dir)) goto err_result; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 80c97fd8c571..a1b328ab1e55 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -73,8 +73,6 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str struct dentry *ext2_get_parent(struct dentry *child) { unsigned long ino; - struct dentry *parent; - struct inode *inode; struct dentry dotdot; dotdot.d_name.name = ".."; @@ -83,16 +81,7 @@ struct dentry *ext2_get_parent(struct dentry *child) ino = ext2_inode_by_name(child->d_inode, &dotdot); if (!ino) return ERR_PTR(-ENOENT); - inode = ext2_iget(child->d_inode->i_sb, ino); - - if (IS_ERR(inode)) - return ERR_CAST(inode); - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - return parent; + return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino)); } /* diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index de13e919cd81..880b54400ac0 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1057,8 +1057,6 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str struct dentry *ext3_get_parent(struct dentry *child) { unsigned long ino; - struct dentry *parent; - struct inode *inode; struct dentry dotdot; struct ext3_dir_entry_2 * de; struct buffer_head *bh; @@ -1068,7 +1066,6 @@ struct dentry *ext3_get_parent(struct dentry *child) dotdot.d_parent = child; /* confusing, isn't it! */ bh = ext3_find_entry(&dotdot, &de); - inode = NULL; if (!bh) return ERR_PTR(-ENOENT); ino = le32_to_cpu(de->inode); @@ -1080,16 +1077,7 @@ struct dentry *ext3_get_parent(struct dentry *child) return ERR_PTR(-EIO); } - inode = ext3_iget(child->d_inode->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - return parent; + return d_obtain_alias(ext3_iget(child->d_inode->i_sb, ino)); } #define S_SHIFT 12 diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 92db9e945147..5b93a7d94d42 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1083,16 +1083,7 @@ struct dentry *ext4_get_parent(struct dentry *child) return ERR_PTR(-EIO); } - inode = ext4_iget(child->d_inode->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - return parent; + return d_obtain_alias(ext4_iget(child->d_inode->i_sb, ino)); } #define S_SHIFT 12 diff --git a/fs/fat/inode.c b/fs/fat/inode.c index d12cdf2a0406..19eafbe3c379 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -681,33 +681,24 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb, inode = NULL; } } - if (!inode) { - /* For now, do nothing - * What we could do is: - * follow the file starting at fh[4], and record - * the ".." entry, and the name of the fh[2] entry. - * The follow the ".." file finding the next step up. - * This way we build a path to the root of - * the tree. If this works, we lookup the path and so - * get this inode into the cache. - * Finally try the fat_iget lookup again - * If that fails, then weare totally out of luck - * But all that is for another day - */ - } - if (!inode) - return ERR_PTR(-ESTALE); - - /* now to find a dentry. - * If possible, get a well-connected one + /* + * For now, do nothing if the inode is not found. + * + * What we could do is: + * + * - follow the file starting at fh[4], and record the ".." entry, + * and the name of the fh[2] entry. + * - then follow the ".." file finding the next step up. + * + * This way we build a path to the root of the tree. If this works, we + * lookup the path and so get this inode into the cache. Finally try + * the fat_iget lookup again. If that fails, then we are totally out + * of luck. But all that is for another day */ - result = d_alloc_anon(inode); - if (result == NULL) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - result->d_op = sb->s_root->d_op; + result = d_obtain_alias(inode); + if (!IS_ERR(result)) + result->d_op = sb->s_root->d_op; return result; } @@ -754,15 +745,8 @@ static struct dentry *fat_get_parent(struct dentry *child) } inode = fat_build_inode(sb, de, i_pos); brelse(bh); - if (IS_ERR(inode)) { - parent = ERR_CAST(inode); - goto out; - } - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } + + parent = d_obtain_alias(inode); out: unlock_super(sb); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 54b1f0e1ef58..2e99f34b4435 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -596,12 +596,8 @@ static struct dentry *fuse_get_dentry(struct super_block *sb, if (inode->i_generation != handle->generation) goto out_iput; - entry = d_alloc_anon(inode); - err = -ENOMEM; - if (!entry) - goto out_iput; - - if (get_node_id(inode) != FUSE_ROOT_ID) { + entry = d_obtain_alias(inode); + if (!IS_ERR(entry) && get_node_id(inode) != FUSE_ROOT_ID) { entry->d_op = &fuse_dentry_operations; fuse_invalidate_entry_cache(entry); } @@ -696,17 +692,14 @@ static struct dentry *fuse_get_parent(struct dentry *child) name.name = ".."; err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode), &name, &outarg, &inode); - if (err && err != -ENOENT) + if (err) { + if (err == -ENOENT) + return ERR_PTR(-ESTALE); return ERR_PTR(err); - if (err || !inode) - return ERR_PTR(-ESTALE); - - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - return ERR_PTR(-ENOMEM); } - if (get_node_id(inode) != FUSE_ROOT_ID) { + + parent = d_obtain_alias(inode); + if (!IS_ERR(parent) && get_node_id(inode) != FUSE_ROOT_ID) { parent->d_op = &fuse_dentry_operations; fuse_invalidate_entry_cache(parent); } diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index 9cda8536530c..bbb8c36403a9 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -130,28 +130,17 @@ static int gfs2_get_name(struct dentry *parent, char *name, static struct dentry *gfs2_get_parent(struct dentry *child) { struct qstr dotdot; - struct inode *inode; struct dentry *dentry; - gfs2_str2qstr(&dotdot, ".."); - inode = gfs2_lookupi(child->d_inode, &dotdot, 1); - - if (!inode) - return ERR_PTR(-ENOENT); /* - * In case of an error, @inode carries the error value, and we - * have to return that as a(n invalid) pointer to dentry. + * XXX(hch): it would be a good idea to keep this around as a + * static variable. */ - if (IS_ERR(inode)) - return ERR_CAST(inode); - - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } + gfs2_str2qstr(&dotdot, ".."); - dentry->d_op = &gfs2_dops; + dentry = d_obtain_alias(gfs2_lookupi(child->d_inode, &dotdot, 1)); + if (!IS_ERR(dentry)) + dentry->d_op = &gfs2_dops; return dentry; } @@ -233,13 +222,9 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, gfs2_glock_dq_uninit(&i_gh); out_inode: - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - - dentry->d_op = &gfs2_dops; + dentry = d_obtain_alias(inode); + if (!IS_ERR(dentry)) + dentry->d_op = &gfs2_dops; return dentry; fail_rgd: diff --git a/fs/isofs/export.c b/fs/isofs/export.c index bb219138331a..e81a30593ba9 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -22,7 +22,7 @@ isofs_export_iget(struct super_block *sb, __u32 generation) { struct inode *inode; - struct dentry *result; + if (block == 0) return ERR_PTR(-ESTALE); inode = isofs_iget(sb, block, offset); @@ -32,12 +32,7 @@ isofs_export_iget(struct super_block *sb, iput(inode); return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return d_obtain_alias(inode); } /* This function is surprisingly simple. The trick is understanding @@ -51,7 +46,6 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) unsigned long parent_offset = 0; struct inode *child_inode = child->d_inode; struct iso_inode_info *e_child_inode = ISOFS_I(child_inode); - struct inode *parent_inode = NULL; struct iso_directory_record *de = NULL; struct buffer_head * bh = NULL; struct dentry *rv = NULL; @@ -104,28 +98,11 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) /* Normalize */ isofs_normalize_block_and_offset(de, &parent_block, &parent_offset); - /* Get the inode. */ - parent_inode = isofs_iget(child_inode->i_sb, - parent_block, - parent_offset); - if (IS_ERR(parent_inode)) { - rv = ERR_CAST(parent_inode); - if (rv != ERR_PTR(-ENOMEM)) - rv = ERR_PTR(-EACCES); - goto out; - } - - /* Allocate the dentry. */ - rv = d_alloc_anon(parent_inode); - if (rv == NULL) { - rv = ERR_PTR(-ENOMEM); - goto out; - } - + rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block, + parent_offset)); out: - if (bh) { + if (bh) brelse(bh); - } return rv; } diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 2aba82386810..e199dde7b83c 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1511,25 +1511,12 @@ struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid, struct dentry *jfs_get_parent(struct dentry *dentry) { - struct super_block *sb = dentry->d_inode->i_sb; - struct dentry *parent = ERR_PTR(-ENOENT); - struct inode *inode; unsigned long parent_ino; parent_ino = le32_to_cpu(JFS_IP(dentry->d_inode)->i_dtroot.header.idotdot); - inode = jfs_iget(sb, parent_ino); - if (IS_ERR(inode)) { - parent = ERR_CAST(inode); - } else { - parent = d_alloc_anon(inode); - if (!parent) { - parent = ERR_PTR(-ENOMEM); - iput(inode); - } - } - return parent; + return d_obtain_alias(jfs_iget(dentry->d_inode->i_sb, parent_ino)); } const struct inode_operations jfs_dir_inode_operations = { diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index fae97196daad..b7c9b2df1f29 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -107,11 +107,10 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ - mntroot = d_alloc_anon(inode); - if (!mntroot) { - iput(inode); + mntroot = d_obtain_alias(inode); + if (IS_ERR(mntroot)) { dprintk("nfs_get_root: get root dentry failed\n"); - return ERR_PTR(-ENOMEM); + return mntroot; } security_d_instantiate(mntroot, inode); @@ -277,11 +276,10 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ - mntroot = d_alloc_anon(inode); - if (!mntroot) { - iput(inode); + mntroot = d_obtain_alias(inode); + if (IS_ERR(mntroot)) { dprintk("nfs_get_root: get root dentry failed\n"); - return ERR_PTR(-ENOMEM); + return mntroot; } security_d_instantiate(mntroot, inode); diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 9e8a95be7a1e..2ca00153b6ec 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -304,8 +304,6 @@ static struct dentry *ntfs_get_parent(struct dentry *child_dent) ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; - struct inode *parent_vi; - struct dentry *parent_dent; unsigned long parent_ino; int err; @@ -345,24 +343,8 @@ try_next: /* Release the search context and the mft record of the child. */ ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); - /* Get the inode of the parent directory. */ - parent_vi = ntfs_iget(vi->i_sb, parent_ino); - if (IS_ERR(parent_vi) || unlikely(is_bad_inode(parent_vi))) { - if (!IS_ERR(parent_vi)) - iput(parent_vi); - ntfs_error(vi->i_sb, "Failed to get parent directory inode " - "0x%lx of child inode 0x%lx.", parent_ino, - vi->i_ino); - return ERR_PTR(-EACCES); - } - /* Finally get a dentry for the parent directory and return it. */ - parent_dent = d_alloc_anon(parent_vi); - if (unlikely(!parent_dent)) { - iput(parent_vi); - return ERR_PTR(-ENOMEM); - } - ntfs_debug("Done for inode 0x%lx.", vi->i_ino); - return parent_dent; + + return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); } static struct inode *ntfs_nfs_get_inode(struct super_block *sb, diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index 67527cebf214..2f27b332d8b3 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -68,14 +68,9 @@ static struct dentry *ocfs2_get_dentry(struct super_block *sb, return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - - if (!result) { - iput(inode); - mlog_errno(-ENOMEM); - return ERR_PTR(-ENOMEM); - } - result->d_op = &ocfs2_dentry_ops; + result = d_obtain_alias(inode); + if (!IS_ERR(result)) + result->d_op = &ocfs2_dentry_ops; mlog_exit_ptr(result); return result; @@ -86,7 +81,6 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) int status; u64 blkno; struct dentry *parent; - struct inode *inode; struct inode *dir = child->d_inode; mlog_entry("(0x%p, '%.*s')\n", child, @@ -109,21 +103,9 @@ static struct dentry *ocfs2_get_parent(struct dentry *child) goto bail_unlock; } - inode = ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0, 0); - if (IS_ERR(inode)) { - mlog(ML_ERROR, "Unable to create inode %llu\n", - (unsigned long long)blkno); - parent = ERR_PTR(-EACCES); - goto bail_unlock; - } - - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - - parent->d_op = &ocfs2_dentry_ops; + parent = d_obtain_alias(ocfs2_iget(OCFS2_SB(dir->i_sb), blkno, 0, 0)); + if (!IS_ERR(parent)) + parent->d_op = &ocfs2_dentry_ops; bail_unlock: ocfs2_inode_unlock(dir, 0); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 5699171212ae..6c4c2c69449f 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1522,7 +1522,6 @@ static struct dentry *reiserfs_get_dentry(struct super_block *sb, { struct cpu_key key; - struct dentry *result; struct inode *inode; key.on_disk_key.k_objectid = objectid; @@ -1535,16 +1534,8 @@ static struct dentry *reiserfs_get_dentry(struct super_block *sb, inode = NULL; } reiserfs_write_unlock(sb); - if (!inode) - inode = ERR_PTR(-ESTALE); - if (IS_ERR(inode)) - return ERR_CAST(inode); - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + + return d_obtain_alias(inode); } struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid, diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index c1add28dd45e..f89ebb943f3f 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -383,7 +383,6 @@ struct dentry *reiserfs_get_parent(struct dentry *child) struct inode *inode = NULL; struct reiserfs_dir_entry de; INITIALIZE_PATH(path_to_entry); - struct dentry *parent; struct inode *dir = child->d_inode; if (dir->i_nlink == 0) { @@ -401,15 +400,7 @@ struct dentry *reiserfs_get_parent(struct dentry *child) inode = reiserfs_iget(dir->i_sb, (struct cpu_key *)&(de.de_dir_id)); reiserfs_write_unlock(dir->i_sb); - if (!inode || IS_ERR(inode)) { - return ERR_PTR(-EACCES); - } - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - return parent; + return d_obtain_alias(inode); } /* add entry to the directory (entry can be hidden). diff --git a/fs/udf/namei.c b/fs/udf/namei.c index d3231947db19..7578fae12d3c 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -1243,7 +1243,6 @@ end_rename: static struct dentry *udf_get_parent(struct dentry *child) { - struct dentry *parent; struct inode *inode = NULL; struct dentry dotdot; struct fileIdentDesc cfi; @@ -1266,13 +1265,7 @@ static struct dentry *udf_get_parent(struct dentry *child) goto out_unlock; unlock_kernel(); - parent = d_alloc_anon(inode); - if (!parent) { - iput(inode); - parent = ERR_PTR(-ENOMEM); - } - - return parent; + return d_obtain_alias(inode); out_unlock: unlock_kernel(); return ERR_PTR(-EACCES); @@ -1283,7 +1276,6 @@ static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block, u16 partref, __u32 generation) { struct inode *inode; - struct dentry *result; kernel_lb_addr loc; if (block == 0) @@ -1300,12 +1292,7 @@ static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block, iput(inode); return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return d_obtain_alias(inode); } static struct dentry *udf_fh_to_dentry(struct super_block *sb, diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index 24fd598af846..7f7abec25e14 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -148,7 +148,6 @@ xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid, { struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; struct inode *inode = NULL; - struct dentry *result; if (fh_len < xfs_fileid_length(fileid_type)) return NULL; @@ -164,16 +163,7 @@ xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid, break; } - if (!inode) - return NULL; - if (IS_ERR(inode)) - return ERR_CAST(inode); - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return d_obtain_alias(inode); } STATIC struct dentry * @@ -182,7 +172,6 @@ xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid, { struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; struct inode *inode = NULL; - struct dentry *result; switch (fileid_type) { case FILEID_INO32_GEN_PARENT: @@ -195,16 +184,7 @@ xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid, break; } - if (!inode) - return NULL; - if (IS_ERR(inode)) - return ERR_CAST(inode); - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return d_obtain_alias(inode); } STATIC struct dentry * @@ -213,18 +193,12 @@ xfs_fs_get_parent( { int error; struct xfs_inode *cip; - struct dentry *parent; error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &cip, NULL); if (unlikely(error)) return ERR_PTR(-error); - parent = d_alloc_anon(VFS_I(cip)); - if (unlikely(!parent)) { - iput(VFS_I(cip)); - return ERR_PTR(-ENOMEM); - } - return parent; + return d_obtain_alias(VFS_I(cip)); } const struct export_operations xfs_export_operations = { diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 48799ba7e3e6..d3438c72dcaf 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -311,11 +311,10 @@ xfs_open_by_handle( return new_fd; } - dentry = d_alloc_anon(inode); - if (dentry == NULL) { - iput(inode); + dentry = d_obtain_alias(inode); + if (IS_ERR(dentry)) { put_unused_fd(new_fd); - return -XFS_ERROR(ENOMEM); + return PTR_ERR(dentry); } /* Ensure umount returns EBUSY on umounts while this file is open. */ -- cgit v1.2.3-59-g8ed1b From 9308a6128d9074e348d9f9b5822546fe12a794a9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 11 Aug 2008 15:49:12 +0200 Subject: [PATCH] kill d_alloc_anon Remove d_alloc_anon now that no users are left. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/dcache.c | 108 +++++++++++++++++-------------------------------- include/linux/dcache.h | 1 - 2 files changed, 37 insertions(+), 72 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index d45ff7f5ecc2..1710d2484fd9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1110,70 +1110,6 @@ static inline struct hlist_head *d_hash(struct dentry *parent, return dentry_hashtable + (hash & D_HASHMASK); } -/** - * d_alloc_anon - allocate an anonymous dentry - * @inode: inode to allocate the dentry for - * - * This is similar to d_alloc_root. It is used by filesystems when - * creating a dentry for a given inode, often in the process of - * mapping a filehandle to a dentry. The returned dentry may be - * anonymous, or may have a full name (if the inode was already - * in the cache). The file system may need to make further - * efforts to connect this dentry into the dcache properly. - * - * When called on a directory inode, we must ensure that - * the inode only ever has one dentry. If a dentry is - * found, that is returned instead of allocating a new one. - * - * On successful return, the reference to the inode has been transferred - * to the dentry. If %NULL is returned (indicating kmalloc failure), - * the reference on the inode has not been released. - */ - -struct dentry * d_alloc_anon(struct inode *inode) -{ - static const struct qstr anonstring = { .name = "" }; - struct dentry *tmp; - struct dentry *res; - - if ((res = d_find_alias(inode))) { - iput(inode); - return res; - } - - tmp = d_alloc(NULL, &anonstring); - if (!tmp) - return NULL; - - tmp->d_parent = tmp; /* make sure dput doesn't croak */ - - spin_lock(&dcache_lock); - res = __d_find_alias(inode, 0); - if (!res) { - /* attach a disconnected dentry */ - res = tmp; - tmp = NULL; - spin_lock(&res->d_lock); - res->d_sb = inode->i_sb; - res->d_parent = res; - res->d_inode = inode; - res->d_flags |= DCACHE_DISCONNECTED; - res->d_flags &= ~DCACHE_UNHASHED; - list_add(&res->d_alias, &inode->i_dentry); - hlist_add_head(&res->d_hash, &inode->i_sb->s_anon); - spin_unlock(&res->d_lock); - - inode = NULL; /* don't drop reference */ - } - spin_unlock(&dcache_lock); - - if (inode) - iput(inode); - if (tmp) - dput(tmp); - return res; -} - /** * d_obtain_alias - find or allocate a dentry for a given inode * @inode: inode to allocate the dentry for @@ -1194,19 +1130,50 @@ struct dentry * d_alloc_anon(struct inode *inode) */ struct dentry *d_obtain_alias(struct inode *inode) { - struct dentry *dentry; + static const struct qstr anonstring = { .name = "" }; + struct dentry *tmp; + struct dentry *res; if (!inode) return ERR_PTR(-ESTALE); if (IS_ERR(inode)) return ERR_CAST(inode); - dentry = d_alloc_anon(inode); - if (!dentry) { - iput(inode); - dentry = ERR_PTR(-ENOMEM); + res = d_find_alias(inode); + if (res) + goto out_iput; + + tmp = d_alloc(NULL, &anonstring); + if (!tmp) { + res = ERR_PTR(-ENOMEM); + goto out_iput; } - return dentry; + tmp->d_parent = tmp; /* make sure dput doesn't croak */ + + spin_lock(&dcache_lock); + res = __d_find_alias(inode, 0); + if (res) { + spin_unlock(&dcache_lock); + dput(tmp); + goto out_iput; + } + + /* attach a disconnected dentry */ + spin_lock(&tmp->d_lock); + tmp->d_sb = inode->i_sb; + tmp->d_inode = inode; + tmp->d_flags |= DCACHE_DISCONNECTED; + tmp->d_flags &= ~DCACHE_UNHASHED; + list_add(&tmp->d_alias, &inode->i_dentry); + hlist_add_head(&tmp->d_hash, &inode->i_sb->s_anon); + spin_unlock(&tmp->d_lock); + + spin_unlock(&dcache_lock); + return tmp; + + out_iput: + iput(inode); + return res; } EXPORT_SYMBOL_GPL(d_obtain_alias); @@ -2379,7 +2346,6 @@ void __init vfs_caches_init(unsigned long mempages) } EXPORT_SYMBOL(d_alloc); -EXPORT_SYMBOL(d_alloc_anon); EXPORT_SYMBOL(d_alloc_root); EXPORT_SYMBOL(d_delete); EXPORT_SYMBOL(d_find_alias); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 2404257d6c67..74c64ae30cf0 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -228,7 +228,6 @@ extern void d_delete(struct dentry *); /* allocate/de-allocate */ extern struct dentry * d_alloc(struct dentry *, const struct qstr *); -extern struct dentry * d_alloc_anon(struct inode *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); extern struct dentry * d_obtain_alias(struct inode *); -- cgit v1.2.3-59-g8ed1b From f3f8e17571934ea253339e15c4ec9b34ea623c6b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 11 Aug 2008 12:39:47 -0400 Subject: [PATCH] reduce the stack footprint of exportfs_decode_fh() no need to have _two_ 256-byte arrays on stack... Signed-off-by: Al Viro --- fs/exportfs/expfs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 7b0f75dcf800..51bdc5cab069 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -94,9 +94,8 @@ find_disconnected_root(struct dentry *dentry) * It may already be, as the flag isn't always updated when connection happens. */ static int -reconnect_path(struct vfsmount *mnt, struct dentry *target_dir) +reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf) { - char nbuf[NAME_MAX+1]; int noprogress = 0; int err = -ESTALE; @@ -360,6 +359,7 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, { const struct export_operations *nop = mnt->mnt_sb->s_export_op; struct dentry *result, *alias; + char nbuf[NAME_MAX+1]; int err; /* @@ -379,7 +379,7 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, * filesystem root. */ if (result->d_flags & DCACHE_DISCONNECTED) { - err = reconnect_path(mnt, result); + err = reconnect_path(mnt, result, nbuf); if (err) goto err_result; } @@ -395,7 +395,6 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, * It's not a directory. Life is a little more complicated. */ struct dentry *target_dir, *nresult; - char nbuf[NAME_MAX+1]; /* * See if either the dentry we just got from the filesystem @@ -429,7 +428,7 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, * connected to the filesystem root. The VFS really doesn't * like disconnected directories.. */ - err = reconnect_path(mnt, target_dir); + err = reconnect_path(mnt, target_dir, nbuf); if (err) { dput(target_dir); goto err_result; -- cgit v1.2.3-59-g8ed1b From 2628b766363aa3791e816a7e5807373ef551c776 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 31 Jul 2008 17:16:51 +0100 Subject: [PATCH] Factor out nfsd_do_readdir() into its own function Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/nfsd/vfs.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index aa1d0d6489a1..14eda20cc291 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1813,6 +1813,29 @@ out: return err; } +static int nfsd_do_readdir(struct file *file, filldir_t func, + struct readdir_cd *cdp, loff_t *offsetp) +{ + int host_err; + + /* + * Read the directory entries. This silly loop is necessary because + * readdir() is not guaranteed to fill up the entire buffer, but + * may choose to do less. + */ + do { + cdp->err = nfserr_eof; /* will be cleared on successful read */ + host_err = vfs_readdir(file, func, cdp); + } while (host_err >=0 && cdp->err == nfs_ok); + + *offsetp = vfs_llseek(file, 0, 1); + + if (host_err) + return nfserrno(host_err); + else + return cdp->err; +} + /* * Read entries from a directory. * The NFSv3/4 verifier we ignore for now. @@ -1822,7 +1845,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, struct readdir_cd *cdp, filldir_t func) { __be32 err; - int host_err; struct file *file; loff_t offset = *offsetp; @@ -1836,21 +1858,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, goto out_close; } - /* - * Read the directory entries. This silly loop is necessary because - * readdir() is not guaranteed to fill up the entire buffer, but - * may choose to do less. - */ - - do { - cdp->err = nfserr_eof; /* will be cleared on successful read */ - host_err = vfs_readdir(file, func, cdp); - } while (host_err >=0 && cdp->err == nfs_ok); - if (host_err) - err = nfserrno(host_err); - else - err = cdp->err; - *offsetp = vfs_llseek(file, 0, 1); + err = nfsd_do_readdir(file, func, cdp, offsetp); if (err == nfserr_eof || err == nfserr_toosmall) err = nfs_ok; /* can still be found in ->err */ -- cgit v1.2.3-59-g8ed1b From 14f7dd632011bb89c035722edd6ea0d90ca6b078 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 31 Jul 2008 20:29:12 +0100 Subject: [PATCH] Copy XFS readdir hack into nfsd code. Some file systems with their own internal locking have problems with the way that nfsd calls the ->lookup() method from within a filldir function called from their ->readdir() method. The recursion back into the file system code can cause deadlock. XFS has a fairly hackish solution to this which involves doing the readdir() into a locally-allocated buffer, then going back through it calling the filldir function afterwards. It's not ideal, but it works. It's particularly suboptimal because XFS does this for local file systems too, where it's completely unnecessary. Copy this hack into the NFS code where it can be used only for NFS export. In response to feedback, use it unconditionally rather than only for the affected file systems. Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/nfsd/vfs.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 14eda20cc291..93b22f661d9d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1813,27 +1813,105 @@ out: return err; } -static int nfsd_do_readdir(struct file *file, filldir_t func, - struct readdir_cd *cdp, loff_t *offsetp) +/* + * We do this buffering because we must not call back into the file + * system's ->lookup() method from the filldir callback. That may well + * deadlock a number of file systems. + * + * This is based heavily on the implementation of same in XFS. + */ +struct buffered_dirent { + u64 ino; + loff_t offset; + int namlen; + unsigned int d_type; + char name[]; +}; + +struct readdir_data { + char *dirent; + size_t used; +}; + +static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct readdir_data *buf = __buf; + struct buffered_dirent *de = (void *)(buf->dirent + buf->used); + unsigned int reclen; + + reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64)); + if (buf->used + reclen > PAGE_SIZE) + return -EINVAL; + + de->namlen = namlen; + de->offset = offset; + de->ino = ino; + de->d_type = d_type; + memcpy(de->name, name, namlen); + buf->used += reclen; + + return 0; +} + +static int nfsd_buffered_readdir(struct file *file, filldir_t func, + struct readdir_cd *cdp, loff_t *offsetp) { + struct readdir_data buf; + struct buffered_dirent *de; int host_err; + int size; + loff_t offset; - /* - * Read the directory entries. This silly loop is necessary because - * readdir() is not guaranteed to fill up the entire buffer, but - * may choose to do less. - */ - do { - cdp->err = nfserr_eof; /* will be cleared on successful read */ - host_err = vfs_readdir(file, func, cdp); - } while (host_err >=0 && cdp->err == nfs_ok); + buf.dirent = (void *)__get_free_page(GFP_KERNEL); + if (!buf.dirent) + return -ENOMEM; + + offset = *offsetp; + cdp->err = nfserr_eof; /* will be cleared on successful read */ - *offsetp = vfs_llseek(file, 0, 1); + while (1) { + unsigned int reclen; + + buf.used = 0; + + host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf); + if (host_err) + break; + + size = buf.used; + + if (!size) + break; + + + de = (struct buffered_dirent *)buf.dirent; + while (size > 0) { + offset = de->offset; + + if (func(cdp, de->name, de->namlen, de->offset, + de->ino, de->d_type)) + goto done; + + if (cdp->err != nfs_ok) + goto done; + + reclen = ALIGN(sizeof(*de) + de->namlen, + sizeof(u64)); + size -= reclen; + de = (struct buffered_dirent *)((char *)de + reclen); + } + offset = vfs_llseek(file, 0, 1); + } + + done: + free_page((unsigned long)(buf.dirent)); if (host_err) return nfserrno(host_err); - else - return cdp->err; + + *offsetp = offset; + return cdp->err; } /* @@ -1858,7 +1936,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, goto out_close; } - err = nfsd_do_readdir(file, func, cdp, offsetp); + err = nfsd_buffered_readdir(file, func, cdp, offsetp); if (err == nfserr_eof || err == nfserr_toosmall) err = nfs_ok; /* can still be found in ->err */ -- cgit v1.2.3-59-g8ed1b From d88f1833fcbb5663c86253039966f880f8f46b1a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 31 Jul 2008 20:38:04 +0100 Subject: [PATCH] Remove XFS buffered readdir hack Now that we've moved the readdir hack to the nfsd code, we can remove the local version from the XFS code. Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/xfs/linux-2.6/xfs_file.c | 128 -------------------------------------------- 1 file changed, 128 deletions(-) diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 5311c1acdd40..3fee790f138b 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -204,15 +204,6 @@ xfs_file_fsync( return -xfs_fsync(XFS_I(dentry->d_inode)); } -/* - * Unfortunately we can't just use the clean and simple readdir implementation - * below, because nfs might call back into ->lookup from the filldir callback - * and that will deadlock the low-level btree code. - * - * Hopefully we'll find a better workaround that allows to use the optimal - * version at least for local readdirs for 2.6.25. - */ -#if 0 STATIC int xfs_file_readdir( struct file *filp, @@ -244,125 +235,6 @@ xfs_file_readdir( return -error; return 0; } -#else - -struct hack_dirent { - u64 ino; - loff_t offset; - int namlen; - unsigned int d_type; - char name[]; -}; - -struct hack_callback { - char *dirent; - size_t len; - size_t used; -}; - -STATIC int -xfs_hack_filldir( - void *__buf, - const char *name, - int namlen, - loff_t offset, - u64 ino, - unsigned int d_type) -{ - struct hack_callback *buf = __buf; - struct hack_dirent *de = (struct hack_dirent *)(buf->dirent + buf->used); - unsigned int reclen; - - reclen = ALIGN(sizeof(struct hack_dirent) + namlen, sizeof(u64)); - if (buf->used + reclen > buf->len) - return -EINVAL; - - de->namlen = namlen; - de->offset = offset; - de->ino = ino; - de->d_type = d_type; - memcpy(de->name, name, namlen); - buf->used += reclen; - return 0; -} - -STATIC int -xfs_file_readdir( - struct file *filp, - void *dirent, - filldir_t filldir) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - xfs_inode_t *ip = XFS_I(inode); - struct hack_callback buf; - struct hack_dirent *de; - int error; - loff_t size; - int eof = 0; - xfs_off_t start_offset, curr_offset, offset; - - /* - * Try fairly hard to get memory - */ - buf.len = PAGE_CACHE_SIZE; - do { - buf.dirent = kmalloc(buf.len, GFP_KERNEL); - if (buf.dirent) - break; - buf.len >>= 1; - } while (buf.len >= 1024); - - if (!buf.dirent) - return -ENOMEM; - - curr_offset = filp->f_pos; - if (curr_offset == 0x7fffffff) - offset = 0xffffffff; - else - offset = filp->f_pos; - - while (!eof) { - unsigned int reclen; - - start_offset = offset; - - buf.used = 0; - error = -xfs_readdir(ip, &buf, buf.len, &offset, - xfs_hack_filldir); - if (error || offset == start_offset) { - size = 0; - break; - } - - size = buf.used; - de = (struct hack_dirent *)buf.dirent; - while (size > 0) { - curr_offset = de->offset /* & 0x7fffffff */; - if (filldir(dirent, de->name, de->namlen, - curr_offset & 0x7fffffff, - de->ino, de->d_type)) { - goto done; - } - - reclen = ALIGN(sizeof(struct hack_dirent) + de->namlen, - sizeof(u64)); - size -= reclen; - de = (struct hack_dirent *)((char *)de + reclen); - } - } - - done: - if (!error) { - if (size == 0) - filp->f_pos = offset & 0x7fffffff; - else if (de) - filp->f_pos = curr_offset; - } - - kfree(buf.dirent); - return error; -} -#endif STATIC int xfs_file_mmap( -- cgit v1.2.3-59-g8ed1b From 5f556aab907a358c7837cc9a83c3aea4e69cff5b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 31 Jul 2008 20:39:25 +0100 Subject: [JFFS2] Reinstate NFS exportability Now that the readdir/lookup deadlock issues have been dealt with, we can export JFFS2 file systems again. (For now, you have to specify fsid manually; we should add a method to the export_ops to handle that too.) Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/jffs2/super.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index efd401257ed9..4c4e18c54a51 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "compr.h" #include "nodelist.h" @@ -62,6 +63,52 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) return 0; } +static struct inode *jffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, + uint32_t generation) +{ + /* We don't care about i_generation. We'll destroy the flash + before we start re-using inode numbers anyway. And even + if that wasn't true, we'd have other problems...*/ + return jffs2_iget(sb, ino); +} + +static struct dentry *jffs2_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + jffs2_nfs_get_inode); +} + +static struct dentry *jffs2_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + jffs2_nfs_get_inode); +} + +static struct dentry *jffs2_get_parent(struct dentry *child) +{ + struct jffs2_inode_info *f; + uint32_t pino; + + BUG_ON(!S_ISDIR(child->d_inode->i_mode)); + + f = JFFS2_INODE_INFO(child->d_inode); + + pino = f->inocache->pino_nlink; + + JFFS2_DEBUG("Parent of directory ino #%u is #%u\n", + f->inocache->ino, pino); + + return d_obtain_alias(jffs2_iget(child->d_inode->i_sb, pino)); +} + +static struct export_operations jffs2_export_ops = { + .get_parent = jffs2_get_parent, + .fh_to_dentry = jffs2_fh_to_dentry, + .fh_to_parent = jffs2_fh_to_parent, +}; + static const struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, @@ -104,6 +151,7 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) spin_lock_init(&c->inocache_lock); sb->s_op = &jffs2_super_operations; + sb->s_export_op = &jffs2_export_ops; sb->s_flags = sb->s_flags | MS_NOATIME; sb->s_xattr = jffs2_xattr_handlers; #ifdef CONFIG_JFFS2_FS_POSIX_ACL -- cgit v1.2.3-59-g8ed1b From 734711abac46c8fee4d70cc9876ebc6d9edb4971 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Aug 2008 07:26:48 -0400 Subject: [PATCH] get rid of on-stack fake dentry in ext3_get_parent() Better pass parent and qstr to ext3_find_entry() explicitly than use such kludges, especially since the stack footprint is nasty enough and we have every chance to be deep in call chain. Signed-off-by: Al Viro --- fs/ext3/namei.c | 70 +++++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 880b54400ac0..3e5edc92aa0b 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -159,7 +159,7 @@ static void dx_set_count (struct dx_entry *entries, unsigned value); static void dx_set_limit (struct dx_entry *entries, unsigned value); static unsigned dx_root_limit (struct inode *dir, unsigned infosize); static unsigned dx_node_limit (struct inode *dir); -static struct dx_frame *dx_probe(struct dentry *dentry, +static struct dx_frame *dx_probe(struct qstr *entry, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame, @@ -176,8 +176,9 @@ static int ext3_htree_next_block(struct inode *dir, __u32 hash, struct dx_frame *frame, struct dx_frame *frames, __u32 *start_hash); -static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, - struct ext3_dir_entry_2 **res_dir, int *err); +static struct buffer_head * ext3_dx_find_entry(struct inode *dir, + struct qstr *entry, struct ext3_dir_entry_2 **res_dir, + int *err); static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode); @@ -342,7 +343,7 @@ struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, * back to userspace. */ static struct dx_frame * -dx_probe(struct dentry *dentry, struct inode *dir, +dx_probe(struct qstr *entry, struct inode *dir, struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err) { unsigned count, indirect; @@ -353,8 +354,6 @@ dx_probe(struct dentry *dentry, struct inode *dir, u32 hash; frame->bh = NULL; - if (dentry) - dir = dentry->d_parent->d_inode; if (!(bh = ext3_bread (NULL,dir, 0, 0, err))) goto fail; root = (struct dx_root *) bh->b_data; @@ -370,8 +369,8 @@ dx_probe(struct dentry *dentry, struct inode *dir, } hinfo->hash_version = root->info.hash_version; hinfo->seed = EXT3_SB(dir->i_sb)->s_hash_seed; - if (dentry) - ext3fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo); + if (entry) + ext3fs_dirhash(entry->name, entry->len, hinfo); hash = hinfo->hash; if (root->info.unused_flags & 1) { @@ -803,15 +802,15 @@ static inline int ext3_match (int len, const char * const name, */ static inline int search_dirblock(struct buffer_head * bh, struct inode *dir, - struct dentry *dentry, + struct qstr *child, unsigned long offset, struct ext3_dir_entry_2 ** res_dir) { struct ext3_dir_entry_2 * de; char * dlimit; int de_len; - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; + const char *name = child->name; + int namelen = child->len; de = (struct ext3_dir_entry_2 *) bh->b_data; dlimit = bh->b_data + dir->i_sb->s_blocksize; @@ -850,8 +849,9 @@ static inline int search_dirblock(struct buffer_head * bh, * The returned buffer_head has ->b_count elevated. The caller is expected * to brelse() it when appropriate. */ -static struct buffer_head * ext3_find_entry (struct dentry *dentry, - struct ext3_dir_entry_2 ** res_dir) +static struct buffer_head *ext3_find_entry(struct inode *dir, + struct qstr *entry, + struct ext3_dir_entry_2 **res_dir) { struct super_block * sb; struct buffer_head * bh_use[NAMEI_RA_SIZE]; @@ -863,16 +863,15 @@ static struct buffer_head * ext3_find_entry (struct dentry *dentry, buffer */ int num = 0; int nblocks, i, err; - struct inode *dir = dentry->d_parent->d_inode; int namelen; *res_dir = NULL; sb = dir->i_sb; - namelen = dentry->d_name.len; + namelen = entry->len; if (namelen > EXT3_NAME_LEN) return NULL; if (is_dx(dir)) { - bh = ext3_dx_find_entry(dentry, res_dir, &err); + bh = ext3_dx_find_entry(dir, entry, res_dir, &err); /* * On success, or if the error was file not found, * return. Otherwise, fall back to doing a search the @@ -923,7 +922,7 @@ restart: brelse(bh); goto next; } - i = search_dirblock(bh, dir, dentry, + i = search_dirblock(bh, dir, entry, block << EXT3_BLOCK_SIZE_BITS(sb), res_dir); if (i == 1) { EXT3_I(dir)->i_dir_start_lookup = block; @@ -957,8 +956,9 @@ cleanup_and_exit: return ret; } -static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, - struct ext3_dir_entry_2 **res_dir, int *err) +static struct buffer_head * ext3_dx_find_entry(struct inode *dir, + struct qstr *entry, struct ext3_dir_entry_2 **res_dir, + int *err) { struct super_block * sb; struct dx_hash_info hinfo; @@ -968,14 +968,13 @@ static struct buffer_head * ext3_dx_find_entry(struct dentry *dentry, struct buffer_head *bh; unsigned long block; int retval; - int namelen = dentry->d_name.len; - const u8 *name = dentry->d_name.name; - struct inode *dir = dentry->d_parent->d_inode; + int namelen = entry->len; + const u8 *name = entry->name; sb = dir->i_sb; /* NFS may look up ".." - look at dx_root directory block */ - if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ - if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) + if (namelen > 2 || name[0] != '.'|| (namelen == 2 && name[1] != '.')) { + if (!(frame = dx_probe(entry, dir, &hinfo, frames, err))) return NULL; } else { frame = frames; @@ -1036,7 +1035,7 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str if (dentry->d_name.len > EXT3_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - bh = ext3_find_entry(dentry, &de); + bh = ext3_find_entry(dir, &dentry->d_name, &de); inode = NULL; if (bh) { unsigned long ino = le32_to_cpu(de->inode); @@ -1057,15 +1056,11 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str struct dentry *ext3_get_parent(struct dentry *child) { unsigned long ino; - struct dentry dotdot; + struct qstr dotdot = {.name = "..", .len = 2}; struct ext3_dir_entry_2 * de; struct buffer_head *bh; - dotdot.d_name.name = ".."; - dotdot.d_name.len = 2; - dotdot.d_parent = child; /* confusing, isn't it! */ - - bh = ext3_find_entry(&dotdot, &de); + bh = ext3_find_entry(child->d_inode, &dotdot, &de); if (!bh) return ERR_PTR(-ENOENT); ino = le32_to_cpu(de->inode); @@ -1491,7 +1486,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, struct ext3_dir_entry_2 *de; int err; - frame = dx_probe(dentry, NULL, &hinfo, frames, &err); + frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err); if (!frame) return err; entries = frame->entries; @@ -2044,7 +2039,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry) return PTR_ERR(handle); retval = -ENOENT; - bh = ext3_find_entry (dentry, &de); + bh = ext3_find_entry(dir, &dentry->d_name, &de); if (!bh) goto end_rmdir; @@ -2106,7 +2101,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry) handle->h_sync = 1; retval = -ENOENT; - bh = ext3_find_entry (dentry, &de); + bh = ext3_find_entry(dir, &dentry->d_name, &de); if (!bh) goto end_unlink; @@ -2264,7 +2259,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) handle->h_sync = 1; - old_bh = ext3_find_entry (old_dentry, &old_de); + old_bh = ext3_find_entry(old_dir, &old_dentry->d_name, &old_de); /* * Check for inode number is _not_ due to possible IO errors. * We might rmdir the source, keep it as pwd of some process @@ -2277,7 +2272,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, goto end_rename; new_inode = new_dentry->d_inode; - new_bh = ext3_find_entry (new_dentry, &new_de); + new_bh = ext3_find_entry(new_dir, &new_dentry->d_name, &new_de); if (new_bh) { if (!new_inode) { brelse (new_bh); @@ -2343,7 +2338,8 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, struct buffer_head *old_bh2; struct ext3_dir_entry_2 *old_de2; - old_bh2 = ext3_find_entry(old_dentry, &old_de2); + old_bh2 = ext3_find_entry(old_dir, &old_dentry->d_name, + &old_de2); if (old_bh2) { retval = ext3_delete_entry(handle, old_dir, old_de2, old_bh2); -- cgit v1.2.3-59-g8ed1b From a9885444f7ff6e9156adb1adf5558ded9a39ad0a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Aug 2008 07:28:39 -0400 Subject: [PATCH] get rid of on-stack dentry in ext2_get_parent() Signed-off-by: Al Viro --- fs/ext2/dir.c | 14 +++++++------- fs/ext2/ext2.h | 4 ++-- fs/ext2/namei.c | 17 ++++++----------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 11a49ce84392..9a0fc400f91c 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -354,11 +354,11 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) * (as a parameter - res_dir). Page is returned mapped and unlocked. * Entry is guaranteed to be valid. */ -struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, - struct dentry *dentry, struct page ** res_page) +struct ext2_dir_entry_2 *ext2_find_entry (struct inode * dir, + struct qstr *child, struct page ** res_page) { - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; + const char *name = child->name; + int namelen = child->len; unsigned reclen = EXT2_DIR_REC_LEN(namelen); unsigned long start, n; unsigned long npages = dir_pages(dir); @@ -431,13 +431,13 @@ struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) return de; } -ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry) +ino_t ext2_inode_by_name(struct inode *dir, struct qstr *child) { ino_t res = 0; - struct ext2_dir_entry_2 * de; + struct ext2_dir_entry_2 *de; struct page *page; - de = ext2_find_entry (dir, dentry, &page); + de = ext2_find_entry (dir, child, &page); if (de) { res = le32_to_cpu(de->inode); ext2_put_page(page); diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index bae998c1e44e..3203042b36ef 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -105,9 +105,9 @@ extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_wind /* dir.c */ extern int ext2_add_link (struct dentry *, struct inode *); -extern ino_t ext2_inode_by_name(struct inode *, struct dentry *); +extern ino_t ext2_inode_by_name(struct inode *, struct qstr *); extern int ext2_make_empty(struct inode *, struct inode *); -extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **); +extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct qstr *, struct page **); extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *); extern int ext2_empty_dir (struct inode *); extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index a1b328ab1e55..2a747252ec12 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -60,7 +60,7 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str if (dentry->d_name.len > EXT2_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); - ino = ext2_inode_by_name(dir, dentry); + ino = ext2_inode_by_name(dir, &dentry->d_name); inode = NULL; if (ino) { inode = ext2_iget(dir->i_sb, ino); @@ -72,13 +72,8 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str struct dentry *ext2_get_parent(struct dentry *child) { - unsigned long ino; - struct dentry dotdot; - - dotdot.d_name.name = ".."; - dotdot.d_name.len = 2; - - ino = ext2_inode_by_name(child->d_inode, &dotdot); + struct qstr dotdot = {.name = "..", .len = 2}; + unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot); if (!ino) return ERR_PTR(-ENOENT); return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino)); @@ -246,7 +241,7 @@ static int ext2_unlink(struct inode * dir, struct dentry *dentry) struct page * page; int err = -ENOENT; - de = ext2_find_entry (dir, dentry, &page); + de = ext2_find_entry (dir, &dentry->d_name, &page); if (!de) goto out; @@ -288,7 +283,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, struct ext2_dir_entry_2 * old_de; int err = -ENOENT; - old_de = ext2_find_entry (old_dir, old_dentry, &old_page); + old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &old_page); if (!old_de) goto out; @@ -308,7 +303,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, goto out_dir; err = -ENOENT; - new_de = ext2_find_entry (new_dir, new_dentry, &new_page); + new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; inode_inc_link_count(old_inode); -- cgit v1.2.3-59-g8ed1b From 53c9c5c0e32c69f9df1822e47671c13e3402c82f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Aug 2008 07:29:52 -0400 Subject: [PATCH] prepare vfs_readdir() callers to returning filldir result It's not the final state, but it allows moving ->readdir() instances to passing filldir return value to caller of vfs_readdir(). Signed-off-by: Al Viro --- arch/alpha/kernel/osf_sys.c | 7 ++----- arch/parisc/hpux/fs.c | 5 ++--- fs/compat.c | 22 ++++++++-------------- fs/exportfs/expfs.c | 7 ++++--- fs/nfsd/vfs.c | 11 +++++++++-- fs/readdir.c | 22 ++++++++-------------- 6 files changed, 33 insertions(+), 41 deletions(-) diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 8509dad31204..f25f6c490952 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -165,14 +165,11 @@ osf_getdirentries(unsigned int fd, struct osf_dirent __user *dirent, buf.error = 0; error = vfs_readdir(file, osf_filldir, &buf); - if (error < 0) - goto out_putf; - - error = buf.error; + if (error >= 0) + error = buf.error; if (count != buf.count) error = count - buf.count; - out_putf: fput(file); out: return error; diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c index 12c04c5e558b..bd9a4db3bd4c 100644 --- a/arch/parisc/hpux/fs.c +++ b/arch/parisc/hpux/fs.c @@ -127,9 +127,8 @@ int hpux_getdents(unsigned int fd, struct hpux_dirent __user *dirent, unsigned i buf.error = 0; error = vfs_readdir(file, filldir, &buf); - if (error < 0) - goto out_putf; - error = buf.error; + if (error >= 0) + error = buf.error; lastdirent = buf.previous; if (lastdirent) { if (put_user(file->f_pos, &lastdirent->d_off)) diff --git a/fs/compat.c b/fs/compat.c index 5f9ec449c799..cb36245f9fe0 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -869,7 +869,7 @@ asmlinkage long compat_sys_old_readdir(unsigned int fd, buf.dirent = dirent; error = vfs_readdir(file, compat_fillonedir, &buf); - if (error >= 0) + if (buf.result) error = buf.result; fput(file); @@ -956,9 +956,8 @@ asmlinkage long compat_sys_getdents(unsigned int fd, buf.error = 0; error = vfs_readdir(file, compat_filldir, &buf); - if (error < 0) - goto out_putf; - error = buf.error; + if (error >= 0) + error = buf.error; lastdirent = buf.previous; if (lastdirent) { if (put_user(file->f_pos, &lastdirent->d_off)) @@ -966,8 +965,6 @@ asmlinkage long compat_sys_getdents(unsigned int fd, else error = count - buf.count; } - -out_putf: fput(file); out: return error; @@ -1047,19 +1044,16 @@ asmlinkage long compat_sys_getdents64(unsigned int fd, buf.error = 0; error = vfs_readdir(file, compat_filldir64, &buf); - if (error < 0) - goto out_putf; - error = buf.error; + if (error >= 0) + error = buf.error; lastdirent = buf.previous; if (lastdirent) { typeof(lastdirent->d_off) d_off = file->f_pos; - error = -EFAULT; if (__put_user_unaligned(d_off, &lastdirent->d_off)) - goto out_putf; - error = count - buf.count; + error = -EFAULT; + else + error = count - buf.count; } - -out_putf: fput(file); out: return error; diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 51bdc5cab069..80246bad1b7f 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -280,13 +280,14 @@ static int get_name(struct vfsmount *mnt, struct dentry *dentry, int old_seq = buffer.sequence; error = vfs_readdir(file, filldir_one, &buffer); + if (buffer.found) { + error = 0; + break; + } if (error < 0) break; - error = 0; - if (buffer.found) - break; error = -ENOENT; if (old_seq == buffer.sequence) break; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 93b22f661d9d..e3e37f7c8477 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1831,6 +1831,7 @@ struct buffered_dirent { struct readdir_data { char *dirent; size_t used; + int full; }; static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen, @@ -1841,8 +1842,10 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen, unsigned int reclen; reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64)); - if (buf->used + reclen > PAGE_SIZE) + if (buf->used + reclen > PAGE_SIZE) { + buf->full = 1; return -EINVAL; + } de->namlen = namlen; de->offset = offset; @@ -1874,9 +1877,13 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func, unsigned int reclen; buf.used = 0; + buf.full = 0; host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf); - if (host_err) + if (buf.full) + host_err = 0; + + if (host_err < 0) break; size = buf.used; diff --git a/fs/readdir.c b/fs/readdir.c index 93a7559bbfd8..b318d9b5af2e 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -117,7 +117,7 @@ asmlinkage long old_readdir(unsigned int fd, struct old_linux_dirent __user * di buf.dirent = dirent; error = vfs_readdir(file, fillonedir, &buf); - if (error >= 0) + if (buf.result) error = buf.result; fput(file); @@ -209,9 +209,8 @@ asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * diren buf.error = 0; error = vfs_readdir(file, filldir, &buf); - if (error < 0) - goto out_putf; - error = buf.error; + if (error >= 0) + error = buf.error; lastdirent = buf.previous; if (lastdirent) { if (put_user(file->f_pos, &lastdirent->d_off)) @@ -219,8 +218,6 @@ asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * diren else error = count - buf.count; } - -out_putf: fput(file); out: return error; @@ -293,19 +290,16 @@ asmlinkage long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * d buf.error = 0; error = vfs_readdir(file, filldir64, &buf); - if (error < 0) - goto out_putf; - error = buf.error; + if (error >= 0) + error = buf.error; lastdirent = buf.previous; if (lastdirent) { typeof(lastdirent->d_off) d_off = file->f_pos; - error = -EFAULT; if (__put_user(d_off, &lastdirent->d_off)) - goto out_putf; - error = count - buf.count; + error = -EFAULT; + else + error = count - buf.count; } - -out_putf: fput(file); out: return error; -- cgit v1.2.3-59-g8ed1b From c002a6c7977320f95b5edede5ce4e0eeecf291ff Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 17 Aug 2008 17:21:18 +0100 Subject: [PATCH] Optimise NFS readdir hack slightly. Avoid calling the underlying ->readdir() again when we reached the end already; keep going round the loop only if we stopped due to our own buffer being full. [AV: tidy the things up a bit, while we are there] Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/nfsd/vfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index e3e37f7c8477..49d4b8725ca3 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1891,7 +1891,6 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func, if (!size) break; - de = (struct buffered_dirent *)buf.dirent; while (size > 0) { offset = de->offset; @@ -1908,7 +1907,9 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func, size -= reclen; de = (struct buffered_dirent *)((char *)de + reclen); } - offset = vfs_llseek(file, 0, 1); + offset = vfs_llseek(file, 0, SEEK_CUR); + if (!buf.full) + break; } done: -- cgit v1.2.3-59-g8ed1b From 8966c5e0fc867f5a7da5756b4cd1b8bbbed3d5dd Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 18 Aug 2008 15:36:47 +0100 Subject: [JFFS2] Use d_splice_alias() not d_add() in jffs2_lookup() Now that JFFS2 can be exported by NFS, we need to get this right. Signed-off-by: David Woodhouse Signed-off-by: Al Viro --- fs/jffs2/dir.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index b1aaae823a52..621bdfa994e7 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -108,9 +108,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, } } - d_add(target, inode); - - return NULL; + return d_splice_alias(inode, target); } /***********************************************************************/ -- cgit v1.2.3-59-g8ed1b From 6de24f0ed08054b2a202902e4d63beff27654db8 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 28 Aug 2008 06:25:49 +0400 Subject: [PATCH 1/2] anondev: init IDR statically Signed-off-by: Alexey Dobriyan --- fs/super.c | 7 +------ include/linux/fs.h | 1 - init/main.c | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/fs/super.c b/fs/super.c index e931ae9511fe..dd23bf927fbc 100644 --- a/fs/super.c +++ b/fs/super.c @@ -682,7 +682,7 @@ void emergency_remount(void) * filesystems which don't use real block-devices. -- jrs */ -static struct idr unnamed_dev_idr; +static DEFINE_IDR(unnamed_dev_idr); static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */ int set_anon_super(struct super_block *s, void *data) @@ -726,11 +726,6 @@ void kill_anon_super(struct super_block *sb) EXPORT_SYMBOL(kill_anon_super); -void __init unnamed_dev_init(void) -{ - idr_init(&unnamed_dev_idr); -} - void kill_litter_super(struct super_block *sb) { if (sb->s_root) diff --git a/include/linux/fs.h b/include/linux/fs.h index a6a625be13fc..5f70aa62cf0f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1593,7 +1593,6 @@ extern int get_sb_pseudo(struct file_system_type *, char *, struct vfsmount *mnt); extern int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb); int __put_super_and_need_restart(struct super_block *sb); -void unnamed_dev_init(void); /* Alas, no aliases. Too much hassle with bringing module.h everywhere */ #define fops_get(fops) \ diff --git a/init/main.c b/init/main.c index 3e17a3bafe60..c6a1024a27a3 100644 --- a/init/main.c +++ b/init/main.c @@ -670,7 +670,6 @@ asmlinkage void __init start_kernel(void) fork_init(num_physpages); proc_caches_init(); buffer_init(); - unnamed_dev_init(); key_init(); security_init(); vfs_caches_init(num_physpages); -- cgit v1.2.3-59-g8ed1b From ad76cbc63b9db7c98da49af3182a783ca1c80a5d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 28 Aug 2008 06:26:23 +0400 Subject: [PATCH 2/2] anondev: switch to IDA Signed-off-by: Alexey Dobriyan --- fs/super.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/super.c b/fs/super.c index dd23bf927fbc..f31ef824d069 100644 --- a/fs/super.c +++ b/fs/super.c @@ -682,7 +682,7 @@ void emergency_remount(void) * filesystems which don't use real block-devices. -- jrs */ -static DEFINE_IDR(unnamed_dev_idr); +static DEFINE_IDA(unnamed_dev_ida); static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */ int set_anon_super(struct super_block *s, void *data) @@ -691,10 +691,10 @@ int set_anon_super(struct super_block *s, void *data) int error; retry: - if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) + if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0) return -ENOMEM; spin_lock(&unnamed_dev_lock); - error = idr_get_new(&unnamed_dev_idr, NULL, &dev); + error = ida_get_new(&unnamed_dev_ida, &dev); spin_unlock(&unnamed_dev_lock); if (error == -EAGAIN) /* We raced and lost with another CPU. */ @@ -704,7 +704,7 @@ int set_anon_super(struct super_block *s, void *data) if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) { spin_lock(&unnamed_dev_lock); - idr_remove(&unnamed_dev_idr, dev); + ida_remove(&unnamed_dev_ida, dev); spin_unlock(&unnamed_dev_lock); return -EMFILE; } @@ -720,7 +720,7 @@ void kill_anon_super(struct super_block *sb) generic_shutdown_super(sb); spin_lock(&unnamed_dev_lock); - idr_remove(&unnamed_dev_idr, slot); + ida_remove(&unnamed_dev_ida, slot); spin_unlock(&unnamed_dev_lock); } -- cgit v1.2.3-59-g8ed1b From 9fbb76ce0fe96c07c44ba2aec3dc99f4b8d2b9c6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 12 Oct 2008 00:15:17 -0400 Subject: [PATCH] get rid of on-stack dentry in udf Signed-off-by: Al Viro --- fs/udf/namei.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 7578fae12d3c..082409cd4b8a 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -142,7 +142,7 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, } static struct fileIdentDesc *udf_find_entry(struct inode *dir, - struct dentry *dentry, + struct qstr *child, struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) { @@ -159,8 +159,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, sector_t offset; struct extent_position epos = {}; struct udf_inode_info *dinfo = UDF_I(dir); - int isdotdot = dentry->d_name.len == 2 && - dentry->d_name.name[0] == '.' && dentry->d_name.name[1] == '.'; + int isdotdot = child->len == 2 && + child->name[0] == '.' && child->name[1] == '.'; size = udf_ext0_offset(dir) + dir->i_size; f_pos = udf_ext0_offset(dir); @@ -238,8 +238,7 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, continue; flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); - if (flen && udf_match(flen, fname, dentry->d_name.len, - dentry->d_name.name)) + if (flen && udf_match(flen, fname, child->len, child->name)) goto out_ok; } @@ -283,7 +282,7 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, } else #endif /* UDF_RECOVERY */ - if (udf_find_entry(dir, dentry, &fibh, &cfi)) { + if (udf_find_entry(dir, &dentry->d_name, &fibh, &cfi)) { if (fibh.sbh != fibh.ebh) brelse(fibh.ebh); brelse(fibh.sbh); @@ -783,7 +782,7 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry) retval = -ENOENT; lock_kernel(); - fi = udf_find_entry(dir, dentry, &fibh, &cfi); + fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (!fi) goto out; @@ -829,7 +828,7 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry) retval = -ENOENT; lock_kernel(); - fi = udf_find_entry(dir, dentry, &fibh, &cfi); + fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (!fi) goto out; @@ -1113,7 +1112,7 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, struct udf_inode_info *old_iinfo = UDF_I(old_inode); lock_kernel(); - ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); if (ofi) { if (ofibh.sbh != ofibh.ebh) brelse(ofibh.ebh); @@ -1124,7 +1123,7 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, != old_inode->i_ino) goto end_rename; - nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); + nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); if (nfi) { if (!new_inode) { if (nfibh.sbh != nfibh.ebh) @@ -1192,7 +1191,7 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); /* The old fid may have moved - find it again */ - ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); if (new_inode) { @@ -1244,13 +1243,10 @@ end_rename: static struct dentry *udf_get_parent(struct dentry *child) { struct inode *inode = NULL; - struct dentry dotdot; + struct qstr dotdot = {.name = "..", .len = 2}; struct fileIdentDesc cfi; struct udf_fileident_bh fibh; - dotdot.d_name.name = ".."; - dotdot.d_name.len = 2; - lock_kernel(); if (!udf_find_entry(child->d_inode, &dotdot, &fibh, &cfi)) goto out_unlock; -- cgit v1.2.3-59-g8ed1b From 871c0067d53ba2dc35897c7da1da675bf4c70511 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:27 +0900 Subject: [PATCH vfs-2.6 1/6] vfs: replace parent == dentry->d_parent by IS_ROOT() Signed-off-by: OGAWA Hirofumi --- fs/dcache.c | 21 ++++++++++++--------- fs/namei.c | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 1710d2484fd9..c6fd1f27da57 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -174,9 +174,12 @@ static struct dentry *d_kill(struct dentry *dentry) dentry_stat.nr_dentry--; /* For d_free, below */ /*drops the locks, at that point nobody can reach this dentry */ dentry_iput(dentry); - parent = dentry->d_parent; + if (IS_ROOT(dentry)) + parent = NULL; + else + parent = dentry->d_parent; d_free(dentry); - return dentry == parent ? NULL : parent; + return parent; } /* @@ -666,11 +669,12 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) BUG(); } - parent = dentry->d_parent; - if (parent == dentry) + if (IS_ROOT(dentry)) parent = NULL; - else + else { + parent = dentry->d_parent; atomic_dec(&parent->d_count); + } list_del(&dentry->d_u.d_child); detached++; @@ -1723,7 +1727,7 @@ static int d_isparent(struct dentry *p1, struct dentry *p2) { struct dentry *p; - for (p = p2; p->d_parent != p; p = p->d_parent) { + for (p = p2; !IS_ROOT(p); p = p->d_parent) { if (p->d_parent == p1) return 1; } @@ -2168,10 +2172,9 @@ int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) seq = read_seqbegin(&rename_lock); for (;;) { if (new_dentry != old_dentry) { - struct dentry * parent = new_dentry->d_parent; - if (parent == new_dentry) + if (IS_ROOT(new_dentry)) break; - new_dentry = parent; + new_dentry = new_dentry->d_parent; continue; } result = 1; diff --git a/fs/namei.c b/fs/namei.c index 2b8f823eda44..068a9e50c8c0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1454,7 +1454,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); - for (p = p1; p->d_parent != p; p = p->d_parent) { + for (p = p1; !IS_ROOT(p); p = p->d_parent) { if (p->d_parent == p2) { mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); @@ -1462,7 +1462,7 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) } } - for (p = p2; p->d_parent != p; p = p->d_parent) { + for (p = p2; !IS_ROOT(p); p = p->d_parent) { if (p->d_parent == p1) { mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); -- cgit v1.2.3-59-g8ed1b From e2761a1167633ed943fea29002f990194923d060 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:28 +0900 Subject: [PATCH vfs-2.6 2/6] vfs: add d_ancestor() This adds d_ancestor() instead of d_isparent(), then use it. If new_dentry == old_dentry, is_subdir() returns 1, looks strange. "new_dentry == old_dentry" is not subdir obviously. But I'm not checking callers for now, so this keeps current behavior. Signed-off-by: OGAWA Hirofumi --- fs/dcache.c | 45 +++++++++++++++++++++++---------------------- fs/namei.c | 22 ++++++++++------------ include/linux/dcache.h | 1 + 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index c6fd1f27da57..64024005da43 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1720,18 +1720,23 @@ void d_move(struct dentry * dentry, struct dentry * target) spin_unlock(&dcache_lock); } -/* - * Helper that returns 1 if p1 is a parent of p2, else 0 +/** + * d_ancestor - search for an ancestor + * @p1: ancestor dentry + * @p2: child dentry + * + * Returns the ancestor dentry of p2 which is a child of p1, if p1 is + * an ancestor of p2, else NULL. */ -static int d_isparent(struct dentry *p1, struct dentry *p2) +struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2) { struct dentry *p; for (p = p2; !IS_ROOT(p); p = p->d_parent) { if (p->d_parent == p1) - return 1; + return p; } - return 0; + return NULL; } /* @@ -1755,7 +1760,7 @@ static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias) /* Check for loops */ ret = ERR_PTR(-ELOOP); - if (d_isparent(alias, dentry)) + if (d_ancestor(alias, dentry)) goto out_err; /* See lock_rename() */ @@ -2155,31 +2160,27 @@ out: * Caller must ensure that "new_dentry" is pinned before calling is_subdir() */ -int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry) +int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry) { int result; - struct dentry * saved = new_dentry; unsigned long seq; - /* need rcu_readlock to protect against the d_parent trashing due to - * d_move + /* FIXME: This is old behavior, needed? Please check callers. */ + if (new_dentry == old_dentry) + return 1; + + /* + * Need rcu_readlock to protect against the d_parent trashing + * due to d_move */ rcu_read_lock(); - do { + do { /* for restarting inner loop in case of seq retry */ - new_dentry = saved; - result = 0; seq = read_seqbegin(&rename_lock); - for (;;) { - if (new_dentry != old_dentry) { - if (IS_ROOT(new_dentry)) - break; - new_dentry = new_dentry->d_parent; - continue; - } + if (d_ancestor(old_dentry, new_dentry)) result = 1; - break; - } + else + result = 0; } while (read_seqretry(&rename_lock, seq)); rcu_read_unlock(); diff --git a/fs/namei.c b/fs/namei.c index 068a9e50c8c0..b7cd65224d60 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1454,20 +1454,18 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); - for (p = p1; !IS_ROOT(p); p = p->d_parent) { - if (p->d_parent == p2) { - mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); - mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); - return p; - } + p = d_ancestor(p2, p1); + if (p) { + mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); + return p; } - for (p = p2; !IS_ROOT(p); p = p->d_parent) { - if (p->d_parent == p1) { - mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); - mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); - return p; - } + p = d_ancestor(p1, p2); + if (p) { + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); + return p; } mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 74c64ae30cf0..a37359d0bad1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -287,6 +287,7 @@ static inline struct dentry *d_add_unique(struct dentry *entry, struct inode *in /* used for rename() and baskets */ extern void d_move(struct dentry *, struct dentry *); +extern struct dentry *d_ancestor(struct dentry *, struct dentry *); /* appendix may either be NULL or be used for transname suffixes */ extern struct dentry * d_lookup(struct dentry *, struct qstr *); -- cgit v1.2.3-59-g8ed1b From 360da90029196c9449bc61e5a07ce8404e4cba57 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:28 +0900 Subject: [PATCH vfs-2.6 3/6] vfs: add __d_instantiate() helper This adds __d_instantiate() for users which is already taking dcache_lock, and replace with it. The part of d_add_ci() isn't equivalent. But it should be needed fsnotify_d_instantiate() actually, because the path is to add the inode to negative dentry. fsnotify_d_instantiate() should be called after change from negative to positive. Signed-off-by: OGAWA Hirofumi --- fs/dcache.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 64024005da43..5a24cee6b76a 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -981,6 +981,15 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name) return d_alloc(parent, &q); } +/* the caller must hold dcache_lock */ +static void __d_instantiate(struct dentry *dentry, struct inode *inode) +{ + if (inode) + list_add(&dentry->d_alias, &inode->i_dentry); + dentry->d_inode = inode; + fsnotify_d_instantiate(dentry, inode); +} + /** * d_instantiate - fill in inode information for a dentry * @entry: dentry to complete @@ -1000,10 +1009,7 @@ void d_instantiate(struct dentry *entry, struct inode * inode) { BUG_ON(!list_empty(&entry->d_alias)); spin_lock(&dcache_lock); - if (inode) - list_add(&entry->d_alias, &inode->i_dentry); - entry->d_inode = inode; - fsnotify_d_instantiate(entry, inode); + __d_instantiate(entry, inode); spin_unlock(&dcache_lock); security_d_instantiate(entry, inode); } @@ -1033,7 +1039,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry, unsigned int hash = entry->d_name.hash; if (!inode) { - entry->d_inode = NULL; + __d_instantiate(entry, NULL); return NULL; } @@ -1052,9 +1058,7 @@ static struct dentry *__d_instantiate_unique(struct dentry *entry, return alias; } - list_add(&entry->d_alias, &inode->i_dentry); - entry->d_inode = inode; - fsnotify_d_instantiate(entry, inode); + __d_instantiate(entry, inode); return NULL; } @@ -1213,10 +1217,8 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) d_move(new, dentry); iput(inode); } else { - /* d_instantiate takes dcache_lock, so we do it by hand */ - list_add(&dentry->d_alias, &inode->i_dentry); - dentry->d_inode = inode; - fsnotify_d_instantiate(dentry, inode); + /* already taking dcache_lock, so d_add() by hand */ + __d_instantiate(dentry, inode); spin_unlock(&dcache_lock); security_d_instantiate(dentry, inode); d_rehash(dentry); @@ -1299,8 +1301,7 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode, * d_instantiate() by hand because it takes dcache_lock which * we already hold. */ - list_add(&found->d_alias, &inode->i_dentry); - found->d_inode = inode; + __d_instantiate(found, inode); spin_unlock(&dcache_lock); security_d_instantiate(found, inode); return found; @@ -1833,7 +1834,7 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) if (!inode) { actual = dentry; - dentry->d_inode = NULL; + __d_instantiate(dentry, NULL); goto found_lock; } -- cgit v1.2.3-59-g8ed1b From 8f3dfaa5bab767a043c5af5b879fb86c03329f8a Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:29 +0900 Subject: [PATCH vfs-2.6 4/6] vfs: remove unnecessary fsnotify_d_instantiate() This calls d_move(), so fsnotify_d_instantiate() is unnecessary like rename path. Signed-off-by: OGAWA Hirofumi --- fs/dcache.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 5a24cee6b76a..900de90d21be 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1210,7 +1210,6 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) new = __d_find_alias(inode, 1); if (new) { BUG_ON(!(new->d_flags & DCACHE_DISCONNECTED)); - fsnotify_d_instantiate(new, inode); spin_unlock(&dcache_lock); security_d_instantiate(new, inode); d_rehash(dentry); -- cgit v1.2.3-59-g8ed1b From 0612d9fb270a474fe6a46cc5b8d3f5b71cf5f580 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:29 +0900 Subject: [PATCH vfs-2.6 5/6] vfs: remove LOOKUP_PARENT from non LOOKUP_PARENT lookup lookup_hash() with LOOKUP_PARENT is bogus. And this prepares to add new intent on those path. The user of LOOKUP_PARENT intent is nfs only, and it checks whether nd->flags has LOOKUP_CREATE or LOOKUP_OPEN, so the result is same. Signed-off-by: OGAWA Hirofumi --- fs/namei.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index b7cd65224d60..18894fdf048a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2170,16 +2170,19 @@ static long do_rmdir(int dfd, const char __user *pathname) return error; switch(nd.last_type) { - case LAST_DOTDOT: - error = -ENOTEMPTY; - goto exit1; - case LAST_DOT: - error = -EINVAL; - goto exit1; - case LAST_ROOT: - error = -EBUSY; - goto exit1; + case LAST_DOTDOT: + error = -ENOTEMPTY; + goto exit1; + case LAST_DOT: + error = -EINVAL; + goto exit1; + case LAST_ROOT: + error = -EBUSY; + goto exit1; } + + nd.flags &= ~LOOKUP_PARENT; + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); @@ -2257,6 +2260,9 @@ static long do_unlinkat(int dfd, const char __user *pathname) error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; + + nd.flags &= ~LOOKUP_PARENT; + mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); @@ -2646,6 +2652,9 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname, if (newnd.last_type != LAST_NORM) goto exit2; + oldnd.flags &= ~LOOKUP_PARENT; + newnd.flags &= ~LOOKUP_PARENT; + trap = lock_rename(new_dir, old_dir); old_dentry = lookup_hash(&oldnd); -- cgit v1.2.3-59-g8ed1b From 4e9ed2f85af7adfa7c3f0efa839a53186254fdcb Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Thu, 16 Oct 2008 07:50:29 +0900 Subject: [PATCH vfs-2.6 6/6] vfs: add LOOKUP_RENAME_TARGET intent This adds LOOKUP_RENAME_TARGET intent for lookup of rename destination. LOOKUP_RENAME_TARGET is going to be used like LOOKUP_CREATE. But since the destination of rename() can be existing directory entry, so it has a difference. Although that difference doesn't matter in my usage, this tells it to user of this intent. Signed-off-by: OGAWA Hirofumi --- fs/namei.c | 1 + include/linux/namei.h | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 18894fdf048a..9e2a534383d9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2654,6 +2654,7 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname, oldnd.flags &= ~LOOKUP_PARENT; newnd.flags &= ~LOOKUP_PARENT; + newnd.flags |= LOOKUP_RENAME_TARGET; trap = lock_rename(new_dir, old_dir); diff --git a/include/linux/namei.h b/include/linux/namei.h index 6b5627afd2eb..99eb80306dc5 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -54,6 +54,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_OPEN 0x0100 #define LOOKUP_CREATE 0x0200 #define LOOKUP_EXCL 0x0400 +#define LOOKUP_RENAME_TARGET 0x0800 extern int user_path_at(int, const char __user *, unsigned, struct path *); -- cgit v1.2.3-59-g8ed1b From 3222a3e55f4025acb2a5a4379cf2f2b7df1f1243 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 3 Sep 2008 21:53:01 +0200 Subject: [PATCH] fix ->llseek for more directories With this patch all directory fops instances that have a readdir that doesn't take the BKL are switched to generic_file_llseek. Signed-off-by: Christoph Hellwig --- fs/afs/dir.c | 1 + fs/bfs/dir.c | 1 + fs/cifs/cifsfs.c | 1 + fs/fat/dir.c | 1 + fs/jffs2/dir.c | 3 ++- fs/jfs/namei.c | 1 + fs/omfs/dir.c | 1 + fs/openpromfs/inode.c | 1 + fs/proc/proc_sysctl.c | 1 + fs/sysfs/dir.c | 1 + fs/ufs/dir.c | 1 + 11 files changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index dfda03d4397d..99cf390641f7 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -45,6 +45,7 @@ const struct file_operations afs_dir_file_operations = { .release = afs_release, .readdir = afs_readdir, .lock = afs_lock, + .llseek = generic_file_llseek, }; const struct inode_operations afs_dir_inode_operations = { diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index ed8feb052df9..daae463068e4 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -80,6 +80,7 @@ const struct file_operations bfs_dir_operations = { .read = generic_read_dir, .readdir = bfs_readdir, .fsync = file_fsync, + .llseek = generic_file_llseek, }; extern void dump_imap(const char *, struct super_block *); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 25ecbd5b0404..89c64a8dcb99 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -765,6 +765,7 @@ const struct file_operations cifs_dir_ops = { .dir_notify = cifs_dir_notify, #endif /* CONFIG_CIFS_EXPERIMENTAL */ .unlocked_ioctl = cifs_ioctl, + .llseek = generic_file_llseek, }; static void diff --git a/fs/fat/dir.c b/fs/fat/dir.c index cd4a0162e10d..bae1c3292522 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -839,6 +839,7 @@ const struct file_operations fat_dir_operations = { .compat_ioctl = fat_compat_dir_ioctl, #endif .fsync = file_fsync, + .llseek = generic_file_llseek, }; static int fat_get_short_entry(struct inode *dir, loff_t *pos, diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 621bdfa994e7..6f60cc910f4c 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -39,7 +39,8 @@ const struct file_operations jffs2_dir_operations = .read = generic_read_dir, .readdir = jffs2_readdir, .unlocked_ioctl=jffs2_ioctl, - .fsync = jffs2_fsync + .fsync = jffs2_fsync, + .llseek = generic_file_llseek, }; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index e199dde7b83c..cc3cedffbfa1 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1547,6 +1547,7 @@ const struct file_operations jfs_dir_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = jfs_compat_ioctl, #endif + .llseek = generic_file_llseek, }; static int jfs_ci_hash(struct dentry *dir, struct qstr *this) diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c index c0757e998876..c7275cfbdcfb 100644 --- a/fs/omfs/dir.c +++ b/fs/omfs/dir.c @@ -501,4 +501,5 @@ struct inode_operations omfs_dir_inops = { struct file_operations omfs_dir_operations = { .read = generic_read_dir, .readdir = omfs_readdir, + .llseek = generic_file_llseek, }; diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 9f5b054f06b9..d41bdc784de4 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -167,6 +167,7 @@ static int openpromfs_readdir(struct file *, void *, filldir_t); static const struct file_operations openprom_operations = { .read = generic_read_dir, .readdir = openpromfs_readdir, + .llseek = generic_file_llseek, }; static struct dentry *openpromfs_lookup(struct inode *, struct dentry *, struct nameidata *); diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 945a81043ba2..5fe210c09171 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -353,6 +353,7 @@ static const struct file_operations proc_sys_file_operations = { static const struct file_operations proc_sys_dir_file_operations = { .readdir = proc_sys_readdir, + .llseek = generic_file_llseek, }; static const struct inode_operations proc_sys_inode_operations = { diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 3a05a596e3b4..82d3b79d0e08 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -983,4 +983,5 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) const struct file_operations sysfs_dir_operations = { .read = generic_read_dir, .readdir = sysfs_readdir, + .llseek = generic_file_llseek, }; diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index df0bef18742d..dbbbc4668769 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -667,4 +667,5 @@ const struct file_operations ufs_dir_operations = { .read = generic_read_dir, .readdir = ufs_readdir, .fsync = file_fsync, + .llseek = generic_file_llseek, }; -- cgit v1.2.3-59-g8ed1b From 91efc167d02509ea78abeff6d668065964c67c0b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 8 Sep 2008 19:42:50 +0200 Subject: [PATCH] reiserfs: add missing llseek method Reiserfs currently doesn't set a llseek method for regular files, which means it will fall back to default_llseek. This means no one can seek beyond 2 Gigabytes on reiserfs, and that there's not protection vs the i_size updates from writers. Signed-off-by: Christoph Hellwig --- fs/reiserfs/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index a804903d31d1..33408417038c 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -296,6 +296,7 @@ const struct file_operations reiserfs_file_operations = { .aio_write = generic_file_aio_write, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, + .llseek = generic_file_llseek, }; const struct inode_operations reiserfs_file_inode_operations = { -- cgit v1.2.3-59-g8ed1b From 0e55a7cca4b66f625d67b292f80b6a976e77c51b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 26 Sep 2008 19:01:20 -0700 Subject: [RFC PATCH] touch_mnt_namespace when the mount flags change Daemons that need to be launched while the rootfs is read-only can now poll /proc/mounts to be notified when their O_RDWR requests may no longer end in EROFS. Cc: Kay Sievers Cc: Neil Brown Signed-off-by: Dan Williams --- fs/namespace.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index f527a0d6c64d..cce46702d33c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1550,8 +1550,13 @@ static int do_remount(struct path *path, int flags, int mnt_flags, if (!err) path->mnt->mnt_flags = mnt_flags; up_write(&sb->s_umount); - if (!err) + if (!err) { security_sb_post_remount(path->mnt, flags, data); + + spin_lock(&vfsmount_lock); + touch_mnt_namespace(path->mnt->mnt_ns); + spin_unlock(&vfsmount_lock); + } return err; } -- cgit v1.2.3-59-g8ed1b From 5cec56deb6d41b5b570306b17cd0b1590ebd0897 Mon Sep 17 00:00:00 2001 From: Qinghuang Feng Date: Mon, 13 Oct 2008 18:32:42 +0800 Subject: [PATCH] fs/dcache.c: update comment of d_validate() Parameters @hash and @len have been removed since 2.4.3, now just to delete them. Signed-off-by: Qinghuang Feng --- fs/dcache.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 900de90d21be..12eac838558a 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1462,8 +1462,6 @@ out: * d_validate - verify dentry provided from insecure source * @dentry: The dentry alleged to be valid child of @dparent * @dparent: The parent dentry (known to be valid) - * @hash: Hash of the dentry - * @len: Length of the name * * An insecure source has sent us a dentry, here we verify it and dget() it. * This is used by ncpfs in its readdir implementation. -- cgit v1.2.3-59-g8ed1b From f696a3659fc4b3a3bf4bc83d9dbec5e5a2ffd929 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 31 Jul 2008 13:41:58 +0200 Subject: [PATCH] move executable checking into ->permission() For execute permission on a regular files we need to check if file has any execute bits at all, regardless of capabilites. This check is normally performed by generic_permission() but was also added to the case when the filesystem defines its own ->permission() method. In the latter case the filesystem should be responsible for performing this check. Move the check from inode_permission() inside filesystems which are not calling generic_permission(). Create a helper function execute_ok() that returns true if the inode is a directory or if any execute bits are present in i_mode. Also fix up the following code: - coda control file is never executable - sysctl files are never executable - hfs_permission seems broken on MAY_EXEC, remove - hfsplus_permission is eqivalent to generic_permission(), remove Signed-off-by: Miklos Szeredi --- fs/cifs/cifsfs.c | 9 ++++++--- fs/coda/dir.c | 3 +++ fs/coda/pioctl.c | 2 +- fs/hfs/inode.c | 8 -------- fs/hfsplus/inode.c | 13 ------------- fs/namei.c | 21 ++++----------------- fs/nfs/dir.c | 3 +++ fs/proc/proc_sysctl.c | 10 ++++++++-- include/linux/fs.h | 5 +++++ 9 files changed, 30 insertions(+), 44 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 89c64a8dcb99..84cc011a16e4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -275,9 +275,12 @@ static int cifs_permission(struct inode *inode, int mask) cifs_sb = CIFS_SB(inode->i_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) - return 0; - else /* file mode might have been restricted at mount time + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) { + if ((mask & MAY_EXEC) && !execute_ok(inode)) + return -EACCES; + else + return 0; + } else /* file mode might have been restricted at mount time on the client (above and beyond ACL on servers) for servers which do not support setting and viewing mode bits, so allowing client to check permissions is useful */ diff --git a/fs/coda/dir.c b/fs/coda/dir.c index c5916228243c..75b1fa90b2cb 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -146,6 +146,9 @@ int coda_permission(struct inode *inode, int mask) if (!mask) return 0; + if ((mask & MAY_EXEC) && !execute_ok(inode)) + return -EACCES; + lock_kernel(); if (coda_cache_check(inode, mask)) diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c index c51365422aa8..773f2ce9aa06 100644 --- a/fs/coda/pioctl.c +++ b/fs/coda/pioctl.c @@ -43,7 +43,7 @@ const struct file_operations coda_ioctl_operations = { /* the coda pioctl inode ops */ static int coda_ioctl_permission(struct inode *inode, int mask) { - return 0; + return (mask & MAY_EXEC) ? -EACCES : 0; } static int coda_pioctl(struct inode * inode, struct file * filp, diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 7e19835efa2e..c69b7ac75bf7 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -511,13 +511,6 @@ void hfs_clear_inode(struct inode *inode) } } -static int hfs_permission(struct inode *inode, int mask) -{ - if (S_ISREG(inode->i_mode) && mask & MAY_EXEC) - return 0; - return generic_permission(inode, mask, NULL); -} - static int hfs_file_open(struct inode *inode, struct file *file) { if (HFS_IS_RSRC(inode)) @@ -616,7 +609,6 @@ static const struct inode_operations hfs_file_inode_operations = { .lookup = hfs_file_lookup, .truncate = hfs_file_truncate, .setattr = hfs_inode_setattr, - .permission = hfs_permission, .setxattr = hfs_setxattr, .getxattr = hfs_getxattr, .listxattr = hfs_listxattr, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 963be644297a..b207f0e6fc22 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -238,18 +238,6 @@ static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms) perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev); } -static int hfsplus_permission(struct inode *inode, int mask) -{ - /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup, - * open_exec has the same test, so it's still not executable, if a x bit - * is set fall back to standard permission check. - */ - if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111)) - return 0; - return generic_permission(inode, mask, NULL); -} - - static int hfsplus_file_open(struct inode *inode, struct file *file) { if (HFSPLUS_IS_RSRC(inode)) @@ -281,7 +269,6 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) static const struct inode_operations hfsplus_file_inode_operations = { .lookup = hfsplus_file_lookup, .truncate = hfsplus_file_truncate, - .permission = hfsplus_permission, .setxattr = hfsplus_setxattr, .getxattr = hfsplus_getxattr, .listxattr = hfsplus_listxattr, diff --git a/fs/namei.c b/fs/namei.c index 9e2a534383d9..09ce58e49e72 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -212,8 +212,7 @@ int generic_permission(struct inode *inode, int mask, * Read/write DACs are always overridable. * Executable DACs are overridable if at least one exec bit is set. */ - if (!(mask & MAY_EXEC) || - (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode)) + if (!(mask & MAY_EXEC) || execute_ok(inode)) if (capable(CAP_DAC_OVERRIDE)) return 0; @@ -249,23 +248,11 @@ int inode_permission(struct inode *inode, int mask) } /* Ordinary permission routines do not understand MAY_APPEND. */ - if (inode->i_op && inode->i_op->permission) { + if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission(inode, mask); - if (!retval) { - /* - * Exec permission on a regular file is denied if none - * of the execute bits are set. - * - * This check should be done by the ->permission() - * method. - */ - if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && - !(inode->i_mode & S_IXUGO)) - return -EACCES; - } - } else { + else retval = generic_permission(inode, mask, NULL); - } + if (retval) return retval; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c216c8786c51..3e64b98f3a93 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1957,6 +1957,9 @@ force_lookup: } else res = PTR_ERR(cred); out: + if (!res && (mask & MAY_EXEC) && !execute_ok(inode)) + res = -EACCES; + dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n", inode->i_sb->s_id, inode->i_ino, mask, res); return res; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 5fe210c09171..7b997754a25e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -298,13 +298,19 @@ static int proc_sys_permission(struct inode *inode, int mask) * sysctl entries that are not writeable, * are _NOT_ writeable, capabilities or not. */ - struct ctl_table_header *head = grab_header(inode); - struct ctl_table *table = PROC_I(inode)->sysctl_entry; + struct ctl_table_header *head; + struct ctl_table *table; int error; + /* Executable files are not allowed under /proc/sys/ */ + if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) + return -EACCES; + + head = grab_header(inode); if (IS_ERR(head)) return PTR_ERR(head); + table = PROC_I(inode)->sysctl_entry; if (!table) /* global root - r-xr-xr-x */ error = mask & MAY_WRITE ? -EACCES : 0; else /* Use the permissions on the sysctl table entry */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 5f70aa62cf0f..025a4a251b64 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1851,6 +1851,11 @@ extern int inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int, int (*check_acl)(struct inode *, int)); +static inline bool execute_ok(struct inode *inode) +{ + return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode); +} + extern int get_write_access(struct inode *); extern int deny_write_access(struct file *); static inline void put_write_access(struct inode * inode) -- cgit v1.2.3-59-g8ed1b From 2c512397ca060f6dbcb3957174a91e29a3b769be Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Wed, 20 Aug 2008 16:56:22 -0700 Subject: [patch 1/3] FS_MBCACHE: don't needlessly make it built-in Assume you have: - one or more of ext2/3/4 statically built into your kernel - none of these with extended attributes enabled and - want to add onother one of ext2/3/4 modular and with extended attributes enabled then you currently have to reboot to use it since this results in CONFIG_FS_MBCACHE=y. That's not a common issue, but I just ran into it and since there's no reason to get a built-in mbcache in this case this patch fixes it. Signed-off-by: Adrian Bunk Cc: Andreas Gruenbacher Cc: Al Viro Signed-off-by: Andrew Morton --- fs/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/Kconfig b/fs/Kconfig index e46297f020c1..522469a7eca3 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -22,9 +22,10 @@ source "fs/jbd2/Kconfig" config FS_MBCACHE # Meta block cache for Extended Attributes (ext2/ext3/ext4) tristate - depends on EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR - default y if EXT2_FS=y || EXT3_FS=y || EXT4_FS=y - default m if EXT2_FS=m || EXT3_FS=m || EXT4_FS=m + default y if EXT2_FS=y && EXT2_FS_XATTR + default y if EXT3_FS=y && EXT3_FS_XATTR + default y if EXT4_FS=y && EXT4_FS_XATTR + default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR config REISERFS_FS tristate "Reiserfs support" -- cgit v1.2.3-59-g8ed1b From a77b72da24ecfe262760874c55e3f6461f1dec0d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 30 Jul 2008 14:06:22 +0200 Subject: [patch] vfs: make security_inode_setattr() calling consistent Call security_inode_setattr() consistetly before inode_change_ok(). It doesn't make sense to try to "optimize" the i_op->setattr == NULL case, as most filesystem do define their own setattr function. Signed-off-by: Miklos Szeredi --- fs/attr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index 26c71ba1eed4..7a83819f6ba2 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -159,17 +159,17 @@ int notify_change(struct dentry * dentry, struct iattr * attr) if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID))) return 0; + error = security_inode_setattr(dentry, attr); + if (error) + return error; + if (ia_valid & ATTR_SIZE) down_write(&dentry->d_inode->i_alloc_sem); if (inode->i_op && inode->i_op->setattr) { - error = security_inode_setattr(dentry, attr); - if (!error) - error = inode->i_op->setattr(dentry, attr); + error = inode->i_op->setattr(dentry, attr); } else { error = inode_change_ok(inode, attr); - if (!error) - error = security_inode_setattr(dentry, attr); if (!error) { if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) -- cgit v1.2.3-59-g8ed1b From 08b9fe6b12d32324f311c46b88102b6b9067d434 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 13 Oct 2008 00:09:50 -0400 Subject: [PATCH] i_version: remount support Add support for remounting a filesystem with the i_version option. Signed-off-by: Mimi Zohar --- include/linux/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 025a4a251b64..7d719c1a18e3 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -136,7 +136,7 @@ extern int dir_notify_enable; /* * Superblock flags that can be altered by MS_REMOUNT */ -#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK) +#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION) /* * Old magic mount flag and mask -- cgit v1.2.3-59-g8ed1b From fd217f4d70172c526478f2bc76859e909fdfa674 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Tue, 21 Oct 2008 06:47:33 -0700 Subject: [PATCH] fs: add a sanity check in d_free Hi Al, remember that debug session we did at KS? You suggested this patch back then.... From 7751eaf30474b8cbfaea64795805a17eab05ac53 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Tue, 16 Sep 2008 16:51:17 -0700 Subject: [PATCH] fs: add a sanity check in d_free we're seeing some corruption in the dentry->d_alias list that appears like a free of an entry still on the list; this patch adds a WARN_ON() to catch this scenario, as suggested by Al Viro Signed-off-by: Arjan van de Ven --- fs/dcache.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/dcache.c b/fs/dcache.c index 12eac838558a..a1d86c7f3e66 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -69,6 +69,7 @@ struct dentry_stat_t dentry_stat = { static void __d_free(struct dentry *dentry) { + WARN_ON(!list_empty(&dentry->d_alias)); if (dname_external(dentry)) kfree(dentry->d_name.name); kmem_cache_free(dentry_cache, dentry); -- cgit v1.2.3-59-g8ed1b