1// Copyright 2013 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 "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h"
6
7#include <CoreFoundation/CoreFoundation.h>
8#include <IOKit/storage/IOBlockStorageDevice.h>
9#include <IOKit/IOBSD.h>
10#include <IOKit/IOKitLib.h>
11#include <IOKit/storage/IOMedia.h>
12#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
13
14#include "base/mac/foundation_util.h"
15#include "base/mac/scoped_cftyperef.h"
16#include "base/mac/scoped_ioobject.h"
17#include "base/strings/sys_string_conversions.h"
18#include "base/threading/thread_restrictions.h"
19#include "chrome/common/extensions/image_writer/image_writer_util_mac.h"
20
21namespace extensions {
22
23// static
24bool RemovableStorageProvider::PopulateDeviceList(
25    scoped_refptr<StorageDeviceList> device_list) {
26  base::ThreadRestrictions::AssertIOAllowed();
27  // Match only writable whole-disks.
28  CFMutableDictionaryRef matching = IOServiceMatching(kIOMediaClass);
29  CFDictionaryAddValue(matching, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
30  CFDictionaryAddValue(matching, CFSTR(kIOMediaWritableKey), kCFBooleanTrue);
31
32  io_service_t disk_iterator;
33  if (IOServiceGetMatchingServices(
34          kIOMasterPortDefault, matching, &disk_iterator) != KERN_SUCCESS) {
35    LOG(ERROR) << "Unable to get disk services.";
36    return false;
37  }
38  base::mac::ScopedIOObject<io_service_t> iterator_ref(disk_iterator);
39
40  io_object_t disk_obj;
41  while ((disk_obj = IOIteratorNext(disk_iterator))) {
42    base::mac::ScopedIOObject<io_object_t> disk_obj_ref(disk_obj);
43
44    CFMutableDictionaryRef dict;
45    if (IORegistryEntryCreateCFProperties(
46            disk_obj, &dict, kCFAllocatorDefault, 0) != KERN_SUCCESS) {
47      LOG(ERROR) << "Unable to get properties of disk object.";
48      continue;
49    }
50    base::ScopedCFTypeRef<CFMutableDictionaryRef> dict_ref(dict);
51
52    CFStringRef cf_bsd_name = base::mac::GetValueFromDictionary<CFStringRef>(
53        dict, CFSTR(kIOBSDNameKey));
54    std::string bsd_name = base::SysCFStringRefToUTF8(cf_bsd_name);
55
56    CFNumberRef size_number = base::mac::GetValueFromDictionary<CFNumberRef>(
57        dict, CFSTR(kIOMediaSizeKey));
58    uint64 size_in_bytes = 0;
59    if (size_number)
60      CFNumberGetValue(size_number, kCFNumberLongLongType, &size_in_bytes);
61
62    CFBooleanRef cf_removable = base::mac::GetValueFromDictionary<CFBooleanRef>(
63        dict, CFSTR(kIOMediaRemovableKey));
64    bool removable = CFBooleanGetValue(cf_removable);
65
66    bool is_usb = IsUsbDevice(disk_obj);
67
68    if (!removable && !is_usb) {
69      continue;
70    }
71
72    base::ScopedCFTypeRef<CFDictionaryRef> characteristics(
73        static_cast<CFDictionaryRef>(IORegistryEntrySearchCFProperty(
74            disk_obj,
75            kIOServicePlane,
76            CFSTR(kIOPropertyDeviceCharacteristicsKey),
77            kCFAllocatorDefault,
78            kIORegistryIterateParents | kIORegistryIterateRecursively)));
79
80    if (characteristics == NULL) {
81      LOG(ERROR) << "Unable to find device characteristics for " << cf_bsd_name;
82      continue;
83    }
84
85    CFStringRef cf_vendor = base::mac::GetValueFromDictionary<CFStringRef>(
86        characteristics, CFSTR(kIOPropertyVendorNameKey));
87    std::string vendor = base::SysCFStringRefToUTF8(cf_vendor);
88
89    CFStringRef cf_model = base::mac::GetValueFromDictionary<CFStringRef>(
90        characteristics, CFSTR(kIOPropertyProductNameKey));
91    std::string model = base::SysCFStringRefToUTF8(cf_model);
92
93    linked_ptr<api::image_writer_private::RemovableStorageDevice> device(
94        new api::image_writer_private::RemovableStorageDevice());
95    device->storage_unit_id = bsd_name;
96    device->capacity = size_in_bytes;
97    device->vendor = vendor;
98    device->model = model;
99    device->removable = removable;
100    device_list->data.push_back(device);
101  }
102
103  return true;
104}
105
106} // namespace extensions
107