Indentation.
[midnight-commander.git] / src / filemanager / mountlist.c
blobdb98e811afd0bc9af96a9dd83cbc89bdc7cf74e4
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 Linux 2.6/3.x */ \
209 || strcmp (Fs_type, "debugfs") == 0 \
210 || strcmp (Fs_type, "devpts") == 0 \
211 || strcmp (Fs_type, "devtmpfs") == 0 \
212 || strcmp (Fs_type, "fusectl") == 0 \
213 || strcmp (Fs_type, "mqueue") == 0 \
214 || strcmp (Fs_type, "rpc_pipefs") == 0 \
215 || strcmp (Fs_type, "sysfs") == 0 \
216 /* FreeBSD, Linux 2.4 */ \
217 || strcmp (Fs_type, "devfs") == 0 \
218 /* for NetBSD 3.0 */ \
219 || strcmp (Fs_type, "kernfs") == 0 \
220 /* for Irix 6.5 */ \
221 || strcmp (Fs_type, "ignore") == 0)
223 /* Historically, we have marked as "dummy" any file system of type "none",
224 but now that programs like du need to know about bind-mounted directories,
225 we grant an exception to any with "bind" in its list of mount options.
226 I.e., those are *not* dummy entries. */
227 #ifdef MOUNTED_GETMNTENT1
228 #define ME_DUMMY(Fs_name, Fs_type, Fs_ent) \
229 (ME_DUMMY_0 (Fs_name, Fs_type) \
230 || (strcmp (Fs_type, "none") == 0 \
231 && !hasmntopt (Fs_ent, "bind")))
232 #else
233 #define ME_DUMMY(Fs_name, Fs_type) \
234 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
235 #endif
237 #ifdef __CYGWIN__
238 #include <windows.h>
239 #define ME_REMOTE me_remote
240 /* All cygwin mount points include `:' or start with `//'; so it
241 requires a native Windows call to determine remote disks. */
242 static int
243 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
245 if (fs_name[0] && fs_name[1] == ':')
247 char drive[4];
248 sprintf (drive, "%c:\\", fs_name[0]);
249 switch (GetDriveType (drive))
251 case DRIVE_REMOVABLE:
252 case DRIVE_FIXED:
253 case DRIVE_CDROM:
254 case DRIVE_RAMDISK:
255 return 0;
258 return 1;
260 #endif
261 #ifndef ME_REMOTE
262 /* A file system is `remote' if its Fs_name contains a `:'
263 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
264 #define ME_REMOTE(Fs_name, Fs_type) \
265 (strchr (Fs_name, ':') != NULL \
266 || ((Fs_name)[0] == '/' \
267 && (Fs_name)[1] == '/' \
268 && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "cifs") == 0)))
269 #endif
271 /* Many space usage primitives use all 1 bits to denote a value that is
272 not applicable or unknown. Propagate this information by returning
273 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
274 is unsigned and narrower than uintmax_t. */
275 #define PROPAGATE_ALL_ONES(x) \
276 ((sizeof (x) < sizeof (uintmax_t) \
277 && (~ (x) == (sizeof (x) < sizeof (int) \
278 ? - (1 << (sizeof (x) * CHAR_BIT)) \
279 : 0))) \
280 ? UINTMAX_MAX : (uintmax_t) (x))
282 /* Extract the top bit of X as an uintmax_t value. */
283 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
285 /* If a value is negative, many space usage primitives store it into an
286 integer variable by assignment, even if the variable's type is unsigned.
287 So, if a space usage variable X's top bit is set, convert X to the
288 uintmax_t value V such that (- (uintmax_t) V) is the negative of
289 the original value. If X's top bit is clear, just yield X.
290 Use PROPAGATE_TOP_BIT if the original value might be negative;
291 otherwise, use PROPAGATE_ALL_ONES. */
292 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
294 #ifdef STAT_STATVFS
295 /* Return true if statvfs works. This is false for statvfs on systems
296 with GNU libc on Linux kernels before 2.6.36, which stats all
297 preceding entries in /proc/mounts; that makes df hang if even one
298 of the corresponding file systems is hard-mounted but not available. */
299 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
300 /* The FRSIZE fallback is not required in this case. */
301 #undef STAT_STATFS2_FRSIZE
302 static int
303 statvfs_works (void)
305 return 1;
307 #else
308 #include <string.h> /* for strverscmp */
309 #include <sys/utsname.h>
310 #include <sys/statfs.h>
311 #define STAT_STATFS2_BSIZE 1
313 static int
314 statvfs_works (void)
316 static int statvfs_works_cache = -1;
317 struct utsname name;
319 if (statvfs_works_cache < 0)
320 statvfs_works_cache = (uname (&name) == 0 && 0 <= strverscmp (name.release, "2.6.36"));
321 return statvfs_works_cache;
323 #endif
324 #endif
326 #ifdef STAT_READ_FILSYS /* SVR2 */
327 /* Set errno to zero upon EOF. */
328 #define ZERO_BYTE_TRANSFER_ERRNO 0
330 #ifdef EINTR
331 #define IS_EINTR(x) ((x) == EINTR)
332 #else
333 #define IS_EINTR(x) 0
334 #endif
335 #endif /* STAT_READ_FILSYS */
337 /*** file scope type declarations ****************************************************************/
339 /* A mount table entry. */
340 struct mount_entry
342 char *me_devname; /* Device node name, including "/dev/". */
343 char *me_mountdir; /* Mount point directory name. */
344 char *me_type; /* "nfs", "4.2", etc. */
345 dev_t me_dev; /* Device number of me_mountdir. */
346 unsigned int me_dummy:1; /* Nonzero for dummy file systems. */
347 unsigned int me_remote:1; /* Nonzero for remote fileystems. */
348 unsigned int me_type_malloced:1; /* Nonzero if me_type was malloced. */
349 struct mount_entry *me_next;
352 struct fs_usage
354 uintmax_t fsu_blocksize; /* Size of a block. */
355 uintmax_t fsu_blocks; /* Total blocks. */
356 uintmax_t fsu_bfree; /* Free blocks available to superuser. */
357 uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
358 int fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
359 uintmax_t fsu_files; /* Total file nodes. */
360 uintmax_t fsu_ffree; /* Free file nodes. */
363 /*** file scope variables ************************************************************************/
365 #ifdef HAVE_INFOMOUNT_LIST
366 static struct mount_entry *mc_mount_list = NULL;
367 #endif /* HAVE_INFOMOUNT_LIST */
369 /*** file scope functions ************************************************************************/
370 /* --------------------------------------------------------------------------------------------- */
372 #ifdef HAVE_INFOMOUNT_LIST
373 static void
374 free_mount_entry (struct mount_entry *me)
376 if (!me)
377 return;
378 g_free (me->me_devname);
379 g_free (me->me_mountdir);
380 if (me->me_type_malloced)
381 g_free (me->me_type);
382 g_free (me);
385 /* --------------------------------------------------------------------------------------------- */
387 #ifdef MOUNTED_GETMNTINFO
389 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
390 static char *
391 fstype_to_string (short int t)
393 switch (t)
395 #ifdef MOUNT_PC
396 case MOUNT_PC:
397 return "pc";
398 #endif
399 #ifdef MOUNT_MFS
400 case MOUNT_MFS:
401 return "mfs";
402 #endif
403 #ifdef MOUNT_LO
404 case MOUNT_LO:
405 return "lo";
406 #endif
407 #ifdef MOUNT_TFS
408 case MOUNT_TFS:
409 return "tfs";
410 #endif
411 #ifdef MOUNT_TMP
412 case MOUNT_TMP:
413 return "tmp";
414 #endif
415 #ifdef MOUNT_UFS
416 case MOUNT_UFS:
417 return "ufs";
418 #endif
419 #ifdef MOUNT_NFS
420 case MOUNT_NFS:
421 return "nfs";
422 #endif
423 #ifdef MOUNT_MSDOS
424 case MOUNT_MSDOS:
425 return "msdos";
426 #endif
427 #ifdef MOUNT_LFS
428 case MOUNT_LFS:
429 return "lfs";
430 #endif
431 #ifdef MOUNT_LOFS
432 case MOUNT_LOFS:
433 return "lofs";
434 #endif
435 #ifdef MOUNT_FDESC
436 case MOUNT_FDESC:
437 return "fdesc";
438 #endif
439 #ifdef MOUNT_PORTAL
440 case MOUNT_PORTAL:
441 return "portal";
442 #endif
443 #ifdef MOUNT_NULL
444 case MOUNT_NULL:
445 return "null";
446 #endif
447 #ifdef MOUNT_UMAP
448 case MOUNT_UMAP:
449 return "umap";
450 #endif
451 #ifdef MOUNT_KERNFS
452 case MOUNT_KERNFS:
453 return "kernfs";
454 #endif
455 #ifdef MOUNT_PROCFS
456 case MOUNT_PROCFS:
457 return "procfs";
458 #endif
459 #ifdef MOUNT_AFS
460 case MOUNT_AFS:
461 return "afs";
462 #endif
463 #ifdef MOUNT_CD9660
464 case MOUNT_CD9660:
465 return "cd9660";
466 #endif
467 #ifdef MOUNT_UNION
468 case MOUNT_UNION:
469 return "union";
470 #endif
471 #ifdef MOUNT_DEVFS
472 case MOUNT_DEVFS:
473 return "devfs";
474 #endif
475 #ifdef MOUNT_EXT2FS
476 case MOUNT_EXT2FS:
477 return "ext2fs";
478 #endif
479 default:
480 return "?";
483 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
485 /* --------------------------------------------------------------------------------------------- */
487 static char *
488 fsp_to_string (const struct statfs *fsp)
490 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
491 return (char *) (fsp->f_fstypename);
492 #else
493 return fstype_to_string (fsp->f_type);
494 #endif
496 #endif /* MOUNTED_GETMNTINFO */
498 /* --------------------------------------------------------------------------------------------- */
500 #ifdef MOUNTED_VMOUNT /* AIX. */
501 static char *
502 fstype_to_string (int t)
504 struct vfs_ent *e;
506 e = getvfsbytype (t);
507 if (!e || !e->vfsent_name)
508 return "none";
509 else
510 return e->vfsent_name;
512 #endif /* MOUNTED_VMOUNT */
514 /* --------------------------------------------------------------------------------------------- */
516 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
518 /* Return the device number from MOUNT_OPTIONS, if possible.
519 Otherwise return (dev_t) -1. */
521 /* --------------------------------------------------------------------------------------------- */
523 static dev_t
524 dev_from_mount_options (char const *mount_options)
526 /* GNU/Linux allows file system implementations to define their own
527 meaning for "dev=" mount options, so don't trust the meaning
528 here. */
529 #ifndef __linux__
530 static char const dev_pattern[] = ",dev=";
531 char const *devopt = strstr (mount_options, dev_pattern);
533 if (devopt)
535 char const *optval = devopt + sizeof (dev_pattern) - 1;
536 char *optvalend;
537 unsigned long int dev;
538 errno = 0;
539 dev = strtoul (optval, &optvalend, 16);
540 if (optval != optvalend
541 && (*optvalend == '\0' || *optvalend == ',')
542 && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
543 return dev;
545 #endif
547 (void) mount_options;
548 return -1;
551 #endif
553 /* --------------------------------------------------------------------------------------------- */
555 #if defined _AIX && defined _I386
556 /* AIX PS/2 does not supply statfs. */
558 static int
559 statfs (char *file, struct statfs *fsb)
561 struct stat stats;
562 struct dustat fsd;
564 if (stat (file, &stats) != 0)
565 return -1;
566 if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
567 return -1;
568 fsb->f_type = 0;
569 fsb->f_bsize = fsd.du_bsize;
570 fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
571 fsb->f_bfree = fsd.du_tfree;
572 fsb->f_bavail = fsd.du_tfree;
573 fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb;
574 fsb->f_ffree = fsd.du_tinode;
575 fsb->f_fsid.val[0] = fsd.du_site;
576 fsb->f_fsid.val[1] = fsd.du_pckno;
577 return 0;
580 #endif /* _AIX && _I386 */
582 /* --------------------------------------------------------------------------------------------- */
584 /* Return a list of the currently mounted file systems, or NULL on error.
585 Add each entry to the tail of the list so that they stay in order.
586 If NEED_FS_TYPE is true, ensure that the file system type fields in
587 the returned list are valid. Otherwise, they might not be. */
589 static struct mount_entry *
590 read_file_system_list (int need_fs_type)
592 struct mount_entry *mount_list;
593 struct mount_entry *me;
594 struct mount_entry **mtail = &mount_list;
596 #ifdef MOUNTED_LISTMNTENT
598 struct tabmntent *mntlist, *p;
599 struct mntent *mnt;
600 struct mount_entry *me;
602 /* the third and fourth arguments could be used to filter mounts,
603 but Crays doesn't seem to have any mounts that we want to
604 remove. Specifically, automount create normal NFS mounts.
607 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
608 return NULL;
609 for (p = mntlist; p; p = p->next)
611 mnt = p->ment;
612 me = g_malloc (sizeof (*me));
613 me->me_devname = g_strdup (mnt->mnt_fsname);
614 me->me_mountdir = g_strdup (mnt->mnt_dir);
615 me->me_type = g_strdup (mnt->mnt_type);
616 me->me_type_malloced = 1;
617 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
618 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
619 me->me_dev = -1;
620 *mtail = me;
621 mtail = &me->me_next;
623 freemntlist (mntlist);
625 #endif
627 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
629 struct mntent *mnt;
630 const char *table = MOUNTED;
631 FILE *fp;
633 fp = setmntent (table, "r");
634 if (fp == NULL)
635 return NULL;
637 while ((mnt = getmntent (fp)))
639 me = g_malloc (sizeof (*me));
640 me->me_devname = g_strdup (mnt->mnt_fsname);
641 me->me_mountdir = g_strdup (mnt->mnt_dir);
642 me->me_type = g_strdup (mnt->mnt_type);
643 me->me_type_malloced = 1;
644 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
645 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
646 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
648 /* Add to the linked list. */
649 *mtail = me;
650 mtail = &me->me_next;
653 if (endmntent (fp) == 0)
654 goto free_then_fail;
656 #endif /* MOUNTED_GETMNTENT1. */
658 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
660 struct statfs *fsp;
661 int entries;
663 entries = getmntinfo (&fsp, MNT_NOWAIT);
664 if (entries < 0)
665 return NULL;
666 for (; entries-- > 0; fsp++)
668 char *fs_type = fsp_to_string (fsp);
670 me = g_malloc (sizeof (*me));
671 me->me_devname = g_strdup (fsp->f_mntfromname);
672 me->me_mountdir = g_strdup (fsp->f_mntonname);
673 me->me_type = fs_type;
674 me->me_type_malloced = 0;
675 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
676 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
677 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
679 /* Add to the linked list. */
680 *mtail = me;
681 mtail = &me->me_next;
684 #endif /* MOUNTED_GETMNTINFO */
686 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
688 struct statvfs *fsp;
689 int entries;
691 entries = getmntinfo (&fsp, MNT_NOWAIT);
692 if (entries < 0)
693 return NULL;
694 for (; entries-- > 0; fsp++)
696 me = g_malloc (sizeof (*me));
697 me->me_devname = g_strdup (fsp->f_mntfromname);
698 me->me_mountdir = g_strdup (fsp->f_mntonname);
699 me->me_type = g_strdup (fsp->f_fstypename);
700 me->me_type_malloced = 1;
701 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
702 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
703 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
705 /* Add to the linked list. */
706 *mtail = me;
707 mtail = &me->me_next;
710 #endif /* MOUNTED_GETMNTINFO2 */
712 #ifdef MOUNTED_GETMNT /* Ultrix. */
714 int offset = 0;
715 int val;
716 struct fs_data fsd;
718 while (errno = 0, 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, (char *) 0)))
720 me = g_malloc (sizeof (*me));
721 me->me_devname = g_strdup (fsd.fd_req.devname);
722 me->me_mountdir = g_strdup (fsd.fd_req.path);
723 me->me_type = gt_names[fsd.fd_req.fstype];
724 me->me_type_malloced = 0;
725 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
726 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
727 me->me_dev = fsd.fd_req.dev;
729 /* Add to the linked list. */
730 *mtail = me;
731 mtail = &me->me_next;
733 if (val < 0)
734 goto free_then_fail;
736 #endif /* MOUNTED_GETMNT. */
738 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
740 /* The next_dev() and fs_stat_dev() system calls give the list of
741 all file systems, including the information returned by statvfs()
742 (fs type, total blocks, free blocks etc.), but without the mount
743 point. But on BeOS all file systems except / are mounted in the
744 rootfs, directly under /.
745 The directory name of the mount point is often, but not always,
746 identical to the volume name of the device.
747 We therefore get the list of subdirectories of /, and the list
748 of all file systems, and match the two lists. */
750 DIR *dirp;
751 struct rootdir_entry
753 char *name;
754 dev_t dev;
755 ino_t ino;
756 struct rootdir_entry *next;
758 struct rootdir_entry *rootdir_list;
759 struct rootdir_entry **rootdir_tail;
760 int32 pos;
761 dev_t dev;
762 fs_info fi;
764 /* All volumes are mounted in the rootfs, directly under /. */
765 rootdir_list = NULL;
766 rootdir_tail = &rootdir_list;
767 dirp = opendir ("/");
768 if (dirp)
770 struct dirent *d;
772 while ((d = readdir (dirp)) != NULL)
774 char *name;
775 struct stat statbuf;
777 if (strcmp (d->d_name, "..") == 0)
778 continue;
780 if (strcmp (d->d_name, ".") == 0)
781 name = g_strdup ("/");
782 else
783 name = g_strconcat ("/", d->d_name, (char *) NULL);
785 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
787 struct rootdir_entry *re = g_malloc (sizeof (*re));
788 re->name = name;
789 re->dev = statbuf.st_dev;
790 re->ino = statbuf.st_ino;
792 /* Add to the linked list. */
793 *rootdir_tail = re;
794 rootdir_tail = &re->next;
796 else
797 g_free (name);
799 closedir (dirp);
801 *rootdir_tail = NULL;
803 for (pos = 0; (dev = next_dev (&pos)) >= 0;)
804 if (fs_stat_dev (dev, &fi) >= 0)
806 /* Note: fi.dev == dev. */
807 struct rootdir_entry *re;
809 for (re = rootdir_list; re; re = re->next)
810 if (re->dev == fi.dev && re->ino == fi.root)
811 break;
813 me = g_malloc (sizeof (*me));
814 me->me_devname =
815 g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
816 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
817 me->me_type = g_strdup (fi.fsh_name);
818 me->me_type_malloced = 1;
819 me->me_dev = fi.dev;
820 me->me_dummy = 0;
821 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
823 /* Add to the linked list. */
824 *mtail = me;
825 mtail = &me->me_next;
827 *mtail = NULL;
829 while (rootdir_list != NULL)
831 struct rootdir_entry *re = rootdir_list;
832 rootdir_list = re->next;
833 g_free (re->name);
834 g_free (re);
837 #endif /* MOUNTED_FS_STAT_DEV */
839 #ifdef MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
841 int numsys, counter;
842 size_t bufsize;
843 struct statfs *stats;
845 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
846 if (numsys < 0)
847 return NULL;
848 if (SIZE_MAX / sizeof (*stats) <= numsys)
850 fprintf (stderr, "%s\n", _("Memory exhausted!"));
851 exit (EXIT_FAILURE);
854 bufsize = (1 + numsys) * sizeof (*stats);
855 stats = g_malloc (bufsize);
856 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
858 if (numsys < 0)
860 g_free (stats);
861 return NULL;
864 for (counter = 0; counter < numsys; counter++)
866 me = g_malloc (sizeof (*me));
867 me->me_devname = g_strdup (stats[counter].f_mntfromname);
868 me->me_mountdir = g_strdup (stats[counter].f_mntonname);
869 me->me_type = g_strdup (FS_TYPE (stats[counter]));
870 me->me_type_malloced = 1;
871 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
872 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
873 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
875 /* Add to the linked list. */
876 *mtail = me;
877 mtail = &me->me_next;
880 g_free (stats);
882 #endif /* MOUNTED_GETFSSTAT */
884 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
886 struct mnttab mnt;
887 char *table = "/etc/mnttab";
888 FILE *fp;
890 fp = fopen (table, "r");
891 if (fp == NULL)
892 return NULL;
894 while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
896 me = g_malloc (sizeof (*me));
897 #ifdef GETFSTYP /* SVR3. */
898 me->me_devname = g_strdup (mnt.mt_dev);
899 #else
900 me->me_devname = g_strconcat ("/dev/", mnt.mt_dev, (char *) NULL);
901 #endif
902 me->me_mountdir = g_strdup (mnt.mt_filsys);
903 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
904 me->me_type = "";
905 me->me_type_malloced = 0;
906 #ifdef GETFSTYP /* SVR3. */
907 if (need_fs_type)
909 struct statfs fsd;
910 char typebuf[FSTYPSZ];
912 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
913 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
915 me->me_type = g_strdup (typebuf);
916 me->me_type_malloced = 1;
919 #endif
920 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
921 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
923 /* Add to the linked list. */
924 *mtail = me;
925 mtail = &me->me_next;
928 if (ferror (fp))
930 /* The last fread() call must have failed. */
931 int saved_errno = errno;
932 fclose (fp);
933 errno = saved_errno;
934 goto free_then_fail;
937 if (fclose (fp) == EOF)
938 goto free_then_fail;
940 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
942 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
944 struct mntent **mnttbl = getmnttbl (), **ent;
945 for (ent = mnttbl; *ent; ent++)
947 me = g_malloc (sizeof (*me));
948 me->me_devname = g_strdup ((*ent)->mt_resource);
949 me->me_mountdir = g_strdup ((*ent)->mt_directory);
950 me->me_type = g_strdup ((*ent)->mt_fstype);
951 me->me_type_malloced = 1;
952 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
953 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
954 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
956 /* Add to the linked list. */
957 *mtail = me;
958 mtail = &me->me_next;
960 endmnttbl ();
962 #endif
964 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
966 struct mnttab mnt;
967 char *table = MNTTAB;
968 FILE *fp;
969 int ret;
970 int lockfd = -1;
972 #if defined F_RDLCK && defined F_SETLKW
973 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
974 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
975 for this file name, we should use their macro name instead.
976 (Why not just lock MNTTAB directly? We don't know.) */
977 #ifndef MNTTAB_LOCK
978 #define MNTTAB_LOCK "/etc/.mnttab.lock"
979 #endif
980 lockfd = open (MNTTAB_LOCK, O_RDONLY);
981 if (0 <= lockfd)
983 struct flock flock;
984 flock.l_type = F_RDLCK;
985 flock.l_whence = SEEK_SET;
986 flock.l_start = 0;
987 flock.l_len = 0;
988 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
989 if (errno != EINTR)
991 int saved_errno = errno;
992 close (lockfd);
993 errno = saved_errno;
994 return NULL;
997 else if (errno != ENOENT)
998 return NULL;
999 #endif
1001 errno = 0;
1002 fp = fopen (table, "r");
1003 if (fp == NULL)
1004 ret = errno;
1005 else
1007 while ((ret = getmntent (fp, &mnt)) == 0)
1009 me = g_malloc (sizeof (*me));
1010 me->me_devname = g_strdup (mnt.mnt_special);
1011 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1012 me->me_type = g_strdup (mnt.mnt_fstype);
1013 me->me_type_malloced = 1;
1014 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1015 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1016 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1018 /* Add to the linked list. */
1019 *mtail = me;
1020 mtail = &me->me_next;
1023 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1026 if (0 <= lockfd && close (lockfd) != 0)
1027 ret = errno;
1029 if (0 <= ret)
1031 errno = ret;
1032 goto free_then_fail;
1035 #endif /* MOUNTED_GETMNTENT2. */
1037 #ifdef MOUNTED_VMOUNT /* AIX. */
1039 int bufsize;
1040 char *entries, *thisent;
1041 struct vmount *vmp;
1042 int n_entries;
1043 int i;
1045 /* Ask how many bytes to allocate for the mounted file system info. */
1046 if (mntctl (MCTL_QUERY, sizeof (bufsize), (struct vmount *) &bufsize) != 0)
1047 return NULL;
1048 entries = g_malloc (bufsize);
1050 /* Get the list of mounted file systems. */
1051 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
1052 if (n_entries < 0)
1054 int saved_errno = errno;
1055 g_free (entries);
1056 errno = saved_errno;
1057 return NULL;
1060 for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1062 char *options, *ignore;
1064 vmp = (struct vmount *) thisent;
1065 me = g_malloc (sizeof (*me));
1066 if (vmp->vmt_flags & MNT_REMOTE)
1068 char *host, *dir;
1070 me->me_remote = 1;
1071 /* Prepend the remote dirname. */
1072 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1073 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1074 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1076 else
1078 me->me_remote = 0;
1079 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1081 me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1082 me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1083 me->me_type_malloced = 1;
1084 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1085 ignore = strstr (options, "ignore");
1086 me->me_dummy = (ignore
1087 && (ignore == options || ignore[-1] == ',')
1088 && (ignore[sizeof ("ignore") - 1] == ','
1089 || ignore[sizeof ("ignore") - 1] == '\0'));
1090 me->me_dev = (dev_t) (-1); /* vmt_fsid might be the info we want. */
1092 /* Add to the linked list. */
1093 *mtail = me;
1094 mtail = &me->me_next;
1096 g_free (entries);
1098 #endif /* MOUNTED_VMOUNT. */
1101 #ifdef MOUNTED_INTERIX_STATVFS
1103 DIR *dirp = opendir ("/dev/fs");
1104 char node[9 + NAME_MAX];
1106 if (!dirp)
1107 goto free_then_fail;
1109 while (1)
1111 struct statvfs dev;
1112 struct dirent entry;
1113 struct dirent *result;
1115 if (readdir_r (dirp, &entry, &result) || result == NULL)
1116 break;
1118 strcpy (node, "/dev/fs/");
1119 strcat (node, entry.d_name);
1121 if (statvfs (node, &dev) == 0)
1123 me = g_malloc (sizeof *me);
1124 me->me_devname = g_strdup (dev.f_mntfromname);
1125 me->me_mountdir = g_strdup (dev.f_mntonname);
1126 me->me_type = g_strdup (dev.f_fstypename);
1127 me->me_type_malloced = 1;
1128 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1129 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1130 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1132 /* Add to the linked list. */
1133 *mtail = me;
1134 mtail = &me->me_next;
1138 #endif /* MOUNTED_INTERIX_STATVFS */
1140 (void) need_fs_type; /* avoid argument-unused warning */
1141 *mtail = NULL;
1142 return mount_list;
1145 free_then_fail:
1147 int saved_errno = errno;
1148 *mtail = NULL;
1150 while (mount_list)
1152 me = mount_list->me_next;
1153 g_free (mount_list->me_devname);
1154 g_free (mount_list->me_mountdir);
1155 if (mount_list->me_type_malloced)
1156 g_free (mount_list->me_type);
1157 g_free (mount_list);
1158 mount_list = me;
1161 errno = saved_errno;
1162 return NULL;
1166 #endif /* HAVE_INFOMOUNT_LIST */
1168 /* --------------------------------------------------------------------------------------------- */
1170 #ifdef HAVE_INFOMOUNT_QNX
1172 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1173 ** this via the following code.
1174 ** Note that, as this is based on CWD, it only fills one mount_entry
1175 ** structure. See my_statfs() in utilunix.c for the "other side" of
1176 ** this hack.
1179 static struct mount_entry *
1180 read_file_system_list (int need_fs_type, int all_fs)
1182 struct _disk_entry de;
1183 struct statfs fs;
1184 int i, fd;
1185 char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1187 static struct mount_entry *me = NULL;
1189 if (me)
1191 g_free (me->me_devname);
1192 g_free (me->me_mountdir);
1193 g_free (me->me_type);
1195 else
1196 me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1198 if (!getcwd (dir, _POSIX_PATH_MAX))
1199 return (NULL);
1201 fd = open (dir, O_RDONLY);
1202 if (fd == -1)
1203 return (NULL);
1205 i = disk_get_entry (fd, &de);
1207 close (fd);
1209 if (i == -1)
1210 return (NULL);
1212 switch (de.disk_type)
1214 case _UNMOUNTED:
1215 tp = "unmounted";
1216 break;
1217 case _FLOPPY:
1218 tp = "Floppy";
1219 break;
1220 case _HARD:
1221 tp = "Hard";
1222 break;
1223 case _RAMDISK:
1224 tp = "Ram";
1225 break;
1226 case _REMOVABLE:
1227 tp = "Removable";
1228 break;
1229 case _TAPE:
1230 tp = "Tape";
1231 break;
1232 case _CDROM:
1233 tp = "CDROM";
1234 break;
1235 default:
1236 tp = "unknown";
1239 if (fsys_get_mount_dev (dir, &dev) == -1)
1240 return (NULL);
1242 if (fsys_get_mount_pt (dev, &dir) == -1)
1243 return (NULL);
1245 me->me_devname = g_strdup (dev);
1246 me->me_mountdir = g_strdup (dir);
1247 me->me_type = g_strdup (tp);
1248 me->me_dev = de.disk_type;
1250 #ifdef DEBUG
1251 fprintf (stderr,
1252 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1253 de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1254 fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1255 fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1256 #endif /* DEBUG */
1258 return (me);
1260 #endif /* HAVE_INFOMOUNT_QNX */
1262 /* --------------------------------------------------------------------------------------------- */
1264 #ifdef STAT_READ_FILSYS /* SVR2 */
1266 /* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
1267 interrupted. Return the actual number of bytes read(written), zero for EOF,
1268 or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
1269 static size_t
1270 safe_read (int fd, void *buf, size_t count)
1272 /* Work around a bug in Tru64 5.1. Attempting to read more than
1273 INT_MAX bytes fails with errno == EINVAL. See
1274 <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
1275 When decreasing COUNT, keep it block-aligned. */
1276 /* *INDENT-OFF* */
1277 enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
1278 /* *INDENT-ON* */
1280 while (TRUE)
1282 ssize_t result;
1284 result = read (fd, buf, count);
1286 if (0 <= result)
1287 return result;
1288 else if (IS_EINTR (errno))
1289 continue;
1290 else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
1291 count = BUGGY_READ_MAXIMUM;
1292 else
1293 return result;
1297 /* --------------------------------------------------------------------------------------------- */
1299 /* Read COUNT bytes at BUF to(from) descriptor FD, retrying if
1300 interrupted or if a partial write(read) occurs. Return the number
1301 of bytes transferred.
1302 When writing, set errno if fewer than COUNT bytes are written.
1303 When reading, if fewer than COUNT bytes are read, you must examine
1304 errno to distinguish failure from EOF (errno == 0). */
1306 static size_t
1307 full_read (int fd, void *buf, size_t count)
1309 size_t total = 0;
1310 char *ptr = (char *) buf;
1312 while (count > 0)
1314 size_t n_rw = safe_read (fd, ptr, count);
1315 if (n_rw == (size_t) (-1))
1316 break;
1317 if (n_rw == 0)
1319 errno = ZERO_BYTE_TRANSFER_ERRNO;
1320 break;
1322 total += n_rw;
1323 ptr += n_rw;
1324 count -= n_rw;
1327 return total;
1330 #endif /* STAT_READ_FILSYS */
1332 /* --------------------------------------------------------------------------------------------- */
1334 #ifdef HAVE_INFOMOUNT
1335 /* Fill in the fields of FSP with information about space usage for
1336 the file system on which FILE resides.
1337 DISK is the device on which FILE is mounted, for space-getting
1338 methods that need to know it.
1339 Return 0 if successful, -1 if not. When returning -1, ensure that
1340 ERRNO is either a system error value, or zero if DISK is NULL
1341 on a system that requires a non-NULL value. */
1342 static int
1343 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
1345 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1347 if (statvfs_works ())
1349 struct statvfs vfsd;
1351 if (statvfs (file, &vfsd) < 0)
1352 return -1;
1354 /* f_frsize isn't guaranteed to be supported. */
1355 fsp->fsu_blocksize = (vfsd.f_frsize
1356 ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1357 : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1359 fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1360 fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1361 fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1362 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1363 fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1364 fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1366 else
1367 #endif
1370 #if defined STAT_STATVFS64 /* AIX */
1372 struct statvfs64 fsd;
1374 if (statvfs64 (file, &fsd) < 0)
1375 return -1;
1377 /* f_frsize isn't guaranteed to be supported. */
1378 /* *INDENT-OFF* */
1379 fsp->fsu_blocksize = fsd.f_frsize
1380 ? PROPAGATE_ALL_ONES (fsd.f_frsize)
1381 : PROPAGATE_ALL_ONES (fsd.f_bsize);
1382 /* *INDENT-ON* */
1384 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1386 struct fs_data fsd;
1388 if (statfs (file, &fsd) != 1)
1389 return -1;
1391 fsp->fsu_blocksize = 1024;
1392 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
1393 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
1394 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
1395 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
1396 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
1397 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
1399 #elif defined STAT_READ_FILSYS /* SVR2 */
1400 #ifndef SUPERBOFF
1401 #define SUPERBOFF (SUPERB * 512)
1402 #endif
1404 struct filsys fsd;
1405 int fd;
1407 if (!disk)
1409 errno = 0;
1410 return -1;
1413 fd = open (disk, O_RDONLY);
1414 if (fd < 0)
1415 return -1;
1416 lseek (fd, (off_t) SUPERBOFF, 0);
1417 if (full_read (fd, (char *) &fsd, sizeof (fsd)) != sizeof (fsd))
1419 close (fd);
1420 return -1;
1422 close (fd);
1424 fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
1425 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
1426 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
1427 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
1428 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
1429 fsp->fsu_files = (fsd.s_isize == -1
1430 ? UINTMAX_MAX : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
1431 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);
1433 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1435 struct statfs fsd;
1437 if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1438 return -1;
1440 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1442 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1444 struct statfs fsd;
1446 if (statfs (file, &fsd) < 0)
1447 return -1;
1449 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1451 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1452 Mac OS X < 10.4, FreeBSD < 5.0, \
1453 NetBSD < 3.0, OpenBSD < 4.4 */
1455 struct statfs fsd;
1457 if (statfs (file, &fsd) < 0)
1458 return -1;
1460 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1462 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1464 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1465 struct statfs are truncated to 2GB. These conditions detect that
1466 truncation, presumably without botching the 4.1.1 case, in which
1467 the values are not truncated. The correct counts are stored in
1468 undocumented spare fields. */
1469 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1471 fsd.f_blocks = fsd.f_spare[0];
1472 fsd.f_bfree = fsd.f_spare[1];
1473 fsd.f_bavail = fsd.f_spare[2];
1475 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1477 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1479 struct statfs fsd;
1481 if (statfs (file, &fsd) < 0)
1482 return -1;
1484 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1486 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \
1487 Dolphin */
1489 #if !defined _AIX && !defined _SEQUENT_ && !defined DOLPHIN
1490 #define f_bavail f_bfree
1491 #endif
1493 struct statfs fsd;
1495 if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1496 return -1;
1498 /* Empirically, the block counts on most SVR3 and SVR3-derived
1499 systems seem to always be in terms of 512-byte blocks,
1500 no matter what value f_bsize has. */
1501 #if defined _AIX || defined _CRAY
1502 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1503 #else
1504 fsp->fsu_blocksize = 512;
1505 #endif
1507 #endif
1509 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1510 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1511 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1513 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1514 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1515 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1516 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1517 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1518 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1520 #endif
1523 (void) disk; /* avoid argument-unused warning */
1525 return 0;
1527 #endif /* HAVE_INFOMOUNT */
1529 /* --------------------------------------------------------------------------------------------- */
1530 /*** public functions ****************************************************************************/
1531 /* --------------------------------------------------------------------------------------------- */
1533 void
1534 free_my_statfs (void)
1536 #ifdef HAVE_INFOMOUNT_LIST
1537 while (mc_mount_list != NULL)
1539 struct mount_entry *next;
1541 next = mc_mount_list->me_next;
1542 free_mount_entry (mc_mount_list);
1543 mc_mount_list = next;
1546 mc_mount_list = NULL;
1547 #endif /* HAVE_INFOMOUNT_LIST */
1550 /* --------------------------------------------------------------------------------------------- */
1552 void
1553 init_my_statfs (void)
1555 #ifdef HAVE_INFOMOUNT_LIST
1556 free_my_statfs ();
1557 mc_mount_list = read_file_system_list (1);
1558 #endif /* HAVE_INFOMOUNT_LIST */
1561 /* --------------------------------------------------------------------------------------------- */
1563 void
1564 my_statfs (struct my_statfs *myfs_stats, const char *path)
1566 #ifdef HAVE_INFOMOUNT_LIST
1567 size_t i, len = 0;
1568 struct mount_entry *entry = NULL;
1569 struct mount_entry *temp = mc_mount_list;
1570 struct fs_usage fs_use;
1572 while (temp)
1574 i = strlen (temp->me_mountdir);
1575 if (i > len && (strncmp (path, temp->me_mountdir, i) == 0))
1576 if (!entry || (path[i] == PATH_SEP || path[i] == '\0'))
1578 len = i;
1579 entry = temp;
1581 temp = temp->me_next;
1584 if (entry)
1586 memset (&fs_use, 0, sizeof (struct fs_usage));
1587 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1589 myfs_stats->type = entry->me_dev;
1590 myfs_stats->typename = entry->me_type;
1591 myfs_stats->mpoint = entry->me_mountdir;
1592 myfs_stats->device = entry->me_devname;
1593 myfs_stats->avail =
1594 ((uintmax_t) (getuid ()? fs_use.fsu_bavail : fs_use.fsu_bfree) *
1595 fs_use.fsu_blocksize) >> 10;
1596 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1597 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1598 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1600 else
1601 #endif /* HAVE_INFOMOUNT_LIST */
1603 #ifdef HAVE_INFOMOUNT_QNX
1605 ** This is the "other side" of the hack to read_file_system_list() in
1606 ** mountlist.c.
1607 ** It's not the most efficient approach, but consumes less memory. It
1608 ** also accomodates QNX's ability to mount filesystems on the fly.
1610 struct mount_entry *entry;
1611 struct fs_usage fs_use;
1613 entry = read_file_system_list (0, 0);
1614 if (entry != NULL)
1616 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1618 myfs_stats->type = entry->me_dev;
1619 myfs_stats->typename = entry->me_type;
1620 myfs_stats->mpoint = entry->me_mountdir;
1621 myfs_stats->device = entry->me_devname;
1623 myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1624 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1625 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1626 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1628 else
1629 #endif /* HAVE_INFOMOUNT_QNX */
1631 myfs_stats->type = 0;
1632 myfs_stats->mpoint = "unknown";
1633 myfs_stats->device = "unknown";
1634 myfs_stats->avail = 0;
1635 myfs_stats->total = 0;
1636 myfs_stats->nfree = 0;
1637 myfs_stats->nodes = 0;
1641 /* --------------------------------------------------------------------------------------------- */