UNPKG

etcher-sdk

Version:
236 lines (235 loc) • 8.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.writeNetworkConfig = exports.getTargetBlockDevice = exports.copyPartitionFromImageToDevice = exports.calcRequiredPartitionSize = exports.getPartitionBoundaries = exports.findFilesystemLabel = exports.findNewPartitions = exports.MS_DATA_PARTITION_ID = void 0; const promises_1 = require("fs/promises"); const process = require("process"); const drivelist = require("drivelist"); const source_destination_1 = require("../source-destination"); exports.MS_DATA_PARTITION_ID = 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7'; /** * Finds partitions in newTable with a partition offset not found in oldTable. * Expects paritioninfo partition objects. * @returns Array of new partitions */ const findNewPartitions = (oldTable, newTable) => { if ((newTable === null || newTable === void 0 ? void 0 : newTable.partitions) === undefined || (oldTable === null || oldTable === void 0 ? void 0 : oldTable.partitions) === undefined) { throw Error('Partitions undefined in request'); } return newTable.partitions.filter((n) => { return !oldTable.partitions.some((o) => o.offset === n.offset); }); }; exports.findNewPartitions = findNewPartitions; /** * Scans the filesystem on the partitions for the provided device for the filesystem * label text, assuming the filesystem is the provided type. * * Partition type must be GPT. * * @returns partition containing the label, or null if not found */ const findFilesystemLabel = async (table, device, label, fs) => { if (table.type === 'mbr') { throw Error("Can't read MBR table"); } // Only check non-system partitions on Windows let partitions = table.partitions; if (process.platform === 'win32') { partitions = table.partitions.filter((p) => p.type.toUpperCase() === exports.MS_DATA_PARTITION_ID); } // Determine label offset let offset = 0; if (fs === 'fat16') { // https://en.wikipedia.org/wiki/Desian_of_the_FAT_file_system#Extended_BIOS_Parameter_Block offset = 0x2b; } else if (fs === 'ext4') { // https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html offset = 0x400 + 0x78; } const buf = Buffer.alloc(label.length); for (const p of partitions) { // Satisfy TypeScript that p is not an MBRPartition even though we tested above on the table if (!('guid' in p)) { continue; } await device.read(buf, 0, buf.length, p.offset + offset); if (buf.toString() === label) { return p; } } return null; }; exports.findFilesystemLabel = findFilesystemLabel; /** * Provides the boundary offsets for a partition on a device * @param {SourceDestination} source - Device containing requested partition * @param {number} partitionIndex - 1-based index of requested partition * @returns {start, end} object with offsets */ const getPartitionBoundaries = async (source, partitionIndex = 1) => { const partitioninfo = await source.getPartitionTable(); const partitions = partitioninfo === null || partitioninfo === void 0 ? void 0 : partitioninfo.partitions; if (partitions === undefined) { throw Error("Can't read partitions"); } const currentPartition = partitions.filter((p) => p.index === partitionIndex)[0]; const start = currentPartition.offset; let end = undefined; if (start) { end = start + currentPartition.size; } return { start, end, }; }; exports.getPartitionBoundaries = getPartitionBoundaries; /** * Calculate the size required for a partition to contain the contents of * the provided source partition. On Windows, rounds up to nearest MB due to * limitations of partitioning tools. * * @param {SourceDestination} source - Device containing requested partition * @param {number} partitionIndex - 1-based index of requested partition * @returns calculated size in bytes */ const calcRequiredPartitionSize = async (source, partitionIndex = 1) => { const sourceBoundaries = await (0, exports.getPartitionBoundaries)(source, partitionIndex); const sourcePartitionSize = sourceBoundaries.end - sourceBoundaries.start; const alignmentBuffer = 4096; if (isNaN(sourcePartitionSize)) { throw Error('Not able to find source partition size.'); } if (process.platform === 'win32') { const sizeMB = Math.ceil((sourcePartitionSize + alignmentBuffer) / (1024 * 1024)); return sizeMB * 1024 * 1024; } else { return sourcePartitionSize + alignmentBuffer; } }; exports.calcRequiredPartitionSize = calcRequiredPartitionSize; /** * Copy a partition, referenced by index, from an image file to a block device, * @param {File} source - source image for partition * @param {number} sourcePartitionIndex * @param {BlockDevice} target - Target device for partition * @param {number} targetOffset - For partition on device * @returns Promise<> */ const copyPartitionFromImageToDevice = async (source, sourcePartitionIndex, target, targetOffset) => { const sourceBoundaries = await (0, exports.getPartitionBoundaries)(source, sourcePartitionIndex); const sourcePartitionSize = sourceBoundaries.end - sourceBoundaries.start; if (isNaN(targetOffset) || isNaN(sourcePartitionSize)) { throw Error('Not able to find source partition size or target offset.'); } const alignments = [source.getAlignment(), target.getAlignment()].filter((a) => a !== undefined); let alignment; if (alignments.length) { alignment = Math.max(...alignments); } const sourceReadStream = await source.createReadStream({ alignment, emitProgress: true, start: sourceBoundaries.start, end: sourceBoundaries.end, }); sourceReadStream.on('progress', (c) => { // console.clear(); console.log(`read: ${JSON.stringify(c)}`); }); void target.open(); const targetStream = await target.createWriteStream({ startOffset: targetOffset, }); targetStream.on('progress', (p) => { // console.clear(); console.log(`write: ${JSON.stringify(p)}`); }); return new Promise((resolve, reject) => { sourceReadStream .on('error', reject) .pipe(targetStream) .on('error', reject) .on('close', resolve); }); }; exports.copyPartitionFromImageToDevice = copyPartitionFromImageToDevice; /** * Creates a block device for the drive on this machine device named with the requested label. * @returns created device */ const getTargetBlockDevice = async (mountLabel = 'C') => { const drives = await drivelist.list(); const drive = drives.filter((d) => d.mountpoints.some((m) => m.path.startsWith(mountLabel)))[0]; return new source_destination_1.BlockDevice({ drive, unmountOnSuccess: false, direct: true, write: true, keepOriginal: true, }); }; exports.getTargetBlockDevice = getTargetBlockDevice; /** Generates the contents for a NetworkManager configuration file. */ const generateWifiConfig = (profile) => { const connectionSection = `[connection] id=${profile.name} type=wifi `; const wifiSection = ` [wifi] mode=infrastructure ssid=${profile.wifiSsid} `; let keyMgmt; switch (profile.wifiAuthType) { case 0 /* WifiAuthType.NONE */: keyMgmt = ''; break; case 1 /* WifiAuthType.WPA2_PSK */: keyMgmt = 'wpa-psk'; break; case 2 /* WifiAuthType.WPA3_SAE */: keyMgmt = 'sae'; break; default: throw Error(`WifiAuthType ${profile.wifiAuthType} not defined`); } let wifiSecuritySection = ''; if (keyMgmt) { wifiSecuritySection = ` [wifi-security] auth-alg=open key-mgmt=${keyMgmt} psk=${profile.wifiKey} `; } const ipSection = ` [ipv4] method=auto [ipv6] addr-gen-mode=stable-privacy method=auto `; return connectionSection.concat(wifiSection, wifiSecuritySection, ipSection); }; /** * Writes a NetworkManager configuration file for the provided network connection * profile. Assumes profile is for WiFi configuration. * * @param {string} pathname - name for file * @param {ConnectionProfile} profile - Profile data to write; must define WiFi SSID * @returns Promise<void> on file write */ const writeNetworkConfig = async (pathname, profile) => { if (!profile.wifiSsid) { throw Error('WiFi SSID not defined for profile'); } const config = generateWifiConfig(profile); return (0, promises_1.writeFile)(pathname, config); }; exports.writeNetworkConfig = writeNetworkConfig; //# sourceMappingURL=helpers.js.map