From b26e577d5acc9983ba80cc6de8b4e4215cf964b4 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 8 Nov 2017 03:33:06 +0100 Subject: [PATCH 01/17] feat: Rewrite native bindings to use IOCTL This completely refactors the native bindings on Windows to avoid COM/WMI etc. and solely rely on DeviceIoControl. This also introduces some new properties to the device, and fixes a range of errors / bugs. Further changes / fixes: - attached virtual hard drives don't get labeled as system drives anymore - errors with one device don't stop enumeration of other devices (and don't cause errors to be thrown) - info about bus / protocol (SCSI, SATA, USB, UAS, etc.) - isReadonly is now only true if the device itself is readonly (i.e. the switch on the sd-card adapter is flicked), not when a fs or volume is mounted or marked as readonly - added SD-card detection - added detection of virtual storage devices - BitLocker volumes no longer cause trouble, and devices containing one will be enumerated as any other storage device Change-Type: major --- binding.gyp | 26 +- package.json | 7 +- src/code.cc | 42 -- src/code.h | 41 -- src/{drivelist.h => darwin/list.cpp} | 14 +- src/device-descriptor.cpp | 117 +++++ src/disk.h | 38 -- src/drivelist.cc | 152 ------ src/drivelist.cpp | 66 +++ src/drivelist.hpp | 59 +++ src/{log.cc => linux/list.cpp} | 16 +- src/log.h | 30 -- src/mountpoint.h | 34 -- src/scanner.h | 39 -- src/windows/com.cc | 129 ----- src/windows/com.h | 46 -- src/windows/disk.cc | 65 --- src/windows/disk.h | 38 -- src/windows/list.cpp | 687 +++++++++++++++++++++++++++ src/windows/scanner.cc | 296 ------------ src/windows/volume.cc | 166 ------- src/windows/volume.h | 53 --- src/windows/wmi.cc | 155 ------ src/windows/wmi.h | 51 -- 24 files changed, 968 insertions(+), 1399 deletions(-) delete mode 100644 src/code.cc delete mode 100644 src/code.h rename src/{drivelist.h => darwin/list.cpp} (72%) create mode 100644 src/device-descriptor.cpp delete mode 100644 src/disk.h delete mode 100644 src/drivelist.cc create mode 100644 src/drivelist.cpp create mode 100644 src/drivelist.hpp rename src/{log.cc => linux/list.cpp} (70%) delete mode 100644 src/log.h delete mode 100644 src/mountpoint.h delete mode 100644 src/scanner.h delete mode 100644 src/windows/com.cc delete mode 100644 src/windows/com.h delete mode 100644 src/windows/disk.cc delete mode 100644 src/windows/disk.h create mode 100644 src/windows/list.cpp delete mode 100644 src/windows/scanner.cc delete mode 100644 src/windows/volume.cc delete mode 100644 src/windows/volume.h delete mode 100644 src/windows/wmi.cc delete mode 100644 src/windows/wmi.h diff --git a/binding.gyp b/binding.gyp index 6351f37c..5db0bd92 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,21 +7,29 @@ "." ], "sources": [ - "src/code.cc", - "src/log.cc" + "src/drivelist.cpp", + "src/device-descriptor.cpp", ], "conditions": [ + [ 'OS=="mac"', { + "sources": [ + "src/darwin/list.cpp" + ], + "link_settings": { + "libraries": [] + } + }], [ 'OS=="win"', { "sources": [ - "src/drivelist.cc", - "src/windows/com.cc", - "src/windows/scanner.cc", - "src/windows/volume.cc", - "src/windows/disk.cc", - "src/windows/wmi.cc" + "src/windows/list.cpp" ], "libraries": [ - "-lwbemuuid.lib", + "-lsetupapi.lib" + ] + }], + [ 'OS=="linux"', { + "sources": [ + "src/linux/list.cpp" ] }] ] diff --git a/package.json b/package.json index 88ae695b..ce76f038 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,11 @@ "test": "tests" }, "scripts": { - "test": "npm run lint && mocha --recursive tests -R spec", - "compile-scripts": "node scripts/compile.js", + "test": "mocha --recursive tests -R spec", "lint": "eslint lib tests example && cpplint --recursive src", "readme": "jsdoc2md --template doc/README.hbs lib/drivelist.js > README.md", "configure": "node-gyp configure", - "build": "node-gyp build", + "build": "node-gyp rebuild", "rebuild": "node-gyp rebuild", "install": "prebuild-install || node-gyp rebuild", "prebuild-release": "prebuild --all --strip" @@ -54,4 +53,4 @@ "nan": "^2.8.0", "prebuild-install": "^2.4.1" } -} \ No newline at end of file +} diff --git a/src/code.cc b/src/code.cc deleted file mode 100644 index 57965420..00000000 --- a/src/code.cc +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/code.h" - -std::string drivelist::GetCodeString(const drivelist::Code &code) { - switch (code) { - case drivelist::Code::SUCCESS: - return "Success"; - case drivelist::Code::ERROR_ABORTED: - return "Aborted"; - case drivelist::Code::ERROR_PERMISSION: - return "Permission error"; - case drivelist::Code::ERROR_HANDLE: - return "Invalid handle"; - case drivelist::Code::ERROR_INVALID_ARGUMENT: - return "Invalid argument"; - case drivelist::Code::ERROR_NO_INTERFACE: - return "No such interface"; - case drivelist::Code::ERROR_NOT_IMPLEMENTED: - return "Not implemented"; - case drivelist::Code::ERROR_OUT_OF_MEMORY: - return "Out of memory"; - case drivelist::Code::ERROR_POINTER: - return "Pointer error"; - default: - return "An unknown error occurred"; - } -} diff --git a/src/code.h b/src/code.h deleted file mode 100644 index 8d01620a..00000000 --- a/src/code.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef SRC_CODE_H_ -#define SRC_CODE_H_ - -/* - * Copyright 2017 resin.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 - -namespace drivelist { - -enum class Code { - SUCCESS, - ERROR_GENERIC, - ERROR_ABORTED, - ERROR_PERMISSION, - ERROR_HANDLE, - ERROR_INVALID_ARGUMENT, - ERROR_NO_INTERFACE, - ERROR_NOT_IMPLEMENTED, - ERROR_OUT_OF_MEMORY, - ERROR_POINTER -}; - -std::string GetCodeString(const Code &code); - -} // namespace drivelist - -#endif // SRC_CODE_H_ diff --git a/src/drivelist.h b/src/darwin/list.cpp similarity index 72% rename from src/drivelist.h rename to src/darwin/list.cpp index 341459a8..fe5c442e 100644 --- a/src/drivelist.h +++ b/src/darwin/list.cpp @@ -14,11 +14,15 @@ * limitations under the License. */ -#ifndef SRC_DRIVELIST_H_ -#define SRC_DRIVELIST_H_ - #include +#include "../drivelist.hpp" + +namespace Drivelist { -NAN_METHOD(list); + // TODO(jhermsmeier): Implement + std::vector ListStorageDevices() { + std::vector drivelist; + return drivelist; + } -#endif // SRC_DRIVELIST_H_ +} // namespace Drivelist diff --git a/src/device-descriptor.cpp b/src/device-descriptor.cpp new file mode 100644 index 00000000..d04244b4 --- /dev/null +++ b/src/device-descriptor.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2017 resin.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 +#include "drivelist.hpp" + +using v8::String; +using v8::Number; +using v8::Boolean; +using Nan::New; + +namespace Drivelist { + +v8::Local PackDriveDescriptor(const DeviceDescriptor *instance) { + v8::Local object = Nan::New(); + + Nan::Set(object, + New("enumerator").ToLocalChecked(), + New(instance->enumerator).ToLocalChecked()); + + Nan::Set(object, + New("busType").ToLocalChecked(), + New(instance->busType).ToLocalChecked()); + + Nan::Set(object, + New("busVersion").ToLocalChecked(), + New(instance->busVersion).ToLocalChecked()); + + Nan::Set(object, + New("device").ToLocalChecked(), + New(instance->device).ToLocalChecked()); + + Nan::Set(object, + New("raw").ToLocalChecked(), + New(instance->raw).ToLocalChecked()); + + Nan::Set(object, + New("description").ToLocalChecked(), + New(instance->description).ToLocalChecked()); + + Nan::Set(object, + New("error").ToLocalChecked(), + New(instance->error).ToLocalChecked()); + + Nan::Set(object, + New("size").ToLocalChecked(), + New(static_cast(instance->size))); + + Nan::Set(object, + New("blockSize").ToLocalChecked(), + New(static_cast(instance->blockSize))); + + Nan::Set(object, + New("logicalBlockSize").ToLocalChecked(), + New(static_cast(instance->logicalBlockSize))); + + v8::Local mountpoints = Nan::New(); + + uint32_t index = 0; + for (std::string mountpoint : instance->mountpoints) { + Nan::Set(mountpoints, index, New(mountpoint).ToLocalChecked()); + index++; + } + + Nan::Set(object, + New("mountpoints").ToLocalChecked(), + mountpoints); + + Nan::Set(object, + New("isReadOnly").ToLocalChecked(), + New(instance->isReadOnly)); + + Nan::Set(object, + New("isSystem").ToLocalChecked(), + New(instance->isSystem)); + + Nan::Set(object, + New("isVirtual").ToLocalChecked(), + New(instance->isVirtual)); + + Nan::Set(object, + New("isRemovable").ToLocalChecked(), + New(instance->isRemovable)); + + Nan::Set(object, + New("isCard").ToLocalChecked(), + New(instance->isCard)); + + Nan::Set(object, + New("isSCSI").ToLocalChecked(), + New(instance->isSCSI)); + + Nan::Set(object, + New("isUSB").ToLocalChecked(), + New(instance->isUSB)); + + Nan::Set(object, + New("isUAS").ToLocalChecked(), + New(instance->isUAS)); + + return object; +} + +} // namespace Drivelist diff --git a/src/disk.h b/src/disk.h deleted file mode 100644 index 86a3746c..00000000 --- a/src/disk.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef SRC_DISK_H_ -#define SRC_DISK_H_ - -/* - * Copyright 2017 resin.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 -#include -#include -#include "src/mountpoint.h" - -namespace drivelist { - -struct disk_s { - std::string id; - std::string caption; - std::string displayName; - uint64_t size; - std::vector mountpoints; - bool removable; -}; - -} // namespace drivelist - -#endif // SRC_DISK_H_ diff --git a/src/drivelist.cc b/src/drivelist.cc deleted file mode 100644 index b568fe37..00000000 --- a/src/drivelist.cc +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/drivelist.h" -#include -#include -#include -#include -#include "src/code.h" -#include "src/disk.h" -#include "src/scanner.h" - -static v8::Local -PackMountpoint(const struct drivelist::mountpoint_s &mountpoint) { - v8::Local object = Nan::New(); - - Nan::Set(object, Nan::New("path").ToLocalChecked(), - Nan::New(mountpoint.path).ToLocalChecked()); - - return object; -} - -static v8::Local PackDisk(const struct drivelist::disk_s &disk) { - v8::Local object = Nan::New(); - - Nan::Set(object, Nan::New("device").ToLocalChecked(), - Nan::New(disk.id).ToLocalChecked()); - - Nan::Set(object, Nan::New("raw").ToLocalChecked(), - Nan::New(disk.id).ToLocalChecked()); - - Nan::Set(object, Nan::New("displayName").ToLocalChecked(), - Nan::New(disk.displayName).ToLocalChecked()); - - Nan::Set(object, Nan::New("description").ToLocalChecked(), - Nan::New(disk.caption).ToLocalChecked()); - - Nan::Set(object, Nan::New("size").ToLocalChecked(), - Nan::New(static_cast(disk.size))); - - bool isSystem = !disk.removable; - bool isProtected = false; - - v8::Local mountpoints = Nan::New(); - - uint32_t index = 0; - for (struct drivelist::mountpoint_s mountpoint : disk.mountpoints) { - if (mountpoint.system) - isSystem = true; - if (mountpoint.readonly) - isProtected = true; - - if (mountpoint.hasFilesystem) { - Nan::Set(mountpoints, index, PackMountpoint(mountpoint)); - index++; - } - } - - Nan::Set(object, Nan::New("system").ToLocalChecked(), - Nan::New(isSystem)); - - Nan::Set(object, Nan::New("protected").ToLocalChecked(), - Nan::New(isProtected)); - - Nan::Set(object, Nan::New("mountpoints").ToLocalChecked(), - mountpoints); - - return object; -} - -static v8::Local -PackDiskList(const std::vector &disks) { - v8::Local array = Nan::New(); - - for (uint32_t index = 0; index < disks.size(); index++) { - Nan::Set(array, index, PackDisk(disks[index])); - } - - return array; -} - -class DrivelistWorker : public Nan::AsyncWorker { - public: - explicit DrivelistWorker(Nan::Callback *callback) - : Nan::AsyncWorker(callback), scanner(), disks() {} - ~DrivelistWorker() {} - - void Execute() { - drivelist::Code code = this->scanner.Initialize(); - if (code != drivelist::Code::SUCCESS) { - const std::string message = "Couldn't initialize the scanner: " - + drivelist::GetCodeString(code); - this->SetErrorMessage(message.c_str()); - return; - } - - code = this->scanner.Scan(&this->disks); - if (code != drivelist::Code::SUCCESS) { - const std::string message = "Couldn't scan the drives: " - + drivelist::GetCodeString(code); - this->SetErrorMessage(message.c_str()); - return; - } - - code = this->scanner.Uninitialize(); - if (code != drivelist::Code::SUCCESS) { - const std::string message = "Couldn't uninitialize the scanner: " - + drivelist::GetCodeString(code); - this->SetErrorMessage(message.c_str()); - return; - } - } - - void HandleOKCallback() { - const unsigned argc = 2; - v8::Local argv[argc] = { Nan::Null(), PackDiskList(disks) }; - callback->Call(argc, argv); - } - - private: - drivelist::Scanner scanner; - std::vector disks; -}; - -NAN_METHOD(list) { - if (!info[0]->IsFunction()) { - return Nan::ThrowTypeError("Callback must be a function"); - } - - Nan::Callback *callback = new Nan::Callback(info[0].As()); - Nan::AsyncQueueWorker(new DrivelistWorker(callback)); - info.GetReturnValue().SetUndefined(); -} - -NAN_MODULE_INIT(DrivelistInit) { - NAN_EXPORT(target, list); -} - -NODE_MODULE(Drivelist, DrivelistInit) diff --git a/src/drivelist.cpp b/src/drivelist.cpp new file mode 100644 index 00000000..68e6d460 --- /dev/null +++ b/src/drivelist.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2017 resin.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 +#include +#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 drives = Nan::New(); + + 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 argv[] = { Nan::Null(), drives }; + callback->Call(2, argv); + } + + private: + std::vector 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()); + Nan::AsyncQueueWorker(new DriveListWorker(callback)); + + info.GetReturnValue().SetUndefined(); +} + +NAN_MODULE_INIT(InitAll) { + NAN_EXPORT(target, list); +} + +NODE_MODULE(DriveList, InitAll); diff --git a/src/drivelist.hpp b/src/drivelist.hpp new file mode 100644 index 00000000..3ed73b26 --- /dev/null +++ b/src/drivelist.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2017 resin.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 +#include +#include + +namespace Drivelist { + +struct MountPoint { + std::string path; +}; + +struct DeviceDescriptor { + std::string enumerator; + std::string busType; + std::string busVersion; + std::string device; + std::string raw; + std::string description; + std::string error; + uint64_t size; + uint32_t blockSize = 512; + uint32_t logicalBlockSize = 512; + std::vector mountpoints; + 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) +}; + +std::vector ListStorageDevices(); +v8::Local PackDriveDescriptor(const DeviceDescriptor *instance); + +} // namespace Drivelist + +NAN_METHOD(list); + +#endif // SRC_DRIVELIST_HPP_ diff --git a/src/log.cc b/src/linux/list.cpp similarity index 70% rename from src/log.cc rename to src/linux/list.cpp index 1645e59e..fe5c442e 100644 --- a/src/log.cc +++ b/src/linux/list.cpp @@ -14,11 +14,15 @@ * limitations under the License. */ -#include "src/log.h" +#include +#include "../drivelist.hpp" -void drivelist::Debug(const std::string &string) { - const char* debug = std::getenv("DRIVELIST_DEBUG"); - if (debug != NULL) { - std::cout << "[drivelist] " << string << std::endl; +namespace Drivelist { + + // TODO(jhermsmeier): Implement + std::vector ListStorageDevices() { + std::vector drivelist; + return drivelist; } -} + +} // namespace Drivelist diff --git a/src/log.h b/src/log.h deleted file mode 100644 index 920591bd..00000000 --- a/src/log.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 resin.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_LOG_H_ -#define SRC_LOG_H_ - -#include -#include - -namespace drivelist { - -void Debug(const std::string &string); - -} // namespace drivelist - - -#endif // SRC_LOG_H_ diff --git a/src/mountpoint.h b/src/mountpoint.h deleted file mode 100644 index 00a321a5..00000000 --- a/src/mountpoint.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef SRC_MOUNTPOINT_H_ -#define SRC_MOUNTPOINT_H_ - -/* - * Copyright 2017 resin.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 - -namespace drivelist { - -struct mountpoint_s { - std::string path; - std::string disk; - bool readonly; - bool system; - bool hasFilesystem; -}; - -} // namespace drivelist - -#endif // SRC_MOUNTPOINT_H_ diff --git a/src/scanner.h b/src/scanner.h deleted file mode 100644 index 605ab36b..00000000 --- a/src/scanner.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef SRC_SCANNER_H_ -#define SRC_SCANNER_H_ - -/* - * Copyright 2017 resin.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 -#include "src/code.h" -#include "src/disk.h" -#include "src/mountpoint.h" - -namespace drivelist { - -class Scanner { - public: - drivelist::Code Initialize(); - drivelist::Code Uninitialize(); - drivelist::Code Scan(std::vector *const output); - - private: - void *context; -}; - -} // namespace drivelist - -#endif // SRC_SCANNER_H_ diff --git a/src/windows/com.cc b/src/windows/com.cc deleted file mode 100644 index 24c614b2..00000000 --- a/src/windows/com.cc +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/windows/com.h" -#include "src/log.h" - -HRESULT drivelist::com::Initialize() { - HRESULT result; - - drivelist::Debug("Initializing COM"); - result = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (FAILED(result)) - return result; - - drivelist::Debug("Initializing COM security levels"); - result = CoInitializeSecurity( - NULL, // Security descriptor - -1, // COM authentication - NULL, // Authentication services - NULL, // Reserved - RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication - RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation - NULL, // Authentication info - EOAC_NONE, // Additional capabilities - NULL); // Reserved - - // This error can be thrown when COM was already initialized, - // which can be the case if the user runs various scans at - // the same time on the same process. - // TODO(jviotti): This workaround assumes that if RPC_E_TOO_LATE - // is returned, COM was already initialized for the current process, - // however the right solution is to get rid of COM altogether. - // See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms693736(v=vs.85).aspx - if (result == RPC_E_TOO_LATE) { - drivelist::Debug("COM is already initialized, continuing..."); - return S_OK; - } - - if (FAILED(result)) { - drivelist::com::Uninitialize(); - } - - return result; -} - -void drivelist::com::Uninitialize() { CoUninitialize(); } - -drivelist::com::Connection::Connection() { - this->locator = NULL; - this->proxy = NULL; -} - -drivelist::com::Connection::~Connection() { - if (this->proxy != NULL) - this->proxy->Release(); - if (this->locator != NULL) - this->locator->Release(); -} - -HRESULT drivelist::com::Connection::Connect(const LPCWSTR server) { - drivelist::Debug("Connecting to server"); - const HRESULT result = this->locator->ConnectServer( - _bstr_t(server), // Path to server - NULL, // User name. NULL = current user - NULL, // User password. NULL = current - 0, // Locale. NULL indicates current - NULL, // Security flags. - 0, // Authority (for example, Kerberos) - 0, // Context object - &this->proxy); // pointer to IWbemServices proxy - - if (FAILED(result)) { - return result; - } - - drivelist::Debug("Setting proxy security levels"); - return CoSetProxyBlanket( - this->proxy, // Indicates the proxy to set - RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx - RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx - NULL, // Server principal name - RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx - RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx - NULL, // client identity - EOAC_NONE); // proxy capabilities -} - -HRESULT drivelist::com::Connection::CreateInstance() { - drivelist::Debug("Creating connection instance"); - return CoCreateInstance( - CLSID_WbemLocator, - NULL, // Not being created as part of an aggregate - - // The COM instance context - // - // From https://msdn.microsoft.com/en-us/library/windows/desktop/ms693716(v=vs.85).aspx - // - // > The code that creates and manages objects of this class - // > is a DLL that runs in the same process as the caller of - // > the function specifying the class context. - CLSCTX_INPROC_SERVER, - - IID_IWbemLocator, - reinterpret_cast(&this->locator)); -} - -HRESULT -drivelist::com::Connection::ExecuteWQLQuery(const BSTR query, - IEnumWbemClassObject **enumerator) { - return this->proxy->ExecQuery( - bstr_t("WQL"), - query, - WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, - NULL, - enumerator); -} diff --git a/src/windows/com.h b/src/windows/com.h deleted file mode 100644 index d5ae929b..00000000 --- a/src/windows/com.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SRC_WINDOWS_COM_H_ -#define SRC_WINDOWS_COM_H_ - -/* - * Copyright 2017 resin.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. - */ - -#define _WIN32_DCOM -#include -#include - -namespace drivelist { -namespace com { - -HRESULT Initialize(); -void Uninitialize(); - -class Connection { - public: - Connection(); - ~Connection(); - - HRESULT Connect(const PCWSTR server); - HRESULT CreateInstance(); - HRESULT ExecuteWQLQuery(const BSTR query, IEnumWbemClassObject **enumerator); - private: - IWbemLocator *locator; - IWbemServices *proxy; -}; - -} // namespace com -} // namespace drivelist - -#endif // SRC_WINDOWS_COM_H_ diff --git a/src/windows/disk.cc b/src/windows/disk.cc deleted file mode 100644 index b7ba8562..00000000 --- a/src/windows/disk.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/windows/disk.h" -#include "src/log.h" - -HANDLE drivelist::disk::OpenHandle(const std::string &disk) { - drivelist::Debug("Opening handle on " + disk); - return CreateFile(disk.c_str(), 0, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, NULL); -} - -HRESULT -drivelist::disk::GetInformation(const std::string &disk, - struct disk_information_s * const information) { - HANDLE handle = drivelist::disk::OpenHandle(disk); - if (handle == INVALID_HANDLE_VALUE) - return HRESULT_FROM_WIN32(GetLastError()); - - DISK_GEOMETRY_EX geometry; - DWORD bytesReturned; - - drivelist::Debug("Getting drive geometry"); - BOOL success = DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, - NULL, 0, - &geometry, sizeof(geometry), - &bytesReturned, NULL); - CloseHandle(handle); - - if (!success) { - HRESULT result = HRESULT_FROM_WIN32(GetLastError()); - - // This error represents "the device is not ready", and will - // occur on SD Card readers that don't have an SD Card plugged in. - // The documentation explicitly states that the ERROR_NOT_READY - // constant is equal to this hexadecimal number, but it doesn't - // seem to be the case. - if (result == 0x80070015) { - drivelist::Debug("Ignoring, device not ready"); - information->removable = NULL; - information->size = NULL; - return S_OK; - } - - return result; - } - - information->removable = geometry.Geometry.MediaType == RemovableMedia; - information->size = geometry.DiskSize.QuadPart; - - return S_OK; -} diff --git a/src/windows/disk.h b/src/windows/disk.h deleted file mode 100644 index 6e34171a..00000000 --- a/src/windows/disk.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef SRC_WINDOWS_DISK_H_ -#define SRC_WINDOWS_DISK_H_ - -/* - * Copyright 2017 resin.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 -#include - -namespace drivelist { -namespace disk { - -struct disk_information_s { - LONGLONG size; - BOOL removable; -}; - -HANDLE OpenHandle(const std::string &disk); -HRESULT GetInformation(const std::string &disk, - struct disk_information_s * const information); - -} // namespace disk -} // namespace drivelist - -#endif // SRC_WINDOWS_DISK_H_ diff --git a/src/windows/list.cpp b/src/windows/list.cpp new file mode 100644 index 00000000..28da9d21 --- /dev/null +++ b/src/windows/list.cpp @@ -0,0 +1,687 @@ +/* + * Copyright 2017 resin.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../drivelist.hpp" + +namespace Drivelist { + +// The , and 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::vector USB_STORAGE_DRIVERS { + "USBSTOR", "UASPSTOR", "VUSBSTOR", + "RTUSER", "CMIUCR", "EUCR", + "ETRONSTOR", "ASUSSTPT" +}; + +const std::vector 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::vector VHD_HARDWARE_IDS { + "Arsenal_________Virtual_", + "KernSafeVirtual_________", + "Msft____Virtual_Disk____", + "VMware__VMware_Virtual_S" +}; + +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(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) { + DWORD size; + DWORD dataType; + char buffer[MAX_PATH]; + + ZeroMemory(&buffer, sizeof(buffer)); + + BOOL hasEnumeratorName = SetupDiGetDeviceRegistryPropertyA( + hDeviceInfo, &deviceInfoData, SPDRP_ENUMERATOR_NAME, + &dataType, (LPBYTE) buffer, sizeof(buffer), &size); + + return hasEnumeratorName ? buffer : NULL; +} + +std::string GetFriendlyName(HDEVINFO hDeviceInfo, + SP_DEVINFO_DATA deviceInfoData) { + DWORD size; + DWORD dataType; + wchar_t wbuffer[MAX_PATH]; + + ZeroMemory(&wbuffer, sizeof(wbuffer)); + + BOOL hasFriendlyName = SetupDiGetDeviceRegistryPropertyW( + hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME, + &dataType, (PBYTE) wbuffer, sizeof(wbuffer), &size); + + 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 size; + DWORD dataType; + DWORD result = 0; + + BOOL hasRemovalPolicy = SetupDiGetDeviceRegistryProperty( + hDeviceInfo, &deviceInfoData, SPDRP_REMOVAL_POLICY, + &dataType, (PBYTE) &result, sizeof(result), &size); + + 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) { + DWORD size; + DWORD dataType; + char buffer[MAX_PATH]; + + ZeroMemory(&buffer, sizeof(buffer)); + + BOOL hasHardwareId = SetupDiGetDeviceRegistryPropertyA( + hDeviceInfo, &deviceInfoData, SPDRP_HARDWAREID, + &dataType, (LPBYTE) buffer, sizeof(buffer), &size); + + 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, SP_DEVINFO_DATA deviceInfoData) { + PWSTR windowsPath = NULL; + HRESULT result = SHGetKnownFolderPath( + FOLDERID_Windows, 0, NULL, &windowsPath); + + if (result == S_OK) { + std::string systemPath = WCharToUtf8(windowsPath); + CoTaskMemFree(windowsPath); + printf("systemPath %s\n", systemPath.c_str()); + // TODO(jhermsmeier): Compare against mountpoints to actually determine this + return true; + } + + CoTaskMemFree(windowsPath); + return false; +} + +std::vector GetAvailableVolumes() { + DWORD logicalDrivesMask = GetLogicalDrives(); + std::vector 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 + 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; + } else { + errorCode = GetLastError(); + printf("[INFO] STORAGE_GET_DEVICE_NUMBER: Error 0x%08lX\n", errorCode); + } + + return diskNumber; +} + +void GetMountpoints(int32_t deviceNumber, + std::vector *mountpoints) { + HANDLE hLogical = INVALID_HANDLE_VALUE; + int32_t logicalVolumeDeviceNumber = -1; + UINT driveType; + + std::vector logicalVolumes = GetAvailableVolumes(); + + for (std::string volumeName : logicalVolumes) { + if (hLogical != INVALID_HANDLE_VALUE) { + CloseHandle(hLogical); + } + + // 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()); + + if ((driveType != DRIVE_FIXED) && (driveType != DRIVE_REMOVABLE)) { + 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) { + mountpoints->push_back(volumeName + ":\\"); + } + } + + if (hLogical != INVALID_HANDLE_VALUE) { + CloseHandle(hLogical); + } +} + +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) { + DWORD size = 0; + STORAGE_PROPERTY_QUERY query; + STORAGE_ADAPTER_DESCRIPTOR adapterDescriptor; + + 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) { + DWORD size = 0; + STORAGE_PROPERTY_QUERY query; + STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignmentDescriptor; + + 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; + DWORD size; + + BOOL hasDiskGeometry = DeviceIoControl( + hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, + &diskGeometry, sizeof(DISK_GEOMETRY_EX), &size, NULL); + + // 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; + + // 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 + DWORD handleOpenFlags = 0; + + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hPhysical = INVALID_HANDLE_VALUE; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA_W deviceDetailData; + + for (index = 0; ; index++) { + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + if (hPhysical != INVALID_HANDLE_VALUE) { + CloseHandle(hPhysical); + } + + deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + printf("[INFO] (%i) SetupDiEnumDeviceInterfaces\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 if (errorCode != ERROR_NO_MORE_ITEMS) { + device->error = "SetupDiEnumDeviceInterfaces: Error " + + std::to_string(errorCode); + } else { + printf("%s Device '%s', slot %i is not a disk\n", + device->enumerator.c_str(), device->description.c_str(), index); + device->error = "Device is not a disk"; + } + result = false; + break; + } + + BOOL hasDeviceInterfaceData = SetupDiGetDeviceInterfaceDetailW( + hDeviceInfo, &deviceInterfaceData, NULL, 0, &size, NULL); + + if (!hasDeviceInterfaceData) { + errorCode = GetLastError(); + if (errorCode == ERROR_INSUFFICIENT_BUFFER) { + deviceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W) calloc(1, size); + if (deviceDetailData == NULL) { + device->error = "GetDeviceInterfaceDetailW: " + "Unable to allocate SP_DEVICE_INTERFACE_DETAIL_DATA; " + "Error " + std::to_string(errorCode); + result = false; + break; + } + deviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + } else { + device->error = "GetDeviceInterfaceDetailW: " + "Couldn't get detail data; Error " + std::to_string(errorCode); + result = false; + break; + } + } + + 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)); + + device->device = std::string(WCharToUtf8(deviceDetailData->DevicePath)); + + hDevice = CreateFileW( + deviceDetailData->DevicePath, handleOpenFlags, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hDevice == INVALID_HANDLE_VALUE) { + errorCode = GetLastError(); + 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); + + GetMountpoints(deviceNumber, &device->mountpoints); + + hPhysical = CreateFileA( + device->raw.c_str(), handleOpenFlags, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hPhysical == INVALID_HANDLE_VALUE) { + errorCode = GetLastError(); + device->error = "Couldn't open handle to physical device: Error " + + std::to_string(errorCode); + result = false; + break; + } + + if (!GetDeviceSize(hPhysical, device)) { + errorCode = GetLastError(); + device->error = "Couldn't get disk geometry: Error " + + std::to_string(errorCode); + result = false; + break; + } + + if (!GetAdapterInfo(hPhysical, device)) { + errorCode = GetLastError(); + device->error = "Couldn't get device adapter descriptor: Error " + + std::to_string(errorCode); + result = false; + break; + } + + // 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); + } + + BOOL isWritable = DeviceIoControl( + hPhysical, IOCTL_DISK_IS_WRITABLE, NULL, 0, + NULL, 0, &size, NULL); + + device->isReadOnly = !isWritable; + } + + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + if (hPhysical != INVALID_HANDLE_VALUE) { + CloseHandle(hPhysical); + } + + free(deviceDetailData); + + return result; +} + +std::vector ListStorageDevices() { + HDEVINFO hDeviceInfo = NULL; + SP_DEVINFO_DATA deviceInfoData; + std::vector 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); + + // If it failed to get the SPDRP_ENUMERATOR_NAME, skip it + if (enumeratorName == NULL) { + continue; + } + + device = DeviceDescriptor(); + + device.enumerator = std::string(enumeratorName); + device.description = GetFriendlyName(hDeviceInfo, deviceInfoData); + device.isRemovable = IsRemovableDevice(hDeviceInfo, deviceInfoData); + device.isVirtual = IsVirtualHardDrive(hDeviceInfo, deviceInfoData); + device.isSCSI = IsSCSIDevice(enumeratorName); + device.isUSB = IsUSBDevice(enumeratorName); + device.isCard = device.enumerator == "SD"; + // device.isSystem = IsSystemDevice(hDeviceInfo, deviceInfoData); + device.isSystem = device.isSCSI && !device.isRemovable; + device.isUAS = device.isSCSI && device.isRemovable && + !device.isVirtual && !device.isCard; + + if (GetDetailData(&device, hDeviceInfo, deviceInfoData)) { + 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 diff --git a/src/windows/scanner.cc b/src/windows/scanner.cc deleted file mode 100644 index f89df17a..00000000 --- a/src/windows/scanner.cc +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/scanner.h" -#include -#include -#include -#include -#include -#include -#include "src/windows/com.h" -#include "src/windows/volume.h" -#include "src/windows/disk.h" -#include "src/windows/wmi.h" -#include "src/log.h" - -static drivelist::Code InterpretHRESULT(const HRESULT result) { - std::ostringstream debugString; - debugString << "Interpreting error 0x" << std::hex << result; - drivelist::Debug(debugString.str()); - - switch (result) { - case S_OK: - return drivelist::Code::SUCCESS; - case E_ABORT: - return drivelist::Code::ERROR_ABORTED; - case E_ACCESSDENIED: - return drivelist::Code::ERROR_PERMISSION; - case E_FAIL: - return drivelist::Code::ERROR_GENERIC; - case E_UNEXPECTED: - return drivelist::Code::ERROR_GENERIC; - case E_HANDLE: - return drivelist::Code::ERROR_HANDLE; - case E_INVALIDARG: - return drivelist::Code::ERROR_INVALID_ARGUMENT; - case E_NOINTERFACE: - return drivelist::Code::ERROR_NO_INTERFACE; - case E_NOTIMPL: - return drivelist::Code::ERROR_NOT_IMPLEMENTED; - case E_OUTOFMEMORY: - return drivelist::Code::ERROR_OUT_OF_MEMORY; - case E_POINTER: - return drivelist::Code::ERROR_POINTER; - } - - if (SUCCEEDED(result)) { - return drivelist::Code::SUCCESS; - } - - return drivelist::Code::ERROR_GENERIC; -} - -// See https://stackoverflow.com/q/6284524 -static std::string ConvertBSTRToString(const BSTR string) { - const UINT wideLength = ::SysStringLen(string); - const wchar_t *const wideString = reinterpret_cast(string); - const int length = ::WideCharToMultiByte(CP_ACP, 0, wideString, wideLength, - NULL, 0, NULL, NULL); - std::string result(length, '\0'); - ::WideCharToMultiByte(CP_ACP, 0, wideString, wideLength, &result[0], length, - NULL, NULL); - return result; -} - -drivelist::Code drivelist::Scanner::Initialize() { - drivelist::Debug("Initializing scanner"); - HRESULT result = drivelist::com::Initialize(); - if (FAILED(result)) - return InterpretHRESULT(result); - this->context = reinterpret_cast(new drivelist::com::Connection()); - result = drivelist::wmi::ConnectToLocalComputer( - (drivelist::com::Connection *)this->context); - return InterpretHRESULT(result); -} - -drivelist::Code drivelist::Scanner::Uninitialize() { - drivelist::Debug("Unitializing scanner"); - delete (drivelist::com::Connection *)this->context; - drivelist::com::Uninitialize(); - return drivelist::Code::SUCCESS; -} - -static drivelist::Code ScanDisks(drivelist::com::Connection *const connection, - std::vector *const output) { - drivelist::wmi::Query query(L"SELECT * FROM Win32_DiskDrive"); - drivelist::Debug("Getting list of drives from WMI"); - HRESULT result = query.Execute(connection); - if (FAILED(result)) - return InterpretHRESULT(result); - - BSTR string; - - while (query.HasResult()) { - drivelist::Debug("Getting id"); - result = query.GetPropertyString(L"DeviceID", &string); - if (FAILED(result)) - return InterpretHRESULT(result); - std::string id = ConvertBSTRToString(string); - drivelist::Debug("Processing " + id); - SysFreeString(string); - - drivelist::Debug("Getting caption"); - result = query.GetPropertyString(L"Caption", &string); - if (FAILED(result)) - return InterpretHRESULT(result); - std::string caption = ConvertBSTRToString(string); - SysFreeString(string); - - drivelist::Debug("Getting disk information"); - drivelist::disk::disk_information_s information; - result = drivelist::disk::GetInformation(id, &information); - if (FAILED(result)) - return InterpretHRESULT(result); - - // The size can be null in the case of internal SD Card readers, - // where they appear in the list of Win32_DiskDrive items, even - // though there is no card plugged in. - if (information.size == NULL) { - drivelist::Debug("No size, ignoring drive"); - result = query.SelectNext(); - if (FAILED(result)) - return InterpretHRESULT(result); - continue; - } - - drivelist::disk_s disk; - disk.id = id; - disk.caption = caption; - disk.size = information.size; - disk.removable = information.removable; - output->push_back(disk); - - result = query.SelectNext(); - if (FAILED(result)) - return InterpretHRESULT(result); - } - - return drivelist::Code::SUCCESS; -} - -static drivelist::Code -ScanMountpoints(std::vector *const output) { - wchar_t systemDriveLetter; - drivelist::Debug("Getting system volume drive letter"); - HRESULT result = drivelist::volume::GetSystemVolume(&systemDriveLetter); - if (FAILED(result)) - return InterpretHRESULT(result); - - std::vector volumes; - drivelist::Debug("Getting available volumes"); - result = drivelist::volume::GetAvailableVolumes(&volumes); - if (FAILED(result)) - return InterpretHRESULT(result); - - for (wchar_t volume : volumes) { - std::stringstream message; - message << "Found volume: "; - message << static_cast(volume); - drivelist::Debug(message.str()); - - // We only want to consider removable or local disks in this module - drivelist::Debug("Getting volume type"); - drivelist::volume::Type volumeType = drivelist::volume::GetType(volume); - if (volumeType != drivelist::volume::Type::REMOVABLE_DISK && - volumeType != drivelist::volume::Type::LOCAL_DISK) { - drivelist::Debug("Ignoring, drive type not removable nor local"); - continue; - } - - BOOL writable = TRUE; - BOOL hasFilesystem; - drivelist::Debug("Checking if volume has filesystem"); - result = drivelist::volume::HasFileSystem(volume, &hasFilesystem); - if (FAILED(result)) { - // See https://msdn.microsoft.com/en-us/library/windows/desktop/dd542648(v=vs.85).aspx - if (result == FVE_E_LOCKED_VOLUME) { - drivelist::Debug("Device is locked using BitLocker"); - hasFilesystem = FALSE; - // We set this drive as non-writable since if the - // drive is locked, we can't even write to it - writable = FALSE; - } else { - return InterpretHRESULT(result); - } - } - - ULONG number; - drivelist::Debug("Getting device number"); - result = drivelist::volume::GetDeviceNumber(volume, &number); - if (FAILED(result)) { - // This error can happen when attempting to get the device - // number of a virtual disk. - // There doesn't seem to be a constant that maps to this - // particular error result number. - // See: https://github.com/resin-io-modules/drivelist/issues/198 - if (result == 0x80070001) { - drivelist::Debug("Ignoring, drive has no device number"); - continue; - } - - return InterpretHRESULT(result); - } - - // Turns out a disk can be writable while its volumes can be read-only - // and viceversa, so we need to check the writable capabilities of - // both the disk and its volumes - if (writable) { - drivelist::Debug("Checking if disk is writable"); - result = drivelist::volume::IsDiskWritable(volume, &writable); - if (FAILED(result)) - return InterpretHRESULT(result); - if (!writable && hasFilesystem) { - drivelist::Debug("Checking if volume is writable"); - result = drivelist::volume::IsVolumeWritable(volume, &writable); - if (FAILED(result)) - return InterpretHRESULT(result); - } - } - - drivelist::mountpoint_s mountpoint; - mountpoint.path = std::string(1, static_cast(volume)) + ":"; - mountpoint.disk = "\\\\.\\PHYSICALDRIVE" + std::to_string(number); - mountpoint.readonly = !writable; - mountpoint.system = volume == systemDriveLetter; - mountpoint.hasFilesystem = hasFilesystem; - output->push_back(mountpoint); - } - - return drivelist::Code::SUCCESS; -} - -drivelist::Code -drivelist::Scanner::Scan(std::vector *const output) { - drivelist::Code code = drivelist::Code::SUCCESS; - drivelist::com::Connection *connection = - (drivelist::com::Connection *)(this->context); - - std::vector disks; - code = ScanDisks(connection, &disks); - if (code != drivelist::Code::SUCCESS) - return code; - - std::vector mountpoints; - code = ScanMountpoints(&mountpoints); - if (code != drivelist::Code::SUCCESS) - return code; - - std::unordered_map> - mountpointMap; - for (drivelist::mountpoint_s mountpoint : mountpoints) { - mountpointMap[mountpoint.disk].push_back(mountpoint); - } - - for (drivelist::disk_s disk : disks) { - disk.mountpoints = mountpointMap[disk.id]; - disk.displayName = disk.id; - - // For user friendly purposes, in Windows, the disk name is - // simply the list of drive letters, separated by commas. - std::string mountpointsDisplayName = std::accumulate( - disk.mountpoints.begin(), - disk.mountpoints.end(), - std::string(), [](std::string accumulator, - drivelist::mountpoint_s mountpoint) { - if (!mountpoint.hasFilesystem) - return accumulator; - - if (accumulator.empty()) - return mountpoint.path; - - return accumulator + ", " + mountpoint.path; - }); - - if (!mountpointsDisplayName.empty()) { - disk.displayName = mountpointsDisplayName; - } - - output->push_back(disk); - } - - return code; -} diff --git a/src/windows/volume.cc b/src/windows/volume.cc deleted file mode 100644 index b1f850ba..00000000 --- a/src/windows/volume.cc +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/windows/volume.h" - -static const size_t kVolumePathShortLength = 4; - -HRESULT drivelist::volume::GetSystemVolume(wchar_t *out) { - PWSTR windowsPath = NULL; - HRESULT result = SHGetKnownFolderPath(FOLDERID_Windows, 0, - NULL, &windowsPath); - if (FAILED(result)) - return result; - - *out = windowsPath[0]; - CoTaskMemFree(windowsPath); - return S_OK; -} - -HRESULT -drivelist::volume::GetAvailableVolumes(std::vector *const output) { - DWORD logicalDrivesMask = GetLogicalDrives(); - if (logicalDrivesMask == 0) - return E_FAIL; - - TCHAR currentDriveLetter = 'A'; - - while (logicalDrivesMask) { - if (logicalDrivesMask & 1) - output->push_back(currentDriveLetter); - currentDriveLetter++; - logicalDrivesMask >>= 1; - } - - return S_OK; -} - -HANDLE drivelist::volume::OpenHandle(const wchar_t letter, DWORD flags) { - TCHAR devicePath[8]; - sprintf_s(devicePath, "\\\\.\\%c:", letter); - return CreateFile(devicePath, flags, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, - NULL); -} - -HRESULT drivelist::volume::GetDeviceNumber(const wchar_t letter, ULONG *out) { - HANDLE handle = drivelist::volume::OpenHandle(letter, 0); - if (handle == INVALID_HANDLE_VALUE) - return HRESULT_FROM_WIN32(GetLastError()); - - STORAGE_DEVICE_NUMBER storageDeviceNumber; - DWORD bytesReturned; - - if (!DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, - &storageDeviceNumber, sizeof(storageDeviceNumber), - &bytesReturned, NULL)) { - CloseHandle(handle); - return HRESULT_FROM_WIN32(GetLastError()); - } - - *out = storageDeviceNumber.DeviceNumber; - CloseHandle(handle); - return S_OK; -} - -HRESULT drivelist::volume::IsDiskWritable(const wchar_t letter, BOOL *out) { - HANDLE handle = drivelist::volume::OpenHandle(letter, 0); - if (handle == INVALID_HANDLE_VALUE) - return HRESULT_FROM_WIN32(GetLastError()); - - DWORD bytesReturned; - - // The IOCTL_DISK_IS_WRITABLE returns - // FALSE if the device is read-only - // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365182(v=vs.85).aspx - const BOOL isDiskWritable = DeviceIoControl(handle, IOCTL_DISK_IS_WRITABLE, - NULL, 0, - NULL, 0, - &bytesReturned, NULL); - - *out = isDiskWritable; - CloseHandle(handle); - return S_OK; -} - -HRESULT drivelist::volume::IsVolumeWritable(const wchar_t letter, BOOL *out) { - DWORD filesystemFlags = 0; - TCHAR drivePath[kVolumePathShortLength]; - sprintf_s(drivePath, "%c:\\", letter); - - BOOL result = GetVolumeInformation(drivePath, NULL, - 0, NULL, - NULL, &filesystemFlags, NULL, 0); - - if (!result) - return HRESULT_FROM_WIN32(GetLastError()); - - if (filesystemFlags & FILE_READ_ONLY_VOLUME) { - *out = TRUE; - } else { - *out = FALSE; - } - - return S_OK; -} - -HRESULT drivelist::volume::HasFileSystem(const wchar_t letter, BOOL *out) { - TCHAR drivePath[kVolumePathShortLength]; - sprintf_s(drivePath, "%c:\\", letter); - BOOL result = GetVolumeInformation(drivePath, NULL, 0, NULL, - NULL, NULL, NULL, 0); - if (result) { - *out = TRUE; - return S_OK; - } - - DWORD error = GetLastError(); - - // ERROR_UNRECOGNIZED_VOLUME: when there is a partition table, but - // no actual recognized partition - // ERROR_INVALID_PARAMETER: when there is no partition table at all - // ERROR_NOT_READY: when accessing an empty SD Card reader - if (error == ERROR_UNRECOGNIZED_VOLUME || - error == ERROR_INVALID_PARAMETER || - error == ERROR_NOT_READY) { - *out = FALSE; - return S_OK; - } - - return HRESULT_FROM_WIN32(error); -} - -drivelist::volume::Type drivelist::volume::GetType(const wchar_t letter) { - TCHAR drivePath[kVolumePathShortLength]; - sprintf_s(drivePath, "%c:\\", letter); - UINT type = GetDriveType(drivePath); - - switch (type) { - case 1: - return drivelist::volume::Type::NO_ROOT_DIRECTORY; - case 2: - return drivelist::volume::Type::REMOVABLE_DISK; - case 3: - return drivelist::volume::Type::LOCAL_DISK; - case 4: - return drivelist::volume::Type::NETWORK_DRIVE; - case 5: - return drivelist::volume::Type::COMPACT_DISK; - case 6: - return drivelist::volume::Type::RAM_DISK; - default: - return drivelist::volume::Type::UNKNOWN; - } -} diff --git a/src/windows/volume.h b/src/windows/volume.h deleted file mode 100644 index 6775f394..00000000 --- a/src/windows/volume.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef SRC_WINDOWS_VOLUME_H_ -#define SRC_WINDOWS_VOLUME_H_ - -/* - * Copyright 2017 resin.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 -#include -#include -#include -#include "src/mountpoint.h" -#include "src/windows/com.h" -#include "src/windows/wmi.h" - -namespace drivelist { -namespace volume { - -enum class Type { - UNKNOWN, - NO_ROOT_DIRECTORY, - REMOVABLE_DISK, - LOCAL_DISK, - NETWORK_DRIVE, - COMPACT_DISK, - RAM_DISK -}; - -HRESULT GetSystemVolume(wchar_t *out); -HRESULT GetAvailableVolumes(std::vector *const output); -HANDLE OpenHandle(const wchar_t letter, DWORD flags); -HRESULT GetDeviceNumber(const wchar_t letter, ULONG *out); -HRESULT IsDiskWritable(const wchar_t letter, BOOL *out); -HRESULT IsVolumeWritable(const wchar_t letter, BOOL *out); -HRESULT HasFileSystem(const wchar_t letter, BOOL *out); -Type GetType(const wchar_t letter); - -} // namespace volume -} // namespace drivelist - -#endif // SRC_WINDOWS_VOLUME_H_ diff --git a/src/windows/wmi.cc b/src/windows/wmi.cc deleted file mode 100644 index eecba9b6..00000000 --- a/src/windows/wmi.cc +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017 resin.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 "src/windows/wmi.h" - -HRESULT -drivelist::wmi::ConnectToLocalComputer( - drivelist::com::Connection *const connection) { - const HRESULT result = connection->CreateInstance(); - if (FAILED(result)) - return result; - return connection->Connect(L"ROOT\\CIMV2"); -} - -drivelist::wmi::Query::Query(BSTR query) { - this->enumerator = NULL; - this->classObject = NULL; - this->code = 0; - this->query = query; -} - -void drivelist::wmi::Query::ClearResult() { - if (this->code > 0) { - this->classObject->Release(); - this->code = 0; - } -} - -drivelist::wmi::Query::~Query() { - drivelist::wmi::Query::ClearResult(); - this->enumerator->Release(); -} - -HRESULT -drivelist::wmi::Query::Execute(drivelist::com::Connection *const connection) { - const HRESULT result = - connection->ExecuteWQLQuery(this->query, &this->enumerator); - if (FAILED(result)) - return result; - - // Select the first one automatically - return SelectNext(); -} - -bool drivelist::wmi::Query::HasResult() { - return this->enumerator != NULL && this->code != 0; -} - -HRESULT drivelist::wmi::Query::SelectNext() { - // Ensure the previous query is freed before - // replacing it with the new one - ClearResult(); - - return this->enumerator->Next( - WBEM_INFINITE, // Timeout - 1, // Request only one object - &this->classObject, // The object to hold the returned object pointers - &this->code); // The number of returned objects -} - -HRESULT drivelist::wmi::Query::GetPropertyString(const LPCWSTR property, - BSTR *out) { - VARIANT variant; - VariantClear(&variant); - const HRESULT result = - this->classObject->Get(property, 0, &variant, NULL, NULL); - if (FAILED(result)) - return result; - - if (variant.bstrVal == NULL) { - *out = NULL; - } else { - *out = SysAllocString(variant.bstrVal); - if (*out == NULL) - return E_FAIL; - } - - return result; -} - -HRESULT -drivelist::wmi::Query::GetPropertyCharacter(const LPCWSTR property, - wchar_t *out) { - BSTR temp; - const HRESULT result = GetPropertyString(property, &temp); - if (FAILED(result)) - return result; - - if (temp == NULL) { - *out = NULL; - } else { - *out = temp[0]; - SysFreeString(temp); - } - - return result; -} - -static const int kVariantTypeLongInteger = 3; - -HRESULT -drivelist::wmi::Query::GetPropertyNumber(const LPCWSTR property, ULONG *out) { - VARIANT variant; - VariantClear(&variant); - const HRESULT result = - this->classObject->Get(property, 0, &variant, NULL, NULL); - if (FAILED(result)) - return result; - - // See https://msdn.microsoft.com/VBA/Language-Reference-VBA/articles/vartype-function - switch (variant.vt) { - case kVariantTypeLongInteger: - *out = variant.lVal; - break; - - // We can add more variant type checks as needed - default: - return E_FAIL; - } - - return result; -} - -HRESULT drivelist::wmi::Query::HasPropertyString(const LPCWSTR property, - BOOL *out) { - BSTR temp; - const HRESULT result = GetPropertyString(property, &temp); - if (FAILED(result)) - return result; - - if (temp == NULL) { - *out = FALSE; - } else { - *out = TRUE; - } - - // Lets free this right away. We only queried - // this to see if it was null or not. - SysFreeString(temp); - - return result; -} diff --git a/src/windows/wmi.h b/src/windows/wmi.h deleted file mode 100644 index 01295f2c..00000000 --- a/src/windows/wmi.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef SRC_WINDOWS_WMI_H_ -#define SRC_WINDOWS_WMI_H_ - -/* - * Copyright 2017 resin.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 -#include "src/windows/com.h" - -namespace drivelist { -namespace wmi { - -HRESULT ConnectToLocalComputer(drivelist::com::Connection * const connection); - -class Query { - public: - explicit Query(BSTR query); - ~Query(); - - HRESULT Execute(drivelist::com::Connection * const connection); - bool HasResult(); - HRESULT SelectNext(); - HRESULT GetPropertyString(const LPCWSTR property, BSTR *out); - HRESULT GetPropertyCharacter(const LPCWSTR property, wchar_t *out); - HRESULT GetPropertyNumber(const LPCWSTR property, ULONG *out); - HRESULT HasPropertyString(const LPCWSTR property, BOOL *out); - private: - void ClearResult(); - BSTR query; - IEnumWbemClassObject *enumerator; - IWbemClassObject *classObject; - ULONG code; -}; - -} // namespace wmi -} // namespace drivelist - -#endif // SRC_WINDOWS_WMI_H_ From 3ee23be0442431a301eb11473ae71c890c577187 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 3 Jan 2018 17:19:29 +0100 Subject: [PATCH 02/17] refactor(windows): Disable printf() stmts for now --- src/windows/list.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/windows/list.cpp b/src/windows/list.cpp index 28da9d21..caa72b25 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -233,7 +233,7 @@ bool IsSystemDevice(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { if (result == S_OK) { std::string systemPath = WCharToUtf8(windowsPath); CoTaskMemFree(windowsPath); - printf("systemPath %s\n", systemPath.c_str()); + // printf("systemPath %s\n", systemPath.c_str()); // TODO(jhermsmeier): Compare against mountpoints to actually determine this return true; } @@ -281,16 +281,17 @@ int32_t GetDeviceNumber(HANDLE hDevice) { &diskExtents, sizeof(VOLUME_DISK_EXTENTS), &size, NULL); if (result && diskExtents.NumberOfDiskExtents > 0) { - printf("[INFO] DiskNumber: %i\n", diskExtents.Extents[0].DiskNumber); + // 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); + // 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); + // printf("[INFO] VOLUME_GET_VOLUME_DISK_EXTENTS: Error 0x%08lX\n", errorCode); } result = DeviceIoControl( @@ -298,11 +299,11 @@ int32_t GetDeviceNumber(HANDLE hDevice) { &deviceNumber, sizeof(deviceNumber), &size, NULL); if (result) { - printf("[INFO] DeviceNumber: %i\n", deviceNumber.DeviceNumber); + // printf("[INFO] DeviceNumber: %i\n", deviceNumber.DeviceNumber); diskNumber = deviceNumber.DeviceNumber; } else { errorCode = GetLastError(); - printf("[INFO] STORAGE_GET_DEVICE_NUMBER: Error 0x%08lX\n", errorCode); + // printf("[INFO] STORAGE_GET_DEVICE_NUMBER: Error 0x%08lX\n", errorCode); } return diskNumber; @@ -337,16 +338,16 @@ void GetMountpoints(int32_t deviceNumber, 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()); + // 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()); + // printf("[INFO] Couldn't get device number for logical volume %s\n", + // volumeName.c_str()); continue; } @@ -480,7 +481,7 @@ bool GetDetailData(DeviceDescriptor* device, deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); - printf("[INFO] (%i) SetupDiEnumDeviceInterfaces\n", index); + // printf("[INFO] (%i) SetupDiEnumDeviceInterfaces\n", index); BOOL isDisk = SetupDiEnumDeviceInterfaces( hDeviceInfo, &deviceInfoData, &GUID_DEVICE_INTERFACE_DISK, index, &deviceInterfaceData); @@ -488,16 +489,16 @@ bool GetDetailData(DeviceDescriptor* device, if (!isDisk) { errorCode = GetLastError(); if (errorCode == ERROR_NO_MORE_ITEMS) { - printf("[INFO] (%i) EnumDeviceInterfaces: No more items 0x%08lX\n", - index, errorCode); + // printf("[INFO] (%i) EnumDeviceInterfaces: No more items 0x%08lX\n", + // index, errorCode); result = index != 0; break; } else if (errorCode != ERROR_NO_MORE_ITEMS) { device->error = "SetupDiEnumDeviceInterfaces: Error " + std::to_string(errorCode); } else { - printf("%s Device '%s', slot %i is not a disk\n", - device->enumerator.c_str(), device->description.c_str(), index); + // printf("%s Device '%s', slot %i is not a disk\n", + // device->enumerator.c_str(), device->description.c_str(), index); device->error = "Device is not a disk"; } result = false; @@ -538,8 +539,8 @@ bool GetDetailData(DeviceDescriptor* device, break; } - printf("[INFO] (%i) SetupDiGetDeviceInterfaceDetailW:\n %s\n", - index, WCharToUtf8(deviceDetailData->DevicePath)); + // printf("[INFO] (%i) SetupDiGetDeviceInterfaceDetailW:\n %s\n", + // index, WCharToUtf8(deviceDetailData->DevicePath)); device->device = std::string(WCharToUtf8(deviceDetailData->DevicePath)); @@ -601,7 +602,7 @@ bool GetDetailData(DeviceDescriptor* device, // 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] Couldn't get block size: Error %u\n", errorCode); } BOOL isWritable = DeviceIoControl( @@ -638,7 +639,7 @@ std::vector ListStorageDevices() { DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { - printf("[ERROR] Invalid DeviceInfo handle\n"); + // printf("[ERROR] Invalid DeviceInfo handle\n"); return deviceList; } From 0c9a6ef43bd5174f7e302d107eb860ada982ca09 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 3 Jan 2018 17:26:11 +0100 Subject: [PATCH 03/17] refactor(windows): Use std::set for storage driver IDs --- src/windows/list.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/windows/list.cpp b/src/windows/list.cpp index caa72b25..cb95eb49 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include "../drivelist.hpp" namespace Drivelist { @@ -85,20 +88,20 @@ const GUID GUID_DEVICE_INTERFACE_STORAGEPORT = { 0x2ACCFE60L, 0xC130, 0x11D2, { 0xB0, 0x82, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B } }; -const std::vector USB_STORAGE_DRIVERS { +const std::set USB_STORAGE_DRIVERS { "USBSTOR", "UASPSTOR", "VUSBSTOR", "RTUSER", "CMIUCR", "EUCR", "ETRONSTOR", "ASUSSTPT" }; -const std::vector GENERIC_STORAGE_DRIVERS { +const std::set 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::vector VHD_HARDWARE_IDS { +const std::set VHD_HARDWARE_IDS { "Arsenal_________Virtual_", "KernSafeVirtual_________", "Msft____Virtual_Disk____", @@ -285,13 +288,15 @@ int32_t GetDeviceNumber(HANDLE hDevice) { // NOTE: Always ignore RAIDs // TODO(jhermsmeier): Handle RAIDs properly if (diskExtents.NumberOfDiskExtents >= 2) { - // printf("[INFO] Possible RAID: %i\n", diskExtents.Extents[0].DiskNumber); + // 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); + // printf("[INFO] VOLUME_GET_VOLUME_DISK_EXTENTS: Error 0x%08lX\n", + // errorCode); } result = DeviceIoControl( From 7e7b20969da736f95c3175e04265402b5b67038d Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 3 Jan 2018 17:29:30 +0100 Subject: [PATCH 04/17] refactor(windows): Move consts to windows/list.hpp --- src/windows/list.cpp | 74 +------------------------------- src/windows/list.hpp | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 src/windows/list.hpp diff --git a/src/windows/list.cpp b/src/windows/list.cpp index cb95eb49..c8ddc0b3 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -32,82 +32,10 @@ #include #include #include "../drivelist.hpp" +#include "list.hpp" namespace Drivelist { -// The , and 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 USB_STORAGE_DRIVERS { - "USBSTOR", "UASPSTOR", "VUSBSTOR", - "RTUSER", "CMIUCR", "EUCR", - "ETRONSTOR", "ASUSSTPT" -}; - -const std::set 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 VHD_HARDWARE_IDS { - "Arsenal_________Virtual_", - "KernSafeVirtual_________", - "Msft____Virtual_Disk____", - "VMware__VMware_Virtual_S" -}; - char* WCharToUtf8(const wchar_t* wstr) { char *str = NULL; size_t size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); diff --git a/src/windows/list.hpp b/src/windows/list.hpp new file mode 100644 index 00000000..a57b3ecd --- /dev/null +++ b/src/windows/list.hpp @@ -0,0 +1,100 @@ +/* + * Copyright 2017 resin.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 +#include + +namespace Drivelist { + +// The , and 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 USB_STORAGE_DRIVERS { + "USBSTOR", "UASPSTOR", "VUSBSTOR", + "RTUSER", "CMIUCR", "EUCR", + "ETRONSTOR", "ASUSSTPT" +}; + +const std::set 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 VHD_HARDWARE_IDS { + "Arsenal_________Virtual_", + "KernSafeVirtual_________", + "Msft____Virtual_Disk____", + "VMware__VMware_Virtual_S" +}; + +} // namespace Drivelist + +#endif // SRC_WINDOWS_LIST_HPP_ From e250c4519fc2530450aace076b389c4b5c729c2b Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 3 Jan 2018 17:34:45 +0100 Subject: [PATCH 05/17] refactor(windows): Remove unused DWORD size & dataType --- src/windows/list.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/windows/list.cpp b/src/windows/list.cpp index c8ddc0b3..c1eddc9a 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -60,30 +60,26 @@ char* WCharToUtf8(const wchar_t* wstr) { } char* GetEnumeratorName(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { - DWORD size; - DWORD dataType; char buffer[MAX_PATH]; ZeroMemory(&buffer, sizeof(buffer)); BOOL hasEnumeratorName = SetupDiGetDeviceRegistryPropertyA( hDeviceInfo, &deviceInfoData, SPDRP_ENUMERATOR_NAME, - &dataType, (LPBYTE) buffer, sizeof(buffer), &size); + NULL, (LPBYTE) buffer, sizeof(buffer), NULL); return hasEnumeratorName ? buffer : NULL; } std::string GetFriendlyName(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { - DWORD size; - DWORD dataType; wchar_t wbuffer[MAX_PATH]; ZeroMemory(&wbuffer, sizeof(wbuffer)); BOOL hasFriendlyName = SetupDiGetDeviceRegistryPropertyW( hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME, - &dataType, (PBYTE) wbuffer, sizeof(wbuffer), &size); + NULL, (PBYTE) wbuffer, sizeof(wbuffer), NULL); return hasFriendlyName ? WCharToUtf8(wbuffer) : std::string(""); } @@ -109,13 +105,11 @@ bool IsUSBDevice(std::string enumeratorName) { } bool IsRemovableDevice(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { - DWORD size; - DWORD dataType; DWORD result = 0; BOOL hasRemovalPolicy = SetupDiGetDeviceRegistryProperty( hDeviceInfo, &deviceInfoData, SPDRP_REMOVAL_POLICY, - &dataType, (PBYTE) &result, sizeof(result), &size); + NULL, (PBYTE) &result, sizeof(result), NULL); switch (result) { case CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL: @@ -129,15 +123,13 @@ bool IsRemovableDevice(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { } bool IsVirtualHardDrive(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) { - DWORD size; - DWORD dataType; char buffer[MAX_PATH]; ZeroMemory(&buffer, sizeof(buffer)); BOOL hasHardwareId = SetupDiGetDeviceRegistryPropertyA( hDeviceInfo, &deviceInfoData, SPDRP_HARDWAREID, - &dataType, (LPBYTE) buffer, sizeof(buffer), &size); + NULL, (LPBYTE) buffer, sizeof(buffer), NULL); if (!hasHardwareId) { return false; @@ -317,7 +309,6 @@ std::string GetBusType(STORAGE_ADAPTER_DESCRIPTOR *adapterDescriptor) { } bool GetAdapterInfo(HANDLE hPhysical, DeviceDescriptor *device) { - DWORD size = 0; STORAGE_PROPERTY_QUERY query; STORAGE_ADAPTER_DESCRIPTOR adapterDescriptor; @@ -329,7 +320,7 @@ bool GetAdapterInfo(HANDLE hPhysical, DeviceDescriptor *device) { BOOL hasAdapterInfo = DeviceIoControl( hPhysical, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY), - &adapterDescriptor, sizeof(STORAGE_ADAPTER_DESCRIPTOR), &size, NULL); + &adapterDescriptor, sizeof(STORAGE_ADAPTER_DESCRIPTOR), NULL, NULL); if (hasAdapterInfo) { device->busType = GetBusType(&adapterDescriptor); @@ -342,7 +333,6 @@ bool GetAdapterInfo(HANDLE hPhysical, DeviceDescriptor *device) { } bool GetDeviceBlockSize(HANDLE hPhysical, DeviceDescriptor *device) { - DWORD size = 0; STORAGE_PROPERTY_QUERY query; STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignmentDescriptor; @@ -355,7 +345,7 @@ bool GetDeviceBlockSize(HANDLE hPhysical, DeviceDescriptor *device) { hPhysical, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY), &alignmentDescriptor, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), - &size, NULL); + NULL, NULL); if (hasAlignmentDescriptor) { device->blockSize = alignmentDescriptor.BytesPerPhysicalSector; @@ -368,11 +358,10 @@ bool GetDeviceBlockSize(HANDLE hPhysical, DeviceDescriptor *device) { bool GetDeviceSize(HANDLE hPhysical, DeviceDescriptor *device) { DISK_GEOMETRY_EX diskGeometry; - DWORD size; BOOL hasDiskGeometry = DeviceIoControl( hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, - &diskGeometry, sizeof(DISK_GEOMETRY_EX), &size, NULL); + &diskGeometry, sizeof(DISK_GEOMETRY_EX), NULL, NULL); // NOTE: Another way to get the block size would be // `IOCTL_STORAGE_QUERY_PROPERTY` with `STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR`, From d62ed39a771f4e93f0b13cf1b354cc8047672568 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Wed, 3 Jan 2018 17:51:37 +0100 Subject: [PATCH 06/17] refactor(windows): Remove unreachable code path --- src/windows/list.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/windows/list.cpp b/src/windows/list.cpp index c1eddc9a..bba45961 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -415,13 +415,9 @@ bool GetDetailData(DeviceDescriptor* device, // index, errorCode); result = index != 0; break; - } else if (errorCode != ERROR_NO_MORE_ITEMS) { + } else { device->error = "SetupDiEnumDeviceInterfaces: Error " + std::to_string(errorCode); - } else { - // printf("%s Device '%s', slot %i is not a disk\n", - // device->enumerator.c_str(), device->description.c_str(), index); - device->error = "Device is not a disk"; } result = false; break; From e3d03854386d2736c4f31fb659a438dac2d8a27e Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:27:18 +0100 Subject: [PATCH 07/17] fix(package): Re-add compile script & lint on test --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ce76f038..0d543b5f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test": "tests" }, "scripts": { - "test": "mocha --recursive tests -R spec", + "test": "npm run lint && mocha --recursive tests -R spec", + "compile-scripts": "node scripts/compile.js", "lint": "eslint lib tests example && cpplint --recursive src", "readme": "jsdoc2md --template doc/README.hbs lib/drivelist.js > README.md", "configure": "node-gyp configure", From 241ad09c584aaac61c330144bcc71f5413a30cab Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:27:49 +0100 Subject: [PATCH 08/17] feat(scripts): Update scripts to match device properties --- lib/scripts.json | 4 ++-- scripts/darwin.sh | 25 ++++++++++++++++++------- scripts/linux.sh | 25 ++++++++++++++++++------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/lib/scripts.json b/lib/scripts.json index 0beda6b2..cf699e26 100644 --- a/lib/scripts.json +++ b/lib/scripts.json @@ -1,11 +1,11 @@ { "darwin": { - "content": "#!/bin/bash\n\nset -u\nset -e\n\nget_key() {\n\n # This expression is intentionally unquoted so that\n # multiple lines get joined as a single one.\n # See https://github.com/resin-io-modules/drivelist/pull/129\n echo $(grep \"$1\" | awk -F \" +\" '{ print $3 }')\n\n}\n\nget_until_paren() {\n awk 'match($0, \"\\\\(|$\"){ print substr($0, 0, RSTART - 1) }'\n}\n\nDISKS=\"$(diskutil list | grep '^\\/' | get_until_paren)\"\nmount_output=\"$(mount)\"\n\nfor disk in $DISKS; do\n\n # Ignore drives that were just unplugged\n if ! diskinfo=\"$(diskutil info \"$disk\")\"; then\n continue\n fi\n\n device=\"$(echo \"$diskinfo\" | get_key \"Device Node\")\"\n\n # See http://superuser.com/q/631592\n raw_device=\"${device//disk/rdisk}\"\n\n description=\"$(echo \"$diskinfo\" | get_key \"Device / Media Name\")\"\n volume_name=\"$(echo \"$diskinfo\" | get_key \"Volume Name\")\"\n removable=\"$(echo \"$diskinfo\" | get_key \"Removable Media\")\"\n protected=\"$(echo \"$diskinfo\" | get_key \"Read-Only Media\")\"\n location=\"$(echo \"$diskinfo\" | get_key \"Device Location\")\"\n size=\"$(echo \"$diskinfo\" | sed 's/Disk Size/Total Size/g' | get_key \"Total Size\" | cut -d '(' -f 2 | cut -d ' ' -f 1)\"\n\n mountpoints=\"$(echo \"$mount_output\" | grep -E \"^${disk}(s[0-9]+)? on \" | cut -d ' ' -f 3)\"\n\n # Omit mounted DMG images\n if [[ \"$description\" == \"Disk Image\" ]]; then\n continue\n fi\n\n echo \"device: $device\"\n echo \"displayName: $device\"\n\n if [[ $volume_name =~ .*Not\\ applicable.* ]]; then\n echo \"description: \\\"$description\\\"\"\n else\n echo \"description: \\\"$volume_name - $description\\\"\"\n fi\n\n echo \"size: $size\"\n\n if [[ -z \"$mountpoints\" ]]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n echo \"raw: $raw_device\"\n\n if [[ \"$protected\" == \"Yes\" ]]; then\n echo \"protected: True\"\n else\n echo \"protected: False\"\n fi\n\n if [[ \"$device\" == \"/dev/disk0\" ]] || \\\n [[ ( \"$removable\" == \"No\" ) || (\"$removable\" == \"Fixed\") ]] || \\\n [[ ( \"$location\" =~ \"Internal\" ) && ( \"$removable\" != \"Yes\" ) && ( \"$removable\" != \"Removable\" ) ]] || \\\n echo \"$mountpoints\" | grep \"^/$\"\n then\n echo \"system: True\"\n else\n echo \"system: False\"\n fi\n\n echo \"\"\ndone\n", + "content": "#!/bin/bash\n\nset -u\nset -e\n\nget_key() {\n\n # This expression is intentionally unquoted so that\n # multiple lines get joined as a single one.\n # See https://github.com/resin-io-modules/drivelist/pull/129\n echo $(grep \"$1\" | awk -F \" +\" '{ print $3 }')\n\n}\n\nget_until_paren() {\n awk 'match($0, \"\\\\(|$\"){ print substr($0, 0, RSTART - 1) }'\n}\n\nDISKS=\"$(diskutil list | grep '^\\/' | get_until_paren)\"\nmount_output=\"$(mount)\"\n\nfor disk in $DISKS; do\n\n # Ignore drives that were just unplugged\n if ! diskinfo=\"$(diskutil info \"$disk\")\"; then\n continue\n fi\n\n device=\"$(echo \"$diskinfo\" | get_key \"Device Node\")\"\n\n # See http://superuser.com/q/631592\n raw_device=\"${device//disk/rdisk}\"\n\n description=\"$(echo \"$diskinfo\" | get_key \"Device / Media Name\")\"\n volume_name=\"$(echo \"$diskinfo\" | get_key \"Volume Name\")\"\n removable=\"$(echo \"$diskinfo\" | get_key \"Removable Media\")\"\n protected=\"$(echo \"$diskinfo\" | get_key \"Read-Only Media\")\"\n location=\"$(echo \"$diskinfo\" | get_key \"Device Location\")\"\n size=\"$(echo \"$diskinfo\" | sed 's/Disk Size/Total Size/g' | get_key \"Total Size\" | cut -d '(' -f 2 | cut -d ' ' -f 1)\"\n\n mountpoints=\"$(echo \"$mount_output\" | grep -E \"^${disk}(s[0-9]+)? on \" | cut -d ' ' -f 3)\"\n\n # Omit mounted DMG images\n if [[ \"$description\" == \"Disk Image\" ]]; then\n continue\n fi\n\n echo \"enumerator: diskutil\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $raw_device\"\n\n if [[ $volume_name =~ .*Not\\ applicable.* ]]; then\n echo \"description: \\\"$description\\\"\"\n else\n echo \"description: \\\"$volume_name - $description\\\"\"\n fi\n\n echo \"error: \\\"\\\"\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [[ -z \"$mountpoints\" ]]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"Yes\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n if [[ \"$device\" == \"/dev/disk0\" ]] || \\\n [[ ( \"$removable\" == \"No\" ) || (\"$removable\" == \"Fixed\") ]] || \\\n [[ ( \"$location\" =~ \"Internal\" ) && ( \"$removable\" != \"Yes\" ) && ( \"$removable\" != \"Removable\" ) ]] || \\\n echo \"$mountpoints\" | grep \"^/$\"\n then\n echo \"isSystem: True\"\n else\n echo \"isSystem: False\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n echo \"\"\ndone\n", "originalFilename": "darwin.sh", "type": "text" }, "linux": { - "content": "#!/bin/bash\n\nset -u\nset -e\n\nignore_first_line() {\n tail -n +2\n}\n\nget_uuids() {\n /sbin/blkid -s UUID -o value \"$1\"*\n}\n\nget_mountpoints() {\n grep \"^$1\" /proc/mounts | cut -d ' ' -f 2 | sed 's,\\\\040, ,g' | sed 's,\\\\011,\\t,g' | sed 's,\\\\012,\\\\n,g' | sed 's,\\\\134,\\\\\\\\,g'\n}\n\nDISKS=\"$(lsblk -d --output NAME | ignore_first_line)\"\n\nfor disk in $DISKS; do\n\n # Omit loop devices and CD/DVD drives\n if [[ $disk == loop* ]] || [[ $disk == sr* ]]; then\n continue\n fi\n\n device=\"/dev/$disk\"\n diskinfo=($(lsblk -b -d \"$device\" --output SIZE,RO,RM,MODEL | ignore_first_line))\n\n # Omit drives for which `lsblk` failed, which means they\n # were unplugged right after we got the list of all drives\n if [ -z \"${diskinfo-}\" ]; then\n continue\n fi\n\n size=${diskinfo[0]}\n protected=${diskinfo[1]}\n removable=${diskinfo[2]}\n description=${diskinfo[*]:3}\n mountpoints=\"$(get_mountpoints \"$device\")\"\n\n # If we couldn't get the mount points as `/dev/$disk`,\n # get the disk UUIDs, and check as `/dev/disk/by-uuid/$uuid`\n if [ -z \"$mountpoints\" ]; then\n for uuid in $(get_uuids \"$device\"); do\n mountpoints=\"$mountpoints$(get_mountpoints \"/dev/disk/by-uuid/$uuid\")\"\n done\n fi\n\n # If we couldn't get the description from `lsblk`, see if we can get it\n # from sysfs (e.g. PCI-connected SD cards that appear as `/dev/mmcblk0`)\n if [ -z \"$description\" ]; then\n subdevice=\"$(echo \"$device\" | cut -d '/' -f 3)\"\n if [ -f \"/sys/class/block/$subdevice/device/name\" ]; then\n description=\"$(cat \"/sys/class/block/$subdevice/device/name\")\"\n fi\n fi\n\n echo \"device: $device\"\n echo \"displayName: $device\"\n echo \"description: \\\"$description\\\"\"\n echo \"size: $size\"\n\n if [ -z \"$mountpoints\" ]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n echo \"raw: $device\"\n\n if [[ \"$protected\" == \"1\" ]]; then\n echo \"protected: True\"\n else\n echo \"protected: False\"\n fi\n\n eval \"$(udevadm info \\\n --query=property \\\n --export \\\n --export-prefix=UDEV_ \\\n --name=\"$disk\" \\\n | awk -F= '{gsub(\"\\\\.\",\"_\",$1); print $1 \"=\" $2}')\"\n\n set +u\n\n if [[ \"$removable\" == \"1\" ]] && \\\n [[ \"$UDEV_ID_DRIVE_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_DRIVE_MEDIA_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_BUS\" == \"usb\" ]]\n then\n echo \"system: False\"\n else\n echo \"system: True\"\n fi\n\n set -u\n\n # Unset UDEV variables used above to prevent them from\n # being interpreted as properties of another drive\n unset UDEV_ID_DRIVE_FLASH_SD\n unset UDEV_ID_DRIVE_MEDIA_FLASH_SD\n unset UDEV_ID_BUS\n\n echo \"\"\ndone\n", + "content": "#!/bin/bash\n\nset -u\nset -e\n\nignore_first_line() {\n tail -n +2\n}\n\nget_uuids() {\n /sbin/blkid -s UUID -o value \"$1\"*\n}\n\nget_mountpoints() {\n grep \"^$1\" /proc/mounts | cut -d ' ' -f 2 | sed 's,\\\\040, ,g' | sed 's,\\\\011,\\t,g' | sed 's,\\\\012,\\\\n,g' | sed 's,\\\\134,\\\\\\\\,g'\n}\n\nDISKS=\"$(lsblk -d --output NAME | ignore_first_line)\"\n\nfor disk in $DISKS; do\n\n # Omit loop devices and CD/DVD drives\n if [[ $disk == loop* ]] || [[ $disk == sr* ]]; then\n continue\n fi\n\n device=\"/dev/$disk\"\n diskinfo=($(lsblk -b -d \"$device\" --output SIZE,RO,RM,MODEL | ignore_first_line))\n\n # Omit drives for which `lsblk` failed, which means they\n # were unplugged right after we got the list of all drives\n if [ -z \"${diskinfo-}\" ]; then\n continue\n fi\n\n size=${diskinfo[0]}\n protected=${diskinfo[1]}\n removable=${diskinfo[2]}\n description=${diskinfo[*]:3}\n mountpoints=\"$(get_mountpoints \"$device\")\"\n\n # If we couldn't get the mount points as `/dev/$disk`,\n # get the disk UUIDs, and check as `/dev/disk/by-uuid/$uuid`\n if [ -z \"$mountpoints\" ]; then\n for uuid in $(get_uuids \"$device\"); do\n mountpoints=\"$mountpoints$(get_mountpoints \"/dev/disk/by-uuid/$uuid\")\"\n done\n fi\n\n # If we couldn't get the description from `lsblk`, see if we can get it\n # from sysfs (e.g. PCI-connected SD cards that appear as `/dev/mmcblk0`)\n if [ -z \"$description\" ]; then\n subdevice=\"$(echo \"$device\" | cut -d '/' -f 3)\"\n if [ -f \"/sys/class/block/$subdevice/device/name\" ]; then\n description=\"$(cat \"/sys/class/block/$subdevice/device/name\")\"\n fi\n fi\n\n echo \"enumerator: lsblk\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $device\"\n echo \"description: \\\"$description\\\"\"\n echo \"error: \\\"\\\"\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [ -z \"$mountpoints\" ]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"1\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n eval \"$(udevadm info \\\n --query=property \\\n --export \\\n --export-prefix=UDEV_ \\\n --name=\"$disk\" \\\n | awk -F= '{gsub(\"\\\\.\",\"_\",$1); print $1 \"=\" $2}')\"\n\n set +u\n\n if [[ \"$removable\" == \"1\" ]] && \\\n [[ \"$UDEV_ID_DRIVE_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_DRIVE_MEDIA_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_BUS\" == \"usb\" ]]\n then\n echo \"isSystem: False\"\n else\n echo \"isSystem: True\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n set -u\n\n # Unset UDEV variables used above to prevent them from\n # being interpreted as properties of another drive\n unset UDEV_ID_DRIVE_FLASH_SD\n unset UDEV_ID_DRIVE_MEDIA_FLASH_SD\n unset UDEV_ID_BUS\n\n echo \"\"\ndone\n", "originalFilename": "linux.sh", "type": "text" } diff --git a/scripts/darwin.sh b/scripts/darwin.sh index fcbc93a3..187f1042 100755 --- a/scripts/darwin.sh +++ b/scripts/darwin.sh @@ -45,8 +45,11 @@ for disk in $DISKS; do continue fi + echo "enumerator: diskutil" + echo "busType: UNKNOWN" + echo "busVersion: \"0.0\"" echo "device: $device" - echo "displayName: $device" + echo "raw: $raw_device" if [[ $volume_name =~ .*Not\ applicable.* ]]; then echo "description: \"$description\"" @@ -54,7 +57,10 @@ for disk in $DISKS; do echo "description: \"$volume_name - $description\"" fi + echo "error: \"\"" echo "size: $size" + echo "blockSize: null" + echo "logicalBlockSize: null" if [[ -z "$mountpoints" ]]; then echo "mountpoints: []" @@ -65,12 +71,10 @@ for disk in $DISKS; do done fi - echo "raw: $raw_device" - if [[ "$protected" == "Yes" ]]; then - echo "protected: True" + echo "isReadOnly: True" else - echo "protected: False" + echo "isReadOnly: False" fi if [[ "$device" == "/dev/disk0" ]] || \ @@ -78,10 +82,17 @@ for disk in $DISKS; do [[ ( "$location" =~ "Internal" ) && ( "$removable" != "Yes" ) && ( "$removable" != "Removable" ) ]] || \ echo "$mountpoints" | grep "^/$" then - echo "system: True" + echo "isSystem: True" else - echo "system: False" + echo "isSystem: False" fi + echo "isVirtual: null" + echo "isRemovable: null" + echo "isCard: null" + echo "isSCSI: null" + echo "isUSB: null" + echo "isUAS: null" + echo "" done diff --git a/scripts/linux.sh b/scripts/linux.sh index f95dc0f1..f2f3c83f 100755 --- a/scripts/linux.sh +++ b/scripts/linux.sh @@ -56,10 +56,16 @@ for disk in $DISKS; do fi fi + echo "enumerator: lsblk" + echo "busType: UNKNOWN" + echo "busVersion: \"0.0\"" echo "device: $device" - echo "displayName: $device" + echo "raw: $device" echo "description: \"$description\"" + echo "error: \"\"" echo "size: $size" + echo "blockSize: null" + echo "logicalBlockSize: null" if [ -z "$mountpoints" ]; then echo "mountpoints: []" @@ -70,12 +76,10 @@ for disk in $DISKS; do done fi - echo "raw: $device" - if [[ "$protected" == "1" ]]; then - echo "protected: True" + echo "isReadOnly: True" else - echo "protected: False" + echo "isReadOnly: False" fi eval "$(udevadm info \ @@ -92,11 +96,18 @@ for disk in $DISKS; do [[ "$UDEV_ID_DRIVE_MEDIA_FLASH_SD" == "1" ]] || \ [[ "$UDEV_ID_BUS" == "usb" ]] then - echo "system: False" + echo "isSystem: False" else - echo "system: True" + echo "isSystem: True" fi + echo "isVirtual: null" + echo "isRemovable: null" + echo "isCard: null" + echo "isSCSI: null" + echo "isUSB: null" + echo "isUAS: null" + set -u # Unset UDEV variables used above to prevent them from From cc394fba01850fb59684747de0e949c8309403bc Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:32:12 +0100 Subject: [PATCH 09/17] refactor(windows): Set error to `null` when empty --- src/device-descriptor.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/device-descriptor.cpp b/src/device-descriptor.cpp index d04244b4..198d260b 100644 --- a/src/device-descriptor.cpp +++ b/src/device-descriptor.cpp @@ -51,9 +51,15 @@ v8::Local PackDriveDescriptor(const DeviceDescriptor *instance) { New("description").ToLocalChecked(), New(instance->description).ToLocalChecked()); - Nan::Set(object, - New("error").ToLocalChecked(), - New(instance->error).ToLocalChecked()); + if (instance->error != "") { + Nan::Set(object, + New("error").ToLocalChecked(), + New(instance->error).ToLocalChecked()); + } else { + Nan::Set(object, + New("error").ToLocalChecked(), + Nan::Null()); + } Nan::Set(object, New("size").ToLocalChecked(), From 4dd5795a5c43253432ec145b79ca2a47a74521ee Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:37:06 +0100 Subject: [PATCH 10/17] fix(windows): Encaspulate mountpoint in object with path --- src/device-descriptor.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/device-descriptor.cpp b/src/device-descriptor.cpp index 198d260b..2daae090 100644 --- a/src/device-descriptor.cpp +++ b/src/device-descriptor.cpp @@ -76,8 +76,12 @@ v8::Local PackDriveDescriptor(const DeviceDescriptor *instance) { v8::Local mountpoints = Nan::New(); uint32_t index = 0; - for (std::string mountpoint : instance->mountpoints) { - Nan::Set(mountpoints, index, New(mountpoint).ToLocalChecked()); + for (std::string mountpointPath : instance->mountpoints) { + v8::Local mountpoint = Nan::New(); + Nan::Set(mountpoint, + New("path").ToLocalChecked(), + New(mountpointPath).ToLocalChecked()); + Nan::Set(mountpoints, index, mountpoint); index++; } From 7f52cac13a0a1cce9ec62f93ad295bc4e41918d9 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:46:02 +0100 Subject: [PATCH 11/17] test(darwin): Update Mac OS tests --- lib/scripts.json | 4 ++-- scripts/darwin.sh | 2 +- scripts/linux.sh | 2 +- tests/drivelist.spec.js | 47 +++++++++++++++++++++++++++++++++++------ 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/scripts.json b/lib/scripts.json index cf699e26..d6f3f751 100644 --- a/lib/scripts.json +++ b/lib/scripts.json @@ -1,11 +1,11 @@ { "darwin": { - "content": "#!/bin/bash\n\nset -u\nset -e\n\nget_key() {\n\n # This expression is intentionally unquoted so that\n # multiple lines get joined as a single one.\n # See https://github.com/resin-io-modules/drivelist/pull/129\n echo $(grep \"$1\" | awk -F \" +\" '{ print $3 }')\n\n}\n\nget_until_paren() {\n awk 'match($0, \"\\\\(|$\"){ print substr($0, 0, RSTART - 1) }'\n}\n\nDISKS=\"$(diskutil list | grep '^\\/' | get_until_paren)\"\nmount_output=\"$(mount)\"\n\nfor disk in $DISKS; do\n\n # Ignore drives that were just unplugged\n if ! diskinfo=\"$(diskutil info \"$disk\")\"; then\n continue\n fi\n\n device=\"$(echo \"$diskinfo\" | get_key \"Device Node\")\"\n\n # See http://superuser.com/q/631592\n raw_device=\"${device//disk/rdisk}\"\n\n description=\"$(echo \"$diskinfo\" | get_key \"Device / Media Name\")\"\n volume_name=\"$(echo \"$diskinfo\" | get_key \"Volume Name\")\"\n removable=\"$(echo \"$diskinfo\" | get_key \"Removable Media\")\"\n protected=\"$(echo \"$diskinfo\" | get_key \"Read-Only Media\")\"\n location=\"$(echo \"$diskinfo\" | get_key \"Device Location\")\"\n size=\"$(echo \"$diskinfo\" | sed 's/Disk Size/Total Size/g' | get_key \"Total Size\" | cut -d '(' -f 2 | cut -d ' ' -f 1)\"\n\n mountpoints=\"$(echo \"$mount_output\" | grep -E \"^${disk}(s[0-9]+)? on \" | cut -d ' ' -f 3)\"\n\n # Omit mounted DMG images\n if [[ \"$description\" == \"Disk Image\" ]]; then\n continue\n fi\n\n echo \"enumerator: diskutil\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $raw_device\"\n\n if [[ $volume_name =~ .*Not\\ applicable.* ]]; then\n echo \"description: \\\"$description\\\"\"\n else\n echo \"description: \\\"$volume_name - $description\\\"\"\n fi\n\n echo \"error: \\\"\\\"\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [[ -z \"$mountpoints\" ]]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"Yes\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n if [[ \"$device\" == \"/dev/disk0\" ]] || \\\n [[ ( \"$removable\" == \"No\" ) || (\"$removable\" == \"Fixed\") ]] || \\\n [[ ( \"$location\" =~ \"Internal\" ) && ( \"$removable\" != \"Yes\" ) && ( \"$removable\" != \"Removable\" ) ]] || \\\n echo \"$mountpoints\" | grep \"^/$\"\n then\n echo \"isSystem: True\"\n else\n echo \"isSystem: False\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n echo \"\"\ndone\n", + "content": "#!/bin/bash\n\nset -u\nset -e\n\nget_key() {\n\n # This expression is intentionally unquoted so that\n # multiple lines get joined as a single one.\n # See https://github.com/resin-io-modules/drivelist/pull/129\n echo $(grep \"$1\" | awk -F \" +\" '{ print $3 }')\n\n}\n\nget_until_paren() {\n awk 'match($0, \"\\\\(|$\"){ print substr($0, 0, RSTART - 1) }'\n}\n\nDISKS=\"$(diskutil list | grep '^\\/' | get_until_paren)\"\nmount_output=\"$(mount)\"\n\nfor disk in $DISKS; do\n\n # Ignore drives that were just unplugged\n if ! diskinfo=\"$(diskutil info \"$disk\")\"; then\n continue\n fi\n\n device=\"$(echo \"$diskinfo\" | get_key \"Device Node\")\"\n\n # See http://superuser.com/q/631592\n raw_device=\"${device//disk/rdisk}\"\n\n description=\"$(echo \"$diskinfo\" | get_key \"Device / Media Name\")\"\n volume_name=\"$(echo \"$diskinfo\" | get_key \"Volume Name\")\"\n removable=\"$(echo \"$diskinfo\" | get_key \"Removable Media\")\"\n protected=\"$(echo \"$diskinfo\" | get_key \"Read-Only Media\")\"\n location=\"$(echo \"$diskinfo\" | get_key \"Device Location\")\"\n size=\"$(echo \"$diskinfo\" | sed 's/Disk Size/Total Size/g' | get_key \"Total Size\" | cut -d '(' -f 2 | cut -d ' ' -f 1)\"\n\n mountpoints=\"$(echo \"$mount_output\" | grep -E \"^${disk}(s[0-9]+)? on \" | cut -d ' ' -f 3)\"\n\n # Omit mounted DMG images\n if [[ \"$description\" == \"Disk Image\" ]]; then\n continue\n fi\n\n echo \"enumerator: diskutil\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $raw_device\"\n\n if [[ $volume_name =~ .*Not\\ applicable.* ]]; then\n echo \"description: \\\"$description\\\"\"\n else\n echo \"description: \\\"$volume_name - $description\\\"\"\n fi\n\n echo \"error: null\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [[ -z \"$mountpoints\" ]]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"Yes\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n if [[ \"$device\" == \"/dev/disk0\" ]] || \\\n [[ ( \"$removable\" == \"No\" ) || (\"$removable\" == \"Fixed\") ]] || \\\n [[ ( \"$location\" =~ \"Internal\" ) && ( \"$removable\" != \"Yes\" ) && ( \"$removable\" != \"Removable\" ) ]] || \\\n echo \"$mountpoints\" | grep \"^/$\"\n then\n echo \"isSystem: True\"\n else\n echo \"isSystem: False\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n echo \"\"\ndone\n", "originalFilename": "darwin.sh", "type": "text" }, "linux": { - "content": "#!/bin/bash\n\nset -u\nset -e\n\nignore_first_line() {\n tail -n +2\n}\n\nget_uuids() {\n /sbin/blkid -s UUID -o value \"$1\"*\n}\n\nget_mountpoints() {\n grep \"^$1\" /proc/mounts | cut -d ' ' -f 2 | sed 's,\\\\040, ,g' | sed 's,\\\\011,\\t,g' | sed 's,\\\\012,\\\\n,g' | sed 's,\\\\134,\\\\\\\\,g'\n}\n\nDISKS=\"$(lsblk -d --output NAME | ignore_first_line)\"\n\nfor disk in $DISKS; do\n\n # Omit loop devices and CD/DVD drives\n if [[ $disk == loop* ]] || [[ $disk == sr* ]]; then\n continue\n fi\n\n device=\"/dev/$disk\"\n diskinfo=($(lsblk -b -d \"$device\" --output SIZE,RO,RM,MODEL | ignore_first_line))\n\n # Omit drives for which `lsblk` failed, which means they\n # were unplugged right after we got the list of all drives\n if [ -z \"${diskinfo-}\" ]; then\n continue\n fi\n\n size=${diskinfo[0]}\n protected=${diskinfo[1]}\n removable=${diskinfo[2]}\n description=${diskinfo[*]:3}\n mountpoints=\"$(get_mountpoints \"$device\")\"\n\n # If we couldn't get the mount points as `/dev/$disk`,\n # get the disk UUIDs, and check as `/dev/disk/by-uuid/$uuid`\n if [ -z \"$mountpoints\" ]; then\n for uuid in $(get_uuids \"$device\"); do\n mountpoints=\"$mountpoints$(get_mountpoints \"/dev/disk/by-uuid/$uuid\")\"\n done\n fi\n\n # If we couldn't get the description from `lsblk`, see if we can get it\n # from sysfs (e.g. PCI-connected SD cards that appear as `/dev/mmcblk0`)\n if [ -z \"$description\" ]; then\n subdevice=\"$(echo \"$device\" | cut -d '/' -f 3)\"\n if [ -f \"/sys/class/block/$subdevice/device/name\" ]; then\n description=\"$(cat \"/sys/class/block/$subdevice/device/name\")\"\n fi\n fi\n\n echo \"enumerator: lsblk\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $device\"\n echo \"description: \\\"$description\\\"\"\n echo \"error: \\\"\\\"\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [ -z \"$mountpoints\" ]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"1\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n eval \"$(udevadm info \\\n --query=property \\\n --export \\\n --export-prefix=UDEV_ \\\n --name=\"$disk\" \\\n | awk -F= '{gsub(\"\\\\.\",\"_\",$1); print $1 \"=\" $2}')\"\n\n set +u\n\n if [[ \"$removable\" == \"1\" ]] && \\\n [[ \"$UDEV_ID_DRIVE_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_DRIVE_MEDIA_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_BUS\" == \"usb\" ]]\n then\n echo \"isSystem: False\"\n else\n echo \"isSystem: True\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n set -u\n\n # Unset UDEV variables used above to prevent them from\n # being interpreted as properties of another drive\n unset UDEV_ID_DRIVE_FLASH_SD\n unset UDEV_ID_DRIVE_MEDIA_FLASH_SD\n unset UDEV_ID_BUS\n\n echo \"\"\ndone\n", + "content": "#!/bin/bash\n\nset -u\nset -e\n\nignore_first_line() {\n tail -n +2\n}\n\nget_uuids() {\n /sbin/blkid -s UUID -o value \"$1\"*\n}\n\nget_mountpoints() {\n grep \"^$1\" /proc/mounts | cut -d ' ' -f 2 | sed 's,\\\\040, ,g' | sed 's,\\\\011,\\t,g' | sed 's,\\\\012,\\\\n,g' | sed 's,\\\\134,\\\\\\\\,g'\n}\n\nDISKS=\"$(lsblk -d --output NAME | ignore_first_line)\"\n\nfor disk in $DISKS; do\n\n # Omit loop devices and CD/DVD drives\n if [[ $disk == loop* ]] || [[ $disk == sr* ]]; then\n continue\n fi\n\n device=\"/dev/$disk\"\n diskinfo=($(lsblk -b -d \"$device\" --output SIZE,RO,RM,MODEL | ignore_first_line))\n\n # Omit drives for which `lsblk` failed, which means they\n # were unplugged right after we got the list of all drives\n if [ -z \"${diskinfo-}\" ]; then\n continue\n fi\n\n size=${diskinfo[0]}\n protected=${diskinfo[1]}\n removable=${diskinfo[2]}\n description=${diskinfo[*]:3}\n mountpoints=\"$(get_mountpoints \"$device\")\"\n\n # If we couldn't get the mount points as `/dev/$disk`,\n # get the disk UUIDs, and check as `/dev/disk/by-uuid/$uuid`\n if [ -z \"$mountpoints\" ]; then\n for uuid in $(get_uuids \"$device\"); do\n mountpoints=\"$mountpoints$(get_mountpoints \"/dev/disk/by-uuid/$uuid\")\"\n done\n fi\n\n # If we couldn't get the description from `lsblk`, see if we can get it\n # from sysfs (e.g. PCI-connected SD cards that appear as `/dev/mmcblk0`)\n if [ -z \"$description\" ]; then\n subdevice=\"$(echo \"$device\" | cut -d '/' -f 3)\"\n if [ -f \"/sys/class/block/$subdevice/device/name\" ]; then\n description=\"$(cat \"/sys/class/block/$subdevice/device/name\")\"\n fi\n fi\n\n echo \"enumerator: lsblk\"\n echo \"busType: UNKNOWN\"\n echo \"busVersion: \\\"0.0\\\"\"\n echo \"device: $device\"\n echo \"raw: $device\"\n echo \"description: \\\"$description\\\"\"\n echo \"error: null\"\n echo \"size: $size\"\n echo \"blockSize: null\"\n echo \"logicalBlockSize: null\"\n\n if [ -z \"$mountpoints\" ]; then\n echo \"mountpoints: []\"\n else\n echo \"mountpoints:\"\n echo \"$mountpoints\" | while read -r mountpoint ; do\n echo \" - path: \\\"$mountpoint\\\"\"\n done\n fi\n\n if [[ \"$protected\" == \"1\" ]]; then\n echo \"isReadOnly: True\"\n else\n echo \"isReadOnly: False\"\n fi\n\n eval \"$(udevadm info \\\n --query=property \\\n --export \\\n --export-prefix=UDEV_ \\\n --name=\"$disk\" \\\n | awk -F= '{gsub(\"\\\\.\",\"_\",$1); print $1 \"=\" $2}')\"\n\n set +u\n\n if [[ \"$removable\" == \"1\" ]] && \\\n [[ \"$UDEV_ID_DRIVE_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_DRIVE_MEDIA_FLASH_SD\" == \"1\" ]] || \\\n [[ \"$UDEV_ID_BUS\" == \"usb\" ]]\n then\n echo \"isSystem: False\"\n else\n echo \"isSystem: True\"\n fi\n\n echo \"isVirtual: null\"\n echo \"isRemovable: null\"\n echo \"isCard: null\"\n echo \"isSCSI: null\"\n echo \"isUSB: null\"\n echo \"isUAS: null\"\n\n set -u\n\n # Unset UDEV variables used above to prevent them from\n # being interpreted as properties of another drive\n unset UDEV_ID_DRIVE_FLASH_SD\n unset UDEV_ID_DRIVE_MEDIA_FLASH_SD\n unset UDEV_ID_BUS\n\n echo \"\"\ndone\n", "originalFilename": "linux.sh", "type": "text" } diff --git a/scripts/darwin.sh b/scripts/darwin.sh index 187f1042..ab973f86 100755 --- a/scripts/darwin.sh +++ b/scripts/darwin.sh @@ -57,7 +57,7 @@ for disk in $DISKS; do echo "description: \"$volume_name - $description\"" fi - echo "error: \"\"" + echo "error: null" echo "size: $size" echo "blockSize: null" echo "logicalBlockSize: null" diff --git a/scripts/linux.sh b/scripts/linux.sh index f2f3c83f..90235138 100755 --- a/scripts/linux.sh +++ b/scripts/linux.sh @@ -62,7 +62,7 @@ for disk in $DISKS; do echo "device: $device" echo "raw: $device" echo "description: \"$description\"" - echo "error: \"\"" + echo "error: null" echo "size: $size" echo "blockSize: null" echo "logicalBlockSize: null" diff --git a/tests/drivelist.spec.js b/tests/drivelist.spec.js index c52e321c..67fb9049 100644 --- a/tests/drivelist.spec.js +++ b/tests/drivelist.spec.js @@ -39,10 +39,26 @@ describe('Drivelist', function() { ].join('\n')); this.executeExtractAndRunStub.withArgs(scripts.darwin).yields(null, [ - 'device: "/dev/disk2"', - 'description: "My drive"', - 'size: "15 GB"', - 'mountpoint: "/Volumes/drive"' + 'enumerator: diskutil', + 'busType: UNKNOWN', + 'busVersion: "0.0"', + 'device: /dev/disk2', + 'raw: /dev/rdisk2', + 'description: "SD Card Reader"', + 'error: null', + 'size: 31104958464', + 'blockSize: null', + 'logicalBlockSize: null', + 'mountpoints:', + ' - path: "/Volumes/Patchwork"', + 'isReadOnly: False', + 'isSystem: False', + 'isVirtual: null', + 'isRemovable: null', + 'isCard: null', + 'isSCSI: null', + 'isUSB: null', + 'isUAS: null' ].join('\n')); }); @@ -94,10 +110,27 @@ describe('Drivelist', function() { m.chai.expect(error).to.not.exist; m.chai.expect(drives).to.deep.equal([ { + enumerator: 'diskutil', + busType: 'UNKNOWN', + busVersion: '0.0', device: '/dev/disk2', - description: 'My drive', - size: '15 GB', - mountpoint: '/Volumes/drive' + raw: '/dev/rdisk2', + description: 'SD Card Reader', + error: null, + size: 31104958464, + blockSize: null, + logicalBlockSize: null, + mountpoints: [ { + path: '/Volumes/Patchwork' + } ], + isReadOnly: false, + isSystem: false, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null } ]); done(); From 589e65b67ffbec8f8dd9d53c6ebdfa81c15d6971 Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:50:02 +0100 Subject: [PATCH 12/17] doc(README): Update Mac OS examples --- README.md | 145 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 104 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 3e4d13bf..5d85617f 100644 --- a/README.md +++ b/README.md @@ -40,24 +40,46 @@ This is how the raw output looks on my MacBook Pro at the time of this writing: ```sh $ ./scripts/darwin.sh +enumerator: diskutil +busType: UNKNOWN +busVersion: "0.0" device: /dev/disk0 -displayName: /dev/disk0 -description: "APPLE SSD SM0256G" -size: 251000193024 -mountpoints: [] raw: /dev/rdisk0 -protected: False -system: True - -device: /dev/disk1 -displayName: /dev/disk1 -description: "Macintosh HD" -size: 249779191808 +description: "APPLE SSD TS128E" +error: null +size: 121332826112 +blockSize: null +logicalBlockSize: null +mountpoints: [] +isReadOnly: False +isSystem: True +isVirtual: null +isRemovable: null +isCard: null +isSCSI: null +isUSB: null +isUAS: null + +enumerator: diskutil +busType: UNKNOWN +busVersion: "0.0" +device: /dev/disk2 +raw: /dev/rdisk2 +description: "SD Card Reader" +error: null +size: 31104958464 +blockSize: null +logicalBlockSize: null mountpoints: - - path: / -raw: /dev/rdisk1 -protected: False -system: True + - path: "/Volumes/Patchwork" +isReadOnly: False +isSystem: False +isVirtual: null +isRemovable: null +isCard: null +isSCSI: null +isUSB: null +isUAS: null ``` Because of the simplicity of this module's design, supporting a new operating @@ -89,32 +111,73 @@ drivelist.list((error, drives) => { Mac OS X: ```sh -[ - { - device: '/dev/disk0', - displayName: '/dev/disk0', - description: 'GUID_partition_scheme', - size: 68719476736, - mountpoints: [ - { - path: '/' - } - ], - raw: '/dev/rdisk0', - protected: false, - system: true - }, - { - device: '/dev/disk1', - displayName: '/dev/disk1', - description: 'Apple_HFS Macintosh HD', - size: 68719476736, - mountpoints: [], - raw: '/dev/rdisk0', - protected: false, - system: true - } -] +[{ + enumerator: 'diskutil', + busType: 'UNKNOWN', + busVersion: '0.0', + device: '/dev/disk0', + raw: '/dev/rdisk0', + description: 'APPLE SSD TS128E', + error: null, + size: 121332826112, + blockSize: null, + logicalBlockSize: null, + mountpoints: [], + isReadOnly: false, + isSystem: true, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null +}, { + enumerator: 'diskutil', + busType: 'UNKNOWN', + busVersion: '0.0', + device: '/dev/disk1', + raw: '/dev/rdisk1', + description: 'APPLE SSD TS128E', + error: null, + size: 120473067520, + blockSize: null, + logicalBlockSize: null, + mountpoints: [ + { path: '/' }, + { path: '/private/var/vm' } + ], + isReadOnly: false, + isSystem: true, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null +}, { + enumerator: 'diskutil', + busType: 'UNKNOWN', + busVersion: '0.0', + device: '/dev/disk2', + raw: '/dev/rdisk2', + description: 'SD Card Reader', + error: null, + size: 31104958464, + blockSize: null, + logicalBlockSize: null, + mountpoints: [ + { path: '/Volumes/Patchwork' } + ], + isReadOnly: false, + isSystem: false, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null +}] + ``` *** From 15c87d0d0b06b41e813ec120d131247d6ba9ef9e Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 19:51:55 +0100 Subject: [PATCH 13/17] doc(README): Update Windows output example --- README.md | 156 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 5d85617f..9326f803 100644 --- a/README.md +++ b/README.md @@ -222,49 +222,119 @@ GNU/Linux Windows ```sh -[ - { - device: '\\\\.\\PHYSICALDRIVE0', - displayName: 'C:', - description: 'WDC WD10JPVX-75JC3T0', - size: 68719476736, - mountpoints: [ - { - path: 'C:' - } - ], - raw: '\\\\.\\PHYSICALDRIVE0', - protected: false, - system: true - }, - { - device: '\\\\.\\PHYSICALDRIVE1', - displayName: 'D:, F:', - description: 'Generic STORAGE DEVICE USB Device', - size: 7823458304, - mountpoints: [ - { - path: 'D:' - }, - { - path: 'F:' - } - ], - raw: '\\\\.\\PHYSICALDRIVE1', - protected: true, - system: false - }, - { - device: '\\\\.\\PHYSICALDRIVE2', - displayName: '\\\\.\\PHYSICALDRIVE2', - description: 'Silicon-Power2G', - size: 2014314496, - mountpoints: [], - raw: '\\\\.\\PHYSICALDRIVE2', - protected: false, - system: false - } -] +[{ + enumerator: 'SCSI', + busType: 'SATA', + busVersion: '2.0', + device: '\\\\?\\scsi#disk&ven_wdc&prod_wd1600bevs-07rst#4&5a60d67&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}', + raw: '\\\\.\\PhysicalDrive0', + description: 'WDC WD1600BEVS-07RST0', + error: null, + size: 160041885696, + blockSize: 512, + logicalBlockSize: 512, + mountpoints: [{ + path: 'D:\\' + }], + isReadOnly: false, + isSystem: true, + isVirtual: false, + isRemovable: false, + isCard: false, + isSCSI: true, + isUSB: false, + isUAS: false +}, { + enumerator: 'SD', + busType: 'SD', + busVersion: '2.0', + device: '\\\\?\\sd#disk&generic&sc16g&8.0#5&c518b2e&0&c3964099&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}', + raw: '\\\\.\\PhysicalDrive4', + description: 'Generic SC16G SD Card', + error: null, + size: 15931539456, + blockSize: 4096, + logicalBlockSize: 512, + mountpoints: [{ + path: 'G:\\' + }, { + path: 'H:\\' + }], + isReadOnly: false, + isSystem: false, + isVirtual: false, + isRemovable: true, + isCard: true, + isSCSI: true, + isUSB: false, + isUAS: false +}, { + enumerator: 'SCSI', + busType: 'USB', + busVersion: '2.0', + device: '\\\\?\\scsi#disk&ven_usb3.0&prod_#000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}', + raw: '\\\\.\\PhysicalDrive2', + description: 'USB3.0 SCSI Disk Device', + error: null, + size: 500107862016, + blockSize: 4096, + logicalBlockSize: 512, + mountpoints: [{ + path: 'E:\\' + }], + isReadOnly: false, + isSystem: false, + isVirtual: false, + isRemovable: true, + isCard: false, + isSCSI: true, + isUSB: false, + isUAS: true +}, { + enumerator: 'SCSI', + busType: 'SATA', + busVersion: '2.0', + device: '\\\\?\\scsi#disk&ven_samsung&prod_ssd_850_evo_m.2#4&5a60d67&0&020000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}', + raw: '\\\\.\\PhysicalDrive1', + description: 'Samsung SSD 850 EVO M.2 120GB', + error: null, + size: 120034123776, + blockSize: 512, + logicalBlockSize: 512, + mountpoints: [{ + path: 'C:\\' + }], + isReadOnly: false, + isSystem: true, + isVirtual: false, + isRemovable: false, + isCard: false, + isSCSI: true, + isUSB: false, + isUAS: false +}, { + enumerator: 'USBSTOR', + busType: 'USB', + busVersion: '2.0', + device: '\\\\?\\usbstor#disk&ven_disk&prod_name&rev_ax10#0012345667888&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}', + raw: '\\\\.\\PhysicalDrive3', + description: 'Disk Name USB Device', + error: null, + size: 1000204886016, + blockSize: 512, + logicalBlockSize: 512, + mountpoints: [{ + path: 'F:\\' + }], + isReadOnly: false, + isSystem: false, + isVirtual: false, + isRemovable: true, + isCard: false, + isSCSI: false, + isUSB: true, + isUAS: false +}] ``` Installation From 23d2634b30aac0bfd5fbe69684fac765572b302b Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 20:05:08 +0100 Subject: [PATCH 14/17] test(linux): Update Linux tests --- tests/drivelist.spec.js | 50 +++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/tests/drivelist.spec.js b/tests/drivelist.spec.js index 67fb9049..c6a7275a 100644 --- a/tests/drivelist.spec.js +++ b/tests/drivelist.spec.js @@ -32,10 +32,27 @@ describe('Drivelist', function() { this.executeExtractAndRunStub = m.sinon.stub(execute, 'extractAndRun'); this.executeExtractAndRunStub.withArgs(scripts.linux).yields(null, [ - 'device: "/dev/sda"', - 'description: "My drive"', - 'size: "15 GB"', - 'mountpoint: "/mnt/drive"' + 'enumerator: lsblk', + 'busType: UNKNOWN', + 'busVersion: "0.0"', + 'device: /dev/sda', + 'raw: /dev/sda', + 'description: "Samsung SSD 850"', + 'error: null', + 'size: 120034123776', + 'blockSize: null', + 'logicalBlockSize: null', + 'mountpoints:', + ' - path: "/"', + ' - path: "/boot/efi"', + 'isReadOnly: False', + 'isSystem: True', + 'isVirtual: null', + 'isRemovable: null', + 'isCard: null', + 'isSCSI: null', + 'isUSB: null', + 'isUAS: null' ].join('\n')); this.executeExtractAndRunStub.withArgs(scripts.darwin).yields(null, [ @@ -82,10 +99,29 @@ describe('Drivelist', function() { m.chai.expect(error).to.not.exist; m.chai.expect(drives).to.deep.equal([ { + enumerator: 'lsblk', + busType: 'UNKNOWN', + busVersion: '0.0', device: '/dev/sda', - description: 'My drive', - size: '15 GB', - mountpoint: '/mnt/drive' + raw: '/dev/sda', + description: 'Samsung SSD 850', + error: null, + size: 120034123776, + blockSize: null, + logicalBlockSize: null, + mountpoints: [ { + path: '/' + }, { + path: '/boot/efi' + } ], + isReadOnly: false, + isSystem: true, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null } ]); done(); From b87a99562c0d8b9c1df00fb7b89ae0fc269b70af Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 20:05:39 +0100 Subject: [PATCH 15/17] doc(README): Update Linux example output --- README.md | 77 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9326f803..e1817e64 100644 --- a/README.md +++ b/README.md @@ -185,36 +185,53 @@ Mac OS X: GNU/Linux ```sh -[ - { - device: '/dev/sda', - displayName: '/dev/sda', - description: 'WDC WD10JPVX-75J', - size: 68719476736, - mountpoints: [ - { - path: '/' - } - ], - raw: '/dev/sda', - protected: false, - system: true - }, - { - device: '/dev/sdb', - displayName: '/dev/sdb', - description: 'DataTraveler 2.0', - size: 7823458304, - mountpoints: [ - { - path: '/media/UNTITLED' - } - ], - raw: '/dev/sdb', - protected: true, - system: false - } -] +[{ + enumerator: 'lsblk', + busType: 'UNKNOWN', + busVersion: '0.0', + device: '/dev/sdb', + raw: '/dev/sdb', + description: 'Storage Device', + error: null, + size: 31914983424, + blockSize: null, + logicalBlockSize: null, + mountpoints: [{ + path: '/media/jonas/Etcher 1.2.0' + }], + isReadOnly: false, + isSystem: false, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null +}, { + enumerator: 'lsblk', + busType: 'UNKNOWN', + busVersion: '0.0', + device: '/dev/sda', + raw: '/dev/sda', + description: 'Samsung SSD 850', + error: null, + size: 120034123776, + blockSize: null, + logicalBlockSize: null, + mountpoints: [{ + path: '/' + }, { + path: '/boot/efi' + }], + isReadOnly: false, + isSystem: true, + isVirtual: null, + isRemovable: null, + isCard: null, + isSCSI: null, + isUSB: null, + isUAS: null +}] ``` *** From ed8cd185489f8654f8269a3023fbe6e5f77e8faf Mon Sep 17 00:00:00 2001 From: Jonas Hermsmeier Date: Thu, 4 Jan 2018 22:09:53 +0100 Subject: [PATCH 16/17] fix(windows): Expose PhysicalDevice under device key --- src/windows/list.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/windows/list.cpp b/src/windows/list.cpp index bba45961..25ae97b8 100644 --- a/src/windows/list.cpp +++ b/src/windows/list.cpp @@ -460,8 +460,6 @@ bool GetDetailData(DeviceDescriptor* device, // printf("[INFO] (%i) SetupDiGetDeviceInterfaceDetailW:\n %s\n", // index, WCharToUtf8(deviceDetailData->DevicePath)); - device->device = std::string(WCharToUtf8(deviceDetailData->DevicePath)); - hDevice = CreateFileW( deviceDetailData->DevicePath, handleOpenFlags, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, @@ -484,6 +482,7 @@ bool GetDetailData(DeviceDescriptor* device, } device->raw = "\\\\.\\PhysicalDrive" + std::to_string(deviceNumber); + device->device = device->raw; GetMountpoints(deviceNumber, &device->mountpoints); From 89127b1c065f53bf800a827dcd240d280c622e3b Mon Sep 17 00:00:00 2001 From: "resin-io-modules-versionbot[bot]" Date: Fri, 5 Jan 2018 02:03:57 +0000 Subject: [PATCH 17/17] v6.0.0 --- CHANGELOG.md | 4 ++++ package.json | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39dd4583..825b8d86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY! This project adheres to [Semantic Versioning](http://semver.org/). +## v6.0.0 - 2018-01-05 + +* Feat: Rewrite native bindings to use IOCTL #243 [Jonas Hermsmeier] + ## v5.2.12 - 2017-12-28 * Chore(package): Bump dependencies #242 [Jonas Hermsmeier] diff --git a/package.json b/package.json index 0d543b5f..d23f5fc0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "drivelist", - "version": "5.2.12", + "version": "6.0.0", "description": "List all connected drives in your computer, in all major operating systems", "main": "lib/drivelist.js", "homepage": "https://github.com/resin-io-modules/drivelist", @@ -54,4 +54,4 @@ "nan": "^2.8.0", "prebuild-install": "^2.4.1" } -} +} \ No newline at end of file