Hello. I'd like to propose this patch. TOMOYO and AppArmor are bothered by " (deleted)" suffix added by __d_path(). Also, converting /proc/PID to /proc/self makes it possible for TOMOYO to grant read access for only self process's information. It would be also useful for other users when auditing pathnames like /proc/self/mounts . In that case, flags (added by this patch) should be passed to d_path() as well. I wrote reason why I can't do it outside __d_path(). If modifying __d_path() is not acceptable, I beg your permission for putting similar code to security/tomoyo/ directory. Regards. -------------------- [PATCH] fs: Add flags to __d_path for suppressing suffix and mapping /proc/self This patch allows __d_path() to (1) suppress " (deleted)" suffix if dentry was already deleted (2) convert /proc/PID to /proc/self if dentry is under /proc/self directory Since "/path/to/file (deleted)" is a legal pathname of a not yet deleted file, the caller of __d_path() can't blindly cut off " (deleted)" part. Also, procfs is usually mounted on /proc , but it can be mounted on /proc2 , /mnt/proc3 or /p . Thus, the caller of __d_path() can't convert /proc/PID to /proc/self from string returned by __d_path() because the caller can't find the mount point of procfs from the returned string. If the caller traverses dentry/vfsmount tree in order to find the mount point of procfs, it results in duplicating __d_path() because the the caller does not use __d_path(). TOMOYO wants to use pathname without " (deleted)" suffix and pathname converted to /proc/self . This patch adds two flags for controlling " (deleted)" suffix and "PID => self" mapping. Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx> --- fs/dcache.c | 36 ++++++++++++++++++++++++++++++------ fs/seq_file.c | 2 +- include/linux/dcache.h | 5 ++++- security/tomoyo/realpath.c | 4 +++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 74da947..cf1a1e1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -33,6 +33,7 @@ #include <linux/bootmem.h> #include <linux/fs_struct.h> #include <linux/hardirq.h> +#include <linux/magic.h> #include "internal.h" int sysctl_vfs_cache_pressure __read_mostly = 100; @@ -1909,9 +1910,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) * @root: root vfsmnt/dentry (may be modified by this function) * @buffer: buffer to return value in * @buflen: buffer length + * @flags: combination of control flags * * Convert a dentry into an ASCII path name. If the entry has been deleted - * the string " (deleted)" is appended. Note that this is ambiguous. + * the string " (deleted)" is appended unless D_PATH_NO_DELETED_SUFFIX is + * set to "flags". Note that this is ambiguous. + * + * If D_PATH_CONVERT_PROC_SELF is set to "flags", "/proc/PID" is converted to + * "/proc/self" if PID is current process. * * Returns a pointer into the buffer or an error code if the * path was too long. @@ -1922,7 +1928,7 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) * root is changed (without modifying refcounts). */ char *__d_path(const struct path *path, struct path *root, - char *buffer, int buflen) + char *buffer, int buflen, unsigned int flags) { struct dentry *dentry = path->dentry; struct vfsmount *vfsmnt = path->mnt; @@ -1931,7 +1937,7 @@ char *__d_path(const struct path *path, struct path *root, spin_lock(&vfsmount_lock); prepend(&end, &buflen, "\0", 1); - if (d_unlinked(dentry) && + if (!(flags & D_PATH_NO_DELETED_SUFFIX) && d_unlinked(dentry) && (prepend(&end, &buflen, " (deleted)", 10) != 0)) goto Elong; @@ -1943,6 +1949,8 @@ char *__d_path(const struct path *path, struct path *root, for (;;) { struct dentry * parent; + const char *name; + int namelen; if (dentry == root->dentry && vfsmnt == root->mnt) break; @@ -1957,7 +1965,23 @@ char *__d_path(const struct path *path, struct path *root, } parent = dentry->d_parent; prefetch(parent); - if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || + name = dentry->d_name.name; + namelen = dentry->d_name.len; + /* Map /proc/PID to /proc/self . */ + if ((flags & D_PATH_CONVERT_PROC_SELF) && IS_ROOT(parent) && + *name > '0' && *name <= '9' && parent->d_sb && + parent->d_sb->s_magic == PROC_SUPER_MAGIC) { + char *ep; + const pid_t pid = (pid_t) simple_strtoul(name, &ep, 10); + const pid_t tgid = task_tgid_nr_ns(current, + dentry->d_sb-> + s_fs_info); + if (!*ep && pid == tgid && tgid) { + name = "self"; + namelen = 4; + } + } + if ((prepend(&end, &buflen, name, namelen) != 0) || (prepend(&end, &buflen, "/", 1) != 0)) goto Elong; retval = end; @@ -2019,7 +2043,7 @@ char *d_path(const struct path *path, char *buf, int buflen) read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); tmp = root; - res = __d_path(path, &tmp, buf, buflen); + res = __d_path(path, &tmp, buf, buflen, 0); spin_unlock(&dcache_lock); path_put(&root); return res; @@ -2125,7 +2149,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) struct path tmp = root; char * cwd; - cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); + cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE, 0); spin_unlock(&dcache_lock); error = PTR_ERR(cwd); diff --git a/fs/seq_file.c b/fs/seq_file.c index eae7d9d..729072d 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -463,7 +463,7 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root, char *p; spin_lock(&dcache_lock); - p = __d_path(path, root, buf, size); + p = __d_path(path, root, buf, size, 0); spin_unlock(&dcache_lock); res = PTR_ERR(p); if (!IS_ERR(p)) { diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 30b93b2..0e2e814 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -311,7 +311,10 @@ extern int d_validate(struct dentry *, struct dentry *); */ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); -extern char *__d_path(const struct path *path, struct path *root, char *, int); +#define D_PATH_NO_DELETED_SUFFIX 1 /* Don't add " (deleted) suffix. */ +#define D_PATH_CONVERT_PROC_SELF 2 /* Map /proc/PID to /proc/self */ +extern char *__d_path(const struct path *path, struct path *root, char *buffer, + int buflen, unsigned int flags); extern char *d_path(const struct path *, char *, int); extern char *dentry_path(struct dentry *, char *, int); diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 455bc39..24d3471 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -93,7 +93,9 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, spin_lock(&dcache_lock); /* go to whatever namespace root we are under */ - sp = __d_path(path, &ns_root, newname, newname_len); + sp = __d_path(path, &ns_root, newname, newname_len, + D_PATH_NO_DELETED_SUFFIX | + D_PATH_CONVERT_PROC_SELF); spin_unlock(&dcache_lock); /* Prepend "/proc" prefix if using internal proc vfs mount. */ if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && -- 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