* config/srclist.txt:
[gnulib.git] / lib / fsusage.c
1 /* fsusage.c -- return space usage of mounted file systems
2
3    Copyright (C) 1991, 1992, 1996, 1998, 1999, 2002, 2003, 2004, 2005
4    Free Software 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 2, or (at your option)
9    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, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #if HAVE_INTTYPES_H
25 # include <inttypes.h>
26 #endif
27 #if HAVE_STDINT_H
28 # include <stdint.h>
29 #endif
30 #if HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #ifndef UINTMAX_MAX
34 # define UINTMAX_MAX ((uintmax_t) -1)
35 #endif
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include "fsusage.h"
40
41 #include <limits.h>
42
43 #if HAVE_SYS_PARAM_H
44 # include <sys/param.h>
45 #endif
46
47 #if HAVE_SYS_MOUNT_H
48 # include <sys/mount.h>
49 #endif
50
51 #if HAVE_SYS_VFS_H
52 # include <sys/vfs.h>
53 #endif
54
55 #if HAVE_SYS_FS_S5PARAM_H       /* Fujitsu UXP/V */
56 # include <sys/fs/s5param.h>
57 #endif
58
59 #if defined HAVE_SYS_FILSYS_H && !defined _CRAY
60 # include <sys/filsys.h>        /* SVR2 */
61 #endif
62
63 #include <fcntl.h>
64
65 #if HAVE_SYS_STATFS_H
66 # include <sys/statfs.h>
67 #endif
68
69 #if HAVE_DUSTAT_H               /* AIX PS/2 */
70 # include <sys/dustat.h>
71 #endif
72
73 #if HAVE_SYS_STATVFS_H          /* SVR4 */
74 # include <sys/statvfs.h>
75 #endif
76
77 #include "full-read.h"
78
79 /* Many space usage primitives use all 1 bits to denote a value that is
80    not applicable or unknown.  Propagate this information by returning
81    a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
82    is unsigned and narrower than uintmax_t.  */
83 #define PROPAGATE_ALL_ONES(x) \
84   ((sizeof (x) < sizeof (uintmax_t) \
85     && (~ (x) == (sizeof (x) < sizeof (int) \
86                   ? - (1 << (sizeof (x) * CHAR_BIT)) \
87                   : 0))) \
88    ? UINTMAX_MAX : (x))
89
90 /* Extract the top bit of X as an uintmax_t value.  */
91 #define EXTRACT_TOP_BIT(x) ((x) \
92                             & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
93
94 /* If a value is negative, many space usage primitives store it into an
95    integer variable by assignment, even if the variable's type is unsigned.
96    So, if a space usage variable X's top bit is set, convert X to the
97    uintmax_t value V such that (- (uintmax_t) V) is the negative of
98    the original value.  If X's top bit is clear, just yield X.
99    Use PROPAGATE_TOP_BIT if the original value might be negative;
100    otherwise, use PROPAGATE_ALL_ONES.  */
101 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
102
103 /* Fill in the fields of FSP with information about space usage for
104    the file system on which FILE resides.
105    DISK is the device on which FILE is mounted, for space-getting
106    methods that need to know it.
107    Return 0 if successful, -1 if not.  When returning -1, ensure that
108    ERRNO is either a system error value, or zero if DISK is NULL
109    on a system that requires a non-NULL value.  */
110 int
111 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
112 {
113 #ifdef STAT_STATFS3_OSF1
114
115   struct statfs fsd;
116
117   if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
118     return -1;
119
120   fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
121
122 #endif /* STAT_STATFS3_OSF1 */
123
124 #ifdef STAT_STATFS2_FS_DATA     /* Ultrix */
125
126   struct fs_data fsd;
127
128   if (statfs (file, &fsd) != 1)
129     return -1;
130
131   fsp->fsu_blocksize = 1024;
132   fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
133   fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
134   fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
135   fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
136   fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
137   fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
138
139 #endif /* STAT_STATFS2_FS_DATA */
140
141 #ifdef STAT_READ_FILSYS         /* SVR2 */
142 # ifndef SUPERBOFF
143 #  define SUPERBOFF (SUPERB * 512)
144 # endif
145
146   struct filsys fsd;
147   int fd;
148
149   if (! disk)
150     {
151       errno = 0;
152       return -1;
153     }
154
155   fd = open (disk, O_RDONLY);
156   if (fd < 0)
157     return -1;
158   lseek (fd, (off_t) SUPERBOFF, 0);
159   if (full_read (fd, (char *) &fsd, sizeof fsd) != sizeof fsd)
160     {
161       close (fd);
162       return -1;
163     }
164   close (fd);
165
166   fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
167   fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
168   fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
169   fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
170   fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
171   fsp->fsu_files = (fsd.s_isize == -1
172                     ? UINTMAX_MAX
173                     : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
174   fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);
175
176 #endif /* STAT_READ_FILSYS */
177
178 #ifdef STAT_STATFS2_BSIZE       /* 4.3BSD, SunOS 4, HP-UX, AIX */
179
180   struct statfs fsd;
181
182   if (statfs (file, &fsd) < 0)
183     return -1;
184
185   fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
186
187 # ifdef STATFS_TRUNCATES_BLOCK_COUNTS
188
189   /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
190      struct statfs are truncated to 2GB.  These conditions detect that
191      truncation, presumably without botching the 4.1.1 case, in which
192      the values are not truncated.  The correct counts are stored in
193      undocumented spare fields.  */
194   if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
195     {
196       fsd.f_blocks = fsd.f_spare[0];
197       fsd.f_bfree = fsd.f_spare[1];
198       fsd.f_bavail = fsd.f_spare[2];
199     }
200 # endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
201
202 #endif /* STAT_STATFS2_BSIZE */
203
204 #ifdef STAT_STATFS2_FSIZE       /* 4.4BSD */
205
206   struct statfs fsd;
207
208   if (statfs (file, &fsd) < 0)
209     return -1;
210
211   fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
212
213 #endif /* STAT_STATFS2_FSIZE */
214
215 #ifdef STAT_STATFS4             /* SVR3, Dynix, Irix, AIX */
216
217 # if !_AIX && !defined _SEQUENT_ && !defined DOLPHIN
218 #  define f_bavail f_bfree
219 # endif
220
221   struct statfs fsd;
222
223   if (statfs (file, &fsd, sizeof fsd, 0) < 0)
224     return -1;
225
226   /* Empirically, the block counts on most SVR3 and SVR3-derived
227      systems seem to always be in terms of 512-byte blocks,
228      no matter what value f_bsize has.  */
229 # if _AIX || defined _CRAY
230    fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
231 # else
232    fsp->fsu_blocksize = 512;
233 # endif
234
235 #endif /* STAT_STATFS4 */
236
237 #ifdef STAT_STATVFS             /* SVR4 */
238
239   struct statvfs fsd;
240
241   if (statvfs (file, &fsd) < 0)
242     return -1;
243
244   /* f_frsize isn't guaranteed to be supported.  */
245   fsp->fsu_blocksize = (fsd.f_frsize
246                         ? PROPAGATE_ALL_ONES (fsd.f_frsize)
247                         : PROPAGATE_ALL_ONES (fsd.f_bsize));
248
249 #endif /* STAT_STATVFS */
250
251 #if !defined STAT_STATFS2_FS_DATA && !defined STAT_READ_FILSYS
252                                 /* !Ultrix && !SVR2 */
253
254   fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
255   fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
256   fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
257   fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
258   fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
259   fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
260
261 #endif /* not STAT_STATFS2_FS_DATA && not STAT_READ_FILSYS */
262
263   return 0;
264 }
265
266 #if defined _AIX && defined _I386
267 /* AIX PS/2 does not supply statfs.  */
268
269 int
270 statfs (char *file, struct statfs *fsb)
271 {
272   struct stat stats;
273   struct dustat fsd;
274
275   if (stat (file, &stats) != 0)
276     return -1;
277   if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
278     return -1;
279   fsb->f_type   = 0;
280   fsb->f_bsize  = fsd.du_bsize;
281   fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
282   fsb->f_bfree  = fsd.du_tfree;
283   fsb->f_bavail = fsd.du_tfree;
284   fsb->f_files  = (fsd.du_isize - 2) * fsd.du_inopb;
285   fsb->f_ffree  = fsd.du_tinode;
286   fsb->f_fsid.val[0] = fsd.du_site;
287   fsb->f_fsid.val[1] = fsd.du_pckno;
288   return 0;
289 }
290
291 #endif /* _AIX && _I386 */