]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/fhandler_registry.cc
* Merge in cygwin-64bit-branch.
[newlib-cygwin.git] / winsup / cygwin / fhandler_registry.cc
CommitLineData
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
6This file is part of Cygwin.
7
8This software is a copyrighted work licensed under the terms of the
9Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10details. */
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
32static const __int32_t REG_ENUM_VALUES_MASK = 0x8000000;
33static 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
53static 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
66static 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 78static 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. */
82static HKEY
83fetch_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 */
106static const char *special_dot_files[] =
107{
108 ".",
109 "..",
110 NULL
111};
112
74fcdaec
CF
113static 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 */
122static const char * const perf_data_files[] =
123{
124 "@",
125 "Costly",
126 "Global"
127};
128
129static const int PERF_DATA_FILE_COUNT =
130 sizeof (perf_data_files) / sizeof (perf_data_files[0]);
131
20f9af53 132static 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 */
136static inline bool
333a47d3 137must_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 */
145static int
333a47d3 146encode_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 */
184static int
333a47d3 185decode_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 */
231class __DIR_hash
232{
233public:
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
249private:
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 */
259static bool
333a47d3 260key_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
270static size_t
271multi_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 308virtual_ftype_t
fc240f58 309fhandler_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 }
435out:
436 if (hKey != (HKEY) INVALID_HANDLE_VALUE)
437 RegCloseKey (hKey);
438 return file_type;
439}
440
20f9af53
CV
441void
442fhandler_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 457fhandler_registry::fhandler_registry ():
7ac61736 458fhandler_proc ()
c477dccf 459{
20f9af53
CV
460 wow64 = 0;
461 prefix_len = sizeof ("registry") - 1;
c477dccf
CF
462}
463
6e75c72b 464int __reg2
61522196 465fhandler_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
586DIR *
587fhandler_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
594int
595fhandler_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
639retry:
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 703out:
b9aa8149 704 syscall_printf ("%d = readdir(%p, %p)", res, dir, de);
c477dccf
CF
705 return res;
706}
707
c492992f 708long
c477dccf
CF
709fhandler_registry::telldir (DIR * dir)
710{
8761c1dc 711 return dir->__d_position & REG_POSITION_MASK;
c477dccf
CF
712}
713
714void
c492992f 715fhandler_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
726void
727fhandler_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
739int
740fhandler_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
756int
7ac61736 757fhandler_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
891success:
892 res = 1;
893 set_flags ((flags & ~O_TEXT) | O_BINARY);
894 set_open_status ();
895out:
61522196 896 syscall_printf ("%d = fhandler_registry::open(%p, 0%o)", res, flags, mode);
74fcdaec
CF
897 return res;
898}
899
900int
901fhandler_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
923bool
924fhandler_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;
1002value_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 1027static HKEY
20f9af53 1028open_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
1099int
23771fa1 1100fhandler_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}
This page took 5.905836 seconds and 5 git commands to generate.