diff options
Diffstat (limited to 'lib/chdir-long.c')
-rw-r--r-- | lib/chdir-long.c | 132 |
1 files changed, 67 insertions, 65 deletions
diff --git a/lib/chdir-long.c b/lib/chdir-long.c index 3fc7ef2..546b4b0 100644 --- a/lib/chdir-long.c +++ b/lib/chdir-long.c @@ -1,10 +1,10 @@ /* provide a chdir function that tries not to fail due to ENAMETOOLONG - Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2004-2016 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + 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 2, or (at your option) - any later version. + 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 @@ -12,8 +12,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* written by Jim Meyering */ @@ -21,44 +20,48 @@ #include "chdir-long.h" +#include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> -#include <errno.h> #include <stdio.h> -#include <assert.h> -#include "openat.h" +#include "assure.h" #ifndef PATH_MAX # error "compile this file only if your system defines PATH_MAX" #endif +/* The results of openat() in this file are not leaked to any + single-threaded code that could use stdio. + FIXME - if the kernel ever adds support for multi-thread safety for + avoiding standard fds, then we should use openat_safer. */ + struct cd_buf { int fd; }; -static inline void +static void cdb_init (struct cd_buf *cdb) { cdb->fd = AT_FDCWD; } -static inline int +static int cdb_fchdir (struct cd_buf const *cdb) { return fchdir (cdb->fd); } -static inline void +static void cdb_free (struct cd_buf const *cdb) { if (0 <= cdb->fd) { bool close_fail = close (cdb->fd); - assert (! close_fail); + assure (! close_fail); } } @@ -70,7 +73,7 @@ static int cdb_advance_fd (struct cd_buf *cdb, char const *dir) { int new_fd = openat (cdb->fd, dir, - O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); + O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); if (new_fd < 0) return -1; @@ -81,7 +84,7 @@ cdb_advance_fd (struct cd_buf *cdb, char const *dir) } /* Return a pointer to the first non-slash in S. */ -static inline char * +static char * _GL_ATTRIBUTE_PURE find_non_slash (char const *s) { size_t n_slash = strspn (s, "/"); @@ -94,7 +97,7 @@ find_non_slash (char const *s) name. It handles an arbitrarily long directory name by operating on manageable portions of the name. On systems without the openat syscall, this means changing the working directory to more and more - `distant' points along the long directory name and then restoring + "distant" points along the long directory name and then restoring the working directory. If any of those attempts to save or restore the working directory fails, this function exits nonzero. @@ -120,8 +123,8 @@ chdir_long (char *dir) /* If DIR is the empty string, then the chdir above must have failed and set errno to ENOENT. */ - assert (0 < len); - assert (PATH_MAX <= len); + assure (0 < len); + assure (PATH_MAX <= len); /* Count leading slashes. */ n_leading_slash = strspn (dir, "/"); @@ -133,59 +136,59 @@ chdir_long (char *dir) code in the following loop cleaner. */ if (n_leading_slash == 2) { - int err; - /* Find next slash. - We already know that dir[2] is neither a slash nor '\0'. */ - char *slash = memchr (dir + 3, '/', dir_end - (dir + 3)); - if (slash == NULL) - { - errno = ENAMETOOLONG; - return -1; - } - *slash = '\0'; - err = cdb_advance_fd (&cdb, dir); - *slash = '/'; - if (err != 0) - goto Fail; - dir = find_non_slash (slash + 1); + int err; + /* Find next slash. + We already know that dir[2] is neither a slash nor '\0'. */ + char *slash = memchr (dir + 3, '/', dir_end - (dir + 3)); + if (slash == NULL) + { + errno = ENAMETOOLONG; + return -1; + } + *slash = '\0'; + err = cdb_advance_fd (&cdb, dir); + *slash = '/'; + if (err != 0) + goto Fail; + dir = find_non_slash (slash + 1); } else if (n_leading_slash) { - if (cdb_advance_fd (&cdb, "/") != 0) - goto Fail; - dir += n_leading_slash; + if (cdb_advance_fd (&cdb, "/") != 0) + goto Fail; + dir += n_leading_slash; } - assert (*dir != '/'); - assert (dir <= dir_end); + assure (*dir != '/'); + assure (dir <= dir_end); while (PATH_MAX <= dir_end - dir) { - int err; - /* Find a slash that is PATH_MAX or fewer bytes away from dir. - I.e. see if there is a slash that will give us a name of - length PATH_MAX-1 or less. */ - char *slash = memrchr (dir, '/', PATH_MAX); - if (slash == NULL) - { - errno = ENAMETOOLONG; - return -1; - } - - *slash = '\0'; - assert (slash - dir < PATH_MAX); - err = cdb_advance_fd (&cdb, dir); - *slash = '/'; - if (err != 0) - goto Fail; - - dir = find_non_slash (slash + 1); + int err; + /* Find a slash that is PATH_MAX or fewer bytes away from dir. + I.e. see if there is a slash that will give us a name of + length PATH_MAX-1 or less. */ + char *slash = memrchr (dir, '/', PATH_MAX); + if (slash == NULL) + { + errno = ENAMETOOLONG; + return -1; + } + + *slash = '\0'; + assure (slash - dir < PATH_MAX); + err = cdb_advance_fd (&cdb, dir); + *slash = '/'; + if (err != 0) + goto Fail; + + dir = find_non_slash (slash + 1); } if (dir < dir_end) { - if (cdb_advance_fd (&cdb, dir) != 0) - goto Fail; + if (cdb_advance_fd (&cdb, dir) != 0) + goto Fail; } if (cdb_fchdir (&cdb) != 0) @@ -206,7 +209,6 @@ chdir_long (char *dir) #if TEST_CHDIR -# include <stdio.h> # include "closeout.h" # include "error.h" @@ -227,10 +229,10 @@ main (int argc, char *argv[]) { int saved_errno = errno; if (feof (stdin)) - exit (0); + exit (0); error (EXIT_FAILURE, saved_errno, - "reading standard input"); + "reading standard input"); } else if (len == 0) exit (0); @@ -240,12 +242,12 @@ main (int argc, char *argv[]) if (chdir_long (line) != 0) error (EXIT_FAILURE, errno, - "chdir_long failed: %s", line); + "chdir_long failed: %s", line); if (argc <= 1) { - /* Using `pwd' here makes sense only if it is a robust implementation, - like the one in coreutils after the 2004-04-19 changes. */ + /* Using 'pwd' here makes sense only if it is a robust implementation, + like the one in coreutils after the 2004-04-19 changes. */ char const *cmd = "pwd"; execlp (cmd, (char *) NULL); error (EXIT_FAILURE, errno, "%s", cmd); |