OpenCoverage

chdir-long.c

Absolute File Name:/home/opencoverage/opencoverage/guest-scripts/coreutils/src/gnulib/lib/chdir-long.c
Source codeSwitch to Preprocessed file
LineSourceCount
1/* provide a chdir function that tries not to fail due to ENAMETOOLONG-
2 Copyright (C) 2004-2018 Free Software Foundation, Inc.-
3-
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 3 of the License, or-
7 (at your option) any later version.-
8-
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.-
13-
14 You should have received a copy of the GNU General Public License-
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */-
16-
17/* written by Jim Meyering */-
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/* The results of openat() in this file are not leaked to any-
37 single-threaded code that could use stdio.-
38 FIXME - if the kernel ever adds support for multi-thread safety for-
39 avoiding standard fds, then we should use openat_safer. */-
40-
41struct cd_buf-
42{-
43 int fd;-
44};-
45-
46static void-
47cdb_init (struct cd_buf *cdb)-
48{-
49 cdb->fd = AT_FDCWD;-
50}
never executed: end of block
0
51-
52static int-
53cdb_fchdir (struct cd_buf const *cdb)-
54{-
55 return fchdir (cdb->fd);
never executed: return fchdir (cdb->fd);
0
56}-
57-
58static void-
59cdb_free (struct cd_buf const *cdb)-
60{-
61 if (0 <= cdb->fd)
0 <= cdb->fdDescription
TRUEnever evaluated
FALSEnever evaluated
0
62 {-
63 bool close_fail = close (cdb->fd);-
64 assure (! close_fail);-
65 }
never executed: end of block
0
66}
never executed: end of block
0
67-
68/* Given a file descriptor of an open directory (or AT_FDCWD), CDB->fd,-
69 try to open the CDB->fd-relative directory, DIR. If the open succeeds,-
70 update CDB->fd with the resulting descriptor, close the incoming file-
71 descriptor, and return zero. Upon failure, return -1 and set errno. */-
72static int-
73cdb_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)
new_fd < 0Description
TRUEnever evaluated
FALSEnever evaluated
0
78 return -1;
never executed: return -1;
0
79-
80 cdb_free (cdb);-
81 cdb->fd = new_fd;-
82-
83 return 0;
never executed: return 0;
0
84}-
85-
86/* Return a pointer to the first non-slash in S. */-
87static char * _GL_ATTRIBUTE_PURE-
88find_non_slash (char const *s)-
89{-
90 size_t n_slash = strspn (s, "/");-
91 return (char *) s + n_slash;
never executed: return (char *) s + n_slash;
0
92}-
93-
94/* This is a function much like chdir, but without the PATH_MAX limitation-
95 on the length of the directory name. A significant difference is that-
96 it must be able to modify (albeit only temporarily) the directory-
97 name. It handles an arbitrarily long directory name by operating-
98 on manageable portions of the name. On systems without the openat-
99 syscall, this means changing the working directory to more and more-
100 "distant" points along the long directory name and then restoring-
101 the working directory. If any of those attempts to save or restore-
102 the working directory fails, this function exits nonzero.-
103-
104 Note that this function may still fail with errno == ENAMETOOLONG, but-
105 only if the specified directory name contains a component that is long-
106 enough to provoke such a failure all by itself (e.g. if the component-
107 has length PATH_MAX or greater on systems that define PATH_MAX). */-
108-
109int-
110chdir_long (char *dir)-
111{-
112 int e = chdir (dir);-
113 if (e == 0 || errno != ENAMETOOLONG)
e == 0Description
TRUEnever evaluated
FALSEnever evaluated
(*__errno_location ()) != 36Description
TRUEnever evaluated
FALSEnever evaluated
0
114 return e;
never executed: return e;
0
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 /* If DIR is the empty string, then the chdir above-
125 must have failed and set errno to ENOENT. */-
126 assure (0 < len);-
127 assure (PATH_MAX <= len);-
128-
129 /* Count leading slashes. */-
130 n_leading_slash = strspn (dir, "/");-
131-
132 /* Handle any leading slashes as well as any name that matches-
133 the regular expression, m!^//hostname[/]*! . Handling this-
134 prefix separately usually results in a single additional-
135 cdb_advance_fd call, but it's worthwhile, since it makes the-
136 code in the following loop cleaner. */-
137 if (n_leading_slash == 2)
n_leading_slash == 2Description
TRUEnever evaluated
FALSEnever evaluated
0
138 {-
139 int err;-
140 /* Find next slash.-
141 We already know that dir[2] is neither a slash nor '\0'. */-
142 char *slash = memchr (dir + 3, '/', dir_end - (dir + 3));-
143 if (slash == NULL)
slash == ((void *)0)Description
TRUEnever evaluated
FALSEnever evaluated
0
144 {-
145 errno = ENAMETOOLONG;-
146 return -1;
never executed: return -1;
0
147 }-
148 *slash = '\0';-
149 err = cdb_advance_fd (&cdb, dir);-
150 *slash = '/';-
151 if (err != 0)
err != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
152 goto Fail;
never executed: goto Fail;
0
153 dir = find_non_slash (slash + 1);-
154 }
never executed: end of block
0
155 else if (n_leading_slash)
n_leading_slashDescription
TRUEnever evaluated
FALSEnever evaluated
0
156 {-
157 if (cdb_advance_fd (&cdb, "/") != 0)
cdb_advance_fd...cdb, "/") != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
158 goto Fail;
never executed: goto Fail;
0
159 dir += n_leading_slash;-
160 }
never executed: end of block
0
161-
162 assure (*dir != '/');-
163 assure (dir <= dir_end);-
164-
165 while (PATH_MAX <= dir_end - dir)
4096 <= dir_end - dirDescription
TRUEnever evaluated
FALSEnever evaluated
0
166 {-
167 int err;-
168 /* Find a slash that is PATH_MAX or fewer bytes away from dir.-
169 I.e. see if there is a slash that will give us a name of-
170 length PATH_MAX-1 or less. */-
171 char *slash = memrchr (dir, '/', PATH_MAX);-
172 if (slash == NULL)
slash == ((void *)0)Description
TRUEnever evaluated
FALSEnever evaluated
0
173 {-
174 errno = ENAMETOOLONG;-
175 return -1;
never executed: return -1;
0
176 }-
177-
178 *slash = '\0';-
179 assure (slash - dir < PATH_MAX);-
180 err = cdb_advance_fd (&cdb, dir);-
181 *slash = '/';-
182 if (err != 0)
err != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
183 goto Fail;
never executed: goto Fail;
0
184-
185 dir = find_non_slash (slash + 1);-
186 }
never executed: end of block
0
187-
188 if (dir < dir_end)
dir < dir_endDescription
TRUEnever evaluated
FALSEnever evaluated
0
189 {-
190 if (cdb_advance_fd (&cdb, dir) != 0)
cdb_advance_fd...cdb, dir) != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
191 goto Fail;
never executed: goto Fail;
0
192 }
never executed: end of block
0
193-
194 if (cdb_fchdir (&cdb) != 0)
cdb_fchdir (&cdb) != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
195 goto Fail;
never executed: goto Fail;
0
196-
197 cdb_free (&cdb);-
198 return 0;
never executed: return 0;
0
199-
200 Fail:-
201 {-
202 int saved_errno = errno;-
203 cdb_free (&cdb);-
204 errno = saved_errno;-
205 return -1;
never executed: return -1;
0
206 }-
207 }-
208}-
209-
210#if TEST_CHDIR-
211-
212# include "closeout.h"-
213# include "error.h"-
214-
215int-
216main (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 /* Using 'pwd' here makes sense only if it is a robust implementation,-
247 like the one in coreutils after the 2004-04-19 changes. */-
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/*-
261Local Variables:-
262compile-command: "gcc -DTEST_CHDIR=1 -g -O -W -Wall chdir-long.c libcoreutils.a"-
263End:-
264*/-
Source codeSwitch to Preprocessed file

Generated by Squish Coco 4.1.2