Qt/QML edition

This commit is contained in:
Floris Bos 2020-03-04 16:55:40 +01:00
commit d7b361ba44
2168 changed files with 721948 additions and 0 deletions

16
dependencies/drivelist/.editorconfig vendored Normal file
View file

@ -0,0 +1,16 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.plist]
indent_style = tab

467
dependencies/drivelist/.eslintrc.yml vendored Normal file
View file

@ -0,0 +1,467 @@
env:
browser: true
commonjs: true
es6: true
node: true
mocha: true
extends: 'eslint:recommended'
rules:
# Possible Errors
comma-dangle:
- error
- never
no-cond-assign:
- off
no-console:
- off
no-constant-condition:
- error
no-control-regex:
- error
no-debugger:
- error
no-dupe-args:
- error
no-dupe-keys:
- error
no-duplicate-case:
- error
no-empty:
- error
no-empty-character-class:
- error
no-ex-assign:
- error
no-extra-boolean-cast:
- error
no-extra-parens:
- error
no-extra-semi:
- error
no-func-assign:
- error
no-inner-declarations:
- error
- both
no-invalid-regexp:
- error
no-irregular-whitespace:
- error
no-negated-in-lhs:
- error
no-obj-calls:
- error
no-prototype-builtins:
- error
no-regex-spaces:
- error
no-sparse-arrays:
- error
no-unexpected-multiline:
- error
no-unreachable:
- error
no-unsafe-finally:
- error
use-isnan:
- error
valid-jsdoc:
- error
- requireReturn: false
requireReturnDescription: false
valid-typeof:
- error
# Best Practices
accessor-pairs:
- error
array-callback-return:
- error
block-scoped-var:
- error
complexity:
- off
curly:
- error
default-case:
- error
dot-location:
- error
- property
dot-notation:
- error
eqeqeq:
- 1
guard-for-in:
- error
no-alert:
- error
no-caller:
- error
no-case-declarations:
- error
no-div-regex:
- error
no-else-return:
- error
no-empty-function:
- error
no-empty-pattern:
- error
no-eq-null:
- off
no-eval:
- error
no-extend-native:
- error
no-extra-bind:
- error
no-extra-label:
- error
no-fallthrough:
- error
no-floating-decimal:
- error
no-implicit-coercion:
- error
no-implicit-globals:
- error
no-implied-eval:
- error
no-iterator:
- error
no-labels:
- error
no-lone-blocks:
- error
no-loop-func:
- error
no-multi-spaces:
- error
no-multi-str:
- error
no-native-reassign:
- error
no-new:
- error
no-new-func:
- error
no-new-wrappers:
- error
no-octal:
- error
no-octal-escape:
- error
no-proto:
- error
no-redeclare:
- error
no-return-assign:
- error
no-script-url:
- error
no-self-assign:
- error
no-self-compare:
- error
no-sequences:
- error
no-throw-literal:
- error
no-unmodified-loop-condition:
- error
no-unused-labels:
- error
no-useless-call:
- error
no-useless-concat:
- error
no-useless-escape:
- error
no-void:
- error
no-warning-comments:
- off
no-with:
- error
radix:
- error
vars-on-top:
- off
wrap-iife:
- error
- outside
yoda:
- error
# Strict mode
strict:
- error
- global
# Variables
no-catch-shadow:
- error
no-delete-var:
- error
no-label-var:
- error
no-shadow:
- error
no-shadow-restricted-names:
- error
no-undef:
- error
no-undef-init:
- error
no-unused-vars:
- error
no-use-before-define:
- error
# NodeJS and CommonJS
callback-return:
- error
global-require:
- off
handle-callback-err:
- error
no-mixed-requires:
- error
no-new-require:
- error
no-path-concat:
- error
no-process-env:
- off
no-process-exit:
- off
no-sync:
- off
# Stylistic Issues
array-bracket-spacing:
- error
- always
block-spacing:
- error
brace-style:
- error
- 1tbs
camelcase:
- error
comma-spacing:
- error
- before: false
after: true
comma-style:
- error
- last
computed-property-spacing:
- error
- never
consistent-this:
- error
- self
eol-last:
- error
func-names:
- error
- never
func-style:
- error
- expression
id-blacklist:
- error
indent:
- error
- 2
- SwitchCase: 1
key-spacing:
- error
- beforeColon: false
afterColon: true
mode: strict
keyword-spacing:
- error
- before: true
after: true
linebreak-style:
- error
- unix
lines-around-comment:
- error
- beforeBlockComment: true
afterBlockComment: false
beforeLineComment: true
afterLineComment: false
allowBlockStart: true
allowBlockEnd: false
allowObjectStart: true
allowObjectEnd: false
allowArrayStart: true
allowArrayEnd: false
max-len:
- error
- code: 130
comments: 150
ignoreComments: false
ignoreTrailingComments: false
ignoreUrls: true
max-statements-per-line:
- error
- max: 1
new-parens:
- error
no-array-constructor:
- error
no-bitwise:
- error
no-continue:
- error
no-inline-comments:
- error
no-mixed-operators:
- off
no-mixed-spaces-and-tabs:
- error
no-multiple-empty-lines:
- error
- max: 1
maxEOF: 1
maxBOF: 0
no-negated-condition:
- error
no-nested-ternary:
- error
no-new-object:
- error
no-plusplus:
- error
no-spaced-func:
- error
no-trailing-spaces:
- error
no-underscore-dangle:
- error
- allowAfterThis: false
no-unneeded-ternary:
- error
no-whitespace-before-property:
- error
object-curly-newline:
- error
- minProperties: 1
object-curly-spacing:
- error
- always
object-property-newline:
- error
one-var:
- error
- never
operator-assignment:
- error
- always
operator-linebreak:
- error
- before
quote-props:
- error
- as-needed
quotes:
- error
- single
require-jsdoc:
- error
- require:
FunctionDeclaration: true
ClassDeclaration: true
MethodDefinition: true
semi:
- error
- always
semi-spacing:
- error
- before: false
after: true
space-before-blocks:
- error
space-before-function-paren:
- error
- never
space-in-parens:
- error
- never
space-infix-ops:
- error
spaced-comment:
- error
- always
unicode-bom:
- error
# ECMAScript 6
arrow-body-style:
- error
- always
arrow-parens:
- error
- always
arrow-spacing:
- error
- before: true
after: true
constructor-super:
- error
generator-star-spacing:
- error
- before: true
after: false
no-class-assign:
- error
no-confusing-arrow:
- error
no-const-assign:
- error
no-dupe-class-members:
- error
no-duplicate-imports:
- error
no-new-symbol:
- error
no-this-before-super:
- error
no-useless-computed-key:
- error
no-useless-constructor:
- error
no-useless-rename:
- error
no-var:
- error
prefer-const:
- error
prefer-reflect:
- error
prefer-spread:
- error
require-yield:
- error
rest-spread-spacing:
- error
template-curly-spacing:
- error
- never
yield-star-spacing:
- error
- before: true
after: false

29
dependencies/drivelist/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
prebuilds

9
dependencies/drivelist/.npmignore vendored Normal file
View file

@ -0,0 +1,9 @@
.*
*.yml
build
doc
example
tests
prebuilds
ci

41
dependencies/drivelist/.resinci.yml vendored Normal file
View file

@ -0,0 +1,41 @@
---
npm:
environment:
- name: CCACHE_TEMPDIR
value: /tmp/.ccache-temp
- name: CCACHE_COMPRESS
value: 1
- name: CC
value: clang
- name: CXX
value: clang++
platforms:
- name: linux
os: ubuntu
architecture: x86_64
node_versions:
- "10"
- "8"
- "6"
- name: linux
os: ubuntu
architecture: x86
node_versions:
- "8"
- "6"
- name: darwin
os: macos
architecture: x86_64
node_versions:
- "6"
- name: windows
os: windows
architecture: x86_64
node_versions:
- "6"
- name: windows
os: windows
architecture: x86
node_versions:
- "6"

39
dependencies/drivelist/.travis.yml vendored Normal file
View file

@ -0,0 +1,39 @@
language: node_js
node_js:
- 10
- 8
- 6
os:
- linux
- osx
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
pip install --user -r requirements.txt;
else
pip2 install -r requirements.txt;
fi
script:
- npm test
compiler: clang-4.0
env:
global:
- CCACHE_TEMPDIR=/tmp/.ccache-temp
- CCACHE_COMPRESS=1
- CC="clang"
- CXX="clang++"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libstdc++-5-dev
notifications:
email: false
deploy:
skip_cleanup: true
provider: script
script: ./ci/prebuild-publish.sh
on:
node: 10
tags: true
branch: master

