coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/6] realpath: fix problems with root handling


From: Eric Blake
Subject: [PATCH 2/6] realpath: fix problems with root handling
Date: Wed, 14 Mar 2012 14:38:13 -0600

When --relative-base is /, all other paths should be treated as
relative (except for // where it matters).

Also, on platforms like Cygwin where / and // are distinct, realpath
was incorrectly collapsing // into /.  http://debbugs.gnu.org/10472.

* src/realpath.c (path_prefix, path_common_prefix): Treat /
and // as having no common match.
(relpath): Allow for no match even without --relative-base.
* NEWS: Document this.
---
 NEWS           |    5 +++++
 src/realpath.c |   28 ++++++++++++++++++++++++++--
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index f783afb..e9b3d20 100644
--- a/NEWS
+++ b/NEWS
@@ -57,6 +57,11 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   surprising rename no-op behavior).  Now, mv handles this case by skipping
   the usually-useless rename and simply unlinking A.

+  realpath no longer mishandles a root directory.  This was most
+  noticeable on platforms where // is a different directory than /,
+  but could also be observed with --relative-base=/ or
+  --relative-to=/.  [bug since the beginning, in 8.15]
+
 ** Improvements

   ls can be much more efficient, especially with large directories on file
diff --git a/src/realpath.c b/src/realpath.c
index 2dc5e11..7834c62 100644
--- a/src/realpath.c
+++ b/src/realpath.c
@@ -108,10 +108,25 @@ realpath_canon (const char *fname, int can_mode)
   return can_fname;
 }

-/* Test whether prefix is parent or match of path.  */
+/* Test whether canonical prefix is parent or match of path.  */
 static bool _GL_ATTRIBUTE_PURE
 path_prefix (const char *prefix, const char *path)
 {
+  /* We already know prefix[0] and path[0] are '/'.  */
+
+  prefix++;
+  path++;
+
+  /* '/' is the prefix of everything except '//' (since we know '//'
+     is only present after canonicalization if it is distinct).  */
+  if (!*prefix)
+    return *path != '/';
+
+  /* Likewise, '//' is a prefix of any double-slash path.  */
+  if (*prefix == '/' && !prefix[1])
+    return *path == '/';
+
+  /* All remaining prefix have a non-slash portion.  */
   while (*prefix && *path)
     {
       if (*prefix != *path)
@@ -123,7 +138,7 @@ path_prefix (const char *prefix, const char *path)
 }

 /* Return the length of the longest common prefix
-   of PATH1 and PATH2, ensuring only full path components
+   of canonical PATH1 and PATH2, ensuring only full path components
    are matched.  Return 0 on no match.  */
 static int _GL_ATTRIBUTE_PURE
 path_common_prefix (const char *path1, const char *path2)
@@ -131,6 +146,12 @@ path_common_prefix (const char *path1, const char *path2)
   int i = 0;
   int ret = 0;

+  /* We already know path1[0] and path2[0] are '/'.  Special case
+     '//', which is only present in a canonical name on platforms
+     where it is distinct.  */
+  if ((path1[1] == '/') != (path2[1] == '/'))
+    return 0;
+
   while (*path1 && *path2)
     {
       if (*path1 != *path2)
@@ -168,6 +189,9 @@ relpath (const char *can_fname)

       /* Skip the prefix common to --relative-to and path.  */
       int common_index = path_common_prefix (can_relative_to, can_fname);
+      if (!common_index)
+        return false;
+
       const char *relto_suffix = can_relative_to + common_index;
       const char *fname_suffix = can_fname + common_index;

-- 
1.7.7.6




reply via email to

[Prev in Thread] Current Thread [Next in Thread]