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
10 #include "miscfuncs.h"
14 #include <cygwin/version.h>
18 #include "shared_info.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); \
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); \
72 && strchr (_invalid_share_char, __c) == NULL; \
76 is_native_path (const char *path
)
78 return isdirsep (path
[0])
79 && (isdirsep (path
[1]) || path
[1] == '?')
80 && (path
[2] == '?' || path
[2] == '.')
86 is_unc_share (const char *path
)
89 return (isdirsep (path
[0])
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. */
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
)
106 strcpy (win32_path
, dev
.native ());
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
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
];
126 #define MAX_FS_INFO_CNT 32
129 static muto fsi_lock
;
134 } entry
[MAX_FS_INFO_CNT
];
136 uint32_t genhash (PFILE_FS_VOLUME_INFORMATION
);
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
;
148 fs_info_cache::genhash (PFILE_FS_VOLUME_INFORMATION pffvi
)
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! */
157 hash
= *p
++ + (hash
<< 6) + (hash
<< 16) - hash
;
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
;
172 fs_info_cache::add (uint32_t hashval
, fs_info
*new_fsi
)
175 if (count
< MAX_FS_INFO_CNT
)
177 entry
[count
].fsi
= *new_fsi
;
178 entry
[count
].hash
= hashval
;
185 fs_info::update (PUNICODE_STRING upath
, HANDLE in_vol
)
187 NTSTATUS status
= STATUS_OBJECT_NAME_NOT_FOUND
;
189 OBJECT_ATTRIBUTES attr
;
191 bool no_media
= false;
192 FILE_FS_DEVICE_INFORMATION ffdi
;
193 FILE_FS_OBJECTID_INFORMATION ffoi
;
195 FILE_FS_ATTRIBUTE_INFORMATION ffai
;
196 WCHAR buf
[NAME_MAX
+ 1];
199 FILE_FS_VOLUME_INFORMATION ffvi
;
200 WCHAR buf
[NAME_MAX
+ 1];
203 UNICODE_STRING fsname
;
206 /* Always caseinsensitive. We really just need access to the drive. */
207 InitializeObjectAttributes (&attr
, upath
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
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
)
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
);
250 status
= NtQueryVolumeInformationFile (vol
, &io
, &ffvi_buf
.ffvi
,
252 FileFsVolumeInformation
);
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
;
264 if (upath
->Buffer
[5] == L
':' && upath
->Buffer
[6] == L
'\\')
265 p
= upath
->Buffer
+ 6;
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" */
278 RtlInitCountedUnicodeString (&path_prefix
, upath
->Buffer
,
279 (p
- upath
->Buffer
) * sizeof (WCHAR
));
280 ffvi_buf
.ffvi
.VolumeSerialNumber
= hash_path_name ((ino_t
) 0,
283 fs_info
*fsi
= fsi_cache
.search (&ffvi_buf
.ffvi
, hash
);
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
)
300 && RtlEqualUnicodePathPrefix (attr
.ObjectName
, &ro_u_uncp
, TRUE
)))
301 is_remote_drive (true);
304 status
= NtQueryVolumeInformationFile (vol
, &io
, &ffai_buf
.ffai
,
306 FileFsAttributeInformation
);
307 if (no_media
|| !NT_SUCCESS (status
))
309 debug_printf ("Cannot get volume attributes (%S), %y",
310 attr
.ObjectName
, status
);
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
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
,
420 FileFsObjectIdInformation
)))
422 smb_extended_info
*extended_info
= (smb_extended_info
*)
424 if (extended_info
->samba_magic
== SAMBA_EXTENDED_INFO_MAGIC
)
427 samba_version (extended_info
->samba_version
);
430 /* First check the remote filesystems claiming to be NTFS. */
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
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. */
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
));
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
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 ());
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
));
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
));
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
523 caseinsensitive ((!(flags () & FILE_CASE_SENSITIVE_SEARCH
) || is_samba ())
526 /* Check for being an SSD */
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
));
542 fsi_cache
.add (hash
, this);
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
)
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. */
569 mount_info::init (bool user_init
)
572 WCHAR path
[PATH_MAX
];
574 pathend
= wcpcpy (path
, cygheap
->installation_root
.Buffer
);
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
];
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
);
590 add_item (native
, "/usr/bin", MOUNT_SYSTEM
| MOUNT_AUTOMATIC
);
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
)
604 const char *real_native_path
;
605 int real_posix_pathlen
;
607 if (!cygheap
->root
.exists () || posix_pathlen
!= 1 || posix_path
[0] != '/')
610 real_native_path
= native_path
;
611 real_posix_pathlen
= chroot_pathlen
?: posix_pathlen
;
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
;
623 else if ((isdrive (dst
) && !dst
[2]) || *p
)
625 if ((n
+ strlen (p
)) >= NT_MAX_PATH
)
628 backslashify (p
, dst
+ n
, 0);
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
,
649 bool chroot_ok
= !cygheap
->root
.exists ();
654 debug_printf ("conv_to_win32_path (%s)", src_path
);
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
663 eg mounting c:/foo /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
))
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
685 if (src_path
[1] == '/')
687 if (!strchr (src_path
+ 2, '/'))
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 */
705 if (isdev_disk (src_path
))
709 strcpy (dst
, src_path
);
712 if (isproc (src_path
))
715 dev
= fhandler_proc::get_proc_fhandler (src_path
);
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. */
725 *flags
= cygdrive_flags
;
728 strcpy (dst
, src_path
);
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;
741 if (mount_table
->cygdrive_len
> 1)
744 else if (cygdrive_win32_path (src_path
, dst
, unit
))
746 *flags
= cygdrive_flags
;
749 else if (mount_table
->cygdrive_len
> 1)
755 /* Check the mount table for prefix matches. */
756 for (i
= 0; i
< nmounts
; i
++)
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
);
779 if (path_prefix_p (path
, src_path
, len
, mi
->flags
& MOUNT_NOPOSIX
))
785 int err
= mi
->build_win32 (dst
, src_path
, flags
, chroot_pathlen
);
793 if (src_path
[1] != '/' && src_path
[1] != ':')
794 offset
= cygheap
->cwd
.get_drive (dst
);
795 backslashify (src_path
, dst
+ offset
, 0);
798 if (chroot_ok
|| cygheap
->root
.ischroot_native (dst
))
802 debug_printf ("attempt to access outside of chroot '%s - %s'",
803 cygheap
->root
.posix_path (), cygheap
->root
.native_path ());
808 debug_printf ("src_path %s, dst %s, flags %y, rc %d", src_path
, dst
, *flags
, rc
);
813 mount_info::get_mounts_here (const char *parent_dir
, size_t parent_dir_len
,
814 PUNICODE_STRING mount_points
,
815 PUNICODE_STRING cygd
)
819 for (int i
= 0; i
< nmounts
; i
++)
821 mount_item
*mi
= mount
+ posix_sorted
[i
];
822 char *last_slash
= strrchr (mi
->posix_path
, '/');
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
,
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
,
836 sys_mbstouni_alloc (cygd
, HEAP_BUF
, cygdrive
+ 1);
838 cygd
->Length
-= 2; // Strip trailing slash
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
861 mount_info::cygdrive_posix_path (const char *src
, char *dst
, int flags
)
865 if (flags
& CCP_PROC_CYGDRIVE
)
867 len
= sizeof ("/proc/cygdrive/") - 1;
868 memcpy (dst
, "/proc/cygdrive/", len
+ 1);
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]))
886 if (isdirsep (src
[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
)
899 const char *p
= src
+ cygdrive_len
;
900 if (!isalpha (*p
) || (!isdirsep (p
[1]) && p
[1]))
902 unit
= -1; /* FIXME: should be zero, maybe? */
908 /* drive letter must always be uppercase for casesensitive native NT. */
909 dst
[0] = cyg_toupper (*p
);
911 strcpy (dst
+ 2, p
+ 1);
912 backslashify (dst
, dst
, !dst
[2]);
916 debug_printf ("src '%s', dst '%s'", src
, dst
);
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
,
933 bool changed
= false;
934 if (!wcsncmp (src_path
, L
"\\\\?\\", 4))
937 if (src_path
[1] != L
':') /* native UNC path */
939 *(src_path
+= 2) = L
'\\';
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
);
953 mount_info::conv_to_posix_path (const char *src_path
, char *posix_path
,
956 int src_path_len
= strlen (src_path
);
957 int relative
= !isabspath (src_path
);
960 if (src_path_len
<= 1)
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");
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
);
989 char *pathbuf
= tp
.c_get ();
991 int rc
= normalize_win32_path (src_path
, pathbuf
, tail
);
994 debug_printf ("%d = conv_to_posix_path(%s)", rc
, src_path
);
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
))
1006 if (cygheap
->root
.exists () && !cygheap
->root
.posix_ok (mi
.posix_path
))
1009 /* SRC_PATH is in the mount table. */
1011 const char *p
= pathbuf
+ mi
.native_pathlen
;
1015 else if (isdirsep (*p
))
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
, "/");
1028 posix_path
+ addslash
+ (mi
.posix_pathlen
== 1
1029 ? 0 : mi
.posix_pathlen
),
1032 if (cygheap
->root
.exists ())
1034 const char *p
= cygheap
->root
.unchroot (posix_path
);
1035 memmove (posix_path
, p
, strlen (p
) + 1);
1040 if (!cygheap
->root
.exists ())
1042 else if (!cygheap
->root
.ischroot_native (pathbuf
))
1046 const char *p
= pathbuf
+ cygheap
->root
.native_length ();
1048 slashify (p
, posix_path
, append_slash
);
1051 posix_path
[0] = '/';
1052 posix_path
[1] = '\0';
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
);
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
);
1072 debug_printf ("%s = conv_to_posix_path (%s)", posix_path
, src_path
);
1079 while (*in
== ' ' || *in
== '\t')
1087 while (*in
&& *in
!= ' ' && *in
!= '\t')
1093 conv_fstab_spaces (char *field
)
1096 while ((sp
= strstr (sp
, "\\040")) != NULL
)
1099 memmove (sp
, sp
+ 3, strlen (sp
+ 3) + 1);
1111 {"acl", MOUNT_NOACL
, 1},
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},
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}
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
);
1141 fstab_read_flags (char **options
, unsigned &flags
, bool external
)
1147 char *p
= strchr (*options
, ',');
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
);
1160 system_printf ("invalid fstab option - '%s'", *options
);
1178 for (o
= oopts
; o
< (oopts
+ (sizeof (oopts
) / sizeof (oopts
[0]))); o
++)
1179 len
+= strlen (o
->name
) + 1;
1180 char *buf
= (char *) malloc (len
);
1184 for (o
= oopts
; o
< (oopts
+ (sizeof (oopts
) / sizeof (oopts
[0]))); o
++)
1186 bp
= stpcpy (bp
, o
->name
);
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
== '#')
1203 char *cend
= find_ws (c
);
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);
1215 posix_path
= conv_fstab_spaces (c
);
1216 /* Third field: FS type. */
1217 c
= skip_ws (cend
+ 1);
1223 /* Forth field: Flags. */
1224 c
= skip_ws (cend
+ 1);
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))
1236 if (mount_flags
& MOUNT_BIND
)
1238 /* Prepend root path to bound path. */
1239 char *bound_path
= native_path
;
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
)
1246 if ((mount_flags
& ~MOUNT_SYSTEM
) == MOUNT_BIND
)
1247 mount_flags
= (MOUNT_BIND
| flags
)
1248 & ~(MOUNT_IMMUTABLE
| MOUNT_AUTOMATIC
);
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
))
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
)
1276 int res
= mount_table
->add_item (native_path
, posix_path
, mount_flags
);
1277 if (res
&& get_errno () == EMFILE
)
1284 mount_info::from_fstab (bool user
, WCHAR fstab
[], PWCHAR fstab_end
)
1286 UNICODE_STRING upath
;
1287 OBJECT_ATTRIBUTES attr
;
1290 char *buf
= tp
.c_get ();
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
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
))
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]))))
1328 /* Don't allow overriding of a system cygdrive prefix. */
1329 if (cygdrive_flags
& MOUNT_SYSTEM
)
1335 slashify (cygdrive_prefix
, cygdrive
, 1);
1336 cygdrive_flags
= flags
& ~MOUNT_SYSTEM
;
1337 cygdrive_len
= strlen (cygdrive
);
1343 mount_info::get_cygdrive_info (char *user
, char *system
, char *user_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
;
1360 strcpy (path
, cygdrive
);
1361 /* Strip trailing slash for backward compatibility. */
1362 if (cygdrive_len
> 2)
1363 path
[cygdrive_len
- 1] = '\0';
1366 strcpy (flags
, (cygdrive_flags
& MOUNT_TEXT
) ? "textmode" : "binmode");
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
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
;
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
);
1398 /* need to select between user and system mount to same POSIX path */
1399 if (!(bp
->flags
& MOUNT_SYSTEM
)) /* user mount */
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
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
;
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
);
1434 /* need to select between user and system mount to same POSIX path */
1435 if (!(bp
->flags
& MOUNT_SYSTEM
)) /* user mount */
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
)
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
)))
1481 nativeerr
= normalize_win32_path (native
, nativetmp
, nativetail
);
1483 if (posix
== NULL
|| !isabspath (posix
) ||
1484 is_unc_share (posix
) || isdrive (posix
))
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
);
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. */
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
))
1518 if ((mount
[i
].flags
& MOUNT_SYSTEM
) != (mountflags
& MOUNT_SYSTEM
))
1520 else if (!(mount
[i
].flags
& MOUNT_IMMUTABLE
))
1522 else if (mountflags
& MOUNT_OVERRIDE
)
1524 mountflags
|= MOUNT_IMMUTABLE
;
1535 if (i
== nmounts
&& nmounts
== MAX_MOUNTS
)
1544 if (strcmp (posixtmp
, "/usr/bin") == 0)
1547 if (strcmp (posixtmp
, "/usr/lib") == 0)
1550 if (posixtmp
[0] == '/' && posixtmp
[1] == '\0' && !(mountflags
& MOUNT_CYGDRIVE
))
1553 mount
[i
].init (nativetmp
, posixtmp
, mountflags
);
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
)
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
))
1581 if (is_unc_share (path
) || strpbrk (path
, ":\\"))
1582 backslashify (path
, pathtmp
, 0);
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() */
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
)
1603 nmounts
--; /* One less mount table entry */
1604 /* Fill in the hole if not at the end of the table */
1606 memmove (mount
+ ent
, mount
+ ent
+ 1,
1607 sizeof (mount
[ent
]) * (nmounts
- ent
));
1608 sort (); /* Resort the table */
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
[] = {
1628 { "netapp", false },
1629 { "iso9660", true },
1631 { "csc-cache", false },
1632 { "unixfs", false },
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
);
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. */
1672 UNICODE_STRING unat
;
1674 get_nt_native_path (native_path
, unat
, false);
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
);
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");
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
;
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
)
1761 __small_sprintf (native_path
, "%c:\\", cyg_toupper (drive
));
1762 if (GetFileAttributes (native_path
) == INVALID_FILE_ATTRIBUTES
)
1764 _my_tls
.locals
.available_drives
&= ~mask
;
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
);
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. */
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
);
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. */
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? */
1819 else if (strpbrk (posix_path
, "\\:"))
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
);
1829 else if (!*win32_path
)
1833 char *w32_path
= (char *) win32_path
;
1834 if (flags
& MOUNT_BIND
)
1836 /* Prepend root path to bound path. */
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
,
1846 if (error
|| strlen (w32_path
) >= MAX_PATH
)
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
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
) {}
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
1872 umount (const char *path
)
1881 return cygwin_umount (path
, 0);
1883 __except (EFAULT
) {}
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
1893 cygwin_umount (const char *path
, unsigned flags
)
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
);
1904 #define is_dev(d,s) wcsncmp((d),(s),sizeof(s) - 1)
1907 get_disk_type (LPCWSTR dos
)
1909 WCHAR dev
[MAX_PATH
], *d
= dev
;
1910 if (!QueryDosDeviceW (dos
, dev
, MAX_PATH
))
1912 if (is_dev (dev
, L
"\\Device\\"))
1918 if (is_dev (d
, L
"CdRom"))
1922 if (is_dev (d
, L
"Floppy"))
1926 if (is_dev (d
, L
"Harddisk"))
1930 if (is_dev (d
, L
"LanmanRedirector\\"))
1931 return DT_SHARE_SMB
;
1934 if (is_dev (d
, L
"MRxNfs\\"))
1935 return DT_SHARE_NFS
;
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
*
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
;
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 *)"";
1983 mntbuf
->mnt_type
= tmplen
> 0 ? buf
+ fsname_len
+ dir_len
: (char *)"";
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
;
1997 dos_drive_mappings::dos_drive_mappings ()
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
2008 HANDLE sh
= FindFirstVolumeW (vol
, 64);
2009 if (sh
== INVALID_HANDLE_VALUE
)
2010 debug_printf ("FindFirstVolumeW, %E");
2014 /* Skip drives which are not mounted. */
2016 if (!GetVolumePathNamesForVolumeNameW (vol
, mounts
, NT_MAX_PATH
, &len
)
2017 || mounts
[0] == L
'\0')
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
;
2032 RtlInitUnicodeString (&upath
, devpath
);
2033 InitializeObjectAttributes (&attr
, &upath
,
2034 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
2035 status
= NtOpenSymbolicLinkObject (&h
, SYMBOLIC_LINK_QUERY
,
2037 if (!NT_SUCCESS (status
))
2039 RtlInitEmptyUnicodeString (&upath
, devpath
, (NT_MAX_PATH
- 1)
2041 status
= NtQuerySymbolicLinkObject (h
, &upath
, NULL
);
2043 if (!NT_SUCCESS (status
))
2045 devpath
[upath
.Length
/ sizeof (WCHAR
)] = L
'\0';
2047 mapping
*m
= new mapping ();
2050 m
->dospath
= wcsdup (mounts
);
2051 m
->ntdevpath
= wcsdup (devpath
);
2052 if (!m
->dospath
|| !m
->ntdevpath
)
2055 free (m
->ntdevpath
);
2059 m
->doslen
= wcslen (m
->dospath
);
2060 m
->dospath
[--m
->doslen
] = L
'\0'; /* Drop trailing backslash */
2061 m
->ntlen
= wcslen (m
->ntdevpath
);
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
);
2076 dos_drive_mappings::fixup_if_match (wchar_t *path
)
2078 /* Check for network drive first. */
2079 if (!wcsncmp (path
, L
"\\Device\\Mup\\", 12))
2085 /* Then test local drives. */
2086 for (mapping
*m
= mappings
; m
; m
= m
->next
)
2087 if (!wcsncmp (m
->ntdevpath
, path
, m
->ntlen
))
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
);
2103 dos_drive_mappings::~dos_drive_mappings ()
2106 for (mapping
*m
= mappings
; m
; m
= n
)
2110 free (m
->ntdevpath
);