balena-image-fs
Version:
Image filesystem manipulation utilities
167 lines (164 loc) • 5.62 kB
JavaScript
;
/*
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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.findPartition = exports.LabelNotFound = exports.getFsLabel = void 0;
exports.interact = interact;
/**
* @module imagefs
*/
const ext2fs = require("ext2fs");
const fatfs = require("fatfs");
const Fs = require("fs");
const util_1 = require("util");
const file_disk_1 = require("file-disk");
const partitioninfo = require("partitioninfo");
const typed_error_1 = require("typed-error");
var fsLabel_1 = require("./fsLabel");
Object.defineProperty(exports, "getFsLabel", { enumerable: true, get: function () { return fsLabel_1.getFsLabel; } });
Object.defineProperty(exports, "LabelNotFound", { enumerable: true, get: function () { return fsLabel_1.LabelNotFound; } });
var utils_1 = require("./utils");
Object.defineProperty(exports, "findPartition", { enumerable: true, get: function () { return utils_1.findPartition; } });
class MountError extends typed_error_1.TypedError {
}
const SECTOR_SIZE = 512;
async function runInFat(disk, offset, size, fn) {
function sectorPosition(sector) {
return offset + sector * SECTOR_SIZE;
}
const fat = fatfs.createFileSystem({
sectorSize: SECTOR_SIZE,
numSectors: size / SECTOR_SIZE,
readSectors: async (sector, dest, callback) => {
try {
const { bytesRead, buffer } = await disk.read(dest, 0, dest.length, sectorPosition(sector));
callback(null, bytesRead, buffer);
}
catch (e) {
callback(e);
}
},
writeSectors: async (sector, data, callback) => {
try {
const { bytesWritten, buffer } = await disk.write(data, 0, data.length, sectorPosition(sector));
callback(null, bytesWritten, buffer);
}
catch (e) {
callback(e);
}
},
});
await new Promise(function (resolve, reject) {
fat.on('error', (e) => {
reject(new MountError(e));
});
fat.on('ready', resolve);
});
// Check whether fatfs added the promises namespace on their side
if (fat.promises == null) {
// Lazily populate the promise based variants
const originalFatKeys = Object.keys(fat);
Object.defineProperty(fat, 'promises', {
enumerable: true,
configurable: true,
get() {
const promises = {};
for (const key of originalFatKeys) {
const value = fat[key];
if (typeof value === 'function' && (key in Fs.promises)) {
promises[key] = (0, util_1.promisify)(value);
}
}
originalFatKeys.length = 0;
// We need the delete first as the current property is read-only
// and the delete removes that restriction
delete this.promises;
return (this.promises = promises);
},
});
}
return await fn(fat);
}
async function runInExt(disk, offset, fn) {
let fs;
try {
fs = await ext2fs.mount(disk, offset);
}
catch (e) {
throw new MountError(e);
}
try {
return await fn(fs);
}
finally {
await ext2fs.umount(fs);
}
}
async function tryInteract(disk, offset, size, fn) {
try {
return await runInFat(disk, offset, size, fn);
}
catch (e) {
if (!(e instanceof MountError)) {
throw e;
}
try {
return await runInExt(disk, offset, fn);
}
catch (e2) {
if (!(e2 instanceof MountError)) {
throw e2;
}
throw new Error('Unsupported filesystem.');
}
}
}
async function getPartitionOffset(disk, partition) {
if (partition === undefined) {
const size = await disk.getCapacity();
return { offset: 0, size };
}
else {
return await partitioninfo.get(disk, partition);
}
}
async function diskInteract(disk, partition, fn) {
const { offset, size } = await getPartitionOffset(disk, partition);
return await tryInteract(disk, offset, size, fn);
}
/**
* @summary Run a function with a node fs like interface for a partition
*
* @example
*
* const contents = await interact('/foo/bar.img', 5, async (fs) => {
* return await promisify(fs.readFile)('/bar/qux');
* });
* console.log(contents);
*
*/
async function interact(disk, partition, fn) {
if (typeof disk === 'string') {
return await (0, file_disk_1.withOpenFile)(disk, 'r+', async (handle) => {
disk = new file_disk_1.FileDisk(handle);
return await diskInteract(disk, partition, fn);
});
}
else if (disk instanceof file_disk_1.Disk) {
return await diskInteract(disk, partition, fn);
}
else {
throw new Error('image must be a String (file path) or a Disk instance');
}
}
//# sourceMappingURL=index.js.map