Annotation of embedaddon/libiconv/srclib/canonicalize-lgpl.c, revision 1.1.1.2
1.1 misho 1: /* Return the canonical absolute name of a given file.
1.1.1.2 ! misho 2: Copyright (C) 1996-2011 Free Software Foundation, Inc.
1.1 misho 3: This file is part of the GNU C Library.
4:
5: This program is free software: you can redistribute it and/or modify
6: it under the terms of the GNU General Public License as published by
7: the Free Software Foundation; either version 3 of the License, or
8: (at your option) any later version.
9:
10: This program is distributed in the hope that it will be useful,
11: but WITHOUT ANY WARRANTY; without even the implied warranty of
12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: GNU General Public License for more details.
14:
15: You should have received a copy of the GNU General Public License
16: along with this program. If not, see <http://www.gnu.org/licenses/>. */
17:
1.1.1.2 ! misho 18: #ifndef _LIBC
! 19: # define _GL_USE_STDLIB_ALLOC 1
! 20: # include <config.h>
! 21: #endif
1.1 misho 22:
1.1.1.2 ! misho 23: #if !HAVE_CANONICALIZE_FILE_NAME || !FUNC_REALPATH_WORKS || defined _LIBC
1.1 misho 24:
1.1.1.2 ! misho 25: /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
! 26: optimizes away the name == NULL test below. */
! 27: #define _GL_ARG_NONNULL(params)
1.1 misho 28:
29: /* Specification. */
30: #include <stdlib.h>
31:
1.1.1.2 ! misho 32: #include <alloca.h>
! 33: #include <string.h>
! 34: #include <unistd.h>
1.1 misho 35: #include <limits.h>
36: #if HAVE_SYS_PARAM_H || defined _LIBC
37: # include <sys/param.h>
38: #endif
39: #include <sys/stat.h>
40: #include <errno.h>
1.1.1.2 ! misho 41: #include <stddef.h>
1.1 misho 42:
43: #ifdef _LIBC
44: # include <shlib-compat.h>
45: #else
46: # define SHLIB_COMPAT(lib, introduced, obsoleted) 0
1.1.1.2 ! misho 47: # define versioned_symbol(lib, local, symbol, version) extern int dummy
1.1 misho 48: # define compat_symbol(lib, local, symbol, version)
49: # define weak_alias(local, symbol)
50: # define __canonicalize_file_name canonicalize_file_name
1.1.1.2 ! misho 51: # define __realpath realpath
1.1 misho 52: # include "pathmax.h"
53: # include "malloca.h"
54: # if HAVE_GETCWD
1.1.1.2 ! misho 55: # if IN_RELOCWRAPPER
! 56: /* When building the relocatable program wrapper, use the system's getcwd
! 57: function, not the gnulib override, otherwise we would get a link error.
! 58: */
! 59: # undef getcwd
! 60: # endif
1.1 misho 61: # ifdef VMS
62: /* We want the directory in Unix syntax, not in VMS syntax. */
63: # define __getcwd(buf, max) getcwd (buf, max, 0)
64: # else
65: # define __getcwd getcwd
66: # endif
67: # else
68: # define __getcwd(buf, max) getwd (buf)
69: # endif
70: # define __readlink readlink
1.1.1.2 ! misho 71: # define __set_errno(e) errno = (e)
! 72: # ifndef MAXSYMLINKS
! 73: # ifdef SYMLOOP_MAX
! 74: # define MAXSYMLINKS SYMLOOP_MAX
! 75: # else
! 76: # define MAXSYMLINKS 20
! 77: # endif
1.1 misho 78: # endif
79: #endif
80:
1.1.1.2 ! misho 81: #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
! 82: # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
! 83: #endif
! 84:
! 85: #if !FUNC_REALPATH_WORKS || defined _LIBC
1.1 misho 86: /* Return the canonical absolute name of file NAME. A canonical name
87: does not contain any `.', `..' components nor any repeated path
88: separators ('/') or symlinks. All path components must exist. If
89: RESOLVED is null, the result is malloc'd; otherwise, if the
90: canonical name is PATH_MAX chars or more, returns null with `errno'
91: set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
92: returns the name in RESOLVED. If the name cannot be resolved and
93: RESOLVED is non-NULL, it contains the path of the first component
94: that cannot be resolved. If the path can be resolved, RESOLVED
95: holds the same value as the value returned. */
96:
97: char *
98: __realpath (const char *name, char *resolved)
99: {
100: char *rpath, *dest, *extra_buf = NULL;
101: const char *start, *end, *rpath_limit;
102: long int path_max;
103: int num_links = 0;
104:
105: if (name == NULL)
106: {
107: /* As per Single Unix Specification V2 we must return an error if
1.1.1.2 ! misho 108: either parameter is a null pointer. We extend this to allow
! 109: the RESOLVED parameter to be NULL in case the we are expected to
! 110: allocate the room for the return value. */
1.1 misho 111: __set_errno (EINVAL);
112: return NULL;
113: }
114:
115: if (name[0] == '\0')
116: {
117: /* As per Single Unix Specification V2 we must return an error if
1.1.1.2 ! misho 118: the name argument points to an empty string. */
1.1 misho 119: __set_errno (ENOENT);
120: return NULL;
121: }
122:
123: #ifdef PATH_MAX
124: path_max = PATH_MAX;
125: #else
126: path_max = pathconf (name, _PC_PATH_MAX);
127: if (path_max <= 0)
1.1.1.2 ! misho 128: path_max = 8192;
1.1 misho 129: #endif
130:
131: if (resolved == NULL)
132: {
133: rpath = malloc (path_max);
134: if (rpath == NULL)
1.1.1.2 ! misho 135: {
! 136: /* It's easier to set errno to ENOMEM than to rely on the
! 137: 'malloc-posix' gnulib module. */
! 138: errno = ENOMEM;
! 139: return NULL;
! 140: }
1.1 misho 141: }
142: else
143: rpath = resolved;
144: rpath_limit = rpath + path_max;
145:
146: if (name[0] != '/')
147: {
148: if (!__getcwd (rpath, path_max))
1.1.1.2 ! misho 149: {
! 150: rpath[0] = '\0';
! 151: goto error;
! 152: }
1.1 misho 153: dest = strchr (rpath, '\0');
154: }
155: else
156: {
157: rpath[0] = '/';
158: dest = rpath + 1;
1.1.1.2 ! misho 159: if (DOUBLE_SLASH_IS_DISTINCT_ROOT && name[1] == '/')
! 160: *dest++ = '/';
1.1 misho 161: }
162:
163: for (start = end = name; *start; start = end)
164: {
165: #ifdef _LIBC
166: struct stat64 st;
167: #else
168: struct stat st;
169: #endif
1.1.1.2 ! misho 170: int n;
1.1 misho 171:
172: /* Skip sequence of multiple path-separators. */
173: while (*start == '/')
1.1.1.2 ! misho 174: ++start;
1.1 misho 175:
176: /* Find end of path component. */
177: for (end = start; *end && *end != '/'; ++end)
1.1.1.2 ! misho 178: /* Nothing. */;
1.1 misho 179:
180: if (end - start == 0)
1.1.1.2 ! misho 181: break;
1.1 misho 182: else if (end - start == 1 && start[0] == '.')
1.1.1.2 ! misho 183: /* nothing */;
1.1 misho 184: else if (end - start == 2 && start[0] == '.' && start[1] == '.')
1.1.1.2 ! misho 185: {
! 186: /* Back up to previous component, ignore if at root already. */
! 187: if (dest > rpath + 1)
! 188: while ((--dest)[-1] != '/');
! 189: if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
! 190: && *dest == '/')
! 191: dest++;
! 192: }
1.1 misho 193: else
1.1.1.2 ! misho 194: {
! 195: size_t new_size;
1.1 misho 196:
1.1.1.2 ! misho 197: if (dest[-1] != '/')
! 198: *dest++ = '/';
1.1 misho 199:
1.1.1.2 ! misho 200: if (dest + (end - start) >= rpath_limit)
! 201: {
! 202: ptrdiff_t dest_offset = dest - rpath;
! 203: char *new_rpath;
! 204:
! 205: if (resolved)
! 206: {
! 207: __set_errno (ENAMETOOLONG);
! 208: if (dest > rpath + 1)
! 209: dest--;
! 210: *dest = '\0';
! 211: goto error;
! 212: }
! 213: new_size = rpath_limit - rpath;
! 214: if (end - start + 1 > path_max)
! 215: new_size += end - start + 1;
! 216: else
! 217: new_size += path_max;
! 218: new_rpath = (char *) realloc (rpath, new_size);
! 219: if (new_rpath == NULL)
! 220: {
! 221: /* It's easier to set errno to ENOMEM than to rely on the
! 222: 'realloc-posix' gnulib module. */
! 223: errno = ENOMEM;
! 224: goto error;
! 225: }
! 226: rpath = new_rpath;
! 227: rpath_limit = rpath + new_size;
1.1 misho 228:
1.1.1.2 ! misho 229: dest = rpath + dest_offset;
! 230: }
1.1 misho 231:
232: #ifdef _LIBC
1.1.1.2 ! misho 233: dest = __mempcpy (dest, start, end - start);
1.1 misho 234: #else
1.1.1.2 ! misho 235: memcpy (dest, start, end - start);
! 236: dest += end - start;
1.1 misho 237: #endif
1.1.1.2 ! misho 238: *dest = '\0';
1.1 misho 239:
240: #ifdef _LIBC
1.1.1.2 ! misho 241: if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
1.1 misho 242: #else
1.1.1.2 ! misho 243: if (lstat (rpath, &st) < 0)
1.1 misho 244: #endif
1.1.1.2 ! misho 245: goto error;
1.1 misho 246:
1.1.1.2 ! misho 247: if (S_ISLNK (st.st_mode))
! 248: {
! 249: char *buf;
! 250: size_t len;
! 251:
! 252: if (++num_links > MAXSYMLINKS)
! 253: {
! 254: __set_errno (ELOOP);
! 255: goto error;
! 256: }
! 257:
! 258: buf = malloca (path_max);
! 259: if (!buf)
! 260: {
! 261: errno = ENOMEM;
! 262: goto error;
! 263: }
! 264:
! 265: n = __readlink (rpath, buf, path_max - 1);
! 266: if (n < 0)
! 267: {
! 268: int saved_errno = errno;
! 269: freea (buf);
! 270: errno = saved_errno;
! 271: goto error;
! 272: }
! 273: buf[n] = '\0';
! 274:
! 275: if (!extra_buf)
! 276: {
! 277: extra_buf = malloca (path_max);
! 278: if (!extra_buf)
! 279: {
! 280: freea (buf);
! 281: errno = ENOMEM;
! 282: goto error;
! 283: }
! 284: }
! 285:
! 286: len = strlen (end);
! 287: if ((long int) (n + len) >= path_max)
! 288: {
! 289: freea (buf);
! 290: __set_errno (ENAMETOOLONG);
! 291: goto error;
! 292: }
! 293:
! 294: /* Careful here, end may be a pointer into extra_buf... */
! 295: memmove (&extra_buf[n], end, len + 1);
! 296: name = end = memcpy (extra_buf, buf, n);
! 297:
! 298: if (buf[0] == '/')
! 299: {
! 300: dest = rpath + 1; /* It's an absolute symlink */
! 301: if (DOUBLE_SLASH_IS_DISTINCT_ROOT && buf[1] == '/')
! 302: *dest++ = '/';
! 303: }
! 304: else
! 305: {
! 306: /* Back up to previous component, ignore if at root
! 307: already: */
! 308: if (dest > rpath + 1)
! 309: while ((--dest)[-1] != '/');
! 310: if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
! 311: && *dest == '/')
! 312: dest++;
! 313: }
! 314: }
! 315: else if (!S_ISDIR (st.st_mode) && *end != '\0')
! 316: {
! 317: __set_errno (ENOTDIR);
! 318: goto error;
! 319: }
! 320: }
1.1 misho 321: }
322: if (dest > rpath + 1 && dest[-1] == '/')
323: --dest;
1.1.1.2 ! misho 324: if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && *dest == '/')
! 325: dest++;
1.1 misho 326: *dest = '\0';
327:
328: if (extra_buf)
329: freea (extra_buf);
330:
1.1.1.2 ! misho 331: return rpath;
1.1 misho 332:
333: error:
334: {
335: int saved_errno = errno;
336: if (extra_buf)
337: freea (extra_buf);
1.1.1.2 ! misho 338: if (resolved == NULL)
1.1 misho 339: free (rpath);
340: errno = saved_errno;
341: }
342: return NULL;
343: }
344: versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
1.1.1.2 ! misho 345: #endif /* !FUNC_REALPATH_WORKS || defined _LIBC */
1.1 misho 346:
347:
348: #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
349: char *
1.1.1.2 ! misho 350: attribute_compat_text_section
1.1 misho 351: __old_realpath (const char *name, char *resolved)
352: {
353: if (resolved == NULL)
354: {
355: __set_errno (EINVAL);
356: return NULL;
357: }
358:
359: return __realpath (name, resolved);
360: }
361: compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
362: #endif
363:
364:
365: char *
366: __canonicalize_file_name (const char *name)
367: {
368: return __realpath (name, NULL);
369: }
370: weak_alias (__canonicalize_file_name, canonicalize_file_name)
371:
372: #else
373:
374: /* This declaration is solely to ensure that after preprocessing
375: this file is never empty. */
376: typedef int dummy;
377:
378: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>