// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
// devguid requires Windows.h be imported first.
#include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h"
#include <windows.h>
#include <setupapi.h>
// LogSeverity is both a macro in setupapi.h and a typedef in base/logging.h
#undef LogSeverity
#include <winioctl.h>
#include <memory>
#include "base/containers/heap_array.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
namespace extensions {
namespace {
bool AddDeviceInfo(HANDLE interface_enumerator,
SP_DEVICE_INTERFACE_DATA* interface_data,
scoped_refptr<StorageDeviceList> device_list) {
// Get the required buffer size by calling with a null output buffer.
DWORD interface_detail_data_size;
BOOL status = SetupDiGetDeviceInterfaceDetail(
interface_enumerator,
interface_data,
NULL, // Output buffer.
0, // Output buffer size.
&interface_detail_data_size, // Receives the buffer size.
NULL); // Optional DEVINFO_DATA.
if (status == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail failed";
return false;
}
auto interface_detail_data_buffer =
base::HeapArray<char>::Uninit(interface_detail_data_size);
SP_DEVICE_INTERFACE_DETAIL_DATA* interface_detail_data =
reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(
interface_detail_data_buffer.data());
interface_detail_data->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
status = SetupDiGetDeviceInterfaceDetail(
interface_enumerator,
interface_data,
interface_detail_data, // Output struct.
interface_detail_data_size, // Output struct size.
NULL, // Receives required size, unneeded.
NULL); // Optional DEVINFO_Data.
if (status == FALSE) {
PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail failed";
return false;
}
// Open a handle to the device to send DeviceIoControl messages.
base::win::ScopedHandle device_handle(CreateFile(
interface_detail_data->DevicePath,
// Desired access, which is none as we only need metadata.
0,
// Required to be read + write for devices.
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, // Optional security attributes.
OPEN_EXISTING, // Devices already exist.
0, // No optional flags.
NULL)); // No template file.
if (!device_handle.IsValid()) {
PLOG(ERROR) << "Opening device handle failed.";
return false;
}
DISK_GEOMETRY geometry;
DWORD bytes_returned;
status = DeviceIoControl(
device_handle.Get(), // Device handle.
IOCTL_DISK_GET_DRIVE_GEOMETRY, // Flag to request disk size.
NULL, // Optional additional parameters.
0, // Optional parameter size.
&geometry, // output buffer.
sizeof(DISK_GEOMETRY), // output size.
&bytes_returned, // Must be non-null. If overlapped is null,
// then value is meaningless.
NULL); // Optional unused overlapped parameter.
if (status == FALSE) {
PLOG(ERROR) << "DeviceIoControl";
return false;
}
ULONGLONG disk_capacity = geometry.Cylinders.QuadPart *
geometry.TracksPerCylinder *
geometry.SectorsPerTrack *
geometry.BytesPerSector;
STORAGE_PROPERTY_QUERY query = STORAGE_PROPERTY_QUERY();
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
auto output_buf = base::HeapArray<char>::Uninit(1024);
status = DeviceIoControl(
device_handle.Get(), // Device handle.
IOCTL_STORAGE_QUERY_PROPERTY, // Flag to request device properties.
&query, // Query parameters.
sizeof(STORAGE_PROPERTY_QUERY), // query parameters size.
output_buf.data(), // output buffer.
1024, // Size of buffer.
&bytes_returned, // Number of bytes returned.
// Must not be null.
NULL); // Optional unused overlapped perameter.
if (status == FALSE) {
PLOG(ERROR) << "Storage property query failed.";
return false;
}
STORAGE_DEVICE_DESCRIPTOR* device_descriptor =
reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(output_buf.data());
if (!device_descriptor->RemovableMedia &&
!(device_descriptor->BusType == BusTypeUsb)) {
// Reject non-removable and non-USB devices.
// Return true to indicate success but not add anything to the device list.
return true;
}
// Create a drive identifier from the drive number.
STORAGE_DEVICE_NUMBER device_number = {0};
status = DeviceIoControl(
device_handle.Get(), // Device handle.
IOCTL_STORAGE_GET_DEVICE_NUMBER,// Flag to request device number.
NULL, // Query parameters, should be NULL.
0, // Query parameters size, should be 0.
&device_number, // output buffer.
sizeof(device_number), // Size of buffer.
&bytes_returned, // Number of bytes returned.
NULL); // Optional unused overlapped perameter.
if (status == FALSE) {
PLOG(ERROR) << "Storage device number query failed.";
return false;
}
std::string drive_id = "\\\\.\\PhysicalDrive";
drive_id.append(base::NumberToString(device_number.DeviceNumber));
api::image_writer_private::RemovableStorageDevice device;
device.capacity = disk_capacity;
device.storage_unit_id = drive_id;
device.removable = device_descriptor->RemovableMedia == TRUE;
if (device_descriptor->VendorIdOffset &&
output_buf[device_descriptor->VendorIdOffset]) {
device.vendor.assign(output_buf.data() + device_descriptor->VendorIdOffset);
}
std::string product_id;
if (device_descriptor->ProductIdOffset &&
output_buf[device_descriptor->ProductIdOffset]) {
device.model.assign(output_buf.data() + device_descriptor->ProductIdOffset);
}
device_list->data.push_back(std::move(device));
return true;
}
} // namespace
// static
scoped_refptr<StorageDeviceList>
RemovableStorageProvider::PopulateDeviceList() {
HDEVINFO interface_enumerator = SetupDiGetClassDevs(
&DiskClassGuid,
NULL, // Enumerator.
NULL, // Parent window.
// Only devices present & interface class.
(DIGCF_PRESENT | DIGCF_INTERFACEDEVICE));
if (interface_enumerator == INVALID_HANDLE_VALUE) {
DPLOG(ERROR) << "SetupDiGetClassDevs failed.";
return nullptr;
}
DWORD index = 0;
SP_DEVICE_INTERFACE_DATA interface_data;
interface_data.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
auto device_list = base::MakeRefCounted<StorageDeviceList>();
while (SetupDiEnumDeviceInterfaces(
interface_enumerator,
NULL, // Device Info data.
&GUID_DEVINTERFACE_DISK, // Only disk devices.
index,
&interface_data)) {
AddDeviceInfo(interface_enumerator, &interface_data, device_list);
index++;
}
DWORD error_code = GetLastError();
if (error_code != ERROR_NO_MORE_ITEMS) {
PLOG(ERROR) << "SetupDiEnumDeviceInterfaces failed";
SetupDiDestroyDeviceInfoList(interface_enumerator);
return nullptr;
}
SetupDiDestroyDeviceInfoList(interface_enumerator);
return device_list;
}
} // namespace extensions