diff options
Diffstat (limited to 'libc/sysdeps/linux/common/getcwd.c')
-rw-r--r-- | libc/sysdeps/linux/common/getcwd.c | 129 |
1 files changed, 63 insertions, 66 deletions
diff --git a/libc/sysdeps/linux/common/getcwd.c b/libc/sysdeps/linux/common/getcwd.c index d1740fa31..daf056e98 100644 --- a/libc/sysdeps/linux/common/getcwd.c +++ b/libc/sysdeps/linux/common/getcwd.c @@ -9,45 +9,10 @@ #ifdef __NR_getcwd -#ifndef INLINE_SYSCALL -#define INLINE_SYSCALL(name, nr, args...) __syscall_getcwd (args) #define __NR___syscall_getcwd __NR_getcwd -static inline _syscall2(int, __syscall_getcwd, char *, buf, unsigned long, size); -#endif - -char *getcwd(char *buf, int size) -{ - int olderrno, ret; - char *allocbuf; +static inline +_syscall2(int, __syscall_getcwd, char *, buf, unsigned long, size); - if (size == 0) { - if (buf != NULL) { - __set_errno(EINVAL); - return NULL; - } - size = PATH_MAX; - } - if (size < 3) { - __set_errno(ERANGE); - return NULL; - } - allocbuf=NULL; - olderrno = errno; - if (buf == NULL) { - buf = allocbuf = malloc (size); - if (buf == NULL) - return NULL; - } - ret = INLINE_SYSCALL(getcwd, 2, buf, size); - if (ret < 0) { - if (allocbuf) { - free(allocbuf); - } - return NULL; - } - __set_errno(olderrno); - return buf; -} #else /* If the syscall is not present, we have to walk up the @@ -74,8 +39,9 @@ static char *search_dir(dev_t this_dev, ino_t this_ino, char *path_buf, int path int slow_search = (sizeof(ino_t) != sizeof(d->d_ino)); #endif - if (stat(path_buf, &st) < 0) - return 0; + if (stat(path_buf, &st) < 0) { + goto oops; + } #ifdef FAST_DIR_SEARCH_POSSIBLE if (this_dev != st.st_dev) slow_search = 1; @@ -85,8 +51,7 @@ static char *search_dir(dev_t this_dev, ino_t this_ino, char *path_buf, int path ptr = path_buf + slen - 1; if (*ptr != '/') { if (slen + 2 > path_size) { - __set_errno(ERANGE); - return 0; + goto oops; } strcpy(++ptr, "/"); slen++; @@ -94,16 +59,16 @@ static char *search_dir(dev_t this_dev, ino_t this_ino, char *path_buf, int path slen++; dp = opendir(path_buf); - if (dp == 0) - return 0; + if (dp == 0) { + goto oops; + } while ((d = readdir(dp)) != 0) { #ifdef FAST_DIR_SEARCH_POSSIBLE if (slow_search || this_ino == d->d_ino) { #endif if (slen + strlen(d->d_name) > path_size) { - __set_errno(ERANGE); - return 0; + goto oops; } strcpy(ptr + 1, d->d_name); if (stat(path_buf, &st) < 0) @@ -118,7 +83,10 @@ static char *search_dir(dev_t this_dev, ino_t this_ino, char *path_buf, int path } closedir(dp); - __set_errno(ENOENT); + return 0; + +oops: + __set_errno(ERANGE); return 0; } @@ -129,54 +97,83 @@ static char *recurser(char *path_buf, int path_size, dev_t root_dev, ino_t root_ dev_t this_dev; ino_t this_ino; - if (stat(path_buf, &st) < 0) - return 0; + if (stat(path_buf, &st) < 0) { + if (errno != EFAULT) + goto oops; + return 0; + } this_dev = st.st_dev; this_ino = st.st_ino; if (this_dev == root_dev && this_ino == root_ino) { + if (path_size < 2) { + goto oops; + } strcpy(path_buf, "/"); return path_buf; } if (strlen(path_buf) + 4 > path_size) { - __set_errno(ERANGE); - return 0; + goto oops; } strcat(path_buf, "/.."); if (recurser(path_buf, path_size, root_dev, root_ino) == 0) return 0; return search_dir(this_dev, this_ino, path_buf, path_size); +oops: + __set_errno(ERANGE); + return 0; +} + +static inline +int __syscall_getcwd(char * buf, unsigned long size) +{ + int len; + char *cwd; + struct stat st; + int olderrno; + + olderrno = errno; + len = -1; + cwd = recurser(buf, size, st.st_dev, st.st_ino); + if (cwd) { + len = strlen(buf); + __set_errno(olderrno); + } + return len; } +#endif char *getcwd(char *buf, int size) { - struct stat st; + int ret; + char *path; + size_t alloc_size = size; if (size == 0) { if (buf != NULL) { __set_errno(EINVAL); return NULL; } - size = PATH_MAX; - } - if (size < 3) { - __set_errno(ERANGE); - return NULL; + alloc_size = PATH_MAX; } - + path=buf; if (buf == NULL) { - buf = malloc (size); - if (buf == NULL) + path = malloc(alloc_size); + if (path == NULL) return NULL; } - - strcpy(buf, "."); - if (stat("/", &st) < 0) { - return NULL; + ret = __syscall_getcwd(path, alloc_size); + if (ret >= 0) + { + if (buf == NULL && size == 0) + buf = realloc(path, ret); + if (buf == NULL) + buf = path; + return buf; } - - return recurser(buf, size, st.st_dev, st.st_ino); + if (buf == NULL) + free (path); + return NULL; } -#endif |