diff options
author | Jim Meyering <jim@meyering.net> | 2004-11-30 14:47:47 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 2004-11-30 14:47:47 +0000 |
commit | e9725a701a992b8b9c26ba0e69ce3865a4b16151 (patch) | |
tree | 9ad2c484d7d86f557f78d83caa3d64616c0e4498 /lib/chdir.c | |
parent | a4304d4187c3f47ba782c798ca0f4b2f4dabf5bd (diff) | |
download | coreutils-e9725a701a992b8b9c26ba0e69ce3865a4b16151.tar.xz |
renamed to chdir-long.[ch]
Diffstat (limited to 'lib/chdir.c')
-rw-r--r-- | lib/chdir.c | 346 |
1 files changed, 0 insertions, 346 deletions
diff --git a/lib/chdir.c b/lib/chdir.c deleted file mode 100644 index e8ce730d5..000000000 --- a/lib/chdir.c +++ /dev/null @@ -1,346 +0,0 @@ -/* provide a chdir function that tries not to fail due to ENAMETOOLONG - Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -/* written by Jim Meyering */ - -#include <config.h> - -#include "chdir.h" - -/* Disable the definition of chdir to rpl_chdir (from chdir.h) in this - file. Otherwise, we'd get conflicting prototypes for rpl_chdir on - most systems. */ -#undef chdir - -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <assert.h> -#include <limits.h> - -#include "mempcpy.h" -#include "openat.h" - -#ifndef O_DIRECTORY -# define O_DIRECTORY 0 -#endif - -#ifndef MIN -# define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef PATH_MAX -# ifdef MAXPATHLEN -# define PATH_MAX MAXPATHLEN -# else -# error "use this module only if your system defines PATH_MAX" -# endif -#endif - -/* FIXME: this use of `MIN' is our sole concession to arbitrary limitations. - If, for some system, PATH_MAX is larger than 8191 and you call - rpl_chdir with a directory name that is longer than PATH_MAX, - yet that contains a single component that is more than 8191 bytes - long, then this function will fail. */ -#define MAX_COMPONENT_LENGTH MIN (PATH_MAX - 1, 8 * 1024) - -struct cd_buf -{ - /* FIXME maybe allocate this via malloc, rather than using the stack. - But that would be the sole use of malloc. Is it worth it to - let chdir fail due to a low-memory condition? - But when using malloc, and assuming we remove the `concession' - above, we'll still have to avoid allocating 2^31 bytes on - systems that define PATH_MAX to very large number. - Ideally, we'd allocate enough to deal with most names, and - dynamically increase the buffer size only necessary. */ - char buffer[MAX_COMPONENT_LENGTH + 1]; - char *avail; - int fd; -}; - -/* Like memchr, but return the number of bytes from MEM - to the first occurrence of C thereafter. Search only - LEN bytes. Return LEN if C is not found. */ -static inline size_t -memchrcspn (char const *mem, int c, size_t len) -{ - char const *found = memchr (mem, c, len); - if (!found) - return len; - - len = found - mem; - return len; -} - -static void -cdb_init (struct cd_buf *cdb) -{ - cdb->avail = cdb->buffer; - cdb->fd = AT_FDCWD; -} - -static inline bool -cdb_empty (struct cd_buf const *cdb) -{ - return cdb->avail == cdb->buffer; -} - -static inline int -cdb_fchdir (struct cd_buf const *cdb) -{ - return fchdir (cdb->fd); -} - -static int -cdb_advance_fd (struct cd_buf *cdb, char const *dir) -{ - int new_fd = openat (cdb->fd, dir, O_RDONLY | O_DIRECTORY); - if (new_fd < 0) - { - new_fd = openat (cdb->fd, dir, O_WRONLY | O_DIRECTORY); - if (new_fd < 0) - return -1; - } - - if (cdb->fd != AT_FDCWD) - close (cdb->fd); - cdb->fd = new_fd; - - return 0; -} - -static int -cdb_flush (struct cd_buf *cdb) -{ - if (cdb_empty (cdb)) - return 0; - - cdb->avail[0] = '\0'; - if (cdb_advance_fd (cdb, cdb->buffer) != 0) - return -1; - - cdb->avail = cdb->buffer; - - return 0; -} - -static void -cdb_free (struct cd_buf *cdb) -{ - if (0 <= cdb->fd && close (cdb->fd) != 0) - abort (); -} - -static int -cdb_append (struct cd_buf *cdb, char const *s, size_t len) -{ - char const *end = cdb->buffer + sizeof cdb->buffer; - - /* Insert a slash separator if there is a preceding byte - and it's not a slash. */ - bool need_slash = (cdb->buffer < cdb->avail && cdb->avail[-1] != '/'); - size_t n_free; - - if (sizeof cdb->buffer < len + 1) - { - /* This single component is too long. */ - errno = ENAMETOOLONG; - return -1; - } - - /* See if there's enough room for the `/', the new component and - a trailing NUL. */ - n_free = end - cdb->avail; - if (n_free < need_slash + len + 1) - { - if (cdb_flush (cdb) != 0) - return -1; - need_slash = false; - } - - if (need_slash) - *(cdb->avail)++ = '/'; - - cdb->avail = mempcpy (cdb->avail, s, len); - return 0; -} - -/* This is a wrapper around chdir that works even on PATH_MAX-limited - systems. It handles an arbitrarily long directory name by extracting - and processing 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 change 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 is longer than PATH_MAX on systems that define PATH_MAX). */ - -int -rpl_chdir (char const *dir) -{ - int e = chdir (dir); - if (e == 0 || errno != ENAMETOOLONG) - return e; - - { - size_t len = strlen (dir); - char const *dir_end = dir + len; - char const *d; - struct cd_buf cdb; - - cdb_init (&cdb); - - /* If DIR is the empty string, then the chdir above - must have failed and set errno to ENOENT. */ - assert (0 < len); - - if (*dir == '/') - { - /* Names starting with exactly two slashes followed by at least - one non-slash are special -- - for example, in some environments //Hostname/file may - denote a file on a different host. - Preserve those two leading slashes. Treat all other - sequences of slashes like a single one. */ - if (3 <= len && dir[1] == '/' && dir[2] != '/') - { - size_t name_len = 1 + strcspn (dir + 3, "/"); - if (cdb_append (&cdb, dir, 2 + name_len) != 0) - goto Fail; - /* Advance D to next slash or to end of string. */ - d = dir + 2 + name_len; - assert (*d == '/' || *d == '\0'); - } - else - { - if (cdb_append (&cdb, "/", 1) != 0) - goto Fail; - d = dir + 1; - } - } - else - { - d = dir; - } - - while (1) - { - /* Skip any slashes to find start of next component -- - or the end of DIR. */ - char const *start = d + strspn (d, "/"); - if (*start == '\0') - { - if (cdb_flush (&cdb) != 0) - goto Fail; - break; - } - /* If the remaining portion is no longer than PATH_MAX, then - flush anything that is buffered and do the rest in one chunk. */ - if (dir_end - start <= PATH_MAX) - { - if (cdb_flush (&cdb) != 0 - || cdb_advance_fd (&cdb, start) != 0) - goto Fail; - break; - } - - len = memchrcspn (start, '/', dir_end - start); - assert (len == strcspn (start, "/")); - d = start + len; - if (cdb_append (&cdb, start, len) != 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 (rpl_chdir (line) != 0) - error (EXIT_FAILURE, errno, - "chdir failed: %s", line); - - { - /* 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); - } - - /* not reached */ - abort (); -} -#endif - -/* -Local Variables: -compile-command: "gcc -DTEST_CHDIR=1 -DHAVE_CONFIG_H -I.. -g -O -W -Wall chdir.c libfetish.a" -End: -*/ |