diff options
Diffstat (limited to '')
-rw-r--r-- | lib/util/realpath.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/lib/util/realpath.c b/lib/util/realpath.c new file mode 100644 index 0000000..9e945af --- /dev/null +++ b/lib/util/realpath.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1989, 1991, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)getcwd.c 8.5 (Berkeley) 2/7/95 + */ + +#include <config.h> + +#ifndef HAVE_REALPATH + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * char *realpath(const char *path, char *resolved); + * + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + */ +char * +sudo_realpath(const char * restrict path, char * restrict resolved) +{ + struct stat sb; + int idx = 0, nlnk = 0; + const char *q; + char *p, wbuf[2][PATH_MAX], *fres = NULL; + static long symloop_max; + size_t len; + ssize_t n; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + + if (symloop_max == 0) { + if ((symloop_max = sysconf(_SC_SYMLOOP_MAX)) <= 0) + symloop_max = 8; /* POSIX */ + } + + if (resolved == NULL) { + fres = resolved = malloc(PATH_MAX); + if (resolved == NULL) + return NULL; + } + + + /* + * Build real path one by one with paying an attention to ., + * .. and symbolic link. + */ + + /* + * `p' is where we'll put a new component with prepending + * a delimiter. + */ + p = resolved; + + if (*path == '\0') { + *p = '\0'; + errno = ENOENT; + goto out; + } + + /* If relative path, start from current working directory. */ + if (*path != '/') { + if (getcwd(resolved, PATH_MAX) == NULL) { + p[0] = '.'; + p[1] = '\0'; + goto out; + } + len = strlen(resolved); + if (len > 1) + p += len; + } + +loop: + /* Skip any slash. */ + while (*path == '/') + path++; + + if (*path == '\0') { + if (p == resolved) + *p++ = '/'; + *p = '\0'; + return resolved; + } + + /* Find the end of this component. */ + q = path; + do { + q++; + } while (*q != '/' && *q != '\0'); + + /* Test . or .. */ + if (path[0] == '.') { + if (q - path == 1) { + path = q; + goto loop; + } + if (path[1] == '.' && q - path == 2) { + /* Trim the last component. */ + if (p != resolved) + while (*--p != '/') + continue; + path = q; + goto loop; + } + } + + /* Append this component. */ + if (p - resolved + 1 + q - path + 1 > PATH_MAX) { + errno = ENAMETOOLONG; + if (p == resolved) + *p++ = '/'; + *p = '\0'; + goto out; + } + p[0] = '/'; + memcpy(&p[1], path, (size_t)(q - path)); + p[1 + q - path] = '\0'; + + /* + * If this component is a symlink, toss it and prepend link + * target to unresolved path. + */ + if (lstat(resolved, &sb) == -1) + goto out; + + if (S_ISLNK(sb.st_mode)) { + if (nlnk++ >= symloop_max) { + errno = ELOOP; + goto out; + } + n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); + if (n < 0) + goto out; + if (n == 0) { + errno = ENOENT; + goto out; + } + + /* Append unresolved path to link target and switch to it. */ + if ((size_t)n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { + errno = ENAMETOOLONG; + goto out; + } + memcpy(&wbuf[idx][n], q, len + 1); + path = wbuf[idx]; + idx ^= 1; + + /* If absolute symlink, start from root. */ + if (*path == '/') + p = resolved; + goto loop; + } + if (*q == '/' && !S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + goto out; + } + + /* Advance both resolved and unresolved path. */ + p += 1 + q - path; + path = q; + goto loop; +out: + free(fres); + return NULL; +} +#endif /* HAVE_REALPATH */ |