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