New module 'idpriv-droptemp'.
[gnulib.git] / lib / canonicalize.c
index 63dfb6d..4f34839 100644 (file)
@@ -1,10 +1,10 @@
 /* Return the canonical absolute name of a given file.
-   Copyright (C) 1996-2006 Free Software Foundation, Inc.
+   Copyright (C) 1996-2008 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify
+   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,9 +12,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; see the file COPYING.
-   If not, write to the Free Software Foundation,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
@@ -34,9 +32,9 @@
 #include <errno.h>
 #include <stddef.h>
 
-#include "cycle-check.h"
+#include "file-set.h"
 #include "filenamecat.h"
-#include "stat-macros.h"
+#include "hash-triple.h"
 #include "xalloc.h"
 #include "xgetcwd.h"
 
@@ -48,9 +46,9 @@
 #endif
 
 #include "pathmax.h"
-#include "xreadlink.h"
+#include "areadlink.h"
 
-#if !HAVE_CANONICALIZE_FILE_NAME
+#if !(HAVE_CANONICALIZE_FILE_NAME || GNULIB_CANONICALIZE_LGPL)
 /* Return the canonical absolute name of file NAME.  A canonical name
    does not contain any `.', `..' components nor any repeated file name
    separators ('/') or symlinks.  All components must exist.
@@ -124,6 +122,30 @@ canonicalize_file_name (const char *name)
 }
 #endif /* !HAVE_CANONICALIZE_FILE_NAME */
 
+/* Return true if we've already seen the triple, <FILENAME, dev, ino>.
+   If *HT is not initialized, initialize it.  */
+static bool
+seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
+{
+  if (*ht == NULL)
+    {
+      size_t initial_capacity = 7;
+      *ht = hash_initialize (initial_capacity,
+                           NULL,
+                           triple_hash,
+                           triple_compare_ino_str,
+                           triple_free);
+      if (*ht == NULL)
+       xalloc_die ();
+    }
+
+  if (seen_file (*ht, filename, st))
+    return true;
+
+  record_file (*ht, filename, st);
+  return false;
+}
+
 /* Return the canonical absolute name of file NAME.  A canonical name
    does not contain any `.', `..' components nor any repeated file name
    separators ('/') or symlinks.  Whether components must exist
@@ -137,7 +159,7 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
   char const *end;
   char const *rname_limit;
   size_t extra_len = 0;
-  struct cycle_check_state cycle_state;
+  Hash_table *ht = NULL;
 
   if (name == NULL)
     {
@@ -177,7 +199,6 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
       dest = rname + 1;
     }
 
-  cycle_check_init (&cycle_state);
   for (start = end = name; *start; start = end)
     {
       /* Skip sequence of multiple file name separators.  */
@@ -238,7 +259,11 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
              char *buf;
              size_t n, len;
 
-             if (cycle_check (&cycle_state, &st))
+             /* Detect loops.  We cannot use the cycle-check module here,
+                since it's actually possible to encounter the same symlink
+                more than once in a given traversal.  However, encountering
+                the same symlink,NAME pair twice does indicate a loop.  */
+             if (seen_triple (&ht, name, &st))
                {
                  __set_errno (ELOOP);
                  if (can_mode == CAN_MISSING)
@@ -247,10 +272,10 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
                    goto error;
                }
 
-             buf = xreadlink (rname, st.st_size);
+             buf = areadlink_with_size (rname, st.st_size);
              if (!buf)
                {
-                 if (can_mode == CAN_MISSING)
+                 if (can_mode == CAN_MISSING && errno != ENOMEM)
                    continue;
                  else
                    goto error;
@@ -299,10 +324,14 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
   *dest = '\0';
 
   free (extra_buf);
+  if (ht)
+    hash_free (ht);
   return rname;
 
 error:
   free (extra_buf);
   free (rname);
+  if (ht)
+    hash_free (ht);
   return NULL;
 }