606
dependencies/drivelist/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,606 @@
# Change Log
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/).
## 8.0.9 - 2019-11-06
* Avoid segfaulting on mac when some disk description fields are null [Alexis Svinartchouk]
## 8.0.8 - 2019-10-08
* Fix tests [Alexis Svinartchouk]
* Fix typo in readme [Alexis Svinartchouk]
## 8.0.7 - 2019-09-20
* Update nan to ^2.14 to fix builds targetting electron 6 [Alexis Svinartchouk]
## 8.0.6 - 2019-03-20
* Update example in readme [Alexis Svinartchouk]
## 8.0.5 - 2019-03-20
* Update resin-lint to ^3.0.1 [Alexis Svinartchouk]
## 8.0.4 - 2019-03-19
* Unregister DASession callback to avoid crash [Robert Vojta]
## 8.0.3 - 2019-03-18
* Fix DASession and NSMutableArray leaks on macOS [Alexis Svinartchouk]
## 8.0.2 - 2019-03-04
* Fix isVirtual value on macOS [Robert Vojta]
## 8.0.1 - 2019-02-27
* Fix macOS crash when SD card is ejected [Robert Vojta]
## 8.0.0 - 2019-02-22
* Address PR comments [Alexis Svinartchouk]
* Try `lsblk --pairs` if parsing `lsblk --json` fails [Alexis Svinartchouk]
* Convert to typescript, use promises instead of callbacks [Alexis Svinartchouk]
## 7.0.3 - 2019-02-20
* Fix parsing lsblk --pairs output for disks with no partition table [Alexis Svinartchouk]
## 7.0.2 - 2019-02-20
* Update prebuild to ^8.1.2 [Alexis Svinartchouk]
## 7.0.1 - 2019-01-28
* Remove unused diskutil file and its tests [Alexis Svinartchouk]
## 7.0.0 - 2019-01-28
* Remove outdated docs. [Alexis Svinartchouk]
* Feat(mac): Native implementation for drivelist [Robin Andersson]
## 6.4.6 - 2019-01-10
* Fix tests incorrect mountpoints [Lorenzo Alberto Maria Ambrosi]
* Fix devices mountpoints for lsblk in pairs mode [Lorenzo Alberto Maria Ambrosi]
## 6.4.5 - 2019-01-10
* Fix tests incorrect device.size [Lorenzo Alberto Maria Ambrosi]
* Fix linting errors & add specs with plist return value assertions [Robin Andersson]
## 6.4.4 - 2019-01-10
* Fix build on xcode 10.1 [Alexis Svinartchouk]
## v6.4.3 - 2018-09-10
* Lsblk: Fix listing of empty mountpoints [Jonas Hermsmeier]
## v6.4.2 - 2018-09-03
* Lsblk: Check `hotplug` to set `isRemovable` [Jonas Hermsmeier]
* Ci: Add .resinci.yml [John (Jack) Brown]
## v6.4.1 - 2018-08-28
* Lsblk: Fix missing `devicePath` property [Jonas Hermsmeier]
## v6.4.0 - 2018-08-02
* Feat(diskutil): Populate devicePath on Mac OS #297 [Jonas Hermsmeier]
## v6.3.2 - 2018-08-02
* Fix(diskutil): Also probe Ejectable to determine removabililty #298 [Jonas Hermsmeier]
## v6.3.1 - 2018-08-01
* Test(lsblk): Add test case for root device mountpoints #293 [Jonas Hermsmeier]
* Fix(lsblk): Enumerate mountpoints on root devices #293 [Jonas Hermsmeier]
* Fix(lsblk): Fall back to mountpoint if no label given #293 [Jonas Hermsmeier]
* Fix(lsblk): Resolve relative device names #293 [Jonas Hermsmeier]
## v6.3.0 - 2018-07-09
* Fix(linux): Fix pair consolidation for lsblk #255 [Jonas Hermsmeier]
* Feat(lib): Use lsblk directly, parse json output #255 [Jonas Hermsmeier]
## v6.2.5 - 2018-07-06
* Fix(linux): Add flag to lsblk to list all devices #287 [Jonas Hermsmeier]
## v6.2.4 - 2018-07-04
* Fix(test): Add python requirements.txt for tests #288 [Jonas Hermsmeier]
## v6.2.3 - 2018-05-25
* Fix(src): Fix nan callback deprecation warning #284 [Jonas Hermsmeier]
## v6.2.2 - 2018-05-10
* Fix(ci): Fix Node version arg passed to prebuild script #280 [Jonas Hermsmeier]
## v6.2.1 - 2018-05-10
* Upgrade(package): Bump prebuild dependencies #279 [Jonas Hermsmeier]
* Fix(ci): Only prebuild with Node 10 #279 [Jonas Hermsmeier]
## v6.2.0 - 2018-05-09
* Feat(linux): Add unique device path #278 [User Name]
## v6.1.8 - 2018-04-26
* Chore(ci): Build, test & publish releases for Node.js v10 #273 [Tim Perry]
## v6.1.7 - 2018-04-16
* Fix(diskutil): Fall back to MediaName if EntryName is not available #270 [Jonas Hermsmeier]
* Fix(diskutil): Track & filter out device info errors #270 [Jonas Hermsmeier]
## v6.1.6 - 2018-04-13
* Fix(diskutil): Added an error for diskutil returning null [grumpyoldman-io]
## v6.1.5 - 2018-04-05
* Fix(diskutil): Fix ReferenceError when DeviceNode is undefined #266 [Jonas Hermsmeier]
## v6.1.4 - 2018-04-02
* Upgrade(package): Bump dependencies #264 [Jonas Hermsmeier]
## v6.1.3 - 2018-03-17
* Fix(diskutil): Use VolumeAllocationBlockSize if available #260 [Jonas Hermsmeier]
## v6.1.2 - 2018-03-16
* Fix(test): Avoid test timeout on AppVeyor #259 [Jonas Hermsmeier]
* Test(diskutil): Add tests for devices without partitions #259 [Jonas Hermsmeier]
* Fix(diskutil): Support mountpoints without partitions #259 [Jonas Hermsmeier]
* Fix(diskutil): Fix handling of devices without partitions #259 [Jonas Hermsmeier]
## v6.1.1 - 2018-03-13
* Fix(package): Remove obsolete bin entries #256 [Jonas Hermsmeier]
## v6.1.0 - 2018-03-09
* Feat(lib): Use diskutil directly, parse plist output #253 [Jonas Hermsmeier]
## v6.0.5 - 2018-03-02
* Upgrade(package): Bump dependencies #252 [Jonas Hermsmeier]
## v6.0.4 - 2018-01-18
* Fix(windows): Impl IsSystemDevice() #250 [Jonas Hermsmeier]
## v6.0.3 - 2018-01-18
* Fix crash on Windows 7 #249 [Jonas Hermsmeier]
## v6.0.2 - 2018-01-14
* Fix(darwin): Use proper flag to enable extended regexes in `sed` Fixes #247 #248 [Andrew Scheller]
## v6.0.1 - 2018-01-12
* Fix(darwin): Allow mountpoints containing space characters #246 [Andrew Scheller]
## 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]
## v5.2.11 - 2017-12-14
* Refactor: Remove lodash #235 [Jonas Hermsmeier]
## v5.2.10 - 2017-12-12
* Fix(package): Fix prebuild script being run on build #236 [Jonas Hermsmeier]
## v5.2.9 - 2017-12-09
* Fix: Remove the final usage of perl from darwin.sh #238 [Andrew Scheller]
## v5.2.8 - 2017-12-08
* Chore: pip on Travis OSX is now pip2 #239 [Andrew Scheller]
## v5.2.7 - 2017-10-17
* Upgrade(package): Bump dependencies #232 [Jonas Hermsmeier]
## v5.2.6 - 2017-10-13
* Fix(ci): Ensure the Travis box finds bash #230 [Jonas Hermsmeier]
## v5.2.5 - 2017-10-13
* Chore(ci): Add CI deployment scripts #227 [Jonas Hermsmeier]
## v5.2.4 - 2017-10-06
* Correctly execute macOS and GNU/Linux scripts inside directories mounted as `noexec`. #229 [Juan Cruz Viotti]
## v5.2.3 - 2017-10-06
* Fix: Bring back support for Node.js v4 #225 [Zoltan Kochan]
## v5.2.2 - 2017-10-02
* Chore(appveyor): Explicitly set base image #223 [Jonas Hermsmeier]
## v5.2.1 - 2017-09-28
* Upgrade(package): Update dependencies #221 [Jonas Hermsmeier]
## v5.2.0 - 2017-09-28
* Feat(package): Add prebuilds for native bindings #219 [Jonas Hermsmeier]
## v5.1.8 - 2017-08-28
* Store GNU/Linux temporary scripts in `$XDG_RUNTIME_DIR`. #209 [Juan Cruz Viotti]
## v5.1.7 - 2017-08-28
* Ensure all volume handles get closed on Windows. #212 [Juan Cruz Viotti]
## v5.1.6 - 2017-08-28
* Fix(windows): log the volume we're working on #211 [Juan Cruz Viotti]
## v5.1.5 - 2017-08-14
* Correctly handle BitLocker locked Windows volumes. #206 [Juan Cruz Viotti]
## v5.1.4 - 2017-08-07
* Get more detailed error codes from the Windows implementation. [Juan Cruz Viotti]
## v5.1.3 - 2017-08-01
* Fix "Couldn't scan the drives: An unknown error occurred" where there is a mounted virtual drive. [Juan Cruz Viotti]
## v5.1.2 - 2017-08-01
* Fix "Couldn't initialize the scanner: An unknown error occurred" error. [Juan Cruz Viotti]
## v5.1.1 - 2017-08-01
* Refactor(windows): don't use COM/WMI to retrieve a drive media type [Juan Cruz Viotti]
## v5.1.0 - 2017-07-31
* Feat: add a `displayName` property to drive objects [Juan Cruz Viotti]
## v5.0.28 - 2017-07-31
* Feat(windows): print HRESULT hex in debug information [Juan Cruz Viotti]
## v5.0.27 - 2017-07-19
* Fix: small typo in EAGAIN error message [Juan Cruz Viotti]
## v5.0.26 - 2017-07-18
* Fix: handle EAGAIN spawning errors [Juan Cruz Viotti]
* Refactor: reduce debug logging if unlink was successful [Juan Cruz Viotti]
## v5.0.25 - 2017-06-29
* Feat(windows): print debugging information when DRIVELIST_DEBUG is set [Juan Cruz Viotti]
* Fix: property clean up tmp file after exec [Jonas Hermsmeier]
* Fix(windows): ERROR_NOT_READY on empty SD Card reader [Juan Cruz Viotti]
* Chore(package): Fix missing EOL in package.json [Jonas Hermsmeier]
* Fix(windows): all drives are interpreted as read-only [Juan Cruz Viotti]
* Refactor(windows): don't use WMI to list volumes [Juan Cruz Viotti]
* Fix(windows): detect read-only flag on SD Cards without a filesystem (#183) [Juan Cruz Viotti]
* Refactor(windows): Use an IOCTL to determine the partition type (#181) [Juan Cruz Viotti]
## v5.0.24 - 2017-06-27
* Refactor(windows): implement volume::GetType() [Juan Cruz Viotti]
## v5.0.23 - 2017-06-15
* Upgrade(package): Bump dependencies [Jonas Hermsmeier]
* Doc(README): Add npm monthly download count [Jonas Hermsmeier]
* Adapt changelog for versionbot [Kostas Lekkas]
* Doc(README): Fix Travis link, use Etcher chat [Jonas Hermsmeier]
## v5.0.22
- Fix perl taking all the CPU in macOS in some cases
## v5.0.21
- Join volume and device names as descriptions if they both exists on macOS
## v5.0.20
- Inline platform scripts as a JSON file.
## v5.0.19
- Correctly deal with unicode characters coming from drive descriptions
- Correctly deal with invalid escape sequences coming from drive descriptions
## v5.0.18
- Fix mountpoints getting cut if they contain a `#` in GNU/Linux and macOS
## v5.0.17
- Fix literal `\040` on mountpoints that contain spaces in GNU/Linux
## v5.0.16
- Fix awk expression that replaces periods with underscores before evaluating
`udevadm` output in GNU/Linux.
## v5.0.15
- Increase debug logging in the case of script errors
- Prevent errors when unplugging drives while the scripts are running
## v5.0.14
- Catch the exit code of the wrapped VBScript on Windows.
## v5.0.13
**Important:** v5.0.11 and v5.0.12 were both published to NPM from a Windows
machine. This messed up the execution permissions of the drive detection
scripts, causing `EACCES` errors.
This version is equal to v5.0.12, but was released from a UNIX based system.
## v5.0.12 [BROKEN]
- Ensure the Windows script has execution permissions.
## v5.0.11 [BROKEN]
- Omit empty SD Card readers on Windows.
## v5.0.10
- Don't include drive letters assigned to no file system in Windows mount points.
## v5.0.9
- Print platform scripts `stderr` output to a debug channel instead of throwing if the script exitted with code zero.
- Fix bug where only the first mount point is reported on GNU/Linux.
- Ensure every potentially problematic value is double quoted in the GNU/Linux and macOS scripts.
## v5.0.8
- Fix "is not recognised as an internal or external command" errors on Windows.
## v5.0.7
- Interpret "Fixed" drives as system drives in macOS.
## v5.0.6
- Fix uncaught error caused by a weird Windows configuration edge where multiple partitions were reported to have the same drive letter.
## v5.0.5
- Fix system drives being detected as removable drives on Mac Mini.
## v5.0.4
- Replace periods with underscores before evaluating `udevadm` output in GNU/Linux.
## v5.0.3
- Fix `drivelist` not detecting mountpoints in Windows due to a space/tab YAML parsing issue.
## v5.0.2
- Don't require .NET VBScript classes in Windows platform script.
## v5.0.1
- Fix `YAMLException: bad indentation of a sequence entry` Windows error.
## v5.0.0
- Transform `mountpoint` into an array of objects called `mountpoints`.
- Improve GNU/Linux detection performance.
- Correctly get SD Card readers descriptions in GNU/Linux.
## v4.0.0
- Ignore invalid YAML output from platform scripts.
- Group mount points of drives with multiple partitions in Windows.
- Switch project to JavaScript.
- Switch license to Apache 2.0.
## v3.3.4
- Ensure `%SYSTEMROOT%\System32` is in the `%PATH%` before calling `cscript.exe` on Windows.
## v3.3.3
- Default device name to device id if drive doesn't have an assigned letter in Windows.
## v3.3.2
- List all partition's mountpoints in OS X.
## v3.3.1
- Fix `blkid: command not found` in GNU/Linux.
## v3.3.0
- Fix `null` device when unplugging a device while detecting drives.
- Expose a "raw" property in all devices.
- Attempt to display the volume name as description in OS X.
## v3.2.6
- Fix internal removable drives being considered "system" in macOS Sierra.
## v3.2.5
- Fix YAML error when a description contains question marks in GNU/Linux.
## v3.2.4
- Fix missing sizes in macOS Sierra.
## v3.2.3
- Handle mountpoints with spaces in GNU/Linux.
## v3.2.2
- Check if drive is removable when the drive has no file system in Windows.
## v3.2.1
- Make use of `df` to determine mountpoints in GNU/Linux.
## v3.2.0
- Add a property called `protected`.
## v3.1.2
- Prevent certain system drives to be detected as removable in GNU/Linux.
## v3.1.1
- Fix issue that caused some hard drives to be listed as removable drives.
## v3.1.0
- Show non-mountable disk drives in Windows.
## v3.0.0
- Parse size as a number representing bytes.
## v2.0.14
- Detect mount points of drives mounted by UUID on GNU/Linux.
## v2.0.13
- Keep once decimal in Windows drive sizes.
## v2.0.12
- Make sure MacBook's internal SDCard readers are marked as removable.
## v2.0.11
- Escape double quotes in description to prevent YAML errors.
## v2.0.10
- Improve the way we detect system drives in Windows.
## v2.0.9
- Use `udev` to determine if a drive is removable on GNU/Linux.
- Mark internal and non-removable drives as system drives in OS X.
- Omit loop devices in GNU/Linux.
- Omit CD/DVD drives in GNU/Linux.
## v2.0.8
- Omit mounted DMG images in OS X.
## v2.0.7
- Add a display name property called `name`.
## v2.0.6
- Fix boolean values being surrounded by quotes.
## v2.0.5
- Trim trailing commas on GNU/Linux mountpoints.
## v2.0.4
- Support running inside Electron.
## v2.0.3
- Treat multiple mountpoint as a comma separated list of paths in GNU/Linux.
## v2.0.2
- Escape paths including spaces in unix based operating systems.
## v2.0.1
- Escape backslashes in Windows devices to avoid weird characters.
## v2.0.0
- Add example file for easy testing.
- Redesign Windows script to match the new output scheme.
- Refer to `osx` as Darwin everywhere.
- Implement GNU/Linux scanning bash script.
- Unify parsing between all supported operating systems.
- Add `system` property to returned drives.
- Remove `drivelist.isSystem()` predicate.
- Fix issue that caused some OS X drives to not be detected.
## v1.3.2
- Handle edge case where OS X drives contain a description within parenthesis in `diskutil list`.
## v1.3.1
- Surround Windows script path in double quotes to avoid issues with paths incuding white space.
## v1.3.0
- Add `mountpoint` attribute to drives.
## v1.2.2
- Fix issue where a removable drive was detected as a system drive in Linux.
## v1.2.1
- Fix win32 issue where DeviceID gets part of the device description.
## v1.2.0
- Implement isSystem predicate.
## v1.1.2
- Prevent empty lsblk model crash the module. Return `undefined` description instead.
## v1.1.1
- Prevent empty wmic size crash the module. Return `undefined` size instead.
## v1.1.0
- Return non supported OS error to the callback instead of just throwing it.

177
dependencies/drivelist/LICENSE vendored Normal file
View file

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

218
dependencies/drivelist/README.md vendored Normal file
View file

@ -0,0 +1,218 @@
<!-- Make sure you edit doc/README.hbs rather than README.md because the latter is auto-generated -->
drivelist
=========
> List all connected drives in your computer, in all major operating systems.
[![Current Release](https://img.shields.io/npm/v/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![License](https://img.shields.io/npm/l/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![Downloads](https://img.shields.io/npm/dm/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![Travis CI status](https://img.shields.io/travis/balena-io-modules/drivelist/master.svg?style=flat-square&label=linux)](https://travis-ci.org/resin-io-modules/drivelist/branches)
[![AppVeyor status](https://img.shields.io/appveyor/ci/balena-io/drivelist/master.svg?style=flat-square&label=windows)](https://ci.appveyor.com/project/resin-io/drivelist/branch/master)
[![Dependency status](https://img.shields.io/david/balena-io-modules/drivelist.svg?style=flat-square)](https://david-dm.org/resin-io-modules/drivelist)
[![Gitter Chat](https://img.shields.io/gitter/room/balena-io/etcher.svg?style=flat-square)](https://gitter.im/resin-io/etcher)
Notice that this module **does not require** admin privileges to get the drives in any supported operating system.
Supports:
- Windows.
- GNU/Linux distributions that include [util-linux](https://github.com/karelzak/util-linux) and [udev](https://wiki.archlinux.org/index.php/udev).
- Mac OS X.
When the user executes `drivelist.list()`, the module checks the operating
system of the client and executes the corresponding drive scanning script.
Examples (the output will vary depending on your machine):
```js
const drivelist = require('drivelist');
const drives = await drivelist.list();
console.log(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
}
]
```
***
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
}
]
```
***
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
}
]
```
Installation
------------
Install `drivelist` by running:
```sh
$ npm install --save drivelist
```
Documentation
-------------
<a name="module_drivelist..list"></a>
### drivelist~list() ⇒ <code>Promise</code>
**Kind**: inner method of [<code>drivelist</code>](#module_drivelist)
**Summary**: List available drives
**Returns**: <code>Promise</code> - <Drive>[]
**Access**: public
**Example**
```js
const drivelist = require('drivelist');
const drives = await drivelist.list();
drives.forEach((drive) => {
console.log(drive);
});
```
Tests
-----
Run the test suite by doing:
```sh
$ npm test
```
Contribute
----------
We're looking forward to support more operating systems. Please raise an issue or even better, send a PR to increase support!
- Issue Tracker: [github.com/balena-io-modules/drivelist/issues](https://github.com/resin-io-modules/drivelist/issues)
- Source Code: [github.com/balena-io-modules/drivelist](https://github.com/resin-io-modules/drivelist)
Before submitting a PR, please make sure that you include tests, and that the linter runs without any warning:
```sh
$ npm run lint
```
Support
-------
If you're having any problem, please [raise an issue](https://github.com/balena-io-modules/drivelist/issues/new) on GitHub.
License
-------
The project is licensed under the Apache 2.0 license.
[yaml]: http://yaml.org

38
dependencies/drivelist/appveyor.yml vendored Normal file
View file

@ -0,0 +1,38 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
image:
- Visual Studio 2015
init:
- git config --global core.autocrlf input
cache:
- C:\Users\appveyor\.node-gyp
- '%AppData%\npm-cache'
# what combinations to test
environment:
matrix:
- nodejs_version: 10
- nodejs_version: 8
- nodejs_version: 6
- nodejs_version: 4
platform:
- x86
- x64
install:
- pip install -r requirements.txt
- ps: Install-Product node $env:nodejs_version x64
- npm install
build: off
test_script:
- node --version
- npm --version
- cmd: npm test
deploy_script:
- ci\prebuild-publish.bat %nodejs_version%

62
dependencies/drivelist/binding.gyp vendored Normal file
View file

@ -0,0 +1,62 @@
{
"targets": [
{
"target_name": "drivelist",
"include_dirs" : [
"<!(node -e \"require('nan')\")",
"."
],
"sources": [
"src/drivelist.cpp",
"src/device-descriptor.cpp",
],
"msvs_settings": {
"VCLinkerTool": {
"SetChecksum": "true"
},
"VCCLCompilerTool": {
"ExceptionHandling": 1,
"AdditionalOptions": [
"/EHsc"
]
}
},
"conditions": [
[ 'OS=="mac"', {
"xcode_settings": {
"OTHER_CPLUSPLUSFLAGS": [
"-stdlib=libc++"
],
"OTHER_LDFLAGS": [
"-stdlib=libc++"
]
},
"sources": [
"src/darwin/list.mm",
"src/darwin/REDiskList.m"
],
"link_settings": {
"libraries": [
"-framework Carbon,DiskArbitration"
]
}
}],
[ 'OS=="win"', {
"sources": [
"src/windows/list.cpp"
],
"libraries": [
"-lKernel32.lib",
"-lShell32.lib",
"-lSetupAPI.lib"
]
}],
[ 'OS=="linux"', {
"sources": [
"src/linux/list.cpp"
]
}]
]
}
]
}

View file

@ -0,0 +1,9 @@
@echo off
if %1 == 10 (
if %APPVEYOR_REPO_BRANCH% == master (
if %GITHUB_TOKEN% neq "" (
npm run prebuild-release -- -u %GITHUB_TOKEN%
)
)
)

View file

@ -0,0 +1,5 @@
#!/bin/bash
if [[ $GITHUB_TOKEN ]]; then
npm run prebuild-release -- -u "$GITHUB_TOKEN"
fi

208
dependencies/drivelist/doc/README.hbs vendored Normal file
View file

@ -0,0 +1,208 @@
<!-- Make sure you edit doc/README.hbs rather than README.md because the latter is auto-generated -->
drivelist
=========
> List all connected drives in your computer, in all major operating systems.
[![Current Release](https://img.shields.io/npm/v/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![License](https://img.shields.io/npm/l/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![Downloads](https://img.shields.io/npm/dm/drivelist.svg?style=flat-square)](https://npmjs.com/package/drivelist)
[![Travis CI status](https://img.shields.io/travis/balena-io-modules/drivelist/master.svg?style=flat-square&label=linux)](https://travis-ci.org/resin-io-modules/drivelist/branches)
[![AppVeyor status](https://img.shields.io/appveyor/ci/balena-io/drivelist/master.svg?style=flat-square&label=windows)](https://ci.appveyor.com/project/resin-io/drivelist/branch/master)
[![Dependency status](https://img.shields.io/david/balena-io-modules/drivelist.svg?style=flat-square)](https://david-dm.org/resin-io-modules/drivelist)
[![Gitter Chat](https://img.shields.io/gitter/room/balena-io/etcher.svg?style=flat-square)](https://gitter.im/resin-io/etcher)
Notice that this module **does not require** admin privileges to get the drives in any supported operating system.
Supports:
- Windows.
- GNU/Linux distributions that include [util-linux](https://github.com/karelzak/util-linux) and [udev](https://wiki.archlinux.org/index.php/udev).
- Mac OS X.
When the user executes `drivelist.list()`, the module checks the operating
system of the client and executes the corresponding drive scanning script.
Examples (the output will vary depending on your machine):
```js
const drivelist = require('drivelist');
const drives = await drivelist.list();
console.log(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
}
]
```
***
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
}
]
```
***
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
}
]
```
Installation
------------
Install `drivelist` by running:
```sh
$ npm install --save drivelist
```
Documentation
-------------
{{#module name="drivelist"}}
{{>body~}}
{{>member-index~}}
{{>separator~}}
{{>members~}}
{{/module}}
Tests
-----
Run the test suite by doing:
```sh
$ npm test
```
Contribute
----------
We're looking forward to support more operating systems. Please raise an issue or even better, send a PR to increase support!
- Issue Tracker: [github.com/balena-io-modules/drivelist/issues](https://github.com/resin-io-modules/drivelist/issues)
- Source Code: [github.com/balena-io-modules/drivelist](https://github.com/resin-io-modules/drivelist)
Before submitting a PR, please make sure that you include tests, and that the linter runs without any warning:
```sh
$ npm run lint
```
Support
-------
If you're having any problem, please [raise an issue](https://github.com/balena-io-modules/drivelist/issues/new) on GitHub.
License
-------
The project is licensed under the Apache 2.0 license.
[yaml]: http://yaml.org

36
dependencies/drivelist/example/index.ts vendored Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright 2017 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { inspect } from 'util';
import { list } from '..';
async function main() {
let drives;
try {
drives = await list();
} catch (error) {
console.error(error);
process.exitCode = 1;
return;
}
console.log(inspect(drives, {
colors: process.stdout.isTTY,
depth: null
}));
}
main();

85
dependencies/drivelist/lib/index.ts vendored Normal file
View file

@ -0,0 +1,85 @@
/*
* Copyright 2016 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @module drivelist
*/
import bindings = require('bindings');
import { platform } from 'os';
import { lsblk } from './lsblk';
export interface Mountpoint {
path: string;
label: string | null;
}
export interface Drive {
blockSize: number;
busType: string;
busVersion: null;
description: string;
device: string;
devicePath: string | null;
enumerator: string;
error: null;
isCard: null;
isReadOnly: boolean;
isRemovable: boolean;
isSCSI: boolean | null;
isSystem: boolean;
isUAS: null;
isUSB: boolean | null;
isVirtual: boolean | null;
logicalBlockSize: number;
mountpoints: Mountpoint[];
raw: string;
size: number | null;
}
/**
* @summary List available drives
* @function
* @public
*
* @returns {Promise} <Drive>[]
*
* @example
* const drivelist = require('drivelist');
*
* const drives = await drivelist.list();
* drives.forEach((drive) => {
* console.log(drive);
* });
*/
export async function list(): Promise<Drive[]> {
const plat = platform();
if (plat === 'win32' || plat === 'darwin') {
return new Promise((resolve, reject) => {
bindings('drivelist').list((error: Error, drives: Drive[]) => {
if (error != null) {
reject(error);
} else {
resolve(drives);
}
});
});
} else if (plat === 'linux') {
return await lsblk();
}
throw new Error(`Your OS is not supported by this module: ${platform()}`);
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2018 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { execFile } from 'mz/child_process';
import { readdir, readlink } from 'mz/fs';
import { join, resolve } from 'path';
import { Drive } from '..';
import { parse as parseJSON } from './json';
import { parse as parsePairs } from './pairs';
const DISK_PATH_DIR = '/dev/disk/by-path/';
let SUPPORTS_JSON = true;
async function getDevicePaths(): Promise<Map<string, string>> {
const mapping = new Map();
for (const filename of await readdir(DISK_PATH_DIR)) {
const linkPath = join(DISK_PATH_DIR, filename);
let link: string;
try {
link = await readlink(linkPath);
} catch (error) {
continue;
}
const devicePath = resolve(DISK_PATH_DIR, link);
mapping.set(devicePath, linkPath);
}
return mapping;
}
async function addDevicePaths(devices: Drive[]): Promise<void> {
const devicePaths = await getDevicePaths();
for (const device of devices) {
device.devicePath = devicePaths.get(device.device) || null;
}
}
async function getOutput(command: string, ...args: string[]) {
const [stdout] = await execFile(command, args);
return stdout;
}
async function lsblkJSON(): Promise<Drive[]> {
return parseJSON(
await getOutput(
'lsblk',
'--bytes',
'--all',
'--json',
'--paths',
'--output-all',
),
);
}
async function lsblkPairs(): Promise<Drive[]> {
return parsePairs(await getOutput('lsblk', '--bytes', '--all', '--pairs'));
}
async function $lsblk(): Promise<Drive[]> {
if (SUPPORTS_JSON) {
try {
return await lsblkJSON();
} catch (error) {
SUPPORTS_JSON = false;
}
}
return await lsblkPairs();
}
export async function lsblk(): Promise<Drive[]> {
const drives = await $lsblk();
try {
await addDevicePaths(drives);
} catch (error) {
// Couldn't add device paths
}
return drives;
}

155
dependencies/drivelist/lib/lsblk/json.ts vendored Normal file
View file

@ -0,0 +1,155 @@
/*
* Copyright 2018 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { posix } from 'path';
import { Drive, Mountpoint } from '..';
interface LsblkJsonOutput {
blockdevices: LsblkJsonOutputDevice[];
}
interface LsblkJsonOutputDevice {
children: LsblkJsonOutputDeviceChild[];
hotplug?: string;
kname?: string;
label: string | null;
'log-sec'?: string;
model: string | null;
mountpoint: string | null;
name: string;
partlabel: string | null;
'phy-sec'?: string;
rm?: string;
ro?: string;
size?: string;
subsystems?: string;
tran?: string;
vendor: string | null;
}
interface LsblkJsonOutputDeviceChild {
label: string | null;
mountpoint?: string;
partlabel: string | null;
}
function getMountpoints(
children: Array<LsblkJsonOutputDeviceChild | LsblkJsonOutputDevice>,
): Mountpoint[] {
return children
.filter(child => {
return child.mountpoint;
})
.map(child => {
return {
path: child.mountpoint!,
label: child.label || child.partlabel,
};
});
}
function getDescription(device: LsblkJsonOutputDevice): string {
const description = [
device.label || '',
device.vendor || '',
device.model || '',
];
if (device.children) {
let subLabels = device.children
.filter(c => (c.label && c.label !== device.label) || c.mountpoint)
.map(c => c.label || c.mountpoint);
subLabels = Array.from(new Set(subLabels));
if (subLabels.length) {
description.push(`(${subLabels.join(', ')})`);
}
}
return description
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}
function resolveDeviceName(name?: string): string | null {
if (!name) {
return null;
}
if (!posix.isAbsolute(name)) {
return posix.resolve('/dev', name);
}
return name;
}
export function transform(data: LsblkJsonOutput): Drive[] {
return data.blockdevices
.map(device =>
Object.assign({}, device, {
name: resolveDeviceName(device.name),
kname: resolveDeviceName(device.kname),
}),
)
.filter(
device =>
// Omit loop devices, CD/DVD drives, and RAM
!device.name.startsWith('/dev/loop') &&
!device.name.startsWith('/dev/sr') &&
!device.name.startsWith('/dev/ram'),
)
.map(
(device: LsblkJsonOutputDevice): Drive => {
const isVirtual = device.subsystems
? /^(block)$/i.test(device.subsystems)
: null;
const isSCSI = device.tran
? /^(sata|scsi|ata|ide|pci)$/i.test(device.tran)
: null;
const isUSB = device.tran ? /^(usb)$/i.test(device.tran) : null;
const isReadOnly = Number(device.ro) === 1;
const isRemovable =
Number(device.rm) === 1 ||
Number(device.hotplug) === 1 ||
Boolean(isVirtual);
return {
enumerator: 'lsblk:json',
busType: (device.tran || 'UNKNOWN').toUpperCase(),
busVersion: null,
device: device.name,
devicePath: null,
raw: device.kname || device.name,
description: getDescription(device),
error: null,
size: Number(device.size) || null,
blockSize: Number(device['phy-sec']) || 512,
logicalBlockSize: Number(device['log-sec']) || 512,
mountpoints: device.children
? getMountpoints(device.children)
: getMountpoints([device]),
isReadOnly,
isSystem: !isRemovable && !isVirtual,
isVirtual,
isRemovable,
isCard: null,
isSCSI,
isUSB,
isUAS: null,
};
},
);
}
export function parse(stdout: string): Drive[] {
return transform(JSON.parse(stdout));
}

View file

@ -0,0 +1,182 @@
/*
* Copyright 2018 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Drive, Mountpoint } from '..';
interface Dict<T> {
[K: string]: T;
}
function parseLsblkLine(line: string): Dict<string> {
const data: Dict<string> = {};
let offset = 0;
let key = '';
let value = '';
const keyChar = /[^"=]/;
const whitespace = /\s+/;
const escape = '\\';
let state = 'key';
while (offset < line.length) {
if (state === 'key') {
while (keyChar.test(line[offset])) {
key += line[offset];
offset += 1;
}
if (line[offset] === '=') {
state = 'value';
offset += 1;
}
} else if (state === 'value') {
if (line[offset] !== '"') {
throw new Error(`Expected '"', saw "${line[offset]}"`);
}
offset += 1;
while (line[offset] !== '"' && line[offset - 1] !== escape) {
value += line[offset];
offset += 1;
}
if (line[offset] !== '"') {
throw new Error(`Expected '"', saw "${line[offset]}"`);
}
offset += 1;
data[key.toLowerCase()] = value.trim();
key = '';
value = '';
state = 'space';
} else if (state === 'space') {
while (whitespace.test(line[offset])) {
offset += 1;
}
state = 'key';
} else {
throw new Error(`Undefined state "${state}"`);
}
}
return data;
}
function parseLsblk(output: string): Array<Dict<string>> {
return output
.trim()
.split(/\r?\n/g)
.map(parseLsblkLine);
}
function consolidate(
devices: Array<Dict<string>>,
): Array<Dict<string> & { mountpoints: Mountpoint[] }> {
const primaries = devices.filter(device => {
return (
device.type === 'disk' &&
!device.name.startsWith('ram') &&
!device.name.startsWith('sr')
);
});
return primaries.map(device => {
return Object.assign({}, device, {
mountpoints: devices
.filter(child => {
return (
['disk', 'part'].includes(child.type) &&
child.mountpoint &&
child.name.startsWith(device.name)
);
})
.map(
(child): Mountpoint => {
return {
path: child.mountpoint,
label: child.label,
};
},
),
});
});
}
function getDescription(
device: Dict<string> & { mountpoints: Mountpoint[] },
): string {
const description = [
device.label || '',
device.vendor || '',
device.model || '',
];
if (device.mountpoints.length) {
let subLabels = device.mountpoints
.filter(c => {
return (c.label && c.label !== device.label) || c.path;
})
.map(c => {
return c.label || c.path;
});
subLabels = Array.from(new Set(subLabels));
if (subLabels.length) {
description.push(`(${subLabels.join(', ')})`);
}
}
return description
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}
export function parse(stdout: string): Drive[] {
const devices = consolidate(parseLsblk(stdout));
return devices.map(
(device: Dict<string> & { mountpoints: Mountpoint[] }): Drive => {
const isVirtual = device.subsystems
? /^block$/i.test(device.subsystems)
: null;
const isSCSI = device.tran
? /^(?:sata|scsi|ata|ide|pci)$/i.test(device.tran)
: null;
const isUSB = device.tran ? /^usb$/i.test(device.tran) : null;
const isReadOnly = Number(device.ro) === 1;
const isRemovable =
Number(device.rm) === 1 ||
Number(device.hotplug) === 1 ||
Boolean(isVirtual);
return {
enumerator: 'lsblk:pairs',
busType: (device.tran || 'UNKNOWN').toUpperCase(),
busVersion: null,
device: '/dev/' + device.name,
devicePath: null,
raw: '/dev/' + device.name,
description: getDescription(device) || device.name,
error: null,
size: Number(device.size) || null,
blockSize: Number(device['phy-sec']) || 512,
logicalBlockSize: Number(device['log-sec']) || 512,
mountpoints: device.mountpoints,
isReadOnly,
isSystem: !isRemovable && !isVirtual,
isVirtual,
isRemovable,
isCard: null,
isSCSI,
isUSB,
isUAS: null,
};
},
);
}

66
dependencies/drivelist/package.json vendored Normal file
View file

@ -0,0 +1,66 @@
{
"name": "drivelist",
"version": "8.0.9",
"description": "List all connected drives in your computer, in all major operating systems",
"main": "js/index.js",
"homepage": "https://github.com/balena-io-modules/drivelist",
"gypfile": true,
"repository": {
"type": "git",
"url": "git://github.com/balena-io-modules/drivelist.git"
},
"keywords": [
"disk",
"cross",
"platform",
"physical",
"drive",
"list"
],
"directories": {
"test": "tests"
},
"scripts": {
"test": "mocha -r ts-node/register tests/**/*.spec.ts -R spec",
"lint": "npm run lint-cpp && npm run lint-ts",
"lint-cpp": "cpplint --recursive src",
"lint-ts": "resin-lint --typescript lib tests",
"prettier": "prettier --config ./node_modules/resin-lint/config/.prettierrc --write \"lib/**/*.ts\" \"tests/**/*.ts\"",
"readme": "jsdoc2md --template doc/README.hbs js/index.js > README.md",
"configure": "node-gyp configure",
"build": "node-gyp rebuild",
"build-ts": "tsc",
"rebuild": "node-gyp rebuild",
"install": "prebuild-install || node-gyp rebuild",
"prebuild-release": "prebuild --all --strip",
"prepublishOnly": "npm run build-ts && npm run readme"
},
"author": "Juan Cruz Viotti <juan@balena.io>",
"license": "Apache-2.0",
"engines": {
"node": ">=6"
},
"devDependencies": {
"@types/bindings": "^1.3.0",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.6",
"@types/mz": "0.0.32",
"@types/sinon": "^7.0.6",
"chai": "^4.2.0",
"eslint": "^4.19.1",
"jsdoc-to-markdown": "^4.0.1",
"mocha": "^6.0.0",
"prebuild": "^8.1.2",
"resin-lint": "^3.0.1",
"sinon": "^7.2.4",
"ts-node": "^8.0.2",
"typescript": "^3.2.4"
},
"dependencies": {
"bindings": "^1.3.0",
"debug": "^3.1.0",
"mz": "^2.7.0",
"nan": "^2.14.0",
"prebuild-install": "^5.2.4"
}
}

View file

@ -0,0 +1 @@
cpplint==1.3.0

View file

@ -0,0 +1,41 @@
/*
* Copyright 2019 balena.io
* Copyright 2018 Robin Andersson <me@robinwassen.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_DARWIN_REDISKLIST_H_
#define SRC_DARWIN_REDISKLIST_H_
#import <Foundation/Foundation.h>
/**
* Class to return a list of disks synchronously
* To use the class, just init an instance of it and
* it will populate the disks property with NSStrings
*
* @author Robin Andersson
*/
@interface REDiskList : NSObject
/**
* NSArray of disks and partitions
* Disks are in the format disk0, disk1 etc
* Partitions are in the format disk0s1, disk1s1 etc
*/
@property(readonly) NSArray *disks;
@end
#endif // SRC_DARWIN_REDISKLIST_H_

View file

@ -0,0 +1,61 @@
/*
* Copyright 2019 balena.io
* Copyright 2018 Robin Andersson <me@robinwassen.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "REDiskList.h"
#import <DiskArbitration/DiskArbitration.h>
@implementation REDiskList
- (id)init {
self = [super init];
if (self) {
_disks = [[NSMutableArray alloc] init];
[self populateDisksBlocking];
[_disks sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
}
return self;
}
-(void)dealloc {
[_disks release];
[super dealloc];
}
void appendDisk(DADiskRef disk, void *context) {
NSMutableArray *_disks = (__bridge NSMutableArray*)context;
const char *bsdName = DADiskGetBSDName(disk);
if (bsdName != nil) {
[_disks addObject:[NSString stringWithUTF8String:bsdName]];
}
}
- (void)populateDisksBlocking {
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
if (session) {
DARegisterDiskAppearedCallback(session, NULL, appendDisk, (void*)_disks);
CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
DASessionScheduleWithRunLoop(session, runLoop, kCFRunLoopDefaultMode);
CFRunLoopStop(runLoop);
CFRunLoopRunInMode((CFStringRef)NSDefaultRunLoopMode, 0.05, NO);
DAUnregisterCallback(session, appendDisk, (void*)_disks);
CFRelease(session);
}
}
@end

View file

@ -0,0 +1,179 @@
/*
* Copyright 2019 balena.io
* Copyright 2018 Robin Andersson <me@robinwassen.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nan.h>
#include "../drivelist.hpp"
#import "REDiskList.h"
#import <Cocoa/Cocoa.h>
#import <DiskArbitration/DiskArbitration.h>
namespace Drivelist {
bool IsDiskPartition(NSString *disk) {
NSPredicate *partitionRegEx = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"disk\\d+s\\d+"];
return [partitionRegEx evaluateWithObject:disk];
}
bool IsCard(CFDictionaryRef diskDescription) {
CFDictionaryRef mediaIconDict = (CFDictionaryRef)CFDictionaryGetValue(
diskDescription,
kDADiskDescriptionMediaIconKey
);
if (mediaIconDict == nil) {
return false;
}
CFStringRef iconFileNameKeyRef = CFStringCreateWithCString(NULL, "IOBundleResourceFile", kCFStringEncodingUTF8);
CFStringRef iconFileNameRef = (CFStringRef)CFDictionaryGetValue(mediaIconDict, iconFileNameKeyRef);
CFRelease(iconFileNameKeyRef);
if (iconFileNameRef == nil) {
return false;
}
// macOS 10.14.3 - External SD card reader provides `Removable.icns`, not `SD.icns`.
// But we can't use it to detect SD card, because external drive has `Removable.icns` as well.
return [(NSString *)iconFileNameRef isEqualToString:@"SD.icns"];
}
NSNumber *DictionaryGetNumber(CFDictionaryRef dict, const void *key) {
return (NSNumber*)CFDictionaryGetValue(dict, key);
}
DeviceDescriptor CreateDeviceDescriptorFromDiskDescription(std::string diskBsdName, CFDictionaryRef diskDescription) {
NSString *deviceProtocol = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey);
NSNumber *blockSize = DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaBlockSizeKey);
bool isInternal = [DictionaryGetNumber(diskDescription, kDADiskDescriptionDeviceInternalKey) boolValue];
bool isRemovable = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaRemovableKey) boolValue];
bool isEjectable = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaEjectableKey) boolValue];
DeviceDescriptor device = DeviceDescriptor();
device.enumerator = "DiskArbitration";
device.busType = (deviceProtocol != nil) ? [deviceProtocol UTF8String] : "";
device.busVersion = "";
device.busVersionNull = true;
device.device = "/dev/" + diskBsdName;
NSString *devicePath = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionBusPathKey);
device.devicePath = (devicePath != nil) ? [devicePath UTF8String] : "";
device.raw = "/dev/r" + diskBsdName;
NSString *description = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionMediaNameKey);
device.description = (description != nil) ? [description UTF8String] : "";
device.error = "";
// NOTE: Not sure if kDADiskDescriptionMediaBlockSizeKey returns
// the physical or logical block size since both values are equal
// on my machine
//
// The can be checked with the following command:
// diskutil info / | grep "Block Size"
device.blockSize = [blockSize unsignedIntValue];
device.logicalBlockSize = [blockSize unsignedIntValue];
device.size = [DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaSizeKey) unsignedLongValue];
device.isReadOnly = ![DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaWritableKey) boolValue];
device.isSystem = isInternal && !isRemovable;
device.isVirtual = ((deviceProtocol != nil) && [deviceProtocol isEqualToString:@"Virtual Interface"]);
device.isRemovable = isRemovable || isEjectable;
device.isCard = IsCard(diskDescription);
// NOTE(robin): Not convinced that these bus types should result
// in device.isSCSI = true, it is rather "not usb or sd drive" bool
// But the old implementation was like this so kept it this way
NSArray *scsiTypes = [NSArray arrayWithObjects:@"SATA", @"SCSI", @"ATA", @"IDE", @"PCI", nil];
device.isSCSI = ((deviceProtocol != nil) && [scsiTypes containsObject:deviceProtocol]);
device.isUSB = ((deviceProtocol != nil) && [deviceProtocol isEqualToString:@"USB"]);
device.isUAS = false;
device.isUASNull = true;
return device;
}
std::vector<DeviceDescriptor> ListStorageDevices() {
std::vector<DeviceDescriptor> deviceList;
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
if (session == nil) {
return deviceList;
}
REDiskList *dl = [[REDiskList alloc] init];
for (NSString* diskBsdName in dl.disks) {
if (IsDiskPartition(diskBsdName)) {
continue;
}
std::string diskBsdNameStr = [diskBsdName UTF8String];
DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, diskBsdNameStr.c_str());
if (disk == nil) {
continue;
}
CFDictionaryRef diskDescription = DADiskCopyDescription(disk);
if (diskDescription == nil) {
CFRelease(disk);
continue;
}
DeviceDescriptor device = CreateDeviceDescriptorFromDiskDescription(diskBsdNameStr, diskDescription);
deviceList.push_back(device);
CFRelease(diskDescription);
CFRelease(disk);
}
[dl release];
// Add mount points
NSArray *volumeKeys = [NSArray arrayWithObjects:NSURLVolumeNameKey, NSURLVolumeLocalizedNameKey, nil];
NSArray *volumePaths = [
[NSFileManager defaultManager]
mountedVolumeURLsIncludingResourceValuesForKeys:volumeKeys
options:0
];
for (NSURL *path in volumePaths) {
DADiskRef disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, (__bridge CFURLRef)path);
if (disk == nil) {
continue;
}
const char *bsdnameChar = DADiskGetBSDName(disk);
if (bsdnameChar == nil) {
CFRelease(disk);
continue;
}
NSString *volumeName;
[path getResourceValue:&volumeName forKey:NSURLVolumeLocalizedNameKey error:nil];
std::string partitionBsdName = std::string(bsdnameChar);
std::string diskBsdName = partitionBsdName.substr(0, partitionBsdName.find("s", 5));
for(std::vector<int>::size_type i = 0; i != deviceList.size(); i++) {
DeviceDescriptor *dd = &deviceList[i];
if (dd->device == "/dev/" + diskBsdName) {
dd->mountpoints.push_back([[path path] UTF8String]);
dd->mountpointLabels.push_back([volumeName UTF8String]);
break;
}
}
CFRelease(disk);
}
CFRelease(session);
return deviceList;
}
} // namespace Drivelist

View file

@ -0,0 +1,152 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nan.h>
#include "drivelist.hpp"
using v8::String;
using v8::Number;
using v8::Boolean;
using v8::Local;
using v8::Value;
using Nan::New;
namespace Drivelist {
v8::Local<v8::Object> PackDriveDescriptor(const DeviceDescriptor *instance) {
v8::Local<v8::Object> object = Nan::New<v8::Object>();
Nan::Set(object,
New<String>("enumerator").ToLocalChecked(),
New<String>(instance->enumerator).ToLocalChecked());
Nan::Set(object,
New<String>("busType").ToLocalChecked(),
New<String>(instance->busType).ToLocalChecked());
Local<Value> busVersion = instance->busVersionNull ?
(Local<Value>)Nan::Null() :
(Local<Value>)New<String>(instance->busVersion).ToLocalChecked();
Nan::Set(object,
New<String>("busVersion").ToLocalChecked(),
busVersion);
Nan::Set(object,
New<String>("device").ToLocalChecked(),
New<String>(instance->device).ToLocalChecked());
Local<Value> devicePath = instance->devicePathNull ?
(Local<Value>)Nan::Null() :
(Local<Value>)New<String>(instance->devicePath).ToLocalChecked();
Nan::Set(object,
New<String>("devicePath").ToLocalChecked(),
devicePath);
Nan::Set(object,
New<String>("raw").ToLocalChecked(),
New<String>(instance->raw).ToLocalChecked());
Nan::Set(object,
New<String>("description").ToLocalChecked(),
New<String>(instance->description).ToLocalChecked());
if (instance->error != "") {
Nan::Set(object,
New<String>("error").ToLocalChecked(),
New<String>(instance->error).ToLocalChecked());
} else {
Nan::Set(object,
New<String>("error").ToLocalChecked(),
Nan::Null());
}
Nan::Set(object,
New<String>("size").ToLocalChecked(),
New<Number>(static_cast<double>(instance->size)));
Nan::Set(object,
New<String>("blockSize").ToLocalChecked(),
New<Number>(static_cast<double>(instance->blockSize)));
Nan::Set(object,
New<String>("logicalBlockSize").ToLocalChecked(),
New<Number>(static_cast<double>(instance->logicalBlockSize)));
v8::Local<v8::Object> mountpoints = Nan::New<v8::Array>();
uint32_t index = 0;
for (std::string mountpointPath : instance->mountpoints) {
v8::Local<v8::Object> mountpoint = Nan::New<v8::Object>();
Nan::Set(mountpoint,
New<String>("path").ToLocalChecked(),
New<String>(mountpointPath).ToLocalChecked());
if (index < instance->mountpointLabels.size()) {
Nan::Set(mountpoint,
New<String>("label").ToLocalChecked(),
New<String>(instance->mountpointLabels[index]).ToLocalChecked());
}
Nan::Set(mountpoints, index, mountpoint);
index++;
}
Nan::Set(object,
New<String>("mountpoints").ToLocalChecked(),
mountpoints);
Nan::Set(object,
New<String>("isReadOnly").ToLocalChecked(),
New<Boolean>(instance->isReadOnly));
Nan::Set(object,
New<String>("isSystem").ToLocalChecked(),
New<Boolean>(instance->isSystem));
Nan::Set(object,
New<String>("isVirtual").ToLocalChecked(),
New<Boolean>(instance->isVirtual));
Nan::Set(object,
New<String>("isRemovable").ToLocalChecked(),
New<Boolean>(instance->isRemovable));
Nan::Set(object,
New<String>("isCard").ToLocalChecked(),
New<Boolean>(instance->isCard));
Nan::Set(object,
New<String>("isSCSI").ToLocalChecked(),
New<Boolean>(instance->isSCSI));
Nan::Set(object,
New<String>("isUSB").ToLocalChecked(),
New<Boolean>(instance->isUSB));
Local<Value> isUAS = instance->isUASNull ?
(Local<Value>)Nan::Null() :
(Local<Value>)New<Boolean>(instance->isUAS);
Nan::Set(object,
New<String>("isUAS").ToLocalChecked(),
isUAS);
return object;
}
} // namespace Drivelist

View file

@ -0,0 +1,66 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nan.h>
#include <vector>
#include "drivelist.hpp"
class DriveListWorker : public Nan::AsyncWorker {
public:
explicit DriveListWorker(Nan::Callback *callback)
: Nan::AsyncWorker(callback), devices() {}
~DriveListWorker() {}
void Execute() {
devices = Drivelist::ListStorageDevices();
}
void HandleOKCallback() {
Nan::HandleScope scope;
v8::Local<v8::Object> drives = Nan::New<v8::Array>();
uint32_t i;
uint32_t size = (uint32_t) devices.size();
for (i = 0; i < size; i++) {
Nan::Set(drives, i, Drivelist::PackDriveDescriptor(&devices[i]));
}
v8::Local<v8::Value> argv[] = { Nan::Null(), drives };
callback->Call(2, argv, async_resource);
}
private:
std::vector<Drivelist::DeviceDescriptor> devices;
};
NAN_METHOD(list) {
if (!info[0]->IsFunction()) {
return Nan::ThrowTypeError("Callback must be a function");
}
Nan::Callback *callback = new Nan::Callback(info[0].As<v8::Function>());
Nan::AsyncQueueWorker(new DriveListWorker(callback));
info.GetReturnValue().SetUndefined();
}
NAN_MODULE_INIT(InitAll) {
NAN_EXPORT(target, list);
}
NODE_MODULE(DriveList, InitAll);

View file

@ -0,0 +1,64 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_DRIVELIST_HPP_
#define SRC_DRIVELIST_HPP_
#include <nan.h>
#include <string>
#include <vector>
namespace Drivelist {
struct MountPoint {
std::string path;
};
struct DeviceDescriptor {
std::string enumerator;
std::string busType;
std::string busVersion;
bool busVersionNull;
std::string device;
std::string devicePath;
bool devicePathNull;
std::string raw;
std::string description;
std::string error;
uint64_t size;
uint32_t blockSize = 512;
uint32_t logicalBlockSize = 512;
std::vector<std::string> mountpoints;
std::vector<std::string> mountpointLabels;
bool isReadOnly; // Device is read-only
bool isSystem; // Device is a system drive
bool isVirtual; // Device is a virtual storage device
bool isRemovable; // Device is removable from the running system
bool isCard; // Device is an SD-card
bool isSCSI; // Connected via the Small Computer System Interface (SCSI)
bool isUSB; // Connected via Universal Serial Bus (USB)
bool isUAS; // Connected via the USB Attached SCSI (UAS)
bool isUASNull;
};
std::vector<DeviceDescriptor> ListStorageDevices();
v8::Local<v8::Object> PackDriveDescriptor(const DeviceDescriptor *instance);
} // namespace Drivelist
NAN_METHOD(list);
#endif // SRC_DRIVELIST_HPP_

View file

@ -0,0 +1,28 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nan.h>
#include "../drivelist.hpp"
namespace Drivelist {
// TODO(jhermsmeier): Implement
std::vector<DeviceDescriptor> ListStorageDevices() {
std::vector<DeviceDescriptor> drivelist;
return drivelist;
}
} // namespace Drivelist

View file

@ -0,0 +1,680 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// See https://support.microsoft.com/en-us/kb/165721
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <winioctl.h>
#include <cfgmgr32.h>
#include <setupapi.h>
#include <shlobj.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <nan.h>
#include <string>
#include <vector>
#include <set>
#include "../drivelist.hpp"
#include "list.hpp"
#include <devpkey.h>
// Maxnet edit
#ifndef DEVPKEY_Device_EnumeratorName
DEFINE_DEVPROPKEY(DEVPKEY_Device_EnumeratorName, 0xa45c254e,0xdf1c,0x4efd,0x80,0x20,0x67,0xd1,0x46,0xa8,0x50,0xe0, 24);
#endif
//
namespace Drivelist {
char* WCharToUtf8(const wchar_t* wstr) {
char *str = NULL;
size_t size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
if (size <= 1) {
return NULL;
}
if ((str = reinterpret_cast<char*>(calloc(size, 1))) == NULL) {
return NULL;
}
size_t utf8Size = WideCharToMultiByte(
CP_UTF8, 0, wstr, -1, str, size, NULL, NULL);
if (utf8Size != size) {
free(str);
return NULL;
}
return str;
}
char* GetEnumeratorName(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
char buffer[MAX_PATH];
ZeroMemory(&buffer, sizeof(buffer));
BOOL hasEnumeratorName = SetupDiGetDeviceRegistryPropertyA(
hDeviceInfo, &deviceInfoData, SPDRP_ENUMERATOR_NAME,
NULL, (LPBYTE) buffer, sizeof(buffer), NULL);
/*if (!hasEnumeratorName)
{
hasEnumeratorName = SetupDiGetDevicePropertyW(hDeviceInfo, &deviceInfoData, &DEVPKEY_Device_EnumeratorName, NULL, (LPBYTE) buffer, sizeof(buffer), NULL, 0);
}*/
return hasEnumeratorName ? buffer : NULL;
}
std::string GetFriendlyName(HDEVINFO hDeviceInfo,
SP_DEVINFO_DATA deviceInfoData) {
wchar_t wbuffer[MAX_PATH];
ZeroMemory(&wbuffer, sizeof(wbuffer));
BOOL hasFriendlyName = SetupDiGetDeviceRegistryPropertyW(
hDeviceInfo, &deviceInfoData, SPDRP_FRIENDLYNAME,
NULL, (PBYTE) wbuffer, sizeof(wbuffer), NULL);
return hasFriendlyName ? WCharToUtf8(wbuffer) : std::string("");
}
bool IsSCSIDevice(std::string enumeratorName) {
for (std::string driverName : GENERIC_STORAGE_DRIVERS) {
if (enumeratorName == driverName) {
return true;
}
}
return false;
}
bool IsUSBDevice(std::string enumeratorName) {
for (std::string driverName : USB_STORAGE_DRIVERS) {
if (enumeratorName == driverName) {
return true;
}
}
return false;
}
bool IsRemovableDevice(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
DWORD result = 0;
BOOL hasRemovalPolicy = SetupDiGetDeviceRegistryProperty(
hDeviceInfo, &deviceInfoData, SPDRP_REMOVAL_POLICY,
NULL, (PBYTE) &result, sizeof(result), NULL);
switch (result) {
case CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL:
case CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL:
return true;
default:
return false;
}
return false;
}
bool IsVirtualHardDrive(HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
char buffer[MAX_PATH];
ZeroMemory(&buffer, sizeof(buffer));
BOOL hasHardwareId = SetupDiGetDeviceRegistryPropertyA(
hDeviceInfo, &deviceInfoData, SPDRP_HARDWAREID,
NULL, (LPBYTE) buffer, sizeof(buffer), NULL);
if (!hasHardwareId) {
return false;
}
// printf("SPDRP_HARDWAREID: %s\n", buffer);
std::string hardwareId(buffer);
for (std::string vhdHardwareId : VHD_HARDWARE_IDS) {
if (hardwareId.find(vhdHardwareId, 0) != std::string::npos) {
return true;
}
}
return false;
}
bool IsSystemDevice(HDEVINFO hDeviceInfo, DeviceDescriptor *device) {
PWSTR folderPath = NULL;
BOOL isSystem = false;
for (const GUID folderId : KNOWN_FOLDER_IDS) {
HRESULT result = SHGetKnownFolderPath(
folderId, 0, NULL, &folderPath);
if (result == S_OK) {
std::string systemPath = WCharToUtf8(folderPath);
// printf("systemPath %s\n", systemPath.c_str());
for (std::string mountpoint : device->mountpoints) {
// printf("%s find %s\n", systemPath.c_str(), mountpoint.c_str());
if (systemPath.find(mountpoint, 0) != std::string::npos) {
isSystem = true;
break;
}
}
}
CoTaskMemFree(folderPath);
}
return isSystem;
}
std::vector<std::string> GetAvailableVolumes() {
DWORD logicalDrivesMask = GetLogicalDrives();
std::vector<std::string> logicalDrives;
if (logicalDrivesMask == 0) {
return logicalDrives;
}
char currentDriveLetter = 'A';
while (logicalDrivesMask) {
if (logicalDrivesMask & 1) {
logicalDrives.push_back(std::string(1, currentDriveLetter));
}
currentDriveLetter++;
logicalDrivesMask >>= 1;
}
return logicalDrives;
}
int32_t GetDeviceNumber(HANDLE hDevice) {
BOOL result;
DWORD size;
DWORD errorCode = 0;
int32_t diskNumber = -1;
STORAGE_DEVICE_NUMBER deviceNumber;
VOLUME_DISK_EXTENTS diskExtents;
// Some devices will have the diskNumber exposed through their disk extents,
// while most of them will only have one accessible through
// `IOCTL_STORAGE_GET_DEVICE_NUMBER`, so we check this one first,
// augmenting / overriding it with the latter
result = DeviceIoControl(
hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0,
&diskExtents, sizeof(VOLUME_DISK_EXTENTS), &size, NULL);
if (result && diskExtents.NumberOfDiskExtents > 0) {
// printf("[INFO] DiskNumber: %i\n", diskExtents.Extents[0].DiskNumber);
// NOTE: Always ignore RAIDs
// TODO(jhermsmeier): Handle RAIDs properly
if (diskExtents.NumberOfDiskExtents >= 2) {
// printf("[INFO] Possible RAID: %i\n",
// diskExtents.Extents[0].DiskNumber);
return -1;
}
diskNumber = diskExtents.Extents[0].DiskNumber;
} else {
errorCode = GetLastError();
// printf("[INFO] VOLUME_GET_VOLUME_DISK_EXTENTS: Error 0x%08lX\n",
// errorCode);
}
result = DeviceIoControl(
hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
&deviceNumber, sizeof(deviceNumber), &size, NULL);
if (result) {
// printf("[INFO] DeviceNumber: %i\n", deviceNumber.DeviceNumber);
diskNumber = deviceNumber.DeviceNumber;
}
// errorCode = GetLastError();
// printf("[INFO] STORAGE_GET_DEVICE_NUMBER: Error 0x%08lX\n", errorCode);
return diskNumber;
}
void GetMountpoints(int32_t deviceNumber,
std::vector<std::string> *mountpoints) {
HANDLE hLogical = INVALID_HANDLE_VALUE;
int32_t logicalVolumeDeviceNumber = -1;
UINT driveType;
std::vector<std::string> logicalVolumes = GetAvailableVolumes();
for (std::string volumeName : logicalVolumes) {
if (hLogical != INVALID_HANDLE_VALUE) {
CloseHandle(hLogical);
hLogical = INVALID_HANDLE_VALUE;
}
// NOTE: Ignore everything that's not a fixed or removable drive,
// as device numbers are not unique across storage type drivers (!?),
// and this would otherwise cause CD/DVD drive letters to be
// attributed to blockdevices
driveType = GetDriveTypeA((volumeName + ":\\").c_str());
// printf("[INFO] Checking %s:/\n", volumeName.c_str());
if ((driveType != DRIVE_FIXED) && (driveType != DRIVE_REMOVABLE)) {
// printf("[INFO] Ignoring volume %s:/ - Not FIXED | REMOVABLE\n",
// volumeName.c_str());
continue;
}
hLogical = CreateFileA(
("\\\\.\\" + volumeName + ":").c_str(), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hLogical == INVALID_HANDLE_VALUE) {
// printf("[INFO] Couldn't open handle to logical volume %s\n",
// volumeName.c_str());
continue;
}
logicalVolumeDeviceNumber = GetDeviceNumber(hLogical);
if (logicalVolumeDeviceNumber == -1) {
// printf("[INFO] Couldn't get device number for logical volume %s\n",
// volumeName.c_str());
continue;
}
if (logicalVolumeDeviceNumber == deviceNumber) {
// printf("[INFO] Device number for logical volume %s is %i\n",
// volumeName.c_str(), logicalVolumeDeviceNumber);
mountpoints->push_back(volumeName + ":\\");
}
}
if (hLogical != INVALID_HANDLE_VALUE) {
CloseHandle(hLogical);
hLogical = INVALID_HANDLE_VALUE;
}
}
std::string GetBusType(STORAGE_ADAPTER_DESCRIPTOR *adapterDescriptor) {
switch (adapterDescriptor->BusType) {
case STORAGE_BUS_TYPE::BusTypeUnknown: return "UNKNOWN";
case STORAGE_BUS_TYPE::BusTypeScsi: return "SCSI";
case STORAGE_BUS_TYPE::BusTypeAtapi: return "ATAPI";
case STORAGE_BUS_TYPE::BusTypeAta: return "ATA";
case STORAGE_BUS_TYPE::BusType1394: return "1394"; // IEEE 1394
case STORAGE_BUS_TYPE::BusTypeSsa: return "SSA";
case STORAGE_BUS_TYPE::BusTypeFibre: return "FIBRE";
case STORAGE_BUS_TYPE::BusTypeUsb: return "USB";
case STORAGE_BUS_TYPE::BusTypeRAID: return "RAID";
case STORAGE_BUS_TYPE::BusTypeiScsi: return "iSCSI";
case STORAGE_BUS_TYPE::BusTypeSas: return "SAS"; // Serial-Attached SCSI
case STORAGE_BUS_TYPE::BusTypeSata: return "SATA";
case STORAGE_BUS_TYPE::BusTypeSd: return "SD"; // Secure Digital (SD)
case STORAGE_BUS_TYPE::BusTypeMmc: return "MMC"; // Multimedia card
case STORAGE_BUS_TYPE::BusTypeVirtual: return "VIRTUAL";
case STORAGE_BUS_TYPE::BusTypeFileBackedVirtual: return "FILEBACKEDVIRTUAL";
default: return "INVALID";
}
}
bool GetAdapterInfo(HANDLE hPhysical, DeviceDescriptor *device) {
STORAGE_PROPERTY_QUERY query;
STORAGE_ADAPTER_DESCRIPTOR adapterDescriptor;
DWORD size = 0;
ZeroMemory(&query, sizeof(query));
query.QueryType = STORAGE_QUERY_TYPE::PropertyStandardQuery;
query.PropertyId = STORAGE_PROPERTY_ID::StorageAdapterProperty;
BOOL hasAdapterInfo = DeviceIoControl(
hPhysical, IOCTL_STORAGE_QUERY_PROPERTY,
&query, sizeof(STORAGE_PROPERTY_QUERY),
&adapterDescriptor, sizeof(STORAGE_ADAPTER_DESCRIPTOR), &size, NULL);
if (hasAdapterInfo) {
device->busType = GetBusType(&adapterDescriptor);
device->busVersion = std::to_string(adapterDescriptor.BusMajorVersion) +
"." + std::to_string(adapterDescriptor.BusMinorVersion);
return true;
}
return false;
}
bool GetDeviceBlockSize(HANDLE hPhysical, DeviceDescriptor *device) {
STORAGE_PROPERTY_QUERY query;
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR alignmentDescriptor;
DWORD size = 0;
ZeroMemory(&query, sizeof(query));
query.QueryType = STORAGE_QUERY_TYPE::PropertyStandardQuery;
query.PropertyId = STORAGE_PROPERTY_ID::StorageAccessAlignmentProperty;
BOOL hasAlignmentDescriptor = DeviceIoControl(
hPhysical, IOCTL_STORAGE_QUERY_PROPERTY,
&query, sizeof(STORAGE_PROPERTY_QUERY),
&alignmentDescriptor, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR),
&size, NULL);
if (hasAlignmentDescriptor) {
device->blockSize = alignmentDescriptor.BytesPerPhysicalSector;
device->logicalBlockSize = alignmentDescriptor.BytesPerLogicalSector;
return true;
}
return false;
}
bool GetDeviceSize(HANDLE hPhysical, DeviceDescriptor *device) {
DISK_GEOMETRY_EX diskGeometry;
// printf("[INFO] hasDiskGeometry\n");
BOOL hasDiskGeometry = false;
DWORD size = 0;
hasDiskGeometry = DeviceIoControl(
hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
&diskGeometry, sizeof(DISK_GEOMETRY_EX), &size, NULL);
// printf("[INFO] hasDiskGeometry %i\n", hasDiskGeometry);
// NOTE: Another way to get the block size would be
// `IOCTL_STORAGE_QUERY_PROPERTY` with `STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR`,
// which can yield more (or possibly more accurate?) info,
// but might not work with external HDDs/SSDs
if (hasDiskGeometry) {
device->size = diskGeometry.DiskSize.QuadPart;
device->blockSize = diskGeometry.Geometry.BytesPerSector;
}
return hasDiskGeometry;
}
bool GetDetailData(DeviceDescriptor* device,
HDEVINFO hDeviceInfo, SP_DEVINFO_DATA deviceInfoData) {
DWORD index;
DWORD size;
DWORD errorCode = 0;
bool result = true;
HANDLE hDevice = INVALID_HANDLE_VALUE;
HANDLE hPhysical = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA_W deviceDetailData(NULL);
for (index = 0; ; index++) {
if (hDevice != INVALID_HANDLE_VALUE) {
CloseHandle(hDevice);
hDevice = INVALID_HANDLE_VALUE;
}
if (hPhysical != INVALID_HANDLE_VALUE) {
CloseHandle(hPhysical);
hPhysical = INVALID_HANDLE_VALUE;
}
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// printf("[INFO] (%i) SetupDiEnumDeviceInterfaces - isDisk\n", index);
BOOL isDisk = SetupDiEnumDeviceInterfaces(
hDeviceInfo, &deviceInfoData, &GUID_DEVICE_INTERFACE_DISK,
index, &deviceInterfaceData);
if (!isDisk) {
errorCode = GetLastError();
if (errorCode == ERROR_NO_MORE_ITEMS) {
// printf("[INFO] (%i) EnumDeviceInterfaces: No more items 0x%08lX\n",
// index, errorCode);
result = index != 0;
break;
} else {
device->error = "SetupDiEnumDeviceInterfaces: Error " +
std::to_string(errorCode);
}
result = false;
break;
}
BOOL hasDeviceInterfaceData = SetupDiGetDeviceInterfaceDetailW(
hDeviceInfo, &deviceInterfaceData, NULL, 0, &size, NULL);
if (!hasDeviceInterfaceData) {
errorCode = GetLastError();
if (errorCode == ERROR_INSUFFICIENT_BUFFER) {
deviceDetailData = static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>
(malloc(size));
if (deviceDetailData == NULL) {
device->error = "SetupDiGetDeviceInterfaceDetailW: "
"Unable to allocate SP_DEVICE_INTERFACE_DETAIL_DATA_W"
"(" + std::to_string(size) + "); "
"Error " + std::to_string(errorCode);
result = false;
break;
}
// printf("Allocated SP_DEVICE_INTERFACE_DETAIL_DATA_W\n");
} else {
device->error = "SetupDiGetDeviceInterfaceDetailW: "
"Couldn't get detail data; Error " + std::to_string(errorCode);
result = false;
break;
}
}
// printf("[INFO] (%i) Getting SP_DEVICE_INTERFACE_DETAIL_DATA_W\n", index);
deviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
BOOL hasDeviceDetail = SetupDiGetDeviceInterfaceDetailW(
hDeviceInfo, &deviceInterfaceData, deviceDetailData, size, &size, NULL);
if (!hasDeviceDetail) {
errorCode = GetLastError();
device->error = "Couldn't SetupDiGetDeviceInterfaceDetailW: Error " +
std::to_string(errorCode);
result = false;
break;
}
// printf("[INFO] (%i) SetupDiGetDeviceInterfaceDetailW:\n %s\n",
// index, WCharToUtf8(deviceDetailData->DevicePath));
// Passing zero to `CreateFile()` doesn't require permissions to
// open the device handle, but only lets you acquire device metadata,
// which is all we want
hDevice = CreateFileW(
deviceDetailData->DevicePath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
errorCode = GetLastError();
// printf("[ERROR] Couldn't open handle to device: Error %i", errorCode);
device->error = "Couldn't open handle to device: Error " +
std::to_string(errorCode);
result = false;
break;
}
int32_t deviceNumber = GetDeviceNumber(hDevice);
if (deviceNumber == -1) {
device->error = "Couldn't get device number";
result = false;
break;
}
device->raw = "\\\\.\\PhysicalDrive" + std::to_string(deviceNumber);
device->device = device->raw;
GetMountpoints(deviceNumber, &device->mountpoints);
// printf("[INFO] Opening handle to %s\n", device->raw.c_str());
hPhysical = CreateFileA(
device->raw.c_str(), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// printf("[INFO] Opened handle to %s\n", device->raw.c_str());
// printf("[INFO] Handle %i (INVALID %i)\n",
// hPhysical, INVALID_HANDLE_VALUE);
if (hPhysical == INVALID_HANDLE_VALUE) {
errorCode = GetLastError();
// printf("[INFO] Couldn't open handle to %s: Error %i\n",
// device->raw.c_str(), errorCode);
device->error = "Couldn't open handle to physical device: Error " +
std::to_string(errorCode);
result = false;
break;
}
// printf("[INFO] GetDeviceSize( %s )\n", device->raw.c_str());
if (!GetDeviceSize(hPhysical, device)) {
errorCode = GetLastError();
// printf("[ERROR] Couldn't get disk geometry: Error %i\n", errorCode);
device->error = "Couldn't get disk geometry: Error " +
std::to_string(errorCode);
result = false;
break;
}
// printf("[INFO] GetAdapterInfo( %s )\n", device->raw.c_str());
if (!GetAdapterInfo(hPhysical, device)) {
errorCode = GetLastError();
// printf("[ERROR] Couldn't get device adapter descriptor: Error %i\n",
// errorCode);
device->error = "Couldn't get device adapter descriptor: Error " +
std::to_string(errorCode);
result = false;
break;
}
// printf("[INFO] GetDeviceBlockSize( %s )\n", device->raw.c_str());
// NOTE: No need to fail over this one,
// as we can safely default to a 512B block size
if (!GetDeviceBlockSize(hPhysical, device)) {
errorCode = GetLastError();
// printf("[INFO] Couldn't get block size: Error %u\n", errorCode);
}
// printf("[INFO] isWritable( %s )\n", device->raw.c_str());
BOOL isWritable = DeviceIoControl(
hPhysical, IOCTL_DISK_IS_WRITABLE, NULL, 0,
NULL, 0, &size, NULL);
device->isReadOnly = !isWritable;
} // end for (index = 0; ; index++)
if (hDevice != INVALID_HANDLE_VALUE) {
CloseHandle(hDevice);
hDevice = INVALID_HANDLE_VALUE;
}
if (hPhysical != INVALID_HANDLE_VALUE) {
CloseHandle(hPhysical);
hPhysical = INVALID_HANDLE_VALUE;
}
free(deviceDetailData);
return result;
}
std::vector<DeviceDescriptor> ListStorageDevices() {
HDEVINFO hDeviceInfo = NULL;
SP_DEVINFO_DATA deviceInfoData;
std::vector<DeviceDescriptor> deviceList;
DWORD i;
char *enumeratorName;
DeviceDescriptor device;
hDeviceInfo = SetupDiGetClassDevsA(
&GUID_DEVICE_INTERFACE_DISK, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDeviceInfo == INVALID_HANDLE_VALUE) {
printf("[ERROR] Invalid DeviceInfo handle\n");
return deviceList;
}
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i = 0; SetupDiEnumDeviceInfo(hDeviceInfo, i, &deviceInfoData); i++) {
enumeratorName = GetEnumeratorName(hDeviceInfo, deviceInfoData);
printf("[INFO] Enumerating %s\n", enumeratorName);
// If it failed to get the SPDRP_ENUMERATOR_NAME, skip it
//if (enumeratorName == NULL) {
// continue;
//}
device = DeviceDescriptor();
device.enumerator = enumeratorName ? std::string(enumeratorName) : "";
device.description = GetFriendlyName(hDeviceInfo, deviceInfoData);
device.isRemovable = IsRemovableDevice(hDeviceInfo, deviceInfoData);
device.isVirtual = IsVirtualHardDrive(hDeviceInfo, deviceInfoData);
device.isSCSI = enumeratorName ? IsSCSIDevice(enumeratorName) : false;
device.isUSB = enumeratorName ? IsUSBDevice(enumeratorName) : false;
device.isCard = device.enumerator == "SD";
device.isSystem = !device.isRemovable &&
(device.enumerator == "SCSI" || device.enumerator == "IDE");
device.isUAS = device.isSCSI && device.isRemovable &&
!device.isVirtual && !device.isCard;
device.devicePathNull = true;
if (GetDetailData(&device, hDeviceInfo, deviceInfoData)) {
device.isSystem = device.isSystem || IsSystemDevice(hDeviceInfo, &device);
device.isCard = device.busType == "SD" || device.busType == "MMC";
device.isUAS = device.enumerator == "SCSI" && device.busType == "USB";
device.isVirtual = device.isVirtual ||
device.busType == "VIRTUAL" ||
device.busType == "FILEBACKEDVIRTUAL";
} else if (device.error == "") {
device.error = "Couldn't get detail data";
}
deviceList.push_back(device);
}
SetupDiDestroyDeviceInfoList(hDeviceInfo);
return deviceList;
}
} // namespace Drivelist

View file

@ -0,0 +1,108 @@
/*
* Copyright 2017 balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_WINDOWS_LIST_HPP_
#define SRC_WINDOWS_LIST_HPP_
#include <knownfolders.h>
#include <string>
#include <set>
namespace Drivelist {
// The <ntddstor.h>, and <usbiodef.h> headers include the following
// device interface GUIDs we're interested in;
// @see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/install-reference
// @see https://msdn.microsoft.com/en-us/library/windows/hardware/ff541389(v=vs.85).aspx
// To avoid cluttering the global namespace,
// we'll just define here what we need:
//
// - GUID_DEVINTERFACE_DISK { 53F56307-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_CDROM { 53F56308-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_USB_HUB { F18A0E88-C30C-11D0-8815-00A0C906BED8 }
// - GUID_DEVINTERFACE_FLOPPY { 53F56311-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_WRITEONCEDISK { 53F5630C-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_TAPE { 53F5630B-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_USB_DEVICE { A5DCBF10-6530-11D2-901F-00C04FB951ED }
// - GUID_DEVINTERFACE_VOLUME { 53F5630D-B6BF-11D0-94F2-00A0C91EFB8B }
// - GUID_DEVINTERFACE_STORAGEPORT { 2ACCFE60-C130-11D2-B082-00A0C91EFB8B }
//
const GUID GUID_DEVICE_INTERFACE_DISK = {
0x53F56307L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_CDROM = {
0x53F56308L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_USB_HUB = {
0xF18A0E88L, 0xC30C, 0x11D0, { 0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8 }
};
const GUID GUID_DEVICE_INTERFACE_FLOPPY = {
0x53F56311L, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_WRITEONCEDISK = {
0x53F5630CL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_TAPE = {
0x53F5630BL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_USB_DEVICE = {
0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }
};
const GUID GUID_DEVICE_INTERFACE_VOLUME = {
0x53F5630DL, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const GUID GUID_DEVICE_INTERFACE_STORAGEPORT = {
0x2ACCFE60L, 0xC130, 0x11D2, { 0xB0, 0x82, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B }
};
const std::set<std::string> USB_STORAGE_DRIVERS {
"USBSTOR", "UASPSTOR", "VUSBSTOR",
"RTUSER", "CMIUCR", "EUCR",
"ETRONSTOR", "ASUSSTPT"
};
const std::set<std::string> GENERIC_STORAGE_DRIVERS {
"SCSI", "SD", "PCISTOR",
"RTSOR", "JMCR", "JMCF", "RIMMPTSK", "RIMSPTSK", "RIXDPTSK",
"TI21SONY", "ESD7SK", "ESM7SK", "O2MD", "O2SD", "VIACR"
};
// List of known virtual disk hardware IDs
const std::set<std::string> VHD_HARDWARE_IDS {
"Arsenal_________Virtual_",
"KernSafeVirtual_________",
"Msft____Virtual_Disk____",
"VMware__VMware_Virtual_S"
};
const GUID KNOWN_FOLDER_IDS[] {
FOLDERID_Windows,
FOLDERID_Profile,
FOLDERID_ProgramFiles,
FOLDERID_ProgramFilesX86
};
} // namespace Drivelist
#endif // SRC_WINDOWS_LIST_HPP_

View file

@ -0,0 +1,44 @@
{
"blockdevices": [{
"name": "sda",
"maj:min": "8:0",
"rm": "1",
"size": "14.3G",
"ro": "0",
"type": "disk",
"mountpoint": "/media/jwentz/Temp"
}, {
"name": "nvme0n1",
"maj:min": "259:0",
"rm": "0",
"size": "477G",
"ro": "0",
"type": "disk",
"mountpoint": null,
"children": [{
"name": "nvme0n1p3",
"maj:min": "259:3",
"rm": "0",
"size": "15.8G",
"ro": "0",
"type": "part",
"mountpoint": "[SWAP]"
}, {
"name": "nvme0n1p1",
"maj:min": "259:1",
"rm": "0",
"size": "512M",
"ro": "0",
"type": "part",
"mountpoint": "/boot/efi"
}, {
"name": "nvme0n1p2",
"maj:min": "259:2",
"rm": "0",
"size": "460.7G",
"ro": "0",
"type": "part",
"mountpoint": "/"
}]
}]
}

View file

@ -0,0 +1,9 @@
NAME="sda" MAJ:MIN="8:0" RM="0" SIZE="240065183744" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sda1" MAJ:MIN="8:1" RM="0" SIZE="209715200" RO="0" TYPE="part" MOUNTPOINT="/boot/efi"
NAME="sda2" MAJ:MIN="8:2" RM="0" SIZE="1073741824" RO="0" TYPE="part" MOUNTPOINT="/boot"
NAME="sda3" MAJ:MIN="8:3" RM="0" SIZE="115835142144" RO="0" TYPE="part" MOUNTPOINT=""
NAME="centos-root" MAJ:MIN="253:0" RM="0" SIZE="53687091200" RO="0" TYPE="lvm" MOUNTPOINT="/"
NAME="centos-swap" MAJ:MIN="253:1" RM="0" SIZE="8455716864" RO="0" TYPE="lvm" MOUNTPOINT="[SWAP]"
NAME="centos-home" MAJ:MIN="253:2" RM="0" SIZE="53687091200" RO="0" TYPE="lvm" MOUNTPOINT="/home"
NAME="sdb" MAJ:MIN="8:16" RM="1" SIZE="" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sdc" MAJ:MIN="8:32" RM="1" SIZE="31457280000" RO="0" TYPE="disk" MOUNTPOINT="/run/media/DA2E4172/BUILD"

View file

@ -0,0 +1,32 @@
NAME="sda" MAJ:MIN="8:0" RM="0" SIZE="1024209543168" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sda1" MAJ:MIN="8:1" RM="0" SIZE="524288000" RO="0" TYPE="part" MOUNTPOINT="/boot/efi"
NAME="sda2" MAJ:MIN="8:2" RM="0" SIZE="41943040" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda3" MAJ:MIN="8:3" RM="0" SIZE="3221225472" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda4" MAJ:MIN="8:4" RM="0" SIZE="235291017216" RO="0" TYPE="part" MOUNTPOINT="/"
NAME="sda5" MAJ:MIN="8:5" RM="0" SIZE="16980639744" RO="0" TYPE="part" MOUNTPOINT="[SWAP]"
NAME="sda6" MAJ:MIN="8:6" RM="0" SIZE="549755813888" RO="0" TYPE="part" MOUNTPOINT="/home"
NAME="sda7" MAJ:MIN="8:7" RM="0" SIZE="218392166400" RO="0" TYPE="part" MOUNTPOINT=""
NAME="ram0" MAJ:MIN="1:0" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram1" MAJ:MIN="1:1" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram2" MAJ:MIN="1:2" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram3" MAJ:MIN="1:3" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram4" MAJ:MIN="1:4" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram5" MAJ:MIN="1:5" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram6" MAJ:MIN="1:6" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram7" MAJ:MIN="1:7" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram8" MAJ:MIN="1:8" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram9" MAJ:MIN="1:9" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="loop0" MAJ:MIN="7:0" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop1" MAJ:MIN="7:1" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop2" MAJ:MIN="7:2" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop3" MAJ:MIN="7:3" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop4" MAJ:MIN="7:4" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop5" MAJ:MIN="7:5" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop6" MAJ:MIN="7:6" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop7" MAJ:MIN="7:7" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="ram10" MAJ:MIN="1:10" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram11" MAJ:MIN="1:11" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram12" MAJ:MIN="1:12" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram13" MAJ:MIN="1:13" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram14" MAJ:MIN="1:14" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram15" MAJ:MIN="1:15" RM="0" SIZE="67108864" RO="0" TYPE="disk" MOUNTPOINT=""

View file

@ -0,0 +1,30 @@
NAME="fd0" MAJ:MIN="2:0" RM="1" SIZE="" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sda" MAJ:MIN="8:0" RM="0" SIZE="32G" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sda1" MAJ:MIN="8:1" RM="0" SIZE="30G" RO="0" TYPE="part" MOUNTPOINT="/"
NAME="sda2" MAJ:MIN="8:2" RM="0" SIZE="1K" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda5" MAJ:MIN="8:5" RM="0" SIZE="2G" RO="0" TYPE="part" MOUNTPOINT="[SWAP]"
NAME="sr0" MAJ:MIN="11:0" RM="1" SIZE="1024M" RO="0" TYPE="rom" MOUNTPOINT=""
NAME="ram0" MAJ:MIN="1:0" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram1" MAJ:MIN="1:1" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram2" MAJ:MIN="1:2" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram3" MAJ:MIN="1:3" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram4" MAJ:MIN="1:4" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram5" MAJ:MIN="1:5" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram6" MAJ:MIN="1:6" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram7" MAJ:MIN="1:7" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram8" MAJ:MIN="1:8" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram9" MAJ:MIN="1:9" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="loop0" MAJ:MIN="7:0" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop1" MAJ:MIN="7:1" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop2" MAJ:MIN="7:2" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop3" MAJ:MIN="7:3" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop4" MAJ:MIN="7:4" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop5" MAJ:MIN="7:5" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop6" MAJ:MIN="7:6" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop7" MAJ:MIN="7:7" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="ram10" MAJ:MIN="1:10" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram11" MAJ:MIN="1:11" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram12" MAJ:MIN="1:12" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram13" MAJ:MIN="1:13" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram14" MAJ:MIN="1:14" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="ram15" MAJ:MIN="1:15" RM="0" SIZE="64M" RO="0" TYPE="disk" MOUNTPOINT=""

View file

@ -0,0 +1,23 @@
NAME="sda" MAJ:MIN="8:0" RM="0" SIZE="931.5G" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sda1" MAJ:MIN="8:1" RM="0" SIZE="1K" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda2" MAJ:MIN="8:2" RM="0" SIZE="488.3G" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda5" MAJ:MIN="8:5" RM="0" SIZE="14.9G" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sda6" MAJ:MIN="8:6" RM="0" SIZE="335.2G" RO="0" TYPE="part" MOUNTPOINT=""
NAME="sdb" MAJ:MIN="8:16" RM="1" SIZE="" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sdc" MAJ:MIN="8:32" RM="1" SIZE="" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sdd" MAJ:MIN="8:48" RM="1" SIZE="7.4G" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="sdd1" MAJ:MIN="8:49" RM="1" SIZE="7.4G" RO="0" TYPE="part" MOUNTPOINT="/media/<username>/85CA-6700"
NAME="sr0" MAJ:MIN="11:0" RM="1" SIZE="1024M" RO="0" TYPE="rom" MOUNTPOINT=""
NAME="loop0" MAJ:MIN="7:0" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop1" MAJ:MIN="7:1" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop2" MAJ:MIN="7:2" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop3" MAJ:MIN="7:3" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop4" MAJ:MIN="7:4" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop5" MAJ:MIN="7:5" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop6" MAJ:MIN="7:6" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="loop7" MAJ:MIN="7:7" RM="0" SIZE="" RO="0" TYPE="loop" MOUNTPOINT=""
NAME="nvme0n1" MAJ:MIN="259:0" RM="0" SIZE="238.5G" RO="0" TYPE="disk" MOUNTPOINT=""
NAME="nvme0n1p1" MAJ:MIN="259:1" RM="0" SIZE="499M" RO="0" TYPE="part" MOUNTPOINT=""
NAME="nvme0n1p2" MAJ:MIN="259:2" RM="0" SIZE="116.6G" RO="0" TYPE="part" MOUNTPOINT=""
NAME="nvme0n1p3" MAJ:MIN="259:3" RM="0" SIZE="4.9G" RO="0" TYPE="part" MOUNTPOINT=""
NAME="nvme0n1p4" MAJ:MIN="259:4" RM="0" SIZE="116.5G" RO="0" TYPE="part" MOUNTPOINT="/"

View file

@ -0,0 +1,114 @@
/*
* Copyright 2016 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ok } from 'assert';
import { expect } from 'chai';
import * as os from 'os';
import { stub } from 'sinon';
import { list } from '../lib';
describe('Drivelist', () => {
describe('.list()', () => {
it('should yield results', async () => {
const devices = await list();
devices.forEach(device => {
ok(device.enumerator, `Invalid enumerator: ${device.enumerator}`);
ok(device.busType, `Invalid busType: ${device.busType}`);
ok(device.device, `Invalid device: ${device.device}`);
ok(device.raw, `Invalid raw: ${device.raw}`);
ok(device.description, `Invalid description: ${device.description}`);
ok(device.error === null, `Invalid error: ${device.error}`);
ok(
device.size === null || Number.isFinite(device.size),
`Invalid size: ${device.size}`,
);
ok(
Number.isFinite(device.blockSize),
`Invalid blockSize: ${device.blockSize}`,
);
ok(
Number.isFinite(device.logicalBlockSize),
`Invalid logicalBlockSize: ${device.logicalBlockSize}`,
);
ok(
Array.isArray(device.mountpoints),
`Invalid mountpoints: ${device.mountpoints}`,
);
ok(
device.isReadOnly === null || typeof device.isReadOnly === 'boolean',
`Invalid isReadOnly flag: ${device.isReadOnly}`,
);
ok(
device.isSystem === null || typeof device.isSystem === 'boolean',
`Invalid isSystem flag: ${device.isSystem}`,
);
ok(
device.isVirtual === null || typeof device.isVirtual === 'boolean',
`Invalid isVirtual flag: ${device.isVirtual}`,
);
ok(
device.isRemovable === null ||
typeof device.isRemovable === 'boolean',
`Invalid isRemovable flag: ${device.isRemovable}`,
);
ok(
device.isCard === null || typeof device.isCard === 'boolean',
`Invalid isCard flag: ${device.isCard}`,
);
ok(
device.isSCSI === null || typeof device.isSCSI === 'boolean',
`Invalid isSCSI flag: ${device.isSCSI}`,
);
ok(
device.isUSB === null || typeof device.isUSB === 'boolean',
`Invalid isUSB flag: ${device.isUSB}`,
);
ok(
device.isUAS === null || typeof device.isUAS === 'boolean',
`Invalid isUAS flag: ${device.isUAS}`,
);
});
});
describe('given an unsupported os', () => {
beforeEach(() => {
// @ts-ignore
this.osPlatformStub = stub(os, 'platform');
// @ts-ignore
this.osPlatformStub.returns('foobar');
});
afterEach(() => {
// @ts-ignore
this.osPlatformStub.restore();
});
it('should yield an unsupported error', async () => {
try {
await list();
} catch (error) {
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal(
'Your OS is not supported by this module: foobar',
);
return;
}
ok(false, 'Expected error not thrown');
});
});
});
});

View file

@ -0,0 +1,461 @@
/*
* Copyright 2018 Balena.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect } from 'chai';
import { readFileSync } from 'fs';
import { join } from 'path';
import * as util from 'util';
import { transform as transformJSON } from '../lib/lsblk/json';
import { parse as parsePairs } from '../lib/lsblk/pairs';
function inspect(value: any) {
console.log(
util.inspect(value, {
colors: true,
depth: null,
}),
);
}
describe('Drivelist', () => {
context('lsblk', () => {
it('can handle --pairs output on Ubuntu 14.04', () => {
const listData = readFileSync(
join(__dirname, 'data', 'lsblk', 'ubuntu-14.04-1.txt'),
'utf8',
);
const devices = parsePairs(listData);
const expected = [
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sda',
devicePath: null,
raw: '/dev/sda',
description: '(/boot/efi, /, [SWAP], /home)',
error: null,
size: 1024209543168,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '/boot/efi',
label: undefined,
},
{
path: '/',
label: undefined,
},
{
path: '[SWAP]',
label: undefined,
},
{
path: '/home',
label: undefined,
},
],
isReadOnly: false,
isSystem: true,
isVirtual: null,
isRemovable: false,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
];
inspect(parsePairs(listData));
expect(devices).to.deep.equal(expected);
});
it('can handle --pairs output on Ubuntu 14.04, sample 2', () => {
const listData = readFileSync(
join(__dirname, 'data', 'lsblk', 'ubuntu-14.04-2.txt'),
'utf8',
);
const devices = parsePairs(listData);
const expected = [
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/fd0',
devicePath: null,
raw: '/dev/fd0',
description: 'fd0',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [],
isReadOnly: false,
isSystem: false,
isVirtual: null,
isRemovable: true,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sda',
devicePath: null,
raw: '/dev/sda',
description: '(/, [SWAP])',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '/',
label: undefined,
},
{
path: '[SWAP]',
label: undefined,
},
],
isReadOnly: false,
isSystem: true,
isVirtual: null,
isRemovable: false,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
];
inspect(parsePairs(listData));
expect(devices).to.deep.equal(expected);
});
it('can handle mountpoints on root devices', () => {
const listData = require('./data/lsblk/no-children-mountpoints.json');
const actual = transformJSON(listData);
const expected = [
{
enumerator: 'lsblk:json',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sda',
devicePath: null,
raw: '/dev/sda',
description: '',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '/media/jwentz/Temp',
label: undefined,
},
],
isReadOnly: false,
isSystem: false,
isVirtual: null,
isRemovable: true,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:json',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/nvme0n1',
devicePath: null,
raw: '/dev/nvme0n1',
description: '([SWAP], /boot/efi, /)',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '[SWAP]',
label: undefined,
},
{
path: '/boot/efi',
label: undefined,
},
{
path: '/',
label: undefined,
},
],
isReadOnly: false,
isSystem: true,
isVirtual: null,
isRemovable: false,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
];
inspect(actual);
expect(actual).to.deep.equal(expected);
});
it('can handle empty mountpoints in lsblk --pairs output', () => {
const listData = readFileSync(
join(__dirname, 'data', 'lsblk', 'ubuntu-14.04-3.txt'),
'utf8',
);
const actual = parsePairs(listData);
const expected = [
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sda',
devicePath: null,
raw: '/dev/sda',
description: 'sda',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [],
isReadOnly: false,
isSystem: true,
isVirtual: null,
isRemovable: false,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sdb',
devicePath: null,
raw: '/dev/sdb',
description: 'sdb',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [],
isReadOnly: false,
isSystem: false,
isVirtual: null,
isRemovable: true,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sdc',
devicePath: null,
raw: '/dev/sdc',
description: 'sdc',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [],
isReadOnly: false,
isSystem: false,
isVirtual: null,
isRemovable: true,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/sdd',
devicePath: null,
raw: '/dev/sdd',
description: '(/media/<username>/85CA-6700)',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '/media/<username>/85CA-6700',
label: undefined,
},
],
isReadOnly: false,
isSystem: false,
isVirtual: null,
isRemovable: true,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
{
enumerator: 'lsblk:pairs',
busType: 'UNKNOWN',
busVersion: null,
device: '/dev/nvme0n1',
devicePath: null,
raw: '/dev/nvme0n1',
description: '(/)',
error: null,
size: null,
blockSize: 512,
logicalBlockSize: 512,
mountpoints: [
{
path: '/',
label: undefined,
},
],
isReadOnly: false,
isSystem: true,
isVirtual: null,
isRemovable: false,
isCard: null,
isSCSI: null,
isUSB: null,
isUAS: null,
},
];
inspect(actual);
expect(actual).to.deep.equal(expected);
});
it('can handle mountpoints on root devices in --pairs output', () => {
const listData = readFileSync(
join(__dirname, 'data', 'lsblk', 'no-partition-table.txt'),
'utf8',
);
const devices = parsePairs(listData);
const expected = [
{
blockSize: 512,
busType: 'UNKNOWN',
busVersion: null,
description: '(/boot/efi, /boot)',
device: '/dev/sda',
devicePath: null,
enumerator: 'lsblk:pairs',
error: null,
isCard: null,
isReadOnly: false,
isRemovable: false,
isSCSI: null,
isSystem: true,
isUAS: null,
isUSB: null,
isVirtual: null,
logicalBlockSize: 512,
mountpoints: [
{
label: undefined,
path: '/boot/efi',
},
{
label: undefined,
path: '/boot',
},
],
raw: '/dev/sda',
size: 240065183744,
},
{
blockSize: 512,
busType: 'UNKNOWN',
busVersion: null,
description: 'sdb',
device: '/dev/sdb',
devicePath: null,
enumerator: 'lsblk:pairs',
error: null,
isCard: null,
isReadOnly: false,
isRemovable: true,
isSCSI: null,
isSystem: false,
isUAS: null,
isUSB: null,
isVirtual: null,
logicalBlockSize: 512,
mountpoints: [],
raw: '/dev/sdb',
size: null,
},
{
blockSize: 512,
busType: 'UNKNOWN',
busVersion: null,
description: '(/run/media/DA2E4172/BUILD)',
device: '/dev/sdc',
devicePath: null,
enumerator: 'lsblk:pairs',
error: null,
isCard: null,
isReadOnly: false,
isRemovable: true,
isSCSI: null,
isSystem: false,
isUAS: null,
isUSB: null,
isVirtual: null,
logicalBlockSize: 512,
mountpoints: [
{
label: undefined,
path: '/run/media/DA2E4172/BUILD',
},
],
raw: '/dev/sdc',
size: 31457280000,
},
];
inspect(parsePairs(listData));
expect(devices).to.deep.equal(expected);
});
});
});

16
dependencies/drivelist/tsconfig.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"outDir": "js",
"sourceMap": true,
"declaration": true,
"target": "es6",
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": [
"lib"
]
}