|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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); |
|
|
} |
|
|
|