summaryrefslogtreecommitdiffstats
path: root/gl/mountlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/mountlist.c')
-rw-r--r--gl/mountlist.c244
1 files changed, 221 insertions, 23 deletions
diff --git a/gl/mountlist.c b/gl/mountlist.c
index 06300d6b..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-2024 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
@@ -396,15 +398,18 @@ dev_from_mount_options (char const *mount_options)
396 if (devopt) 398 if (devopt)
397 { 399 {
398 char const *optval = devopt + sizeof dev_pattern - 1; 400 char const *optval = devopt + sizeof dev_pattern - 1;
399 char *optvalend; 401 if (c_isxdigit (*optval))
400 unsigned long int dev; 402 {
401 errno = 0; 403 char *optvalend;
402 dev = strtoul (optval, &optvalend, 16); 404 unsigned long int dev;
403 if (optval != optvalend 405 errno = 0;
404 && (*optvalend == '\0' || *optvalend == ',') 406 dev = strtoul (optval, &optvalend, 16);
405 && ! (dev == ULONG_MAX && errno == ERANGE) 407 if (optval != optvalend
406 && dev == (dev_t) dev) 408 && (*optvalend == '\0' || *optvalend == ',')
407 return dev; 409 && ! (dev == ULONG_MAX && errno == ERANGE)
410 && dev == (dev_t) dev)
411 return dev;
412 }
408 } 413 }
409 414
410# endif 415# endif
@@ -452,12 +457,8 @@ terminate_at_blank (char *str)
452 *s = '\0'; 457 *s = '\0';
453 return s; 458 return s;
454} 459}
455#endif
456 460
457/* Return a list of the currently mounted file systems, or NULL on error. 461#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 462
462struct mount_entry * 463struct mount_entry *
463read_file_system_list (bool need_fs_type) 464read_file_system_list (bool need_fs_type)
@@ -567,7 +568,7 @@ read_file_system_list (bool need_fs_type)
567 goto free_then_fail; 568 goto free_then_fail;
568 } 569 }
569 else /* fallback to /proc/self/mounts (/etc/mtab). */ 570 else /* fallback to /proc/self/mounts (/etc/mtab). */
570# endif /* __linux __ || __ANDROID__ */ 571# endif /* __linux__ || __ANDROID__ */
571 { 572 {
572 struct mntent *mnt; 573 struct mntent *mnt;
573 char const *table = MOUNTED; 574 char const *table = MOUNTED;
@@ -883,7 +884,9 @@ read_file_system_list (bool need_fs_type)
883 me->me_mntroot = NULL; 884 me->me_mntroot = NULL;
884 me->me_type = xstrdup (mnt.mnt_fstype); 885 me->me_type = xstrdup (mnt.mnt_fstype);
885 me->me_type_malloced = 1; 886 me->me_type_malloced = 1;
886 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;
887 me->me_remote = ME_REMOTE (me->me_devname, me->me_type); 890 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
888 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor); 891 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
889 892
@@ -1092,6 +1095,201 @@ read_file_system_list (bool need_fs_type)
1092 } 1095 }
1093#endif /* MOUNTED_INTERIX_STATVFS */ 1096#endif /* MOUNTED_INTERIX_STATVFS */
1094 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
1095 *mtail = NULL; 1293 *mtail = NULL;
1096 return mount_list; 1294 return mount_list;
1097 1295