OSDN Git Service

* fhandler_registry.cc (encode_regname): Add Parameter add_val.
authorcorinna <corinna>
Mon, 8 Dec 2008 11:45:17 +0000 (11:45 +0000)
committercorinna <corinna>
Mon, 8 Dec 2008 11:45:17 +0000 (11:45 +0000)
Append "%val" if add_val is set.
(decode_regname): Remove trailing "%val". Change returncode accordingly.
(__DIR_hash): New class.
(d_hash): New macro.
(key_exists): New function.
(fhandler_registry::exists): Remove encode of registry name before path
compare, decode file part of path instead.  Skip checks for keys if
trailing "%val" detected.
(fhandler_registry::fstat): Change check of return value of
decode_regname ().
(fhandler_registry::readdir): Allocate __DIR_hash.  Record key names in
hash table. Append "%val" if key with same name exists. Fix error
handling of encode_regname ().  Set dirent.d_type.
(fhandler_registry::closedir): Delete __DIR_hash.
(fhandler_registry::open): Don't open key if trailing "%val" detected
by decode_regname ().
(open_key): Ditto.

winsup/cygwin/ChangeLog
winsup/cygwin/fhandler_registry.cc

index 1ea6196..fdbe3f5 100644 (file)
@@ -1,3 +1,24 @@
+2008-12-07  Christian Franke  <franke@computer.org>
+
+       * fhandler_registry.cc (encode_regname): Add Parameter add_val.
+       Append "%val" if add_val is set.
+       (decode_regname): Remove trailing "%val". Change returncode accordingly.
+       (__DIR_hash): New class.
+       (d_hash): New macro.
+       (key_exists): New function.
+       (fhandler_registry::exists): Remove encode of registry name before path
+       compare, decode file part of path instead.  Skip checks for keys if
+       trailing "%val" detected.
+       (fhandler_registry::fstat): Change check of return value of
+       decode_regname ().
+       (fhandler_registry::readdir): Allocate __DIR_hash.  Record key names in
+       hash table. Append "%val" if key with same name exists. Fix error
+       handling of encode_regname ().  Set dirent.d_type.
+       (fhandler_registry::closedir): Delete __DIR_hash.
+       (fhandler_registry::open): Don't open key if trailing "%val" detected
+       by decode_regname ().
+       (open_key): Ditto.
+
 2008-12-03  Pierre A. Humblet  <Pierre.Humblet@ieee.org>
 
        * libc/minires.c (open_sock): Set non blocking and close on exec.
index ce4335f..4efe48e 100644 (file)
@@ -91,7 +91,7 @@ must_encode (char c)
 /* Encode special chars in registry key or value name.
  */
 static int
-encode_regname (char * dst, const char * src)
+encode_regname (char * dst, const char * src, bool add_val)
 {
   int di = 0;
   for (int si = 0; src[si]; si++)
@@ -108,31 +108,47 @@ encode_regname (char * dst, const char * src)
       else
        dst[di++] = c;
     }
+
+  if (add_val)
+    {
+      if (di + 4 >= NAME_MAX + 1)
+       return ENAMETOOLONG;
+      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 (char * dst, const char * src, int len = -1)
 {
   if (len < 0)
     len = strlen (src);
+  int res = 0;
   int di = 0;
   for (int si = 0; si < len; si++)
     {
       char c = src[si];
       if (c == '%')
        {
+         if (si + 4 == len && !memcmp (src + si, "%val", 4))
+           {
+             res = 1;
+             break;
+           }
          if (si + 2 >= len)
-           return EINVAL;
+           return -1;
          char s[] = {src[si+1], src[si+2], '\0'};
          char *p;
          c = strtoul (s, &p, 16);
          if (!(must_encode (c) ||
                (c == '.' && si == 0 && (len == 3 || (src[3] == '.' && len == 4)))))
-           return EINVAL;
+           return -1;
          dst[di++] = c;
          si += 2;
        }
@@ -140,7 +156,49 @@ decode_regname (char * dst, const char * src, int len = -1)
        dst[di++] = c;
     }
   dst[di] = 0;
-  return 0;
+  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 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,
@@ -190,7 +248,13 @@ fhandler_registry::exists ()
       goto out;
     }
 
-  hKey = open_key (path, KEY_READ, wow64, false);
+  char 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)
     file_type = 1;
   else
