summaryrefslogtreecommitdiffstats
path: root/gl/mountlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/mountlist.c')
-rw-r--r--gl/mountlist.c263
1 files changed, 234 insertions, 29 deletions
diff --git a/gl/mountlist.c b/gl/mountlist.c
index 6d384812..dcff6f83 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-2023 Free Software Foundation, Inc. 3 Copyright (C) 1991-1992, 1997-2025 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
@@ -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
@@ -154,11 +156,18 @@
154 156
155/* The results of opendir() in this file are not used with dirfd and fchdir, 157/* The results of opendir() in this file are not used with dirfd and fchdir,
156 therefore save some unnecessary work in fchdir.c. */ 158 therefore save some unnecessary work in fchdir.c. */
157#ifdef GNULIB_defined_opendir 159#ifdef GNULIB_defined_DIR
160# undef DIR
158# undef opendir 161# undef opendir
159#endif
160#ifdef GNULIB_defined_closedir
161# undef closedir 162# undef closedir
163# undef readdir
164#else
165# ifdef GNULIB_defined_opendir
166# undef opendir
167# endif
168# ifdef GNULIB_defined_closedir
169# undef closedir
170# endif
162#endif 171#endif
163 172
164#define ME_DUMMY_0(Fs_name, Fs_type) \ 173#define ME_DUMMY_0(Fs_name, Fs_type) \
@@ -185,11 +194,11 @@
185 we grant an exception to any with "bind" in its list of mount options. 194 we grant an exception to any with "bind" in its list of mount options.
186 I.e., those are *not* dummy entries. */ 195 I.e., those are *not* dummy entries. */
187#ifdef MOUNTED_GETMNTENT1 196#ifdef MOUNTED_GETMNTENT1
188# define ME_DUMMY(Fs_name, Fs_type, Bind) \ 197# define ME_DUMMY(Fs_name, Fs_type, Bind) \
189 (ME_DUMMY_0 (Fs_name, Fs_type) \ 198 (ME_DUMMY_0 (Fs_name, Fs_type) \
190 || (strcmp (Fs_type, "none") == 0 && !Bind)) 199 || (strcmp (Fs_type, "none") == 0 && !Bind))
191#else 200#else
192# define ME_DUMMY(Fs_name, Fs_type) \ 201# define ME_DUMMY(Fs_name, Fs_type) \
193 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0) 202 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
194#endif 203#endif
195 204
@@ -389,15 +398,18 @@ dev_from_mount_options (char const *mount_options)
389 if (devopt) 398 if (devopt)
390 { 399 {
391 char const *optval = devopt + sizeof dev_pattern - 1; 400 char const *optval = devopt + sizeof dev_pattern - 1;
392 char *optvalend; 401 if (c_isxdigit (*optval))
393 unsigned long int dev; 402 {
394 errno = 0; 403 char *optvalend;
395 dev = strtoul (optval, &optvalend, 16); 404 unsigned long int dev;
396 if (optval != optvalend 405 errno = 0;
397 && (*optvalend == '\0' || *optvalend == ',') 406 dev = strtoul (optval, &optvalend, 16);
398 && ! (dev == ULONG_MAX && errno == ERANGE) 407 if (optval != optvalend
399 && dev == (dev_t) dev) 408 && (*optvalend == '\0' || *optvalend == ',')
400 return dev; 409 && ! (dev == ULONG_MAX && errno == ERANGE)
410 && dev == (dev_t) dev)
411 return dev;
412 }
401 } 413 }
402 414
403# endif 415# endif
@@ -445,12 +457,8 @@ terminate_at_blank (char *str)
445 *s = '\0'; 457 *s = '\0';
446 return s; 458 return s;
447} 459}
448#endif
449 460
450/* Return a list of the currently mounted file systems, or NULL on error. 461#endif
451 Add each entry to the tail of the list so that they stay in order.
452 If NEED_FS_TYPE is true, ensure that the file system type fields in
453 the returned list are valid. Otherwise, they might not be. */
454 462
455struct mount_entry * 463struct mount_entry *
456read_file_system_list (bool need_fs_type) 464read_file_system_list (bool need_fs_type)
@@ -560,7 +568,7 @@ read_file_system_list (bool need_fs_type)
560 goto free_then_fail; 568 goto free_then_fail;
561 } 569 }
562 else /* fallback to /proc/self/mounts (/etc/mtab). */ 570 else /* fallback to /proc/self/mounts (/etc/mtab). */
563# endif /* __linux __ || __ANDROID__ */ 571# endif /* __linux__ || __ANDROID__ */
564 { 572 {
565 struct mntent *mnt; 573 struct mntent *mnt;
566 char const *table = MOUNTED; 574 char const *table = MOUNTED;
@@ -876,7 +884,9 @@ read_file_system_list (bool need_fs_type)
876 me->me_mntroot = NULL; 884 me->me_mntroot = NULL;
877 me->me_type = xstrdup (mnt.mnt_fstype); 885 me->me_type = xstrdup (mnt.mnt_fstype);
878 me->me_type_malloced = 1; 886 me->me_type_malloced = 1;
879 me->me_dummy = MNT_IGNORE (&mnt) != 0; 887 /* The cast from 'struct extmnttab *' to 'struct mnttab *' is OK
888 because 'struct extmnttab' extends 'struct mnttab'. */
889 me->me_dummy = MNT_IGNORE ((struct mnttab *) &mnt) != 0;
880 me->me_remote = ME_REMOTE (me->me_devname, me->me_type); 890 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
881 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor); 891 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
882 892
@@ -1085,6 +1095,201 @@ read_file_system_list (bool need_fs_type)
1085 } 1095 }
1086#endif /* MOUNTED_INTERIX_STATVFS */ 1096#endif /* MOUNTED_INTERIX_STATVFS */
1087 1097
1098#if defined _WIN32 && !defined __CYGWIN__ /* native Windows */
1099/* Don't assume that UNICODE is not defined. */
1100# undef GetDriveType
1101# define GetDriveType GetDriveTypeA
1102# undef GetVolumeInformation
1103# define GetVolumeInformation GetVolumeInformationA
1104 {
1105 /* Windows has drive prefixes which are similar to mount points.
1106 GetLogicalDrives returns a bitmask where the i-th bit is set
1107 if ASCII 'A' + i is an available drive. See:
1108 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives>. */
1109 DWORD value = GetLogicalDrives ();
1110 unsigned int i;
1111
1112 for (i = 0; i < 26; ++i)
1113 {
1114 if (value & (1U << i))
1115 {
1116 char mountdir[4];
1117 char fs_name[MAX_PATH + 1];
1118 mountdir[0] = 'A' + i;
1119 mountdir[1] = ':';
1120 mountdir[2] = '\\';
1121 mountdir[3] = '\0';
1122 /* Test whether the drive actually exists, and
1123 get the name of the file system. See:
1124 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa>. */
1125 if (GetVolumeInformation (mountdir, NULL, 0, NULL, NULL, NULL,
1126 fs_name, sizeof fs_name))
1127 {
1128 me = xmalloc (sizeof *me);
1129 me->me_mountdir = xstrdup (mountdir);
1130 /* Check if drive is remote. See:
1131 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea>. */
1132 me->me_remote = GetDriveType (mountdir) == DRIVE_REMOTE;
1133 /* Here we could use
1134 QueryDosDeviceW -> returns something like '\Device\HarddiskVolume2'
1135 GetVolumeNameForVolumeMountPointW -> return something like '\\?\Volume{...}'
1136 */
1137 me->me_devname = NULL;
1138 {
1139 /* Find the SUBST or NET USE mapping of the given drive.
1140 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-querydosdevicew>
1141 For testing of SUBST: <https://ss64.com/nt/subst.html>
1142 For testing of NET USE: <https://ss64.com/nt/net-use.html> */
1143 wchar_t drive[3];
1144 wchar_t mapping[MAX_PATH + 1];
1145 drive[0] = L'A' + i;
1146 drive[1] = L':';
1147 drive[2] = L'\0';
1148 DWORD mapping_len = QueryDosDeviceW (drive, mapping, sizeof (mapping) / sizeof (mapping[0]));
1149 if (mapping_len > 4 && wcsncmp (mapping, L"\\??\\", 4) == 0)
1150 {
1151 /* It's a SUBSTed drive. */
1152 char subst_dir[MAX_PATH + 1];
1153 size_t subst_dir_len = wcstombs (subst_dir, mapping + 4, sizeof (subst_dir));
1154 if (subst_dir_len > 0 && subst_dir_len <= MAX_PATH)
1155 me->me_mntroot = xstrdup (subst_dir);
1156 else
1157 /* mapping is too long or not convertible to the
1158 locale encoding. */
1159 me->me_mntroot = NULL;
1160 }
1161 else if (mapping_len > 26
1162 && wcsncmp (mapping, L"\\Device\\LanmanRedirector\\;", 26) == 0)
1163 {
1164 wchar_t *next_backslash = wcschr (mapping + 26, L'\\');
1165 if (next_backslash != NULL)
1166 {
1167 *--next_backslash = L'\\';
1168 char share_dir[MAX_PATH + 1];
1169 size_t share_dir_len = wcstombs (share_dir, next_backslash, sizeof (share_dir));
1170 if (share_dir_len > 0 && share_dir_len <= MAX_PATH)
1171 me->me_mntroot = xstrdup (share_dir);
1172 else
1173 /* mapping is too long or not convertible to the
1174 locale encoding. */
1175 me->me_mntroot = NULL;
1176 }
1177 else
1178 /* mapping does not have the expected form. */
1179 me->me_mntroot = NULL;
1180 }
1181 else
1182 /* It's neither a SUBSTed nor a NET USEd drive. */
1183 me->me_mntroot = NULL;
1184 }
1185 me->me_dev = (dev_t) -1;
1186 me->me_dummy = 0;
1187 me->me_type = xstrdup (fs_name);
1188 me->me_type_malloced = 1;
1189
1190 /* Add to the linked list. */
1191 *mtail = me;
1192 mtail = &me->me_next;
1193 }
1194 }
1195 }
1196 }
1197 {
1198 /* Windows also has true mount points, called "mounted folders". See
1199 <https://learn.microsoft.com/en-us/windows/win32/fileio/volume-mount-points>
1200 For testing: <https://learn.microsoft.com/en-us/windows-server/storage/disk-management/assign-a-mount-point-folder-path-to-a-drive> */
1201 /* Enumerate the volumes. See
1202 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstvolumew>
1203 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextvolumew>
1204 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findvolumeclose> */
1205 wchar_t vol_name[MAX_PATH + 1];
1206 HANDLE h = FindFirstVolumeW (vol_name, sizeof (vol_name) / sizeof (vol_name[0]));
1207 if (h != INVALID_HANDLE_VALUE)
1208 {
1209 do
1210 {
1211 /* Look where the volume vol_name is mounted.
1212 There are two APIs for doing this:
1213 - FindFirstVolumeMountPointW, FindNextVolumeMountPointW,
1214 FindVolumeMountPointClose. This API always fails with
1215 error code ERROR_ACCESS_DENIED.
1216 - GetVolumePathNamesForVolumeNameW. This API works but
1217 may require a significantly larger buffer.
1218 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew> */
1219 wchar_t stack_buf[MAX_PATH + 2];
1220 wchar_t *malloced_buf = NULL;
1221 wchar_t *buf = stack_buf;
1222 DWORD bufsize = sizeof (stack_buf) / sizeof (wchar_t);
1223 BOOL success;
1224 for (;;)
1225 {
1226 success = GetVolumePathNamesForVolumeNameW (vol_name, buf, bufsize, &bufsize);
1227 if (!success && GetLastError () == ERROR_MORE_DATA)
1228 {
1229 free (malloced_buf);
1230 malloced_buf = (wchar_t *) xmalloc (bufsize * sizeof (wchar_t));
1231 buf = malloced_buf;
1232 }
1233 else
1234 break;
1235 }
1236 if (success)
1237 {
1238 wchar_t *mount_dir = buf;
1239 while (*mount_dir != L'\0')
1240 {
1241 /* Drive mounts are already handled above. */
1242 if (!(mount_dir[0] >= L'A' && mount_dir[0] <= L'Z'
1243 && mount_dir[1] == L':' && mount_dir[2] == L'\\'
1244 && mount_dir[3] == L'\0'))
1245 {
1246 char mountdir[MAX_PATH + 1];
1247 size_t mountdir_len = wcstombs (mountdir, mount_dir, sizeof (mountdir));
1248 if (mountdir_len > 0 && mountdir_len <= MAX_PATH)
1249 {
1250 char fs_name[MAX_PATH + 1];
1251 /* Get the name of the file system. See:
1252 <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationa>. */
1253 if (GetVolumeInformation (mountdir, NULL, 0, NULL, NULL, NULL,
1254 fs_name, sizeof fs_name))
1255 {
1256 me = xmalloc (sizeof *me);
1257 me->me_mountdir = xstrdup (mountdir);
1258 me->me_remote = false;
1259 /* Here we could use vol_name, something like '\\?\Volume{...}'. */
1260 me->me_devname = NULL;
1261 me->me_mntroot = NULL;
1262 me->me_dev = (dev_t) -1;
1263 me->me_dummy = 0;
1264 me->me_type = xstrdup (fs_name);
1265 me->me_type_malloced = 1;
1266
1267 /* Add to the linked list. */
1268 *mtail = me;
1269 mtail = &me->me_next;
1270 }
1271 }
1272 else
1273 {
1274 /* mount_dir is too long or not convertible to the
1275 locale encoding. */
1276 }
1277 }
1278 mount_dir += wcslen (mount_dir) + 1;
1279 }
1280 }
1281 free (malloced_buf);
1282 }
1283 while (FindNextVolumeW (h, vol_name, sizeof (vol_name) / sizeof (vol_name[0])));
1284 FindVolumeClose (h);
1285 }
1286 }
1287#endif
1288
1289#if MOUNTED_NOT_PORTED
1290# error "Please port gnulib mountlist.c to your platform!"
1291#endif
1292
1088 *mtail = NULL; 1293 *mtail = NULL;
1089 return mount_list; 1294 return mount_list;
1090 1295