1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <config.h>
21:
22: #include "chdir-long.h"
23:
24: #include <fcntl.h>
25: #include <stdlib.h>
26: #include <stdbool.h>
27: #include <string.h>
28: #include <errno.h>
29: #include <stdio.h>
30: #include <assert.h>
31:
32: #include "openat.h"
33:
34: #ifndef PATH_MAX
35: # error "compile this file only if your system defines PATH_MAX"
36: #endif
37:
38: struct cd_buf
39: {
40: int fd;
41: };
42:
43: static inline void
44: cdb_init (struct cd_buf *cdb)
45: {
46: cdb->fd = AT_FDCWD;
47: }
48:
49: static inline int
50: cdb_fchdir (struct cd_buf const *cdb)
51: {
52: return fchdir (cdb->fd);
53: }
54:
55: static inline void
56: cdb_free (struct cd_buf const *cdb)
57: {
58: if (0 <= cdb->fd)
59: {
60: bool close_fail = close (cdb->fd);
61: assert (! close_fail);
62: }
63: }
64:
65:
66:
67:
68:
69: static int
70: cdb_advance_fd (struct cd_buf *cdb, char const *dir)
71: {
72: int new_fd = openat (cdb->fd, dir,
73: O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
74: if (new_fd < 0)
75: return -1;
76:
77: cdb_free (cdb);
78: cdb->fd = new_fd;
79:
80: return 0;
81: }
82:
83:
84: static inline char *
85: find_non_slash (char const *s)
86: {
87: size_t n_slash = strspn (s, "/");
88: return (char *) s + n_slash;
89: }
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106: int
107: chdir_long (char *dir)
108: {
109: int e = chdir (dir);
110: if (e == 0 || errno != ENAMETOOLONG)
111: return e;
112:
113: {
114: size_t len = strlen (dir);
115: char *dir_end = dir + len;
116: struct cd_buf cdb;
117: size_t n_leading_slash;
118:
119: cdb_init (&cdb);
120:
121:
122:
123: assert (0 < len);
124: assert (PATH_MAX <= len);
125:
126:
127: n_leading_slash = strspn (dir, "/");
128:
129:
130:
131:
132:
133:
134: if (n_leading_slash == 2)
135: {
136: int err;
137:
138:
139: char *slash = memchr (dir + 3, '/', dir_end - (dir + 3));
140: if (slash == NULL)
141: {
142: errno = ENAMETOOLONG;
143: return -1;
144: }
145: *slash = '\0';
146: err = cdb_advance_fd (&cdb, dir);
147: *slash = '/';
148: if (err != 0)
149: goto Fail;
150: dir = find_non_slash (slash + 1);
151: }
152: else if (n_leading_slash)
153: {
154: if (cdb_advance_fd (&cdb, "/") != 0)
155: goto Fail;
156: dir += n_leading_slash;
157: }
158:
159: assert (*dir != '/');
160: assert (dir <= dir_end);
161:
162: while (PATH_MAX <= dir_end - dir)
163: {
164: int err;
165:
166:
167:
168: char *slash = memrchr (dir, '/', PATH_MAX);
169: if (slash == NULL)
170: {
171: errno = ENAMETOOLONG;
172: return -1;
173: }
174:
175: *slash = '\0';
176: assert (slash - dir < PATH_MAX);
177: err = cdb_advance_fd (&cdb, dir);
178: *slash = '/';
179: if (err != 0)
180: goto Fail;
181:
182: dir = find_non_slash (slash + 1);
183: }
184:
185: if (dir < dir_end)
186: {
187: if (cdb_advance_fd (&cdb, dir) != 0)
188: goto Fail;
189: }
190:
191: if (cdb_fchdir (&cdb) != 0)
192: goto Fail;
193:
194: cdb_free (&cdb);
195: return 0;
196:
197: Fail:
198: {
199: int saved_errno = errno;
200: cdb_free (&cdb);
201: errno = saved_errno;
202: return -1;
203: }
204: }
205: }
206:
207: #if TEST_CHDIR
208:
209: # include <stdio.h>
210: # include "closeout.h"
211: # include "error.h"
212:
213: char *program_name;
214:
215: int
216: main (int argc, char *argv[])
217: {
218: char *line = NULL;
219: size_t n = 0;
220: int len;
221:
222: program_name = argv[0];
223: atexit (close_stdout);
224:
225: len = getline (&line, &n, stdin);
226: if (len < 0)
227: {
228: int saved_errno = errno;
229: if (feof (stdin))
230: exit (0);
231:
232: error (EXIT_FAILURE, saved_errno,
233: "reading standard input");
234: }
235: else if (len == 0)
236: exit (0);
237:
238: if (line[len-1] == '\n')
239: line[len-1] = '\0';
240:
241: if (chdir_long (line) != 0)
242: error (EXIT_FAILURE, errno,
243: "chdir_long failed: %s", line);
244:
245: if (argc <= 1)
246: {
247:
248:
249: char const *cmd = "pwd";
250: execlp (cmd, (char *) NULL);
251: error (EXIT_FAILURE, errno, "%s", cmd);
252: }
253:
254: fclose (stdin);
255: fclose (stderr);
256:
257: exit (EXIT_SUCCESS);
258: }
259: #endif
260:
261:
262:
263:
264:
265: