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__))
285 #include <string.h> /* for strverscmp */
286 #include <sys/utsname.h>
287 #include <sys/statfs.h>
288 #define STAT_STATFS2_BSIZE 1
293 static int statvfs_works_cache
= -1;
296 if (statvfs_works_cache
< 0)
297 statvfs_works_cache
= (uname (&name
) == 0 && 0 <= strverscmp (name
.release
, "2.6.36"));
298 return statvfs_works_cache
;
303 #ifdef STAT_READ_FILSYS /* SVR2 */
304 /* Set errno to zero upon EOF. */
305 #define ZERO_BYTE_TRANSFER_ERRNO 0
308 #define IS_EINTR(x) ((x) == EINTR)
310 #define IS_EINTR(x) 0
312 #endif /* STAT_READ_FILSYS */
314 /*** file scope type declarations ****************************************************************/
316 /* A mount table entry. */
319 char *me_devname
; /* Device node name, including "/dev/". */
320 char *me_mountdir
; /* Mount point directory name. */
321 char *me_type
; /* "nfs", "4.2", etc. */
322 dev_t me_dev
; /* Device number of me_mountdir. */
323 unsigned int me_dummy
:1; /* Nonzero for dummy file systems. */
324 unsigned int me_remote
:1; /* Nonzero for remote fileystems. */
325 unsigned int me_type_malloced
:1; /* Nonzero if me_type was malloced. */
326 struct mount_entry
*me_next
;
331 uintmax_t fsu_blocksize
; /* Size of a block. */
332 uintmax_t fsu_blocks
; /* Total blocks. */
333 uintmax_t fsu_bfree
; /* Free blocks available to superuser. */
334 uintmax_t fsu_bavail
; /* Free blocks available to non-superuser. */
335 int fsu_bavail_top_bit_set
; /* 1 if fsu_bavail represents a value < 0. */
336 uintmax_t fsu_files
; /* Total file nodes. */
337 uintmax_t fsu_ffree
; /* Free file nodes. */
340 /*** file scope variables ************************************************************************/
342 #ifdef HAVE_INFOMOUNT_LIST
343 static struct mount_entry
*mc_mount_list
= NULL
;
344 #endif /* HAVE_INFOMOUNT_LIST */
346 /*** file scope functions ************************************************************************/
347 /* --------------------------------------------------------------------------------------------- */
349 #ifdef HAVE_INFOMOUNT_LIST
351 free_mount_entry (struct mount_entry
*me
)
356 free (me
->me_devname
);
358 free (me
->me_mountdir
);
359 if (me
->me_type
&& me
->me_type_malloced
)
364 /* --------------------------------------------------------------------------------------------- */
366 #ifdef MOUNTED_GETMNTINFO
368 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
370 fstype_to_string (short int t
)
462 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
464 /* --------------------------------------------------------------------------------------------- */
467 fsp_to_string (const struct statfs
*fsp
)
469 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
470 return (char *) (fsp
->f_fstypename
);
472 return fstype_to_string (fsp
->f_type
);
475 #endif /* MOUNTED_GETMNTINFO */
477 /* --------------------------------------------------------------------------------------------- */
479 #ifdef MOUNTED_VMOUNT /* AIX. */
481 fstype_to_string (int t
)
485 e
= getvfsbytype (t
);
486 if (!e
|| !e
->vfsent_name
)
489 return e
->vfsent_name
;
491 #endif /* MOUNTED_VMOUNT */
493 /* --------------------------------------------------------------------------------------------- */
495 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
497 /* Return the device number from MOUNT_OPTIONS, if possible.
498 Otherwise return (dev_t) -1. */
500 /* --------------------------------------------------------------------------------------------- */
503 dev_from_mount_options (char const *mount_options
)
505 /* GNU/Linux allows file system implementations to define their own
506 meaning for "dev=" mount options, so don't trust the meaning
509 static char const dev_pattern
[] = ",dev=";
510 char const *devopt
= strstr (mount_options
, dev_pattern
);
514 char const *optval
= devopt
+ sizeof (dev_pattern
) - 1;
516 unsigned long int dev
;
518 dev
= strtoul (optval
, &optvalend
, 16);
519 if (optval
!= optvalend
520 && (*optvalend
== '\0' || *optvalend
== ',')
521 && !(dev
== ULONG_MAX
&& errno
== ERANGE
) && dev
== (dev_t
) dev
)
526 (void) mount_options
;
532 /* --------------------------------------------------------------------------------------------- */
534 #if defined _AIX && defined _I386
535 /* AIX PS/2 does not supply statfs. */
538 statfs (char *file
, struct statfs
*fsb
)
543 if (stat (file
, &stats
) != 0)
545 if (dustat (stats
.st_dev
, 0, &fsd
, sizeof (fsd
)))
548 fsb
->f_bsize
= fsd
.du_bsize
;
549 fsb
->f_blocks
= fsd
.du_fsize
- fsd
.du_isize
;
550 fsb
->f_bfree
= fsd
.du_tfree
;
551 fsb
->f_bavail
= fsd
.du_tfree
;
552 fsb
->f_files
= (fsd
.du_isize
- 2) * fsd
.du_inopb
;
553 fsb
->f_ffree
= fsd
.du_tinode
;
554 fsb
->f_fsid
.val
[0] = fsd
.du_site
;
555 fsb
->f_fsid
.val
[1] = fsd
.du_pckno
;
559 #endif /* _AIX && _I386 */
561 /* --------------------------------------------------------------------------------------------- */
563 /* Return a list of the currently mounted file systems, or NULL on error.
564 Add each entry to the tail of the list so that they stay in order.
565 If NEED_FS_TYPE is true, ensure that the file system type fields in
566 the returned list are valid. Otherwise, they might not be. */
568 static struct mount_entry
*
569 read_file_system_list (int need_fs_type
)
571 struct mount_entry
*mount_list
;
572 struct mount_entry
*me
;
573 struct mount_entry
**mtail
= &mount_list
;
575 #ifdef MOUNTED_LISTMNTENT
577 struct tabmntent
*mntlist
, *p
;
579 struct mount_entry
*me
;
581 /* the third and fourth arguments could be used to filter mounts,
582 but Crays doesn't seem to have any mounts that we want to
583 remove. Specifically, automount create normal NFS mounts.
586 if (listmntent (&mntlist
, KMTAB
, NULL
, NULL
) < 0)
588 for (p
= mntlist
; p
; p
= p
->next
)
591 me
= malloc (sizeof (*me
));
592 me
->me_devname
= strdup (mnt
->mnt_fsname
);
593 me
->me_mountdir
= strdup (mnt
->mnt_dir
);
594 me
->me_type
= strdup (mnt
->mnt_type
);
595 me
->me_type_malloced
= 1;
596 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
597 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
600 mtail
= &me
->me_next
;
602 freemntlist (mntlist
);
606 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
609 const char *table
= MOUNTED
;
612 fp
= setmntent (table
, "r");
616 while ((mnt
= getmntent (fp
)))
618 me
= malloc (sizeof (*me
));
619 me
->me_devname
= strdup (mnt
->mnt_fsname
);
620 me
->me_mountdir
= strdup (mnt
->mnt_dir
);
621 me
->me_type
= strdup (mnt
->mnt_type
);
622 me
->me_type_malloced
= 1;
623 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
624 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
625 me
->me_dev
= dev_from_mount_options (mnt
->mnt_opts
);
627 /* Add to the linked list. */
629 mtail
= &me
->me_next
;
632 if (endmntent (fp
) == 0)
635 #endif /* MOUNTED_GETMNTENT1. */
637 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
642 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
645 for (; entries
-- > 0; fsp
++)
647 char *fs_type
= fsp_to_string (fsp
);
649 me
= malloc (sizeof (*me
));
650 me
->me_devname
= strdup (fsp
->f_mntfromname
);
651 me
->me_mountdir
= strdup (fsp
->f_mntonname
);
652 me
->me_type
= fs_type
;
653 me
->me_type_malloced
= 0;
654 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
655 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
656 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
658 /* Add to the linked list. */
660 mtail
= &me
->me_next
;
663 #endif /* MOUNTED_GETMNTINFO */
665 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
670 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
673 for (; entries
-- > 0; fsp
++)
675 me
= malloc (sizeof (*me
));
676 me
->me_devname
= strdup (fsp
->f_mntfromname
);
677 me
->me_mountdir
= strdup (fsp
->f_mntonname
);
678 me
->me_type
= strdup (fsp
->f_fstypename
);
679 me
->me_type_malloced
= 1;
680 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
681 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
682 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
684 /* Add to the linked list. */
686 mtail
= &me
->me_next
;
689 #endif /* MOUNTED_GETMNTINFO2 */
691 #ifdef MOUNTED_GETMNT /* Ultrix. */
697 while (errno
= 0, 0 < (val
= getmnt (&offset
, &fsd
, sizeof (fsd
), NOSTAT_MANY
, (char *) 0)))
699 me
= malloc (sizeof (*me
));
700 me
->me_devname
= strdup (fsd
.fd_req
.devname
);
701 me
->me_mountdir
= strdup (fsd
.fd_req
.path
);
702 me
->me_type
= gt_names
[fsd
.fd_req
.fstype
];
703 me
->me_type_malloced
= 0;
704 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
705 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
706 me
->me_dev
= fsd
.fd_req
.dev
;
708 /* Add to the linked list. */
710 mtail
= &me
->me_next
;
715 #endif /* MOUNTED_GETMNT. */
717 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
719 /* The next_dev() and fs_stat_dev() system calls give the list of
720 all file systems, including the information returned by statvfs()
721 (fs type, total blocks, free blocks etc.), but without the mount
722 point. But on BeOS all file systems except / are mounted in the
723 rootfs, directly under /.
724 The directory name of the mount point is often, but not always,
725 identical to the volume name of the device.
726 We therefore get the list of subdirectories of /, and the list
727 of all file systems, and match the two lists. */
735 struct rootdir_entry
*next
;
737 struct rootdir_entry
*rootdir_list
;
738 struct rootdir_entry
**rootdir_tail
;
743 /* All volumes are mounted in the rootfs, directly under /. */
745 rootdir_tail
= &rootdir_list
;
746 dirp
= opendir ("/");
751 while ((d
= readdir (dirp
)) != NULL
)
756 if (strcmp (d
->d_name
, "..") == 0)
759 if (strcmp (d
->d_name
, ".") == 0)
763 name
= malloc (1 + strlen (d
->d_name
) + 1);
765 strcpy (name
+ 1, d
->d_name
);
768 if (lstat (name
, &statbuf
) >= 0 && S_ISDIR (statbuf
.st_mode
))
770 struct rootdir_entry
*re
= malloc (sizeof (*re
));
772 re
->dev
= statbuf
.st_dev
;
773 re
->ino
= statbuf
.st_ino
;
775 /* Add to the linked list. */
777 rootdir_tail
= &re
->next
;
784 *rootdir_tail
= NULL
;
786 for (pos
= 0; (dev
= next_dev (&pos
)) >= 0;)
787 if (fs_stat_dev (dev
, &fi
) >= 0)
789 /* Note: fi.dev == dev. */
790 struct rootdir_entry
*re
;
792 for (re
= rootdir_list
; re
; re
= re
->next
)
793 if (re
->dev
== fi
.dev
&& re
->ino
== fi
.root
)
796 me
= malloc (sizeof (*me
));
797 me
->me_devname
= strdup (fi
.device_name
[0] != '\0' ? fi
.device_name
: fi
.fsh_name
);
798 me
->me_mountdir
= strdup (re
!= NULL
? re
->name
: fi
.fsh_name
);
799 me
->me_type
= strdup (fi
.fsh_name
);
800 me
->me_type_malloced
= 1;
803 me
->me_remote
= (fi
.flags
& B_FS_IS_SHARED
) != 0;
805 /* Add to the linked list. */
807 mtail
= &me
->me_next
;
811 while (rootdir_list
!= NULL
)
813 struct rootdir_entry
*re
= rootdir_list
;
814 rootdir_list
= re
->next
;
819 #endif /* MOUNTED_FS_STAT_DEV */
821 #ifdef MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
825 struct statfs
*stats
;
827 numsys
= getfsstat (NULL
, 0L, MNT_NOWAIT
);
830 if (SIZE_MAX
/ sizeof (*stats
) <= numsys
)
832 fprintf (stderr
, "%s\n", _("Memory exhausted!"));
836 bufsize
= (1 + numsys
) * sizeof (*stats
);
837 stats
= malloc (bufsize
);
838 numsys
= getfsstat (stats
, bufsize
, MNT_NOWAIT
);
846 for (counter
= 0; counter
< numsys
; counter
++)
848 me
= malloc (sizeof (*me
));
849 me
->me_devname
= strdup (stats
[counter
].f_mntfromname
);
850 me
->me_mountdir
= strdup (stats
[counter
].f_mntonname
);
851 me
->me_type
= strdup (FS_TYPE (stats
[counter
]));
852 me
->me_type_malloced
= 1;
853 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
854 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
855 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
857 /* Add to the linked list. */
859 mtail
= &me
->me_next
;
864 #endif /* MOUNTED_GETFSSTAT */
866 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
869 char *table
= "/etc/mnttab";
872 fp
= fopen (table
, "r");
876 while (fread (&mnt
, sizeof (mnt
), 1, fp
) > 0)
878 me
= malloc (sizeof (*me
));
879 #ifdef GETFSTYP /* SVR3. */
880 me
->me_devname
= strdup (mnt
.mt_dev
);
882 me
->me_devname
= malloc (strlen (mnt
.mt_dev
) + 6);
883 strcpy (me
->me_devname
, "/dev/");
884 strcpy (me
->me_devname
+ 5, mnt
.mt_dev
);
886 me
->me_mountdir
= strdup (mnt
.mt_filsys
);
887 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
889 me
->me_type_malloced
= 0;
890 #ifdef GETFSTYP /* SVR3. */
894 char typebuf
[FSTYPSZ
];
896 if (statfs (me
->me_mountdir
, &fsd
, sizeof (fsd
), 0) != -1
897 && sysfs (GETFSTYP
, fsd
.f_fstyp
, typebuf
) != -1)
899 me
->me_type
= strdup (typebuf
);
900 me
->me_type_malloced
= 1;
904 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
905 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
907 /* Add to the linked list. */
909 mtail
= &me
->me_next
;
914 /* The last fread() call must have failed. */
915 int saved_errno
= errno
;
921 if (fclose (fp
) == EOF
)
924 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
926 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
928 struct mntent
**mnttbl
= getmnttbl (), **ent
;
929 for (ent
= mnttbl
; *ent
; ent
++)
931 me
= malloc (sizeof (*me
));
932 me
->me_devname
= strdup ((*ent
)->mt_resource
);
933 me
->me_mountdir
= strdup ((*ent
)->mt_directory
);
934 me
->me_type
= strdup ((*ent
)->mt_fstype
);
935 me
->me_type_malloced
= 1;
936 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
937 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
938 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
940 /* Add to the linked list. */
942 mtail
= &me
->me_next
;
948 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
951 char *table
= MNTTAB
;
956 #if defined F_RDLCK && defined F_SETLKW
957 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
958 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
959 for this file name, we should use their macro name instead.
960 (Why not just lock MNTTAB directly? We don't know.) */
962 #define MNTTAB_LOCK "/etc/.mnttab.lock"
964 lockfd
= open (MNTTAB_LOCK
, O_RDONLY
);
968 flock
.l_type
= F_RDLCK
;
969 flock
.l_whence
= SEEK_SET
;
972 while (fcntl (lockfd
, F_SETLKW
, &flock
) == -1)
975 int saved_errno
= errno
;
981 else if (errno
!= ENOENT
)
986 fp
= fopen (table
, "r");
991 while ((ret
= getmntent (fp
, &mnt
)) == 0)
993 me
= malloc (sizeof (*me
));
994 me
->me_devname
= strdup (mnt
.mnt_special
);
995 me
->me_mountdir
= strdup (mnt
.mnt_mountp
);
996 me
->me_type
= strdup (mnt
.mnt_fstype
);
997 me
->me_type_malloced
= 1;
998 me
->me_dummy
= MNT_IGNORE (&mnt
) != 0;
999 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
1000 me
->me_dev
= dev_from_mount_options (mnt
.mnt_mntopts
);
1002 /* Add to the linked list. */
1004 mtail
= &me
->me_next
;
1007 ret
= fclose (fp
) == EOF
? errno
: 0 < ret
? 0 : -1;
1010 if (0 <= lockfd
&& close (lockfd
) != 0)
1016 goto free_then_fail
;
1019 #endif /* MOUNTED_GETMNTENT2. */
1021 #ifdef MOUNTED_VMOUNT /* AIX. */
1024 char *entries
, *thisent
;
1029 /* Ask how many bytes to allocate for the mounted file system info. */
1030 if (mntctl (MCTL_QUERY
, sizeof (bufsize
), (struct vmount
*) &bufsize
) != 0)
1032 entries
= malloc (bufsize
);
1034 /* Get the list of mounted file systems. */
1035 n_entries
= mntctl (MCTL_QUERY
, bufsize
, (struct vmount
*) entries
);
1038 int saved_errno
= errno
;
1040 errno
= saved_errno
;
1044 for (i
= 0, thisent
= entries
; i
< n_entries
; i
++, thisent
+= vmp
->vmt_length
)
1046 char *options
, *ignore
;
1048 vmp
= (struct vmount
*) thisent
;
1049 me
= malloc (sizeof (*me
));
1050 if (vmp
->vmt_flags
& MNT_REMOTE
)
1055 /* Prepend the remote dirname. */
1056 host
= thisent
+ vmp
->vmt_data
[VMT_HOSTNAME
].vmt_off
;
1057 dir
= thisent
+ vmp
->vmt_data
[VMT_OBJECT
].vmt_off
;
1058 me
->me_devname
= malloc (strlen (host
) + strlen (dir
) + 2);
1059 strcpy (me
->me_devname
, host
);
1060 strcat (me
->me_devname
, ":");
1061 strcat (me
->me_devname
, dir
);
1066 me
->me_devname
= strdup (thisent
+ vmp
->vmt_data
[VMT_OBJECT
].vmt_off
);
1068 me
->me_mountdir
= strdup (thisent
+ vmp
->vmt_data
[VMT_STUB
].vmt_off
);
1069 me
->me_type
= strdup (fstype_to_string (vmp
->vmt_gfstype
));
1070 me
->me_type_malloced
= 1;
1071 options
= thisent
+ vmp
->vmt_data
[VMT_ARGS
].vmt_off
;
1072 ignore
= strstr (options
, "ignore");
1073 me
->me_dummy
= (ignore
1074 && (ignore
== options
|| ignore
[-1] == ',')
1075 && (ignore
[sizeof ("ignore") - 1] == ','
1076 || ignore
[sizeof ("ignore") - 1] == '\0'));
1077 me
->me_dev
= (dev_t
) (-1); /* vmt_fsid might be the info we want. */
1079 /* Add to the linked list. */
1081 mtail
= &me
->me_next
;
1085 #endif /* MOUNTED_VMOUNT. */
1088 #ifdef MOUNTED_INTERIX_STATVFS
1090 DIR *dirp
= opendir ("/dev/fs");
1091 char node
[9 + NAME_MAX
];
1094 goto free_then_fail
;
1099 struct dirent entry
;
1100 struct dirent
*result
;
1102 if (readdir_r (dirp
, &entry
, &result
) || result
== NULL
)
1105 strcpy (node
, "/dev/fs/");
1106 strcat (node
, entry
.d_name
);
1108 if (statvfs (node
, &dev
) == 0)
1110 me
= malloc (sizeof *me
);
1111 me
->me_devname
= strdup (dev
.f_mntfromname
);
1112 me
->me_mountdir
= strdup (dev
.f_mntonname
);
1113 me
->me_type
= strdup (dev
.f_fstypename
);
1114 me
->me_type_malloced
= 1;
1115 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
1116 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
1117 me
->me_dev
= (dev_t
) - 1; /* Magic; means not known yet. */
1119 /* Add to the linked list. */
1121 mtail
= &me
->me_next
;
1125 #endif /* MOUNTED_INTERIX_STATVFS */
1127 (void) need_fs_type
; /* avoid argument-unused warning */
1134 int saved_errno
= errno
;
1139 me
= mount_list
->me_next
;
1140 free (mount_list
->me_devname
);
1141 free (mount_list
->me_mountdir
);
1142 if (mount_list
->me_type_malloced
)
1143 free (mount_list
->me_type
);
1148 errno
= saved_errno
;
1153 #endif /* HAVE_INFOMOUNT_LIST */
1155 /* --------------------------------------------------------------------------------------------- */
1157 #ifdef HAVE_INFOMOUNT_QNX
1159 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1160 ** this via the following code.
1161 ** Note that, as this is based on CWD, it only fills one mount_entry
1162 ** structure. See my_statfs() in utilunix.c for the "other side" of
1166 static struct mount_entry
*
1167 read_file_system_list (int need_fs_type
, int all_fs
)
1169 struct _disk_entry de
;
1172 char *tp
, dev
[_POSIX_NAME_MAX
], dir
[_POSIX_PATH_MAX
];
1174 static struct mount_entry
*me
= NULL
;
1179 free (me
->me_devname
);
1180 if (me
->me_mountdir
)
1181 free (me
->me_mountdir
);
1186 me
= (struct mount_entry
*) malloc (sizeof (struct mount_entry
));
1188 if (!getcwd (dir
, _POSIX_PATH_MAX
))
1191 fd
= open (dir
, O_RDONLY
);
1195 i
= disk_get_entry (fd
, &de
);
1202 switch (de
.disk_type
)
1229 if (fsys_get_mount_dev (dir
, &dev
) == -1)
1232 if (fsys_get_mount_pt (dev
, &dir
) == -1)
1235 me
->me_devname
= strdup (dev
);
1236 me
->me_mountdir
= strdup (dir
);
1237 me
->me_type
= strdup (tp
);
1238 me
->me_dev
= de
.disk_type
;
1242 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1243 de
.disk_type
, tp
, _DRIVER_NAME_LEN
, _DRIVER_NAME_LEN
, de
.driver_name
, de
.disk_drv
);
1244 fprintf (stderr
, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev
);
1245 fprintf (stderr
, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir
);
1250 #endif /* HAVE_INFOMOUNT_QNX */
1252 /* --------------------------------------------------------------------------------------------- */
1254 #ifdef STAT_READ_FILSYS /* SVR2 */
1256 /* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
1257 interrupted. Return the actual number of bytes read(written), zero for EOF,
1258 or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
1260 safe_read (int fd
, void *buf
, size_t count
)
1262 /* Work around a bug in Tru64 5.1. Attempting to read more than
1263 INT_MAX bytes fails with errno == EINVAL. See
1264 <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
1265 When decreasing COUNT, keep it block-aligned. */
1267 enum { BUGGY_READ_MAXIMUM
= INT_MAX
& ~8191 };
1272 ssize_t result
= read (fd
, buf
, count
);
1276 else if (IS_EINTR (errno
))
1278 else if (errno
== EINVAL
&& BUGGY_READ_MAXIMUM
< count
)
1279 count
= BUGGY_READ_MAXIMUM
;
1285 /* --------------------------------------------------------------------------------------------- */
1287 /* Read COUNT bytes at BUF to(from) descriptor FD, retrying if
1288 interrupted or if a partial write(read) occurs. Return the number
1289 of bytes transferred.
1290 When writing, set errno if fewer than COUNT bytes are written.
1291 When reading, if fewer than COUNT bytes are read, you must examine
1292 errno to distinguish failure from EOF (errno == 0). */
1295 full_read (int fd
, void *buf
, size_t count
)
1298 char *ptr
= (char *) buf
;
1302 size_t n_rw
= safe_read (fd
, ptr
, count
);
1303 if (n_rw
== (size_t) (-1))
1307 errno
= ZERO_BYTE_TRANSFER_ERRNO
;
1318 #endif /* STAT_READ_FILSYS */
1320 /* --------------------------------------------------------------------------------------------- */
1322 #ifdef HAVE_INFOMOUNT
1323 /* Fill in the fields of FSP with information about space usage for
1324 the file system on which FILE resides.
1325 DISK is the device on which FILE is mounted, for space-getting
1326 methods that need to know it.
1327 Return 0 if successful, -1 if not. When returning -1, ensure that
1328 ERRNO is either a system error value, or zero if DISK is NULL
1329 on a system that requires a non-NULL value. */
1331 get_fs_usage (char const *file
, char const *disk
, struct fs_usage
*fsp
)
1333 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1335 if (statvfs_works ())
1337 struct statvfs vfsd
;
1339 if (statvfs (file
, &vfsd
) < 0)
1342 /* f_frsize isn't guaranteed to be supported. */
1343 fsp
->fsu_blocksize
= (vfsd
.f_frsize
1344 ? PROPAGATE_ALL_ONES (vfsd
.f_frsize
)
1345 : PROPAGATE_ALL_ONES (vfsd
.f_bsize
));
1347 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (vfsd
.f_blocks
);
1348 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (vfsd
.f_bfree
);
1349 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (vfsd
.f_bavail
);
1350 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (vfsd
.f_bavail
) != 0;
1351 fsp
->fsu_files
= PROPAGATE_ALL_ONES (vfsd
.f_files
);
1352 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (vfsd
.f_ffree
);
1358 #if defined STAT_STATVFS64 /* AIX */
1360 struct statvfs64 fsd
;
1362 if (statvfs64 (file
, &fsd
) < 0)
1365 /* f_frsize isn't guaranteed to be supported. */
1367 fsp
->fsu_blocksize
= fsd
.f_frsize
1368 ? PROPAGATE_ALL_ONES (fsd
.f_frsize
)
1369 : PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1372 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1376 if (statfs (file
, &fsd
) != 1)
1379 fsp
->fsu_blocksize
= 1024;
1380 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.fd_req
.btot
);
1381 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.fd_req
.bfree
);
1382 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.fd_req
.bfreen
);
1383 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.fd_req
.bfreen
) != 0;
1384 fsp
->fsu_files
= PROPAGATE_ALL_ONES (fsd
.fd_req
.gtot
);
1385 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.fd_req
.gfree
);
1387 #elif defined STAT_READ_FILSYS /* SVR2 */
1389 #define SUPERBOFF (SUPERB * 512)
1401 fd
= open (disk
, O_RDONLY
);
1404 lseek (fd
, (off_t
) SUPERBOFF
, 0);
1405 if (full_read (fd
, (char *) &fsd
, sizeof (fsd
)) != sizeof (fsd
))
1412 fsp
->fsu_blocksize
= (fsd
.s_type
== Fs2b
? 1024 : 512);
1413 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.s_fsize
);
1414 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.s_tfree
);
1415 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.s_tfree
);
1416 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.s_tfree
) != 0;
1417 fsp
->fsu_files
= (fsd
.s_isize
== -1
1418 ? UINTMAX_MAX
: (fsd
.s_isize
- 2) * INOPB
* (fsd
.s_type
== Fs2b
? 2 : 1));
1419 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.s_tinode
);
1421 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1425 if (statfs (file
, &fsd
, sizeof (struct statfs
)) != 0)
1428 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_fsize
);
1430 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1434 if (statfs (file
, &fsd
) < 0)
1437 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_frsize
);
1439 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1440 Mac OS X < 10.4, FreeBSD < 5.0, \
1441 NetBSD < 3.0, OpenBSD < 4.4 */
1445 if (statfs (file
, &fsd
) < 0)
1448 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1450 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1452 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1453 struct statfs are truncated to 2GB. These conditions detect that
1454 truncation, presumably without botching the 4.1.1 case, in which
1455 the values are not truncated. The correct counts are stored in
1456 undocumented spare fields. */
1457 if (fsd
.f_blocks
== 0x7fffffff / fsd
.f_bsize
&& fsd
.f_spare
[0] > 0)
1459 fsd
.f_blocks
= fsd
.f_spare
[0];
1460 fsd
.f_bfree
= fsd
.f_spare
[1];
1461 fsd
.f_bavail
= fsd
.f_spare
[2];
1463 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1465 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1469 if (statfs (file
, &fsd
) < 0)
1472 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_fsize
);
1474 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \
1477 #if !defined _AIX && !defined _SEQUENT_ && !defined DOLPHIN
1478 #define f_bavail f_bfree
1483 if (statfs (file
, &fsd
, sizeof (fsd
), 0) < 0)
1486 /* Empirically, the block counts on most SVR3 and SVR3-derived
1487 systems seem to always be in terms of 512-byte blocks,
1488 no matter what value f_bsize has. */
1489 #if defined _AIX || defined _CRAY
1490 fsp
->fsu_blocksize
= PROPAGATE_ALL_ONES (fsd
.f_bsize
);
1492 fsp
->fsu_blocksize
= 512;
1497 #if (defined STAT_STATVFS64 \
1498 || (!defined STAT_STATFS2_FS_DATA && !defined STAT_READ_FILSYS))
1500 fsp
->fsu_blocks
= PROPAGATE_ALL_ONES (fsd
.f_blocks
);
1501 fsp
->fsu_bfree
= PROPAGATE_ALL_ONES (fsd
.f_bfree
);
1502 fsp
->fsu_bavail
= PROPAGATE_TOP_BIT (fsd
.f_bavail
);
1503 fsp
->fsu_bavail_top_bit_set
= EXTRACT_TOP_BIT (fsd
.f_bavail
) != 0;
1504 fsp
->fsu_files
= PROPAGATE_ALL_ONES (fsd
.f_files
);
1505 fsp
->fsu_ffree
= PROPAGATE_ALL_ONES (fsd
.f_ffree
);
1508 (void) disk
; /* avoid argument-unused warning */
1511 #endif /* HAVE_INFOMOUNT */
1513 /* --------------------------------------------------------------------------------------------- */
1514 /*** public functions ****************************************************************************/
1515 /* --------------------------------------------------------------------------------------------- */
1518 free_my_statfs (void)
1520 #ifdef HAVE_INFOMOUNT_LIST
1521 while (mc_mount_list
!= NULL
)
1523 struct mount_entry
*next
;
1525 next
= mc_mount_list
->me_next
;
1526 free_mount_entry (mc_mount_list
);
1527 mc_mount_list
= next
;
1530 mc_mount_list
= NULL
;
1531 #endif /* HAVE_INFOMOUNT_LIST */
1534 /* --------------------------------------------------------------------------------------------- */
1537 init_my_statfs (void)
1539 #ifdef HAVE_INFOMOUNT_LIST
1541 mc_mount_list
= read_file_system_list (1);
1542 #endif /* HAVE_INFOMOUNT_LIST */
1545 /* --------------------------------------------------------------------------------------------- */
1548 my_statfs (struct my_statfs
*myfs_stats
, const char *path
)
1550 #ifdef HAVE_INFOMOUNT_LIST
1552 struct mount_entry
*entry
= NULL
;
1553 struct mount_entry
*temp
= mc_mount_list
;
1554 struct fs_usage fs_use
;
1558 i
= strlen (temp
->me_mountdir
);
1559 if (i
> len
&& (strncmp (path
, temp
->me_mountdir
, i
) == 0))
1560 if (!entry
|| (path
[i
] == PATH_SEP
|| path
[i
] == '\0'))
1565 temp
= temp
->me_next
;
1570 memset (&fs_use
, 0, sizeof (struct fs_usage
));
1571 get_fs_usage (entry
->me_mountdir
, NULL
, &fs_use
);
1573 myfs_stats
->type
= entry
->me_dev
;
1574 myfs_stats
->typename
= entry
->me_type
;
1575 myfs_stats
->mpoint
= entry
->me_mountdir
;
1576 myfs_stats
->device
= entry
->me_devname
;
1578 ((uintmax_t) (getuid ()? fs_use
.fsu_bavail
: fs_use
.fsu_bfree
) *
1579 fs_use
.fsu_blocksize
) >> 10;
1580 myfs_stats
->total
= ((uintmax_t) fs_use
.fsu_blocks
* fs_use
.fsu_blocksize
) >> 10;
1581 myfs_stats
->nfree
= (uintmax_t) fs_use
.fsu_ffree
;
1582 myfs_stats
->nodes
= (uintmax_t) fs_use
.fsu_files
;
1585 #endif /* HAVE_INFOMOUNT_LIST */
1587 #ifdef HAVE_INFOMOUNT_QNX
1589 ** This is the "other side" of the hack to read_file_system_list() in
1591 ** It's not the most efficient approach, but consumes less memory. It
1592 ** also accomodates QNX's ability to mount filesystems on the fly.
1594 struct mount_entry
*entry
;
1595 struct fs_usage fs_use
;
1597 entry
= read_file_system_list (0, 0);
1600 get_fs_usage (entry
->me_mountdir
, NULL
, &fs_use
);
1602 myfs_stats
->type
= entry
->me_dev
;
1603 myfs_stats
->typename
= entry
->me_type
;
1604 myfs_stats
->mpoint
= entry
->me_mountdir
;
1605 myfs_stats
->device
= entry
->me_devname
;
1607 myfs_stats
->avail
= ((uintmax_t) fs_use
.fsu_bfree
* fs_use
.fsu_blocksize
) >> 10;
1608 myfs_stats
->total
= ((uintmax_t) fs_use
.fsu_blocks
* fs_use
.fsu_blocksize
) >> 10;
1609 myfs_stats
->nfree
= (uintmax_t) fs_use
.fsu_ffree
;
1610 myfs_stats
->nodes
= (uintmax_t) fs_use
.fsu_files
;
1613 #endif /* HAVE_INFOMOUNT_QNX */
1615 myfs_stats
->type
= 0;
1616 myfs_stats
->mpoint
= "unknown";
1617 myfs_stats
->device
= "unknown";
1618 myfs_stats
->avail
= 0;
1619 myfs_stats
->total
= 0;
1620 myfs_stats
->nfree
= 0;
1621 myfs_stats
->nodes
= 0;
1625 /* --------------------------------------------------------------------------------------------- */