1 /* pathchk - check pathnames for validity and portability */
3 /* Usage: pathchk [-p] path ...
5 For each PATH, print a message if any of these conditions are false:
6 * all existing leading directories in PATH have search (execute) permission
7 * strlen (PATH) <= PATH_MAX
8 * strlen (each_directory_in_PATH) <= NAME_MAX
11 0 All PATH names passed all of the tests.
15 -p Instead of performing length checks on the
16 underlying filesystem, test the length of the
17 pathname and its components against the POSIX.1
18 minimum limits for portability, _POSIX_NAME_MAX
19 and _POSIX_PATH_MAX in 2.9.2. Also check that
20 the pathname contains no character not in the
21 portable filename character set. */
23 /* See Makefile for compilation details. */
26 Copyright (C) 1999-2009 Free Software Foundation, Inc.
28 This file is part of GNU Bash.
29 Bash is free software: you can redistribute it and/or modify
30 it under the terms of the GNU General Public License as published by
31 the Free Software Foundation, either version 3 of the License, or
32 (at your option) any later version.
34 Bash is distributed in the hope that it will be useful,
35 but WITHOUT ANY WARRANTY; without even the implied warranty of
36 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 GNU General Public License for more details.
39 You should have received a copy of the GNU General Public License
40 along with Bash. If not, see <http://www.gnu.org/licenses/>.
45 #include <sys/types.h>
46 #include "posixstat.h"
48 #if defined (HAVE_UNISTD_H)
52 #if defined (HAVE_LIMITS_H)
64 #include "bashgetopt.h"
72 #if !defined (_POSIX_PATH_MAX)
73 # define _POSIX_PATH_MAX 255
75 #if !defined (_POSIX_NAME_MAX)
76 # define _POSIX_NAME_MAX 14
79 /* How do we get PATH_MAX? */
80 #if defined (_POSIX_VERSION) && !defined (PATH_MAX)
81 # define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
84 /* How do we get NAME_MAX? */
85 #if defined (_POSIX_VERSION) && !defined (NAME_MAX)
86 # define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX)
89 #if !defined (PATH_MAX_FOR)
90 # define PATH_MAX_FOR(p) PATH_MAX
93 #if !defined (NAME_MAX_FOR)
94 # define NAME_MAX_FOR(p) NAME_MAX
97 extern char *strerror ();
99 static int validate_path ();
101 pathchk_builtin (list
)
104 int retval
, pflag
, opt
;
106 reset_internal_getopt ();
107 while ((opt
= internal_getopt (list
, "p")) != -1)
127 for (retval
= 0; list
; list
= list
->next
)
128 retval
|= validate_path (list
->word
->word
, pflag
);
130 return (retval
? EXECUTION_FAILURE
: EXECUTION_SUCCESS
);
133 char *pathchk_doc
[] = {
134 "Check pathnames for validity.",
136 "Check each pathname argument for validity (i.e., it may be used to",
137 "create or access a file without casuing syntax errors) and portability",
138 "(i.e., no filename truncation will result). If the `-p' option is",
139 "supplied, more extensive portability checks are performed.",
143 /* The standard structure describing a builtin command. bash keeps an array
144 of these structures. */
145 struct builtin pathchk_struct
= {
146 "pathchk", /* builtin name */
147 pathchk_builtin
, /* function implementing the builtin */
148 BUILTIN_ENABLED
, /* initial flags for builtin */
149 pathchk_doc
, /* array of long documentation strings. */
150 "pathchk [-p] pathname ...", /* usage synopsis */
151 0 /* reserved for internal use */
154 /* The remainder of this file is stolen shamelessly from `pathchk.c' in
155 the sh-utils-1.12 distribution, by
157 David MacKenzie <djm@gnu.ai.mit.edu>
158 and Jim Meyering <meyering@cs.utexas.edu> */
160 /* Each element is nonzero if the corresponding ASCII character is
161 in the POSIX portable character set, and zero if it is not.
162 In addition, the entry for `/' is nonzero to simplify checking. */
163 static char const portable_chars
[256] =
165 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
166 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
167 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
168 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
169 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
170 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
171 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
172 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
177 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
179 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
183 /* If PATH contains only portable characters, return 1, else 0. */
186 portable_chars_only (path
)
191 for (p
= path
; *p
; ++p
)
192 if (portable_chars
[(const unsigned char) *p
] == 0)
194 builtin_error ("path `%s' contains nonportable character `%c'", path
, *p
);
200 /* On some systems, stat can return EINTR. */
203 # define SAFE_STAT(name, buf) stat (name, buf)
205 # define SAFE_STAT(name, buf) safe_stat (name, buf)
207 safe_stat (name
, buf
)
214 ret
= stat (name
, buf
);
215 while (ret
< 0 && errno
== EINTR
);
221 /* Return 1 if PATH is a usable leading directory, 0 if not,
222 2 if it doesn't exist. */
230 if (SAFE_STAT (path
, &stats
))
233 if (!S_ISDIR (stats
.st_mode
))
235 builtin_error ("`%s' is not a directory", path
);
239 /* Use access to test for search permission because
240 testing permission bits of st_mode can lose with new
241 access control mechanisms. Of course, access loses if you're
243 if (access (path
, X_OK
) != 0)
246 builtin_error ("directory `%s' is not searchable", path
);
248 builtin_error ("%s: %s", path
, strerror (errno
));
259 return (savestring (s
));
263 strlen (PATH) <= PATH_MAX
264 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
266 If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
267 _POSIX_NAME_MAX instead, and make sure that PATH contains no
268 characters not in the POSIX portable filename character set, which
269 consists of A-Z, a-z, 0-9, ., _, -.
271 Make sure that all leading directories along PATH that exist have
274 Return 0 if all of these tests are successful, 1 if any fail. */
277 validate_path (path
, portability
)
282 int last_elem
; /* Nonzero if checking last element of path. */
283 int exists
; /* 2 if the path element exists. */
285 char *parent
; /* Last existing leading directory so far. */
287 if (portability
&& !portable_chars_only (path
))
294 /* Suppress `used before initialized' warning. */
298 /* Figure out the parent of the first element in PATH. */
299 parent
= xstrdup (*path
== '/' ? "/" : ".");
306 int length
; /* Length of partial path being checked. */
307 char *start
; /* Start of path element being checked. */
309 /* Find the end of this element of the path.
310 Then chop off the rest of the path after this element. */
311 while (*slash
== '/')
314 slash
= strchr (slash
, '/');
320 slash
= strchr (start
, '\0');
325 exists
= dir_ok (path
);
333 length
= slash
- start
;
334 /* Since we know that `parent' is a directory, it's ok to call
335 pathconf with it as the argument. (If `parent' isn't a directory
336 or doesn't exist, the behavior of pathconf is undefined.)
337 But if `parent' is a directory and is on a remote file system,
338 it's likely that pathconf can't give us a reasonable value
339 and will return -1. (NFS and tempfs are not POSIX . . .)
340 In that case, we have no choice but to assume the pessimal
342 name_max
= portability
? _POSIX_NAME_MAX
: NAME_MAX_FOR (parent
);
344 name_max
= _POSIX_NAME_MAX
;
345 if (length
> name_max
)
347 builtin_error ("name `%s' has length %d; exceeds limit of %d",
348 start
, length
, name_max
);
359 parent
= xstrdup (path
);
365 /* `parent' is now the last existing leading directory in the whole path,
366 so it's ok to call pathconf with it as the argument. */
367 path_max
= portability
? _POSIX_PATH_MAX
: PATH_MAX_FOR (parent
);
369 path_max
= _POSIX_PATH_MAX
;
371 if (strlen (path
) > path_max
)
373 builtin_error ("path `%s' has length %d; exceeds limit of %d",
374 path
, strlen (path
), path_max
);