1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2001 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
19 --human-readable and --megabyte options added by lm@sgi.com.
20 --si and large file support added by eggert@twinsun.com. */
28 # include <inttypes.h>
31 #include <sys/types.h>
40 #include "mountlist.h"
41 #include "path-concat.h"
45 /* The official name of this program (e.g., no `g' prefix). */
46 #define PROGRAM_NAME "df"
49 "Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
53 /* Name this program was run with. */
56 /* If nonzero, show inode information. */
57 static int inode_format
;
59 /* If nonzero, show even filesystems with zero size or
60 uninteresting types. */
61 static int show_all_fs
;
63 /* If nonzero, show only local filesystems. */
64 static int show_local_fs
;
66 /* If nonzero, output data for each filesystem corresponding to a
67 command line argument -- even if it's a dummy (automounter) entry. */
68 static int show_listed_fs
;
70 /* If positive, the units to use when printing sizes;
71 if negative, the human-readable base. */
72 static int output_block_size
;
74 /* If nonzero, use the POSIX output format. */
75 static int posix_format
;
77 /* If nonzero, invoke the `sync' system call before getting any usage data.
78 Using this option can make df very slow, especially with many or very
79 busy disks. Note that this may make a difference on some systems --
80 SunOs4.1.3, for one. It is *not* necessary on Linux. */
81 static int require_sync
= 0;
83 /* Nonzero if errors have occurred. */
84 static int exit_status
;
86 /* A filesystem type to display. */
91 struct fs_type_list
*fs_next
;
94 /* Linked list of filesystem types to display.
95 If `fs_select_list' is NULL, list all types.
96 This table is generated dynamically from command-line options,
97 rather than hardcoding into the program what it thinks are the
98 valid filesystem types; let the user specify any filesystem type
99 they want to, and if there are any filesystems of that type, they
102 Some filesystem types:
103 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
105 static struct fs_type_list
*fs_select_list
;
107 /* Linked list of filesystem types to omit.
108 If the list is empty, don't exclude any types. */
110 static struct fs_type_list
*fs_exclude_list
;
112 /* Linked list of mounted filesystems. */
113 static struct mount_entry
*mount_list
;
115 /* If nonzero, print filesystem type as well. */
116 static int print_type
;
118 /* For long options that have no equivalent short option, use a
119 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
122 SYNC_OPTION
= CHAR_MAX
+ 1,
126 static struct option
const long_options
[] =
128 {"all", no_argument
, NULL
, 'a'},
129 {"block-size", required_argument
, NULL
, 'B'},
130 {"inodes", no_argument
, NULL
, 'i'},
131 {"human-readable", no_argument
, NULL
, 'h'},
132 {"si", no_argument
, NULL
, 'H'},
133 {"kilobytes", no_argument
, NULL
, 'k'}, /* long form is obsolescent */
134 {"local", no_argument
, NULL
, 'l'},
135 {"megabytes", no_argument
, NULL
, 'm'}, /* obsolescent */
136 {"portability", no_argument
, NULL
, 'P'},
137 {"print-type", no_argument
, NULL
, 'T'},
138 {"sync", no_argument
, NULL
, SYNC_OPTION
},
139 {"no-sync", no_argument
, NULL
, NO_SYNC_OPTION
},
140 {"type", required_argument
, NULL
, 't'},
141 {"exclude-type", required_argument
, NULL
, 'x'},
142 {GETOPT_HELP_OPTION_DECL
},
143 {GETOPT_VERSION_OPTION_DECL
},
150 printf (_("Filesystem "));
158 printf (_(" Inodes IUsed IFree IUse%%"));
159 else if (output_block_size
< 0)
161 if (output_block_size
== -1000)
162 printf (_(" Size Used Avail Use%%"));
164 printf (_(" Size Used Avail Use%%"));
166 else if (posix_format
)
167 printf (_(" %4d-blocks Used Available Capacity"), output_block_size
);
170 char buf
[LONGEST_HUMAN_READABLE
+ 1];
171 char *p
= human_readable (output_block_size
, buf
, 1, -1024);
173 /* Replace e.g. "1.0K" by "1K". */
174 size_t plen
= strlen (p
);
175 if (3 <= plen
&& strncmp (p
+ plen
- 3, ".0", 2) == 0)
176 strcpy (p
+ plen
- 3, p
+ plen
- 1);
178 printf (_(" %4s-blocks Used Available Use%%"), p
);
181 printf (_(" Mounted on\n"));
184 /* If FSTYPE is a type of filesystem that should be listed,
185 return nonzero, else zero. */
188 selected_fstype (const char *fstype
)
190 const struct fs_type_list
*fsp
;
192 if (fs_select_list
== NULL
|| fstype
== NULL
)
194 for (fsp
= fs_select_list
; fsp
; fsp
= fsp
->fs_next
)
195 if (STREQ (fstype
, fsp
->fs_name
))
200 /* If FSTYPE is a type of filesystem that should be omitted,
201 return nonzero, else zero. */
204 excluded_fstype (const char *fstype
)
206 const struct fs_type_list
*fsp
;
208 if (fs_exclude_list
== NULL
|| fstype
== NULL
)
210 for (fsp
= fs_exclude_list
; fsp
; fsp
= fsp
->fs_next
)
211 if (STREQ (fstype
, fsp
->fs_name
))
216 /* Like human_readable_inexact, except return "-" if the argument is -1,
217 and if NEGATIVE is 1 then N represents a negative number, expressed
218 in two's complement. */
220 df_readable (int negative
, uintmax_t n
, char *buf
,
221 int from_block_size
, int t_output_block_size
,
222 enum human_inexact_style s
)
228 char *p
= human_readable_inexact (negative
? - n
: n
,
229 buf
+ negative
, from_block_size
,
238 /* Display a space listing for the disk device with absolute path DISK.
239 If MOUNT_POINT is non-NULL, it is the path of the root of the
241 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
242 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
243 not be able to produce statistics in this case.
244 ME_DUMMY and ME_REMOTE are the mount entry flags. */
247 show_dev (const char *disk
, const char *mount_point
, const char *fstype
,
248 int me_dummy
, int me_remote
)
251 const char *stat_file
;
252 char buf
[3][LONGEST_HUMAN_READABLE
+ 2];
259 int negate_available
;
260 uintmax_t available_to_root
;
265 if (me_remote
&& show_local_fs
)
268 if (me_dummy
&& show_all_fs
== 0 && !show_listed_fs
)
271 if (!selected_fstype (fstype
) || excluded_fstype (fstype
))
274 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
275 program reports on the filesystem that the special file is on.
276 It would be better to report on the unmounted filesystem,
277 but statfs doesn't do that on most systems. */
278 stat_file
= mount_point
? mount_point
: disk
;
280 if (get_fs_usage (stat_file
, disk
, &fsu
))
282 error (0, errno
, "%s", quote (stat_file
));
287 if (fsu
.fsu_blocks
== 0 && !show_all_fs
&& !show_listed_fs
)
291 disk
= "-"; /* unknown */
293 fstype
= "-"; /* unknown */
295 /* df.c reserved 5 positions for fstype,
296 but that does not suffice for type iso9660 */
299 int disk_name_len
= (int) strlen (disk
);
300 int fstype_len
= (int) strlen (fstype
);
301 if (disk_name_len
+ fstype_len
+ 2 < 20)
302 printf ("%s%*s ", disk
, 18 - disk_name_len
, fstype
);
303 else if (!posix_format
)
304 printf ("%s\n%18s ", disk
, fstype
);
306 printf ("%s %s", disk
, fstype
);
310 if ((int) strlen (disk
) > 20 && !posix_format
)
311 printf ("%s\n%20s", disk
, "");
313 printf ("%-20s", disk
);
321 output_units
= output_block_size
< 0 ? output_block_size
: 1;
322 total
= fsu
.fsu_files
;
323 available
= fsu
.fsu_ffree
;
324 negate_available
= 0;
325 available_to_root
= available
;
329 width
= output_block_size
< 0 ? 5 + (output_block_size
== -1000) : 9;
330 use_width
= posix_format
? 8 : 4;
331 input_units
= fsu
.fsu_blocksize
;
332 output_units
= output_block_size
;
333 total
= fsu
.fsu_blocks
;
334 available
= fsu
.fsu_bavail
;
335 negate_available
= fsu
.fsu_bavail_top_bit_set
;
336 available_to_root
= fsu
.fsu_bfree
;
341 if (total
!= -1 && available_to_root
!= -1)
343 used
= total
- available_to_root
;
344 if (total
< available_to_root
)
351 printf (" %*s %*s %*s ",
352 width
, df_readable (0, total
,
353 buf
[0], input_units
, output_units
,
356 : human_round_to_even
)),
357 width
, df_readable (negate_used
, used
,
358 buf
[1], input_units
, output_units
,
360 width
, df_readable (negate_available
, available
,
361 buf
[2], input_units
, output_units
,
362 posix_format
? human_ceiling
: human_floor
));
364 if (used
== -1 || available
== -1)
366 else if (!negate_used
367 && used
<= TYPE_MAXIMUM (uintmax_t) / 100
368 && used
+ available
!= 0
369 && (used
+ available
< used
) == negate_available
)
371 uintmax_t u100
= used
* 100;
372 uintmax_t nonroot_total
= used
+ available
;
373 pct
= u100
/ nonroot_total
+ (u100
% nonroot_total
!= 0);
377 /* The calculation cannot be done easily with integer
378 arithmetic. Fall back on floating point. This can suffer
379 from minor rounding errors, but doing it exactly requires
380 multiple precision arithmetic, and it's not worth the
382 double u
= negate_used
? - (double) - used
: used
;
383 double a
= negate_available
? - (double) - available
: available
;
384 double nonroot_total
= u
+ a
;
388 pct
= u
* 100 / nonroot_total
;
391 /* Like `pct = ceil (dpct);', but avoid ceil so that
392 the math library needn't be linked. */
393 if (ipct
- 1 < pct
&& pct
<= ipct
+ 1)
394 pct
= ipct
+ (ipct
< pct
);
399 printf ("%*.0f%%", use_width
- 1, pct
);
401 printf ("%*s", use_width
, "- ");
405 #ifdef HIDE_AUTOMOUNT_PREFIX
406 /* Don't print the first directory name in MOUNT_POINT if it's an
407 artifact of an automounter. This is a bit too aggressive to be
409 if (strncmp ("/auto/", mount_point
, 6) == 0)
411 else if (strncmp ("/tmp_mnt/", mount_point
, 9) == 0)
414 printf (" %s", mount_point
);
419 /* Identify the directory, if any, that device
420 DISK is mounted on, and show its disk usage. */
423 show_disk (const char *disk
)
425 struct mount_entry
*me
;
427 for (me
= mount_list
; me
; me
= me
->me_next
)
428 if (STREQ (disk
, me
->me_devname
))
430 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
431 me
->me_dummy
, me
->me_remote
);
434 /* No filesystem is mounted on DISK. */
435 show_dev (disk
, (char *) NULL
, (char *) NULL
, 0, 0);
438 /* Return the root mountpoint of the filesystem on which FILE exists, in
439 malloced storage. FILE_STAT should be the result of stating FILE. */
441 find_mount_point (const char *file
, const struct stat
*file_stat
)
443 struct saved_cwd cwd
;
444 struct stat last_stat
;
445 char *mp
= 0; /* The malloced mount point path. */
450 if (S_ISDIR (file_stat
->st_mode
))
451 /* FILE is a directory, so just chdir there directly. */
453 last_stat
= *file_stat
;
454 if (chdir (file
) < 0)
458 /* FILE is some other kind of file, we need to use its directory. */
460 char *dir
= dir_name (file
);
461 int rv
= chdir (dir
);
467 if (stat (".", &last_stat
) < 0)
471 /* Now walk up FILE's parents until we find another filesystem or /,
472 chdiring as we go. LAST_STAT holds stat information for the last place
477 if (stat ("..", &st
) < 0)
479 if (st
.st_dev
!= last_stat
.st_dev
|| st
.st_ino
== last_stat
.st_ino
)
480 /* cwd is the mount point. */
482 if (chdir ("..") < 0)
487 /* Finally reached a mount point, see what it's called. */
491 /* Restore the original cwd. */
493 int save_errno
= errno
;
494 if (restore_cwd (&cwd
, 0, mp
))
495 exit (1); /* We're scrod. */
503 /* Figure out which device file or directory POINT is mounted on
504 and show its disk usage.
505 STATP is the results of `stat' on POINT. */
507 show_point (const char *point
, const struct stat
*statp
)
509 struct stat disk_stats
;
510 struct mount_entry
*me
;
511 struct mount_entry
*matching_dummy
= NULL
;
512 char *needs_freeing
= NULL
;
514 /* If POINT is an absolute path name, see if we can find the
515 mount point without performing any extra stat calls at all. */
518 for (me
= mount_list
; me
; me
= me
->me_next
)
520 if (STREQ (me
->me_mountdir
, point
) && !STREQ (me
->me_type
, "lofs"))
522 /* Prefer non-dummy entries. */
530 goto show_matching_dummy
;
533 /* Ideally, the following mess of #if'd code would be in a separate
534 file, and there'd be a single function call here. FIXME, someday. */
536 #if HAVE_REALPATH || HAVE_RESOLVEPATH || HAVE_CANONICALIZE_FILE_NAME
537 /* Calculate the real absolute path for POINT, and use that to find
538 the mount point. This avoids statting unavailable mount points,
539 which can hang df. */
541 char const *abspoint
= point
;
543 ssize_t resolved_len
;
544 struct mount_entry
*best_match
= NULL
;
546 # if HAVE_CANONICALIZE_FILE_NAME
547 resolved
= canonicalize_file_name (abspoint
);
548 resolved_len
= resolved
? strlen (resolved
) : -1;
550 # if HAVE_RESOLVEPATH
551 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
552 relative names into absolute ones, so prepend the working
553 directory if the path is not absolute. */
557 static char const *wd
;
561 struct stat pwd_stats
;
562 struct stat dot_stats
;
564 /* Use PWD if it is correct; this is usually cheaper than
568 && stat (wd
, &pwd_stats
) == 0
569 && stat (".", &dot_stats
) == 0
570 && SAME_INODE (pwd_stats
, dot_stats
)))
576 needs_freeing
= path_concat (wd
, point
, NULL
);
578 abspoint
= needs_freeing
;
583 # if HAVE_RESOLVEPATH
585 size_t resolved_size
= strlen (abspoint
);
588 resolved_size
= 2 * resolved_size
+ 1;
589 resolved
= xmalloc (resolved_size
);
590 resolved_len
= resolvepath (abspoint
, resolved
, resolved_size
);
591 if (resolved_len
< resolved_size
)
597 /* Use realpath only as a last resort.
598 It provides a very poor interface. */
599 resolved
= xmalloc (PATH_MAX
+ 1);
600 resolved
= (char *) realpath (abspoint
, resolved
);
601 resolved_len
= resolved
? strlen (resolved
) : -1;
605 if (1 <= resolved_len
&& resolved
[0] == '/')
607 size_t best_match_len
= 0;
609 for (me
= mount_list
; me
; me
= me
->me_next
)
612 size_t len
= strlen (me
->me_mountdir
);
613 if (best_match_len
< len
&& len
<= resolved_len
614 && (len
== 1 /* root file system */
615 || ((len
== resolved_len
|| resolved
[len
] == '/')
616 && strncmp (me
->me_mountdir
, resolved
, len
) == 0)))
619 best_match_len
= len
;
627 if (best_match
&& !STREQ (best_match
->me_type
, "lofs")
628 && stat (best_match
->me_mountdir
, &disk_stats
) == 0
629 && disk_stats
.st_dev
== statp
->st_dev
)
637 for (me
= mount_list
; me
; me
= me
->me_next
)
639 if (me
->me_dev
== (dev_t
) -1)
641 if (stat (me
->me_mountdir
, &disk_stats
) == 0)
642 me
->me_dev
= disk_stats
.st_dev
;
645 error (0, errno
, "%s", quote (me
->me_mountdir
));
647 /* So we won't try and fail repeatedly. */
648 me
->me_dev
= (dev_t
) -2;
652 if (statp
->st_dev
== me
->me_dev
)
654 /* Skip bogus mtab entries. */
655 if (stat (me
->me_mountdir
, &disk_stats
) != 0
656 || disk_stats
.st_dev
!= me
->me_dev
)
658 me
->me_dev
= (dev_t
) -2;
662 /* Prefer non-dummy entries. */
670 goto show_matching_dummy
;
672 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
673 print as much info as we can; methods that require the device to be
674 present will fail at a later point. */
676 /* Find the actual mount point. */
677 char *mp
= find_mount_point (point
, statp
);
680 show_dev (0, mp
, 0, 0, 0);
684 error (0, errno
, "%s", quote (point
));
687 goto free_then_return
;
692 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
, me
->me_dummy
,
696 free (needs_freeing
);
699 /* Determine what kind of node PATH is and show the disk usage
700 for it. STATP is the results of `stat' on PATH. */
703 show_entry (const char *path
, const struct stat
*statp
)
705 if (S_ISBLK (statp
->st_mode
) || S_ISCHR (statp
->st_mode
))
708 show_point (path
, statp
);
711 /* Show all mounted filesystems, except perhaps those that are of
712 an unselected type or are empty. */
715 show_all_entries (void)
717 struct mount_entry
*me
;
719 for (me
= mount_list
; me
; me
= me
->me_next
)
720 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
721 me
->me_dummy
, me
->me_remote
);
724 /* Add FSTYPE to the list of filesystem types to display. */
727 add_fs_type (const char *fstype
)
729 struct fs_type_list
*fsp
;
731 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
732 fsp
->fs_name
= (char *) fstype
;
733 fsp
->fs_next
= fs_select_list
;
734 fs_select_list
= fsp
;
737 /* Add FSTYPE to the list of filesystem types to be omitted. */
740 add_excluded_fs_type (const char *fstype
)
742 struct fs_type_list
*fsp
;
744 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
745 fsp
->fs_name
= (char *) fstype
;
746 fsp
->fs_next
= fs_exclude_list
;
747 fs_exclude_list
= fsp
;
754 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
758 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name
);
760 Show information about the filesystem on which each FILE resides,\n\
761 or all filesystems by default.\n\
765 Mandatory arguments to long options are mandatory for short options too.\n\
768 -a, --all include filesystems having 0 blocks\n\
769 -B, --block-size=SIZE use SIZE-byte blocks\n\
770 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
771 -H, --si likewise, but use powers of 1000 not 1024\n\
774 -i, --inodes list inode information instead of block usage\n\
775 -k like --block-size=1K\n\
776 -l, --local limit listing to local filesystems\n\
777 --no-sync do not invoke sync before getting usage info (default)\n\
780 -P, --portability use the POSIX output format\n\
781 --sync invoke sync before getting usage info\n\
782 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
783 -T, --print-type print filesystem type\n\
784 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
787 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
788 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
790 SIZE may be (or may be an integer optionally followed by) one of following:\n\
791 kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
793 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
799 main (int argc
, char **argv
)
802 struct stat
*stats
IF_LINT (= 0);
803 int n_valid_args
= 0;
805 program_name
= argv
[0];
806 setlocale (LC_ALL
, "");
807 bindtextdomain (PACKAGE
, LOCALEDIR
);
808 textdomain (PACKAGE
);
810 atexit (close_stdout
);
812 fs_select_list
= NULL
;
813 fs_exclude_list
= NULL
;
818 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size
);
824 while ((c
= getopt_long (argc
, argv
, "aB:iF:hHklmPTt:vx:", long_options
, NULL
))
829 case 0: /* Long option. */
835 human_block_size (optarg
, 1, &output_block_size
);
841 output_block_size
= -1024;
844 output_block_size
= -1000;
847 output_block_size
= 1024;
852 case 'm': /* obsolescent */
853 output_block_size
= 1024 * 1024;
869 /* Accept -F as a synonym for -t for compatibility with Solaris. */
871 add_fs_type (optarg
);
874 case 'v': /* For SysV compatibility. */
878 add_excluded_fs_type (optarg
);
881 case_GETOPT_HELP_CHAR
;
882 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
889 /* Fail if the same file system type was both selected and excluded. */
892 struct fs_type_list
*fs_incl
;
893 for (fs_incl
= fs_select_list
; fs_incl
; fs_incl
= fs_incl
->fs_next
)
895 struct fs_type_list
*fs_excl
;
896 for (fs_excl
= fs_exclude_list
; fs_excl
; fs_excl
= fs_excl
->fs_next
)
898 if (STREQ (fs_incl
->fs_name
, fs_excl
->fs_name
))
901 _("file system type %s both selected and excluded"),
902 quote (fs_incl
->fs_name
));
915 /* stat all the given entries to make sure they get automounted,
916 if necessary, before reading the filesystem table. */
917 stats
= (struct stat
*)
918 xmalloc ((argc
- optind
) * sizeof (struct stat
));
919 for (i
= optind
; i
< argc
; ++i
)
921 if (stat (argv
[i
], &stats
[i
- optind
]))
923 error (0, errno
, "%s", quote (argv
[i
]));
935 read_filesystem_list ((fs_select_list
!= NULL
936 || fs_exclude_list
!= NULL
940 if (mount_list
== NULL
)
942 /* Couldn't read the table of mounted filesystems.
943 Fail if df was invoked with no file name arguments;
944 Otherwise, merely give a warning and proceed. */
945 const char *warning
= (optind
== argc
? "" : _("Warning: "));
946 int status
= (optind
== argc
? 1 : 0);
947 error (status
, errno
,
948 _("%scannot read table of mounted filesystems"), warning
);
963 /* Display explicitly requested empty filesystems. */
966 if (n_valid_args
> 0)
969 for (i
= optind
; i
< argc
; ++i
)
971 show_entry (argv
[i
], &stats
[i
- optind
]);