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