1 /* df - summarize free file system space
2    Copyright (C) 1991-2023 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 <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
18    --human-readable option added by lm@sgi.com.
19    --si and large file support added by eggert@twinsun.com.  */
20 
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <getopt.h>
25 #include <c-ctype.h>
26 #include <uchar.h>
27 
28 #include "system.h"
29 #include "assure.h"
30 #include "canonicalize.h"
31 #include "fsusage.h"
32 #include "human.h"
33 #include "mbswidth.h"
34 #include "mountlist.h"
35 #include "quote.h"
36 #include "find-mount-point.h"
37 #include "hash.h"
38 #include "xstrtol-error.h"
39 
40 /* The official name of this program (e.g., no 'g' prefix).  */
41 #define PROGRAM_NAME "df"
42 
43 #define AUTHORS \
44   proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
45   proper_name ("David MacKenzie"), \
46   proper_name ("Paul Eggert")
47 
48 struct devlist
49 {
50   dev_t dev_num;
51   struct mount_entry *me;
52   struct devlist *next;
53   struct devlist *seen_last; /* valid for hashed devlist entries only */
54 };
55 
56 /* Filled with device numbers of examined file systems to avoid
57    duplicates in output.  */
58 static Hash_table *devlist_table;
59 
60 /* If true, show even file systems with zero size or
61    uninteresting types.  */
62 static bool show_all_fs;
63 
64 /* If true, show only local file systems.  */
65 static bool show_local_fs;
66 
67 /* If true, output data for each file system corresponding to a
68    command line argument -- even if it's a dummy (automounter) entry.  */
69 static bool show_listed_fs;
70 
71 /* Human-readable options for output.  */
72 static int human_output_opts;
73 
74 /* The units to use when printing sizes.  */
75 static uintmax_t output_block_size;
76 
77 /* True if a file system has been processed for output.  */
78 static bool file_systems_processed;
79 
80 /* If true, invoke the 'sync' system call before getting any usage data.
81    Using this option can make df very slow, especially with many or very
82    busy file systems.  This may make a difference on some systems --
83    SunOS 4.1.3, for one.  It is *not* necessary on GNU/Linux.  */
84 static bool require_sync;
85 
86 /* Desired exit status.  */
87 static int exit_status;
88 
89 /* A file system type to display.  */
90 
91 struct fs_type_list
92 {
93   char *fs_name;
94   struct fs_type_list *fs_next;
95 };
96 
97 /* Linked list of file system types to display.
98    If 'fs_select_list' is null, list all types.
99    This table is generated dynamically from command-line options,
100    rather than hardcoding into the program what it thinks are the
101    valid file system types; let the user specify any file system type
102    they want to, and if there are any file systems of that type, they
103    will be shown.
104 
105    Some file system types:
106    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
107 
108 static struct fs_type_list *fs_select_list;
109 
110 /* Linked list of file system types to omit.
111    If the list is empty, don't exclude any types.  */
112 
113 static struct fs_type_list *fs_exclude_list;
114 
115 /* Linked list of mounted file systems.  */
116 static struct mount_entry *mount_list;
117 
118 /* If true, print file system type as well.  */
119 static bool print_type;
120 
121 /* If true, print a grand total at the end.  */
122 static bool print_grand_total;
123 
124 /* Grand total data.  */
125 static struct fs_usage grand_fsu;
126 
127 /* Display modes.  */
128 static enum
129 {
130   DEFAULT_MODE,
131   INODES_MODE,
132   HUMAN_MODE,
133   POSIX_MODE,
134   OUTPUT_MODE
135 } header_mode = DEFAULT_MODE;
136 
137 /* Displayable fields.  */
138 typedef enum
139 {
140   SOURCE_FIELD, /* file system */
141   FSTYPE_FIELD, /* FS type */
142   SIZE_FIELD,   /* FS size */
143   USED_FIELD,   /* FS size used  */
144   AVAIL_FIELD,  /* FS size available */
145   PCENT_FIELD,  /* percent used */
146   ITOTAL_FIELD, /* inode total */
147   IUSED_FIELD,  /* inodes used */
148   IAVAIL_FIELD, /* inodes available */
149   IPCENT_FIELD, /* inodes used in percent */
150   TARGET_FIELD, /* mount point */
151   FILE_FIELD,   /* specified file name */
152   INVALID_FIELD /* validation marker */
153 } display_field_t;
154 
155 /* Flag if a field contains a block, an inode or another value.  */
156 typedef enum
157 {
158   BLOCK_FLD, /* Block values field */
159   INODE_FLD, /* Inode values field */
160   OTHER_FLD  /* Neutral field, e.g. target */
161 } field_type_t;
162 
163 /* Attributes of a display field.  */
164 struct field_data_t
165 {
166   display_field_t field;
167   char const *arg;
168   field_type_t field_type;
169   char const *caption;/* nullptr means use default header of this field.  */
170   int width;          /* Auto adjusted (up) widths used to align columns.  */
171   bool align_right;   /* Whether to right-align columns, not left-align.  */
172   bool used;
173 };
174 
175 /* Header strings, minimum width and alignment for the above fields.  */
176 static struct field_data_t field_data[] = {
177   [SOURCE_FIELD] = { SOURCE_FIELD,
178     "source", OTHER_FLD, N_("Filesystem"), 14, false,  false },
179 
180   [FSTYPE_FIELD] = { FSTYPE_FIELD,
181     "fstype", OTHER_FLD, N_("Type"),        4, false,  false },
182 
183   [SIZE_FIELD] = { SIZE_FIELD,
184     "size",   BLOCK_FLD, N_("blocks"),      5, true, false },
185 
186   [USED_FIELD] = { USED_FIELD,
187     "used",   BLOCK_FLD, N_("Used"),        5, true, false },
188 
189   [AVAIL_FIELD] = { AVAIL_FIELD,
190     "avail",  BLOCK_FLD, N_("Available"),   5, true, false },
191 
192   [PCENT_FIELD] = { PCENT_FIELD,
193     "pcent",  BLOCK_FLD, N_("Use%"),        4, true, false },
194 
195   [ITOTAL_FIELD] = { ITOTAL_FIELD,
196     "itotal", INODE_FLD, N_("Inodes"),      5, true, false },
197 
198   [IUSED_FIELD] = { IUSED_FIELD,
199     "iused",  INODE_FLD, N_("IUsed"),       5, true, false },
200 
201   [IAVAIL_FIELD] = { IAVAIL_FIELD,
202     "iavail", INODE_FLD, N_("IFree"),       5, true, false },
203 
204   [IPCENT_FIELD] = { IPCENT_FIELD,
205     "ipcent", INODE_FLD, N_("IUse%"),       4, true, false },
206 
207   [TARGET_FIELD] = { TARGET_FIELD,
208     "target", OTHER_FLD, N_("Mounted on"),  0, false,  false },
209 
210   [FILE_FIELD] = { FILE_FIELD,
211     "file",   OTHER_FLD, N_("File"),        0, false,  false }
212 };
213 
214 static char const *all_args_string =
215   "source,fstype,itotal,iused,iavail,ipcent,size,"
216   "used,avail,pcent,file,target";
217 
218 /* Storage for the definition of output columns.  */
219 static struct field_data_t **columns;
220 
221 /* The current number of output columns.  */
222 static size_t ncolumns;
223 
224 /* Field values.  */
225 struct field_values_t
226 {
227   uintmax_t input_units;
228   uintmax_t output_units;
229   uintmax_t total;
230   uintmax_t available;
231   bool negate_available;
232   uintmax_t available_to_root;
233   uintmax_t used;
234   bool negate_used;
235 };
236 
237 /* Storage for pointers for each string (cell of table).  */
238 static char ***table;
239 
240 /* The current number of processed rows (including header).  */
241 static size_t nrows;
242 
243 /* For long options that have no equivalent short option, use a
244    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
245 enum
246 {
247   NO_SYNC_OPTION = CHAR_MAX + 1,
248   SYNC_OPTION,
249   TOTAL_OPTION,
250   OUTPUT_OPTION
251 };
252 
253 static struct option const long_options[] =
254 {
255   {"all", no_argument, nullptr, 'a'},
256   {"block-size", required_argument, nullptr, 'B'},
257   {"inodes", no_argument, nullptr, 'i'},
258   {"human-readable", no_argument, nullptr, 'h'},
259   {"si", no_argument, nullptr, 'H'},
260   {"local", no_argument, nullptr, 'l'},
261   {"output", optional_argument, nullptr, OUTPUT_OPTION},
262   {"portability", no_argument, nullptr, 'P'},
263   {"print-type", no_argument, nullptr, 'T'},
264   {"sync", no_argument, nullptr, SYNC_OPTION},
265   {"no-sync", no_argument, nullptr, NO_SYNC_OPTION},
266   {"total", no_argument, nullptr, TOTAL_OPTION},
267   {"type", required_argument, nullptr, 't'},
268   {"exclude-type", required_argument, nullptr, 'x'},
269   {GETOPT_HELP_OPTION_DECL},
270   {GETOPT_VERSION_OPTION_DECL},
271   {nullptr, 0, nullptr, 0}
272 };
273 
274 /* Stat FILE and put the results into *ST.  Return 0 if successful, an
275    error number otherwise.  Try to open FILE before statting, to
276    trigger automounts.  */
277 
278 static int
automount_stat_err(char const * file,struct stat * st)279 automount_stat_err (char const *file, struct stat *st)
280 {
281   int fd = open (file, O_RDONLY | O_NOCTTY | O_NONBLOCK);
282   if (fd < 0)
283     {
284       if (errno == ENOENT || errno == ENOTDIR)
285         return errno;
286       return stat (file, st) == 0 ? 0 : errno;
287     }
288   else
289     {
290       int err = fstat (fd, st) == 0 ? 0 : errno;
291       close (fd);
292       return err;
293     }
294 }
295 
296 enum { MBSWIDTH_FLAGS = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE };
297 
298 /* Replace problematic chars with '?'.
299    Since only control characters are currently considered,
300    this should work in all encodings.  */
301 
302 static void
replace_control_chars(char * cell)303 replace_control_chars (char *cell)
304 {
305   char *p = cell;
306   while (*p)
307     {
308       if (c_iscntrl (to_uchar (*p)))
309         *p = '?';
310       p++;
311     }
312 }
313 
314 /* Replace problematic chars with '?'.  */
315 
316 static void
replace_invalid_chars(char * cell)317 replace_invalid_chars (char *cell)
318 {
319   char *srcend = cell + strlen (cell);
320   char *dst = cell;
321   mbstate_t mbstate; mbszero (&mbstate);
322   size_t n;
323 
324   for (char *src = cell; src != srcend; src += n)
325     {
326       char32_t wc;
327       size_t srcbytes = srcend - src;
328       n = mbrtoc32 (&wc, src, srcbytes, &mbstate);
329       bool ok = n <= srcbytes;
330 
331       if (ok)
332         ok = !c32iscntrl (wc);
333       else
334         n = 1;
335 
336       if (ok)
337         {
338           memmove (dst, src, n);
339           dst += n;
340         }
341       else
342         {
343           *dst++ = '?';
344           memset (&mbstate, 0, sizeof mbstate);
345         }
346     }
347 
348   *dst = '\0';
349 }
350 
351 static void
replace_problematic_chars(char * cell)352 replace_problematic_chars (char *cell)
353 {
354   static int tty_out = -1;
355   if (tty_out < 0)
356     tty_out = isatty (STDOUT_FILENO);
357 
358   (tty_out ? replace_invalid_chars : replace_control_chars) (cell) ;
359 }
360 
361 
362 /* Dynamically allocate a row of pointers in TABLE, which
363    can then be accessed with standard 2D array notation.  */
364 
365 static void
alloc_table_row(void)366 alloc_table_row (void)
367 {
368   nrows++;
369   table = xnrealloc (table, nrows, sizeof (char **));
370   table[nrows - 1] = xnmalloc (ncolumns, sizeof (char *));
371 }
372 
373 /* Output each cell in the table, accounting for the
374    alignment and max width of each column.  */
375 
376 static void
print_table(void)377 print_table (void)
378 {
379   size_t row;
380 
381   for (row = 0; row < nrows; row++)
382     {
383       size_t col;
384       for (col = 0; col < ncolumns; col++)
385         {
386           char *cell = table[row][col];
387 
388           /* Note the SOURCE_FIELD used to be displayed on it's own line
389              if (!posix_format && mbswidth (cell) > 20), but that
390              functionality was probably more problematic than helpful,
391              hence changed in commit v8.10-40-g99679ff.  */
392           if (col != 0)
393             putchar (' ');
394 
395           int width = mbswidth (cell, MBSWIDTH_FLAGS);
396           int fill = width < 0 ? 0 : columns[col]->width - width;
397           if (columns[col]->align_right)
398             for (; 0 < fill; fill--)
399               putchar (' ');
400           fputs (cell, stdout);
401           if (col + 1 < ncolumns)
402             for (; 0 < fill; fill--)
403               putchar (' ');
404         }
405       putchar ('\n');
406     }
407 }
408 
409 /* Dynamically allocate a struct field_t in COLUMNS, which
410    can then be accessed with standard array notation.  */
411 
412 static void
alloc_field(int f,char const * c)413 alloc_field (int f, char const *c)
414 {
415   ncolumns++;
416   columns = xnrealloc (columns, ncolumns, sizeof (struct field_data_t *));
417   columns[ncolumns - 1] = &field_data[f];
418   if (c != nullptr)
419     columns[ncolumns - 1]->caption = c;
420 
421   affirm (!field_data[f].used);
422 
423   /* Mark field as used.  */
424   field_data[f].used = true;
425 }
426 
427 
428 /* Given a string, ARG, containing a comma-separated list of arguments
429    to the --output option, add the appropriate fields to columns.  */
430 static void
decode_output_arg(char const * arg)431 decode_output_arg (char const *arg)
432 {
433   char *arg_writable = xstrdup (arg);
434   char *s = arg_writable;
435   do
436     {
437       /* find next comma */
438       char *comma = strchr (s, ',');
439 
440       /* If we found a comma, put a NUL in its place and advance.  */
441       if (comma)
442         *comma++ = 0;
443 
444       /* process S.  */
445       display_field_t field = INVALID_FIELD;
446       for (idx_t i = 0; i < ARRAY_CARDINALITY (field_data); i++)
447         {
448           if (STREQ (field_data[i].arg, s))
449             {
450               field = i;
451               break;
452             }
453         }
454       if (field == INVALID_FIELD)
455         {
456           error (0, 0, _("option --output: field %s unknown"), quote (s));
457           usage (EXIT_FAILURE);
458         }
459 
460       if (field_data[field].used)
461         {
462           /* Prevent the fields from being used more than once.  */
463           error (0, 0, _("option --output: field %s used more than once"),
464                  quote (field_data[field].arg));
465           usage (EXIT_FAILURE);
466         }
467 
468       switch (field)
469         {
470         case SOURCE_FIELD:
471         case FSTYPE_FIELD:
472         case USED_FIELD:
473         case PCENT_FIELD:
474         case ITOTAL_FIELD:
475         case IUSED_FIELD:
476         case IAVAIL_FIELD:
477         case IPCENT_FIELD:
478         case TARGET_FIELD:
479         case FILE_FIELD:
480           alloc_field (field, nullptr);
481           break;
482 
483         case SIZE_FIELD:
484           alloc_field (field, N_("Size"));
485           break;
486 
487         case AVAIL_FIELD:
488           alloc_field (field, N_("Avail"));
489           break;
490 
491         default:
492           affirm (!"invalid field");
493         }
494       s = comma;
495     }
496   while (s);
497 
498   free (arg_writable);
499 }
500 
501 /* Get the appropriate columns for the mode.  */
502 static void
get_field_list(void)503 get_field_list (void)
504 {
505   switch (header_mode)
506     {
507     case DEFAULT_MODE:
508       alloc_field (SOURCE_FIELD, nullptr);
509       if (print_type)
510         alloc_field (FSTYPE_FIELD, nullptr);
511       alloc_field (SIZE_FIELD,   nullptr);
512       alloc_field (USED_FIELD,   nullptr);
513       alloc_field (AVAIL_FIELD,  nullptr);
514       alloc_field (PCENT_FIELD,  nullptr);
515       alloc_field (TARGET_FIELD, nullptr);
516       break;
517 
518     case HUMAN_MODE:
519       alloc_field (SOURCE_FIELD, nullptr);
520       if (print_type)
521         alloc_field (FSTYPE_FIELD, nullptr);
522 
523       alloc_field (SIZE_FIELD,   N_("Size"));
524       alloc_field (USED_FIELD,   nullptr);
525       alloc_field (AVAIL_FIELD,  N_("Avail"));
526       alloc_field (PCENT_FIELD,  nullptr);
527       alloc_field (TARGET_FIELD, nullptr);
528       break;
529 
530     case INODES_MODE:
531       alloc_field (SOURCE_FIELD, nullptr);
532       if (print_type)
533         alloc_field (FSTYPE_FIELD, nullptr);
534       alloc_field (ITOTAL_FIELD,  nullptr);
535       alloc_field (IUSED_FIELD,   nullptr);
536       alloc_field (IAVAIL_FIELD,  nullptr);
537       alloc_field (IPCENT_FIELD,  nullptr);
538       alloc_field (TARGET_FIELD,  nullptr);
539       break;
540 
541     case POSIX_MODE:
542       alloc_field (SOURCE_FIELD, nullptr);
543       if (print_type)
544         alloc_field (FSTYPE_FIELD, nullptr);
545       alloc_field (SIZE_FIELD,   nullptr);
546       alloc_field (USED_FIELD,   nullptr);
547       alloc_field (AVAIL_FIELD,  nullptr);
548       alloc_field (PCENT_FIELD,  N_("Capacity"));
549       alloc_field (TARGET_FIELD, nullptr);
550       break;
551 
552     case OUTPUT_MODE:
553       if (!ncolumns)
554         {
555           /* Add all fields if --output was given without a field list.  */
556           decode_output_arg (all_args_string);
557         }
558       break;
559 
560     default:
561       unreachable ();
562     }
563 }
564 
565 /* Obtain the appropriate header entries.  */
566 
567 static void
get_header(void)568 get_header (void)
569 {
570   size_t col;
571 
572   alloc_table_row ();
573 
574   for (col = 0; col < ncolumns; col++)
575     {
576       char *cell = nullptr;
577       char const *header = _(columns[col]->caption);
578 
579       if (columns[col]->field == SIZE_FIELD
580           && (header_mode == DEFAULT_MODE
581               || (header_mode == OUTPUT_MODE
582                   && !(human_output_opts & human_autoscale))))
583         {
584           char buf[LONGEST_HUMAN_READABLE + 1];
585 
586           int opts = (human_suppress_point_zero
587                       | human_autoscale | human_SI
588                       | (human_output_opts
589                          & (human_group_digits | human_base_1024 | human_B)));
590 
591           /* Prefer the base that makes the human-readable value more exact,
592              if there is a difference.  */
593 
594           uintmax_t q1000 = output_block_size;
595           uintmax_t q1024 = output_block_size;
596           bool divisible_by_1000;
597           bool divisible_by_1024;
598 
599           do
600             {
601               divisible_by_1000 = q1000 % 1000 == 0;  q1000 /= 1000;
602               divisible_by_1024 = q1024 % 1024 == 0;  q1024 /= 1024;
603             }
604           while (divisible_by_1000 & divisible_by_1024);
605 
606           if (divisible_by_1000 < divisible_by_1024)
607             opts |= human_base_1024;
608           if (divisible_by_1024 < divisible_by_1000)
609             opts &= ~human_base_1024;
610           if (! (opts & human_base_1024))
611             opts |= human_B;
612 
613           char *num = human_readable (output_block_size, buf, opts, 1, 1);
614 
615           /* Reset the header back to the default in OUTPUT_MODE.  */
616           header = _("blocks");
617 
618           /* TRANSLATORS: this is the "1K-blocks" header in "df" output.  */
619           if (asprintf (&cell, _("%s-%s"), num, header) == -1)
620             cell = nullptr;
621         }
622       else if (header_mode == POSIX_MODE && columns[col]->field == SIZE_FIELD)
623         {
624           char buf[INT_BUFSIZE_BOUND (uintmax_t)];
625           char *num = umaxtostr (output_block_size, buf);
626 
627           /* TRANSLATORS: this is the "1024-blocks" header in "df -P".  */
628           if (asprintf (&cell, _("%s-%s"), num, header) == -1)
629             cell = nullptr;
630         }
631       else
632         cell = strdup (header);
633 
634       if (!cell)
635         xalloc_die ();
636 
637       replace_problematic_chars (cell);
638 
639       table[nrows - 1][col] = cell;
640 
641       int cell_width = mbswidth (cell, MBSWIDTH_FLAGS);
642       columns[col]->width = MAX (columns[col]->width, cell_width);
643     }
644 }
645 
646 /* Is FSTYPE a type of file system that should be listed?  */
647 
648 ATTRIBUTE_PURE
649 static bool
selected_fstype(char const * fstype)650 selected_fstype (char const *fstype)
651 {
652   const struct fs_type_list *fsp;
653 
654   if (fs_select_list == nullptr || fstype == nullptr)
655     return true;
656   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
657     if (STREQ (fstype, fsp->fs_name))
658       return true;
659   return false;
660 }
661 
662 /* Is FSTYPE a type of file system that should be omitted?  */
663 
664 ATTRIBUTE_PURE
665 static bool
excluded_fstype(char const * fstype)666 excluded_fstype (char const *fstype)
667 {
668   const struct fs_type_list *fsp;
669 
670   if (fs_exclude_list == nullptr || fstype == nullptr)
671     return false;
672   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
673     if (STREQ (fstype, fsp->fs_name))
674       return true;
675   return false;
676 }
677 
678 static size_t
devlist_hash(void const * x,size_t table_size)679 devlist_hash (void const *x, size_t table_size)
680 {
681   struct devlist const *p = x;
682   return (uintmax_t) p->dev_num % table_size;
683 }
684 
685 static bool
devlist_compare(void const * x,void const * y)686 devlist_compare (void const *x, void const *y)
687 {
688   struct devlist const *a = x;
689   struct devlist const *b = y;
690   return a->dev_num == b->dev_num;
691 }
692 
693 static struct devlist *
devlist_for_dev(dev_t dev)694 devlist_for_dev (dev_t dev)
695 {
696   if (devlist_table == nullptr)
697     return nullptr;
698   struct devlist dev_entry;
699   dev_entry.dev_num = dev;
700 
701   struct devlist *found = hash_lookup (devlist_table, &dev_entry);
702   if (found == nullptr)
703     return nullptr;
704 
705   /* Return the last devlist entry we have seen with this dev_num */
706   return found->seen_last;
707 }
708 
709 /* Filter mount list by skipping duplicate entries.
710    In the case of duplicates - based on the device number - the mount entry
711    with a '/' in its me_devname (i.e., not pseudo name like tmpfs) wins.
712    If both have a real devname (e.g. bind mounts), then that with the shorter
713    me_mountdir wins.  With DEVICES_ONLY == true (set with df -a), only update
714    the global devlist_table, rather than filtering the global mount_list.  */
715 
716 static void
filter_mount_list(bool devices_only)717 filter_mount_list (bool devices_only)
718 {
719   struct mount_entry *me;
720 
721   /* Temporary list to keep entries ordered.  */
722   struct devlist *device_list = nullptr;
723   int mount_list_size = 0;
724 
725   for (me = mount_list; me; me = me->me_next)
726     mount_list_size++;
727 
728   devlist_table = hash_initialize (mount_list_size, nullptr,
729                                    devlist_hash, devlist_compare, nullptr);
730   if (devlist_table == nullptr)
731     xalloc_die ();
732 
733   /* Sort all 'wanted' entries into the list device_list.  */
734   for (me = mount_list; me;)
735     {
736       struct stat buf;
737       struct mount_entry *discard_me = nullptr;
738 
739       /* Avoid stating remote file systems as that may hang.
740          On Linux we probably have me_dev populated from /proc/self/mountinfo,
741          however we still stat() in case another device was mounted later.  */
742       if ((me->me_remote && show_local_fs)
743           || (me->me_dummy && !show_all_fs && !show_listed_fs)
744           || (!selected_fstype (me->me_type) || excluded_fstype (me->me_type))
745           || -1 == stat (me->me_mountdir, &buf))
746         {
747           /* If remote, and showing just local, or FS type is excluded,
748              add ME for filtering later.
749              If stat failed; add ME to be able to complain about it later.  */
750           buf.st_dev = me->me_dev;
751         }
752       else
753         {
754           /* If we've already seen this device...  */
755           struct devlist *seen_dev = devlist_for_dev (buf.st_dev);
756 
757           if (seen_dev)
758             {
759               bool target_nearer_root = strlen (seen_dev->me->me_mountdir)
760                                         > strlen (me->me_mountdir);
761               /* With bind mounts, prefer items nearer the root of the source */
762               bool source_below_root = seen_dev->me->me_mntroot != nullptr
763                                        && me->me_mntroot != nullptr
764                                        && (strlen (seen_dev->me->me_mntroot)
765                                            < strlen (me->me_mntroot));
766               if (! print_grand_total
767                   && me->me_remote && seen_dev->me->me_remote
768                   && ! STREQ (seen_dev->me->me_devname, me->me_devname))
769                 {
770                   /* Don't discard remote entries with different locations,
771                      as these are more likely to be explicitly mounted.
772                      However avoid this when producing a total to give
773                      a more accurate value in that case.  */
774                 }
775               else if ((strchr (me->me_devname, '/')
776                        /* let "real" devices with '/' in the name win.  */
777                         && ! strchr (seen_dev->me->me_devname, '/'))
778                        /* let points towards the root of the device win.  */
779                        || (target_nearer_root && ! source_below_root)
780                        /* let an entry overmounted on a new device win...  */
781                        || (! STREQ (seen_dev->me->me_devname, me->me_devname)
782                            /* ... but only when matching an existing mnt point,
783                               to avoid problematic replacement when given
784                               inaccurate mount lists, seen with some chroot
785                               environments for example.  */
786                            && STREQ (me->me_mountdir,
787                                      seen_dev->me->me_mountdir)))
788                 {
789                   /* Discard mount entry for existing device.  */
790                   discard_me = seen_dev->me;
791                   seen_dev->me = me;
792                 }
793               else
794                 {
795                   /* Discard mount entry currently being processed.  */
796                   discard_me = me;
797                 }
798 
799             }
800         }
801 
802       if (discard_me)
803         {
804           me = me->me_next;
805           if (! devices_only)
806             free_mount_entry (discard_me);
807         }
808       else
809         {
810           /* Add the device number to the device_table.  */
811           struct devlist *devlist = xmalloc (sizeof *devlist);
812           devlist->me = me;
813           devlist->dev_num = buf.st_dev;
814           devlist->next = device_list;
815           device_list = devlist;
816 
817           struct devlist *hash_entry = hash_insert (devlist_table, devlist);
818           if (hash_entry == nullptr)
819             xalloc_die ();
820           /* Ensure lookups use this latest devlist.  */
821           hash_entry->seen_last = devlist;
822 
823           me = me->me_next;
824         }
825     }
826 
827   /* Finally rebuild the mount_list from the devlist.  */
828   if (! devices_only) {
829     mount_list = nullptr;
830     while (device_list)
831       {
832         /* Add the mount entry.  */
833         me = device_list->me;
834         me->me_next = mount_list;
835         mount_list = me;
836         struct devlist *next = device_list->next;
837         free (device_list);
838         device_list = next;
839       }
840 
841       hash_free (devlist_table);
842       devlist_table = nullptr;
843   }
844 }
845 
846 
847 /* Search a mount entry list for device id DEV.
848    Return the corresponding mount entry if found or nullptr if not.  */
849 
850 ATTRIBUTE_PURE
851 static struct mount_entry const *
me_for_dev(dev_t dev)852 me_for_dev (dev_t dev)
853 {
854   struct devlist *dl = devlist_for_dev (dev);
855   if (dl)
856         return dl->me;
857 
858   return nullptr;
859 }
860 
861 /* Return true if N is a known integer value.  On many file systems,
862    UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
863    represents unknown.  Use a rule that works on AIX file systems, and
864    that almost-always works on other types.  */
865 static bool
known_value(uintmax_t n)866 known_value (uintmax_t n)
867 {
868   return n < UINTMAX_MAX - 1;
869 }
870 
871 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
872    except:
873 
874     - If NEGATIVE, then N represents a negative number,
875       expressed in two's complement.
876     - Otherwise, return "-" if N is unknown.  */
877 
878 static char const *
df_readable(bool negative,uintmax_t n,char * buf,uintmax_t input_units,uintmax_t output_units)879 df_readable (bool negative, uintmax_t n, char *buf,
880              uintmax_t input_units, uintmax_t output_units)
881 {
882   if (! known_value (n) && !negative)
883     return "-";
884   else
885     {
886       char *p = human_readable (negative ? -n : n, buf + negative,
887                                 human_output_opts, input_units, output_units);
888       if (negative)
889         *--p = '-';
890       return p;
891     }
892 }
893 
894 /* Add integral value while using uintmax_t for value part and separate
895    negation flag.  It adds value of SRC and SRC_NEG to DEST and DEST_NEG.
896    The result will be in DEST and DEST_NEG.  See df_readable to understand
897    how the negation flag is used.  */
898 static void
add_uint_with_neg_flag(uintmax_t * dest,bool * dest_neg,uintmax_t src,bool src_neg)899 add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
900                         uintmax_t src, bool src_neg)
901 {
902   if (*dest_neg == src_neg)
903     {
904       *dest += src;
905       return;
906     }
907 
908   if (*dest_neg)
909     *dest = -*dest;
910 
911   if (src_neg)
912     src = -src;
913 
914   if (src < *dest)
915     *dest -= src;
916   else
917     {
918       *dest = src - *dest;
919       *dest_neg = src_neg;
920     }
921 
922   if (*dest_neg)
923     *dest = -*dest;
924 }
925 
926 /* Return true if S ends in a string that may be a 36-byte UUID,
927    i.e., of the form HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH, where
928    each H is an upper or lower case hexadecimal digit.  */
929 ATTRIBUTE_PURE
930 static bool
has_uuid_suffix(char const * s)931 has_uuid_suffix (char const *s)
932 {
933   size_t len = strlen (s);
934   return (36 < len
935           && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36);
936 }
937 
938 /* Obtain the block values BV and inode values IV
939    from the file system usage FSU.  */
940 static void
get_field_values(struct field_values_t * bv,struct field_values_t * iv,const struct fs_usage * fsu)941 get_field_values (struct field_values_t *bv,
942                   struct field_values_t *iv,
943                   const struct fs_usage *fsu)
944 {
945   /* Inode values.  */
946   iv->input_units = iv->output_units = 1;
947   iv->total = fsu->fsu_files;
948   iv->available = iv->available_to_root = fsu->fsu_ffree;
949   iv->negate_available = false;
950 
951   iv->used = UINTMAX_MAX;
952   iv->negate_used = false;
953   if (known_value (iv->total) && known_value (iv->available_to_root))
954     {
955       iv->used = iv->total - iv->available_to_root;
956       iv->negate_used = (iv->total < iv->available_to_root);
957     }
958 
959   /* Block values.  */
960   bv->input_units = fsu->fsu_blocksize;
961   bv->output_units = output_block_size;
962   bv->total = fsu->fsu_blocks;
963   bv->available = fsu->fsu_bavail;
964   bv->available_to_root = fsu->fsu_bfree;
965   bv->negate_available = (fsu->fsu_bavail_top_bit_set
966                          && known_value (fsu->fsu_bavail));
967 
968   bv->used = UINTMAX_MAX;
969   bv->negate_used = false;
970   if (known_value (bv->total) && known_value (bv->available_to_root))
971     {
972       bv->used = bv->total - bv->available_to_root;
973       bv->negate_used = (bv->total < bv->available_to_root);
974     }
975 }
976 
977 /* Add block and inode values to grand total.  */
978 static void
add_to_grand_total(struct field_values_t * bv,struct field_values_t * iv)979 add_to_grand_total (struct field_values_t *bv, struct field_values_t *iv)
980 {
981   if (known_value (iv->total))
982     grand_fsu.fsu_files += iv->total;
983   if (known_value (iv->available))
984     grand_fsu.fsu_ffree += iv->available;
985 
986   if (known_value (bv->total))
987     grand_fsu.fsu_blocks += bv->input_units * bv->total;
988   if (known_value (bv->available_to_root))
989     grand_fsu.fsu_bfree += bv->input_units * bv->available_to_root;
990   if (known_value (bv->available))
991     add_uint_with_neg_flag (&grand_fsu.fsu_bavail,
992                             &grand_fsu.fsu_bavail_top_bit_set,
993                             bv->input_units * bv->available,
994                             bv->negate_available);
995 }
996 
997 /* Obtain a space listing for the device with absolute file name DEVICE.
998    If MOUNT_POINT is non-null, it is the name of the root of the
999    file system on DEVICE.
1000    If STAT_FILE is non-null, it is the name of a file within the file
1001    system that the user originally asked for; this provides better
1002    diagnostics, and sometimes it provides better results on networked
1003    file systems that give different free-space results depending on
1004    where in the file system you probe.
1005    If FSTYPE is non-null, it is the type of the file system on DEVICE.
1006    If MOUNT_POINT is non-null, then DEVICE may be null -- certain systems may
1007    not be able to produce statistics in this case.
1008    ME_DUMMY and ME_REMOTE are the mount entry flags.
1009    Caller must set PROCESS_ALL to true when iterating over all entries, as
1010    when df is invoked with no non-option argument.  See below for details.  */
1011 
1012 static void
get_dev(char const * device,char const * mount_point,char const * file,char const * stat_file,char const * fstype,bool me_dummy,bool me_remote,const struct fs_usage * force_fsu,bool process_all)1013 get_dev (char const *device, char const *mount_point, char const *file,
1014          char const *stat_file, char const *fstype,
1015          bool me_dummy, bool me_remote,
1016          const struct fs_usage *force_fsu,
1017          bool process_all)
1018 {
1019   if (me_remote && show_local_fs)
1020     return;
1021 
1022   if (me_dummy && !show_all_fs && !show_listed_fs)
1023     return;
1024 
1025   if (!selected_fstype (fstype) || excluded_fstype (fstype))
1026     return;
1027 
1028   /* Ignore relative MOUNT_POINTs, which are present for example
1029      in /proc/mounts on Linux with network namespaces.  */
1030   if (!force_fsu && mount_point && ! IS_ABSOLUTE_FILE_NAME (mount_point))
1031     return;
1032 
1033   /* If MOUNT_POINT is null, then the file system is not mounted, and this
1034      program reports on the file system that the special file is on.
1035      It would be better to report on the unmounted file system,
1036      but statfs doesn't do that on most systems.  */
1037   if (!stat_file)
1038     stat_file = mount_point ? mount_point : device;
1039 
1040   struct fs_usage fsu;
1041   if (force_fsu)
1042     fsu = *force_fsu;
1043   else if (get_fs_usage (stat_file, device, &fsu))
1044     {
1045       /* If we can't access a system provided entry due
1046          to it not being present (now), or due to permissions,
1047          just output placeholder values rather than failing.  */
1048       if (process_all && (errno == EACCES || errno == ENOENT))
1049         {
1050           if (! show_all_fs)
1051             return;
1052 
1053           fstype = "-";
1054           fsu.fsu_bavail_top_bit_set = false;
1055           fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
1056           fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
1057         }
1058       else
1059         {
1060           error (0, errno, "%s", quotef (stat_file));
1061           exit_status = EXIT_FAILURE;
1062           return;
1063         }
1064     }
1065   else if (process_all && show_all_fs)
1066     {
1067       /* Ensure we don't output incorrect stats for over-mounted directories.
1068          Discard stats when the device name doesn't match.  Though don't
1069          discard when used and current mount entries are both remote due
1070          to the possibility of aliased host names or exports.  */
1071       struct stat sb;
1072       if (stat (stat_file, &sb) == 0)
1073         {
1074           struct mount_entry const * dev_me = me_for_dev (sb.st_dev);
1075           if (dev_me && ! STREQ (dev_me->me_devname, device)
1076               && (! dev_me->me_remote || ! me_remote))
1077             {
1078               fstype = "-";
1079               fsu.fsu_bavail_top_bit_set = false;
1080               fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
1081               fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
1082             }
1083         }
1084     }
1085 
1086   if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
1087     return;
1088 
1089   if (! force_fsu)
1090     file_systems_processed = true;
1091 
1092   alloc_table_row ();
1093 
1094   if (! device)
1095     device = "-";		/* unknown */
1096 
1097   if (! file)
1098     file = "-";			/* unspecified */
1099 
1100   char *dev_name = xstrdup (device);
1101   char *resolved_dev;
1102 
1103   /* On some systems, dev_name is a long-named symlink like
1104      /dev/disk/by-uuid/828fc648-9f30-43d8-a0b1-f7196a2edb66 pointing to a
1105      much shorter and more useful name like /dev/sda1.  It may also look
1106      like /dev/mapper/luks-828fc648-9f30-43d8-a0b1-f7196a2edb66 and point to
1107      /dev/dm-0.  When process_all is true and dev_name is a symlink whose
1108      name ends with a UUID use the resolved name instead.  */
1109   if (process_all
1110       && has_uuid_suffix (dev_name)
1111       && (resolved_dev = canonicalize_filename_mode (dev_name, CAN_EXISTING)))
1112     {
1113       free (dev_name);
1114       dev_name = resolved_dev;
1115     }
1116 
1117   if (! fstype)
1118     fstype = "-";		/* unknown */
1119 
1120   struct field_values_t block_values;
1121   struct field_values_t inode_values;
1122   get_field_values (&block_values, &inode_values, &fsu);
1123 
1124   /* Add to grand total unless processing grand total line.  */
1125   if (print_grand_total && ! force_fsu)
1126     add_to_grand_total (&block_values, &inode_values);
1127 
1128   size_t col;
1129   for (col = 0; col < ncolumns; col++)
1130     {
1131       char buf[LONGEST_HUMAN_READABLE + 2];
1132       char *cell;
1133 
1134       struct field_values_t *v;
1135       switch (columns[col]->field_type)
1136         {
1137         case BLOCK_FLD:
1138           v = &block_values;
1139           break;
1140         case INODE_FLD:
1141           v = &inode_values;
1142           break;
1143         case OTHER_FLD:
1144           v = nullptr;
1145           break;
1146         default:
1147           affirm (!"bad field_type");
1148         }
1149 
1150       switch (columns[col]->field)
1151         {
1152         case SOURCE_FIELD:
1153           cell = xstrdup (dev_name);
1154           break;
1155 
1156         case FSTYPE_FIELD:
1157           cell = xstrdup (fstype);
1158           break;
1159 
1160         case SIZE_FIELD:
1161         case ITOTAL_FIELD:
1162           cell = xstrdup (df_readable (false, v->total, buf,
1163                                        v->input_units, v->output_units));
1164           break;
1165 
1166         case USED_FIELD:
1167         case IUSED_FIELD:
1168           cell = xstrdup (df_readable (v->negate_used, v->used, buf,
1169                                        v->input_units, v->output_units));
1170           break;
1171 
1172         case AVAIL_FIELD:
1173         case IAVAIL_FIELD:
1174           cell = xstrdup (df_readable (v->negate_available, v->available, buf,
1175                                        v->input_units, v->output_units));
1176           break;
1177 
1178         case PCENT_FIELD:
1179         case IPCENT_FIELD:
1180           {
1181             double pct = -1;
1182             if (! known_value (v->used) || ! known_value (v->available))
1183               ;
1184             else if (!v->negate_used
1185                      && v->used <= TYPE_MAXIMUM (uintmax_t) / 100
1186                      && v->used + v->available != 0
1187                      && (v->used + v->available < v->used)
1188                      == v->negate_available)
1189               {
1190                 uintmax_t u100 = v->used * 100;
1191                 uintmax_t nonroot_total = v->used + v->available;
1192                 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
1193               }
1194             else
1195               {
1196                 /* The calculation cannot be done easily with integer
1197                    arithmetic.  Fall back on floating point.  This can suffer
1198                    from minor rounding errors, but doing it exactly requires
1199                    multiple precision arithmetic, and it's not worth the
1200                    aggravation.  */
1201                 double u = v->negate_used ? - (double) - v->used : v->used;
1202                 double a = v->negate_available
1203                            ? - (double) - v->available : v->available;
1204                 double nonroot_total = u + a;
1205                 if (nonroot_total)
1206                   {
1207                     long int lipct = pct = u * 100 / nonroot_total;
1208                     double ipct = lipct;
1209 
1210                     /* Like 'pct = ceil (dpct);', but avoid ceil so that
1211                        the math library needn't be linked.  */
1212                     if (ipct - 1 < pct && pct <= ipct + 1)
1213                       pct = ipct + (ipct < pct);
1214                   }
1215               }
1216 
1217             if (0 <= pct)
1218               {
1219                 if (asprintf (&cell, "%.0f%%", pct) == -1)
1220                   cell = nullptr;
1221               }
1222             else
1223               cell = strdup ("-");
1224 
1225             if (!cell)
1226               xalloc_die ();
1227 
1228             break;
1229           }
1230 
1231         case FILE_FIELD:
1232           cell = xstrdup (file);
1233           break;
1234 
1235         case TARGET_FIELD:
1236 #ifdef HIDE_AUTOMOUNT_PREFIX
1237           /* Don't print the first directory name in MOUNT_POINT if it's an
1238              artifact of an automounter.  This is a bit too aggressive to be
1239              the default.  */
1240           if (STRNCMP_LIT (mount_point, "/auto/") == 0)
1241             mount_point += 5;
1242           else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0)
1243             mount_point += 8;
1244 #endif
1245           cell = xstrdup (mount_point);
1246           break;
1247 
1248         default:
1249           affirm (!"unhandled field");
1250         }
1251 
1252       affirm (cell);
1253 
1254       replace_problematic_chars (cell);
1255       int cell_width = mbswidth (cell, MBSWIDTH_FLAGS);
1256       columns[col]->width = MAX (columns[col]->width, cell_width);
1257       table[nrows - 1][col] = cell;
1258     }
1259   free (dev_name);
1260 }
1261 
1262 /* Scan the mount list returning the _last_ device found for MOUNT.
1263    nullptr is returned if MOUNT not found.  The result is malloced.  */
1264 static char *
last_device_for_mount(char const * mount)1265 last_device_for_mount (char const *mount)
1266 {
1267   struct mount_entry const *me;
1268   struct mount_entry const *le = nullptr;
1269 
1270   for (me = mount_list; me; me = me->me_next)
1271     {
1272       if (STREQ (me->me_mountdir, mount))
1273         le = me;
1274     }
1275 
1276   if (le)
1277     {
1278       char *devname = le->me_devname;
1279       char *canon_dev = canonicalize_file_name (devname);
1280       if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
1281         return canon_dev;
1282       free (canon_dev);
1283       return xstrdup (le->me_devname);
1284     }
1285   else
1286     return nullptr;
1287 }
1288 
1289 /* If DEVICE corresponds to a mount point, show its usage
1290    and return true.  Otherwise, return false.  */
1291 static bool
get_device(char const * device)1292 get_device (char const *device)
1293 {
1294   struct mount_entry const *me;
1295   struct mount_entry const *best_match = nullptr;
1296   bool best_match_accessible = false;
1297   bool eclipsed_device = false;
1298   char const *file = device;
1299 
1300   char *resolved = canonicalize_file_name (device);
1301   if (resolved && IS_ABSOLUTE_FILE_NAME (resolved))
1302     device = resolved;
1303 
1304   size_t best_match_len = SIZE_MAX;
1305   for (me = mount_list; me; me = me->me_next)
1306     {
1307       /* TODO: Should cache canon_dev in the mount_entry struct.  */
1308       char *devname = me->me_devname;
1309       char *canon_dev = canonicalize_file_name (me->me_devname);
1310       if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
1311         devname = canon_dev;
1312 
1313       if (STREQ (device, devname))
1314         {
1315           char *last_device = last_device_for_mount (me->me_mountdir);
1316           eclipsed_device = last_device && ! STREQ (last_device, devname);
1317           size_t len = strlen (me->me_mountdir);
1318 
1319           if (! eclipsed_device
1320               && (! best_match_accessible || len < best_match_len))
1321             {
1322               struct stat device_stats;
1323               bool this_match_accessible = false;
1324 
1325               if (stat (me->me_mountdir, &device_stats) == 0)
1326                 best_match_accessible = this_match_accessible = true;
1327 
1328               if (this_match_accessible
1329                   || (! best_match_accessible && len < best_match_len))
1330                 {
1331                   best_match = me;
1332                   if (len == 1) /* Traditional root.  */
1333                     {
1334                       free (last_device);
1335                       free (canon_dev);
1336                       break;
1337                     }
1338                   else
1339                     best_match_len = len;
1340                 }
1341             }
1342 
1343           free (last_device);
1344         }
1345 
1346       free (canon_dev);
1347     }
1348 
1349   free (resolved);
1350 
1351   if (best_match)
1352     {
1353       get_dev (best_match->me_devname, best_match->me_mountdir, file, nullptr,
1354                best_match->me_type, best_match->me_dummy,
1355                best_match->me_remote, nullptr, false);
1356       return true;
1357     }
1358   else if (eclipsed_device)
1359     {
1360       error (0, 0, _("cannot access %s: over-mounted by another device"),
1361              quoteaf (file));
1362       exit_status = EXIT_FAILURE;
1363       return true;
1364     }
1365 
1366   return false;
1367 }
1368 
1369 /* Figure out which device file or directory POINT is mounted on
1370    and show its device usage.
1371    STATP must be the result of 'stat (POINT, STATP)'.  */
1372 static void
get_point(char const * point,const struct stat * statp)1373 get_point (char const *point, const struct stat *statp)
1374 {
1375   struct stat device_stats;
1376   struct mount_entry *me;
1377   struct mount_entry const *best_match = nullptr;
1378 
1379   /* Calculate the real absolute file name for POINT, and use that to find
1380      the mount point.  This avoids statting unavailable mount points,
1381      which can hang df.  */
1382   char *resolved = canonicalize_file_name (point);
1383   if (resolved && resolved[0] == '/')
1384     {
1385       size_t resolved_len = strlen (resolved);
1386       size_t best_match_len = 0;
1387 
1388       for (me = mount_list; me; me = me->me_next)
1389         {
1390           if (!STREQ (me->me_type, "lofs")
1391               && (!best_match || best_match->me_dummy || !me->me_dummy))
1392             {
1393               size_t len = strlen (me->me_mountdir);
1394               if (best_match_len <= len && len <= resolved_len
1395                   && (len == 1 /* root file system */
1396                       || ((len == resolved_len || resolved[len] == '/')
1397                           && STREQ_LEN (me->me_mountdir, resolved, len))))
1398                 {
1399                   best_match = me;
1400                   best_match_len = len;
1401                 }
1402             }
1403         }
1404     }
1405   free (resolved);
1406   if (best_match
1407       && (stat (best_match->me_mountdir, &device_stats) != 0
1408           || device_stats.st_dev != statp->st_dev))
1409     best_match = nullptr;
1410 
1411   if (! best_match)
1412     for (me = mount_list; me; me = me->me_next)
1413       {
1414         if (me->me_dev == (dev_t) -1)
1415           {
1416             if (stat (me->me_mountdir, &device_stats) == 0)
1417               me->me_dev = device_stats.st_dev;
1418             else
1419               {
1420                 /* Report only I/O errors.  Other errors might be
1421                    caused by shadowed mount points, which means POINT
1422                    can't possibly be on this file system.  */
1423                 if (errno == EIO)
1424                   {
1425                     error (0, errno, "%s", quotef (me->me_mountdir));
1426                     exit_status = EXIT_FAILURE;
1427                   }
1428 
1429                 /* So we won't try and fail repeatedly.  */
1430                 me->me_dev = (dev_t) -2;
1431               }
1432           }
1433 
1434         if (statp->st_dev == me->me_dev
1435             && !STREQ (me->me_type, "lofs")
1436             && (!best_match || best_match->me_dummy || !me->me_dummy))
1437           {
1438             /* Skip bogus mtab entries.  */
1439             if (stat (me->me_mountdir, &device_stats) != 0
1440                 || device_stats.st_dev != me->me_dev)
1441               me->me_dev = (dev_t) -2;
1442             else
1443               best_match = me;
1444           }
1445       }
1446 
1447   if (best_match)
1448     get_dev (best_match->me_devname, best_match->me_mountdir, point, point,
1449              best_match->me_type, best_match->me_dummy, best_match->me_remote,
1450              nullptr, false);
1451   else
1452     {
1453       /* We couldn't find the mount entry corresponding to POINT.  Go ahead and
1454          print as much info as we can; methods that require the device to be
1455          present will fail at a later point.  */
1456 
1457       /* Find the actual mount point.  */
1458       char *mp = find_mount_point (point, statp);
1459       if (mp)
1460         {
1461           get_dev (nullptr, mp, point, nullptr, nullptr,
1462                    false, false, nullptr, false);
1463           free (mp);
1464         }
1465     }
1466 }
1467 
1468 /* Determine what kind of node NAME is and show the device usage
1469    for it.  STATP is the results of 'stat' on NAME.  */
1470 
1471 static void
get_entry(char const * name,struct stat const * statp)1472 get_entry (char const *name, struct stat const *statp)
1473 {
1474   if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
1475       && get_device (name))
1476     return;
1477 
1478   get_point (name, statp);
1479 }
1480 
1481 /* Show all mounted file systems, except perhaps those that are of
1482    an unselected type or are empty.  */
1483 
1484 static void
get_all_entries(void)1485 get_all_entries (void)
1486 {
1487   struct mount_entry *me;
1488 
1489   filter_mount_list (show_all_fs);
1490 
1491   for (me = mount_list; me; me = me->me_next)
1492     get_dev (me->me_devname, me->me_mountdir, nullptr, nullptr, me->me_type,
1493              me->me_dummy, me->me_remote, nullptr, true);
1494 }
1495 
1496 /* Add FSTYPE to the list of file system types to display.  */
1497 
1498 static void
add_fs_type(char const * fstype)1499 add_fs_type (char const *fstype)
1500 {
1501   struct fs_type_list *fsp;
1502 
1503   fsp = xmalloc (sizeof *fsp);
1504   fsp->fs_name = (char *) fstype;
1505   fsp->fs_next = fs_select_list;
1506   fs_select_list = fsp;
1507 }
1508 
1509 /* Add FSTYPE to the list of file system types to be omitted.  */
1510 
1511 static void
add_excluded_fs_type(char const * fstype)1512 add_excluded_fs_type (char const *fstype)
1513 {
1514   struct fs_type_list *fsp;
1515 
1516   fsp = xmalloc (sizeof *fsp);
1517   fsp->fs_name = (char *) fstype;
1518   fsp->fs_next = fs_exclude_list;
1519   fs_exclude_list = fsp;
1520 }
1521 
1522 void
usage(int status)1523 usage (int status)
1524 {
1525   if (status != EXIT_SUCCESS)
1526     emit_try_help ();
1527   else
1528     {
1529       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
1530       fputs (_("\
1531 Show information about the file system on which each FILE resides,\n\
1532 or all file systems by default.\n\
1533 "), stdout);
1534 
1535       emit_mandatory_arg_note ();
1536 
1537       /* TRANSLATORS: The thousands and decimal separators are best
1538          adjusted to an appropriate default for your locale.  */
1539       fputs (_("\
1540   -a, --all             include pseudo, duplicate, inaccessible file systems\n\
1541   -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,\n\
1542                            '-BM' prints sizes in units of 1,048,576 bytes;\n\
1543                            see SIZE format below\n\
1544   -h, --human-readable  print sizes in powers of 1024 (e.g., 1023M)\n\
1545   -H, --si              print sizes in powers of 1000 (e.g., 1.1G)\n\
1546 "), stdout);
1547       fputs (_("\
1548   -i, --inodes          list inode information instead of block usage\n\
1549   -k                    like --block-size=1K\n\
1550   -l, --local           limit listing to local file systems\n\
1551       --no-sync         do not invoke sync before getting usage info (default)\
1552 \n\
1553 "), stdout);
1554       fputs (_("\
1555       --output[=FIELD_LIST]  use the output format defined by FIELD_LIST,\n\
1556                                or print all fields if FIELD_LIST is omitted.\n\
1557   -P, --portability     use the POSIX output format\n\
1558       --sync            invoke sync before getting usage info\n\
1559 "), stdout);
1560       fputs (_("\
1561       --total           elide all entries insignificant to available space,\n\
1562                           and produce a grand total\n\
1563 "), stdout);
1564       fputs (_("\
1565   -t, --type=TYPE       limit listing to file systems of type TYPE\n\
1566   -T, --print-type      print file system type\n\
1567   -x, --exclude-type=TYPE   limit listing to file systems not of type TYPE\n\
1568   -v                    (ignored)\n\
1569 "), stdout);
1570       fputs (HELP_OPTION_DESCRIPTION, stdout);
1571       fputs (VERSION_OPTION_DESCRIPTION, stdout);
1572       emit_blocksize_note ("DF");
1573       emit_size_note ();
1574       fputs (_("\n\
1575 FIELD_LIST is a comma-separated list of columns to be included.  Valid\n\
1576 field names are: 'source', 'fstype', 'itotal', 'iused', 'iavail', 'ipcent',\n\
1577 'size', 'used', 'avail', 'pcent', 'file' and 'target' (see info page).\n\
1578 "), stdout);
1579       emit_ancillary_info (PROGRAM_NAME);
1580     }
1581   exit (status);
1582 }
1583 
1584 int
main(int argc,char ** argv)1585 main (int argc, char **argv)
1586 {
1587   struct stat *stats = nullptr;
1588 
1589   initialize_main (&argc, &argv);
1590   set_program_name (argv[0]);
1591   setlocale (LC_ALL, "");
1592   bindtextdomain (PACKAGE, LOCALEDIR);
1593   textdomain (PACKAGE);
1594 
1595   atexit (close_stdout);
1596 
1597   fs_select_list = nullptr;
1598   fs_exclude_list = nullptr;
1599   show_all_fs = false;
1600   show_listed_fs = false;
1601   human_output_opts = -1;
1602   print_type = false;
1603   file_systems_processed = false;
1604   exit_status = EXIT_SUCCESS;
1605   print_grand_total = false;
1606   grand_fsu.fsu_blocksize = 1;
1607 
1608   /* If true, use the POSIX output format.  */
1609   bool posix_format = false;
1610 
1611   char const *msg_mut_excl = _("options %s and %s are mutually exclusive");
1612 
1613   while (true)
1614     {
1615       int oi = -1;
1616       int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options,
1617                            &oi);
1618       if (c == -1)
1619         break;
1620 
1621       switch (c)
1622         {
1623         case 'a':
1624           show_all_fs = true;
1625           break;
1626         case 'B':
1627           {
1628             enum strtol_error e = human_options (optarg, &human_output_opts,
1629                                                  &output_block_size);
1630             if (e != LONGINT_OK)
1631               xstrtol_fatal (e, oi, c, long_options, optarg);
1632           }
1633           break;
1634         case 'i':
1635           if (header_mode == OUTPUT_MODE)
1636             {
1637               error (0, 0, msg_mut_excl, "-i", "--output");
1638               usage (EXIT_FAILURE);
1639             }
1640           header_mode = INODES_MODE;
1641           break;
1642         case 'h':
1643           human_output_opts = human_autoscale | human_SI | human_base_1024;
1644           output_block_size = 1;
1645           break;
1646         case 'H':
1647           human_output_opts = human_autoscale | human_SI;
1648           output_block_size = 1;
1649           break;
1650         case 'k':
1651           human_output_opts = 0;
1652           output_block_size = 1024;
1653           break;
1654         case 'l':
1655           show_local_fs = true;
1656           break;
1657         case 'm': /* obsolescent, exists for BSD compatibility */
1658           human_output_opts = 0;
1659           output_block_size = 1024 * 1024;
1660           break;
1661         case 'T':
1662           if (header_mode == OUTPUT_MODE)
1663             {
1664               error (0, 0, msg_mut_excl, "-T", "--output");
1665               usage (EXIT_FAILURE);
1666             }
1667           print_type = true;
1668           break;
1669         case 'P':
1670           if (header_mode == OUTPUT_MODE)
1671             {
1672               error (0, 0, msg_mut_excl, "-P", "--output");
1673               usage (EXIT_FAILURE);
1674             }
1675           posix_format = true;
1676           break;
1677         case SYNC_OPTION:
1678           require_sync = true;
1679           break;
1680         case NO_SYNC_OPTION:
1681           require_sync = false;
1682           break;
1683 
1684         case 'F':
1685           /* Accept -F as a synonym for -t for compatibility with Solaris.  */
1686         case 't':
1687           add_fs_type (optarg);
1688           break;
1689 
1690         case 'v':		/* For SysV compatibility.  */
1691           /* ignore */
1692           break;
1693         case 'x':
1694           add_excluded_fs_type (optarg);
1695           break;
1696 
1697         case OUTPUT_OPTION:
1698           if (header_mode == INODES_MODE)
1699             {
1700               error (0, 0, msg_mut_excl, "-i", "--output");
1701               usage (EXIT_FAILURE);
1702             }
1703           if (posix_format && header_mode == DEFAULT_MODE)
1704             {
1705               error (0, 0, msg_mut_excl, "-P", "--output");
1706               usage (EXIT_FAILURE);
1707             }
1708           if (print_type)
1709             {
1710               error (0, 0, msg_mut_excl, "-T", "--output");
1711               usage (EXIT_FAILURE);
1712             }
1713           header_mode = OUTPUT_MODE;
1714           if (optarg)
1715             decode_output_arg (optarg);
1716           break;
1717 
1718         case TOTAL_OPTION:
1719           print_grand_total = true;
1720           break;
1721 
1722         case_GETOPT_HELP_CHAR;
1723         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1724 
1725         default:
1726           usage (EXIT_FAILURE);
1727         }
1728     }
1729 
1730   if (human_output_opts == -1)
1731     {
1732       if (posix_format)
1733         {
1734           human_output_opts = 0;
1735           output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
1736         }
1737       else
1738         human_options (getenv ("DF_BLOCK_SIZE"),
1739                        &human_output_opts, &output_block_size);
1740     }
1741 
1742   if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE)
1743     ;
1744   else if (human_output_opts & human_autoscale)
1745     header_mode = HUMAN_MODE;
1746   else if (posix_format)
1747     header_mode = POSIX_MODE;
1748 
1749   /* Fail if the same file system type was both selected and excluded.  */
1750   {
1751     bool match = false;
1752     struct fs_type_list *fs_incl;
1753     for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
1754       {
1755         struct fs_type_list *fs_excl;
1756         for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
1757           {
1758             if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
1759               {
1760                 error (0, 0,
1761                        _("file system type %s both selected and excluded"),
1762                        quote (fs_incl->fs_name));
1763                 match = true;
1764                 break;
1765               }
1766           }
1767       }
1768     if (match)
1769       return EXIT_FAILURE;
1770   }
1771 
1772   if (optind < argc)
1773     {
1774       /* stat each of the given entries to make sure any corresponding
1775          partition is automounted.  This must be done before reading the
1776          file system table.  */
1777       stats = xnmalloc (argc - optind, sizeof *stats);
1778       for (int i = optind; i < argc; ++i)
1779         {
1780           int err = automount_stat_err (argv[i], &stats[i - optind]);
1781           if (err != 0)
1782             {
1783               error (0, err, "%s", quotef (argv[i]));
1784               exit_status = EXIT_FAILURE;
1785               argv[i] = nullptr;
1786             }
1787         }
1788     }
1789 
1790   mount_list =
1791     read_file_system_list ((fs_select_list != nullptr
1792                             || fs_exclude_list != nullptr
1793                             || print_type
1794                             || field_data[FSTYPE_FIELD].used
1795                             || show_local_fs));
1796 
1797   if (mount_list == nullptr)
1798     {
1799       /* Couldn't read the table of mounted file systems.
1800          Fail if df was invoked with no file name arguments,
1801          or when either of -a, -l, -t or -x is used with file name
1802          arguments.  Otherwise, merely give a warning and proceed.  */
1803       int status = 0;
1804       if ( ! (optind < argc)
1805            || (show_all_fs
1806                || show_local_fs
1807                || fs_select_list != nullptr
1808                || fs_exclude_list != nullptr))
1809         {
1810           status = EXIT_FAILURE;
1811         }
1812       char const *warning = (status == 0 ? _("Warning: ") : "");
1813       error (status, errno, "%s%s", warning,
1814              _("cannot read table of mounted file systems"));
1815     }
1816 
1817   if (require_sync)
1818     sync ();
1819 
1820   get_field_list ();
1821   get_header ();
1822 
1823   if (stats)
1824     {
1825       /* Display explicitly requested empty file systems.  */
1826       show_listed_fs = true;
1827 
1828       for (int i = optind; i < argc; ++i)
1829         if (argv[i])
1830           get_entry (argv[i], &stats[i - optind]);
1831     }
1832   else
1833     get_all_entries ();
1834 
1835   if (file_systems_processed)
1836     {
1837       if (print_grand_total)
1838         get_dev ("total",
1839                  (field_data[SOURCE_FIELD].used ? "-" : "total"),
1840                  nullptr, nullptr, nullptr, false, false, &grand_fsu, false);
1841 
1842       print_table ();
1843     }
1844   else
1845     {
1846       /* Print the "no FS processed" diagnostic only if there was no preceding
1847          diagnostic, e.g., if all have been excluded.  */
1848       if (exit_status == EXIT_SUCCESS)
1849         error (EXIT_FAILURE, 0, _("no file systems processed"));
1850     }
1851 
1852   main_exit (exit_status);
1853 }
1854