Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler/registry.cc')
-rw-r--r--winsup/cygwin/fhandler/registry.cc1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/registry.cc b/winsup/cygwin/fhandler/registry.cc
new file mode 100644
index 000000000..2830c708a
--- /dev/null
+++ b/winsup/cygwin/fhandler/registry.cc
@@ -0,0 +1,1115 @@
+/* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem
+
+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. */
+
+/* FIXME: Access permissions are ignored at the moment. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info.h"
+
+#define _LIBC
+#include <dirent.h>
+
+/* If this bit is set in __d_position then we are enumerating values,
+ * else sub-keys. keeping track of where we are is horribly messy
+ * the bottom 16 bits are the absolute position and the top 15 bits
+ * make up the value index if we are enuerating values.
+ */
+static const __int32_t REG_ENUM_VALUES_MASK = 0x8000000;
+static const __int32_t REG_POSITION_MASK = 0xffff;
+
+/* These key paths are used below whenever we return key information.
+ The problem is UAC virtualization when running an admin account with
+ restricted rights. In that case the subkey "Classes" in the VirtualStore
+ points to the HKEY_CLASSES_ROOT key again. If "Classes" is handled as a
+ normal subdirectory, applications recursing throught the directory
+ hirarchy will invariably run into an infinite recursion. What we do here
+ is to handle the "Classes" subkey as a symlink to HKEY_CLASSES_ROOT. This
+ avoids the infinite recursion, unless the application blindly follows
+ symlinks pointing to directories, in which case it's their own fault. */
+#define VIRT_CLASSES_KEY_PREFIX "/VirtualStore/MACHINE/SOFTWARE"
+#define VIRT_CLASSES_KEY_SUFFIX "Classes"
+#define VIRT_CLASSES_KEY VIRT_CLASSES_KEY_PREFIX "/" VIRT_CLASSES_KEY_SUFFIX
+#define VIRT_CLASSES_LINKTGT "/proc/registry/HKEY_CLASSES_ROOT"
+
+/* List of root keys in /proc/registry.
+ * Possibly we should filter out those not relevant to the flavour of Windows
+ * Cygwin is running on.
+ */
+static const char *registry_listing[] =
+{
+ ".",
+ "..",
+ "HKEY_CLASSES_ROOT",
+ "HKEY_CURRENT_CONFIG",
+ "HKEY_CURRENT_USER",
+ "HKEY_LOCAL_MACHINE",
+ "HKEY_USERS",
+ "HKEY_PERFORMANCE_DATA",
+ NULL
+};
+
+static const HKEY registry_keys[] =
+{
+ (HKEY) INVALID_HANDLE_VALUE,
+ (HKEY) INVALID_HANDLE_VALUE,
+ HKEY_CLASSES_ROOT,
+ HKEY_CURRENT_CONFIG,
+ HKEY_CURRENT_USER,
+ HKEY_LOCAL_MACHINE,
+ HKEY_USERS,
+ HKEY_PERFORMANCE_DATA
+};
+
+static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY);
+
+/* Make sure to access the correct per-user HKCR and HKCU hives, even if
+ the current user is only impersonated in another user's session. */
+static HKEY
+fetch_hkey (int idx) /* idx *must* be valid */
+{
+ HKEY key;
+
+ if (registry_keys[idx] == HKEY_CLASSES_ROOT)
+ {
+ if (RegOpenUserClassesRoot (cygheap->user.issetuid ()
+ ? cygheap->user.imp_token () : hProcToken,
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
+ return key;
+ }
+ else if (registry_keys[idx] == HKEY_CURRENT_USER)
+ {
+ if (RegOpenCurrentUser (KEY_READ, &key) == ERROR_SUCCESS)
+ return key;
+ }
+ else if (registry_keys[idx] == HKEY_CURRENT_CONFIG)
+ {
+ if (RegOpenKeyExW (HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Hardware Profiles\\Current",
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
+ return key;
+ }
+ /* Unfortunately there's no way to generate a valid OS registry key for
+ the other root keys. HKEY_USERS and HKEY_LOCAL_MACHINE are file
+ handles internally, HKEY_PERFORMANCE_DATA is just a bad hack and
+ no registry key at all. */
+ return registry_keys[idx];
+}
+
+/* These get added to each subdirectory in /proc/registry.
+ * If we wanted to implement writing, we could maybe add a '.writable' entry or
+ * suchlike.
+ */
+static const char *special_dot_files[] =
+{
+ ".",
+ "..",
+ NULL
+};
+
+static const int SPECIAL_DOT_FILE_COUNT =
+ (sizeof (special_dot_files) / sizeof (const char *)) - 1;
+
+/* Value names for HKEY_PERFORMANCE_DATA.
+ *
+ * CAUTION: Never call RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Add", ...).
+ * It WRITES data and may destroy the perfc009.dat file. Same applies to
+ * name prefixes "Ad" and "A".
+ */
+static const char * const perf_data_files[] =
+{
+ "@",
+ "Costly",
+ "Global"
+};
+
+static const int PERF_DATA_FILE_COUNT =
+ sizeof (perf_data_files) / sizeof (perf_data_files[0]);
+
+static HKEY open_key (const char *name, REGSAM access, DWORD wow64, bool isValue);
+
+/* Return true if char must be encoded.
+ */
+static inline bool
+must_encode (wchar_t c)
+{
+ return (iswdirsep (c) || c == L':' || c == L'%');
+}
+
+/* Encode special chars in registry key or value name.
+ * Returns 0: success, -1: error.
+ */
+static int
+encode_regname (char *dst, const wchar_t *src, bool add_val)
+{
+ int di = 0;
+ if (!src[0])
+ dst[di++] = '@'; // Default value.
+ else
+ for (int si = 0; src[si]; si++)
+ {
+ wchar_t c = src[si];
+ if (must_encode (c) ||
+ (si == 0 && ((c == L'.'
+ && (!src[1] || (src[1] == L'.' && !src[2])))
+ || (c == L'@' && !src[1]))))
+ {
+ if (di + 3 >= NAME_MAX + 1)
+ return -1;
+ __small_sprintf (dst + di, "%%%02x", c);
+ di += 3;
+ }
+ else
+ di += sys_wcstombs (dst + di, NAME_MAX + 1 - di, &c, 1);
+ }
+
+ if (add_val)
+ {
+ if (di + 4 >= NAME_MAX + 1)
+ return -1;
+ memcpy (dst + di, "%val", 4);
+ di += 4;
+ }
+
+ dst[di] = 0;
+ return 0;
+}
+
+/* Decode special chars in registry key or value name.
+ * Returns 0: success, 1: "%val" detected, -1: error.
+ */
+static int
+decode_regname (wchar_t *wdst, const char *src, int len = -1)
+{
+ if (len < 0)
+ len = strlen (src);
+ char dst[len + 1];
+ int res = 0;
+
+ if (len > 4 && !memcmp (src + len - 4, "%val", 4))
+ {
+ len -= 4;
+ res = 1;
+ }
+
+ int di = 0;
+ if (len == 1 && src[0] == '@')
+ ; // Default value.
+ else
+ for (int si = 0; si < len; si++)
+ {
+ char c = src[si];
+ if (c == '%')
+ {
+ if (si + 2 >= len)
+ return -1;
+ char s[] = {src[si+1], src[si+2], '\0'};
+ char *p;
+ c = strtoul (s, &p, 16);
+ if (!(must_encode ((wchar_t) c) ||
+ (si == 0 && ((c == '.' && (len == 3 || (src[3] == '.' && len == 4))) ||
+ (c == '@' && len == 3)))))
+ return -1;
+ dst[di++] = c;
+ si += 2;
+ }
+ else
+ dst[di++] = c;
+ }
+
+ dst[di] = 0;
+ sys_mbstowcs (wdst, NAME_MAX + 1, dst);
+ return res;
+}
+
+
+/* Hash table to limit calls to key_exists ().
+ */
+class __DIR_hash
+{
+public:
+ __DIR_hash ()
+ {
+ memset (table, 0, sizeof(table));
+ }
+
+ void set (unsigned h)
+ {
+ table [(h >> 3) & (HASH_SIZE - 1)] |= (1 << (h & 0x3));
+ }
+
+ bool is_set (unsigned h) const
+ {
+ return (table [(h >> 3) & (HASH_SIZE - 1)] & (1 << (h & 0x3))) != 0;
+ }
+
+private:
+ enum { HASH_SIZE = 1024 };
+ unsigned char table[HASH_SIZE];
+};
+
+#define d_hash(d) ((__DIR_hash *) (d)->__d_internal)
+
+
+/* Return true if subkey NAME exists in key PARENT.
+ */
+static bool
+key_exists (HKEY parent, const wchar_t *name, DWORD wow64)
+{
+ HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+ LONG error = RegOpenKeyExW (parent, name, 0, KEY_READ | wow64, &hKey);
+ if (error == ERROR_SUCCESS)
+ RegCloseKey (hKey);
+
+ return (error == ERROR_SUCCESS || error == ERROR_ACCESS_DENIED);
+}
+
+static size_t
+multi_wcstombs (char *dst, size_t len, const wchar_t *src, size_t nwc)
+{
+ size_t siz, sum = 0;
+ const wchar_t *nsrc;
+
+ while (nwc)
+ {
+ siz = sys_wcstombs (dst, len, src, nwc) + 1;
+ sum += siz;
+ if (dst)
+ {
+ dst += siz;
+ len -= siz;
+ }
+ nsrc = wcschr (src, L'\0') + 1;
+ if ((size_t) (nsrc - src) >= nwc)
+ break;
+ nwc -= nsrc - src;
+ src = nsrc;
+ if (*src == L'\0')
+ {
+ if (dst)
+ *dst++ = '\0';
+ ++sum;
+ break;
+ }
+ }
+ return sum;
+}
+
+virtual_ftype_t
+fhandler_registry::exists ()
+{
+ virtual_ftype_t file_type = virt_none;
+ int index = 0, pathlen;
+ DWORD buf_size = NAME_MAX + 1;
+ LONG error;
+ wchar_t buf[buf_size];
+ const char *file;
+ HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+
+ const char *path = get_name ();
+ debug_printf ("exists (%s)", path);
+ path += proc_len + prefix_len + 1;
+ if (*path)
+ path++;
+ else
+ {
+ file_type = virt_rootdir;
+ goto out;
+ }
+ pathlen = strlen (path);
+ file = path + pathlen - 1;
+ if (isdirsep (*file) && pathlen > 1)
+ file--;
+ while (!isdirsep (*file))
+ file--;
+ file++;
+
+ if (file == path)
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (path_prefix_p (registry_listing[i], path,
+ strlen (registry_listing[i]), true))
+ {
+ file_type = virt_directory;
+ break;
+ }
+ }
+ else
+ {
+ wchar_t dec_file[NAME_MAX + 1];
+
+ int val_only = decode_regname (dec_file, file);
+ if (val_only < 0)
+ goto out;
+
+ if (!val_only)
+ hKey = open_key (path, KEY_READ, wow64, false);
+ if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ if (!strcasecmp (path + strlen (path)
+ - sizeof (VIRT_CLASSES_KEY) + 1,
+ VIRT_CLASSES_KEY))
+ file_type = virt_symlink;
+ else
+ file_type = virt_directory;
+ }
+ else
+ {
+ /* Key does not exist or open failed with EACCES,
+ enumerate subkey and value names of parent key. */
+ hKey = open_key (path, KEY_READ, wow64, true);
+ if (hKey == (HKEY) INVALID_HANDLE_VALUE)
+ return virt_none;
+
+ if (hKey == HKEY_PERFORMANCE_DATA)
+ {
+ /* RegEnumValue () returns garbage for this key.
+ RegQueryValueEx () returns a PERF_DATA_BLOCK even
+ if a value does not contain any counter objects.
+ So allow access to the generic names and to
+ (blank separated) lists of counter numbers.
+ Never allow access to "Add", see above comment. */
+ for (int i = 0; i < PERF_DATA_FILE_COUNT
+ && file_type == virt_none; i++)
+ {
+ if (strcasematch (perf_data_files[i], file))
+ file_type = virt_file;
+ }
+ if (file_type == virt_none && !file[strspn (file, " 0123456789")])
+ file_type = virt_file;
+ goto out;
+ }
+
+ if (!val_only && dec_file[0])
+ {
+ while (ERROR_SUCCESS ==
+ (error = RegEnumKeyExW (hKey, index++, buf, &buf_size,
+ NULL, NULL, NULL, NULL))
+ || (error == ERROR_MORE_DATA))
+ {
+ if (!wcscasecmp (buf, dec_file))
+ {
+ file_type = virt_directory;
+ goto out;
+ }
+ buf_size = NAME_MAX + 1;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+ index = 0;
+ buf_size = NAME_MAX + 1;
+ }
+
+ while (ERROR_SUCCESS ==
+ (error = RegEnumValueW (hKey, index++, buf, &buf_size,
+ NULL, NULL, NULL, NULL))
+ || (error == ERROR_MORE_DATA))
+ {
+ if (!wcscasecmp (buf, dec_file))
+ {
+ file_type = virt_file;
+ goto out;
+ }
+ buf_size = NAME_MAX + 1;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+ }
+ }
+out:
+ if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ RegCloseKey (hKey);
+ return file_type;
+}
+
+void
+fhandler_registry::set_name (path_conv &in_pc)
+{
+ if (strncasematch (in_pc.get_posix (), "/proc/registry32", 16))
+ {
+ wow64 = KEY_WOW64_32KEY;
+ prefix_len += 2;
+ }
+ else if (strncasematch (in_pc.get_posix (), "/proc/registry64", 16))
+ prefix_len += 2;
+ fhandler_base::set_name (in_pc);
+}
+
+fhandler_registry::fhandler_registry ():
+fhandler_proc ()
+{
+ wow64 = 0;
+ prefix_len = sizeof ("registry") - 1;
+}
+
+int
+fhandler_registry::fstat (struct stat *buf)
+{
+ fhandler_base::fstat (buf);
+ buf->st_mode &= ~_IFMT & NO_W;
+ virtual_ftype_t file_type = exists ();
+ switch (file_type)
+ {
+ case virt_none:
+ set_errno (ENOENT);
+ return -1;
+ case virt_symlink:
+ buf->st_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+ break;
+ case virt_directory:
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case virt_rootdir:
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ buf->st_nlink = ROOT_KEY_COUNT;
+ break;
+ default:
+ case virt_file:
+ buf->st_mode |= S_IFREG;
+ buf->st_mode &= NO_X;
+ break;
+ }
+ if (file_type != virt_none && file_type != virt_rootdir)
+ {
+ HKEY hKey;
+ const char *path = get_name () + proc_len + prefix_len + 2;
+ hKey =
+ open_key (path, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, wow64,
+ virt_ftype_isfile (file_type) ? true : false);
+
+ if (hKey == HKEY_PERFORMANCE_DATA)
+ /* RegQueryInfoKey () always returns write time 0,
+ RegQueryValueEx () does not return required buffer size. */
+ ;
+ else if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ FILETIME ftLastWriteTime;
+ DWORD subkey_count;
+ if (ERROR_SUCCESS ==
+ RegQueryInfoKeyW (hKey, NULL, NULL, NULL, &subkey_count, NULL,
+ NULL, NULL, NULL, NULL, NULL, &ftLastWriteTime))
+ {
+ to_timestruc_t ((PLARGE_INTEGER) &ftLastWriteTime, &buf->st_mtim);
+ buf->st_ctim = buf->st_birthtim = buf->st_mtim;
+ time_as_timestruc_t (&buf->st_atim);
+ if (virt_ftype_isdir (file_type))
+ buf->st_nlink = subkey_count + 2;
+ else
+ {
+ int pathlen = strlen (path);
+ const char *value_name = path + pathlen - 1;
+ if (isdirsep (*value_name) && pathlen > 1)
+ value_name--;
+ while (!isdirsep (*value_name))
+ value_name--;
+ value_name++;
+ wchar_t dec_value_name[NAME_MAX + 1];
+ DWORD dwSize = 0;
+ DWORD type;
+ if (decode_regname (dec_value_name, value_name) >= 0
+ && RegQueryValueExW (hKey, dec_value_name, NULL, &type,
+ NULL, &dwSize) == ERROR_SUCCESS
+ && (type == REG_SZ || type == REG_EXPAND_SZ
+ || type == REG_MULTI_SZ || type == REG_LINK))
+ {
+ PBYTE tmpbuf = (PBYTE) malloc (dwSize);
+ if (!tmpbuf
+ || RegQueryValueExW (hKey, dec_value_name,
+ NULL, NULL, tmpbuf, &dwSize)
+ != ERROR_SUCCESS)
+ buf->st_size = dwSize / sizeof (wchar_t);
+ else if (type == REG_MULTI_SZ)
+ buf->st_size = multi_wcstombs (NULL, 0,
+ (wchar_t *) tmpbuf,
+ dwSize / sizeof (wchar_t));
+ else
+ buf->st_size = sys_wcstombs (NULL, 0,
+ (wchar_t *) tmpbuf,
+ dwSize / sizeof (wchar_t))
+ + 1;
+ if (tmpbuf)
+ free (tmpbuf);
+ }
+ else
+ buf->st_size = dwSize;
+ }
+ uid_t uid;
+ gid_t gid;
+ if (get_reg_attribute (hKey, &buf->st_mode, &uid, &gid) == 0)
+ {
+ buf->st_uid = uid;
+ buf->st_gid = gid;
+ buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ if (virt_ftype_isdir (file_type))
+ buf->st_mode |= S_IFDIR;
+ else
+ buf->st_mode &= NO_X;
+ }
+ }
+ RegCloseKey (hKey);
+ }
+ else
+ {
+ /* Here's the problem: If we can't open the key, we don't know
+ nothing at all about the key/value. It's only clear that
+ the current user has no read access. At this point it's
+ rather unlikely that the user has write or execute access
+ and it's also rather unlikely that the user is the owner.
+ Therefore it's probably most safe to assume unknown ownership
+ and no permissions for nobody. */
+ buf->st_uid = ILLEGAL_UID;
+ buf->st_gid = ILLEGAL_GID;
+ buf->st_mode &= ~0777;
+ }
+ }
+ return 0;
+}
+
+DIR *
+fhandler_registry::opendir (int fd)
+{
+ /* Skip fhandler_proc::opendir, which allocates dir->_d_handle for its
+ own devilish purposes... */
+ return fhandler_virtual::opendir (fd);
+}
+
+int
+fhandler_registry::readdir (DIR *dir, dirent *de)
+{
+ DWORD buf_size = NAME_MAX + 1;
+ wchar_t buf[buf_size];
+ const char *path = dir->__d_dirname + proc_len + 1 + prefix_len;
+ LONG error;
+ int res = ENMFILE;
+
+ dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot;
+ if (*path == 0)
+ {
+ if (dir->__d_position >= ROOT_KEY_COUNT)
+ goto out;
+ strcpy (de->d_name, registry_listing[dir->__d_position++]);
+ res = 0;
+ goto out;
+ }
+ if (dir->__handle == INVALID_HANDLE_VALUE)
+ {
+ if (dir->__d_position != 0)
+ goto out;
+ dir->__handle = open_key (path + 1, KEY_READ, wow64, false);
+ if (dir->__handle == INVALID_HANDLE_VALUE)
+ goto out;
+ dir->__d_internal = (uintptr_t) new __DIR_hash ();
+ }
+ if (dir->__d_position < SPECIAL_DOT_FILE_COUNT)
+ {
+ strcpy (de->d_name, special_dot_files[dir->__d_position++]);
+ res = 0;
+ goto out;
+ }
+ if ((HKEY) dir->__handle == HKEY_PERFORMANCE_DATA)
+ {
+ /* RegEnumValue () returns garbage for this key,
+ simulate only a minimal listing of the generic names. */
+ if (dir->__d_position >= SPECIAL_DOT_FILE_COUNT + PERF_DATA_FILE_COUNT)
+ goto out;
+ strcpy (de->d_name, perf_data_files[dir->__d_position - SPECIAL_DOT_FILE_COUNT]);
+ dir->__d_position++;
+ res = 0;
+ goto out;
+ }
+
+retry:
+ if (dir->__d_position & REG_ENUM_VALUES_MASK)
+ /* For the moment, the type of key is ignored here. when write access is added,
+ * maybe add an extension for the type of each value?
+ */
+ error = RegEnumValueW ((HKEY) dir->__handle,
+ (dir->__d_position & ~REG_ENUM_VALUES_MASK) >> 16,
+ buf, &buf_size, NULL, NULL, NULL, NULL);
+ else
+ error =
+ RegEnumKeyExW ((HKEY) dir->__handle, dir->__d_position -
+ SPECIAL_DOT_FILE_COUNT, buf, &buf_size,
+ NULL, NULL, NULL, NULL);
+ if (error == ERROR_NO_MORE_ITEMS
+ && (dir->__d_position & REG_ENUM_VALUES_MASK) == 0)
+ {
+ /* If we're finished with sub-keys, start on values under this key. */
+ dir->__d_position |= REG_ENUM_VALUES_MASK;
+ buf_size = NAME_MAX + 1;
+ goto retry;
+ }
+ if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA)
+ {
+ delete d_hash (dir);
+ RegCloseKey ((HKEY) dir->__handle);
+ dir->__handle = INVALID_HANDLE_VALUE;
+ if (error != ERROR_NO_MORE_ITEMS)
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+
+ /* We get here if `buf' contains valid data. */
+ dir->__d_position++;
+ if (dir->__d_position & REG_ENUM_VALUES_MASK)
+ dir->__d_position += 0x10000;
+
+ {
+ /* Append "%val" if value name is identical to a previous key name. */
+ unsigned h = hash_path_name (1, buf);
+ bool add_val = false;
+ if (! (dir->__d_position & REG_ENUM_VALUES_MASK))
+ d_hash (dir)->set (h);
+ else if (d_hash (dir)->is_set (h)
+ && key_exists ((HKEY) dir->__handle, buf, wow64))
+ add_val = true;
+
+ if (encode_regname (de->d_name, buf, add_val))
+ {
+ buf_size = NAME_MAX + 1;
+ goto retry;
+ }
+ }
+
+ if (dir->__d_position & REG_ENUM_VALUES_MASK)
+ de->d_type = DT_REG;
+ else if (!strcasecmp (de->d_name, "Classes")
+ && !strcasecmp (path + strlen (path)
+ - sizeof (VIRT_CLASSES_KEY_PREFIX) + 1,
+ VIRT_CLASSES_KEY_PREFIX))
+ de->d_type = DT_LNK;
+ else
+ de->d_type = DT_DIR;
+
+ res = 0;
+out:
+ syscall_printf ("%d = readdir(%p, %p)", res, dir, de);
+ return res;
+}
+
+long
+fhandler_registry::telldir (DIR * dir)
+{
+ return dir->__d_position & REG_POSITION_MASK;
+}
+
+void
+fhandler_registry::seekdir (DIR * dir, long loc)
+{
+ /* Unfortunately cannot simply set __d_position due to transition from sub-keys to
+ * values.
+ */
+ rewinddir (dir);
+ while (loc > (dir->__d_position & REG_POSITION_MASK))
+ if (readdir (dir, dir->__d_dirent))
+ break;
+}
+
+void
+fhandler_registry::rewinddir (DIR * dir)
+{
+ if (dir->__handle != INVALID_HANDLE_VALUE)
+ {
+ delete d_hash (dir);
+ RegCloseKey ((HKEY) dir->__handle);
+ dir->__handle = INVALID_HANDLE_VALUE;
+ }
+ dir->__d_position = 0;
+ dir->__flags = dirent_saw_dot | dirent_saw_dot_dot;
+}
+
+int
+fhandler_registry::closedir (DIR * dir)
+{
+ int res = 0;
+ if (dir->__handle != INVALID_HANDLE_VALUE)
+ {
+ delete d_hash (dir);
+ if (RegCloseKey ((HKEY) dir->__handle) != ERROR_SUCCESS)
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ syscall_printf ("%d = closedir(%p)", res, dir);
+ return 0;
+}
+
+int
+fhandler_registry::open (int flags, mode_t mode)
+{
+ int pathlen;
+ const char *file;
+ HKEY handle = (HKEY) INVALID_HANDLE_VALUE;
+
+ int res = fhandler_virtual::open (flags, mode);
+ if (!res)
+ goto out;
+
+ const char *path;
+ path = get_name () + proc_len + 1 + prefix_len;
+ if (!*path)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ diropen = true;
+ /* Marking as nohandle allows to call dup. */
+ nohandle (true);
+ goto success;
+ }
+ }
+ path++;
+ pathlen = strlen (path);
+ file = path + pathlen - 1;
+ if (isdirsep (*file) && pathlen > 1)
+ file--;
+ while (!isdirsep (*file))
+ file--;
+ file++;
+
+ if (file == path)
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (path_prefix_p (registry_listing[i], path,
+ strlen (registry_listing[i]), true))
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ set_handle (fetch_hkey (i));
+ /* Marking as nohandle allows to call dup on pseudo registry
+ handles. */
+ if (get_handle () >= HKEY_CLASSES_ROOT)
+ nohandle (true);
+ diropen = true;
+ goto success;
+ }
+ }
+
+ if (flags & O_CREAT)
+ {
+ set_errno (EROFS);
+ res = 0;
+ }
+ else
+ {
+ set_errno (ENOENT);
+ res = 0;
+ }
+ goto out;
+ }
+
+ if (flags & O_WRONLY)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ wchar_t dec_file[NAME_MAX + 1];
+ int val_only = decode_regname (dec_file, file);
+ if (val_only < 0)
+ {
+ set_errno (EINVAL);
+ res = 0;
+ goto out;
+ }
+
+ if (!val_only)
+ handle = open_key (path, KEY_READ, wow64, false);
+ if (handle == (HKEY) INVALID_HANDLE_VALUE)
+ {
+ if (val_only || get_errno () != EACCES)
+ handle = open_key (path, KEY_READ, wow64, true);
+ if (handle == (HKEY) INVALID_HANDLE_VALUE)
+ {
+ res = 0;
+ goto out;
+ }
+ }
+ else
+ diropen = true;
+
+ set_handle (handle);
+ set_close_on_exec (!!(flags & O_CLOEXEC));
+ value_name = cwcsdup (dec_file);
+
+ if (!diropen && !fill_filebuf ())
+ {
+ RegCloseKey (handle);
+ res = 0;
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ position = filesize;
+ else
+ position = 0;
+ }
+
+success:
+ res = 1;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+out:
+ syscall_printf ("%d = fhandler_registry::open(%p, 0%o)", res, flags, mode);
+ return res;
+}
+
+int
+fhandler_registry::close ()
+{
+ int res = fhandler_virtual::close ();
+ if (res != 0)
+ return res;
+ HKEY handle = (HKEY) get_handle ();
+ if (handle != (HKEY) INVALID_HANDLE_VALUE && handle < HKEY_CLASSES_ROOT)
+ {
+ if (RegCloseKey (handle) != ERROR_SUCCESS)
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ if (!have_execed && value_name)
+ {
+ cfree (value_name);
+ value_name = NULL;
+ }
+ return res;
+}
+
+bool
+fhandler_registry::fill_filebuf ()
+{
+ DWORD type, size;
+ LONG error;
+ HKEY handle = (HKEY) get_handle ();
+ size_t bufalloc;
+
+ if (handle != HKEY_PERFORMANCE_DATA)
+ {
+ error = RegQueryValueExW (handle, value_name, NULL, &type, NULL, &size);
+ if (error != ERROR_SUCCESS)
+ {
+ if (error == ERROR_INVALID_HANDLE
+ && !strcasecmp (get_name () + strlen (get_name ())
+ - sizeof (VIRT_CLASSES_KEY) + 1,
+ VIRT_CLASSES_KEY))
+ {
+ filesize = sizeof (VIRT_CLASSES_LINKTGT);
+ filebuf = (char *) cmalloc_abort (HEAP_BUF, filesize);
+ strcpy (filebuf, VIRT_CLASSES_LINKTGT);
+ return true;
+ }
+ if (error != ERROR_FILE_NOT_FOUND)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return false;
+ }
+ goto value_not_found;
+ }
+ PBYTE tmpbuf = (PBYTE) cmalloc_abort (HEAP_BUF, size);
+ error =
+ RegQueryValueExW (handle, value_name, NULL, NULL, tmpbuf, &size);
+ if (error != ERROR_SUCCESS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return true;
+ }
+ if (type == REG_SZ || type == REG_EXPAND_SZ || type == REG_LINK)
+ bufalloc = sys_wcstombs (NULL, 0, (wchar_t *) tmpbuf,
+ size / sizeof (wchar_t)) + 1;
+ else if (type == REG_MULTI_SZ)
+ bufalloc = multi_wcstombs (NULL, 0, (wchar_t *) tmpbuf,
+ size / sizeof (wchar_t));
+ else
+ bufalloc = size;
+ filebuf = (char *) cmalloc_abort (HEAP_BUF, bufalloc);
+ if (type == REG_SZ || type == REG_EXPAND_SZ || type == REG_LINK)
+ sys_wcstombs (filebuf, bufalloc, (wchar_t *) tmpbuf,
+ size / sizeof (wchar_t));
+ else if (type == REG_MULTI_SZ)
+ multi_wcstombs (filebuf, bufalloc, (wchar_t *) tmpbuf,
+ size / sizeof (wchar_t));
+ else
+ memcpy (filebuf, tmpbuf, bufalloc);
+ filesize = bufalloc;
+ }
+ else
+ {
+ bufalloc = 0;
+ do
+ {
+ bufalloc += 16 * 1024;
+ filebuf = (char *) crealloc_abort (filebuf, bufalloc);
+ size = bufalloc;
+ error = RegQueryValueExW (handle, value_name, NULL, &type,
+ (PBYTE) filebuf, &size);
+ if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return false;
+ }
+ }
+ while (error == ERROR_MORE_DATA);
+ filesize = size;
+ /* RegQueryValueEx () opens HKEY_PERFORMANCE_DATA. */
+ RegCloseKey (handle);
+ }
+ return true;
+value_not_found:
+ DWORD buf_size = NAME_MAX + 1;
+ wchar_t buf[buf_size];
+ int index = 0;
+ while (ERROR_SUCCESS ==
+ (error = RegEnumKeyExW (handle, index++, buf, &buf_size, NULL, NULL,
+ NULL, NULL)) || (error == ERROR_MORE_DATA))
+ {
+ if (!wcscasecmp (buf, value_name))
+ {
+ set_errno (EISDIR);
+ return false;
+ }
+ buf_size = NAME_MAX + 1;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return false;
+ }
+ set_errno (ENOENT);
+ return false;
+}
+
+/* Auxillary member function to open registry keys. */
+static HKEY
+open_key (const char *name, REGSAM access, DWORD wow64, bool isValue)
+{
+ HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+ HKEY hParentKey = (HKEY) INVALID_HANDLE_VALUE;
+ bool parentOpened = false;
+ wchar_t component[NAME_MAX + 1];
+
+ while (*name)
+ {
+ const char *anchor = name;
+ while (*name && !isdirsep (*name))
+ name++;
+ int val_only = decode_regname (component, anchor, name - anchor);
+ if (val_only < 0)
+ {
+ set_errno (EINVAL);
+ if (parentOpened)
+ RegCloseKey (hParentKey);
+ hKey = (HKEY) INVALID_HANDLE_VALUE;
+ break;
+ }
+ if (*name)
+ name++;
+ if (*name == 0 && isValue == true)
+ break;
+
+ if (val_only || !component[0] || hKey == HKEY_PERFORMANCE_DATA)
+ {
+ set_errno (ENOENT);
+ if (parentOpened)
+ RegCloseKey (hParentKey);
+ hKey = (HKEY) INVALID_HANDLE_VALUE;
+ break;
+ }
+
+ if (hParentKey != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ REGSAM effective_access = KEY_READ;
+ if ((strchr (name, '/') == NULL && isValue == true) || *name == 0)
+ effective_access = access;
+ LONG error = RegOpenKeyExW (hParentKey, component, 0,
+ effective_access | wow64, &hKey);
+ if (error == ERROR_ACCESS_DENIED) /* Try opening with backup intent */
+ error = RegCreateKeyExW (hParentKey, component, 0, NULL,
+ REG_OPTION_BACKUP_RESTORE,
+ effective_access | wow64, NULL,
+ &hKey, NULL);
+ if (parentOpened)
+ RegCloseKey (hParentKey);
+ if (error != ERROR_SUCCESS)
+ {
+ hKey = (HKEY) INVALID_HANDLE_VALUE;
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return hKey;
+ }
+ hParentKey = hKey;
+ parentOpened = true;
+ }
+ else
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (strncasematch (anchor, registry_listing[i], name - anchor - 1))
+ hKey = fetch_hkey (i);
+ if (hKey == (HKEY) INVALID_HANDLE_VALUE)
+ return hKey;
+ hParentKey = hKey;
+ }
+ }
+ return hKey;
+}
+
+int
+fhandler_registry::dup (fhandler_base *child, int flags)
+{
+ debug_printf ("here");
+ fhandler_registry *fhs = (fhandler_registry *) child;
+
+ int ret = fhandler_virtual::dup (fhs, flags);
+ /* Pseudo registry handles can't be duplicated using DuplicateHandle.
+ Therefore those fhandlers are marked with the nohandle flag. This
+ allows fhandler_base::dup to succeed as usual for nohandle fhandlers.
+ Here we just have to fix up by copying the pseudo handle value. */
+ if ((HKEY) get_handle () >= HKEY_CLASSES_ROOT)
+ fhs->set_handle (get_handle ());
+ if (value_name)
+ fhs->value_name = cwcsdup (value_name);
+ return ret;
+}