This will allow us to move the generic readlink logic into the VFS and get rid of the readlink method. And it's a cleanup, removing more lines than it adds, since the two functions have a lot in common. /proc/$$/map_files/A allowed reading the symlink with the normal proc permission checks, but following only allowed for CAP_SYS_ADMIN capable tasks. So in proc_map_files_get_link() check for is_following_link() before bailing out if not CAP_SYS_ADMIN capable. Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx> --- fs/proc/base.c | 68 ++++++++++++++++++---------------------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index ac0df4dde823..84769c763afe 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1578,6 +1578,7 @@ static const char *proc_pid_get_link(struct dentry *dentry, { struct path path; int error = -EACCES; + char *res; if (!dentry) return ERR_PTR(-ECHILD); @@ -1590,58 +1591,31 @@ static const char *proc_pid_get_link(struct dentry *dentry, if (error) goto out; - nd_jump_link(&path); - return NULL; -out: - return ERR_PTR(error); -} - -static int do_proc_readlink(struct path *path, char __user *buffer, int buflen) -{ - char *tmp = (char*)__get_free_page(GFP_TEMPORARY); - char *pathname; - int len; - - if (!tmp) - return -ENOMEM; - - pathname = d_path(path, tmp, PAGE_SIZE); - len = PTR_ERR(pathname); - if (IS_ERR(pathname)) - goto out; - len = tmp + PAGE_SIZE - 1 - pathname; - - if (len > buflen) - len = buflen; - if (copy_to_user(buffer, pathname, len)) - len = -EFAULT; - out: - free_page((unsigned long)tmp); - return len; -} - -static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int buflen) -{ - int error = -EACCES; - struct inode *inode = d_inode(dentry); - struct path path; + if (is_following_link()) { + nd_jump_link(&path); + res = NULL; + } else { + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - /* Are we allowed to snoop on the tasks file descriptors? */ - if (!proc_fd_access_allowed(inode)) - goto out; + error = -ENOMEM; + if (!buf) + goto out; - error = PROC_I(inode)->op.proc_get_link(dentry, &path); - if (error) - goto out; + res = d_path(&path, buf, PAGE_SIZE); + if (IS_ERR(res)) + kfree(buf); + else + set_delayed_call(done, kfree_link, buf); + } + return res; - error = do_proc_readlink(&path, buffer, buflen); - path_put(&path); out: - return error; + return ERR_PTR(error); } + const struct inode_operations proc_pid_link_inode_operations = { - .readlink = proc_pid_readlink, + .readlink = generic_readlink, .get_link = proc_pid_get_link, .setattr = proc_setattr, }; @@ -1966,7 +1940,7 @@ proc_map_files_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { - if (!capable(CAP_SYS_ADMIN)) + if (is_following_link() && !capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); return proc_pid_get_link(dentry, inode, done); @@ -1976,7 +1950,7 @@ proc_map_files_get_link(struct dentry *dentry, * Identical to proc_pid_link_inode_operations except for get_link() */ static const struct inode_operations proc_map_files_link_inode_operations = { - .readlink = proc_pid_readlink, + .readlink = generic_readlink, .get_link = proc_map_files_get_link, .setattr = proc_setattr, }; -- 2.5.5 -- 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