Skip to content

Commit

Permalink
vfs: show unreachable paths in getcwd and proc
Browse files Browse the repository at this point in the history
Prepend "(unreachable)" to path strings if the path is not reachable
from the current root.

Two places updated are
 - the return string from getcwd()
 - and symlinks under /proc/$PID.

Other uses of d_path() are left unchanged (we know that some old
software crashes if /proc/mounts is changed).

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Miklos Szeredi authored and Al Viro committed Aug 11, 2010
1 parent ffd1f4e commit 8df9d1a
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 5 deletions.
54 changes: 50 additions & 4 deletions fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,11 @@ static int path_with_deleted(const struct path *path, struct path *root,
return prepend_path(path, root, buf, buflen);
}

static int prepend_unreachable(char **buffer, int *buflen)
{
return prepend(buffer, buflen, "(unreachable)", 13);
}

/**
* d_path - return the path of a dentry
* @path: path to report
Expand Down Expand Up @@ -2064,6 +2069,39 @@ char *d_path(const struct path *path, char *buf, int buflen)
}
EXPORT_SYMBOL(d_path);

/**
* d_path_with_unreachable - return the path of a dentry
* @path: path to report
* @buf: buffer to return value in
* @buflen: buffer length
*
* The difference from d_path() is that this prepends "(unreachable)"
* to paths which are unreachable from the current process' root.
*/
char *d_path_with_unreachable(const struct path *path, char *buf, int buflen)
{
char *res = buf + buflen;
struct path root;
struct path tmp;
int error;

if (path->dentry->d_op && path->dentry->d_op->d_dname)
return path->dentry->d_op->d_dname(path->dentry, buf, buflen);

get_fs_root(current->fs, &root);
spin_lock(&dcache_lock);
tmp = root;
error = path_with_deleted(path, &tmp, &res, &buflen);
if (!error && !path_equal(&tmp, &root))
error = prepend_unreachable(&res, &buflen);
spin_unlock(&dcache_lock);
path_put(&root);
if (error)
res = ERR_PTR(error);

return res;
}

/*
* Helper function for dentry_operations.d_dname() members
*/
Expand Down Expand Up @@ -2173,15 +2211,23 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
if (!d_unlinked(pwd.dentry)) {
unsigned long len;
struct path tmp = root;
char * cwd;
char *cwd = page + PAGE_SIZE;
int buflen = PAGE_SIZE;

cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
prepend(&cwd, &buflen, "\0", 1);
error = prepend_path(&pwd, &tmp, &cwd, &buflen);
spin_unlock(&dcache_lock);

error = PTR_ERR(cwd);
if (IS_ERR(cwd))
if (error)
goto out;

/* Unreachable from current root */
if (!path_equal(&tmp, &root)) {
error = prepend_unreachable(&cwd, &buflen);
if (error)
goto out;
}

error = -ERANGE;
len = PAGE_SIZE + page - cwd;
if (len <= size) {
Expand Down
2 changes: 1 addition & 1 deletion fs/proc/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -1526,7 +1526,7 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen)
if (!tmp)
return -ENOMEM;

pathname = d_path(path, tmp, PAGE_SIZE);
pathname = d_path_with_unreachable(path, tmp, PAGE_SIZE);
len = PTR_ERR(pathname);
if (IS_ERR(pathname))
goto out;
Expand Down
1 change: 1 addition & 0 deletions include/linux/dcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);

extern char *__d_path(const struct path *path, struct path *root, char *, int);
extern char *d_path(const struct path *, char *, int);
extern char *d_path_with_unreachable(const struct path *, char *, int);
extern char *__dentry_path(struct dentry *, char *, int);
extern char *dentry_path(struct dentry *, char *, int);

Expand Down
5 changes: 5 additions & 0 deletions include/linux/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ struct path {
extern void path_get(struct path *);
extern void path_put(struct path *);

static inline int path_equal(const struct path *path1, const struct path *path2)
{
return path1->mnt == path2->mnt && path1->dentry == path2->dentry;
}

#endif /* _LINUX_PATH_H */

0 comments on commit 8df9d1a

Please sign in to comment.