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