@@ -199,33 +263,36 @@ fhandler_registry::exists ()
       if (hKey == (HKEY) INVALID_HANDLE_VALUE)
        return 0;
 
-      while (ERROR_SUCCESS ==
-            (error = RegEnumKeyEx (hKey, index++, buf, &buf_size, NULL, NULL,
-                                    NULL, NULL))
-            || (error == ERROR_MORE_DATA))
+      if (!val_only)
        {
-         if (strcasematch (buf, file))
+         while (ERROR_SUCCESS ==
+                (error = RegEnumKeyEx (hKey, index++, buf, &buf_size,
+                                       NULL, NULL, NULL, NULL))
+                || (error == ERROR_MORE_DATA))
+           {
+             if (strcasematch (buf, dec_file))
+               {
+                 file_type = 1;
+                 goto out;
+               }
+               buf_size = NAME_MAX + 1;
+           }
+         if (error != ERROR_NO_MORE_ITEMS)
            {
-             file_type = 1;
+             seterrno_from_win_error (__FILE__, __LINE__, error);
              goto out;
            }
+         index = 0;
          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 = RegEnumValue (hKey, index++, buf, &buf_size, NULL, NULL,
                                    NULL, NULL))
             || (error == ERROR_MORE_DATA))
        {
-         char enc_buf[NAME_MAX + 1];
          if (   (buf[0] == '\0' && strcasematch (file, DEFAULT_VALUE_NAME))
-             || (!encode_regname (enc_buf, buf) && strcasematch (enc_buf, file)))
+             || strcasematch (buf, dec_file))
            {
              file_type = -1;
              goto out;
@@ -324,7 +391,7 @@ fhandler_registry::fstat (struct __stat64 *buf)
                  value_name++;
                  char dec_value_name[NAME_MAX + 1];
                  DWORD dwSize;
-                 if (!decode_regname (dec_value_name, value_name) &&
+                 if (decode_regname (dec_value_name, value_name) >= 0 &&
                      ERROR_SUCCESS ==
                      RegQueryValueEx (hKey, dec_value_name, NULL, NULL, NULL,
                                       &dwSize))
@@ -381,13 +448,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 +495,35 @@ 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
+    {
+      /* 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;
+       }
+    }
 
-  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 +564,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;
@@ -488,7 +582,7 @@ fhandler_registry::open (int flags, mode_t mode)
 {
   int pathlen;
   const char *file;
-  HKEY handle;
+  HKEY handle = (HKEY) INVALID_HANDLE_VALUE;
 
   int res = fhandler_virtual::open (flags, mode);
   if (!res)
@@ -573,14 +667,16 @@ fhandler_registry::open (int flags, mode_t mode)
     }
 
   char dec_file[NAME_MAX + 1];
-  if (decode_regname (dec_file, file))
+  int val_only = decode_regname (dec_file, file);
+  if (val_only < 0)
     {
       set_errno (EINVAL);
       res = 0;
       goto out;
     }
 
-  handle = open_key (path, KEY_READ, wow64, false);
+  if (!val_only)
+    handle = open_key (path, KEY_READ, wow64, false);
   if (handle == (HKEY) INVALID_HANDLE_VALUE)
     {
       handle = open_key (path, KEY_READ, wow64, true);
@@ -736,7 +832,8 @@ open_key (const char *name, REGSAM access, DWORD wow64, bool isValue)
       const char *anchor = name;
       while (*name && !isdirsep (*name))
        name++;
-      if (decode_regname (component, anchor, name - anchor))
+      int val_only = decode_regname (component, anchor, name - anchor);
+      if (val_only < 0)
         {
          set_errno (EINVAL);
          if (parentOpened)
@@ -749,6 +846,15 @@ open_key (const char *name, REGSAM access, DWORD wow64, bool isValue)
       if (*name == 0 && isValue == true)
        goto out;
 
+      if (val_only)
+       {
+         set_errno (ENOENT);
+         if (parentOpened)
+           RegCloseKey (hParentKey);
+         hKey = (HKEY) INVALID_HANDLE_VALUE;
+         break;
+       }
+
       if (hParentKey != (HKEY) INVALID_HANDLE_VALUE)
        {
          REGSAM effective_access = KEY_READ;