123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- /*
- * realpath.c -- canonicalize pathname by removing symlinks
- * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
- * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
- *
- * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #include <sys/types.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <strings.h>
- #include <limits.h> /* for PATH_MAX */
- #include <sys/param.h> /* for MAXPATHLEN */
- #include <errno.h>
- #include <stdlib.h>
- #include <sys/stat.h> /* for S_IFLNK */
- /* Experimentally off - libc_hidden_proto(strcat) */
- /* Experimentally off - libc_hidden_proto(strcpy) */
- /* Experimentally off - libc_hidden_proto(strlen) */
- libc_hidden_proto(readlink)
- libc_hidden_proto(getcwd)
- #ifndef PATH_MAX
- #ifdef _POSIX_VERSION
- #define PATH_MAX _POSIX_PATH_MAX
- #else
- #ifdef MAXPATHLEN
- #define PATH_MAX MAXPATHLEN
- #else
- #define PATH_MAX 1024
- #endif
- #endif
- #endif
- #define MAX_READLINKS 32
- #ifdef __STDC__
- char *realpath(const char *path, char got_path[])
- #else
- char *realpath(path, got_path)
- const char *path;
- char got_path[];
- #endif
- {
- char copy_path[PATH_MAX];
- /* use user supplied buffer directly - reduces stack usage */
- /* char got_path[PATH_MAX]; */
- char *max_path;
- char *new_path;
- size_t path_len;
- int readlinks = 0;
- #ifdef S_IFLNK
- int link_len;
- #endif
- if (path == NULL) {
- __set_errno(EINVAL);
- return NULL;
- }
- if (*path == '\0') {
- __set_errno(ENOENT);
- return NULL;
- }
- /* Make a copy of the source path since we may need to modify it. */
- path_len = strlen(path);
- if (path_len >= PATH_MAX - 2) {
- __set_errno(ENAMETOOLONG);
- return NULL;
- }
- /* Copy so that path is at the end of copy_path[] */
- strcpy(copy_path + (PATH_MAX-1) - path_len, path);
- path = copy_path + (PATH_MAX-1) - path_len;
- max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
- new_path = got_path;
- if (*path != '/') {
- /* If it's a relative pathname use getcwd for starters. */
- if (!getcwd(new_path, PATH_MAX - 1))
- return NULL;
- new_path += strlen(new_path);
- if (new_path[-1] != '/')
- *new_path++ = '/';
- } else {
- *new_path++ = '/';
- path++;
- }
- /* Expand each slash-separated pathname component. */
- while (*path != '\0') {
- /* Ignore stray "/". */
- if (*path == '/') {
- path++;
- continue;
- }
- if (*path == '.') {
- /* Ignore ".". */
- if (path[1] == '\0' || path[1] == '/') {
- path++;
- continue;
- }
- if (path[1] == '.') {
- if (path[2] == '\0' || path[2] == '/') {
- path += 2;
- /* Ignore ".." at root. */
- if (new_path == got_path + 1)
- continue;
- /* Handle ".." by backing up. */
- while ((--new_path)[-1] != '/');
- continue;
- }
- }
- }
- /* Safely copy the next pathname component. */
- while (*path != '\0' && *path != '/') {
- if (new_path > max_path) {
- __set_errno(ENAMETOOLONG);
- return NULL;
- }
- *new_path++ = *path++;
- }
- #ifdef S_IFLNK
- /* Protect against infinite loops. */
- if (readlinks++ > MAX_READLINKS) {
- __set_errno(ELOOP);
- return NULL;
- }
- path_len = strlen(path);
- /* See if last (so far) pathname component is a symlink. */
- *new_path = '\0';
- {
- int sv_errno = errno;
- link_len = readlink(got_path, copy_path, PATH_MAX - 1);
- if (link_len < 0) {
- /* EINVAL means the file exists but isn't a symlink. */
- if (errno != EINVAL) {
- return NULL;
- }
- } else {
- /* Safe sex check. */
- if (path_len + link_len >= PATH_MAX - 2) {
- __set_errno(ENAMETOOLONG);
- return NULL;
- }
- /* Note: readlink doesn't add the null byte. */
- /* copy_path[link_len] = '\0'; - we don't need it too */
- if (*copy_path == '/')
- /* Start over for an absolute symlink. */
- new_path = got_path;
- else
- /* Otherwise back up over this component. */
- while (*(--new_path) != '/');
- /* Prepend symlink contents to path. */
- memmove(copy_path + (PATH_MAX-1) - link_len - path_len, copy_path, link_len);
- path = copy_path + (PATH_MAX-1) - link_len - path_len;
- }
- __set_errno(sv_errno);
- }
- #endif /* S_IFLNK */
- *new_path++ = '/';
- }
- /* Delete trailing slash but don't whomp a lone slash. */
- if (new_path != got_path + 1 && new_path[-1] == '/')
- new_path--;
- /* Make sure it's null terminated. */
- *new_path = '\0';
- return got_path;
- }
|