From b46c267e4756a88593c4a08de869e70d3907637f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Apr 2015 12:10:34 -0400 Subject: 9p: don't bother with 4K allocation for 24-byte local array... Signed-off-by: Al Viro --- fs/9p/vfs_inode.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 703342e309f5..cda68f70d757 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1370,6 +1370,8 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname); } +#define U32_MAX_DIGITS 10 + /** * v9fs_vfs_link - create a hardlink * @old_dentry: dentry for file to link to @@ -1383,7 +1385,7 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { int retval; - char *name; + char name[1 + U32_MAX_DIGITS + 2]; /* sign + number + \n + \0 */ struct p9_fid *oldfid; p9_debug(P9_DEBUG_VFS, " %lu,%pd,%pd\n", @@ -1393,20 +1395,12 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, if (IS_ERR(oldfid)) return PTR_ERR(oldfid); - name = __getname(); - if (unlikely(!name)) { - retval = -ENOMEM; - goto clunk_fid; - } - sprintf(name, "%d\n", oldfid->fid); retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name); - __putname(name); if (!retval) { v9fs_refresh_inode(oldfid, d_inode(old_dentry)); v9fs_invalidate_inode_attr(dir); } -clunk_fid: p9_client_clunk(oldfid); return retval; } @@ -1425,7 +1419,7 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde { struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); int retval; - char *name; + char name[2 + U32_MAX_DIGITS + 1 + U32_MAX_DIGITS + 1]; u32 perm; p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n", @@ -1435,26 +1429,16 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde if (!new_valid_dev(rdev)) return -EINVAL; - name = __getname(); - if (!name) - return -ENOMEM; /* build extension */ if (S_ISBLK(mode)) sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); else if (S_ISCHR(mode)) sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); - else if (S_ISFIFO(mode)) - *name = 0; - else if (S_ISSOCK(mode)) + else *name = 0; - else { - __putname(name); - return -EINVAL; - } perm = unixmode2p9mode(v9ses, mode); retval = v9fs_vfs_mkspecial(dir, dentry, perm, name); - __putname(name); return retval; } -- cgit v1.2.3-58-ga151 From 90e4fc8890daecea72ef73ac8047050e3e8d32db Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 14 Apr 2015 17:42:49 -0400 Subject: 9p: don't bother with __getname() in ->follow_link() We copy there a kmalloc'ed string and proceed to kfree that string immediately after that. Easier to just feed that string to nd_set_link() and _not_ kfree it until ->put_link() (which becomes kfree_put_link() in that case). Signed-off-by: Al Viro --- fs/9p/v9fs.h | 2 -- fs/9p/vfs_inode.c | 93 ++++++++++---------------------------------------- fs/9p/vfs_inode_dotl.c | 31 +++++------------ 3 files changed, 26 insertions(+), 100 deletions(-) diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index fb9ffcb43277..0923f2cf3c80 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -149,8 +149,6 @@ extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d); extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d); extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); -extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *p); extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, struct super_block *sb, int new); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index cda68f70d757..0ba11712b388 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1224,102 +1224,45 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) } /** - * v9fs_readlink - read a symlink's location (internal version) + * v9fs_vfs_follow_link - follow a symlink path * @dentry: dentry for symlink - * @buffer: buffer to load symlink location into - * @buflen: length of buffer + * @nd: nameidata * */ -static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) +static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) { - int retval; - - struct v9fs_session_info *v9ses; - struct p9_fid *fid; + struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); + struct p9_fid *fid = v9fs_fid_lookup(dentry); struct p9_wstat *st; - p9_debug(P9_DEBUG_VFS, " %pd\n", dentry); - retval = -EPERM; - v9ses = v9fs_dentry2v9ses(dentry); - fid = v9fs_fid_lookup(dentry); + p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); + if (IS_ERR(fid)) - return PTR_ERR(fid); + return ERR_CAST(fid); if (!v9fs_proto_dotu(v9ses)) - return -EBADF; + return ERR_PTR(-EBADF); st = p9_client_stat(fid); if (IS_ERR(st)) - return PTR_ERR(st); + return ERR_CAST(st); if (!(st->mode & P9_DMSYMLINK)) { - retval = -EINVAL; - goto done; + p9stat_free(st); + kfree(st); + return ERR_PTR(-EINVAL); } + if (strlen(st->extension) >= PATH_MAX) + st->extension[PATH_MAX - 1] = '\0'; - /* copy extension buffer into buffer */ - retval = min(strlen(st->extension)+1, (size_t)buflen); - memcpy(buffer, st->extension, retval); - - p9_debug(P9_DEBUG_VFS, "%pd -> %s (%.*s)\n", - dentry, st->extension, buflen, buffer); - -done: + nd_set_link(nd, st->extension); + st->extension = NULL; p9stat_free(st); kfree(st); - return retval; -} - -/** - * v9fs_vfs_follow_link - follow a symlink path - * @dentry: dentry for symlink - * @nd: nameidata - * - */ - -static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - int len = 0; - char *link = __getname(); - - p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); - - if (!link) - link = ERR_PTR(-ENOMEM); - else { - len = v9fs_readlink(dentry, link, PATH_MAX); - - if (len < 0) { - __putname(link); - link = ERR_PTR(len); - } else - link[min(len, PATH_MAX-1)] = 0; - } - nd_set_link(nd, link); - return NULL; } -/** - * v9fs_vfs_put_link - release a symlink path - * @dentry: dentry for symlink - * @nd: nameidata - * @p: unused - * - */ - -void -v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p) -{ - char *s = nd_get_link(nd); - - p9_debug(P9_DEBUG_VFS, " %pd %s\n", - dentry, IS_ERR(s) ? "" : s); - if (!IS_ERR(s)) - __putname(s); -} - /** * v9fs_vfs_mkspecial - create a special file * @dir: inode to create special file in @@ -1514,7 +1457,7 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = v9fs_vfs_follow_link, - .put_link = v9fs_vfs_put_link, + .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, }; diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 9861c7c951a6..bc2a91f2b910 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -912,33 +912,18 @@ error: static void * v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd) { - int retval; - struct p9_fid *fid; - char *link = __getname(); + struct p9_fid *fid = v9fs_fid_lookup(dentry); char *target; + int retval; p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); - if (!link) { - link = ERR_PTR(-ENOMEM); - goto ndset; - } - fid = v9fs_fid_lookup(dentry); - if (IS_ERR(fid)) { - __putname(link); - link = ERR_CAST(fid); - goto ndset; - } + if (IS_ERR(fid)) + return ERR_CAST(fid); retval = p9_client_readlink(fid, &target); - if (!retval) { - strcpy(link, target); - kfree(target); - goto ndset; - } - __putname(link); - link = ERR_PTR(retval); -ndset: - nd_set_link(nd, link); + if (retval) + return ERR_PTR(retval); + nd_set_link(nd, target); return NULL; } @@ -1006,7 +991,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = { const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, .follow_link = v9fs_vfs_follow_link_dotl, - .put_link = v9fs_vfs_put_link, + .put_link = kfree_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, .setxattr = generic_setxattr, -- cgit v1.2.3-58-ga151 From 3188b2955de3d01949ec54eb2c9ff1ecaa0a752d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:39 +1100 Subject: ovl: rearrange ovl_follow_link to it doesn't need to call ->put_link ovl_follow_link current calls ->put_link on an error path. However ->put_link is about to change in a way that it will be impossible to call it from ovl_follow_link. So rearrange the code to avoid the need for that error path. Specifically: move the kmalloc() call before the ->follow_link() call to the subordinate filesystem. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- fs/overlayfs/inode.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 04f124884687..1b4b9c5e51b7 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -145,6 +145,7 @@ static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) void *ret; struct dentry *realdentry; struct inode *realinode; + struct ovl_link_data *data = NULL; realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; @@ -152,25 +153,23 @@ static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) if (WARN_ON(!realinode->i_op->follow_link)) return ERR_PTR(-EPERM); - ret = realinode->i_op->follow_link(realdentry, nd); - if (IS_ERR(ret)) - return ret; - if (realinode->i_op->put_link) { - struct ovl_link_data *data; - data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); - if (!data) { - realinode->i_op->put_link(realdentry, nd, ret); + if (!data) return ERR_PTR(-ENOMEM); - } data->realdentry = realdentry; - data->cookie = ret; + } - return data; - } else { - return NULL; + ret = realinode->i_op->follow_link(realdentry, nd); + if (IS_ERR(ret)) { + kfree(data); + return ret; } + + if (data) + data->cookie = ret; + + return data; } static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) -- cgit v1.2.3-58-ga151 From a7a67e8a089e25ef48ab01dd34ce82678ef70f11 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 27 Apr 2015 17:51:30 -0400 Subject: ext4: split inode_operations for encrypted symlinks off the rest Signed-off-by: Al Viro --- fs/ext4/ext4.h | 1 + fs/ext4/inode.c | 6 ++++-- fs/ext4/namei.c | 9 +++++---- fs/ext4/symlink.c | 30 ++++++++++-------------------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 009a0590b20f..ad358f2def66 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2847,6 +2847,7 @@ extern int ext4_mpage_readpages(struct address_space *mapping, unsigned nr_pages); /* symlink.c */ +extern const struct inode_operations ext4_encrypted_symlink_inode_operations; extern const struct inode_operations ext4_symlink_inode_operations; extern const struct inode_operations ext4_fast_symlink_inode_operations; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 55b187c3bac1..9f3baa25ec54 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4213,8 +4213,10 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_dir_inode_operations; inode->i_fop = &ext4_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (ext4_inode_is_fast_symlink(inode) && - !ext4_encrypted_inode(inode)) { + if (ext4_encrypted_inode(inode)) { + inode->i_op = &ext4_encrypted_symlink_inode_operations; + ext4_set_aops(inode); + } else if (ext4_inode_is_fast_symlink(inode)) { inode->i_op = &ext4_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 814f3beb4369..39f8e6502639 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3206,10 +3206,12 @@ static int ext4_symlink(struct inode *dir, goto err_drop_inode; sd->len = cpu_to_le16(ostr.len); disk_link.name = (char *) sd; + inode->i_op = &ext4_encrypted_symlink_inode_operations; } if ((disk_link.len > EXT4_N_BLOCKS * 4)) { - inode->i_op = &ext4_symlink_inode_operations; + if (!encryption_required) + inode->i_op = &ext4_symlink_inode_operations; ext4_set_aops(inode); /* * We cannot call page_symlink() with transaction started @@ -3249,9 +3251,8 @@ static int ext4_symlink(struct inode *dir, } else { /* clear the extent format for fast symlink */ ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); - inode->i_op = encryption_required ? - &ext4_symlink_inode_operations : - &ext4_fast_symlink_inode_operations; + if (!encryption_required) + inode->i_op = &ext4_fast_symlink_inode_operations; memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, disk_link.len); inode->i_size = disk_link.len - 1; diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 187b78920314..49575ff7a7bb 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -35,9 +35,6 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) int res; u32 plen, max_size = inode->i_sb->s_blocksize; - if (!ext4_encrypted_inode(inode)) - return page_follow_link_light(dentry, nd); - ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize); if (IS_ERR(ctx)) return ctx; @@ -97,18 +94,16 @@ errout: return ERR_PTR(res); } -static void ext4_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - struct page *page = cookie; - - if (!page) { - kfree(nd_get_link(nd)); - } else { - kunmap(page); - page_cache_release(page); - } -} +const struct inode_operations ext4_encrypted_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = ext4_follow_link, + .put_link = kfree_put_link, + .setattr = ext4_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ext4_listxattr, + .removexattr = generic_removexattr, +}; #endif static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) @@ -120,13 +115,8 @@ static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, -#ifdef CONFIG_EXT4_FS_ENCRYPTION - .follow_link = ext4_follow_link, - .put_link = ext4_put_link, -#else .follow_link = page_follow_link_light, .put_link = page_put_link, -#endif .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, -- cgit v1.2.3-58-ga151 From 61ba64fc0768879a300599b011c176203bdf27d9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 09:54:06 -0400 Subject: libfs: simple_follow_link() let "fast" symlinks store the pointer to the body into ->i_link and use simple_follow_link for ->follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/inode.c | 1 + fs/libfs.c | 13 +++++++++++++ include/linux/fs.h | 3 +++ 3 files changed, 17 insertions(+) diff --git a/fs/inode.c b/fs/inode.c index ea37cd17b53f..952fb4852e38 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -152,6 +152,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_pipe = NULL; inode->i_bdev = NULL; inode->i_cdev = NULL; + inode->i_link = NULL; inode->i_rdev = 0; inode->dirtied_when = 0; diff --git a/fs/libfs.c b/fs/libfs.c index cb1fb4b9b637..72e4e015455f 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1093,3 +1093,16 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, return -EINVAL; } EXPORT_SYMBOL(simple_nosetlease); + +void *simple_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + nd_set_link(nd, d_inode(dentry)->i_link); + return NULL; +} +EXPORT_SYMBOL(simple_follow_link); + +const struct inode_operations simple_symlink_inode_operations = { + .follow_link = simple_follow_link, + .readlink = generic_readlink +}; +EXPORT_SYMBOL(simple_symlink_inode_operations); diff --git a/include/linux/fs.h b/include/linux/fs.h index 35ec87e490b1..0ac758fcff00 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -656,6 +656,7 @@ struct inode { struct pipe_inode_info *i_pipe; struct block_device *i_bdev; struct cdev *i_cdev; + char *i_link; }; __u32 i_generation; @@ -2721,6 +2722,8 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); +void *simple_follow_link(struct dentry *, struct nameidata *); +extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); -- cgit v1.2.3-58-ga151 From cbe0fa3858cd638d1b540fe1279484bbd71489fb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:02:46 -0400 Subject: ext2: use simple_follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/ext2/inode.c | 1 + fs/ext2/namei.c | 3 ++- fs/ext2/symlink.c | 10 +--------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f460ae36d5b7..5c09776d347f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1403,6 +1403,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) inode->i_mapping->a_ops = &ext2_aops; } else if (S_ISLNK(inode->i_mode)) { if (ext2_inode_is_fast_symlink(inode)) { + inode->i_link = (char *)ei->i_data; inode->i_op = &ext2_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 3e074a9ccbe6..13ec54a99c96 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -189,7 +189,8 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry, } else { /* fast symlink */ inode->i_op = &ext2_fast_symlink_inode_operations; - memcpy((char*)(EXT2_I(inode)->i_data),symname,l); + inode->i_link = (char*)EXT2_I(inode)->i_data; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } mark_inode_dirty(inode); diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 20608f17c2e5..ae17179f3810 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -19,14 +19,6 @@ #include "ext2.h" #include "xattr.h" -#include - -static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext2_inode_info *ei = EXT2_I(d_inode(dentry)); - nd_set_link(nd, (char *)ei->i_data); - return NULL; -} const struct inode_operations ext2_symlink_inode_operations = { .readlink = generic_readlink, @@ -43,7 +35,7 @@ const struct inode_operations ext2_symlink_inode_operations = { const struct inode_operations ext2_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext2_follow_link, + .follow_link = simple_follow_link, .setattr = ext2_setattr, #ifdef CONFIG_EXT2_FS_XATTR .setxattr = generic_setxattr, -- cgit v1.2.3-58-ga151 From d0deec19917352333e38cfdafe69e796e5dedfbb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:06:54 -0400 Subject: befs: switch to simple_follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/befs/linuxvfs.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 7943533c3868..172e306d68a7 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -43,7 +43,6 @@ static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static void befs_destroy_inodecache(void); static void *befs_follow_link(struct dentry *, struct nameidata *); -static void *befs_fast_follow_link(struct dentry *, struct nameidata *); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, @@ -80,11 +79,6 @@ static const struct address_space_operations befs_aops = { .bmap = befs_bmap, }; -static const struct inode_operations befs_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = befs_fast_follow_link, -}; - static const struct inode_operations befs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = befs_follow_link, @@ -403,10 +397,12 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino) inode->i_op = &befs_dir_inode_operations; inode->i_fop = &befs_dir_operations; } else if (S_ISLNK(inode->i_mode)) { - if (befs_ino->i_flags & BEFS_LONG_SYMLINK) + if (befs_ino->i_flags & BEFS_LONG_SYMLINK) { inode->i_op = &befs_symlink_inode_operations; - else - inode->i_op = &befs_fast_symlink_inode_operations; + } else { + inode->i_link = befs_ino->i_data.symlink; + inode->i_op = &simple_symlink_inode_operations; + } } else { befs_error(sb, "Inode %lu is not a regular file, " "directory or symlink. THAT IS WRONG! BeFS has no " @@ -497,16 +493,6 @@ befs_follow_link(struct dentry *dentry, struct nameidata *nd) return NULL; } - -static void * -befs_fast_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); - - nd_set_link(nd, befs_ino->i_data.symlink); - return NULL; -} - /* * UTF-8 to NLS charset convert routine * -- cgit v1.2.3-58-ga151 From 115b4205120dd5347858837dcdc17de0750bddef Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:10:02 -0400 Subject: ext3: switch to simple_follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/ext3/inode.c | 1 + fs/ext3/namei.c | 3 ++- fs/ext3/symlink.c | 10 +--------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2ee2dc4351d1..6c7e5468a2f8 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2999,6 +2999,7 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext3_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); + inode->i_link = (char *)ei->i_data; } else { inode->i_op = &ext3_symlink_inode_operations; ext3_set_aops(inode); diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 4264b9bd0002..c9e767cd4b67 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -2308,7 +2308,8 @@ retry: } } else { inode->i_op = &ext3_fast_symlink_inode_operations; - memcpy((char*)&EXT3_I(inode)->i_data,symname,l); + inode->i_link = (char*)&EXT3_I(inode)->i_data; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } EXT3_I(inode)->i_disksize = inode->i_size; diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c index ea96df3c58db..c08c59094ae6 100644 --- a/fs/ext3/symlink.c +++ b/fs/ext3/symlink.c @@ -17,17 +17,9 @@ * ext3 symlink handling code */ -#include #include "ext3.h" #include "xattr.h" -static void * ext3_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext3_inode_info *ei = EXT3_I(d_inode(dentry)); - nd_set_link(nd, (char*)ei->i_data); - return NULL; -} - const struct inode_operations ext3_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = page_follow_link_light, @@ -43,7 +35,7 @@ const struct inode_operations ext3_symlink_inode_operations = { const struct inode_operations ext3_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext3_follow_link, + .follow_link = simple_follow_link, .setattr = ext3_setattr, #ifdef CONFIG_EXT3_FS_XATTR .setxattr = generic_setxattr, -- cgit v1.2.3-58-ga151 From 75e7566bea0c9b2a257441b66294be94863ef929 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:13:58 -0400 Subject: ext4: switch to simple_follow_link() for fast symlinks only, of course... Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/ext4/inode.c | 1 + fs/ext4/namei.c | 4 +++- fs/ext4/symlink.c | 9 +-------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9f3baa25ec54..066fdd7a1e2c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4217,6 +4217,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) inode->i_op = &ext4_encrypted_symlink_inode_operations; ext4_set_aops(inode); } else if (ext4_inode_is_fast_symlink(inode)) { + inode->i_link = (char *)ei->i_data; inode->i_op = &ext4_fast_symlink_inode_operations; nd_terminate_link(ei->i_data, inode->i_size, sizeof(ei->i_data) - 1); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 39f8e6502639..5fdb9f6aa869 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3251,8 +3251,10 @@ static int ext4_symlink(struct inode *dir, } else { /* clear the extent format for fast symlink */ ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); - if (!encryption_required) + if (!encryption_required) { inode->i_op = &ext4_fast_symlink_inode_operations; + inode->i_link = (char *)&EXT4_I(inode)->i_data; + } memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, disk_link.len); inode->i_size = disk_link.len - 1; diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 49575ff7a7bb..4264fb1e341a 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -106,13 +106,6 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = { }; #endif -static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ext4_inode_info *ei = EXT4_I(d_inode(dentry)); - nd_set_link(nd, (char *) ei->i_data); - return NULL; -} - const struct inode_operations ext4_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = page_follow_link_light, @@ -126,7 +119,7 @@ const struct inode_operations ext4_symlink_inode_operations = { const struct inode_operations ext4_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ext4_follow_fast_link, + .follow_link = simple_follow_link, .setattr = ext4_setattr, .setxattr = generic_setxattr, .getxattr = generic_getxattr, -- cgit v1.2.3-58-ga151 From a8db149fc97b122ffd14e1a5f6a110124dd721ea Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:21:20 -0400 Subject: jffs2: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/jffs2/dir.c | 1 + fs/jffs2/fs.c | 1 + fs/jffs2/symlink.c | 45 +-------------------------------------------- 3 files changed, 3 insertions(+), 44 deletions(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 1ba5c97943b8..81180022923f 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -354,6 +354,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ret = -ENOMEM; goto fail; } + inode->i_link = f->target; jffs2_dbg(1, "%s(): symlink's target '%s' cached\n", __func__, (char *)f->target); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index fe5ea080b4ec..60d86e8fba6e 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -294,6 +294,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; + inode->i_link = f->target; break; case S_IFDIR: diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 1fefa25d0fa5..8ce2f240125b 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -9,58 +9,15 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include #include "nodelist.h" -static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); - const struct inode_operations jffs2_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = jffs2_follow_link, + .follow_link = simple_follow_link, .setattr = jffs2_setattr, .setxattr = jffs2_setxattr, .getxattr = jffs2_getxattr, .listxattr = jffs2_listxattr, .removexattr = jffs2_removexattr }; - -static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(dentry)); - char *p = (char *)f->target; - - /* - * We don't acquire the f->sem mutex here since the only data we - * use is f->target. - * - * 1. If we are here the inode has already built and f->target has - * to point to the target path. - * 2. Nobody uses f->target (if the inode is symlink's inode). The - * exception is inode freeing function which frees f->target. But - * it can't be called while we are here and before VFS has - * stopped using our f->target string which we provide by means of - * nd_set_link() call. - */ - - if (!p) { - pr_err("%s(): can't find symlink target\n", __func__); - p = ERR_PTR(-EIO); - } - jffs2_dbg(1, "%s(): target path is '%s'\n", - __func__, (char *)f->target); - - nd_set_link(nd, p); - - /* - * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe - * since the only way that may cause f->target to be changed is iput() operation. - * But VFS will not use f->target after iput() has been called. - */ - return NULL; -} - -- cgit v1.2.3-58-ga151 From 60380f193ee5a667842a4da4a3e936689bb0e5e6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:24:43 -0400 Subject: shmem: switch to simple_follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- mm/shmem.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index de981370fbc5..7f6e2f889122 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2451,6 +2451,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return -ENOMEM; } inode->i_op = &shmem_short_symlink_operations; + inode->i_link = info->symlink; } else { error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL); if (error) { @@ -2474,12 +2475,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, SHMEM_I(d_inode(dentry))->symlink); - return NULL; -} - static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd) { struct page *page = NULL; @@ -2642,7 +2637,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) static const struct inode_operations shmem_short_symlink_operations = { .readlink = generic_readlink, - .follow_link = shmem_follow_short_symlink, + .follow_link = simple_follow_link, #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, .getxattr = shmem_getxattr, -- cgit v1.2.3-58-ga151 From 5723cb01f0295ace2b029b0737dd6525a2de337f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:27:18 -0400 Subject: debugfs: switch to simple_follow_link() Reviewed-by: Jan Kara Signed-off-by: Al Viro --- fs/debugfs/file.c | 12 ------------ fs/debugfs/inode.c | 6 +++--- include/linux/debugfs.h | 1 - 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 830a7e76f5c6..284f9aa0028b 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -43,17 +42,6 @@ const struct file_operations debugfs_file_operations = { .llseek = noop_llseek, }; -static void *debugfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, d_inode(dentry)->i_private); - return NULL; -} - -const struct inode_operations debugfs_link_operations = { - .readlink = generic_readlink, - .follow_link = debugfs_follow_link, -}; - static int debugfs_u8_set(void *data, u64 val) { *(u8 *)data = val; diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index c1e7ffb0dab6..7eaec88ea970 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -174,7 +174,7 @@ static void debugfs_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); clear_inode(inode); if (S_ISLNK(inode->i_mode)) - kfree(inode->i_private); + kfree(inode->i_link); } static const struct super_operations debugfs_super_operations = { @@ -511,8 +511,8 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, return failed_creating(dentry); } inode->i_mode = S_IFLNK | S_IRWXUGO; - inode->i_op = &debugfs_link_operations; - inode->i_private = link; + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = link; d_instantiate(dentry, inode); return end_creating(dentry); } diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index cb25af461054..420311bcee38 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -45,7 +45,6 @@ extern struct dentry *arch_debugfs_dir; /* declared over in file.c */ extern const struct file_operations debugfs_file_operations; -extern const struct inode_operations debugfs_link_operations; struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, -- cgit v1.2.3-58-ga151 From 4b8061a67f67ebd28d4273b05d1b6ae38f2a019b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:28:56 -0400 Subject: ufs: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/ufs/inode.c | 5 +++-- fs/ufs/namei.c | 3 ++- fs/ufs/symlink.c | 13 +------------ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index be7d42c7d938..99aaf5c9bf4d 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -572,9 +572,10 @@ static void ufs_set_inode_ops(struct inode *inode) inode->i_fop = &ufs_dir_operations; inode->i_mapping->a_ops = &ufs_aops; } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) + if (!inode->i_blocks) { inode->i_op = &ufs_fast_symlink_inode_operations; - else { + inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; + } else { inode->i_op = &ufs_symlink_inode_operations; inode->i_mapping->a_ops = &ufs_aops; } diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index e491a93a7e9a..f773deb1d2e3 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -144,7 +144,8 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry, } else { /* fast symlink */ inode->i_op = &ufs_fast_symlink_inode_operations; - memcpy(UFS_I(inode)->i_u1.i_symlink, symname, l); + inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink; + memcpy(inode->i_link, symname, l); inode->i_size = l-1; } mark_inode_dirty(inode); diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c index 5b537e2fdda3..874480bb43e9 100644 --- a/fs/ufs/symlink.c +++ b/fs/ufs/symlink.c @@ -25,23 +25,12 @@ * ext2 symlink handling code */ -#include -#include - #include "ufs_fs.h" #include "ufs.h" - -static void *ufs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ufs_inode_info *p = UFS_I(d_inode(dentry)); - nd_set_link(nd, (char*)p->i_u1.i_symlink); - return NULL; -} - const struct inode_operations ufs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ufs_follow_link, + .follow_link = simple_follow_link, .setattr = ufs_setattr, }; -- cgit v1.2.3-58-ga151 From 0f301bd3055e86771e7c738ca009afad0964dbd2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:35:42 -0400 Subject: ubifs: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/ubifs/dir.c | 1 + fs/ubifs/file.c | 11 +---------- fs/ubifs/super.c | 1 + 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 27060fc855d4..5c27c66c224a 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -889,6 +889,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, memcpy(ui->data, symname, len); ((char *)ui->data)[len] = '\0'; + inode->i_link = ui->data; /* * The terminating zero byte is not written to the flash media and it * is put just to make later in-memory string processing simpler. Thus, diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 35efc103c39c..a3dfe2ae79f2 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -51,7 +51,6 @@ #include "ubifs.h" #include -#include #include static int read_block(struct inode *inode, void *addr, unsigned int block, @@ -1300,14 +1299,6 @@ static void ubifs_invalidatepage(struct page *page, unsigned int offset, ClearPageChecked(page); } -static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ubifs_inode *ui = ubifs_inode(d_inode(dentry)); - - nd_set_link(nd, ui->data); - return NULL; -} - int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; @@ -1570,7 +1561,7 @@ const struct inode_operations ubifs_file_inode_operations = { const struct inode_operations ubifs_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = ubifs_follow_link, + .follow_link = simple_follow_link, .setattr = ubifs_setattr, .getattr = ubifs_getattr, .setxattr = ubifs_setxattr, diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 75e6f04bb795..20f5dbd7c6a8 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -195,6 +195,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) } memcpy(ui->data, ino->data, ui->data_len); ((char *)ui->data)[ui->data_len] = '\0'; + inode->i_link = ui->data; break; case S_IFBLK: case S_IFCHR: -- cgit v1.2.3-58-ga151 From 2e03f3ea7acbee59a89ea73a696b2eb4c3145cde Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:37:09 -0400 Subject: sysv: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/sysv/Makefile | 2 +- fs/sysv/inode.c | 5 +++-- fs/sysv/symlink.c | 20 -------------------- fs/sysv/sysv.h | 1 - 4 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 fs/sysv/symlink.c diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile index 3591f9d7a48a..7a75e70a4b61 100644 --- a/fs/sysv/Makefile +++ b/fs/sysv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_SYSV_FS) += sysv.o sysv-objs := ialloc.o balloc.o inode.o itree.o file.o dir.o \ - namei.o super.o symlink.o + namei.o super.o diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 88956309cc86..590ad9206e3f 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -166,8 +166,9 @@ void sysv_set_inode(struct inode *inode, dev_t rdev) inode->i_op = &sysv_symlink_inode_operations; inode->i_mapping->a_ops = &sysv_aops; } else { - inode->i_op = &sysv_fast_symlink_inode_operations; - nd_terminate_link(SYSV_I(inode)->i_data, inode->i_size, + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)SYSV_I(inode)->i_data; + nd_terminate_link(inode->i_link, inode->i_size, sizeof(SYSV_I(inode)->i_data) - 1); } } else diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c deleted file mode 100644 index d3fa0d703314..000000000000 --- a/fs/sysv/symlink.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/fs/sysv/symlink.c - * - * Handling of System V filesystem fast symlinks extensions. - * Aug 2001, Christoph Hellwig (hch@infradead.org) - */ - -#include "sysv.h" -#include - -static void *sysv_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, (char *)SYSV_I(d_inode(dentry))->i_data); - return NULL; -} - -const struct inode_operations sysv_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = sysv_follow_link, -}; diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 69d488986cce..2c13525131cd 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -161,7 +161,6 @@ extern ino_t sysv_inode_by_name(struct dentry *); extern const struct inode_operations sysv_file_inode_operations; extern const struct inode_operations sysv_dir_inode_operations; -extern const struct inode_operations sysv_fast_symlink_inode_operations; extern const struct file_operations sysv_file_operations; extern const struct file_operations sysv_dir_operations; extern const struct address_space_operations sysv_aops; -- cgit v1.2.3-58-ga151 From ad476fedc7805ca7cec12a56e697afe37566e573 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:41:20 -0400 Subject: jfs: switch to simple_follow_link() Reviewed-by: Jan Kara Acked-by: Dave Kleikamp Signed-off-by: Al Viro --- fs/jfs/inode.c | 3 ++- fs/jfs/namei.c | 5 ++--- fs/jfs/symlink.c | 10 +--------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 070dc4b33544..6f1cb2b5ee28 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -63,11 +63,12 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino) inode->i_mapping->a_ops = &jfs_aops; } else { inode->i_op = &jfs_fast_symlink_inode_operations; + inode->i_link = JFS_IP(inode)->i_inline; /* * The inline data should be null-terminated, but * don't let on-disk corruption crash the kernel */ - JFS_IP(inode)->i_inline[inode->i_size] = '\0'; + inode->i_link[inode->i_size] = '\0'; } } else { inode->i_op = &jfs_file_inode_operations; diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 66db7bc0ed10..e33be921aa41 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -880,7 +880,6 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, int ssize; /* source pathname size */ struct btstack btstack; struct inode *ip = d_inode(dentry); - unchar *i_fastsymlink; s64 xlen = 0; int bmask = 0, xsize; s64 xaddr; @@ -946,8 +945,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, if (ssize <= IDATASIZE) { ip->i_op = &jfs_fast_symlink_inode_operations; - i_fastsymlink = JFS_IP(ip)->i_inline; - memcpy(i_fastsymlink, name, ssize); + ip->i_link = JFS_IP(ip)->i_inline; + memcpy(ip->i_link, name, ssize); ip->i_size = ssize - 1; /* diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c index 80f42bcc4ef1..5929e2363cb8 100644 --- a/fs/jfs/symlink.c +++ b/fs/jfs/symlink.c @@ -17,21 +17,13 @@ */ #include -#include #include "jfs_incore.h" #include "jfs_inode.h" #include "jfs_xattr.h" -static void *jfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *s = JFS_IP(d_inode(dentry))->i_inline; - nd_set_link(nd, s); - return NULL; -} - const struct inode_operations jfs_fast_symlink_inode_operations = { .readlink = generic_readlink, - .follow_link = jfs_follow_link, + .follow_link = simple_follow_link, .setattr = jfs_setattr, .setxattr = jfs_setxattr, .getxattr = jfs_getxattr, -- cgit v1.2.3-58-ga151 From df64c082efd1f71d27d942dbfc628877272e6809 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:43:25 -0400 Subject: freevxfs: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/freevxfs/vxfs_extern.h | 3 --- fs/freevxfs/vxfs_immed.c | 34 ---------------------------------- fs/freevxfs/vxfs_inode.c | 7 +++++-- 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h index 881aa3d217f0..e3dcb4467d92 100644 --- a/fs/freevxfs/vxfs_extern.h +++ b/fs/freevxfs/vxfs_extern.h @@ -50,9 +50,6 @@ extern daddr_t vxfs_bmap1(struct inode *, long); /* vxfs_fshead.c */ extern int vxfs_read_fshead(struct super_block *); -/* vxfs_immed.c */ -extern const struct inode_operations vxfs_immed_symlink_iops; - /* vxfs_inode.c */ extern const struct address_space_operations vxfs_immed_aops; extern struct kmem_cache *vxfs_inode_cachep; diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index 8b9229e2ca5c..cb84f0fcc72a 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -32,28 +32,14 @@ */ #include #include -#include #include "vxfs.h" #include "vxfs_extern.h" #include "vxfs_inode.h" -static void * vxfs_immed_follow_link(struct dentry *, struct nameidata *); - static int vxfs_immed_readpage(struct file *, struct page *); -/* - * Inode operations for immed symlinks. - * - * Unliked all other operations we do not go through the pagecache, - * but do all work directly on the inode. - */ -const struct inode_operations vxfs_immed_symlink_iops = { - .readlink = generic_readlink, - .follow_link = vxfs_immed_follow_link, -}; - /* * Address space operations for immed files and directories. */ @@ -61,26 +47,6 @@ const struct address_space_operations vxfs_immed_aops = { .readpage = vxfs_immed_readpage, }; -/** - * vxfs_immed_follow_link - follow immed symlink - * @dp: dentry for the link - * @np: pathname lookup data for the current path walk - * - * Description: - * vxfs_immed_follow_link restarts the pathname lookup with - * the data obtained from @dp. - * - * Returns: - * Zero on success, else a negative error code. - */ -static void * -vxfs_immed_follow_link(struct dentry *dp, struct nameidata *np) -{ - struct vxfs_inode_info *vip = VXFS_INO(d_inode(dp)); - nd_set_link(np, vip->vii_immed.vi_immed); - return NULL; -} - /** * vxfs_immed_readpage - read part of an immed inode into pagecache * @file: file context (unused) diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index 363e3ae25f6b..ef73ed674a27 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "vxfs.h" #include "vxfs_inode.h" @@ -327,8 +328,10 @@ vxfs_iget(struct super_block *sbp, ino_t ino) ip->i_op = &page_symlink_inode_operations; ip->i_mapping->a_ops = &vxfs_aops; } else { - ip->i_op = &vxfs_immed_symlink_iops; - vip->vii_immed.vi_immed[ip->i_size] = '\0'; + ip->i_op = &simple_symlink_inode_operations; + ip->i_link = vip->vii_immed.vi_immed; + nd_terminate_link(ip->i_link, ip->i_size, + sizeof(vip->vii_immed.vi_immed) - 1); } } else init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev)); -- cgit v1.2.3-58-ga151 From a5ef103daad25f5906700cb0929ce4eac7f72e2e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:46:42 -0400 Subject: exofs: switch to {simple,page}_symlink_inode_operations ACK-by: Boaz Harrosh Signed-off-by: Al Viro --- fs/exofs/Kbuild | 2 +- fs/exofs/exofs.h | 4 ---- fs/exofs/inode.c | 9 +++++---- fs/exofs/namei.c | 5 +++-- fs/exofs/symlink.c | 55 ------------------------------------------------------ 5 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 fs/exofs/symlink.c diff --git a/fs/exofs/Kbuild b/fs/exofs/Kbuild index b47c7b8dc275..a364fd0965ec 100644 --- a/fs/exofs/Kbuild +++ b/fs/exofs/Kbuild @@ -16,5 +16,5 @@ libore-y := ore.o ore_raid.o obj-$(CONFIG_ORE) += libore.o -exofs-y := inode.o file.o symlink.o namei.o dir.o super.o sys.o +exofs-y := inode.o file.o namei.o dir.o super.o sys.o obj-$(CONFIG_EXOFS_FS) += exofs.o diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h index ad9cac670a47..2e86086bc940 100644 --- a/fs/exofs/exofs.h +++ b/fs/exofs/exofs.h @@ -207,10 +207,6 @@ extern const struct address_space_operations exofs_aops; extern const struct inode_operations exofs_dir_inode_operations; extern const struct inode_operations exofs_special_inode_operations; -/* symlink.c */ -extern const struct inode_operations exofs_symlink_inode_operations; -extern const struct inode_operations exofs_fast_symlink_inode_operations; - /* exofs_init_comps will initialize an ore_components device array * pointing to a single ore_comp struct, and a round-robin view * of the device table. diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 786e4cc8c889..73c64daa0f55 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1222,10 +1222,11 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &exofs_dir_operations; inode->i_mapping->a_ops = &exofs_aops; } else if (S_ISLNK(inode->i_mode)) { - if (exofs_inode_is_fast_symlink(inode)) - inode->i_op = &exofs_fast_symlink_inode_operations; - else { - inode->i_op = &exofs_symlink_inode_operations; + if (exofs_inode_is_fast_symlink(inode)) { + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)oi->i_data; + } else { + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &exofs_aops; } } else { diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c index 5ae25e431191..09a6bb1ad63c 100644 --- a/fs/exofs/namei.c +++ b/fs/exofs/namei.c @@ -113,7 +113,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry, oi = exofs_i(inode); if (l > sizeof(oi->i_data)) { /* slow symlink */ - inode->i_op = &exofs_symlink_inode_operations; + inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &exofs_aops; memset(oi->i_data, 0, sizeof(oi->i_data)); @@ -122,7 +122,8 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry, goto out_fail; } else { /* fast symlink */ - inode->i_op = &exofs_fast_symlink_inode_operations; + inode->i_op = &simple_symlink_inode_operations; + inode->i_link = (char *)oi->i_data; memcpy(oi->i_data, symname, l); inode->i_size = l-1; } diff --git a/fs/exofs/symlink.c b/fs/exofs/symlink.c deleted file mode 100644 index 6f6f3a4c1365..000000000000 --- a/fs/exofs/symlink.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006 - * Avishay Traeger (avishay@gmail.com) - * Copyright (C) 2008, 2009 - * Boaz Harrosh - * - * Copyrights for code taken from ext2: - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - * from - * linux/fs/minix/inode.c - * Copyright (C) 1991, 1992 Linus Torvalds - * - * This file is part of exofs. - * - * exofs is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation. Since it is based on ext2, and the only - * valid version of GPL for the Linux kernel is version 2, the only valid - * version of GPL for exofs is version 2. - * - * exofs is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with exofs; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "exofs.h" - -static void *exofs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct exofs_i_info *oi = exofs_i(d_inode(dentry)); - - nd_set_link(nd, (char *)oi->i_data); - return NULL; -} - -const struct inode_operations exofs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, -}; - -const struct inode_operations exofs_fast_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = exofs_follow_link, -}; -- cgit v1.2.3-58-ga151 From ac194dccd2395a51b106c9b0aab7310fa6946b0a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 10:50:05 -0400 Subject: ceph: switch to simple_follow_link() Signed-off-by: Al Viro --- fs/ceph/inode.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index e876e1944519..571acd88606c 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -819,6 +818,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page, else kfree(sym); /* lost a race */ } + inode->i_link = ci->i_symlink; break; case S_IFDIR: inode->i_op = &ceph_dir_iops; @@ -1691,16 +1691,9 @@ retry: /* * symlinks */ -static void *ceph_sym_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct ceph_inode_info *ci = ceph_inode(d_inode(dentry)); - nd_set_link(nd, ci->i_symlink); - return NULL; -} - static const struct inode_operations ceph_symlink_iops = { .readlink = generic_readlink, - .follow_link = ceph_sym_follow_link, + .follow_link = simple_follow_link, .setattr = ceph_setattr, .getattr = ceph_getattr, .setxattr = ceph_setxattr, -- cgit v1.2.3-58-ga151 From b4272646293b46fb8c7bc9b5176eb94d8141a278 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 21 Apr 2015 10:48:50 -0400 Subject: logfs: fix a pagecache leak for symlinks Signed-off-by: Al Viro --- fs/logfs/dir.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 4cf38f118549..f9b45d46d4c4 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -779,6 +779,7 @@ fail: const struct inode_operations logfs_symlink_iops = { .readlink = generic_readlink, .follow_link = page_follow_link_light, + .put_link = page_put_link, }; const struct inode_operations logfs_dir_iops = { -- cgit v1.2.3-58-ga151 From 37882db0546c759ff75b561c188539ac96fd0bfe Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:39 +1100 Subject: SECURITY: remove nameidata arg from inode_follow_link. No ->inode_follow_link() methods use the nameidata arg, and it is about to become private to namei.c. So remove from all inode_follow_link() functions. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- fs/namei.c | 2 +- include/linux/security.h | 9 +++------ security/capability.c | 3 +-- security/security.c | 4 ++-- security/selinux/hooks.c | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index fe30d3be43a8..7f20b40426dc 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -871,7 +871,7 @@ follow_link(struct path *link, struct nameidata *nd, void **p) touch_atime(link); nd_set_link(nd, NULL); - error = security_inode_follow_link(link->dentry, nd); + error = security_inode_follow_link(dentry); if (error) goto out_put_nd_path; diff --git a/include/linux/security.h b/include/linux/security.h index 18264ea9e314..62a66202ecf1 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -43,7 +43,6 @@ struct file; struct vfsmount; struct path; struct qstr; -struct nameidata; struct iattr; struct fown_struct; struct file_operations; @@ -477,7 +476,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @inode_follow_link: * Check permission to follow a symbolic link when looking up a pathname. * @dentry contains the dentry structure for the link. - * @nd contains the nameidata structure for the parent directory. * Return 0 if permission is granted. * @inode_permission: * Check permission before accessing an inode. This hook is called by the @@ -1553,7 +1551,7 @@ struct security_operations { int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); int (*inode_readlink) (struct dentry *dentry); - int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); + int (*inode_follow_link) (struct dentry *dentry); int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); int (*inode_getattr) (const struct path *path); @@ -1839,7 +1837,7 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); int security_inode_readlink(struct dentry *dentry); -int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); +int security_inode_follow_link(struct dentry *dentry); int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct dentry *dentry, struct iattr *attr); int security_inode_getattr(const struct path *path); @@ -2241,8 +2239,7 @@ static inline int security_inode_readlink(struct dentry *dentry) return 0; } -static inline int security_inode_follow_link(struct dentry *dentry, - struct nameidata *nd) +static inline int security_inode_follow_link(struct dentry *dentry) { return 0; } diff --git a/security/capability.c b/security/capability.c index 0d03fcc489a4..fae99db180ca 100644 --- a/security/capability.c +++ b/security/capability.c @@ -209,8 +209,7 @@ static int cap_inode_readlink(struct dentry *dentry) return 0; } -static int cap_inode_follow_link(struct dentry *dentry, - struct nameidata *nameidata) +static int cap_inode_follow_link(struct dentry *dentry) { return 0; } diff --git a/security/security.c b/security/security.c index 8e9b1f4b9b45..d7c30b03e144 100644 --- a/security/security.c +++ b/security/security.c @@ -581,11 +581,11 @@ int security_inode_readlink(struct dentry *dentry) return security_ops->inode_readlink(dentry); } -int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) +int security_inode_follow_link(struct dentry *dentry) { if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; - return security_ops->inode_follow_link(dentry, nd); + return security_ops->inode_follow_link(dentry); } int security_inode_permission(struct inode *inode, int mask) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7dade28affba..801622947282 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2861,7 +2861,7 @@ static int selinux_inode_readlink(struct dentry *dentry) return dentry_has_perm(cred, dentry, FILE__READ); } -static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata) +static int selinux_inode_follow_link(struct dentry *dentry) { const struct cred *cred = current_cred(); -- cgit v1.2.3-58-ga151 From 34b128f31c028a28887c6659e90620727a319b16 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Apr 2015 07:48:53 -0400 Subject: uninline walk_component() seriously improves the stack *and* I-cache footprint... Signed-off-by: Al Viro --- fs/namei.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 7f20b40426dc..a77f9ca2c3e7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1569,8 +1569,7 @@ static inline int should_follow_link(struct dentry *dentry, int follow) return unlikely(d_is_symlink(dentry)) ? follow : 0; } -static inline int walk_component(struct nameidata *nd, struct path *path, - int follow) +static int walk_component(struct nameidata *nd, struct path *path, int follow) { struct inode *inode; int err; -- cgit v1.2.3-58-ga151 From f488443d1dc50454acd549ddd856421e8f961f98 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 11:27:43 -0400 Subject: namei: take O_NOFOLLOW treatment into do_last() Signed-off-by: Al Viro --- fs/namei.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a77f9ca2c3e7..4c1a8bf917e4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3059,6 +3059,11 @@ finish_lookup: } } BUG_ON(inode != path->dentry->d_inode); + if (!(nd->flags & LOOKUP_FOLLOW)) { + path_put_conditional(path, nd); + path_put(&nd->path); + return -ELOOP; + } return 1; } @@ -3243,12 +3248,6 @@ static struct file *path_openat(int dfd, struct filename *pathname, while (unlikely(error > 0)) { /* trailing symlink */ struct path link = path; void *cookie; - if (!(nd->flags & LOOKUP_FOLLOW)) { - path_put_conditional(&path, nd); - path_put(&nd->path); - error = -ELOOP; - break; - } error = may_follow_link(&link, nd); if (unlikely(error)) break; -- cgit v1.2.3-58-ga151 From fd2805be238947eb819284189205e8de6f6c41c4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 12:02:25 -0400 Subject: do_last: kill symlink_ok When O_PATH is present, O_CREAT isn't, so symlink_ok is always equal to (open_flags & O_PATH) && !(nd->flags & LOOKUP_FOLLOW). Signed-off-by: Al Viro --- fs/namei.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 4c1a8bf917e4..3e452338f1b7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2931,7 +2931,6 @@ static int do_last(struct nameidata *nd, struct path *path, bool got_write = false; int acc_mode = op->acc_mode; struct inode *inode; - bool symlink_ok = false; struct path save_parent = { .dentry = NULL, .mnt = NULL }; bool retried = false; int error; @@ -2949,8 +2948,6 @@ static int do_last(struct nameidata *nd, struct path *path, if (!(open_flag & O_CREAT)) { if (nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; - if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW)) - symlink_ok = true; /* we _can_ be in RCU mode here */ error = lookup_fast(nd, path, &inode); if (likely(!error)) @@ -3050,7 +3047,8 @@ retry_lookup: } finish_lookup: /* we _can_ be in RCU mode here */ - if (should_follow_link(path->dentry, !symlink_ok)) { + if (should_follow_link(path->dentry, + !(open_flag & O_PATH) || (nd->flags & LOOKUP_FOLLOW))) { if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != path->mnt || unlazy_walk(nd, path->dentry))) { -- cgit v1.2.3-58-ga151 From a5cfe2d5e14d4c2dec06b22d18050c443c7537f9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 12:10:45 -0400 Subject: do_last: regularize the logics around following symlinks With LOOKUP_FOLLOW we unlazy and return 1; without it we either fail with ELOOP or, for O_PATH opens, succeed. No need to mix those cases... Signed-off-by: Al Viro --- fs/namei.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 3e452338f1b7..54cbfe799249 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3046,9 +3046,7 @@ retry_lookup: goto out; } finish_lookup: - /* we _can_ be in RCU mode here */ - if (should_follow_link(path->dentry, - !(open_flag & O_PATH) || (nd->flags & LOOKUP_FOLLOW))) { + if (should_follow_link(path->dentry, nd->flags & LOOKUP_FOLLOW)) { if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != path->mnt || unlazy_walk(nd, path->dentry))) { @@ -3057,14 +3055,15 @@ finish_lookup: } } BUG_ON(inode != path->dentry->d_inode); - if (!(nd->flags & LOOKUP_FOLLOW)) { - path_put_conditional(path, nd); - path_put(&nd->path); - return -ELOOP; - } return 1; } + if (unlikely(d_is_symlink(path->dentry)) && !(open_flag & O_PATH)) { + path_to_nameidata(path, nd); + error = -ELOOP; + goto out; + } + if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) { path_to_nameidata(path, nd); } else { -- cgit v1.2.3-58-ga151 From 6a9f40d6104d74b0eaa06cc59e2dcc8f2dde5e22 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 30 Apr 2015 12:25:18 -0400 Subject: namei: get rid of lookup_hash() it's a convenient helper, but we'll want to shift nameidata down the call chain, so it won't be available there... Signed-off-by: Al Viro --- fs/namei.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 54cbfe799249..1439aa3812e5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2128,16 +2128,6 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -/* - * Restricted form of lookup. Doesn't follow links, single-component only, - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ -static struct dentry *lookup_hash(struct nameidata *nd) -{ - return __lookup_hash(&nd->last, nd->path.dentry, nd->flags); -} - /** * lookup_one_len - filesystem helper to lookup single pathname component * @name: pathname component to lookup @@ -3351,7 +3341,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, * Do the final lookup. */ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); if (IS_ERR(dentry)) goto unlock; @@ -3665,7 +3655,7 @@ retry: goto exit1; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit2; @@ -3785,7 +3775,7 @@ retry: goto exit1; retry_deleg: mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = lookup_hash(&nd); + dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /* Why not before? Because we want correct error value */ @@ -4304,7 +4294,7 @@ retry: retry_deleg: trap = lock_rename(new_dir, old_dir); - old_dentry = lookup_hash(&oldnd); + old_dentry = __lookup_hash(&oldnd.last, oldnd.path.dentry, oldnd.flags); error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) goto exit3; @@ -4312,7 +4302,7 @@ retry_deleg: error = -ENOENT; if (d_is_negative(old_dentry)) goto exit4; - new_dentry = lookup_hash(&newnd); + new_dentry = __lookup_hash(&newnd.last, newnd.path.dentry, newnd.flags); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit4; -- cgit v1.2.3-58-ga151 From f5beed755bce1791d926ded9d83640b25e14a617 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 30 Apr 2015 16:09:11 -0400 Subject: name: shift nameidata down into user_path_walk() that avoids having nameidata on stack during the calls of ->rmdir()/->unlink() and *two* of those during the calls of ->rename(). Signed-off-by: Al Viro --- fs/namei.c | 124 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1439aa3812e5..293300c1a84e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2211,9 +2211,13 @@ EXPORT_SYMBOL(user_path_at); * path-walking is complete. */ static struct filename * -user_path_parent(int dfd, const char __user *path, struct nameidata *nd, +user_path_parent(int dfd, const char __user *path, + struct path *parent, + struct qstr *last, + int *type, unsigned int flags) { + struct nameidata nd; struct filename *s = getname(path); int error; @@ -2223,11 +2227,14 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd, if (IS_ERR(s)) return s; - error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd); + error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, &nd); if (error) { putname(s); return ERR_PTR(error); } + *parent = nd.path; + *last = nd.last; + *type = nd.last_type; return s; } @@ -3630,14 +3637,17 @@ static long do_rmdir(int dfd, const char __user *pathname) int error = 0; struct filename *name; struct dentry *dentry; - struct nameidata nd; + struct path path; + struct qstr last; + int type; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, &nd, lookup_flags); + name = user_path_parent(dfd, pathname, + &path, &last, &type, lookup_flags); if (IS_ERR(name)) return PTR_ERR(name); - switch(nd.last_type) { + switch (type) { case LAST_DOTDOT: error = -ENOTEMPTY; goto exit1; @@ -3649,13 +3659,12 @@ retry: goto exit1; } - nd.flags &= ~LOOKUP_PARENT; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto exit1; - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); + mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (IS_ERR(dentry)) goto exit2; @@ -3663,17 +3672,17 @@ retry: error = -ENOENT; goto exit3; } - error = security_path_rmdir(&nd.path, dentry); + error = security_path_rmdir(&path, dentry); if (error) goto exit3; - error = vfs_rmdir(nd.path.dentry->d_inode, dentry); + error = vfs_rmdir(path.dentry->d_inode, dentry); exit3: dput(dentry); exit2: - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - mnt_drop_write(nd.path.mnt); + mutex_unlock(&path.dentry->d_inode->i_mutex); + mnt_drop_write(path.mnt); exit1: - path_put(&nd.path); + path_put(&path); putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -3756,43 +3765,45 @@ static long do_unlinkat(int dfd, const char __user *pathname) int error; struct filename *name; struct dentry *dentry; - struct nameidata nd; + struct path path; + struct qstr last; + int type; struct inode *inode = NULL; struct inode *delegated_inode = NULL; unsigned int lookup_flags = 0; retry: - name = user_path_parent(dfd, pathname, &nd, lookup_flags); + name = user_path_parent(dfd, pathname, + &path, &last, &type, lookup_flags); if (IS_ERR(name)) return PTR_ERR(name); error = -EISDIR; - if (nd.last_type != LAST_NORM) + if (type != LAST_NORM) goto exit1; - nd.flags &= ~LOOKUP_PARENT; - error = mnt_want_write(nd.path.mnt); + error = mnt_want_write(path.mnt); if (error) goto exit1; retry_deleg: - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); + mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path.dentry, lookup_flags); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /* Why not before? Because we want correct error value */ - if (nd.last.name[nd.last.len]) + if (last.name[last.len]) goto slashes; inode = dentry->d_inode; if (d_is_negative(dentry)) goto slashes; ihold(inode); - error = security_path_unlink(&nd.path, dentry); + error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); exit2: dput(dentry); } - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&path.dentry->d_inode->i_mutex); if (inode) iput(inode); /* truncate the inode here */ inode = NULL; @@ -3801,9 +3812,9 @@ exit2: if (!error) goto retry_deleg; } - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path.mnt); exit1: - path_put(&nd.path); + path_put(&path); putname(name); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4233,14 +4244,15 @@ EXPORT_SYMBOL(vfs_rename); SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { - struct dentry *old_dir, *new_dir; struct dentry *old_dentry, *new_dentry; struct dentry *trap; - struct nameidata oldnd, newnd; + struct path old_path, new_path; + struct qstr old_last, new_last; + int old_type, new_type; struct inode *delegated_inode = NULL; struct filename *from; struct filename *to; - unsigned int lookup_flags = 0; + unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET; bool should_retry = false; int error; @@ -4254,47 +4266,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD)) return -EPERM; + if (flags & RENAME_EXCHANGE) + target_flags = 0; + retry: - from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags); + from = user_path_parent(olddfd, oldname, + &old_path, &old_last, &old_type, lookup_flags); if (IS_ERR(from)) { error = PTR_ERR(from); goto exit; } - to = user_path_parent(newdfd, newname, &newnd, lookup_flags); + to = user_path_parent(newdfd, newname, + &new_path, &new_last, &new_type, lookup_flags); if (IS_ERR(to)) { error = PTR_ERR(to); goto exit1; } error = -EXDEV; - if (oldnd.path.mnt != newnd.path.mnt) + if (old_path.mnt != new_path.mnt) goto exit2; - old_dir = oldnd.path.dentry; error = -EBUSY; - if (oldnd.last_type != LAST_NORM) + if (old_type != LAST_NORM) goto exit2; - new_dir = newnd.path.dentry; if (flags & RENAME_NOREPLACE) error = -EEXIST; - if (newnd.last_type != LAST_NORM) + if (new_type != LAST_NORM) goto exit2; - error = mnt_want_write(oldnd.path.mnt); + error = mnt_want_write(old_path.mnt); if (error) goto exit2; - oldnd.flags &= ~LOOKUP_PARENT; - newnd.flags &= ~LOOKUP_PARENT; - if (!(flags & RENAME_EXCHANGE)) - newnd.flags |= LOOKUP_RENAME_TARGET; - retry_deleg: - trap = lock_rename(new_dir, old_dir); + trap = lock_rename(new_path.dentry, old_path.dentry); - old_dentry = __lookup_hash(&oldnd.last, oldnd.path.dentry, oldnd.flags); + old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags); error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) goto exit3; @@ -4302,7 +4312,7 @@ retry_deleg: error = -ENOENT; if (d_is_negative(old_dentry)) goto exit4; - new_dentry = __lookup_hash(&newnd.last, newnd.path.dentry, newnd.flags); + new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags); error = PTR_ERR(new_dentry); if (IS_ERR(new_dentry)) goto exit4; @@ -4316,16 +4326,16 @@ retry_deleg: if (!d_is_dir(new_dentry)) { error = -ENOTDIR; - if (newnd.last.name[newnd.last.len]) + if (new_last.name[new_last.len]) goto exit5; } } /* unless the source is a directory trailing slashes give -ENOTDIR */ if (!d_is_dir(old_dentry)) { error = -ENOTDIR; - if (oldnd.last.name[oldnd.last.len]) + if (old_last.name[old_last.len]) goto exit5; - if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len]) + if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len]) goto exit5; } /* source should not be ancestor of target */ @@ -4338,32 +4348,32 @@ retry_deleg: if (new_dentry == trap) goto exit5; - error = security_path_rename(&oldnd.path, old_dentry, - &newnd.path, new_dentry, flags); + error = security_path_rename(&old_path, old_dentry, + &new_path, new_dentry, flags); if (error) goto exit5; - error = vfs_rename(old_dir->d_inode, old_dentry, - new_dir->d_inode, new_dentry, + error = vfs_rename(old_path.dentry->d_inode, old_dentry, + new_path.dentry->d_inode, new_dentry, &delegated_inode, flags); exit5: dput(new_dentry); exit4: dput(old_dentry); exit3: - unlock_rename(new_dir, old_dir); + unlock_rename(new_path.dentry, old_path.dentry); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } - mnt_drop_write(oldnd.path.mnt); + mnt_drop_write(old_path.mnt); exit2: if (retry_estale(error, lookup_flags)) should_retry = true; - path_put(&newnd.path); + path_put(&new_path); putname(to); exit1: - path_put(&oldnd.path); + path_put(&old_path); putname(from); if (should_retry) { should_retry = false; -- cgit v1.2.3-58-ga151 From 46afd6f61cc33ae4b3a2aed4bb454d11d4114c27 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 May 2015 22:08:30 -0400 Subject: namei: lift nameidata into filename_mountpoint() when we go for on-demand allocation of saved state in link_path_walk(), we'll want nameidata to stay around for all 3 calls of path_mountpoint(). Signed-off-by: Al Viro --- fs/namei.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 293300c1a84e..ab2bcbdbd683 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2344,31 +2344,28 @@ out: */ static int path_mountpoint(int dfd, const struct filename *name, struct path *path, - unsigned int flags) + struct nameidata *nd, unsigned int flags) { - struct nameidata nd; - int err; - - err = path_init(dfd, name, flags, &nd); + int err = path_init(dfd, name, flags, nd); if (unlikely(err)) goto out; - err = mountpoint_last(&nd, path); + err = mountpoint_last(nd, path); while (err > 0) { void *cookie; struct path link = *path; - err = may_follow_link(&link, &nd); + err = may_follow_link(&link, nd); if (unlikely(err)) break; - nd.flags |= LOOKUP_PARENT; - err = follow_link(&link, &nd, &cookie); + nd->flags |= LOOKUP_PARENT; + err = follow_link(&link, nd, &cookie); if (err) break; - err = mountpoint_last(&nd, path); - put_link(&nd, &link, cookie); + err = mountpoint_last(nd, path); + put_link(nd, &link, cookie); } out: - path_cleanup(&nd); + path_cleanup(nd); return err; } @@ -2376,14 +2373,15 @@ static int filename_mountpoint(int dfd, struct filename *name, struct path *path, unsigned int flags) { + struct nameidata nd; int error; if (IS_ERR(name)) return PTR_ERR(name); - error = path_mountpoint(dfd, name, path, flags | LOOKUP_RCU); + error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_RCU); if (unlikely(error == -ECHILD)) - error = path_mountpoint(dfd, name, path, flags); + error = path_mountpoint(dfd, name, path, &nd, flags); if (unlikely(error == -ESTALE)) - error = path_mountpoint(dfd, name, path, flags | LOOKUP_REVAL); + error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_REVAL); if (likely(!error)) audit_inode(name, path->dentry, 0); putname(name); -- cgit v1.2.3-58-ga151 From 680baacbca69d18a6d7315374ad83d05ac9c0977 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 13:32:22 -0400 Subject: new ->follow_link() and ->put_link() calling conventions a) instead of storing the symlink body (via nd_set_link()) and returning an opaque pointer later passed to ->put_link(), ->follow_link() _stores_ that opaque pointer (into void * passed by address by caller) and returns the symlink body. Returning ERR_PTR() on error, NULL on jump (procfs magic symlinks) and pointer to symlink body for normal symlinks. Stored pointer is ignored in all cases except the last one. Storing NULL for opaque pointer (or not storing it at all) means no call of ->put_link(). b) the body used to be passed to ->put_link() implicitly (via nameidata). Now only the opaque pointer is. In the cases when we used the symlink body to free stuff, ->follow_link() now should store it as opaque pointer in addition to returning it. Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 4 +- Documentation/filesystems/vfs.txt | 4 +- drivers/staging/lustre/lustre/llite/symlink.c | 11 ++--- fs/9p/vfs_inode.c | 13 +++--- fs/9p/vfs_inode_dotl.c | 7 ++- fs/autofs4/symlink.c | 5 +- fs/befs/linuxvfs.c | 35 +++++++------- fs/cifs/cifsfs.h | 2 +- fs/cifs/link.c | 28 ++++++------ fs/configfs/symlink.c | 28 +++++------- fs/ecryptfs/inode.c | 8 ++-- fs/ext4/symlink.c | 9 ++-- fs/f2fs/namei.c | 18 +++----- fs/fuse/dir.c | 19 ++------ fs/gfs2/inode.c | 10 ++-- fs/hostfs/hostfs_kern.c | 15 +++--- fs/hppfs/hppfs.c | 9 ++-- fs/kernfs/symlink.c | 22 ++++----- fs/libfs.c | 12 ++--- fs/namei.c | 66 +++++++++------------------ fs/nfs/symlink.c | 19 +++----- fs/overlayfs/inode.c | 18 ++++---- fs/proc/base.c | 2 +- fs/proc/inode.c | 9 ++-- fs/proc/namespaces.c | 2 +- fs/proc/self.c | 24 +++++----- fs/proc/thread_self.c | 22 ++++----- fs/xfs/xfs_iops.c | 10 ++-- include/linux/fs.h | 12 ++--- include/linux/namei.h | 2 - mm/shmem.c | 23 +++++----- 31 files changed, 195 insertions(+), 273 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 0a926e2ba3ab..7fa6c4ac858c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -50,8 +50,8 @@ prototypes: int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); - void * (*follow_link) (struct dentry *, struct nameidata *); - void (*put_link) (struct dentry *, struct nameidata *, void *); + const char *(*follow_link) (struct dentry *, void **, struct nameidata *); + void (*put_link) (struct dentry *, void *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int, unsigned int); int (*get_acl)(struct inode *, int); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 5d833b32bbcd..1c6b03ac2e5a 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -350,8 +350,8 @@ struct inode_operations { int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); - void * (*follow_link) (struct dentry *, struct nameidata *); - void (*put_link) (struct dentry *, struct nameidata *, void *); + const char *(*follow_link) (struct dentry *, void **, struct nameidata *); + void (*put_link) (struct dentry *, void *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index 3711e671a4df..e488cb3bb25d 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -118,7 +118,7 @@ failed: return rc; } -static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ll_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(dentry); struct ptlrpc_request *request = NULL; @@ -140,18 +140,17 @@ static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd) } if (rc) { ptlrpc_req_finished(request); - request = NULL; - symname = ERR_PTR(rc); + return ERR_PTR(rc); } - nd_set_link(nd, symname); /* symname may contain a pointer to the request message buffer, * we delay request releasing until ll_put_link then. */ - return request; + *cookie = request; + return symname; } -static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +static void ll_put_link(struct dentry *dentry, void *cookie) { ptlrpc_req_finished(cookie); } diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0ba11712b388..7cc70a39a1d8 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1230,11 +1230,12 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) * */ -static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); struct p9_fid *fid = v9fs_fid_lookup(dentry); struct p9_wstat *st; + char *res; p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); @@ -1253,14 +1254,14 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd) kfree(st); return ERR_PTR(-EINVAL); } - if (strlen(st->extension) >= PATH_MAX) - st->extension[PATH_MAX - 1] = '\0'; - - nd_set_link(nd, st->extension); + res = st->extension; st->extension = NULL; + if (strlen(res) >= PATH_MAX) + res[PATH_MAX - 1] = '\0'; + p9stat_free(st); kfree(st); - return NULL; + return *cookie = res; } /** diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index bc2a91f2b910..ae062ffa0f1f 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -909,8 +909,8 @@ error: * */ -static void * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd) +static const char * +v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct p9_fid *fid = v9fs_fid_lookup(dentry); char *target; @@ -923,8 +923,7 @@ v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd) retval = p9_client_readlink(fid, &target); if (retval) return ERR_PTR(retval); - nd_set_link(nd, target); - return NULL; + return *cookie = target; } int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index de58cc7b8076..9c6a07739c9b 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -12,14 +12,13 @@ #include "autofs_i.h" -static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *autofs4_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); if (ino && !autofs4_oz_mode(sbi)) ino->last_used = jiffies; - nd_set_link(nd, d_inode(dentry)->i_private); - return NULL; + return d_inode(dentry)->i_private; } const struct inode_operations autofs4_symlink_inode_operations = { diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 172e306d68a7..3a1aefb86a11 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -42,7 +42,7 @@ static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static void befs_destroy_inodecache(void); -static void *befs_follow_link(struct dentry *, struct nameidata *); +static const char *befs_follow_link(struct dentry *, void **, struct nameidata *nd); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, @@ -463,8 +463,8 @@ befs_destroy_inodecache(void) * The data stream become link name. Unless the LONG_SYMLINK * flag is set. */ -static void * -befs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char * +befs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct super_block *sb = dentry->d_sb; struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); @@ -474,23 +474,20 @@ befs_follow_link(struct dentry *dentry, struct nameidata *nd) if (len == 0) { befs_error(sb, "Long symlink with illegal length"); - link = ERR_PTR(-EIO); - } else { - befs_debug(sb, "Follow long symlink"); - - link = kmalloc(len, GFP_NOFS); - if (!link) { - link = ERR_PTR(-ENOMEM); - } else if (befs_read_lsymlink(sb, data, link, len) != len) { - kfree(link); - befs_error(sb, "Failed to read entire long symlink"); - link = ERR_PTR(-EIO); - } else { - link[len - 1] = '\0'; - } + return ERR_PTR(-EIO); } - nd_set_link(nd, link); - return NULL; + befs_debug(sb, "Follow long symlink"); + + link = kmalloc(len, GFP_NOFS); + if (!link) + return ERR_PTR(-ENOMEM); + if (befs_read_lsymlink(sb, data, link, len) != len) { + kfree(link); + befs_error(sb, "Failed to read entire long symlink"); + return ERR_PTR(-EIO); + } + link[len - 1] = '\0'; + return *cookie = link; } /* diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 252f5c15806b..61012da7e9d8 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,7 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); +extern const char *cifs_follow_link(struct dentry *direntry, void **cookie, struct nameidata *nd); extern int cifs_readlink(struct dentry *direntry, char __user *buffer, int buflen); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 252e672d5604..4a439c2c0c7f 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -626,8 +626,8 @@ cifs_hl_exit: return rc; } -void * -cifs_follow_link(struct dentry *direntry, struct nameidata *nd) +const char * +cifs_follow_link(struct dentry *direntry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(direntry); int rc = -ENOMEM; @@ -643,16 +643,18 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { - rc = PTR_ERR(tlink); - tlink = NULL; - goto out; + free_xid(xid); + return ERR_CAST(tlink); } tcon = tlink_tcon(tlink); server = tcon->ses->server; full_path = build_path_from_dentry(direntry); - if (!full_path) - goto out; + if (!full_path) { + free_xid(xid); + cifs_put_tlink(tlink); + return ERR_PTR(-ENOMEM); + } cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode); @@ -670,17 +672,13 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) &target_path, cifs_sb); kfree(full_path); -out: + free_xid(xid); + cifs_put_tlink(tlink); if (rc != 0) { kfree(target_path); - target_path = ERR_PTR(rc); + return ERR_PTR(rc); } - - free_xid(xid); - if (tlink) - cifs_put_tlink(tlink); - nd_set_link(nd, target_path); - return NULL; + return *cookie = target_path; } int diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index cc9f2546ea4a..fac8e8517f33 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -279,30 +279,26 @@ static int configfs_getlink(struct dentry *dentry, char * path) } -static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *configfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { - int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); + int error; - if (page) { - error = configfs_getlink(dentry, (char *)page); - if (!error) { - nd_set_link(nd, (char *)page); - return (void *)page; - } + if (!page) + return ERR_PTR(-ENOMEM); + + error = configfs_getlink(dentry, (char *)page); + if (!error) { + return *cookie = (void *)page; } - nd_set_link(nd, ERR_PTR(error)); - return NULL; + free_page(page); + return ERR_PTR(error); } -static void configfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +static void configfs_put_link(struct dentry *dentry, void *cookie) { - if (cookie) { - unsigned long page = (unsigned long)cookie; - free_page(page); - } + free_page((unsigned long)cookie); } const struct inode_operations configfs_symlink_inode_operations = { diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index fc850b55db67..cdb9d6c4532d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -675,18 +675,16 @@ out: return rc ? ERR_PTR(rc) : buf; } -static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { size_t len; char *buf = ecryptfs_readlink_lower(dentry, &len); if (IS_ERR(buf)) - goto out; + return buf; fsstack_copy_attr_atime(d_inode(dentry), d_inode(ecryptfs_dentry_to_lower(dentry))); buf[len] = '\0'; -out: - nd_set_link(nd, buf); - return NULL; + return *cookie = buf; } /** diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index 4264fb1e341a..afec475aaf5c 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -23,7 +23,7 @@ #include "xattr.h" #ifdef CONFIG_EXT4_FS_ENCRYPTION -static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ext4_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct page *cpage = NULL; char *caddr, *paddr = NULL; @@ -37,7 +37,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize); if (IS_ERR(ctx)) - return ctx; + return ERR_CAST(ctx); if (ext4_inode_is_fast_symlink(inode)) { caddr = (char *) EXT4_I(inode)->i_data; @@ -46,7 +46,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) cpage = read_mapping_page(inode->i_mapping, 0, NULL); if (IS_ERR(cpage)) { ext4_put_fname_crypto_ctx(&ctx); - return cpage; + return ERR_CAST(cpage); } caddr = kmap(cpage); caddr[size] = 0; @@ -77,13 +77,12 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd) /* Null-terminate the name */ if (res <= plen) paddr[res] = '\0'; - nd_set_link(nd, paddr); ext4_put_fname_crypto_ctx(&ctx); if (cpage) { kunmap(cpage); page_cache_release(cpage); } - return NULL; + return *cookie = paddr; errout: ext4_put_fname_crypto_ctx(&ctx); if (cpage) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 658e8079aaf9..d2947937515e 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -296,19 +296,15 @@ fail: return err; } -static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *f2fs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { - struct page *page = page_follow_link_light(dentry, nd); - - if (IS_ERR_OR_NULL(page)) - return page; - - /* this is broken symlink case */ - if (*nd_get_link(nd) == 0) { - page_put_link(dentry, nd, page); - return ERR_PTR(-ENOENT); + const char *link = page_follow_link_light(dentry, cookie, nd); + if (!IS_ERR(link) && !*link) { + /* this is broken symlink case */ + page_put_link(dentry, *cookie); + link = ERR_PTR(-ENOENT); } - return page; + return link; } static int f2fs_symlink(struct inode *dir, struct dentry *dentry, diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0572bca49f15..f9cb260375cf 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1365,7 +1365,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) return err; } -static char *read_link(struct dentry *dentry) +static const char *fuse_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); @@ -1389,26 +1389,15 @@ static char *read_link(struct dentry *dentry) link = ERR_PTR(ret); } else { link[ret] = '\0'; + *cookie = link; } fuse_invalidate_atime(inode); return link; } -static void free_link(char *link) +static void fuse_put_link(struct dentry *dentry, void *cookie) { - if (!IS_ERR(link)) - free_page((unsigned long) link); -} - -static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - nd_set_link(nd, read_link(dentry)); - return NULL; -} - -static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c) -{ - free_link(nd_get_link(nd)); + free_page((unsigned long) cookie); } static int fuse_dir_open(struct inode *inode, struct file *file) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 1b3ca7a2e3fc..f59390aebffb 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1548,7 +1548,7 @@ out: * Returns: 0 on success or error code */ -static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *gfs2_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct gfs2_inode *ip = GFS2_I(d_inode(dentry)); struct gfs2_holder i_gh; @@ -1561,8 +1561,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) error = gfs2_glock_nq(&i_gh); if (error) { gfs2_holder_uninit(&i_gh); - nd_set_link(nd, ERR_PTR(error)); - return NULL; + return ERR_PTR(error); } size = (unsigned int)i_size_read(&ip->i_inode); @@ -1586,8 +1585,9 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd) brelse(dibh); out: gfs2_glock_dq_uninit(&i_gh); - nd_set_link(nd, buf); - return NULL; + if (!IS_ERR(buf)) + *cookie = buf; + return buf; } /** diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index ef263174acd2..f650ed661fab 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -892,7 +892,7 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *hostfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { char *link = __getname(); if (link) { @@ -906,21 +906,18 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) } if (err < 0) { __putname(link); - link = ERR_PTR(err); + return ERR_PTR(err); } } else { - link = ERR_PTR(-ENOMEM); + return ERR_PTR(-ENOMEM); } - nd_set_link(nd, link); - return NULL; + return *cookie = link; } -static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +static void hostfs_put_link(struct dentry *dentry, void *cookie) { - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - __putname(s); + __putname(cookie); } static const struct inode_operations hostfs_link_iops = { diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index fa2bd5366ecf..b8f24d3b04ee 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -642,20 +642,19 @@ static int hppfs_readlink(struct dentry *dentry, char __user *buffer, buflen); } -static void *hppfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *hppfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; - return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, nd); + return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie, nd); } -static void hppfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +static void hppfs_put_link(struct dentry *dentry, void *cookie) { struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; if (d_inode(proc_dentry)->i_op->put_link) - d_inode(proc_dentry)->i_op->put_link(proc_dentry, nd, cookie); + d_inode(proc_dentry)->i_op->put_link(proc_dentry, cookie); } static const struct inode_operations hppfs_dir_iops = { diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 8a198898e39a..3c7e799974a2 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -112,25 +112,23 @@ static int kernfs_getlink(struct dentry *dentry, char *path) return error; } -static void *kernfs_iop_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); - if (page) { - error = kernfs_getlink(dentry, (char *) page); - if (error < 0) - free_page((unsigned long)page); + if (!page) + return ERR_PTR(-ENOMEM); + error = kernfs_getlink(dentry, (char *)page); + if (unlikely(error < 0)) { + free_page((unsigned long)page); + return ERR_PTR(error); } - nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); - return NULL; + return *cookie = (char *)page; } -static void kernfs_iop_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +static void kernfs_iop_put_link(struct dentry *dentry, void *cookie) { - char *page = nd_get_link(nd); - if (!IS_ERR(page)) - free_page((unsigned long)page); + free_page((unsigned long)cookie); } const struct inode_operations kernfs_symlink_iops = { diff --git a/fs/libfs.c b/fs/libfs.c index 72e4e015455f..0c83fde20dbd 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1024,12 +1024,9 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) } EXPORT_SYMBOL(noop_fsync); -void kfree_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +void kfree_put_link(struct dentry *dentry, void *cookie) { - char *s = nd_get_link(nd); - if (!IS_ERR(s)) - kfree(s); + kfree(cookie); } EXPORT_SYMBOL(kfree_put_link); @@ -1094,10 +1091,9 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, } EXPORT_SYMBOL(simple_nosetlease); -void *simple_follow_link(struct dentry *dentry, struct nameidata *nd) +const char *simple_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { - nd_set_link(nd, d_inode(dentry)->i_link); - return NULL; + return d_inode(dentry)->i_link; } EXPORT_SYMBOL(simple_follow_link); diff --git a/fs/namei.c b/fs/namei.c index ab2bcbdbd683..aeca44877371 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -502,7 +502,6 @@ struct nameidata { int last_type; unsigned depth; struct file *base; - char *saved_names[MAX_NESTED_LINKS + 1]; }; /* @@ -713,23 +712,11 @@ void nd_jump_link(struct nameidata *nd, struct path *path) nd->flags |= LOOKUP_JUMPED; } -void nd_set_link(struct nameidata *nd, char *path) -{ - nd->saved_names[nd->depth] = path; -} -EXPORT_SYMBOL(nd_set_link); - -char *nd_get_link(struct nameidata *nd) -{ - return nd->saved_names[nd->depth]; -} -EXPORT_SYMBOL(nd_get_link); - static inline void put_link(struct nameidata *nd, struct path *link, void *cookie) { struct inode *inode = link->dentry->d_inode; - if (inode->i_op->put_link) - inode->i_op->put_link(link->dentry, nd, cookie); + if (cookie && inode->i_op->put_link) + inode->i_op->put_link(link->dentry, cookie); path_put(link); } @@ -854,7 +841,7 @@ follow_link(struct path *link, struct nameidata *nd, void **p) { struct dentry *dentry = link->dentry; int error; - char *s; + const char *s; BUG_ON(nd->flags & LOOKUP_RCU); @@ -869,26 +856,20 @@ follow_link(struct path *link, struct nameidata *nd, void **p) current->total_link_count++; touch_atime(link); - nd_set_link(nd, NULL); error = security_inode_follow_link(dentry); if (error) goto out_put_nd_path; nd->last_type = LAST_BIND; - *p = dentry->d_inode->i_op->follow_link(dentry, nd); - error = PTR_ERR(*p); - if (IS_ERR(*p)) + *p = NULL; + s = dentry->d_inode->i_op->follow_link(dentry, p, nd); + error = PTR_ERR(s); + if (IS_ERR(s)) goto out_put_nd_path; error = 0; - s = nd_get_link(nd); if (s) { - if (unlikely(IS_ERR(s))) { - path_put(&nd->path); - put_link(nd, link, *p); - return PTR_ERR(s); - } if (*s == '/') { if (!nd->root.mnt) set_root(nd); @@ -906,7 +887,6 @@ follow_link(struct path *link, struct nameidata *nd, void **p) return error; out_put_nd_path: - *p = NULL; path_put(&nd->path); path_put(link); return error; @@ -4430,18 +4410,15 @@ EXPORT_SYMBOL(readlink_copy); */ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - struct nameidata nd; void *cookie; + const char *link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL); int res; - nd.depth = 0; - cookie = dentry->d_inode->i_op->follow_link(dentry, &nd); - if (IS_ERR(cookie)) - return PTR_ERR(cookie); - - res = readlink_copy(buffer, buflen, nd_get_link(&nd)); - if (dentry->d_inode->i_op->put_link) - dentry->d_inode->i_op->put_link(dentry, &nd, cookie); + if (IS_ERR(link)) + return PTR_ERR(link); + res = readlink_copy(buffer, buflen, link); + if (cookie && dentry->d_inode->i_op->put_link) + dentry->d_inode->i_op->put_link(dentry, cookie); return res; } EXPORT_SYMBOL(generic_readlink); @@ -4473,22 +4450,21 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) } EXPORT_SYMBOL(page_readlink); -void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd) +const char *page_follow_link_light(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct page *page = NULL; - nd_set_link(nd, page_getlink(dentry, &page)); - return page; + char *res = page_getlink(dentry, &page); + if (!IS_ERR(res)) + *cookie = page; + return res; } EXPORT_SYMBOL(page_follow_link_light); -void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +void page_put_link(struct dentry *dentry, void *cookie) { struct page *page = cookie; - - if (page) { - kunmap(page); - page_cache_release(page); - } + kunmap(page); + page_cache_release(page); } EXPORT_SYMBOL(page_put_link); diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 2d56200655fe..c992b200ae7e 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -20,7 +20,6 @@ #include #include #include -#include /* Symlink caching in the page cache is even more simplistic * and straight-forward than readdir caching. @@ -43,7 +42,7 @@ error: return -EIO; } -static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *nfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(dentry); struct page *page; @@ -51,19 +50,13 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); if (err) - goto read_failed; + return err; page = read_cache_page(&inode->i_data, 0, (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) { - err = page; - goto read_failed; - } - nd_set_link(nd, kmap(page)); - return page; - -read_failed: - nd_set_link(nd, err); - return NULL; + if (IS_ERR(page)) + return ERR_CAST(page); + *cookie = page; + return kmap(page); } /* diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 1b4b9c5e51b7..235ad42afb57 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -140,12 +140,12 @@ struct ovl_link_data { void *cookie; }; -static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *ovl_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { - void *ret; struct dentry *realdentry; struct inode *realinode; struct ovl_link_data *data = NULL; + const char *ret; realdentry = ovl_dentry_real(dentry); realinode = realdentry->d_inode; @@ -160,19 +160,21 @@ static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) data->realdentry = realdentry; } - ret = realinode->i_op->follow_link(realdentry, nd); - if (IS_ERR(ret)) { + ret = realinode->i_op->follow_link(realdentry, cookie, nd); + if (IS_ERR_OR_NULL(ret)) { kfree(data); return ret; } if (data) - data->cookie = ret; + data->cookie = *cookie; - return data; + *cookie = data; + + return ret; } -static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) +static void ovl_put_link(struct dentry *dentry, void *c) { struct inode *realinode; struct ovl_link_data *data = c; @@ -181,7 +183,7 @@ static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) return; realinode = data->realdentry->d_inode; - realinode->i_op->put_link(data->realdentry, nd, data->cookie); + realinode->i_op->put_link(data->realdentry, data->cookie); kfree(data); } diff --git a/fs/proc/base.c b/fs/proc/base.c index 093ca14f5701..52652f86b187 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1380,7 +1380,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } -static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(dentry); struct path path; diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 8272aaba1bb0..acd51d75387d 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -23,7 +23,6 @@ #include #include #include -#include #include @@ -394,16 +393,16 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif -static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct proc_dir_entry *pde = PDE(d_inode(dentry)); if (unlikely(!use_pde(pde))) return ERR_PTR(-EINVAL); - nd_set_link(nd, pde->data); - return pde; + *cookie = pde; + return pde->data; } -static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p) +static void proc_put_link(struct dentry *dentry, void *p) { unuse_pde(p); } diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index e512642dbbdc..10d24dd096e8 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -30,7 +30,7 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; diff --git a/fs/proc/self.c b/fs/proc/self.c index 6195b4a7c3b1..ad333946b53a 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -1,5 +1,4 @@ #include -#include #include #include #include "internal.h" @@ -19,21 +18,20 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_self_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); - char *name = ERR_PTR(-ENOENT); - if (tgid) { - /* 11 for max length of signed int in decimal + NULL term */ - name = kmalloc(12, GFP_KERNEL); - if (!name) - name = ERR_PTR(-ENOMEM); - else - sprintf(name, "%d", tgid); - } - nd_set_link(nd, name); - return NULL; + char *name; + + if (!tgid) + return ERR_PTR(-ENOENT); + /* 11 for max length of signed int in decimal + NULL term */ + name = kmalloc(12, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + sprintf(name, "%d", tgid); + return *cookie = name; } static const struct inode_operations proc_self_inode_operations = { diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index a8371993b4fb..85c96e0d7aaa 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -1,5 +1,4 @@ #include -#include #include #include #include "internal.h" @@ -20,21 +19,20 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); pid_t pid = task_pid_nr_ns(current, ns); - char *name = ERR_PTR(-ENOENT); - if (pid) { - name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); - if (!name) - name = ERR_PTR(-ENOMEM); - else - sprintf(name, "%d/task/%d", tgid, pid); - } - nd_set_link(nd, name); - return NULL; + char *name; + + if (!pid) + return ERR_PTR(-ENOENT); + name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + sprintf(name, "%d/task/%d", tgid, pid); + return *cookie = name; } static const struct inode_operations proc_thread_self_inode_operations = { diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f4cd7204e236..26c4dcb1ef56 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -41,7 +41,6 @@ #include #include -#include #include #include #include @@ -414,9 +413,10 @@ xfs_vn_rename( * we need to be very careful about how much stack we use. * uio is kmalloced for this reason... */ -STATIC void * +STATIC const char * xfs_vn_follow_link( struct dentry *dentry, + void **cookie, struct nameidata *nd) { char *link; @@ -430,14 +430,12 @@ xfs_vn_follow_link( if (unlikely(error)) goto out_kfree; - nd_set_link(nd, link); - return NULL; + return *cookie = link; out_kfree: kfree(link); out_err: - nd_set_link(nd, ERR_PTR(error)); - return NULL; + return ERR_PTR(error); } STATIC int diff --git a/include/linux/fs.h b/include/linux/fs.h index 0ac758fcff00..9ab934113a28 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1608,12 +1608,12 @@ struct file_operations { struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); - void * (*follow_link) (struct dentry *, struct nameidata *); + const char * (*follow_link) (struct dentry *, void **, struct nameidata *); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); - void (*put_link) (struct dentry *, struct nameidata *, void *); + void (*put_link) (struct dentry *, void *); int (*create) (struct inode *,struct dentry *, umode_t, bool); int (*link) (struct dentry *,struct inode *,struct dentry *); @@ -2705,13 +2705,13 @@ extern const struct file_operations generic_ro_fops; extern int readlink_copy(char __user *, int, const char *); extern int page_readlink(struct dentry *, char __user *, int); -extern void *page_follow_link_light(struct dentry *, struct nameidata *); -extern void page_put_link(struct dentry *, struct nameidata *, void *); +extern const char *page_follow_link_light(struct dentry *, void **, struct nameidata *); +extern void page_put_link(struct dentry *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, int nofs); extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; -extern void kfree_put_link(struct dentry *, struct nameidata *, void *); +extern void kfree_put_link(struct dentry *, void *); extern int generic_readlink(struct dentry *, char __user *, int); extern void generic_fillattr(struct inode *, struct kstat *); int vfs_getattr_nosec(struct path *path, struct kstat *stat); @@ -2722,7 +2722,7 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); -void *simple_follow_link(struct dentry *, struct nameidata *); +const char *simple_follow_link(struct dentry *, void **, struct nameidata *); extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); diff --git a/include/linux/namei.h b/include/linux/namei.h index c8990779f0c3..a5d5bed2c0e1 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -71,8 +71,6 @@ extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *); extern void nd_jump_link(struct nameidata *nd, struct path *path); -extern void nd_set_link(struct nameidata *nd, char *path); -extern char *nd_get_link(struct nameidata *nd); static inline void nd_terminate_link(void *name, size_t len, size_t maxlen) { diff --git a/mm/shmem.c b/mm/shmem.c index 7f6e2f889122..d1693dcb4285 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2475,24 +2475,23 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *shmem_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) { struct page *page = NULL; int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL); - nd_set_link(nd, error ? ERR_PTR(error) : kmap(page)); - if (page) - unlock_page(page); - return page; + if (error) + return ERR_PTR(error); + unlock_page(page); + *cookie = page; + return kmap(page); } -static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +static void shmem_put_link(struct dentry *dentry, void *cookie) { - if (!IS_ERR(nd_get_link(nd))) { - struct page *page = cookie; - kunmap(page); - mark_page_accessed(page); - page_cache_release(page); - } + struct page *page = cookie; + kunmap(page); + mark_page_accessed(page); + page_cache_release(page); } #ifdef CONFIG_TMPFS_XATTR -- cgit v1.2.3-58-ga151 From 0a959df54b088d38371ebae4b1d7bc3112f6ef62 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 18:23:41 -0400 Subject: namei.c: separate the parts of follow_link() that find the link body Split a piece of fs/namei.c:follow_link() that does obtaining the link body into a separate function. follow_link() itself is converted to calling get_link() and then doing the body traversal (if any). The next step will expand follow_link() call in link_path_walk() and this helps to keep the size down... Signed-off-by: Al Viro --- fs/namei.c | 65 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index aeca44877371..a1ba5561daef 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -836,21 +836,22 @@ static int may_linkat(struct path *link) return -EPERM; } -static __always_inline int -follow_link(struct path *link, struct nameidata *nd, void **p) +static __always_inline const char * +get_link(struct path *link, struct nameidata *nd, void **p) { struct dentry *dentry = link->dentry; + struct inode *inode = dentry->d_inode; int error; - const char *s; + const char *res; BUG_ON(nd->flags & LOOKUP_RCU); if (link->mnt == nd->path.mnt) mntget(link->mnt); - error = -ELOOP; + res = ERR_PTR(-ELOOP); if (unlikely(current->total_link_count >= 40)) - goto out_put_nd_path; + goto out; cond_resched(); current->total_link_count++; @@ -858,37 +859,43 @@ follow_link(struct path *link, struct nameidata *nd, void **p) touch_atime(link); error = security_inode_follow_link(dentry); + res = ERR_PTR(error); if (error) - goto out_put_nd_path; + goto out; nd->last_type = LAST_BIND; *p = NULL; - s = dentry->d_inode->i_op->follow_link(dentry, p, nd); - error = PTR_ERR(s); - if (IS_ERR(s)) - goto out_put_nd_path; - - error = 0; - if (s) { - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - } - nd->inode = nd->path.dentry->d_inode; - error = link_path_walk(s, nd); - if (unlikely(error)) - put_link(nd, link, *p); + res = inode->i_op->follow_link(dentry, p, nd); + if (IS_ERR(res)) { +out: + path_put(&nd->path); + path_put(link); } + return res; +} - return error; +static __always_inline int +follow_link(struct path *link, struct nameidata *nd, void **p) +{ + const char *s = get_link(link, nd, p); + int error; -out_put_nd_path: - path_put(&nd->path); - path_put(link); + if (unlikely(IS_ERR(s))) + return PTR_ERR(s); + if (unlikely(!s)) + return 0; + if (*s == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->flags |= LOOKUP_JUMPED; + } + nd->inode = nd->path.dentry->d_inode; + error = link_path_walk(s, nd); + if (unlikely(error)) + put_link(nd, link, *p); return error; } -- cgit v1.2.3-58-ga151 From d4dee48badbb7ccd740087321518abcc870eda65 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 30 Apr 2015 20:08:02 -0400 Subject: namei: don't bother with ->follow_link() if ->i_link is set with new calling conventions it's trivial Signed-off-by: Al Viro Conflicts: fs/namei.c --- fs/namei.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a1ba5561daef..2ffb4afb250a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -865,11 +865,14 @@ get_link(struct path *link, struct nameidata *nd, void **p) nd->last_type = LAST_BIND; *p = NULL; - res = inode->i_op->follow_link(dentry, p, nd); - if (IS_ERR(res)) { + res = inode->i_link; + if (!res) { + res = inode->i_op->follow_link(dentry, p, nd); + if (IS_ERR(res)) { out: - path_put(&nd->path); - path_put(link); + path_put(&nd->path); + path_put(link); + } } return res; } @@ -4418,11 +4421,14 @@ EXPORT_SYMBOL(readlink_copy); int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { void *cookie; - const char *link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL); + const char *link = dentry->d_inode->i_link; int res; - if (IS_ERR(link)) - return PTR_ERR(link); + if (!link) { + link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL); + if (IS_ERR(link)) + return PTR_ERR(link); + } res = readlink_copy(buffer, buflen, link); if (cookie && dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, cookie); -- cgit v1.2.3-58-ga151 From caa8563443539895fc6654b996c68d5e8048b86e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 17:52:47 -0400 Subject: namei: introduce nameidata->link shares space with nameidata->next, walk_component() et.al. store the struct path of symlink instead of returning it into a variable passed by caller. Signed-off-by: Al Viro --- fs/namei.c | 62 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 2ffb4afb250a..06c3e4a6c440 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -494,7 +494,10 @@ EXPORT_SYMBOL(path_put); struct nameidata { struct path path; - struct qstr last; + union { + struct qstr last; + struct path link; + }; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; @@ -1559,8 +1562,9 @@ static inline int should_follow_link(struct dentry *dentry, int follow) return unlikely(d_is_symlink(dentry)) ? follow : 0; } -static int walk_component(struct nameidata *nd, struct path *path, int follow) +static int walk_component(struct nameidata *nd, int follow) { + struct path path; struct inode *inode; int err; /* @@ -1570,38 +1574,39 @@ static int walk_component(struct nameidata *nd, struct path *path, int follow) */ if (unlikely(nd->last_type != LAST_NORM)) return handle_dots(nd, nd->last_type); - err = lookup_fast(nd, path, &inode); + err = lookup_fast(nd, &path, &inode); if (unlikely(err)) { if (err < 0) goto out_err; - err = lookup_slow(nd, path); + err = lookup_slow(nd, &path); if (err < 0) goto out_err; - inode = path->dentry->d_inode; + inode = path.dentry->d_inode; err = -ENOENT; - if (d_is_negative(path->dentry)) + if (d_is_negative(path.dentry)) goto out_path_put; } - if (should_follow_link(path->dentry, follow)) { + if (should_follow_link(path.dentry, follow)) { if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path->mnt || - unlazy_walk(nd, path->dentry))) { + if (unlikely(nd->path.mnt != path.mnt || + unlazy_walk(nd, path.dentry))) { err = -ECHILD; goto out_err; } } - BUG_ON(inode != path->dentry->d_inode); + BUG_ON(inode != path.dentry->d_inode); + nd->link = path; return 1; } - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); nd->inode = inode; return 0; out_path_put: - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); out_err: terminate_walk(nd); return err; @@ -1614,12 +1619,12 @@ out_err: * Without that kind of total limit, nasty chains of consecutive * symlinks can cause almost arbitrarily long lookups. */ -static inline int nested_symlink(struct path *path, struct nameidata *nd) +static inline int nested_symlink(struct nameidata *nd) { int res; if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { - path_put_conditional(path, nd); + path_put_conditional(&nd->link, nd); path_put(&nd->path); return -ELOOP; } @@ -1629,13 +1634,13 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd) current->link_count++; do { - struct path link = *path; + struct path link = nd->link; void *cookie; res = follow_link(&link, nd, &cookie); if (res) break; - res = walk_component(nd, path, LOOKUP_FOLLOW); + res = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); } while (res > 0); @@ -1770,7 +1775,6 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { - struct path next; int err; while (*name=='/') @@ -1830,17 +1834,17 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (!*name) return 0; - err = walk_component(nd, &next, LOOKUP_FOLLOW); + err = walk_component(nd, LOOKUP_FOLLOW); if (err < 0) return err; if (err) { - err = nested_symlink(&next, nd); + err = nested_symlink(nd); if (err) return err; } if (!d_can_lookup(nd->path.dentry)) { - err = -ENOTDIR; + err = -ENOTDIR; break; } } @@ -1960,20 +1964,19 @@ static void path_cleanup(struct nameidata *nd) fput(nd->base); } -static inline int lookup_last(struct nameidata *nd, struct path *path) +static inline int lookup_last(struct nameidata *nd) { if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - return walk_component(nd, path, nd->flags & LOOKUP_FOLLOW); + return walk_component(nd, nd->flags & LOOKUP_FOLLOW); } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ static int path_lookupat(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { - struct path path; int err; /* @@ -1992,10 +1995,10 @@ static int path_lookupat(int dfd, const struct filename *name, */ err = path_init(dfd, name, flags, nd); if (!err && !(flags & LOOKUP_PARENT)) { - err = lookup_last(nd, &path); + err = lookup_last(nd); while (err > 0) { void *cookie; - struct path link = path; + struct path link = nd->link; err = may_follow_link(&link, nd); if (unlikely(err)) break; @@ -2003,7 +2006,7 @@ static int path_lookupat(int dfd, const struct filename *name, err = follow_link(&link, nd, &cookie); if (err) break; - err = lookup_last(nd, &path); + err = lookup_last(nd); put_link(nd, &link, cookie); } } @@ -2312,8 +2315,10 @@ done: } path->dentry = dentry; path->mnt = nd->path.mnt; - if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) + if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) { + nd->link = *path; return 1; + } mntget(path->mnt); follow_mount(path); error = 0; @@ -3040,6 +3045,7 @@ finish_lookup: } } BUG_ON(inode != path->dentry->d_inode); + nd->link = *path; return 1; } @@ -3228,7 +3234,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, error = do_last(nd, &path, file, op, &opened, pathname); while (unlikely(error > 0)) { /* trailing symlink */ - struct path link = path; + struct path link = nd->link; void *cookie; error = may_follow_link(&link, nd); if (unlikely(error)) -- cgit v1.2.3-58-ga151 From 896475d5bd2ee5d094239ff77801089ae25bcbc1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 18:02:17 -0400 Subject: do_last: move path there from caller's stack frame We used to need it to feed to follow_link(). No more... Signed-off-by: Al Viro --- fs/namei.c | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 06c3e4a6c440..858290768d92 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2911,7 +2911,7 @@ out_dput: /* * Handle the last step of open() */ -static int do_last(struct nameidata *nd, struct path *path, +static int do_last(struct nameidata *nd, struct file *file, const struct open_flags *op, int *opened, struct filename *name) { @@ -2922,6 +2922,7 @@ static int do_last(struct nameidata *nd, struct path *path, int acc_mode = op->acc_mode; struct inode *inode; struct path save_parent = { .dentry = NULL, .mnt = NULL }; + struct path path; bool retried = false; int error; @@ -2939,7 +2940,7 @@ static int do_last(struct nameidata *nd, struct path *path, if (nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; /* we _can_ be in RCU mode here */ - error = lookup_fast(nd, path, &inode); + error = lookup_fast(nd, &path, &inode); if (likely(!error)) goto finish_lookup; @@ -2977,7 +2978,7 @@ retry_lookup: */ } mutex_lock(&dir->d_inode->i_mutex); - error = lookup_open(nd, path, file, op, got_write, opened); + error = lookup_open(nd, &path, file, op, got_write, opened); mutex_unlock(&dir->d_inode->i_mutex); if (error <= 0) { @@ -2997,15 +2998,15 @@ retry_lookup: open_flag &= ~O_TRUNC; will_truncate = false; acc_mode = MAY_OPEN; - path_to_nameidata(path, nd); + path_to_nameidata(&path, nd); goto finish_open_created; } /* * create/update audit record if it already exists. */ - if (d_is_positive(path->dentry)) - audit_inode(name, path->dentry, 0); + if (d_is_positive(path.dentry)) + audit_inode(name, path.dentry, 0); /* * If atomic_open() acquired write access it is dropped now due to @@ -3021,7 +3022,7 @@ retry_lookup: if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) goto exit_dput; - error = follow_managed(path, nd->flags); + error = follow_managed(&path, nd->flags); if (error < 0) goto exit_dput; @@ -3029,38 +3030,38 @@ retry_lookup: nd->flags |= LOOKUP_JUMPED; BUG_ON(nd->flags & LOOKUP_RCU); - inode = path->dentry->d_inode; + inode = path.dentry->d_inode; error = -ENOENT; - if (d_is_negative(path->dentry)) { - path_to_nameidata(path, nd); + if (d_is_negative(path.dentry)) { + path_to_nameidata(&path, nd); goto out; } finish_lookup: - if (should_follow_link(path->dentry, nd->flags & LOOKUP_FOLLOW)) { + if (should_follow_link(path.dentry, nd->flags & LOOKUP_FOLLOW)) { if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path->mnt || - unlazy_walk(nd, path->dentry))) { + if (unlikely(nd->path.mnt != path.mnt || + unlazy_walk(nd, path.dentry))) { error = -ECHILD; goto out; } } - BUG_ON(inode != path->dentry->d_inode); - nd->link = *path; + BUG_ON(inode != path.dentry->d_inode); + nd->link = path; return 1; } - if (unlikely(d_is_symlink(path->dentry)) && !(open_flag & O_PATH)) { - path_to_nameidata(path, nd); + if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) { + path_to_nameidata(&path, nd); error = -ELOOP; goto out; } - if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) { - path_to_nameidata(path, nd); + if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) { + path_to_nameidata(&path, nd); } else { save_parent.dentry = nd->path.dentry; - save_parent.mnt = mntget(path->mnt); - nd->path.dentry = path->dentry; + save_parent.mnt = mntget(path.mnt); + nd->path.dentry = path.dentry; } nd->inode = inode; @@ -3122,7 +3123,7 @@ out: return error; exit_dput: - path_put_conditional(path, nd); + path_put_conditional(&path, nd); goto out; exit_fput: fput(file); @@ -3213,7 +3214,6 @@ static struct file *path_openat(int dfd, struct filename *pathname, struct nameidata *nd, const struct open_flags *op, int flags) { struct file *file; - struct path path; int opened = 0; int error; @@ -3232,7 +3232,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(error)) goto out; - error = do_last(nd, &path, file, op, &opened, pathname); + error = do_last(nd, file, op, &opened, pathname); while (unlikely(error > 0)) { /* trailing symlink */ struct path link = nd->link; void *cookie; @@ -3244,7 +3244,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, error = follow_link(&link, nd, &cookie); if (unlikely(error)) break; - error = do_last(nd, &path, file, op, &opened, pathname); + error = do_last(nd, file, op, &opened, pathname); put_link(nd, &link, cookie); } out: -- cgit v1.2.3-58-ga151 From 5a460275ef3c14602040e5dc581a0d8771ce6b43 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 17 Apr 2015 23:44:45 -0400 Subject: namei: expand nested_symlink() in its only caller Signed-off-by: Al Viro --- fs/namei.c | 61 +++++++++++++++++++++++-------------------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 858290768d92..7fcda918a24f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1612,43 +1612,6 @@ out_err: return err; } -/* - * This limits recursive symlink follows to 8, while - * limiting consecutive symlinks to 40. - * - * Without that kind of total limit, nasty chains of consecutive - * symlinks can cause almost arbitrarily long lookups. - */ -static inline int nested_symlink(struct nameidata *nd) -{ - int res; - - if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { - path_put_conditional(&nd->link, nd); - path_put(&nd->path); - return -ELOOP; - } - BUG_ON(nd->depth >= MAX_NESTED_LINKS); - - nd->depth++; - current->link_count++; - - do { - struct path link = nd->link; - void *cookie; - - res = follow_link(&link, nd, &cookie); - if (res) - break; - res = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd, &link, cookie); - } while (res > 0); - - current->link_count--; - nd->depth--; - return res; -} - /* * We can do the critical dentry name comparison and hashing * operations one word at a time, but we are limited to: @@ -1839,7 +1802,29 @@ static int link_path_walk(const char *name, struct nameidata *nd) return err; if (err) { - err = nested_symlink(nd); + if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { + path_put_conditional(&nd->link, nd); + path_put(&nd->path); + return -ELOOP; + } + BUG_ON(nd->depth >= MAX_NESTED_LINKS); + + nd->depth++; + current->link_count++; + + do { + struct path link = nd->link; + void *cookie; + + err = follow_link(&link, nd, &cookie); + if (err) + break; + err = walk_component(nd, LOOKUP_FOLLOW); + put_link(nd, &link, cookie); + } while (err > 0); + + current->link_count--; + nd->depth--; if (err) return err; } -- cgit v1.2.3-58-ga151 From 172a39a059bb8d84649bcad3e252fe416ad188a8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 18:45:16 -0400 Subject: namei: expand the call of follow_link() in link_path_walk() ... and strip __always_inline from follow_link() - remaining callers don't need that. Now link_path_walk() recursion is a direct one. Signed-off-by: Al Viro --- fs/namei.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 7fcda918a24f..2354d4fc0038 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -880,8 +880,7 @@ out: return res; } -static __always_inline int -follow_link(struct path *link, struct nameidata *nd, void **p) +static int follow_link(struct path *link, struct nameidata *nd, void **p) { const char *s = get_link(link, nd, p); int error; @@ -1815,10 +1814,29 @@ static int link_path_walk(const char *name, struct nameidata *nd) do { struct path link = nd->link; void *cookie; + const char *s = get_link(&link, nd, &cookie); - err = follow_link(&link, nd, &cookie); - if (err) + if (unlikely(IS_ERR(s))) { + err = PTR_ERR(s); break; + } + err = 0; + if (likely(s)) { + if (*s == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->flags |= LOOKUP_JUMPED; + } + nd->inode = nd->path.dentry->d_inode; + err = link_path_walk(s, nd); + if (unlikely(err)) { + put_link(nd, &link, cookie); + break; + } + } err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); } while (err > 0); -- cgit v1.2.3-58-ga151 From 21fef2176e50bf785eaac920d949fcac5e2db124 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Apr 2015 00:16:37 -0400 Subject: namei: move the calls of may_follow_link() into follow_link() All remaining callers of the former are preceded by the latter Signed-off-by: Al Viro --- fs/namei.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 2354d4fc0038..f7659eebc92d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -882,9 +882,12 @@ out: static int follow_link(struct path *link, struct nameidata *nd, void **p) { - const char *s = get_link(link, nd, p); - int error; - + const char *s; + int error = may_follow_link(link, nd); + if (unlikely(error)) + return error; + nd->flags |= LOOKUP_PARENT; + s = get_link(link, nd, p); if (unlikely(IS_ERR(s))) return PTR_ERR(s); if (unlikely(!s)) @@ -2002,10 +2005,6 @@ static int path_lookupat(int dfd, const struct filename *name, while (err > 0) { void *cookie; struct path link = nd->link; - err = may_follow_link(&link, nd); - if (unlikely(err)) - break; - nd->flags |= LOOKUP_PARENT; err = follow_link(&link, nd, &cookie); if (err) break; @@ -2352,10 +2351,6 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, while (err > 0) { void *cookie; struct path link = *path; - err = may_follow_link(&link, nd); - if (unlikely(err)) - break; - nd->flags |= LOOKUP_PARENT; err = follow_link(&link, nd, &cookie); if (err) break; @@ -3239,10 +3234,6 @@ static struct file *path_openat(int dfd, struct filename *pathname, while (unlikely(error > 0)) { /* trailing symlink */ struct path link = nd->link; void *cookie; - error = may_follow_link(&link, nd); - if (unlikely(error)) - break; - nd->flags |= LOOKUP_PARENT; nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); error = follow_link(&link, nd, &cookie); if (unlikely(error)) -- cgit v1.2.3-58-ga151 From 95fa25d9f2e4898c62d2732f765c42eac0999285 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 13:46:57 -0400 Subject: namei: rename follow_link to trailing_symlink, move it down Signed-off-by: Al Viro --- fs/namei.c | 62 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index f7659eebc92d..d729ef7cbf46 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -668,8 +668,6 @@ static __always_inline void set_root(struct nameidata *nd) get_fs_root(current->fs, &nd->root); } -static int link_path_walk(const char *, struct nameidata *); - static __always_inline unsigned set_root_rcu(struct nameidata *nd) { struct fs_struct *fs = current->fs; @@ -880,33 +878,6 @@ out: return res; } -static int follow_link(struct path *link, struct nameidata *nd, void **p) -{ - const char *s; - int error = may_follow_link(link, nd); - if (unlikely(error)) - return error; - nd->flags |= LOOKUP_PARENT; - s = get_link(link, nd, p); - if (unlikely(IS_ERR(s))) - return PTR_ERR(s); - if (unlikely(!s)) - return 0; - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - } - nd->inode = nd->path.dentry->d_inode; - error = link_path_walk(s, nd); - if (unlikely(error)) - put_link(nd, link, *p); - return error; -} - static int follow_up_rcu(struct path *path) { struct mount *mnt = real_mount(path->mnt); @@ -1970,6 +1941,33 @@ static void path_cleanup(struct nameidata *nd) fput(nd->base); } +static int trailing_symlink(struct path *link, struct nameidata *nd, void **p) +{ + const char *s; + int error = may_follow_link(link, nd); + if (unlikely(error)) + return error; + nd->flags |= LOOKUP_PARENT; + s = get_link(link, nd, p); + if (unlikely(IS_ERR(s))) + return PTR_ERR(s); + if (unlikely(!s)) + return 0; + if (*s == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->flags |= LOOKUP_JUMPED; + } + nd->inode = nd->path.dentry->d_inode; + error = link_path_walk(s, nd); + if (unlikely(error)) + put_link(nd, link, *p); + return error; +} + static inline int lookup_last(struct nameidata *nd) { if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) @@ -2005,7 +2003,7 @@ static int path_lookupat(int dfd, const struct filename *name, while (err > 0) { void *cookie; struct path link = nd->link; - err = follow_link(&link, nd, &cookie); + err = trailing_symlink(&link, nd, &cookie); if (err) break; err = lookup_last(nd); @@ -2351,7 +2349,7 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, while (err > 0) { void *cookie; struct path link = *path; - err = follow_link(&link, nd, &cookie); + err = trailing_symlink(&link, nd, &cookie); if (err) break; err = mountpoint_last(nd, path); @@ -3235,7 +3233,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, struct path link = nd->link; void *cookie; nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); - error = follow_link(&link, nd, &cookie); + error = trailing_symlink(&link, nd, &cookie); if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); -- cgit v1.2.3-58-ga151 From b0c24c3bdf237182b2f043bfee68bf886b0c9ad3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 19:10:36 -0400 Subject: link_path_walk: handle get_link() returning ERR_PTR() immediately If we get ERR_PTR() from get_link(), we are guaranteed to get err != 0 when we break out of do-while, so we are going to hit if (err) return err; shortly after it. Pull that into the if (IS_ERR(s)) body. Signed-off-by: Al Viro --- fs/namei.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index d729ef7cbf46..99374704c17d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1792,7 +1792,9 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); - break; + current->link_count--; + nd->depth--; + return err; } err = 0; if (likely(s)) { -- cgit v1.2.3-58-ga151 From 12b0957800514535165f98efe7714f2a53bbfbb0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 19:19:01 -0400 Subject: link_path_walk: don't bother with walk_component() after jumping link ... it does nothing if nd->last_type is LAST_BIND. Signed-off-by: Al Viro --- fs/namei.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 99374704c17d..ee083f921ef3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1797,7 +1797,11 @@ static int link_path_walk(const char *name, struct nameidata *nd) return err; } err = 0; - if (likely(s)) { + if (unlikely(!s)) { + /* jumped */ + put_link(nd, &link, cookie); + break; + } else { if (*s == '/') { if (!nd->root.mnt) set_root(nd); @@ -1812,9 +1816,9 @@ static int link_path_walk(const char *name, struct nameidata *nd) put_link(nd, &link, cookie); break; } + err = walk_component(nd, LOOKUP_FOLLOW); + put_link(nd, &link, cookie); } - err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd, &link, cookie); } while (err > 0); current->link_count--; -- cgit v1.2.3-58-ga151 From d40bcc09ab773d9e3cfbc4a45a6e73a4f2d04a8a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:03:03 -0400 Subject: link_path_walk: turn inner loop into explicit goto Signed-off-by: Al Viro --- fs/namei.c | 61 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index ee083f921ef3..6a0dd0782c7d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1775,6 +1775,10 @@ static int link_path_walk(const char *name, struct nameidata *nd) return err; if (err) { + struct path link; + void *cookie; + const char *s; + if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { path_put_conditional(&nd->link, nd); path_put(&nd->path); @@ -1785,41 +1789,40 @@ static int link_path_walk(const char *name, struct nameidata *nd) nd->depth++; current->link_count++; - do { - struct path link = nd->link; - void *cookie; - const char *s = get_link(&link, nd, &cookie); - - if (unlikely(IS_ERR(s))) { - err = PTR_ERR(s); - current->link_count--; - nd->depth--; - return err; +loop: /* will be gone very soon */ + link = nd->link; + s = get_link(&link, nd, &cookie); + + if (unlikely(IS_ERR(s))) { + err = PTR_ERR(s); + current->link_count--; + nd->depth--; + return err; + } + err = 0; + if (unlikely(!s)) { + /* jumped */ + put_link(nd, &link, cookie); + } else { + if (*s == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->flags |= LOOKUP_JUMPED; } - err = 0; - if (unlikely(!s)) { - /* jumped */ + nd->inode = nd->path.dentry->d_inode; + err = link_path_walk(s, nd); + if (unlikely(err)) { put_link(nd, &link, cookie); - break; } else { - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - } - nd->inode = nd->path.dentry->d_inode; - err = link_path_walk(s, nd); - if (unlikely(err)) { - put_link(nd, &link, cookie); - break; - } err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); + if (err > 0) + goto loop; } - } while (err > 0); + } current->link_count--; nd->depth--; -- cgit v1.2.3-58-ga151 From 48c8b0c57176ba081c13abdca8334b9d88c4e4d6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:09:08 -0400 Subject: link_path_walk: massage a bit more Pull the block after the if-else in the end of what used to be do-while body into all branches there. We are almost done with the massage... Signed-off-by: Al Viro --- fs/namei.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6a0dd0782c7d..b7ba71889afb 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1803,6 +1803,8 @@ loop: /* will be gone very soon */ if (unlikely(!s)) { /* jumped */ put_link(nd, &link, cookie); + current->link_count--; + nd->depth--; } else { if (*s == '/') { if (!nd->root.mnt) @@ -1816,18 +1818,23 @@ loop: /* will be gone very soon */ err = link_path_walk(s, nd); if (unlikely(err)) { put_link(nd, &link, cookie); + current->link_count--; + nd->depth--; + return err; } else { err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); - if (err > 0) + current->link_count--; + nd->depth--; + if (err < 0) + return err; + if (err > 0) { + current->link_count++; + nd->depth++; goto loop; + } } } - - current->link_count--; - nd->depth--; - if (err) - return err; } if (!d_can_lookup(nd->path.dentry)) { err = -ENOTDIR; -- cgit v1.2.3-58-ga151 From bb8603f8e166e49f02f865213feee7afddeac9b0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:14:20 -0400 Subject: link_path_walk: get rid of duplication What we do after the second walk_component() + put_link() + depth decrement in there is exactly equivalent to what's done right after the first walk_component(). Easy to verify and not at all surprising, seeing that there we have just walked the last component of nested symlink. Signed-off-by: Al Viro --- fs/namei.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index b7ba71889afb..9f45d339cf45 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1771,6 +1771,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) return 0; err = walk_component(nd, LOOKUP_FOLLOW); +Walked: if (err < 0) return err; @@ -1789,7 +1790,6 @@ static int link_path_walk(const char *name, struct nameidata *nd) nd->depth++; current->link_count++; -loop: /* will be gone very soon */ link = nd->link; s = get_link(&link, nd, &cookie); @@ -1826,13 +1826,7 @@ loop: /* will be gone very soon */ put_link(nd, &link, cookie); current->link_count--; nd->depth--; - if (err < 0) - return err; - if (err > 0) { - current->link_count++; - nd->depth++; - goto loop; - } + goto Walked; } } } -- cgit v1.2.3-58-ga151 From bdf6cbf17923c08dafca70f0231817de1f9d1f30 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:21:40 -0400 Subject: link_path_walk: final preparations to killing recursion reduce the number of returns in there - turn all places where it returns zero into goto OK and places where it returns non-zero into goto Err. The only non-trivial detail is that all breaks in the loop are guaranteed to be with non-zero err. Signed-off-by: Al Viro --- fs/namei.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 9f45d339cf45..b469ce26ff74 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1716,7 +1716,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) while (*name=='/') name++; if (!*name) - return 0; + goto OK; /* At this point we know we have a real path component. */ for(;;) { @@ -1759,7 +1759,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) name += hashlen_len(hash_len); if (!*name) - return 0; + goto OK; /* * If it wasn't NUL, we know it was '/'. Skip that * slash, and continue until no more slashes. @@ -1768,12 +1768,12 @@ static int link_path_walk(const char *name, struct nameidata *nd) name++; } while (unlikely(*name == '/')); if (!*name) - return 0; + goto OK; err = walk_component(nd, LOOKUP_FOLLOW); Walked: if (err < 0) - return err; + goto Err; if (err) { struct path link; @@ -1783,7 +1783,8 @@ Walked: if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { path_put_conditional(&nd->link, nd); path_put(&nd->path); - return -ELOOP; + err = -ELOOP; + goto Err; } BUG_ON(nd->depth >= MAX_NESTED_LINKS); @@ -1797,7 +1798,7 @@ Walked: err = PTR_ERR(s); current->link_count--; nd->depth--; - return err; + goto Err; } err = 0; if (unlikely(!s)) { @@ -1820,7 +1821,7 @@ Walked: put_link(nd, &link, cookie); current->link_count--; nd->depth--; - return err; + goto Err; } else { err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd, &link, cookie); @@ -1836,7 +1837,10 @@ Walked: } } terminate_walk(nd); +Err: return err; +OK: + return 0; } static int path_init(int dfd, const struct filename *name, unsigned int flags, -- cgit v1.2.3-58-ga151 From 32cd74685c75fada80c9444cde150434702aba56 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:30:49 -0400 Subject: link_path_walk: kill the recursion absolutely straightforward now - the only variables we need to preserve across the recursive call are name, link and cookie, and recursion depth is limited (and can is equal to nd->depth). So arrange an array of triples to hold instances of those and be done with that. Signed-off-by: Al Viro --- fs/namei.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index b469ce26ff74..9844bb2f041f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1711,8 +1711,14 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { + struct saved { + struct path link; + void *cookie; + const char *name; + } stack[MAX_NESTED_LINKS], *last = stack + nd->depth - 1; int err; - + +start: while (*name=='/') name++; if (!*name) @@ -1776,8 +1782,6 @@ Walked: goto Err; if (err) { - struct path link; - void *cookie; const char *s; if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { @@ -1790,22 +1794,25 @@ Walked: nd->depth++; current->link_count++; + last++; - link = nd->link; - s = get_link(&link, nd, &cookie); + last->link = nd->link; + s = get_link(&last->link, nd, &last->cookie); if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); current->link_count--; nd->depth--; + last--; goto Err; } err = 0; if (unlikely(!s)) { /* jumped */ - put_link(nd, &link, cookie); + put_link(nd, &last->link, last->cookie); current->link_count--; nd->depth--; + last--; } else { if (*s == '/') { if (!nd->root.mnt) @@ -1816,17 +1823,24 @@ Walked: nd->flags |= LOOKUP_JUMPED; } nd->inode = nd->path.dentry->d_inode; - err = link_path_walk(s, nd); + last->name = name; + name = s; + goto start; + +back: + name = last->name; if (unlikely(err)) { - put_link(nd, &link, cookie); + put_link(nd, &last->link, last->cookie); current->link_count--; nd->depth--; + last--; goto Err; } else { err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd, &link, cookie); + put_link(nd, &last->link, last->cookie); current->link_count--; nd->depth--; + last--; goto Walked; } } @@ -1838,9 +1852,13 @@ Walked: } terminate_walk(nd); Err: - return err; + if (likely(!nd->depth)) + return err; + goto back; OK: - return 0; + if (likely(!nd->depth)) + return 0; + goto back; } static int path_init(int dfd, const struct filename *name, unsigned int flags, -- cgit v1.2.3-58-ga151 From 07681481b865b5dc100f71cc82facaeaa9e69e86 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:40:04 -0400 Subject: link_path_walk: split "return from recursive call" path Signed-off-by: Al Viro --- fs/namei.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 9844bb2f041f..5b0edd331338 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1826,23 +1826,6 @@ Walked: last->name = name; name = s; goto start; - -back: - name = last->name; - if (unlikely(err)) { - put_link(nd, &last->link, last->cookie); - current->link_count--; - nd->depth--; - last--; - goto Err; - } else { - err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd, &last->link, last->cookie); - current->link_count--; - nd->depth--; - last--; - goto Walked; - } } } if (!d_can_lookup(nd->path.dentry)) { @@ -1852,13 +1835,24 @@ back: } terminate_walk(nd); Err: - if (likely(!nd->depth)) - return err; - goto back; + while (unlikely(nd->depth)) { + put_link(nd, &last->link, last->cookie); + current->link_count--; + nd->depth--; + last--; + } + return err; OK: - if (likely(!nd->depth)) - return 0; - goto back; + if (unlikely(nd->depth)) { + name = last->name; + err = walk_component(nd, LOOKUP_FOLLOW); + put_link(nd, &last->link, last->cookie); + current->link_count--; + nd->depth--; + last--; + goto Walked; + } + return 0; } static int path_init(int dfd, const struct filename *name, unsigned int flags, -- cgit v1.2.3-58-ga151 From 9e18f10a30e0c49520fbda39751fcae760445b96 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Apr 2015 20:44:34 -0400 Subject: link_path_walk: cleanup - turn goto start; into continue; Deal with skipping leading slashes before what used to be the recursive call. That way we can get rid of that goto completely. Signed-off-by: Al Viro --- fs/namei.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 5b0edd331338..c105107ddf8f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1718,11 +1718,10 @@ static int link_path_walk(const char *name, struct nameidata *nd) } stack[MAX_NESTED_LINKS], *last = stack + nd->depth - 1; int err; -start: while (*name=='/') name++; if (!*name) - goto OK; + return 0; /* At this point we know we have a real path component. */ for(;;) { @@ -1821,11 +1820,15 @@ Walked: nd->path = nd->root; path_get(&nd->root); nd->flags |= LOOKUP_JUMPED; + while (unlikely(*++s == '/')) + ; } nd->inode = nd->path.dentry->d_inode; last->name = name; + if (!*s) + goto OK; name = s; - goto start; + continue; } } if (!d_can_lookup(nd->path.dentry)) { -- cgit v1.2.3-58-ga151 From 697fc6ca666cdb2638211229c8fa3b81eb6f2f1a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 19:38:35 -0400 Subject: namei: move link/cookie pairs into nameidata Array of MAX_NESTED_LINKS + 1 elements put into nameidata; what used to be a local array in link_path_walk() occupies entries 1 .. MAX_NESTED_LINKS in it, link and cookie from the trailing symlink handling loops - entry 0. This is _not_ the final arrangement; just an easily verified incremental step. Signed-off-by: Al Viro --- fs/namei.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c105107ddf8f..d4b238a58739 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -505,6 +505,11 @@ struct nameidata { int last_type; unsigned depth; struct file *base; + struct saved { + struct path link; + void *cookie; + const char *name; + } stack[MAX_NESTED_LINKS + 1]; }; /* @@ -1711,11 +1716,7 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { - struct saved { - struct path link; - void *cookie; - const char *name; - } stack[MAX_NESTED_LINKS], *last = stack + nd->depth - 1; + struct saved *last = nd->stack; int err; while (*name=='/') @@ -2030,13 +2031,13 @@ static int path_lookupat(int dfd, const struct filename *name, if (!err && !(flags & LOOKUP_PARENT)) { err = lookup_last(nd); while (err > 0) { - void *cookie; - struct path link = nd->link; - err = trailing_symlink(&link, nd, &cookie); + nd->stack[0].link = nd->link; + err = trailing_symlink(&nd->stack[0].link, + nd, &nd->stack[0].cookie); if (err) break; err = lookup_last(nd); - put_link(nd, &link, cookie); + put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); } } @@ -2376,13 +2377,13 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, err = mountpoint_last(nd, path); while (err > 0) { - void *cookie; - struct path link = *path; - err = trailing_symlink(&link, nd, &cookie); + nd->stack[0].link = nd->link; + err = trailing_symlink(&nd->stack[0].link, + nd, &nd->stack[0].cookie); if (err) break; err = mountpoint_last(nd, path); - put_link(nd, &link, cookie); + put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); } out: path_cleanup(nd); @@ -3259,14 +3260,14 @@ static struct file *path_openat(int dfd, struct filename *pathname, error = do_last(nd, file, op, &opened, pathname); while (unlikely(error > 0)) { /* trailing symlink */ - struct path link = nd->link; - void *cookie; nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); - error = trailing_symlink(&link, nd, &cookie); + nd->stack[0].link = nd->link; + error= trailing_symlink(&nd->stack[0].link, + nd, &nd->stack[0].cookie); if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); - put_link(nd, &link, cookie); + put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); } out: path_cleanup(nd); -- cgit v1.2.3-58-ga151 From 1d8e03d359fd07edc884ffc45d42d60be9d9f098 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 19:48:56 -0400 Subject: namei: trim redundant arguments of trailing_symlink() Signed-off-by: Al Viro --- fs/namei.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index d4b238a58739..8c4f2af3a71a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1971,14 +1971,15 @@ static void path_cleanup(struct nameidata *nd) fput(nd->base); } -static int trailing_symlink(struct path *link, struct nameidata *nd, void **p) +static int trailing_symlink(struct nameidata *nd) { const char *s; - int error = may_follow_link(link, nd); + int error = may_follow_link(&nd->link, nd); if (unlikely(error)) return error; nd->flags |= LOOKUP_PARENT; - s = get_link(link, nd, p); + nd->stack[0].link = nd->link; + s = get_link(&nd->stack[0].link, nd, &nd->stack[0].cookie); if (unlikely(IS_ERR(s))) return PTR_ERR(s); if (unlikely(!s)) @@ -1994,7 +1995,7 @@ static int trailing_symlink(struct path *link, struct nameidata *nd, void **p) nd->inode = nd->path.dentry->d_inode; error = link_path_walk(s, nd); if (unlikely(error)) - put_link(nd, link, *p); + put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); return error; } @@ -2031,9 +2032,7 @@ static int path_lookupat(int dfd, const struct filename *name, if (!err && !(flags & LOOKUP_PARENT)) { err = lookup_last(nd); while (err > 0) { - nd->stack[0].link = nd->link; - err = trailing_symlink(&nd->stack[0].link, - nd, &nd->stack[0].cookie); + err = trailing_symlink(nd); if (err) break; err = lookup_last(nd); @@ -2377,9 +2376,7 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, err = mountpoint_last(nd, path); while (err > 0) { - nd->stack[0].link = nd->link; - err = trailing_symlink(&nd->stack[0].link, - nd, &nd->stack[0].cookie); + err = trailing_symlink(nd); if (err) break; err = mountpoint_last(nd, path); @@ -3261,9 +3258,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, error = do_last(nd, file, op, &opened, pathname); while (unlikely(error > 0)) { /* trailing symlink */ nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); - nd->stack[0].link = nd->link; - error= trailing_symlink(&nd->stack[0].link, - nd, &nd->stack[0].cookie); + error = trailing_symlink(nd); if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); -- cgit v1.2.3-58-ga151 From b9ff44293c64bf377e344c4cdc05d774b393cc6f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 20:19:23 -0400 Subject: namei: trim redundant arguments of fs/namei.c:put_link() Signed-off-by: Al Viro --- fs/namei.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 8c4f2af3a71a..1ac32178bd45 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -718,12 +718,13 @@ void nd_jump_link(struct nameidata *nd, struct path *path) nd->flags |= LOOKUP_JUMPED; } -static inline void put_link(struct nameidata *nd, struct path *link, void *cookie) +static inline void put_link(struct nameidata *nd) { - struct inode *inode = link->dentry->d_inode; - if (cookie && inode->i_op->put_link) - inode->i_op->put_link(link->dentry, cookie); - path_put(link); + struct saved *last = nd->stack + nd->depth; + struct inode *inode = last->link.dentry->d_inode; + if (last->cookie && inode->i_op->put_link) + inode->i_op->put_link(last->link.dentry, last->cookie); + path_put(&last->link); } int sysctl_protected_symlinks __read_mostly = 0; @@ -1809,7 +1810,7 @@ Walked: err = 0; if (unlikely(!s)) { /* jumped */ - put_link(nd, &last->link, last->cookie); + put_link(nd); current->link_count--; nd->depth--; last--; @@ -1840,7 +1841,7 @@ Walked: terminate_walk(nd); Err: while (unlikely(nd->depth)) { - put_link(nd, &last->link, last->cookie); + put_link(nd); current->link_count--; nd->depth--; last--; @@ -1850,7 +1851,7 @@ OK: if (unlikely(nd->depth)) { name = last->name; err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd, &last->link, last->cookie); + put_link(nd); current->link_count--; nd->depth--; last--; @@ -1995,7 +1996,7 @@ static int trailing_symlink(struct nameidata *nd) nd->inode = nd->path.dentry->d_inode; error = link_path_walk(s, nd); if (unlikely(error)) - put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); + put_link(nd); return error; } @@ -2036,7 +2037,7 @@ static int path_lookupat(int dfd, const struct filename *name, if (err) break; err = lookup_last(nd); - put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); + put_link(nd); } } @@ -2380,7 +2381,7 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, if (err) break; err = mountpoint_last(nd, path); - put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); + put_link(nd); } out: path_cleanup(nd); @@ -3262,7 +3263,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); - put_link(nd, &nd->stack[0].link, nd->stack[0].cookie); + put_link(nd); } out: path_cleanup(nd); -- cgit v1.2.3-58-ga151 From 3b2e7f7539bdf5650bf5e13f18c883b7b008d03b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Apr 2015 00:53:50 -0400 Subject: namei: trim the arguments of get_link() same story as the previous commit Signed-off-by: Al Viro --- fs/namei.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1ac32178bd45..e5715a567860 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -843,27 +843,33 @@ static int may_linkat(struct path *link) return -EPERM; } -static __always_inline const char * -get_link(struct path *link, struct nameidata *nd, void **p) +static __always_inline +const char *get_link(struct nameidata *nd) { - struct dentry *dentry = link->dentry; + struct saved *last = nd->stack + nd->depth; + struct dentry *dentry = nd->link.dentry; struct inode *inode = dentry->d_inode; int error; const char *res; BUG_ON(nd->flags & LOOKUP_RCU); - if (link->mnt == nd->path.mnt) - mntget(link->mnt); + if (nd->link.mnt == nd->path.mnt) + mntget(nd->link.mnt); - res = ERR_PTR(-ELOOP); - if (unlikely(current->total_link_count >= 40)) - goto out; + if (unlikely(current->total_link_count >= 40)) { + path_put(&nd->path); + path_put(&nd->link); + return ERR_PTR(-ELOOP); + } + + last->link = nd->link; + last->cookie = NULL; cond_resched(); current->total_link_count++; - touch_atime(link); + touch_atime(&last->link); error = security_inode_follow_link(dentry); res = ERR_PTR(error); @@ -871,14 +877,13 @@ get_link(struct path *link, struct nameidata *nd, void **p) goto out; nd->last_type = LAST_BIND; - *p = NULL; res = inode->i_link; if (!res) { - res = inode->i_op->follow_link(dentry, p, nd); + res = inode->i_op->follow_link(dentry, &last->cookie, nd); if (IS_ERR(res)) { out: path_put(&nd->path); - path_put(link); + path_put(&last->link); } } return res; @@ -1717,7 +1722,6 @@ static inline u64 hash_name(const char *name) */ static int link_path_walk(const char *name, struct nameidata *nd) { - struct saved *last = nd->stack; int err; while (*name=='/') @@ -1795,16 +1799,13 @@ Walked: nd->depth++; current->link_count++; - last++; - last->link = nd->link; - s = get_link(&last->link, nd, &last->cookie); + s = get_link(nd); if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); current->link_count--; nd->depth--; - last--; goto Err; } err = 0; @@ -1813,7 +1814,6 @@ Walked: put_link(nd); current->link_count--; nd->depth--; - last--; } else { if (*s == '/') { if (!nd->root.mnt) @@ -1826,7 +1826,7 @@ Walked: ; } nd->inode = nd->path.dentry->d_inode; - last->name = name; + nd->stack[nd->depth].name = name; if (!*s) goto OK; name = s; @@ -1844,17 +1844,15 @@ Err: put_link(nd); current->link_count--; nd->depth--; - last--; } return err; OK: if (unlikely(nd->depth)) { - name = last->name; + name = nd->stack[nd->depth].name; err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd); current->link_count--; nd->depth--; - last--; goto Walked; } return 0; @@ -1979,8 +1977,7 @@ static int trailing_symlink(struct nameidata *nd) if (unlikely(error)) return error; nd->flags |= LOOKUP_PARENT; - nd->stack[0].link = nd->link; - s = get_link(&nd->stack[0].link, nd, &nd->stack[0].cookie); + s = get_link(nd); if (unlikely(IS_ERR(s))) return PTR_ERR(s); if (unlikely(!s)) -- cgit v1.2.3-58-ga151 From 894bc8c4662ba9daceafe943a5ba0dd407da5cd3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 07:16:16 -0400 Subject: namei: remove restrictions on nesting depth The only restriction is that on the total amount of symlinks crossed; how they are nested does not matter Signed-off-by: Al Viro --- fs/namei.c | 66 ++++++++++++++++++++++++++++++++++++++++----------- include/linux/namei.h | 2 ++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e5715a567860..1ae34cd0d590 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -492,6 +492,7 @@ void path_put(const struct path *path) } EXPORT_SYMBOL(path_put); +#define EMBEDDED_LEVELS 2 struct nameidata { struct path path; union { @@ -509,9 +510,42 @@ struct nameidata { struct path link; void *cookie; const char *name; - } stack[MAX_NESTED_LINKS + 1]; + } *stack, internal[EMBEDDED_LEVELS]; }; +static void set_nameidata(struct nameidata *nd) +{ + nd->stack = nd->internal; +} + +static void restore_nameidata(struct nameidata *nd) +{ + if (nd->stack != nd->internal) { + kfree(nd->stack); + nd->stack = nd->internal; + } +} + +static int __nd_alloc_stack(struct nameidata *nd) +{ + struct saved *p = kmalloc((MAXSYMLINKS + 1) * sizeof(struct saved), + GFP_KERNEL); + if (unlikely(!p)) + return -ENOMEM; + memcpy(p, nd->internal, sizeof(nd->internal)); + nd->stack = p; + return 0; +} + +static inline int nd_alloc_stack(struct nameidata *nd) +{ + if (likely(nd->depth != EMBEDDED_LEVELS - 1)) + return 0; + if (likely(nd->stack != nd->internal)) + return 0; + return __nd_alloc_stack(nd); +} + /* * Path walking has 2 modes, rcu-walk and ref-walk (see * Documentation/filesystems/path-lookup.txt). In situations when we can't @@ -857,7 +891,7 @@ const char *get_link(struct nameidata *nd) if (nd->link.mnt == nd->path.mnt) mntget(nd->link.mnt); - if (unlikely(current->total_link_count >= 40)) { + if (unlikely(current->total_link_count >= MAXSYMLINKS)) { path_put(&nd->path); path_put(&nd->link); return ERR_PTR(-ELOOP); @@ -1789,22 +1823,18 @@ Walked: if (err) { const char *s; - if (unlikely(current->link_count >= MAX_NESTED_LINKS)) { - path_put_conditional(&nd->link, nd); - path_put(&nd->path); - err = -ELOOP; - goto Err; + err = nd_alloc_stack(nd); + if (unlikely(err)) { + path_to_nameidata(&nd->link, nd); + break; } - BUG_ON(nd->depth >= MAX_NESTED_LINKS); nd->depth++; - current->link_count++; s = get_link(nd); if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); - current->link_count--; nd->depth--; goto Err; } @@ -1812,7 +1842,6 @@ Walked: if (unlikely(!s)) { /* jumped */ put_link(nd); - current->link_count--; nd->depth--; } else { if (*s == '/') { @@ -1842,7 +1871,6 @@ Walked: Err: while (unlikely(nd->depth)) { put_link(nd); - current->link_count--; nd->depth--; } return err; @@ -1851,7 +1879,6 @@ OK: name = nd->stack[nd->depth].name; err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd); - current->link_count--; nd->depth--; goto Walked; } @@ -2055,7 +2082,11 @@ static int path_lookupat(int dfd, const struct filename *name, static int filename_lookup(int dfd, struct filename *name, unsigned int flags, struct nameidata *nd) { - int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); + int retval; + + set_nameidata(nd); + retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); + if (unlikely(retval == -ECHILD)) retval = path_lookupat(dfd, name, flags, nd); if (unlikely(retval == -ESTALE)) @@ -2063,6 +2094,7 @@ static int filename_lookup(int dfd, struct filename *name, if (likely(!retval)) audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); + restore_nameidata(nd); return retval; } @@ -2393,6 +2425,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path, int error; if (IS_ERR(name)) return PTR_ERR(name); + set_nameidata(&nd); error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_RCU); if (unlikely(error == -ECHILD)) error = path_mountpoint(dfd, name, path, &nd, flags); @@ -2400,6 +2433,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path, error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_REVAL); if (likely(!error)) audit_inode(name, path->dentry, 0); + restore_nameidata(&nd); putname(name); return error; } @@ -3288,11 +3322,13 @@ struct file *do_filp_open(int dfd, struct filename *pathname, int flags = op->lookup_flags; struct file *filp; + set_nameidata(&nd); filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) filp = path_openat(dfd, pathname, &nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL); + restore_nameidata(&nd); return filp; } @@ -3306,6 +3342,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, nd.root.mnt = mnt; nd.root.dentry = dentry; + set_nameidata(&nd); if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); @@ -3319,6 +3356,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, file = path_openat(-1, filename, &nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL); + restore_nameidata(&nd); putname(filename); return file; } diff --git a/include/linux/namei.h b/include/linux/namei.h index a5d5bed2c0e1..3a6cc9651712 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -11,6 +11,8 @@ struct nameidata; enum { MAX_NESTED_LINKS = 8 }; +#define MAXSYMLINKS 40 + /* * Type of the last component on LOOKUP_PARENT */ -- cgit v1.2.3-58-ga151 From 071bf501379560a7631b8d75ac80165bf82735c4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 20:01:27 -0400 Subject: link_path_walk: nd->depth massage, part 1 nd->stack[0] is unused until the handling of trailing symlinks and we want to get rid of that. Having fucked that transformation up several times, I went for bloody pedantic series of provably equivalent transformations. Sorry. Step 1: keep nd->depth higher by one in link_path_walk() - increment upon entry, decrement on exits, adjust the arithmetics inside and surround the calls of functions that care about nd->depth value (nd_alloc_stack(), get_link(), put_link()) with decrement/increment pairs. Signed-off-by: Al Viro --- fs/namei.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1ae34cd0d590..e408f4d0c85f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1763,6 +1763,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (!*name) return 0; + nd->depth++; /* At this point we know we have a real path component. */ for(;;) { u64 hash_len; @@ -1823,15 +1824,18 @@ Walked: if (err) { const char *s; + nd->depth--; err = nd_alloc_stack(nd); + nd->depth++; if (unlikely(err)) { path_to_nameidata(&nd->link, nd); break; } nd->depth++; - + nd->depth--; s = get_link(nd); + nd->depth++; if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); @@ -1841,7 +1845,9 @@ Walked: err = 0; if (unlikely(!s)) { /* jumped */ + nd->depth--; put_link(nd); + nd->depth++; nd->depth--; } else { if (*s == '/') { @@ -1855,7 +1861,7 @@ Walked: ; } nd->inode = nd->path.dentry->d_inode; - nd->stack[nd->depth].name = name; + nd->stack[nd->depth - 1].name = name; if (!*s) goto OK; name = s; @@ -1869,19 +1875,25 @@ Walked: } terminate_walk(nd); Err: - while (unlikely(nd->depth)) { + while (unlikely(nd->depth > 1)) { + nd->depth--; put_link(nd); + nd->depth++; nd->depth--; } + nd->depth--; return err; OK: - if (unlikely(nd->depth)) { - name = nd->stack[nd->depth].name; + if (unlikely(nd->depth > 1)) { + name = nd->stack[nd->depth - 1].name; err = walk_component(nd, LOOKUP_FOLLOW); + nd->depth--; put_link(nd); + nd->depth++; nd->depth--; goto Walked; } + nd->depth--; return 0; } -- cgit v1.2.3-58-ga151 From fd4620bbdf618998fd1a7df19e3443a698357e2b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 20:46:54 -0400 Subject: link_path_walk: nd->depth massage, part 2 collapse adjacent increment/decrement pairs. Signed-off-by: Al Viro --- fs/namei.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e408f4d0c85f..a4034255d827 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1832,8 +1832,6 @@ Walked: break; } - nd->depth++; - nd->depth--; s = get_link(nd); nd->depth++; @@ -1847,8 +1845,6 @@ Walked: /* jumped */ nd->depth--; put_link(nd); - nd->depth++; - nd->depth--; } else { if (*s == '/') { if (!nd->root.mnt) @@ -1878,8 +1874,6 @@ Err: while (unlikely(nd->depth > 1)) { nd->depth--; put_link(nd); - nd->depth++; - nd->depth--; } nd->depth--; return err; @@ -1889,8 +1883,6 @@ OK: err = walk_component(nd, LOOKUP_FOLLOW); nd->depth--; put_link(nd); - nd->depth++; - nd->depth--; goto Walked; } nd->depth--; -- cgit v1.2.3-58-ga151 From da4e0be04d8816bbb9420c1da05ce09f502c5b75 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 20:52:15 -0400 Subject: link_path_walk: nd->depth massage, part 3 remove decrement/increment surrounding nd_alloc_stack(), adjust the test in it. Signed-off-by: Al Viro --- fs/namei.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a4034255d827..3df4731a7467 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -539,7 +539,7 @@ static int __nd_alloc_stack(struct nameidata *nd) static inline int nd_alloc_stack(struct nameidata *nd) { - if (likely(nd->depth != EMBEDDED_LEVELS - 1)) + if (likely(nd->depth != EMBEDDED_LEVELS)) return 0; if (likely(nd->stack != nd->internal)) return 0; @@ -1824,9 +1824,7 @@ Walked: if (err) { const char *s; - nd->depth--; err = nd_alloc_stack(nd); - nd->depth++; if (unlikely(err)) { path_to_nameidata(&nd->link, nd); break; -- cgit v1.2.3-58-ga151 From ef1a3e7b9634cc68740eb1280309ac5e460b95f9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 20:54:27 -0400 Subject: link_path_walk: nd->depth massage, part 4 lift increment/decrement into link_path_walk() callers. Signed-off-by: Al Viro --- fs/namei.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 3df4731a7467..cf5009b87c5a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1763,7 +1763,6 @@ static int link_path_walk(const char *name, struct nameidata *nd) if (!*name) return 0; - nd->depth++; /* At this point we know we have a real path component. */ for(;;) { u64 hash_len; @@ -1873,7 +1872,6 @@ Err: nd->depth--; put_link(nd); } - nd->depth--; return err; OK: if (unlikely(nd->depth > 1)) { @@ -1883,7 +1881,6 @@ OK: put_link(nd); goto Walked; } - nd->depth--; return 0; } @@ -1986,7 +1983,10 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, return -ECHILD; done: current->total_link_count = 0; - return link_path_walk(s, nd); + nd->depth++; + retval = link_path_walk(s, nd); + nd->depth--; + return retval; } static void path_cleanup(struct nameidata *nd) @@ -2020,7 +2020,9 @@ static int trailing_symlink(struct nameidata *nd) nd->flags |= LOOKUP_JUMPED; } nd->inode = nd->path.dentry->d_inode; + nd->depth++; error = link_path_walk(s, nd); + nd->depth--; if (unlikely(error)) put_link(nd); return error; -- cgit v1.2.3-58-ga151 From f7df08ee05db41c6d2ec498703401197f6b68373 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 20:59:58 -0400 Subject: trailing_symlink: nd->depth massage, part 5 move increment of ->depth to the point where we'd discovered that get_link() has not returned an error, adjust exits accordingly. Signed-off-by: Al Viro --- fs/namei.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index cf5009b87c5a..5753f46d8321 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2009,8 +2009,11 @@ static int trailing_symlink(struct nameidata *nd) s = get_link(nd); if (unlikely(IS_ERR(s))) return PTR_ERR(s); - if (unlikely(!s)) + nd->depth++; + if (unlikely(!s)) { + nd->depth--; return 0; + } if (*s == '/') { if (!nd->root.mnt) set_root(nd); @@ -2020,12 +2023,14 @@ static int trailing_symlink(struct nameidata *nd) nd->flags |= LOOKUP_JUMPED; } nd->inode = nd->path.dentry->d_inode; - nd->depth++; error = link_path_walk(s, nd); - nd->depth--; - if (unlikely(error)) + if (unlikely(error)) { + nd->depth--; put_link(nd); - return error; + return error; + } + nd->depth--; + return 0; } static inline int lookup_last(struct nameidata *nd) -- cgit v1.2.3-58-ga151 From 0fd889d59e12f860358866b6ddeee4bb7011aaad Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:02:40 -0400 Subject: get_link: nd->depth massage, part 6 make get_link() increment nd->depth on successful exit Signed-off-by: Al Viro --- fs/namei.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 5753f46d8321..93b5f7353e70 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -918,8 +918,10 @@ const char *get_link(struct nameidata *nd) out: path_put(&nd->path); path_put(&last->link); + return res; } } + nd->depth++; return res; } @@ -1830,11 +1832,9 @@ Walked: } s = get_link(nd); - nd->depth++; if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); - nd->depth--; goto Err; } err = 0; @@ -2009,7 +2009,6 @@ static int trailing_symlink(struct nameidata *nd) s = get_link(nd); if (unlikely(IS_ERR(s))) return PTR_ERR(s); - nd->depth++; if (unlikely(!s)) { nd->depth--; return 0; -- cgit v1.2.3-58-ga151 From 9ea57b72bf4f8b750e939769ae0aeaa41394ac01 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:04:07 -0400 Subject: trailing_symlink: nd->depth massage, part 7 move decrement of nd->depth on successful returns into the callers. Signed-off-by: Al Viro --- fs/namei.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 93b5f7353e70..9df1c7a48e7a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2009,10 +2009,8 @@ static int trailing_symlink(struct nameidata *nd) s = get_link(nd); if (unlikely(IS_ERR(s))) return PTR_ERR(s); - if (unlikely(!s)) { - nd->depth--; + if (unlikely(!s)) return 0; - } if (*s == '/') { if (!nd->root.mnt) set_root(nd); @@ -2028,7 +2026,6 @@ static int trailing_symlink(struct nameidata *nd) put_link(nd); return error; } - nd->depth--; return 0; } @@ -2069,6 +2066,7 @@ static int path_lookupat(int dfd, const struct filename *name, if (err) break; err = lookup_last(nd); + nd->depth--; put_link(nd); } } @@ -2418,6 +2416,7 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, if (err) break; err = mountpoint_last(nd, path); + nd->depth--; put_link(nd); } out: @@ -3302,6 +3301,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); + nd->depth--; put_link(nd); } out: -- cgit v1.2.3-58-ga151 From 21c3003d36a8b6c54811bd87eb5f6fc830de9c82 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:06:24 -0400 Subject: put_link: nd->depth massage, part 8 all calls are preceded by decrement of nd->depth; move it into put_link() itself. Signed-off-by: Al Viro --- fs/namei.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 9df1c7a48e7a..1f6195535206 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -754,7 +754,7 @@ void nd_jump_link(struct nameidata *nd, struct path *path) static inline void put_link(struct nameidata *nd) { - struct saved *last = nd->stack + nd->depth; + struct saved *last = nd->stack + --nd->depth; struct inode *inode = last->link.dentry->d_inode; if (last->cookie && inode->i_op->put_link) inode->i_op->put_link(last->link.dentry, last->cookie); @@ -1840,7 +1840,6 @@ Walked: err = 0; if (unlikely(!s)) { /* jumped */ - nd->depth--; put_link(nd); } else { if (*s == '/') { @@ -1868,16 +1867,13 @@ Walked: } terminate_walk(nd); Err: - while (unlikely(nd->depth > 1)) { - nd->depth--; + while (unlikely(nd->depth > 1)) put_link(nd); - } return err; OK: if (unlikely(nd->depth > 1)) { name = nd->stack[nd->depth - 1].name; err = walk_component(nd, LOOKUP_FOLLOW); - nd->depth--; put_link(nd); goto Walked; } @@ -2021,12 +2017,9 @@ static int trailing_symlink(struct nameidata *nd) } nd->inode = nd->path.dentry->d_inode; error = link_path_walk(s, nd); - if (unlikely(error)) { - nd->depth--; + if (unlikely(error)) put_link(nd); - return error; - } - return 0; + return error; } static inline int lookup_last(struct nameidata *nd) @@ -2066,7 +2059,6 @@ static int path_lookupat(int dfd, const struct filename *name, if (err) break; err = lookup_last(nd); - nd->depth--; put_link(nd); } } @@ -2416,7 +2408,6 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, if (err) break; err = mountpoint_last(nd, path); - nd->depth--; put_link(nd); } out: @@ -3301,7 +3292,6 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(error)) break; error = do_last(nd, file, op, &opened, pathname); - nd->depth--; put_link(nd); } out: -- cgit v1.2.3-58-ga151 From dc7af8dc05bc46410b978dae14fda8414b65db30 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:16:35 -0400 Subject: link_path_walk: nd->depth massage, part 9 Make link_path_walk() work with any value of nd->depth on entry - memorize it and use it in tests instead of comparing with 1. Don't bother with increment/decrement in path_init(). Signed-off-by: Al Viro --- fs/namei.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1f6195535206..bc6d67e1b00e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1759,6 +1759,7 @@ static inline u64 hash_name(const char *name) static int link_path_walk(const char *name, struct nameidata *nd) { int err; + int orig_depth = nd->depth; while (*name=='/') name++; @@ -1867,11 +1868,11 @@ Walked: } terminate_walk(nd); Err: - while (unlikely(nd->depth > 1)) + while (unlikely(nd->depth > orig_depth)) put_link(nd); return err; OK: - if (unlikely(nd->depth > 1)) { + if (unlikely(nd->depth > orig_depth)) { name = nd->stack[nd->depth - 1].name; err = walk_component(nd, LOOKUP_FOLLOW); put_link(nd); @@ -1979,10 +1980,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, return -ECHILD; done: current->total_link_count = 0; - nd->depth++; - retval = link_path_walk(s, nd); - nd->depth--; - return retval; + return link_path_walk(s, nd); } static void path_cleanup(struct nameidata *nd) -- cgit v1.2.3-58-ga151 From 939724df56db970bdbaf8abcdc975c51482aba9b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:21:44 -0400 Subject: link_path_walk: nd->depth massage, part 10 Get rid of orig_depth checks in OK: logics. If nd->depth is zero, we had been called from path_init() and we are done. If it is greater than 1, we are not done, whether we'd been called from path_init() or trailing_symlink(). And in case when it's 1, we might have been called from path_init() and reached the end of nested symlink (in which case nd->stack[0].name will point to the rest of pathname and we are not done) or from trailing_symlink(), in which case we are done. Just have trailing_symlink() leave NULL in nd->stack[0].name and use that to discriminate between those cases. Signed-off-by: Al Viro --- fs/namei.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bc6d67e1b00e..6febe251b242 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1872,13 +1872,15 @@ Err: put_link(nd); return err; OK: - if (unlikely(nd->depth > orig_depth)) { - name = nd->stack[nd->depth - 1].name; - err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd); - goto Walked; - } - return 0; + if (!nd->depth) /* called from path_init(), done */ + return 0; + name = nd->stack[nd->depth - 1].name; + if (!name) /* called from trailing_symlink(), done */ + return 0; + + err = walk_component(nd, LOOKUP_FOLLOW); + put_link(nd); + goto Walked; } static int path_init(int dfd, const struct filename *name, unsigned int flags, @@ -2014,6 +2016,7 @@ static int trailing_symlink(struct nameidata *nd) nd->flags |= LOOKUP_JUMPED; } nd->inode = nd->path.dentry->d_inode; + nd->stack[0].name = NULL; error = link_path_walk(s, nd); if (unlikely(error)) put_link(nd); -- cgit v1.2.3-58-ga151 From 8eff733a45c98f17f254a313859f3b3ed9fc12dc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:27:36 -0400 Subject: link_path_walk: end of nd->depth massage get rid of orig_depth - we only use it on error exit to tell whether to stop doing put_link() when depth reaches 0 (call from path_init()) or when it reaches 1 (call from trailing_symlink()). However, in the latter case the caller would immediately follow with one more put_link(). Just keep doing it until the depth reaches zero (and simplify trailing_symlink() as the result). Signed-off-by: Al Viro --- fs/namei.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6febe251b242..d12b16c13f0c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1759,7 +1759,6 @@ static inline u64 hash_name(const char *name) static int link_path_walk(const char *name, struct nameidata *nd) { int err; - int orig_depth = nd->depth; while (*name=='/') name++; @@ -1868,7 +1867,7 @@ Walked: } terminate_walk(nd); Err: - while (unlikely(nd->depth > orig_depth)) + while (unlikely(nd->depth)) put_link(nd); return err; OK: @@ -2017,10 +2016,7 @@ static int trailing_symlink(struct nameidata *nd) } nd->inode = nd->path.dentry->d_inode; nd->stack[0].name = NULL; - error = link_path_walk(s, nd); - if (unlikely(error)) - put_link(nd); - return error; + return link_path_walk(s, nd); } static inline int lookup_last(struct nameidata *nd) -- cgit v1.2.3-58-ga151 From e269f2a73f92b40169c4229289587b901a00b244 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 May 2015 21:30:27 -0400 Subject: namei: we never need more than MAXSYMLINKS entries in nd->stack The only reason why we needed one more was that purely nested MAXSYMLINKS symlinks could lead to path_init() using that many entries in addition to nd->stack[0] which it left unused. That can't happen now - path_init() starts with entry 0 (and trailing_symlink() is called only when we'd already encountered one symlink, so no more than MAXSYMLINKS-1 are left). Signed-off-by: Al Viro --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index d12b16c13f0c..b939f488bc6f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -528,7 +528,7 @@ static void restore_nameidata(struct nameidata *nd) static int __nd_alloc_stack(struct nameidata *nd) { - struct saved *p = kmalloc((MAXSYMLINKS + 1) * sizeof(struct saved), + struct saved *p = kmalloc(MAXSYMLINKS * sizeof(struct saved), GFP_KERNEL); if (unlikely(!p)) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 70291aecc6aa228c1b3bb36a5f3efdb0af636042 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 07:53:00 -0400 Subject: namei: lift (open-coded) terminate_walk() in follow_dotdot_rcu() into callers follow_dotdot_rcu() does an equivalent of terminate_walk() on failure; shifting it into callers makes for simpler rules and those callers already have terminate_walk() on other failure exits. Signed-off-by: Al Viro --- fs/namei.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index b939f488bc6f..25cd93599ada 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1230,10 +1230,6 @@ static int follow_dotdot_rcu(struct nameidata *nd) return 0; failed: - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - rcu_read_unlock(); return -ECHILD; } @@ -1551,8 +1547,7 @@ static inline int handle_dots(struct nameidata *nd, int type) { if (type == LAST_DOTDOT) { if (nd->flags & LOOKUP_RCU) { - if (follow_dotdot_rcu(nd)) - return -ECHILD; + return follow_dotdot_rcu(nd); } else follow_dotdot(nd); } @@ -1592,8 +1587,12 @@ static int walk_component(struct nameidata *nd, int follow) * to be able to know about the current root directory and * parent relationships. */ - if (unlikely(nd->last_type != LAST_NORM)) - return handle_dots(nd, nd->last_type); + if (unlikely(nd->last_type != LAST_NORM)) { + err = handle_dots(nd, nd->last_type); + if (err) + goto out_err; + return 0; + } err = lookup_fast(nd, &path, &inode); if (unlikely(err)) { if (err < 0) @@ -2981,8 +2980,10 @@ static int do_last(struct nameidata *nd, if (nd->last_type != LAST_NORM) { error = handle_dots(nd, nd->last_type); - if (error) + if (unlikely(error)) { + terminate_walk(nd); return error; + } goto finish_open; } -- cgit v1.2.3-58-ga151 From f0a9ba7021ce286120b69926a17ba55c11707fae Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 07:59:30 -0400 Subject: lift terminate_walk() into callers of walk_component() Signed-off-by: Al Viro --- fs/namei.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 25cd93599ada..d99eaacab2a7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1587,20 +1587,16 @@ static int walk_component(struct nameidata *nd, int follow) * to be able to know about the current root directory and * parent relationships. */ - if (unlikely(nd->last_type != LAST_NORM)) { - err = handle_dots(nd, nd->last_type); - if (err) - goto out_err; - return 0; - } + if (unlikely(nd->last_type != LAST_NORM)) + return handle_dots(nd, nd->last_type); err = lookup_fast(nd, &path, &inode); if (unlikely(err)) { if (err < 0) - goto out_err; + return err; err = lookup_slow(nd, &path); if (err < 0) - goto out_err; + return err; inode = path.dentry->d_inode; err = -ENOENT; @@ -1612,8 +1608,7 @@ static int walk_component(struct nameidata *nd, int follow) if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != path.mnt || unlazy_walk(nd, path.dentry))) { - err = -ECHILD; - goto out_err; + return -ECHILD; } } BUG_ON(inode != path.dentry->d_inode); @@ -1626,8 +1621,6 @@ static int walk_component(struct nameidata *nd, int follow) out_path_put: path_to_nameidata(&path, nd); -out_err: - terminate_walk(nd); return err; } @@ -1819,7 +1812,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) err = walk_component(nd, LOOKUP_FOLLOW); Walked: if (err < 0) - goto Err; + break; if (err) { const char *s; @@ -2020,11 +2013,15 @@ static int trailing_symlink(struct nameidata *nd) static inline int lookup_last(struct nameidata *nd) { + int err; if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - return walk_component(nd, nd->flags & LOOKUP_FOLLOW); + err = walk_component(nd, nd->flags & LOOKUP_FOLLOW); + if (err < 0) + terminate_walk(nd); + return err; } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -- cgit v1.2.3-58-ga151 From 1bc4b813e8e5bda23a31892712e04905f0c7ffba Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 08:15:36 -0400 Subject: namei: lift (open-coded) terminate_walk() into callers of get_link() Signed-off-by: Al Viro --- fs/namei.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index d99eaacab2a7..db21e0426193 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -892,7 +892,6 @@ const char *get_link(struct nameidata *nd) mntget(nd->link.mnt); if (unlikely(current->total_link_count >= MAXSYMLINKS)) { - path_put(&nd->path); path_put(&nd->link); return ERR_PTR(-ELOOP); } @@ -916,7 +915,6 @@ const char *get_link(struct nameidata *nd) res = inode->i_op->follow_link(dentry, &last->cookie, nd); if (IS_ERR(res)) { out: - path_put(&nd->path); path_put(&last->link); return res; } @@ -1827,7 +1825,7 @@ Walked: if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); - goto Err; + break; } err = 0; if (unlikely(!s)) { @@ -1858,7 +1856,6 @@ Walked: } } terminate_walk(nd); -Err: while (unlikely(nd->depth)) put_link(nd); return err; @@ -1994,8 +1991,10 @@ static int trailing_symlink(struct nameidata *nd) return error; nd->flags |= LOOKUP_PARENT; s = get_link(nd); - if (unlikely(IS_ERR(s))) + if (unlikely(IS_ERR(s))) { + terminate_walk(nd); return PTR_ERR(s); + } if (unlikely(!s)) return 0; if (*s == '/') { -- cgit v1.2.3-58-ga151 From 191d7f73e25c460858bd9467d528b48fdb8cef59 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 08:26:45 -0400 Subject: namei: take put_link() into {lookup,mountpoint,do}_last() rationale: we'll need to have terminate_walk() do put_link() on everything, which will mean that in some cases ..._last() will do put_link() anyway. Easier to have them do it in all cases. Signed-off-by: Al Viro --- fs/namei.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index db21e0426193..26466fbc5b56 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2018,6 +2018,8 @@ static inline int lookup_last(struct nameidata *nd) nd->flags &= ~LOOKUP_PARENT; err = walk_component(nd, nd->flags & LOOKUP_FOLLOW); + if (nd->depth) + put_link(nd); if (err < 0) terminate_walk(nd); return err; @@ -2045,13 +2047,10 @@ static int path_lookupat(int dfd, const struct filename *name, */ err = path_init(dfd, name, flags, nd); if (!err && !(flags & LOOKUP_PARENT)) { - err = lookup_last(nd); - while (err > 0) { + while ((err = lookup_last(nd)) > 0) { err = trailing_symlink(nd); if (err) break; - err = lookup_last(nd); - put_link(nd); } } @@ -2362,6 +2361,8 @@ done: dput(dentry); goto out; } + if (nd->depth) + put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) { @@ -2373,6 +2374,8 @@ done: error = 0; out: terminate_walk(nd); + if (nd->depth) + put_link(nd); return error; } @@ -2394,13 +2397,10 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, if (unlikely(err)) goto out; - err = mountpoint_last(nd, path); - while (err > 0) { + while ((err = mountpoint_last(nd, path)) > 0) { err = trailing_symlink(nd); if (err) break; - err = mountpoint_last(nd, path); - put_link(nd); } out: path_cleanup(nd); @@ -2978,6 +2978,8 @@ static int do_last(struct nameidata *nd, error = handle_dots(nd, nd->last_type); if (unlikely(error)) { terminate_walk(nd); + if (nd->depth) + put_link(nd); return error; } goto finish_open; @@ -3003,8 +3005,11 @@ static int do_last(struct nameidata *nd, * about to look up */ error = complete_walk(nd); - if (error) + if (error) { + if (nd->depth) + put_link(nd); return error; + } audit_inode(name, dir, LOOKUP_PARENT); error = -EISDIR; @@ -3093,6 +3098,8 @@ finish_lookup: } } BUG_ON(inode != path.dentry->d_inode); + if (nd->depth) + put_link(nd); nd->link = path; return 1; } @@ -3116,6 +3123,8 @@ finish_lookup: finish_open: error = complete_walk(nd); if (error) { + if (nd->depth) + put_link(nd); path_put(&save_parent); return error; } @@ -3167,6 +3176,8 @@ out: mnt_drop_write(nd->path.mnt); path_put(&save_parent); terminate_walk(nd); + if (nd->depth) + put_link(nd); return error; exit_dput: @@ -3279,14 +3290,11 @@ static struct file *path_openat(int dfd, struct filename *pathname, if (unlikely(error)) goto out; - error = do_last(nd, file, op, &opened, pathname); - while (unlikely(error > 0)) { /* trailing symlink */ + while ((error = do_last(nd, file, op, &opened, pathname)) > 0) { nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); error = trailing_symlink(nd); if (unlikely(error)) break; - error = do_last(nd, file, op, &opened, pathname); - put_link(nd); } out: path_cleanup(nd); -- cgit v1.2.3-58-ga151 From 1543972678e31bcfbc7f15170c12fb601c9f5c90 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 08:34:59 -0400 Subject: namei: have terminate_walk() do put_link() on everything left All callers of terminate_walk() are followed by more or less open-coded eqiuvalent of "do put_link() on everything left in nd->stack". Better done in terminate_walk() itself, and when we go for RCU symlink traversal we'll have to do it there anyway. Signed-off-by: Al Viro --- fs/namei.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 26466fbc5b56..31da71753d7b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1562,6 +1562,8 @@ static void terminate_walk(struct nameidata *nd) nd->root.mnt = NULL; rcu_read_unlock(); } + while (unlikely(nd->depth)) + put_link(nd); } /* @@ -1856,8 +1858,6 @@ Walked: } } terminate_walk(nd); - while (unlikely(nd->depth)) - put_link(nd); return err; OK: if (!nd->depth) /* called from path_init(), done */ @@ -2374,8 +2374,6 @@ done: error = 0; out: terminate_walk(nd); - if (nd->depth) - put_link(nd); return error; } @@ -2978,8 +2976,6 @@ static int do_last(struct nameidata *nd, error = handle_dots(nd, nd->last_type); if (unlikely(error)) { terminate_walk(nd); - if (nd->depth) - put_link(nd); return error; } goto finish_open; @@ -3176,8 +3172,6 @@ out: mnt_drop_write(nd->path.mnt); path_put(&save_parent); terminate_walk(nd); - if (nd->depth) - put_link(nd); return error; exit_dput: -- cgit v1.2.3-58-ga151 From 8620c238edbf373aafcc4ee129e76c8e794c5214 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 08:58:35 -0400 Subject: link_path_walk: move the OK: inside the loop fewer labels that way; in particular, resuming after the end of nested symlink is straight-line. Signed-off-by: Al Viro --- fs/namei.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 31da71753d7b..19e5c8a27ea4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1806,11 +1806,21 @@ static int link_path_walk(const char *name, struct nameidata *nd) do { name++; } while (unlikely(*name == '/')); - if (!*name) - goto OK; - - err = walk_component(nd, LOOKUP_FOLLOW); -Walked: + if (unlikely(!*name)) { +OK: + /* called from path_init(), done */ + if (!nd->depth) + return 0; + name = nd->stack[nd->depth - 1].name; + /* called from trailing_symlink(), done */ + if (!name) + return 0; + /* last component of nested symlink */ + err = walk_component(nd, LOOKUP_FOLLOW); + put_link(nd); + } else { + err = walk_component(nd, LOOKUP_FOLLOW); + } if (err < 0) break; @@ -1859,16 +1869,6 @@ Walked: } terminate_walk(nd); return err; -OK: - if (!nd->depth) /* called from path_init(), done */ - return 0; - name = nd->stack[nd->depth - 1].name; - if (!name) /* called from trailing_symlink(), done */ - return 0; - - err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd); - goto Walked; } static int path_init(int dfd, const struct filename *name, unsigned int flags, -- cgit v1.2.3-58-ga151 From 4693a547cded42807ec468947b4d5287f2cb6aa9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 17:47:11 -0400 Subject: namei: new calling conventions for walk_component() instead of a single flag (!= 0 => we want to follow symlinks) pass two bits - WALK_GET (want to follow symlinks) and WALK_PUT (put_link() once we are done looking at the name). The latter matters only for success exits - on failure the caller will discard everything anyway. Suggestions for better variant are welcome; what this thing aims for is making sure that pending put_link() is done *before* walk_component() decides to pick a symlink up, rather than between picking it up and acting upon it. See the next commit for payoff. Signed-off-by: Al Viro --- fs/namei.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 19e5c8a27ea4..1c9af92518a1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1577,7 +1577,9 @@ static inline int should_follow_link(struct dentry *dentry, int follow) return unlikely(d_is_symlink(dentry)) ? follow : 0; } -static int walk_component(struct nameidata *nd, int follow) +enum {WALK_GET = 1, WALK_PUT = 2}; + +static int walk_component(struct nameidata *nd, int flags) { struct path path; struct inode *inode; @@ -1587,8 +1589,12 @@ static int walk_component(struct nameidata *nd, int follow) * to be able to know about the current root directory and * parent relationships. */ - if (unlikely(nd->last_type != LAST_NORM)) - return handle_dots(nd, nd->last_type); + if (unlikely(nd->last_type != LAST_NORM)) { + err = handle_dots(nd, nd->last_type); + if (flags & WALK_PUT) + put_link(nd); + return err; + } err = lookup_fast(nd, &path, &inode); if (unlikely(err)) { if (err < 0) @@ -1604,7 +1610,9 @@ static int walk_component(struct nameidata *nd, int follow) goto out_path_put; } - if (should_follow_link(path.dentry, follow)) { + if (flags & WALK_PUT) + put_link(nd); + if (should_follow_link(path.dentry, flags & WALK_GET)) { if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != path.mnt || unlazy_walk(nd, path.dentry))) { @@ -1816,10 +1824,9 @@ OK: if (!name) return 0; /* last component of nested symlink */ - err = walk_component(nd, LOOKUP_FOLLOW); - put_link(nd); + err = walk_component(nd, WALK_GET | WALK_PUT); } else { - err = walk_component(nd, LOOKUP_FOLLOW); + err = walk_component(nd, WALK_GET); } if (err < 0) break; @@ -2017,9 +2024,12 @@ static inline int lookup_last(struct nameidata *nd) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - err = walk_component(nd, nd->flags & LOOKUP_FOLLOW); - if (nd->depth) - put_link(nd); + err = walk_component(nd, + nd->flags & LOOKUP_FOLLOW + ? nd->depth + ? WALK_PUT | WALK_GET + : WALK_GET + : 0); if (err < 0) terminate_walk(nd); return err; -- cgit v1.2.3-58-ga151 From d63ff28f0f693196c0e2c587024d272b6fdbfa6e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 18:13:23 -0400 Subject: namei: make should_follow_link() store the link in nd->link ... if it decides to follow, that is. Signed-off-by: Al Viro --- fs/namei.c | 62 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1c9af92518a1..1b4bc1b7ffae 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1566,15 +1566,31 @@ static void terminate_walk(struct nameidata *nd) put_link(nd); } +static int pick_link(struct nameidata *nd, struct path *link) +{ + if (nd->flags & LOOKUP_RCU) { + if (unlikely(nd->path.mnt != link->mnt || + unlazy_walk(nd, link->dentry))) { + return -ECHILD; + } + } + nd->link = *link; + return 1; +} + /* * Do we need to follow links? We _really_ want to be able * to do this check without having to look at inode->i_op, * so we keep a cache of "no, this doesn't need follow_link" * for the common case. */ -static inline int should_follow_link(struct dentry *dentry, int follow) +static inline int should_follow_link(struct nameidata *nd, struct path *link, int follow) { - return unlikely(d_is_symlink(dentry)) ? follow : 0; + if (likely(!d_is_symlink(link->dentry))) + return 0; + if (!follow) + return 0; + return pick_link(nd, link); } enum {WALK_GET = 1, WALK_PUT = 2}; @@ -1612,17 +1628,9 @@ static int walk_component(struct nameidata *nd, int flags) if (flags & WALK_PUT) put_link(nd); - if (should_follow_link(path.dentry, flags & WALK_GET)) { - if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path.mnt || - unlazy_walk(nd, path.dentry))) { - return -ECHILD; - } - } - BUG_ON(inode != path.dentry->d_inode); - nd->link = path; - return 1; - } + err = should_follow_link(nd, &path, flags & WALK_GET); + if (unlikely(err)) + return err; path_to_nameidata(&path, nd); nd->inode = inode; return 0; @@ -2375,9 +2383,11 @@ done: put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; - if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW)) { - nd->link = *path; - return 1; + error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW); + if (unlikely(error)) { + if (error < 0) + goto out; + return error; } mntget(path->mnt); follow_mount(path); @@ -3095,19 +3105,13 @@ retry_lookup: goto out; } finish_lookup: - if (should_follow_link(path.dentry, nd->flags & LOOKUP_FOLLOW)) { - if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != path.mnt || - unlazy_walk(nd, path.dentry))) { - error = -ECHILD; - goto out; - } - } - BUG_ON(inode != path.dentry->d_inode); - if (nd->depth) - put_link(nd); - nd->link = path; - return 1; + if (nd->depth) + put_link(nd); + error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW); + if (unlikely(error)) { + if (error < 0) + goto out; + return error; } if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) { -- cgit v1.2.3-58-ga151 From 626de99676e1e41fc70fc890776518ba936a58c6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 4 May 2015 18:26:59 -0400 Subject: namei: move link count check and stack allocation into pick_link() Signed-off-by: Al Viro --- fs/namei.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1b4bc1b7ffae..046a703c081d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -891,16 +891,10 @@ const char *get_link(struct nameidata *nd) if (nd->link.mnt == nd->path.mnt) mntget(nd->link.mnt); - if (unlikely(current->total_link_count >= MAXSYMLINKS)) { - path_put(&nd->link); - return ERR_PTR(-ELOOP); - } - last->link = nd->link; last->cookie = NULL; cond_resched(); - current->total_link_count++; touch_atime(&last->link); @@ -1568,12 +1562,23 @@ static void terminate_walk(struct nameidata *nd) static int pick_link(struct nameidata *nd, struct path *link) { + int error; + if (unlikely(current->total_link_count++ >= MAXSYMLINKS)) { + path_to_nameidata(link, nd); + return -ELOOP; + } if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != link->mnt || unlazy_walk(nd, link->dentry))) { return -ECHILD; } } + error = nd_alloc_stack(nd); + if (unlikely(error)) { + path_to_nameidata(link, nd); + return error; + } + nd->link = *link; return 1; } @@ -1840,15 +1845,7 @@ OK: break; if (err) { - const char *s; - - err = nd_alloc_stack(nd); - if (unlikely(err)) { - path_to_nameidata(&nd->link, nd); - break; - } - - s = get_link(nd); + const char *s = get_link(nd); if (unlikely(IS_ERR(s))) { err = PTR_ERR(s); -- cgit v1.2.3-58-ga151 From 44163f30059e9869451999e77109a37abba8c968 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 17 Apr 2015 23:02:40 -0400 Subject: lustre: rip the private symlink nesting limit out Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/llite/symlink.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index e488cb3bb25d..da6d9d17c50d 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -126,18 +126,9 @@ static const char *ll_follow_link(struct dentry *dentry, void **cookie, struct n char *symname = NULL; CDEBUG(D_VFSTRACE, "VFS Op\n"); - /* Limit the recursive symlink depth to 5 instead of default - * 8 links when kernel has 4k stack to prevent stack overflow. - * For 8k stacks we need to limit it to 7 for local servers. */ - if (THREAD_SIZE < 8192 && current->link_count >= 6) { - rc = -ELOOP; - } else if (THREAD_SIZE == 8192 && current->link_count >= 8) { - rc = -ELOOP; - } else { - ll_inode_size_lock(inode); - rc = ll_readlink_internal(inode, &request, &symname); - ll_inode_size_unlock(inode); - } + ll_inode_size_lock(inode); + rc = ll_readlink_internal(inode, &request, &symname); + ll_inode_size_unlock(inode); if (rc) { ptlrpc_req_finished(request); return ERR_PTR(rc); -- cgit v1.2.3-58-ga151 From 756daf263ea53a8bfc89db26cb92e963953253a1 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:38 +1100 Subject: VFS: replace {, total_}link_count in task_struct with pointer to nameidata task_struct currently contains two ad-hoc members for use by the VFS: link_count and total_link_count. These are only interesting to fs/namei.c, so exposing them explicitly is poor layering. Incidentally, link_count isn't used anymore, so it can just die. This patches replaces those with a single pointer to 'struct nameidata'. This structure represents the current filename lookup of which there can only be one per process, and is a natural place to store total_link_count. This will allow the current "nameidata" argument to all follow_link operations to be removed as current->nameidata can be used instead in the _very_ few instances that care about it at all. As there are occasional circumstances where pathname lookup can recurse, such as through kern_path_locked, we always save and old current->nameidata (if there is one) when setting a new value, and make sure any active link_counts are preserved. follow_mount and follow_automount now get a 'struct nameidata *' rather than 'int flags' so that they can directly access total_link_count, rather than going through 'current'. Suggested-by: Al Viro Signed-off-by: NeilBrown Signed-off-by: Al Viro --- fs/namei.c | 70 ++++++++++++++++++++++++++++----------------------- include/linux/sched.h | 2 +- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 046a703c081d..699e093f52b5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -505,6 +505,7 @@ struct nameidata { unsigned seq, m_seq; int last_type; unsigned depth; + int total_link_count; struct file *base; struct saved { struct path link; @@ -513,16 +514,25 @@ struct nameidata { } *stack, internal[EMBEDDED_LEVELS]; }; -static void set_nameidata(struct nameidata *nd) +static struct nameidata *set_nameidata(struct nameidata *p) { - nd->stack = nd->internal; + struct nameidata *old = current->nameidata; + p->stack = p->internal; + p->total_link_count = old ? old->total_link_count : 0; + current->nameidata = p; + return old; } -static void restore_nameidata(struct nameidata *nd) +static void restore_nameidata(struct nameidata *old) { - if (nd->stack != nd->internal) { - kfree(nd->stack); - nd->stack = nd->internal; + struct nameidata *now = current->nameidata; + + current->nameidata = old; + if (old) + old->total_link_count = now->total_link_count; + if (now->stack != now->internal) { + kfree(now->stack); + now->stack = now->internal; } } @@ -970,7 +980,7 @@ EXPORT_SYMBOL(follow_up); * - return -EISDIR to tell follow_managed() to stop and return the path we * were called with. */ -static int follow_automount(struct path *path, unsigned flags, +static int follow_automount(struct path *path, struct nameidata *nd, bool *need_mntput) { struct vfsmount *mnt; @@ -990,13 +1000,13 @@ static int follow_automount(struct path *path, unsigned flags, * as being automount points. These will need the attentions * of the daemon to instantiate them before they can be used. */ - if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY | - LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) && + if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY | + LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) && path->dentry->d_inode) return -EISDIR; - current->total_link_count++; - if (current->total_link_count >= 40) + nd->total_link_count++; + if (nd->total_link_count >= 40) return -ELOOP; mnt = path->dentry->d_op->d_automount(path); @@ -1010,7 +1020,7 @@ static int follow_automount(struct path *path, unsigned flags, * the path being looked up; if it wasn't then the remainder of * the path is inaccessible and we should say so. */ - if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_PARENT)) + if (PTR_ERR(mnt) == -EISDIR && (nd->flags & LOOKUP_PARENT)) return -EREMOTE; return PTR_ERR(mnt); } @@ -1050,7 +1060,7 @@ static int follow_automount(struct path *path, unsigned flags, * * Serialization is taken care of in namespace.c */ -static int follow_managed(struct path *path, unsigned flags) +static int follow_managed(struct path *path, struct nameidata *nd) { struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */ unsigned managed; @@ -1094,7 +1104,7 @@ static int follow_managed(struct path *path, unsigned flags) /* Handle an automount point */ if (managed & DCACHE_NEED_AUTOMOUNT) { - ret = follow_automount(path, flags, &need_mntput); + ret = follow_automount(path, nd, &need_mntput); if (ret < 0) break; continue; @@ -1483,7 +1493,7 @@ unlazy: } path->mnt = mnt; path->dentry = dentry; - err = follow_managed(path, nd->flags); + err = follow_managed(path, nd); if (unlikely(err < 0)) { path_put_conditional(path, nd); return err; @@ -1513,7 +1523,7 @@ static int lookup_slow(struct nameidata *nd, struct path *path) return PTR_ERR(dentry); path->mnt = nd->path.mnt; path->dentry = dentry; - err = follow_managed(path, nd->flags); + err = follow_managed(path, nd); if (unlikely(err < 0)) { path_put_conditional(path, nd); return err; @@ -1563,7 +1573,7 @@ static void terminate_walk(struct nameidata *nd) static int pick_link(struct nameidata *nd, struct path *link) { int error; - if (unlikely(current->total_link_count++ >= MAXSYMLINKS)) { + if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) { path_to_nameidata(link, nd); return -ELOOP; } @@ -1981,7 +1991,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, rcu_read_unlock(); return -ECHILD; done: - current->total_link_count = 0; + nd->total_link_count = 0; return link_path_walk(s, nd); } @@ -2087,10 +2097,9 @@ static int filename_lookup(int dfd, struct filename *name, unsigned int flags, struct nameidata *nd) { int retval; + struct nameidata *saved_nd = set_nameidata(nd); - set_nameidata(nd); retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); - if (unlikely(retval == -ECHILD)) retval = path_lookupat(dfd, name, flags, nd); if (unlikely(retval == -ESTALE)) @@ -2098,7 +2107,7 @@ static int filename_lookup(int dfd, struct filename *name, if (likely(!retval)) audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); - restore_nameidata(nd); + restore_nameidata(saved_nd); return retval; } @@ -2426,11 +2435,11 @@ static int filename_mountpoint(int dfd, struct filename *name, struct path *path, unsigned int flags) { - struct nameidata nd; + struct nameidata nd, *saved; int error; if (IS_ERR(name)) return PTR_ERR(name); - set_nameidata(&nd); + saved = set_nameidata(&nd); error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_RCU); if (unlikely(error == -ECHILD)) error = path_mountpoint(dfd, name, path, &nd, flags); @@ -2438,7 +2447,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path, error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_REVAL); if (likely(!error)) audit_inode(name, path->dentry, 0); - restore_nameidata(&nd); + restore_nameidata(saved); putname(name); return error; } @@ -3087,7 +3096,7 @@ retry_lookup: if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) goto exit_dput; - error = follow_managed(&path, nd->flags); + error = follow_managed(&path, nd); if (error < 0) goto exit_dput; @@ -3323,31 +3332,29 @@ out2: struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { - struct nameidata nd; + struct nameidata nd, *saved_nd = set_nameidata(&nd); int flags = op->lookup_flags; struct file *filp; - set_nameidata(&nd); filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) filp = path_openat(dfd, pathname, &nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL); - restore_nameidata(&nd); + restore_nameidata(saved_nd); return filp; } struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, const char *name, const struct open_flags *op) { - struct nameidata nd; + struct nameidata nd, *saved_nd; struct file *file; struct filename *filename; int flags = op->lookup_flags | LOOKUP_ROOT; nd.root.mnt = mnt; nd.root.dentry = dentry; - set_nameidata(&nd); if (d_is_symlink(dentry) && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); @@ -3356,12 +3363,13 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, if (unlikely(IS_ERR(filename))) return ERR_CAST(filename); + saved_nd = set_nameidata(&nd); file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU); if (unlikely(file == ERR_PTR(-ECHILD))) file = path_openat(-1, filename, &nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL); - restore_nameidata(&nd); + restore_nameidata(saved_nd); putname(filename); return file; } diff --git a/include/linux/sched.h b/include/linux/sched.h index 26a2e6122734..f6c9b69d66f2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1461,7 +1461,7 @@ struct task_struct { it with task_lock()) - initialized normally by setup_new_exec */ /* file system info */ - int link_count, total_link_count; + struct nameidata *nameidata; #ifdef CONFIG_SYSVIPC /* ipc stuff */ struct sysv_sem sysvsem; -- cgit v1.2.3-58-ga151 From 8402752ecf829f67527072151fa680292a519193 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 22 Apr 2015 10:30:08 -0400 Subject: namei: simplify the callers of follow_managed() now that it gets nameidata, no reason to have setting LOOKUP_JUMPED on mountpoint crossing and calling path_put_conditional() on failures done in every caller. Signed-off-by: Al Viro --- fs/namei.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 699e093f52b5..b57400ca6a0f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1118,7 +1118,11 @@ static int follow_managed(struct path *path, struct nameidata *nd) mntput(path->mnt); if (ret == -EISDIR) ret = 0; - return ret < 0 ? ret : need_mntput; + if (need_mntput) + nd->flags |= LOOKUP_JUMPED; + if (unlikely(ret < 0)) + path_put_conditional(path, nd); + return ret; } int follow_down_one(struct path *path) @@ -1494,14 +1498,9 @@ unlazy: path->mnt = mnt; path->dentry = dentry; err = follow_managed(path, nd); - if (unlikely(err < 0)) { - path_put_conditional(path, nd); - return err; - } - if (err) - nd->flags |= LOOKUP_JUMPED; - *inode = path->dentry->d_inode; - return 0; + if (likely(!err)) + *inode = path->dentry->d_inode; + return err; need_lookup: return 1; @@ -1511,7 +1510,6 @@ need_lookup: static int lookup_slow(struct nameidata *nd, struct path *path) { struct dentry *dentry, *parent; - int err; parent = nd->path.dentry; BUG_ON(nd->inode != parent->d_inode); @@ -1523,14 +1521,7 @@ static int lookup_slow(struct nameidata *nd, struct path *path) return PTR_ERR(dentry); path->mnt = nd->path.mnt; path->dentry = dentry; - err = follow_managed(path, nd); - if (unlikely(err < 0)) { - path_put_conditional(path, nd); - return err; - } - if (err) - nd->flags |= LOOKUP_JUMPED; - return 0; + return follow_managed(path, nd); } static inline int may_lookup(struct nameidata *nd) @@ -3098,10 +3089,7 @@ retry_lookup: error = follow_managed(&path, nd); if (error < 0) - goto exit_dput; - - if (error) - nd->flags |= LOOKUP_JUMPED; + goto out; BUG_ON(nd->flags & LOOKUP_RCU); inode = path.dentry->d_inode; -- cgit v1.2.3-58-ga151 From 6e77137b363b8d866ac29c5a0c95e953614fb2d8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 2 May 2015 13:37:52 -0400 Subject: don't pass nameidata to ->follow_link() its only use is getting passed to nd_jump_link(), which can obtain it from current->nameidata Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 +- Documentation/filesystems/vfs.txt | 2 +- drivers/staging/lustre/lustre/llite/symlink.c | 2 +- fs/9p/vfs_inode.c | 2 +- fs/9p/vfs_inode_dotl.c | 2 +- fs/autofs4/symlink.c | 2 +- fs/befs/linuxvfs.c | 4 ++-- fs/cifs/cifsfs.h | 2 +- fs/cifs/link.c | 2 +- fs/configfs/symlink.c | 2 +- fs/ecryptfs/inode.c | 2 +- fs/ext4/symlink.c | 2 +- fs/f2fs/namei.c | 4 ++-- fs/fuse/dir.c | 2 +- fs/gfs2/inode.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/hppfs/hppfs.c | 4 ++-- fs/kernfs/symlink.c | 2 +- fs/libfs.c | 2 +- fs/namei.c | 11 ++++++----- fs/nfs/symlink.c | 2 +- fs/overlayfs/inode.c | 4 ++-- fs/proc/base.c | 4 ++-- fs/proc/inode.c | 2 +- fs/proc/namespaces.c | 4 ++-- fs/proc/self.c | 2 +- fs/proc/thread_self.c | 2 +- fs/xfs/xfs_iops.c | 3 +-- include/linux/fs.h | 6 +++--- include/linux/namei.h | 2 +- mm/shmem.c | 2 +- 31 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 7fa6c4ac858c..5b5b4f54c033 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -50,7 +50,7 @@ prototypes: int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); - const char *(*follow_link) (struct dentry *, void **, struct nameidata *); + const char *(*follow_link) (struct dentry *, void **); void (*put_link) (struct dentry *, void *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int, unsigned int); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 1c6b03ac2e5a..0dec8c880be6 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -350,7 +350,7 @@ struct inode_operations { int (*rename2) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); - const char *(*follow_link) (struct dentry *, void **, struct nameidata *); + const char *(*follow_link) (struct dentry *, void **); void (*put_link) (struct dentry *, void *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index da6d9d17c50d..f3be3bf0f66f 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -118,7 +118,7 @@ failed: return rc; } -static const char *ll_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *ll_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct ptlrpc_request *request = NULL; diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 7cc70a39a1d8..271f51af2f75 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1230,7 +1230,7 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) * */ -static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) { struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry); struct p9_fid *fid = v9fs_fid_lookup(dentry); diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index ae062ffa0f1f..16658ed677c9 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -910,7 +910,7 @@ error: */ static const char * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie, struct nameidata *nd) +v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie) { struct p9_fid *fid = v9fs_fid_lookup(dentry); char *target; diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c index 9c6a07739c9b..da0c33481bc0 100644 --- a/fs/autofs4/symlink.c +++ b/fs/autofs4/symlink.c @@ -12,7 +12,7 @@ #include "autofs_i.h" -static const char *autofs4_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *autofs4_follow_link(struct dentry *dentry, void **cookie) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 3a1aefb86a11..46aedacfa6a8 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -42,7 +42,7 @@ static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static void befs_destroy_inodecache(void); -static const char *befs_follow_link(struct dentry *, void **, struct nameidata *nd); +static const char *befs_follow_link(struct dentry *, void **); static int befs_utf2nls(struct super_block *sb, const char *in, int in_len, char **out, int *out_len); static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, @@ -464,7 +464,7 @@ befs_destroy_inodecache(void) * flag is set. */ static const char * -befs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +befs_follow_link(struct dentry *dentry, void **cookie) { struct super_block *sb = dentry->d_sb; struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry)); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 61012da7e9d8..a782b22904e4 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,7 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path); #endif /* Functions related to symlinks */ -extern const char *cifs_follow_link(struct dentry *direntry, void **cookie, struct nameidata *nd); +extern const char *cifs_follow_link(struct dentry *direntry, void **cookie); extern int cifs_readlink(struct dentry *direntry, char __user *buffer, int buflen); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 4a439c2c0c7f..546f86ab09aa 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -627,7 +627,7 @@ cifs_hl_exit: } const char * -cifs_follow_link(struct dentry *direntry, void **cookie, struct nameidata *nd) +cifs_follow_link(struct dentry *direntry, void **cookie) { struct inode *inode = d_inode(direntry); int rc = -ENOMEM; diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index fac8e8517f33..0ace75649009 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -279,7 +279,7 @@ static int configfs_getlink(struct dentry *dentry, char * path) } -static const char *configfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *configfs_follow_link(struct dentry *dentry, void **cookie) { unsigned long page = get_zeroed_page(GFP_KERNEL); int error; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index cdb9d6c4532d..73d20ae92478 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -675,7 +675,7 @@ out: return rc ? ERR_PTR(rc) : buf; } -static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie) { size_t len; char *buf = ecryptfs_readlink_lower(dentry, &len); diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index afec475aaf5c..ba5bd18a9825 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -23,7 +23,7 @@ #include "xattr.h" #ifdef CONFIG_EXT4_FS_ENCRYPTION -static const char *ext4_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *ext4_follow_link(struct dentry *dentry, void **cookie) { struct page *cpage = NULL; char *caddr, *paddr = NULL; diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index d2947937515e..cd05a7c91533 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -296,9 +296,9 @@ fail: return err; } -static const char *f2fs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *f2fs_follow_link(struct dentry *dentry, void **cookie) { - const char *link = page_follow_link_light(dentry, cookie, nd); + const char *link = page_follow_link_light(dentry, cookie); if (!IS_ERR(link) && !*link) { /* this is broken symlink case */ page_put_link(dentry, *cookie); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index f9cb260375cf..d5cdef8b7f3a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1365,7 +1365,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx) return err; } -static const char *fuse_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *fuse_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct fuse_conn *fc = get_fuse_conn(inode); diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index f59390aebffb..3a1461de1551 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1548,7 +1548,7 @@ out: * Returns: 0 on success or error code */ -static const char *gfs2_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *gfs2_follow_link(struct dentry *dentry, void **cookie) { struct gfs2_inode *ip = GFS2_I(d_inode(dentry)); struct gfs2_holder i_gh; diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index f650ed661fab..7b6ed7a908f6 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -892,7 +892,7 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -static const char *hostfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) { char *link = __getname(); if (link) { diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index b8f24d3b04ee..15a774eb5bbf 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -642,11 +642,11 @@ static int hppfs_readlink(struct dentry *dentry, char __user *buffer, buflen); } -static const char *hppfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *hppfs_follow_link(struct dentry *dentry, void **cookie) { struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; - return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie, nd); + return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie); } static void hppfs_put_link(struct dentry *dentry, void *cookie) diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 3c7e799974a2..366c5a17475e 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -112,7 +112,7 @@ static int kernfs_getlink(struct dentry *dentry, char *path) return error; } -static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) { int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); diff --git a/fs/libfs.c b/fs/libfs.c index 0c83fde20dbd..c5f3373e326b 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1091,7 +1091,7 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp, } EXPORT_SYMBOL(simple_nosetlease); -const char *simple_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +const char *simple_follow_link(struct dentry *dentry, void **cookie) { return d_inode(dentry)->i_link; } diff --git a/fs/namei.c b/fs/namei.c index b57400ca6a0f..f311f0369e3c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -753,8 +753,9 @@ static inline void path_to_nameidata(const struct path *path, * Helper to directly jump to a known parsed path from ->follow_link, * caller must have taken a reference to path beforehand. */ -void nd_jump_link(struct nameidata *nd, struct path *path) +void nd_jump_link(struct path *path) { + struct nameidata *nd = current->nameidata; path_put(&nd->path); nd->path = *path; @@ -916,7 +917,7 @@ const char *get_link(struct nameidata *nd) nd->last_type = LAST_BIND; res = inode->i_link; if (!res) { - res = inode->i_op->follow_link(dentry, &last->cookie, nd); + res = inode->i_op->follow_link(dentry, &last->cookie); if (IS_ERR(res)) { out: path_put(&last->link); @@ -4485,12 +4486,12 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) int res; if (!link) { - link = dentry->d_inode->i_op->follow_link(dentry, &cookie, NULL); + link = dentry->d_inode->i_op->follow_link(dentry, &cookie); if (IS_ERR(link)) return PTR_ERR(link); } res = readlink_copy(buffer, buflen, link); - if (cookie && dentry->d_inode->i_op->put_link) + if (dentry->d_inode->i_op->put_link) dentry->d_inode->i_op->put_link(dentry, cookie); return res; } @@ -4523,7 +4524,7 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) } EXPORT_SYMBOL(page_readlink); -const char *page_follow_link_light(struct dentry *dentry, void **cookie, struct nameidata *nd) +const char *page_follow_link_light(struct dentry *dentry, void **cookie) { struct page *page = NULL; char *res = page_getlink(dentry, &page); diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index c992b200ae7e..b6de433da5db 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -42,7 +42,7 @@ error: return -EIO; } -static const char *nfs_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *nfs_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct page *page; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 235ad42afb57..9986833c9fcc 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -140,7 +140,7 @@ struct ovl_link_data { void *cookie; }; -static const char *ovl_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *ovl_follow_link(struct dentry *dentry, void **cookie) { struct dentry *realdentry; struct inode *realinode; @@ -160,7 +160,7 @@ static const char *ovl_follow_link(struct dentry *dentry, void **cookie, struct data->realdentry = realdentry; } - ret = realinode->i_op->follow_link(realdentry, cookie, nd); + ret = realinode->i_op->follow_link(realdentry, cookie); if (IS_ERR_OR_NULL(ret)) { kfree(data); return ret; diff --git a/fs/proc/base.c b/fs/proc/base.c index 52652f86b187..286a422f440e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1380,7 +1380,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path) return -ENOENT; } -static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); struct path path; @@ -1394,7 +1394,7 @@ static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie, st if (error) goto out; - nd_jump_link(nd, &path); + nd_jump_link(&path); return NULL; out: return ERR_PTR(error); diff --git a/fs/proc/inode.c b/fs/proc/inode.c index acd51d75387d..eb35874fe09c 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -393,7 +393,7 @@ static const struct file_operations proc_reg_file_ops_no_compat = { }; #endif -static const char *proc_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *proc_follow_link(struct dentry *dentry, void **cookie) { struct proc_dir_entry *pde = PDE(d_inode(dentry)); if (unlikely(!use_pde(pde))) diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 10d24dd096e8..f6e8354b8cea 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -30,7 +30,7 @@ static const struct proc_ns_operations *ns_entries[] = { &mntns_operations, }; -static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie) { struct inode *inode = d_inode(dentry); const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops; @@ -45,7 +45,7 @@ static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie, str if (ptrace_may_access(task, PTRACE_MODE_READ)) { error = ns_get_path(&ns_path, task, ns_ops); if (!error) - nd_jump_link(nd, &ns_path); + nd_jump_link(&ns_path); } put_task_struct(task); return error; diff --git a/fs/proc/self.c b/fs/proc/self.c index ad333946b53a..113b8d061fc0 100644 --- a/fs/proc/self.c +++ b/fs/proc/self.c @@ -18,7 +18,7 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_self_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *proc_self_follow_link(struct dentry *dentry, void **cookie) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c index 85c96e0d7aaa..947b0f4fd0a1 100644 --- a/fs/proc/thread_self.c +++ b/fs/proc/thread_self.c @@ -19,7 +19,7 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, return readlink_copy(buffer, buflen, tmp); } -static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie) { struct pid_namespace *ns = dentry->d_sb->s_fs_info; pid_t tgid = task_tgid_nr_ns(current, ns); diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 26c4dcb1ef56..7f51f39f8acc 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -416,8 +416,7 @@ xfs_vn_rename( STATIC const char * xfs_vn_follow_link( struct dentry *dentry, - void **cookie, - struct nameidata *nd) + void **cookie) { char *link; int error = -ENOMEM; diff --git a/include/linux/fs.h b/include/linux/fs.h index 9ab934113a28..ed7c9f298759 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1608,7 +1608,7 @@ struct file_operations { struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); - const char * (*follow_link) (struct dentry *, void **, struct nameidata *); + const char * (*follow_link) (struct dentry *, void **); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); @@ -2705,7 +2705,7 @@ extern const struct file_operations generic_ro_fops; extern int readlink_copy(char __user *, int, const char *); extern int page_readlink(struct dentry *, char __user *, int); -extern const char *page_follow_link_light(struct dentry *, void **, struct nameidata *); +extern const char *page_follow_link_light(struct dentry *, void **); extern void page_put_link(struct dentry *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, int nofs); @@ -2722,7 +2722,7 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); -const char *simple_follow_link(struct dentry *, void **, struct nameidata *); +const char *simple_follow_link(struct dentry *, void **); extern const struct inode_operations simple_symlink_inode_operations; extern int iterate_dir(struct file *, struct dir_context *); diff --git a/include/linux/namei.h b/include/linux/namei.h index 3a6cc9651712..d756304aa09b 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -72,7 +72,7 @@ extern int follow_up(struct path *); extern struct dentry *lock_rename(struct dentry *, struct dentry *); extern void unlock_rename(struct dentry *, struct dentry *); -extern void nd_jump_link(struct nameidata *nd, struct path *path); +extern void nd_jump_link(struct path *path); static inline void nd_terminate_link(void *name, size_t len, size_t maxlen) { diff --git a/mm/shmem.c b/mm/shmem.c index d1693dcb4285..e02682267046 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2475,7 +2475,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s return 0; } -static const char *shmem_follow_link(struct dentry *dentry, void **cookie, struct nameidata *nd) +static const char *shmem_follow_link(struct dentry *dentry, void **cookie) { struct page *page = NULL; int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL); -- cgit v1.2.3-58-ga151 From 6920a4405e6ff7813de4d5a9eb5e0b475e41a06b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 10 May 2015 10:43:46 -0400 Subject: namei: simplify failure exits in get_link() when cookie is NULL, put_link() is equivalent to path_put(), so as soon as we'd set last->cookie to NULL, we can bump nd->depth and let the normal logics in terminate_walk() to take care of cleanups. Signed-off-by: Al Viro --- fs/namei.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index f311f0369e3c..678aeef85490 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -904,27 +904,23 @@ const char *get_link(struct nameidata *nd) last->link = nd->link; last->cookie = NULL; + nd->depth++; cond_resched(); touch_atime(&last->link); error = security_inode_follow_link(dentry); - res = ERR_PTR(error); if (error) - goto out; + return ERR_PTR(error); nd->last_type = LAST_BIND; res = inode->i_link; if (!res) { res = inode->i_op->follow_link(dentry, &last->cookie); - if (IS_ERR(res)) { -out: - path_put(&last->link); - return res; - } + if (IS_ERR_OR_NULL(res)) + last->cookie = NULL; } - nd->depth++; return res; } -- cgit v1.2.3-58-ga151 From 4f697a5e173023a4f566339452a9d6f2cc7bd7dc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 10 May 2015 10:50:41 -0400 Subject: namei: simpler treatment of symlinks with nothing other that / in the body Instead of saving name and branching to OK:, where we'll immediately restore it, and call walk_component() with WALK_PUT|WALK_GET and nd->last_type being LAST_BIND, which is equivalent to put_link(nd), err = 0, we can just treat that the same way we'd treat procfs-style "jump" symlinks - do put_link(nd) and move on. Signed-off-by: Al Viro --- fs/namei.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 678aeef85490..c5eb77a57974 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1865,11 +1865,13 @@ OK: ; } nd->inode = nd->path.dentry->d_inode; - nd->stack[nd->depth - 1].name = name; - if (!*s) - goto OK; - name = s; - continue; + if (unlikely(!*s)) { + put_link(nd); + } else { + nd->stack[nd->depth - 1].name = name; + name = s; + continue; + } } } if (!d_can_lookup(nd->path.dentry)) { -- cgit v1.2.3-58-ga151 From fab51e8ab25e1ad661ef8da42077de78477fba83 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 10 May 2015 11:01:00 -0400 Subject: namei: take the treatment of absolute symlinks to get_link() rather than letting the callers handle the jump-to-root part of semantics, do it right in get_link() and return the rest of the body for the caller to deal with - at that point it's treated the same way as relative symlinks would be. And return NULL when there's no "rest of the body" - those are treated the same as pure jump symlink would be. Signed-off-by: Al Viro --- fs/namei.c | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c5eb77a57974..c6ff9da69cec 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -918,9 +918,24 @@ const char *get_link(struct nameidata *nd) res = inode->i_link; if (!res) { res = inode->i_op->follow_link(dentry, &last->cookie); - if (IS_ERR_OR_NULL(res)) + if (IS_ERR_OR_NULL(res)) { last->cookie = NULL; + return res; + } + } + if (*res == '/') { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->inode = nd->path.dentry->d_inode; + nd->flags |= LOOKUP_JUMPED; + while (unlikely(*++res == '/')) + ; } + if (!*res) + res = NULL; return res; } @@ -1854,24 +1869,9 @@ OK: /* jumped */ put_link(nd); } else { - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - while (unlikely(*++s == '/')) - ; - } - nd->inode = nd->path.dentry->d_inode; - if (unlikely(!*s)) { - put_link(nd); - } else { - nd->stack[nd->depth - 1].name = name; - name = s; - continue; - } + nd->stack[nd->depth - 1].name = name; + name = s; + continue; } } if (!d_can_lookup(nd->path.dentry)) { @@ -2002,6 +2002,7 @@ static int trailing_symlink(struct nameidata *nd) if (unlikely(error)) return error; nd->flags |= LOOKUP_PARENT; + nd->stack[0].name = NULL; s = get_link(nd); if (unlikely(IS_ERR(s))) { terminate_walk(nd); @@ -2009,16 +2010,6 @@ static int trailing_symlink(struct nameidata *nd) } if (unlikely(!s)) return 0; - if (*s == '/') { - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->flags |= LOOKUP_JUMPED; - } - nd->inode = nd->path.dentry->d_inode; - nd->stack[0].name = NULL; return link_path_walk(s, nd); } -- cgit v1.2.3-58-ga151 From e8bb73dfb0aad673a3b9650e9af0ba1739a4df01 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 16:28:42 -0400 Subject: namei: fold put_link() into the failure case of complete_walk() ... and don't open-code unlazy_walk() in there - the only reason for that is to avoid verfication of cached nd->root, which is trivially avoided by discarding said cached nd->root first. Signed-off-by: Al Viro --- fs/namei.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index c6ff9da69cec..55283fe95245 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -567,6 +567,8 @@ static inline int nd_alloc_stack(struct nameidata *nd) * to restart the path walk from the beginning in ref-walk mode. */ +static void terminate_walk(struct nameidata *nd); + /** * unlazy_walk - try to switch to ref-walk mode. * @nd: nameidata pathwalk data @@ -673,26 +675,12 @@ static int complete_walk(struct nameidata *nd) int status; if (nd->flags & LOOKUP_RCU) { - nd->flags &= ~LOOKUP_RCU; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; - - if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) { - rcu_read_unlock(); - return -ECHILD; - } - if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) { - rcu_read_unlock(); - mntput(nd->path.mnt); - return -ECHILD; - } - if (read_seqcount_retry(&dentry->d_seq, nd->seq)) { - rcu_read_unlock(); - dput(dentry); - mntput(nd->path.mnt); + if (unlikely(unlazy_walk(nd, NULL))) { + terminate_walk(nd); return -ECHILD; } - rcu_read_unlock(); } if (likely(!(nd->flags & LOOKUP_JUMPED))) @@ -708,7 +696,7 @@ static int complete_walk(struct nameidata *nd) if (!status) status = -ESTALE; - path_put(&nd->path); + terminate_walk(nd); return status; } @@ -3008,11 +2996,8 @@ static int do_last(struct nameidata *nd, * about to look up */ error = complete_walk(nd); - if (error) { - if (nd->depth) - put_link(nd); + if (error) return error; - } audit_inode(name, dir, LOOKUP_PARENT); error = -EISDIR; @@ -3117,8 +3102,6 @@ finish_lookup: finish_open: error = complete_walk(nd); if (error) { - if (nd->depth) - put_link(nd); path_put(&save_parent); return error; } -- cgit v1.2.3-58-ga151 From cd179f4468acfda2b7e9e236dc37bba815996421 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 May 2015 10:52:35 -0400 Subject: namei: move bumping the refcount of link->mnt into pick_link() update the failure cleanup in may_follow_link() to match that. Signed-off-by: Al Viro --- fs/namei.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 55283fe95245..05efcc0fc4b6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -802,7 +802,7 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd) return 0; audit_log_link_denied("follow_link", link); - path_put_conditional(link, nd); + path_put(link); path_put(&nd->path); return -EACCES; } @@ -887,9 +887,6 @@ const char *get_link(struct nameidata *nd) BUG_ON(nd->flags & LOOKUP_RCU); - if (nd->link.mnt == nd->path.mnt) - mntget(nd->link.mnt); - last->link = nd->link; last->cookie = NULL; nd->depth++; @@ -1574,9 +1571,11 @@ static int pick_link(struct nameidata *nd, struct path *link) return -ECHILD; } } + if (link->mnt == nd->path.mnt) + mntget(link->mnt); error = nd_alloc_stack(nd); if (unlikely(error)) { - path_to_nameidata(link, nd); + path_put(link); return error; } -- cgit v1.2.3-58-ga151 From fec2fa24e84a75447341a20d36e808c8d913a81a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 6 May 2015 15:58:18 -0400 Subject: may_follow_link(): trim arguments Signed-off-by: Al Viro --- fs/namei.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 05efcc0fc4b6..82cb1bc9a1bf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -765,7 +765,6 @@ int sysctl_protected_hardlinks __read_mostly = 0; /** * may_follow_link - Check symlink following for unsafe situations - * @link: The path of the symlink * @nd: nameidata pathwalk data * * In the case of the sysctl_protected_symlinks sysctl being enabled, @@ -779,7 +778,7 @@ int sysctl_protected_hardlinks __read_mostly = 0; * * Returns 0 if following the symlink is allowed, -ve on error. */ -static inline int may_follow_link(struct path *link, struct nameidata *nd) +static inline int may_follow_link(struct nameidata *nd) { const struct inode *inode; const struct inode *parent; @@ -788,7 +787,7 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = link->dentry->d_inode; + inode = nd->link.dentry->d_inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; @@ -801,8 +800,8 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd) if (uid_eq(parent->i_uid, inode->i_uid)) return 0; - audit_log_link_denied("follow_link", link); - path_put(link); + audit_log_link_denied("follow_link", &nd->link); + path_put(&nd->link); path_put(&nd->path); return -EACCES; } @@ -1985,7 +1984,7 @@ static void path_cleanup(struct nameidata *nd) static int trailing_symlink(struct nameidata *nd) { const char *s; - int error = may_follow_link(&nd->link, nd); + int error = may_follow_link(nd); if (unlikely(error)) return error; nd->flags |= LOOKUP_PARENT; -- cgit v1.2.3-58-ga151 From 1cf2665b5bdfc63185fb4a416bff54b14ad30c79 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 6 May 2015 16:01:56 -0400 Subject: namei: kill nd->link Just store it in nd->stack[nd->depth].link right in pick_link(). Now that we make sure of stack expansion in pick_link(), we can do so... Signed-off-by: Al Viro --- fs/namei.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 82cb1bc9a1bf..277ca86fc6c6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -495,10 +495,7 @@ EXPORT_SYMBOL(path_put); #define EMBEDDED_LEVELS 2 struct nameidata { struct path path; - union { - struct qstr last; - struct path link; - }; + struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; @@ -787,7 +784,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = nd->link.dentry->d_inode; + inode = nd->stack[0].link.dentry->d_inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; @@ -800,8 +797,8 @@ static inline int may_follow_link(struct nameidata *nd) if (uid_eq(parent->i_uid, inode->i_uid)) return 0; - audit_log_link_denied("follow_link", &nd->link); - path_put(&nd->link); + audit_log_link_denied("follow_link", &nd->stack[0].link); + path_put(&nd->stack[0].link); path_put(&nd->path); return -EACCES; } @@ -879,14 +876,13 @@ static __always_inline const char *get_link(struct nameidata *nd) { struct saved *last = nd->stack + nd->depth; - struct dentry *dentry = nd->link.dentry; + struct dentry *dentry = last->link.dentry; struct inode *inode = dentry->d_inode; int error; const char *res; BUG_ON(nd->flags & LOOKUP_RCU); - last->link = nd->link; last->cookie = NULL; nd->depth++; @@ -1560,6 +1556,7 @@ static void terminate_walk(struct nameidata *nd) static int pick_link(struct nameidata *nd, struct path *link) { int error; + struct saved *last; if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) { path_to_nameidata(link, nd); return -ELOOP; @@ -1578,7 +1575,8 @@ static int pick_link(struct nameidata *nd, struct path *link) return error; } - nd->link = *link; + last = nd->stack + nd->depth; + last->link = *link; return 1; } -- cgit v1.2.3-58-ga151 From ab10492345d1d629743c7e7d56532f4e5284c2c5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 10 May 2015 11:50:01 -0400 Subject: namei: take increment of nd->depth into pick_link() Makes the situation much more regular - we avoid a strange state when the element just after the top of stack is used to store struct path of symlink, but isn't counted in nd->depth. This is much more regular, so the normal failure exits, etc., work fine. Signed-off-by: Al Viro --- fs/namei.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 277ca86fc6c6..6d4692d772f7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -798,8 +798,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; audit_log_link_denied("follow_link", &nd->stack[0].link); - path_put(&nd->stack[0].link); - path_put(&nd->path); + terminate_walk(nd); return -EACCES; } @@ -875,7 +874,7 @@ static int may_linkat(struct path *link) static __always_inline const char *get_link(struct nameidata *nd) { - struct saved *last = nd->stack + nd->depth; + struct saved *last = nd->stack + nd->depth - 1; struct dentry *dentry = last->link.dentry; struct inode *inode = dentry->d_inode; int error; @@ -883,9 +882,6 @@ const char *get_link(struct nameidata *nd) BUG_ON(nd->flags & LOOKUP_RCU); - last->cookie = NULL; - nd->depth++; - cond_resched(); touch_atime(&last->link); @@ -1575,8 +1571,9 @@ static int pick_link(struct nameidata *nd, struct path *link) return error; } - last = nd->stack + nd->depth; + last = nd->stack + nd->depth++; last->link = *link; + last->cookie = NULL; return 1; } -- cgit v1.2.3-58-ga151 From b5cd3397627ab7e200ee068ca39b9ca2d031f26d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 16:38:31 -0400 Subject: namei: may_follow_link() - lift terminate_walk() on failures into caller Signed-off-by: Al Viro --- fs/namei.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6d4692d772f7..51e2214e820d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -798,7 +798,6 @@ static inline int may_follow_link(struct nameidata *nd) return 0; audit_log_link_denied("follow_link", &nd->stack[0].link); - terminate_walk(nd); return -EACCES; } @@ -1980,8 +1979,10 @@ static int trailing_symlink(struct nameidata *nd) { const char *s; int error = may_follow_link(nd); - if (unlikely(error)) + if (unlikely(error)) { + terminate_walk(nd); return error; + } nd->flags |= LOOKUP_PARENT; nd->stack[0].name = NULL; s = get_link(nd); -- cgit v1.2.3-58-ga151 From 8bcb77fabd7cbabcad49f58750be8683febee92b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 16:59:20 -0400 Subject: namei: split off filename_lookupat() with LOOKUP_PARENT new functions: filename_parentat() and path_parentat() resp. Signed-off-by: Al Viro --- fs/namei.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 51e2214e820d..6f95bc9e3686 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2034,7 +2034,7 @@ static int path_lookupat(int dfd, const struct filename *name, * be able to complete). */ err = path_init(dfd, name, flags, nd); - if (!err && !(flags & LOOKUP_PARENT)) { + if (!err) { while ((err = lookup_last(nd)) > 0) { err = trailing_symlink(nd); if (err) @@ -2074,6 +2074,35 @@ static int filename_lookup(int dfd, struct filename *name, return retval; } +/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ +static int path_parentat(int dfd, const struct filename *name, + unsigned int flags, struct nameidata *nd) +{ + int err = path_init(dfd, name, flags | LOOKUP_PARENT, nd); + if (!err) + err = complete_walk(nd); + path_cleanup(nd); + return err; +} + +static int filename_parentat(int dfd, struct filename *name, + unsigned int flags, struct nameidata *nd) +{ + int retval; + struct nameidata *saved_nd = set_nameidata(nd); + + retval = path_parentat(dfd, name, flags | LOOKUP_RCU, nd); + if (unlikely(retval == -ECHILD)) + retval = path_parentat(dfd, name, flags, nd); + if (unlikely(retval == -ESTALE)) + retval = path_parentat(dfd, name, flags | LOOKUP_REVAL, nd); + + if (likely(!retval)) + audit_inode(name, nd->path.dentry, LOOKUP_PARENT); + restore_nameidata(saved_nd); + return retval; +} + /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { @@ -2085,7 +2114,7 @@ struct dentry *kern_path_locked(const char *name, struct path *path) if (IS_ERR(filename)) return ERR_CAST(filename); - err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd); + err = filename_parentat(AT_FDCWD, filename, 0, &nd); if (err) { d = ERR_PTR(err); goto out; @@ -2255,7 +2284,7 @@ user_path_parent(int dfd, const char __user *path, if (IS_ERR(s)) return s; - error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, &nd); + error = filename_parentat(dfd, s, flags, &nd); if (error) { putname(s); return ERR_PTR(error); @@ -3344,7 +3373,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd); + error = filename_parentat(dfd, name, lookup_flags, &nd); if (error) return ERR_PTR(error); -- cgit v1.2.3-58-ga151 From 34a26b99b78148ff342801e732bf20014c291d03 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 11 May 2015 08:05:05 -0400 Subject: namei: get rid of nameidata->base we can do fdput() under rcu_read_lock() just fine; all we need to take care of is fetching nd->inode value first. Signed-off-by: Al Viro --- fs/namei.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6f95bc9e3686..497d5f4163b6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -503,7 +503,6 @@ struct nameidata { int last_type; unsigned depth; int total_link_count; - struct file *base; struct saved { struct path link; void *cookie; @@ -1872,7 +1871,6 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; nd->depth = 0; - nd->base = NULL; if (flags & LOOKUP_ROOT) { struct dentry *root = nd->root.dentry; struct inode *inode = root->d_inode; @@ -1941,14 +1939,15 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, nd->path = f.file->f_path; if (flags & LOOKUP_RCU) { - if (f.flags & FDPUT_FPUT) - nd->base = f.file; - nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); rcu_read_lock(); + nd->inode = nd->path.dentry->d_inode; + nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } else { path_get(&nd->path); - fdput(f); + nd->inode = nd->path.dentry->d_inode; } + fdput(f); + goto done; } nd->inode = nd->path.dentry->d_inode; @@ -1971,8 +1970,6 @@ static void path_cleanup(struct nameidata *nd) path_put(&nd->root); nd->root.mnt = NULL; } - if (unlikely(nd->base)) - fput(nd->base); } static int trailing_symlink(struct nameidata *nd) -- cgit v1.2.3-58-ga151 From 368ee9ba565d6e13912791b05f3cc1dfa945a62a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 17:19:59 -0400 Subject: namei: path_init() calling conventions change * lift link_path_walk() into callers; moving it down into path_init() had been a mistake. Stack footprint, among other things... * do _not_ call path_cleanup() after path_init() failure; on all failure exits out of it we have nothing for path_cleanup() to do * have path_init() return pathname or ERR_PTR(-E...) Signed-off-by: Al Viro --- fs/namei.c | 69 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 497d5f4163b6..06c71200be48 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1821,11 +1821,11 @@ static int link_path_walk(const char *name, struct nameidata *nd) } while (unlikely(*name == '/')); if (unlikely(!*name)) { OK: - /* called from path_init(), done */ + /* pathname body, done */ if (!nd->depth) return 0; name = nd->stack[nd->depth - 1].name; - /* called from trailing_symlink(), done */ + /* trailing symlink, done */ if (!name) return 0; /* last component of nested symlink */ @@ -1862,8 +1862,8 @@ OK: return err; } -static int path_init(int dfd, const struct filename *name, unsigned int flags, - struct nameidata *nd) +static const char *path_init(int dfd, const struct filename *name, + unsigned int flags, struct nameidata *nd) { int retval = 0; const char *s = name->name; @@ -1871,15 +1871,16 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; nd->depth = 0; + nd->total_link_count = 0; if (flags & LOOKUP_ROOT) { struct dentry *root = nd->root.dentry; struct inode *inode = root->d_inode; if (*s) { if (!d_can_lookup(root)) - return -ENOTDIR; + return ERR_PTR(-ENOTDIR); retval = inode_permission(inode, MAY_EXEC); if (retval) - return retval; + return ERR_PTR(retval); } nd->path = nd->root; nd->inode = inode; @@ -1890,7 +1891,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, } else { path_get(&nd->path); } - goto done; + return s; } nd->root.mnt = NULL; @@ -1926,14 +1927,14 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, struct dentry *dentry; if (!f.file) - return -EBADF; + return ERR_PTR(-EBADF); dentry = f.file->f_path.dentry; if (*s) { if (!d_can_lookup(dentry)) { fdput(f); - return -ENOTDIR; + return ERR_PTR(-ENOTDIR); } } @@ -1947,21 +1948,18 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, nd->inode = nd->path.dentry->d_inode; } fdput(f); - goto done; + return s; } nd->inode = nd->path.dentry->d_inode; if (!(flags & LOOKUP_RCU)) - goto done; + return s; if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq))) - goto done; + return s; if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; rcu_read_unlock(); - return -ECHILD; -done: - nd->total_link_count = 0; - return link_path_walk(s, nd); + return ERR_PTR(-ECHILD); } static void path_cleanup(struct nameidata *nd) @@ -2014,23 +2012,12 @@ static inline int lookup_last(struct nameidata *nd) static int path_lookupat(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { + const char *s = path_init(dfd, name, flags, nd); int err; - /* - * Path walking is largely split up into 2 different synchronisation - * schemes, rcu-walk and ref-walk (explained in - * Documentation/filesystems/path-lookup.txt). These share much of the - * path walk code, but some things particularly setup, cleanup, and - * following mounts are sufficiently divergent that functions are - * duplicated. Typically there is a function foo(), and its RCU - * analogue, foo_rcu(). - * - * -ECHILD is the error number of choice (just to avoid clashes) that - * is returned if some aspect of an rcu-walk fails. Such an error must - * be handled by restarting a traditional ref-walk (which will always - * be able to complete). - */ - err = path_init(dfd, name, flags, nd); + if (IS_ERR(s)) + return PTR_ERR(s); + err = link_path_walk(s, nd); if (!err) { while ((err = lookup_last(nd)) > 0) { err = trailing_symlink(nd); @@ -2075,7 +2062,11 @@ static int filename_lookup(int dfd, struct filename *name, static int path_parentat(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { - int err = path_init(dfd, name, flags | LOOKUP_PARENT, nd); + const char *s = path_init(dfd, name, flags, nd); + int err; + if (IS_ERR(s)) + return PTR_ERR(s); + err = link_path_walk(s, nd); if (!err) err = complete_walk(nd); path_cleanup(nd); @@ -2406,7 +2397,11 @@ static int path_mountpoint(int dfd, const struct filename *name, struct path *path, struct nameidata *nd, unsigned int flags) { - int err = path_init(dfd, name, flags, nd); + const char *s = path_init(dfd, name, flags, nd); + int err; + if (IS_ERR(s)) + return PTR_ERR(s); + err = link_path_walk(s, nd); if (unlikely(err)) goto out; @@ -3266,6 +3261,7 @@ out: static struct file *path_openat(int dfd, struct filename *pathname, struct nameidata *nd, const struct open_flags *op, int flags) { + const char *s; struct file *file; int opened = 0; int error; @@ -3281,7 +3277,12 @@ static struct file *path_openat(int dfd, struct filename *pathname, goto out2; } - error = path_init(dfd, pathname, flags, nd); + s = path_init(dfd, pathname, flags, nd); + if (IS_ERR(s)) { + put_filp(file); + return ERR_CAST(s); + } + error = link_path_walk(s, nd); if (unlikely(error)) goto out; -- cgit v1.2.3-58-ga151 From 3bdba28b72f5d2e7f3df031b04008b9a6fbdc775 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 17:37:07 -0400 Subject: namei: lift link_path_walk() call out of trailing_symlink() Make trailing_symlink() return the pathname to traverse or ERR_PTR(-E...). A subtle point is that for "magic" symlinks it returns "" now - that leads to link_path_walk("", nd), which is immediately returning 0 and we are back to the treatment of the last component, at whereever the damn thing has left us. Reduces the stack footprint - link_path_walk() called on more shallow stack now. Signed-off-by: Al Viro --- fs/namei.c | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 06c71200be48..46f4266d1f7f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1970,24 +1970,24 @@ static void path_cleanup(struct nameidata *nd) } } -static int trailing_symlink(struct nameidata *nd) +static const char *trailing_symlink(struct nameidata *nd) { const char *s; int error = may_follow_link(nd); if (unlikely(error)) { terminate_walk(nd); - return error; + return ERR_PTR(error); } nd->flags |= LOOKUP_PARENT; nd->stack[0].name = NULL; s = get_link(nd); if (unlikely(IS_ERR(s))) { terminate_walk(nd); - return PTR_ERR(s); + return s; } if (unlikely(!s)) - return 0; - return link_path_walk(s, nd); + s = ""; + return s; } static inline int lookup_last(struct nameidata *nd) @@ -2017,12 +2017,12 @@ static int path_lookupat(int dfd, const struct filename *name, if (IS_ERR(s)) return PTR_ERR(s); - err = link_path_walk(s, nd); - if (!err) { - while ((err = lookup_last(nd)) > 0) { - err = trailing_symlink(nd); - if (err) - break; + while (!(err = link_path_walk(s, nd)) + && ((err = lookup_last(nd)) > 0)) { + s = trailing_symlink(nd); + if (IS_ERR(s)) { + err = PTR_ERR(s); + break; } } @@ -2401,16 +2401,14 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, int err; if (IS_ERR(s)) return PTR_ERR(s); - err = link_path_walk(s, nd); - if (unlikely(err)) - goto out; - - while ((err = mountpoint_last(nd, path)) > 0) { - err = trailing_symlink(nd); - if (err) + while (!(err = link_path_walk(s, nd)) && + (err = mountpoint_last(nd, path)) > 0) { + s = trailing_symlink(nd); + if (IS_ERR(s)) { + err = PTR_ERR(s); break; + } } -out: path_cleanup(nd); return err; } @@ -3282,17 +3280,15 @@ static struct file *path_openat(int dfd, struct filename *pathname, put_filp(file); return ERR_CAST(s); } - error = link_path_walk(s, nd); - if (unlikely(error)) - goto out; - - while ((error = do_last(nd, file, op, &opened, pathname)) > 0) { + while (!(error = link_path_walk(s, nd)) && + (error = do_last(nd, file, op, &opened, pathname)) > 0) { nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); - error = trailing_symlink(nd); - if (unlikely(error)) + s = trailing_symlink(nd); + if (IS_ERR(s)) { + error = PTR_ERR(s); break; + } } -out: path_cleanup(nd); out2: if (!(opened & FILE_OPENED)) { -- cgit v1.2.3-58-ga151 From deb106c632d73c96b6b2b5ca71bacb8aef38fc7b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 18:05:21 -0400 Subject: namei: lift terminate_walk() all the way up Lift it from link_path_walk(), trailing_symlink(), lookup_last(), mountpoint_last(), complete_walk() and do_last(). A _lot_ of those suckers merge. Signed-off-by: Al Viro --- fs/namei.c | 106 ++++++++++++++++++++----------------------------------------- 1 file changed, 34 insertions(+), 72 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 46f4266d1f7f..27c38591b4ec 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -563,8 +563,6 @@ static inline int nd_alloc_stack(struct nameidata *nd) * to restart the path walk from the beginning in ref-walk mode. */ -static void terminate_walk(struct nameidata *nd); - /** * unlazy_walk - try to switch to ref-walk mode. * @nd: nameidata pathwalk data @@ -673,10 +671,8 @@ static int complete_walk(struct nameidata *nd) if (nd->flags & LOOKUP_RCU) { if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; - if (unlikely(unlazy_walk(nd, NULL))) { - terminate_walk(nd); + if (unlikely(unlazy_walk(nd, NULL))) return -ECHILD; - } } if (likely(!(nd->flags & LOOKUP_JUMPED))) @@ -692,7 +688,6 @@ static int complete_walk(struct nameidata *nd) if (!status) status = -ESTALE; - terminate_walk(nd); return status; } @@ -1858,7 +1853,6 @@ OK: break; } } - terminate_walk(nd); return err; } @@ -1974,38 +1968,26 @@ static const char *trailing_symlink(struct nameidata *nd) { const char *s; int error = may_follow_link(nd); - if (unlikely(error)) { - terminate_walk(nd); + if (unlikely(error)) return ERR_PTR(error); - } nd->flags |= LOOKUP_PARENT; nd->stack[0].name = NULL; s = get_link(nd); - if (unlikely(IS_ERR(s))) { - terminate_walk(nd); - return s; - } - if (unlikely(!s)) - s = ""; - return s; + return s ? s : ""; } static inline int lookup_last(struct nameidata *nd) { - int err; if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; nd->flags &= ~LOOKUP_PARENT; - err = walk_component(nd, + return walk_component(nd, nd->flags & LOOKUP_FOLLOW ? nd->depth ? WALK_PUT | WALK_GET : WALK_GET : 0); - if (err < 0) - terminate_walk(nd); - return err; } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ @@ -2025,16 +2007,14 @@ static int path_lookupat(int dfd, const struct filename *name, break; } } - if (!err) err = complete_walk(nd); - if (!err && nd->flags & LOOKUP_DIRECTORY) { - if (!d_can_lookup(nd->path.dentry)) { - path_put(&nd->path); + if (!err && nd->flags & LOOKUP_DIRECTORY) + if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; - } - } + if (err) + terminate_walk(nd); path_cleanup(nd); return err; @@ -2069,6 +2049,8 @@ static int path_parentat(int dfd, const struct filename *name, err = link_path_walk(s, nd); if (!err) err = complete_walk(nd); + if (err) + terminate_walk(nd); path_cleanup(nd); return err; } @@ -2320,10 +2302,8 @@ mountpoint_last(struct nameidata *nd, struct path *path) /* If we're in rcuwalk, drop out of it to handle last component */ if (nd->flags & LOOKUP_RCU) { - if (unlazy_walk(nd, NULL)) { - error = -ECHILD; - goto out; - } + if (unlazy_walk(nd, NULL)) + return -ECHILD; } nd->flags &= ~LOOKUP_PARENT; @@ -2331,7 +2311,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) if (unlikely(nd->last_type != LAST_NORM)) { error = handle_dots(nd, nd->last_type); if (error) - goto out; + return error; dentry = dget(nd->path.dentry); goto done; } @@ -2346,41 +2326,32 @@ mountpoint_last(struct nameidata *nd, struct path *path) */ dentry = d_alloc(dir, &nd->last); if (!dentry) { - error = -ENOMEM; mutex_unlock(&dir->d_inode->i_mutex); - goto out; + return -ENOMEM; } dentry = lookup_real(dir->d_inode, dentry, nd->flags); - error = PTR_ERR(dentry); if (IS_ERR(dentry)) { mutex_unlock(&dir->d_inode->i_mutex); - goto out; + return PTR_ERR(dentry); } } mutex_unlock(&dir->d_inode->i_mutex); done: if (d_is_negative(dentry)) { - error = -ENOENT; dput(dentry); - goto out; + return -ENOENT; } if (nd->depth) put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW); - if (unlikely(error)) { - if (error < 0) - goto out; + if (unlikely(error)) return error; - } mntget(path->mnt); follow_mount(path); - error = 0; -out: - terminate_walk(nd); - return error; + return 0; } /** @@ -2409,6 +2380,7 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, break; } } + terminate_walk(nd); path_cleanup(nd); return err; } @@ -2982,10 +2954,8 @@ static int do_last(struct nameidata *nd, if (nd->last_type != LAST_NORM) { error = handle_dots(nd, nd->last_type); - if (unlikely(error)) { - terminate_walk(nd); + if (unlikely(error)) return error; - } goto finish_open; } @@ -2998,7 +2968,7 @@ static int do_last(struct nameidata *nd, goto finish_lookup; if (error < 0) - goto out; + return error; BUG_ON(nd->inode != dir->d_inode); } else { @@ -3013,10 +2983,9 @@ static int do_last(struct nameidata *nd, return error; audit_inode(name, dir, LOOKUP_PARENT); - error = -EISDIR; /* trailing slashes? */ - if (nd->last.name[nd->last.len]) - goto out; + if (unlikely(nd->last.name[nd->last.len])) + return -EISDIR; } retry_lookup: @@ -3071,35 +3040,31 @@ retry_lookup: got_write = false; } - error = -EEXIST; - if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) - goto exit_dput; + if (unlikely((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) { + path_to_nameidata(&path, nd); + return -EEXIST; + } error = follow_managed(&path, nd); - if (error < 0) - goto out; + if (unlikely(error < 0)) + return error; BUG_ON(nd->flags & LOOKUP_RCU); inode = path.dentry->d_inode; - error = -ENOENT; - if (d_is_negative(path.dentry)) { + if (unlikely(d_is_negative(path.dentry))) { path_to_nameidata(&path, nd); - goto out; + return -ENOENT; } finish_lookup: if (nd->depth) put_link(nd); error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW); - if (unlikely(error)) { - if (error < 0) - goto out; + if (unlikely(error)) return error; - } if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) { path_to_nameidata(&path, nd); - error = -ELOOP; - goto out; + return -ELOOP; } if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) { @@ -3165,12 +3130,8 @@ out: if (got_write) mnt_drop_write(nd->path.mnt); path_put(&save_parent); - terminate_walk(nd); return error; -exit_dput: - path_put_conditional(&path, nd); - goto out; exit_fput: fput(file); goto out; @@ -3289,6 +3250,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, break; } } + terminate_walk(nd); path_cleanup(nd); out2: if (!(opened & FILE_OPENED)) { -- cgit v1.2.3-58-ga151 From 3595e2346cd91c223efddc79353fe7ced81f21bf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 16:54:45 -0400 Subject: link_path_walk: use explicit returns for failure exits Signed-off-by: Al Viro --- fs/namei.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 27c38591b4ec..756e150c28fc 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1772,7 +1772,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) err = may_lookup(nd); if (err) - break; + return err; hash_len = hash_name(name); @@ -1794,7 +1794,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) struct qstr this = { { .hash_len = hash_len }, .name = name }; err = parent->d_op->d_hash(parent, &this); if (err < 0) - break; + return err; hash_len = this.hash_len; name = this.name; } @@ -1829,15 +1829,13 @@ OK: err = walk_component(nd, WALK_GET); } if (err < 0) - break; + return err; if (err) { const char *s = get_link(nd); - if (unlikely(IS_ERR(s))) { - err = PTR_ERR(s); - break; - } + if (unlikely(IS_ERR(s))) + return PTR_ERR(s); err = 0; if (unlikely(!s)) { /* jumped */ @@ -1848,12 +1846,9 @@ OK: continue; } } - if (!d_can_lookup(nd->path.dentry)) { - err = -ENOTDIR; - break; - } + if (unlikely(!d_can_lookup(nd->path.dentry))) + return -ENOTDIR; } - return err; } static const char *path_init(int dfd, const struct filename *name, -- cgit v1.2.3-58-ga151 From 6e9918b7b3924f7a4cc3faa73c6e15d709ac239f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 May 2015 09:26:05 -0400 Subject: namei: explicitly pass seq number to unlazy_walk() when dentry != NULL Signed-off-by: Al Viro --- fs/namei.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 756e150c28fc..97315dfcf464 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -567,13 +567,14 @@ static inline int nd_alloc_stack(struct nameidata *nd) * unlazy_walk - try to switch to ref-walk mode. * @nd: nameidata pathwalk data * @dentry: child of nd->path.dentry or NULL + * @seq: seq number to check dentry against * Returns: 0 on success, -ECHILD on failure * * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry * for ref-walk mode. @dentry must be a path found by a do_lookup call on * @nd or NULL. Must be called from rcu-walk context. */ -static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) +static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) { struct fs_struct *fs = current->fs; struct dentry *parent = nd->path.dentry; @@ -615,7 +616,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) } else { if (!lockref_get_not_dead(&dentry->d_lockref)) goto out; - if (read_seqcount_retry(&dentry->d_seq, nd->seq)) + if (read_seqcount_retry(&dentry->d_seq, seq)) goto drop_dentry; } @@ -671,7 +672,7 @@ static int complete_walk(struct nameidata *nd) if (nd->flags & LOOKUP_RCU) { if (!(nd->flags & LOOKUP_ROOT)) nd->root.mnt = NULL; - if (unlikely(unlazy_walk(nd, NULL))) + if (unlikely(unlazy_walk(nd, NULL, 0))) return -ECHILD; } @@ -1451,7 +1452,7 @@ static int lookup_fast(struct nameidata *nd, if (likely(__follow_mount_rcu(nd, path, inode))) return 0; unlazy: - if (unlazy_walk(nd, dentry)) + if (unlazy_walk(nd, dentry, nd->seq)) return -ECHILD; } else { dentry = __d_lookup(parent, &nd->last); @@ -1511,7 +1512,7 @@ static inline int may_lookup(struct nameidata *nd) int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); if (err != -ECHILD) return err; - if (unlazy_walk(nd, NULL)) + if (unlazy_walk(nd, NULL, 0)) return -ECHILD; } return inode_permission(nd->inode, MAY_EXEC); @@ -1552,7 +1553,7 @@ static int pick_link(struct nameidata *nd, struct path *link) } if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != link->mnt || - unlazy_walk(nd, link->dentry))) { + unlazy_walk(nd, link->dentry, nd->seq))) { return -ECHILD; } } @@ -2297,7 +2298,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) /* If we're in rcuwalk, drop out of it to handle last component */ if (nd->flags & LOOKUP_RCU) { - if (unlazy_walk(nd, NULL)) + if (unlazy_walk(nd, NULL, 0)) return -ECHILD; } -- cgit v1.2.3-58-ga151 From 254cf582127b200ea6d4e55c9b4965485c3fe4c8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 5 May 2015 09:40:46 -0400 Subject: namei: don't mangle nd->seq in lookup_fast() Signed-off-by: Al Viro --- fs/namei.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 97315dfcf464..e3214506016d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1134,7 +1134,7 @@ static inline int managed_dentry_rcu(struct dentry *dentry) * we meet a managed dentry that would need blocking. */ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, - struct inode **inode) + struct inode **inode, unsigned *seqp) { for (;;) { struct mount *mounted; @@ -1161,7 +1161,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, path->mnt = &mounted->mnt; path->dentry = mounted->mnt.mnt_root; nd->flags |= LOOKUP_JUMPED; - nd->seq = read_seqcount_begin(&path->dentry->d_seq); + *seqp = read_seqcount_begin(&path->dentry->d_seq); /* * Update the inode too. We don't need to re-check the * dentry sequence number here after this d_inode read, @@ -1397,7 +1397,8 @@ static struct dentry *__lookup_hash(struct qstr *name, * It _is_ time-critical. */ static int lookup_fast(struct nameidata *nd, - struct path *path, struct inode **inode) + struct path *path, struct inode **inode, + unsigned *seqp) { struct vfsmount *mnt = nd->path.mnt; struct dentry *dentry, *parent = nd->path.dentry; @@ -1437,8 +1438,8 @@ static int lookup_fast(struct nameidata *nd, */ if (__read_seqcount_retry(&parent->d_seq, nd->seq)) return -ECHILD; - nd->seq = seq; + *seqp = seq; if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) { status = d_revalidate(dentry, nd->flags); if (unlikely(status <= 0)) { @@ -1449,10 +1450,10 @@ static int lookup_fast(struct nameidata *nd, } path->mnt = mnt; path->dentry = dentry; - if (likely(__follow_mount_rcu(nd, path, inode))) + if (likely(__follow_mount_rcu(nd, path, inode, seqp))) return 0; unlazy: - if (unlazy_walk(nd, dentry, nd->seq)) + if (unlazy_walk(nd, dentry, seq)) return -ECHILD; } else { dentry = __d_lookup(parent, &nd->last); @@ -1543,7 +1544,7 @@ static void terminate_walk(struct nameidata *nd) put_link(nd); } -static int pick_link(struct nameidata *nd, struct path *link) +static int pick_link(struct nameidata *nd, struct path *link, unsigned seq) { int error; struct saved *last; @@ -1553,7 +1554,7 @@ static int pick_link(struct nameidata *nd, struct path *link) } if (nd->flags & LOOKUP_RCU) { if (unlikely(nd->path.mnt != link->mnt || - unlazy_walk(nd, link->dentry, nd->seq))) { + unlazy_walk(nd, link->dentry, seq))) { return -ECHILD; } } @@ -1577,13 +1578,14 @@ static int pick_link(struct nameidata *nd, struct path *link) * so we keep a cache of "no, this doesn't need follow_link" * for the common case. */ -static inline int should_follow_link(struct nameidata *nd, struct path *link, int follow) +static inline int should_follow_link(struct nameidata *nd, struct path *link, + int follow, unsigned seq) { if (likely(!d_is_symlink(link->dentry))) return 0; if (!follow) return 0; - return pick_link(nd, link); + return pick_link(nd, link, seq); } enum {WALK_GET = 1, WALK_PUT = 2}; @@ -1592,6 +1594,7 @@ static int walk_component(struct nameidata *nd, int flags) { struct path path; struct inode *inode; + unsigned seq; int err; /* * "." and ".." are special - ".." especially so because it has @@ -1604,7 +1607,7 @@ static int walk_component(struct nameidata *nd, int flags) put_link(nd); return err; } - err = lookup_fast(nd, &path, &inode); + err = lookup_fast(nd, &path, &inode, &seq); if (unlikely(err)) { if (err < 0) return err; @@ -1614,6 +1617,7 @@ static int walk_component(struct nameidata *nd, int flags) return err; inode = path.dentry->d_inode; + seq = 0; /* we are already out of RCU mode */ err = -ENOENT; if (d_is_negative(path.dentry)) goto out_path_put; @@ -1621,11 +1625,12 @@ static int walk_component(struct nameidata *nd, int flags) if (flags & WALK_PUT) put_link(nd); - err = should_follow_link(nd, &path, flags & WALK_GET); + err = should_follow_link(nd, &path, flags & WALK_GET, seq); if (unlikely(err)) return err; path_to_nameidata(&path, nd); nd->inode = inode; + nd->seq = seq; return 0; out_path_put: @@ -2342,7 +2347,7 @@ done: put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; - error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW); + error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW, 0); if (unlikely(error)) return error; mntget(path->mnt); @@ -2939,6 +2944,7 @@ static int do_last(struct nameidata *nd, bool will_truncate = (open_flag & O_TRUNC) != 0; bool got_write = false; int acc_mode = op->acc_mode; + unsigned seq; struct inode *inode; struct path save_parent = { .dentry = NULL, .mnt = NULL }; struct path path; @@ -2959,7 +2965,7 @@ static int do_last(struct nameidata *nd, if (nd->last.name[nd->last.len]) nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; /* we _can_ be in RCU mode here */ - error = lookup_fast(nd, &path, &inode); + error = lookup_fast(nd, &path, &inode, &seq); if (likely(!error)) goto finish_lookup; @@ -3047,6 +3053,7 @@ retry_lookup: BUG_ON(nd->flags & LOOKUP_RCU); inode = path.dentry->d_inode; + seq = 0; /* out of RCU mode, so the value doesn't matter */ if (unlikely(d_is_negative(path.dentry))) { path_to_nameidata(&path, nd); return -ENOENT; @@ -3054,7 +3061,7 @@ retry_lookup: finish_lookup: if (nd->depth) put_link(nd); - error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW); + error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW, seq); if (unlikely(error)) return error; @@ -3072,6 +3079,7 @@ finish_lookup: } nd->inode = inode; + nd->seq = seq; /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ finish_open: error = complete_walk(nd); -- cgit v1.2.3-58-ga151 From 237d8b327abb428ff39b49e32ce07fdac468e32f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 09:21:14 -0400 Subject: namei: store inode in nd->stack[] Signed-off-by: Al Viro --- fs/namei.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index e3214506016d..366b0f348d1c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -507,6 +507,7 @@ struct nameidata { struct path link; void *cookie; const char *name; + struct inode *inode; } *stack, internal[EMBEDDED_LEVELS]; }; @@ -746,7 +747,7 @@ void nd_jump_link(struct path *path) static inline void put_link(struct nameidata *nd) { struct saved *last = nd->stack + --nd->depth; - struct inode *inode = last->link.dentry->d_inode; + struct inode *inode = last->inode; if (last->cookie && inode->i_op->put_link) inode->i_op->put_link(last->link.dentry, last->cookie); path_put(&last->link); @@ -779,7 +780,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if owner and follower match. */ - inode = nd->stack[0].link.dentry->d_inode; + inode = nd->stack[0].inode; if (uid_eq(current_cred()->fsuid, inode->i_uid)) return 0; @@ -870,7 +871,7 @@ const char *get_link(struct nameidata *nd) { struct saved *last = nd->stack + nd->depth - 1; struct dentry *dentry = last->link.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = last->inode; int error; const char *res; @@ -1569,6 +1570,7 @@ static int pick_link(struct nameidata *nd, struct path *link, unsigned seq) last = nd->stack + nd->depth++; last->link = *link; last->cookie = NULL; + last->inode = d_backing_inode(link->dentry); return 1; } -- cgit v1.2.3-58-ga151 From 63afdfc781e10c6b3ec38274e6163422876caa9a Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 6 May 2015 15:59:00 +0100 Subject: VFS: Handle lower layer dentry/inode in pathwalk Make use of d_backing_inode() in pathwalk to gain access to an inode or dentry that's on a lower layer. Signed-off-by: David Howells --- fs/namei.c | 10 +++++----- fs/open.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 366b0f348d1c..bcacb3100317 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1423,7 +1423,7 @@ static int lookup_fast(struct nameidata *nd, * This sequence count validates that the inode matches * the dentry name information from lookup. */ - *inode = dentry->d_inode; + *inode = d_backing_inode(dentry); negative = d_is_negative(dentry); if (read_seqcount_retry(&dentry->d_seq, seq)) return -ECHILD; @@ -1483,7 +1483,7 @@ unlazy: path->dentry = dentry; err = follow_managed(path, nd); if (likely(!err)) - *inode = path->dentry->d_inode; + *inode = d_backing_inode(path->dentry); return err; need_lookup: @@ -1618,7 +1618,7 @@ static int walk_component(struct nameidata *nd, int flags) if (err < 0) return err; - inode = path.dentry->d_inode; + inode = d_backing_inode(path.dentry); seq = 0; /* we are already out of RCU mode */ err = -ENOENT; if (d_is_negative(path.dentry)) @@ -2471,7 +2471,7 @@ EXPORT_SYMBOL(__check_sticky); */ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) { - struct inode *inode = victim->d_inode; + struct inode *inode = d_backing_inode(victim); int error; if (d_is_negative(victim)) @@ -3054,7 +3054,7 @@ retry_lookup: return error; BUG_ON(nd->flags & LOOKUP_RCU); - inode = path.dentry->d_inode; + inode = d_backing_inode(path.dentry); seq = 0; /* out of RCU mode, so the value doesn't matter */ if (unlikely(d_is_negative(path.dentry))) { path_to_nameidata(&path, nd); diff --git a/fs/open.c b/fs/open.c index 98e5a52dc68c..e0250bdcc440 100644 --- a/fs/open.c +++ b/fs/open.c @@ -367,7 +367,7 @@ retry: if (res) goto out; - inode = path.dentry->d_inode; + inode = d_backing_inode(path.dentry); if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* -- cgit v1.2.3-58-ga151 From 181548c05147154605711f3b1cf863267b5b8f7a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 19:54:34 -0400 Subject: namei: pick_link() callers already have inode no need to refetch (and once we move unlazy out of there, recheck ->d_seq). Signed-off-by: Al Viro --- fs/namei.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bcacb3100317..33b655de0ec0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1545,7 +1545,8 @@ static void terminate_walk(struct nameidata *nd) put_link(nd); } -static int pick_link(struct nameidata *nd, struct path *link, unsigned seq) +static int pick_link(struct nameidata *nd, struct path *link, + struct inode *inode, unsigned seq) { int error; struct saved *last; @@ -1570,7 +1571,7 @@ static int pick_link(struct nameidata *nd, struct path *link, unsigned seq) last = nd->stack + nd->depth++; last->link = *link; last->cookie = NULL; - last->inode = d_backing_inode(link->dentry); + last->inode = inode; return 1; } @@ -1581,13 +1582,14 @@ static int pick_link(struct nameidata *nd, struct path *link, unsigned seq) * for the common case. */ static inline int should_follow_link(struct nameidata *nd, struct path *link, - int follow, unsigned seq) + int follow, + struct inode *inode, unsigned seq) { if (likely(!d_is_symlink(link->dentry))) return 0; if (!follow) return 0; - return pick_link(nd, link, seq); + return pick_link(nd, link, inode, seq); } enum {WALK_GET = 1, WALK_PUT = 2}; @@ -1627,7 +1629,7 @@ static int walk_component(struct nameidata *nd, int flags) if (flags & WALK_PUT) put_link(nd); - err = should_follow_link(nd, &path, flags & WALK_GET, seq); + err = should_follow_link(nd, &path, flags & WALK_GET, inode, seq); if (unlikely(err)) return err; path_to_nameidata(&path, nd); @@ -2349,7 +2351,8 @@ done: put_link(nd); path->dentry = dentry; path->mnt = nd->path.mnt; - error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW, 0); + error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW, + d_backing_inode(dentry), 0); if (unlikely(error)) return error; mntget(path->mnt); @@ -3063,7 +3066,8 @@ retry_lookup: finish_lookup: if (nd->depth) put_link(nd); - error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW, seq); + error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW, + inode, seq); if (unlikely(error)) return error; -- cgit v1.2.3-58-ga151 From 7b20ea2579238f5e0da4bc93276c1b63c960c9ef Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:39 +1100 Subject: security/selinux: pass 'flags' arg to avc_audit() and avc_has_perm_flags() This allows MAY_NOT_BLOCK to be passed, in RCU-walk mode, through the new avc_has_perm_flags() to avc_audit() and thence the slow_avc_audit. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- security/selinux/avc.c | 18 +++++++++++++++++- security/selinux/hooks.c | 2 +- security/selinux/include/avc.h | 9 +++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 3c17dda9571d..0b122b1421a9 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -761,7 +761,23 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); - rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); + rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0); + if (rc2) + return rc2; + return rc; +} + +int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass, + u32 requested, struct common_audit_data *auditdata, + int flags) +{ + struct av_decision avd; + int rc, rc2; + + rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd); + + rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, + auditdata, flags); if (rc2) return rc2; return rc; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 801622947282..d56a82967d5e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1564,7 +1564,7 @@ static int cred_has_capability(const struct cred *cred, rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); if (audit == SECURITY_CAP_AUDIT) { - int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); + int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0); if (rc2) return rc2; } diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index ddf8eec03f21..5973c327c54e 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -130,7 +130,8 @@ static inline int avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct av_decision *avd, int result, - struct common_audit_data *a) + struct common_audit_data *a, + int flags) { u32 audited, denied; audited = avc_audit_required(requested, avd, result, 0, &denied); @@ -138,7 +139,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, return 0; return slow_avc_audit(ssid, tsid, tclass, requested, audited, denied, result, - a, 0); + a, flags); } #define AVC_STRICT 1 /* Ignore permissive mode. */ @@ -150,6 +151,10 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested, struct common_audit_data *auditdata); +int avc_has_perm_flags(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct common_audit_data *auditdata, + int flags); u32 avc_policy_seqno(void); -- cgit v1.2.3-58-ga151 From bda0be7ad994812960e9f8f2d2757f72cb4a96cb Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:39 +1100 Subject: security: make inode_follow_link RCU-walk aware inode_follow_link now takes an inode and rcu flag as well as the dentry. inode is used in preference to d_backing_inode(dentry), particularly in RCU-walk mode. selinux_inode_follow_link() gets dentry_has_perm() and inode_has_perm() open-coded into it so that it can call avc_has_perm_flags() in way that is safe if LOOKUP_RCU is set. Calling avc_has_perm_flags() with rcu_read_lock() held means that when avc_has_perm_noaudit calls avc_compute_av(), the attempt to rcu_read_unlock() before calling security_compute_av() will not actually drop the RCU read-lock. However as security_compute_av() is completely in a read_lock()ed region, it should be safe with the RCU read-lock held. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- fs/namei.c | 5 +++-- include/linux/security.h | 12 +++++++++--- security/capability.c | 3 ++- security/security.c | 7 ++++--- security/selinux/hooks.c | 16 ++++++++++++++-- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 33b655de0ec0..0fa7af23cff6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -881,8 +881,9 @@ const char *get_link(struct nameidata *nd) touch_atime(&last->link); - error = security_inode_follow_link(dentry); - if (error) + error = security_inode_follow_link(dentry, inode, + nd->flags & LOOKUP_RCU); + if (unlikely(error)) return ERR_PTR(error); nd->last_type = LAST_BIND; diff --git a/include/linux/security.h b/include/linux/security.h index 62a66202ecf1..52febde52479 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -476,6 +476,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @inode_follow_link: * Check permission to follow a symbolic link when looking up a pathname. * @dentry contains the dentry structure for the link. + * @inode contains the inode, which itself is not stable in RCU-walk + * @rcu indicates whether we are in RCU-walk mode. * Return 0 if permission is granted. * @inode_permission: * Check permission before accessing an inode. This hook is called by the @@ -1551,7 +1553,8 @@ struct security_operations { int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); int (*inode_readlink) (struct dentry *dentry); - int (*inode_follow_link) (struct dentry *dentry); + int (*inode_follow_link) (struct dentry *dentry, struct inode *inode, + bool rcu); int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); int (*inode_getattr) (const struct path *path); @@ -1837,7 +1840,8 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); int security_inode_readlink(struct dentry *dentry); -int security_inode_follow_link(struct dentry *dentry); +int security_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu); int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct dentry *dentry, struct iattr *attr); int security_inode_getattr(const struct path *path); @@ -2239,7 +2243,9 @@ static inline int security_inode_readlink(struct dentry *dentry) return 0; } -static inline int security_inode_follow_link(struct dentry *dentry) +static inline int security_inode_follow_link(struct dentry *dentry, + struct inode *inode, + bool rcu) { return 0; } diff --git a/security/capability.c b/security/capability.c index fae99db180ca..7d3f38fe02ba 100644 --- a/security/capability.c +++ b/security/capability.c @@ -209,7 +209,8 @@ static int cap_inode_readlink(struct dentry *dentry) return 0; } -static int cap_inode_follow_link(struct dentry *dentry) +static int cap_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu) { return 0; } diff --git a/security/security.c b/security/security.c index d7c30b03e144..04c8feca081a 100644 --- a/security/security.c +++ b/security/security.c @@ -581,11 +581,12 @@ int security_inode_readlink(struct dentry *dentry) return security_ops->inode_readlink(dentry); } -int security_inode_follow_link(struct dentry *dentry) +int security_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu) { - if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) + if (unlikely(IS_PRIVATE(inode))) return 0; - return security_ops->inode_follow_link(dentry); + return security_ops->inode_follow_link(dentry, inode, rcu); } int security_inode_permission(struct inode *inode, int mask) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d56a82967d5e..ffa5a642629a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2861,11 +2861,23 @@ static int selinux_inode_readlink(struct dentry *dentry) return dentry_has_perm(cred, dentry, FILE__READ); } -static int selinux_inode_follow_link(struct dentry *dentry) +static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, + bool rcu) { const struct cred *cred = current_cred(); + struct common_audit_data ad; + struct inode_security_struct *isec; + u32 sid; - return dentry_has_perm(cred, dentry, FILE__READ); + validate_creds(cred); + + ad.type = LSM_AUDIT_DATA_DENTRY; + ad.u.dentry = dentry; + sid = cred_sid(cred); + isec = inode->i_security; + + return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, + rcu ? MAY_NOT_BLOCK : 0); } static noinline int audit_inode_permission(struct inode *inode, -- cgit v1.2.3-58-ga151 From 5f2c4179e129bdc47870a81a65d0aff85aa18293 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 11:14:26 -0400 Subject: switch ->put_link() from dentry to inode only one instance looks at that argument at all; that sole exception wants inode rather than dentry. Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 +- Documentation/filesystems/vfs.txt | 2 +- drivers/staging/lustre/lustre/llite/symlink.c | 2 +- fs/configfs/symlink.c | 2 +- fs/f2fs/namei.c | 2 +- fs/fuse/dir.c | 2 +- fs/hostfs/hostfs_kern.c | 2 +- fs/hppfs/hppfs.c | 8 ++++---- fs/kernfs/symlink.c | 2 +- fs/libfs.c | 2 +- fs/namei.c | 13 +++++++------ fs/overlayfs/inode.c | 4 ++-- fs/proc/inode.c | 2 +- include/linux/fs.h | 6 +++--- mm/shmem.c | 2 +- 15 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 5b5b4f54c033..6a34a0f4d37c 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -51,7 +51,7 @@ prototypes: struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); const char *(*follow_link) (struct dentry *, void **); - void (*put_link) (struct dentry *, void *); + void (*put_link) (struct inode *, void *); void (*truncate) (struct inode *); int (*permission) (struct inode *, int, unsigned int); int (*get_acl)(struct inode *, int); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 0dec8c880be6..542d9352d0f2 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -351,7 +351,7 @@ struct inode_operations { struct inode *, struct dentry *, unsigned int); int (*readlink) (struct dentry *, char __user *,int); const char *(*follow_link) (struct dentry *, void **); - void (*put_link) (struct dentry *, void *); + void (*put_link) (struct inode *, void *); int (*permission) (struct inode *, int); int (*get_acl)(struct inode *, int); int (*setattr) (struct dentry *, struct iattr *); diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index f3be3bf0f66f..69b203651905 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -141,7 +141,7 @@ static const char *ll_follow_link(struct dentry *dentry, void **cookie) return symname; } -static void ll_put_link(struct dentry *dentry, void *cookie) +static void ll_put_link(struct inode *unused, void *cookie) { ptlrpc_req_finished(cookie); } diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index 0ace75649009..bc464c26e00e 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -296,7 +296,7 @@ static const char *configfs_follow_link(struct dentry *dentry, void **cookie) return ERR_PTR(error); } -static void configfs_put_link(struct dentry *dentry, void *cookie) +static void configfs_put_link(struct inode *unused, void *cookie) { free_page((unsigned long)cookie); } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index cd05a7c91533..71765d062914 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -301,7 +301,7 @@ static const char *f2fs_follow_link(struct dentry *dentry, void **cookie) const char *link = page_follow_link_light(dentry, cookie); if (!IS_ERR(link) && !*link) { /* this is broken symlink case */ - page_put_link(dentry, *cookie); + page_put_link(NULL, *cookie); link = ERR_PTR(-ENOENT); } return link; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index d5cdef8b7f3a..9e704c124392 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1395,7 +1395,7 @@ static const char *fuse_follow_link(struct dentry *dentry, void **cookie) return link; } -static void fuse_put_link(struct dentry *dentry, void *cookie) +static void fuse_put_link(struct inode *unused, void *cookie) { free_page((unsigned long) cookie); } diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 7b6ed7a908f6..4a437ab5f296 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -915,7 +915,7 @@ static const char *hostfs_follow_link(struct dentry *dentry, void **cookie) return *cookie = link; } -static void hostfs_put_link(struct dentry *dentry, void *cookie) +static void hostfs_put_link(struct inode *unused, void *cookie) { __putname(cookie); } diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index 15a774eb5bbf..2867837909a9 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -649,12 +649,12 @@ static const char *hppfs_follow_link(struct dentry *dentry, void **cookie) return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie); } -static void hppfs_put_link(struct dentry *dentry, void *cookie) +static void hppfs_put_link(struct inode *inode, void *cookie) { - struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry; + struct inode *proc_inode = d_inode(HPPFS_I(inode)->proc_dentry); - if (d_inode(proc_dentry)->i_op->put_link) - d_inode(proc_dentry)->i_op->put_link(proc_dentry, cookie); + if (proc_inode->i_op->put_link) + proc_inode->i_op->put_link(proc_inode, cookie); } static const struct inode_operations hppfs_dir_iops = { diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 366c5a17475e..f6aa2e5a76b4 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -126,7 +126,7 @@ static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) return *cookie = (char *)page; } -static void kernfs_iop_put_link(struct dentry *dentry, void *cookie) +static void kernfs_iop_put_link(struct inode *unused, void *cookie) { free_page((unsigned long)cookie); } diff --git a/fs/libfs.c b/fs/libfs.c index c5f3373e326b..01c337b0fec8 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1024,7 +1024,7 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync) } EXPORT_SYMBOL(noop_fsync); -void kfree_put_link(struct dentry *dentry, void *cookie) +void kfree_put_link(struct inode *unused, void *cookie) { kfree(cookie); } diff --git a/fs/namei.c b/fs/namei.c index 0fa7af23cff6..43034046a0e1 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -749,7 +749,7 @@ static inline void put_link(struct nameidata *nd) struct saved *last = nd->stack + --nd->depth; struct inode *inode = last->inode; if (last->cookie && inode->i_op->put_link) - inode->i_op->put_link(last->link.dentry, last->cookie); + inode->i_op->put_link(inode, last->cookie); path_put(&last->link); } @@ -4444,17 +4444,18 @@ EXPORT_SYMBOL(readlink_copy); int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) { void *cookie; - const char *link = dentry->d_inode->i_link; + struct inode *inode = d_inode(dentry); + const char *link = inode->i_link; int res; if (!link) { - link = dentry->d_inode->i_op->follow_link(dentry, &cookie); + link = inode->i_op->follow_link(dentry, &cookie); if (IS_ERR(link)) return PTR_ERR(link); } res = readlink_copy(buffer, buflen, link); - if (dentry->d_inode->i_op->put_link) - dentry->d_inode->i_op->put_link(dentry, cookie); + if (inode->i_op->put_link) + inode->i_op->put_link(inode, cookie); return res; } EXPORT_SYMBOL(generic_readlink); @@ -4496,7 +4497,7 @@ const char *page_follow_link_light(struct dentry *dentry, void **cookie) } EXPORT_SYMBOL(page_follow_link_light); -void page_put_link(struct dentry *dentry, void *cookie) +void page_put_link(struct inode *unused, void *cookie) { struct page *page = cookie; kunmap(page); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 9986833c9fcc..308379b2d0b2 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -174,7 +174,7 @@ static const char *ovl_follow_link(struct dentry *dentry, void **cookie) return ret; } -static void ovl_put_link(struct dentry *dentry, void *c) +static void ovl_put_link(struct inode *unused, void *c) { struct inode *realinode; struct ovl_link_data *data = c; @@ -183,7 +183,7 @@ static void ovl_put_link(struct dentry *dentry, void *c) return; realinode = data->realdentry->d_inode; - realinode->i_op->put_link(data->realdentry, data->cookie); + realinode->i_op->put_link(realinode, data->cookie); kfree(data); } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index eb35874fe09c..afe232b9df6e 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -402,7 +402,7 @@ static const char *proc_follow_link(struct dentry *dentry, void **cookie) return pde->data; } -static void proc_put_link(struct dentry *dentry, void *p) +static void proc_put_link(struct inode *unused, void *p) { unuse_pde(p); } diff --git a/include/linux/fs.h b/include/linux/fs.h index ed7c9f298759..f21e3328f991 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1613,7 +1613,7 @@ struct inode_operations { struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); - void (*put_link) (struct dentry *, void *); + void (*put_link) (struct inode *, void *); int (*create) (struct inode *,struct dentry *, umode_t, bool); int (*link) (struct dentry *,struct inode *,struct dentry *); @@ -2706,12 +2706,12 @@ extern const struct file_operations generic_ro_fops; extern int readlink_copy(char __user *, int, const char *); extern int page_readlink(struct dentry *, char __user *, int); extern const char *page_follow_link_light(struct dentry *, void **); -extern void page_put_link(struct dentry *, void *); +extern void page_put_link(struct inode *, void *); extern int __page_symlink(struct inode *inode, const char *symname, int len, int nofs); extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; -extern void kfree_put_link(struct dentry *, void *); +extern void kfree_put_link(struct inode *, void *); extern int generic_readlink(struct dentry *, char __user *, int); extern void generic_fillattr(struct inode *, struct kstat *); int vfs_getattr_nosec(struct path *path, struct kstat *stat); diff --git a/mm/shmem.c b/mm/shmem.c index e02682267046..a59087edf728 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2486,7 +2486,7 @@ static const char *shmem_follow_link(struct dentry *dentry, void **cookie) return kmap(page); } -static void shmem_put_link(struct dentry *dentry, void *cookie) +static void shmem_put_link(struct inode *unused, void *cookie) { struct page *page = cookie; kunmap(page); -- cgit v1.2.3-58-ga151 From ecc087ff14352aed52b8e775b4511e7f9cfc64ec Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 11:19:14 -0400 Subject: new helper: free_page_put_link() similar to kfree_put_link() Signed-off-by: Al Viro --- fs/configfs/symlink.c | 7 +------ fs/fuse/dir.c | 7 +------ fs/kernfs/symlink.c | 7 +------ fs/libfs.c | 6 ++++++ include/linux/fs.h | 1 + 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index bc464c26e00e..ec5c8325b503 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -296,15 +296,10 @@ static const char *configfs_follow_link(struct dentry *dentry, void **cookie) return ERR_PTR(error); } -static void configfs_put_link(struct inode *unused, void *cookie) -{ - free_page((unsigned long)cookie); -} - const struct inode_operations configfs_symlink_inode_operations = { .follow_link = configfs_follow_link, .readlink = generic_readlink, - .put_link = configfs_put_link, + .put_link = free_page_put_link, .setattr = configfs_setattr, }; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 9e704c124392..5e2e08712d3b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1395,11 +1395,6 @@ static const char *fuse_follow_link(struct dentry *dentry, void **cookie) return link; } -static void fuse_put_link(struct inode *unused, void *cookie) -{ - free_page((unsigned long) cookie); -} - static int fuse_dir_open(struct inode *inode, struct file *file) { return fuse_open_common(inode, file, true); @@ -1915,7 +1910,7 @@ static const struct inode_operations fuse_common_inode_operations = { static const struct inode_operations fuse_symlink_inode_operations = { .setattr = fuse_setattr, .follow_link = fuse_follow_link, - .put_link = fuse_put_link, + .put_link = free_page_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, .setxattr = fuse_setxattr, diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index f6aa2e5a76b4..db272528ab5b 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -126,11 +126,6 @@ static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie) return *cookie = (char *)page; } -static void kernfs_iop_put_link(struct inode *unused, void *cookie) -{ - free_page((unsigned long)cookie); -} - const struct inode_operations kernfs_symlink_iops = { .setxattr = kernfs_iop_setxattr, .removexattr = kernfs_iop_removexattr, @@ -138,7 +133,7 @@ const struct inode_operations kernfs_symlink_iops = { .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, .follow_link = kernfs_iop_follow_link, - .put_link = kernfs_iop_put_link, + .put_link = free_page_put_link, .setattr = kernfs_iop_setattr, .getattr = kernfs_iop_getattr, .permission = kernfs_iop_permission, diff --git a/fs/libfs.c b/fs/libfs.c index 01c337b0fec8..65e1feca8b98 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1030,6 +1030,12 @@ void kfree_put_link(struct inode *unused, void *cookie) } EXPORT_SYMBOL(kfree_put_link); +void free_page_put_link(struct inode *unused, void *cookie) +{ + free_page((unsigned long) cookie); +} +EXPORT_SYMBOL(free_page_put_link); + /* * nop .set_page_dirty method so that people can use .page_mkwrite on * anon inodes. diff --git a/include/linux/fs.h b/include/linux/fs.h index f21e3328f991..8f738512c874 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2712,6 +2712,7 @@ extern int __page_symlink(struct inode *inode, const char *symname, int len, extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; extern void kfree_put_link(struct inode *, void *); +extern void free_page_put_link(struct inode *, void *); extern int generic_readlink(struct dentry *, char __user *, int); extern void generic_fillattr(struct inode *, struct kstat *); int vfs_getattr_nosec(struct path *path, struct kstat *stat); -- cgit v1.2.3-58-ga151 From 6548fae2eca6b66c7257af6663fdbdf5a50745fd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 20:32:22 -0400 Subject: namei: make put_link() RCU-safe very simple - just make path_put() conditional on !RCU. Note that right now it doesn't get called in RCU mode - we leave it before getting anything into stack. Signed-off-by: Al Viro --- fs/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 43034046a0e1..998c3c2c9488 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -750,7 +750,8 @@ static inline void put_link(struct nameidata *nd) struct inode *inode = last->inode; if (last->cookie && inode->i_op->put_link) inode->i_op->put_link(inode, last->cookie); - path_put(&last->link); + if (!(nd->flags & LOOKUP_RCU)) + path_put(&last->link); } int sysctl_protected_symlinks __read_mostly = 0; -- cgit v1.2.3-58-ga151 From 31956502dd2c9432523d01373a9dc0e5931cfa1c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 7 May 2015 20:37:40 -0400 Subject: namei: make may_follow_link() safe in RCU mode We *can't* call that audit garbage in RCU mode - it's doing a weird mix of allocations (GFP_NOFS, immediately followed by GFP_KERNEL) and I'm not touching that... thing again. So if this security sclero^Whardening feature gets triggered when we are in RCU mode, tough - we'll fail with -ECHILD and have everything restarted in non-RCU mode. Only to hit the same test and fail, this time with EACCES and with (oh, rapture) an audit spew produced. Signed-off-by: Al Viro --- fs/namei.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 998c3c2c9488..20bf494307c9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -794,6 +794,9 @@ static inline int may_follow_link(struct nameidata *nd) if (uid_eq(parent->i_uid, inode->i_uid)) return 0; + if (nd->flags & LOOKUP_RCU) + return -ECHILD; + audit_log_link_denied("follow_link", &nd->stack[0].link); return -EACCES; } -- cgit v1.2.3-58-ga151 From 294d71ff2f020aa2ef7057a7bd10cf2ec71b5ee3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 11:43:53 -0400 Subject: new helper: __legitimize_mnt() same as legitimize_mnt(), except that it does *not* drop and regain rcu_read_lock; return values are 0 => grabbed a reference, we are fine 1 => failed, just go away -1 => failed, go away and mntput(bastard) when outside of rcu_read_lock Signed-off-by: Al Viro --- fs/mount.h | 1 + fs/namespace.c | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/mount.h b/fs/mount.h index 6a61c2b3e385..b5b8082bfa42 100644 --- a/fs/mount.h +++ b/fs/mount.h @@ -88,6 +88,7 @@ static inline int is_mounted(struct vfsmount *mnt) extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *); extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *); +extern int __legitimize_mnt(struct vfsmount *, unsigned); extern bool legitimize_mnt(struct vfsmount *, unsigned); extern void __detach_mounts(struct dentry *dentry); diff --git a/fs/namespace.c b/fs/namespace.c index 1b9e11167bae..9c1c43d0d4f1 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -590,24 +590,35 @@ static void delayed_free_vfsmnt(struct rcu_head *head) } /* call under rcu_read_lock */ -bool legitimize_mnt(struct vfsmount *bastard, unsigned seq) +int __legitimize_mnt(struct vfsmount *bastard, unsigned seq) { struct mount *mnt; if (read_seqretry(&mount_lock, seq)) - return false; + return 1; if (bastard == NULL) - return true; + return 0; mnt = real_mount(bastard); mnt_add_count(mnt, 1); if (likely(!read_seqretry(&mount_lock, seq))) - return true; + return 0; if (bastard->mnt_flags & MNT_SYNC_UMOUNT) { mnt_add_count(mnt, -1); - return false; + return 1; + } + return -1; +} + +/* call under rcu_read_lock */ +bool legitimize_mnt(struct vfsmount *bastard, unsigned seq) +{ + int res = __legitimize_mnt(bastard, seq); + if (likely(!res)) + return true; + if (unlikely(res < 0)) { + rcu_read_unlock(); + mntput(bastard); + rcu_read_lock(); } - rcu_read_unlock(); - mntput(bastard); - rcu_read_lock(); return false; } -- cgit v1.2.3-58-ga151 From 0450b2d120ed9e6d4ac7a6eade0ad116f69b88f7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 8 May 2015 13:23:53 -0400 Subject: namei: store seq numbers in nd->stack[] we'll need them for unlazy_walk() Signed-off-by: Al Viro --- fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index 20bf494307c9..92bf0312fa71 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -508,6 +508,7 @@ struct nameidata { void *cookie; const char *name; struct inode *inode; + unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; }; @@ -1577,6 +1578,7 @@ static int pick_link(struct nameidata *nd, struct path *link, last->link = *link; last->cookie = NULL; last->inode = inode; + last->seq = seq; return 1; } -- cgit v1.2.3-58-ga151 From 7973387a2f629c6ed0283920da44c6539c69aca0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 12:55:43 -0400 Subject: namei: make unlazy_walk and terminate_walk handle nd->stack, add unlazy_link We are almost done - primitives for leaving RCU mode are aware of nd->stack now, a new primitive for going to non-RCU mode when we have a symlink on hands added. The thing we are heavily relying upon is that *any* unlazy failure will be shortly followed by terminate_walk(), with no access to nameidata in between. So it's enough to leave the things in a state terminate_walk() would cope with. Signed-off-by: Al Viro --- fs/namei.c | 138 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 38 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 92bf0312fa71..a64df4c4aaef 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -554,6 +554,68 @@ static inline int nd_alloc_stack(struct nameidata *nd) return __nd_alloc_stack(nd); } +static void drop_links(struct nameidata *nd) +{ + int i = nd->depth; + while (i--) { + struct saved *last = nd->stack + i; + struct inode *inode = last->inode; + if (last->cookie && inode->i_op->put_link) { + inode->i_op->put_link(inode, last->cookie); + last->cookie = NULL; + } + } +} + +static void terminate_walk(struct nameidata *nd) +{ + drop_links(nd); + if (!(nd->flags & LOOKUP_RCU)) { + int i; + path_put(&nd->path); + for (i = 0; i < nd->depth; i++) + path_put(&nd->stack[i].link); + } else { + nd->flags &= ~LOOKUP_RCU; + if (!(nd->flags & LOOKUP_ROOT)) + nd->root.mnt = NULL; + rcu_read_unlock(); + } + nd->depth = 0; +} + +/* path_put is needed afterwards regardless of success or failure */ +static bool legitimize_path(struct nameidata *nd, + struct path *path, unsigned seq) +{ + int res = __legitimize_mnt(path->mnt, nd->m_seq); + if (unlikely(res)) { + if (res > 0) + path->mnt = NULL; + path->dentry = NULL; + return false; + } + if (unlikely(!lockref_get_not_dead(&path->dentry->d_lockref))) { + path->dentry = NULL; + return false; + } + return !read_seqcount_retry(&path->dentry->d_seq, seq); +} + +static bool legitimize_links(struct nameidata *nd) +{ + int i; + for (i = 0; i < nd->depth; i++) { + struct saved *last = nd->stack + i; + if (unlikely(!legitimize_path(nd, &last->link, last->seq))) { + drop_links(nd); + nd->depth = i + 1; + return false; + } + } + return true; +} + /* * Path walking has 2 modes, rcu-walk and ref-walk (see * Documentation/filesystems/path-lookup.txt). In situations when we can't @@ -575,6 +637,8 @@ static inline int nd_alloc_stack(struct nameidata *nd) * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry * for ref-walk mode. @dentry must be a path found by a do_lookup call on * @nd or NULL. Must be called from rcu-walk context. + * Nothing should touch nameidata between unlazy_walk() failure and + * terminate_walk(). */ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) { @@ -583,22 +647,13 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq BUG_ON(!(nd->flags & LOOKUP_RCU)); - /* - * After legitimizing the bastards, terminate_walk() - * will do the right thing for non-RCU mode, and all our - * subsequent exit cases should rcu_read_unlock() - * before returning. Do vfsmount first; if dentry - * can't be legitimized, just set nd->path.dentry to NULL - * and rely on dput(NULL) being a no-op. - */ - if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) - return -ECHILD; nd->flags &= ~LOOKUP_RCU; - - if (!lockref_get_not_dead(&parent->d_lockref)) { - nd->path.dentry = NULL; - goto out; - } + if (unlikely(!legitimize_links(nd))) + goto out2; + if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) + goto out2; + if (unlikely(!lockref_get_not_dead(&parent->d_lockref))) + goto out1; /* * For a negative lookup, the lookup sequence point is the parents @@ -628,8 +683,10 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq */ if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { spin_lock(&fs->lock); - if (nd->root.mnt != fs->root.mnt || nd->root.dentry != fs->root.dentry) - goto unlock_and_drop_dentry; + if (unlikely(!path_equal(&nd->root, &fs->root))) { + spin_unlock(&fs->lock); + goto drop_dentry; + } path_get(&nd->root); spin_unlock(&fs->lock); } @@ -637,12 +694,14 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq rcu_read_unlock(); return 0; -unlock_and_drop_dentry: - spin_unlock(&fs->lock); drop_dentry: rcu_read_unlock(); dput(dentry); goto drop_root_mnt; +out2: + nd->path.mnt = NULL; +out1: + nd->path.dentry = NULL; out: rcu_read_unlock(); drop_root_mnt: @@ -651,6 +710,24 @@ drop_root_mnt: return -ECHILD; } +static int unlazy_link(struct nameidata *nd, struct path *link, unsigned seq) +{ + if (unlikely(!legitimize_path(nd, link, seq))) { + drop_links(nd); + nd->depth = 0; + nd->flags &= ~LOOKUP_RCU; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + if (!(nd->flags & LOOKUP_ROOT)) + nd->root.mnt = NULL; + rcu_read_unlock(); + } else if (likely(unlazy_walk(nd, NULL, 0)) == 0) { + return 0; + } + path_put(link); + return -ECHILD; +} + static inline int d_revalidate(struct dentry *dentry, unsigned int flags) { return dentry->d_op->d_revalidate(dentry, flags); @@ -1537,20 +1614,6 @@ static inline int handle_dots(struct nameidata *nd, int type) return 0; } -static void terminate_walk(struct nameidata *nd) -{ - if (!(nd->flags & LOOKUP_RCU)) { - path_put(&nd->path); - } else { - nd->flags &= ~LOOKUP_RCU; - if (!(nd->flags & LOOKUP_ROOT)) - nd->root.mnt = NULL; - rcu_read_unlock(); - } - while (unlikely(nd->depth)) - put_link(nd); -} - static int pick_link(struct nameidata *nd, struct path *link, struct inode *inode, unsigned seq) { @@ -1561,13 +1624,12 @@ static int pick_link(struct nameidata *nd, struct path *link, return -ELOOP; } if (nd->flags & LOOKUP_RCU) { - if (unlikely(nd->path.mnt != link->mnt || - unlazy_walk(nd, link->dentry, seq))) { + if (unlikely(unlazy_link(nd, link, seq))) return -ECHILD; - } + } else { + if (link->mnt == nd->path.mnt) + mntget(link->mnt); } - if (link->mnt == nd->path.mnt) - mntget(link->mnt); error = nd_alloc_stack(nd); if (unlikely(error)) { path_put(link); -- cgit v1.2.3-58-ga151 From bc40aee053be1c2c443e812034f41e1a3cf34752 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 13:04:24 -0400 Subject: namei: don't unlazy until get_link() Signed-off-by: Al Viro --- fs/namei.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a64df4c4aaef..47b20086e9f3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -536,10 +536,19 @@ static void restore_nameidata(struct nameidata *old) static int __nd_alloc_stack(struct nameidata *nd) { - struct saved *p = kmalloc(MAXSYMLINKS * sizeof(struct saved), + struct saved *p; + + if (nd->flags & LOOKUP_RCU) { + p= kmalloc(MAXSYMLINKS * sizeof(struct saved), + GFP_ATOMIC); + if (unlikely(!p)) + return -ECHILD; + } else { + p= kmalloc(MAXSYMLINKS * sizeof(struct saved), GFP_KERNEL); - if (unlikely(!p)) - return -ENOMEM; + if (unlikely(!p)) + return -ENOMEM; + } memcpy(p, nd->internal, sizeof(nd->internal)); nd->stack = p; return 0; @@ -957,8 +966,10 @@ const char *get_link(struct nameidata *nd) int error; const char *res; - BUG_ON(nd->flags & LOOKUP_RCU); - + if (nd->flags & LOOKUP_RCU) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + } cond_resched(); touch_atime(&last->link); @@ -1623,17 +1634,21 @@ static int pick_link(struct nameidata *nd, struct path *link, path_to_nameidata(link, nd); return -ELOOP; } - if (nd->flags & LOOKUP_RCU) { - if (unlikely(unlazy_link(nd, link, seq))) - return -ECHILD; - } else { + if (!(nd->flags & LOOKUP_RCU)) { if (link->mnt == nd->path.mnt) mntget(link->mnt); } error = nd_alloc_stack(nd); if (unlikely(error)) { - path_put(link); - return error; + if (error == -ECHILD) { + if (unlikely(unlazy_link(nd, link, seq))) + return -ECHILD; + error = nd_alloc_stack(nd); + } + if (error) { + path_put(link); + return error; + } } last = nd->stack + nd->depth++; -- cgit v1.2.3-58-ga151 From 8fa9dd24667f2d6997ec21341019657342859d31 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:40 +1100 Subject: VFS/namei: make the use of touch_atime() in get_link() RCU-safe. touch_atime is not RCU-safe, and so cannot be called on an RCU walk. However, in situations where RCU-walk makes a difference, the symlink will likely to accessed much more often than it is useful to update the atime. So split out the test of "Does the atime actually need to be updated" into atime_needs_update(), and have get_link() unlazy if it finds that it will need to do that update. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- fs/inode.c | 30 +++++++++++++++++++++--------- fs/namei.c | 12 +++++++++--- include/linux/fs.h | 1 + 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 952fb4852e38..e8d62688ed91 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1585,36 +1585,47 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) * This function automatically handles read only file systems and media, * as well as the "noatime" flag and inode specific "noatime" markers. */ -void touch_atime(const struct path *path) +bool atime_needs_update(const struct path *path, struct inode *inode) { struct vfsmount *mnt = path->mnt; - struct inode *inode = d_inode(path->dentry); struct timespec now; if (inode->i_flags & S_NOATIME) - return; + return false; if (IS_NOATIME(inode)) - return; + return false; if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode)) - return; + return false; if (mnt->mnt_flags & MNT_NOATIME) - return; + return false; if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)) - return; + return false; now = current_fs_time(inode->i_sb); if (!relatime_need_update(mnt, inode, now)) - return; + return false; if (timespec_equal(&inode->i_atime, &now)) + return false; + + return true; +} + +void touch_atime(const struct path *path) +{ + struct vfsmount *mnt = path->mnt; + struct inode *inode = d_inode(path->dentry); + struct timespec now; + + if (!atime_needs_update(path, inode)) return; if (!sb_start_write_trylock(inode->i_sb)) return; - if (__mnt_want_write(mnt)) + if (__mnt_want_write(mnt) != 0) goto skip_update; /* * File systems can error out when updating inodes if they need to @@ -1625,6 +1636,7 @@ void touch_atime(const struct path *path) * We may also fail on filesystems that have the ability to make parts * of the fs read only, e.g. subvolumes in Btrfs. */ + now = current_fs_time(inode->i_sb); update_time(inode, &now, S_ATIME); __mnt_drop_write(mnt); skip_update: diff --git a/fs/namei.c b/fs/namei.c index 47b20086e9f3..d9f77ff60b55 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -966,13 +966,19 @@ const char *get_link(struct nameidata *nd) int error; const char *res; - if (nd->flags & LOOKUP_RCU) { + if (!(nd->flags & LOOKUP_RCU)) { + touch_atime(&last->link); + cond_resched(); + } else if (atime_needs_update(&last->link, inode)) { if (unlikely(unlazy_walk(nd, NULL, 0))) return ERR_PTR(-ECHILD); + touch_atime(&last->link); } - cond_resched(); - touch_atime(&last->link); + if (nd->flags & LOOKUP_RCU) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + } error = security_inode_follow_link(dentry, inode, nd->flags & LOOKUP_RCU); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8f738512c874..1426c435d455 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1880,6 +1880,7 @@ enum file_time_flags { S_VERSION = 8, }; +extern bool atime_needs_update(const struct path *, struct inode *); extern void touch_atime(const struct path *); static inline void file_accessed(struct file *file) { -- cgit v1.2.3-58-ga151 From 8c1b456689ac0b27e8e16b35190e89a02fd1f121 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 18:15:21 -0400 Subject: enable passing fast relative symlinks without dropping out of RCU mode Signed-off-by: Al Viro --- fs/namei.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index d9f77ff60b55..bf46e1010a74 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -975,11 +975,6 @@ const char *get_link(struct nameidata *nd) touch_atime(&last->link); } - if (nd->flags & LOOKUP_RCU) { - if (unlikely(unlazy_walk(nd, NULL, 0))) - return ERR_PTR(-ECHILD); - } - error = security_inode_follow_link(dentry, inode, nd->flags & LOOKUP_RCU); if (unlikely(error)) @@ -988,6 +983,10 @@ const char *get_link(struct nameidata *nd) nd->last_type = LAST_BIND; res = inode->i_link; if (!res) { + if (nd->flags & LOOKUP_RCU) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + } res = inode->i_op->follow_link(dentry, &last->cookie); if (IS_ERR_OR_NULL(res)) { last->cookie = NULL; @@ -995,6 +994,10 @@ const char *get_link(struct nameidata *nd) } } if (*res == '/') { + if (nd->flags & LOOKUP_RCU) { + if (unlikely(unlazy_walk(nd, NULL, 0))) + return ERR_PTR(-ECHILD); + } if (!nd->root.mnt) set_root(nd); path_put(&nd->path); -- cgit v1.2.3-58-ga151 From 8f47a0167c567de4ef552e26101b4f54a9b8ad48 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 19:02:01 -0400 Subject: namei: handle absolute symlinks without dropping out of RCU mode Signed-off-by: Al Viro --- fs/namei.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index bf46e1010a74..a5ed0d070a20 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -499,7 +499,7 @@ struct nameidata { struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags; - unsigned seq, m_seq; + unsigned seq, m_seq, root_seq; int last_type; unsigned depth; int total_link_count; @@ -788,14 +788,14 @@ static __always_inline void set_root(struct nameidata *nd) static __always_inline unsigned set_root_rcu(struct nameidata *nd) { struct fs_struct *fs = current->fs; - unsigned seq, res; + unsigned seq; do { seq = read_seqcount_begin(&fs->seq); nd->root = fs->root; - res = __read_seqcount_begin(&nd->root.dentry->d_seq); + nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq); } while (read_seqcount_retry(&fs->seq, seq)); - return res; + return nd->root_seq; } static void path_put_conditional(struct path *path, struct nameidata *nd) @@ -995,15 +995,23 @@ const char *get_link(struct nameidata *nd) } if (*res == '/') { if (nd->flags & LOOKUP_RCU) { - if (unlikely(unlazy_walk(nd, NULL, 0))) + struct dentry *d; + if (!nd->root.mnt) + set_root_rcu(nd); + nd->path = nd->root; + d = nd->path.dentry; + nd->inode = d->d_inode; + nd->seq = nd->root_seq; + if (unlikely(read_seqcount_retry(&d->d_seq, nd->seq))) return ERR_PTR(-ECHILD); + } else { + if (!nd->root.mnt) + set_root(nd); + path_put(&nd->path); + nd->path = nd->root; + path_get(&nd->root); + nd->inode = nd->path.dentry->d_inode; } - if (!nd->root.mnt) - set_root(nd); - path_put(&nd->path); - nd->path = nd->root; - path_get(&nd->root); - nd->inode = nd->path.dentry->d_inode; nd->flags |= LOOKUP_JUMPED; while (unlikely(*++res == '/')) ; @@ -1979,6 +1987,7 @@ static const char *path_init(int dfd, const struct filename *name, if (flags & LOOKUP_RCU) { rcu_read_lock(); nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq); + nd->root_seq = nd->seq; nd->m_seq = read_seqbegin(&mount_lock); } else { path_get(&nd->path); -- cgit v1.2.3-58-ga151 From 203bc643db59e2538e9a3f19be1636cdfd2bb2db Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 11 May 2015 08:29:30 -0400 Subject: update Documentation/filesystems/ regarding the follow_link/put_link changes Signed-off-by: Al Viro --- Documentation/filesystems/porting | 17 +++++++++++++++++ Documentation/filesystems/vfs.txt | 18 ++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index e69274de8d0c..3eae250254d5 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -483,3 +483,20 @@ in your dentry operations instead. -- [mandatory] ->aio_read/->aio_write are gone. Use ->read_iter/->write_iter. +--- +[recommended] + for embedded ("fast") symlinks just set inode->i_link to wherever the + symlink body is and use simple_follow_link() as ->follow_link(). +-- +[mandatory] + calling conventions for ->follow_link() have changed. Instead of returning + cookie and using nd_set_link() to store the body to traverse, we return + the body to traverse and store the cookie using explicit void ** argument. + nameidata isn't passed at all - nd_jump_link() doesn't need it and + nd_[gs]et_link() is gone. +-- +[mandatory] + calling conventions for ->put_link() have changed. It gets inode instead of + dentry, it does not get nameidata at all and it gets called only when cookie + is non-NULL. Note that link body isn't available anymore, so if you need it, + store it as cookie. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 542d9352d0f2..b403b29ef710 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -436,16 +436,18 @@ otherwise noted. follow_link: called by the VFS to follow a symbolic link to the inode it points to. Only required if you want to support - symbolic links. This method returns a void pointer cookie - that is passed to put_link(). + symbolic links. This method returns the symlink body + to traverse (and possibly resets the current position with + nd_jump_link()). If the body won't go away until the inode + is gone, nothing else is needed; if it needs to be otherwise + pinned, the data needed to release whatever we'd grabbed + is to be stored in void * variable passed by address to + follow_link() instance. put_link: called by the VFS to release resources allocated by - follow_link(). The cookie returned by follow_link() is passed - to this method as the last parameter. It is used by - filesystems such as NFS where page cache is not stable - (i.e. page that was installed when the symbolic link walk - started might not be in the page cache at the end of the - walk). + follow_link(). The cookie stored by follow_link() is passed + to this method as the last parameter; only called when + cookie isn't NULL. permission: called by the VFS to check for access rights on a POSIX-like filesystem. -- cgit v1.2.3-58-ga151 From 5a8d87e8ed1d6aabdb865fea31e337b2c627dbe0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 00:10:18 -0400 Subject: namei: unlazy_walk() doesn't need to mess with current->fs anymore now that we have ->root_seq, legitimize_path(&nd->root, nd->root_seq) will do just fine... Signed-off-by: Al Viro --- fs/namei.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a5ed0d070a20..0cccb24ac0b7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -651,7 +651,6 @@ static bool legitimize_links(struct nameidata *nd) */ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq) { - struct fs_struct *fs = current->fs; struct dentry *parent = nd->path.dentry; BUG_ON(!(nd->flags & LOOKUP_RCU)); @@ -691,13 +690,11 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq * still valid and get it if required. */ if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { - spin_lock(&fs->lock); - if (unlikely(!path_equal(&nd->root, &fs->root))) { - spin_unlock(&fs->lock); - goto drop_dentry; + if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq))) { + rcu_read_unlock(); + dput(dentry); + return -ECHILD; } - path_get(&nd->root); - spin_unlock(&fs->lock); } rcu_read_unlock(); -- cgit v1.2.3-58-ga151 From 539fcc0109346fca363257aa75c5e5a3916bf124 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 23 Feb 2015 00:20:40 -0500 Subject: lustre: kill unused macro (LOOKUP_CONTINUE) Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/llite/llite_internal.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index 5f918e3c4683..528af9011653 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -57,12 +57,6 @@ #define VM_FAULT_RETRY 0 #endif -/* Kernel 3.1 kills LOOKUP_CONTINUE, LOOKUP_PARENT is equivalent to it. - * seem kernel commit 49084c3bb2055c401f3493c13edae14d49128ca0 */ -#ifndef LOOKUP_CONTINUE -#define LOOKUP_CONTINUE LOOKUP_PARENT -#endif - /** Only used on client-side for indicating the tail of dir hash/offset. */ #define LL_DIR_END_OFF 0x7fffffffffffffffULL #define LL_DIR_END_OFF_32BIT 0x7fffffffUL -- cgit v1.2.3-58-ga151 From 322105e2fe20b51587a328e7121dc04ea361b083 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 08:29:13 -0400 Subject: lustre: kill unused helper Signed-off-by: Al Viro --- .../staging/lustre/lustre/include/linux/lustre_compat25.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h index 3925db160650..513c81f43d6e 100644 --- a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h +++ b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h @@ -189,22 +189,7 @@ static inline int ll_quota_off(struct super_block *sb, int off, int remount) #endif - -/* - * After 3.1, kernel's nameidata.intent.open.flags is different - * with lustre's lookup_intent.it_flags, as lustre's it_flags' - * lower bits equal to FMODE_xxx while kernel doesn't transliterate - * lower bits of nameidata.intent.open.flags to FMODE_xxx. - * */ #include -static inline int ll_namei_to_lookup_intent_flag(int flag) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) - flag = (flag & ~O_ACCMODE) | OPEN_FMODE(flag); -#endif - return flag; -} - #include # define ll_umode_t umode_t -- cgit v1.2.3-58-ga151 From 89076bc31950eee576ecc06460c23466e2d50939 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 08:29:38 -0400 Subject: get rid of assorted nameidata-related debris pointless forward declarations, stale comments Signed-off-by: Al Viro --- fs/9p/vfs_inode.c | 3 +-- fs/9p/vfs_inode_dotl.c | 3 +-- fs/ecryptfs/inode.c | 3 +-- fs/ntfs/namei.c | 2 +- include/linux/fs.h | 1 - include/linux/namei.h | 1 - include/linux/sched.h | 1 + 7 files changed, 5 insertions(+), 9 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 271f51af2f75..510040b04c96 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1226,8 +1226,7 @@ ino_t v9fs_qid2ino(struct p9_qid *qid) /** * v9fs_vfs_follow_link - follow a symlink path * @dentry: dentry for symlink - * @nd: nameidata - * + * @cookie: place to pass the data to put_link() */ static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie) diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 16658ed677c9..09e4433717b8 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -905,8 +905,7 @@ error: /** * v9fs_vfs_follow_link_dotl - follow a symlink path * @dentry: dentry for symlink - * @nd: nameidata - * + * @cookie: place to pass the data to put_link() */ static const char * diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 73d20ae92478..3c4db1172d22 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -170,7 +170,6 @@ out_unlock: * @directory_inode: inode of the new file's dentry's parent in ecryptfs * @ecryptfs_dentry: New file's dentry in ecryptfs * @mode: The mode of the new file - * @nd: nameidata of ecryptfs' parent's dentry & vfsmount * * Creates the underlying file and the eCryptfs inode which will link to * it. It will also update the eCryptfs directory inode to mimic the @@ -384,7 +383,7 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry, * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up - * @ecryptfs_nd: nameidata; may be NULL + * @flags: lookup flags * * Find a file on disk. If the file does not exist, then we'll add it to the * dentry cache and continue on to read it from the disk. diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 0f35b80d17fe..443abecf01b7 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -35,7 +35,7 @@ * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for - * @nd: lookup nameidata + * @flags: lookup flags * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the diff --git a/include/linux/fs.h b/include/linux/fs.h index 1426c435d455..b577e801b4af 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -38,7 +38,6 @@ struct backing_dev_info; struct export_operations; struct hd_geometry; struct iovec; -struct nameidata; struct kiocb; struct kobject; struct pipe_inode_info; diff --git a/include/linux/namei.h b/include/linux/namei.h index d756304aa09b..1208e489f83e 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -7,7 +7,6 @@ #include struct vfsmount; -struct nameidata; enum { MAX_NESTED_LINKS = 8 }; diff --git a/include/linux/sched.h b/include/linux/sched.h index f6c9b69d66f2..a1158c954f0f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -132,6 +132,7 @@ struct fs_struct; struct perf_event_context; struct blk_plug; struct filename; +struct nameidata; #define VMACACHE_BITS 2 #define VMACACHE_SIZE (1U << VMACACHE_BITS) -- cgit v1.2.3-58-ga151 From 99ff6cf0e67ada025ba8054a055862383355ec0e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 23 Mar 2015 13:37:38 +1100 Subject: Documentation: remove outdated information from automount-support.txt The guidelines for adding automount support to a filesystem in filesystems/automount-support.txt is out or date. filesystems/autofs4.txt contains more current text, so replace the out-of-date content with a reference to that. Signed-off-by: NeilBrown Signed-off-by: Al Viro --- Documentation/filesystems/automount-support.txt | 51 +++++++------------------ 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/Documentation/filesystems/automount-support.txt b/Documentation/filesystems/automount-support.txt index 7cac200e2a85..7eb762eb3136 100644 --- a/Documentation/filesystems/automount-support.txt +++ b/Documentation/filesystems/automount-support.txt @@ -1,41 +1,15 @@ -Support is available for filesystems that wish to do automounting support (such -as kAFS which can be found in fs/afs/). This facility includes allowing -in-kernel mounts to be performed and mountpoint degradation to be -requested. The latter can also be requested by userspace. +Support is available for filesystems that wish to do automounting +support (such as kAFS which can be found in fs/afs/ and NFS in +fs/nfs/). This facility includes allowing in-kernel mounts to be +performed and mountpoint degradation to be requested. The latter can +also be requested by userspace. ====================== IN-KERNEL AUTOMOUNTING ====================== -A filesystem can now mount another filesystem on one of its directories by the -following procedure: - - (1) Give the directory a follow_link() operation. - - When the directory is accessed, the follow_link op will be called, and - it will be provided with the location of the mountpoint in the nameidata - structure (vfsmount and dentry). - - (2) Have the follow_link() op do the following steps: - - (a) Call vfs_kern_mount() to call the appropriate filesystem to set up a - superblock and gain a vfsmount structure representing it. - - (b) Copy the nameidata provided as an argument and substitute the dentry - argument into it the copy. - - (c) Call do_add_mount() to install the new vfsmount into the namespace's - mountpoint tree, thus making it accessible to userspace. Use the - nameidata set up in (b) as the destination. - - If the mountpoint will be automatically expired, then do_add_mount() - should also be given the location of an expiration list (see further - down). - - (d) Release the path in the nameidata argument and substitute in the new - vfsmount and its root dentry. The ref counts on these will need - incrementing. +See section "Mount Traps" of Documentation/filesystems/autofs4.txt Then from userspace, you can just do something like: @@ -61,17 +35,18 @@ AUTOMATIC MOUNTPOINT EXPIRY =========================== Automatic expiration of mountpoints is easy, provided you've mounted the -mountpoint to be expired in the automounting procedure outlined above. +mountpoint to be expired in the automounting procedure outlined separately. To do expiration, you need to follow these steps: - (3) Create at least one list off which the vfsmounts to be expired can be - hung. Access to this list will be governed by the vfsmount_lock. + (1) Create at least one list off which the vfsmounts to be expired can be + hung. - (4) In step (2c) above, the call to do_add_mount() should be provided with a - pointer to this list. It will hang the vfsmount off of it if it succeeds. + (2) When a new mountpoint is created in the ->d_automount method, add + the mnt to the list using mnt_set_expiry() + mnt_set_expiry(newmnt, &afs_vfsmounts); - (5) When you want mountpoints to be expired, call mark_mounts_for_expiry() + (3) When you want mountpoints to be expired, call mark_mounts_for_expiry() with a pointer to this list. This will process the list, marking every vfsmount thereon for potential expiry on the next call. -- cgit v1.2.3-58-ga151 From aed434ada68573549d9f5106cde26b94c48e51c2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 12:22:47 -0400 Subject: namei: be careful with mountpoint crossings in follow_dotdot_rcu() Otherwise we are risking a hard error where nonlazy restart would be the right thing to do; it's a very narrow race with mount --move and most of the time it ends up being completely harmless, but it's possible to construct a case when we'll get a bogus hard error instead of falling back to non-lazy walk... For one thing, when crossing _into_ overmount of parent we need to check for mount_lock bumps when we get NULL from __lookup_mnt() as well. For another, and less exotically, we need to make sure that the data fetched in follow_up_rcu() had been consistent. ->mnt_mountpoint is pinned for as long as it is a mountpoint, but we need to check mount_lock after fetching to verify that. Signed-off-by: Al Viro --- fs/namei.c | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 0cccb24ac0b7..d5f45681ea88 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1018,21 +1018,6 @@ const char *get_link(struct nameidata *nd) return res; } -static int follow_up_rcu(struct path *path) -{ - struct mount *mnt = real_mount(path->mnt); - struct mount *parent; - struct dentry *mountpoint; - - parent = mnt->mnt_parent; - if (&parent->mnt == path->mnt) - return 0; - mountpoint = mnt->mnt_mountpoint; - path->dentry = mountpoint; - path->mnt = &parent->mnt; - return 1; -} - /* * follow_up - Find the mountpoint of path's vfsmount * @@ -1289,10 +1274,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) set_root_rcu(nd); while (1) { - if (nd->path.dentry == nd->root.dentry && - nd->path.mnt == nd->root.mnt) { + if (path_equal(&nd->path, &nd->root)) break; - } if (nd->path.dentry != nd->path.mnt->mnt_root) { struct dentry *old = nd->path.dentry; struct dentry *parent = old->d_parent; @@ -1300,34 +1283,42 @@ static int follow_dotdot_rcu(struct nameidata *nd) inode = parent->d_inode; seq = read_seqcount_begin(&parent->d_seq); - if (read_seqcount_retry(&old->d_seq, nd->seq)) - goto failed; + if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq))) + return -ECHILD; nd->path.dentry = parent; nd->seq = seq; break; + } else { + struct mount *mnt = real_mount(nd->path.mnt); + struct mount *mparent = mnt->mnt_parent; + struct dentry *mountpoint = mnt->mnt_mountpoint; + struct inode *inode2 = mountpoint->d_inode; + unsigned seq = read_seqcount_begin(&mountpoint->d_seq); + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return -ECHILD; + if (&mparent->mnt == nd->path.mnt) + break; + /* we know that mountpoint was pinned */ + nd->path.dentry = mountpoint; + nd->path.mnt = &mparent->mnt; + inode = inode2; + nd->seq = seq; } - if (!follow_up_rcu(&nd->path)) - break; - inode = nd->path.dentry->d_inode; - nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); } - while (d_mountpoint(nd->path.dentry)) { + while (unlikely(d_mountpoint(nd->path.dentry))) { struct mount *mounted; mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry); + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return -ECHILD; if (!mounted) break; nd->path.mnt = &mounted->mnt; nd->path.dentry = mounted->mnt.mnt_root; inode = nd->path.dentry->d_inode; nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); - if (read_seqretry(&mount_lock, nd->m_seq)) - goto failed; } nd->inode = inode; return 0; - -failed: - return -ECHILD; } /* -- cgit v1.2.3-58-ga151 From 18d8c860112f96b62899819f933912833398cd58 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 16:32:34 -0400 Subject: namei: uninline set_root{,_rcu}() Signed-off-by: Al Viro --- fs/namei.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index d5f45681ea88..177934c79a04 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -777,12 +777,12 @@ static int complete_walk(struct nameidata *nd) return status; } -static __always_inline void set_root(struct nameidata *nd) +static void set_root(struct nameidata *nd) { get_fs_root(current->fs, &nd->root); } -static __always_inline unsigned set_root_rcu(struct nameidata *nd) +static unsigned set_root_rcu(struct nameidata *nd) { struct fs_struct *fs = current->fs; unsigned seq; -- cgit v1.2.3-58-ga151 From 625b6d105446e82e95ddb28d6fd3d25f84f6c479 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 16:36:12 -0400 Subject: namei: pass the struct path to store the result down into path_lookupat() Signed-off-by: Al Viro --- fs/namei.c | 72 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 177934c79a04..f4c9c0774b26 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2086,8 +2086,8 @@ static inline int lookup_last(struct nameidata *nd) } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int path_lookupat(int dfd, const struct filename *name, - unsigned int flags, struct nameidata *nd) +static int path_lookupat(int dfd, const struct filename *name, unsigned flags, + struct nameidata *nd, struct path *path) { const char *s = path_init(dfd, name, flags, nd); int err; @@ -2108,27 +2108,31 @@ static int path_lookupat(int dfd, const struct filename *name, if (!err && nd->flags & LOOKUP_DIRECTORY) if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; - if (err) - terminate_walk(nd); - + if (!err) { + *path = nd->path; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + } + terminate_walk(nd); path_cleanup(nd); return err; } -static int filename_lookup(int dfd, struct filename *name, - unsigned int flags, struct nameidata *nd) +static int filename_lookup(int dfd, struct filename *name, unsigned flags, + struct nameidata *nd, struct path *path) { int retval; struct nameidata *saved_nd = set_nameidata(nd); - retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); + retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd, path); if (unlikely(retval == -ECHILD)) - retval = path_lookupat(dfd, name, flags, nd); + retval = path_lookupat(dfd, name, flags, nd, path); if (unlikely(retval == -ESTALE)) - retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd); + retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, + nd, path); if (likely(!retval)) - audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); + audit_inode(name, path->dentry, flags & LOOKUP_PARENT); restore_nameidata(saved_nd); return retval; } @@ -2209,10 +2213,8 @@ int kern_path(const char *name, unsigned int flags, struct path *path) int res = PTR_ERR(filename); if (!IS_ERR(filename)) { - res = filename_lookup(AT_FDCWD, filename, flags, &nd); + res = filename_lookup(AT_FDCWD, filename, flags, &nd, path); putname(filename); - if (!res) - *path = nd.path; } return res; } @@ -2241,9 +2243,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, nd.root.dentry = dentry; nd.root.mnt = mnt; err = filename_lookup(AT_FDCWD, filename, - flags | LOOKUP_ROOT, &nd); - if (!err) - *path = nd.path; + flags | LOOKUP_ROOT, &nd, path); putname(filename); } return err; @@ -2311,10 +2311,8 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, BUG_ON(flags & LOOKUP_PARENT); - err = filename_lookup(dfd, tmp, flags, &nd); + err = filename_lookup(dfd, tmp, flags, &nd, path); putname(tmp); - if (!err) - *path = nd.path; } return err; } @@ -3261,44 +3259,42 @@ static int do_tmpfile(int dfd, struct filename *pathname, struct file *file, int *opened) { static const struct qstr name = QSTR_INIT("/", 1); - struct dentry *dentry, *child; + struct dentry *child; struct inode *dir; + struct path path; int error = path_lookupat(dfd, pathname, - flags | LOOKUP_DIRECTORY, nd); + flags | LOOKUP_DIRECTORY, nd, &path); if (unlikely(error)) return error; - error = mnt_want_write(nd->path.mnt); + error = mnt_want_write(path.mnt); if (unlikely(error)) goto out; + dir = path.dentry->d_inode; /* we want directory to be writable */ - error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC); + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); if (error) goto out2; - dentry = nd->path.dentry; - dir = dentry->d_inode; if (!dir->i_op->tmpfile) { error = -EOPNOTSUPP; goto out2; } - child = d_alloc(dentry, &name); + child = d_alloc(path.dentry, &name); if (unlikely(!child)) { error = -ENOMEM; goto out2; } - nd->flags &= ~LOOKUP_DIRECTORY; - nd->flags |= op->intent; - dput(nd->path.dentry); - nd->path.dentry = child; - error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode); + dput(path.dentry); + path.dentry = child; + error = dir->i_op->tmpfile(dir, child, op->mode); if (error) goto out2; - audit_inode(pathname, nd->path.dentry, 0); + audit_inode(pathname, child, 0); /* Don't check for other permissions, the inode was just created */ - error = may_open(&nd->path, MAY_OPEN, op->open_flag); + error = may_open(&path, MAY_OPEN, op->open_flag); if (error) goto out2; - file->f_path.mnt = nd->path.mnt; - error = finish_open(file, nd->path.dentry, NULL, opened); + file->f_path.mnt = path.mnt; + error = finish_open(file, child, NULL, opened); if (error) goto out2; error = open_check_o_direct(file); @@ -3311,9 +3307,9 @@ static int do_tmpfile(int dfd, struct filename *pathname, spin_unlock(&inode->i_lock); } out2: - mnt_drop_write(nd->path.mnt); + mnt_drop_write(path.mnt); out: - path_put(&nd->path); + path_put(&path); return error; } -- cgit v1.2.3-58-ga151 From e4bd1c1a95409131ad3948accab98f0d6b8093f0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 16:40:39 -0400 Subject: namei: move putname() call into filename_lookup() Signed-off-by: Al Viro --- fs/namei.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index f4c9c0774b26..847c61a48078 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2134,6 +2134,7 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, if (likely(!retval)) audit_inode(name, path->dentry, flags & LOOKUP_PARENT); restore_nameidata(saved_nd); + putname(name); return retval; } @@ -2210,13 +2211,9 @@ int kern_path(const char *name, unsigned int flags, struct path *path) { struct nameidata nd; struct filename *filename = getname_kernel(name); - int res = PTR_ERR(filename); - - if (!IS_ERR(filename)) { - res = filename_lookup(AT_FDCWD, filename, flags, &nd, path); - putname(filename); - } - return res; + if (IS_ERR(filename)) + return PTR_ERR(filename); + return filename_lookup(AT_FDCWD, filename, flags, &nd, path); } EXPORT_SYMBOL(kern_path); @@ -2232,21 +2229,19 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { + struct nameidata nd; struct filename *filename = getname_kernel(name); - int err = PTR_ERR(filename); BUG_ON(flags & LOOKUP_PARENT); + if (IS_ERR(filename)) + return PTR_ERR(filename); + + nd.root.dentry = dentry; + nd.root.mnt = mnt; /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */ - if (!IS_ERR(filename)) { - struct nameidata nd; - nd.root.dentry = dentry; - nd.root.mnt = mnt; - err = filename_lookup(AT_FDCWD, filename, + return filename_lookup(AT_FDCWD, filename, flags | LOOKUP_ROOT, &nd, path); - putname(filename); - } - return err; } EXPORT_SYMBOL(vfs_path_lookup); @@ -2306,15 +2301,12 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, { struct nameidata nd; struct filename *tmp = getname_flags(name, flags, empty); - int err = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { + if (IS_ERR(tmp)) + return PTR_ERR(tmp); - BUG_ON(flags & LOOKUP_PARENT); + BUG_ON(flags & LOOKUP_PARENT); - err = filename_lookup(dfd, tmp, flags, &nd, path); - putname(tmp); - } - return err; + return filename_lookup(dfd, tmp, flags, &nd, path); } int user_path_at(int dfd, const char __user *name, unsigned flags, -- cgit v1.2.3-58-ga151 From 9ad1aaa61522ffba001a5599623182fe23083a94 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 16:44:39 -0400 Subject: namei: shift nameidata inside filename_lookup() pass root instead; non-NULL => copy to nd.root and set LOOKUP_ROOT in flags Signed-off-by: Al Viro --- fs/namei.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 847c61a48078..2999404c8d30 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2119,17 +2119,20 @@ static int path_lookupat(int dfd, const struct filename *name, unsigned flags, } static int filename_lookup(int dfd, struct filename *name, unsigned flags, - struct nameidata *nd, struct path *path) + struct path *path, struct path *root) { int retval; - struct nameidata *saved_nd = set_nameidata(nd); - - retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd, path); + struct nameidata nd, *saved_nd = set_nameidata(&nd); + if (unlikely(root)) { + nd.root = *root; + flags |= LOOKUP_ROOT; + } + retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, &nd, path); if (unlikely(retval == -ECHILD)) - retval = path_lookupat(dfd, name, flags, nd, path); + retval = path_lookupat(dfd, name, flags, &nd, path); if (unlikely(retval == -ESTALE)) retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, - nd, path); + &nd, path); if (likely(!retval)) audit_inode(name, path->dentry, flags & LOOKUP_PARENT); @@ -2209,11 +2212,10 @@ out: int kern_path(const char *name, unsigned int flags, struct path *path) { - struct nameidata nd; struct filename *filename = getname_kernel(name); if (IS_ERR(filename)) return PTR_ERR(filename); - return filename_lookup(AT_FDCWD, filename, flags, &nd, path); + return filename_lookup(AT_FDCWD, filename, flags, path, NULL); } EXPORT_SYMBOL(kern_path); @@ -2229,7 +2231,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { - struct nameidata nd; + struct path root = {.mnt = mnt, .dentry = dentry}; struct filename *filename = getname_kernel(name); BUG_ON(flags & LOOKUP_PARENT); @@ -2237,11 +2239,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, if (IS_ERR(filename)) return PTR_ERR(filename); - nd.root.dentry = dentry; - nd.root.mnt = mnt; - /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */ - return filename_lookup(AT_FDCWD, filename, - flags | LOOKUP_ROOT, &nd, path); + /* the first argument of filename_lookup() is ignored with root */ + return filename_lookup(AT_FDCWD, filename, flags , path, &root); } EXPORT_SYMBOL(vfs_path_lookup); @@ -2299,14 +2298,13 @@ EXPORT_SYMBOL(lookup_one_len); int user_path_at_empty(int dfd, const char __user *name, unsigned flags, struct path *path, int *empty) { - struct nameidata nd; struct filename *tmp = getname_flags(name, flags, empty); if (IS_ERR(tmp)) return PTR_ERR(tmp); BUG_ON(flags & LOOKUP_PARENT); - return filename_lookup(dfd, tmp, flags, &nd, path); + return filename_lookup(dfd, tmp, flags, path, NULL); } int user_path_at(int dfd, const char __user *name, unsigned flags, -- cgit v1.2.3-58-ga151 From abc9f5beb1ca97b5157914c83c85d67a6b665d74 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 16:53:42 -0400 Subject: namei: make filename_lookup() reject ERR_PTR() passed as name makes for much easier life in callers Signed-off-by: Al Viro --- fs/namei.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 2999404c8d30..a9c593e4f35e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2122,7 +2122,10 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root) { int retval; - struct nameidata nd, *saved_nd = set_nameidata(&nd); + struct nameidata nd, *saved_nd; + if (IS_ERR(name)) + return PTR_ERR(name); + saved_nd = set_nameidata(&nd); if (unlikely(root)) { nd.root = *root; flags |= LOOKUP_ROOT; @@ -2212,10 +2215,8 @@ out: int kern_path(const char *name, unsigned int flags, struct path *path) { - struct filename *filename = getname_kernel(name); - if (IS_ERR(filename)) - return PTR_ERR(filename); - return filename_lookup(AT_FDCWD, filename, flags, path, NULL); + return filename_lookup(AT_FDCWD, getname_kernel(name), + flags, path, NULL); } EXPORT_SYMBOL(kern_path); @@ -2232,15 +2233,9 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, struct path *path) { struct path root = {.mnt = mnt, .dentry = dentry}; - struct filename *filename = getname_kernel(name); - - BUG_ON(flags & LOOKUP_PARENT); - - if (IS_ERR(filename)) - return PTR_ERR(filename); - /* the first argument of filename_lookup() is ignored with root */ - return filename_lookup(AT_FDCWD, filename, flags , path, &root); + return filename_lookup(AT_FDCWD, getname_kernel(name), + flags , path, &root); } EXPORT_SYMBOL(vfs_path_lookup); @@ -2298,13 +2293,8 @@ EXPORT_SYMBOL(lookup_one_len); int user_path_at_empty(int dfd, const char __user *name, unsigned flags, struct path *path, int *empty) { - struct filename *tmp = getname_flags(name, flags, empty); - if (IS_ERR(tmp)) - return PTR_ERR(tmp); - - BUG_ON(flags & LOOKUP_PARENT); - - return filename_lookup(dfd, tmp, flags, path, NULL); + return filename_lookup(dfd, getname_flags(name, flags, empty), + flags, path, NULL); } int user_path_at(int dfd, const char __user *name, unsigned flags, -- cgit v1.2.3-58-ga151 From 391172c46e6f9d5d03855ff3ae5720d9826f3b59 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 9 May 2015 11:19:16 -0400 Subject: namei: shift nameidata down into filename_parentat() Signed-off-by: Al Viro --- fs/namei.c | 84 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index a9c593e4f35e..593cf3b01e74 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2146,7 +2146,8 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ static int path_parentat(int dfd, const struct filename *name, - unsigned int flags, struct nameidata *nd) + unsigned int flags, struct nameidata *nd, + struct path *parent) { const char *s = path_init(dfd, name, flags, nd); int err; @@ -2155,26 +2156,34 @@ static int path_parentat(int dfd, const struct filename *name, err = link_path_walk(s, nd); if (!err) err = complete_walk(nd); - if (err) - terminate_walk(nd); + if (!err) { + *parent = nd->path; + nd->path.mnt = NULL; + nd->path.dentry = NULL; + } + terminate_walk(nd); path_cleanup(nd); return err; } static int filename_parentat(int dfd, struct filename *name, - unsigned int flags, struct nameidata *nd) + unsigned int flags, struct path *parent, + struct qstr *last, int *type) { int retval; - struct nameidata *saved_nd = set_nameidata(nd); + struct nameidata nd, *saved_nd = set_nameidata(&nd); - retval = path_parentat(dfd, name, flags | LOOKUP_RCU, nd); + retval = path_parentat(dfd, name, flags | LOOKUP_RCU, &nd, parent); if (unlikely(retval == -ECHILD)) - retval = path_parentat(dfd, name, flags, nd); + retval = path_parentat(dfd, name, flags, &nd, parent); if (unlikely(retval == -ESTALE)) - retval = path_parentat(dfd, name, flags | LOOKUP_REVAL, nd); - - if (likely(!retval)) - audit_inode(name, nd->path.dentry, LOOKUP_PARENT); + retval = path_parentat(dfd, name, flags | LOOKUP_REVAL, + &nd, parent); + if (likely(!retval)) { + *last = nd.last; + *type = nd.last_type; + audit_inode(name, parent->dentry, LOOKUP_PARENT); + } restore_nameidata(saved_nd); return retval; } @@ -2183,31 +2192,30 @@ static int filename_parentat(int dfd, struct filename *name, struct dentry *kern_path_locked(const char *name, struct path *path) { struct filename *filename = getname_kernel(name); - struct nameidata nd; + struct qstr last; + int type; struct dentry *d; int err; if (IS_ERR(filename)) return ERR_CAST(filename); - err = filename_parentat(AT_FDCWD, filename, 0, &nd); + err = filename_parentat(AT_FDCWD, filename, 0, path, &last, &type); if (err) { d = ERR_PTR(err); goto out; } - if (nd.last_type != LAST_NORM) { - path_put(&nd.path); + if (type != LAST_NORM) { + path_put(path); d = ERR_PTR(-EINVAL); goto out; } - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - d = __lookup_hash(&nd.last, nd.path.dentry, 0); + mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT); + d = __lookup_hash(&last, path->dentry, 0); if (IS_ERR(d)) { - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - path_put(&nd.path); - goto out; + mutex_unlock(&path->dentry->d_inode->i_mutex); + path_put(path); } - *path = nd.path; out: putname(filename); return d; @@ -2317,7 +2325,6 @@ user_path_parent(int dfd, const char __user *path, int *type, unsigned int flags) { - struct nameidata nd; struct filename *s = getname(path); int error; @@ -2327,15 +2334,11 @@ user_path_parent(int dfd, const char __user *path, if (IS_ERR(s)) return s; - error = filename_parentat(dfd, s, flags, &nd); + error = filename_parentat(dfd, s, flags, parent, last, type); if (error) { putname(s); - return ERR_PTR(error); + s = ERR_PTR(error); } - *parent = nd.path; - *last = nd.last; - *type = nd.last_type; - return s; } @@ -3394,7 +3397,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, struct path *path, unsigned int lookup_flags) { struct dentry *dentry = ERR_PTR(-EEXIST); - struct nameidata nd; + struct qstr last; + int type; int err2; int error; bool is_dir = (lookup_flags & LOOKUP_DIRECTORY); @@ -3405,7 +3409,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, */ lookup_flags &= LOOKUP_REVAL; - error = filename_parentat(dfd, name, lookup_flags, &nd); + error = filename_parentat(dfd, name, lookup_flags, path, &last, &type); if (error) return ERR_PTR(error); @@ -3413,18 +3417,17 @@ static struct dentry *filename_create(int dfd, struct filename *name, * Yucky last component or no last component at all? * (foo/., foo/.., /////) */ - if (nd.last_type != LAST_NORM) + if (type != LAST_NORM) goto out; - nd.flags &= ~LOOKUP_PARENT; - nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL; /* don't fail immediately if it's r/o, at least try to report other errors */ - err2 = mnt_want_write(nd.path.mnt); + err2 = mnt_want_write(path->mnt); /* * Do the final lookup. */ - mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); - dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); + lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL; + mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT); + dentry = __lookup_hash(&last, path->dentry, lookup_flags); if (IS_ERR(dentry)) goto unlock; @@ -3438,7 +3441,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, * all is fine. Let's be bastards - you had / on the end, you've * been asking for (non-existent) directory. -ENOENT for you. */ - if (unlikely(!is_dir && nd.last.name[nd.last.len])) { + if (unlikely(!is_dir && last.name[last.len])) { error = -ENOENT; goto fail; } @@ -3446,17 +3449,16 @@ static struct dentry *filename_create(int dfd, struct filename *name, error = err2; goto fail; } - *path = nd.path; return dentry; fail: dput(dentry); dentry = ERR_PTR(error); unlock: - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + mutex_unlock(&path->dentry->d_inode->i_mutex); if (!err2) - mnt_drop_write(nd.path.mnt); + mnt_drop_write(path->mnt); out: - path_put(&nd.path); + path_put(path); return dentry; } -- cgit v1.2.3-58-ga151 From 181c37b6e4c1bdb061ed0d884c54f9a6e6cdac89 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 17:21:25 -0400 Subject: namei: saner calling conventions for filename_create() a) make it reject ERR_PTR() for name b) make it putname(name) upon return in all other cases. seriously simplifies the callers... Signed-off-by: Al Viro --- fs/namei.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 593cf3b01e74..628b6eb9415f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3403,6 +3403,8 @@ static struct dentry *filename_create(int dfd, struct filename *name, int error; bool is_dir = (lookup_flags & LOOKUP_DIRECTORY); + if (IS_ERR(name)) + return ERR_CAST(name); /* * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any * other flags passed in are ignored! @@ -3410,8 +3412,10 @@ static struct dentry *filename_create(int dfd, struct filename *name, lookup_flags &= LOOKUP_REVAL; error = filename_parentat(dfd, name, lookup_flags, path, &last, &type); - if (error) + if (error) { + putname(name); return ERR_PTR(error); + } /* * Yucky last component or no last component at all? @@ -3449,6 +3453,7 @@ static struct dentry *filename_create(int dfd, struct filename *name, error = err2; goto fail; } + putname(name); return dentry; fail: dput(dentry); @@ -3459,20 +3464,15 @@ unlock: mnt_drop_write(path->mnt); out: path_put(path); + putname(name); return dentry; } struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *filename = getname_kernel(pathname); - struct dentry *res; - - if (IS_ERR(filename)) - return ERR_CAST(filename); - res = filename_create(dfd, filename, path, lookup_flags); - putname(filename); - return res; + return filename_create(dfd, getname_kernel(pathname), + path, lookup_flags); } EXPORT_SYMBOL(kern_path_create); @@ -3488,13 +3488,7 @@ EXPORT_SYMBOL(done_path_create); struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, unsigned int lookup_flags) { - struct filename *tmp = getname(pathname); - struct dentry *res; - if (IS_ERR(tmp)) - return ERR_CAST(tmp); - res = filename_create(dfd, tmp, path, lookup_flags); - putname(tmp); - return res; + return filename_create(dfd, getname(pathname), path, lookup_flags); } EXPORT_SYMBOL(user_path_create); -- cgit v1.2.3-58-ga151 From 5c31b6cedb675199bfd18c08a1223c9b39daedd7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 17:32:54 -0400 Subject: namei: saner calling conventions for filename_parentat() a) make it reject ERR_PTR() for name b) make it putname(name) on all other failure exits c) make it return name on success again, simplifies the callers Signed-off-by: Al Viro --- fs/namei.c | 60 ++++++++++++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 628b6eb9415f..484b73cb95e3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2166,13 +2166,16 @@ static int path_parentat(int dfd, const struct filename *name, return err; } -static int filename_parentat(int dfd, struct filename *name, +static struct filename *filename_parentat(int dfd, struct filename *name, unsigned int flags, struct path *parent, struct qstr *last, int *type) { int retval; - struct nameidata nd, *saved_nd = set_nameidata(&nd); + struct nameidata nd, *saved_nd; + if (IS_ERR(name)) + return name; + saved_nd = set_nameidata(&nd); retval = path_parentat(dfd, name, flags | LOOKUP_RCU, &nd, parent); if (unlikely(retval == -ECHILD)) retval = path_parentat(dfd, name, flags, &nd, parent); @@ -2183,32 +2186,30 @@ static int filename_parentat(int dfd, struct filename *name, *last = nd.last; *type = nd.last_type; audit_inode(name, parent->dentry, LOOKUP_PARENT); + } else { + putname(name); + name = ERR_PTR(retval); } restore_nameidata(saved_nd); - return retval; + return name; } /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { - struct filename *filename = getname_kernel(name); + struct filename *filename; + struct dentry *d; struct qstr last; int type; - struct dentry *d; - int err; + filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path, + &last, &type); if (IS_ERR(filename)) return ERR_CAST(filename); - - err = filename_parentat(AT_FDCWD, filename, 0, path, &last, &type); - if (err) { - d = ERR_PTR(err); - goto out; - } - if (type != LAST_NORM) { + if (unlikely(type != LAST_NORM)) { path_put(path); - d = ERR_PTR(-EINVAL); - goto out; + putname(filename); + return ERR_PTR(-EINVAL); } mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT); d = __lookup_hash(&last, path->dentry, 0); @@ -2216,7 +2217,6 @@ struct dentry *kern_path_locked(const char *name, struct path *path) mutex_unlock(&path->dentry->d_inode->i_mutex); path_put(path); } -out: putname(filename); return d; } @@ -2325,21 +2325,9 @@ user_path_parent(int dfd, const char __user *path, int *type, unsigned int flags) { - struct filename *s = getname(path); - int error; - /* only LOOKUP_REVAL is allowed in extra flags */ - flags &= LOOKUP_REVAL; - - if (IS_ERR(s)) - return s; - - error = filename_parentat(dfd, s, flags, parent, last, type); - if (error) { - putname(s); - s = ERR_PTR(error); - } - return s; + return filename_parentat(dfd, getname(path), flags & LOOKUP_REVAL, + parent, last, type); } /** @@ -3403,25 +3391,21 @@ static struct dentry *filename_create(int dfd, struct filename *name, int error; bool is_dir = (lookup_flags & LOOKUP_DIRECTORY); - if (IS_ERR(name)) - return ERR_CAST(name); /* * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any * other flags passed in are ignored! */ lookup_flags &= LOOKUP_REVAL; - error = filename_parentat(dfd, name, lookup_flags, path, &last, &type); - if (error) { - putname(name); - return ERR_PTR(error); - } + name = filename_parentat(dfd, name, lookup_flags, path, &last, &type); + if (IS_ERR(name)) + return ERR_CAST(name); /* * Yucky last component or no last component at all? * (foo/., foo/.., /////) */ - if (type != LAST_NORM) + if (unlikely(type != LAST_NORM)) goto out; /* don't fail immediately if it's r/o, at least try to report other errors */ -- cgit v1.2.3-58-ga151 From 102b8af266fbce07b7f8d2396bf2286ba80c93bd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 17:35:52 -0400 Subject: namei: fold path_cleanup() into terminate_walk() they are always called next to each other; moreover, terminate_walk() is more symmetrical that way. Signed-off-by: Al Viro --- fs/namei.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 484b73cb95e3..14aaf00f8ee7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -584,6 +584,10 @@ static void terminate_walk(struct nameidata *nd) path_put(&nd->path); for (i = 0; i < nd->depth; i++) path_put(&nd->stack[i].link); + if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { + path_put(&nd->root); + nd->root.mnt = NULL; + } } else { nd->flags &= ~LOOKUP_RCU; if (!(nd->flags & LOOKUP_ROOT)) @@ -2051,14 +2055,6 @@ static const char *path_init(int dfd, const struct filename *name, return ERR_PTR(-ECHILD); } -static void path_cleanup(struct nameidata *nd) -{ - if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) { - path_put(&nd->root); - nd->root.mnt = NULL; - } -} - static const char *trailing_symlink(struct nameidata *nd) { const char *s; @@ -2114,7 +2110,6 @@ static int path_lookupat(int dfd, const struct filename *name, unsigned flags, nd->path.dentry = NULL; } terminate_walk(nd); - path_cleanup(nd); return err; } @@ -2162,7 +2157,6 @@ static int path_parentat(int dfd, const struct filename *name, nd->path.dentry = NULL; } terminate_walk(nd); - path_cleanup(nd); return err; } @@ -2446,7 +2440,6 @@ path_mountpoint(int dfd, const struct filename *name, struct path *path, } } terminate_walk(nd); - path_cleanup(nd); return err; } @@ -3318,7 +3311,6 @@ static struct file *path_openat(int dfd, struct filename *pathname, } } terminate_walk(nd); - path_cleanup(nd); out2: if (!(opened & FILE_OPENED)) { BUG_ON(!error); -- cgit v1.2.3-58-ga151 From c8a53ee5ee4fcab5ee252f61a69609b65e5e74b8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 18:43:07 -0400 Subject: namei: stash dfd and name into nameidata fewer arguments to pass around... Signed-off-by: Al Viro --- fs/namei.c | 96 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 14aaf00f8ee7..970456fc9ea8 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -498,6 +498,8 @@ struct nameidata { struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ + struct filename *name; + int dfd; unsigned int flags; unsigned seq, m_seq, root_seq; int last_type; @@ -512,10 +514,13 @@ struct nameidata { } *stack, internal[EMBEDDED_LEVELS]; }; -static struct nameidata *set_nameidata(struct nameidata *p) +static struct nameidata *set_nameidata(struct nameidata *p, int dfd, + struct filename *name) { struct nameidata *old = current->nameidata; p->stack = p->internal; + p->dfd = dfd; + p->name = name; p->total_link_count = old ? old->total_link_count : 0; current->nameidata = p; return old; @@ -1954,11 +1959,10 @@ OK: } } -static const char *path_init(int dfd, const struct filename *name, - unsigned int flags, struct nameidata *nd) +static const char *path_init(struct nameidata *nd, unsigned flags) { int retval = 0; - const char *s = name->name; + const char *s = nd->name->name; nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; @@ -1999,7 +2003,7 @@ static const char *path_init(int dfd, const struct filename *name, path_get(&nd->root); } nd->path = nd->root; - } else if (dfd == AT_FDCWD) { + } else if (nd->dfd == AT_FDCWD) { if (flags & LOOKUP_RCU) { struct fs_struct *fs = current->fs; unsigned seq; @@ -2016,7 +2020,7 @@ static const char *path_init(int dfd, const struct filename *name, } } else { /* Caller must check execute permissions on the starting path component */ - struct fd f = fdget_raw(dfd); + struct fd f = fdget_raw(nd->dfd); struct dentry *dentry; if (!f.file) @@ -2082,10 +2086,9 @@ static inline int lookup_last(struct nameidata *nd) } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int path_lookupat(int dfd, const struct filename *name, unsigned flags, - struct nameidata *nd, struct path *path) +static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path) { - const char *s = path_init(dfd, name, flags, nd); + const char *s = path_init(nd, flags); int err; if (IS_ERR(s)) @@ -2120,17 +2123,16 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, struct nameidata nd, *saved_nd; if (IS_ERR(name)) return PTR_ERR(name); - saved_nd = set_nameidata(&nd); + saved_nd = set_nameidata(&nd, dfd, name); if (unlikely(root)) { nd.root = *root; flags |= LOOKUP_ROOT; } - retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, &nd, path); + retval = path_lookupat(&nd, flags | LOOKUP_RCU, path); if (unlikely(retval == -ECHILD)) - retval = path_lookupat(dfd, name, flags, &nd, path); + retval = path_lookupat(&nd, flags, path); if (unlikely(retval == -ESTALE)) - retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, - &nd, path); + retval = path_lookupat(&nd, flags | LOOKUP_REVAL, path); if (likely(!retval)) audit_inode(name, path->dentry, flags & LOOKUP_PARENT); @@ -2140,11 +2142,10 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int path_parentat(int dfd, const struct filename *name, - unsigned int flags, struct nameidata *nd, +static int path_parentat(struct nameidata *nd, unsigned flags, struct path *parent) { - const char *s = path_init(dfd, name, flags, nd); + const char *s = path_init(nd, flags); int err; if (IS_ERR(s)) return PTR_ERR(s); @@ -2169,13 +2170,12 @@ static struct filename *filename_parentat(int dfd, struct filename *name, if (IS_ERR(name)) return name; - saved_nd = set_nameidata(&nd); - retval = path_parentat(dfd, name, flags | LOOKUP_RCU, &nd, parent); + saved_nd = set_nameidata(&nd, dfd, name); + retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); if (unlikely(retval == -ECHILD)) - retval = path_parentat(dfd, name, flags, &nd, parent); + retval = path_parentat(&nd, flags, parent); if (unlikely(retval == -ESTALE)) - retval = path_parentat(dfd, name, flags | LOOKUP_REVAL, - &nd, parent); + retval = path_parentat(&nd, flags | LOOKUP_REVAL, parent); if (likely(!retval)) { *last = nd.last; *type = nd.last_type; @@ -2415,19 +2415,17 @@ done: /** * path_mountpoint - look up a path to be umounted - * @dfd: directory file descriptor to start walk from - * @name: full pathname to walk - * @path: pointer to container for result + * @nameidata: lookup context * @flags: lookup flags + * @path: pointer to container for result * * Look up the given name, but don't attempt to revalidate the last component. * Returns 0 and "path" will be valid on success; Returns error otherwise. */ static int -path_mountpoint(int dfd, const struct filename *name, struct path *path, - struct nameidata *nd, unsigned int flags) +path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path) { - const char *s = path_init(dfd, name, flags, nd); + const char *s = path_init(nd, flags); int err; if (IS_ERR(s)) return PTR_ERR(s); @@ -2451,12 +2449,12 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path, int error; if (IS_ERR(name)) return PTR_ERR(name); - saved = set_nameidata(&nd); - error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_RCU); + saved = set_nameidata(&nd, dfd, name); + error = path_mountpoint(&nd, flags | LOOKUP_RCU, path); if (unlikely(error == -ECHILD)) - error = path_mountpoint(dfd, name, path, &nd, flags); + error = path_mountpoint(&nd, flags, path); if (unlikely(error == -ESTALE)) - error = path_mountpoint(dfd, name, path, &nd, flags | LOOKUP_REVAL); + error = path_mountpoint(&nd, flags | LOOKUP_REVAL, path); if (likely(!error)) audit_inode(name, path->dentry, 0); restore_nameidata(saved); @@ -3217,8 +3215,7 @@ stale_open: goto retry_lookup; } -static int do_tmpfile(int dfd, struct filename *pathname, - struct nameidata *nd, int flags, +static int do_tmpfile(struct nameidata *nd, unsigned flags, const struct open_flags *op, struct file *file, int *opened) { @@ -3226,8 +3223,7 @@ static int do_tmpfile(int dfd, struct filename *pathname, struct dentry *child; struct inode *dir; struct path path; - int error = path_lookupat(dfd, pathname, - flags | LOOKUP_DIRECTORY, nd, &path); + int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path); if (unlikely(error)) return error; error = mnt_want_write(path.mnt); @@ -3252,7 +3248,7 @@ static int do_tmpfile(int dfd, struct filename *pathname, error = dir->i_op->tmpfile(dir, child, op->mode); if (error) goto out2; - audit_inode(pathname, child, 0); + audit_inode(nd->name, child, 0); /* Don't check for other permissions, the inode was just created */ error = may_open(&path, MAY_OPEN, op->open_flag); if (error) @@ -3277,8 +3273,8 @@ out: return error; } -static struct file *path_openat(int dfd, struct filename *pathname, - struct nameidata *nd, const struct open_flags *op, int flags) +static struct file *path_openat(struct nameidata *nd, + const struct open_flags *op, unsigned flags) { const char *s; struct file *file; @@ -3292,17 +3288,17 @@ static struct file *path_openat(int dfd, struct filename *pathname, file->f_flags = op->open_flag; if (unlikely(file->f_flags & __O_TMPFILE)) { - error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); + error = do_tmpfile(nd, flags, op, file, &opened); goto out2; } - s = path_init(dfd, pathname, flags, nd); + s = path_init(nd, flags); if (IS_ERR(s)) { put_filp(file); return ERR_CAST(s); } while (!(error = link_path_walk(s, nd)) && - (error = do_last(nd, file, op, &opened, pathname)) > 0) { + (error = do_last(nd, file, op, &opened, nd->name)) > 0) { nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); s = trailing_symlink(nd); if (IS_ERR(s)) { @@ -3331,15 +3327,15 @@ out2: struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { - struct nameidata nd, *saved_nd = set_nameidata(&nd); + struct nameidata nd, *saved_nd = set_nameidata(&nd, dfd, pathname); int flags = op->lookup_flags; struct file *filp; - filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); + filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) - filp = path_openat(dfd, pathname, &nd, op, flags); + filp = path_openat(&nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) - filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL); + filp = path_openat(&nd, op, flags | LOOKUP_REVAL); restore_nameidata(saved_nd); return filp; } @@ -3362,12 +3358,12 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, if (unlikely(IS_ERR(filename))) return ERR_CAST(filename); - saved_nd = set_nameidata(&nd); - file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU); + saved_nd = set_nameidata(&nd, -1, filename); + file = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(file == ERR_PTR(-ECHILD))) - file = path_openat(-1, filename, &nd, op, flags); + file = path_openat(&nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) - file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL); + file = path_openat(&nd, op, flags | LOOKUP_REVAL); restore_nameidata(saved_nd); putname(filename); return file; -- cgit v1.2.3-58-ga151 From 76ae2a5ab13c4413dd403c940c16bd0b1e609346 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 12 May 2015 18:44:32 -0400 Subject: namei: trim do_last() arguments now that struct filename is stashed in nameidata we have no need to pass it in Signed-off-by: Al Viro --- fs/namei.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 970456fc9ea8..8d453041a323 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2992,7 +2992,7 @@ out_dput: */ static int do_last(struct nameidata *nd, struct file *file, const struct open_flags *op, - int *opened, struct filename *name) + int *opened) { struct dentry *dir = nd->path.dentry; int open_flag = op->open_flag; @@ -3039,7 +3039,7 @@ static int do_last(struct nameidata *nd, if (error) return error; - audit_inode(name, dir, LOOKUP_PARENT); + audit_inode(nd->name, dir, LOOKUP_PARENT); /* trailing slashes? */ if (unlikely(nd->last.name[nd->last.len])) return -EISDIR; @@ -3068,7 +3068,7 @@ retry_lookup: !S_ISREG(file_inode(file)->i_mode)) will_truncate = false; - audit_inode(name, file->f_path.dentry, 0); + audit_inode(nd->name, file->f_path.dentry, 0); goto opened; } @@ -3085,7 +3085,7 @@ retry_lookup: * create/update audit record if it already exists. */ if (d_is_positive(path.dentry)) - audit_inode(name, path.dentry, 0); + audit_inode(nd->name, path.dentry, 0); /* * If atomic_open() acquired write access it is dropped now due to @@ -3143,7 +3143,7 @@ finish_open: path_put(&save_parent); return error; } - audit_inode(name, nd->path.dentry, 0); + audit_inode(nd->name, nd->path.dentry, 0); error = -EISDIR; if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) goto out; @@ -3298,7 +3298,7 @@ static struct file *path_openat(struct nameidata *nd, return ERR_CAST(s); } while (!(error = link_path_walk(s, nd)) && - (error = do_last(nd, file, op, &opened, nd->name)) > 0) { + (error = do_last(nd, file, op, &opened)) > 0) { nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); s = trailing_symlink(nd); if (IS_ERR(s)) { -- cgit v1.2.3-58-ga151 From a2ec4a2d5cf35972b0e4dd84f66142bc76bb6cb2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 May 2015 06:57:49 -0400 Subject: inline user_path_parent() Signed-off-by: Al Viro --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 8d453041a323..53f832adca7e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2312,7 +2312,7 @@ EXPORT_SYMBOL(user_path_at); * allocated by getname. So we must hold the reference to it until all * path-walking is complete. */ -static struct filename * +static inline struct filename * user_path_parent(int dfd, const char __user *path, struct path *parent, struct qstr *last, -- cgit v1.2.3-58-ga151 From 520ae6874726dbfdf52c779d387b584d95cfed7f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 May 2015 07:00:28 -0400 Subject: inline user_path_create() Signed-off-by: Al Viro --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 53f832adca7e..22814d067fed 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3457,7 +3457,7 @@ void done_path_create(struct path *path, struct dentry *dentry) } EXPORT_SYMBOL(done_path_create); -struct dentry *user_path_create(int dfd, const char __user *pathname, +inline struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, unsigned int lookup_flags) { return filename_create(dfd, getname(pathname), path, lookup_flags); -- cgit v1.2.3-58-ga151 From 9883d1855ecfafc60045a93abcee6c42e0a5f571 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 May 2015 07:28:08 -0400 Subject: namei: move saved_nd pointer into struct nameidata these guys are always declared next to each other; might as well put the former (pointer to previous instance) into the latter and simplify the calling conventions for {set,restore}_nameidata() Signed-off-by: Al Viro --- fs/namei.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 22814d067fed..1a117c0d13c5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -498,10 +498,8 @@ struct nameidata { struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ - struct filename *name; - int dfd; unsigned int flags; - unsigned seq, m_seq, root_seq; + unsigned seq, m_seq; int last_type; unsigned depth; int total_link_count; @@ -512,23 +510,26 @@ struct nameidata { struct inode *inode; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; + struct filename *name; + struct nameidata *saved; + unsigned root_seq; + int dfd; }; -static struct nameidata *set_nameidata(struct nameidata *p, int dfd, - struct filename *name) +static void set_nameidata(struct nameidata *p, int dfd, struct filename *name) { struct nameidata *old = current->nameidata; p->stack = p->internal; p->dfd = dfd; p->name = name; p->total_link_count = old ? old->total_link_count : 0; + p->saved = old; current->nameidata = p; - return old; } -static void restore_nameidata(struct nameidata *old) +static void restore_nameidata(void) { - struct nameidata *now = current->nameidata; + struct nameidata *now = current->nameidata, *old = now->saved; current->nameidata = old; if (old) @@ -2120,14 +2121,14 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root) { int retval; - struct nameidata nd, *saved_nd; + struct nameidata nd; if (IS_ERR(name)) return PTR_ERR(name); - saved_nd = set_nameidata(&nd, dfd, name); if (unlikely(root)) { nd.root = *root; flags |= LOOKUP_ROOT; } + set_nameidata(&nd, dfd, name); retval = path_lookupat(&nd, flags | LOOKUP_RCU, path); if (unlikely(retval == -ECHILD)) retval = path_lookupat(&nd, flags, path); @@ -2136,7 +2137,7 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, if (likely(!retval)) audit_inode(name, path->dentry, flags & LOOKUP_PARENT); - restore_nameidata(saved_nd); + restore_nameidata(); putname(name); return retval; } @@ -2166,11 +2167,11 @@ static struct filename *filename_parentat(int dfd, struct filename *name, struct qstr *last, int *type) { int retval; - struct nameidata nd, *saved_nd; + struct nameidata nd; if (IS_ERR(name)) return name; - saved_nd = set_nameidata(&nd, dfd, name); + set_nameidata(&nd, dfd, name); retval = path_parentat(&nd, flags | LOOKUP_RCU, parent); if (unlikely(retval == -ECHILD)) retval = path_parentat(&nd, flags, parent); @@ -2184,7 +2185,7 @@ static struct filename *filename_parentat(int dfd, struct filename *name, putname(name); name = ERR_PTR(retval); } - restore_nameidata(saved_nd); + restore_nameidata(); return name; } @@ -2445,11 +2446,11 @@ static int filename_mountpoint(int dfd, struct filename *name, struct path *path, unsigned int flags) { - struct nameidata nd, *saved; + struct nameidata nd; int error; if (IS_ERR(name)) return PTR_ERR(name); - saved = set_nameidata(&nd, dfd, name); + set_nameidata(&nd, dfd, name); error = path_mountpoint(&nd, flags | LOOKUP_RCU, path); if (unlikely(error == -ECHILD)) error = path_mountpoint(&nd, flags, path); @@ -2457,7 +2458,7 @@ filename_mountpoint(int dfd, struct filename *name, struct path *path, error = path_mountpoint(&nd, flags | LOOKUP_REVAL, path); if (likely(!error)) audit_inode(name, path->dentry, 0); - restore_nameidata(saved); + restore_nameidata(); putname(name); return error; } @@ -3327,23 +3328,24 @@ out2: struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { - struct nameidata nd, *saved_nd = set_nameidata(&nd, dfd, pathname); + struct nameidata nd; int flags = op->lookup_flags; struct file *filp; + set_nameidata(&nd, dfd, pathname); filp = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(filp == ERR_PTR(-ECHILD))) filp = path_openat(&nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(&nd, op, flags | LOOKUP_REVAL); - restore_nameidata(saved_nd); + restore_nameidata(); return filp; } struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, const char *name, const struct open_flags *op) { - struct nameidata nd, *saved_nd; + struct nameidata nd; struct file *file; struct filename *filename; int flags = op->lookup_flags | LOOKUP_ROOT; @@ -3358,13 +3360,13 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, if (unlikely(IS_ERR(filename))) return ERR_CAST(filename); - saved_nd = set_nameidata(&nd, -1, filename); + set_nameidata(&nd, -1, filename); file = path_openat(&nd, op, flags | LOOKUP_RCU); if (unlikely(file == ERR_PTR(-ECHILD))) file = path_openat(&nd, op, flags); if (unlikely(file == ERR_PTR(-ESTALE))) file = path_openat(&nd, op, flags | LOOKUP_REVAL); - restore_nameidata(saved_nd); + restore_nameidata(); putname(filename); return file; } -- cgit v1.2.3-58-ga151 From b853a16176cf3e02c57e215743015614152c2428 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 May 2015 09:12:02 -0400 Subject: turn user_{path_at,path,lpath,path_dir}() into static inlines Signed-off-by: Al Viro --- fs/namei.c | 8 +------- include/linux/namei.h | 34 ++++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 1a117c0d13c5..2dad0eaf91d3 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2299,13 +2299,7 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, return filename_lookup(dfd, getname_flags(name, flags, empty), flags, path, NULL); } - -int user_path_at(int dfd, const char __user *name, unsigned flags, - struct path *path) -{ - return user_path_at_empty(dfd, name, flags, path, NULL); -} -EXPORT_SYMBOL(user_path_at); +EXPORT_SYMBOL(user_path_at_empty); /* * NB: most callers don't do anything directly with the reference to the diff --git a/include/linux/namei.h b/include/linux/namei.h index 1208e489f83e..d8c6334cd150 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -1,12 +1,10 @@ #ifndef _LINUX_NAMEI_H #define _LINUX_NAMEI_H -#include -#include -#include +#include #include - -struct vfsmount; +#include +#include enum { MAX_NESTED_LINKS = 8 }; @@ -46,13 +44,29 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_ROOT 0x2000 #define LOOKUP_EMPTY 0x4000 -extern int user_path_at(int, const char __user *, unsigned, struct path *); extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty); -#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) -#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) -#define user_path_dir(name, path) \ - user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path) +static inline int user_path_at(int dfd, const char __user *name, unsigned flags, + struct path *path) +{ + return user_path_at_empty(dfd, name, flags, path, NULL); +} + +static inline int user_path(const char __user *name, struct path *path) +{ + return user_path_at_empty(AT_FDCWD, name, LOOKUP_FOLLOW, path, NULL); +} + +static inline int user_lpath(const char __user *name, struct path *path) +{ + return user_path_at_empty(AT_FDCWD, name, 0, path, NULL); +} + +static inline int user_path_dir(const char __user *name, struct path *path) +{ + return user_path_at_empty(AT_FDCWD, name, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path, NULL); +} extern int kern_path(const char *, unsigned, struct path *); -- cgit v1.2.3-58-ga151