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