1 /* mount.cc: mount handling.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include "miscfuncs.h"
21 #include <cygwin/version.h>
25 #include "shared_info.h"
36 /* Determine if path prefix matches current cygdrive */
37 #define iscygdrive(path) \
38 (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len, false))
40 #define iscygdrive_device(path) \
41 (isalpha (path[mount_table->cygdrive_len]) && \
42 (path[mount_table->cygdrive_len + 1] == '/' || \
43 !path[mount_table->cygdrive_len + 1]))
45 #define isproc(path) \
46 (path_prefix_p (proc, (path), proc_len, false))
48 bool NO_COPY mount_info::got_usr_bin;
49 bool NO_COPY mount_info::got_usr_lib;
50 int NO_COPY mount_info::root_idx = -1;
52 /* is_unc_share: Return non-zero if PATH begins with //server/share
53 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 static inline bool __stdcall
58 is_native_path (const char *path)
60 return isdirsep (path[0])
61 && (isdirsep (path[1]) || path[1] == '?')
62 && (path[2] == '?' || path[2] == '.')
67 static inline bool __stdcall
68 is_unc_share (const char *path)
71 return (isdirsep (path[0])
74 && ((p = strpbrk (path + 3, "\\/")) != NULL)
78 /* Return true if src_path is a valid, internally supported device name.
79 In that case, win32_path gets the corresponding NT device name and
80 dev is appropriately filled with device information. */
83 win32_device_name (const char *src_path, char *win32_path, device& dev)
86 if (dev == FH_FS || dev == FH_DEV)
88 strcpy (win32_path, dev.native);
92 /* Beginning with Samba 3.0.28a, Samba allows to get version information using
93 the ExtendedInfo member returned by a FileFsObjectIdInformation request.
94 We just store the samba_version information for now. Older versions than
95 3.2 are still guessed at by testing the file system flags. */
96 #define SAMBA_EXTENDED_INFO_MAGIC 0x536d4261 /* "SmBa" */
97 #define SAMBA_EXTENDED_INFO_VERSION_STRING_LENGTH 28
99 struct smb_extended_info {
100 DWORD samba_magic; /* Always SAMBA_EXTENDED_INFO_MAGIC */
101 DWORD samba_version; /* Major/Minor/Release/Revision */
102 DWORD samba_subversion; /* Prerelease/RC/Vendor patch */
103 LARGE_INTEGER samba_gitcommitdate;
104 char samba_version_string[SAMBA_EXTENDED_INFO_VERSION_STRING_LENGTH];
108 #define MAX_FS_INFO_CNT 32
111 static muto fsi_lock;
116 } entry[MAX_FS_INFO_CNT];
118 uint32_t genhash (PFILE_FS_VOLUME_INFORMATION);
121 fs_info_cache () : count (0) { fsi_lock.init ("fsi_lock"); }
122 fs_info *search (PFILE_FS_VOLUME_INFORMATION, uint32_t &);
123 void add (uint32_t, fs_info *);
126 static fs_info_cache fsi_cache;
127 muto NO_COPY fs_info_cache::fsi_lock;
130 fs_info_cache::genhash (PFILE_FS_VOLUME_INFORMATION pffvi)
133 const uint16_t *p = (const uint16_t *) pffvi;
134 const uint16_t *end = (const uint16_t *)
135 ((const uint8_t *) p + sizeof *pffvi
136 + pffvi->VolumeLabelLength - sizeof (WCHAR));
137 pffvi->__dummy = 0; /* This member can have random values! */
139 hash = *p++ + (hash << 6) + (hash << 16) - hash;
144 fs_info_cache::search (PFILE_FS_VOLUME_INFORMATION pffvi, uint32_t &hash)
146 hash = genhash (pffvi);
147 for (uint32_t i = 0; i < count; ++i)
148 if (entry[i].hash == hash)
149 return &entry[i].fsi;
154 fs_info_cache::add (uint32_t hashval, fs_info *new_fsi)
157 if (count < MAX_FS_INFO_CNT)
159 entry[count].fsi = *new_fsi;
160 entry[count].hash = hashval;
167 fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
169 NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
171 OBJECT_ATTRIBUTES attr;
173 bool no_media = false;
174 FILE_FS_DEVICE_INFORMATION ffdi;
175 FILE_FS_OBJECTID_INFORMATION ffoi;
177 FILE_FS_ATTRIBUTE_INFORMATION ffai;
178 WCHAR buf[NAME_MAX + 1];
181 FILE_FS_VOLUME_INFORMATION ffvi;
182 WCHAR buf[NAME_MAX + 1];
184 UNICODE_STRING fsname;
191 ULONG access = READ_CONTROL;
192 /* Always caseinsensitive. We really just need access to the drive. */
193 InitializeObjectAttributes (&attr, upath, OBJ_CASE_INSENSITIVE, NULL,
195 /* Note: Don't use the FILE_OPEN_REPARSE_POINT flag here. The reason
196 is that symlink_info::check relies on being able to open a handle
197 to the target of a volume mount point. */
198 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
199 FILE_OPEN_FOR_BACKUP_INTENT);
200 /* At least one filesystem (HGFS, VMware shared folders) doesn't like
201 to be opened with access set to just READ_CONTROL. */
202 if (status == STATUS_INVALID_PARAMETER)
204 access |= FILE_READ_DATA;
205 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
206 FILE_OPEN_FOR_BACKUP_INTENT);
208 while (!NT_SUCCESS (status)
209 && (attr.ObjectName->Length > 7 * sizeof (WCHAR)
210 || status == STATUS_NO_MEDIA_IN_DEVICE))
213 RtlSplitUnicodePath (attr.ObjectName, &dir, NULL);
214 attr.ObjectName = &dir;
215 if (status == STATUS_NO_MEDIA_IN_DEVICE)
218 dir.Length = 6 * sizeof (WCHAR);
220 else if (dir.Length > 7 * sizeof (WCHAR))
221 dir.Length -= sizeof (WCHAR);
222 status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
223 FILE_OPEN_FOR_BACKUP_INTENT);
225 if (!NT_SUCCESS (status))
227 debug_printf ("Cannot access path %S, status %08lx",
228 attr.ObjectName, status);
233 status = NtQueryVolumeInformationFile (vol, &io, &ffvi_buf.ffvi,
235 FileFsVolumeInformation);
237 if (NT_SUCCESS (status))
239 fs_info *fsi = fsi_cache.search (&ffvi_buf.ffvi, hash);
247 sernum = ffvi_buf.ffvi.VolumeSerialNumber;
249 status = NtQueryVolumeInformationFile (vol, &io, &ffdi, sizeof ffdi,
250 FileFsDeviceInformation);
251 if (!NT_SUCCESS (status))
252 ffdi.DeviceType = ffdi.Characteristics = 0;
254 if ((ffdi.Characteristics & FILE_REMOTE_DEVICE)
256 && RtlEqualUnicodePathPrefix (attr.ObjectName, &ro_u_uncp, TRUE)))
257 is_remote_drive (true);
260 status = NtQueryVolumeInformationFile (vol, &io, &ffai_buf.ffai,
262 FileFsAttributeInformation);
263 if (no_media || !NT_SUCCESS (status))
265 debug_printf ("Cannot get volume attributes (%S), %08lx",
266 attr.ObjectName, status);
271 flags (ffai_buf.ffai.FileSystemAttributes);
272 name_len (ffai_buf.ffai.MaximumComponentNameLength);
273 RtlInitCountedUnicodeString (&fsname, ffai_buf.ffai.FileSystemName,
274 ffai_buf.ffai.FileSystemNameLength);
275 if (is_remote_drive ())
277 /* Should be reevaluated for each new OS. Right now this mask is valid up
278 to Vista. The important point here is to test only flags indicating
279 capabilities and to ignore flags indicating a specific state of this
280 volume. At present these flags to ignore are FILE_VOLUME_IS_COMPRESSED,
281 FILE_READ_ONLY_VOLUME, and FILE_SEQUENTIAL_WRITE_ONCE. The additional
282 filesystem flags supported since Windows 7 are also ignored for now.
283 They add information, but only on W7 and later, and only for filesystems
284 also supporting these flags, right now only NTFS. */
285 #define GETVOLINFO_VALID_MASK (0x002701ffUL)
286 #define TEST_GVI(f,m) (((f) & GETVOLINFO_VALID_MASK) == (m))
288 /* FIXME: This flag twist is getting awkward. There should really be some
289 other method. Maybe we need mount flags to allow the user to fix file
290 system problems without having to wait for a Cygwin fix. */
292 /* Volume quotas are potentially supported since Samba 3.0, object ids and
293 the unicode on disk flag since Samba 3.2. */
294 #define SAMBA_IGNORE (FILE_VOLUME_QUOTAS \
295 | FILE_SUPPORTS_OBJECT_IDS \
296 | FILE_UNICODE_ON_DISK)
297 #define FS_IS_SAMBA TEST_GVI(flags () & ~SAMBA_IGNORE, \
298 FILE_CASE_SENSITIVE_SEARCH \
299 | FILE_CASE_PRESERVED_NAMES \
300 | FILE_PERSISTENT_ACLS)
301 /* Netapp DataOnTap. */
302 #define NETAPP_IGNORE (FILE_SUPPORTS_SPARSE_FILES \
303 | FILE_PERSISTENT_ACLS)
304 #define FS_IS_NETAPP_DATAONTAP TEST_GVI(flags () & ~NETAPP_IGNORE, \
305 FILE_CASE_SENSITIVE_SEARCH \
306 | FILE_CASE_PRESERVED_NAMES \
307 | FILE_UNICODE_ON_DISK \
308 | FILE_NAMED_STREAMS)
309 /* These are the minimal flags supported by NTFS since NT4. Every filesystem
310 not supporting these flags is not a native NTFS. We subsume them under
311 the filesystem type "cifs". */
312 #define MINIMAL_WIN_NTFS_FLAGS (FILE_CASE_SENSITIVE_SEARCH \
313 | FILE_CASE_PRESERVED_NAMES \
314 | FILE_UNICODE_ON_DISK \
315 | FILE_PERSISTENT_ACLS \
316 | FILE_FILE_COMPRESSION)
317 #define FS_IS_WINDOWS_NTFS TEST_GVI(flags () & MINIMAL_WIN_NTFS_FLAGS, \
318 MINIMAL_WIN_NTFS_FLAGS)
319 /* These are the exact flags of a real Windows FAT/FAT32 filesystem.
320 Anything else is a filesystem faking to be FAT. */
321 #define WIN_FAT_FLAGS (FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK)
322 #define FS_IS_WINDOWS_FAT TEST_GVI(flags (), WIN_FAT_FLAGS)
324 if ((flags () & FILE_SUPPORTS_OBJECT_IDS)
325 && NT_SUCCESS (NtQueryVolumeInformationFile (vol, &io, &ffoi,
327 FileFsObjectIdInformation)))
329 smb_extended_info *extended_info = (smb_extended_info *)
331 if (extended_info->samba_magic == SAMBA_EXTENDED_INFO_MAGIC)
334 samba_version (extended_info->samba_version);
337 /* First check the remote filesystems claiming to be NTFS. */
339 && is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE))
340 /* Test for older Samba releases not supporting extended info. */
341 && !is_samba (FS_IS_SAMBA)
342 /* Netapp inode info is unusable, can't handle trailing dots and
343 spaces, has a bug in "move and delete" semantics. */
344 && !is_netapp (FS_IS_NETAPP_DATAONTAP))
345 /* Any other remote FS faking to be NTFS. */
346 is_cifs (!FS_IS_WINDOWS_NTFS);
347 /* Then check remote filesystems claiming to be FAT. Except for real
348 FAT and Netapp, all of them are subsumed under the "CIFS" filesystem
351 && is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))
352 && !is_netapp (FS_IS_NETAPP_DATAONTAP))
353 is_cifs (!FS_IS_WINDOWS_FAT);
354 /* Then check remote filesystems honest about their name. */
356 /* Microsoft NFS needs distinct access methods for metadata. */
357 && !is_nfs (RtlEqualUnicodeString (&fsname, &ro_u_nfs, FALSE))
358 /* MVFS == Rational ClearCase remote filesystem. Has a couple of
359 drawbacks, like not supporting DOS attributes other than R/O
360 and stuff like that. */
361 && !is_mvfs (RtlEqualUnicodePathPrefix (&fsname, &ro_u_mvfs, FALSE))
362 /* NWFS == Novell Netware FS. Broken info class, see below. */
363 /* NcFsd == Novell Netware FS via own driver since Windows Vista. */
364 && !is_nwfs (RtlEqualUnicodeString (&fsname, &ro_u_nwfs, FALSE))
365 && !is_ncfsd (RtlEqualUnicodeString (&fsname, &ro_u_ncfsd, FALSE))
366 /* UNIXFS == TotalNet Advanced Server (TAS). Doesn't support
367 FileIdBothDirectoryInformation. See below. */
368 && !is_unixfs (RtlEqualUnicodeString (&fsname, &ro_u_unixfs, FALSE)))
370 /* Known remote file system with buggy open calls. Further
371 explanation in fhandler.cc (fhandler_disk_file::open_fs). */
372 is_sunwnfs (RtlEqualUnicodeString (&fsname, &ro_u_sunwnfs, FALSE));
373 has_buggy_open (is_sunwnfs ());
377 /* UNIXFS is known to choke on FileIdBothDirectoryInformation.
378 Some other CIFS servers have problems with this call as well.
379 Know example: EMC NS-702. We just don't use that info class on
381 has_buggy_fileid_dirinfo (is_cifs () || is_unixfs ());
382 /* NWFS is known to have a broken FileBasicInformation info
383 class. It can't be used to fetch information, only to set
384 information. Therefore, for NWFS we have to fallback to the
385 FileNetworkOpenInformation info class. Unfortunately we can't
386 use FileNetworkOpenInformation all the time since that fails on
387 other filesystems like NFS.
388 UNUSED, but keep in for information purposes. */
389 has_buggy_basic_info (is_nwfs ());
390 /* Netapp and NWFS/NcFsd are too dumb to allow non-DOS filenames
391 containing trailing dots and spaces when accessed from Windows
392 clients. We subsume CIFS into this class of filesystems right
393 away since at least some of them are not capable either. */
394 has_dos_filenames_only (is_netapp () || is_nwfs ()
395 || is_ncfsd () || is_cifs ());
396 /* Netapp and NWFS don't grok re-opening a file by handle. They
397 only support this if the filename is non-null and the handle is
398 the handle to a directory. NcFsd IR10 is supposed to be ok. */
399 has_buggy_reopen (is_netapp () || is_nwfs ());
403 && !is_ntfs (RtlEqualUnicodeString (&fsname, &ro_u_ntfs, FALSE))
404 && !is_fat (RtlEqualUnicodePathPrefix (&fsname, &ro_u_fat, TRUE))
405 && !is_csc_cache (RtlEqualUnicodeString (&fsname, &ro_u_csc, FALSE))
406 && is_cdrom (ffdi.DeviceType == FILE_DEVICE_CD_ROM))
407 is_udf (RtlEqualUnicodeString (&fsname, &ro_u_udf, FALSE));
410 /* The filesystem name is only used in fillout_mntent and only if
411 the filesystem isn't one of the well-known filesystems anyway. */
412 sys_wcstombs (fsn, sizeof fsn, ffai_buf.ffai.FileSystemName,
413 ffai_buf.ffai.FileSystemNameLength / sizeof (WCHAR));
416 has_acls (flags () & FS_PERSISTENT_ACLS);
417 /* Netapp inode numbers are fly-by-night. */
418 hasgood_inode ((has_acls () && !is_netapp ()) || is_nfs ());
419 /* Case sensitivity is supported if FILE_CASE_SENSITIVE_SEARCH is set,
420 except on Samba which handles Windows clients case insensitive.
422 NFS doesn't set the FILE_CASE_SENSITIVE_SEARCH flag but is case
425 UDF on NT 5.x is broken (at least) in terms of case sensitivity.
426 The UDF driver reports the FILE_CASE_SENSITIVE_SEARCH capability
428 - Opening the root directory for query seems to work at first,
429 but the filenames in the directory listing are mutilated.
430 - When trying to open a file or directory case sensitive, the file
431 appears to be non-existant. */
432 caseinsensitive (((!(flags () & FILE_CASE_SENSITIVE_SEARCH) || is_samba ())
434 || (is_udf () && wincap.has_broken_udf ()));
438 fsi_cache.add (hash, this);
443 mount_info::create_root_entry (const PWCHAR root)
445 /* Create a default root dir derived from the location of the Cygwin DLL.
446 The entry is immutable, unless the "override" option is given in /etc/fstab. */
447 char native_root[PATH_MAX];
448 sys_wcstombs (native_root, PATH_MAX, root);
449 assert (*native_root != '\0');
450 if (add_item (native_root, "/",
451 MOUNT_SYSTEM | MOUNT_BINARY | MOUNT_IMMUTABLE | MOUNT_AUTOMATIC)
453 api_fatal ("add_item (\"%W\", \"/\", ...) failed, errno %d", native_root, errno);
454 /* Create a default cygdrive entry. Note that this is a user entry.
455 This allows to override it with mount, unless the sysadmin created
456 a cygdrive entry in /etc/fstab. */
457 cygdrive_flags = MOUNT_BINARY | MOUNT_NOPOSIX | MOUNT_CYGDRIVE;
458 strcpy (cygdrive, CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX "/");
459 cygdrive_len = strlen (cygdrive);
462 /* init: Initialize the mount table. */
468 WCHAR path[PATH_MAX];
470 pathend = wcpcpy (path, installation_root);
471 create_root_entry (path);
472 pathend = wcpcpy (pathend, L"\\etc\\fstab");
474 from_fstab (false, path, pathend);
475 from_fstab (true, path, pathend);
477 if (!got_usr_bin || !got_usr_lib)
479 char native[PATH_MAX];
481 api_fatal ("root_idx %d, user_shared magic %p, nmounts %d", root_idx, user_shared->version, nmounts);
482 char *p = stpcpy (native, mount[root_idx].native_path);
486 add_item (native, "/usr/bin",
487 MOUNT_SYSTEM | MOUNT_BINARY | MOUNT_AUTOMATIC);
492 add_item (native, "/usr/lib",
493 MOUNT_SYSTEM | MOUNT_BINARY | MOUNT_AUTOMATIC);
499 set_flags (unsigned *flags, unsigned val)
502 if (!(*flags & PATH_BINARY))
505 debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
509 *flags |= PATH_BINARY;
510 debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
515 mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
518 const char *real_native_path;
519 int real_posix_pathlen;
520 set_flags (outflags, (unsigned) flags);
521 if (!cygheap->root.exists () || posix_pathlen != 1 || posix_path[0] != '/')
524 real_native_path = native_path;
525 real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
529 n = cygheap->root.native_length ();
530 real_native_path = cygheap->root.native_path ();
531 real_posix_pathlen = posix_pathlen;
533 memcpy (dst, real_native_path, n + 1);
534 const char *p = src + real_posix_pathlen;
537 else if ((isdrive (dst) && !dst[2]) || *p)
539 if ((n + strlen (p)) >= NT_MAX_PATH)
542 backslashify (p, dst + n, 0);
546 /* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
547 the result in win32_path.
549 If win32_path != NULL, the relative path, if possible to keep, is
550 stored in win32_path. If the relative path isn't possible to keep,
551 the full path is stored.
553 If full_win32_path != NULL, the full path is stored there.
555 The result is zero for success, or an errno value.
557 {,full_}win32_path must have sufficient space (i.e. NT_MAX_PATH bytes). */
560 mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
563 bool chroot_ok = !cygheap->root.exists ();
570 debug_printf ("conv_to_win32_path (%s)", src_path);
573 mount_item *mi = NULL; /* initialized to avoid compiler warning */
575 /* The path is already normalized, without ../../ stuff, we need to have this
576 so that we can move from one mounted directory to another with relative
579 eg mounting c:/foo /foo
585 should look in c:/foo, not d:/foo.
587 converting normalizex UNIX path to a DOS-style path, looking up the
588 appropriate drive in the mount table. */
590 /* See if this is a cygwin "device" */
591 if (win32_device_name (src_path, dst, dev))
593 *flags = MOUNT_BINARY; /* FIXME: Is this a sensible default for devices? */
595 goto out_no_chroot_check;
599 /* If the path is on a network drive or a //./ resp. //?/ path prefix,
600 bypass the mount table. If it's // or //MACHINE, use the netdrive
602 if (src_path[1] == '/')
604 if (!strchr (src_path + 2, '/'))
607 set_flags (flags, PATH_BINARY);
611 /* For UNC paths, use the cygdrive prefix flags as default setting.
612 This is more natural since UNC paths, just like cygdrive paths,
613 are rather (warning, poetic description ahead) windows into the
614 native Win32 world. This also gives the user an elegant way to
615 change the settings for those paths in a central place. */
616 set_flags (flags, (unsigned) cygdrive_flags);
618 backslashify (src_path, dst, 0);
619 /* Go through chroot check */
622 if (isproc (src_path))
625 dev = fhandler_proc::get_proc_fhandler (src_path);
628 set_flags (flags, PATH_BINARY);
629 if (isprocsys_dev (dev))
631 if (src_path[procsys_len])
632 backslashify (src_path + procsys_len, dst, 0);
633 else /* Avoid empty NT path. */
635 set_flags (flags, (unsigned) cygdrive_flags);
638 strcpy (dst, src_path);
641 /* Check if the cygdrive prefix was specified. If so, just strip
642 off the prefix and transform it into an MS-DOS path. */
643 else if (iscygdrive (src_path))
645 int n = mount_table->cygdrive_len - 1;
652 if (mount_table->cygdrive_len > 1)
655 else if (cygdrive_win32_path (src_path, dst, unit))
657 set_flags (flags, (unsigned) cygdrive_flags);
660 else if (mount_table->cygdrive_len > 1)
666 /* Check the mount table for prefix matches. */
667 for (i = 0; i < nmounts; i++)
672 mi = mount + posix_sorted[i];
673 if (!cygheap->root.exists ()
674 || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
676 path = mi->posix_path;
677 len = mi->posix_pathlen;
679 else if (cygheap->root.posix_ok (mi->posix_path))
681 path = cygheap->root.unchroot (mi->posix_path);
682 chroot_pathlen = len = strlen (path);
690 if (path_prefix_p (path, src_path, len, mi->flags & MOUNT_NOPOSIX))
696 int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
704 if (src_path[1] != '/' && src_path[1] != ':')
705 offset = cygheap->cwd.get_drive (dst);
706 backslashify (src_path, dst + offset, 0);
710 if (chroot_ok || cygheap->root.ischroot_native (dst))
714 debug_printf ("attempt to access outside of chroot '%s - %s'",
715 cygheap->root.posix_path (), cygheap->root.native_path ());
720 debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
725 mount_info::get_mounts_here (const char *parent_dir, int parent_dir_len,
726 PUNICODE_STRING mount_points,
727 PUNICODE_STRING cygd)
731 for (int i = 0; i < nmounts; i++)
733 mount_item *mi = mount + posix_sorted[i];
734 char *last_slash = strrchr (mi->posix_path, '/');
737 if (last_slash == mi->posix_path)
739 if (parent_dir_len == 1 && mi->posix_pathlen > 1)
740 RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
743 else if (parent_dir_len == last_slash - mi->posix_path
744 && strncasematch (parent_dir, mi->posix_path, parent_dir_len))
745 RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
748 RtlCreateUnicodeStringFromAsciiz (cygd, cygdrive + 1);
750 cygd->Length -= 2; // Strip trailing slash
754 /* cygdrive_posix_path: Build POSIX path used as the
755 mount point for cygdrives created when there is no other way to
756 obtain a POSIX path from a Win32 one. */
759 mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
761 int len = cygdrive_len;
763 memcpy (dst, cygdrive, len + 1);
765 /* Now finish the path off with the drive letter to be used.
766 The cygdrive prefix always ends with a trailing slash so
767 the drive letter is added after the path. */
768 dst[len++] = cyg_tolower (src[0]);
769 if (!src[2] || (isdirsep (src[2]) && !src[3]))
775 if (isdirsep (src[2]))
779 strcpy (dst + len, src + n);
781 slashify (dst, dst, trailing_slash_p);
785 mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
788 const char *p = src + cygdrive_len;
789 if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
791 unit = -1; /* FIXME: should be zero, maybe? */
797 /* drive letter must always be uppercase for casesensitive native NT. */
798 dst[0] = cyg_toupper (*p);
800 strcpy (dst + 2, p + 1);
801 backslashify (dst, dst, !dst[2]);
805 debug_printf ("src '%s', dst '%s'", src, dst);
809 /* conv_to_posix_path: Ensure src_path is a POSIX path.
811 The result is zero for success, or an errno value.
812 posix_path must have sufficient space (i.e. NT_MAX_PATH bytes).
813 If keep_rel_p is non-zero, relative paths stay that way. */
815 /* TODO: Change conv_to_posix_path to work with native paths. */
817 /* src_path is a wide Win32 path. */
819 mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path,
822 bool changed = false;
823 if (!wcsncmp (src_path, L"\\\\?\\", 4))
826 if (src_path[1] != L':') /* native UNC path */
828 *(src_path += 2) = L'\\';
833 char *buf = tp.c_get ();
834 sys_wcstombs (buf, NT_MAX_PATH, src_path);
835 int ret = conv_to_posix_path (buf, posix_path, keep_rel_p);
842 mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
845 int src_path_len = strlen (src_path);
846 int relative_path_p = !isabspath (src_path);
847 int trailing_slash_p;
849 if (src_path_len <= 1)
850 trailing_slash_p = 0;
853 const char *lastchar = src_path + src_path_len - 1;
854 trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
857 debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
858 keep_rel_p ? "keep-rel" : "no-keep-rel",
859 trailing_slash_p ? "add-slash" : "no-add-slash");
862 if (src_path_len >= NT_MAX_PATH)
864 debug_printf ("ENAMETOOLONG");
868 /* FIXME: For now, if the path is relative and it's supposed to stay
869 that way, skip mount table processing. */
871 if (keep_rel_p && relative_path_p)
873 slashify (src_path, posix_path, 0);
874 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
879 char *pathbuf = tp.c_get ();
881 int rc = normalize_win32_path (src_path, pathbuf, tail);
884 debug_printf ("%d = conv_to_posix_path(%s)", rc, src_path);
888 int pathbuflen = tail - pathbuf;
889 for (int i = 0; i < nmounts; ++i)
891 mount_item &mi = mount[native_sorted[i]];
892 if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen,
893 mi.flags & MOUNT_NOPOSIX))
896 if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
899 /* SRC_PATH is in the mount table. */
901 const char *p = pathbuf + mi.native_pathlen;
905 else if (isdirsep (*p))
910 int addslash = nextchar > 0 ? 1 : 0;
911 if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= NT_MAX_PATH)
913 strcpy (posix_path, mi.posix_path);
915 strcat (posix_path, "/");
918 posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
921 if (cygheap->root.exists ())
923 const char *p = cygheap->root.unchroot (posix_path);
924 memmove (posix_path, p, strlen (p) + 1);
929 if (!cygheap->root.exists ())
931 else if (!cygheap->root.ischroot_native (pathbuf))
935 const char *p = pathbuf + cygheap->root.native_length ();
937 slashify (p, posix_path, trailing_slash_p);
941 posix_path[1] = '\0';
946 /* Not in the database. This should [theoretically] only happen if either
947 the path begins with //, or / isn't mounted, or the path has a drive
948 letter not covered by the mount table. If it's a relative path then the
949 caller must want an absolute path (otherwise we would have returned
950 above). So we always return an absolute path at this point. */
951 if (isdrive (pathbuf))
952 cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
955 /* The use of src_path and not pathbuf here is intentional.
956 We couldn't translate the path, so just ensure no \'s are present. */
957 slashify (src_path, posix_path, trailing_slash_p);
961 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
966 /* Return flags associated with a mount point given the win32 path. */
969 mount_info::set_flags_from_win32_path (const char *p)
971 for (int i = 0; i < nmounts; i++)
973 mount_item &mi = mount[native_sorted[i]];
974 if (path_prefix_p (mi.native_path, p, mi.native_pathlen,
975 mi.flags & MOUNT_NOPOSIX))
984 while (*in == ' ' || *in == '\t')
992 while (*in && *in != ' ' && *in != '\t')
998 conv_fstab_spaces (char *field)
1000 register char *sp = field;
1001 while ((sp = strstr (sp, "\\040")) != NULL)
1004 memmove (sp, sp + 3, strlen (sp + 3) + 1);
1016 {"acl", MOUNT_NOACL, 1},
1018 {"binary", MOUNT_BINARY, 0},
1019 {"bind", MOUNT_BIND, 0},
1020 {"cygexec", MOUNT_CYGWIN_EXEC, 0},
1021 {"dos", MOUNT_DOS, 0},
1022 {"exec", MOUNT_EXEC, 0},
1023 {"ihash", MOUNT_IHASH, 0},
1024 {"noacl", MOUNT_NOACL, 0},
1026 {"notexec", MOUNT_NOTEXEC, 0},
1027 {"nouser", MOUNT_SYSTEM, 0},
1028 {"override", MOUNT_OVERRIDE, 0},
1029 {"posix=0", MOUNT_NOPOSIX, 0},
1030 {"posix=1", MOUNT_NOPOSIX, 1},
1031 {"text", MOUNT_BINARY, 1},
1032 {"user", MOUNT_SYSTEM, 1}
1036 compare_flags (const void *a, const void *b)
1038 const opt *oa = (const opt *) a;
1039 const opt *ob = (const opt *) b;
1041 return strcmp (oa->name, ob->name);
1045 fstab_read_flags (char **options, unsigned &flags, bool external)
1051 char *p = strchr (*options, ',');
1055 p = strchr (*options, '\0');
1057 key.name = *options;
1058 opt *o = (opt *) bsearch (&key, oopts,
1059 sizeof oopts / sizeof (opt),
1060 sizeof (opt), compare_flags);
1064 system_printf ("invalid fstab option - '%s'", *options);
1082 for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1083 len += strlen (o->name) + 1;
1084 char *buf = (char *) malloc (len);
1088 for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1090 bp = stpcpy (bp, o->name);
1099 mount_info::from_fstab_line (char *line, bool user)
1101 char *native_path, *posix_path, *fs_type;
1103 /* First field: Native path. */
1104 char *c = skip_ws (line);
1105 if (!*c || *c == '#')
1107 char *cend = find_ws (c);
1109 native_path = conv_fstab_spaces (c);
1110 /* Always convert drive letter to uppercase for case sensitivity. */
1111 if (isdrive (native_path))
1112 native_path[0] = cyg_toupper (native_path[0]);
1113 /* Second field: POSIX path. */
1114 c = skip_ws (cend + 1);
1119 posix_path = conv_fstab_spaces (c);
1120 /* Third field: FS type. */
1121 c = skip_ws (cend + 1);
1127 /* Forth field: Flags. */
1128 c = skip_ws (cend + 1);
1133 unsigned mount_flags = MOUNT_SYSTEM | MOUNT_BINARY;
1134 if (!strcmp (fs_type, "cygdrive"))
1135 mount_flags |= MOUNT_NOPOSIX;
1136 if (!fstab_read_flags (&c, mount_flags, false))
1138 if (mount_flags & MOUNT_BIND)
1140 /* Prepend root path to bound path. */
1141 char *bound_path = native_path;
1144 native_path = (char *) alloca (PATH_MAX);
1145 int error = conv_to_win32_path (bound_path, native_path, dev, &flags);
1146 if (error || strlen (native_path) >= MAX_PATH)
1148 if ((mount_flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY))
1149 mount_flags = (MOUNT_BIND | flags)
1150 & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1153 mount_flags &= ~MOUNT_SYSTEM;
1154 if (!strcmp (fs_type, "cygdrive"))
1156 cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
1157 slashify (posix_path, cygdrive, 1);
1158 cygdrive_len = strlen (cygdrive);
1162 int res = mount_table->add_item (native_path, posix_path, mount_flags);
1163 if (res && get_errno () == EMFILE)
1170 mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
1172 UNICODE_STRING upath;
1173 OBJECT_ATTRIBUTES attr;
1181 sys_mbstowcs (username = wcpcpy (fstab_end, L".d\\"),
1182 NT_MAX_PATH - (fstab_end - fstab),
1183 cygheap->user.name ());
1184 /* Make sure special chars in the username are converted according to
1186 transform_chars (username, username + wcslen (username) - 1);
1188 RtlInitUnicodeString (&upath, fstab);
1189 InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
1190 debug_printf ("Try to read mounts from %W", fstab);
1191 status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io,
1192 FILE_SHARE_VALID_FLAGS,
1193 FILE_SYNCHRONOUS_IO_NONALERT
1194 | FILE_OPEN_FOR_BACKUP_INTENT);
1195 if (!NT_SUCCESS (status))
1197 debug_printf ("NtOpenFile(%S) failed, %p", &upath, status);
1201 char buf[NT_MAX_PATH];
1205 /* Using buffer size - 2 leaves space to append two \0. */
1206 while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, got,
1207 (sizeof (buf) - 2) - (got - buf), NULL, NULL)))
1211 len = io.Information;
1212 /* Set end marker. */
1213 got[len] = got[len + 1] = '\0';
1214 /* Set len to the absolute len of bytes in buf. */
1216 /* Reset got to start reading at the start of the buffer again. */
1219 bool got_nl = false;
1220 while (got < buf + len && (end = strchr (got, '\n')))
1223 end[end[-1] == '\r' ? -1 : 0] = '\0';
1224 if (!from_fstab_line (got, user))
1229 if (len < (sizeof (buf) - 2))
1231 /* Check if the buffer contained at least one \n. If not, the
1232 line length is > 32K. We don't take such long lines. Print
1233 a debug message and skip this line entirely. */
1236 system_printf ("%W: Line %d too long, skipping...", fstab, line);
1237 while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, buf,
1238 (sizeof (buf) - 2), NULL, NULL)))
1240 len = io.Information;
1241 buf[len] = buf[len + 1] = '\0';
1242 got = strchr (buf, '\n');
1253 /* We have to read once more. Move remaining bytes to the start of
1254 the buffer and reposition got so that it points to the end of
1255 the remaining bytes. */
1256 len = buf + len - got;
1257 memmove (buf, got, len);
1259 buf[len] = buf[len + 1] = '\0';
1261 /* Catch a last line without trailing \n. */
1263 from_fstab_line (got, user);
1269 /* write_cygdrive_info: Store default prefix and flags
1270 to use when creating cygdrives to the special user shared mem
1271 location used to store cygdrive information. */
1274 mount_info::write_cygdrive_info (const char *cygdrive_prefix, unsigned flags)
1276 /* Verify cygdrive prefix starts with a forward slash and if there's
1277 another character, it's not a slash. */
1278 if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
1279 (!isslash (cygdrive_prefix[0])) ||
1280 ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
1285 /* Don't allow overriding of a system cygdrive prefix. */
1286 if (cygdrive_flags & MOUNT_SYSTEM)
1292 slashify (cygdrive_prefix, cygdrive, 1);
1293 cygdrive_flags = flags & ~MOUNT_SYSTEM;
1294 cygdrive_len = strlen (cygdrive);
1300 mount_info::get_cygdrive_info (char *user, char *system, char *user_flags,
1310 *system_flags = '\0';
1312 char *path = (cygdrive_flags & MOUNT_SYSTEM) ? system : user;
1313 char *flags = (cygdrive_flags & MOUNT_SYSTEM) ? system_flags : user_flags;
1317 strcpy (path, cygdrive);
1318 /* Strip trailing slash for backward compatibility. */
1319 if (cygdrive_len > 2)
1320 path[cygdrive_len - 1] = '\0';
1323 strcpy (flags, (cygdrive_flags & MOUNT_BINARY) ? "binmode" : "textmode");
1327 static mount_item *mounts_for_sort;
1329 /* sort_by_posix_name: qsort callback to sort the mount entries. Sort
1330 user mounts ahead of system mounts to the same POSIX path. */
1331 /* FIXME: should the user should be able to choose whether to
1332 prefer user or system mounts??? */
1334 sort_by_posix_name (const void *a, const void *b)
1336 mount_item *ap = mounts_for_sort + (*((int*) a));
1337 mount_item *bp = mounts_for_sort + (*((int*) b));
1339 /* Base weighting on longest posix path first so that the most
1340 obvious path will be chosen. */
1341 size_t alen = strlen (ap->posix_path);
1342 size_t blen = strlen (bp->posix_path);
1344 int res = blen - alen;
1347 return res; /* Path lengths differed */
1349 /* The two paths were the same length, so just determine normal
1350 lexical sorted order. */
1351 res = strcmp (ap->posix_path, bp->posix_path);
1355 /* need to select between user and system mount to same POSIX path */
1356 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
1365 /* sort_by_native_name: qsort callback to sort the mount entries. Sort
1366 user mounts ahead of system mounts to the same POSIX path. */
1367 /* FIXME: should the user should be able to choose whether to
1368 prefer user or system mounts??? */
1370 sort_by_native_name (const void *a, const void *b)
1372 mount_item *ap = mounts_for_sort + (*((int*) a));
1373 mount_item *bp = mounts_for_sort + (*((int*) b));
1375 /* Base weighting on longest win32 path first so that the most
1376 obvious path will be chosen. */
1377 size_t alen = strlen (ap->native_path);
1378 size_t blen = strlen (bp->native_path);
1380 int res = blen - alen;
1383 return res; /* Path lengths differed */
1385 /* The two paths were the same length, so just determine normal
1386 lexical sorted order. */
1387 res = strcmp (ap->native_path, bp->native_path);
1391 /* need to select between user and system mount to same POSIX path */
1392 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
1404 for (int i = 0; i < nmounts; i++)
1405 native_sorted[i] = posix_sorted[i] = i;
1406 /* Sort them into reverse length order, otherwise we won't
1407 be able to look for /foo in /. */
1408 mounts_for_sort = mount; /* ouch. */
1409 qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
1410 qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
1413 /* Add an entry to the mount table.
1414 Returns 0 on success, -1 on failure and errno is set.
1416 This is where all argument validation is done. It may not make sense to
1417 do this when called internally, but it's cleaner to keep it all here. */
1420 mount_info::add_item (const char *native, const char *posix,
1421 unsigned mountflags)
1424 char *nativetmp = tp.c_get ();
1425 /* FIXME: The POSIX path is stored as value name right now, which is
1426 restricted to 256 bytes. */
1427 char posixtmp[CYG_MAX_PATH];
1428 char *nativetail, *posixtail, error[] = "error";
1429 int nativeerr, posixerr;
1431 /* Something's wrong if either path is NULL or empty, or if it's
1432 not a UNC or absolute path. */
1434 if (native == NULL || !isabspath (native) ||
1435 !(is_native_path (native) || is_unc_share (native) || isdrive (native)))
1438 nativeerr = normalize_win32_path (native, nativetmp, nativetail);
1440 if (posix == NULL || !isabspath (posix) ||
1441 is_unc_share (posix) || isdrive (posix))
1444 posixerr = normalize_posix_path (posix, posixtmp, posixtail);
1446 debug_printf ("%s[%s], %s[%s], %p",
1447 native, nativeerr ? error : nativetmp,
1448 posix, posixerr ? error : posixtmp, mountflags);
1450 if (nativeerr || posixerr)
1452 set_errno (nativeerr ?: posixerr);
1456 /* Make sure both paths do not end in /. */
1457 if (nativetail > nativetmp + 1 && nativetail[-1] == '\\')
1458 nativetail[-1] = '\0';
1459 if (posixtail > posixtmp + 1 && posixtail[-1] == '/')
1460 posixtail[-1] = '\0';
1462 /* Write over an existing mount item with the same POSIX path if
1463 it exists and is from the same registry area. */
1465 for (i = 0; i < nmounts; i++)
1467 if (!strcmp (mount[i].posix_path, posixtmp))
1469 /* Don't allow overriding of a system mount with a user mount. */
1470 if ((mount[i].flags & MOUNT_SYSTEM) && !(mountflags & MOUNT_SYSTEM))
1475 if ((mount[i].flags & MOUNT_SYSTEM) != (mountflags & MOUNT_SYSTEM))
1477 else if (!(mount[i].flags & MOUNT_IMMUTABLE))
1479 else if (mountflags & MOUNT_OVERRIDE)
1481 mountflags |= MOUNT_IMMUTABLE;
1492 if (i == nmounts && nmounts == MAX_MOUNTS)
1501 if (strcmp (posixtmp, "/usr/bin") == 0)
1504 if (strcmp (posixtmp, "/usr/lib") == 0)
1507 if (posixtmp[0] == '/' && posixtmp[1] == '\0' && !(mountflags & MOUNT_CYGDRIVE))
1510 mount[i].init (nativetmp, posixtmp, mountflags);
1516 /* Delete a mount table entry where path is either a Win32 or POSIX
1517 path. Since the mount table is really just a table of aliases,
1518 deleting / is ok (although running without a slash mount is
1519 strongly discouraged because some programs may run erratically
1520 without one). If MOUNT_SYSTEM is set in flags, remove from system
1521 registry, otherwise remove the user registry mount.
1525 mount_info::del_item (const char *path, unsigned flags)
1528 char *pathtmp = tp.c_get ();
1529 int posix_path_p = false;
1531 /* Something's wrong if path is NULL or empty. */
1532 if (path == NULL || *path == 0 || !isabspath (path))
1538 if (is_unc_share (path) || strpbrk (path, ":\\"))
1539 backslashify (path, pathtmp, 0);
1542 slashify (path, pathtmp, 0);
1543 posix_path_p = true;
1545 nofinalslash (pathtmp, pathtmp);
1547 for (int i = 0; i < nmounts; i++)
1549 int ent = native_sorted[i]; /* in the same order as getmntent() */
1551 ? !strcmp (mount[ent].posix_path, pathtmp)
1552 : strcasematch (mount[ent].native_path, pathtmp)))
1554 /* Don't allow removal of a system mount. */
1555 if (mount[ent].flags & MOUNT_SYSTEM)
1560 nmounts--; /* One less mount table entry */
1561 /* Fill in the hole if not at the end of the table */
1563 memmove (mount + ent, mount + ent + 1,
1564 sizeof (mount[ent]) * (nmounts - ent));
1565 sort (); /* Resort the table */
1573 /************************* mount_item class ****************************/
1575 /* Order must be identical to mount.h, enum fs_info_type. */
1576 fs_names_t fs_names[] = {
1582 { "netapp", false },
1583 { "iso9660", true },
1585 { "csc-cache", false },
1586 { "sunwnfs", false },
1587 { "unixfs", false },
1595 fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
1597 struct mntent& ret=_my_tls.locals.mntbuf;
1598 bool append_bs = false;
1600 /* Remove drivenum from list if we see a x: style path */
1601 if (strlen (native_path) == 2 && native_path[1] == ':')
1603 int drivenum = cyg_tolower (native_path[0]) - 'a';
1604 if (drivenum >= 0 && drivenum <= 31)
1605 _my_tls.locals.available_drives &= ~(1 << drivenum);
1609 /* Pass back pointers to mount_table strings reserved for use by
1610 getmntent rather than pointers to strings in the internal mount
1611 table because the mount table might change, causing weird effects
1612 from the getmntent user's point of view. */
1614 ret.mnt_fsname = _my_tls.locals.mnt_fsname;
1615 strcpy (_my_tls.locals.mnt_dir, posix_path);
1616 ret.mnt_dir = _my_tls.locals.mnt_dir;
1618 /* Try to give a filesystem type that matches what a Linux application might
1619 expect. Naturally, this is a moving target, but we can make some
1620 reasonable guesses for popular types. */
1624 UNICODE_STRING unat;
1626 get_nt_native_path (native_path, unat, false);
1628 RtlAppendUnicodeToString (&unat, L"\\");
1629 mntinfo.update (&unat, NULL);
1631 if (mntinfo.what_fs () > 0 && mntinfo.what_fs () < max_fs_type)
1632 strcpy (_my_tls.locals.mnt_type, fs_names[mntinfo.what_fs ()].name);
1634 strcpy (_my_tls.locals.mnt_type, mntinfo.fsname ());
1636 ret.mnt_type = _my_tls.locals.mnt_type;
1638 slashify (native_path, _my_tls.locals.mnt_fsname, false);
1640 /* mnt_opts is a string that details mount params such as
1641 binary or textmode, or exec. We don't print
1642 `silent' here; it's a magic internal thing. */
1644 if (!(flags & MOUNT_BINARY))
1645 strcpy (_my_tls.locals.mnt_opts, (char *) "text");
1647 strcpy (_my_tls.locals.mnt_opts, (char *) "binary");
1649 if (flags & MOUNT_CYGWIN_EXEC)
1650 strcat (_my_tls.locals.mnt_opts, (char *) ",cygexec");
1651 else if (flags & MOUNT_EXEC)
1652 strcat (_my_tls.locals.mnt_opts, (char *) ",exec");
1653 else if (flags & MOUNT_NOTEXEC)
1654 strcat (_my_tls.locals.mnt_opts, (char *) ",notexec");
1656 if (flags & MOUNT_NOACL)
1657 strcat (_my_tls.locals.mnt_opts, (char *) ",noacl");
1659 if (flags & MOUNT_DOS)
1660 strcat (_my_tls.locals.mnt_opts, (char *) ",dos");
1662 if (flags & MOUNT_IHASH)
1663 strcat (_my_tls.locals.mnt_opts, (char *) ",ihash");
1665 if (flags & MOUNT_NOPOSIX)
1666 strcat (_my_tls.locals.mnt_opts, (char *) ",posix=0");
1668 if (!(flags & MOUNT_SYSTEM)) /* user mount */
1669 strcat (_my_tls.locals.mnt_opts, (char *) ",user");
1671 if (flags & MOUNT_CYGDRIVE) /* cygdrive */
1672 strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
1674 if (flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
1675 strcat (_my_tls.locals.mnt_opts, (char *) ",auto");
1677 if (flags & (MOUNT_BIND))
1678 strcat (_my_tls.locals.mnt_opts, (char *) ",bind");
1680 ret.mnt_opts = _my_tls.locals.mnt_opts;
1688 mount_item::getmntent ()
1690 return fillout_mntent (native_path, posix_path, flags);
1693 static struct mntent *
1694 cygdrive_getmntent ()
1696 char native_path[4];
1697 char posix_path[CYG_MAX_PATH];
1698 DWORD mask = 1, drive = 'a';
1699 struct mntent *ret = NULL;
1701 while (_my_tls.locals.available_drives)
1703 for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
1704 if (_my_tls.locals.available_drives & mask)
1707 __small_sprintf (native_path, "%c:\\", cyg_toupper (drive));
1708 if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
1710 _my_tls.locals.available_drives &= ~mask;
1713 native_path[2] = '\0';
1714 __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
1715 ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags);
1723 mount_info::getmntent (int x)
1725 if (x < 0 || x >= nmounts)
1726 return cygdrive_getmntent ();
1728 return mount[native_sorted[x]].getmntent ();
1731 /* Fill in the fields of a mount table entry. */
1734 mount_item::init (const char *native, const char *posix, unsigned mountflags)
1736 strcpy ((char *) native_path, native);
1737 strcpy ((char *) posix_path, posix);
1739 native_pathlen = strlen (native_path);
1740 posix_pathlen = strlen (posix_path);
1745 /********************** Mount System Calls **************************/
1747 /* Mount table system calls.
1748 Note that these are exported to the application. */
1750 /* mount: Add a mount to the mount table in memory and to the registry
1751 that will cause paths under win32_path to be translated to paths
1752 under posix_path. */
1755 mount (const char *win32_path, const char *posix_path, unsigned flags)
1757 /* FIXME: Should we disallow setting MOUNT_SYSTEM in flags since it
1758 isn't really supported except from fstab? */
1762 if (efault.faulted (EFAULT))
1764 else if (!*posix_path)
1766 else if (strpbrk (posix_path, "\\:"))
1768 else if (flags & MOUNT_CYGDRIVE) /* normal mount */
1770 /* When flags include MOUNT_CYGDRIVE, take this to mean that
1771 we actually want to change the cygdrive prefix and flags
1772 without actually mounting anything. */
1773 res = mount_table->write_cygdrive_info (posix_path, flags);
1776 else if (!*win32_path)
1780 char *w32_path = (char *) win32_path;
1781 if (flags & MOUNT_BIND)
1783 /* Prepend root path to bound path. */
1787 unsigned conv_flags = 0;
1788 const char *bound_path = w32_path;
1790 w32_path = tp.c_get ();
1791 int error = mount_table->conv_to_win32_path (bound_path, w32_path,
1793 if (error || strlen (w32_path) >= MAX_PATH)
1795 if ((flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY))
1796 flags = (MOUNT_BIND | conv_flags)
1797 & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1799 /* Make sure all mounts are user mounts, even those added via mount -a. */
1800 flags &= ~MOUNT_SYSTEM;
1801 res = mount_table->add_item (w32_path, posix_path, flags);
1804 syscall_printf ("%R = mount(%s, %s, %p)", res, win32_path, posix_path, flags);
1808 /* umount: The standard umount call only has a path parameter. Since
1809 it is not possible for this call to specify whether to remove the
1810 mount from the user or global mount registry table, assume the user
1814 umount (const char *path)
1817 if (efault.faulted (EFAULT))
1824 return cygwin_umount (path, 0);
1827 /* cygwin_umount: This is like umount but takes an additional flags
1828 parameter that specifies whether to umount from the user or system-wide
1832 cygwin_umount (const char *path, unsigned flags)
1836 if (!(flags & MOUNT_CYGDRIVE))
1837 res = mount_table->del_item (path, flags & ~MOUNT_SYSTEM);
1839 syscall_printf ("%R = cygwin_umount(%s, %d)", res, path, flags);
1844 is_floppy (const char *dos)
1847 if (!QueryDosDevice (dos, dev, 256))
1849 return ascii_strncasematch (dev, "\\Device\\Floppy", 14);
1853 setmntent (const char *filep, const char *)
1855 _my_tls.locals.iteration = 0;
1856 _my_tls.locals.available_drives = GetLogicalDrives ();
1857 /* Filter floppy drives on A: and B: */
1858 if ((_my_tls.locals.available_drives & 1) && is_floppy ("A:"))
1859 _my_tls.locals.available_drives &= ~1;
1860 if ((_my_tls.locals.available_drives & 2) && is_floppy ("B:"))
1861 _my_tls.locals.available_drives &= ~2;
1862 return (FILE *) filep;
1865 extern "C" struct mntent *
1868 return mount_table->getmntent (_my_tls.locals.iteration++);