diff options
Diffstat (limited to 'lib/chdir-long.c')
-rw-r--r-- | lib/chdir-long.c | 273 |
1 files changed, 0 insertions, 273 deletions
diff --git a/lib/chdir-long.c b/lib/chdir-long.c deleted file mode 100644 index 8c15d06d2..000000000 --- a/lib/chdir-long.c +++ /dev/null @@ -1,273 +0,0 @@ -/* provide a chdir function that tries not to fail due to ENAMETOOLONG - Copyright (C) 2004, 2005 Free Software Foundation, Inc. - - 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. - - 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 - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* written by Jim Meyering */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include "chdir-long.h" - -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> - -#include "memrchr.h" -#include "openat.h" - -#ifndef O_DIRECTORY -# define O_DIRECTORY 0 -#endif - -#ifndef PATH_MAX -# error "compile this file only if your system defines PATH_MAX" -#endif - -struct cd_buf -{ - int fd; -}; - -static inline void -cdb_init (struct cd_buf *cdb) -{ - cdb->fd = AT_FDCWD; -} - -static inline int -cdb_fchdir (struct cd_buf const *cdb) -{ - return fchdir (cdb->fd); -} - -static inline void -cdb_free (struct cd_buf const *cdb) -{ - if (0 <= cdb->fd) - { - bool close_fail = close (cdb->fd); - assert (! close_fail); - } -} - -/* Given a file descriptor of an open directory (or AT_FDCWD), CDB->fd, - try to open the CDB->fd-relative directory, DIR. If the open succeeds, - update CDB->fd with the resulting descriptor, close the incoming file - descriptor, and return zero. Upon failure, return -1 and set errno. */ -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); - if (new_fd < 0) - return -1; - - cdb_free (cdb); - cdb->fd = new_fd; - - return 0; -} - -/* Return a pointer to the first non-slash in S. */ -static inline char * -find_non_slash (char const *s) -{ - size_t n_slash = strspn (s, "/"); - return (char *) s + n_slash; -} - -/* This is a function much like chdir, but without the PATH_MAX limitation - on the length of the directory name. A significant difference is that - it must be able to modify (albeit only temporarily) the directory - 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 - the working directory. If any of those attempts to save or restore - the working directory fails, this function exits nonzero. - - Note that this function may still fail with errno == ENAMETOOLONG, but - only if the specified directory name contains a component that is long - enough to provoke such a failure all by itself (e.g. if the component - has length PATH_MAX or greater on systems that define PATH_MAX). */ - -int -chdir_long (char *dir) -{ - int e = chdir (dir); - if (e == 0 || errno != ENAMETOOLONG) - return e; - - { - size_t len = strlen (dir); - char *dir_end = dir + len; - struct cd_buf cdb; - size_t n_leading_slash; - - cdb_init (&cdb); - - /* 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); - - /* Count leading slashes. */ - n_leading_slash = strspn (dir, "/"); - - /* Handle any leading slashes as well as any name that matches - the regular expression, m!^//hostname[/]*! . Handling this - prefix separately usually results in a single additional - cdb_advance_fd call, but it's worthwhile, since it makes the - 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); - } - else if (n_leading_slash) - { - if (cdb_advance_fd (&cdb, "/") != 0) - goto Fail; - dir += n_leading_slash; - } - - assert (*dir != '/'); - assert (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); - } - - if (dir < dir_end) - { - if (cdb_advance_fd (&cdb, dir) != 0) - goto Fail; - } - - if (cdb_fchdir (&cdb) != 0) - goto Fail; - - cdb_free (&cdb); - return 0; - - Fail: - { - int saved_errno = errno; - cdb_free (&cdb); - errno = saved_errno; - return -1; - } - } -} - -#if TEST_CHDIR - -# include <stdio.h> -# include "closeout.h" -# include "error.h" - -char *program_name; - -int -main (int argc, char *argv[]) -{ - char *line = NULL; - size_t n = 0; - int len; - - program_name = argv[0]; - atexit (close_stdout); - - len = getline (&line, &n, stdin); - if (len < 0) - { - int saved_errno = errno; - if (feof (stdin)) - exit (0); - - error (EXIT_FAILURE, saved_errno, - "reading standard input"); - } - else if (len == 0) - exit (0); - - if (line[len-1] == '\n') - line[len-1] = '\0'; - - if (chdir_long (line) != 0) - error (EXIT_FAILURE, errno, - "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. */ - char const *cmd = "pwd"; - execlp (cmd, (char *) NULL); - error (EXIT_FAILURE, errno, "%s", cmd); - } - - fclose (stdin); - fclose (stderr); - - exit (EXIT_SUCCESS); -} -#endif - -/* -Local Variables: -compile-command: "gcc -DTEST_CHDIR=1 -DHAVE_CONFIG_H -I.. -g -O -W -Wall chdir-long.c libcoreutils.a" -End: -*/ |