[AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous

2007-12-20 Thread John
First, when __d_path() hits a lazily unmounted mount point, it tries to prepend
the name of the lazily unmounted dentry to the path name.  It gets this wrong,
and also overwrites the slash that separates the name from the following
pathname component. This patch fixes that; if a process was in directory
/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the
missing slash), while the new result with this patch is ``foo/bar''.

Second, it isn't always possible to tell from the __d_path() result whether the
specified root and rootmnt (i.e., the chroot) was reached.  We need an
unambiguous result for AppArmor at least though, so we make sure that paths
will only start with a slash if the path leads all the way up to the root.

We also add a @fail_deleted argument, which allows to get rid of some of the
mess in sys_getcwd().

This patch leaves getcwd() and d_path() as they were before for everything
except for bind-mounted directories; for them, it reports ``/foo/bar'' instead
of ``foobar'' in the example described above.

Signed-off-by: Andreas Gruenbacher <[EMAIL PROTECTED]>
Signed-off-by: John Johansen <[EMAIL PROTECTED]>
Acked-by: Alan Cox <[EMAIL PROTECTED]>

---
 fs/dcache.c |  166 ++--
 1 file changed, 96 insertions(+), 70 deletions(-)

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1748,51 +1748,49 @@ shouldnt_be_hashed:
 }
 
 /**
- * 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
+ * __d_path - return the path of a dentry
+ * @path: path to report
+ * @root: root path
  * @buffer: buffer to return value in
  * @buflen: buffer length
+ * @fail_deleted: what to return for deleted files
  *
- * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * If @path is not connected to @root, the path returned will be relative
+ * (i.e., it will not start with a slash).
  * the string " (deleted)" is appended. Note that this is ambiguous.
  *
  * Returns the buffer or an error code if the path was too long.
  *
- * "buflen" should be positive. Caller holds the dcache_lock.
+ * Returns the buffer or an error code.
  */
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
-  struct path *root, char *buffer, int buflen)
+static char *__d_path(struct path *path, struct path *root,
+ char *buffer, int buflen, int fail_deleted)
 {
-   char * end = buffer+buflen;
-   char * retval;
-   int namelen;
+   int namelen, is_slash;
+   struct dentry *dentry = path->dentry;
+   struct vfsmount *vfsmnt = path->mnt;
+
+   if (buflen < 2)
+   return ERR_PTR(-ENAMETOOLONG);
+   buffer += --buflen;
+   *buffer = '\0';
 
-   *--end = '\0';
-   buflen--;
+   spin_lock(_lock);
if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
-   buflen -= 10;
-   end -= 10;
-   if (buflen < 0)
+   if (fail_deleted) {
+   buffer = ERR_PTR(-ENOENT);
+   goto out;
+   }
+   if (buflen < 10)
goto Elong;
-   memcpy(end, " (deleted)", 10);
+   buflen -= 10;
+   buffer -= 10;
+   memcpy(buffer, " (deleted)", 10);
}
-
-   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? */
spin_lock(_lock);
if (vfsmnt->mnt_parent == vfsmnt) {
spin_unlock(_lock);
@@ -1806,28 +1804,67 @@ static char *__d_path(struct dentry *den
parent = dentry->d_parent;
prefetch(parent);
namelen = dentry->d_name.len;
-   buflen -= namelen + 1;
-   if (buflen < 0)
+   if (buflen < namelen + 1)
goto Elong;
-   end -= namelen;
-   memcpy(end, dentry->d_name.name, namelen);
-   *--end = '/';
-   retval = end;
+   buflen -= namelen + 1;
+   buffer -= namelen;
+   memcpy(buffer, dentry->d_name.name, namelen);
+   *--buffer = '/';
dentry = parent;
}
+   /* Get '/' right. */
+   if (*buffer != '/')
+   *--buffer = '/';
 
-   return retval;
+out:
+   spin_unlock(_lock);
+   return buffer;
 
 global_root:
+   /*
+* 

[AppArmor 29/47] Fix __d_path() for lazy unmounts and make it unambiguous

2007-12-20 Thread John
First, when __d_path() hits a lazily unmounted mount point, it tries to prepend
the name of the lazily unmounted dentry to the path name.  It gets this wrong,
and also overwrites the slash that separates the name from the following
pathname component. This patch fixes that; if a process was in directory
/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the
missing slash), while the new result with this patch is ``foo/bar''.

Second, it isn't always possible to tell from the __d_path() result whether the
specified root and rootmnt (i.e., the chroot) was reached.  We need an
unambiguous result for AppArmor at least though, so we make sure that paths
will only start with a slash if the path leads all the way up to the root.

We also add a @fail_deleted argument, which allows to get rid of some of the
mess in sys_getcwd().

This patch leaves getcwd() and d_path() as they were before for everything
except for bind-mounted directories; for them, it reports ``/foo/bar'' instead
of ``foobar'' in the example described above.

Signed-off-by: Andreas Gruenbacher [EMAIL PROTECTED]
Signed-off-by: John Johansen [EMAIL PROTECTED]
Acked-by: Alan Cox [EMAIL PROTECTED]

---
 fs/dcache.c |  166 ++--
 1 file changed, 96 insertions(+), 70 deletions(-)

--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1748,51 +1748,49 @@ shouldnt_be_hashed:
 }
 
 /**
- * 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
+ * __d_path - return the path of a dentry
+ * @path: path to report
+ * @root: root path
  * @buffer: buffer to return value in
  * @buflen: buffer length
+ * @fail_deleted: what to return for deleted files
  *
- * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * If @path is not connected to @root, the path returned will be relative
+ * (i.e., it will not start with a slash).
  * the string  (deleted) is appended. Note that this is ambiguous.
  *
  * Returns the buffer or an error code if the path was too long.
  *
- * buflen should be positive. Caller holds the dcache_lock.
+ * Returns the buffer or an error code.
  */
-static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
-  struct path *root, char *buffer, int buflen)
+static char *__d_path(struct path *path, struct path *root,
+ char *buffer, int buflen, int fail_deleted)
 {
-   char * end = buffer+buflen;
-   char * retval;
-   int namelen;
+   int namelen, is_slash;
+   struct dentry *dentry = path-dentry;
+   struct vfsmount *vfsmnt = path-mnt;
+
+   if (buflen  2)
+   return ERR_PTR(-ENAMETOOLONG);
+   buffer += --buflen;
+   *buffer = '\0';
 
-   *--end = '\0';
-   buflen--;
+   spin_lock(dcache_lock);
if (!IS_ROOT(dentry)  d_unhashed(dentry)) {
-   buflen -= 10;
-   end -= 10;
-   if (buflen  0)
+   if (fail_deleted) {
+   buffer = ERR_PTR(-ENOENT);
+   goto out;
+   }
+   if (buflen  10)
goto Elong;
-   memcpy(end,  (deleted), 10);
+   buflen -= 10;
+   buffer -= 10;
+   memcpy(buffer,  (deleted), 10);
}
-
-   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? */
spin_lock(vfsmount_lock);
if (vfsmnt-mnt_parent == vfsmnt) {
spin_unlock(vfsmount_lock);
@@ -1806,28 +1804,67 @@ static char *__d_path(struct dentry *den
parent = dentry-d_parent;
prefetch(parent);
namelen = dentry-d_name.len;
-   buflen -= namelen + 1;
-   if (buflen  0)
+   if (buflen  namelen + 1)
goto Elong;
-   end -= namelen;
-   memcpy(end, dentry-d_name.name, namelen);
-   *--end = '/';
-   retval = end;
+   buflen -= namelen + 1;
+   buffer -= namelen;
+   memcpy(buffer, dentry-d_name.name, namelen);
+   *--buffer = '/';
dentry = parent;
}
+   /* Get '/' right. */
+   if (*buffer != '/')
+   *--buffer = '/';
 
-   return retval;
+out:
+   spin_unlock(dcache_lock);
+   return buffer;
 
 global_root:
+   /*
+* We went