| version 1.35, 2005/01/23 01:00:51 |
version 1.36, 2005/01/30 22:37:32 |
| Line 46 __RCSID("$NetBSD$"); |
|
| Line 46 __RCSID("$NetBSD$"); |
|
| #include <sys/stat.h> |
#include <sys/stat.h> |
| |
|
| #include <assert.h> |
#include <assert.h> |
| #include <dirent.h> |
|
| #include <errno.h> |
#include <errno.h> |
| #include <fcntl.h> |
|
| #include <stdio.h> |
|
| #include <stdlib.h> |
#include <stdlib.h> |
| #include <string.h> |
#include <string.h> |
| #include <unistd.h> |
#include <unistd.h> |
| Line 62 __weak_alias(realpath,_realpath) |
|
| Line 59 __weak_alias(realpath,_realpath) |
|
| #endif |
#endif |
| |
|
| /* |
/* |
| * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); |
* char *realpath(const char *path, char resolved[MAXPATHLEN]); |
| * |
* |
| * Find the real name of path, by removing all ".", ".." and symlink |
* Find the real name of path, by removing all ".", ".." and symlink |
| * components. Returns (resolved) on success, or (NULL) on failure, |
* components. Returns (resolved) on success, or (NULL) on failure, |
| * in which case the path which caused trouble is left in (resolved). |
* in which case the path which caused trouble is left in (resolved). |
| */ |
*/ |
| char * |
char * |
| realpath(path, resolved) |
realpath(const char *path, char *resolved) |
| const char *path; |
|
| char *resolved; |
|
| { |
{ |
| struct stat sb; |
struct stat sb; |
| int fd, n, rootd, serrno, nlnk = 0; |
int idx = 0, n, nlnk = 0, serrno = errno; |
| char *p, *q, wbuf[MAXPATHLEN]; |
const char *q; |
| |
char *p, wbuf[2][MAXPATHLEN]; |
| |
size_t len; |
| |
|
| _DIAGASSERT(path != NULL); |
_DIAGASSERT(path != NULL); |
| _DIAGASSERT(resolved != NULL); |
_DIAGASSERT(resolved != NULL); |
| |
|
| /* Save the starting point. */ |
/* |
| if ((fd = open(".", O_RDONLY)) < 0) { |
* Build real path one by one with paying an attention to ., |
| (void)strlcpy(resolved, ".", MAXPATHLEN); |
* .. and symbolic link. |
| return (NULL); |
*/ |
| } |
|
| |
|
| /* |
/* |
| * Find the dirname and basename from the path to be resolved. |
* `p' is where we'll put a new component with prepending |
| * Change directory to the dirname component. |
* a delimiter. |
| * 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. |
|
| */ |
*/ |
| if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) { |
p = resolved; |
| errno = ENAMETOOLONG; |
|
| goto err1; |
if (*path == 0) { |
| |
*p = 0; |
| |
errno = ENOENT; |
| |
return (NULL); |
| } |
} |
| |
|
| |
/* If relative path, start from current working directory. */ |
| |
if (*path != '/') { |
| |
if (getcwd(resolved, MAXPATHLEN) == NULL) { |
| |
p[0] = '.'; |
| |
p[1] = 0; |
| |
return (NULL); |
| |
} |
| |
len = strlen(resolved); |
| |
if (len > 1) |
| |
p += len; |
| |
} |
| |
|
| loop: |
loop: |
| q = strrchr(resolved, '/'); |
/* Skip any slash. */ |
| if (q != NULL) { |
while (*path == '/') |
| p = q + 1; |
path++; |
| if (q == resolved) |
|
| q = "/"; |
if (*path == 0) { |
| else { |
if (p == resolved) |
| do { |
*p++ = '/'; |
| --q; |
*p = 0; |
| } while (q > resolved && *q == '/'); |
return (resolved); |
| q[1] = '\0'; |
} |
| q = resolved; |
|
| } |
/* Find the end of this component. */ |
| if (chdir(q) < 0) |
q = path; |
| goto err1; |
do |
| } else |
q++; |
| p = resolved; |
while (*q != '/' && *q != 0); |
| |
|
| /* Deal with the last component. */ |
/* Test . or .. */ |
| if (lstat(p, &sb) == 0) { |
if (path[0] == '.') { |
| if (S_ISLNK(sb.st_mode)) { |
if (q - path == 1) { |
| if (nlnk++ >= MAXSYMLINKS) { |
path = q; |
| errno = ELOOP; |
|
| goto err1; |
|
| } |
|
| n = readlink(p, resolved, MAXPATHLEN-1); |
|
| if (n < 0) |
|
| goto err1; |
|
| resolved[n] = '\0'; |
|
| goto loop; |
goto loop; |
| } |
} |
| if (S_ISDIR(sb.st_mode)) { |
if (path[1] == '.' && q - path == 2) { |
| if (chdir(p) < 0) |
/* Trim the last component. */ |
| goto err1; |
if (p != resolved) |
| p = ""; |
while (*--p != '/') |
| |
; |
| |
path = q; |
| |
goto loop; |
| } |
} |
| } |
} |
| |
|
| /* |
/* Append this component. */ |
| * Save the last component name and get the full pathname of |
if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) { |
| * the current directory. |
|
| */ |
|
| if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) { |
|
| errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
| goto err1; |
if (p == resolved) |
| |
*p++ = '/'; |
| |
*p = 0; |
| |
return (NULL); |
| } |
} |
| |
p[0] = '/'; |
| |
memcpy(&p[1], path, |
| |
/* LINTED We know q > path. */ |
| |
q - path); |
| |
p[1 + q - path] = 0; |
| |
|
| /* |
/* |
| * Call the inernal internal version of getcwd which |
* If this component is a symlink, toss it and prepend link |
| * does a physical search rather than using the $PWD short-cut |
* target to unresolved path. |
| */ |
*/ |
| if (getcwd(resolved, MAXPATHLEN) == 0) |
if (lstat(resolved, &sb) == -1) { |
| goto err1; |
/* Allow nonexistent component if this is the last one. */ |
| |
if (*q == 0 && errno == ENOENT) { |
| /* |
errno = serrno; |
| * Join the two strings together, ensuring that the right thing |
return (resolved); |
| * 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 ? 0 : 1) + 1 > |
|
| MAXPATHLEN) { |
|
| errno = ENAMETOOLONG; |
|
| goto err1; |
|
| } |
|
| if (rootd == 0) |
|
| if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) { |
|
| errno = ENAMETOOLONG; |
|
| goto err1; |
|
| } |
|
| if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) { |
|
| errno = ENAMETOOLONG; |
|
| goto err1; |
|
| } |
} |
| |
return (NULL); |
| } |
} |
| |
if (S_ISLNK(sb.st_mode)) { |
| |
if (nlnk++ >= MAXSYMLINKS) { |
| |
errno = ELOOP; |
| |
return (NULL); |
| |
} |
| |
n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); |
| |
if (n < 0) |
| |
return (NULL); |
| |
if (n == 0) { |
| |
errno = ENOENT; |
| |
return (NULL); |
| |
} |
| |
|
| /* Go back to where we came from. */ |
/* Append unresolved path to link target and switch to it. */ |
| if (fchdir(fd) < 0) { |
if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { |
| serrno = errno; |
errno = ENAMETOOLONG; |
| goto err2; |
return (NULL); |
| |
} |
| |
memcpy(&wbuf[idx][n], q, len + 1); |
| |
path = wbuf[idx]; |
| |
idx ^= 1; |
| |
|
| |
/* If absolute symlink, start from root. */ |
| |
if (*path == '/') |
| |
p = resolved; |
| |
goto loop; |
| } |
} |
| |
|
| /* It's okay if the close fails, what's an fd more or less? */ |
/* Advance both resolved and unresolved path. */ |
| (void)close(fd); |
p += 1 + q - path; |
| return (resolved); |
path = q; |
| |
goto loop; |
| err1: serrno = errno; |
|
| (void)fchdir(fd); |
|
| err2: (void)close(fd); |
|
| errno = serrno; |
|
| return (NULL); |
|
| } |
} |
| |
|
| char * |
char * |