1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2002 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 with a human_ceiling
217 human_inexact_style, except return "-" if the argument is -1, and
218 if NEGATIVE is 1 then N represents a negative number, expressed in
222 df_readable (int negative
, uintmax_t n
, char *buf
,
223 int from_block_size
, int t_output_block_size
)
229 char *p
= human_readable_inexact (negative
? - n
: n
,
230 buf
+ negative
, from_block_size
,
239 /* Display a space listing for the disk device with absolute path DISK.
240 If MOUNT_POINT is non-NULL, it is the path of the root of the
242 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
243 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
244 not be able to produce statistics in this case.
245 ME_DUMMY and ME_REMOTE are the mount entry flags. */
248 show_dev (const char *disk
, const char *mount_point
, const char *fstype
,
249 int me_dummy
, int me_remote
)
252 const char *stat_file
;
253 char buf
[3][LONGEST_HUMAN_READABLE
+ 2];
260 int negate_available
;
261 uintmax_t available_to_root
;
266 if (me_remote
&& show_local_fs
)
269 if (me_dummy
&& show_all_fs
== 0 && !show_listed_fs
)
272 if (!selected_fstype (fstype
) || excluded_fstype (fstype
))
275 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
276 program reports on the filesystem that the special file is on.
277 It would be better to report on the unmounted filesystem,
278 but statfs doesn't do that on most systems. */
279 stat_file
= mount_point
? mount_point
: disk
;
281 if (get_fs_usage (stat_file
, disk
, &fsu
))
283 error (0, errno
, "%s", quote (stat_file
));
288 if (fsu
.fsu_blocks
== 0 && !show_all_fs
&& !show_listed_fs
)
292 disk
= "-"; /* unknown */
294 fstype
= "-"; /* unknown */
296 /* df.c reserved 5 positions for fstype,
297 but that does not suffice for type iso9660 */
300 int disk_name_len
= (int) strlen (disk
);
301 int fstype_len
= (int) strlen (fstype
);
302 if (disk_name_len
+ fstype_len
+ 2 < 20)
303 printf ("%s%*s ", disk
, 18 - disk_name_len
, fstype
);
304 else if (!posix_format
)
305 printf ("%s\n%18s ", disk
, fstype
);
307 printf ("%s %s", disk
, fstype
);
311 if ((int) strlen (disk
) > 20 && !posix_format
)
312 printf ("%s\n%20s", disk
, "");
314 printf ("%-20s", disk
);
322 output_units
= output_block_size
< 0 ? output_block_size
: 1;
323 total
= fsu
.fsu_files
;
324 available
= fsu
.fsu_ffree
;
325 negate_available
= 0;
326 available_to_root
= available
;
330 width
= output_block_size
< 0 ? 5 + (output_block_size
== -1000) : 9;
331 use_width
= (posix_format
&& 0 <= output_block_size
) ? 8 : 4;
332 input_units
= fsu
.fsu_blocksize
;
333 output_units
= output_block_size
;
334 total
= fsu
.fsu_blocks
;
335 available
= fsu
.fsu_bavail
;
336 negate_available
= fsu
.fsu_bavail_top_bit_set
;
337 available_to_root
= fsu
.fsu_bfree
;
342 if (total
!= -1 && available_to_root
!= -1)
344 used
= total
- available_to_root
;
345 if (total
< available_to_root
)
352 printf (" %*s %*s %*s ",
353 width
, df_readable (0, total
,
354 buf
[0], input_units
, output_units
),
355 width
, df_readable (negate_used
, used
,
356 buf
[1], input_units
, output_units
),
357 width
, df_readable (negate_available
, available
,
358 buf
[2], input_units
, output_units
));
360 if (used
== -1 || available
== -1)
362 else if (!negate_used
363 && used
<= TYPE_MAXIMUM (uintmax_t) / 100
364 && used
+ available
!= 0
365 && (used
+ available
< used
) == negate_available
)
367 uintmax_t u100
= used
* 100;
368 uintmax_t nonroot_total
= used
+ available
;
369 pct
= u100
/ nonroot_total
+ (u100
% nonroot_total
!= 0);
373 /* The calculation cannot be done easily with integer
374 arithmetic. Fall back on floating point. This can suffer
375 from minor rounding errors, but doing it exactly requires
376 multiple precision arithmetic, and it's not worth the
378 double u
= negate_used
? - (double) - used
: used
;
379 double a
= negate_available
? - (double) - available
: available
;
380 double nonroot_total
= u
+ a
;
384 pct
= u
* 100 / nonroot_total
;
387 /* Like `pct = ceil (dpct);', but avoid ceil so that
388 the math library needn't be linked. */
389 if (ipct
- 1 < pct
&& pct
<= ipct
+ 1)
390 pct
= ipct
+ (ipct
< pct
);
395 printf ("%*.0f%%", use_width
- 1, pct
);
397 printf ("%*s", use_width
, "- ");
401 #ifdef HIDE_AUTOMOUNT_PREFIX
402 /* Don't print the first directory name in MOUNT_POINT if it's an
403 artifact of an automounter. This is a bit too aggressive to be
405 if (strncmp ("/auto/", mount_point
, 6) == 0)
407 else if (strncmp ("/tmp_mnt/", mount_point
, 9) == 0)
410 printf (" %s", mount_point
);
415 /* Identify the directory, if any, that device
416 DISK is mounted on, and show its disk usage. */
419 show_disk (const char *disk
)
421 struct mount_entry
*me
;
423 for (me
= mount_list
; me
; me
= me
->me_next
)
424 if (STREQ (disk
, me
->me_devname
))
426 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
427 me
->me_dummy
, me
->me_remote
);
430 /* No filesystem is mounted on DISK. */
431 show_dev (disk
, (char *) NULL
, (char *) NULL
, 0, 0);
434 /* Return the root mountpoint of the filesystem on which FILE exists, in
435 malloced storage. FILE_STAT should be the result of stating FILE. */
437 find_mount_point (const char *file
, const struct stat
*file_stat
)
439 struct saved_cwd cwd
;
440 struct stat last_stat
;
441 char *mp
= 0; /* The malloced mount point path. */
446 if (S_ISDIR (file_stat
->st_mode
))
447 /* FILE is a directory, so just chdir there directly. */
449 last_stat
= *file_stat
;
450 if (chdir (file
) < 0)
454 /* FILE is some other kind of file, we need to use its directory. */
456 char *dir
= dir_name (file
);
457 int rv
= chdir (dir
);
463 if (stat (".", &last_stat
) < 0)
467 /* Now walk up FILE's parents until we find another filesystem or /,
468 chdiring as we go. LAST_STAT holds stat information for the last place
473 if (stat ("..", &st
) < 0)
475 if (st
.st_dev
!= last_stat
.st_dev
|| st
.st_ino
== last_stat
.st_ino
)
476 /* cwd is the mount point. */
478 if (chdir ("..") < 0)
483 /* Finally reached a mount point, see what it's called. */
487 /* Restore the original cwd. */
489 int save_errno
= errno
;
490 if (restore_cwd (&cwd
, 0, mp
))
491 exit (1); /* We're scrod. */
499 /* Figure out which device file or directory POINT is mounted on
500 and show its disk usage.
501 STATP is the results of `stat' on POINT. */
503 show_point (const char *point
, const struct stat
*statp
)
505 struct stat disk_stats
;
506 struct mount_entry
*me
;
507 struct mount_entry
*matching_dummy
= NULL
;
508 char *needs_freeing
= NULL
;
510 /* If POINT is an absolute path name, see if we can find the
511 mount point without performing any extra stat calls at all. */
514 for (me
= mount_list
; me
; me
= me
->me_next
)
516 if (STREQ (me
->me_mountdir
, point
) && !STREQ (me
->me_type
, "lofs"))
518 /* Prefer non-dummy entries. */
526 goto show_matching_dummy
;
529 /* Ideally, the following mess of #if'd code would be in a separate
530 file, and there'd be a single function call here. FIXME, someday. */
532 #if HAVE_REALPATH || HAVE_RESOLVEPATH || HAVE_CANONICALIZE_FILE_NAME
533 /* Calculate the real absolute path for POINT, and use that to find
534 the mount point. This avoids statting unavailable mount points,
535 which can hang df. */
537 char const *abspoint
= point
;
539 ssize_t resolved_len
;
540 struct mount_entry
*best_match
= NULL
;
542 # if HAVE_CANONICALIZE_FILE_NAME
543 resolved
= canonicalize_file_name (abspoint
);
544 resolved_len
= resolved
? strlen (resolved
) : -1;
546 # if HAVE_RESOLVEPATH
547 /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
548 relative names into absolute ones, so prepend the working
549 directory if the path is not absolute. */
553 static char const *wd
;
557 struct stat pwd_stats
;
558 struct stat dot_stats
;
560 /* Use PWD if it is correct; this is usually cheaper than
564 && stat (wd
, &pwd_stats
) == 0
565 && stat (".", &dot_stats
) == 0
566 && SAME_INODE (pwd_stats
, dot_stats
)))
572 needs_freeing
= path_concat (wd
, point
, NULL
);
574 abspoint
= needs_freeing
;
579 # if HAVE_RESOLVEPATH
581 size_t resolved_size
= strlen (abspoint
);
584 resolved_size
= 2 * resolved_size
+ 1;
585 resolved
= xmalloc (resolved_size
);
586 resolved_len
= resolvepath (abspoint
, resolved
, resolved_size
);
587 if (resolved_len
< resolved_size
)
593 /* Use realpath only as a last resort.
594 It provides a very poor interface. */
595 resolved
= xmalloc (PATH_MAX
+ 1);
596 resolved
= (char *) realpath (abspoint
, resolved
);
597 resolved_len
= resolved
? strlen (resolved
) : -1;
601 if (1 <= resolved_len
&& resolved
[0] == '/')
603 size_t best_match_len
= 0;
605 for (me
= mount_list
; me
; me
= me
->me_next
)
608 size_t len
= strlen (me
->me_mountdir
);
609 if (best_match_len
< len
&& len
<= resolved_len
610 && (len
== 1 /* root file system */
611 || ((len
== resolved_len
|| resolved
[len
] == '/')
612 && strncmp (me
->me_mountdir
, resolved
, len
) == 0)))
615 best_match_len
= len
;
623 if (best_match
&& !STREQ (best_match
->me_type
, "lofs")
624 && stat (best_match
->me_mountdir
, &disk_stats
) == 0
625 && disk_stats
.st_dev
== statp
->st_dev
)
633 for (me
= mount_list
; me
; me
= me
->me_next
)
635 if (me
->me_dev
== (dev_t
) -1)
637 if (stat (me
->me_mountdir
, &disk_stats
) == 0)
638 me
->me_dev
= disk_stats
.st_dev
;
641 error (0, errno
, "%s", quote (me
->me_mountdir
));
643 /* So we won't try and fail repeatedly. */
644 me
->me_dev
= (dev_t
) -2;
648 if (statp
->st_dev
== me
->me_dev
)
650 /* Skip bogus mtab entries. */
651 if (stat (me
->me_mountdir
, &disk_stats
) != 0
652 || disk_stats
.st_dev
!= me
->me_dev
)
654 me
->me_dev
= (dev_t
) -2;
658 /* Prefer non-dummy entries. */
666 goto show_matching_dummy
;
668 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
669 print as much info as we can; methods that require the device to be
670 present will fail at a later point. */
672 /* Find the actual mount point. */
673 char *mp
= find_mount_point (point
, statp
);
676 show_dev (0, mp
, 0, 0, 0);
680 error (0, errno
, "%s", quote (point
));
683 goto free_then_return
;
688 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
, me
->me_dummy
,
692 free (needs_freeing
);
695 /* Determine what kind of node PATH is and show the disk usage
696 for it. STATP is the results of `stat' on PATH. */
699 show_entry (const char *path
, const struct stat
*statp
)
701 if (S_ISBLK (statp
->st_mode
) || S_ISCHR (statp
->st_mode
))
704 show_point (path
, statp
);
707 /* Show all mounted filesystems, except perhaps those that are of
708 an unselected type or are empty. */
711 show_all_entries (void)
713 struct mount_entry
*me
;
715 for (me
= mount_list
; me
; me
= me
->me_next
)
716 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
717 me
->me_dummy
, me
->me_remote
);
720 /* Add FSTYPE to the list of filesystem types to display. */
723 add_fs_type (const char *fstype
)
725 struct fs_type_list
*fsp
;
727 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
728 fsp
->fs_name
= (char *) fstype
;
729 fsp
->fs_next
= fs_select_list
;
730 fs_select_list
= fsp
;
733 /* Add FSTYPE to the list of filesystem types to be omitted. */
736 add_excluded_fs_type (const char *fstype
)
738 struct fs_type_list
*fsp
;
740 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
741 fsp
->fs_name
= (char *) fstype
;
742 fsp
->fs_next
= fs_exclude_list
;
743 fs_exclude_list
= fsp
;
750 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
754 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name
);
756 Show information about the filesystem on which each FILE resides,\n\
757 or all filesystems by default.\n\
761 Mandatory arguments to long options are mandatory for short options too.\n\
764 -a, --all include filesystems having 0 blocks\n\
765 -B, --block-size=SIZE use SIZE-byte blocks\n\
766 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
767 -H, --si likewise, but use powers of 1000 not 1024\n\
770 -i, --inodes list inode information instead of block usage\n\
771 -k like --block-size=1K\n\
772 -l, --local limit listing to local filesystems\n\
773 --no-sync do not invoke sync before getting usage info (default)\n\
776 -P, --portability use the POSIX output format\n\
777 --sync invoke sync before getting usage info\n\
778 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
779 -T, --print-type print filesystem type\n\
780 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
783 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
784 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
786 SIZE may be (or may be an integer optionally followed by) one of following:\n\
787 kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
789 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT
);
795 main (int argc
, char **argv
)
798 struct stat
*stats
IF_LINT (= 0);
799 int n_valid_args
= 0;
801 program_name
= argv
[0];
802 setlocale (LC_ALL
, "");
803 bindtextdomain (PACKAGE
, LOCALEDIR
);
804 textdomain (PACKAGE
);
806 atexit (close_stdout
);
808 fs_select_list
= NULL
;
809 fs_exclude_list
= NULL
;
814 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size
);
820 while ((c
= getopt_long (argc
, argv
, "aB:iF:hHklmPTt:vx:", long_options
, NULL
))
825 case 0: /* Long option. */
831 human_block_size (optarg
, 1, &output_block_size
);
837 output_block_size
= -1024;
840 output_block_size
= -1000;
843 output_block_size
= 1024;
848 case 'm': /* obsolescent */
849 output_block_size
= 1024 * 1024;
865 /* Accept -F as a synonym for -t for compatibility with Solaris. */
867 add_fs_type (optarg
);
870 case 'v': /* For SysV compatibility. */
874 add_excluded_fs_type (optarg
);
877 case_GETOPT_HELP_CHAR
;
878 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
885 /* Fail if the same file system type was both selected and excluded. */
888 struct fs_type_list
*fs_incl
;
889 for (fs_incl
= fs_select_list
; fs_incl
; fs_incl
= fs_incl
->fs_next
)
891 struct fs_type_list
*fs_excl
;
892 for (fs_excl
= fs_exclude_list
; fs_excl
; fs_excl
= fs_excl
->fs_next
)
894 if (STREQ (fs_incl
->fs_name
, fs_excl
->fs_name
))
897 _("file system type %s both selected and excluded"),
898 quote (fs_incl
->fs_name
));
911 /* stat all the given entries to make sure they get automounted,
912 if necessary, before reading the filesystem table. */
913 stats
= (struct stat
*)
914 xmalloc ((argc
- optind
) * sizeof (struct stat
));
915 for (i
= optind
; i
< argc
; ++i
)
917 if (stat (argv
[i
], &stats
[i
- optind
]))
919 error (0, errno
, "%s", quote (argv
[i
]));
931 read_filesystem_list ((fs_select_list
!= NULL
932 || fs_exclude_list
!= NULL
936 if (mount_list
== NULL
)
938 /* Couldn't read the table of mounted filesystems.
939 Fail if df was invoked with no file name arguments;
940 Otherwise, merely give a warning and proceed. */
941 const char *warning
= (optind
== argc
? "" : _("Warning: "));
942 int status
= (optind
== argc
? 1 : 0);
943 error (status
, errno
,
944 _("%scannot read table of mounted filesystems"), warning
);
959 /* Display explicitly requested empty filesystems. */
962 if (n_valid_args
> 0)
965 for (i
= optind
; i
< argc
; ++i
)
967 show_entry (argv
[i
], &stats
[i
- optind
]);