OSDN Git Service

* mount.cc (fs_info::update): Set has_buggy_reopen for Netapps as well.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / mount.cc
1 /* mount.cc: mount handling.
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include "miscfuncs.h"
14 #include <mntent.h>
15 #include <ctype.h>
16 #include <winioctl.h>
17 #include <wingdi.h>
18 #include <winuser.h>
19 #include <winnetwk.h>
20 #include <shlobj.h>
21 #include <cygwin/version.h>
22 #include "cygerrno.h"
23 #include "security.h"
24 #include "path.h"
25 #include "shared_info.h"
26 #include "fhandler.h"
27 #include "dtable.h"
28 #include "cygheap.h"
29 #include "cygtls.h"
30 #include "tls_pbuf.h"
31 #include <ntdll.h>
32 #include <wchar.h>
33 #include <stdio.h>
34 #include <assert.h>
35
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))
39
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]))
44
45 #define isproc(path) \
46   (path_prefix_p (proc, (path), proc_len, false))
47
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;
51
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. */
56
57 static inline bool __stdcall
58 is_native_path (const char *path)
59 {
60   return isdirsep (path[0])
61          && (isdirsep (path[1]) || path[1] == '?')
62          && (path[2] == '?' || path[2] == '.')
63          && isdirsep (path[3])
64          && isalpha (path[4]);
65 }
66
67 static inline bool __stdcall
68 is_unc_share (const char *path)
69 {
70   const char *p;
71   return (isdirsep (path[0])
72          && isdirsep (path[1])
73          && isalnum (path[2])
74          && ((p = strpbrk (path + 3, "\\/")) != NULL)
75          && isalnum (p[1]));
76 }
77
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. */
81
82 static bool
83 win32_device_name (const char *src_path, char *win32_path, device& dev)
84 {
85   dev.parse (src_path);
86   if (dev == FH_FS || dev == FH_DEV)
87     return false;
88   strcpy (win32_path, dev.native);
89   return true;
90 }
91
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
98 #pragma pack(push,4)
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];
105 };
106 #pragma pack(pop)
107
108 #define MAX_FS_INFO_CNT 32
109 class fs_info_cache
110 {
111   static muto fsi_lock;
112   uint32_t count;
113   struct {
114     fs_info fsi;
115     uint32_t hash;
116   } entry[MAX_FS_INFO_CNT];
117
118   uint32_t genhash (PFILE_FS_VOLUME_INFORMATION);
119
120 public:
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 *);
124 };
125
126 static fs_info_cache fsi_cache;
127 muto NO_COPY fs_info_cache::fsi_lock;
128
129 uint32_t
130 fs_info_cache::genhash (PFILE_FS_VOLUME_INFORMATION pffvi)
131 {
132   uint32_t hash = 0;
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! */
138   while (p < end)
139     hash = *p++ + (hash << 6) + (hash << 16) - hash;
140   return hash;
141 }
142
143 fs_info *
144 fs_info_cache::search (PFILE_FS_VOLUME_INFORMATION pffvi, uint32_t &hash)
145 {
146   hash = genhash (pffvi);
147   for (uint32_t i = 0; i < count; ++i)
148     if (entry[i].hash == hash)
149       return &entry[i].fsi;
150   return NULL;
151 }
152
153 void
154 fs_info_cache::add (uint32_t hashval, fs_info *new_fsi)
155 {
156   fsi_lock.acquire ();
157   if (count < MAX_FS_INFO_CNT)
158     {
159       entry[count].fsi = *new_fsi;
160       entry[count].hash = hashval;
161       ++count;
162     }
163   fsi_lock.release ();
164 }
165
166 bool
167 fs_info::update (PUNICODE_STRING upath, HANDLE in_vol)
168 {
169   NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
170   HANDLE vol;
171   OBJECT_ATTRIBUTES attr;
172   IO_STATUS_BLOCK io;
173   bool no_media = false;
174   FILE_FS_DEVICE_INFORMATION ffdi;
175   FILE_FS_OBJECTID_INFORMATION ffoi;
176   struct {
177     FILE_FS_ATTRIBUTE_INFORMATION ffai;
178     WCHAR buf[NAME_MAX + 1];
179   } ffai_buf;
180   struct {
181     FILE_FS_VOLUME_INFORMATION ffvi;
182     WCHAR buf[NAME_MAX + 1];
183   } ffvi_buf;
184   UNICODE_STRING fsname;
185
186   clear ();
187   if (in_vol)
188     vol = in_vol;
189   else
190     {
191       ULONG access = READ_CONTROL;
192       /* Always caseinsensitive.  We really just need access to the drive. */
193       InitializeObjectAttributes (&attr, upath, OBJ_CASE_INSENSITIVE, NULL,
194                                   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)
203         {
204           access |= FILE_READ_DATA;
205           status = NtOpenFile (&vol, access, &attr, &io, FILE_SHARE_VALID_FLAGS,
206                                FILE_OPEN_FOR_BACKUP_INTENT);
207         }
208       while (!NT_SUCCESS (status)
209              && (attr.ObjectName->Length > 7 * sizeof (WCHAR)
210                  || status == STATUS_NO_MEDIA_IN_DEVICE))
211         {
212           UNICODE_STRING dir;
213           RtlSplitUnicodePath (attr.ObjectName, &dir, NULL);
214           attr.ObjectName = &dir;
215           if (status == STATUS_NO_MEDIA_IN_DEVICE)
216             {
217               no_media = true;
218               dir.Length = 6 * sizeof (WCHAR);
219             }
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);
224         }
225       if (!NT_SUCCESS (status))
226         {
227           debug_printf ("Cannot access path %S, status %08lx",
228                         attr.ObjectName, status);
229           return false;
230         }
231     }
232   sernum = 0;
233   status = NtQueryVolumeInformationFile (vol, &io, &ffvi_buf.ffvi,
234                                          sizeof ffvi_buf,
235                                          FileFsVolumeInformation);
236   uint32_t hash = 0;
237   if (NT_SUCCESS (status))
238     {
239       fs_info *fsi = fsi_cache.search (&ffvi_buf.ffvi, hash);
240       if (fsi)
241         {
242           *this = *fsi;
243           if (!in_vol)
244             NtClose (vol);
245           return true;
246         }
247       sernum = ffvi_buf.ffvi.VolumeSerialNumber;
248     }
249   status = NtQueryVolumeInformationFile (vol, &io, &ffdi, sizeof ffdi,
250                                          FileFsDeviceInformation);
251   if (!NT_SUCCESS (status))
252     ffdi.DeviceType = ffdi.Characteristics = 0;
253
254   if ((ffdi.Characteristics & FILE_REMOTE_DEVICE)
255       || (!ffdi.DeviceType
256           && RtlEqualUnicodePathPrefix (attr.ObjectName, &ro_u_uncp, TRUE)))
257     is_remote_drive (true);
258
259   if (!no_media)
260     status = NtQueryVolumeInformationFile (vol, &io, &ffai_buf.ffai,
261                                            sizeof ffai_buf,
262                                            FileFsAttributeInformation);
263   if (no_media || !NT_SUCCESS (status))
264     {
265       debug_printf ("Cannot get volume attributes (%S), %08lx",
266                     attr.ObjectName, status);
267       if (!in_vol)
268         NtClose (vol);
269       return false;
270     }
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 ())
276     {
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))
287
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. */
291
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)
323
324       if ((flags () & FILE_SUPPORTS_OBJECT_IDS)
325           && NT_SUCCESS (NtQueryVolumeInformationFile (vol, &io, &ffoi,
326                                                    sizeof ffoi,
327                                                    FileFsObjectIdInformation)))
328         {
329           smb_extended_info *extended_info = (smb_extended_info *)
330                                              &ffoi.ExtendedInfo;
331           if (extended_info->samba_magic == SAMBA_EXTENDED_INFO_MAGIC)
332             {
333               is_samba (true);
334               samba_version (extended_info->samba_version);
335             }
336         }
337       /* First check the remote filesystems claiming to be NTFS. */
338       if (!got_fs ()
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
349          type for now. */
350       if (!got_fs ()
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. */
355       if (!got_fs ()
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)))
369         {
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 ());
374         }
375       if (got_fs ())
376         {
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
380              any remote CIFS.  */
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 ());
400         }
401     }
402   if (!got_fs ()
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));
408   if (!got_fs ())
409     {
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));
414       strlwr (fsn);
415     }
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.
421
422      NFS doesn't set the FILE_CASE_SENSITIVE_SEARCH flag but is case
423      sensitive.
424
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
427      but:
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 ())
433                     && !is_nfs ())
434                    || (is_udf () && wincap.has_broken_udf ()));
435
436   if (!in_vol)
437     NtClose (vol);
438   fsi_cache.add (hash, this);
439   return true;
440 }
441
442 inline void
443 mount_info::create_root_entry (const PWCHAR root)
444 {
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)
452       < 0)
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);
460 }
461
462 /* init: Initialize the mount table.  */
463
464 void
465 mount_info::init ()
466 {
467   PWCHAR pathend;
468   WCHAR path[PATH_MAX];
469
470   pathend = wcpcpy (path, installation_root);
471   create_root_entry (path);
472   pathend = wcpcpy (pathend, L"\\etc\\fstab");
473
474   from_fstab (false, path, pathend);
475   from_fstab (true, path, pathend);
476
477   if (!got_usr_bin || !got_usr_lib)
478     {
479       char native[PATH_MAX];
480       if (root_idx < 0)
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);
483       if (!got_usr_bin)
484       {
485         stpcpy (p, "\\bin");
486         add_item (native, "/usr/bin",
487                   MOUNT_SYSTEM | MOUNT_BINARY | MOUNT_AUTOMATIC);
488       }
489       if (!got_usr_lib)
490       {
491         stpcpy (p, "\\lib");
492         add_item (native, "/usr/lib",
493                   MOUNT_SYSTEM | MOUNT_BINARY | MOUNT_AUTOMATIC);
494       }
495     }
496 }
497
498 static void
499 set_flags (unsigned *flags, unsigned val)
500 {
501   *flags = val;
502   if (!(*flags & PATH_BINARY))
503     {
504       *flags |= PATH_TEXT;
505       debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
506     }
507   else
508     {
509       *flags |= PATH_BINARY;
510       debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
511     }
512 }
513
514 int
515 mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
516 {
517   int n, err = 0;
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] != '/')
522     {
523       n = native_pathlen;
524       real_native_path = native_path;
525       real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
526     }
527   else
528     {
529       n = cygheap->root.native_length ();
530       real_native_path = cygheap->root.native_path ();
531       real_posix_pathlen = posix_pathlen;
532     }
533   memcpy (dst, real_native_path, n + 1);
534   const char *p = src + real_posix_pathlen;
535   if (*p == '/')
536     /* nothing */;
537   else if ((isdrive (dst) && !dst[2]) || *p)
538     dst[n++] = '\\';
539   if ((n + strlen (p)) >= NT_MAX_PATH)
540     err = ENAMETOOLONG;
541   else
542     backslashify (p, dst + n, 0);
543   return err;
544 }
545
546 /* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
547    the result in win32_path.
548
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.
552
553    If full_win32_path != NULL, the full path is stored there.
554
555    The result is zero for success, or an errno value.
556
557    {,full_}win32_path must have sufficient space (i.e. NT_MAX_PATH bytes).  */
558
559 int
560 mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
561                                 unsigned *flags)
562 {
563   bool chroot_ok = !cygheap->root.exists ();
564
565   MALLOC_CHECK;
566
567   dev = FH_FS;
568
569   *flags = 0;
570   debug_printf ("conv_to_win32_path (%s)", src_path);
571
572   int i, rc;
573   mount_item *mi = NULL;        /* initialized to avoid compiler warning */
574
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
577      stuff.
578
579      eg mounting c:/foo /foo
580      d:/bar /bar
581
582      cd /bar
583      ls ../foo
584
585      should look in c:/foo, not d:/foo.
586
587      converting normalizex UNIX path to a DOS-style path, looking up the
588      appropriate drive in the mount table.  */
589
590   /* See if this is a cygwin "device" */
591   if (win32_device_name (src_path, dst, dev))
592     {
593       *flags = MOUNT_BINARY;    /* FIXME: Is this a sensible default for devices? */
594       rc = 0;
595       goto out_no_chroot_check;
596     }
597
598   MALLOC_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
601      device. */
602   if (src_path[1] == '/')
603     {
604       if (!strchr (src_path + 2, '/'))
605         {
606           dev = *netdrive_dev;
607           set_flags (flags, PATH_BINARY);
608         }
609       else
610         {
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);
617         }
618       backslashify (src_path, dst, 0);
619       /* Go through chroot check */
620       goto out;
621     }
622   if (isproc (src_path))
623     {
624       dev = *proc_dev;
625       dev = fhandler_proc::get_proc_fhandler (src_path);
626       if (dev == FH_NADA)
627         return ENOENT;
628       set_flags (flags, PATH_BINARY);
629       if (isprocsys_dev (dev))
630         {
631           if (src_path[procsys_len])
632             backslashify (src_path + procsys_len, dst, 0);
633           else  /* Avoid empty NT path. */
634             stpcpy (dst, "\\");
635           set_flags (flags, (unsigned) cygdrive_flags);
636         }
637       else
638         strcpy (dst, src_path);
639       goto out;
640     }
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))
644     {
645       int n = mount_table->cygdrive_len - 1;
646       int unit;
647
648       if (!src_path[n])
649         {
650           unit = 0;
651           dst[0] = '\0';
652           if (mount_table->cygdrive_len > 1)
653             dev = *cygdrive_dev;
654         }
655       else if (cygdrive_win32_path (src_path, dst, unit))
656         {
657           set_flags (flags, (unsigned) cygdrive_flags);
658           goto out;
659         }
660       else if (mount_table->cygdrive_len > 1)
661         return ENOENT;
662     }
663
664   int chroot_pathlen;
665   chroot_pathlen = 0;
666   /* Check the mount table for prefix matches. */
667   for (i = 0; i < nmounts; i++)
668     {
669       const char *path;
670       int len;
671
672       mi = mount + posix_sorted[i];
673       if (!cygheap->root.exists ()
674           || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
675         {
676           path = mi->posix_path;
677           len = mi->posix_pathlen;
678         }
679       else if (cygheap->root.posix_ok (mi->posix_path))
680         {
681           path = cygheap->root.unchroot (mi->posix_path);
682           chroot_pathlen = len = strlen (path);
683         }
684       else
685         {
686           chroot_pathlen = 0;
687           continue;
688         }
689
690       if (path_prefix_p (path, src_path, len, mi->flags & MOUNT_NOPOSIX))
691         break;
692     }
693
694   if (i < nmounts)
695     {
696       int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
697       if (err)
698         return err;
699       chroot_ok = true;
700     }
701   else
702     {
703       int offset = 0;
704       if (src_path[1] != '/' && src_path[1] != ':')
705         offset = cygheap->cwd.get_drive (dst);
706       backslashify (src_path, dst + offset, 0);
707     }
708  out:
709   MALLOC_CHECK;
710   if (chroot_ok || cygheap->root.ischroot_native (dst))
711     rc = 0;
712   else
713     {
714       debug_printf ("attempt to access outside of chroot '%s - %s'",
715                     cygheap->root.posix_path (), cygheap->root.native_path ());
716       rc = ENOENT;
717     }
718
719  out_no_chroot_check:
720   debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
721   return rc;
722 }
723
724 int
725 mount_info::get_mounts_here (const char *parent_dir, int parent_dir_len,
726                              PUNICODE_STRING mount_points,
727                              PUNICODE_STRING cygd)
728 {
729   int n_mounts = 0;
730
731   for (int i = 0; i < nmounts; i++)
732     {
733       mount_item *mi = mount + posix_sorted[i];
734       char *last_slash = strrchr (mi->posix_path, '/');
735       if (!last_slash)
736         continue;
737       if (last_slash == mi->posix_path)
738         {
739           if (parent_dir_len == 1 && mi->posix_pathlen > 1)
740             RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
741                                               last_slash + 1);
742         }
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++],
746                                           last_slash + 1);
747     }
748   RtlCreateUnicodeStringFromAsciiz (cygd, cygdrive + 1);
749   if (cygd->Length)
750     cygd->Length -= 2;  // Strip trailing slash
751   return n_mounts;
752 }
753
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. */
757
758 void
759 mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
760 {
761   int len = cygdrive_len;
762
763   memcpy (dst, cygdrive, len + 1);
764
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]))
770     dst[len++] = '\000';
771   else
772     {
773       int n;
774       dst[len++] = '/';
775       if (isdirsep (src[2]))
776         n = 3;
777       else
778         n = 2;
779       strcpy (dst + len, src + n);
780     }
781   slashify (dst, dst, trailing_slash_p);
782 }
783
784 int
785 mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
786 {
787   int res;
788   const char *p = src + cygdrive_len;
789   if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
790     {
791       unit = -1; /* FIXME: should be zero, maybe? */
792       dst[0] = '\0';
793       res = 0;
794     }
795   else
796     {
797       /* drive letter must always be uppercase for casesensitive native NT. */
798       dst[0] = cyg_toupper (*p);
799       dst[1] = ':';
800       strcpy (dst + 2, p + 1);
801       backslashify (dst, dst, !dst[2]);
802       unit = dst[0];
803       res = 1;
804     }
805   debug_printf ("src '%s', dst '%s'", src, dst);
806   return res;
807 }
808
809 /* conv_to_posix_path: Ensure src_path is a POSIX path.
810
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.  */
814
815 /* TODO: Change conv_to_posix_path to work with native paths. */
816
817 /* src_path is a wide Win32 path. */
818 int
819 mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path,
820                                 int keep_rel_p)
821 {
822   bool changed = false;
823   if (!wcsncmp (src_path, L"\\\\?\\", 4))
824     {
825       src_path += 4;
826       if (src_path[1] != L':') /* native UNC path */
827         {
828           *(src_path += 2) = L'\\';
829           changed = true;
830         }
831     }
832   tmp_pathbuf tp;
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);
836   if (changed)
837     src_path[0] = L'C';
838   return ret;
839 }
840
841 int
842 mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
843                                 int keep_rel_p)
844 {
845   int src_path_len = strlen (src_path);
846   int relative_path_p = !isabspath (src_path);
847   int trailing_slash_p;
848
849   if (src_path_len <= 1)
850     trailing_slash_p = 0;
851   else
852     {
853       const char *lastchar = src_path + src_path_len - 1;
854       trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
855     }
856
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");
860   MALLOC_CHECK;
861
862   if (src_path_len >= NT_MAX_PATH)
863     {
864       debug_printf ("ENAMETOOLONG");
865       return ENAMETOOLONG;
866     }
867
868   /* FIXME: For now, if the path is relative and it's supposed to stay
869      that way, skip mount table processing. */
870
871   if (keep_rel_p && relative_path_p)
872     {
873       slashify (src_path, posix_path, 0);
874       debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
875       return 0;
876     }
877
878   tmp_pathbuf tp;
879   char *pathbuf = tp.c_get ();
880   char *tail;
881   int rc = normalize_win32_path (src_path, pathbuf, tail);
882   if (rc != 0)
883     {
884       debug_printf ("%d = conv_to_posix_path(%s)", rc, src_path);
885       return rc;
886     }
887
888   int pathbuflen = tail - pathbuf;
889   for (int i = 0; i < nmounts; ++i)
890     {
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))
894         continue;
895
896       if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
897         continue;
898
899       /* SRC_PATH is in the mount table. */
900       int nextchar;
901       const char *p = pathbuf + mi.native_pathlen;
902
903       if (!*p || !p[1])
904         nextchar = 0;
905       else if (isdirsep (*p))
906         nextchar = -1;
907       else
908         nextchar = 1;
909
910       int addslash = nextchar > 0 ? 1 : 0;
911       if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= NT_MAX_PATH)
912         return ENAMETOOLONG;
913       strcpy (posix_path, mi.posix_path);
914       if (addslash)
915         strcat (posix_path, "/");
916       if (nextchar)
917         slashify (p,
918                   posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
919                   trailing_slash_p);
920
921       if (cygheap->root.exists ())
922         {
923           const char *p = cygheap->root.unchroot (posix_path);
924           memmove (posix_path, p, strlen (p) + 1);
925         }
926       goto out;
927     }
928
929   if (!cygheap->root.exists ())
930     /* nothing */;
931   else if (!cygheap->root.ischroot_native (pathbuf))
932     return ENOENT;
933   else
934     {
935       const char *p = pathbuf + cygheap->root.native_length ();
936       if (*p)
937         slashify (p, posix_path, trailing_slash_p);
938       else
939         {
940           posix_path[0] = '/';
941           posix_path[1] = '\0';
942         }
943       goto out;
944     }
945
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);
953   else
954     {
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);
958     }
959
960 out:
961   debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
962   MALLOC_CHECK;
963   return 0;
964 }
965
966 /* Return flags associated with a mount point given the win32 path. */
967
968 unsigned
969 mount_info::set_flags_from_win32_path (const char *p)
970 {
971   for (int i = 0; i < nmounts; i++)
972     {
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))
976         return mi.flags;
977     }
978   return PATH_BINARY;
979 }
980
981 inline char *
982 skip_ws (char *in)
983 {
984   while (*in == ' ' || *in == '\t')
985     ++in;
986   return in;
987 }
988
989 inline char *
990 find_ws (char *in)
991 {
992   while (*in && *in != ' ' && *in != '\t')
993     ++in;
994   return in;
995 }
996
997 inline char *
998 conv_fstab_spaces (char *field)
999 {
1000   register char *sp = field;
1001   while ((sp = strstr (sp, "\\040")) != NULL)
1002     {
1003       *sp++ = ' ';
1004       memmove (sp, sp + 3, strlen (sp + 3) + 1);
1005     }
1006   return field;
1007 }
1008
1009 struct opt
1010 {
1011   const char *name;
1012   unsigned val;
1013   bool clear;
1014 } oopts[] =
1015 {
1016   {"acl", MOUNT_NOACL, 1},
1017   {"auto", 0, 0},
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},
1025   {"nosuid", 0, 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}
1033 };
1034
1035 static int
1036 compare_flags (const void *a, const void *b)
1037 {
1038   const opt *oa = (const opt *) a;
1039   const opt *ob = (const opt *) b;
1040
1041   return strcmp (oa->name, ob->name);
1042 }
1043
1044 extern "C" bool
1045 fstab_read_flags (char **options, unsigned &flags, bool external)
1046 {
1047   opt key;
1048
1049   while (**options)
1050     {
1051       char *p = strchr (*options, ',');
1052       if (p)
1053         *p++ = '\0';
1054       else
1055         p = strchr (*options, '\0');
1056
1057       key.name = *options;
1058       opt *o = (opt *) bsearch (&key, oopts,
1059                                 sizeof oopts / sizeof (opt),
1060                                 sizeof (opt), compare_flags);
1061       if (!o)
1062         {
1063           if (!external)
1064             system_printf ("invalid fstab option - '%s'", *options);
1065           return false;
1066         }
1067       if (o->clear)
1068         flags &= ~o->val;
1069       else
1070         flags |= o->val;
1071       *options = p;
1072     }
1073   return true;
1074 }
1075
1076 extern "C" char *
1077 fstab_list_flags ()
1078 {
1079   size_t len = 0;
1080   opt *o;
1081
1082   for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1083     len += strlen (o->name) + 1;
1084   char *buf = (char *) malloc (len);
1085   if (buf)
1086     {
1087       char *bp = buf;
1088       for (o = oopts; o < (oopts + (sizeof (oopts) / sizeof (oopts[0]))); o++)
1089         {
1090           bp = stpcpy (bp, o->name);
1091           *bp++ = ',';
1092         }
1093       *--bp = '\0';
1094     }
1095   return buf;
1096 }
1097
1098 bool
1099 mount_info::from_fstab_line (char *line, bool user)
1100 {
1101   char *native_path, *posix_path, *fs_type;
1102
1103   /* First field: Native path. */
1104   char *c = skip_ws (line);
1105   if (!*c || *c == '#')
1106     return true;
1107   char *cend = find_ws (c);
1108   *cend = '\0';
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);
1115   if (!*c)
1116     return true;
1117   cend = find_ws (c);
1118   *cend = '\0';
1119   posix_path = conv_fstab_spaces (c);
1120   /* Third field: FS type. */
1121   c = skip_ws (cend + 1);
1122   if (!*c)
1123     return true;
1124   cend = find_ws (c);
1125   *cend = '\0';
1126   fs_type = c;
1127   /* Forth field: Flags. */
1128   c = skip_ws (cend + 1);
1129   if (!*c)
1130     return true;
1131   cend = find_ws (c);
1132   *cend = '\0';
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))
1137     return true;
1138   if (mount_flags & MOUNT_BIND)
1139     {
1140       /* Prepend root path to bound path. */
1141       char *bound_path = native_path;
1142       device dev;
1143       unsigned flags = 0;
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)
1147         return true;
1148       if ((mount_flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY))
1149         mount_flags = (MOUNT_BIND | flags)
1150                       & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1151     }
1152   if (user)
1153     mount_flags &= ~MOUNT_SYSTEM;
1154   if (!strcmp (fs_type, "cygdrive"))
1155     {
1156       cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
1157       slashify (posix_path, cygdrive, 1);
1158       cygdrive_len = strlen (cygdrive);
1159     }
1160   else
1161     {
1162       int res = mount_table->add_item (native_path, posix_path, mount_flags);
1163       if (res && get_errno () == EMFILE)
1164         return false;
1165     }
1166   return true;
1167 }
1168
1169 bool
1170 mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
1171 {
1172   UNICODE_STRING upath;
1173   OBJECT_ATTRIBUTES attr;
1174   IO_STATUS_BLOCK io;
1175   NTSTATUS status;
1176   HANDLE fh;
1177
1178   if (user)
1179     {
1180       PWCHAR username;
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
1185          the rules. */
1186       transform_chars (username, username + wcslen (username) - 1);
1187     }
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))
1196     {
1197       debug_printf ("NtOpenFile(%S) failed, %p", &upath, status);
1198       return false;
1199     }
1200
1201   char buf[NT_MAX_PATH];
1202   char *got = buf;
1203   DWORD len = 0;
1204   unsigned line = 1;
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)))
1208     {
1209       char *end;
1210
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. */
1215       len += got - buf;
1216       /* Reset got to start reading at the start of the buffer again. */
1217       got = buf;
1218 retry:
1219       bool got_nl = false;
1220       while (got < buf + len && (end = strchr (got, '\n')))
1221         {
1222           got_nl = true;
1223           end[end[-1] == '\r' ? -1 : 0] = '\0';
1224           if (!from_fstab_line (got, user))
1225             goto done;
1226           got = end + 1;
1227           ++line;
1228         }
1229       if (len < (sizeof (buf) - 2))
1230         break;
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. */
1234       if (!got_nl)
1235         {
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)))
1239             {
1240               len = io.Information;
1241               buf[len] = buf[len + 1] = '\0';
1242               got = strchr (buf, '\n');
1243               if (got)
1244                 {
1245                   ++got;
1246                   ++line;
1247                   goto retry;
1248                 }
1249             }
1250           got = buf;
1251           break;
1252         }
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);
1258       got = buf + len;
1259       buf[len] = buf[len + 1] = '\0';
1260     }
1261   /* Catch a last line without trailing \n. */
1262   if (got > buf)
1263     from_fstab_line (got, user);
1264 done:
1265   NtClose (fh);
1266   return true;
1267 }
1268
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. */
1272
1273 int
1274 mount_info::write_cygdrive_info (const char *cygdrive_prefix, unsigned flags)
1275 {
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]))))
1281     {
1282       set_errno (EINVAL);
1283       return -1;
1284     }
1285   /* Don't allow overriding of a system cygdrive prefix. */
1286   if (cygdrive_flags & MOUNT_SYSTEM)
1287     {
1288       set_errno (EPERM);
1289       return -1;
1290     }
1291
1292   slashify (cygdrive_prefix, cygdrive, 1);
1293   cygdrive_flags = flags & ~MOUNT_SYSTEM;
1294   cygdrive_len = strlen (cygdrive);
1295
1296   return 0;
1297 }
1298
1299 int
1300 mount_info::get_cygdrive_info (char *user, char *system, char *user_flags,
1301                                char *system_flags)
1302 {
1303   if (user)
1304     *user = '\0';
1305   if (system)
1306     *system = '\0';
1307   if (user_flags)
1308     *user_flags = '\0';
1309   if (system_flags)
1310     *system_flags = '\0';
1311
1312   char *path = (cygdrive_flags & MOUNT_SYSTEM) ? system : user;
1313   char *flags = (cygdrive_flags & MOUNT_SYSTEM) ? system_flags : user_flags;
1314
1315   if (path)
1316     {
1317       strcpy (path, cygdrive);
1318       /* Strip trailing slash for backward compatibility. */
1319       if (cygdrive_len > 2)
1320         path[cygdrive_len - 1] = '\0';
1321     }
1322   if (flags)
1323     strcpy (flags, (cygdrive_flags & MOUNT_BINARY) ? "binmode" : "textmode");
1324   return 0;
1325 }
1326
1327 static mount_item *mounts_for_sort;
1328
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??? */
1333 static int
1334 sort_by_posix_name (const void *a, const void *b)
1335 {
1336   mount_item *ap = mounts_for_sort + (*((int*) a));
1337   mount_item *bp = mounts_for_sort + (*((int*) b));
1338
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);
1343
1344   int res = blen - alen;
1345
1346   if (res)
1347     return res;         /* Path lengths differed */
1348
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);
1352
1353   if (res == 0)
1354    {
1355      /* need to select between user and system mount to same POSIX path */
1356      if (!(bp->flags & MOUNT_SYSTEM))   /* user mount */
1357       return 1;
1358      else
1359       return -1;
1360    }
1361
1362   return res;
1363 }
1364
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??? */
1369 static int
1370 sort_by_native_name (const void *a, const void *b)
1371 {
1372   mount_item *ap = mounts_for_sort + (*((int*) a));
1373   mount_item *bp = mounts_for_sort + (*((int*) b));
1374
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);
1379
1380   int res = blen - alen;
1381
1382   if (res)
1383     return res;         /* Path lengths differed */
1384
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);
1388
1389   if (res == 0)
1390    {
1391      /* need to select between user and system mount to same POSIX path */
1392      if (!(bp->flags & MOUNT_SYSTEM))   /* user mount */
1393       return 1;
1394      else
1395       return -1;
1396    }
1397
1398   return res;
1399 }
1400
1401 void
1402 mount_info::sort ()
1403 {
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);
1411 }
1412
1413 /* Add an entry to the mount table.
1414    Returns 0 on success, -1 on failure and errno is set.
1415
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.  */
1418
1419 int
1420 mount_info::add_item (const char *native, const char *posix,
1421                       unsigned mountflags)
1422 {
1423   tmp_pathbuf tp;
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;
1430
1431   /* Something's wrong if either path is NULL or empty, or if it's
1432      not a UNC or absolute path. */
1433
1434   if (native == NULL || !isabspath (native) ||
1435       !(is_native_path (native) || is_unc_share (native) || isdrive (native)))
1436     nativeerr = EINVAL;
1437   else
1438     nativeerr = normalize_win32_path (native, nativetmp, nativetail);
1439
1440   if (posix == NULL || !isabspath (posix) ||
1441       is_unc_share (posix) || isdrive (posix))
1442     posixerr = EINVAL;
1443   else
1444     posixerr = normalize_posix_path (posix, posixtmp, posixtail);
1445
1446   debug_printf ("%s[%s], %s[%s], %p",
1447                 native, nativeerr ? error : nativetmp,
1448                 posix, posixerr ? error : posixtmp, mountflags);
1449
1450   if (nativeerr || posixerr)
1451     {
1452       set_errno (nativeerr ?: posixerr);
1453       return -1;
1454     }
1455
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';
1461
1462   /* Write over an existing mount item with the same POSIX path if
1463      it exists and is from the same registry area. */
1464   int i;
1465   for (i = 0; i < nmounts; i++)
1466     {
1467       if (!strcmp (mount[i].posix_path, posixtmp))
1468         {
1469           /* Don't allow overriding of a system mount with a user mount. */
1470           if ((mount[i].flags & MOUNT_SYSTEM) && !(mountflags & MOUNT_SYSTEM))
1471             {
1472               set_errno (EPERM);
1473               return -1;
1474             }
1475           if ((mount[i].flags & MOUNT_SYSTEM) != (mountflags & MOUNT_SYSTEM))
1476             continue;
1477           else if (!(mount[i].flags & MOUNT_IMMUTABLE))
1478             break;
1479           else if (mountflags & MOUNT_OVERRIDE)
1480             {
1481               mountflags |= MOUNT_IMMUTABLE;
1482               break;
1483             }
1484           else
1485             {
1486               set_errno (EPERM);
1487               return -1;
1488             }
1489         }
1490     }
1491
1492   if (i == nmounts && nmounts == MAX_MOUNTS)
1493     {
1494       set_errno (EMFILE);
1495       return -1;
1496     }
1497
1498   if (i == nmounts)
1499     nmounts++;
1500
1501   if (strcmp (posixtmp, "/usr/bin") == 0)
1502     got_usr_bin = true;
1503
1504   if (strcmp (posixtmp, "/usr/lib") == 0)
1505     got_usr_lib = true;
1506
1507   if (posixtmp[0] == '/' && posixtmp[1] == '\0' && !(mountflags & MOUNT_CYGDRIVE))
1508     root_idx = i;
1509
1510   mount[i].init (nativetmp, posixtmp, mountflags);
1511   sort ();
1512
1513   return 0;
1514 }
1515
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.
1522 */
1523
1524 int
1525 mount_info::del_item (const char *path, unsigned flags)
1526 {
1527   tmp_pathbuf tp;
1528   char *pathtmp = tp.c_get ();
1529   int posix_path_p = false;
1530
1531   /* Something's wrong if path is NULL or empty. */
1532   if (path == NULL || *path == 0 || !isabspath (path))
1533     {
1534       set_errno (EINVAL);
1535       return -1;
1536     }
1537
1538   if (is_unc_share (path) || strpbrk (path, ":\\"))
1539     backslashify (path, pathtmp, 0);
1540   else
1541     {
1542       slashify (path, pathtmp, 0);
1543       posix_path_p = true;
1544     }
1545   nofinalslash (pathtmp, pathtmp);
1546
1547   for (int i = 0; i < nmounts; i++)
1548     {
1549       int ent = native_sorted[i]; /* in the same order as getmntent() */
1550       if (((posix_path_p)
1551            ? !strcmp (mount[ent].posix_path, pathtmp)
1552            : strcasematch (mount[ent].native_path, pathtmp)))
1553         {
1554           /* Don't allow removal of a system mount. */
1555           if (mount[ent].flags & MOUNT_SYSTEM)
1556             {
1557               set_errno (EPERM);
1558               return -1;
1559             }
1560           nmounts--; /* One less mount table entry */
1561           /* Fill in the hole if not at the end of the table */
1562           if (ent < nmounts)
1563             memmove (mount + ent, mount + ent + 1,
1564                      sizeof (mount[ent]) * (nmounts - ent));
1565           sort (); /* Resort the table */
1566           return 0;
1567         }
1568     }
1569   set_errno (EINVAL);
1570   return -1;
1571 }
1572
1573 /************************* mount_item class ****************************/
1574
1575 /* Order must be identical to mount.h, enum fs_info_type. */
1576 fs_names_t fs_names[] = {
1577     { "none", false },
1578     { "vfat", true },
1579     { "ntfs", true },
1580     { "smbfs", false },
1581     { "nfs", false },
1582     { "netapp", false },
1583     { "iso9660", true },
1584     { "udf", true },
1585     { "csc-cache", false },
1586     { "sunwnfs", false },
1587     { "unixfs", false },
1588     { "mvfs", false },
1589     { "cifs", false },
1590     { "nwfs", false },
1591     { "ncfsd", false }
1592 };
1593
1594 static mntent *
1595 fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
1596 {
1597   struct mntent& ret=_my_tls.locals.mntbuf;
1598   bool append_bs = false;
1599
1600   /* Remove drivenum from list if we see a x: style path */
1601   if (strlen (native_path) == 2 && native_path[1] == ':')
1602     {
1603       int drivenum = cyg_tolower (native_path[0]) - 'a';
1604       if (drivenum >= 0 && drivenum <= 31)
1605         _my_tls.locals.available_drives &= ~(1 << drivenum);
1606       append_bs = true;
1607     }
1608
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. */
1613
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;
1617
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. */
1621
1622   fs_info mntinfo;
1623   tmp_pathbuf tp;
1624   UNICODE_STRING unat;
1625   tp.u_get (&unat);
1626   get_nt_native_path (native_path, unat, false);
1627   if (append_bs)
1628     RtlAppendUnicodeToString (&unat, L"\\");
1629   mntinfo.update (&unat, NULL);
1630
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);
1633   else
1634     strcpy (_my_tls.locals.mnt_type, mntinfo.fsname ());
1635
1636   ret.mnt_type = _my_tls.locals.mnt_type;
1637
1638   slashify (native_path, _my_tls.locals.mnt_fsname, false);
1639
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. */
1643
1644   if (!(flags & MOUNT_BINARY))
1645     strcpy (_my_tls.locals.mnt_opts, (char *) "text");
1646   else
1647     strcpy (_my_tls.locals.mnt_opts, (char *) "binary");
1648
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");
1655
1656   if (flags & MOUNT_NOACL)
1657     strcat (_my_tls.locals.mnt_opts, (char *) ",noacl");
1658
1659   if (flags & MOUNT_DOS)
1660     strcat (_my_tls.locals.mnt_opts, (char *) ",dos");
1661
1662   if (flags & MOUNT_IHASH)
1663     strcat (_my_tls.locals.mnt_opts, (char *) ",ihash");
1664
1665   if (flags & MOUNT_NOPOSIX)
1666     strcat (_my_tls.locals.mnt_opts, (char *) ",posix=0");
1667
1668   if (!(flags & MOUNT_SYSTEM))          /* user mount */
1669     strcat (_my_tls.locals.mnt_opts, (char *) ",user");
1670
1671   if (flags & MOUNT_CYGDRIVE)           /* cygdrive */
1672     strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
1673
1674   if (flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
1675     strcat (_my_tls.locals.mnt_opts, (char *) ",auto");
1676
1677   if (flags & (MOUNT_BIND))
1678     strcat (_my_tls.locals.mnt_opts, (char *) ",bind");
1679
1680   ret.mnt_opts = _my_tls.locals.mnt_opts;
1681
1682   ret.mnt_freq = 1;
1683   ret.mnt_passno = 1;
1684   return &ret;
1685 }
1686
1687 struct mntent *
1688 mount_item::getmntent ()
1689 {
1690   return fillout_mntent (native_path, posix_path, flags);
1691 }
1692
1693 static struct mntent *
1694 cygdrive_getmntent ()
1695 {
1696   char native_path[4];
1697   char posix_path[CYG_MAX_PATH];
1698   DWORD mask = 1, drive = 'a';
1699   struct mntent *ret = NULL;
1700
1701   while (_my_tls.locals.available_drives)
1702     {
1703       for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
1704         if (_my_tls.locals.available_drives & mask)
1705           break;
1706
1707       __small_sprintf (native_path, "%c:\\", cyg_toupper (drive));
1708       if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
1709         {
1710           _my_tls.locals.available_drives &= ~mask;
1711           continue;
1712         }
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);
1716       break;
1717     }
1718
1719   return ret;
1720 }
1721
1722 struct mntent *
1723 mount_info::getmntent (int x)
1724 {
1725   if (x < 0 || x >= nmounts)
1726     return cygdrive_getmntent ();
1727
1728   return mount[native_sorted[x]].getmntent ();
1729 }
1730
1731 /* Fill in the fields of a mount table entry.  */
1732
1733 void
1734 mount_item::init (const char *native, const char *posix, unsigned mountflags)
1735 {
1736   strcpy ((char *) native_path, native);
1737   strcpy ((char *) posix_path, posix);
1738
1739   native_pathlen = strlen (native_path);
1740   posix_pathlen = strlen (posix_path);
1741
1742   flags = mountflags;
1743 }
1744
1745 /********************** Mount System Calls **************************/
1746
1747 /* Mount table system calls.
1748    Note that these are exported to the application.  */
1749
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. */
1753
1754 extern "C" int
1755 mount (const char *win32_path, const char *posix_path, unsigned flags)
1756 {
1757   /* FIXME: Should we disallow setting MOUNT_SYSTEM in flags since it
1758      isn't really supported except from fstab? */
1759   int res = -1;
1760
1761   myfault efault;
1762   if (efault.faulted (EFAULT))
1763     /* errno set */;
1764   else if (!*posix_path)
1765     set_errno (EINVAL);
1766   else if (strpbrk (posix_path, "\\:"))
1767     set_errno (EINVAL);
1768   else if (flags & MOUNT_CYGDRIVE) /* normal mount */
1769     {
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);
1774       win32_path = NULL;
1775     }
1776   else if (!*win32_path)
1777     set_errno (EINVAL);
1778   else
1779     {
1780       char *w32_path = (char *) win32_path;
1781       if (flags & MOUNT_BIND)
1782         {
1783           /* Prepend root path to bound path. */
1784           tmp_pathbuf tp;
1785           device dev;
1786
1787           unsigned conv_flags = 0;
1788           const char *bound_path = w32_path;
1789
1790           w32_path = tp.c_get ();
1791           int error = mount_table->conv_to_win32_path (bound_path, w32_path,
1792                                                        dev, &conv_flags);
1793           if (error || strlen (w32_path) >= MAX_PATH)
1794             return true;
1795           if ((flags & ~MOUNT_SYSTEM) == (MOUNT_BIND | MOUNT_BINARY))
1796             flags = (MOUNT_BIND | conv_flags)
1797                     & ~(MOUNT_IMMUTABLE | MOUNT_AUTOMATIC);
1798         }
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);
1802     }
1803
1804   syscall_printf ("%R = mount(%s, %s, %p)", res, win32_path, posix_path, flags);
1805   return res;
1806 }
1807
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
1811    table. */
1812
1813 extern "C" int
1814 umount (const char *path)
1815 {
1816   myfault efault;
1817   if (efault.faulted (EFAULT))
1818     return -1;
1819   if (!*path)
1820     {
1821       set_errno (EINVAL);
1822       return -1;
1823     }
1824   return cygwin_umount (path, 0);
1825 }
1826
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
1829    registry area. */
1830
1831 extern "C" int
1832 cygwin_umount (const char *path, unsigned flags)
1833 {
1834   int res = -1;
1835
1836   if (!(flags & MOUNT_CYGDRIVE))
1837     res = mount_table->del_item (path, flags & ~MOUNT_SYSTEM);
1838
1839   syscall_printf ("%R = cygwin_umount(%s, %d)", res,  path, flags);
1840   return res;
1841 }
1842
1843 bool
1844 is_floppy (const char *dos)
1845 {
1846   char dev[256];
1847   if (!QueryDosDevice (dos, dev, 256))
1848     return false;
1849   return ascii_strncasematch (dev, "\\Device\\Floppy", 14);
1850 }
1851
1852 extern "C" FILE *
1853 setmntent (const char *filep, const char *)
1854 {
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;
1863 }
1864
1865 extern "C" struct mntent *
1866 getmntent (FILE *)
1867 {
1868   return mount_table->getmntent (_my_tls.locals.iteration++);
1869 }
1870
1871 extern "C" int
1872 endmntent (FILE *)
1873 {
1874   return 1;
1875 }