OSDN Git Service

55b276193c8ff187792592295a11a42282fef84b
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / mount.cc
1 /* path.cc: path support.
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008 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 <wingdi.h>
17 #include <winuser.h>
18 #include <winnetwk.h>
19 #include <shlobj.h>
20 #include <cygwin/version.h>
21 #include "cygerrno.h"
22 #include "security.h"
23 #include "path.h"
24 #include "fhandler.h"
25 #include "dtable.h"
26 #include "cygheap.h"
27 #include "shared_info.h"
28 #include "cygtls.h"
29 #include "tls_pbuf.h"
30 #include <ntdll.h>
31 #include <wchar.h>
32
33 /* Determine if path prefix matches current cygdrive */
34 #define iscygdrive(path) \
35   (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len, false))
36
37 #define iscygdrive_device(path) \
38   (isalpha (path[mount_table->cygdrive_len]) && \
39    (path[mount_table->cygdrive_len + 1] == '/' || \
40     !path[mount_table->cygdrive_len + 1]))
41
42 #define isproc(path) \
43   (path_prefix_p (proc, (path), proc_len, false))
44
45 /* is_unc_share: Return non-zero if PATH begins with //server/share 
46                  or with one of the native prefixes //./ or //?/ 
47    This function is only used to test for valid input strings.
48    The later normalization drops the native prefixes. */
49
50 static inline bool __stdcall
51 is_native_path (const char *path)
52 {
53   return isdirsep (path[0])
54          && (isdirsep (path[1]) || path[1] == '?')
55          && (path[2] == '?' || path[2] == '.')
56          && isdirsep (path[3])
57          && isalpha (path[4]);
58 }
59
60 static inline bool __stdcall
61 is_unc_share (const char *path)
62 {
63   const char *p;
64   return (isdirsep (path[0])
65          && isdirsep (path[1])
66          && isalnum (path[2])
67          && ((p = strpbrk (path + 3, "\\/")) != NULL)
68          && isalnum (p[1]));
69 }
70
71 /* Return true if src_path is a valid, internally supported device name.
72    In that case, win32_path gets the corresponding NT device name and
73    dev is appropriately filled with device information. */
74
75 static bool
76 win32_device_name (const char *src_path, char *win32_path, device& dev)
77 {
78   dev.parse (src_path);
79   if (dev == FH_FS || dev == FH_DEV)
80     return false;
81   strcpy (win32_path, dev.native);
82   return true;
83 }
84
85 inline void
86 mount_info::create_root_entry (const PWCHAR root)
87 {
88   /* Create a default root dir from the path the Cygwin DLL is in. */
89   char native_root[PATH_MAX];
90   sys_wcstombs (native_root, PATH_MAX, root);
91   mount_table->add_item (native_root, "/", MOUNT_SYSTEM | MOUNT_BINARY);
92   /* Create a default cygdrive entry.  Note that this is a user entry.
93      This allows to override it with mount, unless the sysadmin created
94      a cygdrive entry in /etc/fstab. */
95   cygdrive_flags = MOUNT_BINARY | MOUNT_CYGDRIVE;
96   strcpy (cygdrive, CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX "/");
97   cygdrive_len = strlen (cygdrive);
98 }
99
100 /* init: Initialize the mount table.  */
101
102 void
103 mount_info::init ()
104 {
105   nmounts = 0;
106   PWCHAR pathend;
107   WCHAR path[PATH_MAX];
108   
109   pathend = wcpcpy (path, cygwin_shared->installation_root);
110   create_root_entry (path);
111   pathend = wcpcpy (pathend, L"\\etc\\fstab");
112   if (from_fstab (false, path, pathend)   /* The single | is correct! */
113       | from_fstab (true, path, pathend))
114       return;
115
116   /* FIXME: Remove warning message before releasing 1.7.0. */
117   small_printf ("Huh?  No /etc/fstab file in %W?  Using default root and cygdrive prefix...\n", path);
118 }
119
120 static void
121 set_flags (unsigned *flags, unsigned val)
122 {
123   *flags = val;
124   if (!(*flags & PATH_BINARY))
125     {
126       *flags |= PATH_TEXT;
127       debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
128     }
129   else
130     {
131       *flags |= PATH_BINARY;
132       debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
133     }
134 }
135
136 int
137 mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
138 {
139   int n, err = 0;
140   const char *real_native_path;
141   int real_posix_pathlen;
142   set_flags (outflags, (unsigned) flags);
143   if (!cygheap->root.exists () || posix_pathlen != 1 || posix_path[0] != '/')
144     {
145       n = native_pathlen;
146       real_native_path = native_path;
147       real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
148     }
149   else
150     {
151       n = cygheap->root.native_length ();
152       real_native_path = cygheap->root.native_path ();
153       real_posix_pathlen = posix_pathlen;
154     }
155   memcpy (dst, real_native_path, n + 1);
156   const char *p = src + real_posix_pathlen;
157   if (*p == '/')
158     /* nothing */;
159   else if ((isdrive (dst) && !dst[2]) || *p)
160     dst[n++] = '\\';
161   if ((n + strlen (p)) >= NT_MAX_PATH)
162     err = ENAMETOOLONG;
163   else
164     backslashify (p, dst + n, 0);
165   return err;
166 }
167
168 /* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
169    the result in win32_path.
170
171    If win32_path != NULL, the relative path, if possible to keep, is
172    stored in win32_path.  If the relative path isn't possible to keep,
173    the full path is stored.
174
175    If full_win32_path != NULL, the full path is stored there.
176
177    The result is zero for success, or an errno value.
178
179    {,full_}win32_path must have sufficient space (i.e. NT_MAX_PATH bytes).  */
180
181 int
182 mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
183                                 unsigned *flags)
184 {
185   bool chroot_ok = !cygheap->root.exists ();
186   while (sys_mount_table_counter < cygwin_shared->sys_mount_table_counter)
187     {
188       int current = cygwin_shared->sys_mount_table_counter;
189       init ();
190       sys_mount_table_counter = current;
191     }
192   MALLOC_CHECK;
193
194   dev.devn = FH_FS;
195
196   *flags = 0;
197   debug_printf ("conv_to_win32_path (%s)", src_path);
198
199   int i, rc;
200   mount_item *mi = NULL;        /* initialized to avoid compiler warning */
201
202   /* The path is already normalized, without ../../ stuff, we need to have this
203      so that we can move from one mounted directory to another with relative
204      stuff.
205
206      eg mounting c:/foo /foo
207      d:/bar /bar
208
209      cd /bar
210      ls ../foo
211
212      should look in c:/foo, not d:/foo.
213
214      converting normalizex UNIX path to a DOS-style path, looking up the
215      appropriate drive in the mount table.  */
216
217   /* See if this is a cygwin "device" */
218   if (win32_device_name (src_path, dst, dev))
219     {
220       *flags = MOUNT_BINARY;    /* FIXME: Is this a sensible default for devices? */
221       rc = 0;
222       goto out_no_chroot_check;
223     }
224
225   MALLOC_CHECK;
226   /* If the path is on a network drive or a //./ resp.//?/ path prefix,
227      bypass the mount table.  If it's // or //MACHINE, use the netdrive
228      device. */
229   if (src_path[1] == '/')
230     {
231       if (!strchr (src_path + 2, '/'))
232         {
233           dev = *netdrive_dev;
234           set_flags (flags, PATH_BINARY);
235         }
236       backslashify (src_path, dst, 0);
237       /* Go through chroot check */
238       goto out;
239     }
240   if (isproc (src_path))
241     {
242       dev = *proc_dev;
243       dev.devn = fhandler_proc::get_proc_fhandler (src_path);
244       if (dev.devn == FH_BAD)
245         return ENOENT;
246       set_flags (flags, PATH_BINARY);
247       strcpy (dst, src_path);
248       goto out;
249     }
250   /* Check if the cygdrive prefix was specified.  If so, just strip
251      off the prefix and transform it into an MS-DOS path. */
252   else if (iscygdrive (src_path))
253     {
254       int n = mount_table->cygdrive_len - 1;
255       int unit;
256
257       if (!src_path[n])
258         {
259           unit = 0;
260           dst[0] = '\0';
261           if (mount_table->cygdrive_len > 1)
262             dev = *cygdrive_dev;
263         }
264       else if (cygdrive_win32_path (src_path, dst, unit))
265         {
266           set_flags (flags, (unsigned) cygdrive_flags);
267           goto out;
268         }
269       else if (mount_table->cygdrive_len > 1)
270         return ENOENT;
271     }
272
273   int chroot_pathlen;
274   chroot_pathlen = 0;
275   /* Check the mount table for prefix matches. */
276   for (i = 0; i < nmounts; i++)
277     {
278       const char *path;
279       int len;
280
281       mi = mount + posix_sorted[i];
282       if (!cygheap->root.exists ()
283           || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
284         {
285           path = mi->posix_path;
286           len = mi->posix_pathlen;
287         }
288       else if (cygheap->root.posix_ok (mi->posix_path))
289         {
290           path = cygheap->root.unchroot (mi->posix_path);
291           chroot_pathlen = len = strlen (path);
292         }
293       else
294         {
295           chroot_pathlen = 0;
296           continue;
297         }
298
299       if (path_prefix_p (path, src_path, len, mi->flags & MOUNT_NOPOSIX))
300         break;
301     }
302
303   if (i < nmounts)
304     {
305       int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
306       if (err)
307         return err;
308       chroot_ok = true;
309     }
310   else
311     {
312       int offset = 0;
313       if (src_path[1] != '/' && src_path[1] != ':')
314         offset = cygheap->cwd.get_drive (dst);
315       backslashify (src_path, dst + offset, 0);
316     }
317  out:
318   MALLOC_CHECK;
319   if (chroot_ok || cygheap->root.ischroot_native (dst))
320     rc = 0;
321   else
322     {
323       debug_printf ("attempt to access outside of chroot '%s - %s'",
324                     cygheap->root.posix_path (), cygheap->root.native_path ());
325       rc = ENOENT;
326     }
327
328  out_no_chroot_check:
329   debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
330   return rc;
331 }
332
333 int
334 mount_info::get_mounts_here (const char *parent_dir, int parent_dir_len,
335                              PUNICODE_STRING mount_points,
336                              PUNICODE_STRING cygd)
337 {
338   int n_mounts = 0;
339
340   for (int i = 0; i < nmounts; i++)
341     {
342       mount_item *mi = mount + posix_sorted[i];
343       char *last_slash = strrchr (mi->posix_path, '/');
344       if (!last_slash)
345         continue;
346       if (last_slash == mi->posix_path)
347         {
348           if (parent_dir_len == 1 && mi->posix_pathlen > 1)
349             RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
350                                               last_slash + 1);
351         }
352       else if (parent_dir_len == last_slash - mi->posix_path
353                && strncasematch (parent_dir, mi->posix_path, parent_dir_len))
354         RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
355                                           last_slash + 1);
356     }
357   RtlCreateUnicodeStringFromAsciiz (cygd, cygdrive + 1);
358   cygd->Length -= 2;    // Strip trailing slash
359   return n_mounts;
360 }
361
362 /* cygdrive_posix_path: Build POSIX path used as the
363    mount point for cygdrives created when there is no other way to
364    obtain a POSIX path from a Win32 one. */
365
366 void
367 mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
368 {
369   int len = cygdrive_len;
370
371   memcpy (dst, cygdrive, len + 1);
372
373   /* Now finish the path off with the drive letter to be used.
374      The cygdrive prefix always ends with a trailing slash so
375      the drive letter is added after the path. */
376   dst[len++] = cyg_tolower (src[0]);
377   if (!src[2] || (isdirsep (src[2]) && !src[3]))
378     dst[len++] = '\000';
379   else
380     {
381       int n;
382       dst[len++] = '/';
383       if (isdirsep (src[2]))
384         n = 3;
385       else
386         n = 2;
387       strcpy (dst + len, src + n);
388     }
389   slashify (dst, dst, trailing_slash_p);
390 }
391
392 int
393 mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
394 {
395   int res;
396   const char *p = src + cygdrive_len;
397   if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
398     {
399       unit = -1; /* FIXME: should be zero, maybe? */
400       dst[0] = '\0';
401       res = 0;
402     }
403   else
404     {
405       dst[0] = cyg_tolower (*p);
406       dst[1] = ':';
407       strcpy (dst + 2, p + 1);
408       backslashify (dst, dst, !dst[2]);
409       unit = dst[0];
410       res = 1;
411     }
412   debug_printf ("src '%s', dst '%s'", src, dst);
413   return res;
414 }
415
416 /* conv_to_posix_path: Ensure src_path is a POSIX path.
417
418    The result is zero for success, or an errno value.
419    posix_path must have sufficient space (i.e. NT_MAX_PATH bytes).
420    If keep_rel_p is non-zero, relative paths stay that way.  */
421
422 /* TODO: Change conv_to_posix_path to work with native paths. */
423
424 /* src_path is a wide Win32 path. */
425 int
426 mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path,
427                                 int keep_rel_p)
428 {
429   bool changed = false;
430   if (!wcsncmp (src_path, L"\\\\?\\", 4))
431     {
432       src_path += 4;
433       if (src_path[1] != L':') /* native UNC path */
434         {
435           *(src_path += 2) = L'\\';
436           changed = true;
437         }
438     }
439   tmp_pathbuf tp;
440   char *buf = tp.c_get ();
441   sys_wcstombs (buf, NT_MAX_PATH, src_path);
442   int ret = conv_to_posix_path (buf, posix_path, keep_rel_p);
443   if (changed)
444     src_path[0] = L'C';
445   return ret;
446 }
447
448 int
449 mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
450                                 int keep_rel_p)
451 {
452   int src_path_len = strlen (src_path);
453   int relative_path_p = !isabspath (src_path);
454   int trailing_slash_p;
455
456   if (src_path_len <= 1)
457     trailing_slash_p = 0;
458   else
459     {
460       const char *lastchar = src_path + src_path_len - 1;
461       trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
462     }
463
464   debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
465                 keep_rel_p ? "keep-rel" : "no-keep-rel",
466                 trailing_slash_p ? "add-slash" : "no-add-slash");
467   MALLOC_CHECK;
468
469   if (src_path_len >= NT_MAX_PATH)
470     {
471       debug_printf ("ENAMETOOLONG");
472       return ENAMETOOLONG;
473     }
474
475   /* FIXME: For now, if the path is relative and it's supposed to stay
476      that way, skip mount table processing. */
477
478   if (keep_rel_p && relative_path_p)
479     {
480       slashify (src_path, posix_path, 0);
481       debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
482       return 0;
483     }
484
485   tmp_pathbuf tp;
486   char *pathbuf = tp.c_get ();
487   char *tail;
488   int rc = normalize_win32_path (src_path, pathbuf, tail);
489   if (rc != 0)
490     {
491       debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
492       return rc;
493     }
494
495   int pathbuflen = tail - pathbuf;
496   for (int i = 0; i < nmounts; ++i)
497     {
498       mount_item &mi = mount[native_sorted[i]];
499       if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen,
500                           mi.flags & MOUNT_NOPOSIX))
501         continue;
502
503       if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
504         continue;
505
506       /* SRC_PATH is in the mount table. */
507       int nextchar;
508       const char *p = pathbuf + mi.native_pathlen;
509
510       if (!*p || !p[1])
511         nextchar = 0;
512       else if (isdirsep (*p))
513         nextchar = -1;
514       else
515         nextchar = 1;
516
517       int addslash = nextchar > 0 ? 1 : 0;
518       if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= NT_MAX_PATH)
519         return ENAMETOOLONG;
520       strcpy (posix_path, mi.posix_path);
521       if (addslash)
522         strcat (posix_path, "/");
523       if (nextchar)
524         slashify (p,
525                   posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
526                   trailing_slash_p);
527
528       if (cygheap->root.exists ())
529         {
530           const char *p = cygheap->root.unchroot (posix_path);
531           memmove (posix_path, p, strlen (p) + 1);
532         }
533       goto out;
534     }
535
536   if (!cygheap->root.exists ())
537     /* nothing */;
538   else if (!cygheap->root.ischroot_native (pathbuf))
539     return ENOENT;
540   else
541     {
542       const char *p = pathbuf + cygheap->root.native_length ();
543       if (*p)
544         slashify (p, posix_path, trailing_slash_p);
545       else
546         {
547           posix_path[0] = '/';
548           posix_path[1] = '\0';
549         }
550       goto out;
551     }
552
553   /* Not in the database.  This should [theoretically] only happen if either
554      the path begins with //, or / isn't mounted, or the path has a drive
555      letter not covered by the mount table.  If it's a relative path then the
556      caller must want an absolute path (otherwise we would have returned
557      above).  So we always return an absolute path at this point. */
558   if (isdrive (pathbuf))
559     cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
560   else
561     {
562       /* The use of src_path and not pathbuf here is intentional.
563          We couldn't translate the path, so just ensure no \'s are present. */
564       slashify (src_path, posix_path, trailing_slash_p);
565     }
566
567 out:
568   debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
569   MALLOC_CHECK;
570   return 0;
571 }
572
573 /* Return flags associated with a mount point given the win32 path. */
574
575 unsigned
576 mount_info::set_flags_from_win32_path (const char *p)
577 {
578   for (int i = 0; i < nmounts; i++)
579     {
580       mount_item &mi = mount[native_sorted[i]];
581       if (path_prefix_p (mi.native_path, p, mi.native_pathlen,
582                          mi.flags & MOUNT_NOPOSIX))
583         return mi.flags;
584     }
585   return PATH_BINARY;
586 }
587
588 inline char *
589 skip_ws (char *in)
590 {
591   while (*in == ' ' || *in == '\t')
592     ++in;
593   return in;
594 }
595
596 inline char *
597 find_ws (char *in)
598 {
599   while (*in && *in != ' ' && *in != '\t')
600     ++in;
601   return in;
602 }
603
604 inline char *
605 conv_fstab_spaces (char *field)
606 {
607   register char *sp = field;
608   while (sp = strstr (sp, "\\040"))
609     {
610       *sp++ = ' ';
611       memmove (sp, sp + 3, strlen (sp + 3) + 1);
612     }
613   return field;
614 }
615
616 struct opt
617 {   
618   const char *name;
619   unsigned val;
620   bool clear;
621 } oopts[] =
622 {
623   {"user", MOUNT_SYSTEM, 1},
624   {"nouser", MOUNT_SYSTEM, 0},
625   {"binary", MOUNT_BINARY, 0},
626   {"text", MOUNT_BINARY, 1},
627   {"exec", MOUNT_EXEC, 0},
628   {"notexec", MOUNT_NOTEXEC, 0},
629   {"cygexec", MOUNT_CYGWIN_EXEC, 0},
630   {"nosuid", 0, 0},
631   {"acl", MOUNT_NOACL, 1},
632   {"noacl", MOUNT_NOACL, 0},
633   {"posix=1", MOUNT_NOPOSIX, 1},
634   {"posix=0", MOUNT_NOPOSIX, 0}
635 };
636
637 static bool
638 read_flags (char *options, unsigned &flags)
639 {
640   while (*options)
641     {
642       char *p = strchr (options, ',');
643       if (p)
644         *p++ = '\0';
645       else
646         p = strchr (options, '\0');
647
648       for (opt *o = oopts;
649            o < (oopts + (sizeof (oopts) / sizeof (oopts[0])));
650            o++)
651         if (strcmp (options, o->name) == 0)
652           {
653             if (o->clear)
654               flags &= ~o->val;
655             else
656               flags |= o->val;
657             goto gotit;
658           }
659       system_printf ("invalid fstab option - '%s'", options);
660       return false;
661
662     gotit:
663       options = p;
664     }
665   return true;
666 }
667
668 bool
669 mount_info::from_fstab_line (char *line, bool user)
670 {
671   char *native_path, *posix_path, *fs_type;
672
673   /* First field: Native path. */
674   char *c = skip_ws (line);
675   if (!*c || *c == '#')
676     return true;
677   char *cend = find_ws (c);
678   *cend = '\0';
679   native_path = conv_fstab_spaces (c);
680   /* Second field: POSIX path. */
681   c = skip_ws (cend + 1);
682   if (!*c)
683     return true;
684   cend = find_ws (c);
685   *cend = '\0';
686   posix_path = conv_fstab_spaces (c);
687   /* Third field: FS type. */
688   c = skip_ws (cend + 1);
689   if (!*c)
690     return true;
691   cend = find_ws (c);
692   *cend = '\0';
693   fs_type = c;
694   /* Forth field: Flags. */
695   c = skip_ws (cend + 1);
696   if (!*c)
697     return true;
698   cend = find_ws (c);
699   *cend = '\0';
700   unsigned mount_flags = MOUNT_SYSTEM | MOUNT_BINARY;
701   if (!read_flags (c, mount_flags))
702     return true;
703   if (user)
704     mount_flags &= ~MOUNT_SYSTEM;
705   if (!strcmp (fs_type, "cygdrive"))
706     {
707       cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
708       slashify (posix_path, cygdrive, 1);
709       cygdrive_len = strlen (cygdrive);
710     }
711   else
712     {
713       int res = mount_table->add_item (native_path, posix_path, mount_flags);
714       if (res && get_errno () == EMFILE)
715         return false;
716     }
717   return true;
718 }
719
720 bool
721 mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
722 {
723   UNICODE_STRING upath;
724   OBJECT_ATTRIBUTES attr;
725   IO_STATUS_BLOCK io;
726   NTSTATUS status;
727   HANDLE fh;
728
729   if (user)
730     {
731       extern void transform_chars (PWCHAR, PWCHAR);
732       PWCHAR username;
733       sys_mbstowcs (username = wcpcpy (fstab_end, L".d\\"),
734                     NT_MAX_PATH - (fstab_end - fstab),
735                     cygheap->user.name ());
736       /* Make sure special chars in the username are converted according to
737          the rules. */
738       transform_chars (username, username + wcslen (username) - 1);
739     }
740   RtlInitUnicodeString (&upath, fstab);
741   InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
742   debug_printf ("Try to read mounts from %W", fstab);
743   status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io,
744                        FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);
745   if (!NT_SUCCESS (status))
746     {
747       debug_printf ("NtOpenFile(%S) failed, %p", &upath, status);
748       return false;
749     }
750
751   char buf[NT_MAX_PATH];
752   char *got = buf;
753   DWORD len = 0;
754   unsigned line = 1;
755   /* Using buffer size - 2 leaves space to append two \0. */
756   while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, got,
757                                  (sizeof (buf) - 2) - (got - buf), NULL, NULL)))
758     {
759       char *end;
760
761       len = io.Information;
762       /* Set end marker. */
763       got[len] = got[len + 1] = '\0';
764       /* Set len to the absolute len of bytes in buf. */
765       len += got - buf;
766       /* Reset got to start reading at the start of the buffer again. */
767       got = buf;
768 retry:
769       bool got_nl = false;
770       while (got < buf + len && (end = strchr (got, '\n')))
771         {
772           got_nl = true;
773           end[end[-1] == '\r' ? -1 : 0] = '\0';
774           if (!from_fstab_line (got, user))
775             goto done;
776           got = end + 1;
777           ++line;
778         }
779       if (len < (sizeof (buf) - 2))
780         break;
781       /* Check if the buffer contained at least one \n.  If not, the
782          line length is > 32K.  We don't take such long lines.  Print
783          a debug message and skip this line entirely. */
784       if (!got_nl)
785         {
786           system_printf ("%W: Line %d too long, skipping...", fstab, line);
787           while (NT_SUCCESS (NtReadFile (fh, NULL, NULL, NULL, &io, buf,
788                                          (sizeof (buf) - 2), NULL, NULL)))
789             {
790               len = io.Information;
791               buf[len] = buf[len + 1] = '\0';
792               got = strchr (buf, '\n');
793               if (got)
794                 {
795                   ++got;
796                   ++line;
797                   goto retry;
798                 }
799             }
800           got = buf;
801           break;
802         }
803       /* We have to read once more.  Move remaining bytes to the start of
804          the buffer and reposition got so that it points to the end of
805          the remaining bytes. */
806       len = buf + len - got;
807       memmove (buf, got, len);
808       got = buf + len;
809       buf[len] = buf[len + 1] = '\0';
810     }
811   /* Catch a last line without trailing \n. */
812   if (got > buf)
813     from_fstab_line (got, user);
814 done:
815   NtClose (fh);
816   return true;
817 }
818
819 /* write_cygdrive_info: Store default prefix and flags
820    to use when creating cygdrives to the special user shared mem
821    location used to store cygdrive information. */
822
823 int
824 mount_info::write_cygdrive_info (const char *cygdrive_prefix, unsigned flags)
825 {
826   /* Verify cygdrive prefix starts with a forward slash and if there's
827      another character, it's not a slash. */
828   if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
829       (!isslash (cygdrive_prefix[0])) ||
830       ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
831     {
832       set_errno (EINVAL);
833       return -1;
834     }
835   /* Don't allow to override a system cygdrive prefix. */
836   if (cygdrive_flags & MOUNT_SYSTEM)
837     {
838       set_errno (EPERM);
839       return -1;
840     }
841
842   slashify (cygdrive_prefix, cygdrive, 1);
843   cygdrive_flags = flags & ~MOUNT_SYSTEM;
844   cygdrive_len = strlen (cygdrive);
845
846   return 0;
847 }
848
849 int
850 mount_info::get_cygdrive_info (char *user, char *system, char *user_flags,
851                                char *system_flags)
852 {
853   if (user)
854     *user = '\0';
855   if (system)
856     *system = '\0';
857   if (user_flags)
858     *user_flags = '\0';
859   if (system_flags)
860     *system_flags = '\0';
861
862   char *path = (cygdrive_flags & MOUNT_SYSTEM) ? system : user;
863   char *flags = (cygdrive_flags & MOUNT_SYSTEM) ? system_flags : user_flags;
864
865   if (path)
866     {
867       strcpy (path, cygdrive);
868       /* Strip trailing slash for backward compatibility. */
869       if (cygdrive_len > 2)
870         path[cygdrive_len - 1] = '\0';
871     }
872   if (flags)
873     strcpy (flags, (cygdrive_flags & MOUNT_BINARY) ? "binmode" : "textmode");
874   return 0;
875 }
876
877 static mount_item *mounts_for_sort;
878
879 /* sort_by_posix_name: qsort callback to sort the mount entries.  Sort
880    user mounts ahead of system mounts to the same POSIX path. */
881 /* FIXME: should the user should be able to choose whether to
882    prefer user or system mounts??? */
883 static int
884 sort_by_posix_name (const void *a, const void *b)
885 {
886   mount_item *ap = mounts_for_sort + (*((int*) a));
887   mount_item *bp = mounts_for_sort + (*((int*) b));
888
889   /* Base weighting on longest posix path first so that the most
890      obvious path will be chosen. */
891   size_t alen = strlen (ap->posix_path);
892   size_t blen = strlen (bp->posix_path);
893
894   int res = blen - alen;
895
896   if (res)
897     return res;         /* Path lengths differed */
898
899   /* The two paths were the same length, so just determine normal
900      lexical sorted order. */
901   res = strcmp (ap->posix_path, bp->posix_path);
902
903   if (res == 0)
904    {
905      /* need to select between user and system mount to same POSIX path */
906      if (!(bp->flags & MOUNT_SYSTEM))   /* user mount */
907       return 1;
908      else
909       return -1;
910    }
911
912   return res;
913 }
914
915 /* sort_by_native_name: qsort callback to sort the mount entries.  Sort
916    user mounts ahead of system mounts to the same POSIX path. */
917 /* FIXME: should the user should be able to choose whether to
918    prefer user or system mounts??? */
919 static int
920 sort_by_native_name (const void *a, const void *b)
921 {
922   mount_item *ap = mounts_for_sort + (*((int*) a));
923   mount_item *bp = mounts_for_sort + (*((int*) b));
924
925   /* Base weighting on longest win32 path first so that the most
926      obvious path will be chosen. */
927   size_t alen = strlen (ap->native_path);
928   size_t blen = strlen (bp->native_path);
929
930   int res = blen - alen;
931
932   if (res)
933     return res;         /* Path lengths differed */
934
935   /* The two paths were the same length, so just determine normal
936      lexical sorted order. */
937   res = strcmp (ap->native_path, bp->native_path);
938
939   if (res == 0)
940    {
941      /* need to select between user and system mount to same POSIX path */
942      if (!(bp->flags & MOUNT_SYSTEM))   /* user mount */
943       return 1;
944      else
945       return -1;
946    }
947
948   return res;
949 }
950
951 void
952 mount_info::sort ()
953 {
954   for (int i = 0; i < nmounts; i++)
955     native_sorted[i] = posix_sorted[i] = i;
956   /* Sort them into reverse length order, otherwise we won't
957      be able to look for /foo in /.  */
958   mounts_for_sort = mount;      /* ouch. */
959   qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
960   qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
961 }
962
963 /* Add an entry to the mount table.
964    Returns 0 on success, -1 on failure and errno is set.
965
966    This is where all argument validation is done.  It may not make sense to
967    do this when called internally, but it's cleaner to keep it all here.  */
968
969 int
970 mount_info::add_item (const char *native, const char *posix,
971                       unsigned mountflags)
972 {
973   tmp_pathbuf tp;
974   char *nativetmp = tp.c_get ();
975   /* FIXME: The POSIX path is stored as value name right now, which is
976      restricted to 256 bytes. */
977   char posixtmp[CYG_MAX_PATH];
978   char *nativetail, *posixtail, error[] = "error";
979   int nativeerr, posixerr;
980
981   /* Something's wrong if either path is NULL or empty, or if it's
982      not a UNC or absolute path. */
983
984   if (native == NULL || !isabspath (native) ||
985       !(is_native_path (native) || is_unc_share (native) || isdrive (native)))
986     nativeerr = EINVAL;
987   else
988     nativeerr = normalize_win32_path (native, nativetmp, nativetail);
989
990   if (posix == NULL || !isabspath (posix) ||
991       is_unc_share (posix) || isdrive (posix))
992     posixerr = EINVAL;
993   else
994     posixerr = normalize_posix_path (posix, posixtmp, posixtail);
995
996   debug_printf ("%s[%s], %s[%s], %p",
997                 native, nativeerr ? error : nativetmp,
998                 posix, posixerr ? error : posixtmp, mountflags);
999
1000   if (nativeerr || posixerr)
1001     {
1002       set_errno (nativeerr?:posixerr);
1003       return -1;
1004     }
1005
1006   /* Make sure both paths do not end in /. */
1007   if (nativetail > nativetmp + 1 && nativetail[-1] == '\\')
1008     nativetail[-1] = '\0';
1009   if (posixtail > posixtmp + 1 && posixtail[-1] == '/')
1010     posixtail[-1] = '\0';
1011
1012   /* Write over an existing mount item with the same POSIX path if
1013      it exists and is from the same registry area. */
1014   int i;
1015   for (i = 0; i < nmounts; i++)
1016     {
1017       if (!strcmp (mount[i].posix_path, posixtmp))
1018         {
1019           /* Don't allow to override a system mount with a user mount. */
1020           if ((mount[i].flags & MOUNT_SYSTEM) && !(mountflags & MOUNT_SYSTEM))
1021             {
1022               set_errno (EPERM);
1023               return -1;
1024             }
1025           if ((mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
1026             break;
1027         }
1028     }
1029
1030   if (i == nmounts && nmounts == MAX_MOUNTS)
1031     {
1032       set_errno (EMFILE);
1033       return -1;
1034     }
1035
1036   if (i == nmounts)
1037     nmounts++;
1038   mount[i].init (nativetmp, posixtmp, mountflags);
1039   sort ();
1040
1041   return 0;
1042 }
1043
1044 /* Delete a mount table entry where path is either a Win32 or POSIX
1045    path. Since the mount table is really just a table of aliases,
1046    deleting / is ok (although running without a slash mount is
1047    strongly discouraged because some programs may run erratically
1048    without one).  If MOUNT_SYSTEM is set in flags, remove from system
1049    registry, otherwise remove the user registry mount.
1050 */
1051
1052 int
1053 mount_info::del_item (const char *path, unsigned flags)
1054 {
1055   tmp_pathbuf tp;
1056   char *pathtmp = tp.c_get ();
1057   int posix_path_p = false;
1058
1059   /* Something's wrong if path is NULL or empty. */
1060   if (path == NULL || *path == 0 || !isabspath (path))
1061     {
1062       set_errno (EINVAL);
1063       return -1;
1064     }
1065
1066   if (is_unc_share (path) || strpbrk (path, ":\\"))
1067     backslashify (path, pathtmp, 0);
1068   else
1069     {
1070       slashify (path, pathtmp, 0);
1071       posix_path_p = true;
1072     }
1073   nofinalslash (pathtmp, pathtmp);
1074
1075   for (int i = 0; i < nmounts; i++)
1076     {
1077       int ent = native_sorted[i]; /* in the same order as getmntent() */
1078       if (((posix_path_p)
1079            ? !strcmp (mount[ent].posix_path, pathtmp)
1080            : strcasematch (mount[ent].native_path, pathtmp)))
1081         {
1082           /* Don't allow to remove a system mount. */
1083           if ((mount[ent].flags & MOUNT_SYSTEM))
1084             {
1085               set_errno (EPERM);
1086               return -1;
1087             }
1088           nmounts--; /* One less mount table entry */
1089           /* Fill in the hole if not at the end of the table */
1090           if (ent < nmounts)
1091             memmove (mount + ent, mount + ent + 1,
1092                      sizeof (mount[ent]) * (nmounts - ent));
1093           sort (); /* Resort the table */
1094           return 0;
1095         }
1096     }
1097   set_errno (EINVAL);
1098   return -1;
1099 }
1100
1101 /************************* mount_item class ****************************/
1102
1103 static mntent *
1104 fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
1105 {
1106   struct mntent& ret=_my_tls.locals.mntbuf;
1107   bool append_bs = false;
1108
1109   /* Remove drivenum from list if we see a x: style path */
1110   if (strlen (native_path) == 2 && native_path[1] == ':')
1111     {
1112       int drivenum = cyg_tolower (native_path[0]) - 'a';
1113       if (drivenum >= 0 && drivenum <= 31)
1114         _my_tls.locals.available_drives &= ~(1 << drivenum);
1115       append_bs = true;
1116     }
1117
1118   /* Pass back pointers to mount_table strings reserved for use by
1119      getmntent rather than pointers to strings in the internal mount
1120      table because the mount table might change, causing weird effects
1121      from the getmntent user's point of view. */
1122
1123   strcpy (_my_tls.locals.mnt_fsname, native_path);
1124   ret.mnt_fsname = _my_tls.locals.mnt_fsname;
1125   strcpy (_my_tls.locals.mnt_dir, posix_path);
1126   ret.mnt_dir = _my_tls.locals.mnt_dir;
1127
1128   /* Try to give a filesystem type that matches what a Linux application might
1129      expect. Naturally, this is a moving target, but we can make some
1130      reasonable guesses for popular types. */
1131
1132   fs_info mntinfo;
1133   tmp_pathbuf tp;
1134   UNICODE_STRING unat;
1135   tp.u_get (&unat);
1136   get_nt_native_path (native_path, unat);
1137   if (append_bs)
1138     RtlAppendUnicodeToString (&unat, L"\\");
1139   mntinfo.update (&unat, NULL);
1140
1141   if (mntinfo.is_samba())
1142     strcpy (_my_tls.locals.mnt_type, (char *) "smbfs");
1143   else if (mntinfo.is_nfs ())
1144     strcpy (_my_tls.locals.mnt_type, (char *) "nfs");
1145   else if (mntinfo.is_fat ())
1146     strcpy (_my_tls.locals.mnt_type, (char *) "vfat");
1147   else if (mntinfo.is_ntfs ())
1148     strcpy (_my_tls.locals.mnt_type, (char *) "ntfs");
1149   else if (mntinfo.is_netapp ())
1150     strcpy (_my_tls.locals.mnt_type, (char *) "netapp");
1151   else if (mntinfo.is_cdrom ())
1152     strcpy (_my_tls.locals.mnt_type, (char *) "iso9660");
1153   else
1154     strcpy (_my_tls.locals.mnt_type, (char *) "unknown");
1155
1156   ret.mnt_type = _my_tls.locals.mnt_type;
1157
1158   /* mnt_opts is a string that details mount params such as
1159      binary or textmode, or exec.  We don't print
1160      `silent' here; it's a magic internal thing. */
1161
1162   if (!(flags & MOUNT_BINARY))
1163     strcpy (_my_tls.locals.mnt_opts, (char *) "textmode");
1164   else
1165     strcpy (_my_tls.locals.mnt_opts, (char *) "binmode");
1166
1167   if (flags & MOUNT_CYGWIN_EXEC)
1168     strcat (_my_tls.locals.mnt_opts, (char *) ",cygexec");
1169   else if (flags & MOUNT_EXEC)
1170     strcat (_my_tls.locals.mnt_opts, (char *) ",exec");
1171   else if (flags & MOUNT_NOTEXEC)
1172     strcat (_my_tls.locals.mnt_opts, (char *) ",noexec");
1173
1174   if (flags & MOUNT_NOACL)
1175     strcat (_my_tls.locals.mnt_opts, (char *) ",noacl");
1176
1177   if (flags & MOUNT_NOPOSIX)
1178     strcat (_my_tls.locals.mnt_opts, (char *) ",posix=0");
1179
1180   if ((flags & MOUNT_CYGDRIVE))         /* cygdrive */
1181     strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
1182
1183   if (!(flags & MOUNT_SYSTEM))          /* user mount */
1184     strcat (_my_tls.locals.mnt_opts, (char *) ",user");
1185   else                                  /* system mount */
1186     strcat (_my_tls.locals.mnt_opts, (char *) ",system");
1187
1188   ret.mnt_opts = _my_tls.locals.mnt_opts;
1189
1190   ret.mnt_freq = 1;
1191   ret.mnt_passno = 1;
1192   return &ret;
1193 }
1194
1195 struct mntent *
1196 mount_item::getmntent ()
1197 {
1198   return fillout_mntent (native_path, posix_path, flags);
1199 }
1200
1201 static struct mntent *
1202 cygdrive_getmntent ()
1203 {
1204   char native_path[4];
1205   char posix_path[CYG_MAX_PATH];
1206   DWORD mask = 1, drive = 'a';
1207   struct mntent *ret = NULL;
1208
1209   while (_my_tls.locals.available_drives)
1210     {
1211       for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
1212         if (_my_tls.locals.available_drives & mask)
1213           break;
1214
1215       __small_sprintf (native_path, "%c:\\", drive);
1216       if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
1217         {
1218           _my_tls.locals.available_drives &= ~mask;
1219           continue;
1220         }
1221       native_path[2] = '\0';
1222       __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
1223       ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags);
1224       break;
1225     }
1226
1227   return ret;
1228 }
1229
1230 struct mntent *
1231 mount_info::getmntent (int x)
1232 {
1233   if (x < 0 || x >= nmounts)
1234     return cygdrive_getmntent ();
1235
1236   return mount[native_sorted[x]].getmntent ();
1237 }
1238
1239 /* Fill in the fields of a mount table entry.  */
1240
1241 void
1242 mount_item::init (const char *native, const char *posix, unsigned mountflags)
1243 {
1244   strcpy ((char *) native_path, native);
1245   strcpy ((char *) posix_path, posix);
1246
1247   native_pathlen = strlen (native_path);
1248   posix_pathlen = strlen (posix_path);
1249
1250   flags = mountflags;
1251 }
1252
1253 /********************** Mount System Calls **************************/
1254
1255 /* Mount table system calls.
1256    Note that these are exported to the application.  */
1257
1258 /* mount: Add a mount to the mount table in memory and to the registry
1259    that will cause paths under win32_path to be translated to paths
1260    under posix_path. */
1261
1262 extern "C" int
1263 mount (const char *win32_path, const char *posix_path, unsigned flags)
1264 {
1265   int res = -1;
1266   flags &= ~MOUNT_SYSTEM;
1267
1268   myfault efault;
1269   if (efault.faulted (EFAULT))
1270     /* errno set */;
1271   else if (!*posix_path)
1272     set_errno (EINVAL);
1273   else if (strpbrk (posix_path, "\\:"))
1274     set_errno (EINVAL);
1275   else if (flags & MOUNT_CYGDRIVE) /* normal mount */
1276     {
1277       /* When flags include MOUNT_CYGDRIVE, take this to mean that
1278         we actually want to change the cygdrive prefix and flags
1279         without actually mounting anything. */
1280       res = mount_table->write_cygdrive_info (posix_path, flags);
1281       win32_path = NULL;
1282     }
1283   else if (!*win32_path)
1284     set_errno (EINVAL);
1285   else
1286     res = mount_table->add_item (win32_path, posix_path, flags);
1287
1288   syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
1289   return res;
1290 }
1291
1292 /* umount: The standard umount call only has a path parameter.  Since
1293    it is not possible for this call to specify whether to remove the
1294    mount from the user or global mount registry table, assume the user
1295    table. */
1296
1297 extern "C" int
1298 umount (const char *path)
1299 {
1300   myfault efault;
1301   if (efault.faulted (EFAULT))
1302     return -1;
1303   if (!*path)
1304     {
1305       set_errno (EINVAL);
1306       return -1;
1307     }
1308   return cygwin_umount (path, 0);
1309 }
1310
1311 /* cygwin_umount: This is like umount but takes an additional flags
1312    parameter that specifies whether to umount from the user or system-wide
1313    registry area. */
1314
1315 extern "C" int
1316 cygwin_umount (const char *path, unsigned flags)
1317 {
1318   int res = -1;
1319
1320   if (!(flags & MOUNT_CYGDRIVE))
1321     res = mount_table->del_item (path, flags & ~MOUNT_SYSTEM);
1322
1323   syscall_printf ("%d = cygwin_umount (%s, %d)", res,  path, flags);
1324   return res;
1325 }
1326
1327 bool
1328 is_floppy (const char *dos)
1329 {
1330   char dev[256];
1331   if (!QueryDosDevice (dos, dev, 256))
1332     return false;
1333   return ascii_strncasematch (dev, "\\Device\\Floppy", 14);
1334 }
1335
1336 extern "C" FILE *
1337 setmntent (const char *filep, const char *)
1338 {
1339   _my_tls.locals.iteration = 0;
1340   _my_tls.locals.available_drives = GetLogicalDrives ();
1341   /* Filter floppy drives on A: and B: */
1342   if ((_my_tls.locals.available_drives & 1) && is_floppy ("A:"))
1343     _my_tls.locals.available_drives &= ~1;
1344   if ((_my_tls.locals.available_drives & 2) && is_floppy ("B:"))
1345     _my_tls.locals.available_drives &= ~2;
1346   return (FILE *) filep;
1347 }
1348
1349 extern "C" struct mntent *
1350 getmntent (FILE *)
1351 {
1352   return mount_table->getmntent (_my_tls.locals.iteration++);
1353 }
1354
1355 extern "C" int
1356 endmntent (FILE *)
1357 {
1358   return 1;
1359 }