X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/1f2f436a38f7ae2d39a943ad2898d8fed4ed2e58..a28bf75d63c6a64e4c3b417c6052e45f42c6cedd:/gen/FreeBSD/getcwd.c diff --git a/gen/FreeBSD/getcwd.c b/gen/FreeBSD/getcwd.c index d06210b..512b88c 100644 --- a/gen/FreeBSD/getcwd.c +++ b/gen/FreeBSD/getcwd.c @@ -50,12 +50,87 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.29 2007/01/09 00:27:53 imp Exp (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) -extern int __getcwd(char *, size_t); +/* + * If __getcwd() ever becomes a syscall, we can remove this workaround. + * The problem here is that fcntl() assumes a buffer of size MAXPATHLEN, + * if size is less than MAXPATHLEN, we need to use a temporary buffer + * and see if it fits. We also have to assume that open() or fcntl() + * don't fail with errno=ERANGE. + */ +static inline int +__getcwd(char *buf, size_t size) +{ + int fd, err, save; + struct stat dot, pt; + char *b; -char * -getcwd(pt, size) + if ((fd = open(".", O_RDONLY)) < 0) + return -1; + if (fstat(fd, &dot) < 0) { + save = errno; + close(fd); + errno = save; + return -1; + } + /* check that the device and inode are non-zero, otherwise punt */ + if (dot.st_dev == 0 || dot.st_ino == 0) { + close(fd); + errno = EINVAL; + return -1; + } + if (size < MAXPATHLEN) { + /* the hard case; allocate a buffer of size MAXPATHLEN to use */ + b = (char *)alloca(MAXPATHLEN); + if (b == NULL) { + close(fd); + errno = ENOMEM; /* make sure it isn't ERANGE */ + return -1; + } + } else + b = buf; + + err = fcntl(fd, F_GETPATH, b); + if (err) { + save = errno; + close(fd); + errno = save; + return err; + } + close(fd); + /* + * now double-check that the path returned by fcntl() has the same + * device and inode number as '.'. + */ + if (stat(b, &pt) < 0) + return -1; + /* + * Since dot.st_dev and dot.st_ino are non-zero, we don't need to + * separately test for pt.st_dev and pt.st_ino being non-zero, because + * they have to match + */ + if (dot.st_dev != pt.st_dev || dot.st_ino != pt.st_ino) { + errno = EINVAL; + return -1; + } + /* + * For the case where we allocated a buffer, check that it can fit + * in the real buffer, and copy it over. + */ + if (size < MAXPATHLEN) { + if (strlen(b) >= size) { + errno = ERANGE; + return -1; + } + strcpy(buf, b); + } + return 0; +} + +__private_extern__ char * +__private_getcwd(pt, size, usegetpath) char *pt; size_t size; + int usegetpath; { struct dirent *dp; DIR *dir = NULL; @@ -87,33 +162,27 @@ getcwd(pt, size) } ept = pt + size; } else { - if ((pt = malloc(ptsize = PATH_MAX)) == NULL) + if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL) return (NULL); ept = pt + ptsize; } - if (__getcwd(pt, ept - pt) == 0) { - if (*pt != '/') { - bpt = pt; - ept = pt + strlen(pt) - 1; - while (bpt < ept) { - c = *bpt; - *bpt++ = *ept; - *ept-- = c; - } - } - return (pt); + if (usegetpath) { + if (__getcwd(pt, ept - pt) == 0) { + return (pt); + } else if (errno == ERANGE) /* failed because buffer too small */ + return NULL; } bpt = ept - 1; *bpt = '\0'; /* - * Allocate 1024 bytes for the string of "../"'s. + * Allocate MAXPATHLEN bytes for the string of "../"'s. * Should always be enough. If it's not, allocate * as necessary. Special case the first stat, it's ".", not "..". */ - if ((up = malloc(upsize = 1024)) == NULL) + if ((up = malloc(upsize = MAXPATHLEN)) == NULL) goto err; - eup = up + upsize; + eup = up + MAXPATHLEN; bup = up; up[0] = '.'; up[1] = '\0'; @@ -255,3 +324,11 @@ err: errno = save_errno; return (NULL); } + +char * +getcwd(pt, size) + char *pt; + size_t size; +{ + return __private_getcwd(pt, size, 1); +}