1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-2000 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"
51 void strip_trailing_slashes ();
54 /* Name this program was run with. */
57 /* If nonzero, show inode information. */
58 static int inode_format
;
60 /* If nonzero, show even filesystems with zero size or
61 uninteresting types. */
62 static int show_all_fs
;
64 /* If nonzero, show only local filesystems. */
65 static int show_local_fs
;
67 /* If nonzero, output data for each filesystem corresponding to a
68 command line argument -- even if it's a dummy (automounter) entry. */
69 static int show_listed_fs
;
71 /* If positive, the units to use when printing sizes;
72 if negative, the human-readable base. */
73 static int output_block_size
;
75 /* If nonzero, use the POSIX output format. */
76 static int posix_format
;
78 /* If nonzero, invoke the `sync' system call before getting any usage data.
79 Using this option can make df very slow, especially with many or very
80 busy disks. Note that this may make a difference on some systems --
81 SunOs4.1.3, for one. It is *not* necessary on Linux. */
82 static int require_sync
= 0;
84 /* Nonzero if errors have occurred. */
85 static int exit_status
;
87 /* A filesystem type to display. */
92 struct fs_type_list
*fs_next
;
95 /* Linked list of filesystem types to display.
96 If `fs_select_list' is NULL, list all types.
97 This table is generated dynamically from command-line options,
98 rather than hardcoding into the program what it thinks are the
99 valid filesystem types; let the user specify any filesystem type
100 they want to, and if there are any filesystems of that type, they
103 Some filesystem types:
104 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
106 static struct fs_type_list
*fs_select_list
;
108 /* Linked list of filesystem types to omit.
109 If the list is empty, don't exclude any types. */
111 static struct fs_type_list
*fs_exclude_list
;
113 /* Linked list of mounted filesystems. */
114 static struct mount_entry
*mount_list
;
116 /* If nonzero, print filesystem type as well. */
117 static int print_type
;
119 /* For long options that have no equivalent short option, use a
120 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
123 SYNC_OPTION
= CHAR_MAX
+ 1,
128 static struct option
const long_options
[] =
130 {"all", no_argument
, NULL
, 'a'},
131 {"block-size", required_argument
, NULL
, BLOCK_SIZE_OPTION
},
132 {"inodes", no_argument
, NULL
, 'i'},
133 {"human-readable", no_argument
, NULL
, 'h'},
134 {"si", no_argument
, NULL
, 'H'},
135 {"kilobytes", no_argument
, NULL
, 'k'},
136 {"local", no_argument
, NULL
, 'l'},
137 {"megabytes", no_argument
, NULL
, 'm'},
138 {"portability", no_argument
, NULL
, 'P'},
139 {"print-type", no_argument
, NULL
, 'T'},
140 {"sync", no_argument
, NULL
, SYNC_OPTION
},
141 {"no-sync", no_argument
, NULL
, NO_SYNC_OPTION
},
142 {"type", required_argument
, NULL
, 't'},
143 {"exclude-type", required_argument
, NULL
, 'x'},
144 {GETOPT_HELP_OPTION_DECL
},
145 {GETOPT_VERSION_OPTION_DECL
},
152 printf (_("Filesystem "));
160 printf (_(" Inodes IUsed IFree IUse%%"));
161 else if (output_block_size
< 0)
162 printf (_(" Size Used Avail Use%%"));
163 else if (posix_format
)
164 printf (_(" %4d-blocks Used Available Capacity"), output_block_size
);
167 char buf
[LONGEST_HUMAN_READABLE
+ 1];
168 char *p
= human_readable (output_block_size
, buf
, 1, -1024);
170 /* Replace e.g. "1.0k" by "1k". */
171 size_t plen
= strlen (p
);
172 if (3 <= plen
&& strncmp (p
+ plen
- 3, ".0", 2) == 0)
173 strcpy (p
+ plen
- 3, p
+ plen
- 1);
175 printf (_(" %4s-blocks Used Available Use%%"), p
);
178 printf (_(" Mounted on\n"));
181 /* If FSTYPE is a type of filesystem that should be listed,
182 return nonzero, else zero. */
185 selected_fstype (const char *fstype
)
187 const struct fs_type_list
*fsp
;
189 if (fs_select_list
== NULL
|| fstype
== NULL
)
191 for (fsp
= fs_select_list
; fsp
; fsp
= fsp
->fs_next
)
192 if (STREQ (fstype
, fsp
->fs_name
))
197 /* If FSTYPE is a type of filesystem that should be omitted,
198 return nonzero, else zero. */
201 excluded_fstype (const char *fstype
)
203 const struct fs_type_list
*fsp
;
205 if (fs_exclude_list
== NULL
|| fstype
== NULL
)
207 for (fsp
= fs_exclude_list
; fsp
; fsp
= fsp
->fs_next
)
208 if (STREQ (fstype
, fsp
->fs_name
))
213 /* Like human_readable_inexact, except return "-" if the argument is -1,
214 and if NEGATIVE is 1 then N represents a negative number, expressed
215 in two's complement. */
217 df_readable (int negative
, uintmax_t n
, char *buf
,
218 int from_block_size
, int t_output_block_size
,
219 enum human_inexact_style s
)
225 char *p
= human_readable_inexact (negative
? - n
: n
,
226 buf
+ negative
, from_block_size
,
235 /* Display a space listing for the disk device with absolute path DISK.
236 If MOUNT_POINT is non-NULL, it is the path of the root of the
238 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
239 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
240 not be able to produce statistics in this case.
241 ME_DUMMY and ME_REMOTE are the mount entry flags. */
244 show_dev (const char *disk
, const char *mount_point
, const char *fstype
,
245 int me_dummy
, int me_remote
)
248 const char *stat_file
;
249 char buf
[3][LONGEST_HUMAN_READABLE
+ 2];
256 int negate_available
;
257 uintmax_t available_to_root
;
262 if (me_remote
&& show_local_fs
)
265 if (me_dummy
&& show_all_fs
== 0 && !show_listed_fs
)
268 if (!selected_fstype (fstype
) || excluded_fstype (fstype
))
271 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
272 program reports on the filesystem that the special file is on.
273 It would be better to report on the unmounted filesystem,
274 but statfs doesn't do that on most systems. */
275 stat_file
= mount_point
? mount_point
: disk
;
277 if (get_fs_usage (stat_file
, disk
, &fsu
))
279 error (0, errno
, "%s", quote (stat_file
));
284 if (fsu
.fsu_blocks
== 0 && !show_all_fs
&& !show_listed_fs
)
288 disk
= "-"; /* unknown */
290 fstype
= "-"; /* unknown */
292 /* df.c reserved 5 positions for fstype,
293 but that does not suffice for type iso9660 */
296 int disk_name_len
= (int) strlen (disk
);
297 int fstype_len
= (int) strlen (fstype
);
298 if (disk_name_len
+ fstype_len
+ 2 < 20)
299 printf ("%s%*s ", disk
, 18 - disk_name_len
, fstype
);
300 else if (!posix_format
)
301 printf ("%s\n%18s ", disk
, fstype
);
303 printf ("%s %s", disk
, fstype
);
307 if ((int) strlen (disk
) > 20 && !posix_format
)
308 printf ("%s\n%20s", disk
, "");
310 printf ("%-20s", disk
);
318 output_units
= output_block_size
< 0 ? output_block_size
: 1;
319 total
= fsu
.fsu_files
;
320 available
= fsu
.fsu_ffree
;
321 negate_available
= 0;
322 available_to_root
= available
;
326 width
= output_block_size
< 0 ? 5 : 9;
327 use_width
= posix_format
? 8 : 4;
328 input_units
= fsu
.fsu_blocksize
;
329 output_units
= output_block_size
;
330 total
= fsu
.fsu_blocks
;
331 available
= fsu
.fsu_bavail
;
332 negate_available
= fsu
.fsu_bavail_top_bit_set
;
333 available_to_root
= fsu
.fsu_bfree
;
338 if (total
!= -1 && available_to_root
!= -1)
340 used
= total
- available_to_root
;
341 if (total
< available_to_root
)
348 printf (" %*s %*s %*s ",
349 width
, df_readable (0, total
,
350 buf
[0], input_units
, output_units
,
353 : human_round_to_even
)),
354 width
, df_readable (negate_used
, used
,
355 buf
[1], input_units
, output_units
,
357 width
, df_readable (negate_available
, available
,
358 buf
[2], input_units
, output_units
,
359 posix_format
? human_ceiling
: human_floor
));
361 if (used
== -1 || available
== -1)
363 else if (!negate_used
364 && used
<= TYPE_MAXIMUM (uintmax_t) / 100
365 && used
+ available
!= 0
366 && (used
+ available
< used
) == negate_available
)
368 uintmax_t u100
= used
* 100;
369 uintmax_t nonroot_total
= used
+ available
;
370 pct
= u100
/ nonroot_total
+ (u100
% nonroot_total
!= 0);
374 /* The calculation cannot be done easily with integer
375 arithmetic. Fall back on floating point. This can suffer
376 from minor rounding errors, but doing it exactly requires
377 multiple precision arithmetic, and it's not worth the
379 double u
= negate_used
? - (double) - used
: used
;
380 double a
= negate_available
? - (double) - available
: available
;
381 double nonroot_total
= u
+ a
;
385 pct
= u
* 100 / nonroot_total
;
388 /* Like `pct = ceil (dpct);', but avoid ceil so that
389 the math library needn't be linked. */
390 if (ipct
- 1 < pct
&& pct
<= ipct
+ 1)
391 pct
= ipct
+ (ipct
< pct
);
396 printf ("%*.0f%%", use_width
- 1, pct
);
398 printf ("%*s", use_width
, "- ");
402 #ifdef HIDE_AUTOMOUNT_PREFIX
403 /* Don't print the first directory name in MOUNT_POINT if it's an
404 artifact of an automounter. This is a bit too aggressive to be
406 if (strncmp ("/auto/", mount_point
, 6) == 0)
408 else if (strncmp ("/tmp_mnt/", mount_point
, 9) == 0)
411 printf (" %s", mount_point
);
416 /* Identify the directory, if any, that device
417 DISK is mounted on, and show its disk usage. */
420 show_disk (const char *disk
)
422 struct mount_entry
*me
;
424 for (me
= mount_list
; me
; me
= me
->me_next
)
425 if (STREQ (disk
, me
->me_devname
))
427 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
428 me
->me_dummy
, me
->me_remote
);
431 /* No filesystem is mounted on DISK. */
432 show_dev (disk
, (char *) NULL
, (char *) NULL
, 0, 0);
435 /* Return the root mountpoint of the filesystem on which FILE exists, in
436 malloced storage. FILE_STAT should be the result of stating FILE. */
438 find_mount_point (const char *file
, const struct stat
*file_stat
)
440 struct saved_cwd cwd
;
441 struct stat last_stat
;
442 char *mp
= 0; /* The malloced mount point path. */
447 if (S_ISDIR (file_stat
->st_mode
))
448 /* FILE is a directory, so just chdir there directly. */
450 last_stat
= *file_stat
;
451 if (chdir (file
) < 0)
455 /* FILE is some other kind of file, we need to use its directory. */
458 char *tmp
= xstrdup (file
);
461 strip_trailing_slashes (tmp
);
462 dir
= dir_name (tmp
);
470 if (stat (".", &last_stat
) < 0)
474 /* Now walk up FILE's parents until we find another filesystem or /,
475 chdiring as we go. LAST_STAT holds stat information for the last place
480 if (stat ("..", &st
) < 0)
482 if (st
.st_dev
!= last_stat
.st_dev
|| st
.st_ino
== last_stat
.st_ino
)
483 /* cwd is the mount point. */
485 if (chdir ("..") < 0)
490 /* Finally reached a mount point, see what it's called. */
494 /* Restore the original cwd. */
496 int save_errno
= errno
;
497 if (restore_cwd (&cwd
, 0, mp
))
498 exit (1); /* We're scrod. */
506 /* Figure out which device file or directory POINT is mounted on
507 and show its disk usage.
508 STATP is the results of `stat' on POINT. */
510 show_point (const char *point
, const struct stat
*statp
)
512 struct stat disk_stats
;
513 struct mount_entry
*me
;
514 struct mount_entry
*matching_dummy
= NULL
;
515 char *needs_freeing
= NULL
;
517 /* If POINT is an absolute path name, see if we can find the
518 mount point without performing any extra stat calls at all. */
521 for (me
= mount_list
; me
; me
= me
->me_next
)
523 if (STREQ (me
->me_mountdir
, point
) && !STREQ (me
->me_type
, "lofs"))
525 /* Prefer non-dummy entries. */
533 goto show_matching_dummy
;
536 #if HAVE_REALPATH || HAVE_RESOLVEPATH
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_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
= alloca (resolved_size
);
586 resolved_len
= resolvepath (abspoint
, resolved
, resolved_size
);
588 while (resolved_len
== resolved_size
);
591 resolved
= alloca (PATH_MAX
+ 1);
592 resolved
= (char *) realpath (abspoint
, resolved
);
593 resolved_len
= resolved
? strlen (resolved
) : -1;
596 if (1 <= resolved_len
&& resolved
[0] == '/')
598 size_t best_match_len
= 0;
600 for (me
= mount_list
; me
; me
= me
->me_next
)
603 size_t len
= strlen (me
->me_mountdir
);
604 if (best_match_len
< len
&& len
<= resolved_len
605 && (len
== 1 /* root file system */
606 || ((len
== resolved_len
|| resolved
[len
] == '/')
607 && strncmp (me
->me_mountdir
, resolved
, len
) == 0)))
610 best_match_len
= len
;
615 if (best_match
&& !STREQ (best_match
->me_type
, "lofs")
616 && stat (best_match
->me_mountdir
, &disk_stats
) == 0
617 && disk_stats
.st_dev
== statp
->st_dev
)
625 for (me
= mount_list
; me
; me
= me
->me_next
)
627 if (me
->me_dev
== (dev_t
) -1)
629 if (stat (me
->me_mountdir
, &disk_stats
) == 0)
630 me
->me_dev
= disk_stats
.st_dev
;
633 error (0, errno
, "%s", quote (me
->me_mountdir
));
635 /* So we won't try and fail repeatedly. */
636 me
->me_dev
= (dev_t
) -2;
640 if (statp
->st_dev
== me
->me_dev
)
642 /* Skip bogus mtab entries. */
643 if (stat (me
->me_mountdir
, &disk_stats
) != 0
644 || disk_stats
.st_dev
!= me
->me_dev
)
646 me
->me_dev
= (dev_t
) -2;
650 /* Prefer non-dummy entries. */
658 goto show_matching_dummy
;
660 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
661 print as much info as we can; methods that require the device to be
662 present will fail at a later point. */
664 /* Find the actual mount point. */
665 char *mp
= find_mount_point (point
, statp
);
668 show_dev (0, mp
, 0, 0, 0);
672 error (0, errno
, "%s", quote (point
));
675 goto free_then_return
;
680 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
, me
->me_dummy
,
684 free (needs_freeing
);
687 /* Determine what kind of node PATH is and show the disk usage
688 for it. STATP is the results of `stat' on PATH. */
691 show_entry (const char *path
, const struct stat
*statp
)
693 if (S_ISBLK (statp
->st_mode
) || S_ISCHR (statp
->st_mode
))
696 show_point (path
, statp
);
699 /* Show all mounted filesystems, except perhaps those that are of
700 an unselected type or are empty. */
703 show_all_entries (void)
705 struct mount_entry
*me
;
707 for (me
= mount_list
; me
; me
= me
->me_next
)
708 show_dev (me
->me_devname
, me
->me_mountdir
, me
->me_type
,
709 me
->me_dummy
, me
->me_remote
);
712 /* Add FSTYPE to the list of filesystem types to display. */
715 add_fs_type (const char *fstype
)
717 struct fs_type_list
*fsp
;
719 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
720 fsp
->fs_name
= (char *) fstype
;
721 fsp
->fs_next
= fs_select_list
;
722 fs_select_list
= fsp
;
725 /* Add FSTYPE to the list of filesystem types to be omitted. */
728 add_excluded_fs_type (const char *fstype
)
730 struct fs_type_list
*fsp
;
732 fsp
= (struct fs_type_list
*) xmalloc (sizeof (struct fs_type_list
));
733 fsp
->fs_name
= (char *) fstype
;
734 fsp
->fs_next
= fs_exclude_list
;
735 fs_exclude_list
= fsp
;
742 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
746 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name
);
748 Show information about the filesystem on which each FILE resides,\n\
749 or all filesystems by default.\n\
751 -a, --all include filesystems having 0 blocks\n\
752 --block-size=SIZE use SIZE-byte blocks\n\
753 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
754 -H, --si likewise, but use powers of 1000 not 1024\n\
755 -i, --inodes list inode information instead of block usage\n\
756 -k, --kilobytes like --block-size=1024\n\
757 -l, --local limit listing to local filesystems\n\
758 -m, --megabytes like --block-size=1048576\n\
759 --no-sync do not invoke sync before getting usage info (default)\n\
760 -P, --portability use the POSIX output format\n\
761 --sync invoke sync before getting usage info\n\
762 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
763 -T, --print-type print filesystem type\n\
764 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
766 --help display this help and exit\n\
767 --version output version information and exit\n\
769 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
775 main (int argc
, char **argv
)
778 struct stat
*stats
IF_LINT (= 0);
779 int n_valid_args
= 0;
781 program_name
= argv
[0];
782 setlocale (LC_ALL
, "");
783 bindtextdomain (PACKAGE
, LOCALEDIR
);
784 textdomain (PACKAGE
);
786 atexit (close_stdout
);
788 fs_select_list
= NULL
;
789 fs_exclude_list
= NULL
;
794 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size
);
800 while ((c
= getopt_long (argc
, argv
, "aiF:hHklmPTt:vx:", long_options
, NULL
))
805 case 0: /* Long option. */
814 output_block_size
= -1024;
817 output_block_size
= -1000;
820 output_block_size
= 1024;
826 output_block_size
= 1024 * 1024;
841 case BLOCK_SIZE_OPTION
:
842 human_block_size (optarg
, 1, &output_block_size
);
846 /* Accept -F as a synonym for -t for compatibility with Solaris. */
848 add_fs_type (optarg
);
851 case 'v': /* For SysV compatibility. */
855 add_excluded_fs_type (optarg
);
858 case_GETOPT_HELP_CHAR
;
859 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
866 /* Fail if the same file system type was both selected and excluded. */
869 struct fs_type_list
*fs_incl
;
870 for (fs_incl
= fs_select_list
; fs_incl
; fs_incl
= fs_incl
->fs_next
)
872 struct fs_type_list
*fs_excl
;
873 for (fs_excl
= fs_exclude_list
; fs_excl
; fs_excl
= fs_excl
->fs_next
)
875 if (STREQ (fs_incl
->fs_name
, fs_excl
->fs_name
))
878 _("file system type %s both selected and excluded"),
879 quote (fs_incl
->fs_name
));
892 /* stat all the given entries to make sure they get automounted,
893 if necessary, before reading the filesystem table. */
894 stats
= (struct stat
*)
895 xmalloc ((argc
- optind
) * sizeof (struct stat
));
896 for (i
= optind
; i
< argc
; ++i
)
898 if (stat (argv
[i
], &stats
[i
- optind
]))
900 error (0, errno
, "%s", quote (argv
[i
]));
912 read_filesystem_list ((fs_select_list
!= NULL
913 || fs_exclude_list
!= NULL
917 if (mount_list
== NULL
)
919 /* Couldn't read the table of mounted filesystems.
920 Fail if df was invoked with no file name arguments;
921 Otherwise, merely give a warning and proceed. */
922 const char *warning
= (optind
== argc
? "" : _("Warning: "));
923 int status
= (optind
== argc
? 1 : 0);
924 error (status
, errno
,
925 _("%scannot read table of mounted filesystems"), warning
);
940 /* Display explicitly requested empty filesystems. */
943 if (n_valid_args
> 0)
946 for (i
= optind
; i
< argc
; ++i
)
948 show_entry (argv
[i
], &stats
[i
- optind
]);