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