1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/win/registry.h"
6
7#include <shlwapi.h>
8#include <algorithm>
9
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "base/threading/thread_restrictions.h"
13#include "base/win/windows_version.h"
14
15namespace base {
16namespace win {
17
18namespace {
19
20// RegEnumValue() reports the number of characters from the name that were
21// written to the buffer, not how many there are. This constant is the maximum
22// name size, such that a buffer with this size should read any name.
23const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
24
25// Registry values are read as BYTE* but can have wchar_t* data whose last
26// wchar_t is truncated. This function converts the reported |byte_size| to
27// a size in wchar_t that can store a truncated wchar_t if necessary.
28inline DWORD to_wchar_size(DWORD byte_size) {
29  return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
30}
31
32// Mask to pull WOW64 access flags out of REGSAM access.
33const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
34
35}  // namespace
36
37// RegKey ----------------------------------------------------------------------
38
39RegKey::RegKey()
40    : key_(NULL),
41      watch_event_(0),
42      wow64access_(0) {
43}
44
45RegKey::RegKey(HKEY key)
46    : key_(key),
47      watch_event_(0),
48      wow64access_(0) {
49}
50
51RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
52    : key_(NULL),
53      watch_event_(0),
54      wow64access_(0) {
55  if (rootkey) {
56    if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
57      Create(rootkey, subkey, access);
58    else
59      Open(rootkey, subkey, access);
60  } else {
61    DCHECK(!subkey);
62    wow64access_ = access & kWow64AccessMask;
63  }
64}
65
66RegKey::~RegKey() {
67  Close();
68}
69
70LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
71  DWORD disposition_value;
72  return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
73}
74
75LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
76                                   DWORD* disposition, REGSAM access) {
77  DCHECK(rootkey && subkey && access && disposition);
78  HKEY subhkey = NULL;
79  LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
80                               REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
81                               disposition);
82  if (result == ERROR_SUCCESS) {
83    Close();
84    key_ = subhkey;
85    wow64access_ = access & kWow64AccessMask;
86  }
87
88  return result;
89}
90
91LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
92  DCHECK(name && access);
93  // After the application has accessed an alternate registry view using one of
94  // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
95  // (create, delete, or open) on child registry keys must explicitly use the
96  // same flag. Otherwise, there can be unexpected behavior.
97  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
98  if ((access & kWow64AccessMask) != wow64access_) {
99    NOTREACHED();
100    return ERROR_INVALID_PARAMETER;
101  }
102  HKEY subkey = NULL;
103  LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
104                               access, NULL, &subkey, NULL);
105  if (result == ERROR_SUCCESS) {
106    Close();
107    key_ = subkey;
108    wow64access_ = access & kWow64AccessMask;
109  }
110
111  return result;
112}
113
114LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
115  DCHECK(rootkey && subkey && access);
116  HKEY subhkey = NULL;
117
118  LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
119  if (result == ERROR_SUCCESS) {
120    Close();
121    key_ = subhkey;
122    wow64access_ = access & kWow64AccessMask;
123  }
124
125  return result;
126}
127
128LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
129  DCHECK(relative_key_name && access);
130  // After the application has accessed an alternate registry view using one of
131  // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
132  // (create, delete, or open) on child registry keys must explicitly use the
133  // same flag. Otherwise, there can be unexpected behavior.
134  // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
135  if ((access & kWow64AccessMask) != wow64access_) {
136    NOTREACHED();
137    return ERROR_INVALID_PARAMETER;
138  }
139  HKEY subkey = NULL;
140  LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
141
142  // We have to close the current opened key before replacing it with the new
143  // one.
144  if (result == ERROR_SUCCESS) {
145    Close();
146    key_ = subkey;
147    wow64access_ = access & kWow64AccessMask;
148  }
149  return result;
150}
151
152void RegKey::Close() {
153  StopWatching();
154  if (key_) {
155    ::RegCloseKey(key_);
156    key_ = NULL;
157    wow64access_ = 0;
158  }
159}
160
161// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
162void RegKey::Set(HKEY key) {
163  if (key_ != key) {
164    Close();
165    key_ = key;
166  }
167}
168
169HKEY RegKey::Take() {
170  DCHECK(wow64access_ == 0);
171  StopWatching();
172  HKEY key = key_;
173  key_ = NULL;
174  return key;
175}
176
177bool RegKey::HasValue(const wchar_t* name) const {
178  return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
179}
180
181DWORD RegKey::GetValueCount() const {
182  DWORD count = 0;
183  LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
184                                NULL, NULL, NULL, NULL);
185  return (result == ERROR_SUCCESS) ? count : 0;
186}
187
188LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
189  wchar_t buf[256];
190  DWORD bufsize = arraysize(buf);
191  LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
192  if (r == ERROR_SUCCESS)
193    *name = buf;
194
195  return r;
196}
197
198LONG RegKey::DeleteKey(const wchar_t* name) {
199  DCHECK(key_);
200  DCHECK(name);
201  HKEY subkey = NULL;
202
203  // Verify the key exists before attempting delete to replicate previous
204  // behavior.
205  LONG result =
206      RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
207  if (result != ERROR_SUCCESS)
208    return result;
209  RegCloseKey(subkey);
210
211  return RegDelRecurse(key_, std::wstring(name), wow64access_);
212}
213
214LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
215  DCHECK(key_);
216  DCHECK(name);
217
218  HKEY target_key = NULL;
219  LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
220                             &target_key);
221
222  if (result != ERROR_SUCCESS)
223    return result;
224
225  DWORD count = 0;
226  result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
227                           NULL, NULL, NULL, NULL);
228
229  RegCloseKey(target_key);
230
231  if (result != ERROR_SUCCESS)
232    return result;
233
234  if (count == 0)
235    return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
236
237  return ERROR_DIR_NOT_EMPTY;
238}
239
240LONG RegKey::DeleteValue(const wchar_t* value_name) {
241  DCHECK(key_);
242  LONG result = RegDeleteValue(key_, value_name);
243  return result;
244}
245
246LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
247  DCHECK(out_value);
248  DWORD type = REG_DWORD;
249  DWORD size = sizeof(DWORD);
250  DWORD local_value = 0;
251  LONG result = ReadValue(name, &local_value, &size, &type);
252  if (result == ERROR_SUCCESS) {
253    if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
254      *out_value = local_value;
255    else
256      result = ERROR_CANTREAD;
257  }
258
259  return result;
260}
261
262LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
263  DCHECK(out_value);
264  DWORD type = REG_QWORD;
265  int64 local_value = 0;
266  DWORD size = sizeof(local_value);
267  LONG result = ReadValue(name, &local_value, &size, &type);
268  if (result == ERROR_SUCCESS) {
269    if ((type == REG_QWORD || type == REG_BINARY) &&
270        size == sizeof(local_value))
271      *out_value = local_value;
272    else
273      result = ERROR_CANTREAD;
274  }
275
276  return result;
277}
278
279LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
280  DCHECK(out_value);
281  const size_t kMaxStringLength = 1024;  // This is after expansion.
282  // Use the one of the other forms of ReadValue if 1024 is too small for you.
283  wchar_t raw_value[kMaxStringLength];
284  DWORD type = REG_SZ, size = sizeof(raw_value);
285  LONG result = ReadValue(name, raw_value, &size, &type);
286  if (result == ERROR_SUCCESS) {
287    if (type == REG_SZ) {
288      *out_value = raw_value;
289    } else if (type == REG_EXPAND_SZ) {
290      wchar_t expanded[kMaxStringLength];
291      size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
292      // Success: returns the number of wchar_t's copied
293      // Fail: buffer too small, returns the size required
294      // Fail: other, returns 0
295      if (size == 0 || size > kMaxStringLength) {
296        result = ERROR_MORE_DATA;
297      } else {
298        *out_value = expanded;
299      }
300    } else {
301      // Not a string. Oops.
302      result = ERROR_CANTREAD;
303    }
304  }
305
306  return result;
307}
308
309LONG RegKey::ReadValue(const wchar_t* name,
310                       void* data,
311                       DWORD* dsize,
312                       DWORD* dtype) const {
313  LONG result = RegQueryValueEx(key_, name, 0, dtype,
314                                reinterpret_cast<LPBYTE>(data), dsize);
315  return result;
316}
317
318LONG RegKey::ReadValues(const wchar_t* name,
319                        std::vector<std::wstring>* values) {
320  values->clear();
321
322  DWORD type = REG_MULTI_SZ;
323  DWORD size = 0;
324  LONG result = ReadValue(name, NULL, &size, &type);
325  if (FAILED(result) || size == 0)
326    return result;
327
328  if (type != REG_MULTI_SZ)
329    return ERROR_CANTREAD;
330
331  std::vector<wchar_t> buffer(size / sizeof(wchar_t));
332  result = ReadValue(name, &buffer[0], &size, NULL);
333  if (FAILED(result) || size == 0)
334    return result;
335
336  // Parse the double-null-terminated list of strings.
337  // Note: This code is paranoid to not read outside of |buf|, in the case where
338  // it may not be properly terminated.
339  const wchar_t* entry = &buffer[0];
340  const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
341  while (entry < buffer_end && entry[0] != '\0') {
342    const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
343    values->push_back(std::wstring(entry, entry_end));
344    entry = entry_end + 1;
345  }
346  return 0;
347}
348
349LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
350  return WriteValue(
351      name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
352}
353
354LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* in_value) {
355  return WriteValue(name, in_value,
356      static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
357}
358
359LONG RegKey::WriteValue(const wchar_t* name,
360                        const void* data,
361                        DWORD dsize,
362                        DWORD dtype) {
363  DCHECK(data || !dsize);
364
365  LONG result = RegSetValueEx(key_, name, 0, dtype,
366      reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
367  return result;
368}
369
370LONG RegKey::StartWatching() {
371  DCHECK(key_);
372  if (!watch_event_)
373    watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
374
375  DWORD filter = REG_NOTIFY_CHANGE_NAME |
376                 REG_NOTIFY_CHANGE_ATTRIBUTES |
377                 REG_NOTIFY_CHANGE_LAST_SET |
378                 REG_NOTIFY_CHANGE_SECURITY;
379
380  // Watch the registry key for a change of value.
381  LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
382  if (result != ERROR_SUCCESS) {
383    CloseHandle(watch_event_);
384    watch_event_ = 0;
385  }
386
387  return result;
388}
389
390bool RegKey::HasChanged() {
391  if (watch_event_) {
392    if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
393      StartWatching();
394      return true;
395    }
396  }
397  return false;
398}
399
400LONG RegKey::StopWatching() {
401  LONG result = ERROR_INVALID_HANDLE;
402  if (watch_event_) {
403    CloseHandle(watch_event_);
404    watch_event_ = 0;
405    result = ERROR_SUCCESS;
406  }
407  return result;
408}
409
410// static
411LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
412                                   const wchar_t* lpSubKey,
413                                   REGSAM samDesired,
414                                   DWORD Reserved) {
415  typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
416
417  RegDeleteKeyExPtr reg_delete_key_ex_func =
418      reinterpret_cast<RegDeleteKeyExPtr>(
419          GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
420
421  if (reg_delete_key_ex_func)
422    return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
423
424  // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
425  return RegDeleteKey(hKey, lpSubKey);
426}
427
428// static
429LONG RegKey::RegDelRecurse(HKEY root_key,
430                           const std::wstring& name,
431                           REGSAM access) {
432  // First, see if the key can be deleted without having to recurse.
433  LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
434  if (result == ERROR_SUCCESS)
435    return result;
436
437  HKEY target_key = NULL;
438  result = RegOpenKeyEx(
439      root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
440
441  if (result == ERROR_FILE_NOT_FOUND)
442    return ERROR_SUCCESS;
443  if (result != ERROR_SUCCESS)
444    return result;
445
446  std::wstring subkey_name(name);
447
448  // Check for an ending slash and add one if it is missing.
449  if (!name.empty() && subkey_name[name.length() - 1] != L'\\')
450    subkey_name += L"\\";
451
452  // Enumerate the keys
453  result = ERROR_SUCCESS;
454  const DWORD kMaxKeyNameLength = MAX_PATH;
455  const size_t base_key_length = subkey_name.length();
456  std::wstring key_name;
457  while (result == ERROR_SUCCESS) {
458    DWORD key_size = kMaxKeyNameLength;
459    result = RegEnumKeyEx(target_key,
460                          0,
461                          WriteInto(&key_name, kMaxKeyNameLength),
462                          &key_size,
463                          NULL,
464                          NULL,
465                          NULL,
466                          NULL);
467
468    if (result != ERROR_SUCCESS)
469      break;
470
471    key_name.resize(key_size);
472    subkey_name.resize(base_key_length);
473    subkey_name += key_name;
474
475    if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
476      break;
477  }
478
479  RegCloseKey(target_key);
480
481  // Try again to delete the key.
482  result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
483
484  return result;
485}
486
487// RegistryValueIterator ------------------------------------------------------
488
489RegistryValueIterator::RegistryValueIterator(HKEY root_key,
490                                             const wchar_t* folder_key)
491    : name_(MAX_PATH, L'\0'),
492      value_(MAX_PATH, L'\0') {
493  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
494  if (result != ERROR_SUCCESS) {
495    key_ = NULL;
496  } else {
497    DWORD count = 0;
498    result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
499                               NULL, NULL, NULL, NULL);
500
501    if (result != ERROR_SUCCESS) {
502      ::RegCloseKey(key_);
503      key_ = NULL;
504    } else {
505      index_ = count - 1;
506    }
507  }
508
509  Read();
510}
511
512RegistryValueIterator::~RegistryValueIterator() {
513  if (key_)
514    ::RegCloseKey(key_);
515}
516
517DWORD RegistryValueIterator::ValueCount() const {
518  DWORD count = 0;
519  LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
520                                  &count, NULL, NULL, NULL, NULL);
521  if (result != ERROR_SUCCESS)
522    return 0;
523
524  return count;
525}
526
527bool RegistryValueIterator::Valid() const {
528  return key_ != NULL && index_ >= 0;
529}
530
531void RegistryValueIterator::operator++() {
532  --index_;
533  Read();
534}
535
536bool RegistryValueIterator::Read() {
537  if (Valid()) {
538    DWORD capacity = static_cast<DWORD>(name_.capacity());
539    DWORD name_size = capacity;
540    // |value_size_| is in bytes. Reserve the last character for a NUL.
541    value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
542    LONG result = ::RegEnumValue(
543        key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
544        reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
545
546    if (result == ERROR_MORE_DATA) {
547      // Registry key names are limited to 255 characters and fit within
548      // MAX_PATH (which is 260) but registry value names can use up to 16,383
549      // characters and the value itself is not limited
550      // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
551      // ms724872(v=vs.85).aspx).
552      // Resize the buffers and retry if their size caused the failure.
553      DWORD value_size_in_wchars = to_wchar_size(value_size_);
554      if (value_size_in_wchars + 1 > value_.size())
555        value_.resize(value_size_in_wchars + 1, L'\0');
556      value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
557      name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
558      result = ::RegEnumValue(
559          key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
560          reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
561    }
562
563    if (result == ERROR_SUCCESS) {
564      DCHECK_LT(to_wchar_size(value_size_), value_.size());
565      value_[to_wchar_size(value_size_)] = L'\0';
566      return true;
567    }
568  }
569
570  name_[0] = L'\0';
571  value_[0] = L'\0';
572  value_size_ = 0;
573  return false;
574}
575
576// RegistryKeyIterator --------------------------------------------------------
577
578RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
579                                         const wchar_t* folder_key) {
580  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
581  if (result != ERROR_SUCCESS) {
582    key_ = NULL;
583  } else {
584    DWORD count = 0;
585    LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
586                                    NULL, NULL, NULL, NULL, NULL);
587
588    if (result != ERROR_SUCCESS) {
589      ::RegCloseKey(key_);
590      key_ = NULL;
591    } else {
592      index_ = count - 1;
593    }
594  }
595
596  Read();
597}
598
599RegistryKeyIterator::~RegistryKeyIterator() {
600  if (key_)
601    ::RegCloseKey(key_);
602}
603
604DWORD RegistryKeyIterator::SubkeyCount() const {
605  DWORD count = 0;
606  LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
607                                  NULL, NULL, NULL, NULL, NULL);
608  if (result != ERROR_SUCCESS)
609    return 0;
610
611  return count;
612}
613
614bool RegistryKeyIterator::Valid() const {
615  return key_ != NULL && index_ >= 0;
616}
617
618void RegistryKeyIterator::operator++() {
619  --index_;
620  Read();
621}
622
623bool RegistryKeyIterator::Read() {
624  if (Valid()) {
625    DWORD ncount = arraysize(name_);
626    FILETIME written;
627    LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
628                            NULL, &written);
629    if (ERROR_SUCCESS == r)
630      return true;
631  }
632
633  name_[0] = '\0';
634  return false;
635}
636
637}  // namespace win
638}  // namespace base
639