1 /* pathchk -- check whether pathnames are valid or portable
2 Copyright (C) 1991, 1992 Free Software Foundation, Inc.
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 2, or (at your option)
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.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Usage: pathchk [-p] [--portability] path...
20 For each PATH, print a message if any of these conditions are false:
21 * all existing leading directories in PATH have search (execute) permission
22 * strlen (PATH) <= PATH_MAX
23 * strlen (each_directory_in_PATH) <= NAME_MAX
26 0 All PATH names passed all of the tests.
30 -p, --portability Instead of performing length checks on the
31 underlying filesystem, test the length of the
32 pathname and its components against the POSIX.1
33 minimum limits for portability, _POSIX_NAME_MAX
34 and _POSIX_PATH_MAX in 2.9.2. Also check that
35 the pathname contains no characters not in the
36 portable filename character set.
38 David MacKenzie <djm@gnu.ai.mit.edu>
39 and Jim Meyering <meyering@cs.utexas.edu> */
43 #include <sys/types.h>
49 #define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
50 #endif /* not PATH_MAX */
52 #define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX);
53 #endif /* not NAME_MAX */
55 #else /* not _POSIX_VERSION */
57 #include <sys/param.h>
60 #define PATH_MAX MAXPATHLEN
61 #else /* not MAXPATHLEN */
62 #define PATH_MAX _POSIX_PATH_MAX
63 #endif /* not MAXPATHLEN */
64 #endif /* not PATH_MAX */
68 #define NAME_MAX MAXNAMLEN
69 #else /* not MAXNAMLEN */
70 #define NAME_MAX _POSIX_NAME_MAX
71 #endif /* not MAXNAMLEN */
72 #endif /* not NAME_MAX */
74 #endif /* not _POSIX_VERSION */
76 #ifndef _POSIX_PATH_MAX
77 #define _POSIX_PATH_MAX 255
79 #ifndef _POSIX_NAME_MAX
80 #define _POSIX_NAME_MAX 14
84 #define PATH_MAX_FOR(p) PATH_MAX
87 #define NAME_MAX_FOR(p) NAME_MAX
95 /* The name this program was run with. */
98 struct option longopts[] =
100 {"portability", 0, NULL, 'p'},
110 int check_portability = 0;
113 program_name = argv[0];
115 while ((optc = getopt_long (argc, argv, "p", longopts, (int *) 0)) != EOF)
120 check_portability = 1;
130 for (; optind < argc; ++optind)
131 exit_status |= validate_path (argv[optind], check_portability);
136 /* Each element is nonzero if the corresponding ASCII character is
137 in the POSIX portable character set, and zero if it is not.
138 In addition, the entry for `/' is nonzero to simplify checking. */
139 static char portable_chars[] =
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
144 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
145 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
146 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
147 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
148 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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
159 /* If PATH contains only portable characters, return 1, else 0. */
162 portable_chars_only (path)
167 for (p = path; *p; ++p)
168 if (portable_chars[*p] == 0)
170 error (0, 0, "path `%s' contains nonportable character `%c'",
177 /* Return 1 if PATH is a usable leading directory, 0 if not,
178 2 if it doesn't exist. */
186 if (stat (path, &stats))
189 if (!S_ISDIR (stats.st_mode))
191 error (0, 0, "`%s' is not a directory", path);
195 /* Use access to test for search permission because
196 testing permission bits of st_mode can lose with new
197 access control mechanisms. Of course, access loses if you're
199 if (access (path, X_OK) != 0)
202 error (0, 0, "directory `%s' is not searchable", path);
204 error (0, errno, "%s", path);
212 strlen (PATH) <= PATH_MAX
213 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
215 If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
216 _POSIX_NAME_MAX instead, and make sure that PATH contains no
217 characters not in the POSIX portable filename character set, which
218 consists of A-Z, a-z, 0-9, ., _, -.
220 Make sure that all leading directories along PATH that exist have
223 Return 0 if all of these tests are successful, 1 if any fail. */
226 validate_path (path, portability)
231 int last_elem; /* Nonzero if checking last element of path. */
232 int exists; /* 2 if the path element exists. */
234 char *parent; /* Last existing leading directory so far. */
236 if (portability && !portable_chars_only (path))
242 /* Figure out the parent of the first element in PATH. */
243 parent = xstrdup (*path == '/' ? "/" : ".");
250 int length; /* Length of partial path being checked. */
251 char *start; /* Start of path element being checked. */
253 /* Find the end of this element of the path.
254 Then chop off the rest of the path after this element. */
255 while (*slash == '/')
258 slash = index (slash, '/');
264 slash = index (start, '\0');
269 exists = dir_ok (path);
277 length = slash - start;
278 /* Since we know that `parent' is a directory, it's ok to call
279 pathconf with it as the argument. (If `parent' isn't a directory
280 or doesn't exist, the behavior of pathconf is undefined.)
281 But if `parent' is a directory and is on a remote file system,
282 it's likely that pathconf can't give us a reasonable value
283 and will return -1. (NFS and tempfs are not POSIX . . .)
284 In that case, we have no choice but to assume the pessimal
286 name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
288 name_max = _POSIX_NAME_MAX;
289 if (length > name_max)
291 error (0, 0, "name `%s' has length %d; exceeds limit of %d",
292 start, length, name_max);
303 parent = xstrdup (path);
309 /* `parent' is now the last existing leading directory in the whole path,
310 so it's ok to call pathconf with it as the argument. */
311 path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
313 path_max = _POSIX_PATH_MAX;
315 if (strlen (path) > path_max)
317 error (0, 0, "path `%s' has length %d; exceeds limit of %d",
318 path, strlen (path), path_max);
329 Usage: %s [-p] [--portability] path...\n",