Parenthesis around some -1.
[midnight-commander.git] / src / filemanager / mountlist.c
blob71a3d3dad5e08e2bff225cda275f5455be629e33
1 /*
2 Return a list of mounted file systems
4 Copyright (C) 1991, 1992, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
7 Copyright (C) 1991, 1992, 2011
8 The Free Software Foundation, Inc.
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /** \file mountlist.c
27 * \brief Source: list of mounted filesystems
30 #include <config.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdint.h> /* SIZE_MAX */
37 #include <sys/types.h>
39 #include <errno.h>
41 /* This header needs to be included before sys/mount.h on *BSD */
42 #ifdef HAVE_SYS_PARAM_H
43 #include <sys/param.h>
44 #endif
46 #if defined STAT_STATVFS || defined STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
47 #include <sys/statvfs.h>
48 #else
49 /* Don't include backward-compatibility files unless they're needed.
50 Eventually we'd like to remove all this cruft. */
51 #include <fcntl.h>
52 #include <unistd.h>
53 #include <sys/stat.h>
55 #ifdef MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
56 #ifdef HAVE_SYS_UCRED_H
57 #include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
58 NGROUPS is used as an array dimension in ucred.h */
59 #include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
60 #endif
61 #ifdef HAVE_SYS_MOUNT_H
62 #include <sys/mount.h>
63 #endif
64 #ifdef HAVE_SYS_FS_TYPES_H
65 #include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
66 #endif
67 #ifdef HAVE_STRUCT_FSSTAT_F_FSTYPENAME
68 #define FS_TYPE(Ent) ((Ent).f_fstypename)
69 #else
70 #define FS_TYPE(Ent) mnt_names[(Ent).f_type]
71 #endif
72 #endif /* MOUNTED_GETFSSTAT */
73 #endif /* STAT_STATVFS || STAT_STATVFS64 */
75 #ifdef HAVE_SYS_VFS_H
76 #include <sys/vfs.h>
77 #endif
78 #ifdef HAVE_SYS_FS_S5PARAM_H /* Fujitsu UXP/V */
79 #include <sys/fs/s5param.h>
80 #endif
81 #if defined HAVE_SYS_FILSYS_H && !defined _CRAY
82 #include <sys/filsys.h> /* SVR2 */
83 #endif
84 #ifdef HAVE_SYS_STATFS_H
85 #include <sys/statfs.h>
86 #endif
87 #ifdef HAVE_DUSTAT_H /* AIX PS/2 */
88 #include <sys/dustat.h>
89 #endif
91 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
92 #include <mntent.h>
93 #ifndef MOUNTED
94 #ifdef _PATH_MOUNTED /* GNU libc */
95 #define MOUNTED _PATH_MOUNTED
96 #endif
97 #ifdef MNT_MNTTAB /* HP-UX. */
98 #define MOUNTED MNT_MNTTAB
99 #endif
100 #ifdef MNTTABNAME /* Dynix. */
101 #define MOUNTED MNTTABNAME
102 #endif
103 #endif
104 #endif
106 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
107 #include <sys/mount.h>
108 #endif
110 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
111 #include <sys/statvfs.h>
112 #endif
114 #ifdef MOUNTED_GETMNT /* Ultrix. */
115 #include <sys/mount.h>
116 #include <sys/fs_types.h>
117 #endif
119 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
120 #include <fs_info.h>
121 #include <dirent.h>
122 #endif
124 #ifdef MOUNTED_FREAD /* SVR2. */
125 #include <mnttab.h>
126 #endif
128 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
129 #include <mnttab.h>
130 #include <sys/fstyp.h>
131 #include <sys/statfs.h>
132 #endif
134 #ifdef MOUNTED_LISTMNTENT
135 #include <mntent.h>
136 #endif
138 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
139 #include <sys/mnttab.h>
140 #endif
142 #ifdef MOUNTED_VMOUNT /* AIX. */
143 #include <fshelp.h>
144 #include <sys/vfs.h>
145 #endif
147 #ifdef MOUNTED_INTERIX_STATVFS /* Interix. */
148 #include <sys/statvfs.h>
149 #include <dirent.h>
150 #endif
152 #ifdef DOLPHIN
153 /* So special that it's not worth putting this in autoconf. */
154 #undef MOUNTED_FREAD_FSTYP
155 #define MOUNTED_GETMNTTBL
156 #endif
158 #ifdef HAVE_SYS_MNTENT_H
159 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
160 #include <sys/mntent.h>
161 #endif
163 #undef MNT_IGNORE
164 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
165 #define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
166 #else
167 #define MNT_IGNORE(M) 0
168 #endif
170 #ifdef HAVE_INFOMOUNT_QNX
171 #include <sys/disk.h>
172 #include <sys/fsys.h>
173 #endif
175 #ifdef HAVE_SYS_STATVFS_H /* SVR4. */
176 #include <sys/statvfs.h>
177 #endif
179 #include "lib/global.h"
180 #include "mountlist.h"
182 /*** global variables ****************************************************************************/
184 /*** file scope macro definitions ****************************************************************/
186 #if defined (__QNX__) && !defined(__QNXNTO__) && !defined (HAVE_INFOMOUNT_LIST)
187 #define HAVE_INFOMOUNT_QNX
188 #endif
190 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
191 #define HAVE_INFOMOUNT
192 #endif
194 /* The results of open() in this file are not used with fchdir,
195 therefore save some unnecessary work in fchdir.c. */
196 #undef open
197 #undef close
199 /* The results of opendir() in this file are not used with dirfd and fchdir,
200 therefore save some unnecessary work in fchdir.c. */
201 #undef opendir
202 #undef closedir
204 #define ME_DUMMY_0(Fs_name, Fs_type) \
205 (strcmp (Fs_type, "autofs") == 0 \
206 || strcmp (Fs_type, "proc") == 0 \
207 || strcmp (Fs_type, "subfs") == 0 \
208 /* for NetBSD 3.0 */ \
209 || strcmp (Fs_type, "kernfs") == 0 \
210 /* for Irix 6.5 */ \
211 || strcmp (Fs_type, "ignore") == 0)
213 /* Historically, we have marked as "dummy" any file system of type "none",
214 but now that programs like du need to know about bind-mounted directories,
215 we grant an exception to any with "bind" in its list of mount options.
216 I.e., those are *not* dummy entries. */
217 #ifdef MOUNTED_GETMNTENT1
218 #define ME_DUMMY(Fs_name, Fs_type, Fs_ent) \
219 (ME_DUMMY_0 (Fs_name, Fs_type) \
220 || (strcmp (Fs_type, "none") == 0 \
221 && !hasmntopt (Fs_ent, "bind")))
222 #else
223 #define ME_DUMMY(Fs_name, Fs_type) \
224 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
225 #endif
227 #ifdef __CYGWIN__
228 #include <windows.h>
229 #define ME_REMOTE me_remote
230 /* All cygwin mount points include `:' or start with `//'; so it
231 requires a native Windows call to determine remote disks. */
232 static int
233 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
235 if (fs_name[0] && fs_name[1] == ':')
237 char drive[4];
238 sprintf (drive, "%c:\\", fs_name[0]);
239 switch (GetDriveType (drive))
241 case DRIVE_REMOVABLE:
242 case DRIVE_FIXED:
243 case DRIVE_CDROM:
244 case DRIVE_RAMDISK:
245 return 0;
248 return 1;
250 #endif
251 #ifndef ME_REMOTE
252 /* A file system is `remote' if its Fs_name contains a `:'
253 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
254 #define ME_REMOTE(Fs_name, Fs_type) \
255 (strchr (Fs_name, ':') != NULL \
256 || ((Fs_name)[0] == '/' \
257 && (Fs_name)[1] == '/' \
258 && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "cifs") == 0)))
259 #endif
261 /* Many space usage primitives use all 1 bits to denote a value that is
262 not applicable or unknown. Propagate this information by returning
263 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
264 is unsigned and narrower than uintmax_t. */
265 #define PROPAGATE_ALL_ONES(x) \
266 ((sizeof (x) < sizeof (uintmax_t) \
267 && (~ (x) == (sizeof (x) < sizeof (int) \
268 ? - (1 << (sizeof (x) * CHAR_BIT)) \
269 : 0))) \
270 ? UINTMAX_MAX : (uintmax_t) (x))
272 /* Extract the top bit of X as an uintmax_t value. */
273 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
275 /* If a value is negative, many space usage primitives store it into an
276 integer variable by assignment, even if the variable's type is unsigned.
277 So, if a space usage variable X's top bit is set, convert X to the
278 uintmax_t value V such that (- (uintmax_t) V) is the negative of
279 the original value. If X's top bit is clear, just yield X.
280 Use PROPAGATE_TOP_BIT if the original value might be negative;
281 otherwise, use PROPAGATE_ALL_ONES. */
282 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
284 #ifdef STAT_STATVFS
285 /* Return true if statvfs works. This is false for statvfs on systems
286 with GNU libc on Linux kernels before 2.6.36, which stats all
287 preceding entries in /proc/mounts; that makes df hang if even one
288 of the corresponding file systems is hard-mounted but not available. */
289 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
290 /* The FRSIZE fallback is not required in this case. */
291 #undef STAT_STATFS2_FRSIZE
292 static int
293 statvfs_works (void)
295 return 1;
297 #else
298 #include <string.h> /* for strverscmp */
299 #include <sys/utsname.h>
300 #include <sys/statfs.h>
301 #define STAT_STATFS2_BSIZE 1
303 static int
304 statvfs_works (void)
306 static int statvfs_works_cache = -1;
307 struct utsname name;
309 if (statvfs_works_cache < 0)
310 statvfs_works_cache = (uname (&name) == 0 && 0 <= strverscmp (name.release, "2.6.36"));
311 return statvfs_works_cache;
313 #endif
314 #endif
316 #ifdef STAT_READ_FILSYS /* SVR2 */
317 /* Set errno to zero upon EOF. */
318 #define ZERO_BYTE_TRANSFER_ERRNO 0
320 #ifdef EINTR
321 #define IS_EINTR(x) ((x) == EINTR)
322 #else
323 #define IS_EINTR(x) 0
324 #endif
325 #endif /* STAT_READ_FILSYS */
327 /*** file scope type declarations ****************************************************************/
329 /* A mount table entry. */
330 struct mount_entry
332 char *me_devname; /* Device node name, including "/dev/". */
333 char *me_mountdir; /* Mount point directory name. */
334 char *me_type; /* "nfs", "4.2", etc. */
335 dev_t me_dev; /* Device number of me_mountdir. */
336 unsigned int me_dummy:1; /* Nonzero for dummy file systems. */
337 unsigned int me_remote:1; /* Nonzero for remote fileystems. */
338 unsigned int me_type_malloced:1; /* Nonzero if me_type was malloced. */
339 struct mount_entry *me_next;
342 struct fs_usage
344 uintmax_t fsu_blocksize; /* Size of a block. */
345 uintmax_t fsu_blocks; /* Total blocks. */
346 uintmax_t fsu_bfree; /* Free blocks available to superuser. */
347 uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
348 int fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
349 uintmax_t fsu_files; /* Total file nodes. */
350 uintmax_t fsu_ffree; /* Free file nodes. */
353 /*** file scope variables ************************************************************************/
355 #ifdef HAVE_INFOMOUNT_LIST
356 static struct mount_entry *mc_mount_list = NULL;
357 #endif /* HAVE_INFOMOUNT_LIST */
359 /*** file scope functions ************************************************************************/
360 /* --------------------------------------------------------------------------------------------- */
362 #ifdef HAVE_INFOMOUNT_LIST
363 static void
364 free_mount_entry (struct mount_entry *me)
366 if (!me)
367 return;
368 g_free (me->me_devname);
369 g_free (me->me_mountdir);
370 if (me->me_type_malloced)
371 g_free (me->me_type);
372 g_free (me);
375 /* --------------------------------------------------------------------------------------------- */
377 #ifdef MOUNTED_GETMNTINFO
379 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
380 static char *
381 fstype_to_string (short int t)
383 switch (t)
385 #ifdef MOUNT_PC
386 case MOUNT_PC:
387 return "pc";
388 #endif
389 #ifdef MOUNT_MFS
390 case MOUNT_MFS:
391 return "mfs";
392 #endif
393 #ifdef MOUNT_LO
394 case MOUNT_LO:
395 return "lo";
396 #endif
397 #ifdef MOUNT_TFS
398 case MOUNT_TFS:
399 return "tfs";
400 #endif
401 #ifdef MOUNT_TMP
402 case MOUNT_TMP:
403 return "tmp";
404 #endif
405 #ifdef MOUNT_UFS
406 case MOUNT_UFS:
407 return "ufs";
408 #endif
409 #ifdef MOUNT_NFS
410 case MOUNT_NFS:
411 return "nfs";
412 #endif
413 #ifdef MOUNT_MSDOS
414 case MOUNT_MSDOS:
415 return "msdos";
416 #endif
417 #ifdef MOUNT_LFS
418 case MOUNT_LFS:
419 return "lfs";
420 #endif
421 #ifdef MOUNT_LOFS
422 case MOUNT_LOFS:
423 return "lofs";
424 #endif
425 #ifdef MOUNT_FDESC
426 case MOUNT_FDESC:
427 return "fdesc";
428 #endif
429 #ifdef MOUNT_PORTAL
430 case MOUNT_PORTAL:
431 return "portal";
432 #endif
433 #ifdef MOUNT_NULL
434 case MOUNT_NULL:
435 return "null";
436 #endif
437 #ifdef MOUNT_UMAP
438 case MOUNT_UMAP:
439 return "umap";
440 #endif
441 #ifdef MOUNT_KERNFS
442 case MOUNT_KERNFS:
443 return "kernfs";
444 #endif
445 #ifdef MOUNT_PROCFS
446 case MOUNT_PROCFS:
447 return "procfs";
448 #endif
449 #ifdef MOUNT_AFS
450 case MOUNT_AFS:
451 return "afs";
452 #endif
453 #ifdef MOUNT_CD9660
454 case MOUNT_CD9660:
455 return "cd9660";
456 #endif
457 #ifdef MOUNT_UNION
458 case MOUNT_UNION:
459 return "union";
460 #endif
461 #ifdef MOUNT_DEVFS
462 case MOUNT_DEVFS:
463 return "devfs";
464 #endif
465 #ifdef MOUNT_EXT2FS
466 case MOUNT_EXT2FS:
467 return "ext2fs";
468 #endif
469 default:
470 return "?";
473 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
475 /* --------------------------------------------------------------------------------------------- */
477 static char *
478 fsp_to_string (const struct statfs *fsp)
480 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
481 return (char *) (fsp->f_fstypename);
482 #else
483 return fstype_to_string (fsp->f_type);
484 #endif
486 #endif /* MOUNTED_GETMNTINFO */
488 /* --------------------------------------------------------------------------------------------- */
490 #ifdef MOUNTED_VMOUNT /* AIX. */
491 static char *
492 fstype_to_string (int t)
494 struct vfs_ent *e;
496 e = getvfsbytype (t);
497 if (!e || !e->vfsent_name)
498 return "none";
499 else
500 return e->vfsent_name;
502 #endif /* MOUNTED_VMOUNT */
504 /* --------------------------------------------------------------------------------------------- */
506 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
508 /* Return the device number from MOUNT_OPTIONS, if possible.
509 Otherwise return (dev_t) -1. */
511 /* --------------------------------------------------------------------------------------------- */
513 static dev_t
514 dev_from_mount_options (char const *mount_options)
516 /* GNU/Linux allows file system implementations to define their own
517 meaning for "dev=" mount options, so don't trust the meaning
518 here. */
519 #ifndef __linux__
520 static char const dev_pattern[] = ",dev=";
521 char const *devopt = strstr (mount_options, dev_pattern);
523 if (devopt)
525 char const *optval = devopt + sizeof (dev_pattern) - 1;
526 char *optvalend;
527 unsigned long int dev;
528 errno = 0;
529 dev = strtoul (optval, &optvalend, 16);
530 if (optval != optvalend
531 && (*optvalend == '\0' || *optvalend == ',')
532 && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
533 return dev;
535 #endif
537 (void) mount_options;
538 return -1;
541 #endif
543 /* --------------------------------------------------------------------------------------------- */
545 #if defined _AIX && defined _I386
546 /* AIX PS/2 does not supply statfs. */
548 static int
549 statfs (char *file, struct statfs *fsb)
551 struct stat stats;
552 struct dustat fsd;
554 if (stat (file, &stats) != 0)
555 return -1;
556 if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
557 return -1;
558 fsb->f_type = 0;
559 fsb->f_bsize = fsd.du_bsize;
560 fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
561 fsb->f_bfree = fsd.du_tfree;
562 fsb->f_bavail = fsd.du_tfree;
563 fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb;
564 fsb->f_ffree = fsd.du_tinode;
565 fsb->f_fsid.val[0] = fsd.du_site;
566 fsb->f_fsid.val[1] = fsd.du_pckno;
567 return 0;
570 #endif /* _AIX && _I386 */
572 /* --------------------------------------------------------------------------------------------- */
574 /* Return a list of the currently mounted file systems, or NULL on error.
575 Add each entry to the tail of the list so that they stay in order.
576 If NEED_FS_TYPE is true, ensure that the file system type fields in
577 the returned list are valid. Otherwise, they might not be. */
579 static struct mount_entry *
580 read_file_system_list (int need_fs_type)
582 struct mount_entry *mount_list;
583 struct mount_entry *me;
584 struct mount_entry **mtail = &mount_list;
586 #ifdef MOUNTED_LISTMNTENT
588 struct tabmntent *mntlist, *p;
589 struct mntent *mnt;
590 struct mount_entry *me;
592 /* the third and fourth arguments could be used to filter mounts,
593 but Crays doesn't seem to have any mounts that we want to
594 remove. Specifically, automount create normal NFS mounts.
597 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
598 return NULL;
599 for (p = mntlist; p; p = p->next)
601 mnt = p->ment;
602 me = g_malloc (sizeof (*me));
603 me->me_devname = g_strdup (mnt->mnt_fsname);
604 me->me_mountdir = g_strdup (mnt->mnt_dir);
605 me->me_type = g_strdup (mnt->mnt_type);
606 me->me_type_malloced = 1;
607 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
608 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
609 me->me_dev = -1;
610 *mtail = me;
611 mtail = &me->me_next;
613 freemntlist (mntlist);
615 #endif
617 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
619 struct mntent *mnt;
620 const char *table = MOUNTED;
621 FILE *fp;
623 fp = setmntent (table, "r");
624 if (fp == NULL)
625 return NULL;
627 while ((mnt = getmntent (fp)))
629 me = g_malloc (sizeof (*me));
630 me->me_devname = g_strdup (mnt->mnt_fsname);
631 me->me_mountdir = g_strdup (mnt->mnt_dir);
632 me->me_type = g_strdup (mnt->mnt_type);
633 me->me_type_malloced = 1;
634 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
635 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
636 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
638 /* Add to the linked list. */
639 *mtail = me;
640 mtail = &me->me_next;
643 if (endmntent (fp) == 0)
644 goto free_then_fail;
646 #endif /* MOUNTED_GETMNTENT1. */
648 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
650 struct statfs *fsp;
651 int entries;
653 entries = getmntinfo (&fsp, MNT_NOWAIT);
654 if (entries < 0)
655 return NULL;
656 for (; entries-- > 0; fsp++)
658 char *fs_type = fsp_to_string (fsp);
660 me = g_malloc (sizeof (*me));
661 me->me_devname = g_strdup (fsp->f_mntfromname);
662 me->me_mountdir = g_strdup (fsp->f_mntonname);
663 me->me_type = fs_type;
664 me->me_type_malloced = 0;
665 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
666 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
667 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
669 /* Add to the linked list. */
670 *mtail = me;
671 mtail = &me->me_next;
674 #endif /* MOUNTED_GETMNTINFO */
676 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
678 struct statvfs *fsp;
679 int entries;
681 entries = getmntinfo (&fsp, MNT_NOWAIT);
682 if (entries < 0)
683 return NULL;
684 for (; entries-- > 0; fsp++)
686 me = g_malloc (sizeof (*me));
687 me->me_devname = g_strdup (fsp->f_mntfromname);
688 me->me_mountdir = g_strdup (fsp->f_mntonname);
689 me->me_type = g_strdup (fsp->f_fstypename);
690 me->me_type_malloced = 1;
691 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
692 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
693 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
695 /* Add to the linked list. */
696 *mtail = me;
697 mtail = &me->me_next;
700 #endif /* MOUNTED_GETMNTINFO2 */
702 #ifdef MOUNTED_GETMNT /* Ultrix. */
704 int offset = 0;
705 int val;
706 struct fs_data fsd;
708 while (errno = 0, 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, (char *) 0)))
710 me = g_malloc (sizeof (*me));
711 me->me_devname = g_strdup (fsd.fd_req.devname);
712 me->me_mountdir = g_strdup (fsd.fd_req.path);
713 me->me_type = gt_names[fsd.fd_req.fstype];
714 me->me_type_malloced = 0;
715 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
716 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
717 me->me_dev = fsd.fd_req.dev;
719 /* Add to the linked list. */
720 *mtail = me;
721 mtail = &me->me_next;
723 if (val < 0)
724 goto free_then_fail;
726 #endif /* MOUNTED_GETMNT. */
728 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
730 /* The next_dev() and fs_stat_dev() system calls give the list of
731 all file systems, including the information returned by statvfs()
732 (fs type, total blocks, free blocks etc.), but without the mount
733 point. But on BeOS all file systems except / are mounted in the
734 rootfs, directly under /.
735 The directory name of the mount point is often, but not always,
736 identical to the volume name of the device.
737 We therefore get the list of subdirectories of /, and the list
738 of all file systems, and match the two lists. */
740 DIR *dirp;
741 struct rootdir_entry
743 char *name;
744 dev_t dev;
745 ino_t ino;
746 struct rootdir_entry *next;
748 struct rootdir_entry *rootdir_list;
749 struct rootdir_entry **rootdir_tail;
750 int32 pos;
751 dev_t dev;
752 fs_info fi;
754 /* All volumes are mounted in the rootfs, directly under /. */
755 rootdir_list = NULL;
756 rootdir_tail = &rootdir_list;
757 dirp = opendir ("/");
758 if (dirp)
760 struct dirent *d;
762 while ((d = readdir (dirp)) != NULL)
764 char *name;
765 struct stat statbuf;
767 if (strcmp (d->d_name, "..") == 0)
768 continue;
770 if (strcmp (d->d_name, ".") == 0)
771 name = g_strdup ("/");
772 else
773 name = g_strconcat ("/", d->d_name, (char *) NULL);
775 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
777 struct rootdir_entry *re = g_malloc (sizeof (*re));
778 re->name = name;
779 re->dev = statbuf.st_dev;
780 re->ino = statbuf.st_ino;
782 /* Add to the linked list. */
783 *rootdir_tail = re;
784 rootdir_tail = &re->next;
786 else
787 g_free (name);
789 closedir (dirp);
791 *rootdir_tail = NULL;
793 for (pos = 0; (dev = next_dev (&pos)) >= 0;)
794 if (fs_stat_dev (dev, &fi) >= 0)
796 /* Note: fi.dev == dev. */
797 struct rootdir_entry *re;
799 for (re = rootdir_list; re; re = re->next)
800 if (re->dev == fi.dev && re->ino == fi.root)
801 break;
803 me = g_malloc (sizeof (*me));
804 me->me_devname = g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
805 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
806 me->me_type = g_strdup (fi.fsh_name);
807 me->me_type_malloced = 1;
808 me->me_dev = fi.dev;
809 me->me_dummy = 0;
810 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
812 /* Add to the linked list. */
813 *mtail = me;
814 mtail = &me->me_next;
816 *mtail = NULL;
818 while (rootdir_list != NULL)
820 struct rootdir_entry *re = rootdir_list;
821 rootdir_list = re->next;
822 g_free (re->name);
823 g_free (re);
826 #endif /* MOUNTED_FS_STAT_DEV */
828 #ifdef MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
830 int numsys, counter;
831 size_t bufsize;
832 struct statfs *stats;
834 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
835 if (numsys < 0)
836 return NULL;
837 if (SIZE_MAX / sizeof (*stats) <= numsys)
839 fprintf (stderr, "%s\n", _("Memory exhausted!"));
840 exit (EXIT_FAILURE);
843 bufsize = (1 + numsys) * sizeof (*stats);
844 stats = g_malloc (bufsize);
845 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
847 if (numsys < 0)
849 g_free (stats);
850 return NULL;
853 for (counter = 0; counter < numsys; counter++)
855 me = g_malloc (sizeof (*me));
856 me->me_devname = g_strdup (stats[counter].f_mntfromname);
857 me->me_mountdir = g_strdup (stats[counter].f_mntonname);
858 me->me_type = g_strdup (FS_TYPE (stats[counter]));
859 me->me_type_malloced = 1;
860 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
861 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
862 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
864 /* Add to the linked list. */
865 *mtail = me;
866 mtail = &me->me_next;
869 g_free (stats);
871 #endif /* MOUNTED_GETFSSTAT */
873 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
875 struct mnttab mnt;
876 char *table = "/etc/mnttab";
877 FILE *fp;
879 fp = fopen (table, "r");
880 if (fp == NULL)
881 return NULL;
883 while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
885 me = g_malloc (sizeof (*me));
886 #ifdef GETFSTYP /* SVR3. */
887 me->me_devname = g_strdup (mnt.mt_dev);
888 #else
889 me->me_devname = g_strconcat ("/dev/", mnt.mt_dev, (char *) NULL);
890 #endif
891 me->me_mountdir = g_strdup (mnt.mt_filsys);
892 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
893 me->me_type = "";
894 me->me_type_malloced = 0;
895 #ifdef GETFSTYP /* SVR3. */
896 if (need_fs_type)
898 struct statfs fsd;
899 char typebuf[FSTYPSZ];
901 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
902 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
904 me->me_type = g_strdup (typebuf);
905 me->me_type_malloced = 1;
908 #endif
909 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
910 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
912 /* Add to the linked list. */
913 *mtail = me;
914 mtail = &me->me_next;
917 if (ferror (fp))
919 /* The last fread() call must have failed. */
920 int saved_errno = errno;
921 fclose (fp);
922 errno = saved_errno;
923 goto free_then_fail;
926 if (fclose (fp) == EOF)
927 goto free_then_fail;
929 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
931 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
933 struct mntent **mnttbl = getmnttbl (), **ent;
934 for (ent = mnttbl; *ent; ent++)
936 me = g_malloc (sizeof (*me));
937 me->me_devname = g_strdup ((*ent)->mt_resource);
938 me->me_mountdir = g_strdup ((*ent)->mt_directory);
939 me->me_type = g_strdup ((*ent)->mt_fstype);
940 me->me_type_malloced = 1;
941 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
942 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
943 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
945 /* Add to the linked list. */
946 *mtail = me;
947 mtail = &me->me_next;
949 endmnttbl ();
951 #endif
953 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
955 struct mnttab mnt;
956 char *table = MNTTAB;
957 FILE *fp;
958 int ret;
959 int lockfd = -1;
961 #if defined F_RDLCK && defined F_SETLKW
962 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
963 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
964 for this file name, we should use their macro name instead.
965 (Why not just lock MNTTAB directly? We don't know.) */
966 #ifndef MNTTAB_LOCK
967 #define MNTTAB_LOCK "/etc/.mnttab.lock"
968 #endif
969 lockfd = open (MNTTAB_LOCK, O_RDONLY);
970 if (0 <= lockfd)
972 struct flock flock;
973 flock.l_type = F_RDLCK;
974 flock.l_whence = SEEK_SET;
975 flock.l_start = 0;
976 flock.l_len = 0;
977 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
978 if (errno != EINTR)
980 int saved_errno = errno;
981 close (lockfd);
982 errno = saved_errno;
983 return NULL;
986 else if (errno != ENOENT)
987 return NULL;
988 #endif
990 errno = 0;
991 fp = fopen (table, "r");
992 if (fp == NULL)
993 ret = errno;
994 else
996 while ((ret = getmntent (fp, &mnt)) == 0)
998 me = g_malloc (sizeof (*me));
999 me->me_devname = g_strdup (mnt.mnt_special);
1000 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1001 me->me_type = g_strdup (mnt.mnt_fstype);
1002 me->me_type_malloced = 1;
1003 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1004 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1005 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1007 /* Add to the linked list. */
1008 *mtail = me;
1009 mtail = &me->me_next;
1012 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1015 if (0 <= lockfd && close (lockfd) != 0)
1016 ret = errno;
1018 if (0 <= ret)
1020 errno = ret;
1021 goto free_then_fail;
1024 #endif /* MOUNTED_GETMNTENT2. */
1026 #ifdef MOUNTED_VMOUNT /* AIX. */
1028 int bufsize;
1029 char *entries, *thisent;
1030 struct vmount *vmp;
1031 int n_entries;
1032 int i;
1034 /* Ask how many bytes to allocate for the mounted file system info. */
1035 if (mntctl (MCTL_QUERY, sizeof (bufsize), (struct vmount *) &bufsize) != 0)
1036 return NULL;
1037 entries = g_malloc (bufsize);
1039 /* Get the list of mounted file systems. */
1040 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
1041 if (n_entries < 0)
1043 int saved_errno = errno;
1044 g_free (entries);
1045 errno = saved_errno;
1046 return NULL;
1049 for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1051 char *options, *ignore;
1053 vmp = (struct vmount *) thisent;
1054 me = g_malloc (sizeof (*me));
1055 if (vmp->vmt_flags & MNT_REMOTE)
1057 char *host, *dir;
1059 me->me_remote = 1;
1060 /* Prepend the remote dirname. */
1061 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1062 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1063 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1065 else
1067 me->me_remote = 0;
1068 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1070 me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1071 me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1072 me->me_type_malloced = 1;
1073 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1074 ignore = strstr (options, "ignore");
1075 me->me_dummy = (ignore
1076 && (ignore == options || ignore[-1] == ',')
1077 && (ignore[sizeof ("ignore") - 1] == ','
1078 || ignore[sizeof ("ignore") - 1] == '\0'));
1079 me->me_dev = (dev_t) (-1); /* vmt_fsid might be the info we want. */
1081 /* Add to the linked list. */
1082 *mtail = me;
1083 mtail = &me->me_next;
1085 g_free (entries);
1087 #endif /* MOUNTED_VMOUNT. */
1090 #ifdef MOUNTED_INTERIX_STATVFS
1092 DIR *dirp = opendir ("/dev/fs");
1093 char node[9 + NAME_MAX];
1095 if (!dirp)
1096 goto free_then_fail;
1098 while (1)
1100 struct statvfs dev;
1101 struct dirent entry;
1102 struct dirent *result;
1104 if (readdir_r (dirp, &entry, &result) || result == NULL)
1105 break;
1107 strcpy (node, "/dev/fs/");
1108 strcat (node, entry.d_name);
1110 if (statvfs (node, &dev) == 0)
1112 me = g_malloc (sizeof *me);
1113 me->me_devname = g_strdup (dev.f_mntfromname);
1114 me->me_mountdir = g_strdup (dev.f_mntonname);
1115 me->me_type = g_strdup (dev.f_fstypename);
1116 me->me_type_malloced = 1;
1117 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1118 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1119 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1121 /* Add to the linked list. */
1122 *mtail = me;
1123 mtail = &me->me_next;
1127 #endif /* MOUNTED_INTERIX_STATVFS */
1129 (void) need_fs_type; /* avoid argument-unused warning */
1130 *mtail = NULL;
1131 return mount_list;
1134 free_then_fail:
1136 int saved_errno = errno;
1137 *mtail = NULL;
1139 while (mount_list)
1141 me = mount_list->me_next;
1142 g_free (mount_list->me_devname);
1143 g_free (mount_list->me_mountdir);
1144 if (mount_list->me_type_malloced)
1145 g_free (mount_list->me_type);
1146 g_free (mount_list);
1147 mount_list = me;
1150 errno = saved_errno;
1151 return NULL;
1155 #endif /* HAVE_INFOMOUNT_LIST */
1157 /* --------------------------------------------------------------------------------------------- */
1159 #ifdef HAVE_INFOMOUNT_QNX
1161 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1162 ** this via the following code.
1163 ** Note that, as this is based on CWD, it only fills one mount_entry
1164 ** structure. See my_statfs() in utilunix.c for the "other side" of
1165 ** this hack.
1168 static struct mount_entry *
1169 read_file_system_list (int need_fs_type, int all_fs)
1171 struct _disk_entry de;
1172 struct statfs fs;
1173 int i, fd;
1174 char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1176 static struct mount_entry *me = NULL;
1178 if (me)
1180 g_free (me->me_devname);
1181 g_free (me->me_mountdir);
1182 g_free (me->me_type);
1184 else
1185 me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1187 if (!getcwd (dir, _POSIX_PATH_MAX))
1188 return (NULL);
1190 fd = open (dir, O_RDONLY);
1191 if (fd == -1)
1192 return (NULL);
1194 i = disk_get_entry (fd, &de);
1196 close (fd);
1198 if (i == -1)
1199 return (NULL);
1201 switch (de.disk_type)
1203 case _UNMOUNTED:
1204 tp = "unmounted";
1205 break;
1206 case _FLOPPY:
1207 tp = "Floppy";
1208 break;
1209 case _HARD:
1210 tp = "Hard";
1211 break;
1212 case _RAMDISK:
1213 tp = "Ram";
1214 break;
1215 case _REMOVABLE:
1216 tp = "Removable";
1217 break;
1218 case _TAPE:
1219 tp = "Tape";
1220 break;
1221 case _CDROM:
1222 tp = "CDROM";
1223 break;
1224 default:
1225 tp = "unknown";
1228 if (fsys_get_mount_dev (dir, &dev) == -1)
1229 return (NULL);
1231 if (fsys_get_mount_pt (dev, &dir) == -1)
1232 return (NULL);
1234 me->me_devname = g_strdup (dev);
1235 me->me_mountdir = g_strdup (dir);
1236 me->me_type = g_strdup (tp);
1237 me->me_dev = de.disk_type;
1239 #ifdef DEBUG
1240 fprintf (stderr,
1241 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1242 de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1243 fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1244 fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1245 #endif /* DEBUG */
1247 return (me);
1249 #endif /* HAVE_INFOMOUNT_QNX */
1251 /* --------------------------------------------------------------------------------------------- */
1253 #ifdef STAT_READ_FILSYS /* SVR2 */
1255 /* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
1256 interrupted. Return the actual number of bytes read(written), zero for EOF,
1257 or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
1258 static size_t
1259 safe_read (int fd, void *buf, size_t count)
1261 /* Work around a bug in Tru64 5.1. Attempting to read more than
1262 INT_MAX bytes fails with errno == EINVAL. See
1263 <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
1264 When decreasing COUNT, keep it block-aligned. */
1265 /* *INDENT-OFF* */
1266 enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
1267 /* *INDENT-ON* */
1269 while (TRUE)
1271 ssize_t result;
1273 result = read (fd, buf, count);
1275 if (0 <= result)
1276 return result;
1277 else if (IS_EINTR (errno))
1278 continue;
1279 else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
1280 count = BUGGY_READ_MAXIMUM;
1281 else
1282 return result;
1286 /* --------------------------------------------------------------------------------------------- */
1288 /* Read COUNT bytes at BUF to(from) descriptor FD, retrying if
1289 interrupted or if a partial write(read) occurs. Return the number
1290 of bytes transferred.
1291 When writing, set errno if fewer than COUNT bytes are written.
1292 When reading, if fewer than COUNT bytes are read, you must examine
1293 errno to distinguish failure from EOF (errno == 0). */
1295 static size_t
1296 full_read (int fd, void *buf, size_t count)
1298 size_t total = 0;
1299 char *ptr = (char *) buf;
1301 while (count > 0)
1303 size_t n_rw = safe_read (fd, ptr, count);
1304 if (n_rw == (size_t) (-1))
1305 break;
1306 if (n_rw == 0)
1308 errno = ZERO_BYTE_TRANSFER_ERRNO;
1309 break;
1311 total += n_rw;
1312 ptr += n_rw;
1313 count -= n_rw;
1316 return total;
1319 #endif /* STAT_READ_FILSYS */
1321 /* --------------------------------------------------------------------------------------------- */
1323 #ifdef HAVE_INFOMOUNT
1324 /* Fill in the fields of FSP with information about space usage for
1325 the file system on which FILE resides.
1326 DISK is the device on which FILE is mounted, for space-getting
1327 methods that need to know it.
1328 Return 0 if successful, -1 if not. When returning -1, ensure that
1329 ERRNO is either a system error value, or zero if DISK is NULL
1330 on a system that requires a non-NULL value. */
1331 static int
1332 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
1334 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1336 if (statvfs_works ())
1338 struct statvfs vfsd;
1340 if (statvfs (file, &vfsd) < 0)
1341 return -1;
1343 /* f_frsize isn't guaranteed to be supported. */
1344 fsp->fsu_blocksize = (vfsd.f_frsize
1345 ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1346 : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1348 fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1349 fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1350 fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1351 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1352 fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1353 fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1355 else
1356 #endif
1359 #if defined STAT_STATVFS64 /* AIX */
1361 struct statvfs64 fsd;
1363 if (statvfs64 (file, &fsd) < 0)
1364 return -1;
1366 /* f_frsize isn't guaranteed to be supported. */
1367 /* *INDENT-OFF* */
1368 fsp->fsu_blocksize = fsd.f_frsize
1369 ? PROPAGATE_ALL_ONES (fsd.f_frsize)
1370 : PROPAGATE_ALL_ONES (fsd.f_bsize);
1371 /* *INDENT-ON* */
1373 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1375 struct fs_data fsd;
1377 if (statfs (file, &fsd) != 1)
1378 return -1;
1380 fsp->fsu_blocksize = 1024;
1381 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
1382 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
1383 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
1384 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
1385 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
1386 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
1388 #elif defined STAT_READ_FILSYS /* SVR2 */
1389 #ifndef SUPERBOFF
1390 #define SUPERBOFF (SUPERB * 512)
1391 #endif
1393 struct filsys fsd;
1394 int fd;
1396 if (!disk)
1398 errno = 0;
1399 return -1;
1402 fd = open (disk, O_RDONLY);
1403 if (fd < 0)
1404 return -1;
1405 lseek (fd, (off_t) SUPERBOFF, 0);
1406 if (full_read (fd, (char *) &fsd, sizeof (fsd)) != sizeof (fsd))
1408 close (fd);
1409 return -1;
1411 close (fd);
1413 fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
1414 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
1415 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
1416 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
1417 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
1418 fsp->fsu_files = (fsd.s_isize == -1
1419 ? UINTMAX_MAX : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
1420 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);
1422 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1424 struct statfs fsd;
1426 if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1427 return -1;
1429 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1431 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1433 struct statfs fsd;
1435 if (statfs (file, &fsd) < 0)
1436 return -1;
1438 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1440 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1441 Mac OS X < 10.4, FreeBSD < 5.0, \
1442 NetBSD < 3.0, OpenBSD < 4.4 */
1444 struct statfs fsd;
1446 if (statfs (file, &fsd) < 0)
1447 return -1;
1449 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1451 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1453 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1454 struct statfs are truncated to 2GB. These conditions detect that
1455 truncation, presumably without botching the 4.1.1 case, in which
1456 the values are not truncated. The correct counts are stored in
1457 undocumented spare fields. */
1458 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1460 fsd.f_blocks = fsd.f_spare[0];
1461 fsd.f_bfree = fsd.f_spare[1];
1462 fsd.f_bavail = fsd.f_spare[2];
1464 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1466 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1468 struct statfs fsd;
1470 if (statfs (file, &fsd) < 0)
1471 return -1;
1473 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1475 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \
1476 Dolphin */
1478 #if !defined _AIX && !defined _SEQUENT_ && !defined DOLPHIN
1479 #define f_bavail f_bfree
1480 #endif
1482 struct statfs fsd;
1484 if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1485 return -1;
1487 /* Empirically, the block counts on most SVR3 and SVR3-derived
1488 systems seem to always be in terms of 512-byte blocks,
1489 no matter what value f_bsize has. */
1490 #if defined _AIX || defined _CRAY
1491 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1492 #else
1493 fsp->fsu_blocksize = 512;
1494 #endif
1496 #endif
1498 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1499 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1500 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1502 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1503 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1504 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1505 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1506 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1507 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1509 #endif
1512 (void) disk; /* avoid argument-unused warning */
1514 return 0;
1516 #endif /* HAVE_INFOMOUNT */
1518 /* --------------------------------------------------------------------------------------------- */
1519 /*** public functions ****************************************************************************/
1520 /* --------------------------------------------------------------------------------------------- */
1522 void
1523 free_my_statfs (void)
1525 #ifdef HAVE_INFOMOUNT_LIST
1526 while (mc_mount_list != NULL)
1528 struct mount_entry *next;
1530 next = mc_mount_list->me_next;
1531 free_mount_entry (mc_mount_list);
1532 mc_mount_list = next;
1535 mc_mount_list = NULL;
1536 #endif /* HAVE_INFOMOUNT_LIST */
1539 /* --------------------------------------------------------------------------------------------- */
1541 void
1542 init_my_statfs (void)
1544 #ifdef HAVE_INFOMOUNT_LIST
1545 free_my_statfs ();
1546 mc_mount_list = read_file_system_list (1);
1547 #endif /* HAVE_INFOMOUNT_LIST */
1550 /* --------------------------------------------------------------------------------------------- */
1552 void
1553 my_statfs (struct my_statfs *myfs_stats, const char *path)
1555 #ifdef HAVE_INFOMOUNT_LIST
1556 size_t i, len = 0;
1557 struct mount_entry *entry = NULL;
1558 struct mount_entry *temp = mc_mount_list;
1559 struct fs_usage fs_use;
1561 while (temp)
1563 i = strlen (temp->me_mountdir);
1564 if (i > len && (strncmp (path, temp->me_mountdir, i) == 0))
1565 if (!entry || (path[i] == PATH_SEP || path[i] == '\0'))
1567 len = i;
1568 entry = temp;
1570 temp = temp->me_next;
1573 if (entry)
1575 memset (&fs_use, 0, sizeof (struct fs_usage));
1576 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1578 myfs_stats->type = entry->me_dev;
1579 myfs_stats->typename = entry->me_type;
1580 myfs_stats->mpoint = entry->me_mountdir;
1581 myfs_stats->device = entry->me_devname;
1582 myfs_stats->avail =
1583 ((uintmax_t) (getuid ()? fs_use.fsu_bavail : fs_use.fsu_bfree) *
1584 fs_use.fsu_blocksize) >> 10;
1585 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1586 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1587 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1589 else
1590 #endif /* HAVE_INFOMOUNT_LIST */
1592 #ifdef HAVE_INFOMOUNT_QNX
1594 ** This is the "other side" of the hack to read_file_system_list() in
1595 ** mountlist.c.
1596 ** It's not the most efficient approach, but consumes less memory. It
1597 ** also accomodates QNX's ability to mount filesystems on the fly.
1599 struct mount_entry *entry;
1600 struct fs_usage fs_use;
1602 entry = read_file_system_list (0, 0);
1603 if (entry != NULL)
1605 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1607 myfs_stats->type = entry->me_dev;
1608 myfs_stats->typename = entry->me_type;
1609 myfs_stats->mpoint = entry->me_mountdir;
1610 myfs_stats->device = entry->me_devname;
1612 myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1613 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1614 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1615 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1617 else
1618 #endif /* HAVE_INFOMOUNT_QNX */
1620 myfs_stats->type = 0;
1621 myfs_stats->mpoint = "unknown";
1622 myfs_stats->device = "unknown";
1623 myfs_stats->avail = 0;
1624 myfs_stats->total = 0;
1625 myfs_stats->nfree = 0;
1626 myfs_stats->nodes = 0;
1630 /* --------------------------------------------------------------------------------------------- */