diff options
Diffstat (limited to 'REORG.TODO/sysdeps/posix/getcwd.c')
-rw-r--r-- | REORG.TODO/sysdeps/posix/getcwd.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/posix/getcwd.c b/REORG.TODO/sysdeps/posix/getcwd.c new file mode 100644 index 0000000000..eb1706aa5c --- /dev/null +++ b/REORG.TODO/sysdeps/posix/getcwd.c @@ -0,0 +1,535 @@ +/* Copyright (C) 1991-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Wants: + AC_STDC_HEADERS + AC_DIR_HEADER + AC_UNISTD_H + AC_MEMORY_H + AC_CONST + AC_ALLOCA + */ + +/* AIX requires this to be the first thing in the file. */ +#if defined _AIX && !defined __GNUC__ + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef STDC_HEADERS +# include <stddef.h> +#endif + +#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS +extern int errno; +#endif +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +#ifndef NULL +# define NULL 0 +#endif + +#if defined USGr3 && !defined DIRENT +# define DIRENT +#endif /* USGr3 */ +#if defined Xenix && !defined SYSNDIR +# define SYSNDIR +#endif /* Xenix */ + +#if defined POSIX || defined DIRENT || defined __GNU_LIBRARY__ +# include <dirent.h> +# ifndef __GNU_LIBRARY__ +# define D_NAMLEN(d) strlen((d)->d_name) +# else +# define HAVE_D_NAMLEN +# define D_NAMLEN(d) ((d)->d_namlen) +# endif +#else /* not POSIX or DIRENT */ +# define dirent direct +# define D_NAMLEN(d) ((d)->d_namlen) +# define HAVE_D_NAMLEN +# if defined USG && !defined sgi +# if defined SYSNDIR +# include <sys/ndir.h> +# else /* Not SYSNDIR */ +# include "ndir.h" +# endif /* SYSNDIR */ +# else /* not USG */ +# include <sys/dir.h> +# endif /* USG */ +#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */ + +#if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__ +# include <unistd.h> +#endif + +#if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX +# include <stdlib.h> +# include <string.h> +# define ANSI_STRING +#else /* No standard headers. */ + +# ifdef USG + +# include <string.h> +# ifdef NEED_MEMORY_H +# include <memory.h> +# endif +# define ANSI_STRING + +# else /* Not USG. */ + +# ifdef NeXT + +# include <string.h> + +# else /* Not NeXT. */ + +# include <strings.h> + +# ifndef bcmp +extern int bcmp (); +# endif +# ifndef bzero +extern void bzero (); +# endif +# ifndef bcopy +extern void bcopy (); +# endif + +# endif /* NeXT. */ + +# endif /* USG. */ + +extern char *malloc (), *realloc (); +extern void free (); + +#endif /* Standard headers. */ + +#ifndef ANSI_STRING +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memmove memcpy +#endif /* Not ANSI_STRING. */ + +#ifndef MAX +# define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif + +#ifdef _LIBC +# ifndef mempcpy +# define mempcpy __mempcpy +# endif +# define HAVE_MEMPCPY 1 +#endif + +#if !defined __alloca && !defined __GNU_LIBRARY__ + +# ifdef __GNUC__ +# undef alloca +# define alloca(n) __builtin_alloca (n) +# else /* Not GCC. */ +# if defined sparc || defined HAVE_ALLOCA_H +# include <alloca.h> +# else /* Not sparc or HAVE_ALLOCA_H. */ +# ifndef _AIX +extern char *alloca (); +# endif /* Not _AIX. */ +# endif /* sparc or HAVE_ALLOCA_H. */ +# endif /* GCC. */ + +# define __alloca alloca + +#endif + +#if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__ +# include <limits.h> +#else +# include <sys/param.h> +#endif + +#if defined _LIBC +# include <not-cancel.h> +# include <kernel-features.h> +#else +# define openat64_not_cancel_3(dfd, name, mode) openat64 (dfd, name, mode) +# define close_not_cancel_no_status(fd) close (fd) +#endif + +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +#if !defined STDC_HEADERS && !defined __GNU_LIBRARY__ +# undef size_t +# define size_t unsigned int +#endif + +#ifndef __GNU_LIBRARY__ +# define __lstat64 stat64 +#endif + +#ifndef _LIBC +# define __rewinddir rewinddir +#endif + +#ifndef _LIBC +# define __getcwd getcwd +#endif + +#ifndef GETCWD_RETURN_TYPE +# define GETCWD_RETURN_TYPE char * +#endif + +#ifdef __ASSUME_ATFCTS +# define __have_atfcts 1 +#elif IS_IN (rtld) +static int __rtld_have_atfcts; +# define __have_atfcts __rtld_have_atfcts +#endif + +/* Get the pathname of the current working directory, and put it in SIZE + bytes of BUF. Returns NULL if the directory couldn't be determined or + SIZE was too small. If successful, returns BUF. In GNU, if BUF is + NULL, an array is allocated with `malloc'; the array is SIZE bytes long, + unless SIZE == 0, in which case it is as big as necessary. */ + +GETCWD_RETURN_TYPE +__getcwd (char *buf, size_t size) +{ +#ifndef __ASSUME_ATFCTS + static const char dots[] + = "../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../.."; + const char *dotp = &dots[sizeof (dots)]; + const char *dotlist = dots; + size_t dotsize = sizeof (dots) - 1; +#endif + int prev_errno = errno; + DIR *dirstream = NULL; + bool fd_needs_closing = false; + int fd = AT_FDCWD; + + char *path; +#ifndef NO_ALLOCATION + size_t allocated = size; + if (size == 0) + { + if (buf != NULL) + { + __set_errno (EINVAL); + return NULL; + } + + allocated = PATH_MAX + 1; + } + + if (buf == NULL) + { + path = malloc (allocated); + if (path == NULL) + return NULL; + } + else +#else +# define allocated size +#endif + path = buf; + + char *pathp = path + allocated; + *--pathp = '\0'; + + struct stat64 st; + if (__lstat64 (".", &st) < 0) + goto lose; + dev_t thisdev = st.st_dev; + ino_t thisino = st.st_ino; + + if (__lstat64 ("/", &st) < 0) + goto lose; + dev_t rootdev = st.st_dev; + ino_t rootino = st.st_ino; + + while (!(thisdev == rootdev && thisino == rootino)) + { + if (__have_atfcts >= 0) + fd = openat64_not_cancel_3 (fd, "..", O_RDONLY | O_CLOEXEC); + else + fd = -1; + if (fd >= 0) + { + fd_needs_closing = true; + if (__fstat64 (fd, &st) < 0) + goto lose; + } +#ifndef __ASSUME_ATFCTS + else if (errno == ENOSYS) + { + __have_atfcts = -1; + + /* Look at the parent directory. */ + if (dotp == dotlist) + { +# ifdef NO_ALLOCATION + __set_errno (ENOMEM); + goto lose; +# else + /* My, what a deep directory tree you have, Grandma. */ + char *new; + if (dotlist == dots) + { + new = malloc (dotsize * 2 + 1); + if (new == NULL) + goto lose; +# ifdef HAVE_MEMPCPY + dotp = mempcpy (new, dots, dotsize); +# else + memcpy (new, dots, dotsize); + dotp = &new[dotsize]; +# endif + } + else + { + new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1); + if (new == NULL) + goto lose; + dotp = &new[dotsize]; + } +# ifdef HAVE_MEMPCPY + *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0'; + dotsize *= 2; +# else + memcpy ((char *) dotp, new, dotsize); + dotsize *= 2; + new[dotsize] = '\0'; +# endif + dotlist = new; +# endif + } + + dotp -= 3; + + /* Figure out if this directory is a mount point. */ + if (__lstat64 (dotp, &st) < 0) + goto lose; + } +#endif + else + goto lose; + + if (dirstream && __closedir (dirstream) != 0) + { + dirstream = NULL; + goto lose; + } + + dev_t dotdev = st.st_dev; + ino_t dotino = st.st_ino; + bool mount_point = dotdev != thisdev; + + /* Search for the last directory. */ + if (__have_atfcts >= 0) + dirstream = __fdopendir (fd); +#ifndef __ASSUME_ATFCTS + else + dirstream = __opendir (dotp); +#endif + if (dirstream == NULL) + goto lose; + fd_needs_closing = false; + + struct dirent *d; + bool use_d_ino = true; + while (1) + { + /* Clear errno to distinguish EOF from error if readdir returns + NULL. */ + __set_errno (0); + d = __readdir (dirstream); + if (d == NULL) + { + if (errno == 0) + { + /* When we've iterated through all directory entries + without finding one with a matching d_ino, rewind the + stream and consider each name again, but this time, using + lstat64. This is necessary in a chroot on at least one + system. */ + if (use_d_ino) + { + use_d_ino = false; + __rewinddir (dirstream); + continue; + } + + /* EOF on dirstream, which means that the current directory + has been removed. */ + __set_errno (ENOENT); + } + goto lose; + } + +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN) + continue; +#endif + if (d->d_name[0] == '.' + && (d->d_name[1] == '\0' + || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + if (use_d_ino && !mount_point && (ino_t) d->d_ino != thisino) + continue; + + if (__have_atfcts >= 0) + { + /* We don't fail here if we cannot stat64() a directory entry. + This can happen when (network) filesystems fail. If this + entry is in fact the one we are looking for we will find + out soon as we reach the end of the directory without + having found anything. */ + if (__fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + } +#ifndef __ASSUME_ATFCTS + else + { + char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)]; +# ifdef HAVE_MEMPCPY + char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp); + *tmp++ = '/'; + strcpy (tmp, d->d_name); +# else + memcpy (name, dotp, dotlist + dotsize - dotp); + name[dotlist + dotsize - dotp] = '/'; + strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name); +# endif + /* We don't fail here if we cannot stat64() a directory entry. + This can happen when (network) filesystems fail. If this + entry is in fact the one we are looking for we will find + out soon as we reach the end of the directory without + having found anything. */ + if (__lstat64 (name, &st) < 0) + continue; + } +#endif + if (S_ISDIR (st.st_mode) + && st.st_dev == thisdev && st.st_ino == thisino) + break; + } + + size_t namlen = _D_EXACT_NAMLEN (d); + + if ((size_t) (pathp - path) <= namlen) + { +#ifndef NO_ALLOCATION + if (size == 0) + { + size_t oldsize = allocated; + + allocated = 2 * MAX (allocated, namlen); + char *tmp = realloc (path, allocated); + if (tmp == NULL) + goto lose; + + /* Move current contents up to the end of the buffer. + This is guaranteed to be non-overlapping. */ + pathp = memcpy (tmp + allocated - (path + oldsize - pathp), + tmp + (pathp - path), + path + oldsize - pathp); + path = tmp; + } + else +#endif + { + __set_errno (ERANGE); + goto lose; + } + } + pathp -= namlen; + (void) memcpy (pathp, d->d_name, namlen); + *--pathp = '/'; + + thisdev = dotdev; + thisino = dotino; + } + + if (dirstream != NULL && __closedir (dirstream) != 0) + { + dirstream = NULL; + goto lose; + } + + if (pathp == &path[allocated - 1]) + *--pathp = '/'; + +#ifndef __ASSUME_ATFCTS + if (dotlist != dots) + free ((__ptr_t) dotlist); +#endif + + size_t used = path + allocated - pathp; + memmove (path, pathp, used); + + if (size == 0) + /* Ensure that the buffer is only as large as necessary. */ + buf = realloc (path, used); + + if (buf == NULL) + /* Either buf was NULL all along, or `realloc' failed but + we still have the original string. */ + buf = path; + + /* Restore errno on successful return. */ + __set_errno (prev_errno); + + return buf; + + lose:; + int save_errno = errno; +#ifndef __ASSUME_ATFCTS + if (dotlist != dots) + free ((__ptr_t) dotlist); +#endif + if (dirstream != NULL) + __closedir (dirstream); + if (fd_needs_closing) + close_not_cancel_no_status (fd); +#ifndef NO_ALLOCATION + if (buf == NULL) + free (path); +#endif + __set_errno (save_errno); + return NULL; +} + +#if defined _LIBC && !defined __getcwd +weak_alias (__getcwd, getcwd) +#endif |