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 }