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. */
27 #include <sys/types.h>
28 #include "posixstat.h"
30 #if defined (HAVE_UNISTD_H)
34 #if defined (HAVE_LIMITS_H)
46 #include "bashgetopt.h"
53 #if !defined (_POSIX_PATH_MAX)
54 # define _POSIX_PATH_MAX 255
56 #if !defined (_POSIX_NAME_MAX)
57 # define _POSIX_NAME_MAX 14
60 /* How do we get PATH_MAX? */
61 #if defined (_POSIX_VERSION) && !defined (PATH_MAX)
62 # define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
65 /* How do we get NAME_MAX? */
66 #if defined (_POSIX_VERSION) && !defined (NAME_MAX)
67 # define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX)
70 #if !defined (PATH_MAX_FOR)
71 # define PATH_MAX_FOR(p) PATH_MAX
74 #if !defined (NAME_MAX_FOR)
75 # define NAME_MAX_FOR(p) NAME_MAX
78 extern char *strerror ();
80 static int validate_path ();
82 pathchk_builtin (list)
85 int retval, pflag, opt;
87 reset_internal_getopt ();
88 while ((opt = internal_getopt (list, "p")) != -1)
108 for (retval = 0; list; list = list->next)
109 retval |= validate_path (list->word->word, pflag);
111 return (retval ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
114 char *pathchk_doc[] = {
115 "Check each pathname argument for validity (i.e., it may be used to",
116 "create or access a file without casuing syntax errors) and portability",
117 "(i.e., no filename truncation will result). If the `-p' option is",
118 "supplied, more extensive portability checks are performed.",
122 /* The standard structure describing a builtin command. bash keeps an array
123 of these structures. */
124 struct builtin pathchk_struct = {
125 "pathchk", /* builtin name */
126 pathchk_builtin, /* function implementing the builtin */
127 BUILTIN_ENABLED, /* initial flags for builtin */
128 pathchk_doc, /* array of long documentation strings. */
129 "pathchk [-p] pathname ...", /* usage synopsis */
130 0 /* reserved for internal use */
133 /* The remainder of this file is stolen shamelessly from `pathchk.c' in
134 the sh-utils-1.12 distribution, by
136 David MacKenzie <djm@gnu.ai.mit.edu>
137 and Jim Meyering <meyering@cs.utexas.edu> */
139 /* Each element is nonzero if the corresponding ASCII character is
140 in the POSIX portable character set, and zero if it is not.
141 In addition, the entry for `/' is nonzero to simplify checking. */
142 static char const portable_chars[256] =
144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
147 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
148 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
149 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
150 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
151 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
162 /* If PATH contains only portable characters, return 1, else 0. */
165 portable_chars_only (path)
170 for (p = path; *p; ++p)
171 if (portable_chars[(const unsigned char) *p] == 0)
173 error (0, 0, "path `%s' contains nonportable character `%c'",
180 /* On some systems, stat can return EINTR. */
183 # define SAFE_STAT(name, buf) stat (name, buf)
185 # define SAFE_STAT(name, buf) safe_stat (name, buf)
187 safe_stat (name, buf)
194 ret = stat (name, buf);
195 while (ret < 0 && errno == EINTR);
201 /* Return 1 if PATH is a usable leading directory, 0 if not,
202 2 if it doesn't exist. */
210 if (SAFE_STAT (path, &stats))
213 if (!S_ISDIR (stats.st_mode))
215 error (0, 0, "`%s' is not a directory", path);
219 /* Use access to test for search permission because
220 testing permission bits of st_mode can lose with new
221 access control mechanisms. Of course, access loses if you're
223 if (access (path, X_OK) != 0)
226 builtin_error ("directory `%s' is not searchable", path);
228 builtin_error ("%s: %s", path, strerror (errno));
239 return (savestring (s));
243 strlen (PATH) <= PATH_MAX
244 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
246 If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
247 _POSIX_NAME_MAX instead, and make sure that PATH contains no
248 characters not in the POSIX portable filename character set, which
249 consists of A-Z, a-z, 0-9, ., _, -.
251 Make sure that all leading directories along PATH that exist have
254 Return 0 if all of these tests are successful, 1 if any fail. */
257 validate_path (path, portability)
262 int last_elem; /* Nonzero if checking last element of path. */
263 int exists; /* 2 if the path element exists. */
265 char *parent; /* Last existing leading directory so far. */
267 if (portability && !portable_chars_only (path))
274 /* Suppress `used before initialized' warning. */
278 /* Figure out the parent of the first element in PATH. */
279 parent = xstrdup (*path == '/' ? "/" : ".");
286 int length; /* Length of partial path being checked. */
287 char *start; /* Start of path element being checked. */
289 /* Find the end of this element of the path.
290 Then chop off the rest of the path after this element. */
291 while (*slash == '/')
294 slash = strchr (slash, '/');
300 slash = strchr (start, '\0');
305 exists = dir_ok (path);
313 length = slash - start;
314 /* Since we know that `parent' is a directory, it's ok to call
315 pathconf with it as the argument. (If `parent' isn't a directory
316 or doesn't exist, the behavior of pathconf is undefined.)
317 But if `parent' is a directory and is on a remote file system,
318 it's likely that pathconf can't give us a reasonable value
319 and will return -1. (NFS and tempfs are not POSIX . . .)
320 In that case, we have no choice but to assume the pessimal
322 name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
324 name_max = _POSIX_NAME_MAX;
325 if (length > name_max)
327 error (0, 0, "name `%s' has length %d; exceeds limit of %d",
328 start, length, name_max);
339 parent = xstrdup (path);
345 /* `parent' is now the last existing leading directory in the whole path,
346 so it's ok to call pathconf with it as the argument. */
347 path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
349 path_max = _POSIX_PATH_MAX;
351 if (strlen (path) > path_max)
353 error (0, 0, "path `%s' has length %d; exceeds limit of %d",
354 path, strlen (path), path_max);