diff options
Diffstat (limited to 'usr/src/lib/libc')
-rw-r--r-- | usr/src/lib/libc/port/gen/getcwd.c | 50 |
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); |