00b9e4053a2861f05759eac2dae9edd3ef10a667
[platform/upstream/coreutils.git] / src / df.c
1 /* df - summarize free disk space
2    Copyright (C) 91, 95, 1996 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
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <getopt.h>
25 #include <assert.h>
26
27 #include "mountlist.h"
28 #include "fsusage.h"
29 #include "system.h"
30 #include "error.h"
31
32 char *xmalloc ();
33 char *xstrdup ();
34
35 /* The maximum length of a human-readable string.  Be pessimistic
36    and assume `int' is 64-bits wide.  Converting 2^63 - 1 gives the
37    14-character string, 8796093022208G.  The number being converted
38    is the number of 1024-byte blocks, so we divide by 1024 * 1024.  */
39 #define LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS 14
40
41 /* Name this program was run with. */
42 char *program_name;
43
44 /* If nonzero, show inode information. */
45 static int inode_format;
46
47 /* If nonzero, show even filesystems with zero size or
48    uninteresting types. */
49 static int show_all_fs;
50
51 /* If nonzero, output data for each filesystem corresponding to a
52    command line argument -- even if it's a dummy (automounter) entry.  */
53 static int show_listed_fs;
54
55 /* If nonzero, use variable sized printouts instead of 512-byte blocks. */
56 static int human_blocks;
57
58 /* If nonzero, use 1K blocks instead of 512-byte blocks. */
59 static int kilobyte_blocks;
60
61 /* If nonzero, use 1M blocks instead of 512-byte blocks. */
62 static int megabyte_blocks;
63
64 /* If nonzero, use the POSIX output format.  */
65 static int posix_format;
66
67 /* If nonzero, invoke the `sync' system call before getting any usage data.
68    Using this option can make df very slow, especially with many or very
69    busy disks.  Note that this may make a difference on some systems --
70    SunOs4.1.3, for one.  It is *not* necessary on Linux.  */
71 static int require_sync = 0;
72
73 /* Nonzero if errors have occurred. */
74 static int exit_status;
75
76 /* A filesystem type to display. */
77
78 struct fs_type_list
79 {
80   char *fs_name;
81   struct fs_type_list *fs_next;
82 };
83
84 /* Linked list of filesystem types to display.
85    If `fs_select_list' is NULL, list all types.
86    This table is generated dynamically from command-line options,
87    rather than hardcoding into the program what it thinks are the
88    valid filesystem types; let the user specify any filesystem type
89    they want to, and if there are any filesystems of that type, they
90    will be shown.
91
92    Some filesystem types:
93    4.2 4.3 ufs nfs swap ignore io vm efs dbg */
94
95 static struct fs_type_list *fs_select_list;
96
97 /* Linked list of filesystem types to omit.
98    If the list is empty, don't exclude any types.  */
99
100 static struct fs_type_list *fs_exclude_list;
101
102 /* Linked list of mounted filesystems. */
103 static struct mount_entry *mount_list;
104
105 /* If nonzero, display usage information and exit.  */
106 static int show_help;
107
108 /* If nonzero, print the version on standard output and exit.  */
109 static int show_version;
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, &show_all_fs, 1},
117   {"inodes", no_argument, &inode_format, 1},
118   {"human-readable", no_argument, 0, 'h'},
119   {"kilobytes", no_argument, 0, 'k'},
120   {"megabytes", no_argument, 0, 'm'},
121   {"portability", no_argument, &posix_format, 1},
122   {"print-type", no_argument, &print_type, 1},
123   {"sync", no_argument, 0, 129},
124   {"no-sync", no_argument, 0, 130},
125   {"type", required_argument, 0, 't'},
126   {"exclude-type", required_argument, 0, 'x'},
127   {"help", no_argument, &show_help, 1},
128   {"version", no_argument, &show_version, 1},
129   {NULL, 0, NULL, 0}
130 };
131
132 static void
133 print_header (void)
134 {
135   printf ("Filesystem ");
136
137   if (print_type)
138     printf ("   Type");
139   else
140     printf ("       ");
141
142   if (inode_format)
143     printf ("   Inodes   IUsed   IFree  %%IUsed");
144   else
145     if (megabyte_blocks)
146       printf (" MB-blocks    Used Available Capacity");
147     else if (human_blocks)
148       printf ("    Size  Used  Avail  Capacity");
149     else
150       printf (" %s  Used Available Capacity",
151             kilobyte_blocks ? "1024-blocks" : " 512-blocks");
152   printf (" Mounted on\n");
153 }
154
155 /* Convert N_1K_BYTE_BLOCKS to a more readable string than %d would.
156    Most people visually process strings of 3-4 digits effectively,
157    but longer strings of digits are more prone to misinterpretation.
158    Hence, converting to an abbreviated form usually improves readability.
159    Use a suffix indicating multiples of 1024 (M) and 1024*1024 (G).
160    For example, 8500 would be converted to 8.3M, 133456345 to 127G,
161    and so on.  Numbers smaller than 1024 get the `K' suffix.  */
162
163 static char *
164 human_readable_1k_blocks (int n_1k_byte_blocks, char *buf, int buf_len)
165 {
166   const char *suffix;
167   double amt;
168   char *p;
169
170   assert (buf_len > LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS);
171
172   p = buf;
173   amt = n_1k_byte_blocks;
174
175   if (amt >= 1024 * 1024)
176     {
177       amt /= (1024 * 1024);
178       suffix = "G";
179     }
180   else if (amt >= 1024)
181     {
182       amt /= 1024;
183       suffix = "M";
184     }
185   else
186     {
187       suffix = "K";
188     }
189
190   if (amt >= 10)
191     {
192       sprintf (p, "%4.0f%s", amt, suffix);
193     }
194   else if (amt == 0)
195     {
196       strcpy (p, "0");
197     }
198   else
199     {
200       sprintf (p, "%4.1f%s", amt, suffix);
201     }
202   return (p);
203 }
204
205 /* If FSTYPE is a type of filesystem that should be listed,
206    return nonzero, else zero. */
207
208 static int
209 selected_fstype (const char *fstype)
210 {
211   const struct fs_type_list *fsp;
212
213   if (fs_select_list == NULL || fstype == NULL)
214     return 1;
215   for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
216     if (STREQ (fstype, fsp->fs_name))
217       return 1;
218   return 0;
219 }
220
221 /* If FSTYPE is a type of filesystem that should be omitted,
222    return nonzero, else zero. */
223
224 static int
225 excluded_fstype (const char *fstype)
226 {
227   const struct fs_type_list *fsp;
228
229   if (fs_exclude_list == NULL || fstype == NULL)
230     return 0;
231   for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
232     if (STREQ (fstype, fsp->fs_name))
233       return 1;
234   return 0;
235 }
236
237 /* Display a space listing for the disk device with absolute path DISK.
238    If MOUNT_POINT is non-NULL, it is the path of the root of the
239    filesystem on DISK.
240    If FSTYPE is non-NULL, it is the type of the filesystem on DISK. */
241
242 static void
243 show_dev (const char *disk, const char *mount_point, const char *fstype)
244 {
245   struct fs_usage fsu;
246   long blocks_used;
247   long blocks_percent_used;
248   long inodes_used;
249   long inodes_percent_used;
250   const char *stat_file;
251
252   if (!selected_fstype (fstype) || excluded_fstype (fstype))
253     return;
254
255   /* If MOUNT_POINT is NULL, then the filesystem is not mounted, and this
256      program reports on the filesystem that the special file is on.
257      It would be better to report on the unmounted filesystem,
258      but statfs doesn't do that on most systems.  */
259   stat_file = mount_point ? mount_point : disk;
260
261   if (get_fs_usage (stat_file, disk, &fsu))
262     {
263       error (0, errno, "%s", stat_file);
264       exit_status = 1;
265       return;
266     }
267
268   if (megabyte_blocks)
269     {
270       fsu.fsu_blocks /= 2*1024;
271       fsu.fsu_bfree /= 2*1024;
272       fsu.fsu_bavail /= 2*1024;
273     }
274   else if (kilobyte_blocks)
275     {
276       fsu.fsu_blocks /= 2;
277       fsu.fsu_bfree /= 2;
278       fsu.fsu_bavail /= 2;
279     }
280
281   if (fsu.fsu_blocks == 0)
282     {
283       if (!show_all_fs && !show_listed_fs)
284         return;
285       blocks_used = fsu.fsu_bavail = blocks_percent_used = 0;
286     }
287   else
288     {
289       blocks_used = fsu.fsu_blocks - fsu.fsu_bfree;
290       blocks_percent_used = (long)
291         (blocks_used * 100.0 / (blocks_used + fsu.fsu_bavail) + 0.5);
292     }
293
294   if (fsu.fsu_files == 0)
295     {
296       inodes_used = fsu.fsu_ffree = inodes_percent_used = 0;
297     }
298   else
299     {
300       inodes_used = fsu.fsu_files - fsu.fsu_ffree;
301       inodes_percent_used = (long)
302         (inodes_used * 100.0 / fsu.fsu_files + 0.5);
303     }
304
305   printf ((print_type ? "%-13s" : "%-20s"), disk);
306   if (strlen (disk) > (print_type ? 13 : 20) && !posix_format)
307     printf ((print_type ? "\n%13s" : "\n%20s"), "");
308
309   if (print_type)
310     printf (" %-5s ", fstype);
311
312   if (inode_format)
313     printf (" %7ld %7ld %7ld %5ld%%",
314             fsu.fsu_files, inodes_used, fsu.fsu_ffree, inodes_percent_used);
315   else if (human_blocks)
316     {
317       char buf[3][LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1];
318       printf (" %4s %4s  %5s  %5ld%% ",
319               human_readable_1k_blocks (fsu.fsu_blocks, buf[0],
320                                     LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
321               human_readable_1k_blocks (blocks_used, buf[1],
322                                     LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
323               human_readable_1k_blocks (fsu.fsu_bavail, buf[2],
324                                     LONGEST_HUMAN_READABLE_1K_BYTE_BLOCKS + 1),
325               blocks_percent_used);
326     }
327   else
328     printf (" %7ld %7ld  %7ld  %5ld%% ",
329             fsu.fsu_blocks, blocks_used, fsu.fsu_bavail, blocks_percent_used);
330
331   if (mount_point)
332     {
333 #ifdef HIDE_AUTOMOUNT_PREFIX
334       /* Don't print the first directory name in MOUNT_POINT if it's an
335          artifact of an automounter.  This is a bit too aggressive to be
336          the default.  */
337       if (strncmp ("/auto/", mount_point, 6) == 0)
338         mount_point += 5;
339       else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
340         mount_point += 8;
341 #endif
342       printf ("  %s", mount_point);
343     }
344   putchar ('\n');
345 }
346
347 /* Identify the directory, if any, that device
348    DISK is mounted on, and show its disk usage.  */
349
350 static void
351 show_disk (const char *disk)
352 {
353   struct mount_entry *me;
354
355   for (me = mount_list; me; me = me->me_next)
356     if (STREQ (disk, me->me_devname))
357       {
358         show_dev (me->me_devname, me->me_mountdir, me->me_type);
359         return;
360       }
361   /* No filesystem is mounted on DISK. */
362   show_dev (disk, (char *) NULL, (char *) NULL);
363 }
364
365 /* Figure out which device file or directory POINT is mounted on
366    and show its disk usage.
367    STATP is the results of `stat' on POINT.  */
368
369 static void
370 show_point (const char *point, const struct stat *statp)
371 {
372   struct stat disk_stats;
373   struct mount_entry *me;
374
375   for (me = mount_list; me; me = me->me_next)
376     {
377       if (me->me_dev == (dev_t) -1)
378         {
379           if (stat (me->me_mountdir, &disk_stats) == 0)
380             me->me_dev = disk_stats.st_dev;
381           else
382             {
383               error (0, errno, "%s", me->me_mountdir);
384               exit_status = 1;
385               /* So we won't try and fail repeatedly. */
386               me->me_dev = (dev_t) -2;
387             }
388         }
389
390       if (statp->st_dev == me->me_dev)
391         {
392           /* Skip bogus mtab entries.  */
393           if (stat (me->me_mountdir, &disk_stats) != 0 ||
394               disk_stats.st_dev != me->me_dev)
395             continue;
396           show_dev (me->me_devname, me->me_mountdir, me->me_type);
397           return;
398         }
399     }
400   error (0, 0, _("cannot find mount point for %s"), point);
401   exit_status = 1;
402 }
403
404 /* Determine what kind of node PATH is and show the disk usage
405    for it.  STATP is the results of `stat' on PATH.  */
406
407 static void
408 show_entry (const char *path, const struct stat *statp)
409 {
410   if (S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
411     show_disk (path);
412   else
413     show_point (path, statp);
414 }
415
416 /* Show all mounted filesystems, except perhaps those that are of
417    an unselected type or are empty. */
418
419 static void
420 show_all_entries (void)
421 {
422   struct mount_entry *me;
423
424   for (me = mount_list; me; me = me->me_next)
425     show_dev (me->me_devname, me->me_mountdir, me->me_type);
426 }
427
428 /* Add FSTYPE to the list of filesystem types to display. */
429
430 static void
431 add_fs_type (const char *fstype)
432 {
433   struct fs_type_list *fsp;
434
435   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
436   fsp->fs_name = (char *) fstype;
437   fsp->fs_next = fs_select_list;
438   fs_select_list = fsp;
439 }
440
441 /* Add FSTYPE to the list of filesystem types to be omitted. */
442
443 static void
444 add_excluded_fs_type (const char *fstype)
445 {
446   struct fs_type_list *fsp;
447
448   fsp = (struct fs_type_list *) xmalloc (sizeof (struct fs_type_list));
449   fsp->fs_name = (char *) fstype;
450   fsp->fs_next = fs_exclude_list;
451   fs_exclude_list = fsp;
452 }
453
454 static void
455 usage (int status)
456 {
457   if (status != 0)
458     fprintf (stderr, _("Try `%s --help' for more information.\n"),
459              program_name);
460   else
461     {
462       printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
463       printf (_("\
464 Show information about the filesystem on which each FILE resides,\n\
465 or all filesystems by default.\n\
466 \n\
467   -a, --all             include filesystems having 0 blocks\n\
468   -h, --human-readable  print sizes in human readable format (e.g. 1K 234M 2G)\n\
469   -i, --inodes          list inode information instead of block usage\n\
470   -k, --kilobytes       use 1024-byte blocks, not 512 despite POSIXLY_CORRECT\n\
471   -m, --megabytes       use 1024K-byte blocks, not 512 despite POSIXLY_CORRECT\n\
472       --no-sync         do not invoke sync before getting usage info (default)\n\
473       --sync            invoke sync before getting usage info\n\
474   -t, --type=TYPE       limit listing to filesystems of type TYPE\n\
475   -x, --exclude-type=TYPE   limit listing to filesystems not of type TYPE\n\
476   -v                    (ignored)\n\
477   -P, --portability     use the POSIX output format\n\
478   -T, --print-type      print filesystem type\n\
479       --help            display this help and exit\n\
480       --version         output version information and exit\n\
481 "));
482       puts (_("\nReport bugs to bug-gnu-utils@gnu.ai.mit.edu"));
483     }
484   exit (status);
485 }
486
487 int
488 main (int argc, char **argv)
489 {
490   int i;
491   struct stat *stats;
492
493   program_name = argv[0];
494   setlocale (LC_ALL, "");
495   bindtextdomain (PACKAGE, LOCALEDIR);
496   textdomain (PACKAGE);
497
498   fs_select_list = NULL;
499   fs_exclude_list = NULL;
500   inode_format = 0;
501   show_all_fs = 0;
502   show_listed_fs = 0;
503
504   if (getenv ("POSIXLY_CORRECT"))
505     kilobyte_blocks = 0;
506   else
507     {
508       char *bs;
509       kilobyte_blocks = 1;
510       if ((bs = getenv ("BLOCKSIZE"))
511           && strncmp (bs, "HUMAN", sizeof ("HUMAN") - 1) == 0)
512         human_blocks = 1;
513     }
514
515   print_type = 0;
516   posix_format = 0;
517   exit_status = 0;
518
519   while ((i = getopt_long (argc, argv, "aihkmPTt:vx:", long_options, NULL))
520          != EOF)
521     {
522       switch (i)
523         {
524         case 0:                 /* Long option. */
525           break;
526         case 'a':
527           show_all_fs = 1;
528           break;
529         case 'i':
530           inode_format = 1;
531           break;
532         case 'h':
533           human_blocks = 1;
534           kilobyte_blocks = 1;
535           megabyte_blocks = 0;
536           break;
537         case 'k':
538           human_blocks = 0;
539           kilobyte_blocks = 1;
540           megabyte_blocks = 0;
541           break;
542         case 'm':
543           human_blocks = 0;
544           kilobyte_blocks = 0;
545           megabyte_blocks = 1;
546           break;
547         case 'T':
548           print_type = 1;
549           break;
550         case 'P':
551           posix_format = 1;
552           break;
553         case 129:
554           require_sync = 1;
555           break;
556         case 130:
557           require_sync = 0;
558           break;
559         case 't':
560           add_fs_type (optarg);
561           break;
562         case 'v':               /* For SysV compatibility. */
563           /* ignore */
564           break;
565         case 'x':
566           add_excluded_fs_type (optarg);
567           break;
568         default:
569           usage (1);
570         }
571     }
572
573   if (show_version)
574     {
575       printf ("df - %s\n", PACKAGE_VERSION);
576       exit (0);
577     }
578
579   if (show_help)
580     usage (0);
581
582   if (posix_format && megabyte_blocks)
583     error (1, 0, _("the option for counting 1MB blocks may not be used\n\
584 with the portable output format"));
585
586   if (posix_format && human_blocks)
587     error (1, 0,
588            _("the option for printing with adaptive units may not be used\n\
589 with the portable output format"));
590
591   /* Fail if the same file system type was both selected and excluded.  */
592   {
593     int match = 0;
594     struct fs_type_list *i;
595     for (i = fs_select_list; i; i = i->fs_next)
596       {
597         struct fs_type_list *j;
598         for (j = fs_exclude_list; j; j = j->fs_next)
599           {
600             if (STREQ (i->fs_name, j->fs_name))
601               {
602                 error (0, 0,
603                        _("file system type `%s' both selected and excluded"),
604                        i->fs_name);
605                 match = 1;
606                 break;
607               }
608           }
609       }
610     if (match)
611       exit (1);
612   }
613
614   if (optind == argc)
615     {
616 #ifdef lint
617       /* Suppress `used before initialized' warning.  */
618       stats = NULL;
619 #endif
620     }
621   else
622     {
623       /* stat all the given entries to make sure they get automounted,
624          if necessary, before reading the filesystem table.  */
625       stats = (struct stat *)
626         xmalloc ((argc - optind) * sizeof (struct stat));
627       for (i = optind; i < argc; ++i)
628         if (stat (argv[i], &stats[i - optind]))
629           {
630             error (0, errno, "%s", argv[i]);
631             exit_status = 1;
632             argv[i] = NULL;
633           }
634     }
635
636   mount_list =
637     read_filesystem_list ((fs_select_list != NULL
638                            || fs_exclude_list != NULL
639                            || print_type),
640                           show_all_fs);
641
642   if (mount_list == NULL)
643     error (1, errno, _("cannot read table of mounted filesystems"));
644
645   print_header ();
646   if (require_sync)
647     sync ();
648
649   if (optind == argc)
650     show_all_entries ();
651   else
652     {
653       /* Display explicitly requested empty filesystems. */
654       show_listed_fs = 1;
655
656       for (i = optind; i < argc; ++i)
657         if (argv[i])
658           show_entry (argv[i], &stats[i - optind]);
659     }
660
661   exit (exit_status);
662 }
663