pathchk.c revision 107856
1/*-
2 * Copyright (c) 2002 Tim J. Robbins.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * pathchk -- check pathnames
29 *
30 * Check whether files could be created with the names specified on the
31 * command line. If -p is specified, check whether the pathname is portable
32 * to all POSIX systems.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/usr.bin/pathchk/pathchk.c 107856 2002-12-14 11:44:54Z tjr $");
37
38#include <sys/types.h>
39#include <sys/stat.h>
40
41#include <err.h>
42#include <errno.h>
43#include <limits.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49static int	 check(const char *);
50static int	 portable(const char *);
51static void	 usage(void);
52
53static int	 pflag;			/* Perform portability checks */
54
55int
56main(int argc, char *argv[])
57{
58	int ch, rval;
59	const char *arg;
60
61	while ((ch = getopt(argc, argv, "p")) > 0) {
62		switch (ch) {
63		case 'p':
64			pflag = 1;
65			break;
66		default:
67			usage();
68			/*NOTREACHED*/
69		}
70	}
71	argc -= optind;
72	argv += optind;
73
74	if (argc == 0)
75		usage();
76
77	rval = 0;
78	while ((arg = *argv++) != NULL)
79		rval |= check(arg);
80
81	exit(rval);
82}
83
84static void
85usage(void)
86{
87
88	fprintf(stderr, "usage: pathchk [-p] pathname...\n");
89	exit(1);
90}
91
92static int
93check(const char *path)
94{
95	struct stat sb;
96	long complen, namemax, pathmax, svnamemax;
97	int badch, last;
98	char *end, *p, *pathd;
99
100	if ((pathd = strdup(path)) == NULL)
101		err(1, "strdup");
102
103	p = pathd;
104
105	if (!pflag) {
106		errno = 0;
107		namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX);
108		if (namemax == -1 && errno != 0)
109			namemax = NAME_MAX;
110	} else
111		namemax = _POSIX_NAME_MAX;
112
113	for (;;) {
114		p += strspn(p, "/");
115		complen = (long)strcspn(p, "/");
116		end = p + complen;
117		last = *end == '\0';
118		*end = '\0';
119
120		if (namemax != -1 && complen > namemax) {
121			warnx("%s: %s: component too long (limit %ld)", path,
122			    p, namemax);
123			goto bad;
124		}
125
126		if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) {
127			warn("%s: %.*s", path, (int)(strlen(pathd) -
128			    complen - 1), pathd);
129			goto bad;
130		}
131
132		if (pflag && (badch = portable(p)) >= 0) {
133			warnx("%s: %s: component contains non-portable "
134			    "character `%c'", path, p, badch);
135			goto bad;
136		}
137
138		if (last)
139			break;
140
141		if (!pflag) {
142			errno = 0;
143			svnamemax = namemax;
144			namemax = pathconf(pathd, _PC_NAME_MAX);
145			if (namemax == -1 && errno != 0)
146				namemax = svnamemax;
147		}
148
149		*end = '/';
150		p = end + 1;
151	}
152
153	if (!pflag) {
154		errno = 0;
155		pathmax = pathconf(path, _PC_PATH_MAX);
156		if (pathmax == -1 && errno != 0)
157			pathmax = PATH_MAX;
158	} else
159		pathmax = _POSIX_PATH_MAX;
160	/* PATH_MAX includes space for the trailing null byte. */
161	pathmax--;
162	if (pathmax != -1 && strlen(path) > (size_t)pathmax) {
163		warnx("%s: path too long (limit %ld)", path, pathmax);
164		goto bad;
165	}
166
167	free(pathd);
168	return (0);
169
170bad:	free(pathd);
171	return (1);
172}
173
174/*
175 * Check whether a path component contains only portable characters. Return
176 * the first non-portable character found.
177 */
178static int
179portable(const char *path)
180{
181	static const char charset[] =
182	    "abcdefghijklmnopqrstuvwxyz"
183	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
184	    "0123456789._-";
185	long s;
186
187	if (*path == '-')
188		return (*path);
189
190	s = strspn(path, charset);
191	if (path[s] != '\0')
192		return (path[s]);
193
194	return (-1);
195}
196