1/* fsusage.c -- return space usage of mounted filesystems
2   Copyright (C) 1991, 1992 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
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
17
18   This file was modified slightly by Ian Lance Taylor, December 1992,
19   and again July 1995, for use with Taylor UUCP.  */
20
21#include "uucp.h"
22#include "uudefs.h"
23#include "sysdep.h"
24#include "fsusg.h"
25
26int 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_FILSYS_H
41#include <sys/filsys.h>		/* SVR2.  */
42#endif
43
44#if HAVE_FCNTL_H
45#include <fcntl.h>
46#endif
47
48#if HAVE_SYS_STATFS_H
49#include <sys/statfs.h>
50#endif
51
52#if HAVE_SYS_DUSTAT_H		/* AIX PS/2.  */
53#include <sys/dustat.h>
54#endif
55
56#if HAVE_SYS_STATVFS_H		/* SVR4.  */
57#include <sys/statvfs.h>
58int statvfs ();
59#endif
60
61#if HAVE_USTAT_H		/* SVR2 and others.  */
62#include <ustat.h>
63#endif
64
65#if STAT_DISK_SPACE		/* QNX.  */
66#include <sys/disk.h>
67#include <errno.h>
68#endif
69
70#define STAT_NONE 0
71
72#if ! STAT_STATFS3_OSF1
73#if ! STAT_STATFS2_FS_DATA
74#if ! STAT_STATFS2_BSIZE
75#if ! STAT_STATFS2_FSIZE
76#if ! STAT_STATFS4
77#if ! STAT_STATVFS
78#if ! STAT_DISK_SPACE
79#if ! STAT_USTAT
80#undef STAT_NONE
81#define STAT_NONE 1
82#endif
83#endif
84#endif
85#endif
86#endif
87#endif
88#endif
89#endif
90
91#if ! STAT_NONE
92
93static long adjust_blocks P((long blocks, int fromsize, int tosize));
94
95/* Return the number of TOSIZE-byte blocks used by
96   BLOCKS FROMSIZE-byte blocks, rounding away from zero.
97   TOSIZE must be positive.  Return -1 if FROMSIZE is not positive.  */
98
99static long
100adjust_blocks (blocks, fromsize, tosize)
101     long blocks;
102     int fromsize, tosize;
103{
104  if (tosize <= 0)
105    abort ();
106  if (fromsize <= 0)
107    return -1;
108
109  if (fromsize == tosize)	/* E.g., from 512 to 512.  */
110    return blocks;
111  else if (fromsize > tosize)	/* E.g., from 2048 to 512.  */
112    return blocks * (fromsize / tosize);
113  else				/* E.g., from 256 to 512.  */
114    return (blocks + (blocks < 0 ? -1 : 1)) / (tosize / fromsize);
115}
116
117#endif
118
119/* Fill in the fields of FSP with information about space usage for
120   the filesystem on which PATH resides.
121   DISK is the device on which PATH is mounted, for space-getting
122   methods that need to know it.
123   Return 0 if successful, -1 if not. */
124
125int
126get_fs_usage (path, disk, fsp)
127     char *path, *disk ATTRIBUTE_UNUSED;
128     struct fs_usage *fsp;
129{
130#if STAT_NONE
131  return -1;
132#endif
133
134#if STAT_STATFS3_OSF1
135  struct statfs fsd;
136
137  if (statfs (path, &fsd, sizeof (struct statfs)) != 0)
138    return -1;
139#define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_fsize, 512)
140#endif /* STAT_STATFS3_OSF1 */
141
142#if STAT_STATFS2_FS_DATA	/* Ultrix.  */
143  struct fs_data fsd;
144
145  if (statfs (path, &fsd) != 1)
146    return -1;
147#define CONVERT_BLOCKS(b) adjust_blocks ((long) (b), 1024, 512)
148  fsp->fsu_blocks = CONVERT_BLOCKS (fsd.fd_req.btot);
149  fsp->fsu_bfree = CONVERT_BLOCKS (fsd.fd_req.bfree);
150  fsp->fsu_bavail = CONVERT_BLOCKS (fsd.fd_req.bfreen);
151  fsp->fsu_files = fsd.fd_req.gtot;
152  fsp->fsu_ffree = fsd.fd_req.gfree;
153#endif
154
155#if STAT_STATFS2_BSIZE		/* 4.3BSD, SunOS 4, HP-UX, AIX.  */
156  struct statfs fsd;
157
158  if (statfs (path, &fsd) < 0)
159    return -1;
160#define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_bsize, 512)
161#endif
162
163#if STAT_STATFS2_FSIZE		/* 4.4BSD.  */
164  struct statfs fsd;
165
166  if (statfs (path, &fsd) < 0)
167    return -1;
168#define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_fsize, 512)
169#endif
170
171#if STAT_STATFS4		/* SVR3, Dynix, Irix.  */
172  struct statfs fsd;
173
174  if (statfs (path, &fsd, sizeof fsd, 0) < 0)
175    return -1;
176  /* Empirically, the block counts on most SVR3 and SVR3-derived
177     systems seem to always be in terms of 512-byte blocks,
178     no matter what value f_bsize has.  */
179# if _AIX
180#  define CONVERT_BLOCKS(b) adjust_blocks ((b), fsd.f_bsize, 512)
181# else
182#  define CONVERT_BLOCKS(b) (b)
183#  ifndef _SEQUENT_		/* _SEQUENT_ is DYNIX/ptx.  */
184#   ifndef DOLPHIN		/* DOLPHIN 3.8.alfa/7.18 has f_bavail */
185#    define f_bavail f_bfree
186#   endif
187#  endif
188# endif
189#endif
190
191#if STAT_STATVFS		/* SVR4.  */
192  struct statvfs fsd;
193
194  if (statvfs (path, &fsd) < 0)
195    return -1;
196  /* f_frsize isn't guaranteed to be supported.  */
197#define CONVERT_BLOCKS(b) \
198  adjust_blocks ((b), fsd.f_frsize ? fsd.f_frsize : fsd.f_bsize, 512)
199#endif
200
201#if STAT_DISK_SPACE		/* QNX.  */
202  int o;
203  int iret;
204  long cfree_blocks, ctotal_blocks;
205  char *zpath;
206  char *zslash;
207
208  zpath = zbufcpy (path);
209  while ((o = open (zpath, O_RDONLY, 0)) == -1
210	 && errno == ENOENT)
211    {
212      /* The named file doesn't exist, so we can't open it.  Try the
213	 directory containing it. */
214      if ((strcmp ("/", zpath) == 0)
215	  || (strcmp (zpath, ".") == 0)
216	  || (strcmp (zpath, "") == 0)
217	  /* QNX peculiarity: "//2" means root on node 2 */
218	  || ((strncmp (zpath, "//", 2) == 0)
219	      && (strchr (zpath + 2, '/') == NULL)))
220	{
221	  /* We can't shorten this! */
222	  break;
223	}
224
225      /* Shorten the pathname by one component and try again. */
226      zslash = strrchr (zpath, '/');
227      if (zslash == NULL)
228	{
229	  /* Try the current directory.  We can open directories. */
230	  zpath[0] = '.';
231	  zpath[1] = '\0';
232	}
233      else if (zslash == zpath)
234	{
235	  /* Try the root directory. */
236	  zpath[0] = '/';
237	  zpath[1] = '\0';
238	}
239      else
240	{
241	  /* Chop off last path component. */
242	  zslash[0] = '\0';
243	}
244    }
245  if (o == -1)
246    {
247      ulog (LOG_ERROR, "get_fs_usage: open (%s) failed: %s", zpath,
248	    strerror (errno));
249      ubuffree (zpath);
250      return -1;
251    }
252  ubuffree (zpath);
253
254  iret = disk_space (o, &cfree_blocks, &ctotal_blocks);
255  (void) close (o);
256  if (iret == -1)
257    {
258      ulog (LOG_ERROR, "get_fs_usage: disk_space failed: %s",
259	    strerror (errno));
260      return -1;
261    }
262
263  fsp->fsu_blocks = ctotal_blocks;
264  fsp->fsu_bfree = cfree_blocks;
265  fsp->fsu_bavail = cfree_blocks;
266
267  /* QNX has no limit on the number of inodes.  Most inodes are stored
268     directly in the directory entry. */
269  fsp->fsu_files = -1;
270  fsp->fsu_ffree = -1;
271#endif /* STAT_DISK_SPACE */
272
273#if STAT_USTAT
274  struct stat sstat;
275  struct ustat s;
276
277  if (stat (path, &sstat) < 0
278      || ustat (sstat.st_dev, &s) < 0)
279    return -1;
280  fsp->fsu_blocks = -1;
281  fsp->fsu_bfree = s.f_tfree;
282  fsp->fsu_bavail = s.f_tfree;
283  fsp->fsu_files = -1;
284  fsp->fsu_ffree = -1;
285#endif
286
287#if ! STAT_STATFS2_FS_DATA /* ! Ultrix */
288#if ! STAT_DISK_SPACE
289#if ! STAT_USTAT
290#if ! STAT_NONE
291  fsp->fsu_blocks = CONVERT_BLOCKS (fsd.f_blocks);
292  fsp->fsu_bfree = CONVERT_BLOCKS (fsd.f_bfree);
293  fsp->fsu_bavail = CONVERT_BLOCKS (fsd.f_bavail);
294  fsp->fsu_files = fsd.f_files;
295  fsp->fsu_ffree = fsd.f_ffree;
296#endif
297#endif
298#endif
299#endif
300
301  return 0;
302}
303
304#ifdef _AIX
305#ifdef _I386
306/* AIX PS/2 does not supply statfs.  */
307
308int
309statfs (path, fsb)
310     char *path;
311     struct statfs *fsb;
312{
313  struct stat stats;
314  struct dustat fsd;
315
316  if (stat (path, &stats))
317    return -1;
318  if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
319    return -1;
320  fsb->f_type   = 0;
321  fsb->f_bsize  = fsd.du_bsize;
322  fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
323  fsb->f_bfree  = fsd.du_tfree;
324  fsb->f_bavail = fsd.du_tfree;
325  fsb->f_files  = (fsd.du_isize - 2) * fsd.du_inopb;
326  fsb->f_ffree  = fsd.du_tinode;
327  fsb->f_fsid.val[0] = fsd.du_site;
328  fsb->f_fsid.val[1] = fsd.du_pckno;
329  return 0;
330}
331#endif /* _I386 */
332#endif /* _AIX */
333