2018-09-11 00:22:59 +08:00
|
|
|
|
/* Copyright (C) 1991-1999, 2004-2016 Free Software Foundation, Inc.
|
Import "glob" and "getcwd" modules from gnulib
These two modules are necessary because of the rework that will be
done in the "change directory" logic on GDB/gdbserver in the next
commits.
First, we will get rid of the "gdb_dirbuf" global variable and instead
rely on the fact that "getcwd (NULL, 0)", which is a GNU extension,
returns a heap-allocated string with the necessary bytes to hold the
full path. This is a good practice not only because globals are not
ideal but also because there is no good way to know beforehand the
size of the full pathname allowed in the filesystem ("PATH_MAX" is not
portable and does not reflect all the possible filesystems out there).
We will also have a way to "cd" to a directory also on gdbserver, but
in order to do that uniformly, there must be a way to do tilde
expansion on directories provided by the user. Currently, GDB uses
"tilde_expand" from readline to do that, but gdbserver doesn't link
against readline and therefore cannot use this function. The solution
is to use "glob" instead, which can perform tilde expansion as a GNU
extension. Therefore, we need gnulib's version of "glob".
A special note is necessary for gdb/ser-tcp.c. It defines "close" as
"closesocket" on Win32 targets. If we leave the code as is, this
would conflict with gnulib's definition of "close". Therefore, in
order to keep the same code path from before this import, I decided to
"#undef close" first, and then let the original (re)definition of it
take place.
gdb/ChangeLog:
2017-09-22 Sergio Durigan Junior <sergiodj@redhat.com>
* gnulib/aclocal.m4: Regenerate.
* gnulib/config.in: Regenerate.
* gnulib/configure: Regenerate.
* gnulib/import/Makefile.am: Regenerate.
* gnulib/import/Makefile.in: Regenerate.
* gnulib/import/assure.h: New file.
* gnulib/import/at-func.c: Likewise
* gnulib/import/chdir-long.c: New file.
* gnulib/import/chdir-long.h: New file.
* gnulib/import/cloexec.c: New file.
* gnulib/import/cloexec.h: New file.
* gnulib/import/close.c: New file.
* gnulib/import/closedir.c: New file.
* gnulib/import/dirent-private.h: New file.
* gnulib/import/dup-safer.c: New file.
* gnulib/import/dup.c: New file.
* gnulib/import/dup2.c: New file.
* gnulib/import/error.c: New file.
* gnulib/import/error.h: New file.
* gnulib/import/exitfail.c: New file.
* gnulib/import/exitfail.h: New file.
* gnulib/import/fchdir.c: New file.
* gnulib/import/fcntl.c: New file.
* gnulib/import/fcntl.in.h: New file.
* gnulib/import/fd-hook.c: New file.
* gnulib/import/fd-hook.h: New file.
* gnulib/import/fd-safer.c: New file.
* gnulib/import/fdopendir.c: New file.
* gnulib/import/filename.h: New file.
* gnulib/import/filenamecat-lgpl.c: New file.
* gnulib/import/filenamecat.h: New file.
* gnulib/import/fstat.c: New file.
* gnulib/import/fstatat.c: New file.
* gnulib/import/getcwd-lgpl.c: New file.
* gnulib/import/getcwd.c: New file.
* gnulib/import/getdtablesize.c: New file.
* gnulib/import/getlogin_r.c: New file.
* gnulib/import/getprogname.c: New file.
* gnulib/import/getprogname.h: New file.
* gnulib/import/gettext.h: New file.
* gnulib/import/glob-libc.h: New file.
* gnulib/import/glob.c: New file.
* gnulib/import/glob.in.h: New file.
* gnulib/import/intprops.h: New file.
* gnulib/import/m4/chdir-long.m4: New file.
* gnulib/import/m4/close.m4: New file.
* gnulib/import/m4/closedir.m4: New file.
* gnulib/import/m4/d-ino.m4: New file.
* gnulib/import/m4/d-type.m4: New file.
* gnulib/import/m4/dup.m4: New file.
* gnulib/import/m4/dup2.m4: New file.
* gnulib/import/m4/error.m4: New file.
* gnulib/import/m4/fchdir.m4: New file.
* gnulib/import/m4/fcntl.m4: New file.
* gnulib/import/m4/fcntl_h.m4: New file.
* gnulib/import/m4/fdopendir.m4: New file.
* gnulib/import/m4/filenamecat.m4: New file.
* gnulib/import/m4/fstat.m4: New file.
* gnulib/import/m4/fstatat.m4: New file.
* gnulib/import/m4/getcwd-abort-bug.m4: New file.
* gnulib/import/m4/getcwd-path-max.m4: New file.
* gnulib/import/m4/getcwd.m4: New file.
* gnulib/import/m4/getdtablesize.m4: New file.
* gnulib/import/m4/getlogin_r.m4: New file.
* gnulib/import/m4/getprogname.m4: New file.
* gnulib/import/m4/glob.m4: New file.
* gnulib/import/m4/gnulib-cache.m4: Regenerate.
* gnulib/import/m4/gnulib-comp.m4: Regenerate.
* gnulib/import/m4/mempcpy.m4: New file.
* gnulib/import/m4/memrchr.m4: New file.
* gnulib/import/m4/mode_t.m4: New file.
* gnulib/import/m4/msvc-inval.m4: New file.
* gnulib/import/m4/msvc-nothrow.m4: New file.
* gnulib/import/m4/open.m4: New file.
* gnulib/import/m4/openat.m4: New file.
* gnulib/import/m4/opendir.m4: New file.
* gnulib/import/m4/readdir.m4: New file.
* gnulib/import/m4/realloc.m4: New file.
* gnulib/import/m4/rewinddir.m4: New file.
* gnulib/import/m4/save-cwd.m4: New file.
* gnulib/import/m4/strdup.m4: New file.
* gnulib/import/m4/strerror.m4: New file.
* gnulib/import/m4/unistd-safer.m4: New file.
* gnulib/import/mempcpy.c: New file.
* gnulib/import/memrchr.c: New file.
* gnulib/import/msvc-inval.c: New file.
* gnulib/import/msvc-inval.h: New file.
* gnulib/import/msvc-nothrow.c: New file.
* gnulib/import/msvc-nothrow.h: New file.
* gnulib/import/open.c: New file.
* gnulib/import/openat-die.c: New file.
* gnulib/import/openat-priv.h: New file.
* gnulib/import/openat-proc.c: New file.
* gnulib/import/openat.c: New file.
* gnulib/import/openat.h: New file.
* gnulib/import/opendir.c: New file.
* gnulib/import/pipe-safer.c: New file.
* gnulib/import/readdir.c: New file.
* gnulib/import/realloc.c: New file.
* gnulib/import/rewinddir.c: New file.
* gnulib/import/save-cwd.c: New file.
* gnulib/import/save-cwd.h: New file.
* gnulib/import/strdup.c: New file.
* gnulib/import/strerror-override.c: New file.
* gnulib/import/strerror-override.h: New file.
* gnulib/import/strerror.c: New file.
* gnulib/import/unistd--.h: New file.
* gnulib/import/unistd-safer.h: New file.
* gnulib/update-gnulib.sh (IMPORTED_GNULIB_MODULES): Add
"getcwd" and "glob".
* ser-tcp.c: Undefine "close" before redefining it.
2017-09-09 08:09:12 +08:00
|
|
|
|
This file is part of the GNU C Library.
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2018-09-11 00:22:59 +08:00
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
Import "glob" and "getcwd" modules from gnulib
These two modules are necessary because of the rework that will be
done in the "change directory" logic on GDB/gdbserver in the next
commits.
First, we will get rid of the "gdb_dirbuf" global variable and instead
rely on the fact that "getcwd (NULL, 0)", which is a GNU extension,
returns a heap-allocated string with the necessary bytes to hold the
full path. This is a good practice not only because globals are not
ideal but also because there is no good way to know beforehand the
size of the full pathname allowed in the filesystem ("PATH_MAX" is not
portable and does not reflect all the possible filesystems out there).
We will also have a way to "cd" to a directory also on gdbserver, but
in order to do that uniformly, there must be a way to do tilde
expansion on directories provided by the user. Currently, GDB uses
"tilde_expand" from readline to do that, but gdbserver doesn't link
against readline and therefore cannot use this function. The solution
is to use "glob" instead, which can perform tilde expansion as a GNU
extension. Therefore, we need gnulib's version of "glob".
A special note is necessary for gdb/ser-tcp.c. It defines "close" as
"closesocket" on Win32 targets. If we leave the code as is, this
would conflict with gnulib's definition of "close". Therefore, in
order to keep the same code path from before this import, I decided to
"#undef close" first, and then let the original (re)definition of it
take place.
gdb/ChangeLog:
2017-09-22 Sergio Durigan Junior <sergiodj@redhat.com>
* gnulib/aclocal.m4: Regenerate.
* gnulib/config.in: Regenerate.
* gnulib/configure: Regenerate.
* gnulib/import/Makefile.am: Regenerate.
* gnulib/import/Makefile.in: Regenerate.
* gnulib/import/assure.h: New file.
* gnulib/import/at-func.c: Likewise
* gnulib/import/chdir-long.c: New file.
* gnulib/import/chdir-long.h: New file.
* gnulib/import/cloexec.c: New file.
* gnulib/import/cloexec.h: New file.
* gnulib/import/close.c: New file.
* gnulib/import/closedir.c: New file.
* gnulib/import/dirent-private.h: New file.
* gnulib/import/dup-safer.c: New file.
* gnulib/import/dup.c: New file.
* gnulib/import/dup2.c: New file.
* gnulib/import/error.c: New file.
* gnulib/import/error.h: New file.
* gnulib/import/exitfail.c: New file.
* gnulib/import/exitfail.h: New file.
* gnulib/import/fchdir.c: New file.
* gnulib/import/fcntl.c: New file.
* gnulib/import/fcntl.in.h: New file.
* gnulib/import/fd-hook.c: New file.
* gnulib/import/fd-hook.h: New file.
* gnulib/import/fd-safer.c: New file.
* gnulib/import/fdopendir.c: New file.
* gnulib/import/filename.h: New file.
* gnulib/import/filenamecat-lgpl.c: New file.
* gnulib/import/filenamecat.h: New file.
* gnulib/import/fstat.c: New file.
* gnulib/import/fstatat.c: New file.
* gnulib/import/getcwd-lgpl.c: New file.
* gnulib/import/getcwd.c: New file.
* gnulib/import/getdtablesize.c: New file.
* gnulib/import/getlogin_r.c: New file.
* gnulib/import/getprogname.c: New file.
* gnulib/import/getprogname.h: New file.
* gnulib/import/gettext.h: New file.
* gnulib/import/glob-libc.h: New file.
* gnulib/import/glob.c: New file.
* gnulib/import/glob.in.h: New file.
* gnulib/import/intprops.h: New file.
* gnulib/import/m4/chdir-long.m4: New file.
* gnulib/import/m4/close.m4: New file.
* gnulib/import/m4/closedir.m4: New file.
* gnulib/import/m4/d-ino.m4: New file.
* gnulib/import/m4/d-type.m4: New file.
* gnulib/import/m4/dup.m4: New file.
* gnulib/import/m4/dup2.m4: New file.
* gnulib/import/m4/error.m4: New file.
* gnulib/import/m4/fchdir.m4: New file.
* gnulib/import/m4/fcntl.m4: New file.
* gnulib/import/m4/fcntl_h.m4: New file.
* gnulib/import/m4/fdopendir.m4: New file.
* gnulib/import/m4/filenamecat.m4: New file.
* gnulib/import/m4/fstat.m4: New file.
* gnulib/import/m4/fstatat.m4: New file.
* gnulib/import/m4/getcwd-abort-bug.m4: New file.
* gnulib/import/m4/getcwd-path-max.m4: New file.
* gnulib/import/m4/getcwd.m4: New file.
* gnulib/import/m4/getdtablesize.m4: New file.
* gnulib/import/m4/getlogin_r.m4: New file.
* gnulib/import/m4/getprogname.m4: New file.
* gnulib/import/m4/glob.m4: New file.
* gnulib/import/m4/gnulib-cache.m4: Regenerate.
* gnulib/import/m4/gnulib-comp.m4: Regenerate.
* gnulib/import/m4/mempcpy.m4: New file.
* gnulib/import/m4/memrchr.m4: New file.
* gnulib/import/m4/mode_t.m4: New file.
* gnulib/import/m4/msvc-inval.m4: New file.
* gnulib/import/m4/msvc-nothrow.m4: New file.
* gnulib/import/m4/open.m4: New file.
* gnulib/import/m4/openat.m4: New file.
* gnulib/import/m4/opendir.m4: New file.
* gnulib/import/m4/readdir.m4: New file.
* gnulib/import/m4/realloc.m4: New file.
* gnulib/import/m4/rewinddir.m4: New file.
* gnulib/import/m4/save-cwd.m4: New file.
* gnulib/import/m4/strdup.m4: New file.
* gnulib/import/m4/strerror.m4: New file.
* gnulib/import/m4/unistd-safer.m4: New file.
* gnulib/import/mempcpy.c: New file.
* gnulib/import/memrchr.c: New file.
* gnulib/import/msvc-inval.c: New file.
* gnulib/import/msvc-inval.h: New file.
* gnulib/import/msvc-nothrow.c: New file.
* gnulib/import/msvc-nothrow.h: New file.
* gnulib/import/open.c: New file.
* gnulib/import/openat-die.c: New file.
* gnulib/import/openat-priv.h: New file.
* gnulib/import/openat-proc.c: New file.
* gnulib/import/openat.c: New file.
* gnulib/import/openat.h: New file.
* gnulib/import/opendir.c: New file.
* gnulib/import/pipe-safer.c: New file.
* gnulib/import/readdir.c: New file.
* gnulib/import/realloc.c: New file.
* gnulib/import/rewinddir.c: New file.
* gnulib/import/save-cwd.c: New file.
* gnulib/import/save-cwd.h: New file.
* gnulib/import/strdup.c: New file.
* gnulib/import/strerror-override.c: New file.
* gnulib/import/strerror-override.h: New file.
* gnulib/import/strerror.c: New file.
* gnulib/import/unistd--.h: New file.
* gnulib/import/unistd-safer.h: New file.
* gnulib/update-gnulib.sh (IMPORTED_GNULIB_MODULES): Add
"getcwd" and "glob".
* ser-tcp.c: Undefine "close" before redefining it.
2017-09-09 08:09:12 +08:00
|
|
|
|
|
|
|
|
|
#if !_LIBC
|
|
|
|
|
# include <config.h>
|
|
|
|
|
# include <unistd.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
|
|
#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
|
|
|
|
|
|
|
|
|
|
/* If this host provides the openat function or if we're using the
|
|
|
|
|
gnulib replacement function with a native fdopendir, then enable
|
|
|
|
|
code below to make getcwd more efficient and robust. */
|
|
|
|
|
#if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
|
|
|
|
|
# define HAVE_OPENAT_SUPPORT 1
|
|
|
|
|
#else
|
|
|
|
|
# define HAVE_OPENAT_SUPPORT 0
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef __set_errno
|
|
|
|
|
# define __set_errno(val) (errno = (val))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#ifndef _D_EXACT_NAMLEN
|
|
|
|
|
# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef _D_ALLOC_NAMLEN
|
|
|
|
|
# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#if _LIBC
|
|
|
|
|
# ifndef mempcpy
|
|
|
|
|
# define mempcpy __mempcpy
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef MAX
|
|
|
|
|
# define MAX(a, b) ((a) < (b) ? (b) : (a))
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef MIN
|
|
|
|
|
# define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "pathmax.h"
|
|
|
|
|
|
|
|
|
|
/* In this file, PATH_MAX only serves as a threshold for choosing among two
|
|
|
|
|
algorithms. */
|
|
|
|
|
#ifndef PATH_MAX
|
|
|
|
|
# define PATH_MAX 8192
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if D_INO_IN_DIRENT
|
|
|
|
|
# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
|
|
|
|
|
#else
|
|
|
|
|
# define MATCHING_INO(dp, ino) true
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !_LIBC
|
|
|
|
|
# define __getcwd rpl_getcwd
|
|
|
|
|
# define __lstat lstat
|
|
|
|
|
# define __closedir closedir
|
|
|
|
|
# define __opendir opendir
|
|
|
|
|
# define __readdir readdir
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* The results of opendir() in this file are not used with dirfd and fchdir,
|
|
|
|
|
and we do not leak fds to any single-threaded code that could use stdio,
|
|
|
|
|
therefore save some unnecessary recursion in fchdir.c.
|
|
|
|
|
FIXME - if the kernel ever adds support for multi-thread safety for
|
|
|
|
|
avoiding standard fds, then we should use opendir_safer and
|
|
|
|
|
openat_safer. */
|
|
|
|
|
#ifdef GNULIB_defined_opendir
|
|
|
|
|
# undef opendir
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef GNULIB_defined_closedir
|
|
|
|
|
# undef closedir
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Get the name 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. */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
__getcwd (char *buf, size_t size)
|
|
|
|
|
{
|
|
|
|
|
/* Lengths of big file name components and entire file names, and a
|
|
|
|
|
deep level of file name nesting. These numbers are not upper
|
|
|
|
|
bounds; they are merely large values suitable for initial
|
|
|
|
|
allocations, designed to be large enough for most real-world
|
|
|
|
|
uses. */
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
BIG_FILE_NAME_COMPONENT_LENGTH = 255,
|
|
|
|
|
BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
|
|
|
|
|
DEEP_NESTING = 100
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if HAVE_OPENAT_SUPPORT
|
|
|
|
|
int fd = AT_FDCWD;
|
|
|
|
|
bool fd_needs_closing = false;
|
|
|
|
|
#else
|
|
|
|
|
char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
|
|
|
|
|
char *dotlist = dots;
|
|
|
|
|
size_t dotsize = sizeof dots;
|
|
|
|
|
size_t dotlen = 0;
|
|
|
|
|
#endif
|
|
|
|
|
DIR *dirstream = NULL;
|
|
|
|
|
dev_t rootdev, thisdev;
|
|
|
|
|
ino_t rootino, thisino;
|
|
|
|
|
char *dir;
|
|
|
|
|
register char *dirp;
|
|
|
|
|
struct stat st;
|
|
|
|
|
size_t allocated = size;
|
|
|
|
|
size_t used;
|
|
|
|
|
|
|
|
|
|
#if HAVE_MINIMALLY_WORKING_GETCWD
|
|
|
|
|
/* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
|
|
|
|
|
this is much slower than the system getcwd (at least on
|
|
|
|
|
GNU/Linux). So trust the system getcwd's results unless they
|
|
|
|
|
look suspicious.
|
|
|
|
|
|
|
|
|
|
Use the system getcwd even if we have openat support, since the
|
|
|
|
|
system getcwd works even when a parent is unreadable, while the
|
|
|
|
|
openat-based approach does not.
|
|
|
|
|
|
|
|
|
|
But on AIX 5.1..7.1, the system getcwd is not even minimally
|
|
|
|
|
working: If the current directory name is slightly longer than
|
|
|
|
|
PATH_MAX, it omits the first directory component and returns
|
|
|
|
|
this wrong result with errno = 0. */
|
|
|
|
|
|
|
|
|
|
# undef getcwd
|
|
|
|
|
dir = getcwd (buf, size);
|
|
|
|
|
if (dir || (size && errno == ERANGE))
|
|
|
|
|
return dir;
|
|
|
|
|
|
|
|
|
|
/* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
|
|
|
|
|
internal magic that lets it work even if an ancestor directory is
|
|
|
|
|
inaccessible, which is better in many cases. So in this case try
|
|
|
|
|
again with a buffer that's almost always big enough. */
|
|
|
|
|
if (errno == EINVAL && buf == NULL && size == 0)
|
|
|
|
|
{
|
|
|
|
|
char big_buffer[BIG_FILE_NAME_LENGTH + 1];
|
|
|
|
|
dir = getcwd (big_buffer, sizeof big_buffer);
|
|
|
|
|
if (dir)
|
|
|
|
|
return strdup (dir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# if HAVE_PARTLY_WORKING_GETCWD
|
|
|
|
|
/* The system getcwd works, except it sometimes fails when it
|
|
|
|
|
shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. */
|
|
|
|
|
if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
|
|
|
|
|
return NULL;
|
|
|
|
|
# endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
|
{
|
|
|
|
|
if (buf != NULL)
|
|
|
|
|
{
|
|
|
|
|
__set_errno (EINVAL);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allocated = BIG_FILE_NAME_LENGTH + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
|
{
|
|
|
|
|
dir = malloc (allocated);
|
|
|
|
|
if (dir == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
dir = buf;
|
|
|
|
|
|
|
|
|
|
dirp = dir + allocated;
|
|
|
|
|
*--dirp = '\0';
|
|
|
|
|
|
|
|
|
|
if (__lstat (".", &st) < 0)
|
|
|
|
|
goto lose;
|
|
|
|
|
thisdev = st.st_dev;
|
|
|
|
|
thisino = st.st_ino;
|
|
|
|
|
|
|
|
|
|
if (__lstat ("/", &st) < 0)
|
|
|
|
|
goto lose;
|
|
|
|
|
rootdev = st.st_dev;
|
|
|
|
|
rootino = st.st_ino;
|
|
|
|
|
|
|
|
|
|
while (!(thisdev == rootdev && thisino == rootino))
|
|
|
|
|
{
|
|
|
|
|
struct dirent *d;
|
|
|
|
|
dev_t dotdev;
|
|
|
|
|
ino_t dotino;
|
|
|
|
|
bool mount_point;
|
|
|
|
|
int parent_status;
|
|
|
|
|
size_t dirroom;
|
|
|
|
|
size_t namlen;
|
|
|
|
|
bool use_d_ino = true;
|
|
|
|
|
|
|
|
|
|
/* Look at the parent directory. */
|
|
|
|
|
#if HAVE_OPENAT_SUPPORT
|
|
|
|
|
fd = openat (fd, "..", O_RDONLY);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
goto lose;
|
|
|
|
|
fd_needs_closing = true;
|
|
|
|
|
parent_status = fstat (fd, &st);
|
|
|
|
|
#else
|
|
|
|
|
dotlist[dotlen++] = '.';
|
|
|
|
|
dotlist[dotlen++] = '.';
|
|
|
|
|
dotlist[dotlen] = '\0';
|
|
|
|
|
parent_status = __lstat (dotlist, &st);
|
|
|
|
|
#endif
|
|
|
|
|
if (parent_status != 0)
|
|
|
|
|
goto lose;
|
|
|
|
|
|
|
|
|
|
if (dirstream && __closedir (dirstream) != 0)
|
|
|
|
|
{
|
|
|
|
|
dirstream = NULL;
|
|
|
|
|
goto lose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Figure out if this directory is a mount point. */
|
|
|
|
|
dotdev = st.st_dev;
|
|
|
|
|
dotino = st.st_ino;
|
|
|
|
|
mount_point = dotdev != thisdev;
|
|
|
|
|
|
|
|
|
|
/* Search for the last directory. */
|
|
|
|
|
#if HAVE_OPENAT_SUPPORT
|
|
|
|
|
dirstream = fdopendir (fd);
|
|
|
|
|
if (dirstream == NULL)
|
|
|
|
|
goto lose;
|
|
|
|
|
fd_needs_closing = false;
|
|
|
|
|
#else
|
|
|
|
|
dirstream = __opendir (dotlist);
|
|
|
|
|
if (dirstream == NULL)
|
|
|
|
|
goto lose;
|
|
|
|
|
dotlist[dotlen++] = '/';
|
|
|
|
|
#endif
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
/* Clear errno to distinguish EOF from error if readdir returns
|
|
|
|
|
NULL. */
|
|
|
|
|
__set_errno (0);
|
|
|
|
|
d = __readdir (dirstream);
|
|
|
|
|
|
|
|
|
|
/* 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 lstat. This is necessary in a
|
|
|
|
|
chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
|
|
|
|
|
.., ../.., ../../.., etc. all had the same device number, yet the
|
|
|
|
|
d_ino values for entries in / did not match those obtained
|
|
|
|
|
via lstat. */
|
|
|
|
|
if (d == NULL && errno == 0 && use_d_ino)
|
|
|
|
|
{
|
|
|
|
|
use_d_ino = false;
|
|
|
|
|
rewinddir (dirstream);
|
|
|
|
|
d = __readdir (dirstream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (d == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (errno == 0)
|
|
|
|
|
/* EOF on dirstream, which can mean e.g., that the current
|
|
|
|
|
directory has been removed. */
|
|
|
|
|
__set_errno (ENOENT);
|
|
|
|
|
goto lose;
|
|
|
|
|
}
|
|
|
|
|
if (d->d_name[0] == '.' &&
|
|
|
|
|
(d->d_name[1] == '\0' ||
|
|
|
|
|
(d->d_name[1] == '.' && d->d_name[2] == '\0')))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (use_d_ino)
|
|
|
|
|
{
|
|
|
|
|
bool match = (MATCHING_INO (d, thisino) || mount_point);
|
|
|
|
|
if (! match)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
int entry_status;
|
|
|
|
|
#if HAVE_OPENAT_SUPPORT
|
|
|
|
|
entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
|
|
|
|
#else
|
|
|
|
|
/* Compute size needed for this file name, or for the file
|
|
|
|
|
name ".." in the same directory, whichever is larger.
|
|
|
|
|
Room for ".." might be needed the next time through
|
|
|
|
|
the outer loop. */
|
|
|
|
|
size_t name_alloc = _D_ALLOC_NAMLEN (d);
|
|
|
|
|
size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
|
|
|
|
|
|
|
|
|
|
if (filesize < dotlen)
|
|
|
|
|
goto memory_exhausted;
|
|
|
|
|
|
|
|
|
|
if (dotsize < filesize)
|
|
|
|
|
{
|
|
|
|
|
/* My, what a deep directory tree you have, Grandma. */
|
|
|
|
|
size_t newsize = MAX (filesize, dotsize * 2);
|
|
|
|
|
size_t i;
|
|
|
|
|
if (newsize < dotsize)
|
|
|
|
|
goto memory_exhausted;
|
|
|
|
|
if (dotlist != dots)
|
|
|
|
|
free (dotlist);
|
|
|
|
|
dotlist = malloc (newsize);
|
|
|
|
|
if (dotlist == NULL)
|
|
|
|
|
goto lose;
|
|
|
|
|
dotsize = newsize;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
dotlist[i++] = '.';
|
|
|
|
|
dotlist[i++] = '.';
|
|
|
|
|
dotlist[i++] = '/';
|
|
|
|
|
}
|
|
|
|
|
while (i < dotlen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
|
|
|
|
|
entry_status = __lstat (dotlist, &st);
|
|
|
|
|
#endif
|
|
|
|
|
/* We don't fail here if we cannot stat() a directory entry.
|
|
|
|
|
This can happen when (network) file systems 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 (entry_status == 0 && S_ISDIR (st.st_mode)
|
|
|
|
|
&& st.st_dev == thisdev && st.st_ino == thisino)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dirroom = dirp - dir;
|
|
|
|
|
namlen = _D_EXACT_NAMLEN (d);
|
|
|
|
|
|
|
|
|
|
if (dirroom <= namlen)
|
|
|
|
|
{
|
|
|
|
|
if (size != 0)
|
|
|
|
|
{
|
|
|
|
|
__set_errno (ERANGE);
|
|
|
|
|
goto lose;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *tmp;
|
|
|
|
|
size_t oldsize = allocated;
|
|
|
|
|
|
|
|
|
|
allocated += MAX (allocated, namlen);
|
|
|
|
|
if (allocated < oldsize
|
|
|
|
|
|| ! (tmp = realloc (dir, allocated)))
|
|
|
|
|
goto memory_exhausted;
|
|
|
|
|
|
|
|
|
|
/* Move current contents up to the end of the buffer.
|
|
|
|
|
This is guaranteed to be non-overlapping. */
|
|
|
|
|
dirp = memcpy (tmp + allocated - (oldsize - dirroom),
|
|
|
|
|
tmp + dirroom,
|
|
|
|
|
oldsize - dirroom);
|
|
|
|
|
dir = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dirp -= namlen;
|
|
|
|
|
memcpy (dirp, d->d_name, namlen);
|
|
|
|
|
*--dirp = '/';
|
|
|
|
|
|
|
|
|
|
thisdev = dotdev;
|
|
|
|
|
thisino = dotino;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dirstream && __closedir (dirstream) != 0)
|
|
|
|
|
{
|
|
|
|
|
dirstream = NULL;
|
|
|
|
|
goto lose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dirp == &dir[allocated - 1])
|
|
|
|
|
*--dirp = '/';
|
|
|
|
|
|
|
|
|
|
#if ! HAVE_OPENAT_SUPPORT
|
|
|
|
|
if (dotlist != dots)
|
|
|
|
|
free (dotlist);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
used = dir + allocated - dirp;
|
|
|
|
|
memmove (dir, dirp, used);
|
|
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
|
/* Ensure that the buffer is only as large as necessary. */
|
|
|
|
|
buf = realloc (dir, used);
|
|
|
|
|
|
|
|
|
|
if (buf == NULL)
|
|
|
|
|
/* Either buf was NULL all along, or 'realloc' failed but
|
|
|
|
|
we still have the original string. */
|
|
|
|
|
buf = dir;
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
|
|
memory_exhausted:
|
|
|
|
|
__set_errno (ENOMEM);
|
|
|
|
|
lose:
|
|
|
|
|
{
|
|
|
|
|
int save = errno;
|
|
|
|
|
if (dirstream)
|
|
|
|
|
__closedir (dirstream);
|
|
|
|
|
#if HAVE_OPENAT_SUPPORT
|
|
|
|
|
if (fd_needs_closing)
|
|
|
|
|
close (fd);
|
|
|
|
|
#else
|
|
|
|
|
if (dotlist != dots)
|
|
|
|
|
free (dotlist);
|
|
|
|
|
#endif
|
|
|
|
|
if (buf == NULL)
|
|
|
|
|
free (dir);
|
|
|
|
|
__set_errno (save);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef weak_alias
|
|
|
|
|
weak_alias (__getcwd, getcwd)
|
|
|
|
|
#endif
|