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