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