removable_storage_provider_win.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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// devguid requires Windows.h be imported first.
6#include <windows.h>
7#include <setupapi.h>
8#include <winioctl.h>
9
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/win/scoped_handle.h"
14#include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h"
15
16namespace extensions {
17
18namespace {
19
20bool AddDeviceInfo(HANDLE interface_enumerator,
21                   SP_DEVICE_INTERFACE_DATA* interface_data,
22                   scoped_refptr<StorageDeviceList> device_list) {
23  // Get the required buffer size by calling with a null output buffer.
24  DWORD interface_detail_data_size;
25  BOOL status = SetupDiGetDeviceInterfaceDetail(
26      interface_enumerator,
27      interface_data,
28      NULL,                         // Output buffer.
29      0,                            // Output buffer size.
30      &interface_detail_data_size,  // Receives the buffer size.
31      NULL);                        // Optional DEVINFO_DATA.
32
33  if (status == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
34    PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail failed";
35    return false;
36  }
37
38
39  scoped_ptr<char[]> interface_detail_data_buffer(
40      new char[interface_detail_data_size]);
41
42  SP_DEVICE_INTERFACE_DETAIL_DATA* interface_detail_data =
43      reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(
44          interface_detail_data_buffer.get());
45
46  interface_detail_data->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
47
48  status = SetupDiGetDeviceInterfaceDetail(
49      interface_enumerator,
50      interface_data,
51      interface_detail_data, // Output struct.
52      interface_detail_data_size,  // Output struct size.
53      NULL,                        // Receives required size, unneeded.
54      NULL);                       // Optional DEVINFO_Data.
55
56  if (status == FALSE) {
57    PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail failed";
58    return false;
59  }
60
61  // Open a handle to the device to send DeviceIoControl messages.
62  base::win::ScopedHandle device_handle(CreateFile(
63      interface_detail_data->DevicePath,
64      // Desired access, which is none as we only need metadata.
65      0,
66      // Required to be read + write for devices.
67      FILE_SHARE_READ | FILE_SHARE_WRITE,
68      NULL,           // Optional security attributes.
69      OPEN_EXISTING,  // Devices already exist.
70      0,              // No optional flags.
71      NULL));          // No template file.
72
73  if (!device_handle) {
74    PLOG(ERROR) << "Opening device handle failed.";
75    return false;
76  }
77
78  DISK_GEOMETRY geometry;
79  DWORD bytes_returned;
80  status = DeviceIoControl(
81      device_handle,                 // Device handle.
82      IOCTL_DISK_GET_DRIVE_GEOMETRY, // Flag to request disk size.
83      NULL,                          // Optional additional parameters.
84      0,                             // Optional parameter size.
85      &geometry,                     // output buffer.
86      sizeof(DISK_GEOMETRY),         // output size.
87      &bytes_returned,               // Must be non-null. If overlapped is null,
88                                     // then value is meaningless.
89      NULL);                         // Optional unused overlapped parameter.
90
91  if (status == FALSE) {
92    PLOG(ERROR) << "DeviceIoControl";
93    return false;
94  }
95
96  ULONGLONG disk_capacity = geometry.Cylinders.QuadPart *
97    geometry.TracksPerCylinder *
98    geometry.SectorsPerTrack *
99    geometry.BytesPerSector;
100
101  STORAGE_PROPERTY_QUERY query = STORAGE_PROPERTY_QUERY();
102  query.PropertyId = StorageDeviceProperty;
103  query.QueryType = PropertyStandardQuery;
104
105  scoped_ptr<char[]> output_buf(new char[1024]);
106  status = DeviceIoControl(
107      device_handle,                  // Device handle.
108      IOCTL_STORAGE_QUERY_PROPERTY,   // Flag to request device properties.
109      &query,                         // Query parameters.
110      sizeof(STORAGE_PROPERTY_QUERY), // query parameters size.
111      output_buf.get(),               // output buffer.
112      1024,                           // Size of buffer.
113      &bytes_returned,                // Number of bytes returned.
114                                      // Must not be null.
115      NULL);                          // Optional unused overlapped perameter.
116
117  if (status == FALSE) {
118    PLOG(ERROR) << "Storage property query failed.";
119    return false;
120  }
121
122  STORAGE_DEVICE_DESCRIPTOR* device_descriptor =
123      reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(output_buf.get());
124
125  if (!device_descriptor->RemovableMedia &&
126      !(device_descriptor->BusType == BusTypeUsb)) {
127    // Reject non-removable and non-USB devices.
128    // Return true to indicate success but not add anything to the device list.
129    return true;
130  }
131
132  // Create a drive identifier from the drive number.
133  STORAGE_DEVICE_NUMBER device_number = {0};
134  status = DeviceIoControl(
135      device_handle,                  // Device handle.
136      IOCTL_STORAGE_GET_DEVICE_NUMBER,// Flag to request device number.
137      NULL,                           // Query parameters, should be NULL.
138      0,                              // Query parameters size, should be 0.
139      &device_number,                 // output buffer.
140      sizeof(device_number),          // Size of buffer.
141      &bytes_returned,                // Number of bytes returned.
142      NULL);                          // Optional unused overlapped perameter.
143
144  if (status == FALSE) {
145    PLOG(ERROR) << "Storage device number query failed.";
146    return false;
147  }
148
149  std::string drive_id = "\\\\.\\PhysicalDrive";
150  drive_id.append(base::Uint64ToString(device_number.DeviceNumber));
151
152  linked_ptr<api::image_writer_private::RemovableStorageDevice> device(
153    new api::image_writer_private::RemovableStorageDevice());
154  device->capacity = disk_capacity;
155  device->storage_unit_id = drive_id;
156
157  if (device_descriptor->VendorIdOffset &&
158      output_buf[device_descriptor->VendorIdOffset]) {
159    device->vendor.assign(output_buf.get() + device_descriptor->VendorIdOffset);
160  }
161
162  std::string product_id;
163  if (device_descriptor->ProductIdOffset &&
164      output_buf[device_descriptor->ProductIdOffset]) {
165    device->model.assign(output_buf.get() + device_descriptor->ProductIdOffset);
166  }
167
168  device_list->data.push_back(device);
169
170  return true;
171}
172
173}  // namespace
174
175bool RemovableStorageProvider::PopulateDeviceList(
176    scoped_refptr<StorageDeviceList> device_list) {
177  HDEVINFO interface_enumerator = SetupDiGetClassDevs(
178      &DiskClassGuid,
179      NULL, // Enumerator.
180      NULL, // Parent window.
181      // Only devices present & interface class.
182      (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE));
183
184  if (interface_enumerator == INVALID_HANDLE_VALUE) {
185    DPLOG(ERROR) << "SetupDiGetClassDevs failed.";
186    return false;
187  }
188
189  DWORD index = 0;
190  SP_DEVICE_INTERFACE_DATA interface_data;
191  interface_data.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
192
193  while (SetupDiEnumDeviceInterfaces(
194      interface_enumerator,
195      NULL,                    // Device Info data.
196      &GUID_DEVINTERFACE_DISK, // Only disk devices.
197      index,
198      &interface_data)) {
199    AddDeviceInfo(interface_enumerator, &interface_data, device_list);
200    index++;
201  }
202
203  DWORD error_code = GetLastError();
204
205  if (error_code != ERROR_NO_MORE_ITEMS) {
206    PLOG(ERROR) << "SetupDiEnumDeviceInterfaces failed";
207    SetupDiDestroyDeviceInfoList(interface_enumerator);
208    return false;
209  }
210
211  SetupDiDestroyDeviceInfoList(interface_enumerator);
212  return true;
213}
214
215} // namespace extensions
216