197095Stjr/*- 297095Stjr * Copyright (c) 2002 Tim J. Robbins. 397095Stjr * All rights reserved. 497095Stjr * 597095Stjr * Redistribution and use in source and binary forms, with or without 697095Stjr * modification, are permitted provided that the following conditions 797095Stjr * are met: 897095Stjr * 1. Redistributions of source code must retain the above copyright 997095Stjr * notice, this list of conditions and the following disclaimer. 1097095Stjr * 2. Redistributions in binary form must reproduce the above copyright 1197095Stjr * notice, this list of conditions and the following disclaimer in the 1297095Stjr * documentation and/or other materials provided with the distribution. 1397095Stjr * 1497095Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1597095Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1697095Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1797095Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1897095Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1997095Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2097095Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2197095Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2297095Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2397095Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2497095Stjr * SUCH DAMAGE. 2597095Stjr */ 2697095Stjr 2797095Stjr/* 2897095Stjr * pathchk -- check pathnames 2997095Stjr * 3097095Stjr * Check whether files could be created with the names specified on the 3197095Stjr * command line. If -p is specified, check whether the pathname is portable 3297095Stjr * to all POSIX systems. 3397095Stjr */ 3497095Stjr 3597095Stjr#include <sys/cdefs.h> 3697095Stjr__FBSDID("$FreeBSD$"); 3797095Stjr 3897095Stjr#include <sys/types.h> 3997095Stjr#include <sys/stat.h> 4097095Stjr 4197095Stjr#include <err.h> 4297095Stjr#include <errno.h> 4397095Stjr#include <limits.h> 4497095Stjr#include <stdio.h> 4597095Stjr#include <stdlib.h> 4697095Stjr#include <string.h> 4797095Stjr#include <unistd.h> 4897095Stjr 4997095Stjrstatic int check(const char *); 5097095Stjrstatic int portable(const char *); 5197095Stjrstatic void usage(void); 5297095Stjr 5397095Stjrstatic int pflag; /* Perform portability checks */ 54207483Sjillesstatic int Pflag; /* Check for empty paths, leading '-' */ 5597095Stjr 5697095Stjrint 5797095Stjrmain(int argc, char *argv[]) 5897095Stjr{ 5997095Stjr int ch, rval; 6097095Stjr const char *arg; 6197095Stjr 62207483Sjilles while ((ch = getopt(argc, argv, "pP")) > 0) { 6397095Stjr switch (ch) { 6497095Stjr case 'p': 6597095Stjr pflag = 1; 6697095Stjr break; 67207483Sjilles case 'P': 68207483Sjilles Pflag = 1; 69207483Sjilles break; 7097095Stjr default: 7197095Stjr usage(); 7297095Stjr /*NOTREACHED*/ 7397095Stjr } 7497095Stjr } 7597095Stjr argc -= optind; 7697095Stjr argv += optind; 7797095Stjr 7897095Stjr if (argc == 0) 7997095Stjr usage(); 8097095Stjr 8197095Stjr rval = 0; 8297095Stjr while ((arg = *argv++) != NULL) 8397095Stjr rval |= check(arg); 8497095Stjr 8597095Stjr exit(rval); 8697095Stjr} 8797095Stjr 8897095Stjrstatic void 8997095Stjrusage(void) 9097095Stjr{ 9197095Stjr 92146466Sru fprintf(stderr, "usage: pathchk [-p] pathname ...\n"); 9397095Stjr exit(1); 9497095Stjr} 9597095Stjr 9697095Stjrstatic int 9797095Stjrcheck(const char *path) 9897095Stjr{ 9997095Stjr struct stat sb; 10097095Stjr long complen, namemax, pathmax, svnamemax; 10197095Stjr int badch, last; 10297095Stjr char *end, *p, *pathd; 10397095Stjr 10497095Stjr if ((pathd = strdup(path)) == NULL) 10597095Stjr err(1, "strdup"); 10697095Stjr 10797095Stjr p = pathd; 10897095Stjr 109207483Sjilles if (Pflag && *p == '\0') { 110207483Sjilles warnx("%s: empty pathname", path); 111207483Sjilles goto bad; 112207483Sjilles } 113207483Sjilles if ((Pflag || pflag) && (*p == '-' || strstr(p, "/-") != NULL)) { 114207483Sjilles warnx("%s: contains a component starting with '-'", path); 115207483Sjilles goto bad; 116207483Sjilles } 117207483Sjilles 11897095Stjr if (!pflag) { 11997095Stjr errno = 0; 12097095Stjr namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX); 12197095Stjr if (namemax == -1 && errno != 0) 12297095Stjr namemax = NAME_MAX; 12397095Stjr } else 12497095Stjr namemax = _POSIX_NAME_MAX; 12597095Stjr 12697095Stjr for (;;) { 12797095Stjr p += strspn(p, "/"); 12897095Stjr complen = (long)strcspn(p, "/"); 12997095Stjr end = p + complen; 13097095Stjr last = *end == '\0'; 13197095Stjr *end = '\0'; 13297095Stjr 13397095Stjr if (namemax != -1 && complen > namemax) { 13497095Stjr warnx("%s: %s: component too long (limit %ld)", path, 13597095Stjr p, namemax); 13697095Stjr goto bad; 13797095Stjr } 13897095Stjr 13997095Stjr if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) { 14098093Stjr warn("%s: %.*s", path, (int)(strlen(pathd) - 14198093Stjr complen - 1), pathd); 14297095Stjr goto bad; 14397095Stjr } 14497095Stjr 14597095Stjr if (pflag && (badch = portable(p)) >= 0) { 14697095Stjr warnx("%s: %s: component contains non-portable " 14797095Stjr "character `%c'", path, p, badch); 14897095Stjr goto bad; 14997095Stjr } 15097095Stjr 15197095Stjr if (last) 15297095Stjr break; 15397095Stjr 15497095Stjr if (!pflag) { 15597095Stjr errno = 0; 15697095Stjr svnamemax = namemax; 15797095Stjr namemax = pathconf(pathd, _PC_NAME_MAX); 15897095Stjr if (namemax == -1 && errno != 0) 15997095Stjr namemax = svnamemax; 16097095Stjr } 16197095Stjr 16297095Stjr *end = '/'; 16397095Stjr p = end + 1; 16497095Stjr } 16597095Stjr 16697095Stjr if (!pflag) { 16797095Stjr errno = 0; 16897095Stjr pathmax = pathconf(path, _PC_PATH_MAX); 16997095Stjr if (pathmax == -1 && errno != 0) 17097095Stjr pathmax = PATH_MAX; 17197095Stjr } else 17297095Stjr pathmax = _POSIX_PATH_MAX; 173107888Stjr if (pathmax != -1 && strlen(path) >= (size_t)pathmax) { 174107888Stjr warnx("%s: path too long (limit %ld)", path, pathmax - 1); 17597095Stjr goto bad; 17697095Stjr } 17797095Stjr 17897095Stjr free(pathd); 17997095Stjr return (0); 18097095Stjr 18197095Stjrbad: free(pathd); 18297095Stjr return (1); 18397095Stjr} 18497095Stjr 18597095Stjr/* 18697095Stjr * Check whether a path component contains only portable characters. Return 18797095Stjr * the first non-portable character found. 18897095Stjr */ 18997095Stjrstatic int 19097095Stjrportable(const char *path) 19197095Stjr{ 19297095Stjr static const char charset[] = 19397095Stjr "abcdefghijklmnopqrstuvwxyz" 19497095Stjr "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 19597095Stjr "0123456789._-"; 19697095Stjr long s; 19797095Stjr 19897095Stjr s = strspn(path, charset); 19997095Stjr if (path[s] != '\0') 20097095Stjr return (path[s]); 20197095Stjr 20297095Stjr return (-1); 20397095Stjr} 204