From c257dbd915d0b1b589c80021c0eb12334ed02255 Mon Sep 17 00:00:00 2001 From: Edwin Joassart Date: Mon, 22 May 2023 11:56:12 +0200 Subject: [PATCH] FF --- .versionbot/CHANGELOG.yml | 197 ----------------- CHANGELOG.md | 36 ---- lib/block-write-stream.ts | 6 +- lib/diskpart.ts | 282 ++----------------------- lib/index.ts | 20 +- lib/source-destination/block-device.ts | 12 +- package-lock.json | 4 +- package.json | 4 +- 8 files changed, 32 insertions(+), 529 deletions(-) diff --git a/.versionbot/CHANGELOG.yml b/.versionbot/CHANGELOG.yml index 52c6212a..83529a5e 100644 --- a/.versionbot/CHANGELOG.yml +++ b/.versionbot/CHANGELOG.yml @@ -1,200 +1,3 @@ -- commits: - - subject: Establish contract for diskpart functions - hash: 159ced7b74fa2dad776d34d573a19ccfcdd69278 - body: | - All new functions must throw an error on non-Windows platforms. - Only clean() is a no-op for historical reasons. - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.6.1 - title: "" - date: 2023-05-19T15:51:22.661Z -- commits: - - subject: "Migrator: setup task sequence" - hash: f215585ae84f7455aef2f283be155186d0e62436 - body: "" - footer: - Change-type: minor - change-type: minor - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.6.0 - title: "" - date: 2023-05-19T14:06:16.055Z -- commits: - - subject: stop diskpart.clean() from breaking CLI flash cmd - hash: 1d0dad1e62f2c723f706482b2b87ec7835d578fe - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ryan Cooke - signed-off-by: Ryan Cooke - author: rcooke-warwick - nested: [] - version: 8.5.4 - title: "" - date: 2023-05-18T15:25:29.172Z -- commits: - - subject: Convert migrator partition size calculations to use bytes - hash: 4ab3e128ae1d1c0386fa5aa9b71c59f1db872efe - body: | - Was using MB due to tool limitations. diskpart module for - Windows still requires sizes in MB. - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Determine required free space from image file partitions - hash: adf82434bc6f0c970e93cde2667923dda9c45a4c - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Add function to calculate required partition size from an image file - hash: 3df7e4a182b521eb8b0c288d381488485416dc2f - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Verify image file exists before begin migration - hash: e6262aa2a6cf7eddcc076806040e9a5efef02771 - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.5.3 - title: "" - date: 2023-04-14T11:05:38.294Z -- commits: - - subject: Minor formatting updates for migrator script - hash: 9f64f01a3d8d5723e4b0c01d58a979a49f09743e - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Integrate check for unallocated space into migrator - hash: 093f4deb56d4e28c08b1c003fd5c2f1277558ee5 - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Add function to provide unallocated space on disk - hash: ce9f2b36c458f213cab23a634d49b46a1477c15c - body: | - Also modify function that runs diskpart utility to return stdout output, - to support this new function. - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.5.2 - title: "" - date: 2023-04-10T11:17:12.576Z -- commits: - - subject: Add README for new migrator feature - hash: 8c974e45e7d1a923be1a7954ae3d5c77943b390c - body: "" - footer: - Change-type: patch - change-type: patch - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.5.1 - title: "" - date: 2023-03-16T18:30:49.517Z -- commits: - - subject: Add feature to migrate this computer to run balenaOS - hash: e5f95590317d4aad800a89521c0e62ce4835e201 - body: | - Adds balena boot and rootA partitions, copies bootloader, and reboots. - Presently only supports migration from a UEFI based Windows system. - footer: - Change-type: minor - change-type: minor - Co-Authored-By: Peter Makra <6892971+mcraa@users.noreply.github.com> - co-authored-by: Peter Makra <6892971+mcraa@users.noreply.github.com> - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Add parameter for starting offset for write - hash: 735491aeab9a390415f6c15375acb98e313d290d - body: | - Historically, always started write at offset 0. - footer: - Change-type: patch - change-type: patch - Co-Authored-By: Peter Makra <6892971+mcraa@users.noreply.github.com> - co-authored-by: Peter Makra <6892971+mcraa@users.noreply.github.com> - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Add parameter to clear partition table when open device - hash: 08337a249d2ee1a9f975f7d399772687092838c4 - body: | - Historically, always cleared partition table. - footer: - Change-type: patch - change-type: patch - Co-Authored-By: Peter Makra <6892971+mcraa@users.noreply.github.com> - co-authored-by: Peter Makra <6892971+mcraa@users.noreply.github.com> - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - - subject: Add shrink and create partition functions - hash: 7b23adba4d647195264a348947b6c8c6c394a907 - body: | - Also collect command text output on error. - footer: - Change-type: minor - change-type: minor - Co-Authored-By: Peter Makra <6892971+mcraa@users.noreply.github.com> - co-authored-by: Peter Makra <6892971+mcraa@users.noreply.github.com> - Signed-off-by: Ken Bannister - signed-off-by: Ken Bannister - author: Ken Bannister - nested: [] - version: 8.5.0 - title: "" - date: 2023-03-16T17:13:21.748Z - commits: - subject: "patch: revert unbzip2-stream to #4a54f56a25b58950f9e4277c56db2912d62242e7" diff --git a/CHANGELOG.md b/CHANGELOG.md index a1636778..824fe446 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,42 +4,6 @@ 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.6.1 - 2023-05-19 - -* Establish contract for diskpart functions [Ken Bannister] - -## 8.6.0 - 2023-05-19 - -* Migrator: setup task sequence [Ken Bannister] - -## 8.5.4 - 2023-05-18 - -* stop diskpart.clean() from breaking CLI flash cmd [rcooke-warwick] - -## 8.5.3 - 2023-04-14 - -* Convert migrator partition size calculations to use bytes [Ken Bannister] -* Determine required free space from image file partitions [Ken Bannister] -* Add function to calculate required partition size from an image file [Ken Bannister] -* Verify image file exists before begin migration [Ken Bannister] - -## 8.5.2 - 2023-04-10 - -* Minor formatting updates for migrator script [Ken Bannister] -* Integrate check for unallocated space into migrator [Ken Bannister] -* Add function to provide unallocated space on disk [Ken Bannister] - -## 8.5.1 - 2023-03-16 - -* Add README for new migrator feature [Ken Bannister] - -## 8.5.0 - 2023-03-16 - -* Add feature to migrate this computer to run balenaOS [Ken Bannister] -* Add parameter for starting offset for write [Ken Bannister] -* Add parameter to clear partition table when open device [Ken Bannister] -* Add shrink and create partition functions [Ken Bannister] - ## 8.4.1 - 2023-03-09 * patch: revert unbzip2-stream to #4a54f56a25b58950f9e4277c56db2912d62242e7 [Edwin Joassart] diff --git a/lib/block-write-stream.ts b/lib/block-write-stream.ts index 9d097dc8..b22dcef6 100644 --- a/lib/block-write-stream.ts +++ b/lib/block-write-stream.ts @@ -33,7 +33,6 @@ export class BlockWriteStream extends Writable { private maxRetries: number; public bytesWritten = 0; private position = 0; - private startOffset = 0; private firstBuffer?: Buffer; constructor({ @@ -41,25 +40,22 @@ export class BlockWriteStream extends Writable { highWaterMark, delayFirstBuffer = false, maxRetries = 5, - startOffset = 0, }: { destination: BlockDevice; highWaterMark?: number; delayFirstBuffer?: boolean; maxRetries?: number; - startOffset?: number }) { super({ objectMode: true, highWaterMark }); this.destination = destination; this.delayFirstBuffer = delayFirstBuffer; this.maxRetries = maxRetries; - this.startOffset = startOffset; } private async writeBuffer(buffer: Buffer, position: number): Promise { await retryOnTransientError( async () => { - await this.destination.write(buffer, 0, buffer.length, position + this.startOffset); + await this.destination.write(buffer, 0, buffer.length, position); }, this.maxRetries, RETRY_BASE_TIMEOUT, diff --git a/lib/diskpart.ts b/lib/diskpart.ts index 4b20f30e..531139d1 100644 --- a/lib/diskpart.ts +++ b/lib/diskpart.ts @@ -29,51 +29,31 @@ const DISKPART_DELAY = 2000; const DISKPART_RETRIES = 5; const PATTERN = /PHYSICALDRIVE(\d+)/i; -// This module provides utility functions for disk partitioning and related disk -// level tasks. Presently it relies on the Windows 'diskpart' utility to implement -// this functionality. -// Given this reliance, any new exported functions must throw an Error when used on -// non-Windows platforms. Only the clean() function silently accepts non-Windows -// platforms, for historical reasons. Of course it also is fine to implement a function -// with support for other platforms, and we may do so generally in the future. - interface ExecResult { stdout: string; stderr: string; } -/** Subclass to capture stdout from command execution. */ -class ExecError extends Error { - stdout?: string; - - constructor(message?: string, stdout?: string) { - super(message); - this.name = 'ExecError'; - this.stdout = stdout; - Object.setPrototypeOf(this, new.target.prototype); - } -} - const execFileAsync = async ( command: string, args: string[] = [], - options: ExecFileOptions = {} + options: ExecFileOptions = {}, ): Promise => { return await new Promise( - (resolve: (res: ExecResult) => void, reject: (err: ExecError) => void) => { + (resolve: (res: ExecResult) => void, reject: (err: Error) => void) => { execFile( command, args, options, (error: Error, stdout: string, stderr: string) => { if (error) { - reject(new ExecError(error.message, stdout)); + reject(error); } else { resolve({ stdout, stderr }); } - } + }, ); - } + }, ); }; @@ -91,43 +71,26 @@ async function withDiskpartMutex(fn: () => T): Promise { /** * @summary Run a diskpart script * @param {Array} commands - list of commands to run - * @return String with stdout from command */ -const runDiskpart = async (commands: string[]): Promise => { +const runDiskpart = async (commands: string[]): Promise => { if (platform() !== 'win32') { - return ''; + return; } - let output = { 'stdout':'', 'stderr':'' } await withTmpFile({ keepOpen: false }, async (file: TmpFileResult) => { await fs.writeFile(file.path, commands.join('\r\n')); await withDiskpartMutex(async () => { - output = await execFileAsync('diskpart', [ + const { stdout, stderr } = await execFileAsync('diskpart', [ '/s', file.path, ]); - debug('stdout:', output.stdout); - debug('stderr:', output.stderr); + debug('stdout:', stdout); + debug('stderr:', stderr); }); }); - return output.stdout -}; - -/** - * @summary Checks if running on windows and returns device Id - * @param {String} device - */ -const prepareDeviceId = (device: string) => { - const match = device.match(PATTERN); - if (match === null) { - throw new Error(`Invalid device: "${device}"`); - } - - return match.pop(); }; /** * @summary Clean a device's partition tables - * Functional only on Windows platform; otherwise does nothing. * @param {String} device - device path * @example * diskpart.clean('\\\\.\\PhysicalDrive2') @@ -135,19 +98,15 @@ const prepareDeviceId = (device: string) => { * .catch(...) */ export const clean = async (device: string): Promise => { - debug('clean', device); if (platform() !== 'win32') { - return + return; } - - let deviceId; - - try { - deviceId = prepareDeviceId(device); - } catch (error) { + const match = device.match(PATTERN); + if (match === null) { throw new Error(`Invalid device: "${device}"`); } - + debug('clean', device); + const deviceId = match.pop(); let errorCount = 0; while (errorCount <= DISKPART_RETRIES) { try { @@ -165,218 +124,9 @@ export const clean = async (device: string): Promise => { await delay(DISKPART_DELAY); } else { throw new Error( - `Couldn't clean the drive, ${error.message} (code ${error.code})` + `Couldn't clean the drive, ${error.message} (code ${error.code})`, ); } } } }; - -/** - * @summary Reduces the size of the given partition - * @param {String} partition - the identifier of the partition - * @param {number} desiredMB - (optional) megabytes to free up, checked against querymax, defaults to max available - * @example - * shrinkPartition('C', 2048) - * .then(...) - * .catch(...) - */ -export const shrinkPartition = async ( - partition: string, - desiredMB?: number -) => { - debug('shrink', partition, desiredMB); - if (platform() !== 'win32') { - throw new Error("shrinkPartition() not available on this platform") - } - - try { - await runDiskpart([ - `select volume ${partition}`, - `shrink ${desiredMB ? 'DESIRED='.concat(desiredMB + '') : ''}`, - ]); - } catch (error) { - throw(`shrinkPartition: ${error}${error.stdout ? `\n${error.stdout}` : ''}`); - } -}; - -/** - * - * @param {string} device - device path - * @param {number} sizeMB - size of the new partition (free space has to be present) - * @param {string} fs - default "fat32", possible "ntfs" the filesystem to format with - * @param {string} desiredLetter - letter to assign to the new volume, gets the next free letter by default - * @example - * createPartition('\\\\.\\PhysicalDrive2', 2048) - * .then(...) - * .catch(...) - */ -export const createPartition = async ( - device: string, - sizeMB: number, - fs?: 'exFAT' | 'fat32' | 'ntfs', - label?: string, - desiredLetter?: string -) => { - if (platform() !== 'win32') { - throw new Error("createPartition() not available on this platform") - } - - const deviceId = prepareDeviceId(device); - try { - await runDiskpart([ - `select disk ${deviceId}`, - `create partition primary size=${sizeMB}`, - `${desiredLetter ? 'assign letter='.concat(desiredLetter) : ''}`, - `${fs ? 'format fs='.concat(fs).concat(`label=${label ?? 'Balena Volume'}`.concat(' quick')) : ''}`, - `detail partition` - ]) - } catch (error) { - throw(`createPartition: ${error}${error.stdout ? `\n${error.stdout}` : ''}`); - } -}; - -/** - * @summary Sets the online status of a partition (volume) - * @param {String} volume - the identifier of the volume - * @example - * setPartitionOnlineStatus('3', false) - * .then(...) - * .catch(...) - */ -export const setPartitionOnlineStatus = async ( - volume: string, - status: boolean -) => { - if (platform() !== 'win32') { - throw new Error("setPartitionOnlineStatus() not available on this platform") - } - - try { - await runDiskpart([ - `select volume=${volume}`, - `${status ? 'online' : 'offline'} volume`, - ]); - } catch (error) { - throw(`setPartitionOnlineStatus: ${error}${error.stdout ? `\n${error.stdout}` : ''}`); - } -}; - -/** - * Find the volume with the provided label. - * - * @param {string} device - device path - * @param {string} label - volume/partition label - * @return {number} identifier the volume, or '' if not found - * @example - * findVolume('\\\\.\\PhysicalDrive0', 'flash-boot') - * .then(...) - * .catch(...) - */ -export const findVolume = async ( - device: string, - label: string -): Promise => { - const deviceId = prepareDeviceId(device); - - /* Retrieves diskpart output formatted like the example below. - * - * Volume ### Ltr Label Fs Type Size Status Info - * ---------- --- ----------- ----- ---------- ------- --------- -------- - * Volume 0 C NTFS Partition 45 GB Healthy Boot - * Volume 1 flash-boot FAT Partition 41 MB Healthy - * Volume 2 RAW Partition 3793 MB Healthy - * Volume 3 FAT32 Partition 100 MB Healthy System - * Volume 4 NTFS Partition 530 MB Healthy Hidden - */ - if (platform() !== 'win32') { - throw new Error("findVolume() not available on this platform") - } - - let listText = '' - try { - listText = await runDiskpart([ - `select disk ${deviceId}`, - `list volume` - ]); - } catch (error) { - throw(`findVolume: ${error}${error.stdout ? `\n${error.stdout}` : ''}`); - } - - let labelPos = -1; - // Look for 'Label' in column headings; then compare text on subsequent rows - // at that position for the expected label. - for (let line of listText.split('\n')) { - if (labelPos < 0) { - labelPos = line.indexOf('Label'); - } else { - const volMatch = line.match(/Volume\s+(\d+)/); - if (volMatch && (line.substring(labelPos, labelPos + label.length) == label)) { - return volMatch[1] - } - } - } - return '' -}; - -/** - * Provide unallocated space on disk, in KB - * - * @param {string} device - device path - * @example - * getUnallocatedSize('\\\\.\\PhysicalDrive0') - * .then(...) - * .catch(...) - */ -export const getUnallocatedSize = async ( - device: string -): Promise => { - if (platform() !== 'win32') { - throw new Error("getUnallocatedSize() not available on this platform") - } - - const deviceId = prepareDeviceId(device); - - /* Retrieves dispart output formatted like the example below. - * - * Microsoft DiskPart version 10.0.19041.964 - * - * Copyright (C) Microsoft Corporation. - * On computer: DESKTOP-TIDA6VG - * - * Disk ### Status Size Free Dyn Gpt - * -------- ------------- ------- ------- --- --- - * Disk 0 Online 50 GB 6158 MB * - */ - let listText = '' - try { - listText = await runDiskpart([ - `list disk` - ]); - } catch (error) { - throw(`getUnallocatedSize: ${error}${error.stdout ? `\n${error.stdout}` : ''}`); - } - - let freePos = -1; - // Look for 'Free' in column headings; then read size at that position - // on the row for the requested disk. - for (let line of listText.split('\n')) { - if (freePos < 0) { - freePos = line.indexOf('Free'); - } else if (line.indexOf(`Disk ${deviceId}`) >= 0) { - const freeMatch = line.substring(freePos).match(/(\d+)\s+(\w+)B/); - if (freeMatch) { - let res = Number(freeMatch[1]); - for (let units of ['K', 'M', 'G', 'T']) { - if (freeMatch[2] == units) { - return res; - } else { - res *= 1024; - } - } - } - break; // should have matched; break to throw below - } - } - throw(`getUnallocatedSize: Can't read Free space on disk ${deviceId} from: ${listText}`); -}; diff --git a/lib/index.ts b/lib/index.ts index 28dc4260..c0321192 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -14,26 +14,24 @@ * limitations under the License. */ -import * as constants from './constants'; +import * as constants from "./constants"; +import * as errors from "./errors"; +import * as multiWrite from "./multi-write"; +import * as scanner from "./scanner"; +import * as sourceDestination from "./source-destination"; import * as dotEtch from "./dotetch"; -import * as errors from './errors'; -import * as migrator from './migrator'; -import * as multiWrite from './multi-write'; -import * as scanner from './scanner'; -import * as sourceDestination from './source-destination'; -import * as sparseStream from './sparse-stream'; -import * as tmp from './tmp'; -import * as utils from './utils'; +import * as sparseStream from "./sparse-stream"; +import * as tmp from "./tmp"; +import * as utils from "./utils"; export { constants, - dotEtch, errors, - migrator, multiWrite, scanner, sourceDestination, sparseStream, tmp, utils, + dotEtch, }; diff --git a/lib/source-destination/block-device.ts b/lib/source-destination/block-device.ts index 0b773aea..65afc9ec 100644 --- a/lib/source-destination/block-device.ts +++ b/lib/source-destination/block-device.ts @@ -47,7 +47,6 @@ export class BlockDevice extends File implements AdapterSourceDestination { private unmountOnSuccess: boolean; public oDirect: boolean; public emitsProgress = false; - private keepOriginal = false public readonly alignment: number; constructor({ @@ -55,19 +54,16 @@ export class BlockDevice extends File implements AdapterSourceDestination { unmountOnSuccess = false, write = false, direct = true, - keepOriginal = false }: { drive: DrivelistDrive; unmountOnSuccess?: boolean; write?: boolean; direct?: boolean; - keepOriginal?: boolean }) { super({ path: drive.raw, write }); this.drive = drive; this.unmountOnSuccess = unmountOnSuccess; this.oDirect = direct; - this.keepOriginal = keepOriginal; // skip clean of drive before write // alignment must be at most 4k this.alignment = Math.min( drive.blockSize || DEFAULT_ALIGNMENT, @@ -149,13 +145,11 @@ export class BlockDevice extends File implements AdapterSourceDestination { public async createWriteStream({ highWaterMark, - startOffset, - }: { highWaterMark?: number, startOffset?: number } = {}): Promise { + }: { highWaterMark?: number } = {}): Promise { const stream = new ProgressBlockWriteStream({ destination: this, delayFirstBuffer: platform() === 'win32', highWaterMark, - startOffset }); stream.on('finish', stream.emit.bind(stream, 'done')); return stream; @@ -181,9 +175,7 @@ export class BlockDevice extends File implements AdapterSourceDestination { await unmountDisk(this.drive.device); } // diskpart clean on windows - if (!this.keepOriginal) { - await clean(this.drive.device); - } + await clean(this.drive.device); } await super._open(); if (plat === 'darwin') { diff --git a/package-lock.json b/package-lock.json index c87fc227..9ba45ca7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "etcher-sdk", - "version": "8.6.1", + "version": "8.4.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "etcher-sdk", - "version": "8.6.1", + "version": "8.4.1", "license": "Apache-2.0", "dependencies": { "@balena/node-beaglebone-usbboot": "^3.0.0", diff --git a/package.json b/package.json index c2108ae0..d84768d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "etcher-sdk", - "version": "8.6.1", + "version": "8.4.1", "description": "Etcher SDK", "author": "Balena Ltd ", "main": "build/index.js", @@ -109,6 +109,6 @@ "node": ">=16" }, "versionist": { - "publishedAt": "2023-05-19T15:51:22.989Z" + "publishedAt": "2023-03-09T13:30:36.072Z" } }