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