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