(get_fs_usage): If DISK is 0 and we needed to use
[gnulib.git] / lib / fsusage.c
1 /* fsusage.c -- return space usage of mounted filesystems
2    Copyright (C) 1991, 1992, 1996 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 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include "fsusage.h"
25
26 int statfs ();
27
28 #if HAVE_SYS_PARAM_H
29 # include <sys/param.h>
30 #endif
31
32 #if HAVE_SYS_MOUNT_H
33 # include <sys/mount.h>
34 #endif
35
36 #if HAVE_SYS_VFS_H
37 # include <sys/vfs.h>
38 #endif
39
40 #if HAVE_SYS_FS_S5PARAM_H       /* Fujitsu UXP/V */
41 # include <sys/fs/s5param.h>
42 #endif
43
44 #if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY)
45 # include <sys/filsys.h>        /* SVR2 */
46 #endif
47
48 #if HAVE_FCNTL_H
49 # include <fcntl.h>
50 #endif
51
52 #if HAVE_SYS_STATFS_H
53 # include <sys/statfs.h>
54 #endif
55
56 #if HAVE_DUSTAT_H               /* AIX PS/2 */
57 # include <sys/dustat.h>
58 #endif
59
60 #if HAVE_SYS_STATVFS_H          /* SVR4 */
61 # include <sys/statvfs.h>
62 int statvfs ();
63 #endif
64
65 int safe_read ();
66
67 /* Return the number of TOSIZE-byte blocks used by
68    BLOCKS FROMSIZE-byte blocks, rounding away from zero.
69    TOSIZE must be positive.  Return -1 if FROMSIZE is not positive.  */
70
71 static long
72 adjust_blocks (blocks, fromsize, tosize)
73      long blocks;
74      int fromsize, tosize;
75 {
76   if (tosize <= 0)
77     abort ();
78   if (fromsize <= 0)
79     return -1;
80
81   if (fromsize == tosize)       /* e.g., from 512 to 512 */
82     return blocks;
83   else if (fromsize > tosize)   /* e.g., from 2048 to 512 */
84     return blocks * (fromsize / tosize);
85   else                          /* e.g., from 256 to 512 */
86     return (blocks + (blocks < 0 ? -1 : 1)) / (tosize / fromsize);
87 }
88
89 /* Fill in the fields of FSP with information about space usage for
90    the filesystem on which PATH resides.
91    DISK is the device on which PATH is mounted, for space-getting
92    methods that need to know it.
93    Return 0 if successful, -1 if not.  When returning -1, ensure that
94    ERRNO is either a system error value, or zero if DISK is NULL
95    on a system that requires a non-NULL value.  */
96 int
97 get_fs_usage (path, disk, fsp)
98      const char *path;
99      const char *disk;
100      struct fs_usage *fsp;
101 {
102 #ifdef STAT_STATFS3_OSF1
103 # define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_fsize, 512)
104
105   struct statfs fsd;
106
107   if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
108     return -1;
109
110 #endif /* STAT_STATFS3_OSF1 */
111
112 #ifdef STAT_STATFS2_FS_DATA     /* Ultrix */
113 # define CONVERT_BLOCKS(B) adjust_blocks ((B), 1024, 512)
114
115   struct fs_data fsd;
116
117   if (statfs (path, &fsd) != 1)
118     return -1;
119   fsp->fsu_blocks = CONVERT_BLOCKS (fsd.fd_req.btot);
120   fsp->fsu_bfree = CONVERT_BLOCKS (fsd.fd_req.bfree);
121   fsp->fsu_bavail = CONVERT_BLOCKS (fsd.fd_req.bfreen);
122   fsp->fsu_files = fsd.fd_req.gtot;
123   fsp->fsu_ffree = fsd.fd_req.gfree;
124
125 #endif /* STAT_STATFS2_FS_DATA */
126
127 #ifdef STAT_READ_FILSYS         /* SVR2 */
128 # ifndef SUPERBOFF
129 #  define SUPERBOFF (SUPERB * 512)
130 # endif
131 # define CONVERT_BLOCKS(B) \
132     adjust_blocks ((B), (fsd.s_type == Fs2b ? 1024 : 512), 512)
133
134   struct filsys fsd;
135   int fd;
136
137   if (! disk)
138     {
139       errno = 0;
140       return -1;
141     }
142
143   fd = open (disk, O_RDONLY);
144   if (fd < 0)
145     return -1;
146   lseek (fd, (long) SUPERBOFF, 0);
147   if (safe_read (fd, (char *) &fsd, sizeof fsd) != sizeof fsd)
148     {
149       close (fd);
150       return -1;
151     }
152   close (fd);
153   fsp->fsu_blocks = CONVERT_BLOCKS (fsd.s_fsize);
154   fsp->fsu_bfree = CONVERT_BLOCKS (fsd.s_tfree);
155   fsp->fsu_bavail = CONVERT_BLOCKS (fsd.s_tfree);
156   fsp->fsu_files = (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1);
157   fsp->fsu_ffree = fsd.s_tinode;
158
159 #endif /* STAT_READ_FILSYS */
160
161 #ifdef STAT_STATFS2_BSIZE       /* 4.3BSD, SunOS 4, HP-UX, AIX */
162 # define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_bsize, 512)
163
164   struct statfs fsd;
165
166   if (statfs (path, &fsd) < 0)
167     return -1;
168
169 # ifdef STATFS_TRUNCATES_BLOCK_COUNTS
170
171   /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
172      struct statfs are truncated to 2GB.  These conditions detect that
173      truncation, presumably without botching the 4.1.1 case, in which
174      the values are not truncated.  The correct counts are stored in
175      undocumented spare fields.  */
176   if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0)
177     {
178       fsd.f_blocks = fsd.f_spare[0];
179       fsd.f_bfree = fsd.f_spare[1];
180       fsd.f_bavail = fsd.f_spare[2];
181     }
182 # endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
183
184 #endif /* STAT_STATFS2_BSIZE */
185
186 #ifdef STAT_STATFS2_FSIZE       /* 4.4BSD */
187 # define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_fsize, 512)
188
189   struct statfs fsd;
190
191   if (statfs (path, &fsd) < 0)
192     return -1;
193
194 #endif /* STAT_STATFS2_FSIZE */
195
196 #ifdef STAT_STATFS4             /* SVR3, Dynix, Irix, AIX */
197 # if _AIX || defined(_CRAY)
198 #  define CONVERT_BLOCKS(B) adjust_blocks ((B), fsd.f_bsize, 512)
199 #  ifdef _CRAY
200 #   define f_bavail f_bfree
201 #  endif
202 # else
203 #  define CONVERT_BLOCKS(B) (B)
204 #  ifndef _SEQUENT_             /* _SEQUENT_ is DYNIX/ptx */
205 #   ifndef DOLPHIN              /* DOLPHIN 3.8.alfa/7.18 has f_bavail */
206 #    define f_bavail f_bfree
207 #   endif
208 #  endif
209 # endif
210
211   struct statfs fsd;
212
213   if (statfs (path, &fsd, sizeof fsd, 0) < 0)
214     return -1;
215   /* Empirically, the block counts on most SVR3 and SVR3-derived
216      systems seem to always be in terms of 512-byte blocks,
217      no matter what value f_bsize has.  */
218
219 #endif /* STAT_STATFS4 */
220
221 #ifdef STAT_STATVFS             /* SVR4 */
222 # define CONVERT_BLOCKS(B) \
223     adjust_blocks ((B), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512)
224
225   struct statvfs fsd;
226
227   if (statvfs (path, &fsd) < 0)
228     return -1;
229   /* f_frsize isn't guaranteed to be supported.  */
230
231 #endif /* STAT_STATVFS */
232
233 #if !defined(STAT_STATFS2_FS_DATA) && !defined(STAT_READ_FILSYS)
234                                 /* !Ultrix && !SVR2 */
235
236   fsp->fsu_blocks = CONVERT_BLOCKS (fsd.f_blocks);
237   fsp->fsu_bfree = CONVERT_BLOCKS (fsd.f_bfree);
238   fsp->fsu_bavail = CONVERT_BLOCKS (fsd.f_bavail);
239   fsp->fsu_files = fsd.f_files;
240   fsp->fsu_ffree = fsd.f_ffree;
241
242 #endif /* not STAT_STATFS2_FS_DATA && not STAT_READ_FILSYS */
243
244   return 0;
245 }
246
247 #if defined(_AIX) && defined(_I386)
248 /* AIX PS/2 does not supply statfs.  */
249
250 int
251 statfs (path, fsb)
252      char *path;
253      struct statfs *fsb;
254 {
255   struct stat stats;
256   struct dustat fsd;
257
258   if (stat (path, &stats))
259     return -1;
260   if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
261     return -1;
262   fsb->f_type   = 0;
263   fsb->f_bsize  = fsd.du_bsize;
264   fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
265   fsb->f_bfree  = fsd.du_tfree;
266   fsb->f_bavail = fsd.du_tfree;
267   fsb->f_files  = (fsd.du_isize - 2) * fsd.du_inopb;
268   fsb->f_ffree  = fsd.du_tinode;
269   fsb->f_fsid.val[0] = fsd.du_site;
270   fsb->f_fsid.val[1] = fsd.du_pckno;
271   return 0;
272 }
273
274 #endif /* _AIX && _I386 */