/* Return the canonical absolute name of a given file.
- Copyright (C) 1996-2011 Free Software Foundation, Inc.
+ Copyright (C) 1996-2012 Free Software Foundation, Inc.
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
#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
+# define PATH_MAX 8192
+#endif
+
#ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
#endif
#if !((HAVE_CANONICALIZE_FILE_NAME && FUNC_REALPATH_WORKS) \
|| GNULIB_CANONICALIZE_LGPL)
/* Return the canonical absolute name of file NAME. A canonical name
- does not contain any `.', `..' components nor any repeated file name
+ does not contain any ".", ".." components nor any repeated file name
separators ('/') or symlinks. All components must exist.
The result is malloc'd. */
/* 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. */
+ does not contain any ".", ".." components nor any repeated file name
+ separators ('/') or, depending 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)
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)
{
rname_limit = rname + PATH_MAX;
rname[0] = '/';
dest = rname + 1;
- if (DOUBLE_SLASH_IS_DISTINCT_ROOT && name[1] == '/')
+ if (DOUBLE_SLASH_IS_DISTINCT_ROOT && name[1] == '/' && name[2] != '/')
*dest++ = '/';
}
if (dest > rname + 1)
while ((--dest)[-1] != '/');
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
- && *dest == '/')
+ && *dest == '/' && dest[1] != '/')
dest++;
}
else
dest += end - start;
*dest = '\0';
- if (lstat (rname, &st) != 0)
+ if (logical && (can_mode == CAN_MISSING))
+ {
+ /* Avoid the stat in this case as it's inconsequential.
+ i.e. we're neither resolving symlinks or testing
+ component existence. */
+ st.st_mode = 0;
+ }
+ else if ((logical ? stat (rname, &st) : lstat (rname, &st)) != 0)
{
saved_errno = errno;
if (can_mode == CAN_EXISTING)
if (buf[0] == '/')
{
dest = rname + 1; /* It's an absolute symlink */
- if (DOUBLE_SLASH_IS_DISTINCT_ROOT && buf[1] == '/')
+ if (DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && buf[1] == '/' && buf[2] != '/')
*dest++ = '/';
}
else
if (dest > rname + 1)
while ((--dest)[-1] != '/');
if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
- && *dest == '/')
+ && *dest == '/' && dest[1] != '/')
dest++;
}
}
if (dest > rname + 1 && dest[-1] == '/')
--dest;
- if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && *dest == '/')
+ if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
+ && *dest == '/' && dest[1] != '/')
dest++;
*dest = '\0';
if (rname_limit != dest + 1)