diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 1ea6196f4..fdbe3f563 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,24 @@ +2008-12-07 Christian Franke + + * 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 * libc/minires.c (open_sock): Set non blocking and close on exec. diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc index ce4335fd9..4efe48e6f 100644 --- a/winsup/cygwin/fhandler_registry.cc +++ b/winsup/cygwin/fhandler_registry.cc @@ -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)) { - file_type = 1; + if (strcasematch (buf, dec_file)) + { + file_type = 1; + 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; } - 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. */ - if (*buf == 0) - strcpy (de->d_name, DEFAULT_VALUE_NAME); - else if (encode_regname (de->d_name, buf)) - goto retry; - 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 + { + /* 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 + 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;