From 8b9d4f344bdc42a9aa2280058274ce8df944d9f5 Mon Sep 17 00:00:00 2001 From: Edwin Joassart Date: Thu, 9 May 2024 18:34:29 +0200 Subject: [PATCH 1/3] minor: allow passing custom assets to start SB protected CM4 --- .eslintrc.js | 2 +- .gitignore | 2 ++ examples/scanner.ts | 6 ++-- examples/usbboot.ts | 55 ++++++++++++++++++++++++++++++--- lib/scanner/adapters/usbboot.ts | 4 +-- package-lock.json | 27 +++++++++------- package.json | 2 +- 7 files changed, 75 insertions(+), 23 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 8751f10a..27e88ed9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ module.exports = { extends: ["./node_modules/@balena/lint/config/.eslintrc.js"], root: true, - ignorePatterns: ["node_modules/", "dist/", "examples/"], + ignorePatterns: ["node_modules/", "dist/", "tests/", "examples"], rules: { "@typescript-eslint/no-floating-promises": "off", "no-bitwise": "off", diff --git a/.gitignore b/.gitignore index 0229f81f..b3390665 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ build *~ node_modules +yalc.lock +.yalc \ No newline at end of file diff --git a/examples/scanner.ts b/examples/scanner.ts index fcf21620..17894554 100644 --- a/examples/scanner.ts +++ b/examples/scanner.ts @@ -17,12 +17,14 @@ import { platform } from 'os'; import { scanner } from '../lib/'; -async function main() { +const bootImageDir = String(process.argv.slice(2)); + +async function main(usbBootExtraFolder?: string) { const adapters: scanner.adapters.Adapter[] = [ new scanner.adapters.BlockDeviceAdapter({ includeSystemDrives: () => true, }), - new scanner.adapters.UsbbootDeviceAdapter(), + new scanner.adapters.UsbbootDeviceAdapter(bootImageDir), ]; if (platform() === 'win32') { if (scanner.adapters.DriverlessDeviceAdapter !== undefined) { diff --git a/examples/usbboot.ts b/examples/usbboot.ts index 1ddfa999..66c772a0 100644 --- a/examples/usbboot.ts +++ b/examples/usbboot.ts @@ -13,19 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { argv } from 'process'; + +/** + * Unlocking with secureboot + * + * If you need to expose the internal storage of a cm4 with secureboot, you need to provide a signed boot-image to set the device in MSD mode. + * Pass the bootImageDir flag with the path to the folder containing the signed boot-image. + */ + import ProgressBar = require('progress'); import { scanner, sourceDestination } from '../lib/'; import { pipeSourceToDestinationsWithProgressBar } from './utils'; +// Parse command line arguments +const args = process.argv.slice(2); // removes 'node' and the script name from the args +const flags: any = {}; + +args.forEach((arg: string, index: number) => { + // Check if the argument is a flag in the format --flag=value + if (arg.startsWith('--')) { + const key: string = arg.substring(2); + const value: string = args[index + 1]; + flags[key] = value; + } +}); + +if (!flags.source) { + console.log("No source has been provided, won't try to flash anything"); +} + +if (flags.bootImageDir !== '') { + console.log(`Using external directory ${flags['bootImageDir']}`); +} + +if (flags.help) { + console.log( + 'Usage: ts-node usbboot.js --bootImageDir --source ', + ); + console.log( + 'Beware, `source` image will be flashed to all USBboot devices, so make sure you know what you are doing', + ); + console.log( + 'To expose the internal mass storage device on a locked device, set the bootImageDir to the path of a directory containing a signed boot.img and config.txt.', + ); + process.exit(0); +} + async function main() { + const bootImageDir = flags.bootImageDir; const adapters: scanner.adapters.Adapter[] = [ new scanner.adapters.BlockDeviceAdapter({ includeSystemDrives: () => false, }), - new scanner.adapters.UsbbootDeviceAdapter(), + new scanner.adapters.UsbbootDeviceAdapter(bootImageDir), ]; const deviceScanner = new scanner.Scanner(adapters); console.log('Waiting for one compute module'); @@ -74,6 +116,8 @@ async function main() { drive instanceof sourceDestination.BlockDevice && drive.description === 'Compute Module' ) { + drive.oWrite = true; + drive.oDirect = true; resolve(drive); deviceScanner.removeListener('attach', onAttach); } @@ -84,11 +128,12 @@ async function main() { ); deviceScanner.stop(); - if (argv.length >= 3) { - console.log(`Writing image ${argv[2]}`); + if (flags.source) { + console.log(JSON.stringify(dest)); + console.log(`Writing image ${flags.source} to ${dest.path}`); const source: sourceDestination.SourceDestination = new sourceDestination.File({ - path: argv[2], + path: flags.source, }); void pipeSourceToDestinationsWithProgressBar({ source, diff --git a/lib/scanner/adapters/usbboot.ts b/lib/scanner/adapters/usbboot.ts index e3a48acb..17bc46e7 100644 --- a/lib/scanner/adapters/usbboot.ts +++ b/lib/scanner/adapters/usbboot.ts @@ -27,11 +27,11 @@ export class UsbbootDeviceAdapter extends Adapter { private drives: Map = new Map(); private scanner?: UsbbootScannerType; - constructor() { + constructor(usbBootExtraDir?: string | undefined) { super(); const rpiUsbboot = getRaspberrypiUsbboot(); if (rpiUsbboot !== undefined) { - this.scanner = new rpiUsbboot.UsbbootScanner(); + this.scanner = new rpiUsbboot.UsbbootScanner(usbBootExtraDir); this.scanner.on('attach', this.onAttach.bind(this)); this.scanner.on('detach', this.onDetach.bind(this)); this.scanner.on('ready', this.emit.bind(this, 'ready')); diff --git a/package-lock.json b/package-lock.json index 383a4c79..8f87f24a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "lzma-native": "^8.0.6", "minimatch": "^9.0.3", "mountutils": "^1.3.20", - "node-raspberrypi-usbboot": "1.0.7", + "node-raspberrypi-usbboot": "1.1.0", "outdent": "^0.8.0", "partitioninfo": "^6.0.2", "rwmutex": "^1.0.0", @@ -6507,12 +6507,12 @@ } }, "node_modules/node-raspberrypi-usbboot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/node-raspberrypi-usbboot/-/node-raspberrypi-usbboot-1.0.7.tgz", - "integrity": "sha512-ebL2xC7GQSrbrbAdaj2P6rWCViDoh0ewsgu3gHrtOCNeioCZ6ESirUob1iXT/0DCCMqUDPZA0VV3+euCPRruJw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-raspberrypi-usbboot/-/node-raspberrypi-usbboot-1.1.0.tgz", + "integrity": "sha512-X5+S+YO/jHcNosb+532shuSQAkaFrt9y0B+JiimTsH62gO+OenwBqWEEoVxrxr/fOtze30zPMe/66u/gA9WzhA==", "dependencies": { "debug": "^4.3.4", - "usb": "^2.5.2" + "usb": "^2.12.1" } }, "node_modules/noop-logger": { @@ -8258,13 +8258,13 @@ } }, "node_modules/usb": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/usb/-/usb-2.11.0.tgz", - "integrity": "sha512-u5+NZ6DtoW8TIBtuSArQGAZZ/K15i3lYvZBAYmcgI+RcDS9G50/KPrUd3CrU8M92ahyCvg5e0gc8BDvr5Hwejg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/usb/-/usb-2.13.0.tgz", + "integrity": "sha512-pTNKyxD1DfC1DYu8kFcIdpE8f33e0c2Sbmmi0HEs28HTVC555uocvYR1g5DDv4CBibacCh4BqRyYZJylN4mBbw==", "hasInstallScript": true, "dependencies": { "@types/w3c-web-usb": "^1.0.6", - "node-addon-api": "^7.0.0", + "node-addon-api": "^8.0.0", "node-gyp-build": "^4.5.0" }, "engines": { @@ -8272,9 +8272,12 @@ } }, "node_modules/usb/node_modules/node-addon-api": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", - "integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.0.0.tgz", + "integrity": "sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/util-deprecate": { "version": "1.0.2", diff --git a/package.json b/package.json index 9e3dde46..c6ad4352 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "lzma-native": "^8.0.6", "minimatch": "^9.0.3", "mountutils": "^1.3.20", - "node-raspberrypi-usbboot": "1.0.7", + "node-raspberrypi-usbboot": "1.1.0", "outdent": "^0.8.0", "partitioninfo": "^6.0.2", "rwmutex": "^1.0.0", From 0bafb3e7ae9dcc5e035790143ed0c3655d6550c3 Mon Sep 17 00:00:00 2001 From: JOASSART Edwin Date: Fri, 7 Jun 2024 17:27:26 +0200 Subject: [PATCH 2/3] patch: etcher-sdk is not yet compatible with node22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6ad4352..651874b0 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "yargs": "^17.7.2" }, "engines": { - "node": ">=18" + "node": ">=18 <22" }, "versionist": { "publishedAt": "2024-04-26T12:35:55.545Z" From d13f6277275db941430f68998b19cc2593fa72cf Mon Sep 17 00:00:00 2001 From: Edwin Joassart Date: Thu, 13 Jun 2024 14:00:12 +0200 Subject: [PATCH 3/3] fix styling --- tests/tester.ts | 5 ++--- tests/utils.spec.ts | 21 ++++++++------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/tests/tester.ts b/tests/tester.ts index 66c7be7d..fc2db587 100644 --- a/tests/tester.ts +++ b/tests/tester.ts @@ -137,9 +137,8 @@ export async function testImageNoIt( assert(canCreateSparseReadStream); const sourceSparseStream = await innerSource.createSparseReadStream(); - const sourceSparseStreamBuffer = await sparseStreamToBuffer( - sourceSparseStream, - ); + const sourceSparseStreamBuffer = + await sparseStreamToBuffer(sourceSparseStream); expect(sourceSparseStreamBuffer.length).to.be.at.most(compareToData.length); expect(sourceSparseStreamBuffer).to.deep.equal( compareToData.slice(0, sourceSparseStreamBuffer.length), diff --git a/tests/utils.spec.ts b/tests/utils.spec.ts index 795b92d7..fdc0a9ec 100644 --- a/tests/utils.spec.ts +++ b/tests/utils.spec.ts @@ -66,14 +66,11 @@ describe('utils', function () { describe('BalenaS3SourceBase', function () { describe('isESRVersion', function () { it('should return false for non-ESR versions following the original scheme', function () { - [ - '2.7.8.dev', - '2.7.8.prod', - '2.80.3.0.dev', - '2.80.3.0.prod', - ].forEach((v) => { - expect(BalenaS3SourceBase.isESRVersion(v)).to.be.false; - }); + ['2.7.8.dev', '2.7.8.prod', '2.80.3.0.dev', '2.80.3.0.prod'].forEach( + (v) => { + expect(BalenaS3SourceBase.isESRVersion(v)).to.be.false; + }, + ); }); it('should return false for unified non-ESR versions', function () { @@ -83,11 +80,9 @@ describe('utils', function () { }); it('should return true for ESR versions following the original scheme', function () { - ['2020.04.0.prod', '2021.10.1.dev', '2021.10.1.prod'].forEach( - (v) => { - expect(BalenaS3SourceBase.isESRVersion(v)).to.be.true; - }, - ); + ['2020.04.0.prod', '2021.10.1.dev', '2021.10.1.prod'].forEach((v) => { + expect(BalenaS3SourceBase.isESRVersion(v)).to.be.true; + }); }); it('should return true for unified ESR versions', function () {