summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfjoe <fjoe@FreeBSD.org>2003-03-27 20:48:53 +0000
committerfjoe <fjoe@FreeBSD.org>2003-03-27 20:48:53 +0000
commit441ddbc3265c9c70d353b0ad8e8b600fb70fc9ef (patch)
treebfe0c2e2e80d75cc3560d4fc0a107e6fc185206c
parent23e9ae34837fd6a192a53e00cce87b98ffa8d547 (diff)
downloadFreeBSD-src-441ddbc3265c9c70d353b0ad8e8b600fb70fc9ef.zip
FreeBSD-src-441ddbc3265c9c70d353b0ad8e8b600fb70fc9ef.tar.gz
Make realpath() thread-safe. New implementation does not use chdir(2) at all.
Submitted by: Constantin S. Svintsoff <kostik (at) iclub.nsu.ru>
-rw-r--r--lib/libc/stdlib/realpath.34
-rw-r--r--lib/libc/stdlib/realpath.c235
2 files changed, 128 insertions, 111 deletions
diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3
index 2f84d1d..3593df7 100644
--- a/lib/libc/stdlib/realpath.3
+++ b/lib/libc/stdlib/realpath.3
@@ -99,11 +99,7 @@ The function
may fail and set the external variable
.Va errno
for any of the errors specified for the library functions
-.Xr chdir 2 ,
-.Xr close 2 ,
-.Xr fchdir 2 ,
.Xr lstat 2 ,
-.Xr open 2 ,
.Xr readlink 2
and
.Xr getcwd 3 .
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c
index eff5d23..a5d551c 100644
--- a/lib/libc/stdlib/realpath.c
+++ b/lib/libc/stdlib/realpath.c
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Jan-Simon Pendry.
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -13,18 +9,14 @@
* 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
+ * 3. The names of the authors may not 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
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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)
@@ -45,8 +37,6 @@ __FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "un-namespace.h"
@@ -59,108 +49,139 @@ __FBSDID("$FreeBSD$");
* in which case the path which caused trouble is left in (resolved).
*/
char *
-realpath(path, resolved)
- const char *path;
- char *resolved;
+realpath(const char *path, char resolved_path[MAXPATHLEN])
{
- struct stat sb;
- int fd, n, rootd, serrno;
- char *p, *q, wbuf[PATH_MAX];
- int symlinks = 0;
-
- /* Save the starting point. */
- if ((fd = _open(".", O_RDONLY)) < 0) {
- (void)strcpy(resolved, ".");
- return (NULL);
- }
+ unsigned num_symlinks = 0;
+ int saved_errno = errno;
- /*
- * Find the dirname and basename from the path to be resolved.
- * Change directory to the dirname component.
- * lstat the basename part.
- * if it is a symlink, read in the value and loop.
- * if it is a directory, then change to that directory.
- * get the current directory name and append the basename.
- */
- (void)strlcpy(resolved, path, PATH_MAX);
-loop:
- q = strrchr(resolved, '/');
- if (q != NULL) {
- p = q + 1;
- if (q == resolved)
- q = "/";
- else {
- do {
- --q;
- } while (q > resolved && *q == '/');
- q[1] = '\0';
- q = resolved;
+ char left[MAXPATHLEN];
+ size_t left_len, resolved_len;
+
+ if (path[0] == '/') {
+ resolved_path[0] = '/';
+ resolved_path[1] = '\0';
+ if (path[1] == '\0')
+ return resolved_path;
+ resolved_len = 1;
+ left_len = strlcpy(left, path + 1, MAXPATHLEN);
+ } else {
+ if (getcwd(resolved_path, MAXPATHLEN) == NULL) {
+ strlcpy(resolved_path, ".", MAXPATHLEN);
+ return NULL;
}
- if (chdir(q) < 0)
- goto err1;
- } else
- p = resolved;
-
- /* Deal with the last component. */
- if (*p != '\0' && lstat(p, &sb) == 0) {
- if (S_ISLNK(sb.st_mode)) {
- if (++symlinks > MAXSYMLINKS) {
- errno = ELOOP;
- goto err1;
+ resolved_len = strlen(resolved_path);
+ left_len = strlcpy(left, path, MAXPATHLEN);
+ }
+ if (left_len >= MAXPATHLEN || resolved_len >= MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ while (left_len > 0) {
+ struct stat st;
+ char next_token[MAXPATHLEN];
+ char *p;
+ char *s = (p = strchr(left, '/')) ? p : left + left_len;
+
+ memmove(next_token, left, s - left);
+ left_len -= s - left;
+ if (p != NULL)
+ memmove(left, s + 1, left_len + 1);
+
+ next_token[s - left] = '\0';
+ if (resolved_path[resolved_len - 1] != '/') {
+ if (resolved_len + 1 >= MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL;
}
- n = readlink(p, resolved, PATH_MAX - 1);
- if (n < 0)
- goto err1;
- resolved[n] = '\0';
- goto loop;
+
+ resolved_path[resolved_len++] = '/';
+ resolved_path[resolved_len] = '\0';
}
- if (S_ISDIR(sb.st_mode)) {
- if (chdir(p) < 0)
- goto err1;
- p = "";
+
+ if (next_token[0] == '\0')
+ continue;
+ else if (!strcmp(next_token, "."))
+ continue;
+ else if (!strcmp(next_token, "..")) {
+ if (resolved_len > 1) {
+ char *q;
+
+ /* trailing slash */
+ resolved_path[resolved_len - 1] = '\0';
+
+ q = strrchr(resolved_path, '/');
+ *q = '\0';
+ resolved_len = q - resolved_path;
+ }
+ continue;
}
- }
- /*
- * Save the last component name and get the full pathname of
- * the current directory.
- */
- (void)strcpy(wbuf, p);
- if (getcwd(resolved, PATH_MAX) == 0)
- goto err1;
-
- /*
- * Join the two strings together, ensuring that the right thing
- * happens if the last component is empty, or the dirname is root.
- */
- if (resolved[0] == '/' && resolved[1] == '\0')
- rootd = 1;
- else
- rootd = 0;
-
- if (*wbuf) {
- if (strlen(resolved) + strlen(wbuf) + rootd + 1 > PATH_MAX) {
+ /* filename */
+ resolved_len = strlcat(resolved_path, next_token, MAXPATHLEN);
+ if (resolved_len >= MAXPATHLEN) {
errno = ENAMETOOLONG;
- goto err1;
+ return NULL;
}
- if (rootd == 0)
- (void)strcat(resolved, "/");
- (void)strcat(resolved, wbuf);
- }
- /* Go back to where we came from. */
- if (fchdir(fd) < 0) {
- serrno = errno;
- goto err2;
+ if (lstat(resolved_path, &st) < 0) {
+ if (errno == ENOENT && p == NULL) {
+ errno = saved_errno;
+ return resolved_path;
+ }
+
+ return NULL;
+ }
+
+ if ((st.st_mode & S_IFLNK) == S_IFLNK) {
+ char symlink[MAXPATHLEN];
+ int slen;
+
+ if (num_symlinks++ > MAXSYMLINKS) {
+ errno = ELOOP;
+ return NULL;
+ }
+ slen = readlink(resolved_path, symlink, MAXPATHLEN);
+ if (slen < 0)
+ return NULL;
+ symlink[slen] = '\0';
+
+ if (symlink[0] == '/') {
+ /* absolute link */
+ resolved_path[1] = 0;
+ resolved_len = 1;
+ } else if (resolved_len > 1) {
+ char *q;
+
+ /* trailing slash */
+ resolved_path[resolved_len - 1] = '\0';
+
+ q = strrchr(resolved_path, '/');
+ *q = '\0';
+ resolved_len = q - resolved_path;
+ }
+
+ if (symlink[slen - 1] != '/' && p != NULL) {
+ if (slen >= MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ symlink[slen] = '/';
+ symlink[slen + 1] = 0;
+ }
+ if (p != NULL)
+ left_len = strlcat(symlink, left, MAXPATHLEN);
+ if (left_len > MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ left_len = strlcpy(left, symlink, MAXPATHLEN);
+ }
}
- /* It's okay if the close fails, what's an fd more or less? */
- (void)_close(fd);
- return (resolved);
+ if (resolved_len > 1 && resolved_path[resolved_len - 1] == '/')
+ resolved_path[resolved_len - 1] = '\0';
-err1: serrno = errno;
- (void)fchdir(fd);
-err2: (void)_close(fd);
- errno = serrno;
- return (NULL);
+ return resolved_path;
}
OpenPOWER on IntegriCloud