1 /* path.cc: path support.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include "miscfuncs.h"
20 #include <cygwin/version.h>
27 #include "shared_info.h"
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))
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]))
42 #define isproc(path) \
43 (path_prefix_p (proc, (path), proc_len, false))
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. */
50 static inline bool __stdcall
51 is_native_path (const char *path)
53 return isdirsep (path[0])
54 && (isdirsep (path[1]) || path[1] == '?')
55 && (path[2] == '?' || path[2] == '.')
60 static inline bool __stdcall
61 is_unc_share (const char *path)
64 return (isdirsep (path[0])
67 && ((p = strpbrk (path + 3, "\\/")) != NULL)
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. */
76 win32_device_name (const char *src_path, char *win32_path, device& dev)
79 if (dev == FH_FS || dev == FH_DEV)
81 strcpy (win32_path, dev.native);
86 mount_info::create_root_entry (const PWCHAR root)
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);
100 /* init: Initialize the mount table. */
107 WCHAR path[PATH_MAX];
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))
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);
121 set_flags (unsigned *flags, unsigned val)
124 if (!(*flags & PATH_BINARY))
127 debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
131 *flags |= PATH_BINARY;
132 debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
137 mount_item::build_win32 (char *dst, const char *src, unsigned *outflags, unsigned chroot_pathlen)
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] != '/')
146 real_native_path = native_path;
147 real_posix_pathlen = chroot_pathlen ?: posix_pathlen;
151 n = cygheap->root.native_length ();
152 real_native_path = cygheap->root.native_path ();
153 real_posix_pathlen = posix_pathlen;
155 memcpy (dst, real_native_path, n + 1);
156 const char *p = src + real_posix_pathlen;
159 else if ((isdrive (dst) && !dst[2]) || *p)
161 if ((n + strlen (p)) >= NT_MAX_PATH)
164 backslashify (p, dst + n, 0);
168 /* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
169 the result in win32_path.
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.
175 If full_win32_path != NULL, the full path is stored there.
177 The result is zero for success, or an errno value.
179 {,full_}win32_path must have sufficient space (i.e. NT_MAX_PATH bytes). */
182 mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
185 bool chroot_ok = !cygheap->root.exists ();
186 while (sys_mount_table_counter < cygwin_shared->sys_mount_table_counter)
188 int current = cygwin_shared->sys_mount_table_counter;
190 sys_mount_table_counter = current;
197 debug_printf ("conv_to_win32_path (%s)", src_path);
200 mount_item *mi = NULL; /* initialized to avoid compiler warning */
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
206 eg mounting c:/foo /foo
212 should look in c:/foo, not d:/foo.
214 converting normalizex UNIX path to a DOS-style path, looking up the
215 appropriate drive in the mount table. */
217 /* See if this is a cygwin "device" */
218 if (win32_device_name (src_path, dst, dev))
220 *flags = MOUNT_BINARY; /* FIXME: Is this a sensible default for devices? */
222 goto out_no_chroot_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
229 if (src_path[1] == '/')
231 if (!strchr (src_path + 2, '/'))
234 set_flags (flags, PATH_BINARY);
236 backslashify (src_path, dst, 0);
237 /* Go through chroot check */
240 if (isproc (src_path))
243 dev.devn = fhandler_proc::get_proc_fhandler (src_path);
244 if (dev.devn == FH_BAD)
246 set_flags (flags, PATH_BINARY);
247 strcpy (dst, src_path);
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))
254 int n = mount_table->cygdrive_len - 1;
261 if (mount_table->cygdrive_len > 1)
264 else if (cygdrive_win32_path (src_path, dst, unit))
266 set_flags (flags, (unsigned) cygdrive_flags);
269 else if (mount_table->cygdrive_len > 1)
275 /* Check the mount table for prefix matches. */
276 for (i = 0; i < nmounts; i++)
281 mi = mount + posix_sorted[i];
282 if (!cygheap->root.exists ()
283 || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
285 path = mi->posix_path;
286 len = mi->posix_pathlen;
288 else if (cygheap->root.posix_ok (mi->posix_path))
290 path = cygheap->root.unchroot (mi->posix_path);
291 chroot_pathlen = len = strlen (path);
299 if (path_prefix_p (path, src_path, len, mi->flags & MOUNT_NOPOSIX))
305 int err = mi->build_win32 (dst, src_path, flags, chroot_pathlen);
313 if (src_path[1] != '/' && src_path[1] != ':')
314 offset = cygheap->cwd.get_drive (dst);
315 backslashify (src_path, dst + offset, 0);
319 if (chroot_ok || cygheap->root.ischroot_native (dst))
323 debug_printf ("attempt to access outside of chroot '%s - %s'",
324 cygheap->root.posix_path (), cygheap->root.native_path ());
329 debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
334 mount_info::get_mounts_here (const char *parent_dir, int parent_dir_len,
335 PUNICODE_STRING mount_points,
336 PUNICODE_STRING cygd)
340 for (int i = 0; i < nmounts; i++)
342 mount_item *mi = mount + posix_sorted[i];
343 char *last_slash = strrchr (mi->posix_path, '/');
346 if (last_slash == mi->posix_path)
348 if (parent_dir_len == 1 && mi->posix_pathlen > 1)
349 RtlCreateUnicodeStringFromAsciiz (&mount_points[n_mounts++],
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++],
357 RtlCreateUnicodeStringFromAsciiz (cygd, cygdrive + 1);
358 cygd->Length -= 2; // Strip trailing slash
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. */
367 mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
369 int len = cygdrive_len;
371 memcpy (dst, cygdrive, len + 1);
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]))
383 if (isdirsep (src[2]))
387 strcpy (dst + len, src + n);
389 slashify (dst, dst, trailing_slash_p);
393 mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
396 const char *p = src + cygdrive_len;
397 if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
399 unit = -1; /* FIXME: should be zero, maybe? */
405 dst[0] = cyg_tolower (*p);
407 strcpy (dst + 2, p + 1);
408 backslashify (dst, dst, !dst[2]);
412 debug_printf ("src '%s', dst '%s'", src, dst);
416 /* conv_to_posix_path: Ensure src_path is a POSIX path.
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. */
422 /* TODO: Change conv_to_posix_path to work with native paths. */
424 /* src_path is a wide Win32 path. */
426 mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path,
429 bool changed = false;
430 if (!wcsncmp (src_path, L"\\\\?\\", 4))
433 if (src_path[1] != L':') /* native UNC path */
435 *(src_path += 2) = L'\\';
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);
449 mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
452 int src_path_len = strlen (src_path);
453 int relative_path_p = !isabspath (src_path);
454 int trailing_slash_p;
456 if (src_path_len <= 1)
457 trailing_slash_p = 0;
460 const char *lastchar = src_path + src_path_len - 1;
461 trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
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");
469 if (src_path_len >= NT_MAX_PATH)
471 debug_printf ("ENAMETOOLONG");
475 /* FIXME: For now, if the path is relative and it's supposed to stay
476 that way, skip mount table processing. */
478 if (keep_rel_p && relative_path_p)
480 slashify (src_path, posix_path, 0);
481 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
486 char *pathbuf = tp.c_get ();
488 int rc = normalize_win32_path (src_path, pathbuf, tail);
491 debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
495 int pathbuflen = tail - pathbuf;
496 for (int i = 0; i < nmounts; ++i)
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))
503 if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
506 /* SRC_PATH is in the mount table. */
508 const char *p = pathbuf + mi.native_pathlen;
512 else if (isdirsep (*p))
517 int addslash = nextchar > 0 ? 1 : 0;
518 if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= NT_MAX_PATH)
520 strcpy (posix_path, mi.posix_path);
522 strcat (posix_path, "/");
525 posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
528 if (cygheap->root.exists ())
530 const char *p = cygheap->root.unchroot (posix_path);
531 memmove (posix_path, p, strlen (p) + 1);
536 if (!cygheap->root.exists ())
538 else if (!cygheap->root.ischroot_native (pathbuf))
542 const char *p = pathbuf + cygheap->root.native_length ();
544 slashify (p, posix_path, trailing_slash_p);
548 posix_path[1] = '\0';
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);
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);
568 debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
573 /* Return flags associated with a mount point given the win32 path. */
576 mount_info::set_flags_from_win32_path (const char *p)
578 for (int i = 0; i < nmounts; i++)
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))
591 while (*in == ' ' || *in == '\t')
599 while (*in && *in != ' ' && *in != '\t')
605 conv_fstab_spaces (char *field)
607 register char *sp = field;
608 while (sp = strstr (sp, "\\040"))
611 memmove (sp, sp + 3, strlen (sp + 3) + 1);
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},
631 {"acl", MOUNT_NOACL, 1},
632 {"noacl", MOUNT_NOACL, 0},
633 {"posix=1", MOUNT_NOPOSIX, 1},
634 {"posix=0", MOUNT_NOPOSIX, 0}
638 read_flags (char *options, unsigned &flags)
642 char *p = strchr (options, ',');
646 p = strchr (options, '\0');
649 o < (oopts + (sizeof (oopts) / sizeof (oopts[0])));
651 if (strcmp (options, o->name) == 0)
659 system_printf ("invalid fstab option - '%s'", options);
669 mount_info::from_fstab_line (char *line, bool user)
671 char *native_path, *posix_path, *fs_type;
673 /* First field: Native path. */
674 char *c = skip_ws (line);
675 if (!*c || *c == '#')
677 char *cend = find_ws (c);
679 native_path = conv_fstab_spaces (c);
680 /* Second field: POSIX path. */
681 c = skip_ws (cend + 1);
686 posix_path = conv_fstab_spaces (c);
687 /* Third field: FS type. */
688 c = skip_ws (cend + 1);
694 /* Forth field: Flags. */
695 c = skip_ws (cend + 1);
700 unsigned mount_flags = MOUNT_SYSTEM | MOUNT_BINARY;
701 if (!read_flags (c, mount_flags))
704 mount_flags &= ~MOUNT_SYSTEM;
705 if (!strcmp (fs_type, "cygdrive"))
707 cygdrive_flags = mount_flags | MOUNT_CYGDRIVE;
708 slashify (posix_path, cygdrive, 1);
709 cygdrive_len = strlen (cygdrive);
713 int res = mount_table->add_item (native_path, posix_path, mount_flags);
714 if (res && get_errno () == EMFILE)
721 mount_info::from_fstab (bool user, WCHAR fstab[], PWCHAR fstab_end)
723 UNICODE_STRING upath;
724 OBJECT_ATTRIBUTES attr;
731 extern void transform_chars (PWCHAR, PWCHAR);
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
738 transform_chars (username, username + wcslen (username) - 1);
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))
747 debug_printf ("NtOpenFile(%S) failed, %p", &upath, status);
751 char buf[NT_MAX_PATH];
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)))
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. */
766 /* Reset got to start reading at the start of the buffer again. */
770 while (got < buf + len && (end = strchr (got, '\n')))
773 end[end[-1] == '\r' ? -1 : 0] = '\0';
774 if (!from_fstab_line (got, user))
779 if (len < (sizeof (buf) - 2))
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. */
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)))
790 len = io.Information;
791 buf[len] = buf[len + 1] = '\0';
792 got = strchr (buf, '\n');
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);
809 buf[len] = buf[len + 1] = '\0';
811 /* Catch a last line without trailing \n. */
813 from_fstab_line (got, user);
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. */
824 mount_info::write_cygdrive_info (const char *cygdrive_prefix, unsigned flags)
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]))))
835 /* Don't allow to override a system cygdrive prefix. */
836 if (cygdrive_flags & MOUNT_SYSTEM)
842 slashify (cygdrive_prefix, cygdrive, 1);
843 cygdrive_flags = flags & ~MOUNT_SYSTEM;
844 cygdrive_len = strlen (cygdrive);
850 mount_info::get_cygdrive_info (char *user, char *system, char *user_flags,
860 *system_flags = '\0';
862 char *path = (cygdrive_flags & MOUNT_SYSTEM) ? system : user;
863 char *flags = (cygdrive_flags & MOUNT_SYSTEM) ? system_flags : user_flags;
867 strcpy (path, cygdrive);
868 /* Strip trailing slash for backward compatibility. */
869 if (cygdrive_len > 2)
870 path[cygdrive_len - 1] = '\0';
873 strcpy (flags, (cygdrive_flags & MOUNT_BINARY) ? "binmode" : "textmode");
877 static mount_item *mounts_for_sort;
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??? */
884 sort_by_posix_name (const void *a, const void *b)
886 mount_item *ap = mounts_for_sort + (*((int*) a));
887 mount_item *bp = mounts_for_sort + (*((int*) b));
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);
894 int res = blen - alen;
897 return res; /* Path lengths differed */
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);
905 /* need to select between user and system mount to same POSIX path */
906 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
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??? */
920 sort_by_native_name (const void *a, const void *b)
922 mount_item *ap = mounts_for_sort + (*((int*) a));
923 mount_item *bp = mounts_for_sort + (*((int*) b));
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);
930 int res = blen - alen;
933 return res; /* Path lengths differed */
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);
941 /* need to select between user and system mount to same POSIX path */
942 if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
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);
963 /* Add an entry to the mount table.
964 Returns 0 on success, -1 on failure and errno is set.
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. */
970 mount_info::add_item (const char *native, const char *posix,
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;
981 /* Something's wrong if either path is NULL or empty, or if it's
982 not a UNC or absolute path. */
984 if (native == NULL || !isabspath (native) ||
985 !(is_native_path (native) || is_unc_share (native) || isdrive (native)))
988 nativeerr = normalize_win32_path (native, nativetmp, nativetail);
990 if (posix == NULL || !isabspath (posix) ||
991 is_unc_share (posix) || isdrive (posix))
994 posixerr = normalize_posix_path (posix, posixtmp, posixtail);
996 debug_printf ("%s[%s], %s[%s], %p",
997 native, nativeerr ? error : nativetmp,
998 posix, posixerr ? error : posixtmp, mountflags);
1000 if (nativeerr || posixerr)
1002 set_errno (nativeerr?:posixerr);
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';
1012 /* Write over an existing mount item with the same POSIX path if
1013 it exists and is from the same registry area. */
1015 for (i = 0; i < nmounts; i++)
1017 if (!strcmp (mount[i].posix_path, posixtmp))
1019 /* Don't allow to override a system mount with a user mount. */
1020 if ((mount[i].flags & MOUNT_SYSTEM) && !(mountflags & MOUNT_SYSTEM))
1025 if ((mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
1030 if (i == nmounts && nmounts == MAX_MOUNTS)
1038 mount[i].init (nativetmp, posixtmp, mountflags);
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.
1053 mount_info::del_item (const char *path, unsigned flags)
1056 char *pathtmp = tp.c_get ();
1057 int posix_path_p = false;
1059 /* Something's wrong if path is NULL or empty. */
1060 if (path == NULL || *path == 0 || !isabspath (path))
1066 if (is_unc_share (path) || strpbrk (path, ":\\"))
1067 backslashify (path, pathtmp, 0);
1070 slashify (path, pathtmp, 0);
1071 posix_path_p = true;
1073 nofinalslash (pathtmp, pathtmp);
1075 for (int i = 0; i < nmounts; i++)
1077 int ent = native_sorted[i]; /* in the same order as getmntent() */
1079 ? !strcmp (mount[ent].posix_path, pathtmp)
1080 : strcasematch (mount[ent].native_path, pathtmp)))
1082 /* Don't allow to remove a system mount. */
1083 if ((mount[ent].flags & MOUNT_SYSTEM))
1088 nmounts--; /* One less mount table entry */
1089 /* Fill in the hole if not at the end of the table */
1091 memmove (mount + ent, mount + ent + 1,
1092 sizeof (mount[ent]) * (nmounts - ent));
1093 sort (); /* Resort the table */
1101 /************************* mount_item class ****************************/
1104 fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
1106 struct mntent& ret=_my_tls.locals.mntbuf;
1107 bool append_bs = false;
1109 /* Remove drivenum from list if we see a x: style path */
1110 if (strlen (native_path) == 2 && native_path[1] == ':')
1112 int drivenum = cyg_tolower (native_path[0]) - 'a';
1113 if (drivenum >= 0 && drivenum <= 31)
1114 _my_tls.locals.available_drives &= ~(1 << drivenum);
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. */
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;
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. */
1134 UNICODE_STRING unat;
1136 get_nt_native_path (native_path, unat);
1138 RtlAppendUnicodeToString (&unat, L"\\");
1139 mntinfo.update (&unat, NULL);
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");
1154 strcpy (_my_tls.locals.mnt_type, (char *) "unknown");
1156 ret.mnt_type = _my_tls.locals.mnt_type;
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. */
1162 if (!(flags & MOUNT_BINARY))
1163 strcpy (_my_tls.locals.mnt_opts, (char *) "textmode");
1165 strcpy (_my_tls.locals.mnt_opts, (char *) "binmode");
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");
1174 if (flags & MOUNT_NOACL)
1175 strcat (_my_tls.locals.mnt_opts, (char *) ",noacl");
1177 if (flags & MOUNT_NOPOSIX)
1178 strcat (_my_tls.locals.mnt_opts, (char *) ",posix=0");
1180 if ((flags & MOUNT_CYGDRIVE)) /* cygdrive */
1181 strcat (_my_tls.locals.mnt_opts, (char *) ",noumount");
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");
1188 ret.mnt_opts = _my_tls.locals.mnt_opts;
1196 mount_item::getmntent ()
1198 return fillout_mntent (native_path, posix_path, flags);
1201 static struct mntent *
1202 cygdrive_getmntent ()
1204 char native_path[4];
1205 char posix_path[CYG_MAX_PATH];
1206 DWORD mask = 1, drive = 'a';
1207 struct mntent *ret = NULL;
1209 while (_my_tls.locals.available_drives)
1211 for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
1212 if (_my_tls.locals.available_drives & mask)
1215 __small_sprintf (native_path, "%c:\\", drive);
1216 if (GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
1218 _my_tls.locals.available_drives &= ~mask;
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);
1231 mount_info::getmntent (int x)
1233 if (x < 0 || x >= nmounts)
1234 return cygdrive_getmntent ();
1236 return mount[native_sorted[x]].getmntent ();
1239 /* Fill in the fields of a mount table entry. */
1242 mount_item::init (const char *native, const char *posix, unsigned mountflags)
1244 strcpy ((char *) native_path, native);
1245 strcpy ((char *) posix_path, posix);
1247 native_pathlen = strlen (native_path);
1248 posix_pathlen = strlen (posix_path);
1253 /********************** Mount System Calls **************************/
1255 /* Mount table system calls.
1256 Note that these are exported to the application. */
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. */
1263 mount (const char *win32_path, const char *posix_path, unsigned flags)
1266 flags &= ~MOUNT_SYSTEM;
1269 if (efault.faulted (EFAULT))
1271 else if (!*posix_path)
1273 else if (strpbrk (posix_path, "\\:"))
1275 else if (flags & MOUNT_CYGDRIVE) /* normal mount */
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);
1283 else if (!*win32_path)
1286 res = mount_table->add_item (win32_path, posix_path, flags);
1288 syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
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
1298 umount (const char *path)
1301 if (efault.faulted (EFAULT))
1308 return cygwin_umount (path, 0);
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
1316 cygwin_umount (const char *path, unsigned flags)
1320 if (!(flags & MOUNT_CYGDRIVE))
1321 res = mount_table->del_item (path, flags & ~MOUNT_SYSTEM);
1323 syscall_printf ("%d = cygwin_umount (%s, %d)", res, path, flags);
1328 is_floppy (const char *dos)
1331 if (!QueryDosDevice (dos, dev, 256))
1333 return ascii_strncasematch (dev, "\\Device\\Floppy", 14);
1337 setmntent (const char *filep, const char *)
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;
1349 extern "C" struct mntent *
1352 return mount_table->getmntent (_my_tls.locals.iteration++);