This is the mail archive of the
cygwin-patches
mailing list for the Cygwin project.
[Patch] Avoid duplicate names in /proc/registry (which may crash find)
- From: Christian Franke <Christian dot Franke at t-online dot de>
- To: cygwin-patches at cygwin dot com
- Date: Thu, 04 Dec 2008 21:49:20 +0100
- Subject: [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;