diff --git a/winsup/utils/ChangeLog b/winsup/utils/ChangeLog index 82f1e195a..a846fe2e9 100644 --- a/winsup/utils/ChangeLog +++ b/winsup/utils/ChangeLog @@ -1,3 +1,31 @@ +2008-03-22 Corinna Vinschen + + * cygcheck.cc (dump_sysinfo): Fix multiple Cygwin DLL test. + * path.cc: Fetch mount points from fstab files instead of from + registry. Use adapted functions copied from Cygwin DLL. + (mnt_t): Drop issys member. Define as "mnt_t" instead of "struct mnt_t" + subsequently. + (max_mount_entry): New variable. + (unconvert_slashes): Move to earlier location in file. Make inline. + (find2): Remove. + (get_cygdrive0): Remove. + (get_cygdrive): Remove. + (skip_ws): New function. + (find_ws): Ditto. + (conv_fstab_spaces): Ditto. + (read_flags): Ditto. + (from_fstab_line): Ditto. + (get_user): Ditto. + (from_fstab): Ditto. + (mnt_sort): Ditto. + (read_mounts): Drop old registry code. Fetch + "Software\Cygwin\Setup\rootdir" value from HKCU or HKLM key. Use + rootdir extracted from module path as fallback. Call from_fstab for + nouser and user mount points. + (setmntent): Check max_mount_entry to test if read_mounts must be + called. + (getmntent): Use MOUNT_SYSTEM flag instead of mnt_t's issys member. + 2008-03-16 Brian Dessent * path.cc: Include malloc.h for alloca. diff --git a/winsup/utils/cygcheck.cc b/winsup/utils/cygcheck.cc index 4d79e3140..84ee45f9a 100644 --- a/winsup/utils/cygcheck.cc +++ b/winsup/utils/cygcheck.cc @@ -1669,6 +1669,7 @@ dump_sysinfo () if (givehelp) printf ("Looking for various Cygwin DLLs... (-v gives version info)\n"); int cygwin_dll_count = 0; + char cygdll_path[32768]; for (pathlike *pth = paths; pth->dir; pth++) { WIN32_FIND_DATA ffinfo; @@ -1686,7 +1687,11 @@ dump_sysinfo () sprintf (tmp, "%s%s", pth->dir, f); if (strcasecmp (f, "cygwin1.dll") == 0) { - cygwin_dll_count++; + if (!cygwin_dll_count) + strcpy (cygdll_path, pth->dir); + if (!cygwin_dll_count + || strcasecmp (cygdll_path, pth->dir) != 0) + cygwin_dll_count++; found_cygwin_dll = strdup (tmp); } else diff --git a/winsup/utils/path.cc b/winsup/utils/path.cc index 33ecf70ba..6e6b76039 100644 --- a/winsup/utils/path.cc +++ b/winsup/utils/path.cc @@ -16,9 +16,11 @@ details. */ #define str(a) #a #define scat(a,b) str(a##b) #include +#include #include #include #include +#include #include "path.h" #include "cygwin/include/cygwin/version.h" #include "cygwin/include/sys/mount.h" @@ -226,152 +228,347 @@ readlink (HANDLE fh, char *path, int maxlen) return false; } -typedef struct mnt - { - const char *native; - char *posix; - unsigned flags; - int issys; - } mnt_t; +struct mnt_t +{ + char *native; + char *posix; + unsigned flags; +}; #ifndef TESTSUITE static mnt_t mount_table[255]; +static int max_mount_entry; #else # define TESTSUITE_MOUNT_TABLE # include "testsuite.h" # undef TESTSUITE_MOUNT_TABLE #endif -struct mnt *root_here = NULL; +mnt_t *root_here = NULL; + +inline void +unconvert_slashes (char* name) +{ + while ((name = strchr (name, '/')) != NULL) + *name++ = '\\'; +} /* These functions aren't called when defined(TESTSUITE) which results in a compiler warning. */ #ifndef TESTSUITE -static char * -find2 (HKEY rkey, unsigned *flags, char *what) +inline char * +skip_ws (char *in) { - char *retval = 0; - DWORD retvallen = 0; - DWORD type; - HKEY key; + while (*in == ' ' || *in == '\t') + ++in; + return in; +} - if (RegOpenKeyEx (rkey, what, 0, KEY_READ, &key) != ERROR_SUCCESS) - return 0; +inline char * +find_ws (char *in) +{ + while (*in && *in != ' ' && *in != '\t') + ++in; + return in; +} - if (RegQueryValueEx (key, "native", 0, &type, 0, &retvallen) - == ERROR_SUCCESS) +inline char * +conv_fstab_spaces (char *field) +{ + register char *sp = field; + while (sp = strstr (sp, "\\040")) { - retval = (char *) malloc (MAX_PATH + 1); - if (RegQueryValueEx (key, "native", 0, &type, (BYTE *) retval, &retvallen) - != ERROR_SUCCESS) - { - free (retval); - retval = 0; - } + *sp++ = ' '; + memmove (sp, sp + 3, strlen (sp + 3) + 1); + } + return field; +} + +struct opt +{ + const char *name; + unsigned val; + bool clear; +} oopts[] = +{ + {"user", MOUNT_SYSTEM, 1}, + {"nouser", MOUNT_SYSTEM, 0}, + {"binary", MOUNT_BINARY, 0}, + {"text", MOUNT_BINARY, 1}, + {"exec", MOUNT_EXEC, 0}, + {"notexec", MOUNT_NOTEXEC, 0}, + {"cygexec", MOUNT_CYGWIN_EXEC, 0}, + {"nosuid", 0, 0}, + {"managed", MOUNT_ENC, 0} +}; + +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; +} + +static bool +from_fstab_line (mnt_t *m, char *line, bool user) +{ + char *native_path, *posix_path, *fs_type; + + /* First field: Native path. */ + char *c = skip_ws (line); + if (!*c || *c == '#') + return false; + char *cend = find_ws (c); + *cend = '\0'; + native_path = conv_fstab_spaces (c); + /* Second field: POSIX path. */ + c = skip_ws (cend + 1); + if (!*c) + return false; + cend = find_ws (c); + *cend = '\0'; + posix_path = conv_fstab_spaces (c); + /* Third field: FS type. */ + c = skip_ws (cend + 1); + if (!*c) + return false; + cend = find_ws (c); + *cend = '\0'; + fs_type = c; + /* Forth field: Flags. */ + c = skip_ws (cend + 1); + if (!*c) + return false; + cend = find_ws (c); + *cend = '\0'; + unsigned mount_flags = MOUNT_SYSTEM; + if (!read_flags (c, mount_flags)) + return false; + if (user) + mount_flags &= ~MOUNT_SYSTEM; + if (!strcmp (fs_type, "cygdrive")) + { + for (mnt_t *sm = mount_table; sm < m; ++sm) + if (sm->flags & MOUNT_CYGDRIVE) + { + if ((mount_flags & MOUNT_SYSTEM) || !(sm->flags & MOUNT_SYSTEM)) + { + if (sm->posix) + free (sm->posix); + sm->posix = strdup (posix_path); + sm->flags = mount_flags | MOUNT_CYGDRIVE; + } + return false; + } + m->posix = strdup (posix_path); + m->native = strdup ("."); + m->flags = mount_flags | MOUNT_CYGDRIVE; + } + else + { + for (mnt_t *sm = mount_table; sm < m; ++sm) + if (!strcasecmp (sm->posix, posix_path)) + { + if ((mount_flags & MOUNT_SYSTEM) || !(sm->flags & MOUNT_SYSTEM)) + { + if (sm->native) + free (sm->native); + sm->native = strdup (native_path); + sm->flags = mount_flags; + } + return false; + } + m->posix = strdup (posix_path); + unconvert_slashes (native_path); + m->native = strdup (native_path); + m->flags = mount_flags; + } + 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. */ + WideCharToMultiByte (GetACP (), 0, path, -1, buf, BUFSIZE, + NULL, NULL); + 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_BINARY; + ++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 ("."); + m->flags = MOUNT_BINARY | MOUNT_CYGDRIVE; + ++m; + max_mount_entry = m - mount_table; } - retvallen = sizeof (flags); - RegQueryValueEx (key, "flags", 0, &type, (BYTE *)flags, &retvallen); + PWCHAR u = wcscpy (path_end, L"\\etc\\fstab") + 10; + if (user) + MultiByteToWideChar (GetACP (), 0, get_user (), -1, + wcscpy (u, L".d\\") + 3, 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; - RegCloseKey (key); - - return retval; -} - -static LONG -get_cygdrive0 (HKEY key, const char *what, void *val, DWORD len) -{ - LONG status = RegQueryValueEx (key, what, 0, 0, (BYTE *)val, &len); - return status; -} - -static mnt * -get_cygdrive (HKEY key, mnt *m, int issystem) -{ - if (get_cygdrive0 (key, CYGWIN_INFO_CYGDRIVE_FLAGS, &m->flags, - sizeof (m->flags)) != ERROR_SUCCESS) { - free (m->posix); - return m; - } - get_cygdrive0 (key, CYGWIN_INFO_CYGDRIVE_PREFIX, m->posix, MAX_PATH); - m->native = strdup ("."); - m->issys = issystem; - return m + 1; + /* 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 +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); +} + static void read_mounts () { /* If TESTSUITE is defined, bypass this whole function as a harness mount table will be provided. */ #ifndef TESTSUITE - DWORD posix_path_size; - int res; - struct mnt *m = mount_table; - DWORD disposition; - char buf[10000]; + HKEY setup_key; + LONG ret; + DWORD len; + WCHAR path[32768]; + PWCHAR path_end; - root_here = NULL; - for (mnt *m1 = mount_table; m1->posix; m1++) + 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; - /* Loop through subkeys */ - /* FIXME: we would like to not check MAX_MOUNTS but the heap in the - shared area is currently statically allocated so we can't have an - arbitrarily large number of mounts. */ - for (int issystem = 0; issystem <= 1; issystem++) + 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'); + else { - sprintf (buf, "Software\\%s\\%s\\%s", - CYGWIN_INFO_CYGNUS_REGISTRY_NAME, - CYGWIN_INFO_CYGWIN_REGISTRY_NAME, - CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME); - - HKEY key = issystem ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - if (RegCreateKeyEx (key, buf, 0, (LPTSTR) "Cygwin", 0, KEY_READ, - 0, &key, &disposition) != ERROR_SUCCESS) - break; - for (int i = 0; ;i++, m++) + if (!GetModuleFileNameW (NULL, path, 32768)) + return; + path_end = wcsrchr (path, L'\\'); + if (path_end) { - m->posix = (char *) malloc (MAX_PATH + 1); - posix_path_size = MAX_PATH; - /* FIXME: if maximum posix_path_size is 256, we're going to - run into problems if we ever try to store a mount point that's - over 256 but is under MAX_PATH. */ - res = RegEnumKeyEx (key, i, m->posix, &posix_path_size, NULL, - NULL, NULL, NULL); - - if (res == ERROR_NO_MORE_ITEMS) - { - m = get_cygdrive (key, m, issystem); - m->posix = NULL; - break; - } - - if (!*m->posix) - goto no_go; - else if (res != ERROR_SUCCESS) - break; - else - { - m->native = find2 (key, &m->flags, m->posix); - m->issys = issystem; - if (!m->native) - goto no_go; - } - continue; - no_go: - free (m->posix); - m->posix = NULL; - m--; + *path_end = L'\0'; + path_end = wcsrchr (path, L'\\'); } - RegCloseKey (key); } + 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) */ } @@ -477,13 +674,6 @@ concat (const char *s, ...) return vconcat (s, v); } -static void -unconvert_slashes (char* name) -{ - while ((name = strchr (name, '/')) != NULL) - *name++ = '\\'; -} - /* 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) @@ -504,7 +694,7 @@ rel_vconcat (const char *cwd, const char *s, va_list v) } int max_len = -1; - struct mnt *m, *match = NULL; + mnt_t *m, *match = NULL; for (m = mount_table; m->posix; m++) { @@ -547,9 +737,9 @@ static char * vcygpath (const char *cwd, const char *s, va_list v) { int max_len = -1; - struct mnt *m, *match = NULL; + mnt_t *m, *match = NULL; - if (!mount_table[0].posix) + if (!max_mount_entry) read_mounts (); char *path; if (s[0] == '.' && isslash (s[1])) @@ -615,13 +805,13 @@ cygpath (const char *s, ...) return vcygpath (NULL, s, v); } -static mnt *m = NULL; +static mnt_t *m = NULL; extern "C" FILE * setmntent (const char *, const char *) { m = mount_table; - if (!m->posix) + if (!max_mount_entry) read_mounts (); return NULL; } @@ -639,10 +829,10 @@ getmntent (FILE *) mnt.mnt_type = (char *) malloc (1024); if (!mnt.mnt_opts) mnt.mnt_opts = (char *) malloc (1024); - if (!m->issys) - strcpy (mnt.mnt_type, (char *) "user"); - else + if (m->flags & MOUNT_SYSTEM) strcpy (mnt.mnt_type, (char *) "system"); + else + strcpy (mnt.mnt_type, (char *) "user"); if (!(m->flags & MOUNT_BINARY)) strcpy (mnt.mnt_opts, (char *) "textmode"); else