Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Oct 2009 20:58:22 +0000 (UTC)
From:      Jilles Tjoelker <jilles@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r198053 - head/lib/libc/gen
Message-ID:  <200910132058.n9DKwMoW016461@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Tue Oct 13 20:58:22 2009
New Revision: 198053
URL: http://svn.freebsd.org/changeset/base/198053

Log:
  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.

Modified:
  head/lib/libc/gen/getcwd.c

Modified: head/lib/libc/gen/getcwd.c
==============================================================================
--- head/lib/libc/gen/getcwd.c	Tue Oct 13 20:48:05 2009	(r198052)
+++ head/lib/libc/gen/getcwd.c	Tue Oct 13 20:58:22 2009	(r198053)
@@ -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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200910132058.n9DKwMoW016461>