1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-06-06 01:36:28 +02:00
Files
coreutils/src/pathchk.c
T

382 lines
9.9 KiB
C
Raw Normal View History

1992-11-01 05:44:29 +00:00
/* pathchk -- check whether pathnames are valid or portable
Copyright (C) 1991-2002 Free Software Foundation, Inc.
1992-11-01 05:44:29 +00:00
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
1996-03-24 18:33:12 +00:00
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
1992-11-01 05:44:29 +00:00
/* Usage: pathchk [-p] [--portability] path...
For each PATH, print a message if any of these conditions are false:
* all existing leading directories in PATH have search (execute) permission
* strlen (PATH) <= PATH_MAX
* strlen (each_directory_in_PATH) <= NAME_MAX
Exit status:
0 All PATH names passed all of the tests.
1 An error occurred.
Options:
-p, --portability Instead of performing length checks on the
underlying filesystem, test the length of the
pathname and its components against the POSIX
1992-11-01 05:44:29 +00:00
minimum limits for portability, _POSIX_NAME_MAX
and _POSIX_PATH_MAX in 2.9.2. Also check that
1993-10-04 21:20:37 +00:00
the pathname contains no character not in the
1992-11-01 05:44:29 +00:00
portable filename character set.
David MacKenzie <djm@gnu.ai.mit.edu>
and Jim Meyering <meyering@cs.utexas.edu> */
1993-10-12 14:49:11 +00:00
#include <config.h>
1992-11-01 05:44:29 +00:00
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
1993-10-12 01:52:24 +00:00
2001-08-12 18:28:19 +00:00
#include <errno.h>
#ifndef errno
extern int errno;
#endif
1992-11-01 05:44:29 +00:00
#include "system.h"
#include "error.h"
1999-03-06 15:28:54 +00:00
#include "long-options.h"
2000-05-07 14:55:15 +00:00
#include "closeout.h"
1992-11-01 05:44:29 +00:00
1999-03-31 04:16:08 +00:00
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "pathchk"
#define AUTHORS N_ ("David MacKenzie and Jim Meyering")
1999-03-31 04:16:08 +00:00
#if HAVE_PATHCONF
1997-05-01 20:51:16 +00:00
# ifndef PATH_MAX
2001-08-12 18:28:19 +00:00
# define PATH_MAX_FOR(p) pathconf_wrapper ((p), _PC_PATH_MAX)
1997-05-01 20:51:16 +00:00
# endif /* not PATH_MAX */
# ifndef NAME_MAX
2001-08-12 18:28:19 +00:00
# define NAME_MAX_FOR(p) pathconf_wrapper ((p), _PC_NAME_MAX);
1997-05-01 20:51:16 +00:00
# endif /* not NAME_MAX */
1992-11-01 05:44:29 +00:00
#else
1992-11-01 05:44:29 +00:00
1997-05-01 20:51:16 +00:00
# include <sys/param.h>
# ifndef PATH_MAX
# ifdef MAXPATHLEN
# define PATH_MAX MAXPATHLEN
# else /* not MAXPATHLEN */
# define PATH_MAX _POSIX_PATH_MAX
# endif /* not MAXPATHLEN */
# endif /* not PATH_MAX */
# ifndef NAME_MAX
# ifdef MAXNAMLEN
# define NAME_MAX MAXNAMLEN
# else /* not MAXNAMLEN */
# define NAME_MAX _POSIX_NAME_MAX
# endif /* not MAXNAMLEN */
# endif /* not NAME_MAX */
1992-11-01 05:44:29 +00:00
#endif
1992-11-01 05:44:29 +00:00
#ifndef _POSIX_PATH_MAX
1997-05-01 20:51:16 +00:00
# define _POSIX_PATH_MAX 255
1992-11-01 05:44:29 +00:00
#endif
#ifndef _POSIX_NAME_MAX
1997-05-01 20:51:16 +00:00
# define _POSIX_NAME_MAX 14
1992-11-01 05:44:29 +00:00
#endif
#ifndef PATH_MAX_FOR
1997-05-01 20:51:16 +00:00
# define PATH_MAX_FOR(p) PATH_MAX
1992-11-01 05:44:29 +00:00
#endif
#ifndef NAME_MAX_FOR
1997-05-01 20:51:16 +00:00
# define NAME_MAX_FOR(p) NAME_MAX
1992-11-01 05:44:29 +00:00
#endif
1997-12-21 22:30:30 +00:00
static int validate_path PARAMS ((char *path, int portability));
1992-11-01 05:44:29 +00:00
/* The name this program was run with. */
char *program_name;
static struct option const longopts[] =
1992-11-01 05:44:29 +00:00
{
{"portability", no_argument, NULL, 'p'},
1992-11-01 05:44:29 +00:00
{NULL, 0, NULL, 0}
};
2001-08-12 18:28:19 +00:00
/* Distinguish between the cases when pathconf fails and when it reports there
is no limit (the latter is the case for PATH_MAX on the Hurd). When there
is no limit, return LONG_MAX. Otherwise, return pathconf's return value. */
static long int
pathconf_wrapper (const char *filename, int param)
{
long int ret;
errno = 0;
ret = pathconf (filename, param);
if (ret < 0 && errno == 0)
return LONG_MAX;
return ret;
}
1999-01-25 14:29:22 +00:00
void
usage (int status)
{
if (status != 0)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else
{
printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
fputs (_("\
1999-01-25 14:29:22 +00:00
Diagnose unportable constructs in NAME.\n\
\n\
-p, --portability check for all POSIX systems, not only this one\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1999-01-25 14:29:22 +00:00
}
exit (status);
}
int
1996-01-06 11:44:05 +00:00
main (int argc, char **argv)
1992-11-01 05:44:29 +00:00
{
int exit_status = 0;
int check_portability = 0;
int optc;
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
1992-11-01 05:44:29 +00:00
2000-05-07 14:55:15 +00:00
atexit (close_stdout);
1999-03-31 04:16:08 +00:00
parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
AUTHORS, usage);
1999-03-06 15:28:54 +00:00
while ((optc = getopt_long (argc, argv, "p", longopts, NULL)) != -1)
1992-11-01 05:44:29 +00:00
{
switch (optc)
{
1993-10-12 01:52:24 +00:00
case 0:
break;
1992-11-01 05:44:29 +00:00
case 'p':
check_portability = 1;
break;
1993-10-12 01:52:24 +00:00
1992-11-01 05:44:29 +00:00
default:
1993-10-17 03:57:04 +00:00
usage (1);
1992-11-01 05:44:29 +00:00
}
}
if (optind == argc)
1994-10-21 05:25:03 +00:00
{
error (0, 0, _("too few arguments"));
1994-10-21 05:25:03 +00:00
usage (1);
}
1992-11-01 05:44:29 +00:00
for (; optind < argc; ++optind)
exit_status |= validate_path (argv[optind], check_portability);
exit (exit_status);
}
/* Each element is nonzero if the corresponding ASCII character is
in the POSIX portable character set, and zero if it is not.
In addition, the entry for `/' is nonzero to simplify checking. */
1993-10-04 21:20:37 +00:00
static char const portable_chars[256] =
1992-11-01 05:44:29 +00:00
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
1995-11-27 05:34:31 +00:00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1992-11-01 05:44:29 +00:00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* If PATH contains only portable characters, return 1, else 0. */
static int
1996-01-06 11:44:05 +00:00
portable_chars_only (const char *path)
1992-11-01 05:44:29 +00:00
{
1993-10-04 21:20:37 +00:00
const char *p;
1992-11-01 05:44:29 +00:00
for (p = path; *p; ++p)
1993-10-04 21:20:37 +00:00
if (portable_chars[(const unsigned char) *p] == 0)
1992-11-01 05:44:29 +00:00
{
error (0, 0, _("path `%s' contains nonportable character `%c'"),
1992-11-01 05:44:29 +00:00
path, *p);
return 0;
}
return 1;
}
/* Return 1 if PATH is a usable leading directory, 0 if not,
2 if it doesn't exist. */
static int
1996-01-06 11:44:05 +00:00
dir_ok (const char *path)
1992-11-01 05:44:29 +00:00
{
struct stat stats;
1995-05-13 02:31:46 +00:00
if (stat (path, &stats))
1992-11-01 05:44:29 +00:00
return 2;
if (!S_ISDIR (stats.st_mode))
{
error (0, 0, _("`%s' is not a directory"), path);
1992-11-01 05:44:29 +00:00
return 0;
}
/* Use access to test for search permission because
testing permission bits of st_mode can lose with new
access control mechanisms. Of course, access loses if you're
running setuid. */
if (access (path, X_OK) != 0)
{
if (errno == EACCES)
error (0, 0, _("directory `%s' is not searchable"), path);
1992-11-01 05:44:29 +00:00
else
error (0, errno, "%s", path);
return 0;
}
return 1;
}
/* Make sure that
strlen (PATH) <= PATH_MAX
&& strlen (each-existing-directory-in-PATH) <= NAME_MAX
If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
_POSIX_NAME_MAX instead, and make sure that PATH contains no
characters not in the POSIX portable filename character set, which
consists of A-Z, a-z, 0-9, ., _, -.
Make sure that all leading directories along PATH that exist have
`x' permission.
Return 0 if all of these tests are successful, 1 if any fail. */
static int
1996-01-06 11:44:05 +00:00
validate_path (char *path, int portability)
1992-11-01 05:44:29 +00:00
{
2001-08-12 18:28:19 +00:00
long int path_max;
1992-11-01 05:44:29 +00:00
int last_elem; /* Nonzero if checking last element of path. */
int exists IF_LINT (= 0); /* 2 if the path element exists. */
1992-11-01 05:44:29 +00:00
char *slash;
char *parent; /* Last existing leading directory so far. */
if (portability && !portable_chars_only (path))
return 1;
if (*path == '\0')
return 0;
/* Figure out the parent of the first element in PATH. */
parent = xstrdup (*path == '/' ? "/" : ".");
slash = path;
last_elem = 0;
while (1)
{
2001-08-12 18:28:19 +00:00
long int name_max;
long int length; /* Length of partial path being checked. */
1992-11-01 05:44:29 +00:00
char *start; /* Start of path element being checked. */
/* Find the end of this element of the path.
Then chop off the rest of the path after this element. */
while (*slash == '/')
slash++;
start = slash;
1994-12-04 21:15:46 +00:00
slash = strchr (slash, '/');
1992-11-01 05:44:29 +00:00
if (slash != NULL)
*slash = '\0';
else
{
last_elem = 1;
1994-12-04 21:15:46 +00:00
slash = strchr (start, '\0');
1992-11-01 05:44:29 +00:00
}
if (!last_elem)
{
exists = dir_ok (path);
if (exists == 0)
1992-11-01 05:44:29 +00:00
{
free (parent);
return 1;
}
}
length = slash - start;
/* Since we know that `parent' is a directory, it's ok to call
pathconf with it as the argument. (If `parent' isn't a directory
or doesn't exist, the behavior of pathconf is undefined.)
But if `parent' is a directory and is on a remote file system,
it's likely that pathconf can't give us a reasonable value
and will return -1. (NFS and tempfs are not POSIX . . .)
In that case, we have no choice but to assume the pessimal
POSIX minimums. */
name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
if (name_max < 0)
name_max = _POSIX_NAME_MAX;
if (length > name_max)
{
2001-08-12 18:28:19 +00:00
error (0, 0, _("name `%s' has length %ld; exceeds limit of %ld"),
1992-11-01 05:44:29 +00:00
start, length, name_max);
free (parent);
return 1;
}
if (last_elem)
break;
if (exists == 1)
{
free (parent);
parent = xstrdup (path);
}
*slash++ = '/';
}
/* `parent' is now the last existing leading directory in the whole path,
so it's ok to call pathconf with it as the argument. */
path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
if (path_max < 0)
path_max = _POSIX_PATH_MAX;
free (parent);
if (strlen (path) > (size_t) path_max)
1992-11-01 05:44:29 +00:00
{
2001-08-12 18:28:19 +00:00
error (0, 0, _("path `%s' has length %d; exceeds limit of %ld"),
1992-11-01 05:44:29 +00:00
path, strlen (path), path_max);
return 1;
}
return 0;
}