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);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to