1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1994 by Sun Microsystems, Inc. 24 * Copyright (c) 2018, Joyent, Inc. 25 */ 26 27 /* 28 * Copyright 1991, 1992 by Mortice Kern Systems Inc. All rights reserved. 29 * 30 * Standards Conformance : 31 * P1003.2/D11.2 32 * 33 */ 34 /* 35 * Original ident string for reference 36 * ident "$Id: pathchk.c,v 1.29 1994/05/24 15:51:19 mark Exp $" 37 */ 38 39 #include <locale.h> 40 #include <libintl.h> 41 #include <limits.h> 42 #include <sys/stat.h> 43 #include <fcntl.h> /* for creat() prototype */ 44 #include <string.h> 45 #include <errno.h> 46 #include <stdlib.h> 47 #include <stdio.h> 48 #include <ctype.h> 49 #include <unistd.h> 50 #include <stdlib.h> 51 52 /* 53 * These are the characters in the portable filename character set defined 54 * in POSIX P1003.2. 55 */ 56 static char portfsset[] = \ 57 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-"; 58 59 60 #ifndef M_FSDELIM 61 #define M_FSDELIM(c) ((c) == '/') 62 #endif 63 64 static char *nametoolong = "%s: component too long.\n"; 65 static char *pathtoolong = "%s: pathname too long.\n"; 66 static char *notsrch = "%s: Not searchable.\n"; 67 static char *badchar = "%s: Nonportable character '%c' (%#02X) found.\n"; 68 static char *badbyte = "%s: Nonportable byte %#02X found.\n"; 69 70 static char *pathconfprob = "pathchk: warning: \ 71 pathconf(\"%s\", %s) returns '%s'. Using %s = %d\n"; 72 73 74 static int printWarnings = 1; 75 76 static int checkpathname(char *, int); 77 static void usage(void); 78 79 /* 80 * mainline for pathchk 81 */ 82 int 83 main(int argc, char **argv) 84 { 85 int c; 86 int errors; 87 int pflag = 0; 88 89 (void) setlocale(LC_ALL, ""); 90 #if !defined(TEXT_DOMAIN) 91 #define TEXT_DOMAIN "SYS_TEST" 92 #endif 93 (void) textdomain(TEXT_DOMAIN); 94 95 96 while ((c = getopt(argc, argv, "pw")) != EOF) { 97 switch (c) { 98 case 'p': 99 pflag = 1; 100 break; 101 102 case 'w': 103 /* turn off warning messages */ 104 printWarnings = 0; 105 break; 106 107 default: 108 usage(); 109 } 110 } 111 112 argv += optind; 113 114 if (*argv == 0) { 115 usage(); 116 /* NOTREACHED */ 117 } 118 119 errors = 0; 120 while (*argv) { 121 errors += checkpathname(*argv, pflag); 122 argv += 1; 123 } 124 125 return (errors); 126 } 127 128 /* 129 * checkPathConf(const char *, int, long *) 130 * 131 * Calls pathconf(), and returns 1 if pathconf failed, zero 132 * otherwise. If pathconf() succeeded, then *valp contains the 133 * value returned 134 */ 135 static int 136 checkPathConf(const char *path, int type, long *valp) 137 { 138 errno = 0; 139 *valp = pathconf(path, type); 140 if ((*valp == -1) && (errno != 0) && (errno != EACCES)) { 141 /* 142 * pathconf() is not supported on some mounted filesystems 143 * (e.g NFS mounts) and pathconf() is known to fail. 144 * So, we print a warning and use the POSIX default values. 145 */ 146 if (type == _PC_PATH_MAX) 147 *valp = _POSIX_PATH_MAX; 148 else 149 *valp = _POSIX_NAME_MAX; 150 151 if (printWarnings) { 152 (void) fprintf(stderr, gettext(pathconfprob), path, 153 type == _PC_PATH_MAX?"_PC_PATH_MAX" : 154 "_PC_NAME_MAX", strerror(errno), 155 type == _PC_PATH_MAX ? "PATH_MAX" : "NAME_MAX", 156 *valp); 157 } 158 } 159 return ((*valp == -1) && (errno != 0)); 160 } 161 162 163 #define UPDATE_LIMITS(buf)\ 164 {\ 165 if (pflag) {\ 166 nameMax = _POSIX_NAME_MAX;\ 167 pathMax = _POSIX_PATH_MAX;\ 168 } else if (checkPathConf((buf), _PC_PATH_MAX, &pathMax) || \ 169 checkPathConf((buf), _PC_NAME_MAX, &nameMax)) {\ 170 (void) fprintf(stderr, gettext(notsrch), buf);\ 171 return (1);\ 172 }\ 173 } 174 175 /* 176 * checkpathname(char *pname) 177 * pathchk a single pathname. 178 */ 179 int 180 checkpathname(char *path, int pflag) 181 { 182 int checkStat; 183 long nameMax; 184 long pathMax; 185 char *scomp; 186 char *ecomp; 187 register char *p; 188 189 p = path; 190 checkStat = 1; 191 192 /* 193 * Get the initial NAME_MAX and PATH_MAX values 194 */ 195 if (M_FSDELIM(*p)) { 196 char buf[2]; 197 198 buf[0] = *p; 199 buf[1] = '\0'; 200 201 UPDATE_LIMITS(buf); 202 } else { 203 /* 204 * This is a relative pathname, initial values 205 * are relative to the current directory 206 */ 207 UPDATE_LIMITS("."); 208 } 209 210 /* 211 * Check to make sure that the pathname doesn't exceed the 212 * current PATH_MAX 213 */ 214 if (pathMax != -1 && strlen(p) > (size_t)pathMax) { 215 (void) fprintf(stderr, gettext(pathtoolong), path); 216 return (1); 217 } 218 219 220 /* 221 * Now spin around checking all the prefixes of 222 * the pathname, until we hit the end of the 223 * argument 224 */ 225 while (*p != '\0') { 226 /* 227 * Find the beginning of the next 228 * component. Assume that 229 * M_FSDELIM('\0') == 0 230 */ 231 while (M_FSDELIM(*p)) 232 p += 1; 233 234 if (*p == '\0') { 235 /* 236 * There were trailing fsdelim chars on 237 * the path provided, so we were 238 * finished, we just didn't know it. 239 */ 240 return (0); 241 } 242 243 scomp = p; 244 245 /* 246 * Find the end of the current component 247 * and check for valid characters in the component 248 */ 249 while (*p != '\0' && !M_FSDELIM(*p)) { 250 /* 251 * for pflag: check for PFCS characters 252 * otherwise assume all characters are valid 253 */ 254 if (pflag && (strchr(portfsset, *p) == 0)) { 255 if (isprint(*p)) { 256 (void) fprintf(stderr, 257 gettext(badchar), path, *p, *p); 258 } else { 259 (void) fprintf(stderr, 260 gettext(badbyte), path, *p); 261 } 262 return (1); 263 } 264 p += 1; 265 } 266 267 ecomp = p; 268 269 /* 270 * Make sure that this component does not exceed 271 * NAME_MAX in the current prefix directory 272 */ 273 if ((nameMax != -1) && (ecomp - scomp > nameMax)) { 274 (void) fprintf(stderr, gettext(nametoolong), scomp); 275 return (1); 276 } else if (!pflag && checkStat) { 277 /* 278 * Perform the extra checks that 279 * are required when not just 280 * checking for portability. 281 */ 282 struct stat sb; 283 char fsdelim; 284 285 fsdelim = *ecomp; 286 *ecomp = '\0'; 287 288 if (stat(path, &sb) == -1) { 289 /* 290 * We error out if an 291 * intermediate component 292 * is a file, when we 293 * were expecting a 294 * directory, or it is an 295 * unsearchable directory. 296 */ 297 if ((errno == ENOTDIR && fsdelim != '\0') || 298 (errno == EACCES)) { 299 (void) fprintf(stderr, gettext(notsrch), 300 path); 301 return (1); 302 } else if (errno == ENOENT) { 303 checkStat = 0; 304 } 305 } else if (S_ISDIR(sb.st_mode)) { 306 /* 307 * If the current prefix is a 308 * directory, then we need to 309 * update the limits for NAME_MAX 310 * for the next component and the suffix. 311 */ 312 if (checkPathConf(path, _PC_NAME_MAX, 313 &nameMax)) { 314 (void) fprintf(stderr, 315 gettext(notsrch), path); 316 return (1); 317 } 318 } 319 320 /* 321 * restore the fsdelim char that we 322 * stomped to produce a prefix. 323 */ 324 *ecomp = fsdelim; 325 } /* if (we need to stat the path) */ 326 } /* while (more of this path to check) */ 327 328 /* 329 * We successfully traversed the whole pathname 330 */ 331 return (0); 332 } 333 334 void 335 usage() 336 { 337 (void) fprintf(stderr, gettext("usage: pathchk [-p] pathname ...")); 338 (void) fprintf(stderr, "\n"); 339 exit(2); 340 } 341