Don't include closeout.h or version-etc.h explicitly. Now, they're included via sys2.h.
[platform/upstream/coreutils.git] / src / df.c
1 /* df - summarize free disk space
2    Copyright (C) 91, 1995-1999 Free Software Foundation, Inc.
3
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.
8
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.
13
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.  */
17
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.  */
21
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>
30
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"
38
39 /* The official name of this program (e.g., no `g' prefix).  */
40 #define PROGRAM_NAME "df"
41
42 #define AUTHORS \
43   "Torbjorn Granlund, David MacKenzie, Larry McVoy, and Paul Eggert"
44
45 void strip_trailing_slashes ();
46 char *xstrdup ();
47 char *xgetcwd ();
48
49 /* Name this program was run with. */
50 char *program_name;
51
52 /* If nonzero, show inode information. */
53 static int inode_format;
54
55 /* If nonzero, show even filesystems with zero size or
56    uninteresting types. */
57 static int show_all_fs;
58
59 /* If nonzero, show only local filesystems.  */
60 static int show_local_fs;
61
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;
65
66 /* If positive, the units to use when printing sizes;
67    if negative, the human-readable base.  */
68 static int output_block_size;
69
70 /* If nonzero, use the POSIX output format.  */
71 static int posix_format;
72
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;
78
79 /* Nonzero if errors have occurred. */
80 static int exit_status;
81
82 /* A filesystem type to display. */
83
84 struct fs_type_list
85 {
86   char *fs_name;
87   struct fs_type_list *fs_next;
88 };
89
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.
97
98    Some filesystem types:
99    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
100
101 static struct fs_type_list *fs_select_list;
102
103 /* Linked list of filesystem types to omit.
104    If the list is empty, don't exclude any types.  */
105
106 static struct fs_type_list *fs_exclude_list;
107
108 /* Linked list of mounted filesystems. */
109 static struct mount_entry *mount_list;
110
111 /* If nonzero, print filesystem type as well.  */
112 static int print_type;
113
114 static struct option const long_options[] =
115 {
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}
133 };
134
135 static void
136 print_header (void)
137 {
138   printf ("Filesystem ");
139
140   if (print_type)
141     printf ("   Type");
142   else
143     printf ("       ");
144
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
150     {
151       char buf[LONGEST_HUMAN_READABLE + 1];
152       char *p = human_readable (output_block_size, buf, 1, -1024);
153
154       /* Replace e.g. "1.0k" by "1k".  */
155       size_t plen = strlen (p);
156       if (3 <= plen && strncmp (p + plen - 3, ".0", 2) == 0)
157         strcpy (p + plen - 3, p + plen - 1);
158
159       printf (" %4s-blocks      Used Available Use%%", p);
160     }
161
162   printf (" Mounted on\n");
163 }
164
165 /* If FSTYPE is a type of filesystem that should be listed,
166    return nonzero, else zero. */
167
168 static int
169 selected_fstype (const char *fstype)
170 {
171   const struct fs_type_list *fsp;
172
173   if (fs_select_list == NULL || fstype == NULL)
174     return 1;
175   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
176     if (STREQ (fstype, fsp->fs_name))
177       return 1;
178   return 0;
179 }
180
181 /* If FSTYPE is a type of filesystem that should be omitted,
182    return nonzero, else zero. */
183
184 static int
185 excluded_fstype (const char *fstype)
186 {
187   const struct fs_type_list *fsp;
188
189   if (fs_exclude_list == NULL || fstype == NULL)
190     return 0;
191   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
192     if (STREQ (fstype, fsp->fs_name))
193       return 1;
194   return 0;
195 }
196
197 /* Like human_readable, except return "-" if the argument is -1.  */
198 static char *
199 df_readable (uintmax_t n, char *buf,
200              int from_block_size, int t_output_block_size)
201 {
202   return (n == -1 ? "-"
203           : human_readable (n, buf, from_block_size, t_output_block_size));
204 }
205
206 /* Display a space listing for the disk device with absolute path DISK.
207    If MOUNT_POINT is non-NULL, it is the path of the root of the
208    filesystem on DISK.
209    If FSTYPE is non-NULL, it is the type of the filesystem on DISK.
210    If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
211    not be able to produce statistics in this case.
212    ME_DUMMY and ME_REMOTE are the mount entry flags.  */
213
214 static void
215 show_dev (const char *disk, const char *mount_point, const char *fstype,
216           int me_dummy, int me_remote)
217 {
218   struct fs_usage fsu;
219   const char *stat_file;
220
221   if (me_remote && show_local_fs)
222     return;
223
224   if (me_dummy && show_all_fs == 0 && !show_listed_fs)
225     return;
226
227   if (!selected_fstype (fstype) || excluded_fstype (fstype))
228     return;
229
230   /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
231      program reports on the filesystem that the special file is on.
232      It would be better to report on the unmounted filesystem,
233      but statfs doesn't do that on most systems.  */
234   stat_file = mount_point ? mount_point : disk;
235
236   if (get_fs_usage (stat_file, disk, &fsu))
237     {
238       error (0, errno, "%s", stat_file);
239       exit_status = 1;
240       return;
241     }
242
243   if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
244     return;
245
246   if (! disk)
247     disk = "-";                 /* unknown */
248   if (! fstype)
249     fstype = "-";               /* unknown */
250
251   /* df.c reserved 5 positions for fstype,
252      but that does not suffice for type iso9660 */
253   if (print_type)
254     {
255       int disk_name_len = (int) strlen (disk);
256       int fstype_len = (int) strlen (fstype);
257       if (disk_name_len + fstype_len + 2 < 20)
258         printf ("%s%*s  ", disk, 18 - disk_name_len, fstype);
259       else if (!posix_format)
260         printf ("%s\n%18s  ", disk, fstype);
261       else
262         printf ("%s %s", disk, fstype);
263     }
264   else
265     {
266       if ((int) strlen (disk) > 20 && !posix_format)
267         printf ("%s\n%20s", disk, "");
268       else
269         printf ("%-20s", disk);
270     }
271
272   if (inode_format)
273     {
274       char buf[3][LONGEST_HUMAN_READABLE + 1];
275       double inodes_percent_used;
276       uintmax_t inodes_used;
277       int inode_units = output_block_size < 0 ? output_block_size : 1;
278
279       if (fsu.fsu_files == -1 || fsu.fsu_files < fsu.fsu_ffree)
280         {
281           inodes_used = -1;
282           inodes_percent_used = -1;
283         }
284       else
285         {
286           inodes_used = fsu.fsu_files - fsu.fsu_ffree;
287           inodes_percent_used =
288             (fsu.fsu_files == 0 ? 0
289              : inodes_used * 100.0 / fsu.fsu_files);
290         }
291
292       printf (" %7s %7s %7s ",
293               df_readable (fsu.fsu_files, buf[0], 1, inode_units),
294               df_readable (inodes_used, buf[1], 1, inode_units),
295               df_readable (fsu.fsu_ffree, buf[2], 1, inode_units));
296
297       if (inodes_percent_used < 0)
298         printf ("   - ");
299       else
300         printf ("%4.0f%%", inodes_percent_used);
301     }
302   else
303     {
304       int w = output_block_size < 0 ? 5 : 9;
305       char buf[2][LONGEST_HUMAN_READABLE + 1];
306       char availbuf[LONGEST_HUMAN_READABLE + 2];
307       char *avail;
308       double blocks_percent_used;
309       uintmax_t blocks_used;
310
311       if (fsu.fsu_blocks == -1 || fsu.fsu_blocks < fsu.fsu_bfree)
312         {
313           blocks_used = -1;
314           blocks_percent_used = -1;
315         }
316       else
317         {
318           blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
319           blocks_percent_used =
320             ((fsu.fsu_bavail == -1
321               || blocks_used + fsu.fsu_bavail == 0
322               || (fsu.fsu_bavail_top_bit_set
323                   ? blocks_used < - fsu.fsu_bavail
324                   : fsu.fsu_bfree < fsu.fsu_bavail))
325              ? -1
326              : blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail));
327         }
328
329       avail = df_readable ((fsu.fsu_bavail_top_bit_set
330                             ? - fsu.fsu_bavail
331                             : fsu.fsu_bavail),
332                            availbuf + 1, fsu.fsu_blocksize,
333                            output_block_size);
334
335       if (fsu.fsu_bavail_top_bit_set)
336         *--avail = '-';
337
338       printf (" %*s %*s %*s ",
339               w, df_readable (fsu.fsu_blocks, buf[0], fsu.fsu_blocksize,
340                               output_block_size),
341               w, df_readable (blocks_used, buf[1], fsu.fsu_blocksize,
342                               output_block_size),
343               w, avail);
344
345       if (blocks_percent_used < 0)
346         printf ("  - ");
347       else
348         printf ("%3.0f%%", blocks_percent_used);
349     }
350
351   if (mount_point)
352     {
353 #ifdef HIDE_AUTOMOUNT_PREFIX
354       /* Don't print the first directory name in MOUNT_POINT if it's an
355          artifact of an automounter.  This is a bit too aggressive to be
356          the default.  */
357       if (strncmp ("/auto/", mount_point, 6) == 0)
358         mount_point += 5;
359       else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
360         mount_point += 8;
361 #endif
362       printf (" %s", mount_point);
363     }
364   putchar ('\n');
365 }
366
367 /* Identify the directory, if any, that device
368    DISK is mounted on, and show its disk usage.  */
369
370 static void
371 show_disk (const char *disk)
372 {
373   struct mount_entry *me;
374
375   for (me = mount_list; me; me = me->me_next)
376     if (STREQ (disk, me->me_devname))
377       {
378         show_dev (me->me_devname, me->me_mountdir, me->me_type,
379                   me->me_dummy, me->me_remote);
380         return;
381       }
382   /* No filesystem is mounted on DISK. */
383   show_dev (disk, (char *) NULL, (char *) NULL, 0, 0);
384 }
385
386 /* Return the root mountpoint of the filesystem on which FILE exists, in
387    malloced storage.  FILE_STAT should be the result of stating FILE.  */
388 static char *
389 find_mount_point (const char *file, const struct stat *file_stat)
390 {
391   struct saved_cwd cwd;
392   struct stat last_stat;
393   char *mp = 0;                 /* The malloced mount point path.  */
394
395   if (save_cwd (&cwd))
396     return NULL;
397
398   if (S_ISDIR (file_stat->st_mode))
399     /* FILE is a directory, so just chdir there directly.  */
400     {
401       last_stat = *file_stat;
402       if (chdir (file) < 0)
403         return NULL;
404     }
405   else
406     /* FILE is some other kind of file, we need to use its directory.  */
407     {
408       int rv;
409       char *tmp = xstrdup (file);
410       char *dir;
411
412       strip_trailing_slashes (tmp);
413       dir = dir_name (tmp);
414       free (tmp);
415       rv = chdir (dir);
416       free (dir);
417
418       if (rv < 0)
419         return NULL;
420
421       if (stat (".", &last_stat) < 0)
422         goto done;
423     }
424
425   /* Now walk up FILE's parents until we find another filesystem or /,
426      chdiring as we go.  LAST_STAT holds stat information for the last place
427      we visited.  */
428   for (;;)
429     {
430       struct stat st;
431       if (stat ("..", &st) < 0)
432         goto done;
433       if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
434         /* cwd is the mount point.  */
435         break;
436       if (chdir ("..") < 0)
437         goto done;
438       last_stat = st;
439     }
440
441   /* Finally reached a mount point, see what it's called.  */
442   mp = xgetcwd ();
443
444 done:
445   /* Restore the original cwd.  */
446   {
447     int save_errno = errno;
448     if (restore_cwd (&cwd, 0, mp))
449       exit (1);                 /* We're scrod.  */
450     free_cwd (&cwd);
451     errno = save_errno;
452   }
453
454   return mp;
455 }
456
457 /* Figure out which device file or directory POINT is mounted on
458    and show its disk usage.
459    STATP is the results of `stat' on POINT.  */
460 static void
461 show_point (const char *point, const struct stat *statp)
462 {
463   struct stat disk_stats;
464   struct mount_entry *me;
465   struct mount_entry *matching_dummy = NULL;
466
467   for (me = mount_list; me; me = me->me_next)
468     {
469       if (me->me_dev == (dev_t) -1)
470         {
471           if (stat (me->me_mountdir, &disk_stats) == 0)
472             me->me_dev = disk_stats.st_dev;
473           else
474             {
475               error (0, errno, "%s", me->me_mountdir);
476               exit_status = 1;
477               /* So we won't try and fail repeatedly. */
478               me->me_dev = (dev_t) -2;
479             }
480         }
481
482       if (statp->st_dev == me->me_dev)
483         {
484           /* Skip bogus mtab entries.  */
485           if (stat (me->me_mountdir, &disk_stats) != 0 ||
486               disk_stats.st_dev != me->me_dev)
487             continue;
488
489           /* Prefer non-dummy entries.  */
490           if (me->me_dummy)
491             {
492               matching_dummy = me;
493               continue;
494             }
495
496           show_dev (me->me_devname, me->me_mountdir, me->me_type,
497                     me->me_dummy, me->me_remote);
498           return;
499         }
500     }
501
502   if (matching_dummy)
503     {
504       show_dev (matching_dummy->me_devname, matching_dummy->me_mountdir,
505                 matching_dummy->me_type, 1, matching_dummy->me_remote);
506       return;
507     }
508
509   /* We couldn't find the mount entry corresponding to POINT.  Go ahead and
510      print as much info as we can; methods that require the device to be
511      present will fail at a later point.  */
512   {
513     /* Find the actual mount point.  */
514     char *mp = find_mount_point (point, statp);
515     if (mp)
516       {
517         show_dev (0, mp, 0, 0, 0);
518         free (mp);
519       }
520     else
521       error (0, errno, "%s", point);
522   }
523 }
524
525 /* Determine what kind of node PATH is and show the disk usage
526    for it.  STATP is the results of `stat' on PATH.  */
527
528 static void
529 show_entry (const char *path, const struct stat *statp)
530 {
531   if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
532     show_disk (path);
533   else
534     show_point (path, statp);
535 }
536
537 /* Show all mounted filesystems, except perhaps those that are of
538    an unselected type or are empty. */
539
540 static void
541 show_all_entries (void)
542 {
543   struct mount_entry *me;
544
545   for (me = mount_list; me; me = me->me_next)
546     show_dev (me->me_devname, me->me_mountdir, me->me_type,
547               me->me_dummy, me->me_remote);
548 }
549
550 /* Add FSTYPE to the list of filesystem types to display. */
551
552 static void
553 add_fs_type (const char *fstype)
554 {
555   struct fs_type_list *fsp;
556
557   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
558   fsp->fs_name = (char *) fstype;
559   fsp->fs_next = fs_select_list;
560   fs_select_list = fsp;
561 }
562
563 /* Add FSTYPE to the list of filesystem types to be omitted. */
564
565 static void
566 add_excluded_fs_type (const char *fstype)
567 {
568   struct fs_type_list *fsp;
569
570   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
571   fsp->fs_name = (char *) fstype;
572   fsp->fs_next = fs_exclude_list;
573   fs_exclude_list = fsp;
574 }
575
576 void
577 usage (int status)
578 {
579   if (status != 0)
580     fprintf (stderr, _("Try `%s --help' for more information.\n"),
581              program_name);
582   else
583     {
584       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
585       printf (_("\
586 Show information about the filesystem on which each FILE resides,\n\
587 or all filesystems by default.\n\
588 \n\
589   -a, --all             include filesystems having 0 blocks\n\
590       --block-size=SIZE use SIZE-byte blocks\n\
591   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
592   -H, --si              likewise, but use powers of 1000 not 1024\n\
593   -i, --inodes          list inode information instead of block usage\n\
594   -k, --kilobytes       like --block-size=1024\n\
595   -l, --local           limit listing to local filesystems\n\
596   -m, --megabytes       like --block-size=1048576\n\
597       --no-sync         do not invoke sync before getting usage info (default)\n\
598   -P, --portability     use the POSIX output format\n\
599       --sync            invoke sync before getting usage info\n\
600   -t, --type=TYPE       limit listing to filesystems of type TYPE\n\
601   -T, --print-type      print filesystem type\n\
602   -x, --exclude-type=TYPE   limit listing to filesystems not of type TYPE\n\
603   -v                    (ignored)\n\
604       --help            display this help and exit\n\
605       --version         output version information and exit\n\
606 "));
607       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
608       close_stdout ();
609     }
610   exit (status);
611 }
612
613 int
614 main (int argc, char **argv)
615 {
616   int c;
617   struct stat *stats;
618
619   program_name = argv[0];
620   setlocale (LC_ALL, "");
621   bindtextdomain (PACKAGE, LOCALEDIR);
622   textdomain (PACKAGE);
623
624   fs_select_list = NULL;
625   fs_exclude_list = NULL;
626   inode_format = 0;
627   show_all_fs = 0;
628   show_listed_fs = 0;
629
630   human_block_size (getenv ("DF_BLOCK_SIZE"), 0, &output_block_size);
631
632   print_type = 0;
633   posix_format = 0;
634   exit_status = 0;
635
636   while ((c = getopt_long (argc, argv, "aiF:hHklmPTt:vx:", long_options, NULL))
637          != -1)
638     {
639       switch (c)
640         {
641         case 0:                 /* Long option. */
642           break;
643         case 'a':
644           show_all_fs = 1;
645           break;
646         case 'i':
647           inode_format = 1;
648           break;
649         case 'h':
650           output_block_size = -1024;
651           break;
652         case 'H':
653           output_block_size = -1000;
654           break;
655         case 'k':
656           output_block_size = 1024;
657           break;
658         case 'l':
659           show_local_fs = 1;
660           break;
661         case 'm':
662           output_block_size = 1024 * 1024;
663           break;
664         case 'T':
665           print_type = 1;
666           break;
667         case 'P':
668           posix_format = 1;
669           break;
670         case CHAR_MAX + 1:
671           require_sync = 1;
672           break;
673         case CHAR_MAX + 2:
674           require_sync = 0;
675           break;
676
677         case CHAR_MAX + 3:
678           human_block_size (optarg, 1, &output_block_size);
679           break;
680
681         case 'F':
682           /* Accept -F as a synonym for -t for compatibility with Solaris.  */
683         case 't':
684           add_fs_type (optarg);
685           break;
686
687         case 'v':               /* For SysV compatibility. */
688           /* ignore */
689           break;
690         case 'x':
691           add_excluded_fs_type (optarg);
692           break;
693
694         case_GETOPT_HELP_CHAR;
695         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
696
697         default:
698           usage (1);
699         }
700     }
701
702   /* Fail if the same file system type was both selected and excluded.  */
703   {
704     int match = 0;
705     struct fs_type_list *i;
706     for (i = fs_select_list; i; i = i->fs_next)
707       {
708         struct fs_type_list *j;
709         for (j = fs_exclude_list; j; j = j->fs_next)
710           {
711             if (STREQ (i->fs_name, j->fs_name))
712               {
713                 error (0, 0,
714                        _("file system type `%s' both selected and excluded"),
715                        i->fs_name);
716                 match = 1;
717                 break;
718               }
719           }
720       }
721     if (match)
722       exit (1);
723   }
724
725   if (optind == argc)
726     {
727 #ifdef lint
728       /* Suppress `used before initialized' warning.  */
729       stats = NULL;
730 #endif
731     }
732   else
733     {
734       int i;
735
736       /* stat all the given entries to make sure they get automounted,
737          if necessary, before reading the filesystem table.  */
738       stats = (struct stat *)
739         xmalloc ((argc - optind) * sizeof (struct stat));
740       for (i = optind; i < argc; ++i)
741         if (stat (argv[i], &stats[i - optind]))
742           {
743             error (0, errno, "%s", argv[i]);
744             exit_status = 1;
745             argv[i] = NULL;
746           }
747     }
748
749   mount_list =
750     read_filesystem_list ((fs_select_list != NULL
751                            || fs_exclude_list != NULL
752                            || print_type
753                            || show_local_fs));
754
755   if (mount_list == NULL)
756     error (1, errno, _("cannot read table of mounted filesystems"));
757
758   if (require_sync)
759     sync ();
760
761   if (optind == argc)
762     {
763       print_header ();
764       show_all_entries ();
765     }
766   else
767     {
768       int i;
769
770       /* Display explicitly requested empty filesystems. */
771       show_listed_fs = 1;
772
773       print_header ();
774       for (i = optind; i < argc; ++i)
775         if (argv[i])
776           show_entry (argv[i], &stats[i - optind]);
777     }
778
779   close_stdout ();
780   exit (exit_status);
781 }