kill: with -l,-t list signal 0
[coreutils.git] / src / df.c
blob66c84b8533b9c9f2357b63bf78f93e06a793d0a3
1 /* df - summarize free file system space
2 Copyright (C) 1991-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
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. */
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>
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 #include "xvasprintf.h"
41 /* The official name of this program (e.g., no 'g' prefix). */
42 #define PROGRAM_NAME "df"
44 #define AUTHORS \
45 proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
46 proper_name ("David MacKenzie"), \
47 proper_name ("Paul Eggert")
49 struct devlist
51 dev_t dev_num;
52 struct mount_entry *me;
53 struct devlist *next;
54 struct devlist *seen_last; /* valid for hashed devlist entries only */
57 /* Filled with device numbers of examined file systems to avoid
58 duplicates in output. */
59 static Hash_table *devlist_table;
61 /* If true, show even file systems with zero size or
62 uninteresting types. */
63 static bool show_all_fs;
65 /* If true, show only local file systems. */
66 static bool show_local_fs;
68 /* If true, output data for each file system corresponding to a
69 command line argument -- even if it's a dummy (automounter) entry. */
70 static bool show_listed_fs;
72 /* Human-readable options for output. */
73 static int human_output_opts;
75 /* The units to use when printing sizes. */
76 static uintmax_t output_block_size;
78 /* True if a file system has been processed for output. */
79 static bool file_systems_processed;
81 /* If true, invoke the 'sync' system call before getting any usage data.
82 Using this option can make df very slow, especially with many or very
83 busy file systems. This may make a difference on some systems --
84 SunOS 4.1.3, for one. It is *not* necessary on GNU/Linux. */
85 static bool require_sync;
87 /* Desired exit status. */
88 static int exit_status;
90 /* A file system type to display. */
92 struct fs_type_list
94 char *fs_name;
95 struct fs_type_list *fs_next;
98 /* Linked list of file system types to display.
99 If 'fs_select_list' is null, list all types.
100 This table is generated dynamically from command-line options,
101 rather than hardcoding into the program what it thinks are the
102 valid file system types; let the user specify any file system type
103 they want to, and if there are any file systems of that type, they
104 will be shown.
106 Some file system types:
107 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
109 static struct fs_type_list *fs_select_list;
111 /* Linked list of file system types to omit.
112 If the list is empty, don't exclude any types. */
114 static struct fs_type_list *fs_exclude_list;
116 /* Linked list of mounted file systems. */
117 static struct mount_entry *mount_list;
119 /* If true, print file system type as well. */
120 static bool print_type;
122 /* If true, print a grand total at the end. */
123 static bool print_grand_total;
125 /* Grand total data. */
126 static struct fs_usage grand_fsu;
128 /* Display modes. */
129 static enum
131 DEFAULT_MODE,
132 INODES_MODE,
133 HUMAN_MODE,
134 POSIX_MODE,
135 OUTPUT_MODE
136 } header_mode = DEFAULT_MODE;
138 /* Displayable fields. */
139 typedef enum
141 SOURCE_FIELD, /* file system */
142 FSTYPE_FIELD, /* FS type */
143 SIZE_FIELD, /* FS size */
144 USED_FIELD, /* FS size used */
145 AVAIL_FIELD, /* FS size available */
146 PCENT_FIELD, /* percent used */
147 ITOTAL_FIELD, /* inode total */
148 IUSED_FIELD, /* inodes used */
149 IAVAIL_FIELD, /* inodes available */
150 IPCENT_FIELD, /* inodes used in percent */
151 TARGET_FIELD, /* mount point */
152 FILE_FIELD, /* specified file name */
153 INVALID_FIELD /* validation marker */
154 } display_field_t;
156 /* Flag if a field contains a block, an inode or another value. */
157 typedef enum
159 BLOCK_FLD, /* Block values field */
160 INODE_FLD, /* Inode values field */
161 OTHER_FLD /* Neutral field, e.g. target */
162 } field_type_t;
164 /* Attributes of a display field. */
165 struct field_data_t
167 display_field_t field;
168 char const *arg;
169 field_type_t field_type;
170 char const *caption;/* nullptr means use default header of this field. */
171 int width; /* Auto adjusted (up) widths used to align columns. */
172 bool align_right; /* Whether to right-align columns, not left-align. */
173 bool used;
176 /* Header strings, minimum width and alignment for the above fields. */
177 static struct field_data_t field_data[] = {
178 [SOURCE_FIELD] = { SOURCE_FIELD,
179 "source", OTHER_FLD, N_("Filesystem"), 14, false, false },
181 [FSTYPE_FIELD] = { FSTYPE_FIELD,
182 "fstype", OTHER_FLD, N_("Type"), 4, false, false },
184 [SIZE_FIELD] = { SIZE_FIELD,
185 "size", BLOCK_FLD, N_("blocks"), 5, true, false },
187 [USED_FIELD] = { USED_FIELD,
188 "used", BLOCK_FLD, N_("Used"), 5, true, false },
190 [AVAIL_FIELD] = { AVAIL_FIELD,
191 "avail", BLOCK_FLD, N_("Available"), 5, true, false },
193 [PCENT_FIELD] = { PCENT_FIELD,
194 "pcent", BLOCK_FLD, N_("Use%"), 4, true, false },
196 [ITOTAL_FIELD] = { ITOTAL_FIELD,
197 "itotal", INODE_FLD, N_("Inodes"), 5, true, false },
199 [IUSED_FIELD] = { IUSED_FIELD,
200 "iused", INODE_FLD, N_("IUsed"), 5, true, false },
202 [IAVAIL_FIELD] = { IAVAIL_FIELD,
203 "iavail", INODE_FLD, N_("IFree"), 5, true, false },
205 [IPCENT_FIELD] = { IPCENT_FIELD,
206 "ipcent", INODE_FLD, N_("IUse%"), 4, true, false },
208 [TARGET_FIELD] = { TARGET_FIELD,
209 "target", OTHER_FLD, N_("Mounted on"), 0, false, false },
211 [FILE_FIELD] = { FILE_FIELD,
212 "file", OTHER_FLD, N_("File"), 0, false, false }
215 static char const *all_args_string =
216 "source,fstype,itotal,iused,iavail,ipcent,size,"
217 "used,avail,pcent,file,target";
219 /* Storage for the definition of output columns. */
220 static struct field_data_t **columns;
222 /* The current and allocated number of output columns. */
223 static idx_t ncolumns, ncolumns_alloc;
225 /* Field values. */
226 struct field_values_t
228 uintmax_t input_units;
229 uintmax_t output_units;
230 uintmax_t total;
231 uintmax_t available;
232 bool negate_available;
233 uintmax_t available_to_root;
234 uintmax_t used;
235 bool negate_used;
238 /* Storage for pointers for each string (cell of table). */
239 static char ***table;
241 /* The current number of processed rows (including header),
242 and the number of slots allocated for rows. */
243 static idx_t nrows, nrows_alloc;
245 /* For long options that have no equivalent short option, use a
246 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
247 enum
249 NO_SYNC_OPTION = CHAR_MAX + 1,
250 SYNC_OPTION,
251 TOTAL_OPTION,
252 OUTPUT_OPTION
255 static struct option const long_options[] =
257 {"all", no_argument, nullptr, 'a'},
258 {"block-size", required_argument, nullptr, 'B'},
259 {"inodes", no_argument, nullptr, 'i'},
260 {"human-readable", no_argument, nullptr, 'h'},
261 {"si", no_argument, nullptr, 'H'},
262 {"local", no_argument, nullptr, 'l'},
263 {"output", optional_argument, nullptr, OUTPUT_OPTION},
264 {"portability", no_argument, nullptr, 'P'},
265 {"print-type", no_argument, nullptr, 'T'},
266 {"sync", no_argument, nullptr, SYNC_OPTION},
267 {"no-sync", no_argument, nullptr, NO_SYNC_OPTION},
268 {"total", no_argument, nullptr, TOTAL_OPTION},
269 {"type", required_argument, nullptr, 't'},
270 {"exclude-type", required_argument, nullptr, 'x'},
271 {GETOPT_HELP_OPTION_DECL},
272 {GETOPT_VERSION_OPTION_DECL},
273 {nullptr, 0, nullptr, 0}
276 /* Stat FILE and put the results into *ST. Return 0 if successful, an
277 error number otherwise. Try to open FILE before statting, to
278 trigger automounts. */
280 static int
281 automount_stat_err (char const *file, struct stat *st)
283 int fd = open (file, O_RDONLY | O_NOCTTY | O_NONBLOCK);
284 if (fd < 0)
286 if (errno == ENOENT || errno == ENOTDIR)
287 return errno;
288 return stat (file, st) == 0 ? 0 : errno;
290 else
292 int err = fstat (fd, st) == 0 ? 0 : errno;
293 close (fd);
294 return err;
298 enum { MBSWIDTH_FLAGS = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE };
300 /* Replace problematic chars with '?'.
301 Since only control characters are currently considered,
302 this should work in all encodings. */
304 static void
305 replace_control_chars (char *cell)
307 char *p = cell;
308 while (*p)
310 if (c_iscntrl (*p))
311 *p = '?';
312 p++;
316 /* Replace problematic chars with '?'. */
318 static void
319 replace_invalid_chars (char *cell)
321 char *srcend = cell + strlen (cell);
322 char *dst = cell;
323 mbstate_t mbstate; mbszero (&mbstate);
324 size_t n;
326 for (char *src = cell; src != srcend; src += n)
328 char32_t wc;
329 size_t srcbytes = srcend - src;
330 n = mbrtoc32 (&wc, src, srcbytes, &mbstate);
331 bool ok = n <= srcbytes;
333 if (ok)
334 ok = !c32iscntrl (wc);
335 else
336 n = 1;
338 if (ok)
340 memmove (dst, src, n);
341 dst += n;
343 else
345 *dst++ = '?';
346 mbszero (&mbstate);
350 *dst = '\0';
353 static void
354 replace_problematic_chars (char *cell)
356 static int tty_out = -1;
357 if (tty_out < 0)
358 tty_out = isatty (STDOUT_FILENO);
360 (tty_out ? replace_invalid_chars : replace_control_chars) (cell) ;
364 /* Dynamically allocate a row of pointers in TABLE, which
365 can then be accessed with standard 2D array notation. */
367 static void
368 alloc_table_row (void)
370 if (nrows == nrows_alloc)
371 table = xpalloc (table, &nrows_alloc, 1, -1, sizeof *table);
372 table[nrows++] = xinmalloc (ncolumns, sizeof *table[0]);
375 /* Output each cell in the table, accounting for the
376 alignment and max width of each column. */
378 static void
379 print_table (void)
381 for (idx_t row = 0; row < nrows; row++)
383 for (idx_t col = 0; col < ncolumns; col++)
385 char *cell = table[row][col];
387 /* Note the SOURCE_FIELD used to be displayed on it's own line
388 if (!posix_format && mbswidth (cell) > 20), but that
389 functionality was probably more problematic than helpful,
390 hence changed in commit v8.10-40-g99679ff. */
391 if (col != 0)
392 putchar (' ');
394 int width = mbswidth (cell, MBSWIDTH_FLAGS);
395 int fill = width < 0 ? 0 : columns[col]->width - width;
396 if (columns[col]->align_right)
397 for (; 0 < fill; fill--)
398 putchar (' ');
399 fputs (cell, stdout);
400 if (col + 1 < ncolumns)
401 for (; 0 < fill; fill--)
402 putchar (' ');
404 putchar ('\n');
408 /* Dynamically allocate a struct field_t in COLUMNS, which
409 can then be accessed with standard array notation. */
411 static void
412 alloc_field (int f, char const *c)
414 if (ncolumns == ncolumns_alloc)
415 columns = xpalloc (columns, &ncolumns_alloc, 1, -1, sizeof *columns);
416 columns[ncolumns++] = &field_data[f];
417 if (c != nullptr)
418 field_data[f].caption = c;
420 affirm (!field_data[f].used);
422 /* Mark field as used. */
423 field_data[f].used = true;
427 /* Given a string, ARG, containing a comma-separated list of arguments
428 to the --output option, add the appropriate fields to columns. */
429 static void
430 decode_output_arg (char const *arg)
432 char *arg_writable = xstrdup (arg);
433 char *s = arg_writable;
436 /* find next comma */
437 char *comma = strchr (s, ',');
439 /* If we found a comma, put a NUL in its place and advance. */
440 if (comma)
441 *comma++ = 0;
443 /* process S. */
444 display_field_t field = INVALID_FIELD;
445 for (idx_t i = 0; i < ARRAY_CARDINALITY (field_data); i++)
447 if (STREQ (field_data[i].arg, s))
449 field = i;
450 break;
453 if (field == INVALID_FIELD)
455 error (0, 0, _("option --output: field %s unknown"), quote (s));
456 usage (EXIT_FAILURE);
459 if (field_data[field].used)
461 /* Prevent the fields from being used more than once. */
462 error (0, 0, _("option --output: field %s used more than once"),
463 quote (field_data[field].arg));
464 usage (EXIT_FAILURE);
467 switch (field)
469 case SOURCE_FIELD:
470 case FSTYPE_FIELD:
471 case USED_FIELD:
472 case PCENT_FIELD:
473 case ITOTAL_FIELD:
474 case IUSED_FIELD:
475 case IAVAIL_FIELD:
476 case IPCENT_FIELD:
477 case TARGET_FIELD:
478 case FILE_FIELD:
479 alloc_field (field, nullptr);
480 break;
482 case SIZE_FIELD:
483 alloc_field (field, N_("Size"));
484 break;
486 case AVAIL_FIELD:
487 alloc_field (field, N_("Avail"));
488 break;
490 default:
491 affirm (!"invalid field");
493 s = comma;
495 while (s);
497 free (arg_writable);
500 /* Get the appropriate columns for the mode. */
501 static void
502 get_field_list (void)
504 switch (header_mode)
506 case DEFAULT_MODE:
507 alloc_field (SOURCE_FIELD, nullptr);
508 if (print_type)
509 alloc_field (FSTYPE_FIELD, nullptr);
510 alloc_field (SIZE_FIELD, nullptr);
511 alloc_field (USED_FIELD, nullptr);
512 alloc_field (AVAIL_FIELD, nullptr);
513 alloc_field (PCENT_FIELD, nullptr);
514 alloc_field (TARGET_FIELD, nullptr);
515 break;
517 case HUMAN_MODE:
518 alloc_field (SOURCE_FIELD, nullptr);
519 if (print_type)
520 alloc_field (FSTYPE_FIELD, nullptr);
522 alloc_field (SIZE_FIELD, N_("Size"));
523 alloc_field (USED_FIELD, nullptr);
524 alloc_field (AVAIL_FIELD, N_("Avail"));
525 alloc_field (PCENT_FIELD, nullptr);
526 alloc_field (TARGET_FIELD, nullptr);
527 break;
529 case INODES_MODE:
530 alloc_field (SOURCE_FIELD, nullptr);
531 if (print_type)
532 alloc_field (FSTYPE_FIELD, nullptr);
533 alloc_field (ITOTAL_FIELD, nullptr);
534 alloc_field (IUSED_FIELD, nullptr);
535 alloc_field (IAVAIL_FIELD, nullptr);
536 alloc_field (IPCENT_FIELD, nullptr);
537 alloc_field (TARGET_FIELD, nullptr);
538 break;
540 case POSIX_MODE:
541 alloc_field (SOURCE_FIELD, nullptr);
542 if (print_type)
543 alloc_field (FSTYPE_FIELD, nullptr);
544 alloc_field (SIZE_FIELD, nullptr);
545 alloc_field (USED_FIELD, nullptr);
546 alloc_field (AVAIL_FIELD, nullptr);
547 alloc_field (PCENT_FIELD, N_("Capacity"));
548 alloc_field (TARGET_FIELD, nullptr);
549 break;
551 case OUTPUT_MODE:
552 if (!ncolumns)
554 /* Add all fields if --output was given without a field list. */
555 decode_output_arg (all_args_string);
557 break;
559 default:
560 unreachable ();
564 /* Obtain the appropriate header entries. */
566 static void
567 get_header (void)
569 alloc_table_row ();
571 for (idx_t col = 0; col < ncolumns; col++)
573 char *cell;
574 char const *header = _(columns[col]->caption);
576 if (columns[col]->field == SIZE_FIELD
577 && (header_mode == DEFAULT_MODE
578 || (header_mode == OUTPUT_MODE
579 && !(human_output_opts & human_autoscale))))
581 char buf[LONGEST_HUMAN_READABLE + 1];
583 int opts = (human_suppress_point_zero
584 | human_autoscale | human_SI
585 | (human_output_opts
586 & (human_group_digits | human_base_1024 | human_B)));
588 /* Prefer the base that makes the human-readable value more exact,
589 if there is a difference. */
591 uintmax_t q1000 = output_block_size;
592 uintmax_t q1024 = output_block_size;
593 bool divisible_by_1000;
594 bool divisible_by_1024;
598 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
599 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
601 while (divisible_by_1000 & divisible_by_1024);
603 if (divisible_by_1000 < divisible_by_1024)
604 opts |= human_base_1024;
605 if (divisible_by_1024 < divisible_by_1000)
606 opts &= ~human_base_1024;
607 if (! (opts & human_base_1024))
608 opts |= human_B;
610 char *num = human_readable (output_block_size, buf, opts, 1, 1);
612 /* Reset the header back to the default in OUTPUT_MODE. */
613 header = _("blocks");
615 /* TRANSLATORS: this is the "1K-blocks" header in "df" output. */
616 cell = xasprintf (_("%s-%s"), num, header);
618 else if (header_mode == POSIX_MODE && columns[col]->field == SIZE_FIELD)
620 char buf[INT_BUFSIZE_BOUND (uintmax_t)];
621 char *num = umaxtostr (output_block_size, buf);
623 /* TRANSLATORS: this is the "1024-blocks" header in "df -P". */
624 cell = xasprintf (_("%s-%s"), num, header);
626 else
627 cell = xstrdup (header);
629 replace_problematic_chars (cell);
631 table[nrows - 1][col] = cell;
633 int cell_width = mbswidth (cell, MBSWIDTH_FLAGS);
634 columns[col]->width = MAX (columns[col]->width, cell_width);
638 /* Is FSTYPE a type of file system that should be listed? */
640 ATTRIBUTE_PURE
641 static bool
642 selected_fstype (char const *fstype)
644 const struct fs_type_list *fsp;
646 if (fs_select_list == nullptr || fstype == nullptr)
647 return true;
648 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
649 if (STREQ (fstype, fsp->fs_name))
650 return true;
651 return false;
654 /* Is FSTYPE a type of file system that should be omitted? */
656 ATTRIBUTE_PURE
657 static bool
658 excluded_fstype (char const *fstype)
660 const struct fs_type_list *fsp;
662 if (fs_exclude_list == nullptr || fstype == nullptr)
663 return false;
664 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
665 if (STREQ (fstype, fsp->fs_name))
666 return true;
667 return false;
670 static size_t
671 devlist_hash (void const *x, size_t table_size)
673 struct devlist const *p = x;
674 return (uintmax_t) p->dev_num % table_size;
677 static bool
678 devlist_compare (void const *x, void const *y)
680 struct devlist const *a = x;
681 struct devlist const *b = y;
682 return a->dev_num == b->dev_num;
685 static struct devlist *
686 devlist_for_dev (dev_t dev)
688 if (devlist_table == nullptr)
689 return nullptr;
690 struct devlist dev_entry;
691 dev_entry.dev_num = dev;
693 struct devlist *found = hash_lookup (devlist_table, &dev_entry);
694 if (found == nullptr)
695 return nullptr;
697 /* Return the last devlist entry we have seen with this dev_num */
698 return found->seen_last;
701 /* Filter mount list by skipping duplicate entries.
702 In the case of duplicates - based on the device number - the mount entry
703 with a '/' in its me_devname (i.e., not pseudo name like tmpfs) wins.
704 If both have a real devname (e.g. bind mounts), then that with the shorter
705 me_mountdir wins. With DEVICES_ONLY == true (set with df -a), only update
706 the global devlist_table, rather than filtering the global mount_list. */
708 static void
709 filter_mount_list (bool devices_only)
711 struct mount_entry *me;
713 /* Temporary list to keep entries ordered. */
714 struct devlist *device_list = nullptr;
715 int mount_list_size = 0;
717 for (me = mount_list; me; me = me->me_next)
718 mount_list_size++;
720 devlist_table = hash_initialize (mount_list_size, nullptr,
721 devlist_hash, devlist_compare, nullptr);
722 if (devlist_table == nullptr)
723 xalloc_die ();
725 /* Sort all 'wanted' entries into the list device_list. */
726 for (me = mount_list; me;)
728 struct stat buf;
729 struct mount_entry *discard_me = nullptr;
731 /* Avoid stating remote file systems as that may hang.
732 On Linux we probably have me_dev populated from /proc/self/mountinfo,
733 however we still stat() in case another device was mounted later. */
734 if ((me->me_remote && show_local_fs)
735 || (me->me_dummy && !show_all_fs && !show_listed_fs)
736 || (!selected_fstype (me->me_type) || excluded_fstype (me->me_type))
737 || -1 == stat (me->me_mountdir, &buf))
739 /* If remote, and showing just local, or FS type is excluded,
740 add ME for filtering later.
741 If stat failed; add ME to be able to complain about it later. */
742 buf.st_dev = me->me_dev;
744 else
746 /* If we've already seen this device... */
747 struct devlist *seen_dev = devlist_for_dev (buf.st_dev);
749 if (seen_dev)
751 bool target_nearer_root = strlen (seen_dev->me->me_mountdir)
752 > strlen (me->me_mountdir);
753 /* With bind mounts, prefer items nearer the root of the source */
754 bool source_below_root = seen_dev->me->me_mntroot != nullptr
755 && me->me_mntroot != nullptr
756 && (strlen (seen_dev->me->me_mntroot)
757 < strlen (me->me_mntroot));
758 if (! print_grand_total
759 && me->me_remote && seen_dev->me->me_remote
760 && ! STREQ (seen_dev->me->me_devname, me->me_devname))
762 /* Don't discard remote entries with different locations,
763 as these are more likely to be explicitly mounted.
764 However avoid this when producing a total to give
765 a more accurate value in that case. */
767 else if ((strchr (me->me_devname, '/')
768 /* let "real" devices with '/' in the name win. */
769 && ! strchr (seen_dev->me->me_devname, '/'))
770 /* let points towards the root of the device win. */
771 || (target_nearer_root && ! source_below_root)
772 /* let an entry overmounted on a new device win... */
773 || (! STREQ (seen_dev->me->me_devname, me->me_devname)
774 /* ... but only when matching an existing mnt point,
775 to avoid problematic replacement when given
776 inaccurate mount lists, seen with some chroot
777 environments for example. */
778 && STREQ (me->me_mountdir,
779 seen_dev->me->me_mountdir)))
781 /* Discard mount entry for existing device. */
782 discard_me = seen_dev->me;
783 seen_dev->me = me;
785 else
787 /* Discard mount entry currently being processed. */
788 discard_me = me;
794 if (discard_me)
796 me = me->me_next;
797 if (! devices_only)
798 free_mount_entry (discard_me);
800 else
802 /* Add the device number to the device_table. */
803 struct devlist *devlist = xmalloc (sizeof *devlist);
804 devlist->me = me;
805 devlist->dev_num = buf.st_dev;
806 devlist->next = device_list;
807 device_list = devlist;
809 struct devlist *hash_entry = hash_insert (devlist_table, devlist);
810 if (hash_entry == nullptr)
811 xalloc_die ();
812 /* Ensure lookups use this latest devlist. */
813 hash_entry->seen_last = devlist;
815 me = me->me_next;
819 /* Finally rebuild the mount_list from the devlist. */
820 if (! devices_only) {
821 mount_list = nullptr;
822 while (device_list)
824 /* Add the mount entry. */
825 me = device_list->me;
826 me->me_next = mount_list;
827 mount_list = me;
828 struct devlist *next = device_list->next;
829 free (device_list);
830 device_list = next;
833 hash_free (devlist_table);
834 devlist_table = nullptr;
839 /* Search a mount entry list for device id DEV.
840 Return the corresponding mount entry if found or nullptr if not. */
842 ATTRIBUTE_PURE
843 static struct mount_entry const *
844 me_for_dev (dev_t dev)
846 struct devlist *dl = devlist_for_dev (dev);
847 if (dl)
848 return dl->me;
850 return nullptr;
853 /* Return true if N is a known integer value. On many file systems,
854 UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
855 represents unknown. Use a rule that works on AIX file systems, and
856 that almost-always works on other types. */
857 static bool
858 known_value (uintmax_t n)
860 return n < UINTMAX_MAX - 1;
863 /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
864 except:
866 - If NEGATIVE, then N represents a negative number,
867 expressed in two's complement.
868 - Otherwise, return "-" if N is unknown. */
870 static char const *
871 df_readable (bool negative, uintmax_t n, char *buf,
872 uintmax_t input_units, uintmax_t output_units)
874 if (! known_value (n) && !negative)
875 return "-";
876 else
878 char *p = human_readable (negative ? -n : n, buf + negative,
879 human_output_opts, input_units, output_units);
880 if (negative)
881 *--p = '-';
882 return p;
886 /* Add integral value while using uintmax_t for value part and separate
887 negation flag. It adds value of SRC and SRC_NEG to DEST and DEST_NEG.
888 The result will be in DEST and DEST_NEG. See df_readable to understand
889 how the negation flag is used. */
890 static void
891 add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
892 uintmax_t src, bool src_neg)
894 if (*dest_neg == src_neg)
896 *dest += src;
897 return;
900 if (*dest_neg)
901 *dest = -*dest;
903 if (src_neg)
904 src = -src;
906 if (src < *dest)
907 *dest -= src;
908 else
910 *dest = src - *dest;
911 *dest_neg = src_neg;
914 if (*dest_neg)
915 *dest = -*dest;
918 /* Return true if S ends in a string that may be a 36-byte UUID,
919 i.e., of the form HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH, where
920 each H is an upper or lower case hexadecimal digit. */
921 ATTRIBUTE_PURE
922 static bool
923 has_uuid_suffix (char const *s)
925 size_t len = strlen (s);
926 return (36 < len
927 && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36);
930 /* Obtain the block values BV and inode values IV
931 from the file system usage FSU. */
932 static void
933 get_field_values (struct field_values_t *bv,
934 struct field_values_t *iv,
935 const struct fs_usage *fsu)
937 /* Inode values. */
938 iv->input_units = iv->output_units = 1;
939 iv->total = fsu->fsu_files;
940 iv->available = iv->available_to_root = fsu->fsu_ffree;
941 iv->negate_available = false;
943 iv->used = UINTMAX_MAX;
944 iv->negate_used = false;
945 if (known_value (iv->total) && known_value (iv->available_to_root))
947 iv->used = iv->total - iv->available_to_root;
948 iv->negate_used = (iv->total < iv->available_to_root);
951 /* Block values. */
952 bv->input_units = fsu->fsu_blocksize;
953 bv->output_units = output_block_size;
954 bv->total = fsu->fsu_blocks;
955 bv->available = fsu->fsu_bavail;
956 bv->available_to_root = fsu->fsu_bfree;
957 bv->negate_available = (fsu->fsu_bavail_top_bit_set
958 && known_value (fsu->fsu_bavail));
960 bv->used = UINTMAX_MAX;
961 bv->negate_used = false;
962 if (known_value (bv->total) && known_value (bv->available_to_root))
964 bv->used = bv->total - bv->available_to_root;
965 bv->negate_used = (bv->total < bv->available_to_root);
969 /* Add block and inode values to grand total. */
970 static void
971 add_to_grand_total (struct field_values_t *bv, struct field_values_t *iv)
973 if (known_value (iv->total))
974 grand_fsu.fsu_files += iv->total;
975 if (known_value (iv->available))
976 grand_fsu.fsu_ffree += iv->available;
978 if (known_value (bv->total))
979 grand_fsu.fsu_blocks += bv->input_units * bv->total;
980 if (known_value (bv->available_to_root))
981 grand_fsu.fsu_bfree += bv->input_units * bv->available_to_root;
982 if (known_value (bv->available))
983 add_uint_with_neg_flag (&grand_fsu.fsu_bavail,
984 &grand_fsu.fsu_bavail_top_bit_set,
985 bv->input_units * bv->available,
986 bv->negate_available);
989 /* Obtain a space listing for the device with absolute file name DEVICE.
990 If MOUNT_POINT is non-null, it is the name of the root of the
991 file system on DEVICE.
992 If STAT_FILE is non-null, it is the name of a file within the file
993 system that the user originally asked for; this provides better
994 diagnostics, and sometimes it provides better results on networked
995 file systems that give different free-space results depending on
996 where in the file system you probe.
997 If FSTYPE is non-null, it is the type of the file system on DEVICE.
998 If MOUNT_POINT is non-null, then DEVICE may be null -- certain systems may
999 not be able to produce statistics in this case.
1000 ME_DUMMY and ME_REMOTE are the mount entry flags.
1001 Caller must set PROCESS_ALL to true when iterating over all entries, as
1002 when df is invoked with no non-option argument. See below for details. */
1004 static void
1005 get_dev (char const *device, char const *mount_point, char const *file,
1006 char const *stat_file, char const *fstype,
1007 bool me_dummy, bool me_remote,
1008 const struct fs_usage *force_fsu,
1009 bool process_all)
1011 if (me_remote && show_local_fs)
1012 return;
1014 if (me_dummy && !show_all_fs && !show_listed_fs)
1015 return;
1017 if (!selected_fstype (fstype) || excluded_fstype (fstype))
1018 return;
1020 /* Ignore relative MOUNT_POINTs, which are present for example
1021 in /proc/mounts on Linux with network namespaces. */
1022 if (!force_fsu && mount_point && ! IS_ABSOLUTE_FILE_NAME (mount_point))
1023 return;
1025 /* If MOUNT_POINT is null, then the file system is not mounted, and this
1026 program reports on the file system that the special file is on.
1027 It would be better to report on the unmounted file system,
1028 but statfs doesn't do that on most systems. */
1029 if (!stat_file)
1030 stat_file = mount_point ? mount_point : device;
1032 struct fs_usage fsu;
1033 if (force_fsu)
1034 fsu = *force_fsu;
1035 else if (get_fs_usage (stat_file, device, &fsu))
1037 /* If we can't access a system provided entry due
1038 to it not being present (now), or due to permissions,
1039 just output placeholder values rather than failing. */
1040 if (process_all && (errno == EACCES || errno == ENOENT))
1042 if (! show_all_fs)
1043 return;
1045 fstype = "-";
1046 fsu.fsu_bavail_top_bit_set = false;
1047 fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
1048 fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
1050 else
1052 error (0, errno, "%s", quotef (stat_file));
1053 exit_status = EXIT_FAILURE;
1054 return;
1057 else if (process_all && show_all_fs)
1059 /* Ensure we don't output incorrect stats for over-mounted directories.
1060 Discard stats when the device name doesn't match. Though don't
1061 discard when used and current mount entries are both remote due
1062 to the possibility of aliased host names or exports. */
1063 struct stat sb;
1064 if (stat (stat_file, &sb) == 0)
1066 struct mount_entry const * dev_me = me_for_dev (sb.st_dev);
1067 if (dev_me && ! STREQ (dev_me->me_devname, device)
1068 && (! dev_me->me_remote || ! me_remote))
1070 fstype = "-";
1071 fsu.fsu_bavail_top_bit_set = false;
1072 fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
1073 fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
1078 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
1079 return;
1081 if (! force_fsu)
1082 file_systems_processed = true;
1084 alloc_table_row ();
1086 if (! device)
1087 device = "-"; /* unknown */
1089 if (! file)
1090 file = "-"; /* unspecified */
1092 char *dev_name = xstrdup (device);
1093 char *resolved_dev;
1095 /* On some systems, dev_name is a long-named symlink like
1096 /dev/disk/by-uuid/828fc648-9f30-43d8-a0b1-f7196a2edb66 pointing to a
1097 much shorter and more useful name like /dev/sda1. It may also look
1098 like /dev/mapper/luks-828fc648-9f30-43d8-a0b1-f7196a2edb66 and point to
1099 /dev/dm-0. When process_all is true and dev_name is a symlink whose
1100 name ends with a UUID use the resolved name instead. */
1101 if (process_all
1102 && has_uuid_suffix (dev_name)
1103 && (resolved_dev = canonicalize_filename_mode (dev_name, CAN_EXISTING)))
1105 free (dev_name);
1106 dev_name = resolved_dev;
1109 if (! fstype)
1110 fstype = "-"; /* unknown */
1112 struct field_values_t block_values;
1113 struct field_values_t inode_values;
1114 get_field_values (&block_values, &inode_values, &fsu);
1116 /* Add to grand total unless processing grand total line. */
1117 if (print_grand_total && ! force_fsu)
1118 add_to_grand_total (&block_values, &inode_values);
1120 for (idx_t col = 0; col < ncolumns; col++)
1122 char buf[LONGEST_HUMAN_READABLE + 2];
1123 char *cell;
1125 struct field_values_t *v;
1126 switch (columns[col]->field_type)
1128 case BLOCK_FLD:
1129 v = &block_values;
1130 break;
1131 case INODE_FLD:
1132 v = &inode_values;
1133 break;
1134 case OTHER_FLD:
1135 v = nullptr;
1136 break;
1137 default:
1138 affirm (!"bad field_type");
1141 switch (columns[col]->field)
1143 case SOURCE_FIELD:
1144 cell = xstrdup (dev_name);
1145 break;
1147 case FSTYPE_FIELD:
1148 cell = xstrdup (fstype);
1149 break;
1151 case SIZE_FIELD:
1152 case ITOTAL_FIELD:
1153 cell = xstrdup (df_readable (false, v->total, buf,
1154 v->input_units, v->output_units));
1155 break;
1157 case USED_FIELD:
1158 case IUSED_FIELD:
1159 cell = xstrdup (df_readable (v->negate_used, v->used, buf,
1160 v->input_units, v->output_units));
1161 break;
1163 case AVAIL_FIELD:
1164 case IAVAIL_FIELD:
1165 cell = xstrdup (df_readable (v->negate_available, v->available, buf,
1166 v->input_units, v->output_units));
1167 break;
1169 case PCENT_FIELD:
1170 case IPCENT_FIELD:
1172 double pct = -1;
1173 if (! known_value (v->used) || ! known_value (v->available))
1175 else if (!v->negate_used
1176 && v->used <= TYPE_MAXIMUM (uintmax_t) / 100
1177 && v->used + v->available != 0
1178 && (v->used + v->available < v->used)
1179 == v->negate_available)
1181 uintmax_t u100 = v->used * 100;
1182 uintmax_t nonroot_total = v->used + v->available;
1183 pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
1185 else
1187 /* The calculation cannot be done easily with integer
1188 arithmetic. Fall back on floating point. This can suffer
1189 from minor rounding errors, but doing it exactly requires
1190 multiple precision arithmetic, and it's not worth the
1191 aggravation. */
1192 double u = v->negate_used ? - (double) - v->used : v->used;
1193 double a = v->negate_available
1194 ? - (double) - v->available : v->available;
1195 double nonroot_total = u + a;
1196 if (nonroot_total)
1198 long int lipct = pct = u * 100 / nonroot_total;
1199 double ipct = lipct;
1201 /* Like 'pct = ceil (dpct);', but avoid ceil so that
1202 the math library needn't be linked. */
1203 if (ipct - 1 < pct && pct <= ipct + 1)
1204 pct = ipct + (ipct < pct);
1208 cell = pct < 0 ? xstrdup ("-") : xasprintf ("%.0f%%", pct);
1209 break;
1212 case FILE_FIELD:
1213 cell = xstrdup (file);
1214 break;
1216 case TARGET_FIELD:
1217 #ifdef HIDE_AUTOMOUNT_PREFIX
1218 /* Don't print the first directory name in MOUNT_POINT if it's an
1219 artifact of an automounter. This is a bit too aggressive to be
1220 the default. */
1221 if (STRNCMP_LIT (mount_point, "/auto/") == 0)
1222 mount_point += 5;
1223 else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0)
1224 mount_point += 8;
1225 #endif
1226 cell = xstrdup (mount_point);
1227 break;
1229 default:
1230 affirm (!"unhandled field");
1233 affirm (cell);
1235 replace_problematic_chars (cell);
1236 int cell_width = mbswidth (cell, MBSWIDTH_FLAGS);
1237 columns[col]->width = MAX (columns[col]->width, cell_width);
1238 table[nrows - 1][col] = cell;
1240 free (dev_name);
1243 /* Scan the mount list returning the _last_ device found for MOUNT.
1244 nullptr is returned if MOUNT not found. The result is malloced. */
1245 static char *
1246 last_device_for_mount (char const *mount)
1248 struct mount_entry const *me;
1249 struct mount_entry const *le = nullptr;
1251 for (me = mount_list; me; me = me->me_next)
1253 if (STREQ (me->me_mountdir, mount))
1254 le = me;
1257 if (le)
1259 char *devname = le->me_devname;
1260 char *canon_dev = canonicalize_file_name (devname);
1261 if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
1262 return canon_dev;
1263 free (canon_dev);
1264 return xstrdup (le->me_devname);
1266 else
1267 return nullptr;
1270 /* If DEVICE corresponds to a mount point, show its usage
1271 and return true. Otherwise, return false. */
1272 static bool
1273 get_device (char const *device)
1275 struct mount_entry const *me;
1276 struct mount_entry const *best_match = nullptr;
1277 bool best_match_accessible = false;
1278 bool eclipsed_device = false;
1279 char const *file = device;
1281 char *resolved = canonicalize_file_name (device);
1282 if (resolved && IS_ABSOLUTE_FILE_NAME (resolved))
1283 device = resolved;
1285 size_t best_match_len = SIZE_MAX;
1286 for (me = mount_list; me; me = me->me_next)
1288 /* TODO: Should cache canon_dev in the mount_entry struct. */
1289 char *devname = me->me_devname;
1290 char *canon_dev = canonicalize_file_name (me->me_devname);
1291 if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
1292 devname = canon_dev;
1294 if (STREQ (device, devname))
1296 char *last_device = last_device_for_mount (me->me_mountdir);
1297 eclipsed_device = last_device && ! STREQ (last_device, devname);
1298 size_t len = strlen (me->me_mountdir);
1300 if (! eclipsed_device
1301 && (! best_match_accessible || len < best_match_len))
1303 struct stat device_stats;
1304 bool this_match_accessible = false;
1306 if (stat (me->me_mountdir, &device_stats) == 0)
1307 best_match_accessible = this_match_accessible = true;
1309 if (this_match_accessible
1310 || (! best_match_accessible && len < best_match_len))
1312 best_match = me;
1313 if (len == 1) /* Traditional root. */
1315 free (last_device);
1316 free (canon_dev);
1317 break;
1319 else
1320 best_match_len = len;
1324 free (last_device);
1327 free (canon_dev);
1330 free (resolved);
1332 if (best_match)
1334 get_dev (best_match->me_devname, best_match->me_mountdir, file, nullptr,
1335 best_match->me_type, best_match->me_dummy,
1336 best_match->me_remote, nullptr, false);
1337 return true;
1339 else if (eclipsed_device)
1341 error (0, 0, _("cannot access %s: over-mounted by another device"),
1342 quoteaf (file));
1343 exit_status = EXIT_FAILURE;
1344 return true;
1347 return false;
1350 /* Figure out which device file or directory POINT is mounted on
1351 and show its device usage.
1352 STATP must be the result of 'stat (POINT, STATP)'. */
1353 static void
1354 get_point (char const *point, const struct stat *statp)
1356 struct stat device_stats;
1357 struct mount_entry *me;
1358 struct mount_entry const *best_match = nullptr;
1360 /* Calculate the real absolute file name for POINT, and use that to find
1361 the mount point. This avoids statting unavailable mount points,
1362 which can hang df. */
1363 char *resolved = canonicalize_file_name (point);
1364 if (resolved && resolved[0] == '/')
1366 size_t resolved_len = strlen (resolved);
1367 size_t best_match_len = 0;
1369 for (me = mount_list; me; me = me->me_next)
1371 if (!STREQ (me->me_type, "lofs")
1372 && (!best_match || best_match->me_dummy || !me->me_dummy))
1374 size_t len = strlen (me->me_mountdir);
1375 if (best_match_len <= len && len <= resolved_len
1376 && (len == 1 /* root file system */
1377 || ((len == resolved_len || resolved[len] == '/')
1378 && STREQ_LEN (me->me_mountdir, resolved, len))))
1380 best_match = me;
1381 best_match_len = len;
1386 free (resolved);
1387 if (best_match
1388 && (stat (best_match->me_mountdir, &device_stats) != 0
1389 || device_stats.st_dev != statp->st_dev))
1390 best_match = nullptr;
1392 if (! best_match)
1393 for (me = mount_list; me; me = me->me_next)
1395 if (me->me_dev == (dev_t) -1)
1397 if (stat (me->me_mountdir, &device_stats) == 0)
1398 me->me_dev = device_stats.st_dev;
1399 else
1401 /* Report only I/O errors. Other errors might be
1402 caused by shadowed mount points, which means POINT
1403 can't possibly be on this file system. */
1404 if (errno == EIO)
1406 error (0, errno, "%s", quotef (me->me_mountdir));
1407 exit_status = EXIT_FAILURE;
1410 /* So we won't try and fail repeatedly. */
1411 me->me_dev = (dev_t) -2;
1415 if (statp->st_dev == me->me_dev
1416 && !STREQ (me->me_type, "lofs")
1417 && (!best_match || best_match->me_dummy || !me->me_dummy))
1419 /* Skip bogus mtab entries. */
1420 if (stat (me->me_mountdir, &device_stats) != 0
1421 || device_stats.st_dev != me->me_dev)
1422 me->me_dev = (dev_t) -2;
1423 else
1424 best_match = me;
1428 if (best_match)
1429 get_dev (best_match->me_devname, best_match->me_mountdir, point, point,
1430 best_match->me_type, best_match->me_dummy, best_match->me_remote,
1431 nullptr, false);
1432 else
1434 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
1435 print as much info as we can; methods that require the device to be
1436 present will fail at a later point. */
1438 /* Find the actual mount point. */
1439 char *mp = find_mount_point (point, statp);
1440 if (mp)
1442 get_dev (nullptr, mp, point, nullptr, nullptr,
1443 false, false, nullptr, false);
1444 free (mp);
1449 /* Determine what kind of node NAME is and show the device usage
1450 for it. STATP is the results of 'stat' on NAME. */
1452 static void
1453 get_entry (char const *name, struct stat const *statp)
1455 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
1456 && get_device (name))
1457 return;
1459 get_point (name, statp);
1462 /* Show all mounted file systems, except perhaps those that are of
1463 an unselected type or are empty. */
1465 static void
1466 get_all_entries (void)
1468 struct mount_entry *me;
1470 filter_mount_list (show_all_fs);
1472 for (me = mount_list; me; me = me->me_next)
1473 get_dev (me->me_devname, me->me_mountdir, nullptr, nullptr, me->me_type,
1474 me->me_dummy, me->me_remote, nullptr, true);
1477 /* Add FSTYPE to the list of file system types to display. */
1479 static void
1480 add_fs_type (char const *fstype)
1482 struct fs_type_list *fsp;
1484 fsp = xmalloc (sizeof *fsp);
1485 fsp->fs_name = (char *) fstype;
1486 fsp->fs_next = fs_select_list;
1487 fs_select_list = fsp;
1490 /* Add FSTYPE to the list of file system types to be omitted. */
1492 static void
1493 add_excluded_fs_type (char const *fstype)
1495 struct fs_type_list *fsp;
1497 fsp = xmalloc (sizeof *fsp);
1498 fsp->fs_name = (char *) fstype;
1499 fsp->fs_next = fs_exclude_list;
1500 fs_exclude_list = fsp;
1503 void
1504 usage (int status)
1506 if (status != EXIT_SUCCESS)
1507 emit_try_help ();
1508 else
1510 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
1511 fputs (_("\
1512 Show information about the file system on which each FILE resides,\n\
1513 or all file systems by default.\n\
1514 "), stdout);
1516 emit_mandatory_arg_note ();
1518 /* TRANSLATORS: The thousands and decimal separators are best
1519 adjusted to an appropriate default for your locale. */
1520 fputs (_("\
1521 -a, --all include pseudo, duplicate, inaccessible file systems\n\
1522 -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
1523 '-BM' prints sizes in units of 1,048,576 bytes;\n\
1524 see SIZE format below\n\
1525 -h, --human-readable print sizes in powers of 1024 (e.g., 1023M)\n\
1526 -H, --si print sizes in powers of 1000 (e.g., 1.1G)\n\
1527 "), stdout);
1528 fputs (_("\
1529 -i, --inodes list inode information instead of block usage\n\
1530 -k like --block-size=1K\n\
1531 -l, --local limit listing to local file systems\n\
1532 --no-sync do not invoke sync before getting usage info (default)\
1534 "), stdout);
1535 fputs (_("\
1536 --output[=FIELD_LIST] use the output format defined by FIELD_LIST,\n\
1537 or print all fields if FIELD_LIST is omitted\n\
1538 "), stdout);
1539 fputs (_("\
1540 -P, --portability use the POSIX output format\n\
1541 "), stdout);
1542 fputs (_("\
1543 --sync invoke sync before getting usage info\n\
1544 "), stdout);
1545 fputs (_("\
1546 --total elide all entries insignificant to available space,\n\
1547 and produce a grand total\n\
1548 "), stdout);
1549 fputs (_("\
1550 -t, --type=TYPE limit listing to file systems of type TYPE\n\
1551 -T, --print-type print file system type\n\
1552 -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
1553 -v (ignored)\n\
1554 "), stdout);
1555 fputs (HELP_OPTION_DESCRIPTION, stdout);
1556 fputs (VERSION_OPTION_DESCRIPTION, stdout);
1557 emit_blocksize_note ("DF");
1558 emit_size_note ();
1559 fputs (_("\n\
1560 FIELD_LIST is a comma-separated list of columns to be included. Valid\n\
1561 field names are: 'source', 'fstype', 'itotal', 'iused', 'iavail', 'ipcent',\n\
1562 'size', 'used', 'avail', 'pcent', 'file' and 'target' (see info page).\n\
1563 "), stdout);
1564 emit_ancillary_info (PROGRAM_NAME);
1566 exit (status);
1570 main (int argc, char **argv)
1572 struct stat *stats = nullptr;
1574 initialize_main (&argc, &argv);
1575 set_program_name (argv[0]);
1576 setlocale (LC_ALL, "");
1577 bindtextdomain (PACKAGE, LOCALEDIR);
1578 textdomain (PACKAGE);
1580 atexit (close_stdout);
1582 fs_select_list = nullptr;
1583 fs_exclude_list = nullptr;
1584 show_all_fs = false;
1585 show_listed_fs = false;
1586 human_output_opts = -1;
1587 print_type = false;
1588 file_systems_processed = false;
1589 exit_status = EXIT_SUCCESS;
1590 print_grand_total = false;
1591 grand_fsu.fsu_blocksize = 1;
1593 /* If true, use the POSIX output format. */
1594 bool posix_format = false;
1596 char const *msg_mut_excl = _("options %s and %s are mutually exclusive");
1598 while (true)
1600 int oi = -1;
1601 int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options,
1602 &oi);
1603 if (c == -1)
1604 break;
1606 switch (c)
1608 case 'a':
1609 show_all_fs = true;
1610 break;
1611 case 'B':
1613 enum strtol_error e = human_options (optarg, &human_output_opts,
1614 &output_block_size);
1615 if (e != LONGINT_OK)
1616 xstrtol_fatal (e, oi, c, long_options, optarg);
1618 break;
1619 case 'i':
1620 if (header_mode == OUTPUT_MODE)
1622 error (0, 0, msg_mut_excl, "-i", "--output");
1623 usage (EXIT_FAILURE);
1625 header_mode = INODES_MODE;
1626 break;
1627 case 'h':
1628 human_output_opts = human_autoscale | human_SI | human_base_1024;
1629 output_block_size = 1;
1630 break;
1631 case 'H':
1632 human_output_opts = human_autoscale | human_SI;
1633 output_block_size = 1;
1634 break;
1635 case 'k':
1636 human_output_opts = 0;
1637 output_block_size = 1024;
1638 break;
1639 case 'l':
1640 show_local_fs = true;
1641 break;
1642 case 'm': /* obsolescent, exists for BSD compatibility */
1643 human_output_opts = 0;
1644 output_block_size = 1024 * 1024;
1645 break;
1646 case 'T':
1647 if (header_mode == OUTPUT_MODE)
1649 error (0, 0, msg_mut_excl, "-T", "--output");
1650 usage (EXIT_FAILURE);
1652 print_type = true;
1653 break;
1654 case 'P':
1655 if (header_mode == OUTPUT_MODE)
1657 error (0, 0, msg_mut_excl, "-P", "--output");
1658 usage (EXIT_FAILURE);
1660 posix_format = true;
1661 break;
1662 case SYNC_OPTION:
1663 require_sync = true;
1664 break;
1665 case NO_SYNC_OPTION:
1666 require_sync = false;
1667 break;
1669 case 'F':
1670 /* Accept -F as a synonym for -t for compatibility with Solaris. */
1671 case 't':
1672 add_fs_type (optarg);
1673 break;
1675 case 'v': /* For SysV compatibility. */
1676 /* ignore */
1677 break;
1678 case 'x':
1679 add_excluded_fs_type (optarg);
1680 break;
1682 case OUTPUT_OPTION:
1683 if (header_mode == INODES_MODE)
1685 error (0, 0, msg_mut_excl, "-i", "--output");
1686 usage (EXIT_FAILURE);
1688 if (posix_format && header_mode == DEFAULT_MODE)
1690 error (0, 0, msg_mut_excl, "-P", "--output");
1691 usage (EXIT_FAILURE);
1693 if (print_type)
1695 error (0, 0, msg_mut_excl, "-T", "--output");
1696 usage (EXIT_FAILURE);
1698 header_mode = OUTPUT_MODE;
1699 if (optarg)
1700 decode_output_arg (optarg);
1701 break;
1703 case TOTAL_OPTION:
1704 print_grand_total = true;
1705 break;
1707 case_GETOPT_HELP_CHAR;
1708 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
1710 default:
1711 usage (EXIT_FAILURE);
1715 if (human_output_opts == -1)
1717 if (posix_format)
1719 human_output_opts = 0;
1720 output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
1722 else
1723 human_options (getenv ("DF_BLOCK_SIZE"),
1724 &human_output_opts, &output_block_size);
1727 if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE)
1729 else if (human_output_opts & human_autoscale)
1730 header_mode = HUMAN_MODE;
1731 else if (posix_format)
1732 header_mode = POSIX_MODE;
1734 /* Fail if the same file system type was both selected and excluded. */
1736 bool match = false;
1737 struct fs_type_list *fs_incl;
1738 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
1740 struct fs_type_list *fs_excl;
1741 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
1743 if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
1745 error (0, 0,
1746 _("file system type %s both selected and excluded"),
1747 quote (fs_incl->fs_name));
1748 match = true;
1749 break;
1753 if (match)
1754 return EXIT_FAILURE;
1757 if (optind < argc)
1759 /* stat each of the given entries to make sure any corresponding
1760 partition is automounted. This must be done before reading the
1761 file system table. */
1762 stats = xnmalloc (argc - optind, sizeof *stats);
1763 for (int i = optind; i < argc; ++i)
1765 int err = automount_stat_err (argv[i], &stats[i - optind]);
1766 if (err != 0)
1768 error (0, err, "%s", quotef (argv[i]));
1769 exit_status = EXIT_FAILURE;
1770 argv[i] = nullptr;
1775 mount_list =
1776 read_file_system_list ((fs_select_list != nullptr
1777 || fs_exclude_list != nullptr
1778 || print_type
1779 || field_data[FSTYPE_FIELD].used
1780 || show_local_fs));
1782 if (mount_list == nullptr)
1784 /* Couldn't read the table of mounted file systems.
1785 Fail if df was invoked with no file name arguments,
1786 or when either of -a, -l, -t or -x is used with file name
1787 arguments. Otherwise, merely give a warning and proceed. */
1788 int status = 0;
1789 if ( ! (optind < argc)
1790 || (show_all_fs
1791 || show_local_fs
1792 || fs_select_list != nullptr
1793 || fs_exclude_list != nullptr))
1795 status = EXIT_FAILURE;
1797 char const *warning = (status == 0 ? _("Warning: ") : "");
1798 error (status, errno, "%s%s", warning,
1799 _("cannot read table of mounted file systems"));
1802 if (require_sync)
1803 sync ();
1805 get_field_list ();
1806 get_header ();
1808 if (stats)
1810 /* Display explicitly requested empty file systems. */
1811 show_listed_fs = true;
1813 for (int i = optind; i < argc; ++i)
1814 if (argv[i])
1815 get_entry (argv[i], &stats[i - optind]);
1817 else
1818 get_all_entries ();
1820 if (file_systems_processed)
1822 if (print_grand_total)
1823 get_dev ("total",
1824 (field_data[SOURCE_FIELD].used ? "-" : "total"),
1825 nullptr, nullptr, nullptr, false, false, &grand_fsu, false);
1827 print_table ();
1829 else
1831 /* Print the "no FS processed" diagnostic only if there was no preceding
1832 diagnostic, e.g., if all have been excluded. */
1833 if (exit_status == EXIT_SUCCESS)
1834 error (EXIT_FAILURE, 0, _("no file systems processed"));
1837 main_exit (exit_status);