version 1.24, 2000/01/22 22:19:10 |
version 1.42, 2005/12/31 12:32:30 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
* 2. Redistributions in binary form must reproduce the above copyright |
* notice, this list of conditions and the following disclaimer in the |
* notice, this list of conditions and the following disclaimer in the |
* documentation and/or other materials provided with the distribution. |
* documentation and/or other materials provided with the distribution. |
* 3. All advertising materials mentioning features or use of this software |
* 3. Neither the name of the University nor the names of its contributors |
* 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 |
* may be used to endorse or promote products derived from this software |
* without specific prior written permission. |
* without specific prior written permission. |
* |
* |
Line 50 __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 65 __weak_alias(getcwd,_getcwd) |
|
Line 58 __weak_alias(getcwd,_getcwd) |
|
__weak_alias(realpath,_realpath) |
__weak_alias(realpath,_realpath) |
#endif |
#endif |
|
|
#define ISDOT(dp) \ |
|
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ |
|
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
|
|
|
|
|
#if defined(__SVR4) || defined(__svr4__) |
|
#define d_fileno d_ino |
|
#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; |
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)strcpy(resolved, "."); |
* .. 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. |
|
*/ |
*/ |
(void)strncpy(resolved, path, MAXPATHLEN - 1); |
p = resolved; |
resolved[MAXPATHLEN - 1] = '\0'; |
|
loop: |
if (*path == 0) { |
q = strrchr(resolved, '/'); |
*p = 0; |
if (q != NULL) { |
errno = ENOENT; |
p = q + 1; |
return (NULL); |
if (q == resolved) |
} |
q = "/"; |
|
else { |
/* If relative path, start from current working directory. */ |
do { |
if (*path != '/') { |
--q; |
if (getcwd(resolved, MAXPATHLEN) == NULL) { |
} while (q > resolved && *q == '/'); |
p[0] = '.'; |
q[1] = '\0'; |
p[1] = 0; |
q = resolved; |
return (NULL); |
} |
} |
if (chdir(q) < 0) |
len = strlen(resolved); |
goto err1; |
if (len > 1) |
} else |
p += len; |
p = resolved; |
} |
|
|
/* Deal with the last component. */ |
loop: |
if (lstat(p, &sb) == 0) { |
/* Skip any slash. */ |
if (S_ISLNK(sb.st_mode)) { |
while (*path == '/') |
if (nlnk++ >= MAXSYMLINKS) { |
path++; |
errno = ELOOP; |
|
goto err1; |
if (*path == 0) { |
} |
if (p == resolved) |
n = readlink(p, resolved, MAXPATHLEN); |
*p++ = '/'; |
if (n < 0) |
*p = 0; |
goto err1; |
return (resolved); |
resolved[n] = '\0'; |
} |
|
|
|
/* 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; |
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. |
errno = ENAMETOOLONG; |
*/ |
if (p == resolved) |
(void)strncpy(wbuf, p, (sizeof(wbuf) - 1)); |
*p++ = '/'; |
|
*p = 0; |
/* |
return (NULL); |
* Call the inernal internal version of getcwd which |
} |
* does a physical search rather than using the $PWD short-cut |
p[0] = '/'; |
*/ |
memcpy(&p[1], path, |
if (getcwd(resolved, MAXPATHLEN) == 0) |
/* LINTED We know q > path. */ |
goto err1; |
q - path); |
|
p[1 + q - path] = 0; |
|
|
/* |
/* |
* Join the two strings together, ensuring that the right thing |
* If this component is a symlink, toss it and prepend link |
* happens if the last component is empty, or the dirname is root. |
* target to unresolved path. |
*/ |
*/ |
if (resolved[0] == '/' && resolved[1] == '\0') |
if (lstat(resolved, &sb) == -1) { |
rootd = 1; |
return (NULL); |
else |
} |
rootd = 0; |
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); |
|
} |
|
|
if (*wbuf) { |
/* Append unresolved path to link target and switch to it. */ |
if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { |
if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
goto err1; |
return (NULL); |
} |
} |
if (rootd == 0) |
memcpy(&wbuf[idx][n], q, len + 1); |
(void)strcat(resolved, "/"); /* XXX: strcat is safe */ |
path = wbuf[idx]; |
(void)strcat(resolved, wbuf); /* XXX: strcat is safe */ |
idx ^= 1; |
|
|
|
/* If absolute symlink, start from root. */ |
|
if (*path == '/') |
|
p = resolved; |
|
goto loop; |
} |
} |
|
if (*q == '/' && !S_ISDIR(sb.st_mode)) { |
/* Go back to where we came from. */ |
errno = ENOTDIR; |
if (fchdir(fd) < 0) { |
return (NULL); |
serrno = errno; |
|
goto err2; |
|
} |
} |
|
|
/* 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); |
|
} |
} |
|
|
#ifdef OLD_GETCWD |
|
|
|
char * |
char * |
getcwd(pt, size) |
getcwd(char *pt, size_t size) |
char *pt; |
|
size_t size; |
|
{ |
{ |
struct dirent *dp; |
char *npt; |
DIR *dir; |
|
dev_t dev; |
|
ino_t ino; |
|
int first; |
|
char *bpt, *bup; |
|
struct stat s; |
|
dev_t root_dev; |
|
ino_t root_ino; |
|
size_t ptsize, upsize; |
|
int save_errno; |
|
char *ept, *eup, *up; |
|
size_t dlen; |
|
|
|
/* |
/* |
* If no buffer specified by the user, allocate one as necessary. |
* If a buffer is specified, the size has to be non-zero. |
* If a buffer is specified, the size has to be non-zero. The path |
|
* is built from the end of the buffer backwards. |
|
*/ |
*/ |
if (pt) { |
if (pt != NULL) { |
ptsize = 0; |
if (size == 0) { |
if (!size) { |
/* __getcwd(pt, 0) results ERANGE. */ |
errno = EINVAL; |
errno = EINVAL; |
return (NULL); |
return (NULL); |
} |
} |
ept = pt + size; |
if (__getcwd(pt, size) >= 0) |
} else { |
return (pt); |
if ((pt = malloc(ptsize = 1024 - 4)) == NULL) |
return (NULL); |
return (NULL); |
|
ept = pt + ptsize; |
|
} |
} |
bpt = ept - 1; |
|
*bpt = '\0'; |
|
|
|
/* |
/* |
* Allocate bytes (1024 - malloc space) for the string of "../"'s. |
* If no buffer specified by the user, allocate one as necessary. |
* Should always be enough (it's 340 levels). If it's not, allocate |
|
* as necessary. Special case the first stat, it's ".", not "..". |
|
*/ |
*/ |
if ((up = malloc(upsize = 1024 - 4)) == NULL) |
size = 1024 >> 1; |
goto err; |
do { |
eup = up + MAXPATHLEN; |
if ((npt = realloc(pt, size <<= 1)) == NULL) |
bup = up; |
break; |
up[0] = '.'; |
pt = npt; |
up[1] = '\0'; |
if (__getcwd(pt, size) >= 0) |
|
|
/* Save root values, so know when to stop. */ |
|
if (stat("/", &s)) |
|
goto err; |
|
root_dev = s.st_dev; |
|
root_ino = s.st_ino; |
|
|
|
errno = 0; /* XXX readdir has no error return. */ |
|
|
|
for (first = 1;; first = 0) { |
|
/* Stat the current level. */ |
|
if (lstat(up, &s)) |
|
goto err; |
|
|
|
/* Save current node values. */ |
|
ino = s.st_ino; |
|
dev = s.st_dev; |
|
|
|
/* Check for reaching root. */ |
|
if (root_dev == dev && root_ino == ino) { |
|
*--bpt = '/'; |
|
/* |
|
* It's unclear that it's a requirement to copy the |
|
* path to the beginning of the buffer, but it's always |
|
* been that way and stuff would probably break. |
|
*/ |
|
memmove(pt, bpt, (size_t)(ept - bpt)); |
|
free(up); |
|
return (pt); |
return (pt); |
} |
} while (size <= MAXPATHLEN * 4 && errno == ERANGE); |
|
|
/* |
|
* Build pointer to the parent directory, allocating memory |
|
* as necessary. Max length is 3 for "../", the largest |
|
* possible component name, plus a trailing NULL. |
|
*/ |
|
if (bup + 3 + MAXNAMLEN + 1 >= eup) { |
|
if ((up = realloc(up, upsize *= 2)) == NULL) |
|
goto err; |
|
bup = up; |
|
eup = up + upsize; |
|
} |
|
*bup++ = '.'; |
|
*bup++ = '.'; |
|
*bup = '\0'; |
|
|
|
/* Open and stat parent directory. */ |
|
if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) |
|
goto err; |
|
|
|
/* Add trailing slash for next directory. */ |
|
*bup++ = '/'; |
|
|
|
/* |
|
* If it's a mount point, have to stat each element because |
|
* the inode number in the directory is for the entry in the |
|
* parent directory, not the inode number of the mounted file. |
|
*/ |
|
save_errno = 0; |
|
if (s.st_dev == dev) { |
|
for (;;) { |
|
if (!(dp = readdir(dir))) |
|
goto notfound; |
|
if (dp->d_fileno == ino) { |
|
#if defined(__SVR4) || defined(__svr4__) || defined(__linux__) |
|
dlen = strlen(dp->d_name); |
|
#else |
|
dlen = dp->d_namlen; |
|
#endif |
|
break; |
|
} |
|
} |
|
} else |
|
for (;;) { |
|
if (!(dp = readdir(dir))) |
|
goto notfound; |
|
if (ISDOT(dp)) |
|
continue; |
|
#if defined(__SVR4) || defined(__svr4__) || defined(__linux__) |
|
dlen = strlen(dp->d_name); |
|
#else |
|
dlen = dp->d_namlen; |
|
#endif |
|
memmove(bup, dp->d_name, dlen + 1); |
|
|
|
/* Save the first error for later. */ |
free(pt); |
if (lstat(up, &s)) { |
|
if (!save_errno) |
|
save_errno = errno; |
|
errno = 0; |
|
continue; |
|
} |
|
if (s.st_dev == dev && s.st_ino == ino) |
|
break; |
|
} |
|
|
|
/* |
|
* Check for length of the current name, preceding slash, |
|
* leading slash. |
|
*/ |
|
if (bpt - pt <= dlen + (first ? 1 : 2)) { |
|
size_t len, off; |
|
|
|
if (!ptsize) { |
|
errno = ERANGE; |
|
goto err; |
|
} |
|
off = bpt - pt; |
|
len = ept - bpt; |
|
if ((pt = realloc(pt, ptsize *= 2)) == NULL) |
|
goto err; |
|
bpt = pt + off; |
|
ept = pt + ptsize; |
|
memmove(ept - len, bpt, len); |
|
bpt = ept - len; |
|
} |
|
if (!first) |
|
*--bpt = '/'; |
|
bpt -= dlen; |
|
memmove(bpt, dp->d_name, dlen); |
|
(void)closedir(dir); |
|
|
|
/* Truncate any file name. */ |
|
*bup = '\0'; |
|
} |
|
|
|
notfound: |
|
/* |
|
* If readdir set errno, use it, not any saved error; otherwise, |
|
* didn't find the current directory in its parent directory, set |
|
* errno to ENOENT. |
|
*/ |
|
if (!errno) |
|
errno = save_errno ? save_errno : ENOENT; |
|
/* FALLTHROUGH */ |
|
err: |
|
if (ptsize) |
|
free(pt); |
|
free(up); |
|
return (NULL); |
return (NULL); |
} |
} |
|
|
#else /* New getcwd */ |
|
|
|
char * |
|
getcwd(pt, size) |
|
char *pt; |
|
size_t size; |
|
{ |
|
size_t ptsize, bufsize; |
|
int len; |
|
|
|
/* |
|
* If no buffer specified by the user, allocate one as necessary. |
|
* If a buffer is specified, the size has to be non-zero. The path |
|
* is built from the end of the buffer backwards. |
|
*/ |
|
if (pt) { |
|
ptsize = 0; |
|
if (!size) { |
|
errno = EINVAL; |
|
return (NULL); |
|
} |
|
bufsize = size; |
|
} else { |
|
if ((pt = malloc(ptsize = 1024 - 4)) == NULL) |
|
return (NULL); |
|
bufsize = ptsize; |
|
} |
|
for (;;) { |
|
len = __getcwd(pt, bufsize); |
|
if ((len < 0) && (size == 0) && (errno == ERANGE)) { |
|
if (ptsize > (MAXPATHLEN*4)) |
|
return NULL; |
|
if ((pt = realloc(pt, ptsize *= 2)) == NULL) |
|
return NULL; |
|
bufsize = ptsize; |
|
continue; |
|
} |
|
break; |
|
} |
|
if (len < 0) |
|
return NULL; |
|
else |
|
return pt; |
|
} |
|
|
|
#endif |
|