Line data Source code
1 : /* df - summarize free disk space
2 : Copyright (C) 91, 1995-2007 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 3 of the License, or
7 : (at your option) 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, see <http://www.gnu.org/licenses/>. */
16 :
17 : /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
18 : --human-readable and --megabyte options added by lm@sgi.com.
19 : --si and large file support added by eggert@twinsun.com. */
20 :
21 : #include <config.h>
22 : #include <stdio.h>
23 : #include <sys/types.h>
24 : #include <getopt.h>
25 :
26 : #include "system.h"
27 : #include "canonicalize.h"
28 : #include "error.h"
29 : #include "fsusage.h"
30 : #include "human.h"
31 : #include "inttostr.h"
32 : #include "mountlist.h"
33 : #include "quote.h"
34 : #include "save-cwd.h"
35 : #include "xgetcwd.h"
36 :
37 : /* The official name of this program (e.g., no `g' prefix). */
38 : #define PROGRAM_NAME "df"
39 :
40 : #define AUTHORS \
41 : "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
42 :
43 : /* Name this program was run with. */
44 : char *program_name;
45 :
46 : /* If true, show inode information. */
47 : static bool inode_format;
48 :
49 : /* If true, show even file systems with zero size or
50 : uninteresting types. */
51 : static bool show_all_fs;
52 :
53 : /* If true, show only local file systems. */
54 : static bool show_local_fs;
55 :
56 : /* If true, output data for each file system corresponding to a
57 : command line argument -- even if it's a dummy (automounter) entry. */
58 : static bool show_listed_fs;
59 :
60 : /* Human-readable options for output. */
61 : static int human_output_opts;
62 :
63 : /* The units to use when printing sizes. */
64 : static uintmax_t output_block_size;
65 :
66 : /* If true, use the POSIX output format. */
67 : static bool posix_format;
68 :
69 : /* True if a file system has been processed for output. */
70 : static bool file_systems_processed;
71 :
72 : /* If true, invoke the `sync' system call before getting any usage data.
73 : Using this option can make df very slow, especially with many or very
74 : busy disks. Note that this may make a difference on some systems --
75 : SunOS 4.1.3, for one. It is *not* necessary on Linux. */
76 : static bool require_sync;
77 :
78 : /* Desired exit status. */
79 : static int exit_status;
80 :
81 : /* A file system type to display. */
82 :
83 : struct fs_type_list
84 : {
85 : char *fs_name;
86 : struct fs_type_list *fs_next;
87 : };
88 :
89 : /* Linked list of file system types to display.
90 : If `fs_select_list' is NULL, list all types.
91 : This table is generated dynamically from command-line options,
92 : rather than hardcoding into the program what it thinks are the
93 : valid file system types; let the user specify any file system type
94 : they want to, and if there are any file systems of that type, they
95 : will be shown.
96 :
97 : Some file system types:
98 : 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
99 :
100 : static struct fs_type_list *fs_select_list;
101 :
102 : /* Linked list of file system types to omit.
103 : If the list is empty, don't exclude any types. */
104 :
105 : static struct fs_type_list *fs_exclude_list;
106 :
107 : /* Linked list of mounted file systems. */
108 : static struct mount_entry *mount_list;
109 :
110 : /* If true, print file system type as well. */
111 : static bool print_type;
112 :
113 : /* For long options that have no equivalent short option, use a
114 : non-character as a pseudo short option, starting with CHAR_MAX + 1. */
115 : enum
116 : {
117 : NO_SYNC_OPTION = CHAR_MAX + 1,
118 : SYNC_OPTION
119 : };
120 :
121 : static struct option const long_options[] =
122 : {
123 : {"all", no_argument, NULL, 'a'},
124 : {"block-size", required_argument, NULL, 'B'},
125 : {"inodes", no_argument, NULL, 'i'},
126 : {"human-readable", no_argument, NULL, 'h'},
127 : {"si", no_argument, NULL, 'H'},
128 : {"local", no_argument, NULL, 'l'},
129 : {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
130 : {"portability", no_argument, NULL, 'P'},
131 : {"print-type", no_argument, NULL, 'T'},
132 : {"sync", no_argument, NULL, SYNC_OPTION},
133 : {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
134 : {"type", required_argument, NULL, 't'},
135 : {"exclude-type", required_argument, NULL, 'x'},
136 : {GETOPT_HELP_OPTION_DECL},
137 : {GETOPT_VERSION_OPTION_DECL},
138 : {NULL, 0, NULL, 0}
139 : };
140 :
141 : static void
142 39 : print_header (void)
143 : {
144 : char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
145 :
146 39 : if (print_type)
147 2 : fputs (_("Filesystem Type"), stdout);
148 : else
149 37 : fputs (_("Filesystem "), stdout);
150 :
151 39 : if (inode_format)
152 2 : printf (_(" Inodes IUsed IFree IUse%%"));
153 37 : else if (human_output_opts & human_autoscale)
154 : {
155 7 : if (human_output_opts & human_base_1024)
156 4 : printf (_(" Size Used Avail Use%%"));
157 : else
158 3 : printf (_(" Size Used Avail Use%%"));
159 : }
160 30 : else if (posix_format)
161 2 : printf (_(" %s-blocks Used Available Capacity"),
162 : umaxtostr (output_block_size, buf));
163 : else
164 : {
165 28 : int opts = (human_suppress_point_zero
166 : | human_autoscale | human_SI
167 : | (human_output_opts
168 28 : & (human_group_digits | human_base_1024 | human_B)));
169 :
170 : /* Prefer the base that makes the human-readable value more exact,
171 : if there is a difference. */
172 :
173 28 : uintmax_t q1000 = output_block_size;
174 28 : uintmax_t q1024 = output_block_size;
175 : bool divisible_by_1000;
176 : bool divisible_by_1024;
177 :
178 : do
179 : {
180 30 : divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
181 30 : divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
182 : }
183 30 : while (divisible_by_1000 & divisible_by_1024);
184 :
185 28 : if (divisible_by_1000 < divisible_by_1024)
186 21 : opts |= human_base_1024;
187 28 : if (divisible_by_1024 < divisible_by_1000)
188 3 : opts &= ~human_base_1024;
189 28 : if (! (opts & human_base_1024))
190 7 : opts |= human_B;
191 :
192 28 : printf (_(" %4s-blocks Used Available Use%%"),
193 : human_readable (output_block_size, buf, opts, 1, 1));
194 : }
195 :
196 39 : printf (_(" Mounted on\n"));
197 39 : }
198 :
199 : /* Is FSTYPE a type of file system that should be listed? */
200 :
201 : static bool
202 846 : selected_fstype (const char *fstype)
203 : {
204 : const struct fs_type_list *fsp;
205 :
206 846 : if (fs_select_list == NULL || fstype == NULL)
207 758 : return true;
208 209 : for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
209 122 : if (STREQ (fstype, fsp->fs_name))
210 1 : return true;
211 87 : return false;
212 : }
213 :
214 : /* Is FSTYPE a type of file system that should be omitted? */
215 :
216 : static bool
217 759 : excluded_fstype (const char *fstype)
218 : {
219 : const struct fs_type_list *fsp;
220 :
221 759 : if (fs_exclude_list == NULL || fstype == NULL)
222 705 : return false;
223 107 : for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
224 54 : if (STREQ (fstype, fsp->fs_name))
225 1 : return true;
226 53 : return false;
227 : }
228 :
229 : /* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
230 : except:
231 :
232 : - If NEGATIVE, then N represents a negative number,
233 : expressed in two's complement.
234 : - Otherwise, return "-" if N is UINTMAX_MAX. */
235 :
236 : static char const *
237 1149 : df_readable (bool negative, uintmax_t n, char *buf,
238 : uintmax_t input_units, uintmax_t output_units)
239 : {
240 1149 : if (n == UINTMAX_MAX && !negative)
241 0 : return "-";
242 : else
243 : {
244 1149 : char *p = human_readable (negative ? -n : n, buf + negative,
245 : human_output_opts, input_units, output_units);
246 1149 : if (negative)
247 0 : *--p = '-';
248 1149 : return p;
249 : }
250 : }
251 :
252 : /* Display a space listing for the disk device with absolute file name DISK.
253 : If MOUNT_POINT is non-NULL, it is the name of the root of the
254 : file system on DISK.
255 : If STAT_FILE is non-null, it is the name of a file within the file
256 : system that the user originally asked for; this provides better
257 : diagnostics, and sometimes it provides better results on networked
258 : file systems that give different free-space results depending on
259 : where in the file system you probe.
260 : If FSTYPE is non-NULL, it is the type of the file system on DISK.
261 : If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
262 : not be able to produce statistics in this case.
263 : ME_DUMMY and ME_REMOTE are the mount entry flags. */
264 :
265 : static void
266 1035 : show_dev (char const *disk, char const *mount_point,
267 : char const *stat_file, char const *fstype,
268 : bool me_dummy, bool me_remote)
269 : {
270 : struct fs_usage fsu;
271 : char buf[3][LONGEST_HUMAN_READABLE + 2];
272 : int width;
273 1035 : int col1_adjustment = 0;
274 : int use_width;
275 : uintmax_t input_units;
276 : uintmax_t output_units;
277 : uintmax_t total;
278 : uintmax_t available;
279 : bool negate_available;
280 : uintmax_t available_to_root;
281 : uintmax_t used;
282 : bool negate_used;
283 1035 : double pct = -1;
284 :
285 1035 : if (me_remote & show_local_fs)
286 652 : return;
287 :
288 1035 : if (me_dummy & !show_all_fs & !show_listed_fs)
289 189 : return;
290 :
291 846 : if (!selected_fstype (fstype) || excluded_fstype (fstype))
292 88 : return;
293 :
294 : /* If MOUNT_POINT is NULL, then the file system is not mounted, and this
295 : program reports on the file system that the special file is on.
296 : It would be better to report on the unmounted file system,
297 : but statfs doesn't do that on most systems. */
298 758 : if (!stat_file)
299 743 : stat_file = mount_point ? mount_point : disk;
300 :
301 758 : if (get_fs_usage (stat_file, disk, &fsu))
302 : {
303 0 : error (0, errno, "%s", quote (stat_file));
304 0 : exit_status = EXIT_FAILURE;
305 0 : return;
306 : }
307 :
308 758 : if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
309 375 : return;
310 :
311 383 : if (! file_systems_processed)
312 : {
313 39 : file_systems_processed = true;
314 39 : print_header ();
315 : }
316 :
317 383 : if (! disk)
318 0 : disk = "-"; /* unknown */
319 383 : if (! fstype)
320 0 : fstype = "-"; /* unknown */
321 :
322 : /* df.c reserved 5 positions for fstype,
323 : but that does not suffice for type iso9660 */
324 383 : if (print_type)
325 : {
326 24 : size_t disk_name_len = strlen (disk);
327 24 : size_t fstype_len = strlen (fstype);
328 24 : if (disk_name_len + fstype_len < 18)
329 18 : printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
330 6 : else if (!posix_format)
331 3 : printf ("%s\n%18s ", disk, fstype);
332 : else
333 3 : printf ("%s %s", disk, fstype);
334 : }
335 : else
336 : {
337 359 : if (strlen (disk) > 20 && !posix_format)
338 0 : printf ("%s\n%20s", disk, "");
339 : else
340 359 : printf ("%-20s", disk);
341 : }
342 :
343 383 : if (inode_format)
344 : {
345 24 : width = 7;
346 24 : use_width = 5;
347 24 : input_units = output_units = 1;
348 24 : total = fsu.fsu_files;
349 24 : available = fsu.fsu_ffree;
350 24 : negate_available = false;
351 24 : available_to_root = available;
352 : }
353 : else
354 : {
355 359 : if (human_output_opts & human_autoscale)
356 52 : width = 5 + ! (human_output_opts & human_base_1024);
357 : else
358 : {
359 307 : width = 9;
360 307 : if (posix_format)
361 : {
362 : uintmax_t b;
363 24 : col1_adjustment = -3;
364 96 : for (b = output_block_size; 9 < b; b /= 10)
365 72 : col1_adjustment++;
366 : }
367 : }
368 718 : use_width = ((posix_format
369 24 : && ! (human_output_opts & human_autoscale))
370 383 : ? 8 : 4);
371 359 : input_units = fsu.fsu_blocksize;
372 359 : output_units = output_block_size;
373 359 : total = fsu.fsu_blocks;
374 359 : available = fsu.fsu_bavail;
375 718 : negate_available = (fsu.fsu_bavail_top_bit_set
376 359 : & (available != UINTMAX_MAX));
377 359 : available_to_root = fsu.fsu_bfree;
378 : }
379 :
380 383 : used = UINTMAX_MAX;
381 383 : negate_used = false;
382 383 : if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
383 : {
384 383 : used = total - available_to_root;
385 383 : negate_used = (total < available_to_root);
386 : }
387 :
388 383 : printf (" %*s %*s %*s ",
389 : width + col1_adjustment,
390 : df_readable (false, total,
391 : buf[0], input_units, output_units),
392 : width, df_readable (negate_used, used,
393 : buf[1], input_units, output_units),
394 : width, df_readable (negate_available, available,
395 : buf[2], input_units, output_units));
396 :
397 383 : if (used == UINTMAX_MAX || available == UINTMAX_MAX)
398 : ;
399 383 : else if (!negate_used
400 383 : && used <= TYPE_MAXIMUM (uintmax_t) / 100
401 383 : && used + available != 0
402 339 : && (used + available < used) == negate_available)
403 339 : {
404 339 : uintmax_t u100 = used * 100;
405 339 : uintmax_t nonroot_total = used + available;
406 339 : pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
407 : }
408 : else
409 : {
410 : /* The calculation cannot be done easily with integer
411 : arithmetic. Fall back on floating point. This can suffer
412 : from minor rounding errors, but doing it exactly requires
413 : multiple precision arithmetic, and it's not worth the
414 : aggravation. */
415 44 : double u = negate_used ? - (double) - used : used;
416 44 : double a = negate_available ? - (double) - available : available;
417 44 : double nonroot_total = u + a;
418 44 : if (nonroot_total)
419 : {
420 0 : long int lipct = pct = u * 100 / nonroot_total;
421 0 : double ipct = lipct;
422 :
423 : /* Like `pct = ceil (dpct);', but avoid ceil so that
424 : the math library needn't be linked. */
425 0 : if (ipct - 1 < pct && pct <= ipct + 1)
426 0 : pct = ipct + (ipct < pct);
427 : }
428 : }
429 :
430 383 : if (0 <= pct)
431 339 : printf ("%*.0f%%", use_width - 1, pct);
432 : else
433 44 : printf ("%*s", use_width, "- ");
434 :
435 383 : if (mount_point)
436 : {
437 : #ifdef HIDE_AUTOMOUNT_PREFIX
438 : /* Don't print the first directory name in MOUNT_POINT if it's an
439 : artifact of an automounter. This is a bit too aggressive to be
440 : the default. */
441 : if (strncmp ("/auto/", mount_point, 6) == 0)
442 : mount_point += 5;
443 : else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
444 : mount_point += 8;
445 : #endif
446 383 : printf (" %s", mount_point);
447 : }
448 383 : putchar ('\n');
449 : }
450 :
451 : /* Return the root mountpoint of the file system on which FILE exists, in
452 : malloced storage. FILE_STAT should be the result of stating FILE.
453 : Give a diagnostic and return NULL if unable to determine the mount point.
454 : Exit if unable to restore current working directory. */
455 : static char *
456 0 : find_mount_point (const char *file, const struct stat *file_stat)
457 : {
458 : struct saved_cwd cwd;
459 : struct stat last_stat;
460 0 : char *mp = NULL; /* The malloced mount point. */
461 :
462 0 : if (save_cwd (&cwd) != 0)
463 : {
464 0 : error (0, errno, _("cannot get current directory"));
465 0 : return NULL;
466 : }
467 :
468 0 : if (S_ISDIR (file_stat->st_mode))
469 : /* FILE is a directory, so just chdir there directly. */
470 : {
471 0 : last_stat = *file_stat;
472 0 : if (chdir (file) < 0)
473 : {
474 0 : error (0, errno, _("cannot change to directory %s"), quote (file));
475 0 : return NULL;
476 : }
477 : }
478 : else
479 : /* FILE is some other kind of file; use its directory. */
480 : {
481 0 : char *xdir = dir_name (file);
482 : char *dir;
483 0 : ASSIGN_STRDUPA (dir, xdir);
484 0 : free (xdir);
485 :
486 0 : if (chdir (dir) < 0)
487 : {
488 0 : error (0, errno, _("cannot change to directory %s"), quote (dir));
489 0 : return NULL;
490 : }
491 :
492 0 : if (stat (".", &last_stat) < 0)
493 : {
494 0 : error (0, errno, _("cannot stat current directory (now %s)"),
495 : quote (dir));
496 0 : goto done;
497 : }
498 : }
499 :
500 : /* Now walk up FILE's parents until we find another file system or /,
501 : chdiring as we go. LAST_STAT holds stat information for the last place
502 : we visited. */
503 : for (;;)
504 0 : {
505 : struct stat st;
506 0 : if (stat ("..", &st) < 0)
507 : {
508 0 : error (0, errno, _("cannot stat %s"), quote (".."));
509 0 : goto done;
510 : }
511 0 : if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
512 : /* cwd is the mount point. */
513 : break;
514 0 : if (chdir ("..") < 0)
515 : {
516 0 : error (0, errno, _("cannot change to directory %s"), quote (".."));
517 0 : goto done;
518 : }
519 0 : last_stat = st;
520 : }
521 :
522 : /* Finally reached a mount point, see what it's called. */
523 0 : mp = xgetcwd ();
524 :
525 0 : done:
526 : /* Restore the original cwd. */
527 : {
528 0 : int save_errno = errno;
529 0 : if (restore_cwd (&cwd) != 0)
530 0 : error (EXIT_FAILURE, errno,
531 : _("failed to return to initial working directory"));
532 0 : free_cwd (&cwd);
533 0 : errno = save_errno;
534 : }
535 :
536 0 : return mp;
537 : }
538 :
539 : /* If DISK corresponds to a mount point, show its usage
540 : and return true. Otherwise, return false. */
541 : static bool
542 0 : show_disk (char const *disk)
543 : {
544 : struct mount_entry const *me;
545 0 : struct mount_entry const *best_match = NULL;
546 :
547 0 : for (me = mount_list; me; me = me->me_next)
548 0 : if (STREQ (disk, me->me_devname))
549 0 : best_match = me;
550 :
551 0 : if (best_match)
552 : {
553 0 : show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
554 0 : best_match->me_type, best_match->me_dummy,
555 0 : best_match->me_remote);
556 0 : return true;
557 : }
558 :
559 0 : return false;
560 : }
561 :
562 : /* Figure out which device file or directory POINT is mounted on
563 : and show its disk usage.
564 : STATP must be the result of `stat (POINT, STATP)'. */
565 : static void
566 15 : show_point (const char *point, const struct stat *statp)
567 : {
568 : struct stat disk_stats;
569 : struct mount_entry *me;
570 15 : struct mount_entry const *best_match = NULL;
571 :
572 : /* If POINT is an absolute file name, see if we can find the
573 : mount point without performing any extra stat calls at all. */
574 15 : if (*point == '/')
575 : {
576 : /* Find the best match: prefer non-dummies, and then prefer the
577 : last match if there are ties. */
578 :
579 70 : for (me = mount_list; me; me = me->me_next)
580 68 : if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
581 1 : && (!best_match || best_match->me_dummy || !me->me_dummy))
582 1 : best_match = me;
583 : }
584 :
585 : /* Calculate the real absolute file name for POINT, and use that to find
586 : the mount point. This avoids statting unavailable mount points,
587 : which can hang df. */
588 15 : if (! best_match)
589 : {
590 14 : char *resolved = canonicalize_file_name (point);
591 :
592 14 : if (resolved && resolved[0] == '/')
593 : {
594 14 : size_t resolved_len = strlen (resolved);
595 14 : size_t best_match_len = 0;
596 :
597 490 : for (me = mount_list; me; me = me->me_next)
598 476 : if (!STREQ (me->me_type, "lofs")
599 476 : && (!best_match || best_match->me_dummy || !me->me_dummy))
600 : {
601 378 : size_t len = strlen (me->me_mountdir);
602 378 : if (best_match_len <= len && len <= resolved_len
603 352 : && (len == 1 /* root file system */
604 338 : || ((len == resolved_len || resolved[len] == '/')
605 26 : && strncmp (me->me_mountdir, resolved, len) == 0)))
606 : {
607 14 : best_match = me;
608 14 : best_match_len = len;
609 : }
610 : }
611 : }
612 :
613 14 : free (resolved);
614 :
615 14 : if (best_match
616 14 : && (stat (best_match->me_mountdir, &disk_stats) != 0
617 14 : || disk_stats.st_dev != statp->st_dev))
618 0 : best_match = NULL;
619 : }
620 :
621 15 : if (! best_match)
622 0 : for (me = mount_list; me; me = me->me_next)
623 : {
624 0 : if (me->me_dev == (dev_t) -1)
625 : {
626 0 : if (stat (me->me_mountdir, &disk_stats) == 0)
627 0 : me->me_dev = disk_stats.st_dev;
628 : else
629 : {
630 : /* Report only I/O errors. Other errors might be
631 : caused by shadowed mount points, which means POINT
632 : can't possibly be on this file system. */
633 0 : if (errno == EIO)
634 : {
635 0 : error (0, errno, "%s", quote (me->me_mountdir));
636 0 : exit_status = EXIT_FAILURE;
637 : }
638 :
639 : /* So we won't try and fail repeatedly. */
640 0 : me->me_dev = (dev_t) -2;
641 : }
642 : }
643 :
644 0 : if (statp->st_dev == me->me_dev
645 0 : && !STREQ (me->me_type, "lofs")
646 0 : && (!best_match || best_match->me_dummy || !me->me_dummy))
647 : {
648 : /* Skip bogus mtab entries. */
649 0 : if (stat (me->me_mountdir, &disk_stats) != 0
650 0 : || disk_stats.st_dev != me->me_dev)
651 0 : me->me_dev = (dev_t) -2;
652 : else
653 0 : best_match = me;
654 : }
655 : }
656 :
657 15 : if (best_match)
658 45 : show_dev (best_match->me_devname, best_match->me_mountdir, point,
659 45 : best_match->me_type, best_match->me_dummy, best_match->me_remote);
660 : else
661 : {
662 : /* We couldn't find the mount entry corresponding to POINT. Go ahead and
663 : print as much info as we can; methods that require the device to be
664 : present will fail at a later point. */
665 :
666 : /* Find the actual mount point. */
667 0 : char *mp = find_mount_point (point, statp);
668 0 : if (mp)
669 : {
670 0 : show_dev (NULL, mp, NULL, NULL, false, false);
671 0 : free (mp);
672 : }
673 : }
674 15 : }
675 :
676 : /* Determine what kind of node NAME is and show the disk usage
677 : for it. STATP is the results of `stat' on NAME. */
678 :
679 : static void
680 15 : show_entry (char const *name, struct stat const *statp)
681 : {
682 15 : if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
683 0 : && show_disk (name))
684 0 : return;
685 :
686 15 : show_point (name, statp);
687 : }
688 :
689 : /* Show all mounted file systems, except perhaps those that are of
690 : an unselected type or are empty. */
691 :
692 : static void
693 30 : show_all_entries (void)
694 : {
695 : struct mount_entry *me;
696 :
697 1050 : for (me = mount_list; me; me = me->me_next)
698 2040 : show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
699 2040 : me->me_dummy, me->me_remote);
700 30 : }
701 :
702 : /* Add FSTYPE to the list of file system types to display. */
703 :
704 : static void
705 4 : add_fs_type (const char *fstype)
706 : {
707 : struct fs_type_list *fsp;
708 :
709 4 : fsp = xmalloc (sizeof *fsp);
710 4 : fsp->fs_name = (char *) fstype;
711 4 : fsp->fs_next = fs_select_list;
712 4 : fs_select_list = fsp;
713 4 : }
714 :
715 : /* Add FSTYPE to the list of file system types to be omitted. */
716 :
717 : static void
718 2 : add_excluded_fs_type (const char *fstype)
719 : {
720 : struct fs_type_list *fsp;
721 :
722 2 : fsp = xmalloc (sizeof *fsp);
723 2 : fsp->fs_name = (char *) fstype;
724 2 : fsp->fs_next = fs_exclude_list;
725 2 : fs_exclude_list = fsp;
726 2 : }
727 :
728 : void
729 7 : usage (int status)
730 : {
731 7 : if (status != EXIT_SUCCESS)
732 6 : fprintf (stderr, _("Try `%s --help' for more information.\n"),
733 : program_name);
734 : else
735 : {
736 1 : printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
737 1 : fputs (_("\
738 : Show information about the file system on which each FILE resides,\n\
739 : or all file systems by default.\n\
740 : \n\
741 : "), stdout);
742 1 : fputs (_("\
743 : Mandatory arguments to long options are mandatory for short options too.\n\
744 : "), stdout);
745 1 : fputs (_("\
746 : -a, --all include dummy file systems\n\
747 : -B, --block-size=SIZE use SIZE-byte blocks\n\
748 : -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
749 : -H, --si likewise, but use powers of 1000 not 1024\n\
750 : "), stdout);
751 1 : fputs (_("\
752 : -i, --inodes list inode information instead of block usage\n\
753 : -k like --block-size=1K\n\
754 : -l, --local limit listing to local file systems\n\
755 : --no-sync do not invoke sync before getting usage info (default)\n\
756 : "), stdout);
757 1 : fputs (_("\
758 : -P, --portability use the POSIX output format\n\
759 : --sync invoke sync before getting usage info\n\
760 : -t, --type=TYPE limit listing to file systems of type TYPE\n\
761 : -T, --print-type print file system type\n\
762 : -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
763 : -v (ignored)\n\
764 : "), stdout);
765 1 : fputs (HELP_OPTION_DESCRIPTION, stdout);
766 1 : fputs (VERSION_OPTION_DESCRIPTION, stdout);
767 1 : fputs (_("\n\
768 : SIZE may be (or may be an integer optionally followed by) one of following:\n\
769 : kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
770 : "), stdout);
771 1 : emit_bug_reporting_address ();
772 : }
773 7 : exit (status);
774 : }
775 :
776 : int
777 80 : main (int argc, char **argv)
778 : {
779 : struct stat *stats IF_LINT (= 0);
780 :
781 : initialize_main (&argc, &argv);
782 80 : program_name = argv[0];
783 80 : setlocale (LC_ALL, "");
784 : bindtextdomain (PACKAGE, LOCALEDIR);
785 : textdomain (PACKAGE);
786 :
787 80 : atexit (close_stdout);
788 :
789 80 : fs_select_list = NULL;
790 80 : fs_exclude_list = NULL;
791 80 : inode_format = false;
792 80 : show_all_fs = false;
793 80 : show_listed_fs = false;
794 80 : human_output_opts = -1;
795 80 : print_type = false;
796 80 : file_systems_processed = false;
797 80 : posix_format = false;
798 80 : exit_status = EXIT_SUCCESS;
799 :
800 : for (;;)
801 47 : {
802 127 : int oi = -1;
803 127 : int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options,
804 : &oi);
805 127 : if (c == -1)
806 45 : break;
807 :
808 82 : switch (c)
809 : {
810 11 : case 'a':
811 11 : show_all_fs = true;
812 11 : break;
813 42 : case 'B':
814 : {
815 42 : enum strtol_error e = human_options (optarg, &human_output_opts,
816 : &output_block_size);
817 42 : if (e != LONGINT_OK)
818 27 : xstrtol_fatal (e, oi, c, long_options, optarg);
819 : }
820 15 : break;
821 2 : case 'i':
822 2 : inode_format = true;
823 2 : break;
824 3 : case 'h':
825 3 : human_output_opts = human_autoscale | human_SI | human_base_1024;
826 3 : output_block_size = 1;
827 3 : break;
828 1 : case 'H':
829 1 : human_output_opts = human_autoscale | human_SI;
830 1 : output_block_size = 1;
831 1 : break;
832 1 : case 'k':
833 1 : human_output_opts = 0;
834 1 : output_block_size = 1024;
835 1 : break;
836 1 : case 'l':
837 1 : show_local_fs = true;
838 1 : break;
839 1 : case 'm': /* obsolescent */
840 1 : human_output_opts = 0;
841 1 : output_block_size = 1024 * 1024;
842 1 : break;
843 2 : case 'T':
844 2 : print_type = true;
845 2 : break;
846 2 : case 'P':
847 2 : posix_format = true;
848 2 : break;
849 1 : case SYNC_OPTION:
850 1 : require_sync = true;
851 1 : break;
852 1 : case NO_SYNC_OPTION:
853 1 : require_sync = false;
854 1 : break;
855 :
856 4 : case 'F':
857 : /* Accept -F as a synonym for -t for compatibility with Solaris. */
858 : case 't':
859 4 : add_fs_type (optarg);
860 4 : break;
861 :
862 0 : case 'v': /* For SysV compatibility. */
863 : /* ignore */
864 0 : break;
865 2 : case 'x':
866 2 : add_excluded_fs_type (optarg);
867 2 : break;
868 :
869 1 : case_GETOPT_HELP_CHAR;
870 1 : case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
871 :
872 6 : default:
873 6 : usage (EXIT_FAILURE);
874 : }
875 : }
876 :
877 45 : if (human_output_opts == -1)
878 : {
879 24 : if (posix_format)
880 : {
881 2 : human_output_opts = 0;
882 2 : output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
883 : }
884 : else
885 22 : human_options (getenv ("DF_BLOCK_SIZE"),
886 : &human_output_opts, &output_block_size);
887 : }
888 :
889 : /* Fail if the same file system type was both selected and excluded. */
890 : {
891 45 : bool match = false;
892 : struct fs_type_list *fs_incl;
893 49 : for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
894 : {
895 : struct fs_type_list *fs_excl;
896 4 : for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
897 : {
898 0 : if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
899 : {
900 0 : error (0, 0,
901 : _("file system type %s both selected and excluded"),
902 0 : quote (fs_incl->fs_name));
903 0 : match = true;
904 0 : break;
905 : }
906 : }
907 : }
908 45 : if (match)
909 0 : exit (EXIT_FAILURE);
910 : }
911 :
912 45 : if (optind < argc)
913 : {
914 : int i;
915 :
916 : /* stat all the given entries to make sure they get automounted,
917 : if necessary, before reading the file system table. */
918 15 : stats = xnmalloc (argc - optind, sizeof *stats);
919 35 : for (i = optind; i < argc; ++i)
920 : {
921 20 : if (stat (argv[i], &stats[i - optind]))
922 : {
923 5 : error (0, errno, "%s", quote (argv[i]));
924 5 : exit_status = EXIT_FAILURE;
925 5 : argv[i] = NULL;
926 : }
927 : }
928 : }
929 :
930 45 : mount_list =
931 90 : read_file_system_list ((fs_select_list != NULL
932 42 : || fs_exclude_list != NULL
933 40 : || print_type
934 83 : || show_local_fs));
935 :
936 45 : if (mount_list == NULL)
937 : {
938 : /* Couldn't read the table of mounted file systems.
939 : Fail if df was invoked with no file name arguments;
940 : Otherwise, merely give a warning and proceed. */
941 0 : int status = (optind < argc ? 0 : EXIT_FAILURE);
942 0 : const char *warning = (optind < argc ? _("Warning: ") : "");
943 0 : error (status, errno, "%s%s", warning,
944 : _("cannot read table of mounted file systems"));
945 : }
946 :
947 45 : if (require_sync)
948 1 : sync ();
949 :
950 45 : if (optind < argc)
951 : {
952 : int i;
953 :
954 : /* Display explicitly requested empty file systems. */
955 15 : show_listed_fs = true;
956 :
957 35 : for (i = optind; i < argc; ++i)
958 20 : if (argv[i])
959 15 : show_entry (argv[i], &stats[i - optind]);
960 : }
961 : else
962 30 : show_all_entries ();
963 :
964 45 : if (! file_systems_processed)
965 6 : error (EXIT_FAILURE, 0, _("no file systems processed"));
966 :
967 39 : exit (exit_status);
968 : }
|