LCOV - code coverage report
Current view: top level - src - df.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 296 388 76.3 %
Date: 2018-01-30 Functions: 12 14 85.7 %

          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             : }

Generated by: LCOV version 1.10