]>
Commit | Line | Data |
---|---|---|
c477dccf CF |
1 | /* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem |
2 | ||
6e75c72b CF |
3 | Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
4 | 2013 Red Hat, Inc. | |
c477dccf CF |
5 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
11 | ||
12 | /* FIXME: Access permissions are ignored at the moment. */ | |
13 | ||
14 | #include "winsup.h" | |
651d8393 | 15 | #include <stdlib.h> |
c477dccf CF |
16 | #include "cygerrno.h" |
17 | #include "security.h" | |
c477dccf | 18 | #include "path.h" |
7ac61736 | 19 | #include "fhandler.h" |
8761c1dc CF |
20 | #include "dtable.h" |
21 | #include "cygheap.h" | |
56a19715 | 22 | #include "child_info.h" |
c477dccf CF |
23 | |
24 | #define _COMPILING_NEWLIB | |
25 | #include <dirent.h> | |
26 | ||
c477dccf CF |
27 | /* If this bit is set in __d_position then we are enumerating values, |
28 | * else sub-keys. keeping track of where we are is horribly messy | |
29 | * the bottom 16 bits are the absolute position and the top 15 bits | |
30 | * make up the value index if we are enuerating values. | |
31 | */ | |
61522196 CV |
32 | static const __int32_t REG_ENUM_VALUES_MASK = 0x8000000; |
33 | static const __int32_t REG_POSITION_MASK = 0xffff; | |
c477dccf | 34 | |
b2ec6677 CV |
35 | /* These key paths are used below whenever we return key information. |
36 | The problem is UAC virtualization when running an admin account with | |
37 | restricted rights. In that case the subkey "Classes" in the VirtualStore | |
38 | points to the HKEY_CLASSES_ROOT key again. If "Classes" is handled as a | |
39 | normal subdirectory, applications recursing throught the directory | |
40 | hirarchy will invariably run into an infinite recursion. What we do here | |
41 | is to handle the "Classes" subkey as a symlink to HKEY_CLASSES_ROOT. This | |
42 | avoids the infinite recursion, unless the application blindly follows | |
43 | symlinks pointing to directories, in which case it's their own fault. */ | |
44 | #define VIRT_CLASSES_KEY_PREFIX "/VirtualStore/MACHINE/SOFTWARE" | |
45 | #define VIRT_CLASSES_KEY_SUFFIX "Classes" | |
46 | #define VIRT_CLASSES_KEY VIRT_CLASSES_KEY_PREFIX "/" VIRT_CLASSES_KEY_SUFFIX | |
47 | #define VIRT_CLASSES_LINKTGT "/proc/registry/HKEY_CLASSES_ROOT" | |
48 | ||
c477dccf CF |
49 | /* List of root keys in /proc/registry. |
50 | * Possibly we should filter out those not relevant to the flavour of Windows | |
51 | * Cygwin is running on. | |
52 | */ | |
8761c1dc CF |
53 | static const char *registry_listing[] = |
54 | { | |
55 | ".", | |
56 | "..", | |
c477dccf CF |
57 | "HKEY_CLASSES_ROOT", |
58 | "HKEY_CURRENT_CONFIG", | |
59 | "HKEY_CURRENT_USER", | |
60 | "HKEY_LOCAL_MACHINE", | |
61 | "HKEY_USERS", | |
61522196 | 62 | "HKEY_PERFORMANCE_DATA", |
c477dccf CF |
63 | NULL |
64 | }; | |
65 | ||
8761c1dc CF |
66 | static const HKEY registry_keys[] = |
67 | { | |
68 | (HKEY) INVALID_HANDLE_VALUE, | |
69 | (HKEY) INVALID_HANDLE_VALUE, | |
c477dccf CF |
70 | HKEY_CLASSES_ROOT, |
71 | HKEY_CURRENT_CONFIG, | |
72 | HKEY_CURRENT_USER, | |
73 | HKEY_LOCAL_MACHINE, | |
74 | HKEY_USERS, | |
c477dccf CF |
75 | HKEY_PERFORMANCE_DATA |
76 | }; | |
77 | ||
74fcdaec | 78 | static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY); |
c477dccf | 79 | |
b2ec6677 CV |
80 | /* Make sure to access the correct per-user HKCR and HKCU hives, even if |
81 | the current user is only impersonated in another user's session. */ | |
82 | static HKEY | |
83 | fetch_hkey (int idx) /* idx *must* be valid */ | |
84 | { | |
85 | HKEY key; | |
86 | ||
87 | if (registry_keys[idx] == HKEY_CLASSES_ROOT) | |
88 | { | |
89 | if (RegOpenUserClassesRoot (cygheap->user.issetuid () | |
90 | ? cygheap->user.imp_token () : hProcImpToken, | |
91 | 0, KEY_READ, &key) == ERROR_SUCCESS) | |
92 | return key; | |
93 | } | |
94 | else if (registry_keys[idx] == HKEY_CURRENT_USER) | |
95 | { | |
96 | if (RegOpenCurrentUser (KEY_READ, &key) == ERROR_SUCCESS) | |
97 | return key; | |
98 | } | |
99 | return registry_keys[idx]; | |
100 | } | |
101 | ||
8761c1dc CF |
102 | /* These get added to each subdirectory in /proc/registry. |
103 | * If we wanted to implement writing, we could maybe add a '.writable' entry or | |
104 | * suchlike. | |
105 | */ | |
106 | static const char *special_dot_files[] = | |
107 | { | |
108 | ".", | |
109 | "..", | |
110 | NULL | |
111 | }; | |
112 | ||
74fcdaec CF |
113 | static const int SPECIAL_DOT_FILE_COUNT = |
114 | (sizeof (special_dot_files) / sizeof (const char *)) - 1; | |
8761c1dc | 115 | |
887eb76f CV |
116 | /* Value names for HKEY_PERFORMANCE_DATA. |
117 | * | |
118 | * CAUTION: Never call RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Add", ...). | |
119 | * It WRITES data and may destroy the perfc009.dat file. Same applies to | |
120 | * name prefixes "Ad" and "A". | |
121 | */ | |
122 | static const char * const perf_data_files[] = | |
123 | { | |
124 | "@", | |
125 | "Costly", | |
126 | "Global" | |
127 | }; | |
128 | ||
129 | static const int PERF_DATA_FILE_COUNT = | |
130 | sizeof (perf_data_files) / sizeof (perf_data_files[0]); | |
131 | ||
20f9af53 | 132 | static HKEY open_key (const char *name, REGSAM access, DWORD wow64, bool isValue); |
74fcdaec | 133 | |
651d8393 CV |
134 | /* Return true if char must be encoded. |
135 | */ | |
136 | static inline bool | |
333a47d3 | 137 | must_encode (wchar_t c) |
651d8393 | 138 | { |
333a47d3 | 139 | return (iswdirsep (c) || c == L':' || c == L'%'); |
651d8393 CV |
140 | } |
141 | ||
142 | /* Encode special chars in registry key or value name. | |
2cc8b9e0 | 143 | * Returns 0: success, -1: error. |
651d8393 CV |
144 | */ |
145 | static int | |
333a47d3 | 146 | encode_regname (char *dst, const wchar_t *src, bool add_val) |
651d8393 CV |
147 | { |
148 | int di = 0; | |
2cc8b9e0 CV |
149 | if (!src[0]) |
150 | dst[di++] = '@'; // Default value. | |
151 | else | |
152 | for (int si = 0; src[si]; si++) | |
153 | { | |
333a47d3 | 154 | wchar_t c = src[si]; |
2cc8b9e0 | 155 | if (must_encode (c) || |
333a47d3 CV |
156 | (si == 0 && ((c == L'.' |
157 | && (!src[1] || (src[1] == L'.' && !src[2]))) | |
158 | || (c == L'@' && !src[1])))) | |
2cc8b9e0 CV |
159 | { |
160 | if (di + 3 >= NAME_MAX + 1) | |
161 | return -1; | |
162 | __small_sprintf (dst + di, "%%%02x", c); | |
163 | di += 3; | |
164 | } | |
165 | else | |
333a47d3 | 166 | di += sys_wcstombs (dst + di, NAME_MAX + 1 - di, &c, 1); |
2cc8b9e0 | 167 | } |
d27ebea9 CV |
168 | |
169 | if (add_val) | |
170 | { | |
171 | if (di + 4 >= NAME_MAX + 1) | |
2cc8b9e0 | 172 | return -1; |
d27ebea9 CV |
173 | memcpy (dst + di, "%val", 4); |
174 | di += 4; | |
175 | } | |
176 | ||
651d8393 CV |
177 | dst[di] = 0; |
178 | return 0; | |
179 | } | |
180 | ||
181 | /* Decode special chars in registry key or value name. | |
d27ebea9 | 182 | * Returns 0: success, 1: "%val" detected, -1: error. |
651d8393 CV |
183 | */ |
184 | static int | |
333a47d3 | 185 | decode_regname (wchar_t *wdst, const char *src, int len = -1) |
651d8393 CV |
186 | { |
187 | if (len < 0) | |
188 | len = strlen (src); | |
333a47d3 | 189 | char dst[len + 1]; |
d27ebea9 | 190 | int res = 0; |
333a47d3 | 191 | |
2cc8b9e0 | 192 | if (len > 4 && !memcmp (src + len - 4, "%val", 4)) |
651d8393 | 193 | { |
2cc8b9e0 CV |
194 | len -= 4; |
195 | res = 1; | |
651d8393 | 196 | } |
2cc8b9e0 CV |
197 | |
198 | int di = 0; | |
199 | if (len == 1 && src[0] == '@') | |
200 | ; // Default value. | |
201 | else | |
202 | for (int si = 0; si < len; si++) | |
203 | { | |
204 | char c = src[si]; | |
205 | if (c == '%') | |
206 | { | |
207 | if (si + 2 >= len) | |
208 | return -1; | |
209 | char s[] = {src[si+1], src[si+2], '\0'}; | |
210 | char *p; | |
211 | c = strtoul (s, &p, 16); | |
333a47d3 | 212 | if (!(must_encode ((wchar_t) c) || |
2cc8b9e0 CV |
213 | (si == 0 && ((c == '.' && (len == 3 || (src[3] == '.' && len == 4))) || |
214 | (c == '@' && len == 3))))) | |
215 | return -1; | |
216 | dst[di++] = c; | |
217 | si += 2; | |
218 | } | |
219 | else | |
220 | dst[di++] = c; | |
221 | } | |
222 | ||
651d8393 | 223 | dst[di] = 0; |
333a47d3 | 224 | sys_mbstowcs (wdst, NAME_MAX + 1, dst); |
d27ebea9 CV |
225 | return res; |
226 | } | |
227 | ||
228 | ||
229 | /* Hash table to limit calls to key_exists (). | |
230 | */ | |
231 | class __DIR_hash | |
232 | { | |
233 | public: | |
234 | __DIR_hash () | |
235 | { | |
236 | memset (table, 0, sizeof(table)); | |
237 | } | |
238 | ||
239 | void set (unsigned h) | |
240 | { | |
241 | table [(h >> 3) & (HASH_SIZE - 1)] |= (1 << (h & 0x3)); | |
242 | } | |
243 | ||
244 | bool is_set (unsigned h) const | |
245 | { | |
246 | return (table [(h >> 3) & (HASH_SIZE - 1)] & (1 << (h & 0x3))) != 0; | |
247 | } | |
248 | ||
249 | private: | |
250 | enum { HASH_SIZE = 1024 }; | |
251 | unsigned char table[HASH_SIZE]; | |
252 | }; | |
253 | ||
254 | #define d_hash(d) ((__DIR_hash *) (d)->__d_internal) | |
255 | ||
256 | ||
257 | /* Return true if subkey NAME exists in key PARENT. | |
258 | */ | |
259 | static bool | |
333a47d3 | 260 | key_exists (HKEY parent, const wchar_t *name, DWORD wow64) |
d27ebea9 CV |
261 | { |
262 | HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; | |
333a47d3 | 263 | LONG error = RegOpenKeyExW (parent, name, 0, KEY_READ | wow64, &hKey); |
d27ebea9 CV |
264 | if (error == ERROR_SUCCESS) |
265 | RegCloseKey (hKey); | |
266 | ||
267 | return (error == ERROR_SUCCESS || error == ERROR_ACCESS_DENIED); | |
651d8393 CV |
268 | } |
269 | ||
88addc64 CV |
270 | static size_t |
271 | multi_wcstombs (char *dst, size_t len, const wchar_t *src, size_t nwc) | |
272 | { | |
273 | size_t siz, sum = 0; | |
274 | const wchar_t *nsrc; | |
275 | ||
276 | while (nwc) | |
277 | { | |
278 | siz = sys_wcstombs (dst, len, src, nwc); | |
279 | sum += siz; | |
280 | if (dst) | |
281 | { | |
282 | dst += siz; | |
283 | len -= siz; | |
284 | } | |
285 | nsrc = wcschr (src, L'\0') + 1; | |
286 | if ((size_t) (nsrc - src) >= nwc) | |
287 | break; | |
288 | nwc -= nsrc - src; | |
289 | src = nsrc; | |
290 | if (*src == L'\0') | |
291 | { | |
292 | if (dst) | |
293 | *dst++ = '\0'; | |
294 | ++sum; | |
295 | break; | |
296 | } | |
297 | } | |
298 | return sum; | |
299 | } | |
300 | ||
c477dccf CF |
301 | /* Returns 0 if path doesn't exist, >0 if path is a directory, |
302 | * <0 if path is a file. | |
303 | * | |
304 | * We open the last key but one and then enum it's sub-keys and values to see if the | |
305 | * final component is there. This gets round the problem of not having security access | |
306 | * to the final key in the path. | |
307 | */ | |
43f65cdd | 308 | virtual_ftype_t |
fc240f58 | 309 | fhandler_registry::exists () |
c477dccf | 310 | { |
43f65cdd CV |
311 | virtual_ftype_t file_type = virt_none; |
312 | int index = 0, pathlen; | |
5224a7bb | 313 | DWORD buf_size = NAME_MAX + 1; |
c477dccf | 314 | LONG error; |
333a47d3 | 315 | wchar_t buf[buf_size]; |
c477dccf CF |
316 | const char *file; |
317 | HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; | |
318 | ||
fc240f58 | 319 | const char *path = get_name (); |
c477dccf | 320 | debug_printf ("exists (%s)", path); |
20f9af53 | 321 | path += proc_len + prefix_len + 1; |
704ce461 CF |
322 | if (*path) |
323 | path++; | |
324 | else | |
c477dccf | 325 | { |
43f65cdd | 326 | file_type = virt_rootdir; |
c477dccf CF |
327 | goto out; |
328 | } | |
329 | pathlen = strlen (path); | |
330 | file = path + pathlen - 1; | |
85ba109d | 331 | if (isdirsep (*file) && pathlen > 1) |
c477dccf | 332 | file--; |
85ba109d | 333 | while (!isdirsep (*file)) |
c477dccf CF |
334 | file--; |
335 | file++; | |
336 | ||
337 | if (file == path) | |
305b19d7 CV |
338 | { |
339 | for (int i = 0; registry_listing[i]; i++) | |
340 | if (path_prefix_p (registry_listing[i], path, | |
341 | strlen (registry_listing[i]), true)) | |
342 | { | |
43f65cdd | 343 | file_type = virt_directory; |
305b19d7 CV |
344 | break; |
345 | } | |
346 | } | |
74fcdaec | 347 | else |
c477dccf | 348 | { |
333a47d3 | 349 | wchar_t dec_file[NAME_MAX + 1]; |
9fe25af7 CF |
350 | |
351 | int val_only = decode_regname (dec_file, file); | |
352 | if (val_only < 0) | |
353 | goto out; | |
74fcdaec | 354 | |
d27ebea9 | 355 | if (!val_only) |
9fe25af7 | 356 | hKey = open_key (path, KEY_READ, wow64, false); |
2849cbde | 357 | if (hKey != (HKEY) INVALID_HANDLE_VALUE) |
b2ec6677 CV |
358 | { |
359 | if (!strcasecmp (path + strlen (path) | |
360 | - sizeof (VIRT_CLASSES_KEY) + 1, | |
361 | VIRT_CLASSES_KEY)) | |
362 | file_type = virt_symlink; | |
363 | else | |
364 | file_type = virt_directory; | |
365 | } | |
9fe25af7 | 366 | else |
74fcdaec | 367 | { |
2849cbde CV |
368 | /* Key does not exist or open failed with EACCES, |
369 | enumerate subkey and value names of parent key. */ | |
9fe25af7 CF |
370 | hKey = open_key (path, KEY_READ, wow64, true); |
371 | if (hKey == (HKEY) INVALID_HANDLE_VALUE) | |
43f65cdd | 372 | return virt_none; |
9fe25af7 | 373 | |
887eb76f CV |
374 | if (hKey == HKEY_PERFORMANCE_DATA) |
375 | { | |
376 | /* RegEnumValue () returns garbage for this key. | |
b86f999a CF |
377 | RegQueryValueEx () returns a PERF_DATA_BLOCK even |
378 | if a value does not contain any counter objects. | |
379 | So allow access to the generic names and to | |
380 | (blank separated) lists of counter numbers. | |
381 | Never allow access to "Add", see above comment. */ | |
43f65cdd CV |
382 | for (int i = 0; i < PERF_DATA_FILE_COUNT |
383 | && file_type == virt_none; i++) | |
887eb76f CV |
384 | { |
385 | if (strcasematch (perf_data_files[i], file)) | |
43f65cdd | 386 | file_type = virt_file; |
887eb76f | 387 | } |
43f65cdd CV |
388 | if (file_type == virt_none && !file[strspn (file, " 0123456789")]) |
389 | file_type = virt_file; | |
887eb76f CV |
390 | goto out; |
391 | } | |
392 | ||
2cc8b9e0 | 393 | if (!val_only && dec_file[0]) |
9fe25af7 CF |
394 | { |
395 | while (ERROR_SUCCESS == | |
333a47d3 CV |
396 | (error = RegEnumKeyExW (hKey, index++, buf, &buf_size, |
397 | NULL, NULL, NULL, NULL)) | |
9fe25af7 CF |
398 | || (error == ERROR_MORE_DATA)) |
399 | { | |
333a47d3 | 400 | if (!wcscasecmp (buf, dec_file)) |
9fe25af7 | 401 | { |
43f65cdd | 402 | file_type = virt_directory; |
9fe25af7 CF |
403 | goto out; |
404 | } | |
405 | buf_size = NAME_MAX + 1; | |
406 | } | |
407 | if (error != ERROR_NO_MORE_ITEMS) | |
408 | { | |
409 | seterrno_from_win_error (__FILE__, __LINE__, error); | |
410 | goto out; | |
411 | } | |
412 | index = 0; | |
413 | buf_size = NAME_MAX + 1; | |
414 | } | |
415 | ||
d27ebea9 | 416 | while (ERROR_SUCCESS == |
333a47d3 CV |
417 | (error = RegEnumValueW (hKey, index++, buf, &buf_size, |
418 | NULL, NULL, NULL, NULL)) | |
d27ebea9 CV |
419 | || (error == ERROR_MORE_DATA)) |
420 | { | |
333a47d3 | 421 | if (!wcscasecmp (buf, dec_file)) |
d27ebea9 | 422 | { |
43f65cdd | 423 | file_type = virt_file; |
d27ebea9 CV |
424 | goto out; |
425 | } | |
9fe25af7 | 426 | buf_size = NAME_MAX + 1; |
d27ebea9 CV |
427 | } |
428 | if (error != ERROR_NO_MORE_ITEMS) | |
74fcdaec | 429 | { |
d27ebea9 | 430 | seterrno_from_win_error (__FILE__, __LINE__, error); |
74fcdaec CF |
431 | goto out; |
432 | } | |
2402700d | 433 | } |
c477dccf CF |
434 | } |
435 | out: | |
436 | if (hKey != (HKEY) INVALID_HANDLE_VALUE) | |
437 | RegCloseKey (hKey); | |
438 | return file_type; | |
439 | } | |
440 | ||
20f9af53 CV |
441 | void |
442 | fhandler_registry::set_name (path_conv &in_pc) | |
510a85cb | 443 | { |
20f9af53 CV |
444 | if (strncasematch (in_pc.normalized_path, "/proc/registry32", 16)) |
445 | { | |
446 | wow64 = KEY_WOW64_32KEY; | |
447 | prefix_len += 2; | |
448 | } | |
449 | else if (strncasematch (in_pc.normalized_path, "/proc/registry64", 16)) | |
450 | { | |
451 | wow64 = KEY_WOW64_64KEY; | |
452 | prefix_len += 2; | |
453 | } | |
454 | fhandler_base::set_name (in_pc); | |
455 | } | |
456 | ||
c477dccf | 457 | fhandler_registry::fhandler_registry (): |
7ac61736 | 458 | fhandler_proc () |
c477dccf | 459 | { |
20f9af53 CV |
460 | wow64 = 0; |
461 | prefix_len = sizeof ("registry") - 1; | |
c477dccf CF |
462 | } |
463 | ||
6e75c72b | 464 | int __reg2 |
61522196 | 465 | fhandler_registry::fstat (struct stat *buf) |
c477dccf | 466 | { |
7ac61736 | 467 | fhandler_base::fstat (buf); |
c477dccf | 468 | buf->st_mode &= ~_IFMT & NO_W; |
43f65cdd | 469 | virtual_ftype_t file_type = exists (); |
c477dccf CF |
470 | switch (file_type) |
471 | { | |
43f65cdd | 472 | case virt_none: |
c477dccf CF |
473 | set_errno (ENOENT); |
474 | return -1; | |
b2ec6677 CV |
475 | case virt_symlink: |
476 | buf->st_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | |
477 | break; | |
43f65cdd | 478 | case virt_directory: |
c477dccf CF |
479 | buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; |
480 | break; | |
43f65cdd | 481 | case virt_rootdir: |
c477dccf CF |
482 | buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; |
483 | buf->st_nlink = ROOT_KEY_COUNT; | |
484 | break; | |
485 | default: | |
43f65cdd | 486 | case virt_file: |
c477dccf CF |
487 | buf->st_mode |= S_IFREG; |
488 | buf->st_mode &= NO_X; | |
489 | break; | |
490 | } | |
43f65cdd | 491 | if (file_type != virt_none && file_type != virt_rootdir) |
74fcdaec CF |
492 | { |
493 | HKEY hKey; | |
20f9af53 | 494 | const char *path = get_name () + proc_len + prefix_len + 2; |
74fcdaec | 495 | hKey = |
20f9af53 | 496 | open_key (path, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, wow64, |
43f65cdd | 497 | (file_type < virt_none) ? true : false); |
74fcdaec | 498 | |
887eb76f CV |
499 | if (hKey == HKEY_PERFORMANCE_DATA) |
500 | /* RegQueryInfoKey () always returns write time 0, | |
501 | RegQueryValueEx () does not return required buffer size. */ | |
502 | ; | |
503 | else if (hKey != (HKEY) INVALID_HANDLE_VALUE) | |
74fcdaec CF |
504 | { |
505 | FILETIME ftLastWriteTime; | |
506 | DWORD subkey_count; | |
507 | if (ERROR_SUCCESS == | |
e5c91e16 CV |
508 | RegQueryInfoKeyW (hKey, NULL, NULL, NULL, &subkey_count, NULL, |
509 | NULL, NULL, NULL, NULL, NULL, &ftLastWriteTime)) | |
74fcdaec CF |
510 | { |
511 | to_timestruc_t (&ftLastWriteTime, &buf->st_mtim); | |
70de8290 | 512 | buf->st_ctim = buf->st_birthtim = buf->st_mtim; |
74fcdaec | 513 | time_as_timestruc_t (&buf->st_atim); |
43f65cdd | 514 | if (file_type > virt_none) |
4f27e288 | 515 | buf->st_nlink = subkey_count + 2; |
74fcdaec CF |
516 | else |
517 | { | |
518 | int pathlen = strlen (path); | |
519 | const char *value_name = path + pathlen - 1; | |
85ba109d | 520 | if (isdirsep (*value_name) && pathlen > 1) |
74fcdaec | 521 | value_name--; |
85ba109d | 522 | while (!isdirsep (*value_name)) |
74fcdaec CF |
523 | value_name--; |
524 | value_name++; | |
333a47d3 CV |
525 | wchar_t dec_value_name[NAME_MAX + 1]; |
526 | DWORD dwSize = 0; | |
527 | DWORD type; | |
528 | if (decode_regname (dec_value_name, value_name) >= 0 | |
529 | && RegQueryValueExW (hKey, dec_value_name, NULL, &type, | |
530 | NULL, &dwSize) == ERROR_SUCCESS | |
531 | && (type == REG_SZ || type == REG_EXPAND_SZ | |
532 | || type == REG_MULTI_SZ || type == REG_LINK)) | |
533 | { | |
534 | PBYTE tmpbuf = (PBYTE) malloc (dwSize); | |
535 | if (!tmpbuf | |
536 | || RegQueryValueExW (hKey, dec_value_name, | |
537 | NULL, NULL, tmpbuf, &dwSize) | |
538 | != ERROR_SUCCESS) | |
539 | buf->st_size = dwSize / sizeof (wchar_t); | |
88addc64 CV |
540 | else if (type == REG_MULTI_SZ) |
541 | buf->st_size = multi_wcstombs (NULL, 0, | |
542 | (wchar_t *) tmpbuf, | |
543 | dwSize / sizeof (wchar_t)); | |
333a47d3 CV |
544 | else |
545 | buf->st_size = sys_wcstombs (NULL, 0, | |
546 | (wchar_t *) tmpbuf, | |
547 | dwSize / sizeof (wchar_t)); | |
88addc64 CV |
548 | if (tmpbuf) |
549 | free (tmpbuf); | |
333a47d3 CV |
550 | } |
551 | else | |
74fcdaec CF |
552 | buf->st_size = dwSize; |
553 | } | |
61522196 CV |
554 | uid_t uid; |
555 | gid_t gid; | |
eea4e482 | 556 | if (get_reg_attribute (hKey, &buf->st_mode, &uid, &gid) == 0) |
74fcdaec CF |
557 | { |
558 | buf->st_uid = uid; | |
559 | buf->st_gid = gid; | |
560 | buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); | |
43f65cdd | 561 | if (file_type > virt_none) |
74fcdaec CF |
562 | buf->st_mode |= S_IFDIR; |
563 | else | |
564 | buf->st_mode &= NO_X; | |
565 | } | |
566 | } | |
567 | RegCloseKey (hKey); | |
568 | } | |
9367c0dc | 569 | else |
510a85cb | 570 | { |
9367c0dc CV |
571 | /* Here's the problem: If we can't open the key, we don't know |
572 | nothing at all about the key/value. It's only clear that | |
573 | the current user has no read access. At this point it's | |
574 | rather unlikely that the user has write or execute access | |
575 | and it's also rather unlikely that the user is the owner. | |
576 | Therefore it's probably most safe to assume unknown ownership | |
577 | and no permissions for nobody. */ | |
578 | buf->st_uid = UNKNOWN_UID; | |
579 | buf->st_gid = UNKNOWN_GID; | |
580 | buf->st_mode &= ~0777; | |
581 | } | |
74fcdaec | 582 | } |
c477dccf CF |
583 | return 0; |
584 | } | |
585 | ||
52592730 CV |
586 | DIR * |
587 | fhandler_registry::opendir (int fd) | |
588 | { | |
589 | /* Skip fhandler_proc::opendir, which allocates dir->_d_handle for its | |
590 | own devilish purposes... */ | |
591 | return fhandler_virtual::opendir (fd); | |
592 | } | |
593 | ||
d9a22764 CF |
594 | int |
595 | fhandler_registry::readdir (DIR *dir, dirent *de) | |
c477dccf | 596 | { |
5224a7bb | 597 | DWORD buf_size = NAME_MAX + 1; |
333a47d3 | 598 | wchar_t buf[buf_size]; |
20f9af53 | 599 | const char *path = dir->__d_dirname + proc_len + 1 + prefix_len; |
c477dccf | 600 | LONG error; |
d9a22764 | 601 | int res = ENMFILE; |
c477dccf | 602 | |
9e5f45ed | 603 | dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; |
c477dccf CF |
604 | if (*path == 0) |
605 | { | |
606 | if (dir->__d_position >= ROOT_KEY_COUNT) | |
2402700d | 607 | goto out; |
d9a22764 CF |
608 | strcpy (de->d_name, registry_listing[dir->__d_position++]); |
609 | res = 0; | |
c477dccf CF |
610 | goto out; |
611 | } | |
d27ebea9 | 612 | if (dir->__handle == INVALID_HANDLE_VALUE) |
c477dccf | 613 | { |
d27ebea9 CV |
614 | if (dir->__d_position != 0) |
615 | goto out; | |
b2ec6677 | 616 | dir->__handle = open_key (path + 1, KEY_READ, wow64, false); |
d27ebea9 CV |
617 | if (dir->__handle == INVALID_HANDLE_VALUE) |
618 | goto out; | |
61522196 | 619 | dir->__d_internal = (uintptr_t) new __DIR_hash (); |
c477dccf | 620 | } |
8761c1dc CF |
621 | if (dir->__d_position < SPECIAL_DOT_FILE_COUNT) |
622 | { | |
d9a22764 CF |
623 | strcpy (de->d_name, special_dot_files[dir->__d_position++]); |
624 | res = 0; | |
8761c1dc CF |
625 | goto out; |
626 | } | |
887eb76f CV |
627 | if ((HKEY) dir->__handle == HKEY_PERFORMANCE_DATA) |
628 | { | |
629 | /* RegEnumValue () returns garbage for this key, | |
b86f999a | 630 | simulate only a minimal listing of the generic names. */ |
887eb76f CV |
631 | if (dir->__d_position >= SPECIAL_DOT_FILE_COUNT + PERF_DATA_FILE_COUNT) |
632 | goto out; | |
633 | strcpy (de->d_name, perf_data_files[dir->__d_position - SPECIAL_DOT_FILE_COUNT]); | |
634 | dir->__d_position++; | |
635 | res = 0; | |
636 | goto out; | |
637 | } | |
638 | ||
c477dccf CF |
639 | retry: |
640 | if (dir->__d_position & REG_ENUM_VALUES_MASK) | |
641 | /* For the moment, the type of key is ignored here. when write access is added, | |
642 | * maybe add an extension for the type of each value? | |
643 | */ | |
333a47d3 CV |
644 | error = RegEnumValueW ((HKEY) dir->__handle, |
645 | (dir->__d_position & ~REG_ENUM_VALUES_MASK) >> 16, | |
646 | buf, &buf_size, NULL, NULL, NULL, NULL); | |
c477dccf CF |
647 | else |
648 | error = | |
333a47d3 CV |
649 | RegEnumKeyExW ((HKEY) dir->__handle, dir->__d_position - |
650 | SPECIAL_DOT_FILE_COUNT, buf, &buf_size, | |
651 | NULL, NULL, NULL, NULL); | |
c477dccf CF |
652 | if (error == ERROR_NO_MORE_ITEMS |
653 | && (dir->__d_position & REG_ENUM_VALUES_MASK) == 0) | |
654 | { | |
655 | /* If we're finished with sub-keys, start on values under this key. */ | |
656 | dir->__d_position |= REG_ENUM_VALUES_MASK; | |
5224a7bb | 657 | buf_size = NAME_MAX + 1; |
c477dccf CF |
658 | goto retry; |
659 | } | |
660 | if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) | |
661 | { | |
b2ec6677 | 662 | delete d_hash (dir); |
0c7b5572 CF |
663 | RegCloseKey ((HKEY) dir->__handle); |
664 | dir->__handle = INVALID_HANDLE_VALUE; | |
3fea2e40 CF |
665 | if (error != ERROR_NO_MORE_ITEMS) |
666 | seterrno_from_win_error (__FILE__, __LINE__, error); | |
c477dccf CF |
667 | goto out; |
668 | } | |
669 | ||
670 | /* We get here if `buf' contains valid data. */ | |
d27ebea9 CV |
671 | dir->__d_position++; |
672 | if (dir->__d_position & REG_ENUM_VALUES_MASK) | |
673 | dir->__d_position += 0x10000; | |
674 | ||
2cc8b9e0 CV |
675 | { |
676 | /* Append "%val" if value name is identical to a previous key name. */ | |
677 | unsigned h = hash_path_name (1, buf); | |
678 | bool add_val = false; | |
679 | if (! (dir->__d_position & REG_ENUM_VALUES_MASK)) | |
680 | d_hash (dir)->set (h); | |
681 | else if (d_hash (dir)->is_set (h) | |
682 | && key_exists ((HKEY) dir->__handle, buf, wow64)) | |
683 | add_val = true; | |
684 | ||
685 | if (encode_regname (de->d_name, buf, add_val)) | |
686 | { | |
687 | buf_size = NAME_MAX + 1; | |
688 | goto retry; | |
689 | } | |
690 | } | |
c477dccf | 691 | |
c477dccf | 692 | if (dir->__d_position & REG_ENUM_VALUES_MASK) |
d27ebea9 | 693 | de->d_type = DT_REG; |
b2ec6677 CV |
694 | else if (!strcasecmp (de->d_name, "Classes") |
695 | && !strcasecmp (path + strlen (path) | |
696 | - sizeof (VIRT_CLASSES_KEY_PREFIX) + 1, | |
697 | VIRT_CLASSES_KEY_PREFIX)) | |
698 | de->d_type = DT_LNK; | |
d27ebea9 CV |
699 | else |
700 | de->d_type = DT_DIR; | |
701 | ||
d9a22764 | 702 | res = 0; |
c477dccf | 703 | out: |
b9aa8149 | 704 | syscall_printf ("%d = readdir(%p, %p)", res, dir, de); |
c477dccf CF |
705 | return res; |
706 | } | |
707 | ||
c492992f | 708 | long |
c477dccf CF |
709 | fhandler_registry::telldir (DIR * dir) |
710 | { | |
8761c1dc | 711 | return dir->__d_position & REG_POSITION_MASK; |
c477dccf CF |
712 | } |
713 | ||
714 | void | |
c492992f | 715 | fhandler_registry::seekdir (DIR * dir, long loc) |
c477dccf CF |
716 | { |
717 | /* Unfortunately cannot simply set __d_position due to transition from sub-keys to | |
718 | * values. | |
719 | */ | |
720 | rewinddir (dir); | |
8761c1dc | 721 | while (loc > (dir->__d_position & REG_POSITION_MASK)) |
20973ec9 | 722 | if (readdir (dir, dir->__d_dirent)) |
c477dccf CF |
723 | break; |
724 | } | |
725 | ||
726 | void | |
727 | fhandler_registry::rewinddir (DIR * dir) | |
728 | { | |
0c7b5572 | 729 | if (dir->__handle != INVALID_HANDLE_VALUE) |
c477dccf | 730 | { |
b2ec6677 | 731 | delete d_hash (dir); |
0c55f6ed | 732 | RegCloseKey ((HKEY) dir->__handle); |
0c7b5572 | 733 | dir->__handle = INVALID_HANDLE_VALUE; |
c477dccf CF |
734 | } |
735 | dir->__d_position = 0; | |
2693c1ac | 736 | dir->__flags = dirent_saw_dot | dirent_saw_dot_dot; |
c477dccf CF |
737 | } |
738 | ||
739 | int | |
740 | fhandler_registry::closedir (DIR * dir) | |
741 | { | |
742 | int res = 0; | |
d27ebea9 | 743 | if (dir->__handle != INVALID_HANDLE_VALUE) |
c477dccf | 744 | { |
d27ebea9 CV |
745 | delete d_hash (dir); |
746 | if (RegCloseKey ((HKEY) dir->__handle) != ERROR_SUCCESS) | |
747 | { | |
748 | __seterrno (); | |
749 | res = -1; | |
750 | } | |
c477dccf | 751 | } |
b9aa8149 | 752 | syscall_printf ("%d = closedir(%p)", res, dir); |
c477dccf CF |
753 | return 0; |
754 | } | |
755 | ||
756 | int | |
7ac61736 | 757 | fhandler_registry::open (int flags, mode_t mode) |
c477dccf | 758 | { |
c477dccf CF |
759 | int pathlen; |
760 | const char *file; | |
d27ebea9 | 761 | HKEY handle = (HKEY) INVALID_HANDLE_VALUE; |
c477dccf | 762 | |
7ac61736 | 763 | int res = fhandler_virtual::open (flags, mode); |
c477dccf CF |
764 | if (!res) |
765 | goto out; | |
766 | ||
767 | const char *path; | |
20f9af53 | 768 | path = get_name () + proc_len + 1 + prefix_len; |
c477dccf CF |
769 | if (!*path) |
770 | { | |
8761c1dc | 771 | if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) |
2402700d CF |
772 | { |
773 | set_errno (EEXIST); | |
774 | res = 0; | |
775 | goto out; | |
776 | } | |
8761c1dc | 777 | else if (flags & O_WRONLY) |
2402700d CF |
778 | { |
779 | set_errno (EISDIR); | |
780 | res = 0; | |
781 | goto out; | |
782 | } | |
c477dccf | 783 | else |
2402700d CF |
784 | { |
785 | flags |= O_DIROPEN; | |
b4a3a3e3 CV |
786 | /* Marking as nohandle allows to call dup. */ |
787 | nohandle (true); | |
2402700d CF |
788 | goto success; |
789 | } | |
c477dccf CF |
790 | } |
791 | path++; | |
792 | pathlen = strlen (path); | |
793 | file = path + pathlen - 1; | |
85ba109d | 794 | if (isdirsep (*file) && pathlen > 1) |
c477dccf | 795 | file--; |
85ba109d | 796 | while (!isdirsep (*file)) |
c477dccf CF |
797 | file--; |
798 | file++; | |
799 | ||
800 | if (file == path) | |
801 | { | |
802 | for (int i = 0; registry_listing[i]; i++) | |
e4b57503 CV |
803 | if (path_prefix_p (registry_listing[i], path, |
804 | strlen (registry_listing[i]), true)) | |
2402700d CF |
805 | { |
806 | if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) | |
807 | { | |
808 | set_errno (EEXIST); | |
809 | res = 0; | |
810 | goto out; | |
811 | } | |
812 | else if (flags & O_WRONLY) | |
813 | { | |
814 | set_errno (EISDIR); | |
815 | res = 0; | |
816 | goto out; | |
817 | } | |
818 | else | |
819 | { | |
b2ec6677 | 820 | set_io_handle (fetch_hkey (i)); |
305b19d7 CV |
821 | /* Marking as nohandle allows to call dup on pseudo registry |
822 | handles. */ | |
823 | nohandle (true); | |
2402700d CF |
824 | flags |= O_DIROPEN; |
825 | goto success; | |
826 | } | |
827 | } | |
c477dccf | 828 | |
8761c1dc | 829 | if (flags & O_CREAT) |
2402700d CF |
830 | { |
831 | set_errno (EROFS); | |
832 | res = 0; | |
2402700d | 833 | } |
c477dccf | 834 | else |
2402700d CF |
835 | { |
836 | set_errno (ENOENT); | |
837 | res = 0; | |
2402700d | 838 | } |
9fe25af7 | 839 | goto out; |
c477dccf CF |
840 | } |
841 | ||
8761c1dc | 842 | if (flags & O_WRONLY) |
c477dccf | 843 | { |
8761c1dc | 844 | set_errno (EROFS); |
c477dccf | 845 | res = 0; |
e82360f6 | 846 | goto out; |
651d8393 | 847 | } |
9fe25af7 | 848 | else |
c477dccf | 849 | { |
333a47d3 | 850 | wchar_t dec_file[NAME_MAX + 1]; |
9fe25af7 CF |
851 | int val_only = decode_regname (dec_file, file); |
852 | if (val_only < 0) | |
af925fe4 | 853 | { |
9fe25af7 | 854 | set_errno (EINVAL); |
af925fe4 CV |
855 | res = 0; |
856 | goto out; | |
857 | } | |
74fcdaec | 858 | |
9fe25af7 CF |
859 | if (!val_only) |
860 | handle = open_key (path, KEY_READ, wow64, false); | |
861 | if (handle == (HKEY) INVALID_HANDLE_VALUE) | |
862 | { | |
2849cbde | 863 | if (val_only || get_errno () != EACCES) |
64529866 | 864 | handle = open_key (path, KEY_READ, wow64, true); |
9fe25af7 CF |
865 | if (handle == (HKEY) INVALID_HANDLE_VALUE) |
866 | { | |
867 | res = 0; | |
868 | goto out; | |
869 | } | |
870 | } | |
871 | else | |
872 | flags |= O_DIROPEN; | |
873 | ||
874 | set_io_handle (handle); | |
e70fdfb9 | 875 | set_close_on_exec (!!(flags & O_CLOEXEC)); |
333a47d3 | 876 | value_name = cwcsdup (dec_file); |
c477dccf | 877 | |
9fe25af7 CF |
878 | if (!(flags & O_DIROPEN) && !fill_filebuf ()) |
879 | { | |
880 | RegCloseKey (handle); | |
881 | res = 0; | |
882 | goto out; | |
883 | } | |
74fcdaec | 884 | |
9fe25af7 CF |
885 | if (flags & O_APPEND) |
886 | position = filesize; | |
887 | else | |
888 | position = 0; | |
889 | } | |
74fcdaec CF |
890 | |
891 | success: | |
892 | res = 1; | |
893 | set_flags ((flags & ~O_TEXT) | O_BINARY); | |
894 | set_open_status (); | |
895 | out: | |
61522196 | 896 | syscall_printf ("%d = fhandler_registry::open(%p, 0%o)", res, flags, mode); |
74fcdaec CF |
897 | return res; |
898 | } | |
899 | ||
900 | int | |
901 | fhandler_registry::close () | |
902 | { | |
903 | int res = fhandler_virtual::close (); | |
904 | if (res != 0) | |
905 | return res; | |
906 | HKEY handle = (HKEY) get_handle (); | |
305b19d7 | 907 | if (handle != (HKEY) INVALID_HANDLE_VALUE && handle < HKEY_CLASSES_ROOT) |
74fcdaec CF |
908 | { |
909 | if (RegCloseKey (handle) != ERROR_SUCCESS) | |
2402700d | 910 | { |
74fcdaec | 911 | __seterrno (); |
2402700d | 912 | res = -1; |
74fcdaec CF |
913 | } |
914 | } | |
56a19715 | 915 | if (!have_execed && value_name) |
14c4d65e CF |
916 | { |
917 | cfree (value_name); | |
918 | value_name = NULL; | |
919 | } | |
6b3c247d | 920 | return res; |
74fcdaec CF |
921 | } |
922 | ||
923 | bool | |
924 | fhandler_registry::fill_filebuf () | |
925 | { | |
926 | DWORD type, size; | |
927 | LONG error; | |
928 | HKEY handle = (HKEY) get_handle (); | |
38f50ae4 CV |
929 | size_t bufalloc; |
930 | ||
74fcdaec CF |
931 | if (handle != HKEY_PERFORMANCE_DATA) |
932 | { | |
333a47d3 | 933 | error = RegQueryValueExW (handle, value_name, NULL, &type, NULL, &size); |
74fcdaec CF |
934 | if (error != ERROR_SUCCESS) |
935 | { | |
b2ec6677 CV |
936 | if (error == ERROR_INVALID_HANDLE |
937 | && !strcasecmp (get_name () + strlen (get_name ()) | |
938 | - sizeof (VIRT_CLASSES_KEY) + 1, | |
939 | VIRT_CLASSES_KEY)) | |
940 | { | |
941 | filesize = sizeof (VIRT_CLASSES_LINKTGT); | |
942 | filebuf = (char *) cmalloc_abort (HEAP_BUF, filesize); | |
943 | strcpy (filebuf, VIRT_CLASSES_LINKTGT); | |
944 | return true; | |
945 | } | |
74fcdaec CF |
946 | if (error != ERROR_FILE_NOT_FOUND) |
947 | { | |
948 | seterrno_from_win_error (__FILE__, __LINE__, error); | |
949 | return false; | |
950 | } | |
951 | goto value_not_found; | |
2402700d | 952 | } |
333a47d3 | 953 | PBYTE tmpbuf = (PBYTE) cmalloc_abort (HEAP_BUF, size); |
c477dccf | 954 | error = |
333a47d3 | 955 | RegQueryValueExW (handle, value_name, NULL, NULL, tmpbuf, &size); |
c477dccf | 956 | if (error != ERROR_SUCCESS) |
2402700d CF |
957 | { |
958 | seterrno_from_win_error (__FILE__, __LINE__, error); | |
74fcdaec | 959 | return true; |
2402700d | 960 | } |
88addc64 | 961 | if (type == REG_SZ || type == REG_EXPAND_SZ || type == REG_LINK) |
333a47d3 CV |
962 | bufalloc = sys_wcstombs (NULL, 0, (wchar_t *) tmpbuf, |
963 | size / sizeof (wchar_t)); | |
88addc64 CV |
964 | else if (type == REG_MULTI_SZ) |
965 | bufalloc = multi_wcstombs (NULL, 0, (wchar_t *) tmpbuf, | |
966 | size / sizeof (wchar_t)); | |
333a47d3 CV |
967 | else |
968 | bufalloc = size; | |
969 | filebuf = (char *) cmalloc_abort (HEAP_BUF, bufalloc); | |
88addc64 | 970 | if (type == REG_SZ || type == REG_EXPAND_SZ || type == REG_LINK) |
333a47d3 CV |
971 | sys_wcstombs (filebuf, bufalloc, (wchar_t *) tmpbuf, |
972 | size / sizeof (wchar_t)); | |
88addc64 CV |
973 | else if (type == REG_MULTI_SZ) |
974 | multi_wcstombs (filebuf, bufalloc, (wchar_t *) tmpbuf, | |
975 | size / sizeof (wchar_t)); | |
333a47d3 CV |
976 | else |
977 | memcpy (filebuf, tmpbuf, bufalloc); | |
978 | filesize = bufalloc; | |
c477dccf CF |
979 | } |
980 | else | |
981 | { | |
982 | bufalloc = 0; | |
983 | do | |
2402700d | 984 | { |
887eb76f | 985 | bufalloc += 16 * 1024; |
ee4388c4 | 986 | filebuf = (char *) crealloc_abort (filebuf, bufalloc); |
83967714 | 987 | size = bufalloc; |
333a47d3 CV |
988 | error = RegQueryValueExW (handle, value_name, NULL, &type, |
989 | (PBYTE) filebuf, &size); | |
74fcdaec | 990 | if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) |
2402700d | 991 | { |
887eb76f CV |
992 | seterrno_from_win_error (__FILE__, __LINE__, error); |
993 | return false; | |
2402700d CF |
994 | } |
995 | } | |
c477dccf CF |
996 | while (error == ERROR_MORE_DATA); |
997 | filesize = size; | |
887eb76f CV |
998 | /* RegQueryValueEx () opens HKEY_PERFORMANCE_DATA. */ |
999 | RegCloseKey (handle); | |
c477dccf | 1000 | } |
74fcdaec CF |
1001 | return true; |
1002 | value_not_found: | |
5224a7bb | 1003 | DWORD buf_size = NAME_MAX + 1; |
333a47d3 | 1004 | wchar_t buf[buf_size]; |
74fcdaec CF |
1005 | int index = 0; |
1006 | while (ERROR_SUCCESS == | |
333a47d3 CV |
1007 | (error = RegEnumKeyExW (handle, index++, buf, &buf_size, NULL, NULL, |
1008 | NULL, NULL)) || (error == ERROR_MORE_DATA)) | |
74fcdaec | 1009 | { |
333a47d3 | 1010 | if (!wcscasecmp (buf, value_name)) |
74fcdaec CF |
1011 | { |
1012 | set_errno (EISDIR); | |
1013 | return false; | |
1014 | } | |
5224a7bb | 1015 | buf_size = NAME_MAX + 1; |
74fcdaec CF |
1016 | } |
1017 | if (error != ERROR_NO_MORE_ITEMS) | |
1018 | { | |
1019 | seterrno_from_win_error (__FILE__, __LINE__, error); | |
1020 | return false; | |
1021 | } | |
1022 | set_errno (ENOENT); | |
1023 | return false; | |
9ba913a5 CF |
1024 | } |
1025 | ||
c477dccf | 1026 | /* Auxillary member function to open registry keys. */ |
74fcdaec | 1027 | static HKEY |
20f9af53 | 1028 | open_key (const char *name, REGSAM access, DWORD wow64, bool isValue) |
c477dccf CF |
1029 | { |
1030 | HKEY hKey = (HKEY) INVALID_HANDLE_VALUE; | |
1031 | HKEY hParentKey = (HKEY) INVALID_HANDLE_VALUE; | |
1032 | bool parentOpened = false; | |
333a47d3 | 1033 | wchar_t component[NAME_MAX + 1]; |
c477dccf CF |
1034 | |
1035 | while (*name) | |
1036 | { | |
1037 | const char *anchor = name; | |
85ba109d | 1038 | while (*name && !isdirsep (*name)) |
2402700d | 1039 | name++; |
d27ebea9 CV |
1040 | int val_only = decode_regname (component, anchor, name - anchor); |
1041 | if (val_only < 0) | |
9fe25af7 | 1042 | { |
651d8393 CV |
1043 | set_errno (EINVAL); |
1044 | if (parentOpened) | |
1045 | RegCloseKey (hParentKey); | |
1046 | hKey = (HKEY) INVALID_HANDLE_VALUE; | |
1047 | break; | |
1048 | } | |
c477dccf | 1049 | if (*name) |
2402700d | 1050 | name++; |
c477dccf | 1051 | if (*name == 0 && isValue == true) |
887eb76f | 1052 | break; |
c477dccf | 1053 | |
887eb76f | 1054 | if (val_only || !component[0] || hKey == HKEY_PERFORMANCE_DATA) |
d27ebea9 CV |
1055 | { |
1056 | set_errno (ENOENT); | |
1057 | if (parentOpened) | |
1058 | RegCloseKey (hParentKey); | |
1059 | hKey = (HKEY) INVALID_HANDLE_VALUE; | |
1060 | break; | |
1061 | } | |
1062 | ||
c477dccf | 1063 | if (hParentKey != (HKEY) INVALID_HANDLE_VALUE) |
2402700d | 1064 | { |
74fcdaec CF |
1065 | REGSAM effective_access = KEY_READ; |
1066 | if ((strchr (name, '/') == NULL && isValue == true) || *name == 0) | |
1067 | effective_access = access; | |
333a47d3 CV |
1068 | LONG error = RegOpenKeyExW (hParentKey, component, 0, |
1069 | effective_access | wow64, &hKey); | |
9367c0dc | 1070 | if (error == ERROR_ACCESS_DENIED) /* Try opening with backup intent */ |
333a47d3 CV |
1071 | error = RegCreateKeyExW (hParentKey, component, 0, NULL, |
1072 | REG_OPTION_BACKUP_RESTORE, | |
1073 | effective_access | wow64, NULL, | |
1074 | &hKey, NULL); | |
887eb76f CV |
1075 | if (parentOpened) |
1076 | RegCloseKey (hParentKey); | |
74fcdaec | 1077 | if (error != ERROR_SUCCESS) |
2402700d | 1078 | { |
74fcdaec | 1079 | hKey = (HKEY) INVALID_HANDLE_VALUE; |
2402700d CF |
1080 | seterrno_from_win_error (__FILE__, __LINE__, error); |
1081 | return hKey; | |
1082 | } | |
2402700d CF |
1083 | hParentKey = hKey; |
1084 | parentOpened = true; | |
1085 | } | |
c477dccf | 1086 | else |
2402700d CF |
1087 | { |
1088 | for (int i = 0; registry_listing[i]; i++) | |
333a47d3 | 1089 | if (strncasematch (anchor, registry_listing[i], name - anchor - 1)) |
b2ec6677 | 1090 | hKey = fetch_hkey (i); |
2402700d CF |
1091 | if (hKey == (HKEY) INVALID_HANDLE_VALUE) |
1092 | return hKey; | |
1093 | hParentKey = hKey; | |
1094 | } | |
c477dccf | 1095 | } |
c477dccf CF |
1096 | return hKey; |
1097 | } | |
305b19d7 CV |
1098 | |
1099 | int | |
23771fa1 | 1100 | fhandler_registry::dup (fhandler_base *child, int flags) |
305b19d7 | 1101 | { |
9e242ba4 CV |
1102 | debug_printf ("here"); |
1103 | fhandler_registry *fhs = (fhandler_registry *) child; | |
1104 | ||
23771fa1 | 1105 | int ret = fhandler_virtual::dup (fhs, flags); |
305b19d7 CV |
1106 | /* Pseudo registry handles can't be duplicated using DuplicateHandle. |
1107 | Therefore those fhandlers are marked with the nohandle flag. This | |
1108 | allows fhandler_base::dup to succeed as usual for nohandle fhandlers. | |
1109 | Here we just have to fix up by copying the pseudo handle value. */ | |
1110 | if ((HKEY) get_handle () >= HKEY_CLASSES_ROOT) | |
9e242ba4 CV |
1111 | fhs->set_io_handle (get_handle ()); |
1112 | if (value_name) | |
1113 | fhs->value_name = cwcsdup (value_name); | |
305b19d7 CV |
1114 | return ret; |
1115 | } |