changeset 16193:0b2b4b60945f

canonicalize: add support for not resolving symlinks This will initially be used by a new coreutils realpath command. * lib/canonicalize.h: Add the CAN_NOLINKS flag to indicate we don't want to follow symlinks. Also provide CAN_MODE_MASK to aid setting these existing mutually exclusive values. * lib/canonicalize.c (canonicalize_filename_mode): Extract the flags from can_mode parameter, which are currently just used to select between stat() and lstat(). Also ensure that mutually exclusive values are flagged immediately as invalid. * tests/test-canonicalize.c: Verify symlinks are not followed, and that invalid flag combinations are diagnosed.
author Pádraig Brady <P@draigBrady.com>
date Thu, 29 Dec 2011 23:49:53 +0000
parents 557452ce040c
children 63675a6cbed4
files ChangeLog lib/canonicalize.c lib/canonicalize.h tests/test-canonicalize.c
diffstat 4 files changed, 50 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2011-12-29  Pádraig Brady  <P@draigBrady.com>
+
+	canonicalize: add support for not resolving symlinks
+	* lib/canonicalize.h: Add the CAN_NOLINKS flag to
+	indicate we don't want to follow symlinks.  Also
+	provide CAN_MODE_MASK to aid setting these existing
+	mutually exclusive values.
+	* lib/canonicalize.c (canonicalize_filename_mode):
+	Extract the flags from can_mode parameter, which
+	are currently just used to select between stat()
+	and lstat().  Also ensure that mutually exclusive
+	values are flagged immediately as invalid.
+	* tests/test-canonicalize.c: Verify symlinks are
+	not followed, and that invalid flag combinations
+	are diagnosed.
+
 2011-12-25  Jim Meyering  <meyering@redhat.com>
 
 	gitlog-to-changelog: do not clump multi-paragraph entries
--- a/lib/canonicalize.c
+++ b/lib/canonicalize.c
@@ -31,6 +31,8 @@
 #include "xalloc.h"
 #include "xgetcwd.h"
 
+#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
+
 /* In this file, we cannot handle file names longer than PATH_MAX.
    On systems with no file name length limit, use a fallback.  */
 #ifndef PATH_MAX
@@ -82,8 +84,9 @@
 /* Return the canonical absolute name of file NAME, while treating
    missing elements according to CAN_MODE.  A canonical name
    does not contain any `.', `..' components nor any repeated file name
-   separators ('/') or symlinks.  Whether components must exist
-   or not depends on canonicalize mode.  The result is malloc'd.  */
+   separators ('/') or, depdending on other CAN_MODE flags, symlinks.
+   Whether components must exist or not depends on canonicalize mode.
+   The result is malloc'd.  */
 
 char *
 canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
@@ -95,6 +98,16 @@
   size_t extra_len = 0;
   Hash_table *ht = NULL;
   int saved_errno;
+  int can_flags = can_mode & ~CAN_MODE_MASK;
+  can_mode &= CAN_MODE_MASK;
+  bool logical = can_flags & CAN_NOLINKS;
+  /* Perhaps in future we might support CAN_NOALLOC with CAN_NOLINKS.  */
+
+  if (MULTIPLE_BITS_SET (can_mode))
+    {
+      errno = EINVAL;
+      return NULL;
+    }
 
   if (name == NULL)
     {
@@ -185,7 +198,7 @@
           dest += end - start;
           *dest = '\0';
 
-          if (lstat (rname, &st) != 0)
+          if ((logical ? stat : lstat) (rname, &st) != 0)
             {
               saved_errno = errno;
               if (can_mode == CAN_EXISTING)
--- a/lib/canonicalize.h
+++ b/lib/canonicalize.h
@@ -19,6 +19,8 @@
 
 #include <stdlib.h> /* for canonicalize_file_name */
 
+#define CAN_MODE_MASK (CAN_EXISTING | CAN_ALL_BUT_LAST | CAN_MISSING)
+
 enum canonicalize_mode_t
   {
     /* All components must exist.  */
@@ -28,7 +30,10 @@
     CAN_ALL_BUT_LAST = 1,
 
     /* No requirements on components existence.  */
-    CAN_MISSING = 2
+    CAN_MISSING = 2,
+
+    /* Don't expand symlinks.  */
+    CAN_NOLINKS = 4
   };
 typedef enum canonicalize_mode_t canonicalize_mode_t;
 
--- a/tests/test-canonicalize.c
+++ b/tests/test-canonicalize.c
@@ -82,6 +82,9 @@
     result2 = canonicalize_filename_mode (NULL, CAN_EXISTING);
     ASSERT (result2 == NULL);
     ASSERT (errno == EINVAL);
+    result2 = canonicalize_filename_mode (".", CAN_MISSING | CAN_ALL_BUT_LAST);
+    ASSERT (result2 == NULL);
+    ASSERT (errno == EINVAL);
   }
 
   /* Check that a non-directory with trailing slash yields NULL.  */
@@ -134,6 +137,15 @@
   ASSERT (symlink ("../s/2", BASE "/d/1") == 0);
   ASSERT (symlink ("//.//../..", BASE "/droot") == 0);
 
+  /* Check that symbolic links are not resolved, with CAN_NOLINKS.  */
+  {
+    char *result1 = canonicalize_filename_mode (BASE "/huk", CAN_NOLINKS);
+    ASSERT (result1 != NULL);
+    ASSERT (strcmp (result1 + strlen (result1) - strlen ("/" BASE "/huk"),
+                    "/" BASE "/huk") == 0);
+    free (result1);
+  }
+
   /* Check that the symbolic link to a file can be resolved.  */
   {
     char *result1 = canonicalize_file_name (BASE "/huk");