This is for use in a proposed coreutils `realpath` command.
Specifically by these options:

  -L, --logical                resolve `..' components before symlinks
  -s, --strip                  don't expand symlinks

cheers,
Pádraig.
From bb40841634386d53c7e83d88f40396fa8ee83e3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com>
Date: Thu, 29 Dec 2011 23:49:53 +0000
Subject: [PATCH] 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 and flagged immediately as invalid.
* tests/test-canonicalize.c: Verify symlinks are
not followed, and that invalid flag combinations
are diagnosed.
---
 ChangeLog                 |   16 ++++++++++++++++
 lib/canonicalize.c        |   18 +++++++++++++++---
 lib/canonicalize.h        |   10 +++++++++-
 tests/test-canonicalize.c |   12 ++++++++++++
 4 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cc50b4a..06dfe77 100644
--- 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 and 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  <meyer...@redhat.com>
 
        gitlog-to-changelog: do not clump multi-paragraph entries
diff --git a/lib/canonicalize.c b/lib/canonicalize.c
index 4fe9f30..c73a963 100644
--- 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 @@ seen_triple (Hash_table **ht, char const *filename, struct 
stat const *st)
 /* 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,15 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
   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;
+
+  if (MULTIPLE_BITS_SET (can_mode))
+    {
+      errno = EINVAL;
+      return NULL;
+    }
 
   if (name == NULL)
     {
@@ -185,7 +197,7 @@ canonicalize_filename_mode (const char *name, 
canonicalize_mode_t can_mode)
           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)
diff --git a/lib/canonicalize.h b/lib/canonicalize.h
index 04ad79c..4bb5898 100644
--- 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,13 @@ enum canonicalize_mode_t
     CAN_ALL_BUT_LAST = 1,
 
     /* No requirements on components existence.  */
-    CAN_MISSING = 2
+    CAN_MISSING = 2,
+
+    /* Don't expand symlinks.  */
+    CAN_NOLINKS = 4,
+
+    /* Modify the original (Not yet supported).  */
+    CAN_NOALLOC = 8
   };
 typedef enum canonicalize_mode_t canonicalize_mode_t;
 
diff --git a/tests/test-canonicalize.c b/tests/test-canonicalize.c
index a095e0e..9f1b876 100644
--- a/tests/test-canonicalize.c
+++ b/tests/test-canonicalize.c
@@ -82,6 +82,9 @@ main (void)
     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 @@ main (void)
   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");
-- 
1.7.6.4

Reply via email to