diff --git a/fs/dcache.c b/fs/dcache.c index 635c2aa427e..3ee588d5f58 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1759,10 +1759,8 @@ static int prepend(char **buffer, int *buflen, const char *str, /** * d_path - return the path of a dentry - * @dentry: dentry to report - * @vfsmnt: vfsmnt to which the dentry belongs - * @root: root dentry - * @rootmnt: vfsmnt to which the root dentry belongs + * @path: the dentry/vfsmount to report + * @root: root vfsmnt/dentry (may be modified by this function) * @buffer: buffer to return value in * @buflen: buffer length * @@ -1772,10 +1770,15 @@ static int prepend(char **buffer, int *buflen, const char *str, * Returns the buffer or an error code if the path was too long. * * "buflen" should be positive. Caller holds the dcache_lock. + * + * If path is not reachable from the supplied root, then the value of + * root is changed (without modifying refcounts). */ -static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, - struct path *root, char *buffer, int buflen) +char *__d_path(const struct path *path, struct path *root, + char *buffer, int buflen) { + struct dentry *dentry = path->dentry; + struct vfsmount *vfsmnt = path->mnt; char * end = buffer+buflen; char * retval; @@ -1824,6 +1827,8 @@ global_root: if (prepend(&retval, &buflen, dentry->d_name.name, dentry->d_name.len) != 0) goto Elong; + root->mnt = vfsmnt; + root->dentry = dentry; return retval; Elong: return ERR_PTR(-ENAMETOOLONG); @@ -1846,6 +1851,7 @@ char *d_path(struct path *path, char *buf, int buflen) { char *res; struct path root; + struct path tmp; /* * We have various synthetic filesystems that never get mounted. On @@ -1862,7 +1868,8 @@ char *d_path(struct path *path, char *buf, int buflen) path_get(&root); read_unlock(¤t->fs->lock); spin_lock(&dcache_lock); - res = __d_path(path->dentry, path->mnt, &root, buf, buflen); + tmp = root; + res = __d_path(path, &tmp, buf, buflen); spin_unlock(&dcache_lock); path_put(&root); return res; @@ -1970,9 +1977,10 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) spin_lock(&dcache_lock); if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) { unsigned long len; + struct path tmp = root; char * cwd; - cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE); + cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); spin_unlock(&dcache_lock); error = PTR_ERR(cwd); diff --git a/fs/seq_file.c b/fs/seq_file.c index ae59f5a6c5c..3f54dbd6c49 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -392,6 +392,36 @@ int seq_path(struct seq_file *m, struct path *path, char *esc) } EXPORT_SYMBOL(seq_path); +/* + * Same as seq_path, but relative to supplied root. + * + * root may be changed, see __d_path(). + */ +int seq_path_root(struct seq_file *m, struct path *path, struct path *root, + char *esc) +{ + int err = -ENAMETOOLONG; + if (m->count < m->size) { + char *s = m->buf + m->count; + char *p; + + spin_lock(&dcache_lock); + p = __d_path(path, root, s, m->size - m->count); + spin_unlock(&dcache_lock); + err = PTR_ERR(p); + if (!IS_ERR(p)) { + s = mangle_path(s, p, esc); + if (s) { + p = m->buf + m->count; + m->count = s - m->buf; + return 0; + } + } + } + m->count = m->size; + return err; +} + /* * returns the path of the 'dentry' from the root of its filesystem. */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 63960033b6f..cfb1627ac51 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -301,6 +301,7 @@ 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); extern char *d_path(struct path *, char *, int); extern char *dentry_path(struct dentry *, char *, int); diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index 11676ccef7b..5b5369c3c20 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h @@ -46,6 +46,8 @@ int seq_printf(struct seq_file *, const char *, ...) int seq_path(struct seq_file *, struct path *, char *); int seq_dentry(struct seq_file *, struct dentry *, char *); +int seq_path_root(struct seq_file *m, struct path *path, struct path *root, + char *esc); int single_open(struct file *, int (*)(struct seq_file *, void *), void *); int single_release(struct inode *, struct file *);