| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| #include <config.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <getopt.h> |
| #include <c-ctype.h> |
|
|
| #include "system.h" |
| #include "assure.h" |
| #include "canonicalize.h" |
| #include "fsusage.h" |
| #include "human.h" |
| #include "mbswidth.h" |
| #include "mountlist.h" |
| #include "quote.h" |
| #include "find-mount-point.h" |
| #include "hash.h" |
| #include "xstrtol-error.h" |
| #include "xvasprintf.h" |
|
|
| |
| #define PROGRAM_NAME "df" |
|
|
| #define AUTHORS \ |
| proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \ |
| proper_name ("David MacKenzie"), \ |
| proper_name ("Paul Eggert") |
|
|
| struct devlist |
| { |
| dev_t dev_num; |
| struct mount_entry *me; |
| struct devlist *next; |
| struct devlist *seen_last; |
| }; |
|
|
| |
| |
| static Hash_table *devlist_table; |
|
|
| |
| |
| static bool show_all_fs; |
|
|
| |
| static bool show_local_fs; |
|
|
| |
| |
| static bool show_listed_fs; |
|
|
| |
| static int human_output_opts; |
|
|
| |
| static uintmax_t output_block_size; |
|
|
| |
| static bool file_systems_processed; |
|
|
| |
| |
| |
| |
| static bool require_sync; |
|
|
| |
| static int exit_status; |
|
|
| |
|
|
| struct fs_type_list |
| { |
| char *fs_name; |
| struct fs_type_list *fs_next; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static struct fs_type_list *fs_select_list; |
|
|
| |
| |
|
|
| static struct fs_type_list *fs_exclude_list; |
|
|
| |
| static struct mount_entry *mount_list; |
|
|
| |
| static bool print_type; |
|
|
| |
| static bool print_grand_total; |
|
|
| |
| static struct fs_usage grand_fsu; |
|
|
| |
| static enum |
| { |
| DEFAULT_MODE, |
| INODES_MODE, |
| HUMAN_MODE, |
| POSIX_MODE, |
| OUTPUT_MODE |
| } header_mode = DEFAULT_MODE; |
|
|
| |
| typedef enum |
| { |
| SOURCE_FIELD, |
| FSTYPE_FIELD, |
| SIZE_FIELD, |
| USED_FIELD, |
| AVAIL_FIELD, |
| PCENT_FIELD, |
| ITOTAL_FIELD, |
| IUSED_FIELD, |
| IAVAIL_FIELD, |
| IPCENT_FIELD, |
| TARGET_FIELD, |
| FILE_FIELD, |
| INVALID_FIELD |
| } display_field_t; |
|
|
| |
| typedef enum |
| { |
| BLOCK_FLD, |
| INODE_FLD, |
| OTHER_FLD |
| } field_type_t; |
|
|
| |
| struct field_data_t |
| { |
| display_field_t field; |
| char const *arg; |
| field_type_t field_type; |
| char const *caption; |
| int width; |
| bool align_right; |
| bool used; |
| }; |
|
|
| |
| static struct field_data_t field_data[] = { |
| [SOURCE_FIELD] = { SOURCE_FIELD, |
| "source", OTHER_FLD, N_("Filesystem"), 14, false, false }, |
|
|
| [FSTYPE_FIELD] = { FSTYPE_FIELD, |
| "fstype", OTHER_FLD, N_("Type"), 4, false, false }, |
|
|
| [SIZE_FIELD] = { SIZE_FIELD, |
| "size", BLOCK_FLD, N_("blocks"), 5, true, false }, |
|
|
| [USED_FIELD] = { USED_FIELD, |
| "used", BLOCK_FLD, N_("Used"), 5, true, false }, |
|
|
| [AVAIL_FIELD] = { AVAIL_FIELD, |
| "avail", BLOCK_FLD, N_("Available"), 5, true, false }, |
|
|
| [PCENT_FIELD] = { PCENT_FIELD, |
| "pcent", BLOCK_FLD, N_("Use%"), 4, true, false }, |
|
|
| [ITOTAL_FIELD] = { ITOTAL_FIELD, |
| "itotal", INODE_FLD, N_("Inodes"), 5, true, false }, |
|
|
| [IUSED_FIELD] = { IUSED_FIELD, |
| "iused", INODE_FLD, N_("IUsed"), 5, true, false }, |
|
|
| [IAVAIL_FIELD] = { IAVAIL_FIELD, |
| "iavail", INODE_FLD, N_("IFree"), 5, true, false }, |
|
|
| [IPCENT_FIELD] = { IPCENT_FIELD, |
| "ipcent", INODE_FLD, N_("IUse%"), 4, true, false }, |
|
|
| [TARGET_FIELD] = { TARGET_FIELD, |
| "target", OTHER_FLD, N_("Mounted on"), 0, false, false }, |
|
|
| [FILE_FIELD] = { FILE_FIELD, |
| "file", OTHER_FLD, N_("File"), 0, false, false } |
| }; |
|
|
| static char const *all_args_string = |
| "source,fstype,itotal,iused,iavail,ipcent,size," |
| "used,avail,pcent,file,target"; |
|
|
| |
| static struct field_data_t **columns; |
|
|
| |
| static idx_t ncolumns, ncolumns_alloc; |
|
|
| |
| struct field_values_t |
| { |
| uintmax_t input_units; |
| uintmax_t output_units; |
| uintmax_t total; |
| uintmax_t available; |
| bool negate_available; |
| uintmax_t available_to_root; |
| uintmax_t used; |
| bool negate_used; |
| }; |
|
|
| |
| static char ***table; |
|
|
| |
| |
| static idx_t nrows, nrows_alloc; |
|
|
| |
| |
| enum |
| { |
| NO_SYNC_OPTION = CHAR_MAX + 1, |
| SYNC_OPTION, |
| TOTAL_OPTION, |
| OUTPUT_OPTION |
| }; |
|
|
| static struct option const long_options[] = |
| { |
| {"all", no_argument, nullptr, 'a'}, |
| {"block-size", required_argument, nullptr, 'B'}, |
| {"inodes", no_argument, nullptr, 'i'}, |
| {"human-readable", no_argument, nullptr, 'h'}, |
| {"si", no_argument, nullptr, 'H'}, |
| {"local", no_argument, nullptr, 'l'}, |
| {"output", optional_argument, nullptr, OUTPUT_OPTION}, |
| {"portability", no_argument, nullptr, 'P'}, |
| {"print-type", no_argument, nullptr, 'T'}, |
| {"sync", no_argument, nullptr, SYNC_OPTION}, |
| {"no-sync", no_argument, nullptr, NO_SYNC_OPTION}, |
| {"total", no_argument, nullptr, TOTAL_OPTION}, |
| {"type", required_argument, nullptr, 't'}, |
| {"exclude-type", required_argument, nullptr, 'x'}, |
| {GETOPT_HELP_OPTION_DECL}, |
| {GETOPT_VERSION_OPTION_DECL}, |
| {nullptr, 0, nullptr, 0} |
| }; |
|
|
| |
| |
| |
|
|
| static int |
| automount_stat_err (char const *file, struct stat *st) |
| { |
| int fd = open (file, O_RDONLY | O_NOCTTY | O_NONBLOCK); |
| if (fd < 0) |
| { |
| if (errno == ENOENT || errno == ENOTDIR) |
| return errno; |
| return stat (file, st) == 0 ? 0 : errno; |
| } |
| else |
| { |
| int err = fstat (fd, st) == 0 ? 0 : errno; |
| close (fd); |
| return err; |
| } |
| } |
|
|
| enum { MBSWIDTH_FLAGS = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE }; |
|
|
| |
| |
| |
|
|
| static void |
| replace_control_chars (char *cell) |
| { |
| char *p = cell; |
| while (*p) |
| { |
| if (c_iscntrl (*p)) |
| *p = '?'; |
| p++; |
| } |
| } |
|
|
| |
|
|
| static void |
| replace_invalid_chars (char *cell) |
| { |
| char *srcend = cell + strlen (cell); |
| char *dst = cell; |
| mbstate_t mbstate; mbszero (&mbstate); |
| size_t n; |
|
|
| for (char *src = cell; src != srcend; src += n) |
| { |
| char32_t wc; |
| size_t srcbytes = srcend - src; |
| n = mbrtoc32 (&wc, src, srcbytes, &mbstate); |
| bool ok = n <= srcbytes; |
|
|
| if (ok) |
| ok = !c32iscntrl (wc); |
| else |
| n = 1; |
|
|
| if (ok) |
| { |
| memmove (dst, src, n); |
| dst += n; |
| } |
| else |
| { |
| *dst++ = '?'; |
| mbszero (&mbstate); |
| } |
| } |
|
|
| *dst = '\0'; |
| } |
|
|
| static void |
| replace_problematic_chars (char *cell) |
| { |
| static int tty_out = -1; |
| if (tty_out < 0) |
| tty_out = isatty (STDOUT_FILENO); |
|
|
| (tty_out ? replace_invalid_chars : replace_control_chars) (cell) ; |
| } |
|
|
|
|
| |
| |
|
|
| static void |
| alloc_table_row (void) |
| { |
| if (nrows == nrows_alloc) |
| table = xpalloc (table, &nrows_alloc, 1, -1, sizeof *table); |
| table[nrows++] = xinmalloc (ncolumns, sizeof *table[0]); |
| } |
|
|
| |
| |
|
|
| static void |
| print_table (void) |
| { |
| for (idx_t row = 0; row < nrows; row++) |
| { |
| for (idx_t col = 0; col < ncolumns; col++) |
| { |
| char *cell = table[row][col]; |
|
|
| |
| |
| |
| |
| if (col != 0) |
| putchar (' '); |
|
|
| int width = mbswidth (cell, MBSWIDTH_FLAGS); |
| int fill = width < 0 ? 0 : columns[col]->width - width; |
| if (columns[col]->align_right) |
| for (; 0 < fill; fill--) |
| putchar (' '); |
| fputs (cell, stdout); |
| if (col + 1 < ncolumns) |
| for (; 0 < fill; fill--) |
| putchar (' '); |
| } |
| putchar ('\n'); |
| } |
| } |
|
|
| |
| |
|
|
| static void |
| alloc_field (int f, char const *c) |
| { |
| if (ncolumns == ncolumns_alloc) |
| columns = xpalloc (columns, &ncolumns_alloc, 1, -1, sizeof *columns); |
| columns[ncolumns++] = &field_data[f]; |
| if (c != nullptr) |
| field_data[f].caption = c; |
|
|
| affirm (!field_data[f].used); |
|
|
| |
| field_data[f].used = true; |
| } |
|
|
|
|
| |
| |
| static void |
| decode_output_arg (char const *arg) |
| { |
| char *arg_writable = xstrdup (arg); |
| char *s = arg_writable; |
| do |
| { |
| |
| char *comma = strchr (s, ','); |
|
|
| |
| if (comma) |
| *comma++ = 0; |
|
|
| |
| display_field_t field = INVALID_FIELD; |
| for (idx_t i = 0; i < countof (field_data); i++) |
| { |
| if (streq (field_data[i].arg, s)) |
| { |
| field = i; |
| break; |
| } |
| } |
| if (field == INVALID_FIELD) |
| { |
| error (0, 0, _("option --output: field %s unknown"), quote (s)); |
| usage (EXIT_FAILURE); |
| } |
|
|
| if (field_data[field].used) |
| { |
| |
| error (0, 0, _("option --output: field %s used more than once"), |
| quote (field_data[field].arg)); |
| usage (EXIT_FAILURE); |
| } |
|
|
| switch (field) |
| { |
| case SOURCE_FIELD: |
| case FSTYPE_FIELD: |
| case USED_FIELD: |
| case PCENT_FIELD: |
| case ITOTAL_FIELD: |
| case IUSED_FIELD: |
| case IAVAIL_FIELD: |
| case IPCENT_FIELD: |
| case TARGET_FIELD: |
| case FILE_FIELD: |
| alloc_field (field, nullptr); |
| break; |
|
|
| case SIZE_FIELD: |
| alloc_field (field, N_("Size")); |
| break; |
|
|
| case AVAIL_FIELD: |
| alloc_field (field, N_("Avail")); |
| break; |
|
|
| case INVALID_FIELD: |
| default: |
| affirm (!"invalid field"); |
| } |
| s = comma; |
| } |
| while (s); |
|
|
| free (arg_writable); |
| } |
|
|
| |
| static void |
| get_field_list (void) |
| { |
| switch (header_mode) |
| { |
| case DEFAULT_MODE: |
| alloc_field (SOURCE_FIELD, nullptr); |
| if (print_type) |
| alloc_field (FSTYPE_FIELD, nullptr); |
| alloc_field (SIZE_FIELD, nullptr); |
| alloc_field (USED_FIELD, nullptr); |
| alloc_field (AVAIL_FIELD, nullptr); |
| alloc_field (PCENT_FIELD, nullptr); |
| alloc_field (TARGET_FIELD, nullptr); |
| break; |
|
|
| case HUMAN_MODE: |
| alloc_field (SOURCE_FIELD, nullptr); |
| if (print_type) |
| alloc_field (FSTYPE_FIELD, nullptr); |
|
|
| alloc_field (SIZE_FIELD, N_("Size")); |
| alloc_field (USED_FIELD, nullptr); |
| alloc_field (AVAIL_FIELD, N_("Avail")); |
| alloc_field (PCENT_FIELD, nullptr); |
| alloc_field (TARGET_FIELD, nullptr); |
| break; |
|
|
| case INODES_MODE: |
| alloc_field (SOURCE_FIELD, nullptr); |
| if (print_type) |
| alloc_field (FSTYPE_FIELD, nullptr); |
| alloc_field (ITOTAL_FIELD, nullptr); |
| alloc_field (IUSED_FIELD, nullptr); |
| alloc_field (IAVAIL_FIELD, nullptr); |
| alloc_field (IPCENT_FIELD, nullptr); |
| alloc_field (TARGET_FIELD, nullptr); |
| break; |
|
|
| case POSIX_MODE: |
| alloc_field (SOURCE_FIELD, nullptr); |
| if (print_type) |
| alloc_field (FSTYPE_FIELD, nullptr); |
| alloc_field (SIZE_FIELD, nullptr); |
| alloc_field (USED_FIELD, nullptr); |
| alloc_field (AVAIL_FIELD, nullptr); |
| alloc_field (PCENT_FIELD, N_("Capacity")); |
| alloc_field (TARGET_FIELD, nullptr); |
| break; |
|
|
| case OUTPUT_MODE: |
| if (!ncolumns) |
| { |
| |
| decode_output_arg (all_args_string); |
| } |
| break; |
|
|
| default: |
| unreachable (); |
| } |
| } |
|
|
| |
|
|
| static void |
| get_header (void) |
| { |
| alloc_table_row (); |
|
|
| for (idx_t col = 0; col < ncolumns; col++) |
| { |
| char *cell; |
| char const *header = _(columns[col]->caption); |
|
|
| if (columns[col]->field == SIZE_FIELD |
| && (header_mode == DEFAULT_MODE |
| || (header_mode == OUTPUT_MODE |
| && !(human_output_opts & human_autoscale)))) |
| { |
| char buf[LONGEST_HUMAN_READABLE + 1]; |
|
|
| int opts = (human_suppress_point_zero |
| | human_autoscale | human_SI |
| | (human_output_opts |
| & (human_group_digits | human_base_1024 | human_B))); |
|
|
| |
| |
|
|
| uintmax_t q1000 = output_block_size; |
| uintmax_t q1024 = output_block_size; |
| bool divisible_by_1000; |
| bool divisible_by_1024; |
|
|
| do |
| { |
| divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000; |
| divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024; |
| } |
| while (divisible_by_1000 & divisible_by_1024); |
|
|
| if (divisible_by_1000 < divisible_by_1024) |
| opts |= human_base_1024; |
| if (divisible_by_1024 < divisible_by_1000) |
| opts &= ~human_base_1024; |
| if (! (opts & human_base_1024)) |
| opts |= human_B; |
|
|
| char *num = human_readable (output_block_size, buf, opts, 1, 1); |
|
|
| |
| header = _("blocks"); |
|
|
| |
| cell = xasprintf (_("%s-%s"), num, header); |
| } |
| else if (header_mode == POSIX_MODE && columns[col]->field == SIZE_FIELD) |
| { |
| |
| cell = xasprintf (_("%ju-%s"), output_block_size, header); |
| } |
| else |
| cell = xstrdup (header); |
|
|
| replace_problematic_chars (cell); |
|
|
| table[nrows - 1][col] = cell; |
|
|
| int cell_width = mbswidth (cell, MBSWIDTH_FLAGS); |
| columns[col]->width = MAX (columns[col]->width, cell_width); |
| } |
| } |
|
|
| |
|
|
| ATTRIBUTE_PURE |
| static bool |
| selected_fstype (char const *fstype) |
| { |
| const struct fs_type_list *fsp; |
|
|
| if (fs_select_list == nullptr || fstype == nullptr) |
| return true; |
| for (fsp = fs_select_list; fsp; fsp = fsp->fs_next) |
| if (streq (fstype, fsp->fs_name)) |
| return true; |
| return false; |
| } |
|
|
| |
|
|
| ATTRIBUTE_PURE |
| static bool |
| excluded_fstype (char const *fstype) |
| { |
| const struct fs_type_list *fsp; |
|
|
| if (fs_exclude_list == nullptr || fstype == nullptr) |
| return false; |
| for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next) |
| if (streq (fstype, fsp->fs_name)) |
| return true; |
| return false; |
| } |
|
|
| static size_t |
| devlist_hash (void const *x, size_t table_size) |
| { |
| struct devlist const *p = x; |
| return (uintmax_t) p->dev_num % table_size; |
| } |
|
|
| static bool |
| devlist_compare (void const *x, void const *y) |
| { |
| struct devlist const *a = x; |
| struct devlist const *b = y; |
| return a->dev_num == b->dev_num; |
| } |
|
|
| static struct devlist * |
| devlist_for_dev (dev_t dev) |
| { |
| if (devlist_table == nullptr) |
| return nullptr; |
| struct devlist dev_entry; |
| dev_entry.dev_num = dev; |
|
|
| struct devlist *found = hash_lookup (devlist_table, &dev_entry); |
| if (found == nullptr) |
| return nullptr; |
|
|
| |
| return found->seen_last; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| static void |
| filter_mount_list (bool devices_only) |
| { |
| struct mount_entry *me; |
|
|
| |
| struct devlist *device_list = nullptr; |
| int mount_list_size = 0; |
|
|
| for (me = mount_list; me; me = me->me_next) |
| mount_list_size++; |
|
|
| devlist_table = hash_initialize (mount_list_size, nullptr, |
| devlist_hash, devlist_compare, nullptr); |
| if (devlist_table == nullptr) |
| xalloc_die (); |
|
|
| |
| for (me = mount_list; me;) |
| { |
| struct stat buf; |
| struct mount_entry *discard_me = nullptr; |
|
|
| |
| |
| |
| if ((me->me_remote && show_local_fs) |
| || (me->me_dummy && !show_all_fs && !show_listed_fs) |
| || (!selected_fstype (me->me_type) || excluded_fstype (me->me_type)) |
| || -1 == stat (me->me_mountdir, &buf)) |
| { |
| |
| |
| |
| buf.st_dev = me->me_dev; |
| } |
| else |
| { |
| |
| struct devlist *seen_dev = devlist_for_dev (buf.st_dev); |
|
|
| if (seen_dev) |
| { |
| bool target_nearer_root = strlen (seen_dev->me->me_mountdir) |
| > strlen (me->me_mountdir); |
| |
| bool source_below_root = seen_dev->me->me_mntroot != nullptr |
| && me->me_mntroot != nullptr |
| && (strlen (seen_dev->me->me_mntroot) |
| < strlen (me->me_mntroot)); |
| if (! print_grand_total |
| && me->me_remote && seen_dev->me->me_remote |
| && ! streq (seen_dev->me->me_devname, me->me_devname)) |
| { |
| |
| |
| |
| |
| } |
| else if ((strchr (me->me_devname, '/') |
| |
| && ! strchr (seen_dev->me->me_devname, '/')) |
| |
| || (target_nearer_root && ! source_below_root) |
| |
| || (! streq (seen_dev->me->me_devname, me->me_devname) |
| |
| |
| |
| |
| && streq (me->me_mountdir, |
| seen_dev->me->me_mountdir))) |
| { |
| |
| discard_me = seen_dev->me; |
| seen_dev->me = me; |
| } |
| else |
| { |
| |
| discard_me = me; |
| } |
|
|
| } |
| } |
|
|
| if (discard_me) |
| { |
| me = me->me_next; |
| if (! devices_only) |
| free_mount_entry (discard_me); |
| } |
| else |
| { |
| |
| struct devlist *devlist = xmalloc (sizeof *devlist); |
| devlist->me = me; |
| devlist->dev_num = buf.st_dev; |
| devlist->next = device_list; |
| device_list = devlist; |
|
|
| struct devlist *hash_entry = hash_insert (devlist_table, devlist); |
| if (hash_entry == nullptr) |
| xalloc_die (); |
| |
| hash_entry->seen_last = devlist; |
|
|
| me = me->me_next; |
| } |
| } |
|
|
| |
| if (! devices_only) { |
| mount_list = nullptr; |
| while (device_list) |
| { |
| |
| me = device_list->me; |
| me->me_next = mount_list; |
| mount_list = me; |
| struct devlist *next = device_list->next; |
| free (device_list); |
| device_list = next; |
| } |
|
|
| hash_free (devlist_table); |
| devlist_table = nullptr; |
| } |
| } |
|
|
|
|
| |
| |
|
|
| ATTRIBUTE_PURE |
| static struct mount_entry const * |
| me_for_dev (dev_t dev) |
| { |
| struct devlist *dl = devlist_for_dev (dev); |
| if (dl) |
| return dl->me; |
|
|
| return nullptr; |
| } |
|
|
| |
| |
| |
| |
| static bool |
| known_value (uintmax_t n) |
| { |
| return n < UINTMAX_MAX - 1; |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| static char const * |
| df_readable (bool negative, uintmax_t n, char *buf, |
| uintmax_t input_units, uintmax_t output_units) |
| { |
| if (! known_value (n) && !negative) |
| return "-"; |
| else |
| { |
| char *p = human_readable (negative ? -n : n, buf + negative, |
| human_output_opts, input_units, output_units); |
| if (negative) |
| *--p = '-'; |
| return p; |
| } |
| } |
|
|
| |
| |
| |
| |
| static void |
| add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg, |
| uintmax_t src, bool src_neg) |
| { |
| if (*dest_neg == src_neg) |
| { |
| *dest += src; |
| return; |
| } |
|
|
| if (*dest_neg) |
| *dest = -*dest; |
|
|
| if (src_neg) |
| src = -src; |
|
|
| if (src < *dest) |
| *dest -= src; |
| else |
| { |
| *dest = src - *dest; |
| *dest_neg = src_neg; |
| } |
|
|
| if (*dest_neg) |
| *dest = -*dest; |
| } |
|
|
| |
| |
| |
| ATTRIBUTE_PURE |
| static bool |
| has_uuid_suffix (char const *s) |
| { |
| size_t len = strlen (s); |
| return (36 < len |
| && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36); |
| } |
|
|
| |
| |
| static void |
| get_field_values (struct field_values_t *bv, |
| struct field_values_t *iv, |
| const struct fs_usage *fsu) |
| { |
| |
| iv->input_units = iv->output_units = 1; |
| iv->total = fsu->fsu_files; |
| iv->available = iv->available_to_root = fsu->fsu_ffree; |
| iv->negate_available = false; |
|
|
| iv->used = UINTMAX_MAX; |
| iv->negate_used = false; |
| if (known_value (iv->total) && known_value (iv->available_to_root)) |
| { |
| iv->used = iv->total - iv->available_to_root; |
| iv->negate_used = (iv->total < iv->available_to_root); |
| } |
|
|
| |
| bv->input_units = fsu->fsu_blocksize; |
| bv->output_units = output_block_size; |
| bv->total = fsu->fsu_blocks; |
| bv->available = fsu->fsu_bavail; |
| bv->available_to_root = fsu->fsu_bfree; |
| bv->negate_available = (fsu->fsu_bavail_top_bit_set |
| && known_value (fsu->fsu_bavail)); |
|
|
| bv->used = UINTMAX_MAX; |
| bv->negate_used = false; |
| if (known_value (bv->total) && known_value (bv->available_to_root)) |
| { |
| bv->used = bv->total - bv->available_to_root; |
| bv->negate_used = (bv->total < bv->available_to_root); |
| } |
| } |
|
|
| |
| static void |
| add_to_grand_total (struct field_values_t *bv, struct field_values_t *iv) |
| { |
| if (known_value (iv->total)) |
| grand_fsu.fsu_files += iv->total; |
| if (known_value (iv->available)) |
| grand_fsu.fsu_ffree += iv->available; |
|
|
| if (known_value (bv->total)) |
| grand_fsu.fsu_blocks += bv->input_units * bv->total; |
| if (known_value (bv->available_to_root)) |
| grand_fsu.fsu_bfree += bv->input_units * bv->available_to_root; |
| if (known_value (bv->available)) |
| add_uint_with_neg_flag (&grand_fsu.fsu_bavail, |
| &grand_fsu.fsu_bavail_top_bit_set, |
| bv->input_units * bv->available, |
| bv->negate_available); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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) |
| { |
| if (me_remote && show_local_fs) |
| return; |
|
|
| if (me_dummy && !show_all_fs && !show_listed_fs) |
| return; |
|
|
| if (!selected_fstype (fstype) || excluded_fstype (fstype)) |
| return; |
|
|
| |
| |
| if (!force_fsu && ! IS_ABSOLUTE_FILE_NAME (mount_point)) |
| return; |
|
|
| if (!stat_file) |
| stat_file = mount_point; |
|
|
| struct fs_usage fsu; |
| if (force_fsu) |
| fsu = *force_fsu; |
| else if (get_fs_usage (stat_file, device, &fsu)) |
| { |
| |
| |
| |
| if (process_all && (errno == EACCES || errno == ENOENT)) |
| { |
| if (! show_all_fs) |
| return; |
|
|
| fstype = "-"; |
| fsu.fsu_bavail_top_bit_set = false; |
| fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree = |
| fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX; |
| } |
| else |
| { |
| error (0, errno, "%s", quotef (stat_file)); |
| exit_status = EXIT_FAILURE; |
| return; |
| } |
| } |
| else if (process_all && show_all_fs) |
| { |
| |
| |
| |
| |
| struct stat sb; |
| if (stat (stat_file, &sb) == 0) |
| { |
| struct mount_entry const * dev_me = me_for_dev (sb.st_dev); |
| if (dev_me && ! streq (dev_me->me_devname, device) |
| && (! dev_me->me_remote || ! me_remote)) |
| { |
| fstype = "-"; |
| fsu.fsu_bavail_top_bit_set = false; |
| fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree = |
| fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX; |
| } |
| } |
| } |
|
|
| if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs) |
| return; |
|
|
| if (! force_fsu) |
| file_systems_processed = true; |
|
|
| alloc_table_row (); |
|
|
| if (! device) |
| device = "-"; |
|
|
| if (! file) |
| file = "-"; |
|
|
| char *dev_name = xstrdup (device); |
| char *resolved_dev; |
|
|
| |
| |
| |
| |
| |
| |
| if (process_all |
| && has_uuid_suffix (dev_name) |
| && (resolved_dev = canonicalize_filename_mode (dev_name, CAN_EXISTING))) |
| { |
| free (dev_name); |
| dev_name = resolved_dev; |
| } |
|
|
| if (! fstype) |
| fstype = "-"; |
|
|
| struct field_values_t block_values; |
| struct field_values_t inode_values; |
| get_field_values (&block_values, &inode_values, &fsu); |
|
|
| |
| if (print_grand_total && ! force_fsu) |
| add_to_grand_total (&block_values, &inode_values); |
|
|
| for (idx_t col = 0; col < ncolumns; col++) |
| { |
| char buf[LONGEST_HUMAN_READABLE + 2]; |
| char *cell; |
|
|
| struct field_values_t *v; |
| switch (columns[col]->field_type) |
| { |
| case BLOCK_FLD: |
| v = &block_values; |
| break; |
| case INODE_FLD: |
| v = &inode_values; |
| break; |
| case OTHER_FLD: |
| v = nullptr; |
| break; |
| default: |
| affirm (!"bad field_type"); |
| } |
|
|
| switch (columns[col]->field) |
| { |
| case SOURCE_FIELD: |
| cell = xstrdup (dev_name); |
| break; |
|
|
| case FSTYPE_FIELD: |
| cell = xstrdup (fstype); |
| break; |
|
|
| case SIZE_FIELD: |
| case ITOTAL_FIELD: |
| cell = xstrdup (df_readable (false, v->total, buf, |
| v->input_units, v->output_units)); |
| break; |
|
|
| case USED_FIELD: |
| case IUSED_FIELD: |
| cell = xstrdup (df_readable (v->negate_used, v->used, buf, |
| v->input_units, v->output_units)); |
| break; |
|
|
| case AVAIL_FIELD: |
| case IAVAIL_FIELD: |
| cell = xstrdup (df_readable (v->negate_available, v->available, buf, |
| v->input_units, v->output_units)); |
| break; |
|
|
| case PCENT_FIELD: |
| case IPCENT_FIELD: |
| { |
| double pct = -1; |
| if (! known_value (v->used) || ! known_value (v->available)) |
| ; |
| else if (!v->negate_used |
| && v->used <= UINTMAX_MAX / 100 |
| && v->used + v->available != 0 |
| && (v->used + v->available < v->used) |
| == v->negate_available) |
| { |
| uintmax_t u100 = v->used * 100; |
| uintmax_t nonroot_total = v->used + v->available; |
| pct = u100 / nonroot_total + (u100 % nonroot_total != 0); |
| } |
| else |
| { |
| |
| |
| |
| |
| |
| double u = v->negate_used ? - (double) - v->used : v->used; |
| double a = v->negate_available |
| ? - (double) - v->available : v->available; |
| double nonroot_total = u + a; |
| if (nonroot_total) |
| { |
| long int lipct = pct = u * 100 / nonroot_total; |
| double ipct = lipct; |
|
|
| |
| |
| if (ipct - 1 < pct && pct <= ipct + 1) |
| pct = ipct + (ipct < pct); |
| } |
| } |
|
|
| cell = pct < 0 ? xstrdup ("-") : xasprintf ("%.0f%%", pct); |
| break; |
| } |
|
|
| case FILE_FIELD: |
| cell = xstrdup (file); |
| break; |
|
|
| case TARGET_FIELD: |
| #ifdef HIDE_AUTOMOUNT_PREFIX |
| |
| |
| |
| if (STRNCMP_LIT (mount_point, "/auto/") == 0) |
| mount_point += 5; |
| else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0) |
| mount_point += 8; |
| #endif |
| cell = xstrdup (mount_point); |
| break; |
|
|
| case INVALID_FIELD: |
| default: |
| affirm (!"unhandled field"); |
| } |
|
|
| affirm (cell); |
|
|
| replace_problematic_chars (cell); |
| int cell_width = mbswidth (cell, MBSWIDTH_FLAGS); |
| columns[col]->width = MAX (columns[col]->width, cell_width); |
| table[nrows - 1][col] = cell; |
| } |
| free (dev_name); |
| } |
|
|
| |
| |
| static char * |
| last_device_for_mount (char const *mount) |
| { |
| struct mount_entry const *me; |
| struct mount_entry const *le = nullptr; |
|
|
| for (me = mount_list; me; me = me->me_next) |
| { |
| if (streq (me->me_mountdir, mount)) |
| le = me; |
| } |
|
|
| if (le) |
| { |
| char *devname = le->me_devname; |
| char *canon_dev = canonicalize_file_name (devname); |
| if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev)) |
| return canon_dev; |
| free (canon_dev); |
| return xstrdup (le->me_devname); |
| } |
| else |
| return nullptr; |
| } |
|
|
| |
| |
| static bool |
| get_device (char const *device) |
| { |
| struct mount_entry const *me; |
| struct mount_entry const *best_match = nullptr; |
| bool best_match_accessible = false; |
| bool eclipsed_device = false; |
| char const *file = device; |
|
|
| char *resolved = canonicalize_file_name (device); |
| if (resolved && IS_ABSOLUTE_FILE_NAME (resolved)) |
| device = resolved; |
|
|
| size_t best_match_len = SIZE_MAX; |
| for (me = mount_list; me; me = me->me_next) |
| { |
| |
| char *devname = me->me_devname; |
| char *canon_dev = canonicalize_file_name (me->me_devname); |
| if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev)) |
| devname = canon_dev; |
|
|
| if (streq (device, devname)) |
| { |
| char *last_device = last_device_for_mount (me->me_mountdir); |
| eclipsed_device = last_device && ! streq (last_device, devname); |
| size_t len = strlen (me->me_mountdir); |
|
|
| if (! eclipsed_device |
| && (! best_match_accessible || len < best_match_len)) |
| { |
| struct stat device_stats; |
| bool this_match_accessible = false; |
|
|
| if (stat (me->me_mountdir, &device_stats) == 0) |
| best_match_accessible = this_match_accessible = true; |
|
|
| if (this_match_accessible |
| || (! best_match_accessible && len < best_match_len)) |
| { |
| best_match = me; |
| if (len == 1) |
| { |
| free (last_device); |
| free (canon_dev); |
| break; |
| } |
| else |
| best_match_len = len; |
| } |
| } |
|
|
| free (last_device); |
| } |
|
|
| free (canon_dev); |
| } |
|
|
| free (resolved); |
|
|
| if (best_match) |
| { |
| get_dev (best_match->me_devname, best_match->me_mountdir, file, nullptr, |
| best_match->me_type, best_match->me_dummy, |
| best_match->me_remote, nullptr, false); |
| return true; |
| } |
| else if (eclipsed_device) |
| { |
| error (0, 0, _("cannot access %s: over-mounted by another device"), |
| quoteaf (file)); |
| exit_status = EXIT_FAILURE; |
| return true; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| static void |
| get_point (char const *point, const struct stat *statp) |
| { |
| struct stat device_stats; |
| struct mount_entry *me; |
| struct mount_entry const *best_match = nullptr; |
|
|
| |
| |
| |
| char *resolved = canonicalize_file_name (point); |
| if (resolved && resolved[0] == '/') |
| { |
| size_t resolved_len = strlen (resolved); |
| size_t best_match_len = 0; |
|
|
| for (me = mount_list; me; me = me->me_next) |
| { |
| if (!streq (me->me_type, "lofs") |
| && (!best_match || best_match->me_dummy || !me->me_dummy)) |
| { |
| size_t len = strlen (me->me_mountdir); |
| if (best_match_len <= len && len <= resolved_len |
| && (len == 1 |
| || ((len == resolved_len || resolved[len] == '/') |
| && STREQ_LEN (me->me_mountdir, resolved, len)))) |
| { |
| best_match = me; |
| best_match_len = len; |
| } |
| } |
| } |
| } |
| free (resolved); |
| if (best_match |
| && (stat (best_match->me_mountdir, &device_stats) != 0 |
| || device_stats.st_dev != statp->st_dev)) |
| best_match = nullptr; |
|
|
| if (! best_match) |
| for (me = mount_list; me; me = me->me_next) |
| { |
| if (me->me_dev == (dev_t) -1) |
| { |
| if (stat (me->me_mountdir, &device_stats) == 0) |
| me->me_dev = device_stats.st_dev; |
| else |
| { |
| |
| |
| |
| if (errno == EIO) |
| { |
| error (0, errno, "%s", quotef (me->me_mountdir)); |
| exit_status = EXIT_FAILURE; |
| } |
|
|
| |
| me->me_dev = (dev_t) -2; |
| } |
| } |
|
|
| if (statp->st_dev == me->me_dev |
| && !streq (me->me_type, "lofs") |
| && (!best_match || best_match->me_dummy || !me->me_dummy)) |
| { |
| |
| if (stat (me->me_mountdir, &device_stats) != 0 |
| || device_stats.st_dev != me->me_dev) |
| me->me_dev = (dev_t) -2; |
| else |
| best_match = me; |
| } |
| } |
|
|
| if (best_match) |
| get_dev (best_match->me_devname, best_match->me_mountdir, point, point, |
| best_match->me_type, best_match->me_dummy, best_match->me_remote, |
| nullptr, false); |
| else |
| { |
| |
| |
| |
|
|
| |
| char *mp = find_mount_point (point, statp); |
| if (mp) |
| { |
| get_dev (nullptr, mp, point, nullptr, nullptr, |
| false, false, nullptr, false); |
| free (mp); |
| } |
| } |
| } |
|
|
| |
| |
|
|
| static void |
| get_entry (char const *name, struct stat const *statp) |
| { |
| if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode)) |
| && get_device (name)) |
| return; |
|
|
| get_point (name, statp); |
| } |
|
|
| |
| |
|
|
| static void |
| get_all_entries (void) |
| { |
| struct mount_entry *me; |
|
|
| filter_mount_list (show_all_fs); |
|
|
| for (me = mount_list; me; me = me->me_next) |
| get_dev (me->me_devname, me->me_mountdir, nullptr, nullptr, me->me_type, |
| me->me_dummy, me->me_remote, nullptr, true); |
| } |
|
|
| |
|
|
| static void |
| add_fs_type (char const *fstype) |
| { |
| struct fs_type_list *fsp; |
|
|
| fsp = xmalloc (sizeof *fsp); |
| fsp->fs_name = (char *) fstype; |
| fsp->fs_next = fs_select_list; |
| fs_select_list = fsp; |
| } |
|
|
| |
|
|
| static void |
| add_excluded_fs_type (char const *fstype) |
| { |
| struct fs_type_list *fsp; |
|
|
| fsp = xmalloc (sizeof *fsp); |
| fsp->fs_name = (char *) fstype; |
| fsp->fs_next = fs_exclude_list; |
| fs_exclude_list = fsp; |
| } |
|
|
| void |
| usage (int status) |
| { |
| if (status != EXIT_SUCCESS) |
| emit_try_help (); |
| else |
| { |
| printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); |
| fputs (_("\ |
| Show information about the file system on which each FILE resides,\n\ |
| or all file systems by default.\n\ |
| "), stdout); |
|
|
| emit_mandatory_arg_note (); |
|
|
| |
| |
| fputs (_("\ |
| -a, --all include pseudo, duplicate, inaccessible file systems\n\ |
| -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\ |
| '-BM' prints sizes in units of 1,048,576 bytes;\n\ |
| see SIZE format below\n\ |
| -h, --human-readable print sizes in powers of 1024 (e.g., 1023M)\n\ |
| -H, --si print sizes in powers of 1000 (e.g., 1.1G)\n\ |
| "), stdout); |
| fputs (_("\ |
| -i, --inodes list inode information instead of block usage\n\ |
| -k like --block-size=1K\n\ |
| -l, --local limit listing to local file systems\n\ |
| --no-sync do not invoke sync before getting usage info (default)\ |
| \n\ |
| "), stdout); |
| fputs (_("\ |
| --output[=FIELD_LIST] use the output format defined by FIELD_LIST,\n\ |
| or print all fields if FIELD_LIST is omitted\n\ |
| "), stdout); |
| fputs (_("\ |
| -P, --portability use the POSIX output format\n\ |
| "), stdout); |
| fputs (_("\ |
| --sync invoke sync before getting usage info\n\ |
| "), stdout); |
| fputs (_("\ |
| --total elide all entries insignificant to available space,\n\ |
| and produce a grand total\n\ |
| "), stdout); |
| fputs (_("\ |
| -t, --type=TYPE limit listing to file systems of type TYPE\n\ |
| -T, --print-type print file system type\n\ |
| -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\ |
| -v (ignored)\n\ |
| "), stdout); |
| fputs (HELP_OPTION_DESCRIPTION, stdout); |
| fputs (VERSION_OPTION_DESCRIPTION, stdout); |
| emit_blocksize_note ("DF"); |
| emit_size_note (); |
| fputs (_("\n\ |
| FIELD_LIST is a comma-separated list of columns to be included. Valid\n\ |
| field names are: 'source', 'fstype', 'itotal', 'iused', 'iavail', 'ipcent',\n\ |
| 'size', 'used', 'avail', 'pcent', 'file' and 'target' (see info page).\n\ |
| "), stdout); |
| emit_ancillary_info (PROGRAM_NAME); |
| } |
| exit (status); |
| } |
|
|
| int |
| main (int argc, char **argv) |
| { |
| struct stat *stats = nullptr; |
|
|
| initialize_main (&argc, &argv); |
| set_program_name (argv[0]); |
| setlocale (LC_ALL, ""); |
| bindtextdomain (PACKAGE, LOCALEDIR); |
| textdomain (PACKAGE); |
|
|
| atexit (close_stdout); |
|
|
| fs_select_list = nullptr; |
| fs_exclude_list = nullptr; |
| show_all_fs = false; |
| show_listed_fs = false; |
| human_output_opts = -1; |
| print_type = false; |
| file_systems_processed = false; |
| exit_status = EXIT_SUCCESS; |
| print_grand_total = false; |
| grand_fsu.fsu_blocksize = 1; |
|
|
| |
| bool posix_format = false; |
|
|
| char const *msg_mut_excl = _("options %s and %s are mutually exclusive"); |
|
|
| while (true) |
| { |
| int oi = -1; |
| int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, |
| &oi); |
| if (c == -1) |
| break; |
|
|
| switch (c) |
| { |
| case 'a': |
| show_all_fs = true; |
| break; |
| case 'B': |
| { |
| enum strtol_error e = human_options (optarg, &human_output_opts, |
| &output_block_size); |
| if (e != LONGINT_OK) |
| xstrtol_fatal (e, oi, c, long_options, optarg); |
| } |
| break; |
| case 'i': |
| if (header_mode == OUTPUT_MODE) |
| { |
| error (0, 0, msg_mut_excl, "-i", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| header_mode = INODES_MODE; |
| break; |
| case 'h': |
| human_output_opts = human_autoscale | human_SI | human_base_1024; |
| output_block_size = 1; |
| break; |
| case 'H': |
| human_output_opts = human_autoscale | human_SI; |
| output_block_size = 1; |
| break; |
| case 'k': |
| human_output_opts = 0; |
| output_block_size = 1024; |
| break; |
| case 'l': |
| show_local_fs = true; |
| break; |
| case 'm': |
| human_output_opts = 0; |
| output_block_size = 1024 * 1024; |
| break; |
| case 'T': |
| if (header_mode == OUTPUT_MODE) |
| { |
| error (0, 0, msg_mut_excl, "-T", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| print_type = true; |
| break; |
| case 'P': |
| if (header_mode == OUTPUT_MODE) |
| { |
| error (0, 0, msg_mut_excl, "-P", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| posix_format = true; |
| break; |
| case SYNC_OPTION: |
| require_sync = true; |
| break; |
| case NO_SYNC_OPTION: |
| require_sync = false; |
| break; |
|
|
| case 'F': |
| |
| case 't': |
| add_fs_type (optarg); |
| break; |
|
|
| case 'v': |
| |
| break; |
| case 'x': |
| add_excluded_fs_type (optarg); |
| break; |
|
|
| case OUTPUT_OPTION: |
| if (header_mode == INODES_MODE) |
| { |
| error (0, 0, msg_mut_excl, "-i", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| if (posix_format && header_mode == DEFAULT_MODE) |
| { |
| error (0, 0, msg_mut_excl, "-P", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| if (print_type) |
| { |
| error (0, 0, msg_mut_excl, "-T", "--output"); |
| usage (EXIT_FAILURE); |
| } |
| header_mode = OUTPUT_MODE; |
| if (optarg) |
| decode_output_arg (optarg); |
| break; |
|
|
| case TOTAL_OPTION: |
| print_grand_total = true; |
| break; |
|
|
| case_GETOPT_HELP_CHAR; |
| case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
|
|
| default: |
| usage (EXIT_FAILURE); |
| } |
| } |
|
|
| if (human_output_opts == -1) |
| { |
| if (posix_format) |
| { |
| human_output_opts = 0; |
| output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024); |
| } |
| else |
| human_options (getenv ("DF_BLOCK_SIZE"), |
| &human_output_opts, &output_block_size); |
| } |
|
|
| if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE) |
| ; |
| else if (human_output_opts & human_autoscale) |
| header_mode = HUMAN_MODE; |
| else if (posix_format) |
| header_mode = POSIX_MODE; |
|
|
| |
| { |
| bool match = false; |
| struct fs_type_list *fs_incl; |
| for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next) |
| { |
| struct fs_type_list *fs_excl; |
| for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next) |
| { |
| if (streq (fs_incl->fs_name, fs_excl->fs_name)) |
| { |
| error (0, 0, |
| _("file system type %s both selected and excluded"), |
| quote (fs_incl->fs_name)); |
| match = true; |
| break; |
| } |
| } |
| } |
| if (match) |
| return EXIT_FAILURE; |
| } |
|
|
| if (optind < argc) |
| { |
| |
| |
| |
| stats = xnmalloc (argc - optind, sizeof *stats); |
| for (int i = optind; i < argc; ++i) |
| { |
| int err = automount_stat_err (argv[i], &stats[i - optind]); |
| if (err != 0) |
| { |
| error (0, err, "%s", quotef (argv[i])); |
| exit_status = EXIT_FAILURE; |
| argv[i] = nullptr; |
| } |
| } |
| } |
|
|
| mount_list = |
| read_file_system_list ((fs_select_list != nullptr |
| || fs_exclude_list != nullptr |
| || print_type |
| || field_data[FSTYPE_FIELD].used |
| || show_local_fs)); |
|
|
| if (mount_list == nullptr) |
| { |
| |
| |
| |
| |
| int status = 0; |
| if ( ! (optind < argc) |
| || (show_all_fs |
| || show_local_fs |
| || fs_select_list != nullptr |
| || fs_exclude_list != nullptr)) |
| { |
| status = EXIT_FAILURE; |
| } |
| char const *warning = (status == 0 ? _("Warning: ") : ""); |
| error (status, errno, "%s%s", warning, |
| _("cannot read table of mounted file systems")); |
| } |
|
|
| if (require_sync) |
| sync (); |
|
|
| get_field_list (); |
| get_header (); |
|
|
| if (stats) |
| { |
| |
| show_listed_fs = true; |
|
|
| for (int i = optind; i < argc; ++i) |
| if (argv[i]) |
| get_entry (argv[i], &stats[i - optind]); |
| } |
| else |
| get_all_entries (); |
|
|
| if (file_systems_processed) |
| { |
| if (print_grand_total) |
| get_dev ("total", |
| (field_data[SOURCE_FIELD].used ? "-" : "total"), |
| nullptr, nullptr, nullptr, false, false, &grand_fsu, false); |
|
|
| print_table (); |
| } |
| else |
| { |
| |
| |
| if (exit_status == EXIT_SUCCESS) |
| error (EXIT_FAILURE, 0, _("no file systems processed")); |
| } |
|
|
| main_exit (exit_status); |
| } |
|
|