summaryrefslogtreecommitdiffstats
path: root/gl/mountlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/mountlist.c')
-rw-r--r--gl/mountlist.c629
1 files changed, 399 insertions, 230 deletions
diff --git a/gl/mountlist.c b/gl/mountlist.c
index 06300d6b..66b3f3d5 100644
--- a/gl/mountlist.c
+++ b/gl/mountlist.c
@@ -1,6 +1,6 @@
1/* mountlist.c -- return a list of mounted file systems 1/* mountlist.c -- return a list of mounted file systems
2 2
3 Copyright (C) 1991-1992, 1997-2024 Free Software Foundation, Inc. 3 Copyright (C) 1991-1992, 1997-2026 Free Software Foundation, Inc.
4 4
5 This program is free software: you can redistribute it and/or modify 5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by 6 it under the terms of the GNU General Public License as published by
@@ -19,20 +19,18 @@
19 19
20#include "mountlist.h" 20#include "mountlist.h"
21 21
22#include <errno.h>
23#include <fcntl.h>
22#include <limits.h> 24#include <limits.h>
23#include <stdio.h> 25#include <stdio.h>
24#include <stdlib.h> 26#include <stdlib.h>
25#include <string.h> 27#include <string.h>
26#include <stdint.h> 28#include <stdint.h>
29#include <unistd.h>
27 30
31#include "c-ctype.h"
28#include "xalloc.h" 32#include "xalloc.h"
29 33
30#include <errno.h>
31
32#include <fcntl.h>
33
34#include <unistd.h>
35
36#if HAVE_SYS_PARAM_H 34#if HAVE_SYS_PARAM_H
37# include <sys/param.h> 35# include <sys/param.h>
38#endif 36#endif
@@ -43,7 +41,7 @@
43# include <sys/sysmacros.h> 41# include <sys/sysmacros.h>
44#endif 42#endif
45 43
46#if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */ 44#if defined MOUNTED_GETFSSTAT /* (obsolete) Apple Darwin 1.3 */
47# if HAVE_SYS_UCRED_H 45# if HAVE_SYS_UCRED_H
48# include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS, 46# include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
49 NGROUPS is used as an array dimension in ucred.h */ 47 NGROUPS is used as an array dimension in ucred.h */
@@ -62,7 +60,7 @@
62# endif 60# endif
63#endif /* MOUNTED_GETFSSTAT */ 61#endif /* MOUNTED_GETFSSTAT */
64 62
65#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android, 63#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, Cygwin, Android,
66 also (obsolete) 4.3BSD, SunOS */ 64 also (obsolete) 4.3BSD, SunOS */
67# include <mntent.h> 65# include <mntent.h>
68# include <sys/types.h> 66# include <sys/types.h>
@@ -132,6 +130,10 @@
132# endif 130# endif
133#endif 131#endif
134 132
133#if defined _WIN32 && !defined __CYGWIN__
134# include <windows.h>
135#endif
136
135#ifndef HAVE_HASMNTOPT 137#ifndef HAVE_HASMNTOPT
136# define hasmntopt(mnt, opt) ((char *) 0) 138# define hasmntopt(mnt, opt) ((char *) 0)
137#endif 139#endif
@@ -169,23 +171,21 @@
169#endif 171#endif
170 172
171#define ME_DUMMY_0(Fs_name, Fs_type) \ 173#define ME_DUMMY_0(Fs_name, Fs_type) \
172 (strcmp (Fs_type, "autofs") == 0 \ 174 (streq (Fs_type, "autofs") \
173 || strcmp (Fs_type, "proc") == 0 \ 175 || streq (Fs_type, "proc") \
174 || strcmp (Fs_type, "subfs") == 0 \ 176 || streq (Fs_type, "subfs") \
175 /* for Linux 2.6/3.x */ \ 177 /* for Linux 2.6/3.x */ \
176 || strcmp (Fs_type, "debugfs") == 0 \ 178 || streq (Fs_type, "debugfs") \
177 || strcmp (Fs_type, "devpts") == 0 \ 179 || streq (Fs_type, "devpts") \
178 || strcmp (Fs_type, "fusectl") == 0 \ 180 || streq (Fs_type, "fusectl") \
179 || strcmp (Fs_type, "fuse.portal") == 0 \ 181 || streq (Fs_type, "fuse.portal") \
180 || strcmp (Fs_type, "mqueue") == 0 \ 182 || streq (Fs_type, "mqueue") \
181 || strcmp (Fs_type, "rpc_pipefs") == 0 \ 183 || streq (Fs_type, "rpc_pipefs") \
182 || strcmp (Fs_type, "sysfs") == 0 \ 184 || streq (Fs_type, "sysfs") \
183 /* FreeBSD, Linux 2.4 */ \ 185 /* FreeBSD, Linux 2.4 */ \
184 || strcmp (Fs_type, "devfs") == 0 \ 186 || streq (Fs_type, "devfs") \
185 /* for NetBSD 3.0 */ \ 187 /* for NetBSD 3.0 */ \
186 || strcmp (Fs_type, "kernfs") == 0 \ 188 || streq (Fs_type, "kernfs"))
187 /* for Irix 6.5 */ \
188 || strcmp (Fs_type, "ignore") == 0)
189 189
190/* Historically, we have marked as "dummy" any file system of type "none", 190/* Historically, we have marked as "dummy" any file system of type "none",
191 but now that programs like du need to know about bind-mounted directories, 191 but now that programs like du need to know about bind-mounted directories,
@@ -194,10 +194,10 @@
194#ifdef MOUNTED_GETMNTENT1 194#ifdef MOUNTED_GETMNTENT1
195# define ME_DUMMY(Fs_name, Fs_type, Bind) \ 195# define ME_DUMMY(Fs_name, Fs_type, Bind) \
196 (ME_DUMMY_0 (Fs_name, Fs_type) \ 196 (ME_DUMMY_0 (Fs_name, Fs_type) \
197 || (strcmp (Fs_type, "none") == 0 && !Bind)) 197 || (streq (Fs_type, "none") && !Bind))
198#else 198#else
199# define ME_DUMMY(Fs_name, Fs_type) \ 199# define ME_DUMMY(Fs_name, Fs_type) \
200 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0) 200 (ME_DUMMY_0 (Fs_name, Fs_type) || streq (Fs_type, "none"))
201#endif 201#endif
202 202
203#ifdef __CYGWIN__ 203#ifdef __CYGWIN__
@@ -238,19 +238,19 @@ me_remote (char const *fs_name, _GL_UNUSED char const *fs_type)
238 (strchr (Fs_name, ':') != NULL \ 238 (strchr (Fs_name, ':') != NULL \
239 || ((Fs_name)[0] == '/' \ 239 || ((Fs_name)[0] == '/' \
240 && (Fs_name)[1] == '/' \ 240 && (Fs_name)[1] == '/' \
241 && (strcmp (Fs_type, "smbfs") == 0 \ 241 && (streq (Fs_type, "smbfs") \
242 || strcmp (Fs_type, "smb3") == 0 \ 242 || streq (Fs_type, "smb3") \
243 || strcmp (Fs_type, "cifs") == 0)) \ 243 || streq (Fs_type, "cifs"))) \
244 || strcmp (Fs_type, "acfs") == 0 \ 244 || streq (Fs_type, "acfs") \
245 || strcmp (Fs_type, "afs") == 0 \ 245 || streq (Fs_type, "afs") \
246 || strcmp (Fs_type, "coda") == 0 \ 246 || streq (Fs_type, "coda") \
247 || strcmp (Fs_type, "auristorfs") == 0 \ 247 || streq (Fs_type, "auristorfs") \
248 || strcmp (Fs_type, "fhgfs") == 0 \ 248 || streq (Fs_type, "fhgfs") \
249 || strcmp (Fs_type, "gpfs") == 0 \ 249 || streq (Fs_type, "gpfs") \
250 || strcmp (Fs_type, "ibrix") == 0 \ 250 || streq (Fs_type, "ibrix") \
251 || strcmp (Fs_type, "ocfs2") == 0 \ 251 || streq (Fs_type, "ocfs2") \
252 || strcmp (Fs_type, "vxfs") == 0 \ 252 || streq (Fs_type, "vxfs") \
253 || strcmp ("-hosts", Fs_name) == 0) 253 || streq ("-hosts", Fs_name))
254#endif 254#endif
255 255
256#if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */ 256#if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
@@ -367,9 +367,7 @@ fsp_to_string (const struct statfs *fsp)
367static char * 367static char *
368fstype_to_string (int t) 368fstype_to_string (int t)
369{ 369{
370 struct vfs_ent *e; 370 struct vfs_ent *e = getvfsbytype (t);
371
372 e = getvfsbytype (t);
373 if (!e || !e->vfsent_name) 371 if (!e || !e->vfsent_name)
374 return "none"; 372 return "none";
375 else 373 else
@@ -396,15 +394,17 @@ dev_from_mount_options (char const *mount_options)
396 if (devopt) 394 if (devopt)
397 { 395 {
398 char const *optval = devopt + sizeof dev_pattern - 1; 396 char const *optval = devopt + sizeof dev_pattern - 1;
399 char *optvalend; 397 if (c_isxdigit (*optval))
400 unsigned long int dev; 398 {
401 errno = 0; 399 errno = 0;
402 dev = strtoul (optval, &optvalend, 16); 400 char *optvalend;
403 if (optval != optvalend 401 unsigned long int dev = strtoul (optval, &optvalend, 16);
404 && (*optvalend == '\0' || *optvalend == ',') 402 if (optval != optvalend
405 && ! (dev == ULONG_MAX && errno == ERANGE) 403 && (*optvalend == '\0' || *optvalend == ',')
406 && dev == (dev_t) dev) 404 && ! (dev == ULONG_MAX && errno == ERANGE)
407 return dev; 405 && dev == (dev_t) dev)
406 return dev;
407 }
408 } 408 }
409 409
410# endif 410# endif
@@ -422,9 +422,9 @@ dev_from_mount_options (char const *mount_options)
422static void 422static void
423unescape_tab (char *str) 423unescape_tab (char *str)
424{ 424{
425 size_t i, j = 0; 425 size_t j = 0;
426 size_t len = strlen (str) + 1; 426 size_t len = strlen (str) + 1;
427 for (i = 0; i < len; i++) 427 for (size_t i = 0; i < len; i++)
428 { 428 {
429 if (str[i] == '\\' && (i + 4 < len) 429 if (str[i] == '\\' && (i + 4 < len)
430 && str[i + 1] >= '0' && str[i + 1] <= '3' 430 && str[i + 1] >= '0' && str[i + 1] <= '3'
@@ -452,12 +452,8 @@ terminate_at_blank (char *str)
452 *s = '\0'; 452 *s = '\0';
453 return s; 453 return s;
454} 454}
455#endif
456 455
457/* Return a list of the currently mounted file systems, or NULL on error. 456#endif
458 Add each entry to the tail of the list so that they stay in order.
459 If NEED_FS_TYPE is true, ensure that the file system type fields in
460 the returned list are valid. Otherwise, they might not be. */
461 457
462struct mount_entry * 458struct mount_entry *
463read_file_system_list (bool need_fs_type) 459read_file_system_list (bool need_fs_type)
@@ -467,18 +463,16 @@ read_file_system_list (bool need_fs_type)
467 struct mount_entry **mtail = &mount_list; 463 struct mount_entry **mtail = &mount_list;
468 (void) need_fs_type; 464 (void) need_fs_type;
469 465
470#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android, 466#ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, Cygwin, Android,
471 also (obsolete) 4.3BSD, SunOS */ 467 also (obsolete) 4.3BSD, SunOS */
472 { 468 {
473 FILE *fp;
474
475# if defined __linux__ || defined __ANDROID__ 469# if defined __linux__ || defined __ANDROID__
476 /* Try parsing mountinfo first, as that make device IDs available. 470 /* Try parsing mountinfo first, as that make device IDs available.
477 Note we could use libmount routines to simplify this parsing a little 471 Note we could use libmount routines to simplify this parsing a little
478 (and that code is in previous versions of this function), however 472 (and that code is in previous versions of this function), however
479 libmount depends on libselinux which pulls in many dependencies. */ 473 libmount depends on libselinux which pulls in many dependencies. */
480 char const *mountinfo = "/proc/self/mountinfo"; 474 char const *mountinfo = "/proc/self/mountinfo";
481 fp = fopen (mountinfo, "re"); 475 FILE *fp = fopen (mountinfo, "re");
482 if (fp != NULL) 476 if (fp != NULL)
483 { 477 {
484 char *line = NULL; 478 char *line = NULL;
@@ -496,61 +490,61 @@ read_file_system_list (bool need_fs_type)
496 &devmaj, &devmin, 490 &devmaj, &devmin,
497 &mntroot_s); 491 &mntroot_s);
498 492
499 if (rc != 2 && rc != 3) /* 3 if %n included in count. */ 493 if (rc == 2 || rc == 3) /* 3 if %n included in count. */
500 continue; 494 {
501 495 /* find end of MNTROOT. */
502 /* find end of MNTROOT. */ 496 char *mntroot = line + mntroot_s;
503 char *mntroot = line + mntroot_s; 497 char *blank = terminate_at_blank (mntroot);
504 char *blank = terminate_at_blank (mntroot); 498 if (blank)
505 if (! blank) 499 {
506 continue; 500 /* find end of TARGET. */
507 501 char *target = blank + 1;
508 /* find end of TARGET. */ 502 blank = terminate_at_blank (target);
509 char *target = blank + 1; 503 if (blank)
510 blank = terminate_at_blank (target); 504 {
511 if (! blank) 505 /* skip optional fields, terminated by " - " */
512 continue; 506 char *dash = strstr (blank + 1, " - ");
513 507 if (dash)
514 /* skip optional fields, terminated by " - " */ 508 {
515 char *dash = strstr (blank + 1, " - "); 509 /* advance past the " - " separator. */
516 if (! dash) 510 char *fstype = dash + 3;
517 continue; 511 blank = terminate_at_blank (fstype);
518 512 if (blank)
519 /* advance past the " - " separator. */ 513 {
520 char *fstype = dash + 3; 514 /* find end of SOURCE. */
521 blank = terminate_at_blank (fstype); 515 char *source = blank + 1;
522 if (! blank) 516 if (terminate_at_blank (source))
523 continue; 517 {
524 518 /* manipulate the sub-strings in place. */
525 /* find end of SOURCE. */ 519 unescape_tab (source);
526 char *source = blank + 1; 520 unescape_tab (target);
527 if (! terminate_at_blank (source)) 521 unescape_tab (mntroot);
528 continue; 522 unescape_tab (fstype);
529 523
530 /* manipulate the sub-strings in place. */ 524 me = xmalloc (sizeof *me);
531 unescape_tab (source); 525
532 unescape_tab (target); 526 me->me_devname = xstrdup (source);
533 unescape_tab (mntroot); 527 me->me_mountdir = xstrdup (target);
534 unescape_tab (fstype); 528 me->me_mntroot = xstrdup (mntroot);
535 529 me->me_type = xstrdup (fstype);
536 me = xmalloc (sizeof *me); 530 me->me_type_malloced = 1;
537 531 me->me_dev = makedev (devmaj, devmin);
538 me->me_devname = xstrdup (source); 532 /* we pass "false" for the "Bind" option as that's only
539 me->me_mountdir = xstrdup (target); 533 significant when the Fs_type is "none" which will not be
540 me->me_mntroot = xstrdup (mntroot); 534 the case when parsing "/proc/self/mountinfo", and only
541 me->me_type = xstrdup (fstype); 535 applies for static /etc/mtab files. */
542 me->me_type_malloced = 1; 536 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
543 me->me_dev = makedev (devmaj, devmin); 537 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
544 /* we pass "false" for the "Bind" option as that's only 538
545 significant when the Fs_type is "none" which will not be 539 /* Add to the linked list. */
546 the case when parsing "/proc/self/mountinfo", and only 540 *mtail = me;
547 applies for static /etc/mtab files. */ 541 mtail = &me->me_next;
548 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false); 542 }
549 me->me_remote = ME_REMOTE (me->me_devname, me->me_type); 543 }
550 544 }
551 /* Add to the linked list. */ 545 }
552 *mtail = me; 546 }
553 mtail = &me->me_next; 547 }
554 } 548 }
555 549
556 free (line); 550 free (line);
@@ -567,16 +561,16 @@ read_file_system_list (bool need_fs_type)
567 goto free_then_fail; 561 goto free_then_fail;
568 } 562 }
569 else /* fallback to /proc/self/mounts (/etc/mtab). */ 563 else /* fallback to /proc/self/mounts (/etc/mtab). */
570# endif /* __linux __ || __ANDROID__ */ 564# endif /* __linux__ || __ANDROID__ */
571 { 565 {
572 struct mntent *mnt;
573 char const *table = MOUNTED; 566 char const *table = MOUNTED;
574 567
575 fp = setmntent (table, "r"); 568 FILE *mfp = setmntent (table, "r");
576 if (fp == NULL) 569 if (mfp == NULL)
577 return NULL; 570 return NULL;
578 571
579 while ((mnt = getmntent (fp))) 572 struct mntent *mnt;
573 while ((mnt = getmntent (mfp)))
580 { 574 {
581 bool bind = hasmntopt (mnt, "bind"); 575 bool bind = hasmntopt (mnt, "bind");
582 576
@@ -595,7 +589,7 @@ read_file_system_list (bool need_fs_type)
595 mtail = &me->me_next; 589 mtail = &me->me_next;
596 } 590 }
597 591
598 if (endmntent (fp) == 0) 592 if (endmntent (mfp) == 0)
599 goto free_then_fail; 593 goto free_then_fail;
600 } 594 }
601 } 595 }
@@ -604,9 +598,7 @@ read_file_system_list (bool need_fs_type)
604#ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */ 598#ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
605 { 599 {
606 struct statfs *fsp; 600 struct statfs *fsp;
607 int entries; 601 int entries = getmntinfo (&fsp, MNT_NOWAIT);
608
609 entries = getmntinfo (&fsp, MNT_NOWAIT);
610 if (entries < 0) 602 if (entries < 0)
611 return NULL; 603 return NULL;
612 for (; entries-- > 0; fsp++) 604 for (; entries-- > 0; fsp++)
@@ -633,9 +625,7 @@ read_file_system_list (bool need_fs_type)
633#ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */ 625#ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
634 { 626 {
635 struct statvfs *fsp; 627 struct statvfs *fsp;
636 int entries; 628 int entries = getmntinfo (&fsp, MNT_NOWAIT);
637
638 entries = getmntinfo (&fsp, MNT_NOWAIT);
639 if (entries < 0) 629 if (entries < 0)
640 return NULL; 630 return NULL;
641 for (; entries-- > 0; fsp++) 631 for (; entries-- > 0; fsp++)
@@ -669,7 +659,6 @@ read_file_system_list (bool need_fs_type)
669 We therefore get the list of subdirectories of /, and the list 659 We therefore get the list of subdirectories of /, and the list
670 of all file systems, and match the two lists. */ 660 of all file systems, and match the two lists. */
671 661
672 DIR *dirp;
673 struct rootdir_entry 662 struct rootdir_entry
674 { 663 {
675 char *name; 664 char *name;
@@ -677,16 +666,11 @@ read_file_system_list (bool need_fs_type)
677 ino_t ino; 666 ino_t ino;
678 struct rootdir_entry *next; 667 struct rootdir_entry *next;
679 }; 668 };
680 struct rootdir_entry *rootdir_list;
681 struct rootdir_entry **rootdir_tail;
682 int32 pos;
683 dev_t dev;
684 fs_info fi;
685 669
686 /* All volumes are mounted in the rootfs, directly under /. */ 670 /* All volumes are mounted in the rootfs, directly under /. */
687 rootdir_list = NULL; 671 struct rootdir_entry *rootdir_list = NULL;
688 rootdir_tail = &rootdir_list; 672 struct rootdir_entry **rootdir_tail = &rootdir_list;
689 dirp = opendir ("/"); 673 DIR *dirp = opendir ("/");
690 if (dirp) 674 if (dirp)
691 { 675 {
692 struct dirent *d; 676 struct dirent *d;
@@ -696,61 +680,64 @@ read_file_system_list (bool need_fs_type)
696 char *name; 680 char *name;
697 struct stat statbuf; 681 struct stat statbuf;
698 682
699 if (strcmp (d->d_name, "..") == 0) 683 if (! streq (d->d_name, ".."))
700 continue;
701
702 if (strcmp (d->d_name, ".") == 0)
703 name = xstrdup ("/");
704 else
705 {
706 name = xmalloc (1 + strlen (d->d_name) + 1);
707 name[0] = '/';
708 strcpy (name + 1, d->d_name);
709 }
710
711 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
712 { 684 {
713 struct rootdir_entry *re = xmalloc (sizeof *re); 685 if (streq (d->d_name, "."))
714 re->name = name; 686 name = xstrdup ("/");
715 re->dev = statbuf.st_dev; 687 else
716 re->ino = statbuf.st_ino; 688 {
717 689 name = xmalloc (1 + strlen (d->d_name) + 1);
718 /* Add to the linked list. */ 690 name[0] = '/';
719 *rootdir_tail = re; 691 strcpy (name + 1, d->d_name);
720 rootdir_tail = &re->next; 692 }
693
694 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
695 {
696 struct rootdir_entry *re = xmalloc (sizeof *re);
697 re->name = name;
698 re->dev = statbuf.st_dev;
699 re->ino = statbuf.st_ino;
700
701 /* Add to the linked list. */
702 *rootdir_tail = re;
703 rootdir_tail = &re->next;
704 }
705 else
706 free (name);
721 } 707 }
722 else
723 free (name);
724 } 708 }
725 closedir (dirp); 709 closedir (dirp);
726 } 710 }
727 *rootdir_tail = NULL; 711 *rootdir_tail = NULL;
728 712
729 for (pos = 0; (dev = next_dev (&pos)) >= 0; ) 713 dev_t dev;
730 if (fs_stat_dev (dev, &fi) >= 0) 714 for (int32 pos = 0; (dev = next_dev (&pos)) >= 0; )
731 { 715 {
732 /* Note: fi.dev == dev. */ 716 fs_info fi;
733 struct rootdir_entry *re; 717 if (fs_stat_dev (dev, &fi) >= 0)
734 718 {
735 for (re = rootdir_list; re; re = re->next) 719 /* Note: fi.dev == dev. */
736 if (re->dev == fi.dev && re->ino == fi.root) 720 struct rootdir_entry *re;
737 break; 721 for (re = rootdir_list; re; re = re->next)
738 722 if (re->dev == fi.dev && re->ino == fi.root)
739 me = xmalloc (sizeof *me); 723 break;
740 me->me_devname = xstrdup (fi.device_name[0] != '\0' 724
741 ? fi.device_name : fi.fsh_name); 725 me = xmalloc (sizeof *me);
742 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name); 726 me->me_devname = xstrdup (fi.device_name[0] != '\0'
743 me->me_mntroot = NULL; 727 ? fi.device_name : fi.fsh_name);
744 me->me_type = xstrdup (fi.fsh_name); 728 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
745 me->me_type_malloced = 1; 729 me->me_mntroot = NULL;
746 me->me_dev = fi.dev; 730 me->me_type = xstrdup (fi.fsh_name);
747 me->me_dummy = 0; 731 me->me_type_malloced = 1;
748 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0; 732 me->me_dev = fi.dev;
749 733 me->me_dummy = 0;
750 /* Add to the linked list. */ 734 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
751 *mtail = me; 735
752 mtail = &me->me_next; 736 /* Add to the linked list. */
753 } 737 *mtail = me;
738 mtail = &me->me_next;
739 }
740 }
754 *mtail = NULL; 741 *mtail = NULL;
755 742
756 while (rootdir_list != NULL) 743 while (rootdir_list != NULL)
@@ -763,19 +750,17 @@ read_file_system_list (bool need_fs_type)
763 } 750 }
764#endif /* MOUNTED_FS_STAT_DEV */ 751#endif /* MOUNTED_FS_STAT_DEV */
765 752
766#if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */ 753#if defined MOUNTED_GETFSSTAT /* (obsolete) Apple Darwin 1.3 */
767 { 754 {
768 int numsys, counter;
769 size_t bufsize;
770 struct statfs *stats;
771 755
772 numsys = getfsstat (NULL, 0L, MNT_NOWAIT); 756 int numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
773 if (numsys < 0) 757 if (numsys < 0)
774 return NULL; 758 return NULL;
759
760 struct statfs *stats;
775 if (SIZE_MAX / sizeof *stats <= numsys) 761 if (SIZE_MAX / sizeof *stats <= numsys)
776 xalloc_die (); 762 xalloc_die ();
777 763 size_t bufsize = (1 + numsys) * sizeof *stats;
778 bufsize = (1 + numsys) * sizeof *stats;
779 stats = xmalloc (bufsize); 764 stats = xmalloc (bufsize);
780 numsys = getfsstat (stats, bufsize, MNT_NOWAIT); 765 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
781 766
@@ -785,7 +770,7 @@ read_file_system_list (bool need_fs_type)
785 return NULL; 770 return NULL;
786 } 771 }
787 772
788 for (counter = 0; counter < numsys; counter++) 773 for (int counter = 0; counter < numsys; counter++)
789 { 774 {
790 me = xmalloc (sizeof *me); 775 me = xmalloc (sizeof *me);
791 me->me_devname = xstrdup (stats[counter].f_mntfromname); 776 me->me_devname = xstrdup (stats[counter].f_mntfromname);
@@ -808,14 +793,13 @@ read_file_system_list (bool need_fs_type)
808 793
809#if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */ 794#if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
810 { 795 {
811 struct mnttab mnt;
812 char *table = "/etc/mnttab"; 796 char *table = "/etc/mnttab";
813 FILE *fp;
814 797
815 fp = fopen (table, "re"); 798 FILE *fp = fopen (table, "re");
816 if (fp == NULL) 799 if (fp == NULL)
817 return NULL; 800 return NULL;
818 801
802 struct mnttab mnt;
819 while (fread (&mnt, sizeof mnt, 1, fp) > 0) 803 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
820 { 804 {
821 me = xmalloc (sizeof *me); 805 me = xmalloc (sizeof *me);
@@ -861,20 +845,19 @@ read_file_system_list (bool need_fs_type)
861 845
862#ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */ 846#ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
863 { 847 {
864 struct extmnttab mnt;
865 const char *table = MNTTAB; 848 const char *table = MNTTAB;
866 FILE *fp;
867 int ret;
868 849
869 /* No locking is needed, because the contents of /etc/mnttab is generated 850 /* No locking is needed, because the contents of /etc/mnttab is generated
870 by the kernel. */ 851 by the kernel. */
871 852
872 errno = 0; 853 errno = 0;
873 fp = fopen (table, "re"); 854 FILE *fp = fopen (table, "re");
855 int ret;
874 if (fp == NULL) 856 if (fp == NULL)
875 ret = errno; 857 ret = errno;
876 else 858 else
877 { 859 {
860 struct extmnttab mnt;
878 while ((ret = getextmntent (fp, &mnt, 1)) == 0) 861 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
879 { 862 {
880 me = xmalloc (sizeof *me); 863 me = xmalloc (sizeof *me);
@@ -883,7 +866,9 @@ read_file_system_list (bool need_fs_type)
883 me->me_mntroot = NULL; 866 me->me_mntroot = NULL;
884 me->me_type = xstrdup (mnt.mnt_fstype); 867 me->me_type = xstrdup (mnt.mnt_fstype);
885 me->me_type_malloced = 1; 868 me->me_type_malloced = 1;
886 me->me_dummy = MNT_IGNORE (&mnt) != 0; 869 /* The cast from 'struct extmnttab *' to 'struct mnttab *' is OK
870 because 'struct extmnttab' extends 'struct mnttab'. */
871 me->me_dummy = MNT_IGNORE ((struct mnttab *) &mnt) != 0;
887 me->me_remote = ME_REMOTE (me->me_devname, me->me_type); 872 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
888 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor); 873 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
889 874
@@ -906,10 +891,7 @@ read_file_system_list (bool need_fs_type)
906 891
907#ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */ 892#ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
908 { 893 {
909 struct mnttab mnt;
910 const char *table = MNTTAB; 894 const char *table = MNTTAB;
911 FILE *fp;
912 int ret;
913 int lockfd = -1; 895 int lockfd = -1;
914 896
915# if defined F_RDLCK && defined F_SETLKW 897# if defined F_RDLCK && defined F_SETLKW
@@ -942,11 +924,13 @@ read_file_system_list (bool need_fs_type)
942# endif 924# endif
943 925
944 errno = 0; 926 errno = 0;
945 fp = fopen (table, "re"); 927 FILE *fp = fopen (table, "re");
928 int ret;
946 if (fp == NULL) 929 if (fp == NULL)
947 ret = errno; 930 ret = errno;
948 else 931 else
949 { 932 {
933 struct mnttab mnt;
950 while ((ret = getmntent (fp, &mnt)) == 0) 934 while ((ret = getmntent (fp, &mnt)) == 0)
951 { 935 {
952 me = xmalloc (sizeof *me); 936 me = xmalloc (sizeof *me);
@@ -981,34 +965,24 @@ read_file_system_list (bool need_fs_type)
981 965
982#ifdef MOUNTED_VMOUNT /* AIX */ 966#ifdef MOUNTED_VMOUNT /* AIX */
983 { 967 {
984 int bufsize;
985 void *entries;
986 char *thisent;
987 struct vmount *vmp;
988 int n_entries;
989 int i;
990
991 /* Ask how many bytes to allocate for the mounted file system info. */ 968 /* Ask how many bytes to allocate for the mounted file system info. */
992 entries = &bufsize; 969 int bufsize;
993 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0) 970 if (mntctl (MCTL_QUERY, sizeof bufsize, &bufsize) != 0)
994 return NULL; 971 return NULL;
995 entries = xmalloc (bufsize); 972 void *entries = xmalloc (bufsize);
996 973
997 /* Get the list of mounted file systems. */ 974 /* Get the list of mounted file systems. */
998 n_entries = mntctl (MCTL_QUERY, bufsize, entries); 975 int n_entries = mntctl (MCTL_QUERY, bufsize, entries);
999 if (n_entries < 0) 976 if (n_entries < 0)
1000 { 977 {
1001 free (entries); 978 free (entries);
1002 return NULL; 979 return NULL;
1003 } 980 }
1004 981
1005 for (i = 0, thisent = entries; 982 char *thisent = entries;
1006 i < n_entries; 983 for (int i = 0; i < n_entries; i++)
1007 i++, thisent += vmp->vmt_length)
1008 { 984 {
1009 char *options, *ignore; 985 struct vmount *vmp = (struct vmount *) thisent;
1010
1011 vmp = (struct vmount *) thisent;
1012 me = xmalloc (sizeof *me); 986 me = xmalloc (sizeof *me);
1013 if (vmp->vmt_flags & MNT_REMOTE) 987 if (vmp->vmt_flags & MNT_REMOTE)
1014 { 988 {
@@ -1033,8 +1007,8 @@ read_file_system_list (bool need_fs_type)
1033 me->me_mntroot = NULL; 1007 me->me_mntroot = NULL;
1034 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype)); 1008 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1035 me->me_type_malloced = 1; 1009 me->me_type_malloced = 1;
1036 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off; 1010 char *options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1037 ignore = strstr (options, "ignore"); 1011 char *ignore = strstr (options, "ignore");
1038 me->me_dummy = (ignore 1012 me->me_dummy = (ignore
1039 && (ignore == options || ignore[-1] == ',') 1013 && (ignore == options || ignore[-1] == ',')
1040 && (ignore[sizeof "ignore" - 1] == ',' 1014 && (ignore[sizeof "ignore" - 1] == ','
@@ -1044,6 +1018,8 @@ read_file_system_list (bool need_fs_type)
1044 /* Add to the linked list. */ 1018 /* Add to the linked list. */
1045 *mtail = me; 1019 *mtail = me;
1046 mtail = &me->me_next; 1020 mtail = &me->me_next;
1021
1022 thisent += vmp->vmt_length;
1047 } 1023 }
1048 free (entries); 1024 free (entries);
1049 } 1025 }
@@ -1052,25 +1028,23 @@ read_file_system_list (bool need_fs_type)
1052#ifdef MOUNTED_INTERIX_STATVFS /* Interix */ 1028#ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1053 { 1029 {
1054 DIR *dirp = opendir ("/dev/fs"); 1030 DIR *dirp = opendir ("/dev/fs");
1055 char node[9 + NAME_MAX];
1056
1057 if (!dirp) 1031 if (!dirp)
1058 goto free_then_fail; 1032 goto free_then_fail;
1059 1033
1060 while (1) 1034 while (1)
1061 { 1035 {
1062 struct statvfs dev;
1063 struct dirent entry;
1064 struct dirent *result;
1065
1066 /* FIXME: readdir_r is planned to be withdrawn from POSIX and 1036 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1067 marked obsolescent in glibc. Use readdir instead. */ 1037 marked obsolescent in glibc. Use readdir instead. */
1038 struct dirent entry;
1039 struct dirent *result;
1068 if (readdir_r (dirp, &entry, &result) || result == NULL) 1040 if (readdir_r (dirp, &entry, &result) || result == NULL)
1069 break; 1041 break;
1070 1042
1043 char node[9 + NAME_MAX];
1071 strcpy (node, "/dev/fs/"); 1044 strcpy (node, "/dev/fs/");
1072 strcat (node, entry.d_name); 1045 strcat (node, entry.d_name);
1073 1046
1047 struct statvfs dev;
1074 if (statvfs (node, &dev) == 0) 1048 if (statvfs (node, &dev) == 0)
1075 { 1049 {
1076 me = xmalloc (sizeof *me); 1050 me = xmalloc (sizeof *me);
@@ -1092,6 +1066,201 @@ read_file_system_list (bool need_fs_type)
1092 } 1066 }
1093#endif /* MOUNTED_INTERIX_STATVFS */ 1067#endif /* MOUNTED_INTERIX_STATVFS */
1094 1068
1069#if defined _WIN32 && !defined __CYGWIN__ /* native Windows */
1070/* Don't assume that UNICODE is not defined. */
1071# undef GetDriveType
1072# define GetDriveType GetDriveTypeA
1073# undef GetVolumeInformation
1074# define GetVolumeInformation GetVolumeInformationA
1075 {
1076 /* Windows has drive prefixes which are similar to mount points.
1077 GetLogicalDrives returns a bitmask where the i-th bit is set
1078 if ASCII 'A' + i is an available drive. See:
1079 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives>. */
1080 DWORD value = GetLogicalDrives ();
1081
1082 for (unsigned int i = 0; i < 26; ++i)
1083 {
1084 if (value & (1U << i))
1085 {
1086 char mountdir[4];
1087 mountdir[0] = 'A' + i;
1088 mountdir[1] = ':';
1089 mountdir[2] = '\\';
1090 mountdir[3] = '\0';
1091
1092 char fs_name[MAX_PATH + 1];
1093 /* Test whether the drive actually exists, and
1094 get the name of the file system. See:
1095 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa>. */
1096 if (GetVolumeInformation (mountdir, NULL, 0, NULL, NULL, NULL,
1097 fs_name, sizeof fs_name))
1098 {
1099 me = xmalloc (sizeof *me);
1100 me->me_mountdir = xstrdup (mountdir);
1101 /* Check if drive is remote. See:
1102 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea>. */
1103 me->me_remote = GetDriveType (mountdir) == DRIVE_REMOTE;
1104 /* Here we could use
1105 QueryDosDeviceW -> returns something like '\Device\HarddiskVolume2'
1106 GetVolumeNameForVolumeMountPointW -> return something like '\\?\Volume{...}'
1107 */
1108 me->me_devname = NULL;
1109 {
1110 /* Find the SUBST or NET USE mapping of the given drive.
1111 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-querydosdevicew>
1112 For testing of SUBST: <https://ss64.com/nt/subst.html>
1113 For testing of NET USE: <https://ss64.com/nt/net-use.html> */
1114 wchar_t drive[3];
1115 drive[0] = L'A' + i;
1116 drive[1] = L':';
1117 drive[2] = L'\0';
1118 wchar_t mapping[MAX_PATH + 1];
1119 DWORD mapping_len = QueryDosDeviceW (drive, mapping, sizeof (mapping) / sizeof (mapping[0]));
1120 if (mapping_len > 4 && wcsncmp (mapping, L"\\??\\", 4) == 0)
1121 {
1122 /* It's a SUBSTed drive. */
1123 char subst_dir[MAX_PATH + 1];
1124 size_t subst_dir_len = wcstombs (subst_dir, mapping + 4, sizeof (subst_dir));
1125 if (subst_dir_len > 0 && subst_dir_len <= MAX_PATH)
1126 me->me_mntroot = xstrdup (subst_dir);
1127 else
1128 /* mapping is too long or not convertible to the
1129 locale encoding. */
1130 me->me_mntroot = NULL;
1131 }
1132 else if (mapping_len > 26
1133 && wcsncmp (mapping, L"\\Device\\LanmanRedirector\\;", 26) == 0)
1134 {
1135 wchar_t *next_backslash = wcschr (mapping + 26, L'\\');
1136 if (next_backslash != NULL)
1137 {
1138 *--next_backslash = L'\\';
1139 char share_dir[MAX_PATH + 1];
1140 size_t share_dir_len = wcstombs (share_dir, next_backslash, sizeof (share_dir));
1141 if (share_dir_len > 0 && share_dir_len <= MAX_PATH)
1142 me->me_mntroot = xstrdup (share_dir);
1143 else
1144 /* mapping is too long or not convertible to the
1145 locale encoding. */
1146 me->me_mntroot = NULL;
1147 }
1148 else
1149 /* mapping does not have the expected form. */
1150 me->me_mntroot = NULL;
1151 }
1152 else
1153 /* It's neither a SUBSTed nor a NET USEd drive. */
1154 me->me_mntroot = NULL;
1155 }
1156 me->me_dev = (dev_t) -1;
1157 me->me_dummy = 0;
1158 me->me_type = xstrdup (fs_name);
1159 me->me_type_malloced = 1;
1160
1161 /* Add to the linked list. */
1162 *mtail = me;
1163 mtail = &me->me_next;
1164 }
1165 }
1166 }
1167 }
1168 {
1169 /* Windows also has true mount points, called "mounted folders". See
1170 <https://learn.microsoft.com/en-us/windows/win32/fileio/volume-mount-points>
1171 For testing: <https://learn.microsoft.com/en-us/windows-server/storage/disk-management/assign-a-mount-point-folder-path-to-a-drive> */
1172 /* Enumerate the volumes. See
1173 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstvolumew>
1174 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextvolumew>
1175 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findvolumeclose> */
1176 wchar_t vol_name[MAX_PATH + 1];
1177 HANDLE h = FindFirstVolumeW (vol_name, sizeof (vol_name) / sizeof (vol_name[0]));
1178 if (h != INVALID_HANDLE_VALUE)
1179 {
1180 do
1181 {
1182 /* Look where the volume vol_name is mounted.
1183 There are two APIs for doing this:
1184 - FindFirstVolumeMountPointW, FindNextVolumeMountPointW,
1185 FindVolumeMountPointClose. This API always fails with
1186 error code ERROR_ACCESS_DENIED.
1187 - GetVolumePathNamesForVolumeNameW. This API works but
1188 may require a significantly larger buffer.
1189 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew> */
1190 wchar_t stack_buf[MAX_PATH + 2];
1191 wchar_t *malloced_buf = NULL;
1192 wchar_t *buf = stack_buf;
1193 DWORD bufsize = sizeof (stack_buf) / sizeof (wchar_t);
1194 BOOL success;
1195 for (;;)
1196 {
1197 success = GetVolumePathNamesForVolumeNameW (vol_name, buf, bufsize, &bufsize);
1198 if (!success && GetLastError () == ERROR_MORE_DATA)
1199 {
1200 free (malloced_buf);
1201 malloced_buf = (wchar_t *) xmalloc (bufsize * sizeof (wchar_t));
1202 buf = malloced_buf;
1203 }
1204 else
1205 break;
1206 }
1207 if (success)
1208 {
1209 wchar_t *mount_dir = buf;
1210 while (*mount_dir != L'\0')
1211 {
1212 /* Drive mounts are already handled above. */
1213 if (!(mount_dir[0] >= L'A' && mount_dir[0] <= L'Z'
1214 && mount_dir[1] == L':' && mount_dir[2] == L'\\'
1215 && mount_dir[3] == L'\0'))
1216 {
1217 char mountdir[MAX_PATH + 1];
1218 size_t mountdir_len = wcstombs (mountdir, mount_dir, sizeof (mountdir));
1219 if (mountdir_len > 0 && mountdir_len <= MAX_PATH)
1220 {
1221 char fs_name[MAX_PATH + 1];
1222 /* Get the name of the file system. See:
1223 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa>. */
1224 if (GetVolumeInformation (mountdir, NULL, 0, NULL, NULL, NULL,
1225 fs_name, sizeof fs_name))
1226 {
1227 me = xmalloc (sizeof *me);
1228 me->me_mountdir = xstrdup (mountdir);
1229 me->me_remote = false;
1230 /* Here we could use vol_name, something like '\\?\Volume{...}'. */
1231 me->me_devname = NULL;
1232 me->me_mntroot = NULL;
1233 me->me_dev = (dev_t) -1;
1234 me->me_dummy = 0;
1235 me->me_type = xstrdup (fs_name);
1236 me->me_type_malloced = 1;
1237
1238 /* Add to the linked list. */
1239 *mtail = me;
1240 mtail = &me->me_next;
1241 }
1242 }
1243 else
1244 {
1245 /* mount_dir is too long or not convertible to the
1246 locale encoding. */
1247 }
1248 }
1249 mount_dir += wcslen (mount_dir) + 1;
1250 }
1251 }
1252 free (malloced_buf);
1253 }
1254 while (FindNextVolumeW (h, vol_name, sizeof (vol_name) / sizeof (vol_name[0])));
1255 FindVolumeClose (h);
1256 }
1257 }
1258#endif
1259
1260#if MOUNTED_NOT_PORTED
1261# error "Please port gnulib mountlist.c to your platform!"
1262#endif
1263
1095 *mtail = NULL; 1264 *mtail = NULL;
1096 return mount_list; 1265 return mount_list;
1097 1266