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/>.
27 * \brief Source: list of mounted filesystems
36 #include <stdint.h> /* SIZE_MAX */
37 #include <sys/types.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>
46 #if defined STAT_STATVFS || defined STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
47 #include <sys/statvfs.h>
49 /* Don't include backward-compatibility files unless they're needed.
50 Eventually we'd like to remove all this cruft. */
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 */
61 #ifdef HAVE_SYS_MOUNT_H
62 #include <sys/mount.h>
64 #ifdef HAVE_SYS_FS_TYPES_H
65 #include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
67 #ifdef HAVE_STRUCT_FSSTAT_F_FSTYPENAME
68 #define FS_TYPE(Ent) ((Ent).f_fstypename)
70 #define FS_TYPE(Ent) mnt_names[(Ent).f_type]
72 #endif /* MOUNTED_GETFSSTAT */
73 #endif /* STAT_STATVFS || STAT_STATVFS64 */
78 #ifdef HAVE_SYS_FS_S5PARAM_H /* Fujitsu UXP/V */
79 #include <sys/fs/s5param.h>
81 #if defined HAVE_SYS_FILSYS_H && !defined _CRAY
82 #include <sys/filsys.h> /* SVR2 */
84 #ifdef HAVE_SYS_STATFS_H
85 #include <sys/statfs.h>
87 #ifdef HAVE_DUSTAT_H /* AIX PS/2 */
88 #include <sys/dustat.h>
91 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
94 #ifdef _PATH_MOUNTED /* GNU libc */
95 #define MOUNTED _PATH_MOUNTED
97 #ifdef MNT_MNTTAB /* HP-UX. */
98 #define MOUNTED MNT_MNTTAB
100 #ifdef MNTTABNAME /* Dynix. */
101 #define MOUNTED MNTTABNAME
106 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
107 #include <sys/mount.h>
110 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
111 #include <sys/statvfs.h>
114 #ifdef MOUNTED_GETMNT /* Ultrix. */
115 #include <sys/mount.h>
116 #include <sys/fs_types.h>
119 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
124 #ifdef MOUNTED_FREAD /* SVR2. */
128 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
130 #include <sys/fstyp.h>
131 #include <sys/statfs.h>
134 #ifdef MOUNTED_LISTMNTENT
138 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
139 #include <sys/mnttab.h>
142 #ifdef MOUNTED_VMOUNT /* AIX. */
147 #ifdef MOUNTED_INTERIX_STATVFS /* Interix. */
148 #include <sys/statvfs.h>
153 /* So special that it's not worth putting this in autoconf. */
154 #undef MOUNTED_FREAD_FSTYP
155 #define MOUNTED_GETMNTTBL
158 #ifdef HAVE_SYS_MNTENT_H
159 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
160 #include <sys/mntent.h>
164 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
165 #define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
167 #define MNT_IGNORE(M) 0
170 #ifdef HAVE_INFOMOUNT_QNX
171 #include <sys/disk.h>
172 #include <sys/fsys.h>
175 #ifdef HAVE_SYS_STATVFS_H /* SVR4. */
176 #include <sys/statvfs.h>
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
190 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
191 #define HAVE_INFOMOUNT
194 /* The results of open() in this file are not used with fchdir,
195 therefore save some unnecessary work in fchdir.c. */
199 /* The results of opendir() in this file are not used with dirfd and fchdir,
200 therefore save some unnecessary work in fchdir.c. */
205 #define ME_DUMMY(Fs_name, Fs_type) \
206 (strcmp (Fs_type, "autofs") == 0 \
207 || strcmp (Fs_type, "none") == 0 \
208 || strcmp (Fs_type, "proc") == 0 \
209 || strcmp (Fs_type, "subfs") == 0 \
210 /* for NetBSD 3.0 */ \
211 || strcmp (Fs_type, "kernfs") == 0 \
213 || strcmp (Fs_type, "ignore") == 0)
218 #define ME_REMOTE me_remote
219 /* All cygwin mount points include `:' or start with `//'; so it
220 requires a native Windows call to determine remote disks. */
222 me_remote (char const *fs_name
, char const *fs_type _GL_UNUSED
)
224 if (fs_name
[0] && fs_name
[1] == ':')
227 sprintf (drive
, "%c:\\", fs_name
[0]);
228 switch (GetDriveType (drive
))
230 case DRIVE_REMOVABLE
:
241 /* A file system is `remote' if its Fs_name contains a `:'
242 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
243 #define ME_REMOTE(Fs_name, Fs_type) \
244 (strchr (Fs_name, ':') != NULL \
245 || ((Fs_name)[0] == '/' \
246 && (Fs_name)[1] == '/' \
247 && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "cifs") == 0)))
250 /* Many space usage primitives use all 1 bits to denote a value that is
251 not applicable or unknown. Propagate this information by returning
252 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
253 is unsigned and narrower than uintmax_t. */
254 #define PROPAGATE_ALL_ONES(x) \
255 ((sizeof (x) < sizeof (uintmax_t) \
256 && (~ (x) == (sizeof (x) < sizeof (int) \
257 ? - (1 << (sizeof (x) * CHAR_BIT)) \
259 ? UINTMAX_MAX : (uintmax_t) (x))
261 /* Extract the top bit of X as an uintmax_t value. */
262 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
264 /* If a value is negative, many space usage primitives store it into an
265 integer variable by assignment, even if the variable's type is unsigned.
266 So, if a space usage variable X's top bit is set, convert X to the
267 uintmax_t value V such that (- (uintmax_t) V) is the negative of
268 the original value. If X's top bit is clear, just yield X.
269 Use PROPAGATE_TOP_BIT if the original value might be negative;
270 otherwise, use PROPAGATE_ALL_ONES. */
271 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
274 /* Return true if statvfs works. This is false for statvfs on systems
275 with GNU libc on Linux kernels before 2.6.36, which stats all
276 preceding entries in /proc/mounts; that makes df hang if even one
277 of the corresponding file systems is hard-mounted but not available. */
278 # if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
279 /* The FRSIZE fallback is not required in this case. */
280 #undef STAT_STATFS2_FRSIZE
287 #include <string.h> /* for strverscmp */
288 #include <sys/utsname.h>
289 #include <sys/statfs.h>
290 #define STAT_STATFS2_BSIZE 1
295 static int statvfs_works_cache
= -1;
298 if (statvfs_works_cache
< 0)
299 statvfs_works_cache
= (uname (&name
) == 0 && 0 <= strverscmp (name
.release
, "2.6.36"));
300 return statvfs_works_cache
;
305 #ifdef STAT_READ_FILSYS /* SVR2 */
306 /* Set errno to zero upon EOF. */
307 #define ZERO_BYTE_TRANSFER_ERRNO 0
310 #define IS_EINTR(x) ((x) == EINTR)
312 #define IS_EINTR(x) 0
314 #endif /* STAT_READ_FILSYS */
316 /*** file scope type declarations ****************************************************************/
318 /* A mount table entry. */
321 char *me_devname
; /* Device node name, including "/dev/". */
322 char *me_mountdir
; /* Mount point directory name. */
323 char *me_type
; /* "nfs", "4.2", etc. */
324 dev_t me_dev
; /* Device number of me_mountdir. */
325 unsigned int me_dummy
:1; /* Nonzero for dummy file systems. */
326 unsigned int me_remote
:1; /* Nonzero for remote fileystems. */
327 unsigned int me_type_malloced
:1; /* Nonzero if me_type was malloced. */
328 struct mount_entry
*me_next
;
333 uintmax_t fsu_blocksize
; /* Size of a block. */
334 uintmax_t fsu_blocks
; /* Total blocks. */
335 uintmax_t fsu_bfree
; /* Free blocks available to superuser. */
336 uintmax_t fsu_bavail
; /* Free blocks available to non-superuser. */
337 int fsu_bavail_top_bit_set
; /* 1 if fsu_bavail represents a value < 0. */
338 uintmax_t fsu_files
; /* Total file nodes. */
339 uintmax_t fsu_ffree
; /* Free file nodes. */
342 /*** file scope variables ************************************************************************/
344 #ifdef HAVE_INFOMOUNT_LIST
345 static struct mount_entry
*mc_mount_list
= NULL
;
346 #endif /* HAVE_INFOMOUNT_LIST */
348 /*** file scope functions ************************************************************************/
349 /* --------------------------------------------------------------------------------------------- */
351 #ifdef HAVE_INFOMOUNT_LIST
353 free_mount_entry (struct mount_entry
*me
)
358 free (me
->me_devname
);
360 free (me
->me_mountdir
);
361 if (me
->me_type
&& me
->me_type_malloced
)
366 /* --------------------------------------------------------------------------------------------- */
368 #ifdef MOUNTED_GETMNTINFO
370 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
372 fstype_to_string (short int t
)
464 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
466 /* --------------------------------------------------------------------------------------------- */
469 fsp_to_string (const struct statfs
*fsp
)
471 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
472 return (char *) (fsp
->f_fstypename
);
474 return fstype_to_string (fsp
->f_type
);
477 #endif /* MOUNTED_GETMNTINFO */
479 /* --------------------------------------------------------------------------------------------- */
481 #ifdef MOUNTED_VMOUNT /* AIX. */
483 fstype_to_string (int t
)
487 e
= getvfsbytype (t
);
488 if (!e
|| !e
->vfsent_name
)
491 return e
->vfsent_name
;
493 #endif /* MOUNTED_VMOUNT */
495 /* --------------------------------------------------------------------------------------------- */
497 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
499 /* Return the device number from MOUNT_OPTIONS, if possible.
500 Otherwise return (dev_t) -1. */
502 /* --------------------------------------------------------------------------------------------- */
505 dev_from_mount_options (char const *mount_options
)
507 /* GNU/Linux allows file system implementations to define their own
508 meaning for "dev=" mount options, so don't trust the meaning
511 static char const dev_pattern
[] = ",dev=";
512 char const *devopt
= strstr (mount_options
, dev_pattern
);
516 char const *optval
= devopt
+ sizeof (dev_pattern
) - 1;
518 unsigned long int dev
;
520 dev
= strtoul (optval
, &optvalend
, 16);
521 if (optval
!= optvalend
522 && (*optvalend
== '\0' || *optvalend
== ',')
523 && !(dev
== ULONG_MAX
&& errno
== ERANGE
) && dev
== (dev_t
) dev
)
528 (void) mount_options
;
534 /* --------------------------------------------------------------------------------------------- */
536 #if defined _AIX && defined _I386
537 /* AIX PS/2 does not supply statfs. */
540 statfs (char *file
, struct statfs
*fsb
)
545 if (stat (file
, &stats
) != 0)
547 if (dustat (stats
.st_dev
, 0, &fsd
, sizeof (fsd
)))
550 fsb
->f_bsize
= fsd
.du_bsize
;
551 fsb
->f_blocks
= fsd
.du_fsize
- fsd
.du_isize
;
552 fsb
->f_bfree
= fsd
.du_tfree
;
553 fsb
->f_bavail
= fsd
.du_tfree
;
554 fsb
->f_files
= (fsd
.du_isize
- 2) * fsd
.du_inopb
;
555 fsb
->f_ffree
= fsd
.du_tinode
;
556 fsb
->f_fsid
.val
[0] = fsd
.du_site
;
557 fsb
->f_fsid
.val
[1] = fsd
.du_pckno
;
561 #endif /* _AIX && _I386 */
563 /* --------------------------------------------------------------------------------------------- */
565 /* Return a list of the currently mounted file systems, or NULL on error.
566 Add each entry to the tail of the list so that they stay in order.
567 If NEED_FS_TYPE is true, ensure that the file system type fields in
568 the returned list are valid. Otherwise, they might not be. */
570 static struct mount_entry
*
571 read_file_system_list (int need_fs_type
)
573 struct mount_entry
*mount_list
;
574 struct mount_entry
*me
;
575 struct mount_entry
**mtail
= &mount_list
;
577 #ifdef MOUNTED_LISTMNTENT
579 struct tabmntent
*mntlist
, *p
;
581 struct mount_entry
*me
;
583 /* the third and fourth arguments could be used to filter mounts,
584 but Crays doesn't seem to have any mounts that we want to
585 remove. Specifically, automount create normal NFS mounts.
588 if (listmntent (&mntlist
, KMTAB
, NULL
, NULL
) < 0)
590 for (p
= mntlist
; p
; p
= p
->next
)
593 me
= malloc (sizeof (*me
));
594 me
->me_devname
= strdup (mnt
->mnt_fsname
);
595 me
->me_mountdir
= strdup (mnt
->mnt_dir
);
596 me
->me_type
= strdup (mnt
->mnt_type
);
597 me
->me_type_malloced
= 1;
598 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
599 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
602 mtail
= &me
->me_next
;
604 freemntlist (mntlist
);
608 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
611 const char *table
= MOUNTED
;
614 fp
= setmntent (table
, "r");
618 while ((mnt
= getmntent (fp
)))
620 me
= malloc (sizeof (*me
));
621 me
->me_devname
= strdup (mnt
->mnt_fsname
);
622 me
->me_mountdir
= strdup (mnt
->mnt_dir
);
623 me
->me_type
= strdup (mnt
->mnt_type
);
624 me
->me_type_malloced
= 1;
625 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
626 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
627 me
->me_dev
= dev_from_mount_options (mnt
->mnt_opts
);
629 /* Add to the linked list. */
631 mtail
= &me
->me_next
;
634 if (endmntent (fp
) == 0)
637 #endif /* MOUNTED_GETMNTENT1. */
639 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
644 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
647 for (; entries
-- > 0; fsp
++)
649 char *fs_type
= fsp_to_string (fsp
);
651 me
= malloc (sizeof (*me
));
652 me
->me_devname
= strdup (fsp
->f_mntfromname
);
653 me
->me_mountdir
= strdup (fsp
->f_mntonname
);
654 me
->me_type
= fs_type
;
655 me
->me_type_malloced
= 0;
656 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
657 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
658 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
660 /* Add to the linked list. */
662 mtail
= &me
->me_next
;
665 #endif /* MOUNTED_GETMNTINFO */
667 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
672 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
675 for (; entries
-- > 0; fsp
++)
677 me
= malloc (sizeof (*me
));
678 me
->me_devname
= strdup (fsp
->f_mntfromname
);
679 me
->me_mountdir
= strdup (fsp
->f_mntonname
);
680 me
->me_type
= strdup (fsp
->f_fstypename
);
681 me
->me_type_malloced
= 1;
682 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
683 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
684 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
686 /* Add to the linked list. */
688 mtail
= &me
->me_next
;
691 #endif /* MOUNTED_GETMNTINFO2 */
693 #ifdef MOUNTED_GETMNT /* Ultrix. */
699 while (errno
= 0, 0 < (val
= getmnt (&offset
, &fsd
, sizeof (fsd
), NOSTAT_MANY
, (char *) 0)))
701 me
= malloc (sizeof (*me
));
702 me
->me_devname
= strdup (fsd
.fd_req
.devname
);
703 me
->me_mountdir
= strdup (fsd
.fd_req
.path
);
704 me
->me_type
= gt_names
[fsd
.fd_req
.fstype
];
705 me
->me_type_malloced
= 0;
706 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
707 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
708 me
->me_dev
= fsd
.fd_req
.dev
;
710 /* Add to the linked list. */
712 mtail
= &me
->me_next
;
717 #endif /* MOUNTED_GETMNT. */
719 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
721 /* The next_dev() and fs_stat_dev() system calls give the list of
722 all file systems, including the information returned by statvfs()
723 (fs type, total blocks, free blocks etc.), but without the mount
724 point. But on BeOS all file systems except / are mounted in the
725 rootfs, directly under /.
726 The directory name of the mount point is often, but not always,
727 identical to the volume name of the device.
728 We therefore get the list of subdirectories of /, and the list
729 of all file systems, and match the two lists. */
737 struct rootdir_entry
*next
;
739 struct rootdir_entry
*rootdir_list
;
740 struct rootdir_entry
**rootdir_tail
;
745 /* All volumes are mounted in the rootfs, directly under /. */
747 rootdir_tail
= &rootdir_list
;
748 dirp
= opendir ("/");
753 while ((d
= readdir (dirp
)) != NULL
)
758 if (strcmp (d
->d_name
, "..") == 0)
761 if (strcmp (d
->d_name
, ".") == 0)
765 name
= malloc (1 + strlen (d
->d_name
) + 1);
767 strcpy (name
+ 1, d
->d_name
);
770 if (lstat (name
, &statbuf
) >= 0 && S_ISDIR (statbuf
.st_mode
))
772 struct rootdir_entry
*re
= malloc (sizeof (*re
));
774 re
->dev
= statbuf
.st_dev
;
775 re
->ino
= statbuf
.st_ino
;
777 /* Add to the linked list. */
779 rootdir_tail
= &re
->next
;
786 *rootdir_tail
= NULL
;
788 for (pos
= 0; (dev
= next_dev (&pos
)) >= 0;)
789 if (fs_stat_dev (dev
, &fi
) >= 0)
791 /* Note: fi.dev == dev. */
792 struct rootdir_entry
*re
;
794 for (re
= rootdir_list
; re
; re
= re
->next
)
795 if (re
->dev
== fi
.dev
&& re
->ino
== fi
.root
)
798 me
= malloc (sizeof (*me
));
799 me
->me_devname
= strdup (fi
.device_name
[0] != '\0' ? fi
.device_name
: fi
.fsh_name
);
800 me
->me_mountdir
= strdup (re
!= NULL
? re
->name
: fi
.fsh_name
);
801 me
->me_type
= strdup (fi
.fsh_name
);
802 me
->me_type_malloced
= 1;
805 me
->me_remote
= (fi
.flags
& B_FS_IS_SHARED
) != 0;
807 /* Add to the linked list. */
809 mtail
= &me
->me_next
;
813 while (rootdir_list
!= NULL
)
815 struct rootdir_entry
*re
= rootdir_list
;
816 rootdir_list
= re
->next
;
821 #endif /* MOUNTED_FS_STAT_DEV */
823 #ifdef MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
827 struct statfs
*stats
;
829 numsys
= getfsstat (NULL
, 0L, MNT_NOWAIT
);
832 if (SIZE_MAX
/ sizeof (*stats
) <= numsys
)
834 fprintf (stderr
, "%s\n", _("Memory exhausted!"));
838 bufsize
= (1 + numsys
) * sizeof (*stats
);
839 stats
= malloc (bufsize
);
840 numsys
= getfsstat (stats
, bufsize
, MNT_NOWAIT
);
848 for (counter
= 0; counter
< numsys
; counter
++)
850 me
= malloc (sizeof (*me
));
851 me
->me_devname
= strdup (stats
[counter
].f_mntfromname
);
852 me
->me_mountdir
= strdup (stats
[counter
].f_mntonname
);
853 me
->me_type
= strdup (FS_TYPE (stats
[counter
]));
854 me
->me_type_malloced
= 1;
855 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
856 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
857 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
859 /* Add to the linked list. */
861 mtail
= &me
->me_next
;
866 #endif /* MOUNTED_GETFSSTAT */
868 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
871 char *table
= "/etc/mnttab";
874 fp
= fopen (table
, "r");
878 while (fread (&mnt
, sizeof (mnt
), 1, fp
) > 0)
880 me
= malloc (sizeof (*me
));
881 #ifdef GETFSTYP /* SVR3. */
882 me
->me_devname
= strdup (mnt
.mt_dev
);
884 me
->me_devname
= malloc (strlen (mnt
.mt_dev
) + 6);
885 strcpy (me
->me_devname
, "/dev/");
886 strcpy (me
->me_devname
+ 5, mnt
.mt_dev
);
888 me
->me_mountdir
= strdup (mnt
.mt_filsys
);
889 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
891 me
->me_type_malloced
= 0;
892 #ifdef GETFSTYP /* SVR3. */
896 char typebuf
[FSTYPSZ
];
898 if (statfs (me
->me_mountdir
, &fsd
, sizeof (fsd
), 0) != -1
899 && sysfs (GETFSTYP
, fsd
.f_fstyp
, typebuf
) != -1)
901 me
->me_type
= strdup (typebuf
);
902 me
->me_type_malloced
= 1;
906 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
907 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
909 /* Add to the linked list. */
911 mtail
= &me
->me_next
;
916 /* The last fread() call must have failed. */
917 int saved_errno
= errno
;
923 if (fclose (fp
) == EOF
)
926 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
928 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
930 struct mntent
**mnttbl
= getmnttbl (), **ent
;
931 for (ent
= mnttbl
; *ent
; ent
++)
933 me
= malloc (sizeof (*me
));
934 me
->me_devname
= strdup ((*ent
)->mt_resource
);
935 me
->me_mountdir
= strdup ((*ent
)->mt_directory
);
936 me
->me_type
= strdup ((*ent
)->mt_fstype
);
937 me
->me_type_malloced
= 1;
938 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
939 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
940 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
942 /* Add to the linked list. */
944 mtail
= &me
->me_next
;
950 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
953 char *table
= MNTTAB
;
958 #if defined F_RDLCK && defined F_SETLKW
959 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
960 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
961 for this file name, we should use their macro name instead.
962 (Why not just lock MNTTAB directly? We don't know.) */
964 #define MNTTAB_LOCK "/etc/.mnttab.lock"
966 lockfd
= open (MNTTAB_LOCK
, O_RDONLY
);
970 flock
.l_type
= F_RDLCK
;
971 flock
.l_whence
= SEEK_SET
;
974 while (fcntl (lockfd
, F_SETLKW
, &flock
) == -1)
977 int saved_errno
= errno
;
983 else if (errno
!= ENOENT
)
988 fp
= fopen (table
, "r");
993 while ((ret
= getmntent (fp
, &mnt
)) == 0)
995 me
= malloc (sizeof (*me
));
996 me
->me_devname
= strdup (mnt
.mnt_special
);
997 me
->me_mountdir
= strdup (mnt
.mnt_mountp
);
998 me
->me_type
= strdup (mnt
.mnt_fstype
);
999 me
->me_type_malloced
= 1;
1000 me
->me_dummy
= MNT_IGNORE (&mnt
) != 0;
1001 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
1002 me
->me_dev
= dev_from_mount_options (mnt
.mnt_mntopts
);
1004 /* Add to the linked list. */
1006 mtail
= &me
->me_next
;
1009 ret
= fclose (fp
) == EOF
? errno
: 0 < ret
? 0 : -1;
1012 if (0 <= lockfd
&& close (lockfd
) != 0)
1018 goto free_then_fail
;
1021 #endif /* MOUNTED_GETMNTENT2. */
1023 #ifdef MOUNTED_VMOUNT /* AIX. */
1026 char *entries
, *thisent
;
1031 /* Ask how many bytes to allocate for the mounted file system info. */
1032 if (mntctl (MCTL_QUERY
, sizeof (bufsize
), (struct vmount
*) &bufsize
) != 0)
1034 entries
= malloc (bufsize
);
1036 /* Get the list of mounted file systems. */
1037 n_entries
= mntctl (MCTL_QUERY
, bufsize
, (struct vmount
*) entries
);
1040 int saved_errno
= errno
;
1042 errno
= saved_errno
;
1046 for (i
= 0, thisent
= entries
; i
< n_entries
; i
++, thisent
+= vmp
->vmt_length
)
1048 char *options
, *ignore
;
1050 vmp
= (struct vmount
*) thisent
;
1051 me
= malloc (sizeof (*me
));
1052 if (vmp
->vmt_flags
& MNT_REMOTE
)
1057 /* Prepend the remote dirname. */
1058 host
= thisent
+ vmp
->vmt_data
[VMT_HOSTNAME
].vmt_off
;
1059 dir
= thisent
+ vmp
->vmt_data
[VMT_OBJECT
].vmt_off
;
1060 me
->me_devname
= malloc (strlen (host
) + strlen (dir
) + 2);
1061 strcpy (me
->me_devname
, host
);
1062 strcat (me
->me_devname
, ":");
1063 strcat (me
->me_devname
, dir
);
1068 me
->me_devname
= strdup (thisent
+ vmp
->vmt_data
[VMT_OBJECT
].vmt_off
);
1070 me
->me_mountdir
= strdup (thisent
+ vmp
->vmt_data
[VMT_STUB
].vmt_off
);
1071 me
->me_type
= 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. */
1083 mtail
= &me
->me_next
;
1087 #endif /* MOUNTED_VMOUNT. */
1090 #ifdef MOUNTED_INTERIX_STATVFS
1092 DIR *dirp
= opendir ("/dev/fs");
1093 char node
[9 + NAME_MAX
];
1096 goto free_then_fail
;
1101 struct dirent entry
;
1102 struct dirent
*result
;
1104 if (readdir_r (dirp
, &entry
, &result
) || result
== NULL
)
1107 strcpy (node
, "/dev/fs/");
1108 strcat (node
, entry
.d_name
);
1110 if (statvfs (node
, &dev
) == 0)
1112 me
= malloc (sizeof *me
);
1113 me
->me_devname
= strdup (dev
.f_mntfromname
);
1114 me
->me_mountdir
= strdup (dev
.f_mntonname
);
1115 me
->me_type
= 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. */
1123 mtail
= &me
->me_next
;
1127 #endif /* MOUNTED_INTERIX_STATVFS */
1129 (void) need_fs_type
; /* avoid argument-unused warning */
1136 int saved_errno
= errno
;
1141 me
= mount_list
->me_next
;
1142 free (mount_list
->me_devname
);
1143 free (mount_list
->me_mountdir
);
1144 if (mount_list
->me_type_malloced
)
1145 free (mount_list
->me_type
);
1150 errno
= saved_errno
;
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
1168 static struct mount_entry
*
1169 read_file_system_list (int need_fs_type
, int all_fs
)
1171 struct _disk_entry de
;
1174 char *tp
, dev
[_POSIX_NAME_MAX
], dir
[_POSIX_PATH_MAX
];
1176 static struct mount_entry
*me
= NULL
;
1181 free (me
->me_devname
);
1182 if (me
->me_mountdir
)
1183 free (me
->me_mountdir
);
1188 me
= (struct mount_entry
*) malloc (sizeof (struct mount_entry
));
1190 if (!getcwd (dir
, _POSIX_PATH_MAX
))
1193 fd
= open (dir
, O_RDONLY
);
1197 i
= disk_get_entry (fd
, &de
);
1204 switch (de
.disk_type
)
1231 if (fsys_get_mount_dev (dir
, &dev
) == -1)
1234 if (fsys_get_mount_pt (dev
, &dir
) == -1)
1237 me
->me_devname
= strdup (dev
);
1238 me
->me_mountdir
= strdup (dir
);
1239 me
->me_type
= strdup (tp
);
1240 me
->me_dev
= de
.disk_type
;
1244 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1245 de
.disk_type
, tp
, _DRIVER_NAME_LEN
, _DRIVER_NAME_LEN
, de
.driver_name
, de
.disk_drv
);
1246 fprintf (stderr
, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev
);
1247 fprintf (stderr
, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir
);
1252 #endif /* HAVE_INFOMOUNT_QNX */
1254 /* --------------------------------------------------------------------------------------------- */
1256 #ifdef STAT_READ_FILSYS /* SVR2 */
1258 /* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
1259 interrupted. Return the actual number of bytes read(written), zero for EOF,
1260 or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
1262 safe_read (int fd
, void *buf
, size_t count
)
1264 /* Work around a bug in Tru64 5.1. Attempting to read more than
1265 INT_MAX bytes fails with errno == EINVAL. See
1266 <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
1267 When decreasing COUNT, keep it block-aligned. */
1269 enum { BUGGY_READ_MAXIMUM
= INT_MAX
& ~8191 };
1274 ssize_t result
= read (fd
, buf
, count
);
1278 else if (IS_EINTR (errno
))
1280 else if (errno
== EINVAL
&& BUGGY_READ_MAXIMUM
< count
)
1281 count
= BUGGY_READ_MAXIMUM
;
1287 /* --------------------------------------------------------------------------------------------- */
1289 /* Read COUNT bytes at BUF to(from) descriptor FD, retrying if
1290 interrupted or if a partial write(read) occurs. Return the number
1291 of bytes transferred.
1292 When writing, set errno if fewer than COUNT bytes are written.
1293 When reading, if fewer than COUNT bytes are read, you must examine
1294 errno to distinguish failure from EOF (errno == 0). */
1297 full_read (int fd
, void *buf
, size_t count
)
1300 char *ptr
= (char *) buf
;
1304 size_t n_rw
= safe_read (fd
, ptr
, count
);
1305 if (n_rw
== (size_t) (-1))
1309 errno
= ZERO_BYTE_TRANSFER_ERRNO
;
1320 #endif /* STAT_READ_FILSYS */
1322 /* --------------------------------------------------------------------------------------------- */
1324 #ifdef HAVE_INFOMOUNT
1325 /* Fill in the fields of FSP with information about space usage for
1326 the file system on which FILE resides.
1327 DISK is the device on which FILE is mounted, for space-getting
1328 methods that need to know it.
1329 Return 0 if successful, -1 if not. When returning -1, ensure that
1330 ERRNO is either a system error value, or zero if DISK is NULL
1331 on a system that requires a non-NULL value. */
1333 get_fs_usage (char const *file
, char const *disk
, struct fs_usage
*fsp
)
1335 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1337 if (statvfs_works ())
1339 struct statvfs vfsd
;
1341 if (statvfs (file
, &vfsd
) < 0)
1344 /* f_frsize isn't guaranteed to be supported. */
1345 fsp
->fsu_blocksize
= (vfsd
.f_frsize
1346 ? PROPAGATE_ALL_ONES (vfsd
.f_frsize
)
1347 : PROPAGATE_ALL_ONES (vfsd
.f_bsize
));
1349 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (vfsd
.f_blocks
);
1350 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (vfsd
.f_bfree
);
1351 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (vfsd
.f_bavail
);
1352 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (vfsd
.f_bavail
) != 0;
1353 fsp
->fsu_files
= PROPAGATE_ALL_ONES (vfsd
.f_files
);
1354 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (vfsd
.f_ffree
);
1360 #if defined STAT_STATVFS64 /* AIX */
1362 struct statvfs64 fsd
;
1364 if (statvfs64 (file
, &fsd
) < 0)
1367 /* f_frsize isn't guaranteed to be supported. */
1369 fsp
->fsu_blocksize
= fsd
.f_frsize
1370 ? PROPAGATE_ALL_ONES (fsd
.f_frsize
)
1371 : PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1374 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1378 if (statfs (file
, &fsd
) != 1)
1381 fsp
->fsu_blocksize
= 1024;
1382 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.fd_req
.btot
);
1383 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.fd_req
.bfree
);
1384 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.fd_req
.bfreen
);
1385 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.fd_req
.bfreen
) != 0;
1386 fsp
->fsu_files
= PROPAGATE_ALL_ONES (fsd
.fd_req
.gtot
);
1387 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.fd_req
.gfree
);
1389 #elif defined STAT_READ_FILSYS /* SVR2 */
1391 #define SUPERBOFF (SUPERB * 512)
1403 fd
= open (disk
, O_RDONLY
);
1406 lseek (fd
, (off_t
) SUPERBOFF
, 0);
1407 if (full_read (fd
, (char *) &fsd
, sizeof (fsd
)) != sizeof (fsd
))
1414 fsp
->fsu_blocksize
= (fsd
.s_type
== Fs2b
? 1024 : 512);
1415 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.s_fsize
);
1416 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.s_tfree
);
1417 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.s_tfree
);
1418 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.s_tfree
) != 0;
1419 fsp
->fsu_files
= (fsd
.s_isize
== -1
1420 ? UINTMAX_MAX
: (fsd
.s_isize
- 2) * INOPB
* (fsd
.s_type
== Fs2b
? 2 : 1));
1421 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.s_tinode
);
1423 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1427 if (statfs (file
, &fsd
, sizeof (struct statfs
)) != 0)
1430 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_fsize
);
1432 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1436 if (statfs (file
, &fsd
) < 0)
1439 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_frsize
);
1441 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1442 Mac OS X < 10.4, FreeBSD < 5.0, \
1443 NetBSD < 3.0, OpenBSD < 4.4 */
1447 if (statfs (file
, &fsd
) < 0)
1450 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1452 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1454 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1455 struct statfs are truncated to 2GB. These conditions detect that
1456 truncation, presumably without botching the 4.1.1 case, in which
1457 the values are not truncated. The correct counts are stored in
1458 undocumented spare fields. */
1459 if (fsd
.f_blocks
== 0x7fffffff / fsd
.f_bsize
&& fsd
.f_spare
[0] > 0)
1461 fsd
.f_blocks
= fsd
.f_spare
[0];
1462 fsd
.f_bfree
= fsd
.f_spare
[1];
1463 fsd
.f_bavail
= fsd
.f_spare
[2];
1465 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1467 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1471 if (statfs (file
, &fsd
) < 0)
1474 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_fsize
);
1476 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \
1479 #if !defined _AIX && !defined _SEQUENT_ && !defined DOLPHIN
1480 #define f_bavail f_bfree
1485 if (statfs (file
, &fsd
, sizeof (fsd
), 0) < 0)
1488 /* Empirically, the block counts on most SVR3 and SVR3-derived
1489 systems seem to always be in terms of 512-byte blocks,
1490 no matter what value f_bsize has. */
1491 #if defined _AIX || defined _CRAY
1492 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1494 fsp
->fsu_blocksize
= 512;
1499 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1500 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1501 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1503 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.f_blocks
);
1504 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.f_bfree
);
1505 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.f_bavail
);
1506 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.f_bavail
) != 0;
1507 fsp
->fsu_files
= PROPAGATE_ALL_ONES (fsd
.f_files
);
1508 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.f_ffree
);
1513 (void) disk
; /* avoid argument-unused warning */
1517 #endif /* HAVE_INFOMOUNT */
1519 /* --------------------------------------------------------------------------------------------- */
1520 /*** public functions ****************************************************************************/
1521 /* --------------------------------------------------------------------------------------------- */
1524 free_my_statfs (void)
1526 #ifdef HAVE_INFOMOUNT_LIST
1527 while (mc_mount_list
!= NULL
)
1529 struct mount_entry
*next
;
1531 next
= mc_mount_list
->me_next
;
1532 free_mount_entry (mc_mount_list
);
1533 mc_mount_list
= next
;
1536 mc_mount_list
= NULL
;
1537 #endif /* HAVE_INFOMOUNT_LIST */
1540 /* --------------------------------------------------------------------------------------------- */
1543 init_my_statfs (void)
1545 #ifdef HAVE_INFOMOUNT_LIST
1547 mc_mount_list
= read_file_system_list (1);
1548 #endif /* HAVE_INFOMOUNT_LIST */
1551 /* --------------------------------------------------------------------------------------------- */
1554 my_statfs (struct my_statfs
*myfs_stats
, const char *path
)
1556 #ifdef HAVE_INFOMOUNT_LIST
1558 struct mount_entry
*entry
= NULL
;
1559 struct mount_entry
*temp
= mc_mount_list
;
1560 struct fs_usage fs_use
;
1564 i
= strlen (temp
->me_mountdir
);
1565 if (i
> len
&& (strncmp (path
, temp
->me_mountdir
, i
) == 0))
1566 if (!entry
|| (path
[i
] == PATH_SEP
|| path
[i
] == '\0'))
1571 temp
= temp
->me_next
;
1576 memset (&fs_use
, 0, sizeof (struct fs_usage
));
1577 get_fs_usage (entry
->me_mountdir
, NULL
, &fs_use
);
1579 myfs_stats
->type
= entry
->me_dev
;
1580 myfs_stats
->typename
= entry
->me_type
;
1581 myfs_stats
->mpoint
= entry
->me_mountdir
;
1582 myfs_stats
->device
= entry
->me_devname
;
1584 ((uintmax_t) (getuid ()? fs_use
.fsu_bavail
: fs_use
.fsu_bfree
) *
1585 fs_use
.fsu_blocksize
) >> 10;
1586 myfs_stats
->total
= ((uintmax_t) fs_use
.fsu_blocks
* fs_use
.fsu_blocksize
) >> 10;
1587 myfs_stats
->nfree
= (uintmax_t) fs_use
.fsu_ffree
;
1588 myfs_stats
->nodes
= (uintmax_t) fs_use
.fsu_files
;
1591 #endif /* HAVE_INFOMOUNT_LIST */
1593 #ifdef HAVE_INFOMOUNT_QNX
1595 ** This is the "other side" of the hack to read_file_system_list() in
1597 ** It's not the most efficient approach, but consumes less memory. It
1598 ** also accomodates QNX's ability to mount filesystems on the fly.
1600 struct mount_entry
*entry
;
1601 struct fs_usage fs_use
;
1603 entry
= read_file_system_list (0, 0);
1606 get_fs_usage (entry
->me_mountdir
, NULL
, &fs_use
);
1608 myfs_stats
->type
= entry
->me_dev
;
1609 myfs_stats
->typename
= entry
->me_type
;
1610 myfs_stats
->mpoint
= entry
->me_mountdir
;
1611 myfs_stats
->device
= entry
->me_devname
;
1613 myfs_stats
->avail
= ((uintmax_t) fs_use
.fsu_bfree
* fs_use
.fsu_blocksize
) >> 10;
1614 myfs_stats
->total
= ((uintmax_t) fs_use
.fsu_blocks
* fs_use
.fsu_blocksize
) >> 10;
1615 myfs_stats
->nfree
= (uintmax_t) fs_use
.fsu_ffree
;
1616 myfs_stats
->nodes
= (uintmax_t) fs_use
.fsu_files
;
1619 #endif /* HAVE_INFOMOUNT_QNX */
1621 myfs_stats
->type
= 0;
1622 myfs_stats
->mpoint
= "unknown";
1623 myfs_stats
->device
= "unknown";
1624 myfs_stats
->avail
= 0;
1625 myfs_stats
->total
= 0;
1626 myfs_stats
->nfree
= 0;
1627 myfs_stats
->nodes
= 0;
1631 /* --------------------------------------------------------------------------------------------- */