canonicalize: in CAN_ALL_BUT_LAST, allow trailing slash
authorEric Blake <ebb9@byu.net>
Sat, 12 Sep 2009 12:04:46 +0000 (06:04 -0600)
committerEric Blake <ebb9@byu.net>
Fri, 18 Sep 2009 01:16:41 +0000 (19:16 -0600)
Coreutils' `readlink -f foo/' should not fail if lstat("foo")
gives ENOENT.

* lib/canonicalize.c (canonicalize_filename_mode): Skip trailing
slashes when checking if last component is missing.
* tests/test-canonicalize.c (main): Test this.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
lib/canonicalize.c
tests/test-canonicalize.c

index b0464b4..70b592d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2009-09-17  Eric Blake  <ebb9@byu.net>
 
+       canonicalize: in CAN_ALL_BUT_LAST, allow trailing slash
+       * lib/canonicalize.c (canonicalize_filename_mode): Skip trailing
+       slashes when checking if last component is missing.
+       * tests/test-canonicalize.c (main): Test this.
+
        canonicalize, canonicalize-lgpl: honor // if distinct from /
        * modules/canonicalize (Files): Add double-slash-root.m4.
        * modules/canonicalize-lgpl (Files): Likewise.
index 9434699..65e73a7 100644 (file)
@@ -184,8 +184,12 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
              saved_errno = errno;
              if (can_mode == CAN_EXISTING)
                goto error;
-             if (can_mode == CAN_ALL_BUT_LAST && *end)
-               goto error;
+             if (can_mode == CAN_ALL_BUT_LAST)
+               {
+                 if (end[strspn (end, "/")] || saved_errno != ENOENT)
+                   goto error;
+                 continue;
+               }
              st.st_mode = 0;
            }
 
index f8dcc54..463297f 100644 (file)
@@ -240,26 +240,42 @@ main ()
   {
     char *result1 = canonicalize_filename_mode (BASE "/zzz", CAN_ALL_BUT_LAST);
     char *result2 = canonicalize_filename_mode (BASE "/zzz", CAN_MISSING);
+    char *result3 = canonicalize_filename_mode (BASE "/zzz/", CAN_ALL_BUT_LAST);
+    char *result4 = canonicalize_filename_mode (BASE "/zzz/", CAN_MISSING);
     ASSERT (result1 != NULL);
     ASSERT (result2 != NULL);
+    ASSERT (result3 != NULL);
+    ASSERT (result4 != NULL);
     ASSERT (strcmp (result1, result2) == 0);
+    ASSERT (strcmp (result2, result3) == 0);
+    ASSERT (strcmp (result3, result4) == 0);
     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/zzz"),
                     "/" BASE "/zzz") == 0);
     free (result1);
     free (result2);
+    free (result3);
+    free (result4);
   }
 
   /* Check that alternate modes can resolve broken symlink basenames.  */
   {
     char *result1 = canonicalize_filename_mode (BASE "/ouk", CAN_ALL_BUT_LAST);
     char *result2 = canonicalize_filename_mode (BASE "/ouk", CAN_MISSING);
+    char *result3 = canonicalize_filename_mode (BASE "/ouk/", CAN_ALL_BUT_LAST);
+    char *result4 = canonicalize_filename_mode (BASE "/ouk/", CAN_MISSING);
     ASSERT (result1 != NULL);
     ASSERT (result2 != NULL);
+    ASSERT (result3 != NULL);
+    ASSERT (result4 != NULL);
     ASSERT (strcmp (result1, result2) == 0);
+    ASSERT (strcmp (result2, result3) == 0);
+    ASSERT (strcmp (result3, result4) == 0);
     ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/wum"),
                     "/" BASE "/wum") == 0);
     free (result1);
     free (result2);
+    free (result3);
+    free (result4);
   }
 
   /* Check that alternate modes can handle missing dirnames.  */