summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2009-10-13 20:58:22 +0000
committerjilles <jilles@FreeBSD.org>2009-10-13 20:58:22 +0000
commit4fdff21cbb6547c80b57bdc57b4340925039ca59 (patch)
treed88be392af0fa3b5460b18eab99247fed63bc93b /lib
parent58b36bef21f0fab5576a80af9fcb75c0a8c4928e (diff)
downloadFreeBSD-src-4fdff21cbb6547c80b57bdc57b4340925039ca59.zip
FreeBSD-src-4fdff21cbb6547c80b57bdc57b4340925039ca59.tar.gz
Make getcwd(3) faster, simpler and more compliant using *at syscalls.
It is no longer necessary to construct long paths consisting of repeated "../" which may be slow to process and may exceed PATH_MAX.
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/gen/getcwd.c63
1 files changed, 18 insertions, 45 deletions
diff --git a/lib/libc/gen/getcwd.c b/lib/libc/gen/getcwd.c
index 0cd3208..c886dde 100644
--- a/lib/libc/gen/getcwd.c
+++ b/lib/libc/gen/getcwd.c
@@ -62,13 +62,14 @@ getcwd(pt, size)
dev_t dev;
ino_t ino;
int first;
- char *bpt, *bup;
+ char *bpt;
struct stat s;
dev_t root_dev;
ino_t root_ino;
- size_t ptsize, upsize;
+ size_t ptsize;
int save_errno;
- char *ept, *eup, *up, c;
+ char *ept, c;
+ int fd;
/*
* If no buffer specified by the user, allocate one as necessary.
@@ -106,18 +107,6 @@ getcwd(pt, size)
bpt = ept - 1;
*bpt = '\0';
- /*
- * Allocate 1024 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)
- goto err;
- eup = up + upsize;
- bup = up;
- up[0] = '.';
- up[1] = '\0';
-
/* Save root values, so know when to stop. */
if (stat("/", &s))
goto err;
@@ -128,7 +117,7 @@ getcwd(pt, size)
for (first = 1;; first = 0) {
/* Stat the current level. */
- if (lstat(up, &s))
+ if (dir != NULL ? _fstat(dirfd(dir), &s) : lstat(".", &s))
goto err;
/* Save current node values. */
@@ -144,32 +133,22 @@ getcwd(pt, size)
* been that way and stuff would probably break.
*/
bcopy(bpt, pt, ept - bpt);
- free(up);
+ if (dir)
+ (void) closedir(dir);
return (pt);
}
- /*
- * Build pointer to the parent directory, allocating memory
- * as necessary. Max length is 3 for "../", the largest
- * possible component name, plus a trailing NUL.
- */
- while (bup + 3 + MAXNAMLEN + 1 >= eup) {
- if ((up = reallocf(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))
+ fd = _openat(dir != NULL ? dirfd(dir) : AT_FDCWD,
+ "..", O_RDONLY);
+ if (fd == -1)
goto err;
-
- /* Add trailing slash for next directory. */
- *bup++ = '/';
- *bup = '\0';
+ if (dir)
+ (void) closedir(dir);
+ if (!(dir = fdopendir(fd)) || _fstat(dirfd(dir), &s)) {
+ _close(fd);
+ goto err;
+ }
/*
* If it's a mount point, have to stat each element because
@@ -190,10 +169,10 @@ getcwd(pt, size)
goto notfound;
if (ISDOT(dp))
continue;
- bcopy(dp->d_name, bup, dp->d_namlen + 1);
/* Save the first error for later. */
- if (lstat(up, &s)) {
+ if (fstatat(dirfd(dir), dp->d_name, &s,
+ AT_SYMLINK_NOFOLLOW)) {
if (!save_errno)
save_errno = errno;
errno = 0;
@@ -227,11 +206,6 @@ getcwd(pt, size)
*--bpt = '/';
bpt -= dp->d_namlen;
bcopy(dp->d_name, bpt, dp->d_namlen);
- (void) closedir(dir);
- dir = NULL;
-
- /* Truncate any file name. */
- *bup = '\0';
}
notfound:
@@ -250,7 +224,6 @@ err:
free(pt);
if (dir)
(void) closedir(dir);
- free(up);
errno = save_errno;
return (NULL);
OpenPOWER on IntegriCloud