---- getcwd.c.orig Thu Jan 9 18:58:25 2003
-+++ getcwd.c Sat May 3 14:04:22 2003
-@@ -54,8 +54,6 @@
+--- getcwd.c.bsdnew 2009-11-08 15:25:00.000000000 -0800
++++ getcwd.c 2009-11-08 15:30:17.000000000 -0800
+@@ -50,12 +50,87 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/get
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
-extern int __getcwd(char *, size_t);
--
- char *
- getcwd(pt, size)
++/*
++ * 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;
++
++ 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;
+
+-char *
+-getcwd(pt, size)
++ 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;
-@@ -95,20 +93,6 @@
+ 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 !defined(__NETBSD_SYSCALLS)
- if (__getcwd(pt, ept - pt) == 0) {
- if (*pt != '/') {
- bpt = pt;
- }
- }
- return (pt);
-- }
--#endif
++ 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);
++}