Separate the __user pathname handling from the bulk of the syscall. Since we're doing this to enable relinking of unlinked files by sys_checkpoint and not sys_linkat we're not using a sys-wrapper. Signed-off-by: Matt Helsley <matthltc@xxxxxxxxxx> Cc: containers@xxxxxxxxxxxxxxxxxxxxxxxxxx Cc: Oren Laadan <orenl@xxxxxxxxxxxxxxx> Cc: Amir Goldstein <amir73il@xxxxxxxxxxxx> Cc: linux-fsdevel@xxxxxxxxxxxxxxx Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx> Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx> Cc: Jamie Lokier <jamie@xxxxxxxxxxxxx> --- fs/namei.c | 79 ++++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 51 insertions(+), 28 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index eb279e3..8c9663d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2456,6 +2456,51 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de return error; } +/* If the file has been unlinked then old_dentry doesn't match old_path */ +static int do_linkat(struct path *old_path, struct dentry *old_dentry, + struct nameidata *nd, int flags) +{ + struct dentry *new_dentry; + int error = -EXDEV; + + if (old_path->mnt != nd->path.mnt) + goto out; + new_dentry = lookup_create(nd, 0); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto out_unlock; + error = mnt_want_write(nd->path.mnt); + if (error) + goto out_dput; + error = security_path_link(old_dentry, &nd->path, new_dentry); + if (error) + goto out_drop_write; + error = vfs_link(old_dentry, nd->path.dentry->d_inode, new_dentry); +out_drop_write: + mnt_drop_write(nd->path.mnt); +out_dput: + dput(new_dentry); +out_unlock: + mutex_unlock(&nd->path.dentry->d_inode->i_mutex); +out: + return error; +} + +static int do_kern_linkat(struct path *old_path, struct dentry *old_dentry, + int newdfd, const char * newname, int flags) +{ + struct nameidata nd; + int error; + + error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &nd); + if (error) + goto out; + error = do_linkat(old_path, old_dentry, &nd, flags); + path_put(&nd.path); +out: + return error; +} + /* * Hardlinks are often used in delicate situations. We avoid * security-related surprises by not following symlinks on the @@ -2468,11 +2513,10 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, int, flags) { - struct dentry *new_dentry; struct nameidata nd; struct path old_path; - int error; char *to; + int error; if ((flags & ~AT_SYMLINK_FOLLOW) != 0) return -EINVAL; @@ -2481,37 +2525,16 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, &old_path); if (error) - return error; - - error = user_path_parent(newdfd, newname, &nd, &to); - if (error) goto out; - error = -EXDEV; - if (old_path.mnt != nd.path.mnt) - goto out_release; - new_dentry = lookup_create(&nd, 0); - error = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - goto out_unlock; - error = mnt_want_write(nd.path.mnt); - if (error) - goto out_dput; - error = security_path_link(old_path.dentry, &nd.path, new_dentry); + error = user_path_parent(newdfd, newname, &nd, &to); if (error) - goto out_drop_write; - error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); -out_drop_write: - mnt_drop_write(nd.path.mnt); -out_dput: - dput(new_dentry); -out_unlock: - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); -out_release: + goto out_put_old_path; + error = do_linkat(&old_path, old_path.dentry, &nd, flags); path_put(&nd.path); putname(to); -out: +out_put_old_path: path_put(&old_path); - +out: return error; } -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html