1/* df - summarize free disk space 2 Copyright (C) 1991, 1995-2010 Free Software Foundation, Inc. 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 17/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. 18 --human-readable and --megabyte options added by lm@sgi.com. 19 --si and large file support added by eggert@twinsun.com. */ 20 21#include <config.h> 22#include <stdio.h> 23#include <sys/types.h> 24#include <getopt.h> 25 26#include "system.h" 27#include "error.h" 28#include "fsusage.h" 29#include "human.h" 30#include "mountlist.h" 31#include "quote.h" 32#include "save-cwd.h" 33#include "xgetcwd.h" 34 35/* The official name of this program (e.g., no `g' prefix). */ 36#define PROGRAM_NAME "df" 37 38#define AUTHORS \ 39 proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \ 40 proper_name ("David MacKenzie"), \ 41 proper_name ("Paul Eggert") 42 43/* If true, show inode information. */ 44static bool inode_format; 45 46/* If true, show even file systems with zero size or 47 uninteresting types. */ 48static bool show_all_fs; 49 50/* If true, show only local file systems. */ 51static bool show_local_fs; 52 53/* If true, output data for each file system corresponding to a 54 command line argument -- even if it's a dummy (automounter) entry. */ 55static bool show_listed_fs; 56 57/* Human-readable options for output. */ 58static int human_output_opts; 59 60/* The units to use when printing sizes. */ 61static uintmax_t output_block_size; 62 63/* If true, use the POSIX output format. */ 64static bool posix_format; 65 66/* True if a file system has been processed for output. */ 67static bool file_systems_processed; 68 69/* If true, invoke the `sync' system call before getting any usage data. 70 Using this option can make df very slow, especially with many or very 71 busy disks. Note that this may make a difference on some systems -- 72 SunOS 4.1.3, for one. It is *not* necessary on GNU/Linux. */ 73static bool require_sync; 74 75/* Desired exit status. */ 76static int exit_status; 77 78/* A file system type to display. */ 79 80struct fs_type_list 81{ 82 char *fs_name; 83 struct fs_type_list *fs_next; 84}; 85 86/* Linked list of file system types to display. 87 If `fs_select_list' is NULL, list all types. 88 This table is generated dynamically from command-line options, 89 rather than hardcoding into the program what it thinks are the 90 valid file system types; let the user specify any file system type 91 they want to, and if there are any file systems of that type, they 92 will be shown. 93 94 Some file system types: 95 4.2 4.3 ufs nfs swap ignore io vm efs dbg */ 96 97static struct fs_type_list *fs_select_list; 98 99/* Linked list of file system types to omit. 100 If the list is empty, don't exclude any types. */ 101 102static struct fs_type_list *fs_exclude_list; 103 104/* Linked list of mounted file systems. */ 105static struct mount_entry *mount_list; 106 107/* If true, print file system type as well. */ 108static bool print_type; 109 110/* If true, print a grand total at the end. */ 111static bool print_grand_total; 112 113/* Grand total data. */ 114static struct fs_usage grand_fsu; 115 116/* For long options that have no equivalent short option, use a 117 non-character as a pseudo short option, starting with CHAR_MAX + 1. */ 118enum 119{ 120 NO_SYNC_OPTION = CHAR_MAX + 1, 121 SYNC_OPTION 122}; 123 124static struct option const long_options[] = 125{ 126 {"all", no_argument, NULL, 'a'}, 127 {"block-size", required_argument, NULL, 'B'}, 128 {"inodes", no_argument, NULL, 'i'}, 129 {"human-readable", no_argument, NULL, 'h'}, 130 {"si", no_argument, NULL, 'H'}, 131 {"local", no_argument, NULL, 'l'}, 132 {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */ 133 {"portability", no_argument, NULL, 'P'}, 134 {"print-type", no_argument, NULL, 'T'}, 135 {"sync", no_argument, NULL, SYNC_OPTION}, 136 {"no-sync", no_argument, NULL, NO_SYNC_OPTION}, 137 {"total", no_argument, NULL, 'c'}, 138 {"type", required_argument, NULL, 't'}, 139 {"exclude-type", required_argument, NULL, 'x'}, 140 {GETOPT_HELP_OPTION_DECL}, 141 {GETOPT_VERSION_OPTION_DECL}, 142 {NULL, 0, NULL, 0} 143}; 144 145static void 146print_header (void) 147{ 148 char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))]; 149 150 if (print_type) 151 /* TRANSLATORS: 152 For best results (df header/column alignment), ensure that 153 your translation has the same length as the original. */ 154 fputs (_("Filesystem Type"), stdout); 155 else 156 fputs (_("Filesystem "), stdout); 157 158 if (inode_format) 159 /* TRANSLATORS: 160 For best results (df header/column alignment), ensure that 161 your translation has the same length as the original. 162 Also, each column name translation should end at the same 163 column as the corresponding original. */ 164 fputs (_(" Inodes IUsed IFree IUse%"), stdout); 165 else if (human_output_opts & human_autoscale) 166 { 167 if (human_output_opts & human_base_1024) 168 fputs (_(" Size Used Avail Use%"), stdout); 169 else 170 fputs (_(" Size Used Avail Use%"), stdout); 171 } 172 else if (posix_format) 173 printf (_(" %s-blocks Used Available Capacity"), 174 umaxtostr (output_block_size, buf)); 175 else 176 { 177 int opts = (human_suppress_point_zero 178 | human_autoscale | human_SI 179 | (human_output_opts 180 & (human_group_digits | human_base_1024 | human_B))); 181 182 /* Prefer the base that makes the human-readable value more exact, 183 if there is a difference. */ 184 185 uintmax_t q1000 = output_block_size; 186 uintmax_t q1024 = output_block_size; 187 bool divisible_by_1000; 188 bool divisible_by_1024; 189 190 do 191 { 192 divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000; 193 divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024; 194 } 195 while (divisible_by_1000 & divisible_by_1024); 196 197 if (divisible_by_1000 < divisible_by_1024) 198 opts |= human_base_1024; 199 if (divisible_by_1024 < divisible_by_1000) 200 opts &= ~human_base_1024; 201 if (! (opts & human_base_1024)) 202 opts |= human_B; 203 204 printf (_(" %4s-blocks Used Available Use%%"), 205 human_readable (output_block_size, buf, opts, 1, 1)); 206 } 207 208 fputs (_(" Mounted on\n"), stdout); 209} 210 211/* Is FSTYPE a type of file system that should be listed? */ 212 213static bool 214selected_fstype (const char *fstype) 215{ 216 const struct fs_type_list *fsp; 217 218 if (fs_select_list == NULL || fstype == NULL) 219 return true; 220 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next) 221 if (STREQ (fstype, fsp->fs_name)) 222 return true; 223 return false; 224} 225 226/* Is FSTYPE a type of file system that should be omitted? */ 227 228static bool 229excluded_fstype (const char *fstype) 230{ 231 const struct fs_type_list *fsp; 232 233 if (fs_exclude_list == NULL || fstype == NULL) 234 return false; 235 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next) 236 if (STREQ (fstype, fsp->fs_name)) 237 return true; 238 return false; 239} 240 241/* Return true if N is a known integer value. On many file systems, 242 UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1 243 represents unknown. Use a rule that works on AIX file systems, and 244 that almost-always works on other types. */ 245static bool 246known_value (uintmax_t n) 247{ 248 return n < UINTMAX_MAX - 1; 249} 250 251/* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS), 252 except: 253 254 - If NEGATIVE, then N represents a negative number, 255 expressed in two's complement. 256 - Otherwise, return "-" if N is unknown. */ 257 258static char const * 259df_readable (bool negative, uintmax_t n, char *buf, 260 uintmax_t input_units, uintmax_t output_units) 261{ 262 if (! known_value (n) && !negative) 263 return "-"; 264 else 265 { 266 char *p = human_readable (negative ? -n : n, buf + negative, 267 human_output_opts, input_units, output_units); 268 if (negative) 269 *--p = '-'; 270 return p; 271 } 272} 273 274/* Logical equivalence */ 275#define LOG_EQ(a, b) (!(a) == !(b)) 276 277/* Add integral value while using uintmax_t for value part and separate 278 negation flag. It adds value of SRC and SRC_NEG to DEST and DEST_NEG. 279 The result will be in DEST and DEST_NEG. See df_readable to understand 280 how the negation flag is used. */ 281static void 282add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg, 283 uintmax_t src, bool src_neg) 284{ 285 if (LOG_EQ (*dest_neg, src_neg)) 286 { 287 *dest += src; 288 return; 289 } 290 291 if (*dest_neg) 292 *dest = -*dest; 293 294 if (src_neg) 295 src = -src; 296 297 if (src < *dest) 298 *dest -= src; 299 else 300 { 301 *dest = src - *dest; 302 *dest_neg = src_neg; 303 } 304 305 if (*dest_neg) 306 *dest = -*dest; 307} 308 309/* Display a space listing for the disk device with absolute file name DISK. 310 If MOUNT_POINT is non-NULL, it is the name of the root of the 311 file system on DISK. 312 If STAT_FILE is non-null, it is the name of a file within the file 313 system that the user originally asked for; this provides better 314 diagnostics, and sometimes it provides better results on networked 315 file systems that give different free-space results depending on 316 where in the file system you probe. 317 If FSTYPE is non-NULL, it is the type of the file system on DISK. 318 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may 319 not be able to produce statistics in this case. 320 ME_DUMMY and ME_REMOTE are the mount entry flags. */ 321 322static void 323show_dev (char const *disk, char const *mount_point, 324 char const *stat_file, char const *fstype, 325 bool me_dummy, bool me_remote, 326 const struct fs_usage *force_fsu) 327{ 328 struct fs_usage fsu; 329 char buf[3][LONGEST_HUMAN_READABLE + 2]; 330 int width; 331 int col1_adjustment = 0; 332 int use_width; 333 uintmax_t input_units; 334 uintmax_t output_units; 335 uintmax_t total; 336 uintmax_t available; 337 bool negate_available; 338 uintmax_t available_to_root; 339 uintmax_t used; 340 bool negate_used; 341 double pct = -1; 342 343 if (me_remote && show_local_fs) 344 return; 345 346 if (me_dummy && !show_all_fs && !show_listed_fs) 347 return; 348 349 if (!selected_fstype (fstype) || excluded_fstype (fstype)) 350 return; 351 352 /* If MOUNT_POINT is NULL, then the file system is not mounted, and this 353 program reports on the file system that the special file is on. 354 It would be better to report on the unmounted file system, 355 but statfs doesn't do that on most systems. */ 356 if (!stat_file) 357 stat_file = mount_point ? mount_point : disk; 358 359 if (force_fsu) 360 fsu = *force_fsu; 361 else if (get_fs_usage (stat_file, disk, &fsu)) 362 { 363 error (0, errno, "%s", quote (stat_file)); 364 exit_status = EXIT_FAILURE; 365 return; 366 } 367 368 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs) 369 return; 370 371 if (! file_systems_processed) 372 { 373 file_systems_processed = true; 374 print_header (); 375 } 376 377 if (! disk) 378 disk = "-"; /* unknown */ 379 if (! fstype) 380 fstype = "-"; /* unknown */ 381 382 /* df.c reserved 5 positions for fstype, 383 but that does not suffice for type iso9660 */ 384 if (print_type) 385 { 386 size_t disk_name_len = strlen (disk); 387 size_t fstype_len = strlen (fstype); 388 if (disk_name_len + fstype_len < 18) 389 printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype); 390 else if (!posix_format) 391 printf ("%s\n%18s ", disk, fstype); 392 else 393 printf ("%s %s", disk, fstype); 394 } 395 else 396 { 397 if (strlen (disk) > 20 && !posix_format) 398 printf ("%s\n%20s", disk, ""); 399 else 400 printf ("%-20s", disk); 401 } 402 403 if (inode_format) 404 { 405 width = 7; 406 use_width = 5; 407 input_units = output_units = 1; 408 total = fsu.fsu_files; 409 available = fsu.fsu_ffree; 410 negate_available = false; 411 available_to_root = available; 412 413 if (known_value (total)) 414 grand_fsu.fsu_files += total; 415 if (known_value (available)) 416 grand_fsu.fsu_ffree += available; 417 } 418 else 419 { 420 if (human_output_opts & human_autoscale) 421 width = 5 + ! (human_output_opts & human_base_1024); 422 else 423 { 424 width = 9; 425 if (posix_format) 426 { 427 uintmax_t b; 428 col1_adjustment = -3; 429 for (b = output_block_size; 9 < b; b /= 10) 430 col1_adjustment++; 431 } 432 } 433 use_width = ((posix_format 434 && ! (human_output_opts & human_autoscale)) 435 ? 8 : 4); 436 input_units = fsu.fsu_blocksize; 437 output_units = output_block_size; 438 total = fsu.fsu_blocks; 439 available = fsu.fsu_bavail; 440 negate_available = (fsu.fsu_bavail_top_bit_set 441 && known_value (available)); 442 available_to_root = fsu.fsu_bfree; 443 444 if (known_value (total)) 445 grand_fsu.fsu_blocks += input_units * total; 446 if (known_value (available_to_root)) 447 grand_fsu.fsu_bfree += input_units * available_to_root; 448 if (known_value (available)) 449 add_uint_with_neg_flag (&grand_fsu.fsu_bavail, 450 &grand_fsu.fsu_bavail_top_bit_set, 451 input_units * available, negate_available); 452 } 453 454 used = UINTMAX_MAX; 455 negate_used = false; 456 if (known_value (total) && known_value (available_to_root)) 457 { 458 used = total - available_to_root; 459 negate_used = (total < available_to_root); 460 } 461 462 printf (" %*s %*s %*s ", 463 width + col1_adjustment, 464 df_readable (false, total, 465 buf[0], input_units, output_units), 466 width, df_readable (negate_used, used, 467 buf[1], input_units, output_units), 468 width, df_readable (negate_available, available, 469 buf[2], input_units, output_units)); 470 471 if (! known_value (used) || ! known_value (available)) 472 ; 473 else if (!negate_used 474 && used <= TYPE_MAXIMUM (uintmax_t) / 100 475 && used + available != 0 476 && (used + available < used) == negate_available) 477 { 478 uintmax_t u100 = used * 100; 479 uintmax_t nonroot_total = used + available; 480 pct = u100 / nonroot_total + (u100 % nonroot_total != 0); 481 } 482 else 483 { 484 /* The calculation cannot be done easily with integer 485 arithmetic. Fall back on floating point. This can suffer 486 from minor rounding errors, but doing it exactly requires 487 multiple precision arithmetic, and it's not worth the 488 aggravation. */ 489 double u = negate_used ? - (double) - used : used; 490 double a = negate_available ? - (double) - available : available; 491 double nonroot_total = u + a; 492 if (nonroot_total) 493 { 494 long int lipct = pct = u * 100 / nonroot_total; 495 double ipct = lipct; 496 497 /* Like `pct = ceil (dpct);', but avoid ceil so that 498 the math library needn't be linked. */ 499 if (ipct - 1 < pct && pct <= ipct + 1) 500 pct = ipct + (ipct < pct); 501 } 502 } 503 504 if (0 <= pct) 505 printf ("%*.0f%%", use_width - 1, pct); 506 else 507 printf ("%*s", use_width, "- "); 508 509 if (mount_point) 510 { 511#ifdef HIDE_AUTOMOUNT_PREFIX 512 /* Don't print the first directory name in MOUNT_POINT if it's an 513 artifact of an automounter. This is a bit too aggressive to be 514 the default. */ 515 if (strncmp ("/auto/", mount_point, 6) == 0) 516 mount_point += 5; 517 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0) 518 mount_point += 8; 519#endif 520 printf (" %s", mount_point); 521 } 522 putchar ('\n'); 523} 524 525/* Return the root mountpoint of the file system on which FILE exists, in 526 malloced storage. FILE_STAT should be the result of stating FILE. 527 Give a diagnostic and return NULL if unable to determine the mount point. 528 Exit if unable to restore current working directory. */ 529static char * 530find_mount_point (const char *file, const struct stat *file_stat) 531{ 532 struct saved_cwd cwd; 533 struct stat last_stat; 534 char *mp = NULL; /* The malloced mount point. */ 535 536 if (save_cwd (&cwd) != 0) 537 { 538 error (0, errno, _("cannot get current directory")); 539 return NULL; 540 } 541 542 if (S_ISDIR (file_stat->st_mode)) 543 /* FILE is a directory, so just chdir there directly. */ 544 { 545 last_stat = *file_stat; 546 if (chdir (file) < 0) 547 { 548 error (0, errno, _("cannot change to directory %s"), quote (file)); 549 return NULL; 550 } 551 } 552 else 553 /* FILE is some other kind of file; use its directory. */ 554 { 555 char *xdir = dir_name (file); 556 char *dir; 557 ASSIGN_STRDUPA (dir, xdir); 558 free (xdir); 559 560 if (chdir (dir) < 0) 561 { 562 error (0, errno, _("cannot change to directory %s"), quote (dir)); 563 return NULL; 564 } 565 566 if (stat (".", &last_stat) < 0) 567 { 568 error (0, errno, _("cannot stat current directory (now %s)"), 569 quote (dir)); 570 goto done; 571 } 572 } 573 574 /* Now walk up FILE's parents until we find another file system or /, 575 chdiring as we go. LAST_STAT holds stat information for the last place 576 we visited. */ 577 for (;;) 578 { 579 struct stat st; 580 if (stat ("..", &st) < 0) 581 { 582 error (0, errno, _("cannot stat %s"), quote ("..")); 583 goto done; 584 } 585 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino) 586 /* cwd is the mount point. */ 587 break; 588 if (chdir ("..") < 0) 589 { 590 error (0, errno, _("cannot change to directory %s"), quote ("..")); 591 goto done; 592 } 593 last_stat = st; 594 } 595 596 /* Finally reached a mount point, see what it's called. */ 597 mp = xgetcwd (); 598 599done: 600 /* Restore the original cwd. */ 601 { 602 int save_errno = errno; 603 if (restore_cwd (&cwd) != 0) 604 error (EXIT_FAILURE, errno, 605 _("failed to return to initial working directory")); 606 free_cwd (&cwd); 607 errno = save_errno; 608 } 609 610 return mp; 611} 612 613/* If DISK corresponds to a mount point, show its usage 614 and return true. Otherwise, return false. */ 615static bool 616show_disk (char const *disk) 617{ 618 struct mount_entry const *me; 619 struct mount_entry const *best_match = NULL; 620 621 for (me = mount_list; me; me = me->me_next) 622 if (STREQ (disk, me->me_devname)) 623 best_match = me; 624 625 if (best_match) 626 { 627 show_dev (best_match->me_devname, best_match->me_mountdir, NULL, 628 best_match->me_type, best_match->me_dummy, 629 best_match->me_remote, NULL); 630 return true; 631 } 632 633 return false; 634} 635 636/* Figure out which device file or directory POINT is mounted on 637 and show its disk usage. 638 STATP must be the result of `stat (POINT, STATP)'. */ 639static void 640show_point (const char *point, const struct stat *statp) 641{ 642 struct stat disk_stats; 643 struct mount_entry *me; 644 struct mount_entry const *best_match = NULL; 645 646 /* If POINT is an absolute file name, see if we can find the 647 mount point without performing any extra stat calls at all. */ 648 if (*point == '/') 649 { 650 /* Find the best match: prefer non-dummies, and then prefer the 651 last match if there are ties. */ 652 653 for (me = mount_list; me; me = me->me_next) 654 if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs") 655 && (!best_match || best_match->me_dummy || !me->me_dummy)) 656 best_match = me; 657 } 658 659 /* Calculate the real absolute file name for POINT, and use that to find 660 the mount point. This avoids statting unavailable mount points, 661 which can hang df. */ 662 if (! best_match) 663 { 664 char *resolved = canonicalize_file_name (point); 665 666 if (resolved && resolved[0] == '/') 667 { 668 size_t resolved_len = strlen (resolved); 669 size_t best_match_len = 0; 670 671 for (me = mount_list; me; me = me->me_next) 672 if (!STREQ (me->me_type, "lofs") 673 && (!best_match || best_match->me_dummy || !me->me_dummy)) 674 { 675 size_t len = strlen (me->me_mountdir); 676 if (best_match_len <= len && len <= resolved_len 677 && (len == 1 /* root file system */ 678 || ((len == resolved_len || resolved[len] == '/') 679 && strncmp (me->me_mountdir, resolved, len) == 0))) 680 { 681 best_match = me; 682 best_match_len = len; 683 } 684 } 685 } 686 687 free (resolved); 688 689 if (best_match 690 && (stat (best_match->me_mountdir, &disk_stats) != 0 691 || disk_stats.st_dev != statp->st_dev)) 692 best_match = NULL; 693 } 694 695 if (! best_match) 696 for (me = mount_list; me; me = me->me_next) 697 { 698 if (me->me_dev == (dev_t) -1) 699 { 700 if (stat (me->me_mountdir, &disk_stats) == 0) 701 me->me_dev = disk_stats.st_dev; 702 else 703 { 704 /* Report only I/O errors. Other errors might be 705 caused by shadowed mount points, which means POINT 706 can't possibly be on this file system. */ 707 if (errno == EIO) 708 { 709 error (0, errno, "%s", quote (me->me_mountdir)); 710 exit_status = EXIT_FAILURE; 711 } 712 713 /* So we won't try and fail repeatedly. */ 714 me->me_dev = (dev_t) -2; 715 } 716 } 717 718 if (statp->st_dev == me->me_dev 719 && !STREQ (me->me_type, "lofs") 720 && (!best_match || best_match->me_dummy || !me->me_dummy)) 721 { 722 /* Skip bogus mtab entries. */ 723 if (stat (me->me_mountdir, &disk_stats) != 0 724 || disk_stats.st_dev != me->me_dev) 725 me->me_dev = (dev_t) -2; 726 else 727 best_match = me; 728 } 729 } 730 731 if (best_match) 732 show_dev (best_match->me_devname, best_match->me_mountdir, point, 733 best_match->me_type, best_match->me_dummy, best_match->me_remote, 734 NULL); 735 else 736 { 737 /* We couldn't find the mount entry corresponding to POINT. Go ahead and 738 print as much info as we can; methods that require the device to be 739 present will fail at a later point. */ 740 741 /* Find the actual mount point. */ 742 char *mp = find_mount_point (point, statp); 743 if (mp) 744 { 745 show_dev (NULL, mp, NULL, NULL, false, false, NULL); 746 free (mp); 747 } 748 } 749} 750 751/* Determine what kind of node NAME is and show the disk usage 752 for it. STATP is the results of `stat' on NAME. */ 753 754static void 755show_entry (char const *name, struct stat const *statp) 756{ 757 if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode)) 758 && show_disk (name)) 759 return; 760 761 show_point (name, statp); 762} 763 764/* Show all mounted file systems, except perhaps those that are of 765 an unselected type or are empty. */ 766 767static void 768show_all_entries (void) 769{ 770 struct mount_entry *me; 771 772 for (me = mount_list; me; me = me->me_next) 773 show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type, 774 me->me_dummy, me->me_remote, NULL); 775} 776 777/* Add FSTYPE to the list of file system types to display. */ 778 779static void 780add_fs_type (const char *fstype) 781{ 782 struct fs_type_list *fsp; 783 784 fsp = xmalloc (sizeof *fsp); 785 fsp->fs_name = (char *) fstype; 786 fsp->fs_next = fs_select_list; 787 fs_select_list = fsp; 788} 789 790/* Add FSTYPE to the list of file system types to be omitted. */ 791 792static void 793add_excluded_fs_type (const char *fstype) 794{ 795 struct fs_type_list *fsp; 796 797 fsp = xmalloc (sizeof *fsp); 798 fsp->fs_name = (char *) fstype; 799 fsp->fs_next = fs_exclude_list; 800 fs_exclude_list = fsp; 801} 802 803void 804usage (int status) 805{ 806 if (status != EXIT_SUCCESS) 807 fprintf (stderr, _("Try `%s --help' for more information.\n"), 808 program_name); 809 else 810 { 811 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); 812 fputs (_("\ 813Show information about the file system on which each FILE resides,\n\ 814or all file systems by default.\n\ 815\n\ 816"), stdout); 817 fputs (_("\ 818Mandatory arguments to long options are mandatory for short options too.\n\ 819"), stdout); 820 fputs (_("\ 821 -a, --all include dummy file systems\n\ 822 -B, --block-size=SIZE use SIZE-byte blocks\n\ 823 --total produce a grand total\n\ 824 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\ 825 -H, --si likewise, but use powers of 1000 not 1024\n\ 826"), stdout); 827 fputs (_("\ 828 -i, --inodes list inode information instead of block usage\n\ 829 -k like --block-size=1K\n\ 830 -l, --local limit listing to local file systems\n\ 831 --no-sync do not invoke sync before getting usage info (default)\n\ 832"), stdout); 833 fputs (_("\ 834 -P, --portability use the POSIX output format\n\ 835 --sync invoke sync before getting usage info\n\ 836 -t, --type=TYPE limit listing to file systems of type TYPE\n\ 837 -T, --print-type print file system type\n\ 838 -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\ 839 -v (ignored)\n\ 840"), stdout); 841 fputs (HELP_OPTION_DESCRIPTION, stdout); 842 fputs (VERSION_OPTION_DESCRIPTION, stdout); 843 emit_blocksize_note ("DF"); 844 emit_size_note (); 845 emit_ancillary_info (); 846 } 847 exit (status); 848} 849 850int 851main (int argc, char **argv) 852{ 853 struct stat *stats IF_LINT (= 0); 854 855 initialize_main (&argc, &argv); 856 set_program_name (argv[0]); 857 setlocale (LC_ALL, ""); 858 bindtextdomain (PACKAGE, LOCALEDIR); 859 textdomain (PACKAGE); 860 861 atexit (close_stdout); 862 863 fs_select_list = NULL; 864 fs_exclude_list = NULL; 865 inode_format = false; 866 show_all_fs = false; 867 show_listed_fs = false; 868 human_output_opts = -1; 869 print_type = false; 870 file_systems_processed = false; 871 posix_format = false; 872 exit_status = EXIT_SUCCESS; 873 print_grand_total = false; 874 grand_fsu.fsu_blocksize = 1; 875 876 for (;;) 877 { 878 int oi = -1; 879 int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, 880 &oi); 881 if (c == -1) 882 break; 883 884 switch (c) 885 { 886 case 'a': 887 show_all_fs = true; 888 break; 889 case 'B': 890 { 891 enum strtol_error e = human_options (optarg, &human_output_opts, 892 &output_block_size); 893 if (e != LONGINT_OK) 894 xstrtol_fatal (e, oi, c, long_options, optarg); 895 } 896 break; 897 case 'i': 898 inode_format = true; 899 break; 900 case 'h': 901 human_output_opts = human_autoscale | human_SI | human_base_1024; 902 output_block_size = 1; 903 break; 904 case 'H': 905 human_output_opts = human_autoscale | human_SI; 906 output_block_size = 1; 907 break; 908 case 'k': 909 human_output_opts = 0; 910 output_block_size = 1024; 911 break; 912 case 'l': 913 show_local_fs = true; 914 break; 915 case 'm': /* obsolescent */ 916 human_output_opts = 0; 917 output_block_size = 1024 * 1024; 918 break; 919 case 'T': 920 print_type = true; 921 break; 922 case 'P': 923 posix_format = true; 924 break; 925 case SYNC_OPTION: 926 require_sync = true; 927 break; 928 case NO_SYNC_OPTION: 929 require_sync = false; 930 break; 931 932 case 'F': 933 /* Accept -F as a synonym for -t for compatibility with Solaris. */ 934 case 't': 935 add_fs_type (optarg); 936 break; 937 938 case 'v': /* For SysV compatibility. */ 939 /* ignore */ 940 break; 941 case 'x': 942 add_excluded_fs_type (optarg); 943 break; 944 945 case 'c': 946 print_grand_total = true; 947 break; 948 949 case_GETOPT_HELP_CHAR; 950 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); 951 952 default: 953 usage (EXIT_FAILURE); 954 } 955 } 956 957 if (human_output_opts == -1) 958 { 959 if (posix_format) 960 { 961 human_output_opts = 0; 962 output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024); 963 } 964 else 965 human_options (getenv ("DF_BLOCK_SIZE"), 966 &human_output_opts, &output_block_size); 967 } 968 969 /* Fail if the same file system type was both selected and excluded. */ 970 { 971 bool match = false; 972 struct fs_type_list *fs_incl; 973 for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next) 974 { 975 struct fs_type_list *fs_excl; 976 for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next) 977 { 978 if (STREQ (fs_incl->fs_name, fs_excl->fs_name)) 979 { 980 error (0, 0, 981 _("file system type %s both selected and excluded"), 982 quote (fs_incl->fs_name)); 983 match = true; 984 break; 985 } 986 } 987 } 988 if (match) 989 exit (EXIT_FAILURE); 990 } 991 992 if (optind < argc) 993 { 994 int i; 995 996 /* Open each of the given entries to make sure any corresponding 997 partition is automounted. This must be done before reading the 998 file system table. */ 999 stats = xnmalloc (argc - optind, sizeof *stats); 1000 for (i = optind; i < argc; ++i) 1001 { 1002 /* Prefer to open with O_NOCTTY and use fstat, but fall back 1003 on using "stat", in case the file is unreadable. */ 1004 int fd = open (argv[i], O_RDONLY | O_NOCTTY); 1005 if ((fd < 0 || fstat (fd, &stats[i - optind])) 1006 && stat (argv[i], &stats[i - optind])) 1007 { 1008 error (0, errno, "%s", quote (argv[i])); 1009 exit_status = EXIT_FAILURE; 1010 argv[i] = NULL; 1011 } 1012 if (0 <= fd) 1013 close (fd); 1014 } 1015 } 1016 1017 mount_list = 1018 read_file_system_list ((fs_select_list != NULL 1019 || fs_exclude_list != NULL 1020 || print_type 1021 || show_local_fs)); 1022 1023 if (mount_list == NULL) 1024 { 1025 /* Couldn't read the table of mounted file systems. 1026 Fail if df was invoked with no file name arguments; 1027 Otherwise, merely give a warning and proceed. */ 1028 int status = (optind < argc ? 0 : EXIT_FAILURE); 1029 const char *warning = (optind < argc ? _("Warning: ") : ""); 1030 error (status, errno, "%s%s", warning, 1031 _("cannot read table of mounted file systems")); 1032 } 1033 1034 if (require_sync) 1035 sync (); 1036 1037 if (optind < argc) 1038 { 1039 int i; 1040 1041 /* Display explicitly requested empty file systems. */ 1042 show_listed_fs = true; 1043 1044 for (i = optind; i < argc; ++i) 1045 if (argv[i]) 1046 show_entry (argv[i], &stats[i - optind]); 1047 } 1048 else 1049 show_all_entries (); 1050 1051 if (print_grand_total) 1052 { 1053 if (inode_format) 1054 grand_fsu.fsu_blocks = 1; 1055 show_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu); 1056 } 1057 1058 if (! file_systems_processed) 1059 error (EXIT_FAILURE, 0, _("no file systems processed")); 1060 1061 exit (exit_status); 1062} 1063