*** empty log message ***
[coreutils.git] / src / df.c
bloba0ef77921ac2a79287d1466c4799e462f47092e4
1 /* df - summarize free disk space
2 Copyright (C) 91, 1995-1999 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)
7 any later version.
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. */
22 #include <config.h>
23 #if HAVE_INTTYPES_H
24 # include <inttypes.h>
25 #endif
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <getopt.h>
29 #include <assert.h>
31 #include "system.h"
32 #include "dirname.h"
33 #include "error.h"
34 #include "fsusage.h"
35 #include "human.h"
36 #include "mountlist.h"
37 #include "save-cwd.h"
39 /* The official name of this program (e.g., no `g' prefix). */
40 #define PROGRAM_NAME "df"
42 #define AUTHORS \
43 "Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
45 void strip_trailing_slashes ();
46 char *xstrdup ();
47 char *xgetcwd ();
49 /* Name this program was run with. */
50 char *program_name;
52 /* If nonzero, show inode information. */
53 static int inode_format;
55 /* If nonzero, show even filesystems with zero size or
56 uninteresting types. */
57 static int show_all_fs;
59 /* If nonzero, show only local filesystems. */
60 static int show_local_fs;
62 /* If nonzero, output data for each filesystem corresponding to a
63 command line argument -- even if it's a dummy (automounter) entry. */
64 static int show_listed_fs;
66 /* If positive, the units to use when printing sizes;
67 if negative, the human-readable base. */
68 static int output_block_size;
70 /* If nonzero, use the POSIX output format. */
71 static int posix_format;
73 /* If nonzero, invoke the `sync' system call before getting any usage data.
74 Using this option can make df very slow, especially with many or very
75 busy disks. Note that this may make a difference on some systems --
76 SunOs4.1.3, for one. It is *not* necessary on Linux. */
77 static int require_sync = 0;
79 /* Nonzero if errors have occurred. */
80 static int exit_status;
82 /* A filesystem type to display. */
84 struct fs_type_list
86 char *fs_name;
87 struct fs_type_list *fs_next;
90 /* Linked list of filesystem types to display.
91 If `fs_select_list' is NULL, list all types.
92 This table is generated dynamically from command-line options,
93 rather than hardcoding into the program what it thinks are the
94 valid filesystem types; let the user specify any filesystem type
95 they want to, and if there are any filesystems of that type, they
96 will be shown.
98 Some filesystem types:
99 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
101 static struct fs_type_list *fs_select_list;
103 /* Linked list of filesystem types to omit.
104 If the list is empty, don't exclude any types. */
106 static struct fs_type_list *fs_exclude_list;
108 /* Linked list of mounted filesystems. */
109 static struct mount_entry *mount_list;
111 /* If nonzero, print filesystem type as well. */
112 static int print_type;
114 static struct option const long_options[] =
116 {"all", no_argument, NULL, 'a'},
117 {"block-size", required_argument, NULL, CHAR_MAX + 3},
118 {"inodes", no_argument, NULL, 'i'},
119 {"human-readable", no_argument, NULL, 'h'},
120 {"si", no_argument, NULL, 'H'},
121 {"kilobytes", no_argument, NULL, 'k'},
122 {"local", no_argument, NULL, 'l'},
123 {"megabytes", no_argument, NULL, 'm'},
124 {"portability", no_argument, NULL, 'P'},
125 {"print-type", no_argument, NULL, 'T'},
126 {"sync", no_argument, NULL, CHAR_MAX + 1},
127 {"no-sync", no_argument, NULL, CHAR_MAX + 2},
128 {"type", required_argument, NULL, 't'},
129 {"exclude-type", required_argument, NULL, 'x'},
130 {GETOPT_HELP_OPTION_DECL},
131 {GETOPT_VERSION_OPTION_DECL},
132 {NULL, 0, NULL, 0}
135 static void
136 print_header (void)
138 printf ("Filesystem ");
140 if (print_type)
141 printf (" Type");
142 else
143 printf (" ");
145 if (inode_format)
146 printf (" Inodes IUsed IFree IUse%%");
147 else if (output_block_size < 0)
148 printf (" Size Used Avail Use%%");
149 else if (posix_format)
150 printf (" %4d-blocks Used Available Capacity", output_block_size);
151 else
153 char buf[LONGEST_HUMAN_READABLE + 1];
154 char *p = human_readable (output_block_size, buf, 1, -1024);
156 /* Replace e.g. "1.0k" by "1k". */
157 size_t plen = strlen (p);
158 if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
159 strcpy (p + plen - 3, p + plen - 1);
161 printf (" %4s-blocks Used Available Use%%", p);
164 printf (" Mounted on\n");
167 /* If FSTYPE is a type of filesystem that should be listed,
168 return nonzero, else zero. */
170 static int
171 selected_fstype (const char *fstype)
173 const struct fs_type_list *fsp;
175 if (fs_select_list == NULL || fstype == NULL)
176 return 1;
177 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
178 if (STREQ (fstype, fsp->fs_name))
179 return 1;
180 return 0;
183 /* If FSTYPE is a type of filesystem that should be omitted,
184 return nonzero, else zero. */
186 static int
187 excluded_fstype (const char *fstype)
189 const struct fs_type_list *fsp;
191 if (fs_exclude_list == NULL || fstype == NULL)
192 return 0;
193 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
194 if (STREQ (fstype, fsp->fs_name))
195 return 1;
196 return 0;
199 /* Like human_readable, except return "-" if the argument is -1,
200 and take ceiling of fractions if posix_format. */
201 static char *
202 df_readable (uintmax_t n, char *buf,
203 int from_block_size, int t_output_block_size)
205 return (n == -1 ? "-"
206 : human_readable_inexact (n, buf, from_block_size,
207 t_output_block_size,
208 (posix_format
209 ? human_ceiling
210 : human_round_to_even)));
213 /* Return the ceiling of N * 100 / D. Avoid the ceil function, so that
214 we needn't link the math library. */
215 static int
216 ceil_percent (uintmax_t n, uintmax_t d)
218 if (n <= (uintmax_t) -1 / 100)
220 uintmax_t n100 = n * 100;
221 return n100 / d + (n100 % d != 0);
223 else
225 /* Avoid integer overflow. We should use multiple precision
226 arithmetic here, but we'll be lazy and resort to floating
227 point. This can yield answers that are slightly off. In
228 practice it is quite rare to overflow uintmax_t, so this is
229 good enough for now. */
230 double pct = n * 100.0 / d;
231 int r = pct;
232 return r + (r != pct);
236 /* Display a space listing for the disk device with absolute path DISK.
237 If MOUNT_POINT is non-NULL, it is the path of the root of the
238 filesystem on DISK.
239 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
240 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
241 not be able to produce statistics in this case.
242 ME_DUMMY and ME_REMOTE are the mount entry flags. */
244 static void
245 show_dev (const char *disk, const char *mount_point, const char *fstype,
246 int me_dummy, int me_remote)
248 struct fs_usage fsu;
249 const char *stat_file;
251 if (me_remote && show_local_fs)
252 return;
254 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
255 return;
257 if (!selected_fstype (fstype) || excluded_fstype (fstype))
258 return;
260 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
261 program reports on the filesystem that the special file is on.
262 It would be better to report on the unmounted filesystem,
263 but statfs doesn't do that on most systems. */
264 stat_file = mount_point ? mount_point : disk;
266 if (get_fs_usage (stat_file, disk, &fsu))
268 error (0, errno, "%s", stat_file);
269 exit_status = 1;
270 return;
273 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
274 return;
276 if (! disk)
277 disk = "-"; /* unknown */
278 if (! fstype)
279 fstype = "-"; /* unknown */
281 /* df.c reserved 5 positions for fstype,
282 but that does not suffice for type iso9660 */
283 if (print_type)
285 int disk_name_len = (int) strlen (disk);
286 int fstype_len = (int) strlen (fstype);
287 if (disk_name_len + fstype_len + 2 < 20)
288 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
289 else if (!posix_format)
290 printf ("%s\n%18s ", disk, fstype);
291 else
292 printf ("%s %s", disk, fstype);
294 else
296 if ((int) strlen (disk) > 20 && !posix_format)
297 printf ("%s\n%20s", disk, "");
298 else
299 printf ("%-20s", disk);
302 if (inode_format)
304 char buf[3][LONGEST_HUMAN_READABLE + 1];
305 double inodes_percent_used;
306 uintmax_t inodes_used;
307 int inode_units = output_block_size < 0 ? output_block_size : 1;
309 if (fsu.fsu_files == -1 || fsu.fsu_files < fsu.fsu_ffree)
311 inodes_used = -1;
312 inodes_percent_used = -1;
314 else
316 inodes_used = fsu.fsu_files - fsu.fsu_ffree;
317 inodes_percent_used =
318 (fsu.fsu_files == 0 ? 0
319 : inodes_used * 100.0 / fsu.fsu_files);
322 printf (" %7s %7s %7s ",
323 df_readable (fsu.fsu_files, buf[0], 1, inode_units),
324 df_readable (inodes_used, buf[1], 1, inode_units),
325 df_readable (fsu.fsu_ffree, buf[2], 1, inode_units));
327 if (inodes_percent_used < 0)
328 printf (" - ");
329 else
330 printf ("%4.0f%%", inodes_percent_used);
332 else
334 int w = output_block_size < 0 ? 5 : 9;
335 char buf[2][LONGEST_HUMAN_READABLE + 1];
336 char availbuf[LONGEST_HUMAN_READABLE + 2];
337 char *avail;
338 double blocks_percent_used;
339 uintmax_t blocks_used;
341 if (fsu.fsu_blocks == -1 || fsu.fsu_blocks < fsu.fsu_bfree)
343 blocks_used = -1;
344 blocks_percent_used = -1;
346 else
348 uintmax_t blocks_avail;
350 blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
351 blocks_avail = blocks_used + fsu.fsu_bavail;
352 blocks_percent_used =
353 ((fsu.fsu_bavail == -1
354 || blocks_avail == 0
355 || (fsu.fsu_bavail_top_bit_set
356 ? blocks_used < - fsu.fsu_bavail
357 : fsu.fsu_bfree < fsu.fsu_bavail))
358 ? -1
359 : posix_format
360 ? ceil_percent (blocks_used, blocks_avail)
361 : blocks_used * 100.0 / blocks_avail);
364 avail = df_readable ((fsu.fsu_bavail_top_bit_set
365 ? - fsu.fsu_bavail
366 : fsu.fsu_bavail),
367 availbuf + 1, fsu.fsu_blocksize,
368 output_block_size);
370 if (fsu.fsu_bavail_top_bit_set)
371 *--avail = '-';
373 printf (" %*s %*s %*s ",
374 w, df_readable (fsu.fsu_blocks, buf[0], fsu.fsu_blocksize,
375 output_block_size),
376 w, df_readable (blocks_used, buf[1], fsu.fsu_blocksize,
377 output_block_size),
378 w, avail);
381 int use_width = posix_format ? 8 : 4;
383 if (blocks_percent_used < 0)
384 printf ("%*s", use_width, "- ");
385 else
386 printf ("%*.0f%%", use_width - 1, blocks_percent_used);
390 if (mount_point)
392 #ifdef HIDE_AUTOMOUNT_PREFIX
393 /* Don't print the first directory name in MOUNT_POINT if it's an
394 artifact of an automounter. This is a bit too aggressive to be
395 the default. */
396 if (strncmp ("/auto/", mount_point, 6) == 0)
397 mount_point += 5;
398 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
399 mount_point += 8;
400 #endif
401 printf (" %s", mount_point);
403 putchar ('\n');
406 /* Identify the directory, if any, that device
407 DISK is mounted on, and show its disk usage. */
409 static void
410 show_disk (const char *disk)
412 struct mount_entry *me;
414 for (me = mount_list; me; me = me->me_next)
415 if (STREQ (disk, me->me_devname))
417 show_dev (me->me_devname, me->me_mountdir, me->me_type,
418 me->me_dummy, me->me_remote);
419 return;
421 /* No filesystem is mounted on DISK. */
422 show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
425 /* Return the root mountpoint of the filesystem on which FILE exists, in
426 malloced storage. FILE_STAT should be the result of stating FILE. */
427 static char *
428 find_mount_point (const char *file, const struct stat *file_stat)
430 struct saved_cwd cwd;
431 struct stat last_stat;
432 char *mp = 0; /* The malloced mount point path. */
434 if (save_cwd (&cwd))
435 return NULL;
437 if (S_ISDIR (file_stat->st_mode))
438 /* FILE is a directory, so just chdir there directly. */
440 last_stat = *file_stat;
441 if (chdir (file) < 0)
442 return NULL;
444 else
445 /* FILE is some other kind of file, we need to use its directory. */
447 int rv;
448 char *tmp = xstrdup (file);
449 char *dir;
451 strip_trailing_slashes (tmp);
452 dir = dir_name (tmp);
453 free (tmp);
454 rv = chdir (dir);
455 free (dir);
457 if (rv < 0)
458 return NULL;
460 if (stat (".", &last_stat) < 0)
461 goto done;
464 /* Now walk up FILE's parents until we find another filesystem or /,
465 chdiring as we go. LAST_STAT holds stat information for the last place
466 we visited. */
467 for (;;)
469 struct stat st;
470 if (stat ("..", &st) < 0)
471 goto done;
472 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
473 /* cwd is the mount point. */
474 break;
475 if (chdir ("..") < 0)
476 goto done;
477 last_stat = st;
480 /* Finally reached a mount point, see what it's called. */
481 mp = xgetcwd ();
483 done:
484 /* Restore the original cwd. */
486 int save_errno = errno;
487 if (restore_cwd (&cwd, 0, mp))
488 exit (1); /* We're scrod. */
489 free_cwd (&cwd);
490 errno = save_errno;
493 return mp;
496 /* Figure out which device file or directory POINT is mounted on
497 and show its disk usage.
498 STATP is the results of `stat' on POINT. */
499 static void
500 show_point (const char *point, const struct stat *statp)
502 struct stat disk_stats;
503 struct mount_entry *me;
504 struct mount_entry *matching_dummy = NULL;
506 for (me = mount_list; me; me = me->me_next)
508 if (me->me_dev == (dev_t) -1)
510 if (stat (me->me_mountdir, &disk_stats) == 0)
511 me->me_dev = disk_stats.st_dev;
512 else
514 error (0, errno, "%s", me->me_mountdir);
515 exit_status = 1;
516 /* So we won't try and fail repeatedly. */
517 me->me_dev = (dev_t) -2;
521 if (statp->st_dev == me->me_dev)
523 /* Skip bogus mtab entries. */
524 if (stat (me->me_mountdir, &disk_stats) != 0 ||
525 disk_stats.st_dev != me->me_dev)
526 continue;
528 /* Prefer non-dummy entries. */
529 if (me->me_dummy)
531 matching_dummy = me;
532 continue;
535 show_dev (me->me_devname, me->me_mountdir, me->me_type,
536 me->me_dummy, me->me_remote);
537 return;
541 if (matching_dummy)
543 show_dev (matching_dummy->me_devname, matching_dummy->me_mountdir,
544 matching_dummy->me_type, 1, matching_dummy->me_remote);
545 return;
548 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
549 print as much info as we can; methods that require the device to be
550 present will fail at a later point. */
552 /* Find the actual mount point. */
553 char *mp = find_mount_point (point, statp);
554 if (mp)
556 show_dev (0, mp, 0, 0, 0);
557 free (mp);
559 else
560 error (0, errno, "%s", point);
564 /* Determine what kind of node PATH is and show the disk usage
565 for it. STATP is the results of `stat' on PATH. */
567 static void
568 show_entry (const char *path, const struct stat *statp)
570 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
571 show_disk (path);
572 else
573 show_point (path, statp);
576 /* Show all mounted filesystems, except perhaps those that are of
577 an unselected type or are empty. */
579 static void
580 show_all_entries (void)
582 struct mount_entry *me;
584 for (me = mount_list; me; me = me->me_next)
585 show_dev (me->me_devname, me->me_mountdir, me->me_type,
586 me->me_dummy, me->me_remote);
589 /* Add FSTYPE to the list of filesystem types to display. */
591 static void
592 add_fs_type (const char *fstype)
594 struct fs_type_list *fsp;
596 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
597 fsp->fs_name = (char *) fstype;
598 fsp->fs_next = fs_select_list;
599 fs_select_list = fsp;
602 /* Add FSTYPE to the list of filesystem types to be omitted. */
604 static void
605 add_excluded_fs_type (const char *fstype)
607 struct fs_type_list *fsp;
609 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
610 fsp->fs_name = (char *) fstype;
611 fsp->fs_next = fs_exclude_list;
612 fs_exclude_list = fsp;
615 void
616 usage (int status)
618 if (status != 0)
619 fprintf (stderr, _("Try `%s --help' for more information.\n"),
620 program_name);
621 else
623 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
624 printf (_("\
625 Show information about the filesystem on which each FILE resides,\n\
626 or all filesystems by default.\n\
628 -a, --all include filesystems having 0 blocks\n\
629 --block-size=SIZE use SIZE-byte blocks\n\
630 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
631 -H, --si likewise, but use powers of 1000 not 1024\n\
632 -i, --inodes list inode information instead of block usage\n\
633 -k, --kilobytes like --block-size=1024\n\
634 -l, --local limit listing to local filesystems\n\
635 -m, --megabytes like --block-size=1048576\n\
636 --no-sync do not invoke sync before getting usage info (default)\n\
637 -P, --portability use the POSIX output format\n\
638 --sync invoke sync before getting usage info\n\
639 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
640 -T, --print-type print filesystem type\n\
641 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
642 -v (ignored)\n\
643 --help display this help and exit\n\
644 --version output version information and exit\n\
645 "));
646 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
647 close_stdout ();
649 exit (status);
653 main (int argc, char **argv)
655 int c;
656 struct stat *stats;
658 program_name = argv[0];
659 setlocale (LC_ALL, "");
660 bindtextdomain (PACKAGE, LOCALEDIR);
661 textdomain (PACKAGE);
663 fs_select_list = NULL;
664 fs_exclude_list = NULL;
665 inode_format = 0;
666 show_all_fs = 0;
667 show_listed_fs = 0;
669 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
671 print_type = 0;
672 posix_format = 0;
673 exit_status = 0;
675 while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
676 != -1)
678 switch (c)
680 case 0: /* Long option. */
681 break;
682 case 'a':
683 show_all_fs = 1;
684 break;
685 case 'i':
686 inode_format = 1;
687 break;
688 case 'h':
689 output_block_size = -1024;
690 break;
691 case 'H':
692 output_block_size = -1000;
693 break;
694 case 'k':
695 output_block_size = 1024;
696 break;
697 case 'l':
698 show_local_fs = 1;
699 break;
700 case 'm':
701 output_block_size = 1024 * 1024;
702 break;
703 case 'T':
704 print_type = 1;
705 break;
706 case 'P':
707 posix_format = 1;
708 break;
709 case CHAR_MAX + 1:
710 require_sync = 1;
711 break;
712 case CHAR_MAX + 2:
713 require_sync = 0;
714 break;
716 case CHAR_MAX + 3:
717 human_block_size (optarg, 1, &output_block_size);
718 break;
720 case 'F':
721 /* Accept -F as a synonym for -t for compatibility with Solaris. */
722 case 't':
723 add_fs_type (optarg);
724 break;
726 case 'v': /* For SysV compatibility. */
727 /* ignore */
728 break;
729 case 'x':
730 add_excluded_fs_type (optarg);
731 break;
733 case_GETOPT_HELP_CHAR;
734 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
736 default:
737 usage (1);
741 /* Fail if the same file system type was both selected and excluded. */
743 int match = 0;
744 struct fs_type_list *i;
745 for (i = fs_select_list; i; i = i->fs_next)
747 struct fs_type_list *j;
748 for (j = fs_exclude_list; j; j = j->fs_next)
750 if (STREQ (i->fs_name, j->fs_name))
752 error (0, 0,
753 _("file system type `%s' both selected and excluded"),
754 i->fs_name);
755 match = 1;
756 break;
760 if (match)
761 exit (1);
764 if (optind == argc)
766 #ifdef lint
767 /* Suppress `used before initialized' warning. */
768 stats = NULL;
769 #endif
771 else
773 int i;
775 /* stat all the given entries to make sure they get automounted,
776 if necessary, before reading the filesystem table. */
777 stats = (struct stat *)
778 xmalloc ((argc - optind) * sizeof (struct stat));
779 for (i = optind; i < argc; ++i)
780 if (stat (argv[i], &stats[i - optind]))
782 error (0, errno, "%s", argv[i]);
783 exit_status = 1;
784 argv[i] = NULL;
788 mount_list =
789 read_filesystem_list ((fs_select_list != NULL
790 || fs_exclude_list != NULL
791 || print_type
792 || show_local_fs));
794 if (mount_list == NULL)
796 /* Couldn't read the table of mounted filesystems.
797 Fail if df was invoked with no file name arguments;
798 Otherwise, merely give a warning and proceed. */
799 const char *warning = (optind == argc ? "" : _("Warning: "));
800 int status = (optind == argc ? 1 : 0);
801 error (status, errno,
802 _("%scannot read table of mounted filesystems"), warning);
805 if (require_sync)
806 sync ();
808 if (optind == argc)
810 print_header ();
811 show_all_entries ();
813 else
815 int i;
817 /* Display explicitly requested empty filesystems. */
818 show_listed_fs = 1;
820 print_header ();
821 for (i = optind; i < argc; ++i)
822 if (argv[i])
823 show_entry (argv[i], &stats[i - optind]);
826 close_stdout ();
827 exit (exit_status);