1 /* pathchk -- check whether pathnames are valid or portable
2 Copyright (C) 1991-2004 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 Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 file system, test the length of the
32 pathname and its components against the POSIX
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 character not in the
36 portable filename character set.
38 David MacKenzie <djm@gnu.ai.mit.edu>
39 and Jim Meyering <meyering@cs.utexas.edu> */
44 #include <sys/types.h>
48 #include "euidaccess.h"
50 /* The official name of this program (e.g., no `g' prefix). */
51 #define PROGRAM_NAME "pathchk"
53 #define AUTHORS "David MacKenzie", "Jim Meyering"
55 #define NEED_PATHCONF_WRAPPER 0
58 # define PATH_MAX_FOR(p) pathconf_wrapper ((p), _PC_PATH_MAX)
59 # define NEED_PATHCONF_WRAPPER 1
60 # endif /* not PATH_MAX */
62 # define NAME_MAX_FOR(p) pathconf_wrapper ((p), _PC_NAME_MAX);
63 # undef NEED_PATHCONF_WRAPPER
64 # define NEED_PATHCONF_WRAPPER 1
65 # endif /* not NAME_MAX */
69 # include <sys/param.h>
72 # define PATH_MAX MAXPATHLEN
73 # else /* not MAXPATHLEN */
74 # define PATH_MAX _POSIX_PATH_MAX
75 # endif /* not MAXPATHLEN */
76 # endif /* not PATH_MAX */
80 # define NAME_MAX MAXNAMLEN
81 # else /* not MAXNAMLEN */
82 # define NAME_MAX _POSIX_NAME_MAX
83 # endif /* not MAXNAMLEN */
84 # endif /* not NAME_MAX */
88 #ifndef _POSIX_PATH_MAX
89 # define _POSIX_PATH_MAX 255
91 #ifndef _POSIX_NAME_MAX
92 # define _POSIX_NAME_MAX 14
96 # define PATH_MAX_FOR(p) PATH_MAX
99 # define NAME_MAX_FOR(p) NAME_MAX
102 static bool validate_path (char *path, bool portability);
104 /* The name this program was run with. */
107 static struct option const longopts[] =
109 {"portability", no_argument, NULL, 'p'},
110 {GETOPT_HELP_OPTION_DECL},
111 {GETOPT_VERSION_OPTION_DECL},
115 #if NEED_PATHCONF_WRAPPER
116 /* Distinguish between the cases when pathconf fails and when it reports there
117 is no limit (the latter is the case for PATH_MAX on the Hurd). When there
118 is no limit, return LONG_MAX. Otherwise, return pathconf's return value. */
121 pathconf_wrapper (const char *filename, int param)
126 ret = pathconf (filename, param);
127 if (ret < 0 && errno == 0)
137 if (status != EXIT_SUCCESS)
138 fprintf (stderr, _("Try `%s --help' for more information.\n"),
142 printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
144 Diagnose unportable constructs in NAME.\n\
146 -p, --portability check for all POSIX systems, not only this one\n\
148 fputs (HELP_OPTION_DESCRIPTION, stdout);
149 fputs (VERSION_OPTION_DESCRIPTION, stdout);
150 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
156 main (int argc, char **argv)
159 bool check_portability = false;
162 initialize_main (&argc, &argv);
163 program_name = argv[0];
164 setlocale (LC_ALL, "");
165 bindtextdomain (PACKAGE, LOCALEDIR);
166 textdomain (PACKAGE);
168 atexit (close_stdout);
170 while ((optc = getopt_long (argc, argv, "+p", longopts, NULL)) != -1)
175 check_portability = true;
178 case_GETOPT_HELP_CHAR;
180 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
183 usage (EXIT_FAILURE);
189 error (0, 0, _("missing operand"));
190 usage (EXIT_FAILURE);
193 for (; optind < argc; ++optind)
194 ok &= validate_path (argv[optind], check_portability);
196 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
199 /* Each element is nonzero if the corresponding ASCII character is
200 in the POSIX portable character set, and zero if it is not.
201 In addition, the entry for `/' is nonzero to simplify checking. */
202 static char const portable_chars[256] =
204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
207 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
208 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
209 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
210 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
211 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
214 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
215 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
216 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
217 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
222 /* If PATH contains only portable characters, return true, else false. */
225 portable_chars_only (const char *path)
229 for (p = path; *p; ++p)
230 if (portable_chars[to_uchar (*p)] == 0)
232 error (0, 0, _("path `%s' contains nonportable character `%c'"),
239 /* Return 1 if PATH is a usable leading directory, 0 if not,
240 2 if it doesn't exist. */
243 dir_ok (const char *path)
247 if (stat (path, &stats))
250 if (!S_ISDIR (stats.st_mode))
252 error (0, 0, _("`%s' is not a directory"), path);
256 /* Use access to test for search permission because
257 testing permission bits of st_mode can lose with new
258 access control mechanisms. */
259 if (euidaccess (path, X_OK) != 0)
262 error (0, 0, _("directory `%s' is not searchable"), path);
264 error (0, errno, "%s", path);
272 strlen (PATH) <= PATH_MAX
273 && strlen (each-existing-directory-in-PATH) <= NAME_MAX
275 If PORTABILITY is true, compare against _POSIX_PATH_MAX and
276 _POSIX_NAME_MAX instead, and make sure that PATH contains no
277 characters not in the POSIX portable filename character set, which
278 consists of A-Z, a-z, 0-9, ., _, -.
280 Make sure that all leading directories along PATH that exist have
283 Return true if all of these tests are successful, false if any fail. */
286 validate_path (char *path, bool portability)
289 int last_elem; /* Nonzero if checking last element of path. */
290 int exists IF_LINT (= 0); /* 2 if the path element exists. */
292 char *parent; /* Last existing leading directory so far. */
294 if (portability && !portable_chars_only (path))
300 /* Figure out the parent of the first element in PATH. */
301 parent = xstrdup (*path == '/' ? "/" : ".");
308 long int length; /* Length of partial path being checked. */
309 char *start; /* Start of path element being checked. */
311 /* Find the end of this element of the path.
312 Then chop off the rest of the path after this element. */
313 while (*slash == '/')
316 slash = strchr (slash, '/');
322 slash = strchr (start, '\0');
327 exists = dir_ok (path);
335 length = slash - start;
336 /* Since we know that `parent' is a directory, it's ok to call
337 pathconf with it as the argument. (If `parent' isn't a directory
338 or doesn't exist, the behavior of pathconf is undefined.)
339 But if `parent' is a directory and is on a remote file system,
340 it's likely that pathconf can't give us a reasonable value
341 and will return -1. (NFS and tempfs are not POSIX . . .)
342 In that case, we have no choice but to assume the pessimal
344 name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
346 name_max = _POSIX_NAME_MAX;
347 if (length > name_max)
349 error (0, 0, _("name `%s' has length %ld; exceeds limit of %ld"),
350 start, length, name_max);
361 parent = xstrdup (path);
367 /* `parent' is now the last existing leading directory in the whole path,
368 so it's ok to call pathconf with it as the argument. */
369 path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
371 path_max = _POSIX_PATH_MAX;
373 if (strlen (path) > (size_t) path_max)
375 error (0, 0, _("path `%s' has length %lu; exceeds limit of %ld"),
376 path, (unsigned long int) strlen (path), path_max);