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