balena-image-fs
Advanced tools
Comparing version 7.4.0-build-get-filesystem-label-dd0cf1da6d688913dbb3a01f372b1df9a2bdf86f-1 to 7.4.0-build-get-filesystem-label-e101822ba5412f3e4c1b9ee09856b35a55497e75-1
@@ -5,3 +5,3 @@ import { Disk } from 'file-disk'; | ||
/** | ||
* @summary An error to describe why getLabel() failed to find a filesystem label. | ||
* @summary An error to describe why getFsLabel() failed to find a filesystem label. | ||
*/ | ||
@@ -24,3 +24,3 @@ export declare class LabelNotFound extends TypedError { | ||
* for (const partition of info.partitions) { | ||
* const label = await getLabel(disk, partition); | ||
* const label = await getFsLabel(disk, partition); | ||
* console.log(`${partition.index}: ${label}`); | ||
@@ -30,2 +30,2 @@ * } | ||
*/ | ||
export declare function getLabel(disk: Disk, partition: GPTPartition | MBRPartition): Promise<string>; | ||
export declare function getFsLabel(disk: Disk, partition: GPTPartition | MBRPartition): Promise<string>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getLabel = exports.LabelNotFound = void 0; | ||
exports.getFsLabel = exports.LabelNotFound = void 0; | ||
const typed_error_1 = require("typed-error"); | ||
@@ -10,4 +10,29 @@ // GPT partition GUIDs | ||
const GUID_LINUX_NATIVE = '0FC63DAF-8483-4772-8E79-3D69D8477DE4'; | ||
// Maximum length to read among various filesystem metadata. Change as needed | ||
// to support additional filesystems. | ||
const FS_METADATA_MAXLEN = 0x100; | ||
// MBR partition type IDs | ||
// https://en.wikipedia.org/wiki/Partition_type | ||
const PARTID_FAT32_CHS = 0xB; | ||
const PARTID_FAT32_LBA = 0xC; | ||
const PARTID_FAT16_LBA = 0xE; | ||
const PARTID_LINUX_NATIVE = 0x83; | ||
// FAT constants | ||
// https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Extended_BIOS_Parameter_Block | ||
// https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#FAT32_Extended_BIOS_Parameter_Block | ||
const FAT_LABEL_MAXLEN = 11; | ||
const FAT_MAGIC_VALUE = 0x29; | ||
const FAT16_MAGIC_OFFSET = 0x26; | ||
const FAT16_LABEL_OFFSET = 0x2B; | ||
const FAT32_MAGIC_OFFSET = 0x42; | ||
const FAT32_LABEL_OFFSET = 0x47; | ||
// EXT4 constants | ||
// https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html | ||
const EXT4_SUPERBLOCK_OFFSET = 0x400; | ||
const EXT4_MAGIC_OFFSET = 0x38; | ||
const EXT4_MAGIC_VALUE_LE = 0xEF53; | ||
const EXT4_LABEL_OFFSET = 0x78; | ||
const EXT4_LABEL_MAXLEN = 16; | ||
/** | ||
* @summary An error to describe why getLabel() failed to find a filesystem label. | ||
* @summary An error to describe why getFsLabel() failed to find a filesystem label. | ||
*/ | ||
@@ -33,3 +58,3 @@ class LabelNotFound extends typed_error_1.TypedError { | ||
* for (const partition of info.partitions) { | ||
* const label = await getLabel(disk, partition); | ||
* const label = await getFsLabel(disk, partition); | ||
* console.log(`${partition.index}: ${label}`); | ||
@@ -39,74 +64,65 @@ * } | ||
*/ | ||
async function getLabel(disk, partition) { | ||
// If GPT, can we just read the protective MBR? | ||
// Is there a more Typescript native way to determine partition table type? | ||
async function getFsLabel(disk, partition) { | ||
const isGpt = 'guid' in partition; | ||
const isMbr = !isGpt; | ||
// Read filesystem metadata. May need further reads depending on filesystem. | ||
let buf = Buffer.alloc(0x100); | ||
await disk.read(buf, 0, buf.length, partition.offset); | ||
let labelOffset = 0; | ||
let maxLength = 0; | ||
// Use a buffer capable of reading any filesystem metadata. Defer actual | ||
// read until select filesystem. | ||
const buf = Buffer.alloc(FS_METADATA_MAXLEN); | ||
let labelOffset; | ||
let maxLength; | ||
// A filesystem places the label in a certain position in its metadata. So | ||
// we must determine the type of filesystem, but there are many possibilities. | ||
// We first narrow the possibilities by checking the partition type (MBR) or | ||
// GUID (GPT) for the containing partition. There are two broad categories -- | ||
// FAT types and Linux native types. Finally we confirm the presence of signature | ||
// bytes within the filesystem. | ||
// https://en.wikipedia.org/wiki/Partition_type | ||
const mbrFatTypes = [0xB, 0xC, 0xE]; | ||
// we must determine the type of filesystem. We first narrow the possibilities | ||
// by checking the partition type (MBR) or GUID (GPT) for the containing partition. | ||
// There are two broad categories of filesystem -- FAT types and Linux native | ||
// types. Finally we confirm the presence of signature bytes within the filesystem | ||
// metadata. | ||
// FAT types | ||
const mbrFatTypes = [PARTID_FAT32_CHS, PARTID_FAT32_LBA, PARTID_FAT16_LBA]; | ||
const gptFatTypes = [GUID_EFI_SYSTEM, GUID_MS_BASIC_DATA]; | ||
let foundPartType = false; | ||
let foundLabel = false; | ||
if (isMbr && mbrFatTypes.some(ptype => partition.type === ptype) | ||
|| isGpt && gptFatTypes.some(ptype => partition.type === ptype)) { | ||
foundPartType = true; | ||
maxLength = 11; | ||
if (isMbr && mbrFatTypes.includes(partition.type) | ||
|| isGpt && gptFatTypes.includes(partition.type)) { | ||
maxLength = FAT_LABEL_MAXLEN; | ||
await disk.read(buf, 0, buf.length, partition.offset); | ||
// FAT16 | ||
if (buf.readUInt8(0x26) === 0x29) { | ||
labelOffset = 0x2B; | ||
foundLabel = true; | ||
if (buf.readUInt8(FAT16_MAGIC_OFFSET) === FAT_MAGIC_VALUE) { | ||
labelOffset = FAT16_LABEL_OFFSET; | ||
// FAT32 | ||
} | ||
else if (buf.readUInt8(0x42) === 0x29) { | ||
labelOffset = 0x47; | ||
foundLabel = true; | ||
else if (buf.readUInt8(FAT32_MAGIC_OFFSET) === FAT_MAGIC_VALUE) { | ||
labelOffset = FAT32_LABEL_OFFSET; | ||
} | ||
} | ||
// Linux filesytem of some kind | ||
// Linux filesytem of some kind; expecting ext2+ | ||
const gptLinuxTypes = [GUID_LINUX_NATIVE, GUID_MS_BASIC_DATA]; | ||
if (!foundLabel | ||
&& ((isMbr && partition.type === 0x83) | ||
|| (isGpt && gptLinuxTypes.some(ptype => partition.type === ptype)))) { | ||
foundPartType = true; | ||
maxLength = 16; | ||
if (labelOffset == null && ((isMbr && partition.type === PARTID_LINUX_NATIVE) | ||
|| (isGpt && gptLinuxTypes.includes(partition.type)))) { | ||
maxLength = EXT4_LABEL_MAXLEN; | ||
// ext2+; reload buffer within superblock | ||
await disk.read(buf, 0, buf.length, partition.offset + 0x400); | ||
if (buf.readUInt16LE(0x38) === 0xEF53) { | ||
labelOffset = 0x78; | ||
foundLabel = true; | ||
await disk.read(buf, 0, buf.length, partition.offset + EXT4_SUPERBLOCK_OFFSET); | ||
if (buf.readUInt16LE(EXT4_MAGIC_OFFSET) === EXT4_MAGIC_VALUE_LE) { | ||
labelOffset = EXT4_LABEL_OFFSET; | ||
} | ||
} | ||
// Fail on unexpected partition/filesystem | ||
if (!foundPartType) { | ||
// If max length for a label not defined, then implicitly we have not even | ||
// found a partition type. | ||
if (maxLength == null) { | ||
throw new LabelNotFound(partition.index, 'unexpected partition type'); | ||
} | ||
else if (!foundLabel) { | ||
if (labelOffset == null) { | ||
throw new LabelNotFound(partition.index, 'can\'t read filesystem'); | ||
} | ||
// Exclude trailing /0 bytes to stringify. | ||
let i = 0; | ||
for (; i <= maxLength; i++) { | ||
// If label fills available space, no need to test. We just need i | ||
// to have the expected value for Buffer.toString() call below. | ||
if (i == maxLength) { | ||
let zeroBytePosition; | ||
for (let i = 0; i < maxLength; i++) { | ||
if (buf.readUInt8(labelOffset + i) === 0) { | ||
zeroBytePosition = i; | ||
break; | ||
} | ||
if (buf.readUInt8(labelOffset + i) == 0) { | ||
break; | ||
} | ||
} | ||
return buf.toString('utf8', labelOffset, labelOffset + i).trim(); | ||
// Didn't find a /0 byte; must be max length. | ||
zeroBytePosition !== null && zeroBytePosition !== void 0 ? zeroBytePosition : (zeroBytePosition = maxLength); | ||
return buf.toString('utf8', labelOffset, labelOffset + zeroBytePosition).trim(); | ||
} | ||
exports.getLabel = getLabel; | ||
exports.getFsLabel = getFsLabel; | ||
//# sourceMappingURL=fsLabel.js.map |
/// <reference types="node" /> | ||
import * as Fs from 'fs'; | ||
import { Disk } from 'file-disk'; | ||
export { getLabel, LabelNotFound } from './fsLabel'; | ||
export { getFsLabel, LabelNotFound } from './fsLabel'; | ||
/** | ||
@@ -6,0 +6,0 @@ * @summary Run a function with a node fs like interface for a partition |
@@ -18,3 +18,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.interact = exports.LabelNotFound = exports.getLabel = void 0; | ||
exports.interact = exports.LabelNotFound = exports.getFsLabel = void 0; | ||
/** | ||
@@ -31,3 +31,3 @@ * @module imagefs | ||
var fsLabel_1 = require("./fsLabel"); | ||
Object.defineProperty(exports, "getLabel", { enumerable: true, get: function () { return fsLabel_1.getLabel; } }); | ||
Object.defineProperty(exports, "getFsLabel", { enumerable: true, get: function () { return fsLabel_1.getFsLabel; } }); | ||
Object.defineProperty(exports, "LabelNotFound", { enumerable: true, get: function () { return fsLabel_1.LabelNotFound; } }); | ||
@@ -34,0 +34,0 @@ class MountError extends typed_error_1.TypedError { |
@@ -7,3 +7,3 @@ # Change Log | ||
## 7.4.0 - 2025-01-17 | ||
## 7.4.0 - 2025-01-22 | ||
@@ -10,0 +10,0 @@ * Add getLabel() to retrieve filesystem label [Ken Bannister] |
{ | ||
"name": "balena-image-fs", | ||
"version": "7.4.0-build-get-filesystem-label-dd0cf1da6d688913dbb3a01f372b1df9a2bdf86f-1", | ||
"version": "7.4.0-build-get-filesystem-label-e101822ba5412f3e4c1b9ee09856b35a55497e75-1", | ||
"description": "Image filesystem manipulation utilities", | ||
@@ -62,4 +62,4 @@ "main": "build/index.js", | ||
"versionist": { | ||
"publishedAt": "2025-01-17T03:40:29.783Z" | ||
"publishedAt": "2025-01-22T01:24:43.336Z" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
38943
329