diff options
Diffstat (limited to 'gl/mountlist.c')
| -rw-r--r-- | gl/mountlist.c | 244 |
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 | ||
| 462 | struct mount_entry * | 463 | struct mount_entry * |
| 463 | read_file_system_list (bool need_fs_type) | 464 | read_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 | ||
