LCOV - code coverage report
Current view: top level - src - pathchk.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 99 139 71.2 %
Date: 2018-01-30 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* pathchk -- check whether file names are valid or portable
       2             :    Copyright (C) 1991-2007 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : #include <config.h>
      18             : #include <stdio.h>
      19             : #include <getopt.h>
      20             : #include <sys/types.h>
      21             : #if HAVE_WCHAR_H
      22             : # include <wchar.h>
      23             : #endif
      24             : 
      25             : #include "system.h"
      26             : #include "error.h"
      27             : #include "quote.h"
      28             : #include "quotearg.h"
      29             : 
      30             : #if ! (HAVE_MBRLEN && HAVE_MBSTATE_T)
      31             : # define mbrlen(s, n, ps) 1
      32             : # define mbstate_t int
      33             : #endif
      34             : 
      35             : /* The official name of this program (e.g., no `g' prefix).  */
      36             : #define PROGRAM_NAME "pathchk"
      37             : 
      38             : #define AUTHORS "Paul Eggert", "David MacKenzie", "Jim Meyering"
      39             : 
      40             : #ifndef _POSIX_PATH_MAX
      41             : # define _POSIX_PATH_MAX 256
      42             : #endif
      43             : #ifndef _POSIX_NAME_MAX
      44             : # define _POSIX_NAME_MAX 14
      45             : #endif
      46             : 
      47             : #ifdef _XOPEN_NAME_MAX
      48             : # define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
      49             : #else
      50             : # define NAME_MAX_MINIMUM _POSIX_NAME_MAX
      51             : #endif
      52             : #ifdef _XOPEN_PATH_MAX
      53             : # define PATH_MAX_MINIMUM _XOPEN_PATH_MAX
      54             : #else
      55             : # define PATH_MAX_MINIMUM _POSIX_PATH_MAX
      56             : #endif
      57             : 
      58             : #if ! (HAVE_PATHCONF && defined _PC_NAME_MAX && defined _PC_PATH_MAX)
      59             : # ifndef _PC_NAME_MAX
      60             : #  define _PC_NAME_MAX 0
      61             : #  define _PC_PATH_MAX 1
      62             : # endif
      63             : # ifndef pathconf
      64             : #  define pathconf(file, flag) \
      65             :      (flag == _PC_NAME_MAX ? NAME_MAX_MINIMUM : PATH_MAX_MINIMUM)
      66             : # endif
      67             : #endif
      68             : 
      69             : static bool validate_file_name (char *, bool, bool);
      70             : 
      71             : /* The name this program was run with. */
      72             : char *program_name;
      73             : 
      74             : /* For long options that have no equivalent short option, use a
      75             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
      76             : enum
      77             : {
      78             :   PORTABILITY_OPTION = CHAR_MAX + 1
      79             : };
      80             : 
      81             : static struct option const longopts[] =
      82             : {
      83             :   {"portability", no_argument, NULL, PORTABILITY_OPTION},
      84             :   {GETOPT_HELP_OPTION_DECL},
      85             :   {GETOPT_VERSION_OPTION_DECL},
      86             :   {NULL, 0, NULL, 0}
      87             : };
      88             : 
      89             : void
      90          10 : usage (int status)
      91             : {
      92          10 :   if (status != EXIT_SUCCESS)
      93           8 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      94             :              program_name);
      95             :   else
      96             :     {
      97           2 :       printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
      98           2 :       fputs (_("\
      99             : Diagnose unportable constructs in NAME.\n\
     100             : \n\
     101             :   -p                  check for most POSIX systems\n\
     102             :   -P                  check for empty names and leading \"-\"\n\
     103             :       --portability   check for all POSIX systems (equivalent to -p -P)\n\
     104             : "), stdout);
     105           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     106           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     107           2 :       emit_bug_reporting_address ();
     108             :     }
     109          10 :   exit (status);
     110             : }
     111             : 
     112             : int
     113          48 : main (int argc, char **argv)
     114             : {
     115          48 :   bool ok = true;
     116          48 :   bool check_basic_portability = false;
     117          48 :   bool check_extra_portability = false;
     118             :   int optc;
     119             : 
     120             :   initialize_main (&argc, &argv);
     121          48 :   program_name = argv[0];
     122          48 :   setlocale (LC_ALL, "");
     123             :   bindtextdomain (PACKAGE, LOCALEDIR);
     124             :   textdomain (PACKAGE);
     125             : 
     126          48 :   atexit (close_stdout);
     127             : 
     128         123 :   while ((optc = getopt_long (argc, argv, "+pP", longopts, NULL)) != -1)
     129             :     {
     130          35 :       switch (optc)
     131             :         {
     132           2 :         case PORTABILITY_OPTION:
     133           2 :           check_basic_portability = true;
     134           2 :           check_extra_portability = true;
     135           2 :           break;
     136             : 
     137           8 :         case 'p':
     138           8 :           check_basic_portability = true;
     139           8 :           break;
     140             : 
     141          17 :         case 'P':
     142          17 :           check_extra_portability = true;
     143          17 :           break;
     144             : 
     145           2 :         case_GETOPT_HELP_CHAR;
     146             : 
     147           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     148             : 
     149           5 :         default:
     150           5 :           usage (EXIT_FAILURE);
     151             :         }
     152             :     }
     153             : 
     154          40 :   if (optind == argc)
     155             :     {
     156           3 :       error (0, 0, _("missing operand"));
     157           3 :       usage (EXIT_FAILURE);
     158             :     }
     159             : 
     160          83 :   for (; optind < argc; ++optind)
     161          46 :     ok &= validate_file_name (argv[optind],
     162             :                               check_basic_portability, check_extra_portability);
     163             : 
     164          37 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     165             : }
     166             : 
     167             : /* If FILE contains a component with a leading "-", report an error
     168             :    and return false; otherwise, return true.  */
     169             : 
     170             : static bool
     171          18 : no_leading_hyphen (char const *file)
     172             : {
     173             :   char const *p;
     174             : 
     175          19 :   for (p = file;  (p = strchr (p, '-'));  p++)
     176          17 :     if (p == file || p[-1] == '/')
     177             :       {
     178          16 :         error (0, 0, _("leading `-' in a component of file name %s"),
     179             :                quote (file));
     180          16 :         return false;
     181             :       }
     182             : 
     183           2 :   return true;
     184             : }
     185             : 
     186             : /* If FILE (of length FILELEN) contains only portable characters,
     187             :    return true, else report an error and return false.  */
     188             : 
     189             : static bool
     190           6 : portable_chars_only (char const *file, size_t filelen)
     191             : {
     192           6 :   size_t validlen = strspn (file,
     193             :                             ("/"
     194             :                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     195             :                              "abcdefghijklmnopqrstuvwxyz"
     196             :                              "0123456789._-"));
     197           6 :   char const *invalid = file + validlen;
     198             : 
     199           6 :   if (*invalid)
     200             :     {
     201           5 :       mbstate_t mbstate = { 0, };
     202           5 :       size_t charlen = mbrlen (invalid, filelen - validlen, &mbstate);
     203           5 :       error (0, 0,
     204             :              _("nonportable character %s in file name %s"),
     205             :              quotearg_n_style_mem (1, locale_quoting_style, invalid,
     206             :                                    (charlen <= MB_LEN_MAX ? charlen : 1)),
     207             :              quote_n (0, file));
     208           5 :       return false;
     209             :     }
     210             : 
     211           1 :   return true;
     212             : }
     213             : 
     214             : /* Return the address of the start of the next file name component in F.  */
     215             : 
     216             : static char *
     217          14 : component_start (char *f)
     218             : {
     219          36 :   while (*f == '/')
     220           8 :     f++;
     221          14 :   return f;
     222             : }
     223             : 
     224             : /* Return the size of the file name component F.  F must be nonempty.  */
     225             : 
     226             : static size_t
     227           8 : component_len (char const *f)
     228             : {
     229             :   size_t len;
     230          11 :   for (len = 1; f[len] != '/' && f[len]; len++)
     231           3 :     continue;
     232           8 :   return len;
     233             : }
     234             : 
     235             : /* Make sure that
     236             :    strlen (FILE) <= PATH_MAX
     237             :    && strlen (each-existing-directory-in-FILE) <= NAME_MAX
     238             : 
     239             :    If CHECK_BASIC_PORTABILITY is true, compare against _POSIX_PATH_MAX and
     240             :    _POSIX_NAME_MAX instead, and make sure that FILE contains no
     241             :    characters not in the POSIX portable filename character set, which
     242             :    consists of A-Z, a-z, 0-9, ., _, - (plus / for separators).
     243             : 
     244             :    If CHECK_BASIC_PORTABILITY is false, make sure that all leading directories
     245             :    along FILE that exist are searchable.
     246             : 
     247             :    If CHECK_EXTRA_PORTABILITY is true, check that file name components do not
     248             :    begin with "-".
     249             : 
     250             :    If either CHECK_BASIC_PORTABILITY or CHECK_EXTRA_PORTABILITY is true,
     251             :    check that the file name is not empty.
     252             : 
     253             :    Return true if all of these tests are successful, false if any fail.  */
     254             : 
     255             : static bool
     256          46 : validate_file_name (char *file, bool check_basic_portability,
     257             :                     bool check_extra_portability)
     258             : {
     259          46 :   size_t filelen = strlen (file);
     260             : 
     261             :   /* Start of file name component being checked.  */
     262             :   char *start;
     263             : 
     264             :   /* True if component lengths need to be checked.  */
     265             :   bool check_component_lengths;
     266             : 
     267             :   /* True if the file is known to exist.  */
     268          46 :   bool file_exists = false;
     269             : 
     270          46 :   if (check_extra_portability && ! no_leading_hyphen (file))
     271          16 :     return false;
     272             : 
     273          30 :   if ((check_basic_portability | check_extra_portability)
     274          10 :       && filelen == 0)
     275             :     {
     276             :       /* Fail, since empty names are not portable.  As of
     277             :          2005-01-06 POSIX does not address whether "pathchk -p ''"
     278             :          should (or is allowed to) fail, so this is not a
     279             :          conformance violation.  */
     280           3 :       error (0, 0, _("empty file name"));
     281           3 :       return false;
     282             :     }
     283             : 
     284          27 :   if (check_basic_portability)
     285             :     {
     286           6 :       if (! portable_chars_only (file, filelen))
     287           5 :         return false;
     288             :     }
     289             :   else
     290             :     {
     291             :       /* Check whether a file name component is in a directory that
     292             :          is not searchable, or has some other serious problem.
     293             :          POSIX does not allow "" as a file name, but some non-POSIX
     294             :          hosts do (as an alias for "."), so allow "" if lstat does.  */
     295             : 
     296             :       struct stat st;
     297          21 :       if (lstat (file, &st) == 0)
     298           5 :         file_exists = true;
     299          16 :       else if (errno != ENOENT || filelen == 0)
     300             :         {
     301          11 :           error (0, errno, "%s", file);
     302          11 :           return false;
     303             :         }
     304             :     }
     305             : 
     306          11 :   if (check_basic_portability
     307          10 :       || (! file_exists && PATH_MAX_MINIMUM <= filelen))
     308             :     {
     309             :       size_t maxsize;
     310             : 
     311           1 :       if (check_basic_portability)
     312           1 :         maxsize = _POSIX_PATH_MAX;
     313             :       else
     314             :         {
     315             :           long int size;
     316           0 :           char const *dir = (*file == '/' ? "/" : ".");
     317           0 :           errno = 0;
     318           0 :           size = pathconf (dir, _PC_PATH_MAX);
     319           0 :           if (size < 0 && errno != 0)
     320             :             {
     321           0 :               error (0, errno,
     322             :                      _("%s: unable to determine maximum file name length"),
     323             :                      dir);
     324           0 :               return false;
     325             :             }
     326           0 :           maxsize = MIN (size, SIZE_MAX);
     327             :         }
     328             : 
     329           1 :       if (maxsize <= filelen)
     330             :         {
     331           0 :           unsigned long int len = filelen;
     332           0 :           unsigned long int maxlen = maxsize - 1;
     333           0 :           error (0, 0, _("limit %lu exceeded by length %lu of file name %s"),
     334             :                  maxlen, len, quote (file));
     335           0 :           return false;
     336             :         }
     337             :     }
     338             : 
     339             :   /* Check whether pathconf (..., _PC_NAME_MAX) can be avoided, i.e.,
     340             :      whether all file name components are so short that they are valid
     341             :      in any file system on this platform.  If CHECK_BASIC_PORTABILITY, though,
     342             :      it's more convenient to check component lengths below.  */
     343             : 
     344          11 :   check_component_lengths = check_basic_portability;
     345          11 :   if (! check_component_lengths && ! file_exists)
     346             :     {
     347          17 :       for (start = file; *(start = component_start (start)); )
     348             :         {
     349           7 :           size_t length = component_len (start);
     350             : 
     351           7 :           if (NAME_MAX_MINIMUM < length)
     352             :             {
     353           0 :               check_component_lengths = true;
     354           0 :               break;
     355             :             }
     356             : 
     357           7 :           start += length;
     358             :         }
     359             :     }
     360             : 
     361          11 :   if (check_component_lengths)
     362             :     {
     363             :       /* The limit on file name components for the current component.
     364             :          This defaults to NAME_MAX_MINIMUM, for the sake of non-POSIX
     365             :          systems (NFS, say?) where pathconf fails on "." or "/" with
     366             :          errno == ENOENT.  */
     367           1 :       size_t name_max = NAME_MAX_MINIMUM;
     368             : 
     369             :       /* If nonzero, the known limit on file name components.  */
     370           1 :       size_t known_name_max = (check_basic_portability ? _POSIX_NAME_MAX : 0);
     371             : 
     372           3 :       for (start = file; *(start = component_start (start)); )
     373             :         {
     374             :           size_t length;
     375             : 
     376           1 :           if (known_name_max)
     377           1 :             name_max = known_name_max;
     378             :           else
     379             :             {
     380             :               long int len;
     381           0 :               char const *dir = (start == file ? "." : file);
     382           0 :               char c = *start;
     383           0 :               errno = 0;
     384           0 :               *start = '\0';
     385           0 :               len = pathconf (dir, _PC_NAME_MAX);
     386           0 :               *start = c;
     387           0 :               if (0 <= len)
     388           0 :                 name_max = MIN (len, SIZE_MAX);
     389             :               else
     390           0 :                 switch (errno)
     391             :                   {
     392           0 :                   case 0:
     393             :                     /* There is no limit.  */
     394           0 :                     name_max = SIZE_MAX;
     395           0 :                     break;
     396             : 
     397           0 :                   case ENOENT:
     398             :                     /* DIR does not exist; use its parent's maximum.  */
     399           0 :                     known_name_max = name_max;
     400           0 :                     break;
     401             : 
     402           0 :                   default:
     403           0 :                     *start = '\0';
     404           0 :                     error (0, errno, "%s", dir);
     405           0 :                     *start = c;
     406           0 :                     return false;
     407             :                   }
     408             :             }
     409             : 
     410           1 :           length = component_len (start);
     411             : 
     412           1 :           if (name_max < length)
     413             :             {
     414           0 :               unsigned long int len = length;
     415           0 :               unsigned long int maxlen = name_max;
     416           0 :               char c = start[len];
     417           0 :               start[len] = '\0';
     418           0 :               error (0, 0,
     419             :                      _("limit %lu exceeded by length %lu "
     420             :                        "of file name component %s"),
     421             :                      maxlen, len, quote (start));
     422           0 :               start[len] = c;
     423           0 :               return false;
     424             :             }
     425             : 
     426           1 :           start += length;
     427             :         }
     428             :     }
     429             : 
     430          11 :   return true;
     431             : }

Generated by: LCOV version 1.10