]> git.saurik.com Git - apple/libc.git/blobdiff - gen/FreeBSD/getcwd.c
Libc-1353.100.2.tar.gz
[apple/libc.git] / gen / FreeBSD / getcwd.c
index e28044e2ae8c607d4da8eea1942e95a3cab9b5a1..4a1989026b67df4e10f5b768c946d1646b5612ee 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    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
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstrict-prototypes"
+
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)getcwd.c   8.5 (Berkeley) 2/7/95";
 #endif /* LIBC_SCCS and not lint */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.24 2003/01/10 02:58:25 tjr Exp $");
+__FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.29 2007/01/09 00:27:53 imp Exp $");
 
 #include "namespace.h"
 #include <sys/param.h>
@@ -54,12 +53,87 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.24 2003/01/10 02:58:25 tjr 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;
@@ -72,7 +146,7 @@ getcwd(pt, size)
        ino_t root_ino;
        size_t ptsize, upsize;
        int save_errno;
-       char *ept, *eup, *up, c;
+       char *ept, *eup, *up;
 
        /*
         * If no buffer specified by the user, allocate one as necessary.
@@ -91,33 +165,25 @@ getcwd(pt, size)
                }
                ept = pt + size;
        } else {
-               if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
+               if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL)
                        return (NULL);
                ept = pt + ptsize;
        }
-#if    !defined(__NETBSD_SYSCALLS)
-       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;
        }
-#endif
        bpt = ept - 1;
        *bpt = '\0';
 
        /*
-        * Allocate bytes (1024 - malloc space) for the string of "../"'s.
-        * Should always be enough (it's 340 levels).  If it's not, allocate
+        * 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 - 4)) == NULL)
+       if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
                goto err;
        eup = up + MAXPATHLEN;
        bup = up;
@@ -159,7 +225,7 @@ getcwd(pt, size)
                 * as necessary.  Max length is 3 for "../", the largest
                 * possible component name, plus a trailing NUL.
                 */
-               if (bup + 3  + MAXNAMLEN + 1 >= eup) {
+               while (bup + 3  + MAXNAMLEN + 1 >= eup) {
                        if ((up = reallocf(up, upsize *= 2)) == NULL)
                                goto err;
                        bup = up;
@@ -213,7 +279,7 @@ getcwd(pt, size)
                 * Check for length of the current name, preceding slash,
                 * leading slash.
                 */
-               if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
+               while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
                        size_t len, off;
 
                        if (!ptsize) {
@@ -261,3 +327,12 @@ err:
        errno = save_errno;
        return (NULL);
 }
+
+char *
+getcwd(pt, size)
+       char *pt;
+       size_t size;
+{
+       return __private_getcwd(pt, size, 1);
+}
+#pragma clang diagnostic pop