Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / mount.cc
blobbf26c4af3ed970750aef9fab3b17ac1b6c16382a
1 /* mount.cc: mount handling.
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include "miscfuncs.h"
11 #include <mntent.h>
12 #include <ctype.h>
13 #include <winioctl.h>
14 #include <cygwin/version.h>
15 #include "cygerrno.h"
16 #include "security.h"
17 #include "path.h"
18 #include "shared_info.h"
19 #include "fhandler.h"
20 #include "dtable.h"
21 #include "cygheap.h"
22 #include "cygtls.h"
23 #include "tls_pbuf.h"
24 #include <ntdll.h>
25 #include <wchar.h>
26 #include <stdio.h>
27 #include <assert.h>
29 /* Determine if path prefix matches current cygdrive */
30 #define iscygdrive(path) \
31 (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len, false))
33 #define iscygdrive_device(path) \
34 (isalpha (path[mount_table->cygdrive_len]) && \
35 (path[mount_table->cygdrive_len + 1] == '/' || \
36 !path[mount_table->cygdrive_len + 1]))
38 #define isdev_disk(path) \
39 (path_prefix_p (dev_disk, (path), dev_disk_len, false))
41 #define isproc(path) \
42 (path_prefix_p (proc, (path), proc_len, false))
44 bool NO_COPY mount_info::got_usr_bin;
45 bool NO_COPY mount_info::got_usr_lib;
46 int NO_COPY mount_info::root_idx = -1;
48 /* is_native_path: Return non-zero if PATH starts with \??\[a-zA-Z] or
49 with \\?\[a-zA-Z] or with \\.\[a-zA-Z].
51 is_unc_share: Return non-zero if PATH begins with //server/share
52 or with one of the native prefixes //./ or //?/
54 This function is only used to test for valid input strings.
55 The later normalization drops the native prefixes. */
57 /* list of invalid chars in server names. Note that underscore is ok,
58 but it cripples interoperability. */
59 const char _invalid_server_char[] = ",~:!@#$%^&'.(){} ";
60 #define valid_server_char(_c) ({ \
61 const char __c = (_c); \
62 !iscntrl(__c) \
63 && strchr (_invalid_server_char, (_c)) == NULL; \
66 /* list of invalid chars in UNC filenames. These are a few more than
67 for "normal" filenames. */
68 const char _invalid_share_char[] = "\"/\\[]:|<>+=;,?*";
69 #define valid_share_char(_c) ({ \
70 const char __c = (_c); \
71 !iscntrl(__c) \
72 && strchr (_invalid_share_char, __c) == NULL; \
75 static inline bool
76 is_native_path (const char *path)
78 return isdirsep (path[0])
79 && (isdirsep (path[1]) || path[1] == '?')
80 && (path[2] == '?' || path[2] == '.')
81 && isdirsep (path[3])
82 && isalpha (path[4]);
85 static inline bool
86 is_unc_share (const char *path)
88 const char *p;
89 return (isdirsep (path[0])
90 && isdirsep (path[1])
91 && valid_server_char (path[2])
92 && ((p = strpbrk (path + 3, "\\/")) != NULL)
93 && valid_share_char (p[1]));
96 /* Return true if src_path is a valid, internally supported device name.
97 In that case, win32_path gets the corresponding NT device name and
98 dev is appropriately filled with device information. */
100 static bool
101 win32_device_name (const char *src_path, char *win32_path, device& dev)
103 dev.parse (src_path);
104 if (dev == FH_FS || dev == FH_DEV)
105 return false;
106 strcpy (win32_path, dev.native ());
107 return true;
110 /* Beginning with Samba 3.0.28a, Samba allows to get version information using
111 the ExtendedInfo member returned by a FileFsObjectIdInformation request.
112 We just store the samba_version information for now. Older versions than
113 3.2 are still guessed at by testing the file system flags. */
114 #define SAMBA_EXTENDED_INFO_MAGIC 0x536d4261 /* "SmBa" */
115 #define SAMBA_EXTENDED_INFO_VERSION_STRING_LENGTH 28
116 #pragma pack(push,4)
117 struct smb_extended_info {
118 DWORD samba_magic; /* Always SAMBA_EXTENDED_INFO_MAGIC */
119 DWORD samba_version; /* Major/Minor/Release/Revision */
120 DWORD samba_subversion; /* Prerelease/RC/Vendor patch */
121 LARGE_INTEGER samba_gitcommitdate;
122 char samba_version_string[SAMBA_EXTENDED_INFO_VERSION_STRING_LENGTH];
124 #pragma pack(pop)
126 #define MAX_FS_INFO_CNT 32
127 class fs_info_cache
129 static muto fsi_lock;
130 uint32_t count;
131 struct {
132 fs_info fsi;
133 uint32_t hash;
134 } entry[MAX_FS_INFO_CNT];
136 uint32_t genhash (PFILE_FS_VOLUME_INFORMATION);
138 public:
139 fs_info_cache () : count (0) { fsi_lock.init ("fsi_lock"); }
140 fs_info *search (PFILE_FS_VOLUME_INFORMATION, uint32_t &);
141 void add (uint32_t, fs_info *);
144 static fs_info_cache fsi_cache;
145 muto NO_COPY fs_info_cache::fsi_lock;
147 uint32_t
148 fs_info_cache::genhash (PFILE_FS_VOLUME_INFORMATION pffvi)
150 uint32_t hash = 0;
151 const uint16_t *p = (const uint16_t *) pffvi;
152 const uint16_t *end = (const uint16_t *)
153 ((const uint8_t *) p + sizeof *pffvi
154 + pffvi->VolumeLabelLength - sizeof (WCHAR));
155 pffvi->__dummy = 0; /* This member can have random values! */
156 while (p < end)
157 hash = *p++ + (hash << 6) + (hash << 16) - hash;
158 return hash;
161 fs_info *
162 fs_info_cache::search (PFILE_FS_VOLUME_INFORMATION pffvi, uint32_t &hash)
164 hash = genhash (pffvi);
165 for (uint32_t i = 0; i < count; ++i)
166 if (entry[i].hash == hash)
167 return &entry[i].fsi;
168 return NULL;
171 void
172 fs_info_cache::add (uint32_t hashval, fs_info *new_fsi)
174 fsi_lock.acquire ();
175 if (count < MAX_FS_INFO_CNT)
177 entry[count].fsi = *new_fsi;
178 entry[count].hash = hashval;
179 ++count;
181 fsi_lock.release ();
184 bool
185 fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
187 NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
188 HANDLE vol;
189 OBJECT_ATTRIBUTES attr;
190 IO_STATUS_BLOCK io;
191 bool no_media = false;
192 FILE_FS_DEVICE_INFORMATION ffdi;
193 FILE_FS_OBJECTID_INFORMATION ffoi;
194 struct {
195 FILE_FS_ATTRIBUTE_INFORMATION ffai;
196 WCHAR buf[NAME_MAX + 1];
197 } ffai_buf;
198 struct {
199 FILE_FS_VOLUME_INFORMATION ffvi;
200 WCHAR buf[NAME_MAX + 1];
201 } ffvi_buf;
202 UNICODE_STRING dir;
203 UNICODE_STRING fsname;
205 clear ();
206 /* Always caseinsensitive. We really just need access to the drive. */
207 InitializeObjectAttributes (&attr, upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
208 if (in_vol)
209 vol = in_vol;
210 else
212 ULONG access = READ_CONTROL;
213 /* Note: Don't use the FILE_OPEN_REPARSE_POINT flag here. The reason
214 is that symlink_info::check relies on being able to open a handle
215 to the target of a volume mount point. */
216 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
217 FILE_OPEN_FOR_BACKUP_INTENT);
218 /* At least one filesystem (HGFS, VMware shared folders) doesn't like
219 to be opened with access set to just READ_CONTROL. */
220 if (status == STATUS_INVALID_PARAMETER)
222 access |= FILE_READ_DATA;
223 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
224 FILE_OPEN_FOR_BACKUP_INTENT);
226 while (!NT_SUCCESS (status)
227 && (attr.ObjectName->Length > 7 * sizeof (WCHAR)
228 || status == STATUS_NO_MEDIA_IN_DEVICE))
230 RtlSplitUnicodePath (attr.ObjectName, &dir, NULL);
231 attr.ObjectName = &dir;
232 if (status == STATUS_NO_MEDIA_IN_DEVICE)
234 no_media = true;
235 dir.Length = 6 * sizeof (WCHAR);
237 else if (dir.Length > 7 * sizeof (WCHAR))
238 dir.Length -= sizeof (WCHAR);
239 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
240 FILE_OPEN_FOR_BACKUP_INTENT);
242 if (!NT_SUCCESS (status))
244 debug_printf ("Cannot access path %S, status %y",
245 attr.ObjectName, status);
246 return false;
249 sernum = 0;
250 status = NtQueryVolumeInformationFile (vol, &io, &ffvi_buf.ffvi,
251 sizeof ffvi_buf,
252 FileFsVolumeInformation);
253 uint32_t hash = 0;
254 if (NT_SUCCESS (status))
256 /* If the FS doesn't return a valid serial number (PrlSF is a candidate),
257 create reproducible serial number from path. We need this to create
258 a unique per-drive/share hash. */
259 if (ffvi_buf.ffvi.VolumeSerialNumber == 0)
261 UNICODE_STRING path_prefix;
262 WCHAR *p;
264 if (upath->Buffer[5] == L':' && upath->Buffer[6] == L'\\')
265 p = upath->Buffer + 6;
266 else
268 /* We're expecting an UNC path. Move p to the backslash after
269 "\??\UNC\server\share" or the trailing NUL. */
270 p = upath->Buffer + 7; /* Skip "\??\UNC" */
271 int bs_cnt = 0;
273 while (*++p)
274 if (*p == L'\\')
275 if (++bs_cnt > 1)
276 break;
278 RtlInitCountedUnicodeString (&path_prefix, upath->Buffer,
279 (p - upath->Buffer) * sizeof (WCHAR));
280 ffvi_buf.ffvi.VolumeSerialNumber = hash_path_name ((ino_t) 0,
281 &path_prefix);
283 fs_info *fsi = fsi_cache.search (&ffvi_buf.ffvi, hash);
284 if (fsi)
286 *this = *fsi;
287 if (!in_vol)
288 NtClose (vol);
289 return true;
291 sernum = ffvi_buf.ffvi.VolumeSerialNumber;
293 status = NtQueryVolumeInformationFile (vol, &io, &ffdi, sizeof ffdi,
294 FileFsDeviceInformation);
295 if (!NT_SUCCESS (status))
296 ffdi.DeviceType = ffdi.Characteristics = 0;
298 if ((ffdi.Characteristics & FILE_REMOTE_DEVICE)
299 || (!ffdi.DeviceType
300 && RtlEqualUnicodePathPrefix (attr.ObjectName, &ro_u_uncp, TRUE)))
301 is_remote_drive (true);
303 if (!no_media)
304 status = NtQueryVolumeInformationFile (vol, &io, &ffai_buf.ffai,
305 sizeof ffai_buf,
306 FileFsAttributeInformation);
307 if (no_media || !NT_SUCCESS (status))
309 debug_printf ("Cannot get volume attributes (%S), %y",
310 attr.ObjectName, status);
311 if (!in_vol)
312 NtClose (vol);
313 return false;
315 flags (ffai_buf.ffai.FileSystemAttributes);
316 name_len (ffai_buf.ffai.MaximumComponentNameLength);
317 RtlInitCountedUnicodeString (&fsname, ffai_buf.ffai.FileSystemName,
318 ffai_buf.ffai.FileSystemNameLength);
319 if (is_remote_drive ())
321 /* Should be reevaluated for each new OS. Right now this mask is valid up
322 to Windows 11. The important point here is to test only flags indicating
323 capabilities and to ignore flags indicating a specific state of this
324 volume. At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED,
325 FILE_READ_ONLY_VOLUME, FILE_SEQUENTIAL_WRITE_ONCE and FILE_DAX_VOLUME. */
326 #define GETVOLINFO_VALID_MASK (FILE_CASE_SENSITIVE_SEARCH \
327 | FILE_CASE_PRESERVED_NAMES \
328 | FILE_UNICODE_ON_DISK \
329 | FILE_PERSISTENT_ACLS \
330 | FILE_FILE_COMPRESSION \
331 | FILE_VOLUME_QUOTAS \
332 | FILE_SUPPORTS_SPARSE_FILES \
333 | FILE_SUPPORTS_REPARSE_POINTS \
334 | FILE_SUPPORTS_REMOTE_STORAGE \
335 | FILE_RETURNS_CLEANUP_RESULT_INFO \
336 | FILE_SUPPORTS_POSIX_UNLINK_RENAME \
337 | FILE_SUPPORTS_OBJECT_IDS \
338 | FILE_SUPPORTS_ENCRYPTION \
339 | FILE_NAMED_STREAMS \
340 | FILE_SUPPORTS_TRANSACTIONS \
341 | FILE_SUPPORTS_HARD_LINKS \
342 | FILE_SUPPORTS_EXTENDED_ATTRIBUTES \
343 | FILE_SUPPORTS_OPEN_BY_FILE_ID \
344 | FILE_SUPPORTS_USN_JOURNAL \
345 | FILE_SUPPORTS_INTEGRITY_STREAMS \
346 | FILE_SUPPORTS_BLOCK_REFCOUNTING \
347 | FILE_SUPPORTS_SPARSE_VDL \
348 | FILE_SUPPORTS_GHOSTING)
349 /* This is the pre-Win7 mask used to recognize 3rd-party drivers. We'll never
350 learn in time when those drivers start to support the new (har har) Win7 FS
351 flags. */
352 #define GETVOLINFO_NON_WIN_MASK (FILE_CASE_SENSITIVE_SEARCH \
353 | FILE_CASE_PRESERVED_NAMES \
354 | FILE_UNICODE_ON_DISK \
355 | FILE_PERSISTENT_ACLS \
356 | FILE_FILE_COMPRESSION \
357 | FILE_VOLUME_QUOTAS \
358 | FILE_SUPPORTS_SPARSE_FILES \
359 | FILE_SUPPORTS_REPARSE_POINTS \
360 | FILE_SUPPORTS_REMOTE_STORAGE \
361 | FILE_SUPPORTS_OBJECT_IDS \
362 | FILE_SUPPORTS_ENCRYPTION \
363 | FILE_NAMED_STREAMS \
364 | FILE_SUPPORTS_TRANSACTIONS)
366 #define TEST_GVI(f,m) (((f) & GETVOLINFO_VALID_MASK) == (m))
367 #define TEST_GVI_NON_WIN(f,m) (((f) & GETVOLINFO_NON_WIN_MASK) == (m))
369 /* FIXME: This flag twist is getting awkward. There should really be some
370 other method. Maybe we need mount flags to allow the user to fix file
371 system problems without having to wait for a Cygwin fix. */
373 /* Volume quotas are potentially supported since Samba 3.0, object ids and
374 the unicode on disk flag since Samba 3.2. */
375 #define SAMBA_IGNORE (FILE_VOLUME_QUOTAS \
376 | FILE_SUPPORTS_OBJECT_IDS \
377 | FILE_UNICODE_ON_DISK)
378 #define FS_IS_SAMBA TEST_GVI_NON_WIN(flags () & ~SAMBA_IGNORE, \
379 FILE_CASE_SENSITIVE_SEARCH \
380 | FILE_CASE_PRESERVED_NAMES \
381 | FILE_PERSISTENT_ACLS)
382 /* Netapp DataOnTap. */
383 #define NETAPP_IGNORE (FILE_SUPPORTS_SPARSE_FILES \
384 | FILE_SUPPORTS_REPARSE_POINTS \
385 | FILE_PERSISTENT_ACLS)
386 #define FS_IS_NETAPP_DATAONTAP TEST_GVI_NON_WIN(flags () & ~NETAPP_IGNORE, \
387 FILE_CASE_SENSITIVE_SEARCH \
388 | FILE_CASE_PRESERVED_NAMES \
389 | FILE_UNICODE_ON_DISK \
390 | FILE_NAMED_STREAMS)
391 /* These are the minimal flags supported by NTFS since Windows 7, when
392 checking a remote NTFS filesystem. Every filesystem not supporting these
393 flags is not a native NTFS.
394 We subsume them under the filesystem type "cifs". */
395 #define MINIMAL_WIN_NTFS_FLAGS (FILE_CASE_SENSITIVE_SEARCH \
396 | FILE_CASE_PRESERVED_NAMES \
397 | FILE_UNICODE_ON_DISK \
398 | FILE_PERSISTENT_ACLS \
399 | FILE_FILE_COMPRESSION \
400 | FILE_VOLUME_QUOTAS \
401 | FILE_SUPPORTS_SPARSE_FILES \
402 | FILE_SUPPORTS_REPARSE_POINTS \
403 | FILE_SUPPORTS_OBJECT_IDS \
404 | FILE_SUPPORTS_ENCRYPTION \
405 | FILE_NAMED_STREAMS \
406 | FILE_SUPPORTS_HARD_LINKS \
407 | FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
408 #define FS_IS_WINDOWS_NTFS TEST_GVI(flags () & MINIMAL_WIN_NTFS_FLAGS, \
409 MINIMAL_WIN_NTFS_FLAGS)
410 /* These are the exact flags of a real Windows FAT/FAT32 filesystem.
411 Newer FAT32/exFAT support FILE_SUPPORTS_ENCRYPTION as well.
412 Anything else is a filesystem faking to be FAT. */
413 #define WIN_FAT_FLAGS (FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK)
414 #define FAT_IGNORE (FILE_SUPPORTS_ENCRYPTION)
415 #define FS_IS_WINDOWS_FAT TEST_GVI(flags () & ~FAT_IGNORE, WIN_FAT_FLAGS)
417 if ((flags () & FILE_SUPPORTS_OBJECT_IDS)
418 && NT_SUCCESS (NtQueryVolumeInformationFile (vol, &io, &ffoi,
419 sizeof ffoi,
420 FileFsObjectIdInformation)))
422 smb_extended_info *extended_info = (smb_extended_info *)
423 &ffoi.ExtendedInfo;
424 if (extended_info->samba_magic == SAMBA_EXTENDED_INFO_MAGIC)
426 is_samba (true);
427 samba_version (extended_info->samba_version);
430 /* First check the remote filesystems claiming to be NTFS. */
431 if (!got_fs ()
432 && is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE))
433 /* Test for older Samba releases not supporting extended info. */
434 && !is_samba (FS_IS_SAMBA)
435 /* Netapp inode info is unusable, can't handle trailing dots and
436 spaces, has a bug in "move and delete" semantics. */
437 && !is_netapp (FS_IS_NETAPP_DATAONTAP))
438 /* Any other remote FS faking to be NTFS. */
439 is_cifs (!FS_IS_WINDOWS_NTFS);
440 /* Then check remote filesystems claiming to be FAT. Except for real
441 FAT and Netapp, all of them are subsumed under the "CIFS" filesystem
442 type for now. */
443 if (!got_fs ()
444 && is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))
445 && !is_netapp (FS_IS_NETAPP_DATAONTAP))
446 is_cifs (!FS_IS_WINDOWS_FAT);
447 /* Then check remote filesystems honest about their name. */
448 if (!got_fs ()
449 /* Microsoft exFAT */
450 && !is_exfat (RtlEqualUnicodeString (&fsname, &ro_u_exfat, FALSE))
451 /* Microsoft NFS needs distinct access methods for metadata. */
452 && !is_nfs (RtlEqualUnicodeString (&fsname, &ro_u_nfs, FALSE))
453 /* MVFS == Rational ClearCase remote filesystem. Has a couple of
454 drawbacks, like not supporting DOS attributes other than R/O
455 and stuff like that. */
456 && !is_mvfs (RtlEqualUnicodePathPrefix (&fsname, &ro_u_mvfs, FALSE))
457 /* NWFS == Novell Netware FS. Broken info class, see below. */
458 /* NcFsd == Novell Netware FS via own driver. */
459 && !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE))
460 && !is_ncfsd (RtlEqualUnicodeString (&fsname, &ro_u_ncfsd, FALSE))
461 /* UNIXFS == TotalNet Advanced Server (TAS). Doesn't support
462 FileIdBothDirectoryInformation. See below. */
463 && !is_unixfs (RtlEqualUnicodeString (&fsname, &ro_u_unixfs, FALSE))
464 /* AFSRDRFsd == Andrew File System. Doesn't support DOS attributes.
465 Only native symlinks are supported. */
466 && !is_afs (RtlEqualUnicodeString (&fsname, &ro_u_afs, FALSE)))
468 /* PrlSF == Parallels Desktop File System. Has a bug in
469 FileNetworkOpenInformation, see below. */
470 is_prlfs (RtlEqualUnicodeString (&fsname, &ro_u_prlfs, FALSE));
472 if (got_fs ())
474 /* UNIXFS is known to choke on FileIdBothDirectoryInformation.
475 Some other CIFS servers have problems with this call as well.
476 Know example: EMC NS-702. We just don't use that info class on
477 any remote CIFS. */
478 has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ());
479 /* NWFS is known to have a broken FileBasicInformation info
480 class. It can't be used to fetch information, only to set
481 information. Therefore, for NWFS we have to fallback to the
482 FileNetworkOpenInformation info class. Unfortunately we can't
483 use FileNetworkOpenInformation all the time since that fails on
484 other filesystems like NFS.
485 UNUSED, but keep in for information purposes. */
486 has_buggy_basic_info (is_nwfs ());
487 /* Netapp and NWFS/NcFsd are too dumb to allow non-DOS filenames
488 containing trailing dots and spaces when accessed from Windows
489 clients. We subsume CIFS into this class of filesystems right
490 away since at least some of them are not capable either. */
491 has_dos_filenames_only (is_netapp () || is_nwfs ()
492 || is_ncfsd () || is_cifs ());
493 /* Netapp and NWFS don't grok re-opening a file by handle. They
494 only support this if the filename is non-null and the handle is
495 the handle to a directory. NcFsd IR10 is supposed to be ok. */
496 has_buggy_reopen (is_netapp () || is_nwfs ());
499 if (!got_fs ()
500 && !is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE))
501 && !is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))
502 && !is_exfat (RtlEqualUnicodeString (&fsname, &ro_u_exfat, FALSE))
503 && !is_refs (RtlEqualUnicodeString (&fsname, &ro_u_refs, FALSE))
504 && !is_csc_cache (RtlEqualUnicodeString (&fsname, &ro_u_csc, FALSE))
505 && is_cdrom (ffdi.DeviceType == FILE_DEVICE_CD_ROM))
506 is_udf (RtlEqualUnicodeString (&fsname, &ro_u_udf, FALSE));
507 if (!got_fs ())
509 /* The filesystem name is only used in fillout_mntent and only if
510 the filesystem isn't one of the well-known filesystems anyway. */
511 sys_wcstombs (fsn, sizeof fsn, ffai_buf.ffai.FileSystemName,
512 ffai_buf.ffai.FileSystemNameLength / sizeof (WCHAR));
513 strlwr (fsn);
515 has_acls (flags () & FS_PERSISTENT_ACLS);
516 /* Netapp inode numbers are fly-by-night. */
517 hasgood_inode ((has_acls () && !is_netapp ()) || is_nfs ());
518 /* Case sensitivity is supported if FILE_CASE_SENSITIVE_SEARCH is set,
519 except on Samba which handles Windows clients case insensitive.
521 NFS doesn't set the FILE_CASE_SENSITIVE_SEARCH flag but is case
522 sensitive. */
523 caseinsensitive ((!(flags () & FILE_CASE_SENSITIVE_SEARCH) || is_samba ())
524 && !is_nfs ());
526 /* Check for being an SSD */
527 if (!is_cdrom ())
529 /* Theoretically FileFsVolumeFlagsInformation would be sufficient,
530 but apparently it's not exposed into userspace. */
531 FILE_FS_SECTOR_SIZE_INFORMATION ffssi;
533 status = NtQueryVolumeInformationFile (vol, &io, &ffssi, sizeof ffssi,
534 FileFsSectorSizeInformation);
535 if (NT_SUCCESS (status))
536 is_ssd (!!(ffssi.Flags & SSINFO_FLAGS_NO_SEEK_PENALTY));
539 if (!in_vol)
540 NtClose (vol);
542 fsi_cache.add (hash, this);
543 return true;
546 inline void
547 mount_info::create_root_entry (const PWCHAR root)
549 /* Create a default root dir derived from the location of the Cygwin DLL.
550 The entry is immutable, unless the "override" option is given in /etc/fstab. */
551 char native_root[PATH_MAX];
552 sys_wcstombs (native_root, PATH_MAX, root);
553 assert (*native_root != '\0');
554 if (add_item (native_root, "/",
555 MOUNT_SYSTEM | MOUNT_IMMUTABLE | MOUNT_AUTOMATIC)
556 < 0)
557 api_fatal ("add_item (\"%s\", \"/\", ...) failed, errno %d", native_root, errno);
558 /* Create a default cygdrive entry. Note that this is a user entry.
559 This allows to override it with mount, unless the sysadmin created
560 a cygdrive entry in /etc/fstab. */
561 cygdrive_flags = MOUNT_NOPOSIX | MOUNT_CYGDRIVE;
562 strcpy (cygdrive, CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX "/");
563 cygdrive_len = strlen (cygdrive);
566 /* init: Initialize the mount table. */
568 void
569 mount_info::init (bool user_init)
571 PWCHAR pathend;
572 WCHAR path[PATH_MAX];
574 pathend = wcpcpy (path, cygheap->installation_root.Buffer);
575 if (!user_init)
576 create_root_entry (path);
578 pathend = wcpcpy (pathend, L"\\etc\\fstab");
579 from_fstab (user_init, path, pathend);
581 if (!user_init && (!got_usr_bin || !got_usr_lib))
583 char native[PATH_MAX];
584 if (root_idx < 0)
585 api_fatal ("root_idx %d, user_shared magic %y, nmounts %d", root_idx, user_shared->version, nmounts);
586 char *p = stpcpy (native, mount[root_idx].native_path);
587 if (!got_usr_bin)
589 stpcpy (p, "\\bin");
590 add_item (native, "/usr/bin", MOUNT_SYSTEM | MOUNT_AUTOMATIC);
592 if (!got_usr_lib)
594 stpcpy (p, "\\lib");
595 add_item (native, "/usr/lib", MOUNT_SYSTEM | MOUNT_AUTOMATIC);
601 mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
603 int n, err = 0;
604 const char *real_native_path;
605 int real_posix_pathlen;
606 *outflags = flags;
607 if (!cygheap->root.exists () || posix_pathlen != 1 || posix_path[0] != '/')
609 n = native_pathlen;
610 real_native_path = native_path;
611 real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
613 else
615 n = cygheap->root.native_length ();
616 real_native_path = cygheap->root.native_path ();
617 real_posix_pathlen = posix_pathlen;
619 memcpy (dst, real_native_path, n + 1);
620 const char *p = src + real_posix_pathlen;
621 if (*p == '/')
622 /* nothing */;
623 else if ((isdrive (dst) && !dst[2]) || *p)
624 dst[n++] = '\\';
625 if ((n + strlen (p)) >= NT_MAX_PATH)
626 err = ENAMETOOLONG;
627 else
628 backslashify (p, dst + n, 0);
629 return err;
632 /* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
633 the result in win32_path.
635 If win32_path != NULL, the relative path, if possible to keep, is
636 stored in win32_path. If the relative path isn't possible to keep,
637 the full path is stored.
639 If full_win32_path != NULL, the full path is stored there.
641 The result is zero for success, or an errno value.
643 {,full_}win32_path must have sufficient space (i.e. NT_MAX_PATH bytes). */
646 mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
647 unsigned *flags)
649 bool chroot_ok = !cygheap->root.exists ();
651 dev = FH_FS;
653 *flags = 0;
654 debug_printf ("conv_to_win32_path (%s)", src_path);
656 int i, rc;
657 mount_item *mi = NULL; /* initialized to avoid compiler warning */
659 /* The path is already normalized, without ../../ stuff, we need to have this
660 so that we can move from one mounted directory to another with relative
661 stuff.
663 eg mounting c:/foo /foo
664 d:/bar /bar
666 cd /bar
667 ls ../foo
669 should look in c:/foo, not d:/foo.
671 converting normalizex UNIX path to a DOS-style path, looking up the
672 appropriate drive in the mount table. */
674 /* See if this is a cygwin "device" */
675 if (win32_device_name (src_path, dst, dev))
677 *flags = 0;
678 rc = 0;
679 goto out_no_chroot_check;
682 /* If the path is on a network drive or a //./ resp. //?/ path prefix,
683 bypass the mount table. If it's // or //MACHINE, use the netdrive
684 device. */
685 if (src_path[1] == '/')
687 if (!strchr (src_path + 2, '/'))
689 dev = *netdrive_dev;
690 *flags = 0;
692 else
694 /* For UNC paths, use the cygdrive prefix flags as default setting.
695 This is more natural since UNC paths, just like cygdrive paths,
696 are rather (warning, poetic description ahead) windows into the
697 native Win32 world. This also gives the user an elegant way to
698 change the settings for those paths in a central place. */
699 *flags = cygdrive_flags;
701 backslashify (src_path, dst, 0);
702 /* Go through chroot check */
703 goto out;
705 if (isdev_disk (src_path))
707 dev = *dev_disk_dev;
708 *flags = 0;
709 strcpy (dst, src_path);
710 goto out;
712 if (isproc (src_path))
714 dev = *proc_dev;
715 dev = fhandler_proc::get_proc_fhandler (src_path);
716 if (dev == FH_NADA)
717 return ENOENT;
718 *flags = 0;
719 if (isprocsys_dev (dev))
721 if (src_path[procsys_len])
722 backslashify (src_path + procsys_len, dst, 0);
723 else /* Avoid empty NT path. */
724 stpcpy (dst, "\\");
725 *flags = cygdrive_flags;
727 else
728 strcpy (dst, src_path);
729 goto out;
731 /* Check if the cygdrive prefix was specified. If so, just strip
732 off the prefix and transform it into an MS-DOS path. */
733 else if (iscygdrive (src_path))
735 int n = mount_table->cygdrive_len - 1;
736 int unit;
738 if (!src_path[n])
740 dst[0] = '\0';
741 if (mount_table->cygdrive_len > 1)
742 dev = *cygdrive_dev;
744 else if (cygdrive_win32_path (src_path, dst, unit))
746 *flags = cygdrive_flags;
747 goto out;
749 else if (mount_table->cygdrive_len > 1)
750 return ENOENT;
753 int chroot_pathlen;
754 chroot_pathlen = 0;
755 /* Check the mount table for prefix matches. */
756 for (i = 0; i < nmounts; i++)
758 const char *path;
759 int len;
761 mi = mount + posix_sorted[i];
762 if (!cygheap->root.exists ()
763 || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
765 path = mi->posix_path;
766 len = mi->posix_pathlen;
768 else if (cygheap->root.posix_ok (mi->posix_path))
770 path = cygheap->root.unchroot (mi->posix_path);
771 chroot_pathlen = len = strlen (path);
773 else
775 chroot_pathlen = 0;
776 continue;
779 if (path_prefix_p (path, src_path, len, mi->flags & MOUNT_NOPOSIX))
780 break;
783 if (i < nmounts)
785 int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
786 if (err)
787 return err;
788 chroot_ok = true;
790 else
792 int offset = 0;
793 if (src_path[1] != '/' && src_path[1] != ':')
794 offset = cygheap->cwd.get_drive (dst);
795 backslashify (src_path, dst + offset, 0);
797 out:
798 if (chroot_ok || cygheap->root.ischroot_native (dst))
799 rc = 0;
800 else
802 debug_printf ("attempt to access outside of chroot '%s - %s'",
803 cygheap->root.posix_path (), cygheap->root.native_path ());
804 rc = ENOENT;
807 out_no_chroot_check:
808 debug_printf ("src_path %s, dst %s, flags %y, rc %d", src_path, dst, *flags, rc);
809 return rc;
812 size_t
813 mount_info::get_mounts_here (const char *parent_dir, size_t parent_dir_len,
814 PUNICODE_STRING mount_points,
815 PUNICODE_STRING cygd)
817 size_t n_mounts = 0;
819 for (int i = 0; i < nmounts; i++)
821 mount_item *mi = mount + posix_sorted[i];
822 char *last_slash = strrchr (mi->posix_path, '/');
823 if (!last_slash)
824 continue;
825 if (last_slash == mi->posix_path)
827 if (parent_dir_len == 1 && mi->posix_pathlen > 1)
828 sys_mbstouni_alloc (&mount_points[n_mounts++], HEAP_BUF,
829 last_slash + 1);
831 else if (parent_dir_len == (size_t) (last_slash - mi->posix_path)
832 && strncasematch (parent_dir, mi->posix_path, parent_dir_len))
833 sys_mbstouni_alloc (&mount_points[n_mounts++], HEAP_BUF,
834 last_slash + 1);
836 sys_mbstouni_alloc (cygd, HEAP_BUF, cygdrive + 1);
837 if (cygd->Length)
838 cygd->Length -= 2; // Strip trailing slash
839 return n_mounts;
842 void
843 mount_info::free_mounts_here (PUNICODE_STRING mount_points, int n_mounts,
844 PUNICODE_STRING cygd)
846 for (int i = 0; i < n_mounts; ++i)
847 cfree (mount_points[i].Buffer);
848 cfree (cygd->Buffer);
851 /* cygdrive_posix_path: Build POSIX path used as the
852 mount point for cygdrives created when there is no other way to
853 obtain a POSIX path from a Win32 one.
855 Recognized flag values:
856 - 0x001: Add trailing slash.
857 - 0x200 == CCP_PROC_CYGDRIVE: Return /proc/cygdrive rather than actual
858 cygdrive prefix. */
860 void
861 mount_info::cygdrive_posix_path (const char *src, char *dst, int flags)
863 int len;
865 if (flags & CCP_PROC_CYGDRIVE)
867 len = sizeof ("/proc/cygdrive/") - 1;
868 memcpy (dst, "/proc/cygdrive/", len + 1);
870 else
872 len = cygdrive_len;
873 memcpy (dst, cygdrive, len + 1);
876 /* Now finish the path off with the drive letter to be used.
877 The cygdrive prefix always ends with a trailing slash so
878 the drive letter is added after the path. */
879 dst[len++] = cyg_tolower (src[0]);
880 if (!src[2] || (isdirsep (src[2]) && !src[3]))
881 dst[len++] = '\000';
882 else
884 int n;
885 dst[len++] = '/';
886 if (isdirsep (src[2]))
887 n = 3;
888 else
889 n = 2;
890 strcpy (dst + len, src + n);
892 slashify (dst, dst, !!(flags & 0x1));
896 mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
898 int res;
899 const char *p = src + cygdrive_len;
900 if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
902 unit = -1; /* FIXME: should be zero, maybe? */
903 dst[0] = '\0';
904 res = 0;
906 else
908 /* drive letter must always be uppercase for casesensitive native NT. */
909 dst[0] = cyg_toupper (*p);
910 dst[1] = ':';
911 strcpy (dst + 2, p + 1);
912 backslashify (dst, dst, !dst[2]);
913 unit = dst[0];
914 res = 1;
916 debug_printf ("src '%s', dst '%s'", src, dst);
917 return res;
920 /* conv_to_posix_path: Ensure src_path is a POSIX path.
922 The result is zero for success, or an errno value.
923 posix_path must have sufficient space (i.e. NT_MAX_PATH bytes).
924 If keep_rel_p is non-zero, relative paths stay that way. */
926 /* TODO: Change conv_to_posix_path to work with native paths. */
928 /* src_path is a wide Win32 path. */
930 mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path,
931 int ccp_flags)
933 bool changed = false;
934 if (!wcsncmp (src_path, L"\\\\?\\", 4))
936 src_path += 4;
937 if (src_path[1] != L':') /* native UNC path */
939 *(src_path += 2) = L'\\';
940 changed = true;
943 tmp_pathbuf tp;
944 char *buf = tp.c_get ();
945 sys_wcstombs (buf, NT_MAX_PATH, src_path);
946 int ret = conv_to_posix_path (buf, posix_path, ccp_flags);
947 if (changed)
948 src_path[0] = L'C';
949 return ret;
953 mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
954 int ccp_flags)
956 int src_path_len = strlen (src_path);
957 int relative = !isabspath (src_path);
958 int append_slash;
960 if (src_path_len <= 1)
961 append_slash = 0;
962 else
964 const char *lastchar = src_path + src_path_len - 1;
965 append_slash = isdirsep (*lastchar)
966 && ((ccp_flags & __CCP_APP_SLASH) || lastchar[-1] != ':');
969 debug_printf ("conv_to_posix_path (%s, 0x%x, %s)", src_path, ccp_flags,
970 append_slash ? "add-slash" : "no-add-slash");
972 if (src_path_len >= NT_MAX_PATH)
974 debug_printf ("ENAMETOOLONG");
975 return ENAMETOOLONG;
978 /* FIXME: For now, if the path is relative and it's supposed to stay
979 that way, skip mount table processing. */
981 if ((ccp_flags & CCP_RELATIVE) && relative)
983 slashify (src_path, posix_path, 0);
984 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
985 return 0;
988 tmp_pathbuf tp;
989 char *pathbuf = tp.c_get ();
990 char *tail;
991 int rc = normalize_win32_path (src_path, pathbuf, tail);
992 if (rc != 0)
994 debug_printf ("%d = conv_to_posix_path(%s)", rc, src_path);
995 return rc;
998 int pathbuflen = tail - pathbuf;
999 for (int i = 0; i < nmounts; ++i)
1001 mount_item &mi = mount[native_sorted[i]];
1002 if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen,
1003 mi.flags & MOUNT_NOPOSIX))
1004 continue;
1006 if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
1007 continue;
1009 /* SRC_PATH is in the mount table. */
1010 int nextchar;
1011 const char *p = pathbuf + mi.native_pathlen;
1013 if (!*p || !p[1])
1014 nextchar = 0;
1015 else if (isdirsep (*p))
1016 nextchar = -1;
1017 else
1018 nextchar = 1;
1020 int addslash = nextchar > 0 ? 1 : 0;
1021 if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= NT_MAX_PATH)
1022 return ENAMETOOLONG;
1023 strcpy (posix_path, mi.posix_path);
1024 if (addslash || (!nextchar && append_slash))
1025 strcat (posix_path, "/");
1026 if (nextchar)
1027 slashify (p,
1028 posix_path + addslash + (mi.posix_pathlen == 1
1029 ? 0 : mi.posix_pathlen),
1030 append_slash);
1032 if (cygheap->root.exists ())
1034 const char *p = cygheap->root.unchroot (posix_path);
1035 memmove (posix_path, p, strlen (p) + 1);
1037 goto out;
1040 if (!cygheap->root.exists ())
1041 /* nothing */;
1042 else if (!cygheap->root.ischroot_native (pathbuf))
1043 return ENOENT;
1044 else
1046 const char *p = pathbuf + cygheap->root.native_length ();
1047 if (*p)
1048 slashify (p, posix_path, append_slash);
1049 else
1051 posix_path[0] = '/';
1052 posix_path[1] = '\0';
1054 goto out;
1057 /* Not in the database. This should [theoretically] only happen if either
1058 the path begins with //, or / isn't mounted, or the path has a drive
1059 letter not covered by the mount table. If it's a relative path then the
1060 caller must want an absolute path (otherwise we would have returned
1061 above). So we always return an absolute path at this point. */
1062 if (isdrive (pathbuf))
1063 cygdrive_posix_path (pathbuf, posix_path, append_slash | ccp_flags);
1064 else
1066 /* The use of src_path and not pathbuf here is intentional.
1067 We couldn't translate the path, so just ensure no \'s are present. */
1068 slashify (src_path, posix_path, append_slash);
1071 out:
1072 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
1073 return 0;
1076 inline char *
1077 skip_ws (char *in)
1079 while (*in == ' ' || *in == '\t')
1080 ++in;
1081 return in;
1084 inline char *
1085 find_ws (char *in)
1087 while (*in && *in != ' ' && *in != '\t')
1088 ++in;
1089 return in;
1092 inline char *
1093 conv_fstab_spaces (char *field)
1095 char *sp = field;
1096 while ((sp = strstr (sp, "\\040")) != NULL)
1098 *sp++ = ' ';
1099 memmove (sp, sp + 3, strlen (sp + 3) + 1);
1101 return field;
1104 struct opt
1106 const char *name;
1107 unsigned val;
1108 bool clear;
1109 } oopts[] =
1111 {"acl", MOUNT_NOACL, 1},
1112 {"auto", 0, 0},
1113 {"binary", MOUNT_TEXT, 1},
1114 {"bind", MOUNT_BIND, 0},
1115 {"cygexec", MOUNT_CYGWIN_EXEC, 0},
1116 {"dos", MOUNT_DOS, 0},
1117 {"exec", MOUNT_EXEC, 0},
1118 {"ihash", MOUNT_IHASH, 0},
1119 {"noacl", MOUNT_NOACL, 0},
1120 {"nosuid", 0, 0},
1121 {"notexec", MOUNT_NOTEXEC, 0},
1122 {"nouser", MOUNT_SYSTEM, 0},
1123 {"override", MOUNT_OVERRIDE, 0},
1124 {"posix=0", MOUNT_NOPOSIX, 0},
1125 {"posix=1", MOUNT_NOPOSIX, 1},
1126 {"sparse", MOUNT_SPARSE, 0},
1127 {"text", MOUNT_TEXT, 0},
1128 {"user", MOUNT_SYSTEM, 1}
1131 static int
1132 compare_flags (const void *a, const void *b)
1134 const opt *oa = (const opt *) a;
1135 const opt *ob = (const opt *) b;
1137 return strcmp (oa->name, ob->name);
1140 extern "C" bool
1141 fstab_read_flags (char **options, unsigned &flags, bool external)
1143 opt key;
1145 while (**options)
1147 char *p = strchr (*options, ',');
1148 if (p)
1149 *p++ = '\0';
1150 else
1151 p = strchr (*options, '\0');
1153 key.name = *options;
1154 opt *o = (opt *) bsearch (&key, oopts,
1155 sizeof oopts / sizeof (opt),
1156 sizeof (opt), compare_flags);
1157 if (!o)
1159 if (!external)
1160 system_printf ("invalid fstab option - '%s'", *options);
1161 return false;
1163 if (o->clear)
1164 flags &= ~o->val;
1165 else
1166 flags |= o->val;
1167 *options = p;
1169 return true;
1172 extern "C" char *
1173 fstab_list_flags ()
1175 size_t len = 0;
1176 opt *o;
1178 for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1179 len += strlen (o->name) + 1;
1180 char *buf = (char *) malloc (len);
1181 if (buf)
1183 char *bp = buf;
1184 for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1186 bp = stpcpy (bp, o->name);
1187 *bp++ = ',';
1189 *--bp = '\0';
1191 return buf;
1194 bool
1195 mount_info::from_fstab_line (char *line, bool user)
1197 char *native_path, *posix_path, *fs_type;
1199 /* First field: Native path. */
1200 char *c = skip_ws (line);
1201 if (!*c || *c == '#')
1202 return true;
1203 char *cend = find_ws (c);
1204 *cend = '\0';
1205 native_path = conv_fstab_spaces (c);
1206 /* Always convert drive letter to uppercase for case sensitivity. */
1207 if (isdrive (native_path))
1208 native_path[0] = cyg_toupper (native_path[0]);
1209 /* Second field: POSIX path. */
1210 c = skip_ws (cend + 1);
1211 if (!*c)
1212 return true;
1213 cend = find_ws (c);
1214 *cend = '\0';
1215 posix_path = conv_fstab_spaces (c);
1216 /* Third field: FS type. */
1217 c = skip_ws (cend + 1);
1218 if (!*c)
1219 return true;
1220 cend = find_ws (c);
1221 *cend = '\0';
1222 fs_type = c;
1223 /* Forth field: Flags. */
1224 c = skip_ws (cend + 1);
1225 if (!*c)
1226 return true;
1227 cend = find_ws (c);
1228 *cend = '\0';
1229 unsigned mount_flags = MOUNT_SYSTEM;
1230 if (!strcmp (fs_type, "cygdrive"))
1231 mount_flags |= MOUNT_NOPOSIX;
1232 if (!strcmp (fs_type, "usertemp"))
1233 mount_flags |= MOUNT_IMMUTABLE;
1234 if (!fstab_read_flags (&c, mount_flags, false))
1235 return true;
1236 if (mount_flags & MOUNT_BIND)
1238 /* Prepend root path to bound path. */
1239 char *bound_path = native_path;
1240 device dev;
1241 unsigned flags = 0;
1242 native_path = (char *) alloca (PATH_MAX);
1243 int error = conv_to_win32_path (bound_path, native_path, dev, &flags);
1244 if (error || strlen (native_path) >= MAX_PATH)
1245 return true;
1246 if ((mount_flags & ~MOUNT_SYSTEM) == MOUNT_BIND)
1247 mount_flags = (MOUNT_BIND | flags)
1248 & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1250 if (user)
1251 mount_flags &= ~MOUNT_SYSTEM;
1252 if (!strcmp (fs_type, "cygdrive"))
1254 cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
1255 slashify (posix_path, cygdrive, 1);
1256 cygdrive_len = strlen (cygdrive);
1258 else if (!strcmp (fs_type, "usertemp"))
1260 WCHAR tmp[PATH_MAX + 1];
1262 if (GetTempPathW (PATH_MAX, tmp))
1264 tmp_pathbuf tp;
1265 char *mb_tmp = tp.c_get ();
1266 sys_wcstombs (mb_tmp, PATH_MAX, tmp);
1268 mount_flags |= MOUNT_USER_TEMP;
1269 int res = mount_table->add_item (mb_tmp, posix_path, mount_flags);
1270 if (res && get_errno () == EMFILE)
1271 return false;
1274 else
1276 int res = mount_table->add_item (native_path, posix_path, mount_flags);
1277 if (res && get_errno () == EMFILE)
1278 return false;
1280 return true;
1283 bool
1284 mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
1286 UNICODE_STRING upath;
1287 OBJECT_ATTRIBUTES attr;
1288 NT_readline rl;
1289 tmp_pathbuf tp;
1290 char *buf = tp.c_get ();
1292 if (user)
1294 PWCHAR username;
1295 sys_mbstowcs (username = wcpcpy (fstab_end, L".d\\"),
1296 NT_MAX_PATH - (fstab_end - fstab),
1297 cygheap->user.name ());
1298 /* Make sure special chars in the username are converted according to
1299 the rules. */
1300 transform_chars (username, username + wcslen (username) - 1);
1302 RtlInitUnicodeString (&upath, fstab);
1303 InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
1304 debug_printf ("Try to read mounts from %W", fstab);
1305 if (rl.init (&attr, buf, NT_MAX_PATH))
1306 while ((buf = rl.gets ()))
1307 if (!from_fstab_line (buf, user))
1308 break;
1309 return true;
1312 /* write_cygdrive_info: Store default prefix and flags
1313 to use when creating cygdrives to the special user shared mem
1314 location used to store cygdrive information. */
1317 mount_info::write_cygdrive_info (const char *cygdrive_prefix, unsigned flags)
1319 /* Verify cygdrive prefix starts with a forward slash and if there's
1320 another character, it's not a slash. */
1321 if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
1322 (!isslash (cygdrive_prefix[0])) ||
1323 ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
1325 set_errno (EINVAL);
1326 return -1;
1328 /* Don't allow overriding of a system cygdrive prefix. */
1329 if (cygdrive_flags & MOUNT_SYSTEM)
1331 set_errno (EPERM);
1332 return -1;
1335 slashify (cygdrive_prefix, cygdrive, 1);
1336 cygdrive_flags = flags & ~MOUNT_SYSTEM;
1337 cygdrive_len = strlen (cygdrive);
1339 return 0;
1343 mount_info::get_cygdrive_info (char *user, char *system, char *user_flags,
1344 char *system_flags)
1346 if (user)
1347 *user = '\0';
1348 if (system)
1349 *system = '\0';
1350 if (user_flags)
1351 *user_flags = '\0';
1352 if (system_flags)
1353 *system_flags = '\0';
1355 char *path = (cygdrive_flags & MOUNT_SYSTEM) ? system : user;
1356 char *flags = (cygdrive_flags & MOUNT_SYSTEM) ? system_flags : user_flags;
1358 if (path)
1360 strcpy (path, cygdrive);
1361 /* Strip trailing slash for backward compatibility. */
1362 if (cygdrive_len > 2)
1363 path[cygdrive_len - 1] = '\0';
1365 if (flags)
1366 strcpy (flags, (cygdrive_flags & MOUNT_TEXT) ? "textmode" : "binmode");
1367 return 0;
1370 static mount_item *mounts_for_sort;
1372 /* sort_by_posix_name: qsort callback to sort the mount entries. Sort
1373 user mounts ahead of system mounts to the same POSIX path. */
1374 /* FIXME: should the user be able to choose whether to prefer user or
1375 system mounts??? */
1376 static int
1377 sort_by_posix_name (const void *a, const void *b)
1379 mount_item *ap = mounts_for_sort + (*((int*) a));
1380 mount_item *bp = mounts_for_sort + (*((int*) b));
1382 /* Base weighting on longest posix path first so that the most
1383 obvious path will be chosen. */
1384 size_t alen = strlen (ap->posix_path);
1385 size_t blen = strlen (bp->posix_path);
1387 int res = blen - alen;
1389 if (res)
1390 return res; /* Path lengths differed */
1392 /* The two paths were the same length, so just determine normal
1393 lexical sorted order. */
1394 res = strcmp (ap->posix_path, bp->posix_path);
1396 if (res == 0)
1398 /* need to select between user and system mount to same POSIX path */
1399 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
1400 return 1;
1401 else
1402 return -1;
1405 return res;
1408 /* sort_by_native_name: qsort callback to sort the mount entries. Sort
1409 user mounts ahead of system mounts to the same POSIX path. */
1410 /* FIXME: should the user be able to choose whether to prefer user or
1411 system mounts??? */
1412 static int
1413 sort_by_native_name (const void *a, const void *b)
1415 mount_item *ap = mounts_for_sort + (*((int*) a));
1416 mount_item *bp = mounts_for_sort + (*((int*) b));
1418 /* Base weighting on longest win32 path first so that the most
1419 obvious path will be chosen. */
1420 size_t alen = strlen (ap->native_path);
1421 size_t blen = strlen (bp->native_path);
1423 int res = blen - alen;
1425 if (res)
1426 return res; /* Path lengths differed */
1428 /* The two paths were the same length, so just determine normal
1429 lexical sorted order. */
1430 res = strcmp (ap->native_path, bp->native_path);
1432 if (res == 0)
1434 /* need to select between user and system mount to same POSIX path */
1435 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
1436 return 1;
1437 else
1438 return -1;
1441 return res;
1444 void
1445 mount_info::sort ()
1447 for (int i = 0; i < nmounts; i++)
1448 native_sorted[i] = posix_sorted[i] = i;
1449 /* Sort them into reverse length order, otherwise we won't
1450 be able to look for /foo in /. */
1451 mounts_for_sort = mount; /* ouch. */
1452 qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
1453 qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
1456 /* Add an entry to the mount table.
1457 Returns 0 on success, -1 on failure and errno is set.
1459 This is where all argument validation is done. It may not make sense to
1460 do this when called internally, but it's cleaner to keep it all here. */
1463 mount_info::add_item (const char *native, const char *posix,
1464 unsigned mountflags)
1466 tmp_pathbuf tp;
1467 char *nativetmp = tp.c_get ();
1468 /* FIXME: The POSIX path is stored as value name right now, which is
1469 restricted to 256 bytes. */
1470 char posixtmp[CYG_MAX_PATH];
1471 char *nativetail, *posixtail, error[] = "error";
1472 int nativeerr, posixerr;
1474 /* Something's wrong if either path is NULL or empty, or if it's
1475 not a UNC or absolute path. */
1477 if (native == NULL || !isabspath (native) ||
1478 !(is_native_path (native) || is_unc_share (native) || isdrive (native)))
1479 nativeerr = EINVAL;
1480 else
1481 nativeerr = normalize_win32_path (native, nativetmp, nativetail);
1483 if (posix == NULL || !isabspath (posix) ||
1484 is_unc_share (posix) || isdrive (posix))
1485 posixerr = EINVAL;
1486 else
1487 posixerr = normalize_posix_path (posix, posixtmp, posixtail);
1489 debug_printf ("%s[%s], %s[%s], %y",
1490 native, nativeerr ? error : nativetmp,
1491 posix, posixerr ? error : posixtmp, mountflags);
1493 if (nativeerr || posixerr)
1495 set_errno (nativeerr ?: posixerr);
1496 return -1;
1499 /* Make sure both paths do not end in /. */
1500 if (nativetail > nativetmp + 1 && nativetail[-1] == '\\')
1501 nativetail[-1] = '\0';
1502 if (posixtail > posixtmp + 1 && posixtail[-1] == '/')
1503 posixtail[-1] = '\0';
1505 /* Write over an existing mount item with the same POSIX path if
1506 it exists and is from the same registry area. */
1507 int i;
1508 for (i = 0; i < nmounts; i++)
1510 if (!strcmp (mount[i].posix_path, posixtmp))
1512 /* Don't allow overriding of a system mount with a user mount. */
1513 if ((mount[i].flags & MOUNT_SYSTEM) && !(mountflags & MOUNT_SYSTEM))
1515 set_errno (EPERM);
1516 return -1;
1518 if ((mount[i].flags & MOUNT_SYSTEM) != (mountflags & MOUNT_SYSTEM))
1519 continue;
1520 else if (!(mount[i].flags & MOUNT_IMMUTABLE))
1521 break;
1522 else if (mountflags & MOUNT_OVERRIDE)
1524 mountflags |= MOUNT_IMMUTABLE;
1525 break;
1527 else
1529 set_errno (EPERM);
1530 return -1;
1535 if (i == nmounts && nmounts == MAX_MOUNTS)
1537 set_errno (EMFILE);
1538 return -1;
1541 if (i == nmounts)
1542 nmounts++;
1544 if (strcmp (posixtmp, "/usr/bin") == 0)
1545 got_usr_bin = true;
1547 if (strcmp (posixtmp, "/usr/lib") == 0)
1548 got_usr_lib = true;
1550 if (posixtmp[0] == '/' && posixtmp[1] == '\0' && !(mountflags & MOUNT_CYGDRIVE))
1551 root_idx = i;
1553 mount[i].init (nativetmp, posixtmp, mountflags);
1554 sort ();
1556 return 0;
1559 /* Delete a mount table entry where path is either a Win32 or POSIX
1560 path. Since the mount table is really just a table of aliases,
1561 deleting / is ok (although running without a slash mount is
1562 strongly discouraged because some programs may run erratically
1563 without one). If MOUNT_SYSTEM is set in flags, remove from system
1564 registry, otherwise remove the user registry mount.
1568 mount_info::del_item (const char *path, unsigned flags)
1570 tmp_pathbuf tp;
1571 char *pathtmp = tp.c_get ();
1572 int posix_path_p = false;
1574 /* Something's wrong if path is NULL or empty. */
1575 if (path == NULL || *path == 0 || !isabspath (path))
1577 set_errno (EINVAL);
1578 return -1;
1581 if (is_unc_share (path) || strpbrk (path, ":\\"))
1582 backslashify (path, pathtmp, 0);
1583 else
1585 slashify (path, pathtmp, 0);
1586 posix_path_p = true;
1588 nofinalslash (pathtmp, pathtmp);
1590 for (int i = 0; i < nmounts; i++)
1592 int ent = native_sorted[i]; /* in the same order as getmntent() */
1593 if (((posix_path_p)
1594 ? !strcmp (mount[ent].posix_path, pathtmp)
1595 : strcasematch (mount[ent].native_path, pathtmp)))
1597 /* Don't allow removal of a system mount. */
1598 if (mount[ent].flags & MOUNT_SYSTEM)
1600 set_errno (EPERM);
1601 return -1;
1603 nmounts--; /* One less mount table entry */
1604 /* Fill in the hole if not at the end of the table */
1605 if (ent < nmounts)
1606 memmove (mount + ent, mount + ent + 1,
1607 sizeof (mount[ent]) * (nmounts - ent));
1608 sort (); /* Resort the table */
1609 return 0;
1612 set_errno (EINVAL);
1613 return -1;
1616 /************************* mount_item class ****************************/
1618 /* Don't add new fs types without adding them to fs_info_type in mount.h!
1619 Don't reorder without reordering fs_info_type in mount.h!*/
1620 fs_names_t fs_names[] = {
1621 { "none", false },
1622 { "vfat", true },
1623 { "exfat", true },
1624 { "ntfs", true },
1625 { "refs", true },
1626 { "smbfs", false },
1627 { "nfs", false },
1628 { "netapp", false },
1629 { "iso9660", true },
1630 { "udf", true },
1631 { "csc-cache", false },
1632 { "unixfs", false },
1633 { "mvfs", false },
1634 { "cifs", false },
1635 { "nwfs", false },
1636 { "ncfsd", false },
1637 { "afs", false },
1638 { "prlfs", false },
1639 { NULL, false }
1642 static mntent *
1643 fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
1645 struct mntent& ret=_my_tls.locals.mntbuf;
1646 bool append_bs = false;
1648 /* Remove drivenum from list if we see a x: style path */
1649 if (strlen (native_path) == 2 && native_path[1] == ':')
1651 int drivenum = cyg_tolower (native_path[0]) - 'a';
1652 if (drivenum >= 0 && drivenum <= 31)
1653 _my_tls.locals.available_drives &= ~(1 << drivenum);
1654 append_bs = true;
1657 /* Pass back pointers to mount_table strings reserved for use by
1658 getmntent rather than pointers to strings in the internal mount
1659 table because the mount table might change, causing weird effects
1660 from the getmntent user's point of view. */
1662 ret.mnt_fsname = _my_tls.locals.mnt_fsname;
1663 strcpy (_my_tls.locals.mnt_dir, posix_path);
1664 ret.mnt_dir = _my_tls.locals.mnt_dir;
1666 /* Try to give a filesystem type that matches what a Linux application might
1667 expect. Naturally, this is a moving target, but we can make some
1668 reasonable guesses for popular types. */
1670 fs_info mntinfo;
1671 tmp_pathbuf tp;
1672 UNICODE_STRING unat;
1673 tp.u_get (&unat);
1674 get_nt_native_path (native_path, unat, false);
1675 if (append_bs)
1676 RtlAppendUnicodeToString (&unat, L"\\");
1677 mntinfo.update (&unat, NULL);
1679 if (mntinfo.what_fs () > none && mntinfo.what_fs () < max_fs_type)
1680 strcpy (_my_tls.locals.mnt_type, fs_names[mntinfo.what_fs ()].name);
1681 else
1682 strcpy (_my_tls.locals.mnt_type, mntinfo.fsname ());
1684 ret.mnt_type = _my_tls.locals.mnt_type;
1686 slashify (native_path, _my_tls.locals.mnt_fsname, false);
1688 /* mnt_opts is a string that details mount params such as
1689 binary or textmode, or exec. We don't print
1690 `silent' here; it's a magic internal thing. */
1692 if (flags & MOUNT_TEXT)
1693 strcpy (_my_tls.locals.mnt_opts, (char *) "text");
1694 else
1695 strcpy (_my_tls.locals.mnt_opts, (char *) "binary");
1697 if (flags & MOUNT_CYGWIN_EXEC)
1698 strcat (_my_tls.locals.mnt_opts, (char *) ",cygexec");
1699 else if (flags & MOUNT_EXEC)
1700 strcat (_my_tls.locals.mnt_opts, (char *) ",exec");
1701 else if (flags & MOUNT_NOTEXEC)
1702 strcat (_my_tls.locals.mnt_opts, (char *) ",notexec");
1704 if (flags & MOUNT_NOACL)
1705 strcat (_my_tls.locals.mnt_opts, (char *) ",noacl");
1707 if (flags & MOUNT_DOS)
1708 strcat (_my_tls.locals.mnt_opts, (char *) ",dos");
1710 if (flags & MOUNT_IHASH)
1711 strcat (_my_tls.locals.mnt_opts, (char *) ",ihash");
1713 if (flags & MOUNT_NOPOSIX)
1714 strcat (_my_tls.locals.mnt_opts, (char *) ",posix=0");
1716 if (flags & MOUNT_SPARSE)
1717 strcat (_my_tls.locals.mnt_opts, (char *) ",sparse");
1719 if (!(flags & MOUNT_SYSTEM)) /* user mount */
1720 strcat (_my_tls.locals.mnt_opts, (char *) ",user");
1722 if (flags & MOUNT_CYGDRIVE) /* cygdrive */
1723 strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
1725 if (flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
1726 strcat (_my_tls.locals.mnt_opts, (char *) ",auto");
1728 if (flags & (MOUNT_BIND))
1729 strcat (_my_tls.locals.mnt_opts, (char *) ",bind");
1731 if (flags & (MOUNT_USER_TEMP))
1732 strcat (_my_tls.locals.mnt_opts, (char *) ",usertemp");
1734 ret.mnt_opts = _my_tls.locals.mnt_opts;
1736 ret.mnt_freq = 1;
1737 ret.mnt_passno = 1;
1738 return &ret;
1741 struct mntent *
1742 mount_item::getmntent ()
1744 return fillout_mntent (native_path, posix_path, flags);
1747 static struct mntent *
1748 cygdrive_getmntent ()
1750 char native_path[4];
1751 char posix_path[CYG_MAX_PATH];
1752 DWORD mask = 1, drive = 'a';
1753 struct mntent *ret = NULL;
1755 while (_my_tls.locals.available_drives)
1757 for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
1758 if (_my_tls.locals.available_drives & mask)
1759 break;
1761 __small_sprintf (native_path, "%c:\\", cyg_toupper (drive));
1762 if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
1764 _my_tls.locals.available_drives &= ~mask;
1765 continue;
1767 native_path[2] = '\0';
1768 __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
1769 ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags);
1770 break;
1773 return ret;
1776 struct mntent *
1777 mount_info::getmntent (int x)
1779 if (x < 0 || x >= nmounts)
1780 return cygdrive_getmntent ();
1782 return mount[native_sorted[x]].getmntent ();
1785 /* Fill in the fields of a mount table entry. */
1787 void
1788 mount_item::init (const char *native, const char *posix, unsigned mountflags)
1790 strcpy ((char *) native_path, native);
1791 strcpy ((char *) posix_path, posix);
1793 native_pathlen = strlen (native_path);
1794 posix_pathlen = strlen (posix_path);
1796 flags = mountflags;
1799 /********************** Mount System Calls **************************/
1801 /* Mount table system calls.
1802 Note that these are exported to the application. */
1804 /* mount: Add a mount to the mount table in memory and to the registry
1805 that will cause paths under win32_path to be translated to paths
1806 under posix_path. */
1808 extern "C" int
1809 mount (const char *win32_path, const char *posix_path, unsigned flags)
1811 /* FIXME: Should we disallow setting MOUNT_SYSTEM in flags since it
1812 isn't really supported except from fstab? */
1813 int res = -1;
1815 __try
1817 if (!*posix_path)
1818 set_errno (EINVAL);
1819 else if (strpbrk (posix_path, "\\:"))
1820 set_errno (EINVAL);
1821 else if (flags & MOUNT_CYGDRIVE) /* normal mount */
1823 /* When flags include MOUNT_CYGDRIVE, take this to mean that
1824 we actually want to change the cygdrive prefix and flags
1825 without actually mounting anything. */
1826 res = mount_table->write_cygdrive_info (posix_path, flags);
1827 win32_path = NULL;
1829 else if (!*win32_path)
1830 set_errno (EINVAL);
1831 else
1833 char *w32_path = (char *) win32_path;
1834 if (flags & MOUNT_BIND)
1836 /* Prepend root path to bound path. */
1837 tmp_pathbuf tp;
1838 device dev;
1840 unsigned conv_flags = 0;
1841 const char *bound_path = w32_path;
1843 w32_path = tp.c_get ();
1844 int error = mount_table->conv_to_win32_path (bound_path, w32_path,
1845 dev, &conv_flags);
1846 if (error || strlen (w32_path) >= MAX_PATH)
1847 return true;
1848 if ((flags & ~MOUNT_SYSTEM) == MOUNT_BIND)
1849 flags = (MOUNT_BIND | conv_flags)
1850 & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1852 /* Make sure all mounts are user mounts, even those added via
1853 mount -a. */
1854 flags &= ~MOUNT_SYSTEM;
1855 res = mount_table->add_item (w32_path, posix_path, flags);
1858 syscall_printf ("%R = mount(%s, %s, %y)",
1859 res, win32_path, posix_path, flags);
1861 __except (EFAULT) {}
1862 __endtry
1863 return res;
1866 /* umount: The standard umount call only has a path parameter. Since
1867 it is not possible for this call to specify whether to remove the
1868 mount from the user or global mount registry table, assume the user
1869 table. */
1871 extern "C" int
1872 umount (const char *path)
1874 __try
1876 if (!*path)
1878 set_errno (EINVAL);
1879 __leave;
1881 return cygwin_umount (path, 0);
1883 __except (EFAULT) {}
1884 __endtry
1885 return -1;
1888 /* cygwin_umount: This is like umount but takes an additional flags
1889 parameter that specifies whether to umount from the user or system-wide
1890 registry area. */
1892 extern "C" int
1893 cygwin_umount (const char *path, unsigned flags)
1895 int res = -1;
1897 if (!(flags & MOUNT_CYGDRIVE))
1898 res = mount_table->del_item (path, flags & ~MOUNT_SYSTEM);
1900 syscall_printf ("%R = cygwin_umount(%s, %d)", res, path, flags);
1901 return res;
1904 #define is_dev(d,s) wcsncmp((d),(s),sizeof(s) - 1)
1906 disk_type
1907 get_disk_type (LPCWSTR dos)
1909 WCHAR dev[MAX_PATH], *d = dev;
1910 if (!QueryDosDeviceW (dos, dev, MAX_PATH))
1911 return DT_NODISK;
1912 if (is_dev (dev, L"\\Device\\"))
1914 d += 8;
1915 switch (*d)
1917 case L'C':
1918 if (is_dev (d, L"CdRom"))
1919 return DT_CDROM;
1920 break;
1921 case L'F':
1922 if (is_dev (d, L"Floppy"))
1923 return DT_FLOPPY;
1924 break;
1925 case L'H':
1926 if (is_dev (d, L"Harddisk"))
1927 return DT_HARDDISK;
1928 break;
1929 case L'L':
1930 if (is_dev (d, L"LanmanRedirector\\"))
1931 return DT_SHARE_SMB;
1932 break;
1933 case L'M':
1934 if (is_dev (d, L"MRxNfs\\"))
1935 return DT_SHARE_NFS;
1936 break;
1939 return DT_NODISK;
1942 extern "C" FILE *
1943 setmntent (const char *filep, const char *)
1945 _my_tls.locals.iteration = 0;
1946 _my_tls.locals.available_drives = GetLogicalDrives ();
1947 /* Filter floppy drives on A: and B: */
1948 if ((_my_tls.locals.available_drives & 1)
1949 && get_disk_type (L"A:") == DT_FLOPPY)
1950 _my_tls.locals.available_drives &= ~1;
1951 if ((_my_tls.locals.available_drives & 2)
1952 && get_disk_type (L"B:") == DT_FLOPPY)
1953 _my_tls.locals.available_drives &= ~2;
1954 return (FILE *) filep;
1957 extern "C" struct mntent *
1958 getmntent (FILE *)
1960 return mount_table->getmntent (_my_tls.locals.iteration++);
1963 extern "C" struct mntent *
1964 getmntent_r (FILE *, struct mntent *mntbuf, char *buf, int buflen)
1966 struct mntent *mnt = mount_table->getmntent (_my_tls.locals.iteration++);
1967 int fsname_len, dir_len, type_len, tmplen = buflen;
1969 if (!mnt)
1970 return NULL;
1972 fsname_len = strlen (mnt->mnt_fsname) + 1;
1973 dir_len = strlen (mnt->mnt_dir) + 1;
1974 type_len = strlen (mnt->mnt_type) + 1;
1976 snprintf (buf, buflen, "%s%c%s%c%s%c%s", mnt->mnt_fsname, '\0',
1977 mnt->mnt_dir, '\0', mnt->mnt_type, '\0', mnt->mnt_opts);
1979 mntbuf->mnt_fsname = buf;
1980 tmplen -= fsname_len;
1981 mntbuf->mnt_dir = tmplen > 0 ? buf + fsname_len : (char *)"";
1982 tmplen -= dir_len;
1983 mntbuf->mnt_type = tmplen > 0 ? buf + fsname_len + dir_len : (char *)"";
1984 tmplen -= type_len;
1985 mntbuf->mnt_opts = tmplen > 0 ? buf + fsname_len + dir_len + type_len : (char *)"";
1986 mntbuf->mnt_freq = mnt->mnt_freq;
1987 mntbuf->mnt_passno = mnt->mnt_passno;
1988 return mntbuf;
1991 extern "C" int
1992 endmntent (FILE *)
1994 return 1;
1997 dos_drive_mappings::dos_drive_mappings ()
1998 : mappings(0)
2000 tmp_pathbuf tp;
2001 wchar_t vol[64]; /* Long enough for Volume GUID string */
2002 wchar_t *devpath = tp.w_get ();
2003 wchar_t *mounts = tp.w_get ();
2005 /* Iterate over all volumes, fetch the first path from the list of
2006 DOS paths the volume is mounted to, or use the GUID volume path
2007 otherwise. */
2008 HANDLE sh = FindFirstVolumeW (vol, 64);
2009 if (sh == INVALID_HANDLE_VALUE)
2010 debug_printf ("FindFirstVolumeW, %E");
2011 else {
2014 /* Skip drives which are not mounted. */
2015 DWORD len;
2016 if (!GetVolumePathNamesForVolumeNameW (vol, mounts, NT_MAX_PATH, &len)
2017 || mounts[0] == L'\0')
2018 continue;
2019 *wcsrchr (vol, L'\\') = L'\0';
2020 if (QueryDosDeviceW (vol + 4, devpath, NT_MAX_PATH))
2022 /* The DOS drive mapping can be another symbolic link. If so,
2023 the mapping won't work since the section name is the name
2024 after resolving all symlinks. Resolve symlinks here, too. */
2025 for (int syml_cnt = 0; syml_cnt < SYMLOOP_MAX; ++syml_cnt)
2027 UNICODE_STRING upath;
2028 OBJECT_ATTRIBUTES attr;
2029 NTSTATUS status;
2030 HANDLE h;
2032 RtlInitUnicodeString (&upath, devpath);
2033 InitializeObjectAttributes (&attr, &upath,
2034 OBJ_CASE_INSENSITIVE, NULL, NULL);
2035 status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY,
2036 &attr);
2037 if (!NT_SUCCESS (status))
2038 break;
2039 RtlInitEmptyUnicodeString (&upath, devpath, (NT_MAX_PATH - 1)
2040 * sizeof (WCHAR));
2041 status = NtQuerySymbolicLinkObject (h, &upath, NULL);
2042 NtClose (h);
2043 if (!NT_SUCCESS (status))
2044 break;
2045 devpath[upath.Length / sizeof (WCHAR)] = L'\0';
2047 mapping *m = new mapping ();
2048 if (m)
2050 m->dospath = wcsdup (mounts);
2051 m->ntdevpath = wcsdup (devpath);
2052 if (!m->dospath || !m->ntdevpath)
2054 free (m->dospath);
2055 free (m->ntdevpath);
2056 delete m;
2057 continue;
2059 m->doslen = wcslen (m->dospath);
2060 m->dospath[--m->doslen] = L'\0'; /* Drop trailing backslash */
2061 m->ntlen = wcslen (m->ntdevpath);
2062 m->next = mappings;
2063 mappings = m;
2066 else
2067 debug_printf ("Unable to determine the native mapping for %ls "
2068 "(error %u)", vol, GetLastError ());
2070 while (FindNextVolumeW (sh, vol, 64));
2071 FindVolumeClose (sh);
2075 wchar_t *
2076 dos_drive_mappings::fixup_if_match (wchar_t *path)
2078 /* Check for network drive first. */
2079 if (!wcsncmp (path, L"\\Device\\Mup\\", 12))
2081 path += 10;
2082 path[0] = L'\\';
2083 return path;
2085 /* Then test local drives. */
2086 for (mapping *m = mappings; m; m = m->next)
2087 if (!wcsncmp (m->ntdevpath, path, m->ntlen))
2089 wchar_t *tmppath;
2091 if (m->ntlen > m->doslen)
2092 wcsncpy (path += m->ntlen - m->doslen, m->dospath, m->doslen);
2093 else if ((tmppath = wcsdup (path + m->ntlen)) != NULL)
2095 wcpcpy (wcpcpy (path, m->dospath), tmppath);
2096 free (tmppath);
2098 break;
2100 return path;
2103 dos_drive_mappings::~dos_drive_mappings ()
2105 mapping *n = 0;
2106 for (mapping *m = mappings; m; m = n)
2108 n = m->next;
2109 free (m->dospath);
2110 free (m->ntdevpath);
2111 delete m;