(root)/
coreutils-9.4/
lib/
fsusage.c
       1  /* fsusage.c -- return space usage of mounted file systems
       2  
       3     Copyright (C) 1991-1992, 1996, 1998-1999, 2002-2006, 2009-2023 Free Software
       4     Foundation, Inc.
       5  
       6     This file is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU Lesser General Public License as
       8     published by the Free Software Foundation, either version 3 of the
       9     License, or (at your option) any later version.
      10  
      11     This file is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <config.h>
      20  
      21  #include "fsusage.h"
      22  
      23  #include <limits.h>
      24  #include <sys/types.h>
      25  
      26  #if STAT_STATVFS || STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
      27  # include <sys/statvfs.h>
      28  #else
      29  /* Don't include backward-compatibility files unless they're needed.
      30     Eventually we'd like to remove all this cruft.  */
      31  # include <fcntl.h>
      32  # include <unistd.h>
      33  # include <sys/stat.h>
      34  #if HAVE_SYS_PARAM_H
      35  # include <sys/param.h>
      36  #endif
      37  #if HAVE_SYS_MOUNT_H
      38  # include <sys/mount.h>
      39  #endif
      40  #if HAVE_SYS_VFS_H
      41  # include <sys/vfs.h>
      42  #endif
      43  # if HAVE_SYS_FS_S5PARAM_H      /* Fujitsu UXP/V */
      44  #  include <sys/fs/s5param.h>
      45  # endif
      46  # if HAVE_SYS_STATFS_H
      47  #  include <sys/statfs.h>
      48  # endif
      49  #endif
      50  
      51  /* Many space usage primitives use all 1 bits to denote a value that is
      52     not applicable or unknown.  Propagate this information by returning
      53     a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
      54     is unsigned and narrower than uintmax_t.  */
      55  #define PROPAGATE_ALL_ONES(x) \
      56    ((sizeof (x) < sizeof (uintmax_t) \
      57      && (~ (x) == (sizeof (x) < sizeof (int) \
      58                    ? - (1 << (sizeof (x) * CHAR_BIT)) \
      59                    : 0))) \
      60     ? UINTMAX_MAX : (uintmax_t) (x))
      61  
      62  /* Extract the top bit of X as an uintmax_t value.  */
      63  #define EXTRACT_TOP_BIT(x) ((x) \
      64                              & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
      65  
      66  /* If a value is negative, many space usage primitives store it into an
      67     integer variable by assignment, even if the variable's type is unsigned.
      68     So, if a space usage variable X's top bit is set, convert X to the
      69     uintmax_t value V such that (- (uintmax_t) V) is the negative of
      70     the original value.  If X's top bit is clear, just yield X.
      71     Use PROPAGATE_TOP_BIT if the original value might be negative;
      72     otherwise, use PROPAGATE_ALL_ONES.  */
      73  #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
      74  
      75  #ifdef STAT_STATVFS
      76  /* Return true if statvfs works.  This is false for statvfs on systems
      77     with GNU libc on Linux kernels before 2.6.36, which stats all
      78     preceding entries in /proc/mounts; that makes df hang if even one
      79     of the corresponding file systems is hard-mounted but not available.  */
      80  # if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
      81  /* The FRSIZE fallback is not required in this case.  */
      82  #  undef STAT_STATFS2_FRSIZE
      83  static int statvfs_works (void) { return 1; }
      84  # else
      85  #  include <string.h> /* for strverscmp */
      86  #  include <sys/utsname.h>
      87  #  include <sys/statfs.h>
      88  #  define STAT_STATFS2_BSIZE 1
      89  
      90  static int
      91  statvfs_works (void)
      92  {
      93    static int statvfs_works_cache = -1;
      94    struct utsname name;
      95    if (statvfs_works_cache < 0)
      96      statvfs_works_cache = (uname (&name) == 0
      97                             && 0 <= strverscmp (name.release, "2.6.36"));
      98    return statvfs_works_cache;
      99  }
     100  # endif
     101  #endif
     102  
     103  
     104  /* Fill in the fields of FSP with information about space usage for
     105     the file system on which FILE resides.
     106     DISK is the device on which FILE is mounted, for space-getting
     107     methods that need to know it.
     108     Return 0 if successful, -1 if not.  When returning -1, ensure that
     109     ERRNO is either a system error value, or zero if DISK is NULL
     110     on a system that requires a non-NULL value.  */
     111  int
     112  get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
     113  {
     114  #ifdef STAT_STATVFS     /* POSIX, except pre-2.6.36 glibc/Linux */
     115  
     116    if (statvfs_works ())
     117      {
     118        struct statvfs vfsd;
     119  
     120        if (statvfs (file, &vfsd) < 0)
     121          return -1;
     122  
     123        /* f_frsize isn't guaranteed to be supported.  */
     124        fsp->fsu_blocksize = (vfsd.f_frsize
     125                              ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
     126                              : PROPAGATE_ALL_ONES (vfsd.f_bsize));
     127  
     128        fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
     129        fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
     130        fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
     131        fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
     132        fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
     133        fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
     134        return 0;
     135      }
     136  
     137  #endif
     138  
     139  #if defined STAT_STATVFS64            /* AIX */
     140  
     141    struct statvfs64 fsd;
     142  
     143    if (statvfs64 (file, &fsd) < 0)
     144      return -1;
     145  
     146    /* f_frsize isn't guaranteed to be supported.  */
     147    fsp->fsu_blocksize = (fsd.f_frsize
     148                          ? PROPAGATE_ALL_ONES (fsd.f_frsize)
     149                          : PROPAGATE_ALL_ONES (fsd.f_bsize));
     150  
     151  #elif defined STAT_STATFS3_OSF1         /* OSF/1 */
     152  
     153    struct statfs fsd;
     154  
     155    if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
     156      return -1;
     157  
     158    fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
     159  
     160  #elif defined STAT_STATFS2_FRSIZE        /* 2.6 < glibc/Linux < 2.6.36 */
     161  
     162    struct statfs fsd;
     163  
     164    if (statfs (file, &fsd) < 0)
     165      return -1;
     166  
     167    fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
     168  
     169  #elif defined STAT_STATFS2_BSIZE        /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
     170                                             Mac OS X < 10.4, FreeBSD < 5.0, \
     171                                             NetBSD < 3.0, OpenBSD < 4.4 */
     172  
     173    struct statfs fsd;
     174  
     175    if (statfs (file, &fsd) < 0)
     176      return -1;
     177  
     178    fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
     179  
     180  # ifdef STATFS_TRUNCATES_BLOCK_COUNTS
     181  
     182    /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
     183       struct statfs are truncated to 2GB.  These conditions detect that
     184       truncation, presumably without botching the 4.1.1 case, in which
     185       the values are not truncated.  The correct counts are stored in
     186       undocumented spare fields.  */
     187    if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
     188      {
     189        fsd.f_blocks = fsd.f_spare[0];
     190        fsd.f_bfree = fsd.f_spare[1];
     191        fsd.f_bavail = fsd.f_spare[2];
     192      }
     193  # endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
     194  
     195  #elif defined STAT_STATFS2_FSIZE        /* 4.4BSD and older NetBSD */
     196  
     197    struct statfs fsd;
     198  
     199    if (statfs (file, &fsd) < 0)
     200      return -1;
     201  
     202    fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
     203  
     204  #elif defined STAT_STATFS4              /* SVR3, old Irix */
     205  
     206    struct statfs fsd;
     207  
     208    if (statfs (file, &fsd, sizeof fsd, 0) < 0)
     209      return -1;
     210  
     211    /* Empirically, the block counts on most SVR3 and SVR3-derived
     212       systems seem to always be in terms of 512-byte blocks,
     213       no matter what value f_bsize has.  */
     214     fsp->fsu_blocksize = 512;
     215  
     216  #endif
     217  
     218  #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1                \
     219       || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE       \
     220       || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
     221  
     222    fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
     223    fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
     224    fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
     225    fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
     226    fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
     227    fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
     228  
     229  #endif
     230  
     231    (void) disk;  /* avoid argument-unused warning */
     232    return 0;
     233  }