From: Corinna Vinschen <corinna-cygwin@cygwin.com>
To: cygwin-patches@cygwin.com
Subject: Re: [PATCH] Use automake (v5)
Date: Mon, 26 Apr 2021 17:03:55 +0200 [thread overview]
Message-ID: <YIbWW3mJsphIW9hd@calimero.vinschen.de> (raw)
In-Reply-To: <YIFkrv4KPAQypN8o@calimero.vinschen.de>
On Apr 22 13:57, Corinna Vinschen wrote:
> On Apr 21 18:40, Corinna Vinschen wrote:
> > On Apr 20 21:15, Jon Turney wrote:
> > > On 20/04/2021 21:13, Jon Turney wrote:
> > > > For ease of reviewing, this patch doesn't contain changes to generated
> > > > files which would be made by running ./autogen.sh.
> > >
> > > Sorry about getting distracted from this. To summarize what I believe were
> > > the outstanding issues with v3 [1]:
> > >
> > > [1] https://cygwin.com/pipermail/cygwin-patches/2020q4/010827.html
> > >
> > > * 'INCLUDES' is the old name for 'AM_CPPFLAGS' warning from autogen.sh
> > >
> > > I plan to clean this up in a future patch
> > >
> > > * 'ps$(EXEEXT)' previously defined' warning from autogen.sh
> > >
> > > It seems to be a shortcoming of automake that there's no way to suppress
> > > just that warning.
> > >
> > > One possible solution is build ps.exe with a different name and rename it
> > > while installing, but I think that is counter-productive (in the sense that
> > > it trades this warning for making the build more complex to understand)
> > >
> > > * some object files are in a unexpected places in the build file hierarchy
> > > (compared to naive expectations and/or the non-automake build)
> >
> > This is the only minor qualm I have with this patch. It would be nice
> > to have the mingw sources and .o files in the mingw subdir. It would
> > simply be a bit cleaner. The files shared between cygwin and mingw
> > (that's only path.cc, I think) could be handled by an include, i. e.
> >
> > utils/
> >
> > path.cc (full implementation)
> >
> > utils/mingw/
> >
> > path.cc:
> >
> > #include "../path.cc"
>
> I wonder if it wouldn't make sense to split out the mingw-only parts
> of path.cc entirely. I had a quick view into the file and it turns
> out that of the almost 1000 lines in this file, only about 100 lines
> are used by mount. All the rest is only used by mingw code, i. e.,
> cygcheck and strace.
>
> That's obviously not part of this patch, but something we should keep
> in mind for a later cleanup.
I tried this as a POC and it's not much of a problem. See the below
patch. Cleaning up the includes is still to do.
Corinna
diff --git a/winsup/utils/Makefile.in b/winsup/utils/Makefile.in
index e4f55dd3c50e..a2d8c426fdac 100644
--- a/winsup/utils/Makefile.in
+++ b/winsup/utils/Makefile.in
@@ -58,10 +58,10 @@ endif
# List all objects to be compiled in MinGW mode. Any object not on this
# list will will be compiled in Cygwin mode implicitly, so there is no
# need for a CYGWIN_OBJS.
-MINGW_OBJS := bloda.o cygcheck.o cygwin-console-helper.o dump_setup.o ldh.o path.o strace.o
+MINGW_OBJS := bloda.o cygcheck.o cygwin-console-helper.o dump_setup.o ldh.o mingw-path.o strace.o
MINGW_LDFLAGS:=-static
-CYGCHECK_OBJS:=cygcheck.o bloda.o path.o dump_setup.o
+CYGCHECK_OBJS:=cygcheck.o bloda.o mingw-path.o dump_setup.o
ZLIB:=-lz
.PHONY: all
@@ -69,12 +69,10 @@ all:
# If a binary should link in any objects besides the .o with the same
# name as the binary, then list those here.
-strace.exe: path.o
-cygcheck.exe: cygcheck.o bloda.o path.o dump_setup.o
+strace.exe: mingw-path.o
+cygcheck.exe: cygcheck.o bloda.o mingw-path.o dump_setup.o
-path-mount.o: path.cc
- ${COMPILE.cc} -c -DFSTAB_ONLY -o $@ $<
-mount.exe: path-mount.o
+mount.exe: path.o
.PHONY: tzmap
tzmap:
diff --git a/winsup/utils/mingw-path.cc b/winsup/utils/mingw-path.cc
new file mode 100644
index 000000000000..6c60a8eb9bae
--- /dev/null
+++ b/winsup/utils/mingw-path.cc
@@ -0,0 +1,795 @@
+/* path.cc
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* The purpose of this file is to hide all the details about accessing
+ Cygwin's mount table, shortcuts, etc. If the format or location of
+ the mount table, or the shortcut format changes, this is the file to
+ change to match it. */
+
+#include "path.cc"
+
+/* Used when treating / and \ as equivalent. */
+#define isslash(ch) \
+ ({ \
+ char __c = (ch); \
+ ((__c) == '/' || (__c) == '\\'); \
+ })
+
+
+static const GUID GUID_shortcut =
+ {0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
+
+enum {
+ WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
+ WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
+ WSH_FLAG_DESC = 0x04, /* Contains a description. */
+ WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
+ WSH_FLAG_WD = 0x10, /* Contains a working dir. */
+ WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
+ WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
+};
+
+struct win_shortcut_hdr
+ {
+ DWORD size; /* Header size in bytes. Must contain 0x4c. */
+ GUID magic; /* GUID of shortcut files. */
+ DWORD flags; /* Content flags. See above. */
+
+ /* The next fields from attr to icon_no are always set to 0 in Cygwin
+ and U/Win shortcuts. */
+ DWORD attr; /* Target file attributes. */
+ FILETIME ctime; /* These filetime items are never touched by the */
+ FILETIME mtime; /* system, apparently. Values don't matter. */
+ FILETIME atime;
+ DWORD filesize; /* Target filesize. */
+ DWORD icon_no; /* Icon number. */
+
+ DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
+ DWORD hotkey; /* Hotkey value. Set to 0. */
+ DWORD dummy[2]; /* Future extension probably. Always 0. */
+ };
+
+static bool
+cmp_shortcut_header (win_shortcut_hdr *file_header)
+{
+ /* A Cygwin or U/Win shortcut only contains a description and a relpath.
+ Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
+ always set to SW_NORMAL. */
+ return file_header->size == sizeof (win_shortcut_hdr)
+ && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
+ && (file_header->flags & ~WSH_FLAG_IDLIST)
+ == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
+ && file_header->run == SW_NORMAL;
+}
+
+int
+get_word (HANDLE fh, int offset)
+{
+ unsigned short rv;
+ unsigned r;
+
+ SetLastError(NO_ERROR);
+ if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
+ && GetLastError () != NO_ERROR)
+ return -1;
+
+ if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0))
+ return -1;
+
+ return rv;
+}
+
+/*
+ * Check the value of GetLastError() to find out whether there was an error.
+ */
+int
+get_dword (HANDLE fh, int offset)
+{
+ int rv;
+ unsigned r;
+
+ SetLastError(NO_ERROR);
+ if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
+ && GetLastError () != NO_ERROR)
+ return -1;
+
+ if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0))
+ return -1;
+
+ return rv;
+}
+
+#define EXE_MAGIC ((int)*(unsigned short *)"MZ")
+#define SHORTCUT_MAGIC ((int)*(unsigned short *)"L\0")
+#define SYMLINK_COOKIE "!<symlink>"
+#define SYMLINK_MAGIC ((int)*(unsigned short *)SYMLINK_COOKIE)
+
+bool
+is_exe (HANDLE fh)
+{
+ int magic = get_word (fh, 0x0);
+ return magic == EXE_MAGIC;
+}
+
+bool
+is_symlink (HANDLE fh)
+{
+ bool ret = false;
+ int magic = get_word (fh, 0x0);
+ if (magic != SHORTCUT_MAGIC && magic != SYMLINK_MAGIC)
+ goto out;
+ DWORD got;
+ BY_HANDLE_FILE_INFORMATION local;
+ if (!GetFileInformationByHandle (fh, &local))
+ return false;
+ if (magic == SHORTCUT_MAGIC)
+ {
+ DWORD size;
+ if (!local.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ goto out; /* Not a Cygwin symlink. */
+ if ((size = GetFileSize (fh, NULL)) > 8192)
+ goto out; /* Not a Cygwin symlink. */
+ char buf[size];
+ SetFilePointer (fh, 0, 0, FILE_BEGIN);
+ if (!ReadFile (fh, buf, size, &got, 0))
+ goto out;
+ if (got != size || !cmp_shortcut_header ((win_shortcut_hdr *) buf))
+ goto out; /* Not a Cygwin symlink. */
+ /* TODO: check for invalid path contents
+ (see symlink_info::check() in ../cygwin/path.cc) */
+ }
+ else /* magic == SYMLINK_MAGIC */
+ {
+ if (!(local.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
+ goto out; /* Not a Cygwin symlink. */
+ char buf[sizeof (SYMLINK_COOKIE) - 1];
+ SetFilePointer (fh, 0, 0, FILE_BEGIN);
+ if (!ReadFile (fh, buf, sizeof (buf), &got, 0))
+ goto out;
+ if (got != sizeof (buf) ||
+ memcmp (buf, SYMLINK_COOKIE, sizeof (buf)) != 0)
+ goto out; /* Not a Cygwin symlink. */
+ }
+ ret = true;
+out:
+ SetFilePointer (fh, 0, 0, FILE_BEGIN);
+ return ret;
+}
+
+/* Assumes is_symlink(fh) is true */
+bool
+readlink (HANDLE fh, char *path, size_t maxlen)
+{
+ DWORD rv;
+ char *buf, *cp;
+ unsigned short len;
+ win_shortcut_hdr *file_header;
+ BY_HANDLE_FILE_INFORMATION fi;
+
+ if (!GetFileInformationByHandle (fh, &fi)
+ || fi.nFileSizeHigh != 0
+ || fi.nFileSizeLow > 4 * 65536)
+ return false;
+
+ buf = (char *) alloca (fi.nFileSizeLow + 1);
+ file_header = (win_shortcut_hdr *) buf;
+
+ if (!ReadFile (fh, buf, fi.nFileSizeLow, &rv, NULL)
+ || rv != fi.nFileSizeLow)
+ return false;
+
+ if (fi.nFileSizeLow > sizeof (file_header)
+ && cmp_shortcut_header (file_header))
+ {
+ cp = buf + sizeof (win_shortcut_hdr);
+ if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
+ cp += *(unsigned short *) cp + 2;
+ if (!(len = *(unsigned short *) cp))
+ return false;
+ cp += 2;
+ /* Has appended full path? If so, use it instead of description. */
+ unsigned short relpath_len = *(unsigned short *) (cp + len);
+ if (cp + len + 2 + relpath_len < buf + fi.nFileSizeLow)
+ {
+ cp += len + 2 + relpath_len;
+ len = *(unsigned short *) cp;
+ cp += 2;
+ }
+ if (*(PWCHAR) cp == 0xfeff) /* BOM */
+ {
+ size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
+ if (wlen == (size_t) -1 || wlen + 1 > maxlen)
+ return false;
+ wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
+ }
+ else if ((size_t) (len + 1) > maxlen)
+ return false;
+ else
+ memcpy (path, cp, len);
+ path[len] = '\0';
+ return true;
+ }
+ else if (strncmp (buf, SYMLINK_COOKIE, strlen (SYMLINK_COOKIE)) == 0
+ && buf[fi.nFileSizeLow - 1] == '\0')
+ {
+ cp = buf + strlen (SYMLINK_COOKIE);
+ if (*(PWCHAR) cp == 0xfeff) /* BOM */
+ {
+ size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
+ if (wlen == (size_t) -1 || wlen + 1 > maxlen)
+ return false;
+ wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
+ }
+ else if (fi.nFileSizeLow - strlen (SYMLINK_COOKIE) > maxlen)
+ return false;
+ else
+ strcpy (path, cp);
+ return true;
+ }
+ else
+ return false;
+}
+
+static struct opt
+{
+ const char *name;
+ unsigned val;
+ bool clear;
+} oopts[] =
+{
+ {"acl", MOUNT_NOACL, 1},
+ {"auto", 0, 0},
+ {"binary", MOUNT_TEXT, 1},
+ {"cygexec", MOUNT_CYGWIN_EXEC, 0},
+ {"dos", MOUNT_DOS, 0},
+ {"exec", MOUNT_EXEC, 0},
+ {"ihash", MOUNT_IHASH, 0},
+ {"noacl", MOUNT_NOACL, 0},
+ {"nosuid", 0, 0},
+ {"notexec", MOUNT_NOTEXEC, 0},
+ {"nouser", MOUNT_SYSTEM, 0},
+ {"override", MOUNT_OVERRIDE, 0},
+ {"posix=0", MOUNT_NOPOSIX, 0},
+ {"posix=1", MOUNT_NOPOSIX, 1},
+ {"text", MOUNT_TEXT, 0},
+ {"user", MOUNT_SYSTEM, 1}
+};
+
+#ifndef TESTSUITE
+static bool
+read_flags (char *options, unsigned &flags)
+{
+ while (*options)
+ {
+ char *p = strchr (options, ',');
+ if (p)
+ *p++ = '\0';
+ else
+ p = strchr (options, '\0');
+
+ for (opt *o = oopts;
+ o < (oopts + (sizeof (oopts) / sizeof (oopts[0])));
+ o++)
+ if (strcmp (options, o->name) == 0)
+ {
+ if (o->clear)
+ flags &= ~o->val;
+ else
+ flags |= o->val;
+ goto gotit;
+ }
+ return false;
+
+ gotit:
+ options = p;
+ }
+ return true;
+}
+
+#define BUFSIZE 65536
+
+static char *
+get_user ()
+{
+ static char user[UNLEN + 1];
+ char *userenv;
+
+ user[0] = '\0';
+ if ((userenv = getenv ("USER")) || (userenv = getenv ("USERNAME")))
+ strncat (user, userenv, UNLEN);
+ return user;
+}
+
+void
+from_fstab (bool user, PWCHAR path, PWCHAR path_end)
+{
+ mnt_t *m = mount_table + max_mount_entry;
+ char buf[BUFSIZE];
+
+ if (!user)
+ {
+ /* Create a default root dir from path. */
+ wcstombs (buf, path, BUFSIZE);
+ unconvert_slashes (buf);
+ char *native_path = buf;
+ if (!strncmp (native_path, "\\\\?\\", 4))
+ native_path += 4;
+ if (!strncmp (native_path, "UNC\\", 4))
+ *(native_path += 2) = '\\';
+ m->posix = strdup ("/");
+ m->native = strdup (native_path);
+ m->flags = MOUNT_SYSTEM | MOUNT_IMMUTABLE | MOUNT_AUTOMATIC;
+ ++m;
+ /* Create default /usr/bin and /usr/lib entries. */
+ char *trail = strchr (native_path, '\0');
+ strcpy (trail, "\\bin");
+ m->posix = strdup ("/usr/bin");
+ m->native = strdup (native_path);
+ m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
+ ++m;
+ strcpy (trail, "\\lib");
+ m->posix = strdup ("/usr/lib");
+ m->native = strdup (native_path);
+ m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
+ ++m;
+ /* Create a default cygdrive entry. Note that this is a user entry.
+ This allows to override it with mount, unless the sysadmin created
+ a cygdrive entry in /etc/fstab. */
+ m->posix = strdup (CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX);
+ m->native = strdup ("cygdrive prefix");
+ m->flags = MOUNT_CYGDRIVE;
+ ++m;
+ max_mount_entry = m - mount_table;
+ }
+
+ PWCHAR u = wcscpy (path_end, L"\\etc\\fstab") + 10;
+ if (user)
+ mbstowcs (wcscpy (u, L".d\\") + 3, get_user (), BUFSIZE - (u - path));
+ HANDLE h = CreateFileW (path, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return;
+ char *got = buf;
+ DWORD len = 0;
+ /* Using BUFSIZE-1 leaves space to append two \0. */
+ while (ReadFile (h, got, BUFSIZE - 1 - (got - buf),
+ &len, NULL))
+ {
+ char *end;
+
+ /* Set end marker. */
+ got[len] = got[len + 1] = '\0';
+ /* Set len to the absolute len of bytes in buf. */
+ len += got - buf;
+ /* Reset got to start reading at the start of the buffer again. */
+ got = buf;
+ while (got < buf + len && (end = strchr (got, '\n')))
+ {
+ end[end[-1] == '\r' ? -1 : 0] = '\0';
+ if (from_fstab_line (m, got, user))
+ ++m;
+ got = end + 1;
+ }
+ if (len < BUFSIZE - 1)
+ break;
+ /* We have to read once more. Move remaining bytes to the start of
+ the buffer and reposition got so that it points to the end of
+ the remaining bytes. */
+ len = buf + len - got;
+ memmove (buf, got, len);
+ got = buf + len;
+ buf[len] = buf[len + 1] = '\0';
+ }
+ if (got > buf && from_fstab_line (m, got, user))
+ ++m;
+ max_mount_entry = m - mount_table;
+ CloseHandle (h);
+}
+
+static int
+mnt_sort (const void *a, const void *b)
+{
+ const mnt_t *ma = (const mnt_t *) a;
+ const mnt_t *mb = (const mnt_t *) b;
+ int ret;
+
+ ret = (ma->flags & MOUNT_CYGDRIVE) - (mb->flags & MOUNT_CYGDRIVE);
+ if (ret)
+ return ret;
+ ret = (ma->flags & MOUNT_SYSTEM) - (mb->flags & MOUNT_SYSTEM);
+ if (ret)
+ return ret;
+ return strcmp (ma->posix, mb->posix);
+}
+
+extern "C" WCHAR cygwin_dll_path[];
+
+static void
+read_mounts ()
+{
+ HKEY setup_key;
+ LONG ret;
+ DWORD len;
+ WCHAR path[32768];
+ PWCHAR path_end;
+
+ for (mnt_t *m1 = mount_table; m1->posix; m1++)
+ {
+ free (m1->posix);
+ if (m1->native)
+ free ((char *) m1->native);
+ m1->posix = NULL;
+ }
+ max_mount_entry = 0;
+
+ /* First fetch the cygwin1.dll path from the LoadLibrary call in load_cygwin.
+ This utilizes the DLL search order to find a matching cygwin1.dll and to
+ compute the installation path from that DLL's path. */
+ if (cygwin_dll_path[0])
+ wcscpy (path, cygwin_dll_path);
+ /* If we can't load cygwin1.dll, check where cygcheck is living itself and
+ try to fetch installation path from here. Does cygwin1.dll exist in the
+ same path? This should only kick in if the cygwin1.dll in the same path
+ has been made non-executable for the current user accidentally. */
+ else if (!GetModuleFileNameW (NULL, path, 32768))
+ return;
+ path_end = wcsrchr (path, L'\\');
+ if (path_end)
+ {
+ if (!cygwin_dll_path[0])
+ {
+ wcscpy (path_end, L"\\cygwin1.dll");
+ DWORD attr = GetFileAttributesW (path);
+ if (attr == (DWORD) -1
+ || (attr & (FILE_ATTRIBUTE_DIRECTORY
+ | FILE_ATTRIBUTE_REPARSE_POINT)))
+ path_end = NULL;
+ }
+ if (path_end)
+ {
+ *path_end = L'\0';
+ path_end = wcsrchr (path, L'\\');
+ }
+ }
+ /* If we can't create a valid installation dir from that, try to fetch
+ the installation dir from the setup registry key. */
+ if (!path_end)
+ {
+ for (int i = 0; i < 2; ++i)
+ if ((ret = RegOpenKeyExW (i ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ L"Software\\Cygwin\\setup", 0,
+ KEY_READ, &setup_key)) == ERROR_SUCCESS)
+ {
+ len = 32768 * sizeof (WCHAR);
+ ret = RegQueryValueExW (setup_key, L"rootdir", NULL, NULL,
+ (PBYTE) path, &len);
+ RegCloseKey (setup_key);
+ if (ret == ERROR_SUCCESS)
+ break;
+ }
+ if (ret == ERROR_SUCCESS)
+ path_end = wcschr (path, L'\0');
+ }
+ /* If we can't fetch an installation dir, bail out. */
+ if (!path_end)
+ return;
+ *path_end = L'\0';
+
+ from_fstab (false, path, path_end);
+ from_fstab (true, path, path_end);
+ qsort (mount_table, max_mount_entry, sizeof (mnt_t), mnt_sort);
+}
+#endif /* !defined(TESTSUITE) */
+
+/* Return non-zero if PATH1 is a prefix of PATH2.
+ Both are assumed to be of the same path style and / vs \ usage.
+ Neither may be "".
+ LEN1 = strlen (PATH1). It's passed because often it's already known.
+
+ Examples:
+ /foo/ is a prefix of /foo <-- may seem odd, but desired
+ /foo is a prefix of /foo/
+ / is a prefix of /foo/bar
+ / is not a prefix of foo/bar
+ foo/ is a prefix foo/bar
+ /foo is not a prefix of /foobar
+*/
+
+static int
+path_prefix_p (const char *path1, const char *path2, size_t len1)
+{
+ /* Handle case where PATH1 has trailing '/' and when it doesn't. */
+ if (len1 > 0 && isslash (path1[len1 - 1]))
+ len1--;
+
+ if (len1 == 0)
+ return isslash (path2[0]) && !isslash (path2[1]);
+
+ if (strncasecmp (path1, path2, len1) != 0)
+ return 0;
+
+ return isslash (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';
+}
+
+static char *
+vconcat (const char *s, va_list v)
+{
+ int len;
+ char *rv, *arg;
+ va_list save_v = v;
+ int unc;
+
+ if (!s)
+ return 0;
+
+ len = strlen (s);
+
+ unc = isslash (*s) && isslash (s[1]);
+
+ while (1)
+ {
+ arg = va_arg (v, char *);
+ if (arg == 0)
+ break;
+ len += strlen (arg);
+ }
+ va_end (v);
+
+ rv = (char *) malloc (len + 1);
+ strcpy (rv, s);
+ v = save_v;
+ while (1)
+ {
+ arg = va_arg (v, char *);
+ if (arg == 0)
+ break;
+ strcat (rv, arg);
+ }
+ va_end (v);
+
+ char *d, *p;
+
+ /* concat is only used for urls and files, so we can safely
+ canonicalize the results */
+ for (p = d = rv; *p; p++)
+ {
+ *d++ = *p;
+ /* special case for URLs */
+ if (*p == ':' && p[1] == '/' && p[2] == '/' && p > rv + 1)
+ {
+ *d++ = *++p;
+ *d++ = *++p;
+ }
+ else if (isslash (*p))
+ {
+ if (p == rv && unc)
+ *d++ = *p++;
+ while (p[1] == '/')
+ p++;
+ }
+ }
+ *d = 0;
+
+ return rv;
+}
+
+static char *
+concat (const char *s, ...)
+{
+ va_list v;
+
+ va_start (v, s);
+
+ return vconcat (s, v);
+}
+
+/* This is a helper function for when vcygpath is passed what appears
+ to be a relative POSIX path. We take a Win32 CWD (either as specified
+ in 'cwd' or as retrieved with GetCurrentDirectory() if 'cwd' is NULL)
+ and find the mount table entry with the longest match. We replace the
+ matching portion with the corresponding POSIX prefix, and to that append
+ 's' and anything in 'v'. The returned result is a mostly-POSIX
+ absolute path -- 'mostly' because the portions of CWD that didn't
+ match the mount prefix will still have '\\' separators. */
+static char *
+rel_vconcat (const char *cwd, const char *s, va_list v)
+{
+ char pathbuf[MAX_PATH];
+ if (!cwd || *cwd == '\0')
+ {
+ if (!GetCurrentDirectory (MAX_PATH, pathbuf))
+ return NULL;
+ cwd = pathbuf;
+ }
+
+ size_t max_len = 0;
+ mnt_t *m, *match = NULL;
+
+ for (m = mount_table; m->posix; m++)
+ {
+ if (m->flags & MOUNT_CYGDRIVE)
+ continue;
+
+ size_t n = strlen (m->native);
+ if (n < max_len || !path_prefix_p (m->native, cwd, n))
+ continue;
+ max_len = n;
+ match = m;
+ }
+
+ char *temppath;
+ if (!match)
+ // No prefix matched - best effort to return meaningful value.
+ temppath = concat (cwd, "/", s, NULL);
+ else if (strcmp (match->posix, "/") != 0)
+ // Matched on non-root. Copy matching prefix + remaining 'path'.
+ temppath = concat (match->posix, cwd + max_len, "/", s, NULL);
+ else if (cwd[max_len] == '\0')
+ // Matched on root and there's no remaining 'path'.
+ temppath = concat ("/", s, NULL);
+ else if (isslash (cwd[max_len]))
+ // Matched on root but remaining 'path' starts with a slash anyway.
+ temppath = concat (cwd + max_len, "/", s, NULL);
+ else
+ temppath = concat ("/", cwd + max_len, "/", s, NULL);
+
+ char *res = vconcat (temppath, v);
+ free (temppath);
+ return res;
+}
+
+/* Convert a POSIX path in 's' to an absolute Win32 path, and append
+ anything in 'v' to the end, returning the result. If 's' is a
+ relative path then 'cwd' is used as the working directory to make
+ it absolute. Pass NULL in 'cwd' to use GetCurrentDirectory. */
+static char *
+vcygpath (const char *cwd, const char *s, va_list v)
+{
+ size_t max_len = 0;
+ mnt_t *m, *match = NULL;
+
+#ifndef TESTSUITE
+ if (!max_mount_entry)
+ read_mounts ();
+#endif
+ char *path;
+ if (s[0] == '.' && isslash (s[1]))
+ s += 2;
+
+ if (s[0] == '/' || s[1] == ':') /* FIXME: too crude? */
+ path = vconcat (s, v);
+ else
+ path = rel_vconcat (cwd, s, v);
+
+ if (!path)
+ return NULL;
+
+ if (strncmp (path, "/./", 3) == 0)
+ memmove (path + 1, path + 3, strlen (path + 3) + 1);
+
+ for (m = mount_table; m->posix; m++)
+ {
+ size_t n = strlen (m->posix);
+ if (n < max_len || !path_prefix_p (m->posix, path, n))
+ continue;
+ if (m->flags & MOUNT_CYGDRIVE)
+ {
+ if (strlen (path) < n + 2)
+ continue;
+ /* If cygdrive path is just '/', fix n for followup evaluation. */
+ if (n == 1)
+ n = 0;
+ if (path[n] != '/')
+ continue;
+ if (!isalpha (path[n + 1]))
+ continue;
+ if (path[n + 2] != '/')
+ continue;
+ }
+ max_len = n;
+ match = m;
+ }
+
+ char *native;
+ if (match == NULL)
+ native = strdup (path);
+ else if (max_len == strlen (path))
+ native = strdup (match->native);
+ else if (match->flags & MOUNT_CYGDRIVE)
+ {
+ char drive[3] = { path[max_len + 1], ':', '\0' };
+ native = concat (drive, path + max_len + 2, NULL);
+ }
+ else if (isslash (path[max_len]))
+ native = concat (match->native, path + max_len, NULL);
+ else
+ native = concat (match->native, "\\", path + max_len, NULL);
+ free (path);
+
+ unconvert_slashes (native);
+ for (char *s = strstr (native + 1, "\\.\\"); s && *s; s = strstr (s, "\\.\\"))
+ memmove (s + 1, s + 3, strlen (s + 3) + 1);
+ return native;
+}
+
+char *
+cygpath_rel (const char *cwd, const char *s, ...)
+{
+ va_list v;
+
+ va_start (v, s);
+
+ return vcygpath (cwd, s, v);
+}
+
+char *
+cygpath (const char *s, ...)
+{
+ va_list v;
+
+ va_start (v, s);
+
+ return vcygpath (NULL, s, v);
+}
+
+static mnt_t *m = NULL;
+
+extern "C" FILE *
+setmntent (const char *, const char *)
+{
+ m = mount_table;
+#ifndef TESTSUITE
+ if (!max_mount_entry)
+ read_mounts ();
+#endif
+ return NULL;
+}
+
+extern "C" struct mntent *
+getmntent (FILE *)
+{
+ static mntent mnt;
+ if (!m->posix)
+ return NULL;
+
+ mnt.mnt_fsname = (char *) m->native;
+ mnt.mnt_dir = (char *) m->posix;
+ if (!mnt.mnt_type)
+ mnt.mnt_type = (char *) malloc (16);
+ if (!mnt.mnt_opts)
+ mnt.mnt_opts = (char *) malloc (64);
+
+ strcpy (mnt.mnt_type,
+ (char *) ((m->flags & MOUNT_SYSTEM) ? "system" : "user"));
+
+ if (m->flags & MOUNT_TEXT)
+ strcpy (mnt.mnt_opts, (char *) "text");
+ else
+ strcpy (mnt.mnt_opts, (char *) "binary");
+
+ if (m->flags & MOUNT_CYGWIN_EXEC)
+ strcat (mnt.mnt_opts, (char *) ",cygexec");
+ else if (m->flags & MOUNT_EXEC)
+ strcat (mnt.mnt_opts, (char *) ",exec");
+ else if (m->flags & MOUNT_NOTEXEC)
+ strcat (mnt.mnt_opts, (char *) ",notexec");
+
+ if (m->flags & MOUNT_NOACL)
+ strcat (mnt.mnt_opts, (char *) ",noacl");
+
+ if (m->flags & MOUNT_NOPOSIX)
+ strcat (mnt.mnt_opts, (char *) ",posix=0");
+
+ if (m->flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
+ strcat (mnt.mnt_opts, (char *) ",auto");
+
+ mnt.mnt_freq = 1;
+ mnt.mnt_passno = 1;
+ m++;
+ return &mnt;
+}
diff --git a/winsup/utils/path.cc b/winsup/utils/path.cc
index 29344be02033..7e3f3cab74be 100644
--- a/winsup/utils/path.cc
+++ b/winsup/utils/path.cc
@@ -26,235 +26,11 @@ details. */
#define _NOMNTENT_MACROS
#include "../cygwin/include/mntent.h"
#include "testsuite.h"
-#ifdef FSTAB_ONLY
+#ifdef __CYGWIN__
#include <sys/cygwin.h>
#endif
#include "loadlib.h"
-#ifndef FSTAB_ONLY
-/* Used when treating / and \ as equivalent. */
-#define isslash(ch) \
- ({ \
- char __c = (ch); \
- ((__c) == '/' || (__c) == '\\'); \
- })
-
-
-static const GUID GUID_shortcut =
- {0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
-
-enum {
- WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
- WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
- WSH_FLAG_DESC = 0x04, /* Contains a description. */
- WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
- WSH_FLAG_WD = 0x10, /* Contains a working dir. */
- WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
- WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
-};
-
-struct win_shortcut_hdr
- {
- DWORD size; /* Header size in bytes. Must contain 0x4c. */
- GUID magic; /* GUID of shortcut files. */
- DWORD flags; /* Content flags. See above. */
-
- /* The next fields from attr to icon_no are always set to 0 in Cygwin
- and U/Win shortcuts. */
- DWORD attr; /* Target file attributes. */
- FILETIME ctime; /* These filetime items are never touched by the */
- FILETIME mtime; /* system, apparently. Values don't matter. */
- FILETIME atime;
- DWORD filesize; /* Target filesize. */
- DWORD icon_no; /* Icon number. */
-
- DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
- DWORD hotkey; /* Hotkey value. Set to 0. */
- DWORD dummy[2]; /* Future extension probably. Always 0. */
- };
-
-static bool
-cmp_shortcut_header (win_shortcut_hdr *file_header)
-{
- /* A Cygwin or U/Win shortcut only contains a description and a relpath.
- Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
- always set to SW_NORMAL. */
- return file_header->size == sizeof (win_shortcut_hdr)
- && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
- && (file_header->flags & ~WSH_FLAG_IDLIST)
- == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
- && file_header->run == SW_NORMAL;
-}
-
-int
-get_word (HANDLE fh, int offset)
-{
- unsigned short rv;
- unsigned r;
-
- SetLastError(NO_ERROR);
- if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
- && GetLastError () != NO_ERROR)
- return -1;
-
- if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0))
- return -1;
-
- return rv;
-}
-
-/*
- * Check the value of GetLastError() to find out whether there was an error.
- */
-int
-get_dword (HANDLE fh, int offset)
-{
- int rv;
- unsigned r;
-
- SetLastError(NO_ERROR);
- if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
- && GetLastError () != NO_ERROR)
- return -1;
-
- if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0))
- return -1;
-
- return rv;
-}
-
-#define EXE_MAGIC ((int)*(unsigned short *)"MZ")
-#define SHORTCUT_MAGIC ((int)*(unsigned short *)"L\0")
-#define SYMLINK_COOKIE "!<symlink>"
-#define SYMLINK_MAGIC ((int)*(unsigned short *)SYMLINK_COOKIE)
-
-bool
-is_exe (HANDLE fh)
-{
- int magic = get_word (fh, 0x0);
- return magic == EXE_MAGIC;
-}
-
-bool
-is_symlink (HANDLE fh)
-{
- bool ret = false;
- int magic = get_word (fh, 0x0);
- if (magic != SHORTCUT_MAGIC && magic != SYMLINK_MAGIC)
- goto out;
- DWORD got;
- BY_HANDLE_FILE_INFORMATION local;
- if (!GetFileInformationByHandle (fh, &local))
- return false;
- if (magic == SHORTCUT_MAGIC)
- {
- DWORD size;
- if (!local.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- goto out; /* Not a Cygwin symlink. */
- if ((size = GetFileSize (fh, NULL)) > 8192)
- goto out; /* Not a Cygwin symlink. */
- char buf[size];
- SetFilePointer (fh, 0, 0, FILE_BEGIN);
- if (!ReadFile (fh, buf, size, &got, 0))
- goto out;
- if (got != size || !cmp_shortcut_header ((win_shortcut_hdr *) buf))
- goto out; /* Not a Cygwin symlink. */
- /* TODO: check for invalid path contents
- (see symlink_info::check() in ../cygwin/path.cc) */
- }
- else /* magic == SYMLINK_MAGIC */
- {
- if (!(local.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
- goto out; /* Not a Cygwin symlink. */
- char buf[sizeof (SYMLINK_COOKIE) - 1];
- SetFilePointer (fh, 0, 0, FILE_BEGIN);
- if (!ReadFile (fh, buf, sizeof (buf), &got, 0))
- goto out;
- if (got != sizeof (buf) ||
- memcmp (buf, SYMLINK_COOKIE, sizeof (buf)) != 0)
- goto out; /* Not a Cygwin symlink. */
- }
- ret = true;
-out:
- SetFilePointer (fh, 0, 0, FILE_BEGIN);
- return ret;
-}
-
-/* Assumes is_symlink(fh) is true */
-bool
-readlink (HANDLE fh, char *path, size_t maxlen)
-{
- DWORD rv;
- char *buf, *cp;
- unsigned short len;
- win_shortcut_hdr *file_header;
- BY_HANDLE_FILE_INFORMATION fi;
-
- if (!GetFileInformationByHandle (fh, &fi)
- || fi.nFileSizeHigh != 0
- || fi.nFileSizeLow > 4 * 65536)
- return false;
-
- buf = (char *) alloca (fi.nFileSizeLow + 1);
- file_header = (win_shortcut_hdr *) buf;
-
- if (!ReadFile (fh, buf, fi.nFileSizeLow, &rv, NULL)
- || rv != fi.nFileSizeLow)
- return false;
-
- if (fi.nFileSizeLow > sizeof (file_header)
- && cmp_shortcut_header (file_header))
- {
- cp = buf + sizeof (win_shortcut_hdr);
- if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
- cp += *(unsigned short *) cp + 2;
- if (!(len = *(unsigned short *) cp))
- return false;
- cp += 2;
- /* Has appended full path? If so, use it instead of description. */
- unsigned short relpath_len = *(unsigned short *) (cp + len);
- if (cp + len + 2 + relpath_len < buf + fi.nFileSizeLow)
- {
- cp += len + 2 + relpath_len;
- len = *(unsigned short *) cp;
- cp += 2;
- }
- if (*(PWCHAR) cp == 0xfeff) /* BOM */
- {
- size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
- if (wlen == (size_t) -1 || wlen + 1 > maxlen)
- return false;
- wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
- }
- else if ((size_t) (len + 1) > maxlen)
- return false;
- else
- memcpy (path, cp, len);
- path[len] = '\0';
- return true;
- }
- else if (strncmp (buf, SYMLINK_COOKIE, strlen (SYMLINK_COOKIE)) == 0
- && buf[fi.nFileSizeLow - 1] == '\0')
- {
- cp = buf + strlen (SYMLINK_COOKIE);
- if (*(PWCHAR) cp == 0xfeff) /* BOM */
- {
- size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
- if (wlen == (size_t) -1 || wlen + 1 > maxlen)
- return false;
- wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
- }
- else if (fi.nFileSizeLow - strlen (SYMLINK_COOKIE) > maxlen)
- return false;
- else
- strcpy (path, cp);
- return true;
- }
- else
- return false;
-}
-#endif /* !FSTAB_ONLY */
-
#ifndef TESTSUITE
mnt_t mount_table[255];
int max_mount_entry;
@@ -302,61 +78,8 @@ conv_fstab_spaces (char *field)
return field;
}
-#ifndef FSTAB_ONLY
-static struct opt
-{
- const char *name;
- unsigned val;
- bool clear;
-} oopts[] =
-{
- {"acl", MOUNT_NOACL, 1},
- {"auto", 0, 0},
- {"binary", MOUNT_TEXT, 1},
- {"cygexec", MOUNT_CYGWIN_EXEC, 0},
- {"dos", MOUNT_DOS, 0},
- {"exec", MOUNT_EXEC, 0},
- {"ihash", MOUNT_IHASH, 0},
- {"noacl", MOUNT_NOACL, 0},
- {"nosuid", 0, 0},
- {"notexec", MOUNT_NOTEXEC, 0},
- {"nouser", MOUNT_SYSTEM, 0},
- {"override", MOUNT_OVERRIDE, 0},
- {"posix=0", MOUNT_NOPOSIX, 0},
- {"posix=1", MOUNT_NOPOSIX, 1},
- {"text", MOUNT_TEXT, 0},
- {"user", MOUNT_SYSTEM, 1}
-};
-
-static bool
-read_flags (char *options, unsigned &flags)
-{
- while (*options)
- {
- char *p = strchr (options, ',');
- if (p)
- *p++ = '\0';
- else
- p = strchr (options, '\0');
-
- for (opt *o = oopts;
- o < (oopts + (sizeof (oopts) / sizeof (oopts[0])));
- o++)
- if (strcmp (options, o->name) == 0)
- {
- if (o->clear)
- flags &= ~o->val;
- else
- flags |= o->val;
- goto gotit;
- }
- return false;
-
- gotit:
- options = p;
- }
- return true;
-}
+#ifndef __CYGWIN__
+static bool read_flags (char *, unsigned &);
#endif
bool
@@ -392,7 +115,7 @@ from_fstab_line (mnt_t *m, char *line, bool user)
cend = find_ws (c);
*cend = '\0';
unsigned mount_flags = MOUNT_SYSTEM;
-#ifndef FSTAB_ONLY
+#ifndef __CYGWIN__
if (!read_flags (c, mount_flags))
#else
if (cygwin_internal (CW_CVT_MNT_OPTS, &c, &mount_flags))
@@ -453,514 +176,4 @@ from_fstab_line (mnt_t *m, char *line, bool user)
return true;
}
-#ifndef FSTAB_ONLY
-
-#define BUFSIZE 65536
-
-static char *
-get_user ()
-{
- static char user[UNLEN + 1];
- char *userenv;
-
- user[0] = '\0';
- if ((userenv = getenv ("USER")) || (userenv = getenv ("USERNAME")))
- strncat (user, userenv, UNLEN);
- return user;
-}
-
-void
-from_fstab (bool user, PWCHAR path, PWCHAR path_end)
-{
- mnt_t *m = mount_table + max_mount_entry;
- char buf[BUFSIZE];
-
- if (!user)
- {
- /* Create a default root dir from path. */
- wcstombs (buf, path, BUFSIZE);
- unconvert_slashes (buf);
- char *native_path = buf;
- if (!strncmp (native_path, "\\\\?\\", 4))
- native_path += 4;
- if (!strncmp (native_path, "UNC\\", 4))
- *(native_path += 2) = '\\';
- m->posix = strdup ("/");
- m->native = strdup (native_path);
- m->flags = MOUNT_SYSTEM | MOUNT_IMMUTABLE | MOUNT_AUTOMATIC;
- ++m;
- /* Create default /usr/bin and /usr/lib entries. */
- char *trail = strchr (native_path, '\0');
- strcpy (trail, "\\bin");
- m->posix = strdup ("/usr/bin");
- m->native = strdup (native_path);
- m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
- ++m;
- strcpy (trail, "\\lib");
- m->posix = strdup ("/usr/lib");
- m->native = strdup (native_path);
- m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
- ++m;
- /* Create a default cygdrive entry. Note that this is a user entry.
- This allows to override it with mount, unless the sysadmin created
- a cygdrive entry in /etc/fstab. */
- m->posix = strdup (CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX);
- m->native = strdup ("cygdrive prefix");
- m->flags = MOUNT_CYGDRIVE;
- ++m;
- max_mount_entry = m - mount_table;
- }
-
- PWCHAR u = wcscpy (path_end, L"\\etc\\fstab") + 10;
- if (user)
- mbstowcs (wcscpy (u, L".d\\") + 3, get_user (), BUFSIZE - (u - path));
- HANDLE h = CreateFileW (path, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (h == INVALID_HANDLE_VALUE)
- return;
- char *got = buf;
- DWORD len = 0;
- /* Using BUFSIZE-1 leaves space to append two \0. */
- while (ReadFile (h, got, BUFSIZE - 1 - (got - buf),
- &len, NULL))
- {
- char *end;
-
- /* Set end marker. */
- got[len] = got[len + 1] = '\0';
- /* Set len to the absolute len of bytes in buf. */
- len += got - buf;
- /* Reset got to start reading at the start of the buffer again. */
- got = buf;
- while (got < buf + len && (end = strchr (got, '\n')))
- {
- end[end[-1] == '\r' ? -1 : 0] = '\0';
- if (from_fstab_line (m, got, user))
- ++m;
- got = end + 1;
- }
- if (len < BUFSIZE - 1)
- break;
- /* We have to read once more. Move remaining bytes to the start of
- the buffer and reposition got so that it points to the end of
- the remaining bytes. */
- len = buf + len - got;
- memmove (buf, got, len);
- got = buf + len;
- buf[len] = buf[len + 1] = '\0';
- }
- if (got > buf && from_fstab_line (m, got, user))
- ++m;
- max_mount_entry = m - mount_table;
- CloseHandle (h);
-}
-#endif /* !FSTAB_ONLY */
#endif /* !TESTSUITE */
-
-#ifndef FSTAB_ONLY
-
-#ifndef TESTSUITE
-static int
-mnt_sort (const void *a, const void *b)
-{
- const mnt_t *ma = (const mnt_t *) a;
- const mnt_t *mb = (const mnt_t *) b;
- int ret;
-
- ret = (ma->flags & MOUNT_CYGDRIVE) - (mb->flags & MOUNT_CYGDRIVE);
- if (ret)
- return ret;
- ret = (ma->flags & MOUNT_SYSTEM) - (mb->flags & MOUNT_SYSTEM);
- if (ret)
- return ret;
- return strcmp (ma->posix, mb->posix);
-}
-
-extern "C" WCHAR cygwin_dll_path[];
-
-static void
-read_mounts ()
-{
- HKEY setup_key;
- LONG ret;
- DWORD len;
- WCHAR path[32768];
- PWCHAR path_end;
-
- for (mnt_t *m1 = mount_table; m1->posix; m1++)
- {
- free (m1->posix);
- if (m1->native)
- free ((char *) m1->native);
- m1->posix = NULL;
- }
- max_mount_entry = 0;
-
- /* First fetch the cygwin1.dll path from the LoadLibrary call in load_cygwin.
- This utilizes the DLL search order to find a matching cygwin1.dll and to
- compute the installation path from that DLL's path. */
- if (cygwin_dll_path[0])
- wcscpy (path, cygwin_dll_path);
- /* If we can't load cygwin1.dll, check where cygcheck is living itself and
- try to fetch installation path from here. Does cygwin1.dll exist in the
- same path? This should only kick in if the cygwin1.dll in the same path
- has been made non-executable for the current user accidentally. */
- else if (!GetModuleFileNameW (NULL, path, 32768))
- return;
- path_end = wcsrchr (path, L'\\');
- if (path_end)
- {
- if (!cygwin_dll_path[0])
- {
- wcscpy (path_end, L"\\cygwin1.dll");
- DWORD attr = GetFileAttributesW (path);
- if (attr == (DWORD) -1
- || (attr & (FILE_ATTRIBUTE_DIRECTORY
- | FILE_ATTRIBUTE_REPARSE_POINT)))
- path_end = NULL;
- }
- if (path_end)
- {
- *path_end = L'\0';
- path_end = wcsrchr (path, L'\\');
- }
- }
- /* If we can't create a valid installation dir from that, try to fetch
- the installation dir from the setup registry key. */
- if (!path_end)
- {
- for (int i = 0; i < 2; ++i)
- if ((ret = RegOpenKeyExW (i ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
- L"Software\\Cygwin\\setup", 0,
- KEY_READ, &setup_key)) == ERROR_SUCCESS)
- {
- len = 32768 * sizeof (WCHAR);
- ret = RegQueryValueExW (setup_key, L"rootdir", NULL, NULL,
- (PBYTE) path, &len);
- RegCloseKey (setup_key);
- if (ret == ERROR_SUCCESS)
- break;
- }
- if (ret == ERROR_SUCCESS)
- path_end = wcschr (path, L'\0');
- }
- /* If we can't fetch an installation dir, bail out. */
- if (!path_end)
- return;
- *path_end = L'\0';
-
- from_fstab (false, path, path_end);
- from_fstab (true, path, path_end);
- qsort (mount_table, max_mount_entry, sizeof (mnt_t), mnt_sort);
-}
-#endif /* !defined(TESTSUITE) */
-
-/* Return non-zero if PATH1 is a prefix of PATH2.
- Both are assumed to be of the same path style and / vs \ usage.
- Neither may be "".
- LEN1 = strlen (PATH1). It's passed because often it's already known.
-
- Examples:
- /foo/ is a prefix of /foo <-- may seem odd, but desired
- /foo is a prefix of /foo/
- / is a prefix of /foo/bar
- / is not a prefix of foo/bar
- foo/ is a prefix foo/bar
- /foo is not a prefix of /foobar
-*/
-
-static int
-path_prefix_p (const char *path1, const char *path2, size_t len1)
-{
- /* Handle case where PATH1 has trailing '/' and when it doesn't. */
- if (len1 > 0 && isslash (path1[len1 - 1]))
- len1--;
-
- if (len1 == 0)
- return isslash (path2[0]) && !isslash (path2[1]);
-
- if (strncasecmp (path1, path2, len1) != 0)
- return 0;
-
- return isslash (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';
-}
-
-static char *
-vconcat (const char *s, va_list v)
-{
- int len;
- char *rv, *arg;
- va_list save_v = v;
- int unc;
-
- if (!s)
- return 0;
-
- len = strlen (s);
-
- unc = isslash (*s) && isslash (s[1]);
-
- while (1)
- {
- arg = va_arg (v, char *);
- if (arg == 0)
- break;
- len += strlen (arg);
- }
- va_end (v);
-
- rv = (char *) malloc (len + 1);
- strcpy (rv, s);
- v = save_v;
- while (1)
- {
- arg = va_arg (v, char *);
- if (arg == 0)
- break;
- strcat (rv, arg);
- }
- va_end (v);
-
- char *d, *p;
-
- /* concat is only used for urls and files, so we can safely
- canonicalize the results */
- for (p = d = rv; *p; p++)
- {
- *d++ = *p;
- /* special case for URLs */
- if (*p == ':' && p[1] == '/' && p[2] == '/' && p > rv + 1)
- {
- *d++ = *++p;
- *d++ = *++p;
- }
- else if (isslash (*p))
- {
- if (p == rv && unc)
- *d++ = *p++;
- while (p[1] == '/')
- p++;
- }
- }
- *d = 0;
-
- return rv;
-}
-
-static char *
-concat (const char *s, ...)
-{
- va_list v;
-
- va_start (v, s);
-
- return vconcat (s, v);
-}
-
-/* This is a helper function for when vcygpath is passed what appears
- to be a relative POSIX path. We take a Win32 CWD (either as specified
- in 'cwd' or as retrieved with GetCurrentDirectory() if 'cwd' is NULL)
- and find the mount table entry with the longest match. We replace the
- matching portion with the corresponding POSIX prefix, and to that append
- 's' and anything in 'v'. The returned result is a mostly-POSIX
- absolute path -- 'mostly' because the portions of CWD that didn't
- match the mount prefix will still have '\\' separators. */
-static char *
-rel_vconcat (const char *cwd, const char *s, va_list v)
-{
- char pathbuf[MAX_PATH];
- if (!cwd || *cwd == '\0')
- {
- if (!GetCurrentDirectory (MAX_PATH, pathbuf))
- return NULL;
- cwd = pathbuf;
- }
-
- size_t max_len = 0;
- mnt_t *m, *match = NULL;
-
- for (m = mount_table; m->posix; m++)
- {
- if (m->flags & MOUNT_CYGDRIVE)
- continue;
-
- size_t n = strlen (m->native);
- if (n < max_len || !path_prefix_p (m->native, cwd, n))
- continue;
- max_len = n;
- match = m;
- }
-
- char *temppath;
- if (!match)
- // No prefix matched - best effort to return meaningful value.
- temppath = concat (cwd, "/", s, NULL);
- else if (strcmp (match->posix, "/") != 0)
- // Matched on non-root. Copy matching prefix + remaining 'path'.
- temppath = concat (match->posix, cwd + max_len, "/", s, NULL);
- else if (cwd[max_len] == '\0')
- // Matched on root and there's no remaining 'path'.
- temppath = concat ("/", s, NULL);
- else if (isslash (cwd[max_len]))
- // Matched on root but remaining 'path' starts with a slash anyway.
- temppath = concat (cwd + max_len, "/", s, NULL);
- else
- temppath = concat ("/", cwd + max_len, "/", s, NULL);
-
- char *res = vconcat (temppath, v);
- free (temppath);
- return res;
-}
-
-/* Convert a POSIX path in 's' to an absolute Win32 path, and append
- anything in 'v' to the end, returning the result. If 's' is a
- relative path then 'cwd' is used as the working directory to make
- it absolute. Pass NULL in 'cwd' to use GetCurrentDirectory. */
-static char *
-vcygpath (const char *cwd, const char *s, va_list v)
-{
- size_t max_len = 0;
- mnt_t *m, *match = NULL;
-
-#ifndef TESTSUITE
- if (!max_mount_entry)
- read_mounts ();
-#endif
- char *path;
- if (s[0] == '.' && isslash (s[1]))
- s += 2;
-
- if (s[0] == '/' || s[1] == ':') /* FIXME: too crude? */
- path = vconcat (s, v);
- else
- path = rel_vconcat (cwd, s, v);
-
- if (!path)
- return NULL;
-
- if (strncmp (path, "/./", 3) == 0)
- memmove (path + 1, path + 3, strlen (path + 3) + 1);
-
- for (m = mount_table; m->posix; m++)
- {
- size_t n = strlen (m->posix);
- if (n < max_len || !path_prefix_p (m->posix, path, n))
- continue;
- if (m->flags & MOUNT_CYGDRIVE)
- {
- if (strlen (path) < n + 2)
- continue;
- /* If cygdrive path is just '/', fix n for followup evaluation. */
- if (n == 1)
- n = 0;
- if (path[n] != '/')
- continue;
- if (!isalpha (path[n + 1]))
- continue;
- if (path[n + 2] != '/')
- continue;
- }
- max_len = n;
- match = m;
- }
-
- char *native;
- if (match == NULL)
- native = strdup (path);
- else if (max_len == strlen (path))
- native = strdup (match->native);
- else if (match->flags & MOUNT_CYGDRIVE)
- {
- char drive[3] = { path[max_len + 1], ':', '\0' };
- native = concat (drive, path + max_len + 2, NULL);
- }
- else if (isslash (path[max_len]))
- native = concat (match->native, path + max_len, NULL);
- else
- native = concat (match->native, "\\", path + max_len, NULL);
- free (path);
-
- unconvert_slashes (native);
- for (char *s = strstr (native + 1, "\\.\\"); s && *s; s = strstr (s, "\\.\\"))
- memmove (s + 1, s + 3, strlen (s + 3) + 1);
- return native;
-}
-
-char *
-cygpath_rel (const char *cwd, const char *s, ...)
-{
- va_list v;
-
- va_start (v, s);
-
- return vcygpath (cwd, s, v);
-}
-
-char *
-cygpath (const char *s, ...)
-{
- va_list v;
-
- va_start (v, s);
-
- return vcygpath (NULL, s, v);
-}
-
-static mnt_t *m = NULL;
-
-extern "C" FILE *
-setmntent (const char *, const char *)
-{
- m = mount_table;
-#ifndef TESTSUITE
- if (!max_mount_entry)
- read_mounts ();
-#endif
- return NULL;
-}
-
-extern "C" struct mntent *
-getmntent (FILE *)
-{
- static mntent mnt;
- if (!m->posix)
- return NULL;
-
- mnt.mnt_fsname = (char *) m->native;
- mnt.mnt_dir = (char *) m->posix;
- if (!mnt.mnt_type)
- mnt.mnt_type = (char *) malloc (16);
- if (!mnt.mnt_opts)
- mnt.mnt_opts = (char *) malloc (64);
-
- strcpy (mnt.mnt_type,
- (char *) ((m->flags & MOUNT_SYSTEM) ? "system" : "user"));
-
- if (m->flags & MOUNT_TEXT)
- strcpy (mnt.mnt_opts, (char *) "text");
- else
- strcpy (mnt.mnt_opts, (char *) "binary");
-
- if (m->flags & MOUNT_CYGWIN_EXEC)
- strcat (mnt.mnt_opts, (char *) ",cygexec");
- else if (m->flags & MOUNT_EXEC)
- strcat (mnt.mnt_opts, (char *) ",exec");
- else if (m->flags & MOUNT_NOTEXEC)
- strcat (mnt.mnt_opts, (char *) ",notexec");
-
- if (m->flags & MOUNT_NOACL)
- strcat (mnt.mnt_opts, (char *) ",noacl");
-
- if (m->flags & MOUNT_NOPOSIX)
- strcat (mnt.mnt_opts, (char *) ",posix=0");
-
- if (m->flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
- strcat (mnt.mnt_opts, (char *) ",auto");
-
- mnt.mnt_freq = 1;
- mnt.mnt_passno = 1;
- m++;
- return &mnt;
-}
-
-#endif /* !FSTAB_ONLY */
next prev parent reply other threads:[~2021-04-26 15:03 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-20 20:13 Jon Turney
2021-04-20 20:15 ` Jon Turney
2021-04-21 16:40 ` Corinna Vinschen
2021-04-22 11:57 ` Corinna Vinschen
2021-04-26 15:03 ` Corinna Vinschen [this message]
2021-04-27 15:54 ` Jon Turney
2021-04-27 15:50 ` Jon Turney
2021-04-27 16:52 ` Ken Brown
2021-04-27 17:00 ` Ken Brown
2021-04-27 18:32 ` Jon Turney
2021-05-02 15:28 ` Jon Turney
2021-05-02 18:25 ` Brian Inglis
2021-05-03 10:40 ` Corinna Vinschen
2021-05-03 10:43 ` Corinna Vinschen
2021-05-04 18:12 ` Jon Turney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YIbWW3mJsphIW9hd@calimero.vinschen.de \
--to=corinna-cygwin@cygwin.com \
--cc=cygwin-patches@cygwin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).