*** empty log message ***
[coreutils.git] / src / df.c
blob9f3da262e04718ca81861dd00217de6b5155b6c7
1 /* df - summarize free disk space
2 Copyright (C) 91, 95, 96, 97, 1998 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 "mountlist.h"
32 #include "fsusage.h"
33 #include "system.h"
34 #include "save-cwd.h"
35 #include "closeout.h"
36 #include "error.h"
37 #include "human.h"
38 #include "dirname.h"
40 void strip_trailing_slashes ();
41 char *xstrdup ();
42 char *xgetcwd ();
44 /* Name this program was run with. */
45 char *program_name;
47 /* If nonzero, show inode information. */
48 static int inode_format;
50 /* If nonzero, show even filesystems with zero size or
51 uninteresting types. */
52 static int show_all_fs;
54 /* If nonzero, show only local filesystems. */
55 static int show_local_fs;
57 /* If nonzero, output data for each filesystem corresponding to a
58 command line argument -- even if it's a dummy (automounter) entry. */
59 static int show_listed_fs;
61 /* If positive, the units to use when printing sizes;
62 if negative, the human-readable base. */
63 static int output_block_size;
65 /* If nonzero, use the POSIX output format. */
66 static int posix_format;
68 /* If nonzero, invoke the `sync' system call before getting any usage data.
69 Using this option can make df very slow, especially with many or very
70 busy disks. Note that this may make a difference on some systems --
71 SunOs4.1.3, for one. It is *not* necessary on Linux. */
72 static int require_sync = 0;
74 /* Nonzero if errors have occurred. */
75 static int exit_status;
77 /* A filesystem type to display. */
79 struct fs_type_list
81 char *fs_name;
82 struct fs_type_list *fs_next;
85 /* Linked list of filesystem types to display.
86 If `fs_select_list' is NULL, list all types.
87 This table is generated dynamically from command-line options,
88 rather than hardcoding into the program what it thinks are the
89 valid filesystem types; let the user specify any filesystem type
90 they want to, and if there are any filesystems of that type, they
91 will be shown.
93 Some filesystem types:
94 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
96 static struct fs_type_list *fs_select_list;
98 /* Linked list of filesystem types to omit.
99 If the list is empty, don't exclude any types. */
101 static struct fs_type_list *fs_exclude_list;
103 /* Linked list of mounted filesystems. */
104 static struct mount_entry *mount_list;
106 /* If nonzero, display usage information and exit. */
107 static int show_help;
109 /* If nonzero, print the version on standard output and exit. */
110 static int show_version;
112 /* If nonzero, print filesystem type as well. */
113 static int print_type;
115 static struct option const long_options[] =
117 {"all", no_argument, NULL, 'a'},
118 {"block-size", required_argument, NULL, CHAR_MAX + 3},
119 {"inodes", no_argument, NULL, 'i'},
120 {"human-readable", no_argument, NULL, 'h'},
121 {"si", no_argument, NULL, 'H'},
122 {"kilobytes", no_argument, NULL, 'k'},
123 {"local", no_argument, NULL, 'l'},
124 {"megabytes", no_argument, NULL, 'm'},
125 {"portability", no_argument, NULL, 'P'},
126 {"print-type", no_argument, NULL, 'T'},
127 {"sync", no_argument, NULL, CHAR_MAX + 1},
128 {"no-sync", no_argument, NULL, CHAR_MAX + 2},
129 {"type", required_argument, NULL, 't'},
130 {"exclude-type", required_argument, NULL, 'x'},
131 {"help", no_argument, &show_help, 1},
132 {"version", no_argument, &show_version, 1},
133 {NULL, 0, NULL, 0}
136 static void
137 print_header (void)
139 printf ("Filesystem ");
141 if (print_type)
142 printf (" Type");
143 else
144 printf (" ");
146 if (inode_format)
147 printf (" Inodes IUsed IFree IUse%%");
148 else if (output_block_size < 0)
149 printf (" Size Used Avail Use%%");
150 else
152 char buf[LONGEST_HUMAN_READABLE + 1];
153 char *p = human_readable (output_block_size, buf, 1, -1024);
155 /* Replace e.g. "1.0k" by "1k". */
156 size_t plen = strlen (p);
157 if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
158 strcpy (p + plen - 3, p + plen - 1);
160 printf (" %4s-blocks Used Available Use%%", p);
163 printf (" Mounted on\n");
166 /* If FSTYPE is a type of filesystem that should be listed,
167 return nonzero, else zero. */
169 static int
170 selected_fstype (const char *fstype)
172 const struct fs_type_list *fsp;
174 if (fs_select_list == NULL || fstype == NULL)
175 return 1;
176 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
177 if (STREQ (fstype, fsp->fs_name))
178 return 1;
179 return 0;
182 /* If FSTYPE is a type of filesystem that should be omitted,
183 return nonzero, else zero. */
185 static int
186 excluded_fstype (const char *fstype)
188 const struct fs_type_list *fsp;
190 if (fs_exclude_list == NULL || fstype == NULL)
191 return 0;
192 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
193 if (STREQ (fstype, fsp->fs_name))
194 return 1;
195 return 0;
198 /* Like human_readable, except return "-" if the argument is -1. */
199 static char *
200 df_readable (uintmax_t n, char *buf,
201 int from_block_size, int t_output_block_size)
203 return (n == -1 ? "-"
204 : human_readable (n, buf, from_block_size, t_output_block_size));
207 /* Display a space listing for the disk device with absolute path DISK.
208 If MOUNT_POINT is non-NULL, it is the path of the root of the
209 filesystem on DISK.
210 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
211 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
212 not be able to produce statistics in this case.
213 ME_DUMMY and ME_REMOTE are the mount entry flags. */
215 static void
216 show_dev (const char *disk, const char *mount_point, const char *fstype,
217 int me_dummy, int me_remote)
219 struct fs_usage fsu;
220 const char *stat_file;
222 if (me_remote && show_local_fs)
223 return;
225 if (me_dummy && show_all_fs == 0 && !show_listed_fs)
226 return;
228 if (!selected_fstype (fstype) || excluded_fstype (fstype))
229 return;
231 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
232 program reports on the filesystem that the special file is on.
233 It would be better to report on the unmounted filesystem,
234 but statfs doesn't do that on most systems. */
235 stat_file = mount_point ? mount_point : disk;
237 if (get_fs_usage (stat_file, disk, &fsu))
239 error (0, errno, "%s", stat_file);
240 exit_status = 1;
241 return;
244 if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
245 return;
247 if (! disk)
248 disk = "-"; /* unknown */
249 if (! fstype)
250 fstype = "-"; /* unknown */
252 /* df.c reserved 5 positions for fstype,
253 but that does not suffice for type iso9660 */
254 if (print_type)
256 int disk_name_len = (int) strlen (disk);
257 int fstype_len = (int) strlen (fstype);
258 if (disk_name_len + fstype_len + 2 < 20)
259 printf ("%s%*s ", disk, 18 - disk_name_len, fstype);
260 else if (!posix_format)
261 printf ("%s\n%18s ", disk, fstype);
262 else
263 printf ("%s %s", disk, fstype);
265 else
267 if ((int) strlen (disk) > 20 && !posix_format)
268 printf ("%s\n%20s", disk, "");
269 else
270 printf ("%-20s", disk);
273 if (inode_format)
275 char buf[3][LONGEST_HUMAN_READABLE + 1];
276 double inodes_percent_used;
277 uintmax_t inodes_used;
278 int inode_units = output_block_size < 0 ? output_block_size : 1;
280 if (fsu.fsu_files == -1 || fsu.fsu_files < fsu.fsu_ffree)
282 inodes_used = -1;
283 inodes_percent_used = -1;
285 else
287 inodes_used = fsu.fsu_files - fsu.fsu_ffree;
288 inodes_percent_used =
289 (fsu.fsu_files == 0 ? 0
290 : inodes_used * 100.0 / fsu.fsu_files);
293 printf (" %7s %7s %7s ",
294 df_readable (fsu.fsu_files, buf[0], 1, inode_units),
295 df_readable (inodes_used, buf[1], 1, inode_units),
296 df_readable (fsu.fsu_ffree, buf[2], 1, inode_units));
298 if (inodes_percent_used < 0)
299 printf (" - ");
300 else
301 printf ("%4.0f%%", inodes_percent_used);
303 else
305 int w = output_block_size < 0 ? 5 : 9;
306 char buf[2][LONGEST_HUMAN_READABLE + 1];
307 char availbuf[LONGEST_HUMAN_READABLE + 2];
308 char *avail;
309 double blocks_percent_used;
310 uintmax_t blocks_used;
312 if (fsu.fsu_blocks == -1 || fsu.fsu_blocks < fsu.fsu_bfree)
314 blocks_used = -1;
315 blocks_percent_used = -1;
317 else
319 blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
320 blocks_percent_used =
321 ((fsu.fsu_bavail == -1
322 || blocks_used + fsu.fsu_bavail == 0
323 || (fsu.fsu_bavail_top_bit_set
324 ? blocks_used < - fsu.fsu_bavail
325 : fsu.fsu_bfree < fsu.fsu_bavail))
326 ? -1
327 : blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail));
330 avail = df_readable ((fsu.fsu_bavail_top_bit_set
331 ? - fsu.fsu_bavail
332 : fsu.fsu_bavail),
333 availbuf + 1, fsu.fsu_blocksize,
334 output_block_size);
336 if (fsu.fsu_bavail_top_bit_set)
337 *--avail = '-';
339 printf (" %*s %*s %*s ",
340 w, df_readable (fsu.fsu_blocks, buf[0], fsu.fsu_blocksize,
341 output_block_size),
342 w, df_readable (blocks_used, buf[1], fsu.fsu_blocksize,
343 output_block_size),
344 w, avail);
346 if (blocks_percent_used < 0)
347 printf (" - ");
348 else
349 printf ("%3.0f%%", blocks_percent_used);
352 if (mount_point)
354 #ifdef HIDE_AUTOMOUNT_PREFIX
355 /* Don't print the first directory name in MOUNT_POINT if it's an
356 artifact of an automounter. This is a bit too aggressive to be
357 the default. */
358 if (strncmp ("/auto/", mount_point, 6) == 0)
359 mount_point += 5;
360 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
361 mount_point += 8;
362 #endif
363 printf (" %s", mount_point);
365 putchar ('\n');
368 /* Identify the directory, if any, that device
369 DISK is mounted on, and show its disk usage. */
371 static void
372 show_disk (const char *disk)
374 struct mount_entry *me;
376 for (me = mount_list; me; me = me->me_next)
377 if (STREQ (disk, me->me_devname))
379 show_dev (me->me_devname, me->me_mountdir, me->me_type,
380 me->me_dummy, me->me_remote);
381 return;
383 /* No filesystem is mounted on DISK. */
384 show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
387 /* Return the root mountpoint of the filesystem on which FILE exists, in
388 malloced storage. FILE_STAT should be the result of stating FILE. */
389 static char *
390 find_mount_point (const char *file, const struct stat *file_stat)
392 struct saved_cwd cwd;
393 struct stat last_stat;
394 char *mp = 0; /* The malloced mount point path. */
396 if (save_cwd (&cwd))
397 return NULL;
399 if (S_ISDIR (file_stat->st_mode))
400 /* FILE is a directory, so just chdir there directly. */
402 last_stat = *file_stat;
403 if (chdir (file) < 0)
404 return NULL;
406 else
407 /* FILE is some other kind of file, we need to use its directory. */
409 int rv;
410 char *tmp = xstrdup (file);
411 char *dir;
413 strip_trailing_slashes (tmp);
414 dir = dir_name (tmp);
415 free (tmp);
416 rv = chdir (dir);
417 free (dir);
419 if (rv < 0)
420 return NULL;
422 if (stat (".", &last_stat) < 0)
423 goto done;
426 /* Now walk up FILE's parents until we find another filesystem or /,
427 chdiring as we go. LAST_STAT holds stat information for the last place
428 we visited. */
429 for (;;)
431 struct stat st;
432 if (stat ("..", &st) < 0)
433 goto done;
434 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
435 /* cwd is the mount point. */
436 break;
437 if (chdir ("..") < 0)
438 goto done;
439 last_stat = st;
442 /* Finally reached a mount point, see what it's called. */
443 mp = xgetcwd ();
445 done:
446 /* Restore the original cwd. */
448 int save_errno = errno;
449 if (restore_cwd (&cwd, 0, mp))
450 exit (1); /* We're scrod. */
451 free_cwd (&cwd);
452 errno = save_errno;
455 return mp;
458 /* Figure out which device file or directory POINT is mounted on
459 and show its disk usage.
460 STATP is the results of `stat' on POINT. */
461 static void
462 show_point (const char *point, const struct stat *statp)
464 struct stat disk_stats;
465 struct mount_entry *me;
466 struct mount_entry *matching_dummy = NULL;
468 for (me = mount_list; me; me = me->me_next)
470 if (me->me_dev == (dev_t) -1)
472 if (stat (me->me_mountdir, &disk_stats) == 0)
473 me->me_dev = disk_stats.st_dev;
474 else
476 error (0, errno, "%s", me->me_mountdir);
477 exit_status = 1;
478 /* So we won't try and fail repeatedly. */
479 me->me_dev = (dev_t) -2;
483 if (statp->st_dev == me->me_dev)
485 /* Skip bogus mtab entries. */
486 if (stat (me->me_mountdir, &disk_stats) != 0 ||
487 disk_stats.st_dev != me->me_dev)
488 continue;
490 /* Prefer non-dummy entries. */
491 if (me->me_dummy)
493 matching_dummy = me;
494 continue;
497 show_dev (me->me_devname, me->me_mountdir, me->me_type,
498 me->me_dummy, me->me_remote);
499 return;
503 if (matching_dummy)
505 show_dev (matching_dummy->me_devname, matching_dummy->me_mountdir,
506 matching_dummy->me_type, 1, matching_dummy->me_remote);
507 return;
510 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
511 print as much info as we can; methods that require the device to be
512 present will fail at a later point. */
514 /* Find the actual mount point. */
515 char *mp = find_mount_point (point, statp);
516 if (mp)
518 show_dev (0, mp, 0, 0, 0);
519 free (mp);
521 else
522 error (0, errno, "%s", point);
526 /* Determine what kind of node PATH is and show the disk usage
527 for it. STATP is the results of `stat' on PATH. */
529 static void
530 show_entry (const char *path, const struct stat *statp)
532 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
533 show_disk (path);
534 else
535 show_point (path, statp);
538 /* Show all mounted filesystems, except perhaps those that are of
539 an unselected type or are empty. */
541 static void
542 show_all_entries (void)
544 struct mount_entry *me;
546 for (me = mount_list; me; me = me->me_next)
547 show_dev (me->me_devname, me->me_mountdir, me->me_type,
548 me->me_dummy, me->me_remote);
551 /* Add FSTYPE to the list of filesystem types to display. */
553 static void
554 add_fs_type (const char *fstype)
556 struct fs_type_list *fsp;
558 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
559 fsp->fs_name = (char *) fstype;
560 fsp->fs_next = fs_select_list;
561 fs_select_list = fsp;
564 /* Add FSTYPE to the list of filesystem types to be omitted. */
566 static void
567 add_excluded_fs_type (const char *fstype)
569 struct fs_type_list *fsp;
571 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
572 fsp->fs_name = (char *) fstype;
573 fsp->fs_next = fs_exclude_list;
574 fs_exclude_list = fsp;
577 void
578 usage (int status)
580 if (status != 0)
581 fprintf (stderr, _("Try `%s --help' for more information.\n"),
582 program_name);
583 else
585 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
586 printf (_("\
587 Show information about the filesystem on which each FILE resides,\n\
588 or all filesystems by default.\n\
590 -a, --all include filesystems having 0 blocks\n\
591 --block-size=SIZE use SIZE-byte blocks\n\
592 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
593 -H, --si likewise, but use powers of 1000 not 1024\n\
594 -i, --inodes list inode information instead of block usage\n\
595 -k, --kilobytes like --block-size=1024\n\
596 -l, --local limit listing to local filesystems\n\
597 -m, --megabytes like --block-size=1048576\n\
598 --no-sync do not invoke sync before getting usage info (default)\n\
599 -P, --portability use the POSIX output format\n\
600 --sync invoke sync before getting usage info\n\
601 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
602 -T, --print-type print filesystem type\n\
603 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
604 -v (ignored)\n\
605 --help display this help and exit\n\
606 --version output version information and exit\n\
607 "));
608 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
609 close_stdout ();
611 exit (status);
615 main (int argc, char **argv)
617 int c;
618 struct stat *stats;
620 program_name = argv[0];
621 setlocale (LC_ALL, "");
622 bindtextdomain (PACKAGE, LOCALEDIR);
623 textdomain (PACKAGE);
625 fs_select_list = NULL;
626 fs_exclude_list = NULL;
627 inode_format = 0;
628 show_all_fs = 0;
629 show_listed_fs = 0;
631 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
633 print_type = 0;
634 posix_format = 0;
635 exit_status = 0;
637 while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
638 != -1)
640 switch (c)
642 case 0: /* Long option. */
643 break;
644 case 'a':
645 show_all_fs = 1;
646 break;
647 case 'i':
648 inode_format = 1;
649 break;
650 case 'h':
651 output_block_size = -1024;
652 break;
653 case 'H':
654 output_block_size = -1000;
655 break;
656 case 'k':
657 output_block_size = 1024;
658 break;
659 case 'l':
660 show_local_fs = 1;
661 break;
662 case 'm':
663 output_block_size = 1024 * 1024;
664 break;
665 case 'T':
666 print_type = 1;
667 break;
668 case 'P':
669 posix_format = 1;
670 break;
671 case CHAR_MAX + 1:
672 require_sync = 1;
673 break;
674 case CHAR_MAX + 2:
675 require_sync = 0;
676 break;
678 case CHAR_MAX + 3:
679 human_block_size (optarg, 1, &output_block_size);
680 break;
682 case 'F':
683 /* Accept -F as a synonym for -t for compatibility with Solaris. */
684 case 't':
685 add_fs_type (optarg);
686 break;
688 case 'v': /* For SysV compatibility. */
689 /* ignore */
690 break;
691 case 'x':
692 add_excluded_fs_type (optarg);
693 break;
694 default:
695 usage (1);
699 if (show_version)
701 printf ("df (%s) %s\n", GNU_PACKAGE, VERSION);
702 close_stdout ();
703 exit (0);
706 if (show_help)
707 usage (0);
709 /* Fail if the same file system type was both selected and excluded. */
711 int match = 0;
712 struct fs_type_list *i;
713 for (i = fs_select_list; i; i = i->fs_next)
715 struct fs_type_list *j;
716 for (j = fs_exclude_list; j; j = j->fs_next)
718 if (STREQ (i->fs_name, j->fs_name))
720 error (0, 0,
721 _("file system type `%s' both selected and excluded"),
722 i->fs_name);
723 match = 1;
724 break;
728 if (match)
729 exit (1);
732 if (optind == argc)
734 #ifdef lint
735 /* Suppress `used before initialized' warning. */
736 stats = NULL;
737 #endif
739 else
741 int i;
743 /* stat all the given entries to make sure they get automounted,
744 if necessary, before reading the filesystem table. */
745 stats = (struct stat *)
746 xmalloc ((argc - optind) * sizeof (struct stat));
747 for (i = optind; i < argc; ++i)
748 if (stat (argv[i], &stats[i - optind]))
750 error (0, errno, "%s", argv[i]);
751 exit_status = 1;
752 argv[i] = NULL;
756 mount_list =
757 read_filesystem_list ((fs_select_list != NULL
758 || fs_exclude_list != NULL
759 || print_type
760 || show_local_fs));
762 if (mount_list == NULL)
763 error (1, errno, _("cannot read table of mounted filesystems"));
765 if (require_sync)
766 sync ();
768 if (optind == argc)
770 print_header ();
771 show_all_entries ();
773 else
775 int i;
777 /* Display explicitly requested empty filesystems. */
778 show_listed_fs = 1;
780 print_header ();
781 for (i = optind; i < argc; ++i)
782 if (argv[i])
783 show_entry (argv[i], &stats[i - optind]);
786 close_stdout ();
787 exit (exit_status);