1/* fsusage.c -- return space usage of mounted file systems 2 3 Copyright (C) 1991-1992, 1996, 1998-1999, 2002-2006, 2009-2010 Free Software 4 Foundation, Inc. 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program 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 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://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 /* 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 defined HAVE_SYS_FILSYS_H && !defined _CRAY 47# include <sys/filsys.h> /* SVR2 */ 48# endif 49# if HAVE_SYS_STATFS_H 50# include <sys/statfs.h> 51# endif 52# if HAVE_DUSTAT_H /* AIX PS/2 */ 53# include <sys/dustat.h> 54# endif 55# include "full-read.h" 56#endif 57 58/* The results of open() in this file are not used with fchdir, 59 therefore save some unnecessary work in fchdir.c. */ 60#undef open 61#undef close 62 63/* Many space usage primitives use all 1 bits to denote a value that is 64 not applicable or unknown. Propagate this information by returning 65 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X 66 is unsigned and narrower than uintmax_t. */ 67#define PROPAGATE_ALL_ONES(x) \ 68 ((sizeof (x) < sizeof (uintmax_t) \ 69 && (~ (x) == (sizeof (x) < sizeof (int) \ 70 ? - (1 << (sizeof (x) * CHAR_BIT)) \ 71 : 0))) \ 72 ? UINTMAX_MAX : (uintmax_t) (x)) 73 74/* Extract the top bit of X as an uintmax_t value. */ 75#define EXTRACT_TOP_BIT(x) ((x) \ 76 & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1))) 77 78/* If a value is negative, many space usage primitives store it into an 79 integer variable by assignment, even if the variable's type is unsigned. 80 So, if a space usage variable X's top bit is set, convert X to the 81 uintmax_t value V such that (- (uintmax_t) V) is the negative of 82 the original value. If X's top bit is clear, just yield X. 83 Use PROPAGATE_TOP_BIT if the original value might be negative; 84 otherwise, use PROPAGATE_ALL_ONES. */ 85#define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1)) 86 87/* Fill in the fields of FSP with information about space usage for 88 the file system on which FILE resides. 89 DISK is the device on which FILE is mounted, for space-getting 90 methods that need to know it. 91 Return 0 if successful, -1 if not. When returning -1, ensure that 92 ERRNO is either a system error value, or zero if DISK is NULL 93 on a system that requires a non-NULL value. */ 94int 95get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp) 96{ 97#if defined STAT_STATVFS /* POSIX */ 98 99 struct statvfs fsd; 100 101 if (statvfs (file, &fsd) < 0) 102 return -1; 103 104 /* f_frsize isn't guaranteed to be supported. */ 105 fsp->fsu_blocksize = (fsd.f_frsize 106 ? PROPAGATE_ALL_ONES (fsd.f_frsize) 107 : PROPAGATE_ALL_ONES (fsd.f_bsize)); 108 109#elif defined STAT_STATFS2_FS_DATA /* Ultrix */ 110 111 struct fs_data fsd; 112 113 if (statfs (file, &fsd) != 1) 114 return -1; 115 116 fsp->fsu_blocksize = 1024; 117 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot); 118 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree); 119 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen); 120 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0; 121 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot); 122 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree); 123 124#elif defined STAT_READ_FILSYS /* SVR2 */ 125# ifndef SUPERBOFF 126# define SUPERBOFF (SUPERB * 512) 127# endif 128 129 struct filsys fsd; 130 int fd; 131 132 if (! disk) 133 { 134 errno = 0; 135 return -1; 136 } 137 138 fd = open (disk, O_RDONLY); 139 if (fd < 0) 140 return -1; 141 lseek (fd, (off_t) SUPERBOFF, 0); 142 if (full_read (fd, (char *) &fsd, sizeof fsd) != sizeof fsd) 143 { 144 close (fd); 145 return -1; 146 } 147 close (fd); 148 149 fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512); 150 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize); 151 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree); 152 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree); 153 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0; 154 fsp->fsu_files = (fsd.s_isize == -1 155 ? UINTMAX_MAX 156 : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1)); 157 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode); 158 159#elif defined STAT_STATFS3_OSF1 160 161 struct statfs fsd; 162 163 if (statfs (file, &fsd, sizeof (struct statfs)) != 0) 164 return -1; 165 166 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize); 167 168#elif defined STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX */ 169 170 struct statfs fsd; 171 172 if (statfs (file, &fsd) < 0) 173 return -1; 174 175 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize); 176 177# ifdef STATFS_TRUNCATES_BLOCK_COUNTS 178 179 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the 180 struct statfs are truncated to 2GB. These conditions detect that 181 truncation, presumably without botching the 4.1.1 case, in which 182 the values are not truncated. The correct counts are stored in 183 undocumented spare fields. */ 184 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0) 185 { 186 fsd.f_blocks = fsd.f_spare[0]; 187 fsd.f_bfree = fsd.f_spare[1]; 188 fsd.f_bavail = fsd.f_spare[2]; 189 } 190# endif /* STATFS_TRUNCATES_BLOCK_COUNTS */ 191 192#elif defined STAT_STATFS2_FSIZE /* 4.4BSD */ 193 194 struct statfs fsd; 195 196 if (statfs (file, &fsd) < 0) 197 return -1; 198 199 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize); 200 201#elif defined STAT_STATFS4 /* SVR3, Dynix, Irix, AIX */ 202 203# if !_AIX && !defined _SEQUENT_ && !defined DOLPHIN 204# define f_bavail f_bfree 205# endif 206 207 struct statfs fsd; 208 209 if (statfs (file, &fsd, sizeof fsd, 0) < 0) 210 return -1; 211 212 /* Empirically, the block counts on most SVR3 and SVR3-derived 213 systems seem to always be in terms of 512-byte blocks, 214 no matter what value f_bsize has. */ 215# if _AIX || defined _CRAY 216 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize); 217# else 218 fsp->fsu_blocksize = 512; 219# endif 220 221#endif 222 223#if (defined STAT_STATVFS \ 224 || (!defined STAT_STATFS2_FS_DATA && !defined STAT_READ_FILSYS)) 225 226 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks); 227 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree); 228 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail); 229 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0; 230 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files); 231 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree); 232 233#endif 234 235 (void) disk; /* avoid argument-unused warning */ 236 return 0; 237} 238 239#if defined _AIX && defined _I386 240/* AIX PS/2 does not supply statfs. */ 241 242int 243statfs (char *file, struct statfs *fsb) 244{ 245 struct stat stats; 246 struct dustat fsd; 247 248 if (stat (file, &stats) != 0) 249 return -1; 250 if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd))) 251 return -1; 252 fsb->f_type = 0; 253 fsb->f_bsize = fsd.du_bsize; 254 fsb->f_blocks = fsd.du_fsize - fsd.du_isize; 255 fsb->f_bfree = fsd.du_tfree; 256 fsb->f_bavail = fsd.du_tfree; 257 fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb; 258 fsb->f_ffree = fsd.du_tinode; 259 fsb->f_fsid.val[0] = fsd.du_site; 260 fsb->f_fsid.val[1] = fsd.du_pckno; 261 return 0; 262} 263 264#endif /* _AIX && _I386 */ 265