From: John Johansen Subject: Fix __d_path to allow for old and new behavior bnc#380763 Fix __d_path so that it can be told whether or not to connect disconnect path to the root. This is easier and more efficient than trying to reconnect these paths for d_path and get_cwd after the fact. Signed-off-by: John Johansen --- fs/dcache.c | 57 ++++++++++++++++++------------------------------- fs/namespace.c | 2 - include/linux/dcache.h | 2 - 3 files changed, 24 insertions(+), 37 deletions(-) --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1772,6 +1772,7 @@ shouldnt_be_hashed: * @buffer: buffer to return value in * @buflen: buffer length * @fail_deleted: what to return for deleted files + * @disconnect: don't return a path starting with / when disconnected * * Convert a dentry into an ASCII path name. If the entry has been deleted, * then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, @@ -1784,9 +1785,10 @@ shouldnt_be_hashed: */ char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, struct dentry *root, struct vfsmount *rootmnt, - char *buffer, int buflen, int fail_deleted) + char *buffer, int buflen, int fail_deleted, int disconnect) { - int namelen, is_slash, vfsmount_locked = 0; + int namelen, vfsmount_locked = 0; + const unsigned char *name; if (buflen < 2) return ERR_PTR(-ENAMETOOLONG); @@ -1847,27 +1849,26 @@ global_root: * unconnected dentry, or the file is on a pseudo filesystem. */ namelen = dentry->d_name.len; - is_slash = (namelen == 1 && *dentry->d_name.name == '/'); - if (is_slash || (dentry->d_sb->s_flags & MS_NOUSER)) { - /* - * Make sure we won't return a pathname starting with '/'. - * - * Historically, we also glue together the root dentry and - * remaining name for pseudo filesystems like pipefs, which - * have the MS_NOUSER flag set. This results in pathnames - * like "pipe:[439336]". - */ - if (*buffer == '/') { - buffer++; - buflen++; - } - if (is_slash) - goto out; + name = dentry->d_name.name; + + /* + * If this is a root dentry, then overwrite the slash. This + * will also DTRT with pseudo filesystems which have root + * dentries named "foo:". + */ + if (IS_ROOT(dentry)) { + buffer++; + buflen++; + } + if (disconnect && *name == '/') { + /* Make sure we won't return a pathname starting with '/' */ + name++; + namelen--; } if (buflen < namelen) goto Elong; buffer -= namelen; - memcpy(buffer, dentry->d_name.name, namelen); + memcpy(buffer, name, namelen); goto out; Elong: @@ -1875,18 +1876,6 @@ Elong: goto out; } -static char *__connect_d_path(char *path, char *buffer) -{ - if (!IS_ERR(path) && *path != '/') { - /* Pretend that disconnected paths are hanging off the root. */ - if (path == buffer) - path = ERR_PTR(-ENAMETOOLONG); - else - *--path = '/'; - } - return path; -} - /* write full pathname into buffer and return start of pathname */ char *d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, int buflen) @@ -1909,8 +1898,7 @@ char *d_path(struct dentry *dentry, stru rootmnt = mntget(current->fs->rootmnt); root = dget(current->fs->root); read_unlock(¤t->fs->lock); - res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0); - res = __connect_d_path(res, buf); + res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, 0, 0); dput(root); mntput(rootmnt); return res; @@ -1972,8 +1960,7 @@ asmlinkage long sys_getcwd(char __user * root = dget(current->fs->root); read_unlock(¤t->fs->lock); - cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1); - cwd = __connect_d_path(cwd, page); + cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 1, 0); error = PTR_ERR(cwd); if (IS_ERR(cwd)) goto out; --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1901,7 +1901,7 @@ char *d_namespace_path(struct dentry *de mntput(rootmnt); if (nsrootmnt) root = dget(nsrootmnt->mnt_root); - res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1); + res = __d_path(dentry, vfsmnt, root, nsrootmnt, buf, buflen, 1, 1); dput(root); mntput(nsrootmnt); /* Prevent empty path for lazily unmounted filesystems. */ --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -301,7 +301,7 @@ extern int d_validate(struct dentry *, s extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(struct dentry *, struct vfsmount *, struct dentry *, - struct vfsmount *, char *, int, int); + struct vfsmount *, char *, int, int, int); extern char * d_path(struct dentry *, struct vfsmount *, char *, int); /* Allocation counts.. */