(main): Stat all non-`-' input file files (and fail if a
[coreutils.git] / src / df.c
blobce7ff3b210d08485bb6e98d6660521d4a68af1c3
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"
39 char *dirname ();
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 positive, show all entries; if zero, omit size-zero entries and
51 automounter dummies; if negative, also omit non-local filesystems. */
52 static int show_all_fs;
54 /* If nonzero, output data for each filesystem corresponding to a
55 command line argument -- even if it's a dummy (automounter) entry. */
56 static int show_listed_fs;
58 /* If positive, the units to use when printing sizes;
59 if negative, the human-readable base. */
60 static int output_block_size;
62 /* If nonzero, use the POSIX output format. */
63 static int posix_format;
65 /* If nonzero, invoke the `sync' system call before getting any usage data.
66 Using this option can make df very slow, especially with many or very
67 busy disks. Note that this may make a difference on some systems --
68 SunOs4.1.3, for one. It is *not* necessary on Linux. */
69 static int require_sync = 0;
71 /* Nonzero if errors have occurred. */
72 static int exit_status;
74 /* A filesystem type to display. */
76 struct fs_type_list
78 char *fs_name;
79 struct fs_type_list *fs_next;
82 /* Linked list of filesystem types to display.
83 If `fs_select_list' is NULL, list all types.
84 This table is generated dynamically from command-line options,
85 rather than hardcoding into the program what it thinks are the
86 valid filesystem types; let the user specify any filesystem type
87 they want to, and if there are any filesystems of that type, they
88 will be shown.
90 Some filesystem types:
91 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
93 static struct fs_type_list *fs_select_list;
95 /* Linked list of filesystem types to omit.
96 If the list is empty, don't exclude any types. */
98 static struct fs_type_list *fs_exclude_list;
100 /* Linked list of mounted filesystems. */
101 static struct mount_entry *mount_list;
103 /* If nonzero, display usage information and exit. */
104 static int show_help;
106 /* If nonzero, print the version on standard output and exit. */
107 static int show_version;
109 /* If nonzero, print filesystem type as well. */
110 static int print_type;
112 static struct option const long_options[] =
114 {"all", no_argument, NULL, 'a'},
115 {"block-size", required_argument, NULL, 131},
116 {"inodes", no_argument, NULL, 'i'},
117 {"human-readable", no_argument, NULL, 'h'},
118 {"si", no_argument, NULL, 'H'},
119 {"kilobytes", no_argument, NULL, 'k'},
120 {"local", no_argument, NULL, 'l'},
121 {"megabytes", no_argument, NULL, 'm'},
122 {"portability", no_argument, NULL, 'P'},
123 {"print-type", no_argument, NULL, 'T'},
124 {"sync", no_argument, NULL, 129},
125 {"no-sync", no_argument, NULL, 130},
126 {"type", required_argument, NULL, 't'},
127 {"exclude-type", required_argument, NULL, 'x'},
128 {"help", no_argument, &show_help, 1},
129 {"version", no_argument, &show_version, 1},
130 {NULL, 0, NULL, 0}
133 static void
134 print_header (void)
136 printf ("Filesystem ");
138 if (print_type)
139 printf (" Type");
140 else
141 printf (" ");
143 if (inode_format)
144 printf (" Inodes IUsed IFree IUse%%");
145 else if (output_block_size < 0)
146 printf (" Size Used Avail Use%%");
147 else
149 char buf[LONGEST_HUMAN_READABLE + 1];
150 char *p = human_readable (output_block_size, buf, 1, -1024);
152 /* Replace e.g. "1.0k" by "1k". */
153 size_t plen = strlen (p);
154 if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
155 strcpy (p + plen - 3, p + plen - 1);
157 printf (" %4s-blocks Used Available Use%%", p);
160 printf (" Mounted on\n");
163 /* If FSTYPE is a type of filesystem that should be listed,
164 return nonzero, else zero. */
166 static int
167 selected_fstype (const char *fstype)
169 const struct fs_type_list *fsp;
171 if (fs_select_list == NULL || fstype == NULL)
172 return 1;
173 for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
174 if (STREQ (fstype, fsp->fs_name))
175 return 1;
176 return 0;
179 /* If FSTYPE is a type of filesystem that should be omitted,
180 return nonzero, else zero. */
182 static int
183 excluded_fstype (const char *fstype)
185 const struct fs_type_list *fsp;
187 if (fs_exclude_list == NULL || fstype == NULL)
188 return 0;
189 for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
190 if (STREQ (fstype, fsp->fs_name))
191 return 1;
192 return 0;
195 /* Like human_readable, except return "-" if the argument is -1. */
196 static char *
197 df_readable (uintmax_t n, char *buf,
198 int from_block_size, int t_output_block_size)
200 return (n == -1 ? "-"
201 : human_readable (n, buf, from_block_size, t_output_block_size));
204 /* Display a space listing for the disk device with absolute path DISK.
205 If MOUNT_POINT is non-NULL, it is the path of the root of the
206 filesystem on DISK.
207 If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
208 If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
209 not be able to produce statistics in this case. */
211 static void
212 show_dev (const char *disk, const char *mount_point, const char *fstype)
214 struct fs_usage fsu;
215 const char *stat_file;
217 /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
218 program reports on the filesystem that the special file is on.
219 It would be better to report on the unmounted filesystem,
220 but statfs doesn't do that on most systems. */
221 stat_file = mount_point ? mount_point : disk;
223 if (show_all_fs < 0 && fstype && REMOTE_FS_TYPE (fstype))
224 return;
226 if (!selected_fstype (fstype) || excluded_fstype (fstype))
227 return;
229 if (get_fs_usage (stat_file, disk, &fsu))
231 error (0, errno, "%s", stat_file);
232 exit_status = 1;
233 return;
236 if (fsu.fsu_blocks == 0 && show_all_fs <= 0 && !show_listed_fs)
237 return;
239 if (! disk)
240 disk = "-"; /* unknown */
242 printf ((print_type ? "%-13s" : "%-20s"), disk);
243 if ((int) strlen (disk) > (print_type ? 13 : 20) && !posix_format)
244 printf ((print_type ? "\n%13s" : "\n%20s"), "");
246 if (! fstype)
247 fstype = "-"; /* unknown */
248 if (print_type)
249 printf (" %-5s ", fstype);
251 if (inode_format)
253 char buf[3][LONGEST_HUMAN_READABLE + 1];
254 double inodes_percent_used;
255 uintmax_t inodes_used;
256 int inode_units = output_block_size < 0 ? output_block_size : 1;
258 if (fsu.fsu_files == -1 || fsu.fsu_files < fsu.fsu_ffree)
260 inodes_used = -1;
261 inodes_percent_used = -1;
263 else
265 inodes_used = fsu.fsu_files - fsu.fsu_ffree;
266 inodes_percent_used =
267 (fsu.fsu_files == 0 ? 0
268 : inodes_used * 100.0 / fsu.fsu_files);
271 printf (" %7s %7s %7s ",
272 df_readable (fsu.fsu_files, buf[0], 1, inode_units),
273 df_readable (inodes_used, buf[1], 1, inode_units),
274 df_readable (fsu.fsu_ffree, buf[2], 1, inode_units));
276 if (inodes_percent_used < 0)
277 printf (" - ");
278 else
279 printf ("%4.0f%%", inodes_percent_used);
281 else
283 int w = output_block_size < 0 ? 5 : 9;
284 char buf[2][LONGEST_HUMAN_READABLE + 1];
285 char availbuf[LONGEST_HUMAN_READABLE + 2];
286 char *avail;
287 double blocks_percent_used;
288 uintmax_t blocks_used;
290 if (fsu.fsu_blocks == -1 || fsu.fsu_blocks < fsu.fsu_bfree)
292 blocks_used = -1;
293 blocks_percent_used = -1;
295 else
297 blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
298 blocks_percent_used =
299 ((fsu.fsu_bavail == -1
300 || blocks_used + fsu.fsu_bavail == 0
301 || (fsu.fsu_bavail_top_bit_set
302 ? blocks_used < - fsu.fsu_bavail
303 : fsu.fsu_bfree < fsu.fsu_bavail))
304 ? -1
305 : blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail));
308 avail = df_readable ((fsu.fsu_bavail_top_bit_set
309 ? - fsu.fsu_bavail
310 : fsu.fsu_bavail),
311 availbuf + 1, fsu.fsu_blocksize,
312 output_block_size);
314 if (fsu.fsu_bavail_top_bit_set)
315 *--avail = '-';
317 printf (" %*s %*s %*s ",
318 w, df_readable (fsu.fsu_blocks, buf[0], fsu.fsu_blocksize,
319 output_block_size),
320 w, df_readable (blocks_used, buf[1], fsu.fsu_blocksize,
321 output_block_size),
322 w, avail);
324 if (blocks_percent_used < 0)
325 printf (" - ");
326 else
327 printf ("%3.0f%%", blocks_percent_used);
330 if (mount_point)
332 #ifdef HIDE_AUTOMOUNT_PREFIX
333 /* Don't print the first directory name in MOUNT_POINT if it's an
334 artifact of an automounter. This is a bit too aggressive to be
335 the default. */
336 if (strncmp ("/auto/", mount_point, 6) == 0)
337 mount_point += 5;
338 else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
339 mount_point += 8;
340 #endif
341 printf (" %s", mount_point);
343 putchar ('\n');
346 /* Identify the directory, if any, that device
347 DISK is mounted on, and show its disk usage. */
349 static void
350 show_disk (const char *disk)
352 struct mount_entry *me;
354 for (me = mount_list; me; me = me->me_next)
355 if (STREQ (disk, me->me_devname))
357 show_dev (me->me_devname, me->me_mountdir, me->me_type);
358 return;
360 /* No filesystem is mounted on DISK. */
361 show_dev (disk, (char *) NULL, (char *) NULL);
364 /* Return the root mountpoint of the filesystem on which FILE exists, in
365 malloced storage. FILE_STAT should be the result of stating FILE. */
366 static char *
367 find_mount_point (const char *file, const struct stat *file_stat)
369 struct saved_cwd cwd;
370 struct stat last_stat;
371 char *mp = 0; /* The malloced mount point path. */
373 if (save_cwd (&cwd))
374 return NULL;
376 if (S_ISDIR (file_stat->st_mode))
377 /* FILE is a directory, so just chdir there directly. */
379 last_stat = *file_stat;
380 if (chdir (file) < 0)
381 return NULL;
383 else
384 /* FILE is some other kind of file, we need to use its directory. */
386 int rv;
387 char *tmp = xstrdup (file);
388 char *dir;
390 strip_trailing_slashes (tmp);
391 dir = dirname (tmp);
392 free (tmp);
393 rv = chdir (dir);
394 free (dir);
396 if (rv < 0)
397 return NULL;
399 if (stat (".", &last_stat) < 0)
400 goto done;
403 /* Now walk up FILE's parents until we find another filesystem or /,
404 chdiring as we go. LAST_STAT holds stat information for the last place
405 we visited. */
406 for (;;)
408 struct stat st;
409 if (stat ("..", &st) < 0)
410 goto done;
411 if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
412 /* cwd is the mount point. */
413 break;
414 if (chdir ("..") < 0)
415 goto done;
416 last_stat = st;
419 /* Finally reached a mount point, see what it's called. */
420 mp = xgetcwd ();
422 done:
423 /* Restore the original cwd. */
425 int save_errno = errno;
426 if (restore_cwd (&cwd, 0, mp))
427 exit (1); /* We're scrod. */
428 free_cwd (&cwd);
429 errno = save_errno;
432 return mp;
435 /* Figure out which device file or directory POINT is mounted on
436 and show its disk usage.
437 STATP is the results of `stat' on POINT. */
438 static void
439 show_point (const char *point, const struct stat *statp)
441 struct stat disk_stats;
442 struct mount_entry *me;
444 for (me = mount_list; me; me = me->me_next)
446 if (me->me_dev == (dev_t) -1)
448 if (stat (me->me_mountdir, &disk_stats) == 0)
449 me->me_dev = disk_stats.st_dev;
450 else
452 error (0, errno, "%s", me->me_mountdir);
453 exit_status = 1;
454 /* So we won't try and fail repeatedly. */
455 me->me_dev = (dev_t) -2;
459 if (statp->st_dev == me->me_dev)
461 /* Skip bogus mtab entries. */
462 if (stat (me->me_mountdir, &disk_stats) != 0 ||
463 disk_stats.st_dev != me->me_dev)
464 continue;
465 show_dev (me->me_devname, me->me_mountdir, me->me_type);
466 return;
470 /* We couldn't find the mount entry corresponding to POINT. Go ahead and
471 print as much info as we can; methods that require the device to be
472 present will fail at a later point. */
474 /* Find the actual mount point. */
475 char *mp = find_mount_point (point, statp);
476 if (mp)
478 show_dev (0, mp, 0);
479 free (mp);
481 else
482 error (0, errno, "%s", point);
486 /* Determine what kind of node PATH is and show the disk usage
487 for it. STATP is the results of `stat' on PATH. */
489 static void
490 show_entry (const char *path, const struct stat *statp)
492 if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
493 show_disk (path);
494 else
495 show_point (path, statp);
498 /* Show all mounted filesystems, except perhaps those that are of
499 an unselected type or are empty. */
501 static void
502 show_all_entries (void)
504 struct mount_entry *me;
506 for (me = mount_list; me; me = me->me_next)
507 show_dev (me->me_devname, me->me_mountdir, me->me_type);
510 /* Add FSTYPE to the list of filesystem types to display. */
512 static void
513 add_fs_type (const char *fstype)
515 struct fs_type_list *fsp;
517 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
518 fsp->fs_name = (char *) fstype;
519 fsp->fs_next = fs_select_list;
520 fs_select_list = fsp;
523 /* Add FSTYPE to the list of filesystem types to be omitted. */
525 static void
526 add_excluded_fs_type (const char *fstype)
528 struct fs_type_list *fsp;
530 fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
531 fsp->fs_name = (char *) fstype;
532 fsp->fs_next = fs_exclude_list;
533 fs_exclude_list = fsp;
536 static void
537 usage (int status)
539 if (status != 0)
540 fprintf (stderr, _("Try `%s --help' for more information.\n"),
541 program_name);
542 else
544 printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
545 printf (_("\
546 Show information about the filesystem on which each FILE resides,\n\
547 or all filesystems by default.\n\
549 -a, --all include filesystems having 0 blocks\n\
550 --block-size=SIZE use SIZE-byte blocks\n\
551 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
552 -H, --si likewise, but use powers of 1000 not 1024\n\
553 -i, --inodes list inode information instead of block usage\n\
554 -k, --kilobytes like --block-size=1024\n\
555 -l, --local limit listing to local filesystems\n\
556 -m, --megabytes like --block-size=1048576\n\
557 --no-sync do not invoke sync before getting usage info (default)\n\
558 -P, --portability use the POSIX output format\n\
559 --sync invoke sync before getting usage info\n\
560 -t, --type=TYPE limit listing to filesystems of type TYPE\n\
561 -T, --print-type print filesystem type\n\
562 -x, --exclude-type=TYPE limit listing to filesystems not of type TYPE\n\
563 -v (ignored)\n\
564 --help display this help and exit\n\
565 --version output version information and exit\n\
566 "));
567 puts (_("\nReport bugs to <fileutils-bugs@gnu.org>."));
568 close_stdout ();
570 exit (status);
574 main (int argc, char **argv)
576 int c;
577 struct stat *stats;
579 program_name = argv[0];
580 setlocale (LC_ALL, "");
581 bindtextdomain (PACKAGE, LOCALEDIR);
582 textdomain (PACKAGE);
584 fs_select_list = NULL;
585 fs_exclude_list = NULL;
586 inode_format = 0;
587 show_all_fs = 0;
588 show_listed_fs = 0;
590 human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
592 print_type = 0;
593 posix_format = 0;
594 exit_status = 0;
596 while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
597 != -1)
599 switch (c)
601 case 0: /* Long option. */
602 break;
603 case 'a':
604 show_all_fs = 1;
605 break;
606 case 'i':
607 inode_format = 1;
608 break;
609 case 'h':
610 output_block_size = -1024;
611 break;
612 case 'H':
613 output_block_size = -1000;
614 break;
615 case 'k':
616 output_block_size = 1024;
617 break;
618 case 'l':
619 show_all_fs = -1;
620 break;
621 case 'm':
622 output_block_size = 1024 * 1024;
623 break;
624 case 'T':
625 print_type = 1;
626 break;
627 case 'P':
628 posix_format = 1;
629 break;
630 case 129:
631 require_sync = 1;
632 break;
633 case 130:
634 require_sync = 0;
635 break;
637 case 131:
638 human_block_size (optarg, 1, &output_block_size);
639 break;
641 case 'F':
642 /* Accept -F as a synonym for -t for compatibility with Solaris. */
643 case 't':
644 add_fs_type (optarg);
645 break;
647 case 'v': /* For SysV compatibility. */
648 /* ignore */
649 break;
650 case 'x':
651 add_excluded_fs_type (optarg);
652 break;
653 default:
654 usage (1);
658 if (show_version)
660 printf ("df (%s) %s\n", GNU_PACKAGE, VERSION);
661 close_stdout ();
662 exit (0);
665 if (show_help)
666 usage (0);
668 /* Fail if the same file system type was both selected and excluded. */
670 int match = 0;
671 struct fs_type_list *i;
672 for (i = fs_select_list; i; i = i->fs_next)
674 struct fs_type_list *j;
675 for (j = fs_exclude_list; j; j = j->fs_next)
677 if (STREQ (i->fs_name, j->fs_name))
679 error (0, 0,
680 _("file system type `%s' both selected and excluded"),
681 i->fs_name);
682 match = 1;
683 break;
687 if (match)
688 exit (1);
691 if (optind == argc)
693 #ifdef lint
694 /* Suppress `used before initialized' warning. */
695 stats = NULL;
696 #endif
698 else
700 int i;
702 /* stat all the given entries to make sure they get automounted,
703 if necessary, before reading the filesystem table. */
704 stats = (struct stat *)
705 xmalloc ((argc - optind) * sizeof (struct stat));
706 for (i = optind; i < argc; ++i)
707 if (stat (argv[i], &stats[i - optind]))
709 error (0, errno, "%s", argv[i]);
710 exit_status = 1;
711 argv[i] = NULL;
715 mount_list =
716 read_filesystem_list ((fs_select_list != NULL
717 || fs_exclude_list != NULL
718 || print_type),
719 optind == argc ? show_all_fs : 1);
721 if (mount_list == NULL)
722 error (1, errno, _("cannot read table of mounted filesystems"));
724 if (require_sync)
725 sync ();
727 if (optind == argc)
729 print_header ();
730 show_all_entries ();
732 else
734 int i;
736 /* Display explicitly requested empty filesystems. */
737 show_listed_fs = 1;
739 print_header ();
740 for (i = optind; i < argc; ++i)
741 if (argv[i])
742 show_entry (argv[i], &stats[i - optind]);
745 close_stdout ();
746 exit (exit_status);