mirror of
https://github.com/cmclark00/retro-imager.git
synced 2025-05-22 01:45:21 +01:00
Qt/QML edition
This commit is contained in:
commit
d7b361ba44
2168 changed files with 721948 additions and 0 deletions
41
dependencies/drivelist/src/darwin/REDiskList.h
vendored
Normal file
41
dependencies/drivelist/src/darwin/REDiskList.h
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2019 balena.io
|
||||
* Copyright 2018 Robin Andersson <me@robinwassen.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SRC_DARWIN_REDISKLIST_H_
|
||||
#define SRC_DARWIN_REDISKLIST_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Class to return a list of disks synchronously
|
||||
* To use the class, just init an instance of it and
|
||||
* it will populate the disks property with NSStrings
|
||||
*
|
||||
* @author Robin Andersson
|
||||
*/
|
||||
@interface REDiskList : NSObject
|
||||
|
||||
/**
|
||||
* NSArray of disks and partitions
|
||||
* Disks are in the format disk0, disk1 etc
|
||||
* Partitions are in the format disk0s1, disk1s1 etc
|
||||
*/
|
||||
@property(readonly) NSArray *disks;
|
||||
|
||||
@end
|
||||
|
||||
#endif // SRC_DARWIN_REDISKLIST_H_
|
61
dependencies/drivelist/src/darwin/REDiskList.m
vendored
Normal file
61
dependencies/drivelist/src/darwin/REDiskList.m
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2019 balena.io
|
||||
* Copyright 2018 Robin Andersson <me@robinwassen.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "REDiskList.h"
|
||||
#import <DiskArbitration/DiskArbitration.h>
|
||||
|
||||
@implementation REDiskList
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_disks = [[NSMutableArray alloc] init];
|
||||
[self populateDisksBlocking];
|
||||
[_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
[_disks release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
void appendDisk(DADiskRef disk, void *context) {
|
||||
NSMutableArray *_disks = (__bridge NSMutableArray*)context;
|
||||
const char *bsdName = DADiskGetBSDName(disk);
|
||||
if (bsdName != nil) {
|
||||
[_disks addObject:[NSString stringWithUTF8String:bsdName]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)populateDisksBlocking {
|
||||
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
|
||||
if (session) {
|
||||
DARegisterDiskAppearedCallback(session, NULL, appendDisk, (void*)_disks);
|
||||
CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
|
||||
DASessionScheduleWithRunLoop(session, runLoop, kCFRunLoopDefaultMode);
|
||||
CFRunLoopStop(runLoop);
|
||||
CFRunLoopRunInMode((CFStringRef)NSDefaultRunLoopMode, 0.05, NO);
|
||||
DAUnregisterCallback(session, appendDisk, (void*)_disks);
|
||||
CFRelease(session);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
179
dependencies/drivelist/src/darwin/list.mm
vendored
Normal file
179
dependencies/drivelist/src/darwin/list.mm
vendored
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright 2019 balena.io
|
||||
* Copyright 2018 Robin Andersson <me@robinwassen.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nan.h>
|
||||
#include "../drivelist.hpp"
|
||||
|
||||
#import "REDiskList.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <DiskArbitration/DiskArbitration.h>
|
||||
|
||||
namespace Drivelist {
|
||||
bool IsDiskPartition(NSString *disk) {
|
||||
NSPredicate *partitionRegEx = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"disk\\d+s\\d+"];
|
||||
return [partitionRegEx evaluateWithObject:disk];
|
||||
}
|
||||
|
||||
bool IsCard(CFDictionaryRef diskDescription) {
|
||||
CFDictionaryRef mediaIconDict = (CFDictionaryRef)CFDictionaryGetValue(
|
||||
diskDescription,
|
||||
kDADiskDescriptionMediaIconKey
|
||||
);
|
||||
if (mediaIconDict == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFStringRef iconFileNameKeyRef = CFStringCreateWithCString(NULL, "IOBundleResourceFile", kCFStringEncodingUTF8);
|
||||
CFStringRef iconFileNameRef = (CFStringRef)CFDictionaryGetValue(mediaIconDict, iconFileNameKeyRef);
|
||||
CFRelease(iconFileNameKeyRef);
|
||||
|
||||
if (iconFileNameRef == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// macOS 10.14.3 - External SD card reader provides `Removable.icns`, not `SD.icns`.
|
||||
// But we can't use it to detect SD card, because external drive has `Removable.icns` as well.
|
||||
return [(NSString *)iconFileNameRef isEqualToString:@"SD.icns"];
|
||||
}
|
||||
|
||||
NSNumber *DictionaryGetNumber(CFDictionaryRef dict, const void *key) {
|
||||
return (NSNumber*)CFDictionaryGetValue(dict, key);
|
||||
}
|
||||
|
||||
DeviceDescriptor CreateDeviceDescriptorFromDiskDescription(std::string diskBsdName, CFDictionaryRef diskDescription) {
|
||||
NSString *deviceProtocol = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey);
|
||||
NSNumber *blockSize = DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaBlockSizeKey);
|
||||
bool isInternal = [DictionaryGetNumber(diskDescription, kDADiskDescriptionDeviceInternalKey) boolValue];
|
||||
bool isRemovable = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaRemovableKey) boolValue];
|
||||
bool isEjectable = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaEjectableKey) boolValue];
|
||||
|
||||
DeviceDescriptor device = DeviceDescriptor();
|
||||
device.enumerator = "DiskArbitration";
|
||||
device.busType = (deviceProtocol != nil) ? [deviceProtocol UTF8String] : "";
|
||||
device.busVersion = "";
|
||||
device.busVersionNull = true;
|
||||
device.device = "/dev/" + diskBsdName;
|
||||
NSString *devicePath = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionBusPathKey);
|
||||
device.devicePath = (devicePath != nil) ? [devicePath UTF8String] : "";
|
||||
device.raw = "/dev/r" + diskBsdName;
|
||||
NSString *description = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionMediaNameKey);
|
||||
device.description = (description != nil) ? [description UTF8String] : "";
|
||||
device.error = "";
|
||||
// NOTE: Not sure if kDADiskDescriptionMediaBlockSizeKey returns
|
||||
// the physical or logical block size since both values are equal
|
||||
// on my machine
|
||||
//
|
||||
// The can be checked with the following command:
|
||||
// diskutil info / | grep "Block Size"
|
||||
device.blockSize = [blockSize unsignedIntValue];
|
||||
device.logicalBlockSize = [blockSize unsignedIntValue];
|
||||
device.size = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaSizeKey) unsignedLongValue];
|
||||
device.isReadOnly = ![DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaWritableKey) boolValue];
|
||||
device.isSystem = isInternal && !isRemovable;
|
||||
device.isVirtual = ((deviceProtocol != nil) && [deviceProtocol isEqualToString:@"Virtual Interface"]);
|
||||
device.isRemovable = isRemovable || isEjectable;
|
||||
device.isCard = IsCard(diskDescription);
|
||||
// NOTE(robin): Not convinced that these bus types should result
|
||||
// in device.isSCSI = true, it is rather "not usb or sd drive" bool
|
||||
// But the old implementation was like this so kept it this way
|
||||
NSArray *scsiTypes = [NSArray arrayWithObjects:@"SATA", @"SCSI", @"ATA", @"IDE", @"PCI", nil];
|
||||
device.isSCSI = ((deviceProtocol != nil) && [scsiTypes containsObject:deviceProtocol]);
|
||||
device.isUSB = ((deviceProtocol != nil) && [deviceProtocol isEqualToString:@"USB"]);
|
||||
device.isUAS = false;
|
||||
device.isUASNull = true;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
std::vector<DeviceDescriptor> ListStorageDevices() {
|
||||
std::vector<DeviceDescriptor> deviceList;
|
||||
|
||||
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
|
||||
if (session == nil) {
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
REDiskList *dl = [[REDiskList alloc] init];
|
||||
for (NSString* diskBsdName in dl.disks) {
|
||||
if (IsDiskPartition(diskBsdName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string diskBsdNameStr = [diskBsdName UTF8String];
|
||||
DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, diskBsdNameStr.c_str());
|
||||
if (disk == nil) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFDictionaryRef diskDescription = DADiskCopyDescription(disk);
|
||||
if (diskDescription == nil) {
|
||||
CFRelease(disk);
|
||||
continue;
|
||||
}
|
||||
|
||||
DeviceDescriptor device = CreateDeviceDescriptorFromDiskDescription(diskBsdNameStr, diskDescription);
|
||||
deviceList.push_back(device);
|
||||
|
||||
CFRelease(diskDescription);
|
||||
CFRelease(disk);
|
||||
}
|
||||
[dl release];
|
||||
|
||||
// Add mount points
|
||||
NSArray *volumeKeys = [NSArray arrayWithObjects:NSURLVolumeNameKey, NSURLVolumeLocalizedNameKey, nil];
|
||||
NSArray *volumePaths = [
|
||||
[NSFileManager defaultManager]
|
||||
mountedVolumeURLsIncludingResourceValuesForKeys:volumeKeys
|
||||
options:0
|
||||
];
|
||||
|
||||
for (NSURL *path in volumePaths) {
|
||||
DADiskRef disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, (__bridge CFURLRef)path);
|
||||
if (disk == nil) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *bsdnameChar = DADiskGetBSDName(disk);
|
||||
if (bsdnameChar == nil) {
|
||||
CFRelease(disk);
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *volumeName;
|
||||
[path getResourceValue:&volumeName forKey:NSURLVolumeLocalizedNameKey error:nil];
|
||||
|
||||
std::string partitionBsdName = std::string(bsdnameChar);
|
||||
std::string diskBsdName = partitionBsdName.substr(0, partitionBsdName.find("s", 5));
|
||||
|
||||
for(std::vector<int>::size_type i = 0; i != deviceList.size(); i++) {
|
||||
DeviceDescriptor *dd = &deviceList[i];
|
||||
|
||||
if (dd->device == "/dev/" + diskBsdName) {
|
||||
dd->mountpoints.push_back([[path path] UTF8String]);
|
||||
dd->mountpointLabels.push_back([volumeName UTF8String]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(disk);
|
||||
}
|
||||
CFRelease(session);
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
} // namespace Drivelist
|
152
dependencies/drivelist/src/device-descriptor.cpp
vendored
Normal file
152
dependencies/drivelist/src/device-descriptor.cpp
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nan.h>
|
||||
#include "drivelist.hpp"
|
||||
|
||||
using v8::String;
|
||||
using v8::Number;
|
||||
using v8::Boolean;
|
||||
using v8::Local;
|
||||
using v8::Value;
|
||||
using Nan::New;
|
||||
|
||||
namespace Drivelist {
|
||||
|
||||
v8::Local<v8::Object> PackDriveDescriptor(const DeviceDescriptor *instance) {
|
||||
v8::Local<v8::Object> object = Nan::New<v8::Object>();
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("enumerator").ToLocalChecked(),
|
||||
New<String>(instance->enumerator).ToLocalChecked());
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("busType").ToLocalChecked(),
|
||||
New<String>(instance->busType).ToLocalChecked());
|
||||
|
||||
Local<Value> busVersion = instance->busVersionNull ?
|
||||
(Local<Value>)Nan::Null() :
|
||||
(Local<Value>)New<String>(instance->busVersion).ToLocalChecked();
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("busVersion").ToLocalChecked(),
|
||||
busVersion);
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("device").ToLocalChecked(),
|
||||
New<String>(instance->device).ToLocalChecked());
|
||||
|
||||
Local<Value> devicePath = instance->devicePathNull ?
|
||||
(Local<Value>)Nan::Null() :
|
||||
(Local<Value>)New<String>(instance->devicePath).ToLocalChecked();
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("devicePath").ToLocalChecked(),
|
||||
devicePath);
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("raw").ToLocalChecked(),
|
||||
New<String>(instance->raw).ToLocalChecked());
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("description").ToLocalChecked(),
|
||||
New<String>(instance->description).ToLocalChecked());
|
||||
|
||||
if (instance->error != "") {
|
||||
Nan::Set(object,
|
||||
New<String>("error").ToLocalChecked(),
|
||||
New<String>(instance->error).ToLocalChecked());
|
||||
} else {
|
||||
Nan::Set(object,
|
||||
New<String>("error").ToLocalChecked(),
|
||||
Nan::Null());
|
||||
}
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("size").ToLocalChecked(),
|
||||
New<Number>(static_cast<double>(instance->size)));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("blockSize").ToLocalChecked(),
|
||||
New<Number>(static_cast<double>(instance->blockSize)));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("logicalBlockSize").ToLocalChecked(),
|
||||
New<Number>(static_cast<double>(instance->logicalBlockSize)));
|
||||
|
||||
v8::Local<v8::Object> mountpoints = Nan::New<v8::Array>();
|
||||
|
||||
uint32_t index = 0;
|
||||
for (std::string mountpointPath : instance->mountpoints) {
|
||||
v8::Local<v8::Object> mountpoint = Nan::New<v8::Object>();
|
||||
Nan::Set(mountpoint,
|
||||
New<String>("path").ToLocalChecked(),
|
||||
New<String>(mountpointPath).ToLocalChecked());
|
||||
|
||||
if (index < instance->mountpointLabels.size()) {
|
||||
Nan::Set(mountpoint,
|
||||
New<String>("label").ToLocalChecked(),
|
||||
New<String>(instance->mountpointLabels[index]).ToLocalChecked());
|
||||
}
|
||||
|
||||
Nan::Set(mountpoints, index, mountpoint);
|
||||
index++;
|
||||
}
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("mountpoints").ToLocalChecked(),
|
||||
mountpoints);
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isReadOnly").ToLocalChecked(),
|
||||
New<Boolean>(instance->isReadOnly));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isSystem").ToLocalChecked(),
|
||||
New<Boolean>(instance->isSystem));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isVirtual").ToLocalChecked(),
|
||||
New<Boolean>(instance->isVirtual));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isRemovable").ToLocalChecked(),
|
||||
New<Boolean>(instance->isRemovable));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isCard").ToLocalChecked(),
|
||||
New<Boolean>(instance->isCard));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isSCSI").ToLocalChecked(),
|
||||
New<Boolean>(instance->isSCSI));
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isUSB").ToLocalChecked(),
|
||||
New<Boolean>(instance->isUSB));
|
||||
|
||||
Local<Value> isUAS = instance->isUASNull ?
|
||||
(Local<Value>)Nan::Null() :
|
||||
(Local<Value>)New<Boolean>(instance->isUAS);
|
||||
|
||||
Nan::Set(object,
|
||||
New<String>("isUAS").ToLocalChecked(),
|
||||
isUAS);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
} // namespace Drivelist
|
66
dependencies/drivelist/src/drivelist.cpp
vendored
Normal file
66
dependencies/drivelist/src/drivelist.cpp
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nan.h>
|
||||
#include <vector>
|
||||
#include "drivelist.hpp"
|
||||
|
||||
class DriveListWorker : public Nan::AsyncWorker {
|
||||
public:
|
||||
explicit DriveListWorker(Nan::Callback *callback)
|
||||
: Nan::AsyncWorker(callback), devices() {}
|
||||
|
||||
~DriveListWorker() {}
|
||||
|
||||
void Execute() {
|
||||
devices = Drivelist::ListStorageDevices();
|
||||
}
|
||||
|
||||
void HandleOKCallback() {
|
||||
Nan::HandleScope scope;
|
||||
v8::Local<v8::Object> drives = Nan::New<v8::Array>();
|
||||
|
||||
uint32_t i;
|
||||
uint32_t size = (uint32_t) devices.size();
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
Nan::Set(drives, i, Drivelist::PackDriveDescriptor(&devices[i]));
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> argv[] = { Nan::Null(), drives };
|
||||
callback->Call(2, argv, async_resource);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Drivelist::DeviceDescriptor> devices;
|
||||
};
|
||||
|
||||
NAN_METHOD(list) {
|
||||
if (!info[0]->IsFunction()) {
|
||||
return Nan::ThrowTypeError("Callback must be a function");
|
||||
}
|
||||
|
||||
Nan::Callback *callback = new Nan::Callback(info[0].As<v8::Function>());
|
||||
Nan::AsyncQueueWorker(new DriveListWorker(callback));
|
||||
|
||||
info.GetReturnValue().SetUndefined();
|
||||
}
|
||||
|
||||
NAN_MODULE_INIT(InitAll) {
|
||||
NAN_EXPORT(target, list);
|
||||
}
|
||||
|
||||
NODE_MODULE(DriveList, InitAll);
|
64
dependencies/drivelist/src/drivelist.hpp
vendored
Normal file
64
dependencies/drivelist/src/drivelist.hpp
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SRC_DRIVELIST_HPP_
|
||||
#define SRC_DRIVELIST_HPP_
|
||||
|
||||
#include <nan.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Drivelist {
|
||||
|
||||
struct MountPoint {
|
||||
std::string path;
|
||||
};
|
||||
|
||||
struct DeviceDescriptor {
|
||||
std::string enumerator;
|
||||
std::string busType;
|
||||
std::string busVersion;
|
||||
bool busVersionNull;
|
||||
std::string device;
|
||||
std::string devicePath;
|
||||
bool devicePathNull;
|
||||
std::string raw;
|
||||
std::string description;
|
||||
std::string error;
|
||||
uint64_t size;
|
||||
uint32_t blockSize = 512;
|
||||
uint32_t logicalBlockSize = 512;
|
||||
std::vector<std::string> mountpoints;
|
||||
std::vector<std::string> mountpointLabels;
|
||||
bool isReadOnly; // Device is read-only
|
||||
bool isSystem; // Device is a system drive
|
||||
bool isVirtual; // Device is a virtual storage device
|
||||
bool isRemovable; // Device is removable from the running system
|
||||
bool isCard; // Device is an SD-card
|
||||
bool isSCSI; // Connected via the Small Computer System Interface (SCSI)
|
||||
bool isUSB; // Connected via Universal Serial Bus (USB)
|
||||
bool isUAS; // Connected via the USB Attached SCSI (UAS)
|
||||
bool isUASNull;
|
||||
};
|
||||
|
||||
std::vector<DeviceDescriptor> ListStorageDevices();
|
||||
v8::Local<v8::Object> PackDriveDescriptor(const DeviceDescriptor *instance);
|
||||
|
||||
} // namespace Drivelist
|
||||
|
||||
NAN_METHOD(list);
|
||||
|
||||
#endif // SRC_DRIVELIST_HPP_
|
28
dependencies/drivelist/src/linux/list.cpp
vendored
Normal file
28
dependencies/drivelist/src/linux/list.cpp
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <nan.h>
|
||||
#include "../drivelist.hpp"
|
||||
|
||||
namespace Drivelist {
|
||||
|
||||
// TODO(jhermsmeier): Implement
|
||||
std::vector<DeviceDescriptor> ListStorageDevices() {
|
||||
std::vector<DeviceDescriptor> drivelist;
|
||||
return drivelist;
|
||||
}
|
||||
|
||||
} // namespace Drivelist
|
680
dependencies/drivelist/src/windows/list.cpp
vendored
Normal file
680
dependencies/drivelist/src/windows/list.cpp
vendored
Normal file
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// See https://support.microsoft.com/en-us/kb/165721
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include <setupapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <nan.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "../drivelist.hpp"
|
||||
#include "list.hpp"
|
||||
|
||||
#include <devpkey.h>
|
||||
|
||||
// Maxnet edit
|
||||
#ifndef DEVPKEY_Device_EnumeratorName
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Device_EnumeratorName, 0xa45c254e,0xdf1c,0x4efd,0x80,0x20,0x67,0xd1,0x46,0xa8,0x50,0xe0, 24);
|
||||
#endif
|
||||
//
|
||||
|
||||
namespace Drivelist {
|
||||
|
||||
char* WCharToUtf8(const wchar_t* wstr) {
|
||||
char *str = NULL;
|
||||
size_t size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
|
||||
|
||||
if (size <= 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((str = reinterpret_cast<char*>(calloc(size, 1))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t utf8Size = WideCharToMultiByte(
|
||||
CP_UTF8, 0, wstr, -1, str, size, NULL, NULL);
|
||||
|
||||
if (utf8Size != size) {
|
||||
free(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char* GetEnumeratorName(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
|
||||
char buffer[MAX_PATH];
|
||||
|
||||
ZeroMemory(&buffer, sizeof(buffer));
|
||||
|
||||
BOOL hasEnumeratorName = SetupDiGetDeviceRegistryPropertyA(
|
||||
hDeviceInfo, &deviceInfoData, SPDRP_ENUMERATOR_NAME,
|
||||
NULL, (LPBYTE) buffer, sizeof(buffer), NULL);
|
||||
|
||||
/*if (!hasEnumeratorName)
|
||||
{
|
||||
hasEnumeratorName = SetupDiGetDevicePropertyW(hDeviceInfo, &deviceInfoData, &DEVPKEY_Device_EnumeratorName, NULL, (LPBYTE) buffer, sizeof(buffer), NULL, 0);
|
||||
}*/
|
||||
|
||||
return hasEnumeratorName ? buffer : NULL;
|
||||
}
|
||||
|
||||
std::string GetFriendlyName(HDEVINFO hDeviceInfo,
|
||||
SP_DEVINFO_DATA deviceInfoData) {
|
||||
wchar_t wbuffer[MAX_PATH];
|
||||
|
||||
ZeroMemory(&wbuffer, sizeof(wbuffer));
|
||||
|
||||
BOOL hasFriendlyName = SetupDiGetDeviceRegistryPropertyW(
|
||||
hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME,
|
||||
NULL, (PBYTE) wbuffer, sizeof(wbuffer), NULL);
|
||||
|
||||
return hasFriendlyName ? WCharToUtf8(wbuffer) : std::string("");
|
||||
}
|
||||
|
||||
bool IsSCSIDevice(std::string enumeratorName) {
|
||||
for (std::string driverName : GENERIC_STORAGE_DRIVERS) {
|
||||
if (enumeratorName == driverName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsUSBDevice(std::string enumeratorName) {
|
||||
for (std::string driverName : USB_STORAGE_DRIVERS) {
|
||||
if (enumeratorName == driverName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsRemovableDevice(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
|
||||
DWORD result = 0;
|
||||
|
||||
BOOL hasRemovalPolicy = SetupDiGetDeviceRegistryProperty(
|
||||
hDeviceInfo, &deviceInfoData, SPDRP_REMOVAL_POLICY,
|
||||
NULL, (PBYTE) &result, sizeof(result), NULL);
|
||||
|
||||
switch (result) {
|
||||
case CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL:
|
||||
case CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVirtualHardDrive(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
|
||||
char buffer[MAX_PATH];
|
||||
|
||||
ZeroMemory(&buffer, sizeof(buffer));
|
||||
|
||||
BOOL hasHardwareId = SetupDiGetDeviceRegistryPropertyA(
|
||||
hDeviceInfo, &deviceInfoData, SPDRP_HARDWAREID,
|
||||
NULL, (LPBYTE) buffer, sizeof(buffer), NULL);
|
||||
|
||||
if (!hasHardwareId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// printf("SPDRP_HARDWAREID: %s\n", buffer);
|
||||
|
||||
std::string hardwareId(buffer);
|
||||
|
||||
for (std::string vhdHardwareId : VHD_HARDWARE_IDS) {
|
||||
if (hardwareId.find(vhdHardwareId, 0) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSystemDevice(HDEVINFO hDeviceInfo, DeviceDescriptor *device) {
|
||||
PWSTR folderPath = NULL;
|
||||
BOOL isSystem = false;
|
||||
|
||||
for (const GUID folderId : KNOWN_FOLDER_IDS) {
|
||||
HRESULT result = SHGetKnownFolderPath(
|
||||
folderId, 0, NULL, &folderPath);
|
||||
|
||||
if (result == S_OK) {
|
||||
std::string systemPath = WCharToUtf8(folderPath);
|
||||
// printf("systemPath %s\n", systemPath.c_str());
|
||||
for (std::string mountpoint : device->mountpoints) {
|
||||
// printf("%s find %s\n", systemPath.c_str(), mountpoint.c_str());
|
||||
if (systemPath.find(mountpoint, 0) != std::string::npos) {
|
||||
isSystem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CoTaskMemFree(folderPath);
|
||||
}
|
||||
|
||||
return isSystem;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetAvailableVolumes() {
|
||||
DWORD logicalDrivesMask = GetLogicalDrives();
|
||||
std::vector<std::string> logicalDrives;
|
||||
|
||||
if (logicalDrivesMask == 0) {
|
||||
return logicalDrives;
|
||||
}
|
||||
|
||||
char currentDriveLetter = 'A';
|
||||
|
||||
while (logicalDrivesMask) {
|
||||
if (logicalDrivesMask & 1) {
|
||||
logicalDrives.push_back(std::string(1, currentDriveLetter));
|
||||
}
|
||||
currentDriveLetter++;
|
||||
logicalDrivesMask >>= 1;
|
||||
}
|
||||
|
||||
return logicalDrives;
|
||||
}
|
||||
|
||||
int32_t GetDeviceNumber(HANDLE hDevice) {
|
||||
BOOL result;
|
||||
DWORD size;
|
||||
DWORD errorCode = 0;
|
||||
int32_t diskNumber = -1;
|
||||
|
||||
STORAGE_DEVICE_NUMBER deviceNumber;
|
||||
VOLUME_DISK_EXTENTS diskExtents;
|
||||
|
||||
// Some devices will have the diskNumber exposed through their disk extents,
|
||||
// while most of them will only have one accessible through
|
||||
// `IOCTL_STORAGE_GET_DEVICE_NUMBER`, so we check this one first,
|
||||
// augmenting / overriding it with the latter
|
||||
result = DeviceIoControl(
|
||||
hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
|
||||
&diskExtents, sizeof(VOLUME_DISK_EXTENTS), &size, NULL);
|
||||
|
||||
if (result && diskExtents.NumberOfDiskExtents > 0) {
|
||||
// printf("[INFO] DiskNumber: %i\n", diskExtents.Extents[0].DiskNumber);
|
||||
|
||||
// NOTE: Always ignore RAIDs
|
||||
// TODO(jhermsmeier): Handle RAIDs properly
|
||||
if (diskExtents.NumberOfDiskExtents >= 2) {
|
||||
// printf("[INFO] Possible RAID: %i\n",
|
||||
// diskExtents.Extents[0].DiskNumber);
|
||||
return -1;
|
||||
}
|
||||
diskNumber = diskExtents.Extents[0].DiskNumber;
|
||||
} else {
|
||||
errorCode = GetLastError();
|
||||
// printf("[INFO] VOLUME_GET_VOLUME_DISK_EXTENTS: Error 0x%08lX\n",
|
||||
// errorCode);
|
||||
}
|
||||
|
||||
result = DeviceIoControl(
|
||||
hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
|
||||
&deviceNumber, sizeof(deviceNumber), &size, NULL);
|
||||
|
||||
if (result) {
|
||||
// printf("[INFO] DeviceNumber: %i\n", deviceNumber.DeviceNumber);
|
||||
diskNumber = deviceNumber.DeviceNumber;
|
||||
}
|
||||
|
||||
// errorCode = GetLastError();
|
||||
// printf("[INFO] STORAGE_GET_DEVICE_NUMBER: Error 0x%08lX\n", errorCode);
|
||||
|
||||
return diskNumber;
|
||||
}
|
||||
|
||||
void GetMountpoints(int32_t deviceNumber,
|
||||
std::vector<std::string> *mountpoints) {
|
||||
HANDLE hLogical = INVALID_HANDLE_VALUE;
|
||||
int32_t logicalVolumeDeviceNumber = -1;
|
||||
UINT driveType;
|
||||
|
||||
std::vector<std::string> logicalVolumes = GetAvailableVolumes();
|
||||
|
||||
for (std::string volumeName : logicalVolumes) {
|
||||
if (hLogical != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hLogical);
|
||||
hLogical = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
// NOTE: Ignore everything that's not a fixed or removable drive,
|
||||
// as device numbers are not unique across storage type drivers (!?),
|
||||
// and this would otherwise cause CD/DVD drive letters to be
|
||||
// attributed to blockdevices
|
||||
driveType = GetDriveTypeA((volumeName + ":\\").c_str());
|
||||
|
||||
// printf("[INFO] Checking %s:/\n", volumeName.c_str());
|
||||
|
||||
if ((driveType != DRIVE_FIXED) && (driveType != DRIVE_REMOVABLE)) {
|
||||
// printf("[INFO] Ignoring volume %s:/ - Not FIXED | REMOVABLE\n",
|
||||
// volumeName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
hLogical = CreateFileA(
|
||||
("\\\\.\\" + volumeName + ":").c_str(), 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hLogical == INVALID_HANDLE_VALUE) {
|
||||
// printf("[INFO] Couldn't open handle to logical volume %s\n",
|
||||
// volumeName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
logicalVolumeDeviceNumber = GetDeviceNumber(hLogical);
|
||||
|
||||
if (logicalVolumeDeviceNumber == -1) {
|
||||
// printf("[INFO] Couldn't get device number for logical volume %s\n",
|
||||
// volumeName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (logicalVolumeDeviceNumber == deviceNumber) {
|
||||
// printf("[INFO] Device number for logical volume %s is %i\n",
|
||||
// volumeName.c_str(), logicalVolumeDeviceNumber);
|
||||
mountpoints->push_back(volumeName + ":\\");
|
||||
}
|
||||
}
|
||||
|
||||
if (hLogical != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hLogical);
|
||||
hLogical = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetBusType(STORAGE_ADAPTER_DESCRIPTOR *adapterDescriptor) {
|
||||
switch (adapterDescriptor->BusType) {
|
||||
case STORAGE_BUS_TYPE::BusTypeUnknown: return "UNKNOWN";
|
||||
case STORAGE_BUS_TYPE::BusTypeScsi: return "SCSI";
|
||||
case STORAGE_BUS_TYPE::BusTypeAtapi: return "ATAPI";
|
||||
case STORAGE_BUS_TYPE::BusTypeAta: return "ATA";
|
||||
case STORAGE_BUS_TYPE::BusType1394: return "1394"; // IEEE 1394
|
||||
case STORAGE_BUS_TYPE::BusTypeSsa: return "SSA";
|
||||
case STORAGE_BUS_TYPE::BusTypeFibre: return "FIBRE";
|
||||
case STORAGE_BUS_TYPE::BusTypeUsb: return "USB";
|
||||
case STORAGE_BUS_TYPE::BusTypeRAID: return "RAID";
|
||||
case STORAGE_BUS_TYPE::BusTypeiScsi: return "iSCSI";
|
||||
case STORAGE_BUS_TYPE::BusTypeSas: return "SAS"; // Serial-Attached SCSI
|
||||
case STORAGE_BUS_TYPE::BusTypeSata: return "SATA";
|
||||
case STORAGE_BUS_TYPE::BusTypeSd: return "SD"; // Secure Digital (SD)
|
||||
case STORAGE_BUS_TYPE::BusTypeMmc: return "MMC"; // Multimedia card
|
||||
case STORAGE_BUS_TYPE::BusTypeVirtual: return "VIRTUAL";
|
||||
case STORAGE_BUS_TYPE::BusTypeFileBackedVirtual: return "FILEBACKEDVIRTUAL";
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
bool GetAdapterInfo(HANDLE hPhysical, DeviceDescriptor *device) {
|
||||
STORAGE_PROPERTY_QUERY query;
|
||||
STORAGE_ADAPTER_DESCRIPTOR adapterDescriptor;
|
||||
DWORD size = 0;
|
||||
|
||||
ZeroMemory(&query, sizeof(query));
|
||||
|
||||
query.QueryType = STORAGE_QUERY_TYPE::PropertyStandardQuery;
|
||||
query.PropertyId = STORAGE_PROPERTY_ID::StorageAdapterProperty;
|
||||
|
||||
BOOL hasAdapterInfo = DeviceIoControl(
|
||||
hPhysical, IOCTL_STORAGE_QUERY_PROPERTY,
|
||||
&query, sizeof(STORAGE_PROPERTY_QUERY),
|
||||
&adapterDescriptor, sizeof(STORAGE_ADAPTER_DESCRIPTOR), &size, NULL);
|
||||
|
||||
if (hasAdapterInfo) {
|
||||
device->busType = GetBusType(&adapterDescriptor);
|
||||
device->busVersion = std::to_string(adapterDescriptor.BusMajorVersion) +
|
||||
"." + std::to_string(adapterDescriptor.BusMinorVersion);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetDeviceBlockSize(HANDLE hPhysical, DeviceDescriptor *device) {
|
||||
STORAGE_PROPERTY_QUERY query;
|
||||
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignmentDescriptor;
|
||||
DWORD size = 0;
|
||||
|
||||
ZeroMemory(&query, sizeof(query));
|
||||
|
||||
query.QueryType = STORAGE_QUERY_TYPE::PropertyStandardQuery;
|
||||
query.PropertyId = STORAGE_PROPERTY_ID::StorageAccessAlignmentProperty;
|
||||
|
||||
BOOL hasAlignmentDescriptor = DeviceIoControl(
|
||||
hPhysical, IOCTL_STORAGE_QUERY_PROPERTY,
|
||||
&query, sizeof(STORAGE_PROPERTY_QUERY),
|
||||
&alignmentDescriptor, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR),
|
||||
&size, NULL);
|
||||
|
||||
if (hasAlignmentDescriptor) {
|
||||
device->blockSize = alignmentDescriptor.BytesPerPhysicalSector;
|
||||
device->logicalBlockSize = alignmentDescriptor.BytesPerLogicalSector;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetDeviceSize(HANDLE hPhysical, DeviceDescriptor *device) {
|
||||
DISK_GEOMETRY_EX diskGeometry;
|
||||
|
||||
// printf("[INFO] hasDiskGeometry\n");
|
||||
|
||||
BOOL hasDiskGeometry = false;
|
||||
DWORD size = 0;
|
||||
|
||||
hasDiskGeometry = DeviceIoControl(
|
||||
hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
|
||||
&diskGeometry, sizeof(DISK_GEOMETRY_EX), &size, NULL);
|
||||
|
||||
// printf("[INFO] hasDiskGeometry %i\n", hasDiskGeometry);
|
||||
|
||||
// NOTE: Another way to get the block size would be
|
||||
// `IOCTL_STORAGE_QUERY_PROPERTY` with `STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR`,
|
||||
// which can yield more (or possibly more accurate?) info,
|
||||
// but might not work with external HDDs/SSDs
|
||||
if (hasDiskGeometry) {
|
||||
device->size = diskGeometry.DiskSize.QuadPart;
|
||||
device->blockSize = diskGeometry.Geometry.BytesPerSector;
|
||||
}
|
||||
|
||||
return hasDiskGeometry;
|
||||
}
|
||||
|
||||
bool GetDetailData(DeviceDescriptor* device,
|
||||
HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
|
||||
DWORD index;
|
||||
DWORD size;
|
||||
DWORD errorCode = 0;
|
||||
bool result = true;
|
||||
|
||||
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
||||
HANDLE hPhysical = INVALID_HANDLE_VALUE;
|
||||
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA_W deviceDetailData(NULL);
|
||||
|
||||
for (index = 0; ; index++) {
|
||||
if (hDevice != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hDevice);
|
||||
hDevice = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (hPhysical != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hPhysical);
|
||||
hPhysical = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
// printf("[INFO] (%i) SetupDiEnumDeviceInterfaces - isDisk\n", index);
|
||||
|
||||
BOOL isDisk = SetupDiEnumDeviceInterfaces(
|
||||
hDeviceInfo, &deviceInfoData, &GUID_DEVICE_INTERFACE_DISK,
|
||||
index, &deviceInterfaceData);
|
||||
|
||||
if (!isDisk) {
|
||||
errorCode = GetLastError();
|
||||
if (errorCode == ERROR_NO_MORE_ITEMS) {
|
||||
// printf("[INFO] (%i) EnumDeviceInterfaces: No more items 0x%08lX\n",
|
||||
// index, errorCode);
|
||||
result = index != 0;
|
||||
break;
|
||||
} else {
|
||||
device->error = "SetupDiEnumDeviceInterfaces: Error " +
|
||||
std::to_string(errorCode);
|
||||
}
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL hasDeviceInterfaceData = SetupDiGetDeviceInterfaceDetailW(
|
||||
hDeviceInfo, &deviceInterfaceData, NULL, 0, &size, NULL);
|
||||
|
||||
if (!hasDeviceInterfaceData) {
|
||||
errorCode = GetLastError();
|
||||
if (errorCode == ERROR_INSUFFICIENT_BUFFER) {
|
||||
deviceDetailData = static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>
|
||||
(malloc(size));
|
||||
if (deviceDetailData == NULL) {
|
||||
device->error = "SetupDiGetDeviceInterfaceDetailW: "
|
||||
"Unable to allocate SP_DEVICE_INTERFACE_DETAIL_DATA_W"
|
||||
"(" + std::to_string(size) + "); "
|
||||
"Error " + std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
// printf("Allocated SP_DEVICE_INTERFACE_DETAIL_DATA_W\n");
|
||||
} else {
|
||||
device->error = "SetupDiGetDeviceInterfaceDetailW: "
|
||||
"Couldn't get detail data; Error " + std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("[INFO] (%i) Getting SP_DEVICE_INTERFACE_DETAIL_DATA_W\n", index);
|
||||
|
||||
deviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||||
|
||||
BOOL hasDeviceDetail = SetupDiGetDeviceInterfaceDetailW(
|
||||
hDeviceInfo, &deviceInterfaceData, deviceDetailData, size, &size, NULL);
|
||||
|
||||
if (!hasDeviceDetail) {
|
||||
errorCode = GetLastError();
|
||||
device->error = "Couldn't SetupDiGetDeviceInterfaceDetailW: Error " +
|
||||
std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// printf("[INFO] (%i) SetupDiGetDeviceInterfaceDetailW:\n %s\n",
|
||||
// index, WCharToUtf8(deviceDetailData->DevicePath));
|
||||
|
||||
// Passing zero to `CreateFile()` doesn't require permissions to
|
||||
// open the device handle, but only lets you acquire device metadata,
|
||||
// which is all we want
|
||||
hDevice = CreateFileW(
|
||||
deviceDetailData->DevicePath, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hDevice == INVALID_HANDLE_VALUE) {
|
||||
errorCode = GetLastError();
|
||||
// printf("[ERROR] Couldn't open handle to device: Error %i", errorCode);
|
||||
device->error = "Couldn't open handle to device: Error " +
|
||||
std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int32_t deviceNumber = GetDeviceNumber(hDevice);
|
||||
|
||||
if (deviceNumber == -1) {
|
||||
device->error = "Couldn't get device number";
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
device->raw = "\\\\.\\PhysicalDrive" + std::to_string(deviceNumber);
|
||||
device->device = device->raw;
|
||||
|
||||
GetMountpoints(deviceNumber, &device->mountpoints);
|
||||
|
||||
// printf("[INFO] Opening handle to %s\n", device->raw.c_str());
|
||||
|
||||
hPhysical = CreateFileA(
|
||||
device->raw.c_str(), 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
// printf("[INFO] Opened handle to %s\n", device->raw.c_str());
|
||||
// printf("[INFO] Handle %i (INVALID %i)\n",
|
||||
// hPhysical, INVALID_HANDLE_VALUE);
|
||||
|
||||
if (hPhysical == INVALID_HANDLE_VALUE) {
|
||||
errorCode = GetLastError();
|
||||
// printf("[INFO] Couldn't open handle to %s: Error %i\n",
|
||||
// device->raw.c_str(), errorCode);
|
||||
device->error = "Couldn't open handle to physical device: Error " +
|
||||
std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// printf("[INFO] GetDeviceSize( %s )\n", device->raw.c_str());
|
||||
|
||||
if (!GetDeviceSize(hPhysical, device)) {
|
||||
errorCode = GetLastError();
|
||||
// printf("[ERROR] Couldn't get disk geometry: Error %i\n", errorCode);
|
||||
device->error = "Couldn't get disk geometry: Error " +
|
||||
std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// printf("[INFO] GetAdapterInfo( %s )\n", device->raw.c_str());
|
||||
|
||||
if (!GetAdapterInfo(hPhysical, device)) {
|
||||
errorCode = GetLastError();
|
||||
// printf("[ERROR] Couldn't get device adapter descriptor: Error %i\n",
|
||||
// errorCode);
|
||||
device->error = "Couldn't get device adapter descriptor: Error " +
|
||||
std::to_string(errorCode);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// printf("[INFO] GetDeviceBlockSize( %s )\n", device->raw.c_str());
|
||||
|
||||
// NOTE: No need to fail over this one,
|
||||
// as we can safely default to a 512B block size
|
||||
if (!GetDeviceBlockSize(hPhysical, device)) {
|
||||
errorCode = GetLastError();
|
||||
// printf("[INFO] Couldn't get block size: Error %u\n", errorCode);
|
||||
}
|
||||
|
||||
// printf("[INFO] isWritable( %s )\n", device->raw.c_str());
|
||||
|
||||
BOOL isWritable = DeviceIoControl(
|
||||
hPhysical, IOCTL_DISK_IS_WRITABLE, NULL, 0,
|
||||
NULL, 0, &size, NULL);
|
||||
|
||||
device->isReadOnly = !isWritable;
|
||||
} // end for (index = 0; ; index++)
|
||||
|
||||
if (hDevice != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hDevice);
|
||||
hDevice = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (hPhysical != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hPhysical);
|
||||
hPhysical = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
free(deviceDetailData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DeviceDescriptor> ListStorageDevices() {
|
||||
HDEVINFO hDeviceInfo = NULL;
|
||||
SP_DEVINFO_DATA deviceInfoData;
|
||||
std::vector<DeviceDescriptor> deviceList;
|
||||
|
||||
DWORD i;
|
||||
char *enumeratorName;
|
||||
DeviceDescriptor device;
|
||||
|
||||
hDeviceInfo = SetupDiGetClassDevsA(
|
||||
&GUID_DEVICE_INTERFACE_DISK, NULL, NULL,
|
||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
if (hDeviceInfo == INVALID_HANDLE_VALUE) {
|
||||
printf("[ERROR] Invalid DeviceInfo handle\n");
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
|
||||
for (i = 0; SetupDiEnumDeviceInfo(hDeviceInfo, i, &deviceInfoData); i++) {
|
||||
enumeratorName = GetEnumeratorName(hDeviceInfo, deviceInfoData);
|
||||
|
||||
printf("[INFO] Enumerating %s\n", enumeratorName);
|
||||
|
||||
// If it failed to get the SPDRP_ENUMERATOR_NAME, skip it
|
||||
//if (enumeratorName == NULL) {
|
||||
// continue;
|
||||
//}
|
||||
|
||||
device = DeviceDescriptor();
|
||||
|
||||
device.enumerator = enumeratorName ? std::string(enumeratorName) : "";
|
||||
device.description = GetFriendlyName(hDeviceInfo, deviceInfoData);
|
||||
device.isRemovable = IsRemovableDevice(hDeviceInfo, deviceInfoData);
|
||||
device.isVirtual = IsVirtualHardDrive(hDeviceInfo, deviceInfoData);
|
||||
device.isSCSI = enumeratorName ? IsSCSIDevice(enumeratorName) : false;
|
||||
device.isUSB = enumeratorName ? IsUSBDevice(enumeratorName) : false;
|
||||
device.isCard = device.enumerator == "SD";
|
||||
device.isSystem = !device.isRemovable &&
|
||||
(device.enumerator == "SCSI" || device.enumerator == "IDE");
|
||||
device.isUAS = device.isSCSI && device.isRemovable &&
|
||||
!device.isVirtual && !device.isCard;
|
||||
device.devicePathNull = true;
|
||||
|
||||
if (GetDetailData(&device, hDeviceInfo, deviceInfoData)) {
|
||||
device.isSystem = device.isSystem || IsSystemDevice(hDeviceInfo, &device);
|
||||
device.isCard = device.busType == "SD" || device.busType == "MMC";
|
||||
device.isUAS = device.enumerator == "SCSI" && device.busType == "USB";
|
||||
device.isVirtual = device.isVirtual ||
|
||||
device.busType == "VIRTUAL" ||
|
||||
device.busType == "FILEBACKEDVIRTUAL";
|
||||
} else if (device.error == "") {
|
||||
device.error = "Couldn't get detail data";
|
||||
}
|
||||
|
||||
deviceList.push_back(device);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDeviceInfo);
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
} // namespace Drivelist
|
108
dependencies/drivelist/src/windows/list.hpp
vendored
Normal file
108
dependencies/drivelist/src/windows/list.hpp
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2017 balena.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SRC_WINDOWS_LIST_HPP_
|
||||
#define SRC_WINDOWS_LIST_HPP_
|
||||
|
||||
#include <knownfolders.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
namespace Drivelist {
|
||||
|
||||
// The <ntddstor.h>, and <usbiodef.h> headers include the following
|
||||
// device interface GUIDs we're interested in;
|
||||
// @see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/install-reference
|
||||
// @see https://msdn.microsoft.com/en-us/library/windows/hardware/ff541389(v=vs.85).aspx
|
||||
// To avoid cluttering the global namespace,
|
||||
// we'll just define here what we need:
|
||||
//
|
||||
// - GUID_DEVINTERFACE_DISK { 53F56307-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_CDROM { 53F56308-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_USB_HUB { F18A0E88-C30C-11D0-8815-00A0C906BED8 }
|
||||
// - GUID_DEVINTERFACE_FLOPPY { 53F56311-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_WRITEONCEDISK { 53F5630C-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_TAPE { 53F5630B-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_USB_DEVICE { A5DCBF10-6530-11D2-901F-00C04FB951ED }
|
||||
// - GUID_DEVINTERFACE_VOLUME { 53F5630D-B6BF-11D0-94F2-00A0C91EFB8B }
|
||||
// - GUID_DEVINTERFACE_STORAGEPORT { 2ACCFE60-C130-11D2-B082-00A0C91EFB8B }
|
||||
//
|
||||
const GUID GUID_DEVICE_INTERFACE_DISK = {
|
||||
0x53F56307L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_CDROM = {
|
||||
0x53F56308L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_USB_HUB = {
|
||||
0xF18A0E88L, 0xC30C, 0x11D0, { 0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8 }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_FLOPPY = {
|
||||
0x53F56311L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_WRITEONCEDISK = {
|
||||
0x53F5630CL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_TAPE = {
|
||||
0x53F5630BL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_USB_DEVICE = {
|
||||
0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_VOLUME = {
|
||||
0x53F5630DL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const GUID GUID_DEVICE_INTERFACE_STORAGEPORT = {
|
||||
0x2ACCFE60L, 0xC130, 0x11D2, { 0xB0, 0x82, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
|
||||
};
|
||||
|
||||
const std::set<std::string> USB_STORAGE_DRIVERS {
|
||||
"USBSTOR", "UASPSTOR", "VUSBSTOR",
|
||||
"RTUSER", "CMIUCR", "EUCR",
|
||||
"ETRONSTOR", "ASUSSTPT"
|
||||
};
|
||||
|
||||
const std::set<std::string> GENERIC_STORAGE_DRIVERS {
|
||||
"SCSI", "SD", "PCISTOR",
|
||||
"RTSOR", "JMCR", "JMCF", "RIMMPTSK", "RIMSPTSK", "RIXDPTSK",
|
||||
"TI21SONY", "ESD7SK", "ESM7SK", "O2MD", "O2SD", "VIACR"
|
||||
};
|
||||
|
||||
// List of known virtual disk hardware IDs
|
||||
const std::set<std::string> VHD_HARDWARE_IDS {
|
||||
"Arsenal_________Virtual_",
|
||||
"KernSafeVirtual_________",
|
||||
"Msft____Virtual_Disk____",
|
||||
"VMware__VMware_Virtual_S"
|
||||
};
|
||||
|
||||
const GUID KNOWN_FOLDER_IDS[] {
|
||||
FOLDERID_Windows,
|
||||
FOLDERID_Profile,
|
||||
FOLDERID_ProgramFiles,
|
||||
FOLDERID_ProgramFilesX86
|
||||
};
|
||||
|
||||
} // namespace Drivelist
|
||||
|
||||
#endif // SRC_WINDOWS_LIST_HPP_
|
Loading…
Add table
Add a link
Reference in a new issue