1 /* provide a chdir function that tries not to fail due to ENAMETOOLONG
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* written by Jim Meyering */
22 #include "chdir-long.h"
37 # define O_DIRECTORY 0
41 # define MIN(a, b) ((a) < (b) ? (a) : (b))
45 # error "compile this file only if your system defines PATH_MAX"
48 /* FIXME: this use of `MIN' is our sole concession to arbitrary limitations.
49 If, for some system, PATH_MAX is larger than 8191 and you call
50 chdir_long with a directory name that is longer than PATH_MAX,
51 yet that contains a single component that is more than 8191 bytes
52 long, then this function will fail. */
53 #define MAX_COMPONENT_LENGTH MIN (PATH_MAX - 1, 8 * 1024)
57 /* FIXME maybe allocate this via malloc, rather than using the stack.
58 But that would be the sole use of malloc. Is it worth it to
59 let chdir_long fail due to a low-memory condition?
60 But when using malloc, and assuming we remove the `concession'
61 above, we'll still have to avoid allocating 2^31 bytes on
62 systems that define PATH_MAX to very large number.
63 Ideally, we'd allocate enough to deal with most names, and
64 dynamically increase the buffer size only when necessary. */
65 char buffer[MAX_COMPONENT_LENGTH + 1];
70 /* Like memchr, but return the number of bytes from MEM
71 to the first occurrence of C thereafter. Search only
72 LEN bytes. Return LEN if C is not found. */
74 memchrcspn (char const *mem, int c, size_t len)
76 char const *found = memchr (mem, c, len);
85 cdb_init (struct cd_buf *cdb)
87 cdb->avail = cdb->buffer;
92 cdb_empty (struct cd_buf const *cdb)
94 return cdb->avail == cdb->buffer;
98 cdb_fchdir (struct cd_buf const *cdb)
100 return fchdir (cdb->fd);
104 cdb_advance_fd (struct cd_buf *cdb, char const *dir)
106 int new_fd = openat (cdb->fd, dir, O_RDONLY | O_DIRECTORY);
109 new_fd = openat (cdb->fd, dir, O_WRONLY | O_DIRECTORY);
114 if (cdb->fd != AT_FDCWD)
122 cdb_flush (struct cd_buf *cdb)
127 cdb->avail[0] = '\0';
128 if (cdb_advance_fd (cdb, cdb->buffer) != 0)
131 cdb->avail = cdb->buffer;
137 cdb_free (struct cd_buf *cdb)
139 if (0 <= cdb->fd && close (cdb->fd) != 0)
144 cdb_append (struct cd_buf *cdb, char const *s, size_t len)
146 char const *end = cdb->buffer + sizeof cdb->buffer;
148 /* Insert a slash separator if there is a preceding byte
149 and it's not a slash. */
150 bool need_slash = (cdb->buffer < cdb->avail && cdb->avail[-1] != '/');
153 if (sizeof cdb->buffer < len + 1)
155 /* This single component is too long. */
156 errno = ENAMETOOLONG;
160 /* See if there's enough room for the `/', the new component and
162 n_free = end - cdb->avail;
163 if (n_free < need_slash + len + 1)
165 if (cdb_flush (cdb) != 0)
171 *(cdb->avail)++ = '/';
173 cdb->avail = mempcpy (cdb->avail, s, len);
177 /* This is a wrapper around chdir that works even on PATH_MAX-limited
178 systems. It handles an arbitrarily long directory name by extracting
179 and processing manageable portions of the name. On systems without
180 the openat syscall, this means changing the working directory to
181 more and more `distant' points along the long directory name and
182 then restoring the working directory.
183 If any of those attempts to change or restore the working directory
184 fails, this function exits nonzero.
186 Note that this function may still fail with errno == ENAMETOOLONG,
187 but only if the specified directory name contains a component that
188 is long enough to provoke such a failure all by itself (e.g. if the
189 component is longer than PATH_MAX on systems that define PATH_MAX). */
192 chdir_long (char const *dir)
195 if (e == 0 || errno != ENAMETOOLONG)
199 size_t len = strlen (dir);
200 char const *dir_end = dir + len;
206 /* If DIR is the empty string, then the chdir above
207 must have failed and set errno to ENOENT. */
212 /* Names starting with exactly two slashes followed by at least
213 one non-slash are special --
214 for example, in some environments //Hostname/file may
215 denote a file on a different host.
216 Preserve those two leading slashes. Treat all other
217 sequences of slashes like a single one. */
218 if (3 <= len && dir[1] == '/' && dir[2] != '/')
220 size_t name_len = 1 + strcspn (dir + 3, "/");
221 if (cdb_append (&cdb, dir, 2 + name_len) != 0)
223 /* Advance D to next slash or to end of string. */
224 d = dir + 2 + name_len;
225 assert (*d == '/' || *d == '\0');
229 if (cdb_append (&cdb, "/", 1) != 0)
241 /* Skip any slashes to find start of next component --
242 or the end of DIR. */
243 char const *start = d + strspn (d, "/");
246 if (cdb_flush (&cdb) != 0)
250 /* If the remaining portion is no longer than PATH_MAX, then
251 flush anything that is buffered and do the rest in one chunk. */
252 if (dir_end - start <= PATH_MAX)
254 if (cdb_flush (&cdb) != 0
255 || cdb_advance_fd (&cdb, start) != 0)
260 len = memchrcspn (start, '/', dir_end - start);
261 assert (len == strcspn (start, "/"));
263 if (cdb_append (&cdb, start, len) != 0)
267 if (cdb_fchdir (&cdb) != 0)
275 int saved_errno = errno;
286 # include "closeout.h"
292 main (int argc, char *argv[])
298 program_name = argv[0];
299 atexit (close_stdout);
301 len = getline (&line, &n, stdin);
304 int saved_errno = errno;
308 error (EXIT_FAILURE, saved_errno,
309 "reading standard input");
314 if (line[len-1] == '\n')
317 if (chdir_long (line) != 0)
318 error (EXIT_FAILURE, errno,
319 "chdir_long failed: %s", line);
322 /* Using `pwd' here makes sense only if it is a robust implementation,
323 like the one in coreutils after the 2004-04-19 changes. */
324 char const *cmd = "pwd";
325 execlp (cmd, (char *) NULL);
326 error (EXIT_FAILURE, errno, "%s", cmd);
336 compile-command: "gcc -DTEST_CHDIR=1 -DHAVE_CONFIG_H -I.. -g -O -W -Wall chdir-long.c libfetish.a"