summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc')
-rw-r--r--usr/src/lib/libc/port/gen/getcwd.c50
1 files changed, 48 insertions, 2 deletions
diff --git a/usr/src/lib/libc/port/gen/getcwd.c b/usr/src/lib/libc/port/gen/getcwd.c
index d832d798d7..62f12e027e 100644
--- a/usr/src/lib/libc/port/gen/getcwd.c
+++ b/usr/src/lib/libc/port/gen/getcwd.c
@@ -22,10 +22,9 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* getcwd() returns the pathname of the current working directory.
* On error, a NULL pointer is returned and errno is set.
@@ -34,16 +33,63 @@
#pragma weak _getcwd = getcwd
#include "lint.h"
+#include <sys/param.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>
+#include <limits.h>
#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
char *
getcwd(char *pathname, size_t size)
{
int alloc = 0;
+ if (size == 0 && pathname == NULL) {
+ /*
+ * If no size was provided, start with a buffer that should
+ * accommodate any normal path and, if it is not big enough,
+ * keep doubling it to try and make enough space.
+ *
+ * Any non-global zone path is longer when observed from the
+ * global zone, and some filesystems, including ZFS, support
+ * paths much longer than MAXPATHLEN/_PC_PATH_MAX.
+ *
+ * To protect against unbounded memory usage, cap to 128KiB.
+ * This is an arbitrary limit which is far bigger than the
+ * length of any practical path on the system.
+ */
+ if ((size = pathconf(".", _PC_PATH_MAX)) == -1)
+ size = MAXPATHLEN;
+
+ while (size <= 0x20000) {
+ if ((pathname = reallocf(pathname, size)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if (syscall(SYS_getcwd, pathname, size) == 0) {
+ char *ret;
+
+ /*
+ * Shrink the buffer to the length actually
+ * required to hold the final path.
+ */
+ ret = realloc(pathname, strlen(pathname) + 1);
+ if (ret == NULL)
+ return (pathname);
+
+ return (ret);
+ }
+ if (errno != ERANGE)
+ break;
+ size <<= 1;
+ }
+ free(pathname);
+ return (NULL);
+ }
+
if (size == 0) {
errno = EINVAL;
return (NULL);