Update copyright years.
[midnight-commander.git] / src / filemanager / mountlist.c
blob3aa7a6a3399466e6c23d6b14be8aeadc26b7c8bf
1 /*
2 Return a list of mounted file systems
4 Copyright (C) 1991-2016
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file mountlist.c
24 * \brief Source: list of mounted filesystems
27 #include <config.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdint.h> /* SIZE_MAX */
34 #include <sys/types.h>
36 #include <errno.h>
38 /* This header needs to be included before sys/mount.h on *BSD */
39 #ifdef HAVE_SYS_PARAM_H
40 #include <sys/param.h>
41 #endif
43 #if defined STAT_STATVFS || defined STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
44 #include <sys/statvfs.h>
45 #else
46 /* Don't include backward-compatibility files unless they're needed.
47 Eventually we'd like to remove all this cruft. */
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <sys/stat.h>
52 #ifdef MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
53 #ifdef HAVE_SYS_UCRED_H
54 #include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
55 NGROUPS is used as an array dimension in ucred.h */
56 #include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
57 #endif
58 #ifdef HAVE_SYS_MOUNT_H
59 #include <sys/mount.h>
60 #endif
61 #ifdef HAVE_SYS_FS_TYPES_H
62 #include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
63 #endif
64 #ifdef HAVE_STRUCT_FSSTAT_F_FSTYPENAME
65 #define FS_TYPE(Ent) ((Ent).f_fstypename)
66 #else
67 #define FS_TYPE(Ent) mnt_names[(Ent).f_type]
68 #endif
69 #endif /* MOUNTED_GETFSSTAT */
70 #endif /* STAT_STATVFS || STAT_STATVFS64 */
72 #ifdef HAVE_SYS_VFS_H
73 #include <sys/vfs.h>
74 #endif
75 #ifdef HAVE_SYS_FS_S5PARAM_H /* Fujitsu UXP/V */
76 #include <sys/fs/s5param.h>
77 #endif
78 #if defined HAVE_SYS_FILSYS_H && !defined _CRAY
79 #include <sys/filsys.h> /* SVR2 */
80 #endif
81 #ifdef HAVE_SYS_STATFS_H
82 #include <sys/statfs.h>
83 #endif
84 #ifdef HAVE_DUSTAT_H /* AIX PS/2 */
85 #include <sys/dustat.h>
86 #endif
88 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
89 #include <mntent.h>
90 #include <sys/types.h>
91 #ifndef MOUNTED
92 #ifdef _PATH_MOUNTED /* GNU libc */
93 #define MOUNTED _PATH_MOUNTED
94 #endif
95 #ifdef MNT_MNTTAB /* HP-UX. */
96 #define MOUNTED MNT_MNTTAB
97 #endif
98 #ifdef MNTTABNAME /* Dynix. */
99 #define MOUNTED MNTTABNAME
100 #endif
101 #endif
102 #endif
104 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
105 #include <sys/mount.h>
106 #endif
108 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
109 #include <sys/statvfs.h>
110 #endif
112 #ifdef MOUNTED_GETMNT /* Ultrix. */
113 #include <sys/mount.h>
114 #include <sys/fs_types.h>
115 #endif
117 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
118 #include <fs_info.h>
119 #include <dirent.h>
120 #endif
122 #ifdef MOUNTED_FREAD /* SVR2. */
123 #include <mnttab.h>
124 #endif
126 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
127 #include <mnttab.h>
128 #include <sys/fstyp.h>
129 #include <sys/statfs.h>
130 #endif
132 #ifdef MOUNTED_LISTMNTENT
133 #include <mntent.h>
134 #endif
136 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
137 #include <sys/mnttab.h>
138 #endif
140 #ifdef MOUNTED_VMOUNT /* AIX. */
141 #include <fshelp.h>
142 #include <sys/vfs.h>
143 #endif
145 #ifdef MOUNTED_INTERIX_STATVFS /* Interix. */
146 #include <sys/statvfs.h>
147 #include <dirent.h>
148 #endif
150 #ifdef DOLPHIN
151 /* So special that it's not worth putting this in autoconf. */
152 #undef MOUNTED_FREAD_FSTYP
153 #define MOUNTED_GETMNTTBL
154 #endif
156 #ifdef HAVE_SYS_MNTENT_H
157 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
158 #include <sys/mntent.h>
159 #endif
161 #ifndef HAVE_HASMNTOPT
162 #define hasmntopt(mnt, opt) ((char *) 0)
163 #endif
165 #undef MNT_IGNORE
166 #ifdef MNTOPT_IGNORE
167 #if defined __sun && defined __SVR4
168 /* Solaris defines hasmntopt(struct mnttab *, char *)
169 while it is otherwise hasmntopt(struct mnttab *, const char *). */
170 #define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
171 #else
172 #define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
173 #endif
174 #else
175 #define MNT_IGNORE(M) 0
176 #endif
178 #ifdef HAVE_INFOMOUNT_QNX
179 #include <sys/disk.h>
180 #include <sys/fsys.h>
181 #endif
183 #ifdef HAVE_SYS_STATVFS_H /* SVR4. */
184 #include <sys/statvfs.h>
185 #endif
187 #include "lib/global.h"
188 #include "lib/strutil.h" /* str_verscmp() */
189 #include "mountlist.h"
191 /*** global variables ****************************************************************************/
193 /*** file scope macro definitions ****************************************************************/
195 #if defined (__QNX__) && !defined(__QNXNTO__) && !defined (HAVE_INFOMOUNT_LIST)
196 #define HAVE_INFOMOUNT_QNX
197 #endif
199 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
200 #define HAVE_INFOMOUNT
201 #endif
203 /* The results of opendir() in this file are not used with dirfd and fchdir,
204 therefore save some unnecessary work in fchdir.c. */
205 #undef opendir
206 #undef closedir
208 #define ME_DUMMY_0(Fs_name, Fs_type) \
209 (strcmp (Fs_type, "autofs") == 0 \
210 || strcmp (Fs_type, "proc") == 0 \
211 || strcmp (Fs_type, "subfs") == 0 \
212 /* for Linux 2.6/3.x */ \
213 || strcmp (Fs_type, "debugfs") == 0 \
214 || strcmp (Fs_type, "devpts") == 0 \
215 || strcmp (Fs_type, "fusectl") == 0 \
216 || strcmp (Fs_type, "mqueue") == 0 \
217 || strcmp (Fs_type, "rpc_pipefs") == 0 \
218 || strcmp (Fs_type, "sysfs") == 0 \
219 /* FreeBSD, Linux 2.4 */ \
220 || strcmp (Fs_type, "devfs") == 0 \
221 /* for NetBSD 3.0 */ \
222 || strcmp (Fs_type, "kernfs") == 0 \
223 /* for Irix 6.5 */ \
224 || strcmp (Fs_type, "ignore") == 0)
226 /* Historically, we have marked as "dummy" any file system of type "none",
227 but now that programs like du need to know about bind-mounted directories,
228 we grant an exception to any with "bind" in its list of mount options.
229 I.e., those are *not* dummy entries. */
230 #ifdef MOUNTED_GETMNTENT1
231 #define ME_DUMMY(Fs_name, Fs_type, Bind) \
232 (ME_DUMMY_0 (Fs_name, Fs_type) \
233 || (strcmp (Fs_type, "none") == 0 && !Bind))
234 #else
235 #define ME_DUMMY(Fs_name, Fs_type) \
236 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
237 #endif
239 #ifdef __CYGWIN__
240 #include <windows.h>
241 #define ME_REMOTE me_remote
242 /* All cygwin mount points include ':' or start with '//'; so it
243 requires a native Windows call to determine remote disks. */
244 static int
245 me_remote (char const *fs_name, char const *fs_type)
247 (void) fs_type;
249 if (fs_name[0] && fs_name[1] == ':')
251 char drive[4];
252 sprintf (drive, "%c:\\", fs_name[0]);
253 switch (GetDriveType (drive))
255 case DRIVE_REMOVABLE:
256 case DRIVE_FIXED:
257 case DRIVE_CDROM:
258 case DRIVE_RAMDISK:
259 return 0;
262 return 1;
264 #endif
265 #ifndef ME_REMOTE
266 /* A file system is 'remote' if its Fs_name contains a ':'
267 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//'). */
268 #define ME_REMOTE(Fs_name, Fs_type) \
269 (strchr (Fs_name, ':') != NULL \
270 || ((Fs_name)[0] == '/' \
271 && (Fs_name)[1] == '/' \
272 && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "cifs") == 0)))
273 #endif
275 /* Many space usage primitives use all 1 bits to denote a value that is
276 not applicable or unknown. Propagate this information by returning
277 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
278 is unsigned and narrower than uintmax_t. */
279 #define PROPAGATE_ALL_ONES(x) \
280 ((sizeof (x) < sizeof (uintmax_t) \
281 && (~ (x) == (sizeof (x) < sizeof (int) \
282 ? - (1 << (sizeof (x) * CHAR_BIT)) \
283 : 0))) \
284 ? UINTMAX_MAX : (uintmax_t) (x))
286 /* Extract the top bit of X as an uintmax_t value. */
287 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
289 /* If a value is negative, many space usage primitives store it into an
290 integer variable by assignment, even if the variable's type is unsigned.
291 So, if a space usage variable X's top bit is set, convert X to the
292 uintmax_t value V such that (- (uintmax_t) V) is the negative of
293 the original value. If X's top bit is clear, just yield X.
294 Use PROPAGATE_TOP_BIT if the original value might be negative;
295 otherwise, use PROPAGATE_ALL_ONES. */
296 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
298 #ifdef STAT_STATVFS
299 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
300 /* The FRSIZE fallback is not required in this case. */
301 #undef STAT_STATFS2_FRSIZE
302 #else
303 #include <sys/utsname.h>
304 #include <sys/statfs.h>
305 #define STAT_STATFS2_BSIZE 1
306 #endif
307 #endif
309 #ifdef STAT_READ_FILSYS /* SVR2 */
310 /* Set errno to zero upon EOF. */
311 #define ZERO_BYTE_TRANSFER_ERRNO 0
313 #ifdef EINTR
314 #define IS_EINTR(x) ((x) == EINTR)
315 #else
316 #define IS_EINTR(x) 0
317 #endif
318 #endif /* STAT_READ_FILSYS */
320 /*** file scope type declarations ****************************************************************/
322 /* A mount table entry. */
323 struct mount_entry
325 char *me_devname; /* Device node name, including "/dev/". */
326 char *me_mountdir; /* Mount point directory name. */
327 char *me_mntroot; /* Directory on filesystem of device used
328 as root for the (bind) mount. */
329 char *me_type; /* "nfs", "4.2", etc. */
330 dev_t me_dev; /* Device number of me_mountdir. */
331 unsigned int me_dummy:1; /* Nonzero for dummy file systems. */
332 unsigned int me_remote:1; /* Nonzero for remote fileystems. */
333 unsigned int me_type_malloced:1; /* Nonzero if me_type was malloced. */
336 struct fs_usage
338 uintmax_t fsu_blocksize; /* Size of a block. */
339 uintmax_t fsu_blocks; /* Total blocks. */
340 uintmax_t fsu_bfree; /* Free blocks available to superuser. */
341 uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
342 int fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
343 uintmax_t fsu_files; /* Total file nodes. */
344 uintmax_t fsu_ffree; /* Free file nodes. */
347 /*** file scope variables ************************************************************************/
349 #ifdef HAVE_INFOMOUNT_LIST
350 static GSList *mc_mount_list = NULL;
351 #endif /* HAVE_INFOMOUNT_LIST */
353 /*** file scope functions ************************************************************************/
354 /* --------------------------------------------------------------------------------------------- */
356 #ifdef STAT_STATVFS
357 /* Return true if statvfs works. This is false for statvfs on systems
358 with GNU libc on Linux kernels before 2.6.36, which stats all
359 preceding entries in /proc/mounts; that makes df hang if even one
360 of the corresponding file systems is hard-mounted but not available. */
361 static int
362 statvfs_works (void)
364 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
365 return 1;
366 #else
367 static int statvfs_works_cache = -1;
368 struct utsname name;
370 if (statvfs_works_cache < 0)
371 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
372 return statvfs_works_cache;
373 #endif
375 #endif
377 /* --------------------------------------------------------------------------------------------- */
379 #ifdef HAVE_INFOMOUNT_LIST
380 static void
381 free_mount_entry (struct mount_entry *me)
383 if (me == NULL)
384 return;
385 g_free (me->me_devname);
386 g_free (me->me_mountdir);
387 g_free (me->me_mntroot);
388 if (me->me_type_malloced)
389 g_free (me->me_type);
390 g_free (me);
393 /* --------------------------------------------------------------------------------------------- */
395 #ifdef MOUNTED_GETMNTINFO
397 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
398 static char *
399 fstype_to_string (short int t)
401 switch (t)
403 #ifdef MOUNT_PC
404 /* cppcheck-suppress syntaxError */
405 case MOUNT_PC:
406 return "pc";
407 #endif
408 #ifdef MOUNT_MFS
409 /* cppcheck-suppress syntaxError */
410 case MOUNT_MFS:
411 return "mfs";
412 #endif
413 #ifdef MOUNT_LO
414 /* cppcheck-suppress syntaxError */
415 case MOUNT_LO:
416 return "lo";
417 #endif
418 #ifdef MOUNT_TFS
419 /* cppcheck-suppress syntaxError */
420 case MOUNT_TFS:
421 return "tfs";
422 #endif
423 #ifdef MOUNT_TMP
424 /* cppcheck-suppress syntaxError */
425 case MOUNT_TMP:
426 return "tmp";
427 #endif
428 #ifdef MOUNT_UFS
429 /* cppcheck-suppress syntaxError */
430 case MOUNT_UFS:
431 return "ufs";
432 #endif
433 #ifdef MOUNT_NFS
434 /* cppcheck-suppress syntaxError */
435 case MOUNT_NFS:
436 return "nfs";
437 #endif
438 #ifdef MOUNT_MSDOS
439 /* cppcheck-suppress syntaxError */
440 case MOUNT_MSDOS:
441 return "msdos";
442 #endif
443 #ifdef MOUNT_LFS
444 /* cppcheck-suppress syntaxError */
445 case MOUNT_LFS:
446 return "lfs";
447 #endif
448 #ifdef MOUNT_LOFS
449 /* cppcheck-suppress syntaxError */
450 case MOUNT_LOFS:
451 return "lofs";
452 #endif
453 #ifdef MOUNT_FDESC
454 /* cppcheck-suppress syntaxError */
455 case MOUNT_FDESC:
456 return "fdesc";
457 #endif
458 #ifdef MOUNT_PORTAL
459 /* cppcheck-suppress syntaxError */
460 case MOUNT_PORTAL:
461 return "portal";
462 #endif
463 #ifdef MOUNT_NULL
464 /* cppcheck-suppress syntaxError */
465 case MOUNT_NULL:
466 return "null";
467 #endif
468 #ifdef MOUNT_UMAP
469 /* cppcheck-suppress syntaxError */
470 case MOUNT_UMAP:
471 return "umap";
472 #endif
473 #ifdef MOUNT_KERNFS
474 /* cppcheck-suppress syntaxError */
475 case MOUNT_KERNFS:
476 return "kernfs";
477 #endif
478 #ifdef MOUNT_PROCFS
479 /* cppcheck-suppress syntaxError */
480 case MOUNT_PROCFS:
481 return "procfs";
482 #endif
483 #ifdef MOUNT_AFS
484 /* cppcheck-suppress syntaxError */
485 case MOUNT_AFS:
486 return "afs";
487 #endif
488 #ifdef MOUNT_CD9660
489 /* cppcheck-suppress syntaxError */
490 case MOUNT_CD9660:
491 return "cd9660";
492 #endif
493 #ifdef MOUNT_UNION
494 /* cppcheck-suppress syntaxError */
495 case MOUNT_UNION:
496 return "union";
497 #endif
498 #ifdef MOUNT_DEVFS
499 /* cppcheck-suppress syntaxError */
500 case MOUNT_DEVFS:
501 return "devfs";
502 #endif
503 #ifdef MOUNT_EXT2FS
504 /* cppcheck-suppress syntaxError */
505 case MOUNT_EXT2FS:
506 return "ext2fs";
507 #endif
508 default:
509 return "?";
512 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
514 /* --------------------------------------------------------------------------------------------- */
516 static char *
517 fsp_to_string (const struct statfs *fsp)
519 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
520 return (char *) (fsp->f_fstypename);
521 #else
522 return fstype_to_string (fsp->f_type);
523 #endif
525 #endif /* MOUNTED_GETMNTINFO */
527 /* --------------------------------------------------------------------------------------------- */
529 #ifdef MOUNTED_VMOUNT /* AIX. */
530 static char *
531 fstype_to_string (int t)
533 struct vfs_ent *e;
535 e = getvfsbytype (t);
536 if (!e || !e->vfsent_name)
537 return "none";
538 else
539 return e->vfsent_name;
541 #endif /* MOUNTED_VMOUNT */
543 /* --------------------------------------------------------------------------------------------- */
545 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
547 /* Return the device number from MOUNT_OPTIONS, if possible.
548 Otherwise return (dev_t) -1. */
550 /* --------------------------------------------------------------------------------------------- */
552 static dev_t
553 dev_from_mount_options (char const *mount_options)
555 /* GNU/Linux allows file system implementations to define their own
556 meaning for "dev=" mount options, so don't trust the meaning
557 here. */
558 #ifndef __linux__
559 static char const dev_pattern[] = ",dev=";
560 char const *devopt = strstr (mount_options, dev_pattern);
562 if (devopt)
564 char const *optval = devopt + sizeof (dev_pattern) - 1;
565 char *optvalend;
566 unsigned long int dev;
567 errno = 0;
568 dev = strtoul (optval, &optvalend, 16);
569 if (optval != optvalend
570 && (*optvalend == '\0' || *optvalend == ',')
571 && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
572 return dev;
574 #endif
576 (void) mount_options;
577 return -1;
580 #endif
582 /* --------------------------------------------------------------------------------------------- */
584 #if defined _AIX && defined _I386
585 /* AIX PS/2 does not supply statfs. */
587 static int
588 statfs (char *file, struct statfs *fsb)
590 struct stat stats;
591 struct dustat fsd;
593 if (stat (file, &stats) != 0)
594 return -1;
595 if (dustat (stats.st_dev, 0, &fsd, sizeof (fsd)))
596 return -1;
597 fsb->f_type = 0;
598 fsb->f_bsize = fsd.du_bsize;
599 fsb->f_blocks = fsd.du_fsize - fsd.du_isize;
600 fsb->f_bfree = fsd.du_tfree;
601 fsb->f_bavail = fsd.du_tfree;
602 fsb->f_files = (fsd.du_isize - 2) * fsd.du_inopb;
603 fsb->f_ffree = fsd.du_tinode;
604 fsb->f_fsid.val[0] = fsd.du_site;
605 fsb->f_fsid.val[1] = fsd.du_pckno;
606 return 0;
609 #endif /* _AIX && _I386 */
611 /* --------------------------------------------------------------------------------------------- */
613 #if defined MOUNTED_GETMNTENT1 && defined __linux__
615 /* Unescape the paths in mount tables.
616 STR is updated in place. */
617 static void
618 unescape_tab (char *str)
620 size_t i, j = 0;
621 size_t len;
623 len = strlen (str) + 1;
625 for (i = 0; i < len; i++)
627 if (str[i] == '\\' && (i + 4 < len)
628 && str[i + 1] >= '0' && str[i + 1] <= '3'
629 && str[i + 2] >= '0' && str[i + 2] <= '7' && str[i + 3] >= '0' && str[i + 3] <= '7')
631 str[j++] = (str[i + 1] - '0') * 64 + (str[i + 2] - '0') * 8 + (str[i + 3] - '0');
632 i += 3;
634 else
635 str[j++] = str[i];
638 #endif
640 /* --------------------------------------------------------------------------------------------- */
642 /* Return a list of the currently mounted file systems, or NULL on error.
643 Add each entry to the tail of the list so that they stay in order.
644 If NEED_FS_TYPE is true, ensure that the file system type fields in
645 the returned list are valid. Otherwise, they might not be. */
647 static GSList *
648 read_file_system_list (int need_fs_type)
650 GSList *mount_list = NULL;
651 struct mount_entry *me;
653 #ifdef MOUNTED_LISTMNTENT
655 struct tabmntent *mntlist, *p;
657 /* the third and fourth arguments could be used to filter mounts,
658 but Crays doesn't seem to have any mounts that we want to
659 remove. Specifically, automount create normal NFS mounts.
662 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
663 return NULL;
664 for (p = mntlist; p; p = p->next)
666 struct mntent *mnt = p->ment;
668 me = g_malloc (sizeof (*me));
669 me->me_devname = g_strdup (mnt->mnt_fsname);
670 me->me_mountdir = g_strdup (mnt->mnt_dir);
671 me->me_mntroot = NULL;
672 me->me_type = g_strdup (mnt->mnt_type);
673 me->me_type_malloced = 1;
674 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
675 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
676 me->me_dev = -1;
678 mount_list = g_slist_prepend (mount_list, me);
680 freemntlist (mntlist);
682 #endif
684 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
686 FILE *fp;
688 #ifdef __linux__
689 /* Try parsing mountinfo first, as that make device IDs available.
690 Note we could use libmount routines to simplify this parsing a little
691 (and that code is in previous versions of this function), however
692 libmount depends on libselinux which pulls in many dependencies. */
693 char const *mountinfo = "/proc/self/mountinfo";
695 fp = fopen (mountinfo, "r");
696 if (fp != NULL)
698 char *line = NULL;
699 size_t buf_size = 0;
701 while (getline (&line, &buf_size, fp) != -1)
703 unsigned int devmaj, devmin;
704 int target_s, target_e, type_s, type_e;
705 int source_s, source_e, mntroot_s, mntroot_e;
706 char test;
707 char *dash;
708 int rc;
710 rc = sscanf (line, "%*u " /* id - discarded */
711 "%*u " /* parent - discarded */
712 "%u:%u " /* dev major:minor */
713 "%n%*s%n " /* mountroot */
714 "%n%*s%n" /* target, start and end */
715 "%c", /* more data... */
716 &devmaj, &devmin, &mntroot_s, &mntroot_e, &target_s, &target_e, &test);
718 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
719 continue;
721 /* skip optional fields, terminated by " - " */
722 dash = strstr (line + target_e, " - ");
723 if (dash == NULL)
724 continue;
726 rc = sscanf (dash, " - " /* */
727 "%n%*s%n " /* FS type, start and end */
728 "%n%*s%n " /* source, start and end */
729 "%c", /* more data... */
730 &type_s, &type_e, &source_s, &source_e, &test);
731 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
732 continue;
734 /* manipulate the sub-strings in place. */
735 line[mntroot_e] = '\0';
736 line[target_e] = '\0';
737 dash[type_e] = '\0';
738 dash[source_e] = '\0';
739 unescape_tab (dash + source_s);
740 unescape_tab (line + target_s);
741 unescape_tab (line + mntroot_s);
743 me = g_malloc (sizeof *me);
745 me->me_devname = g_strdup (dash + source_s);
746 me->me_mountdir = g_strdup (line + target_s);
747 me->me_mntroot = g_strdup (line + mntroot_s);
748 me->me_type = g_strdup (dash + type_s);
749 me->me_type_malloced = 1;
750 me->me_dev = makedev (devmaj, devmin);
751 /* we pass "false" for the "Bind" option as that's only
752 significant when the Fs_type is "none" which will not be
753 the case when parsing "/proc/self/mountinfo", and only
754 applies for static /etc/mtab files. */
755 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, FALSE);
756 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
758 mount_list = g_slist_prepend (mount_list, me);
761 free (line);
763 if (ferror (fp) != 0)
765 int saved_errno = errno;
767 fclose (fp);
768 errno = saved_errno;
769 goto free_then_fail;
772 if (fclose (fp) == EOF)
773 goto free_then_fail;
775 else /* fallback to /proc/self/mounts (/etc/mtab). */
776 #endif /* __linux __ */
778 struct mntent *mnt;
779 const char *table = MOUNTED;
781 fp = setmntent (table, "r");
782 if (fp == NULL)
783 return NULL;
785 while ((mnt = getmntent (fp)) != NULL)
787 gboolean bind;
789 bind = hasmntopt (mnt, "bind") != NULL;
791 me = g_malloc (sizeof (*me));
792 me->me_devname = g_strdup (mnt->mnt_fsname);
793 me->me_mountdir = g_strdup (mnt->mnt_dir);
794 me->me_mntroot = NULL;
795 me->me_type = g_strdup (mnt->mnt_type);
796 me->me_type_malloced = 1;
797 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
798 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
799 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
801 mount_list = g_slist_prepend (mount_list, me);
804 if (endmntent (fp) == 0)
805 goto free_then_fail;
808 #endif /* MOUNTED_GETMNTENT1. */
810 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
812 struct statfs *fsp;
813 int entries;
815 entries = getmntinfo (&fsp, MNT_NOWAIT);
816 if (entries < 0)
817 return NULL;
818 for (; entries-- > 0; fsp++)
820 char *fs_type = fsp_to_string (fsp);
822 me = g_malloc (sizeof (*me));
823 me->me_devname = g_strdup (fsp->f_mntfromname);
824 me->me_mountdir = g_strdup (fsp->f_mntonname);
825 me->me_mntroot = NULL;
826 me->me_type = fs_type;
827 me->me_type_malloced = 0;
828 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
829 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
830 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
832 mount_list = g_slist_prepend (mount_list, me);
835 #endif /* MOUNTED_GETMNTINFO */
837 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
839 struct statvfs *fsp;
840 int entries;
842 entries = getmntinfo (&fsp, MNT_NOWAIT);
843 if (entries < 0)
844 return NULL;
845 for (; entries-- > 0; fsp++)
847 me = g_malloc (sizeof (*me));
848 me->me_devname = g_strdup (fsp->f_mntfromname);
849 me->me_mountdir = g_strdup (fsp->f_mntonname);
850 me->me_mntroot = NULL;
851 me->me_type = g_strdup (fsp->f_fstypename);
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 mount_list = g_slist_prepend (mount_list, me);
860 #endif /* MOUNTED_GETMNTINFO2 */
862 #ifdef MOUNTED_GETMNT /* Ultrix. */
864 int offset = 0;
865 int val;
866 struct fs_data fsd;
868 while (TRUE)
870 errno = 0;
871 val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, (char *) NULL);
872 if (val < 0)
873 goto free_then_fail;
874 if (val == 0)
875 break;
877 me = g_malloc (sizeof (*me));
878 me->me_devname = g_strdup (fsd.fd_req.devname);
879 me->me_mountdir = g_strdup (fsd.fd_req.path);
880 me->me_mntroot = NULL;
881 me->me_type = gt_names[fsd.fd_req.fstype];
882 me->me_type_malloced = 0;
883 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
884 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
885 me->me_dev = fsd.fd_req.dev;
887 mount_list = g_slist_prepend (mount_list, me);
890 #endif /* MOUNTED_GETMNT. */
892 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
894 /* The next_dev() and fs_stat_dev() system calls give the list of
895 all file systems, including the information returned by statvfs()
896 (fs type, total blocks, free blocks etc.), but without the mount
897 point. But on BeOS all file systems except / are mounted in the
898 rootfs, directly under /.
899 The directory name of the mount point is often, but not always,
900 identical to the volume name of the device.
901 We therefore get the list of subdirectories of /, and the list
902 of all file systems, and match the two lists. */
904 DIR *dirp;
905 struct rootdir_entry
907 char *name;
908 dev_t dev;
909 ino_t ino;
910 struct rootdir_entry *next;
912 struct rootdir_entry *rootdir_list;
913 struct rootdir_entry **rootdir_tail;
914 int32 pos;
915 dev_t dev;
916 fs_info fi;
918 /* All volumes are mounted in the rootfs, directly under /. */
919 rootdir_list = NULL;
920 rootdir_tail = &rootdir_list;
921 dirp = opendir (PATH_SEP_STR);
922 if (dirp)
924 struct dirent *d;
926 while ((d = readdir (dirp)) != NULL)
928 char *name;
929 struct stat statbuf;
931 if (DIR_IS_DOT (d->d_name))
932 continue;
934 if (DIR_IS_DOTDOT (d->d_name))
935 name = g_strdup (PATH_SEP_STR);
936 else
937 name = g_strconcat (PATH_SEP_STR, d->d_name, (char *) NULL);
939 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
941 struct rootdir_entry *re = g_malloc (sizeof (*re));
942 re->name = name;
943 re->dev = statbuf.st_dev;
944 re->ino = statbuf.st_ino;
946 /* Add to the linked list. */
947 *rootdir_tail = re;
948 rootdir_tail = &re->next;
950 else
951 g_free (name);
953 closedir (dirp);
955 *rootdir_tail = NULL;
957 for (pos = 0; (dev = next_dev (&pos)) >= 0;)
958 if (fs_stat_dev (dev, &fi) >= 0)
960 /* Note: fi.dev == dev. */
961 struct rootdir_entry *re;
963 for (re = rootdir_list; re; re = re->next)
964 if (re->dev == fi.dev && re->ino == fi.root)
965 break;
967 me = g_malloc (sizeof (*me));
968 me->me_devname =
969 g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
970 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
971 me->me_mntroot = NULL;
972 me->me_type = g_strdup (fi.fsh_name);
973 me->me_type_malloced = 1;
974 me->me_dev = fi.dev;
975 me->me_dummy = 0;
976 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
978 mount_list = g_slist_prepend (mount_list, me);
981 while (rootdir_list != NULL)
983 struct rootdir_entry *re = rootdir_list;
985 rootdir_list = re->next;
986 g_free (re->name);
987 g_free (re);
990 #endif /* MOUNTED_FS_STAT_DEV */
992 #ifdef MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
994 int numsys, counter;
995 size_t bufsize;
996 struct statfs *stats;
998 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
999 if (numsys < 0)
1000 return NULL;
1001 if (SIZE_MAX / sizeof (*stats) <= numsys)
1003 fprintf (stderr, "%s\n", _("Memory exhausted!"));
1004 exit (EXIT_FAILURE);
1007 bufsize = (1 + numsys) * sizeof (*stats);
1008 stats = g_malloc (bufsize);
1009 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
1011 if (numsys < 0)
1013 g_free (stats);
1014 return NULL;
1017 for (counter = 0; counter < numsys; counter++)
1019 me = g_malloc (sizeof (*me));
1020 me->me_devname = g_strdup (stats[counter].f_mntfromname);
1021 me->me_mountdir = g_strdup (stats[counter].f_mntonname);
1022 me->me_mntroot = NULL;
1023 me->me_type = g_strdup (FS_TYPE (stats[counter]));
1024 me->me_type_malloced = 1;
1025 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1026 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1027 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1029 mount_list = g_slist_prepend (mount_list, me);
1032 g_free (stats);
1034 #endif /* MOUNTED_GETFSSTAT */
1036 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
1038 struct mnttab mnt;
1039 char *table = "/etc/mnttab";
1040 FILE *fp;
1042 fp = fopen (table, "r");
1043 if (fp == NULL)
1044 return NULL;
1046 while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
1048 me = g_malloc (sizeof (*me));
1049 #ifdef GETFSTYP /* SVR3. */
1050 me->me_devname = g_strdup (mnt.mt_dev);
1051 #else
1052 me->me_devname = g_strconcat ("/dev/", mnt.mt_dev, (char *) NULL);
1053 #endif
1054 me->me_mountdir = g_strdup (mnt.mt_filsys);
1055 me->me_mntroot = NULL;
1056 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1057 me->me_type = "";
1058 me->me_type_malloced = 0;
1059 #ifdef GETFSTYP /* SVR3. */
1060 if (need_fs_type)
1062 struct statfs fsd;
1063 char typebuf[FSTYPSZ];
1065 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
1066 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
1068 me->me_type = g_strdup (typebuf);
1069 me->me_type_malloced = 1;
1072 #endif
1073 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1074 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1076 mount_list = g_slist_prepend (mount_list, me);
1079 if (ferror (fp))
1081 /* The last fread() call must have failed. */
1082 int saved_errno = errno;
1084 fclose (fp);
1085 errno = saved_errno;
1086 goto free_then_fail;
1089 if (fclose (fp) == EOF)
1090 goto free_then_fail;
1092 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
1094 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
1096 struct mntent **mnttbl = getmnttbl (), **ent;
1098 for (ent = mnttbl; *ent; ent++)
1100 me = g_malloc (sizeof (*me));
1101 me->me_devname = g_strdup ((*ent)->mt_resource);
1102 me->me_mountdir = g_strdup ((*ent)->mt_directory);
1103 me->me_mntroot = NULL;
1104 me->me_type = g_strdup ((*ent)->mt_fstype);
1105 me->me_type_malloced = 1;
1106 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1107 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1108 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1110 mount_list = g_slist_prepend (mount_list, me);
1112 endmnttbl ();
1114 #endif
1116 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
1118 struct mnttab mnt;
1119 char *table = MNTTAB;
1120 FILE *fp;
1121 int ret;
1122 int lockfd = -1;
1124 #if defined F_RDLCK && defined F_SETLKW
1125 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
1126 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
1127 for this file name, we should use their macro name instead.
1128 (Why not just lock MNTTAB directly? We don't know.) */
1129 #ifndef MNTTAB_LOCK
1130 #define MNTTAB_LOCK "/etc/.mnttab.lock"
1131 #endif
1132 lockfd = open (MNTTAB_LOCK, O_RDONLY);
1133 if (lockfd >= 0)
1135 struct flock flock;
1137 flock.l_type = F_RDLCK;
1138 flock.l_whence = SEEK_SET;
1139 flock.l_start = 0;
1140 flock.l_len = 0;
1141 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
1142 if (errno != EINTR)
1144 int saved_errno = errno;
1145 close (lockfd);
1146 errno = saved_errno;
1147 return NULL;
1150 else if (errno != ENOENT)
1151 return NULL;
1152 #endif
1154 errno = 0;
1155 fp = fopen (table, "r");
1156 if (fp == NULL)
1157 ret = errno;
1158 else
1160 while ((ret = getmntent (fp, &mnt)) == 0)
1162 me = g_malloc (sizeof (*me));
1163 me->me_devname = g_strdup (mnt.mnt_special);
1164 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1165 me->me_mntroot = NULL;
1166 me->me_type = g_strdup (mnt.mnt_fstype);
1167 me->me_type_malloced = 1;
1168 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1169 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1170 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1172 mount_list = g_slist_prepend (mount_list, me);
1175 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1178 if (lockfd >= 0 && close (lockfd) != 0)
1179 ret = errno;
1181 if (ret >= 0)
1183 errno = ret;
1184 goto free_then_fail;
1187 #endif /* MOUNTED_GETMNTENT2. */
1189 #ifdef MOUNTED_VMOUNT /* AIX. */
1191 int bufsize;
1192 void *entries;
1193 char *thisent;
1194 struct vmount *vmp;
1195 int n_entries;
1196 int i;
1198 /* Ask how many bytes to allocate for the mounted file system info. */
1199 entries = &bufsize;
1200 if (mntctl (MCTL_QUERY, sizeof (bufsize), entries) != 0)
1201 return NULL;
1202 entries = g_malloc (bufsize);
1204 /* Get the list of mounted file systems. */
1205 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1206 if (n_entries < 0)
1208 int saved_errno = errno;
1210 g_free (entries);
1211 errno = saved_errno;
1212 return NULL;
1215 for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1217 char *options, *ignore;
1219 vmp = (struct vmount *) thisent;
1220 me = g_malloc (sizeof (*me));
1221 if (vmp->vmt_flags & MNT_REMOTE)
1223 char *host, *dir;
1225 me->me_remote = 1;
1226 /* Prepend the remote dirname. */
1227 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1228 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1229 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1231 else
1233 me->me_remote = 0;
1234 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1236 me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1237 me->me_mntroot = NULL;
1238 me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1239 me->me_type_malloced = 1;
1240 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1241 ignore = strstr (options, "ignore");
1242 me->me_dummy = (ignore
1243 && (ignore == options || ignore[-1] == ',')
1244 && (ignore[sizeof ("ignore") - 1] == ','
1245 || ignore[sizeof ("ignore") - 1] == '\0'));
1246 me->me_dev = (dev_t) (-1); /* vmt_fsid might be the info we want. */
1248 mount_list = g_slist_prepend (mount_list, me);
1250 g_free (entries);
1252 #endif /* MOUNTED_VMOUNT. */
1254 #ifdef MOUNTED_INTERIX_STATVFS
1256 DIR *dirp = opendir ("/dev/fs");
1257 char node[9 + NAME_MAX];
1259 if (!dirp)
1260 goto free_then_fail;
1262 while (1)
1264 struct statvfs dev;
1265 struct dirent entry;
1266 struct dirent *result;
1268 if (readdir_r (dirp, &entry, &result) || result == NULL)
1269 break;
1271 strcpy (node, "/dev/fs/");
1272 strcat (node, entry.d_name);
1274 if (statvfs (node, &dev) == 0)
1276 me = g_malloc (sizeof *me);
1277 me->me_devname = g_strdup (dev.f_mntfromname);
1278 me->me_mountdir = g_strdup (dev.f_mntonname);
1279 me->me_mntroot = NULL;
1280 me->me_type = g_strdup (dev.f_fstypename);
1281 me->me_type_malloced = 1;
1282 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1283 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1284 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1286 mount_list = g_slist_prepend (mount_list, me);
1289 closedir (dirp);
1291 #endif /* MOUNTED_INTERIX_STATVFS */
1293 (void) need_fs_type; /* avoid argument-unused warning */
1295 return g_slist_reverse (mount_list);
1297 free_then_fail:
1299 int saved_errno = errno;
1301 g_slist_free_full (mount_list, (GDestroyNotify) free_mount_entry);
1303 errno = saved_errno;
1304 return NULL;
1307 #endif /* HAVE_INFOMOUNT_LIST */
1309 /* --------------------------------------------------------------------------------------------- */
1311 #ifdef HAVE_INFOMOUNT_QNX
1313 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1314 ** this via the following code.
1315 ** Note that, as this is based on CWD, it only fills one mount_entry
1316 ** structure. See my_statfs() in utilunix.c for the "other side" of
1317 ** this hack.
1320 static GSList *
1321 read_file_system_list (int need_fs_type, int all_fs)
1323 struct _disk_entry de;
1324 struct statfs fs;
1325 int i, fd;
1326 char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1327 struct mount_entry *me = NULL;
1328 static GSList *list = NULL;
1330 if (list != NULL)
1332 me = (struct mount_entry *) list->data;
1334 g_free (me->me_devname);
1335 g_free (me->me_mountdir);
1336 g_free (me->me_mntroot);
1337 g_free (me->me_type);
1339 else
1341 me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1342 list = g_slist_prepend (list, me);
1345 if (!getcwd (dir, _POSIX_PATH_MAX))
1346 return (NULL);
1348 fd = open (dir, O_RDONLY);
1349 if (fd == -1)
1350 return (NULL);
1352 i = disk_get_entry (fd, &de);
1354 close (fd);
1356 if (i == -1)
1357 return (NULL);
1359 switch (de.disk_type)
1361 case _UNMOUNTED:
1362 tp = "unmounted";
1363 break;
1364 case _FLOPPY:
1365 tp = "Floppy";
1366 break;
1367 case _HARD:
1368 tp = "Hard";
1369 break;
1370 case _RAMDISK:
1371 tp = "Ram";
1372 break;
1373 case _REMOVABLE:
1374 tp = "Removable";
1375 break;
1376 case _TAPE:
1377 tp = "Tape";
1378 break;
1379 case _CDROM:
1380 tp = "CDROM";
1381 break;
1382 default:
1383 tp = "unknown";
1386 if (fsys_get_mount_dev (dir, &dev) == -1)
1387 return (NULL);
1389 if (fsys_get_mount_pt (dev, &dir) == -1)
1390 return (NULL);
1392 me->me_devname = g_strdup (dev);
1393 me->me_mountdir = g_strdup (dir);
1394 me->me_mntroot = NULL;
1395 me->me_type = g_strdup (tp);
1396 me->me_dev = de.disk_type;
1398 #ifdef DEBUG
1399 fprintf (stderr,
1400 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1401 de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1402 fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1403 fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1404 #endif /* DEBUG */
1406 return (list);
1408 #endif /* HAVE_INFOMOUNT_QNX */
1410 /* --------------------------------------------------------------------------------------------- */
1412 #ifdef STAT_READ_FILSYS /* SVR2 */
1414 /* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
1415 interrupted. Return the actual number of bytes read(written), zero for EOF,
1416 or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
1417 static size_t
1418 safe_read (int fd, void *buf, size_t count)
1420 /* Work around a bug in Tru64 5.1. Attempting to read more than
1421 INT_MAX bytes fails with errno == EINVAL. See
1422 <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
1423 When decreasing COUNT, keep it block-aligned. */
1424 /* *INDENT-OFF* */
1425 enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
1426 /* *INDENT-ON* */
1428 while (TRUE)
1430 ssize_t result;
1432 result = read (fd, buf, count);
1434 if (0 <= result)
1435 return result;
1436 else if (IS_EINTR (errno))
1437 continue;
1438 else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
1439 count = BUGGY_READ_MAXIMUM;
1440 else
1441 return result;
1445 /* --------------------------------------------------------------------------------------------- */
1447 /* Read COUNT bytes at BUF to(from) descriptor FD, retrying if
1448 interrupted or if a partial write(read) occurs. Return the number
1449 of bytes transferred.
1450 When writing, set errno if fewer than COUNT bytes are written.
1451 When reading, if fewer than COUNT bytes are read, you must examine
1452 errno to distinguish failure from EOF (errno == 0). */
1454 static size_t
1455 full_read (int fd, void *buf, size_t count)
1457 size_t total = 0;
1458 char *ptr = (char *) buf;
1460 while (count > 0)
1462 size_t n_rw = safe_read (fd, ptr, count);
1463 if (n_rw == (size_t) (-1))
1464 break;
1465 if (n_rw == 0)
1467 errno = ZERO_BYTE_TRANSFER_ERRNO;
1468 break;
1470 total += n_rw;
1471 ptr += n_rw;
1472 count -= n_rw;
1475 return total;
1478 #endif /* STAT_READ_FILSYS */
1480 /* --------------------------------------------------------------------------------------------- */
1482 #ifdef HAVE_INFOMOUNT
1483 /* Fill in the fields of FSP with information about space usage for
1484 the file system on which FILE resides.
1485 DISK is the device on which FILE is mounted, for space-getting
1486 methods that need to know it.
1487 Return 0 if successful, -1 if not. When returning -1, ensure that
1488 ERRNO is either a system error value, or zero if DISK is NULL
1489 on a system that requires a non-NULL value. */
1490 static int
1491 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
1493 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1495 if (statvfs_works ())
1497 struct statvfs vfsd;
1499 if (statvfs (file, &vfsd) < 0)
1500 return -1;
1502 /* f_frsize isn't guaranteed to be supported. */
1503 fsp->fsu_blocksize = (vfsd.f_frsize
1504 ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1505 : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1507 fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1508 fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1509 fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1510 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1511 fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1512 fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1514 else
1515 #endif
1518 #if defined STAT_STATVFS64 /* AIX */
1520 struct statvfs64 fsd;
1522 if (statvfs64 (file, &fsd) < 0)
1523 return -1;
1525 /* f_frsize isn't guaranteed to be supported. */
1526 /* *INDENT-OFF* */
1527 fsp->fsu_blocksize = fsd.f_frsize
1528 ? PROPAGATE_ALL_ONES (fsd.f_frsize)
1529 : PROPAGATE_ALL_ONES (fsd.f_bsize);
1530 /* *INDENT-ON* */
1532 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1534 struct fs_data fsd;
1536 if (statfs (file, &fsd) != 1)
1537 return -1;
1539 fsp->fsu_blocksize = 1024;
1540 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
1541 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
1542 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
1543 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
1544 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
1545 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
1547 #elif defined STAT_READ_FILSYS /* SVR2 */
1548 #ifndef SUPERBOFF
1549 #define SUPERBOFF (SUPERB * 512)
1550 #endif
1552 struct filsys fsd;
1553 int fd;
1555 if (!disk)
1557 errno = 0;
1558 return -1;
1561 fd = open (disk, O_RDONLY);
1562 if (fd < 0)
1563 return -1;
1564 lseek (fd, (off_t) SUPERBOFF, 0);
1565 if (full_read (fd, (char *) &fsd, sizeof (fsd)) != sizeof (fsd))
1567 close (fd);
1568 return -1;
1570 close (fd);
1572 fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
1573 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
1574 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
1575 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
1576 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
1577 fsp->fsu_files = (fsd.s_isize == -1
1578 ? UINTMAX_MAX : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
1579 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);
1581 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1583 struct statfs fsd;
1585 if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1586 return -1;
1588 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1590 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1592 struct statfs fsd;
1594 if (statfs (file, &fsd) < 0)
1595 return -1;
1597 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1599 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1600 Mac OS X < 10.4, FreeBSD < 5.0, \
1601 NetBSD < 3.0, OpenBSD < 4.4 */
1603 struct statfs fsd;
1605 if (statfs (file, &fsd) < 0)
1606 return -1;
1608 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1610 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1612 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1613 struct statfs are truncated to 2GB. These conditions detect that
1614 truncation, presumably without botching the 4.1.1 case, in which
1615 the values are not truncated. The correct counts are stored in
1616 undocumented spare fields. */
1617 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1619 fsd.f_blocks = fsd.f_spare[0];
1620 fsd.f_bfree = fsd.f_spare[1];
1621 fsd.f_bavail = fsd.f_spare[2];
1623 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1625 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1627 struct statfs fsd;
1629 if (statfs (file, &fsd) < 0)
1630 return -1;
1632 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1634 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix, old AIX, \
1635 Dolphin */
1637 #if !defined _AIX && !defined _SEQUENT_ && !defined DOLPHIN
1638 #define f_bavail f_bfree
1639 #endif
1641 struct statfs fsd;
1643 if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1644 return -1;
1646 /* Empirically, the block counts on most SVR3 and SVR3-derived
1647 systems seem to always be in terms of 512-byte blocks,
1648 no matter what value f_bsize has. */
1649 #if defined _AIX || defined _CRAY
1650 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1651 #else
1652 fsp->fsu_blocksize = 512;
1653 #endif
1655 #endif
1657 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1658 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1659 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1661 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1662 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1663 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1664 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1665 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1666 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1668 #endif
1671 (void) disk; /* avoid argument-unused warning */
1673 return 0;
1675 #endif /* HAVE_INFOMOUNT */
1677 /* --------------------------------------------------------------------------------------------- */
1678 /*** public functions ****************************************************************************/
1679 /* --------------------------------------------------------------------------------------------- */
1681 void
1682 free_my_statfs (void)
1684 #ifdef HAVE_INFOMOUNT_LIST
1685 g_slist_free_full (mc_mount_list, (GDestroyNotify) free_mount_entry);
1686 mc_mount_list = NULL;
1687 #endif /* HAVE_INFOMOUNT_LIST */
1690 /* --------------------------------------------------------------------------------------------- */
1692 void
1693 init_my_statfs (void)
1695 #ifdef HAVE_INFOMOUNT_LIST
1696 free_my_statfs ();
1697 mc_mount_list = read_file_system_list (1);
1698 #endif /* HAVE_INFOMOUNT_LIST */
1701 /* --------------------------------------------------------------------------------------------- */
1703 void
1704 my_statfs (struct my_statfs *myfs_stats, const char *path)
1706 #ifdef HAVE_INFOMOUNT_LIST
1707 size_t len = 0;
1708 struct mount_entry *entry = NULL;
1709 GSList *temp;
1710 struct fs_usage fs_use;
1712 for (temp = mc_mount_list; temp != NULL; temp = g_slist_next (temp))
1714 struct mount_entry *me;
1715 size_t i;
1717 me = (struct mount_entry *) temp->data;
1718 i = strlen (me->me_mountdir);
1719 if (i > len && (strncmp (path, me->me_mountdir, i) == 0) &&
1720 (entry == NULL || IS_PATH_SEP (path[i]) || path[i] == '\0'))
1722 len = i;
1723 entry = me;
1727 if (entry != NULL)
1729 memset (&fs_use, 0, sizeof (fs_use));
1730 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1732 myfs_stats->type = entry->me_dev;
1733 myfs_stats->typename = entry->me_type;
1734 myfs_stats->mpoint = entry->me_mountdir;
1735 myfs_stats->mroot = entry->me_mntroot;
1736 myfs_stats->device = entry->me_devname;
1737 myfs_stats->avail =
1738 ((uintmax_t) (getuid ()? fs_use.fsu_bavail : fs_use.fsu_bfree) *
1739 fs_use.fsu_blocksize) >> 10;
1740 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1741 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1742 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1744 else
1745 #endif /* HAVE_INFOMOUNT_LIST */
1747 #ifdef HAVE_INFOMOUNT_QNX
1749 ** This is the "other side" of the hack to read_file_system_list() in
1750 ** mountlist.c.
1751 ** It's not the most efficient approach, but consumes less memory. It
1752 ** also accommodates QNX's ability to mount filesystems on the fly.
1754 struct mount_entry *entry;
1755 struct fs_usage fs_use;
1757 entry = read_file_system_list (0, 0);
1758 if (entry != NULL)
1760 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1762 myfs_stats->type = entry->me_dev;
1763 myfs_stats->typename = entry->me_type;
1764 myfs_stats->mpoint = entry->me_mountdir;
1765 myfs_stats->mroot = entry->me_mntroot;
1766 myfs_stats->device = entry->me_devname;
1768 myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1769 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1770 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1771 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1773 else
1774 #endif /* HAVE_INFOMOUNT_QNX */
1776 myfs_stats->type = 0;
1777 myfs_stats->mpoint = "unknown";
1778 myfs_stats->device = "unknown";
1779 myfs_stats->avail = 0;
1780 myfs_stats->total = 0;
1781 myfs_stats->nfree = 0;
1782 myfs_stats->nodes = 0;
1786 /* --------------------------------------------------------------------------------------------- */