diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-26 00:39:45 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-26 00:39:45 +0000 |
commit | 6dba1dd2efa3ed67bd41312bc682fc12c3c69544 (patch) | |
tree | bfb0bca756ad3e274227d085b82dedda508424fe /rlz/mac | |
parent | 4a44f6610eb14a728a22a9dea00d6707d2e616e1 (diff) | |
download | chromium_src-6dba1dd2efa3ed67bd41312bc682fc12c3c69544.zip chromium_src-6dba1dd2efa3ed67bd41312bc682fc12c3c69544.tar.gz chromium_src-6dba1dd2efa3ed67bd41312bc682fc12c3c69544.tar.bz2 |
Revert 144071 - Add a regenerate button to regenerate the password in Windows.
BUG=120480
TEST=Not tested.
Review URL: https://chromiumcodereview.appspot.com/10642009
TBR=zysxqn@google.com
Review URL: https://chromiumcodereview.appspot.com/10659022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144074 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'rlz/mac')
-rw-r--r-- | rlz/mac/lib/machine_id_mac.cc | 147 | ||||
-rw-r--r-- | rlz/mac/lib/rlz_value_store_mac.h | 80 | ||||
-rw-r--r-- | rlz/mac/lib/rlz_value_store_mac.mm | 447 |
3 files changed, 674 insertions, 0 deletions
diff --git a/rlz/mac/lib/machine_id_mac.cc b/rlz/mac/lib/machine_id_mac.cc new file mode 100644 index 0000000..2198978 --- /dev/null +++ b/rlz/mac/lib/machine_id_mac.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/network/IOEthernetInterface.h> +#include <IOKit/network/IONetworkInterface.h> +#include <IOKit/network/IOEthernetController.h> + +#include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_ioobject.h" +#include "base/string16.h" +#include "base/stringprintf.h" +#include "base/sys_string_conversions.h" +#include "base/utf_string_conversions.h" + +namespace rlz_lib { + +namespace { + +// See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html + +// The caller is responsible for freeing |matching_services|. +bool FindEthernetInterfaces(io_iterator_t* matching_services) { + base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict( + IOServiceMatching(kIOEthernetInterfaceClass)); + if (!matching_dict) + return false; + + base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface( + CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + if (!primary_interface) + return false; + + CFDictionarySetValue( + primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); + CFDictionarySetValue( + matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface); + + kern_return_t kern_result = IOServiceGetMatchingServices( + kIOMasterPortDefault, matching_dict.release(), matching_services); + + return kern_result == KERN_SUCCESS; +} + +bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator, + uint8_t* buffer, size_t buffer_size) { + if (buffer_size < kIOEthernetAddressSize) + return false; + + bool success = false; + + bzero(buffer, buffer_size); + base::mac::ScopedIOObject<io_object_t> primary_interface; + while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)), + primary_interface) { + io_object_t primary_interface_parent; + kern_return_t kern_result = IORegistryEntryGetParentEntry( + primary_interface, kIOServicePlane, &primary_interface_parent); + base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter( + primary_interface_parent); + success = kern_result == KERN_SUCCESS; + + if (!success) + continue; + + base::mac::ScopedCFTypeRef<CFTypeRef> mac_data( + IORegistryEntryCreateCFProperty(primary_interface_parent, + CFSTR(kIOMACAddress), + kCFAllocatorDefault, + 0)); + CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data); + if (mac_data_data) { + CFDataGetBytes( + mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer); + } + } + + return success; +} + +bool GetMacAddress(unsigned char* buffer, size_t size) { + io_iterator_t primary_interface_iterator; + if (!FindEthernetInterfaces(&primary_interface_iterator)) + return false; + bool result = GetMACAddressFromIterator( + primary_interface_iterator, buffer, size); + IOObjectRelease(primary_interface_iterator); + return result; +} + +CFStringRef CopySerialNumber() { + base::mac::ScopedIOObject<io_service_t> expert_device( + IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("IOPlatformExpertDevice"))); + if (!expert_device) + return NULL; + + base::mac::ScopedCFTypeRef<CFTypeRef> serial_number( + IORegistryEntryCreateCFProperty(expert_device, + CFSTR(kIOPlatformSerialNumberKey), + kCFAllocatorDefault, + 0)); + CFStringRef serial_number_cfstring = + base::mac::CFCast<CFStringRef>(serial_number); + if (!serial_number_cfstring) + return NULL; + + ignore_result(serial_number.release()); + return serial_number_cfstring; +} + +} // namespace + +bool GetRawMachineId(string16* data, int* more_data) { + uint8_t mac_address[kIOEthernetAddressSize]; + + data->clear(); + if (GetMacAddress(mac_address, sizeof(mac_address))) { + *data += ASCIIToUTF16(StringPrintf("mac:%02x%02x%02x%02x%02x%02x", + mac_address[0], mac_address[1], mac_address[2], + mac_address[3], mac_address[4], mac_address[5])); + } + + // A MAC address is enough to uniquely identify a machine, but it's only 6 + // bytes, 3 of which are manufacturer-determined. To make brute-forcing the + // SHA1 of this harder, also append the system's serial number. + CFStringRef serial = CopySerialNumber(); + if (serial) { + if (!data->empty()) + *data += UTF8ToUTF16(" "); + *data += UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial); + CFRelease(serial); + } + + // On windows, this is set to the volume id. Since it's not scrambled before + // being sent, just set it to 1. + *more_data = 1; + return true; +} + +} // namespace rlz_lib diff --git a/rlz/mac/lib/rlz_value_store_mac.h b/rlz/mac/lib/rlz_value_store_mac.h new file mode 100644 index 0000000..b7ffb4e --- /dev/null +++ b/rlz/mac/lib/rlz_value_store_mac.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ +#define RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ + +#include "rlz/lib/rlz_value_store.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_nsobject.h" + +@class NSDictionary; +@class NSMutableDictionary; + +namespace rlz_lib { + +// An implementation of RlzValueStore for mac. It stores information in a +// plist file in the user's Application Support folder. +class RlzValueStoreMac : public RlzValueStore { + public: + virtual bool HasAccess(AccessType type) OVERRIDE; + + virtual bool WritePingTime(Product product, int64 time) OVERRIDE; + virtual bool ReadPingTime(Product product, int64* time) OVERRIDE; + virtual bool ClearPingTime(Product product) OVERRIDE; + + virtual bool WriteAccessPointRlz(AccessPoint access_point, + const char* new_rlz) OVERRIDE; + virtual bool ReadAccessPointRlz(AccessPoint access_point, + char* rlz, + size_t rlz_size) OVERRIDE; + virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE; + + virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE; + virtual bool ReadProductEvents(Product product, + std::vector<std::string>* events) OVERRIDE; + virtual bool ClearProductEvent(Product product, + const char* event_rlz) OVERRIDE; + virtual bool ClearAllProductEvents(Product product) OVERRIDE; + + virtual bool AddStatefulEvent(Product product, + const char* event_rlz) OVERRIDE; + virtual bool IsStatefulEvent(Product product, + const char* event_rlz) OVERRIDE; + virtual bool ClearAllStatefulEvents(Product product) OVERRIDE; + + virtual void CollectGarbage() OVERRIDE; + + private: + // |dict| is the dictionary that backs all data. plist_path is the name of the + // plist file, used solely for implementing HasAccess(). + RlzValueStoreMac(NSMutableDictionary* dict, NSString* plist_path); + virtual ~RlzValueStoreMac(); + friend class ScopedRlzValueStoreLock; + + // Returns the backing dictionary that should be written to disk. + NSDictionary* dictionary(); + + // Returns the dictionary to which all data should be written. Usually, this + // is just |dictionary()|, but if supplementary branding is used, it's a + // subdirectory at key "brand_<supplementary branding code>". + // Note that windows stores data at + // rlz/name (e.g. "pingtime")/supplementalbranding/productcode + // Mac on the other hand does + // supplementalbranding/productcode/pingtime. + NSMutableDictionary* WorkingDict(); + + // Returns the subdirectory of |WorkingDict()| used to store data for + // product p. + NSMutableDictionary* ProductDict(Product p); + + scoped_nsobject<NSMutableDictionary> dict_; + scoped_nsobject<NSString> plist_path_; + + DISALLOW_COPY_AND_ASSIGN(RlzValueStoreMac); +}; + +} // namespace rlz_lib + +#endif // RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_ diff --git a/rlz/mac/lib/rlz_value_store_mac.mm b/rlz/mac/lib/rlz_value_store_mac.mm new file mode 100644 index 0000000..5313a30 --- /dev/null +++ b/rlz/mac/lib/rlz_value_store_mac.mm @@ -0,0 +1,447 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "rlz/mac/lib/rlz_value_store_mac.h" + +#include "base/mac/foundation_util.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/sys_string_conversions.h" +#include "rlz/lib/assert.h" +#include "rlz/lib/lib_values.h" +#include "rlz/lib/rlz_lib.h" + +#import <Foundation/Foundation.h> +#include <pthread.h> + +using base::mac::ObjCCast; + +namespace rlz_lib { + +// These are written to disk and should not be changed. +NSString* const kPingTimeKey = @"pingTime"; +NSString* const kAccessPointKey = @"accessPoints"; +NSString* const kProductEventKey = @"productEvents"; +NSString* const kStatefulEventKey = @"statefulEvents"; + +namespace { + +NSString* GetNSProductName(Product product) { + return base::SysUTF8ToNSString(GetProductName(product)); +} + +NSString* GetNSAccessPointName(AccessPoint p) { + return base::SysUTF8ToNSString(GetAccessPointName(p)); +} + +// Retrieves a subdictionary in |p| for key |k|, creating it if necessary. +// If the dictionary contains an object for |k| that is not a mutable +// dictionary, that object is replaced with an empty mutable dictinary. +NSMutableDictionary* GetOrCreateDict( + NSMutableDictionary* p, NSString* k) { + NSMutableDictionary* d = ObjCCast<NSMutableDictionary>([p objectForKey:k]); + if (!d) { + d = [NSMutableDictionary dictionaryWithCapacity:0]; + [p setObject:d forKey:k]; + } + return d; +} + +} // namespace + +RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict, + NSString* plist_path) + : dict_([dict retain]), plist_path_([plist_path retain]) { +} + +RlzValueStoreMac::~RlzValueStoreMac() { +} + +bool RlzValueStoreMac::HasAccess(AccessType type) { + NSFileManager* manager = [NSFileManager defaultManager]; + switch (type) { + case kReadAccess: return [manager isReadableFileAtPath:plist_path_]; + case kWriteAccess: return [manager isWritableFileAtPath:plist_path_]; + } +} + +bool RlzValueStoreMac::WritePingTime(Product product, int64 time) { + NSNumber* n = [NSNumber numberWithLongLong:time]; + [ProductDict(product) setObject:n forKey:kPingTimeKey]; + return true; +} + +bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) { + if (NSNumber* n = + ObjCCast<NSNumber>([ProductDict(product) objectForKey:kPingTimeKey])) { + *time = [n longLongValue]; + return true; + } + return false; +} + +bool RlzValueStoreMac::ClearPingTime(Product product) { + [ProductDict(product) removeObjectForKey:kPingTimeKey]; + return true; +} + + +bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point, + const char* new_rlz) { + NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey); + [d setObject:base::SysUTF8ToNSString(new_rlz) + forKey:GetNSAccessPointName(access_point)]; + return true; +} + +bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point, + char* rlz, + size_t rlz_size) { + // Reading a non-existent access point counts as success. + if (NSDictionary* d = ObjCCast<NSDictionary>( + [WorkingDict() objectForKey:kAccessPointKey])) { + NSString* val = ObjCCast<NSString>( + [d objectForKey:GetNSAccessPointName(access_point)]); + if (!val) { + if (rlz_size > 0) + rlz[0] = '\0'; + return true; + } + + std::string s = base::SysNSStringToUTF8(val); + if (s.size() >= rlz_size) { + rlz[0] = 0; + ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size"); + return false; + } + strncpy(rlz, s.c_str(), rlz_size); + return true; + } + if (rlz_size > 0) + rlz[0] = '\0'; + return true; +} + +bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) { + if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>( + [WorkingDict() objectForKey:kAccessPointKey])) { + [d removeObjectForKey:GetNSAccessPointName(access_point)]; + } + return true; +} + + +bool RlzValueStoreMac::AddProductEvent(Product product, + const char* event_rlz) { + [GetOrCreateDict(ProductDict(product), kProductEventKey) + setObject:[NSNumber numberWithBool:YES] + forKey:base::SysUTF8ToNSString(event_rlz)]; + return true; +} + +bool RlzValueStoreMac::ReadProductEvents(Product product, + std::vector<std::string>* events) { + if (NSDictionary* d = ObjCCast<NSDictionary>( + [ProductDict(product) objectForKey:kProductEventKey])) { + for (NSString* s in d) + events->push_back(base::SysNSStringToUTF8(s)); + return true; + } + return true; +} + +bool RlzValueStoreMac::ClearProductEvent(Product product, + const char* event_rlz) { + if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>( + [ProductDict(product) objectForKey:kProductEventKey])) { + [d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)]; + return true; + } + return false; +} + +bool RlzValueStoreMac::ClearAllProductEvents(Product product) { + [ProductDict(product) removeObjectForKey:kProductEventKey]; + return true; +} + + +bool RlzValueStoreMac::AddStatefulEvent(Product product, + const char* event_rlz) { + [GetOrCreateDict(ProductDict(product), kStatefulEventKey) + setObject:[NSNumber numberWithBool:YES] + forKey:base::SysUTF8ToNSString(event_rlz)]; + return true; +} + +bool RlzValueStoreMac::IsStatefulEvent(Product product, + const char* event_rlz) { + if (NSDictionary* d = ObjCCast<NSDictionary>( + [ProductDict(product) objectForKey:kStatefulEventKey])) { + return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil; + } + return false; +} + +bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) { + [ProductDict(product) removeObjectForKey:kStatefulEventKey]; + return true; +} + + +void RlzValueStoreMac::CollectGarbage() { + NOTIMPLEMENTED(); +} + +NSDictionary* RlzValueStoreMac::dictionary() { + return dict_.get(); +} + +NSMutableDictionary* RlzValueStoreMac::WorkingDict() { + std::string brand(SupplementaryBranding::GetBrand()); + if (brand.empty()) + return dict_; + + NSString* brand_ns = + [@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)]; + + return GetOrCreateDict(dict_.get(), brand_ns); +} + +NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) { + return GetOrCreateDict(WorkingDict(), GetNSProductName(p)); +} + + +namespace { + +// Creating a recursive cross-process mutex on windows is one line. On mac, +// there's no primitve for that, so this lock is emulated by an in-process +// mutex to get the recursive part, followed by a cross-process lock for the +// cross-process part. + +// This is a struct so that it doesn't need a static initializer. +struct RecursiveCrossProcessLock { + // Tries to acquire a recursive cross-process lock. Note that this _always_ + // acquires the in-process lock (if it wasn't already acquired). The parent + // directory of |lock_file| must exist. + bool TryGetCrossProcessLock(NSString* lock_filename); + + // Releases the lock. Should always be called, even if + // TryGetCrossProcessLock() returns false. + void ReleaseLock(); + + pthread_mutex_t recursive_lock_; + pthread_t locking_thread_; + + NSDistributedLock* file_lock_; +} g_recursive_lock = { + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy + // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate + // recursive locking with a normal non-recursive mutex. + PTHREAD_MUTEX_INITIALIZER +}; + +bool RecursiveCrossProcessLock::TryGetCrossProcessLock( + NSString* lock_filename) { + bool just_got_lock = false; + + // Emulate a recursive mutex with a non-recursive one. + if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) { + if (pthread_equal(pthread_self(), locking_thread_) == 0) { + // Some other thread has the lock, wait for it. + pthread_mutex_lock(&recursive_lock_); + CHECK(locking_thread_ == 0); + just_got_lock = true; + } + } else { + just_got_lock = true; + } + + locking_thread_ = pthread_self(); + + // Try to acquire file lock. + if (just_got_lock) { + const int kMaxTimeoutMS = 5000; // Matches windows. + const int kSleepPerTryMS = 200; + + CHECK(!file_lock_); + file_lock_ = [[NSDistributedLock alloc] initWithPath:lock_filename]; + + BOOL got_file_lock = NO; + int elapsedMS = 0; + while (!(got_file_lock = [file_lock_ tryLock]) && + elapsedMS < kMaxTimeoutMS) { + usleep(kSleepPerTryMS * 1000); + elapsedMS += kSleepPerTryMS; + } + + if (!got_file_lock) { + [file_lock_ release]; + file_lock_ = nil; + return false; + } + return true; + } else { + return file_lock_ != nil; + } +} + +void RecursiveCrossProcessLock::ReleaseLock() { + if (file_lock_) { + [file_lock_ unlock]; + [file_lock_ release]; + file_lock_ = nil; + } + + locking_thread_ = 0; + pthread_mutex_unlock(&recursive_lock_); +} + + +// This is set during test execution, to write RLZ files into a temporary +// directory instead of the user's Application Support folder. +NSString* g_test_folder; + +// RlzValueStoreMac keeps its data in memory and only writes it to disk when +// ScopedRlzValueStoreLock goes out of scope. Hence, if several +// ScopedRlzValueStoreLocks are nested, they all need to use the same store +// object. + +// This counts the nesting depth. +int g_lock_depth = 0; + +// This is the store object that might be shared. Only set if g_lock_depth > 0. +RlzValueStoreMac* g_store_object = NULL; + + +NSString* CreateRlzDirectory() { + NSFileManager* manager = [NSFileManager defaultManager]; + NSArray* paths = NSSearchPathForDirectoriesInDomains( + NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES); + NSString* folder = nil; + if ([paths count] > 0) + folder = ObjCCast<NSString>([paths objectAtIndex:0]); + if (!folder) + folder = [@"~/Library/Application Support" stringByStandardizingPath]; + folder = [folder stringByAppendingPathComponent:@"Google/RLZ"]; + + if (g_test_folder) + folder = [g_test_folder stringByAppendingPathComponent:folder]; + + [manager createDirectoryAtPath:folder + withIntermediateDirectories:YES + attributes:nil + error:nil]; + return folder; +} + +// Returns the path of the rlz plist store, also creates the parent directory +// path if it doesn't exist. +NSString* RlzPlistFilename() { + NSString* const kRlzFile = @"RlzStore.plist"; + return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; +} + +// Returns the path of the rlz lock file, also creates the parent directory +// path if it doesn't exist. +NSString* RlzLockFilename() { + NSString* const kRlzFile = @"lockfile"; + return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile]; +} + +} // namespace + +ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { + bool got_distributed_lock = + g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename()); + // At this point, we hold the in-process lock, no matter the value of + // |got_distributed_lock|. + + ++g_lock_depth; + + if (!got_distributed_lock) { + // Give up. |store_| isn't set, which signals to callers that acquiring + // the lock failed. |g_recursive_lock| will be released by the + // destructor. + CHECK(!g_store_object); + return; + } + + if (g_lock_depth > 1) { + // Reuse the already existing store object. + CHECK(g_store_object); + store_.reset(g_store_object); + return; + } + + CHECK(!g_store_object); + + NSString* plist = RlzPlistFilename(); + + // Create an empty file if none exists yet. + NSFileManager* manager = [NSFileManager defaultManager]; + if (![manager fileExistsAtPath:plist isDirectory:NULL]) + [[NSDictionary dictionary] writeToFile:plist atomically:YES]; + + NSMutableDictionary* dict = + [NSMutableDictionary dictionaryWithContentsOfFile:plist]; + VERIFY(dict); + + if (dict) { + store_.reset(new RlzValueStoreMac(dict, plist)); + g_store_object = (RlzValueStoreMac*)store_.get(); + } +} + +ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { + --g_lock_depth; + CHECK(g_lock_depth >= 0); + + if (g_lock_depth > 0) { + // Other locks are still using store_, don't free it yet. + ignore_result(store_.release()); + return; + } + + if (store_.get()) { + g_store_object = NULL; + + NSDictionary* dict = + static_cast<RlzValueStoreMac*>(store_.get())->dictionary(); + VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]); + } + + // Check that "store_ set" => "file_lock acquired". The converse isn't true, + // for example if the rlz data file can't be read. + if (store_.get()) + CHECK(g_recursive_lock.file_lock_); + if (!g_recursive_lock.file_lock_) + CHECK(!store_.get()); + + g_recursive_lock.ReleaseLock(); +} + +RlzValueStore* ScopedRlzValueStoreLock::GetStore() { + return store_.get(); +} + +namespace testing { + +void SetRlzStoreDirectory(const FilePath& directory) { + base::mac::ScopedNSAutoreleasePool pool; + + [g_test_folder release]; + if (directory.empty()) { + g_test_folder = nil; + } else { + // Not Unsafe on OS X. + g_test_folder = + [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()]; + } +} + +} // namespace testing + +} // namespace rlz_lib |