This is the mail archive of the cygwin-patches mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Patch] Avoid duplicate names in /proc/registry (which may crash find)


Here is a simple approach to handle the duplicate key/value name problem in /proc/registry. A value is skipped if key with same name exists. Number of actual key existence checks are reduced by a simple hash table.

The patch also adds dirent.d_type support, find does no longer crash.

Christian


2008-12-04 Christian Franke <franke@computer.org>


	* fhandler_registry.cc (__DIR_hash): New class.
	(d_hash): New macro.
	(key_exists): New function.
	(fhandler_registry::readdir): Allocate __DIR_hash.
	Record key names in hash table. Skip value if key
	with same name exists. Fix error handling of
	encode_regname (). Set dirent.d_type.
	(fhandler_registry::closedir): Delete __DIR_hash.


diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc
index ce4335f..4c95c77 100644
--- a/winsup/cygwin/fhandler_registry.cc
+++ b/winsup/cygwin/fhandler_registry.cc
@@ -143,6 +143,48 @@ decode_regname (char * dst, const char * src, int len = -1)
   return 0;
 }
 
+
+/* 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 char * name, DWORD wow64)
+{
+  HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+  LONG error = RegOpenKeyEx (parent, name, 0, KEY_READ | wow64, &hKey);
+  if (error == ERROR_SUCCESS)
+    RegCloseKey (hKey);
+
+  return (error == ERROR_SUCCESS || error == ERROR_ACCESS_DENIED);
+}
+
 /* Returns 0 if path doesn't exist, >0 if path is a directory,
  * <0 if path is a file.
  *
@@ -381,13 +423,16 @@ fhandler_registry::readdir (DIR *dir, dirent *de)
       res = 0;
       goto out;
     }
-  if (dir->__handle == INVALID_HANDLE_VALUE && dir->__d_position == 0)
+  if (dir->__handle == INVALID_HANDLE_VALUE)
     {
+      if (dir->__d_position != 0)
+	goto out;
       handle = open_key (path + 1, KEY_READ, wow64, false);
       dir->__handle = handle;
+      if (dir->__handle == INVALID_HANDLE_VALUE)
+	goto out;
+      dir->__d_internal = (unsigned) new __DIR_hash ();
     }
-  if (dir->__handle == INVALID_HANDLE_VALUE)
-    goto out;
   if (dir->__d_position < SPECIAL_DOT_FILE_COUNT)
     {
       strcpy (de->d_name, special_dot_files[dir->__d_position++]);
@@ -425,14 +470,37 @@ retry:
     }
 
   /* We get here if `buf' contains valid data.  */
+  dir->__d_position++;
+  if (dir->__d_position & REG_ENUM_VALUES_MASK)
+    dir->__d_position += 0x10000;
+
   if (*buf == 0)
     strcpy (de->d_name, DEFAULT_VALUE_NAME);
-  else if (encode_regname (de->d_name, buf))
-    goto retry;
+  else
+    {
+      /* Skip value if name is identical to a previous key name.  */
+      unsigned h = hash_path_name (1, buf);
+      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))
+	{
+	  buf_size = NAME_MAX + 1;
+	  goto retry;
+	}
+
+      if (encode_regname (de->d_name, buf))
+	{
+	  buf_size = NAME_MAX + 1;
+	  goto retry;
+	}
+    }
 
-  dir->__d_position++;
   if (dir->__d_position & REG_ENUM_VALUES_MASK)
-    dir->__d_position += 0x10000;
+    de->d_type = DT_REG;
+  else
+    de->d_type = DT_DIR;
+
   res = 0;
 out:
   syscall_printf ("%d = readdir (%p, %p)", res, dir, de);
@@ -473,11 +541,14 @@ int
 fhandler_registry::closedir (DIR * dir)
 {
   int res = 0;
-  if (dir->__handle != INVALID_HANDLE_VALUE &&
-      RegCloseKey ((HKEY) dir->__handle) != ERROR_SUCCESS)
+  if (dir->__handle != INVALID_HANDLE_VALUE)
     {
-      __seterrno ();
-      res = -1;
+      delete d_hash (dir);
+      if (RegCloseKey ((HKEY) dir->__handle) != ERROR_SUCCESS)
+	{
+	  __seterrno ();
+	  res = -1;
+	}
     }
   syscall_printf ("%d = closedir (%p)", res, dir);
   return 0;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]