|
@@ -1905,48 +1905,30 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * __d_path - return the path of a dentry
|
|
|
+ * Prepend path string to a buffer
|
|
|
+ *
|
|
|
* @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
|
|
|
- *
|
|
|
- * Convert a dentry into an ASCII path name. If the entry has been deleted
|
|
|
- * the string " (deleted)" is appended. Note that this is ambiguous.
|
|
|
- *
|
|
|
- * Returns a pointer into the buffer or an error code if the
|
|
|
- * path was too long.
|
|
|
+ * @buffer: pointer to the end of the buffer
|
|
|
+ * @buflen: pointer to buffer length
|
|
|
*
|
|
|
- * "buflen" should be positive. Caller holds the dcache_lock.
|
|
|
+ * Caller holds the dcache_lock.
|
|
|
*
|
|
|
* If path is not reachable from the supplied root, then the value of
|
|
|
* root is changed (without modifying refcounts).
|
|
|
*/
|
|
|
-char *__d_path(const struct path *path, struct path *root,
|
|
|
- char *buffer, int buflen)
|
|
|
+static int prepend_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;
|
|
|
+ bool slash = false;
|
|
|
+ int error = 0;
|
|
|
|
|
|
spin_lock(&vfsmount_lock);
|
|
|
- prepend(&end, &buflen, "\0", 1);
|
|
|
- if (d_unlinked(dentry) &&
|
|
|
- (prepend(&end, &buflen, " (deleted)", 10) != 0))
|
|
|
- goto Elong;
|
|
|
-
|
|
|
- if (buflen < 1)
|
|
|
- goto Elong;
|
|
|
- /* Get '/' right */
|
|
|
- retval = end-1;
|
|
|
- *retval = '/';
|
|
|
-
|
|
|
- for (;;) {
|
|
|
+ while (dentry != root->dentry || vfsmnt != root->mnt) {
|
|
|
struct dentry * parent;
|
|
|
|
|
|
- if (dentry == root->dentry && vfsmnt == root->mnt)
|
|
|
- break;
|
|
|
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
|
|
|
/* Global root? */
|
|
|
if (vfsmnt->mnt_parent == vfsmnt) {
|
|
@@ -1958,16 +1940,22 @@ 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) ||
|
|
|
- (prepend(&end, &buflen, "/", 1) != 0))
|
|
|
- goto Elong;
|
|
|
- retval = end;
|
|
|
+ error = prepend_name(buffer, buflen, &dentry->d_name);
|
|
|
+ if (!error)
|
|
|
+ error = prepend(buffer, buflen, "/", 1);
|
|
|
+ if (error)
|
|
|
+ break;
|
|
|
+
|
|
|
+ slash = true;
|
|
|
dentry = parent;
|
|
|
}
|
|
|
|
|
|
out:
|
|
|
+ if (!error && !slash)
|
|
|
+ error = prepend(buffer, buflen, "/", 1);
|
|
|
+
|
|
|
spin_unlock(&vfsmount_lock);
|
|
|
- return retval;
|
|
|
+ return error;
|
|
|
|
|
|
global_root:
|
|
|
/*
|
|
@@ -1982,10 +1970,44 @@ global_root:
|
|
|
root->mnt = vfsmnt;
|
|
|
root->dentry = dentry;
|
|
|
goto out;
|
|
|
+}
|
|
|
|
|
|
-Elong:
|
|
|
- retval = ERR_PTR(-ENAMETOOLONG);
|
|
|
- goto out;
|
|
|
+/**
|
|
|
+ * __d_path - return the path of a dentry
|
|
|
+ * @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
|
|
|
+ *
|
|
|
+ * Convert a dentry into an ASCII path name. If the entry has been deleted
|
|
|
+ * the string " (deleted)" is appended. Note that this is ambiguous.
|
|
|
+ *
|
|
|
+ * Returns a pointer into 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).
|
|
|
+ */
|
|
|
+char *__d_path(const struct path *path, struct path *root,
|
|
|
+ char *buf, int buflen)
|
|
|
+{
|
|
|
+ char *res = buf + buflen;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ prepend(&res, &buflen, "\0", 1);
|
|
|
+ if (d_unlinked(path->dentry)) {
|
|
|
+ error = prepend(&res, &buflen, " (deleted)", 10);
|
|
|
+ if (error)
|
|
|
+ return ERR_PTR(error);
|
|
|
+ }
|
|
|
+
|
|
|
+ error = prepend_path(path, root, &res, &buflen);
|
|
|
+ if (error)
|
|
|
+ return ERR_PTR(error);
|
|
|
+
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
/**
|