diff options
-rw-r--r-- | lib/libc/stdlib/realpath.c | 168 |
1 files changed, 89 insertions, 79 deletions
diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c index 56ece3a..c269471 100644 --- a/lib/libc/stdlib/realpath.c +++ b/lib/libc/stdlib/realpath.c @@ -37,151 +37,161 @@ __FBSDID("$FreeBSD$"); #include <sys/stat.h> #include <errno.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> #include "un-namespace.h" /* - * char *realpath(const char *path, char resolved_path[PATH_MAX]); + * char *realpath(const char *path, char resolved[PATH_MAX]); * * 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 * -realpath(const char *path, char resolved_path[PATH_MAX]) +realpath(const char *path, char resolved[PATH_MAX]) { - unsigned num_symlinks = 0; - int saved_errno = errno; - - char left[PATH_MAX]; + struct stat sb; + char *p, *q, *s; size_t left_len, resolved_len; + unsigned symlinks; + int serrno, slen; + char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; + serrno = errno; + symlinks = 0; if (path[0] == '/') { - resolved_path[0] = '/'; - resolved_path[1] = '\0'; + resolved[0] = '/'; + resolved[1] = '\0'; if (path[1] == '\0') - return resolved_path; + return (resolved); resolved_len = 1; - left_len = strlcpy(left, path + 1, PATH_MAX); + left_len = strlcpy(left, path + 1, sizeof(left)); } else { - if (getcwd(resolved_path, PATH_MAX) == NULL) { - strlcpy(resolved_path, ".", PATH_MAX); - return NULL; + if (getcwd(resolved, PATH_MAX) == NULL) { + strlcpy(resolved, ".", PATH_MAX); + return (NULL); } - resolved_len = strlen(resolved_path); - left_len = strlcpy(left, path, PATH_MAX); + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof(left)); } - if (left_len >= PATH_MAX || resolved_len >= PATH_MAX) { + if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { errno = ENAMETOOLONG; - return NULL; + return (NULL); } - while (left_len > 0) { - struct stat st; - char next_token[PATH_MAX]; - char *p; - char *s = (p = strchr(left, '/')) ? p : left + left_len; - - memmove(next_token, left, s - left); + /* + * Iterate over path components in `left'. + */ + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + s = p ? p : left + left_len; + if (s - left >= sizeof(next_token)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(next_token, left, s - left); + next_token[s - left] = '\0'; 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[resolved_len - 1] != '/') { if (resolved_len + 1 >= PATH_MAX) { errno = ENAMETOOLONG; - return NULL; + return (NULL); } - - resolved_path[resolved_len++] = '/'; - resolved_path[resolved_len] = '\0'; + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; } - if (next_token[0] == '\0') continue; - else if (!strcmp(next_token, ".")) + else if (strcmp(next_token, ".") == 0) continue; - else if (!strcmp(next_token, "..")) { + else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ if (resolved_len > 1) { - char *q; - - /* trailing slash */ - resolved_path[resolved_len - 1] = '\0'; - - q = strrchr(resolved_path, '/'); + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/'); *q = '\0'; - resolved_len = q - resolved_path; + resolved_len = q - resolved; } continue; } - /* filename */ - resolved_len = strlcat(resolved_path, next_token, PATH_MAX); + /* + * Append the next path component and lstat() it. If + * lstat() fails we still can return successfully if + * there are no more path components left. + */ + resolved_len = strlcat(resolved, next_token, PATH_MAX); if (resolved_len >= PATH_MAX) { errno = ENAMETOOLONG; - return NULL; + return (NULL); } - - if (lstat(resolved_path, &st) < 0) { + if (lstat(resolved, &sb) != 0) { if (errno == ENOENT && p == NULL) { - errno = saved_errno; - return resolved_path; + errno = serrno; + return (resolved); } - - return NULL; + return (NULL); } - - if ((st.st_mode & S_IFLNK) == S_IFLNK) { - char symlink[PATH_MAX]; - int slen; - - if (num_symlinks++ > MAXSYMLINKS) { + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > MAXSYMLINKS) { errno = ELOOP; - return NULL; + return (NULL); } - slen = readlink(resolved_path, symlink, PATH_MAX - 1); + slen = readlink(resolved, symlink, sizeof(symlink) - 1); if (slen < 0) - return NULL; + return (NULL); symlink[slen] = '\0'; - if (symlink[0] == '/') { - /* absolute link */ - resolved_path[1] = 0; + resolved[1] = 0; resolved_len = 1; } else if (resolved_len > 1) { - char *q; - - /* trailing slash */ - resolved_path[resolved_len - 1] = '\0'; - - q = strrchr(resolved_path, '/'); + /* Strip the last path component. */ + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/'); *q = '\0'; - resolved_len = q - resolved_path; + resolved_len = q - resolved; } + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ if (p != NULL) { if (symlink[slen - 1] != '/') { - if (slen + 1 >= PATH_MAX) { + if (slen + 1 >= sizeof(symlink)) { errno = ENAMETOOLONG; - return NULL; + return (NULL); } symlink[slen] = '/'; symlink[slen + 1] = 0; } - left_len = strlcat(symlink, left, PATH_MAX); - if (left_len >= PATH_MAX) { + left_len = strlcat(symlink, left, sizeof(left)); + if (left_len >= sizeof(left)) { errno = ENAMETOOLONG; - return NULL; + return (NULL); } } - left_len = strlcpy(left, symlink, PATH_MAX); + left_len = strlcpy(left, symlink, sizeof(left)); } } - if (resolved_len > 1 && resolved_path[resolved_len - 1] == '/') - resolved_path[resolved_len - 1] = '\0'; - - return resolved_path; + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; + return (resolved); } |