(validate_file_name): Give a more descriptive
[platform/upstream/coreutils.git] / src / pathchk.c
1 /* pathchk -- check whether file names are valid or portable
2    Copyright (C) 1991-2004 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 2, or (at your option)
7    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, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #include <config.h>
19 #include <stdio.h>
20 #include <getopt.h>
21 #include <sys/types.h>
22 #if HAVE_WCHAR_H
23 # include <wchar.h>
24 #endif
25
26 #include "system.h"
27 #include "error.h"
28 #include "euidaccess.h"
29 #include "quote.h"
30 #include "quotearg.h"
31
32 #if ! (HAVE_MBRLEN && HAVE_MBSTATE_T)
33 # define mbrlen(s, n, ps) 1
34 # define mbstate_t int
35 #endif
36
37 /* The official name of this program (e.g., no `g' prefix).  */
38 #define PROGRAM_NAME "pathchk"
39
40 #define AUTHORS "Paul Eggert", "David MacKenzie", "Jim Meyering"
41
42 #ifndef _POSIX_PATH_MAX
43 # define _POSIX_PATH_MAX 255
44 #endif
45 #ifndef _POSIX_NAME_MAX
46 # define _POSIX_NAME_MAX 14
47 #endif
48
49 #ifdef _XOPEN_NAME_MAX
50 # define NAME_MAX_MINIMUM _XOPEN_NAME_MAX
51 #else
52 # define NAME_MAX_MINIMUM _POSIX_NAME_MAX
53 #endif
54 #ifdef _XOPEN_PATH_MAX
55 # define PATH_MAX_MINIMUM _XOPEN_PATH_MAX
56 #else
57 # define PATH_MAX_MINIMUM _POSIX_PATH_MAX
58 #endif
59
60 #if ! (HAVE_PATHCONF && defined _PC_NAME_MAX && defined _PC_PATH_MAX)
61 # ifndef _PC_NAME_MAX
62 #  define _PC_NAME_MAX 0
63 #  define _PC_PATH_MAX 1
64 # endif
65 # ifndef pathconf
66 #  define pathconf(file, flag) \
67      (flag == _PC_NAME_MAX ? NAME_MAX_MINIMUM : PATH_MAX_MINIMUM)
68 # endif
69 #endif
70
71 static bool validate_file_name (char *file, bool portability);
72
73 /* The name this program was run with. */
74 char *program_name;
75
76 static struct option const longopts[] =
77 {
78   {"portability", no_argument, NULL, 'p'},
79   {GETOPT_HELP_OPTION_DECL},
80   {GETOPT_VERSION_OPTION_DECL},
81   {NULL, 0, NULL, 0}
82 };
83
84 void
85 usage (int status)
86 {
87   if (status != EXIT_SUCCESS)
88     fprintf (stderr, _("Try `%s --help' for more information.\n"),
89              program_name);
90   else
91     {
92       printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
93       fputs (_("\
94 Diagnose unportable constructs in NAME.\n\
95 \n\
96   -p, --portability   check for all POSIX systems, not only this one\n\
97 "), stdout);
98       fputs (HELP_OPTION_DESCRIPTION, stdout);
99       fputs (VERSION_OPTION_DESCRIPTION, stdout);
100       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
101     }
102   exit (status);
103 }
104
105 int
106 main (int argc, char **argv)
107 {
108   bool ok = true;
109   bool check_portability = false;
110   int optc;
111
112   initialize_main (&argc, &argv);
113   program_name = argv[0];
114   setlocale (LC_ALL, "");
115   bindtextdomain (PACKAGE, LOCALEDIR);
116   textdomain (PACKAGE);
117
118   atexit (close_stdout);
119
120   while ((optc = getopt_long (argc, argv, "+p", longopts, NULL)) != -1)
121     {
122       switch (optc)
123         {
124         case 'p':
125           check_portability = true;
126           break;
127
128         case_GETOPT_HELP_CHAR;
129
130         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
131
132         default:
133           usage (EXIT_FAILURE);
134         }
135     }
136
137   if (optind == argc)
138     {
139       error (0, 0, _("missing operand"));
140       usage (EXIT_FAILURE);
141     }
142
143   for (; optind < argc; ++optind)
144     ok &= validate_file_name (argv[optind], check_portability);
145
146   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
147 }
148
149 /* If FILE (of length FILELEN) contains only portable characters,
150    return true, else report an error and return false.  */
151
152 static bool
153 portable_chars_only (char const *file, size_t filelen)
154 {
155   size_t validlen = strspn (file,
156                             ("/"
157                              "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
158                              "abcdefghijklmnopqrstuvwxyz"
159                              "0123456789._-"));
160   char const *invalid = file + validlen;
161
162   if (*invalid)
163     {
164       mbstate_t mbstate = {0};
165       size_t charlen = mbrlen (invalid, filelen - validlen, &mbstate);
166       error (0, 0,
167              _("nonportable character %s in file name %s"),
168              quotearg_n_style_mem (1, locale_quoting_style, invalid,
169                                    (charlen <= MB_LEN_MAX ? charlen : 1)),
170              quote_n (0, file));
171       return false;
172     }
173
174   return true;
175 }
176
177 /* Return the address of the start of the next file name component in F.  */
178
179 static char *
180 component_start (char *f)
181 {
182   while (*f == '/')
183     f++;
184   return f;
185 }
186
187 /* Return the size of the file name component F.  F must be nonempty.  */
188
189 static size_t
190 component_len (char const *f)
191 {
192   size_t len;
193   for (len = 1; f[len] != '/' && f[len]; len++)
194     continue;
195   return len;
196 }
197
198 /* Make sure that
199    strlen (FILE) <= PATH_MAX
200    && strlen (each-existing-directory-in-FILE) <= NAME_MAX
201
202    If PORTABILITY is true, compare against _POSIX_PATH_MAX and
203    _POSIX_NAME_MAX instead, and make sure that FILE contains no
204    characters not in the POSIX portable filename character set, which
205    consists of A-Z, a-z, 0-9, ., _, - (plus / for separators).
206
207    If PORTABILITY is false, make sure that all leading directories
208    along FILE that exist are searchable.
209
210    Return true if all of these tests are successful, false if any fail.  */
211
212 static bool
213 validate_file_name (char *file, bool portability)
214 {
215   size_t filelen = strlen (file);
216
217   /* Start of file name component being checked.  */
218   char *start;
219
220   /* True if component lengths need to be checked.  */
221   bool check_component_lengths;
222
223   if (portability && ! portable_chars_only (file, filelen))
224     return false;
225
226   if (*file == '\0')
227     return true;
228
229   if (portability || PATH_MAX_MINIMUM <= filelen)
230     {
231       size_t maxsize;
232
233       if (portability)
234         maxsize = _POSIX_PATH_MAX;
235       else
236         {
237           long int size;
238           char const *dir = (*file == '/' ? "/" : ".");
239           errno = 0;
240           size = pathconf (dir, _PC_PATH_MAX);
241           if (size < 0 && errno != 0)
242             {
243               error (0, errno,
244                      _("%s: unable to determine maximum file name length"),
245                      dir);
246               return false;
247             }
248           maxsize = MIN (size, SIZE_MAX);
249         }
250
251       if (maxsize <= filelen)
252         {
253           unsigned long int len = filelen;
254           unsigned long int maxlen = maxsize - 1;
255           error (0, 0, _("limit %lu exceeded by length %lu of file name %s"),
256                  maxlen, len, quote (file));
257           return false;
258         }
259     }
260
261   if (! portability)
262     {
263       /* Check whether a file name component is in a directory that
264          is not searchable, or has some other serious problem.  */
265
266       struct stat st;
267       if (lstat (file, &st) != 0 && errno != ENOENT)
268         {
269           error (0, errno, "%s", file);
270           return false;
271         }
272     }
273
274   /* Check whether pathconf (..., _PC_NAME_MAX) can be avoided, i.e.,
275      whether all file name components are so short that they are valid
276      in any file system on this platform.  If PORTABILITY, though,
277      it's more convenient to check component lengths below.  */
278
279   check_component_lengths = portability;
280   if (! check_component_lengths)
281     {
282       for (start = file; *(start = component_start (start)); )
283         {
284           size_t length = component_len (start);
285
286           if (NAME_MAX_MINIMUM < length)
287             {
288               check_component_lengths = true;
289               break;
290             }
291
292           start += length;
293         }
294     }
295
296   if (check_component_lengths)
297     {
298       /* The limit on file name components for the current component.
299          This defaults to NAME_MAX_MINIMUM, for the sake of non-POSIX
300          systems (NFS, say?) where pathconf fails on "." or "/" with
301          errno == ENOENT.  */
302       size_t name_max = NAME_MAX_MINIMUM;
303
304       /* If nonzero, the known limit on file name components.  */
305       size_t known_name_max = (portability ? _POSIX_NAME_MAX : 0);
306
307       for (start = file; *(start = component_start (start)); )
308         {
309           size_t length;
310
311           if (known_name_max)
312             name_max = known_name_max;
313           else
314             {
315               long int len;
316               char const *dir = (start == file ? "." : file);
317               char c = *start;
318               errno = 0;
319               *start = '\0';
320               len = pathconf (dir, _PC_NAME_MAX);
321               *start = c;
322               if (0 <= len)
323                 name_max = MIN (len, SIZE_MAX);
324               else
325                 switch (errno)
326                   {
327                   case 0:
328                     /* There is no limit.  */
329                     name_max = SIZE_MAX;
330                     break;
331
332                   case ENOENT:
333                     /* DIR does not exist; use its parent's maximum.  */
334                     known_name_max = name_max;
335                     break;
336
337                   default:
338                     *start = '\0';
339                     error (0, errno, "%s", dir);
340                     *start = c;
341                     return false;
342                   }
343             }
344
345           length = component_len (start);
346
347           if (name_max < length)
348             {
349               unsigned long int len = length;
350               unsigned long int maxlen = name_max;
351               char c = start[len];
352               start[len] = '\0';
353               error (0, 0,
354                      _("limit %lu exceeded by length %lu "
355                        "of file name component %s"),
356                      maxlen, len, quote (start));
357               start[len] = c;
358               return false;
359             }
360
361           start += length;
362         }
363     }
364
365   return true;
366 }