1129 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1129 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
/* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem
 | 
						|
 | 
						|
   Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012,
 | 
						|
   2013, 2015 Red Hat, Inc.
 | 
						|
 | 
						|
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 _COMPILING_NEWLIB
 | 
						|
#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;
 | 
						|
}
 | 
						|
 | 
						|
/* Returns 0 if path doesn't exist, >0 if path is a directory,
 | 
						|
 * <0 if path is a file.
 | 
						|
 *
 | 
						|
 * We open the last key but one and then enum it's sub-keys and values to see if the
 | 
						|
 * final component is there. This gets round the problem of not having security access
 | 
						|
 * to the final key in the path.
 | 
						|
 */
 | 
						|
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))
 | 
						|
    {
 | 
						|
      wow64 = KEY_WOW64_64KEY;
 | 
						|
      prefix_len += 2;
 | 
						|
    }
 | 
						|
  fhandler_base::set_name (in_pc);
 | 
						|
}
 | 
						|
 | 
						|
fhandler_registry::fhandler_registry ():
 | 
						|
fhandler_proc ()
 | 
						|
{
 | 
						|
  wow64 = 0;
 | 
						|
  prefix_len = sizeof ("registry") - 1;
 | 
						|
}
 | 
						|
 | 
						|
int __reg2
 | 
						|
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,
 | 
						|
		  (file_type < virt_none) ? 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 (file_type > virt_none)
 | 
						|
		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 (file_type > virt_none)
 | 
						|
		    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
 | 
						|
	{
 | 
						|
	  flags |= O_DIROPEN;
 | 
						|
	  /* 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_io_handle (fetch_hkey (i));
 | 
						|
		/* Marking as nohandle allows to call dup on pseudo registry
 | 
						|
		   handles. */
 | 
						|
		if (get_handle () >= HKEY_CLASSES_ROOT)
 | 
						|
		  nohandle (true);
 | 
						|
		flags |= O_DIROPEN;
 | 
						|
		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
 | 
						|
	flags |= O_DIROPEN;
 | 
						|
 | 
						|
      set_io_handle (handle);
 | 
						|
      set_close_on_exec (!!(flags & O_CLOEXEC));
 | 
						|
      value_name = cwcsdup (dec_file);
 | 
						|
 | 
						|
      if (!(flags & O_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_io_handle (get_handle ());
 | 
						|
  if (value_name)
 | 
						|
    fhs->value_name = cwcsdup (value_name);
 | 
						|
  return ret;
 | 
						|
}
 |