@zenfs/core
Version:
A filesystem, anywhere
730 lines (729 loc) • 41 kB
JavaScript
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
// SPDX-License-Identifier: LGPL-3.0-or-later
import { withErrno } from 'kerium';
import { alert, crit, debug, err, warn } from 'kerium/log';
import { array, offsetof, sizeof } from 'memium';
import { $from, field, struct, types as t } from 'memium/decorators';
import { BufferView } from 'utilium/buffer';
import { crc32c } from 'utilium/checksum';
import { decodeUUID, encodeUUID } from 'utilium/string';
import { _inode_version, Inode } from '../internal/inode.js';
import { StoreFS } from './store/fs.js';
import { SyncMapTransaction } from './store/map.js';
const hex = (value) => '0x' + value.toString(16).padStart(8, '0');
// eslint-disable-next-line @typescript-eslint/unbound-method
const { format } = new Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: 2,
unit: 'byte',
unitDisplay: 'narrow',
});
let MetadataEntry = (() => {
var _a, _b, _c, _d;
let _classDecorators = [struct.packed()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = $from(BufferView);
let _id_decorators;
let _id_initializers = [];
let _id_extraInitializers = [];
let _offset__decorators;
let _offset__initializers = [];
let _offset__extraInitializers = [];
let _offset_decorators;
let _offset_initializers = [];
let _offset_extraInitializers = [];
let _size_decorators;
let _size_initializers = [];
let _size_extraInitializers = [];
var MetadataEntry = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_id_decorators = [(_a = t).uint32.bind(_a)];
_offset__decorators = [(_b = t).uint32.bind(_b)];
_offset_decorators = [(_c = t).uint32.bind(_c)];
_size_decorators = [(_d = t).uint32.bind(_d)];
__esDecorate(this, null, _id_decorators, { kind: "accessor", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
__esDecorate(this, null, _offset__decorators, { kind: "accessor", name: "offset_", static: false, private: false, access: { has: obj => "offset_" in obj, get: obj => obj.offset_, set: (obj, value) => { obj.offset_ = value; } }, metadata: _metadata }, _offset__initializers, _offset__extraInitializers);
__esDecorate(this, null, _offset_decorators, { kind: "accessor", name: "offset", static: false, private: false, access: { has: obj => "offset" in obj, get: obj => obj.offset, set: (obj, value) => { obj.offset = value; } }, metadata: _metadata }, _offset_initializers, _offset_extraInitializers);
__esDecorate(this, null, _size_decorators, { kind: "accessor", name: "size", static: false, private: false, access: { has: obj => "size" in obj, get: obj => obj.size, set: (obj, value) => { obj.size = value; } }, metadata: _metadata }, _size_initializers, _size_extraInitializers);
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
MetadataEntry = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static name = 'MetadataEntry';
#id_accessor_storage = __runInitializers(this, _id_initializers, void 0);
/** Inode or data ID */
get id() { return this.#id_accessor_storage; }
set id(value) { this.#id_accessor_storage = value; }
#offset__accessor_storage = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _offset__initializers, void 0));
/** Reserved for 64-bit offset expansion */
get offset_() { return this.#offset__accessor_storage; }
set offset_(value) { this.#offset__accessor_storage = value; }
#offset_accessor_storage = (__runInitializers(this, _offset__extraInitializers), __runInitializers(this, _offset_initializers, void 0));
/** Offset into the buffer the data is stored at. */
get offset() { return this.#offset_accessor_storage; }
set offset(value) { this.#offset_accessor_storage = value; }
#size_accessor_storage = (__runInitializers(this, _offset_extraInitializers), __runInitializers(this, _size_initializers, void 0));
/** The size of the data */
get size() { return this.#size_accessor_storage; }
set size(value) { this.#size_accessor_storage = value; }
toString() {
return `<MetadataEntry @ ${hex(this.byteOffset)}>`;
}
constructor() {
super(...arguments);
__runInitializers(this, _size_extraInitializers);
}
static {
__runInitializers(_classThis, _classExtraInitializers);
}
};
return MetadataEntry = _classThis;
})();
/**
* Number of entries per block of metadata
*/
const entries_per_block = 255;
/**
* Number of times to attempt to acquire a lock before giving up.
*/
const max_lock_attempts = 5;
/**
* A block of metadata for a single-buffer file system.
* This metadata maps IDs (for inodes and data) to actual offsets in the buffer.
* This is done since IDs are not guaranteed to be sequential.
*/
let MetadataBlock = (() => {
var _a, _b, _c, _d;
let _classDecorators = [struct.packed()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = $from.typed(Int32Array);
let _checksum_decorators;
let _checksum_initializers = [];
let _checksum_extraInitializers = [];
let _timestamp_decorators;
let _timestamp_initializers = [];
let _timestamp_extraInitializers = [];
let _previous_offset_decorators;
let _previous_offset_initializers = [];
let _previous_offset_extraInitializers = [];
let _items_decorators;
let _items_initializers = [];
let _items_extraInitializers = [];
let _locked_decorators;
let _locked_initializers = [];
let _locked_extraInitializers = [];
var MetadataBlock = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_checksum_decorators = [(_a = t).uint32.bind(_a)];
_timestamp_decorators = [(_b = t).uint64.bind(_b)];
_previous_offset_decorators = [(_c = t).uint32.bind(_c)];
_items_decorators = [field(array(MetadataEntry, entries_per_block))];
_locked_decorators = [(_d = t).int32.bind(_d)];
__esDecorate(this, null, _checksum_decorators, { kind: "accessor", name: "checksum", static: false, private: false, access: { has: obj => "checksum" in obj, get: obj => obj.checksum, set: (obj, value) => { obj.checksum = value; } }, metadata: _metadata }, _checksum_initializers, _checksum_extraInitializers);
__esDecorate(this, null, _timestamp_decorators, { kind: "accessor", name: "timestamp", static: false, private: false, access: { has: obj => "timestamp" in obj, get: obj => obj.timestamp, set: (obj, value) => { obj.timestamp = value; } }, metadata: _metadata }, _timestamp_initializers, _timestamp_extraInitializers);
__esDecorate(this, null, _previous_offset_decorators, { kind: "accessor", name: "previous_offset", static: false, private: false, access: { has: obj => "previous_offset" in obj, get: obj => obj.previous_offset, set: (obj, value) => { obj.previous_offset = value; } }, metadata: _metadata }, _previous_offset_initializers, _previous_offset_extraInitializers);
__esDecorate(this, null, _items_decorators, { kind: "accessor", name: "items", static: false, private: false, access: { has: obj => "items" in obj, get: obj => obj.items, set: (obj, value) => { obj.items = value; } }, metadata: _metadata }, _items_initializers, _items_extraInitializers);
__esDecorate(this, null, _locked_decorators, { kind: "accessor", name: "locked", static: false, private: false, access: { has: obj => "locked" in obj, get: obj => obj.locked, set: (obj, value) => { obj.locked = value; } }, metadata: _metadata }, _locked_initializers, _locked_extraInitializers);
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
MetadataBlock = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static name = 'MetadataBlock';
static lockIndex;
#checksum_accessor_storage = __runInitializers(this, _checksum_initializers, void 0);
/**
* The crc32c checksum for the metadata block.
* @privateRemarks Keep this first!
*/
get checksum() { return this.#checksum_accessor_storage; }
set checksum(value) { this.#checksum_accessor_storage = value; }
#timestamp_accessor_storage = (__runInitializers(this, _checksum_extraInitializers), __runInitializers(this, _timestamp_initializers, BigInt(Date.now())));
/** The (last) time this metadata block was updated */
get timestamp() { return this.#timestamp_accessor_storage; }
set timestamp(value) { this.#timestamp_accessor_storage = value; }
#previous_offset_accessor_storage = (__runInitializers(this, _timestamp_extraInitializers), __runInitializers(this, _previous_offset_initializers, void 0));
/** Offset to the previous metadata block */
get previous_offset() { return this.#previous_offset_accessor_storage; }
set previous_offset(value) { this.#previous_offset_accessor_storage = value; }
_previous = __runInitializers(this, _previous_offset_extraInitializers);
get previous() {
if (!this.previous_offset)
return;
this._previous ??= new MetadataBlock(this.buffer, this.previous_offset);
return this._previous;
}
#items_accessor_storage = __runInitializers(this, _items_initializers, void 0);
/** Metadata entries. */
get items() { return this.#items_accessor_storage; }
set items(value) { this.#items_accessor_storage = value; }
toString(long = false) {
if (!long)
return `<MetadataBlock @ ${hex(this.byteOffset)}>`;
let text = [
`---- Metadata block at ${hex(this.byteOffset)} ----`,
`Checksum: ${hex(this.checksum)}`,
`Last updated: ${new Date(Number(this.timestamp)).toLocaleString()}`,
`Previous block: ${hex(this.previous_offset)}`,
'Entries:',
].join('\n');
for (const entry of this.items) {
if (!entry.offset)
continue;
text += `\n\t${hex(entry.id)}: ${format(entry.size).padStart(5)} at ${hex(entry.offset)}`;
}
return text;
}
#locked_accessor_storage = (__runInitializers(this, _items_extraInitializers), __runInitializers(this, _locked_initializers, void 0));
/**
* If non-zero, this block is locked for writing.
* Note a int32 is used for `Atomics.wait`
*/
get locked() { return this.#locked_accessor_storage; }
set locked(value) { this.#locked_accessor_storage = value; }
/**
* Wait for the block to be unlocked.
*/
waitUnlocked(depth = 0) {
if (depth > max_lock_attempts)
throw crit(withErrno('EBUSY', `sbfs: exceeded max attempts waiting for metadata block at ${hex(this.byteOffset)} to be unlocked`));
if (!Atomics.load(this, MetadataBlock.lockIndex))
return;
switch (Atomics.wait(this, MetadataBlock.lockIndex, 1)) {
case 'ok':
break;
case 'not-equal':
depth++;
err(`sbfs: waiting for metadata block at ${hex(this.byteOffset)} to be unlocked (${depth}/${max_lock_attempts})`);
return this.waitUnlocked(depth);
case 'timed-out':
throw crit(withErrno('EBUSY', `sbfs: timed out waiting for metadata block at ${hex(this.byteOffset)} to be unlocked`));
}
}
lock() {
this.waitUnlocked();
Atomics.store(this, MetadataBlock.lockIndex, 1);
const release = () => {
Atomics.store(this, MetadataBlock.lockIndex, 0);
Atomics.notify(this, MetadataBlock.lockIndex, 1);
};
release[Symbol.dispose] = release;
return release;
}
constructor() {
super(...arguments);
__runInitializers(this, _locked_extraInitializers);
}
static {
__runInitializers(_classThis, _classExtraInitializers);
}
};
return MetadataBlock = _classThis;
})();
export { MetadataBlock };
// workaround for https://github.com/microsoft/TypeScript/issues/61862
Object.assign(MetadataBlock, { lockIndex: offsetof(MetadataBlock, 'locked') / Int32Array.BYTES_PER_ELEMENT });
const sb_magic = 0x62732e7a; // 'z.sb'
/**
* Shortcut for minor perf. bump
* @internal
*/
const usedBytes = 2;
/**
* The super block structure for a single-buffer file system
*/
let SuperBlock = (() => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
let _classDecorators = [struct.packed()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = $from.typed(BigUint64Array);
let _checksum_decorators;
let _checksum_initializers = [];
let _checksum_extraInitializers = [];
let _magic_decorators;
let _magic_initializers = [];
let _magic_extraInitializers = [];
let _version_decorators;
let _version_initializers = [];
let _version_extraInitializers = [];
let _inode_format_decorators;
let _inode_format_initializers = [];
let _inode_format_extraInitializers = [];
let _flags_decorators;
let _flags_initializers = [];
let _flags_extraInitializers = [];
let _used_bytes_decorators;
let _used_bytes_initializers = [];
let _used_bytes_extraInitializers = [];
let _total_bytes_decorators;
let _total_bytes_initializers = [];
let _total_bytes_extraInitializers = [];
let _uuid_decorators;
let _uuid_initializers = [];
let _uuid_extraInitializers = [];
let _metadata_block_size_decorators;
let _metadata_block_size_initializers = [];
let _metadata_block_size_extraInitializers = [];
let _metadata_offset__decorators;
let _metadata_offset__initializers = [];
let _metadata_offset__extraInitializers = [];
let _metadata_offset_decorators;
let _metadata_offset_initializers = [];
let _metadata_offset_extraInitializers = [];
let _label_decorators;
let _label_initializers = [];
let _label_extraInitializers = [];
let __padding_decorators;
let __padding_initializers = [];
let __padding_extraInitializers = [];
var SuperBlock = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_checksum_decorators = [(_a = t).uint32.bind(_a)];
_magic_decorators = [(_b = t).uint32.bind(_b)];
_version_decorators = [(_c = t).uint16.bind(_c)];
_inode_format_decorators = [(_d = t).uint16.bind(_d)];
_flags_decorators = [(_e = t).uint32.bind(_e)];
_used_bytes_decorators = [(_f = t).uint64.bind(_f)];
_total_bytes_decorators = [(_g = t).uint64.bind(_g)];
_uuid_decorators = [t.uint8(16)];
_metadata_block_size_decorators = [(_h = t).uint32.bind(_h)];
_metadata_offset__decorators = [(_j = t).uint32.bind(_j)];
_metadata_offset_decorators = [(_k = t).uint32.bind(_k)];
_label_decorators = [t.char(64)];
__padding_decorators = [t.char(132)];
__esDecorate(this, null, _checksum_decorators, { kind: "accessor", name: "checksum", static: false, private: false, access: { has: obj => "checksum" in obj, get: obj => obj.checksum, set: (obj, value) => { obj.checksum = value; } }, metadata: _metadata }, _checksum_initializers, _checksum_extraInitializers);
__esDecorate(this, null, _magic_decorators, { kind: "accessor", name: "magic", static: false, private: false, access: { has: obj => "magic" in obj, get: obj => obj.magic, set: (obj, value) => { obj.magic = value; } }, metadata: _metadata }, _magic_initializers, _magic_extraInitializers);
__esDecorate(this, null, _version_decorators, { kind: "accessor", name: "version", static: false, private: false, access: { has: obj => "version" in obj, get: obj => obj.version, set: (obj, value) => { obj.version = value; } }, metadata: _metadata }, _version_initializers, _version_extraInitializers);
__esDecorate(this, null, _inode_format_decorators, { kind: "accessor", name: "inode_format", static: false, private: false, access: { has: obj => "inode_format" in obj, get: obj => obj.inode_format, set: (obj, value) => { obj.inode_format = value; } }, metadata: _metadata }, _inode_format_initializers, _inode_format_extraInitializers);
__esDecorate(this, null, _flags_decorators, { kind: "accessor", name: "flags", static: false, private: false, access: { has: obj => "flags" in obj, get: obj => obj.flags, set: (obj, value) => { obj.flags = value; } }, metadata: _metadata }, _flags_initializers, _flags_extraInitializers);
__esDecorate(this, null, _used_bytes_decorators, { kind: "accessor", name: "used_bytes", static: false, private: false, access: { has: obj => "used_bytes" in obj, get: obj => obj.used_bytes, set: (obj, value) => { obj.used_bytes = value; } }, metadata: _metadata }, _used_bytes_initializers, _used_bytes_extraInitializers);
__esDecorate(this, null, _total_bytes_decorators, { kind: "accessor", name: "total_bytes", static: false, private: false, access: { has: obj => "total_bytes" in obj, get: obj => obj.total_bytes, set: (obj, value) => { obj.total_bytes = value; } }, metadata: _metadata }, _total_bytes_initializers, _total_bytes_extraInitializers);
__esDecorate(this, null, _uuid_decorators, { kind: "accessor", name: "uuid", static: false, private: false, access: { has: obj => "uuid" in obj, get: obj => obj.uuid, set: (obj, value) => { obj.uuid = value; } }, metadata: _metadata }, _uuid_initializers, _uuid_extraInitializers);
__esDecorate(this, null, _metadata_block_size_decorators, { kind: "accessor", name: "metadata_block_size", static: false, private: false, access: { has: obj => "metadata_block_size" in obj, get: obj => obj.metadata_block_size, set: (obj, value) => { obj.metadata_block_size = value; } }, metadata: _metadata }, _metadata_block_size_initializers, _metadata_block_size_extraInitializers);
__esDecorate(this, null, _metadata_offset__decorators, { kind: "accessor", name: "metadata_offset_", static: false, private: false, access: { has: obj => "metadata_offset_" in obj, get: obj => obj.metadata_offset_, set: (obj, value) => { obj.metadata_offset_ = value; } }, metadata: _metadata }, _metadata_offset__initializers, _metadata_offset__extraInitializers);
__esDecorate(this, null, _metadata_offset_decorators, { kind: "accessor", name: "metadata_offset", static: false, private: false, access: { has: obj => "metadata_offset" in obj, get: obj => obj.metadata_offset, set: (obj, value) => { obj.metadata_offset = value; } }, metadata: _metadata }, _metadata_offset_initializers, _metadata_offset_extraInitializers);
__esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
__esDecorate(this, null, __padding_decorators, { kind: "accessor", name: "_padding", static: false, private: false, access: { has: obj => "_padding" in obj, get: obj => obj._padding, set: (obj, value) => { obj._padding = value; } }, metadata: _metadata }, __padding_initializers, __padding_extraInitializers);
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
SuperBlock = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static name = 'SuperBlock';
constructor(...args) {
super(...args);
__runInitializers(this, __padding_extraInitializers);
if (this.magic != sb_magic) {
warn('sbfs: Invalid magic value, assuming this is a fresh super block');
const md = new MetadataBlock(this.buffer, sizeof(SuperBlock));
Object.assign(this, {
metadata: md,
metadata_offset: md.byteOffset,
used_bytes: BigInt(sizeof(SuperBlock) + sizeof(MetadataBlock)),
total_bytes: BigInt(this.buffer.byteLength),
magic: sb_magic,
version: 1,
inode_format: _inode_version,
metadata_block_size: sizeof(MetadataBlock),
uuid: encodeUUID(crypto.randomUUID()),
});
_update(this);
_update(md);
return;
}
if (this.checksum !== checksum(this))
throw crit(withErrno('EIO', 'sbfs: checksum mismatch for super block'));
this.metadata = new MetadataBlock(this.buffer, this.metadata_offset);
if (this.metadata.checksum !== checksum(this.metadata))
throw crit(withErrno('EIO', `sbfs: checksum mismatch for metadata block (saved ${hex(this.metadata.checksum)}, computed ${hex(checksum(this.metadata))})`));
if (this.inode_format != _inode_version)
throw crit(withErrno('EIO', 'sbfs: inode format mismatch'));
if (this.metadata_block_size != sizeof(MetadataBlock))
throw crit(withErrno('EIO', 'sbfs: metadata block size mismatch'));
}
#checksum_accessor_storage = __runInitializers(this, _checksum_initializers, void 0);
/**
* The crc32c checksum for the super block.
* @privateRemarks Keep this first!
*/
get checksum() { return this.#checksum_accessor_storage; }
set checksum(value) { this.#checksum_accessor_storage = value; }
#magic_accessor_storage = (__runInitializers(this, _checksum_extraInitializers), __runInitializers(this, _magic_initializers, void 0));
/** Signature for the superblock. */
get magic() { return this.#magic_accessor_storage; }
set magic(value) { this.#magic_accessor_storage = value; }
#version_accessor_storage = (__runInitializers(this, _magic_extraInitializers), __runInitializers(this, _version_initializers, void 0));
/** The version of the on-disk format */
get version() { return this.#version_accessor_storage; }
set version(value) { this.#version_accessor_storage = value; }
#inode_format_accessor_storage = (__runInitializers(this, _version_extraInitializers), __runInitializers(this, _inode_format_initializers, void 0));
/** Which format of `Inode` is used */
get inode_format() { return this.#inode_format_accessor_storage; }
set inode_format(value) { this.#inode_format_accessor_storage = value; }
#flags_accessor_storage = (__runInitializers(this, _inode_format_extraInitializers), __runInitializers(this, _flags_initializers, void 0));
/** Flags for the file system. Currently unused */
get flags() { return this.#flags_accessor_storage; }
set flags(value) { this.#flags_accessor_storage = value; }
#used_bytes_accessor_storage = (__runInitializers(this, _flags_extraInitializers), __runInitializers(this, _used_bytes_initializers, void 0));
/** The number of used bytes, including the super block and metadata */
get used_bytes() { return this.#used_bytes_accessor_storage; }
set used_bytes(value) { this.#used_bytes_accessor_storage = value; }
#total_bytes_accessor_storage = (__runInitializers(this, _used_bytes_extraInitializers), __runInitializers(this, _total_bytes_initializers, void 0));
/** The total size of the entire file system, including the super block and metadata */
get total_bytes() { return this.#total_bytes_accessor_storage; }
set total_bytes(value) { this.#total_bytes_accessor_storage = value; }
#uuid_accessor_storage = (__runInitializers(this, _total_bytes_extraInitializers), __runInitializers(this, _uuid_initializers, void 0));
/** A UUID for this file system */
get uuid() { return this.#uuid_accessor_storage; }
set uuid(value) { this.#uuid_accessor_storage = value; }
#metadata_block_size_accessor_storage = (__runInitializers(this, _uuid_extraInitializers), __runInitializers(this, _metadata_block_size_initializers, void 0));
/**
* The size in bytes of a metadata block.
* Not currently configurable.
*/
get metadata_block_size() { return this.#metadata_block_size_accessor_storage; }
set metadata_block_size(value) { this.#metadata_block_size_accessor_storage = value; }
#metadata_offset__accessor_storage = (__runInitializers(this, _metadata_block_size_extraInitializers), __runInitializers(this, _metadata_offset__initializers, void 0));
/** Reserved for 64-bit offset expansion */
get metadata_offset_() { return this.#metadata_offset__accessor_storage; }
set metadata_offset_(value) { this.#metadata_offset__accessor_storage = value; }
#metadata_offset_accessor_storage = (__runInitializers(this, _metadata_offset__extraInitializers), __runInitializers(this, _metadata_offset_initializers, void 0));
/** Offset of the current metadata block */
get metadata_offset() { return this.#metadata_offset_accessor_storage; }
set metadata_offset(value) { this.#metadata_offset_accessor_storage = value; }
metadata = __runInitializers(this, _metadata_offset_extraInitializers);
#label_accessor_storage = __runInitializers(this, _label_initializers, void 0);
/** An optional label for the file system */
get label() { return this.#label_accessor_storage; }
set label(value) { this.#label_accessor_storage = value; }
#_padding_accessor_storage = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, __padding_initializers, void 0));
/** Padded to 256 bytes */
get _padding() { return this.#_padding_accessor_storage; }
set _padding(value) { this.#_padding_accessor_storage = value; }
/**
* Rotate out the current metadata block.
* Allocates a new metadata block, moves the current one to backup,
* and updates used_bytes accordingly.
* @returns the new metadata block
*/
rotateMetadata() {
const padding = this.used_bytes % BigInt(4);
Atomics.add(this, usedBytes, padding);
const offset = Number(Atomics.add(this, usedBytes, BigInt(sizeof(MetadataBlock))));
const metadata = new MetadataBlock(this.buffer, offset);
metadata.previous_offset = this.metadata_offset;
this.metadata = metadata;
this.metadata_offset = metadata.byteOffset;
_update(metadata);
_update(this);
debug(`sbfs: rotated metadata block at ${hex(metadata.previous_offset)} with new block at ${hex(offset)}`);
return metadata;
}
/**
* Checks to see if `length` bytes are unused, starting at `offset`.
* @internal Not for external use!
*/
isUnused(offset, length) {
if (!length)
return true;
if (offset + length > this.total_bytes || offset < sizeof(SuperBlock))
return false;
for (let block = this.metadata; block; block = block.previous) {
if (offset < block.byteOffset + sizeof(MetadataBlock) && offset + length > block.byteOffset)
return false;
for (const entry of block.items) {
if (!entry.offset)
continue;
if ((offset >= entry.offset && offset < entry.offset + entry.size)
|| (offset + length > entry.offset && offset + length <= entry.offset + entry.size)
|| (offset <= entry.offset && offset + length >= entry.offset + entry.size)) {
return false;
}
}
}
return true;
}
static {
__runInitializers(_classThis, _classExtraInitializers);
}
};
return SuperBlock = _classThis;
})();
export { SuperBlock };
/**
* Compute the checksum for a super block or metadata block.
* Note we don't include the checksum when computing a new one.
*/
function checksum(value) {
let length = sizeof(value) - 4;
if (value instanceof MetadataBlock)
length -= Int32Array.BYTES_PER_ELEMENT;
return crc32c(new Uint8Array(value.buffer, value.byteOffset + 4, length));
}
/**
* Update a block's checksum and timestamp.
* @internal @hidden
*/
function _update(value) {
if (value instanceof MetadataBlock)
value.timestamp = BigInt(Date.now());
value.checksum = checksum(value);
}
/**
*
* @category Stores and Transactions
*/
export class SingleBufferStore extends BufferView {
flags = [];
name = 'sbfs';
type = 0x73626673; // 'sbfs'
get uuid() {
return decodeUUID(this.superblock.uuid);
}
superblock;
/**
* @internal @hidden
*/
_view;
_u8;
constructor(...args) {
super(...args);
if (this.byteLength < sizeof(SuperBlock) + sizeof(MetadataBlock))
throw crit(withErrno('EINVAL', 'sbfs: Buffer is too small for a file system'));
this._view = new DataView(this.buffer, this.byteOffset, this.byteLength);
this._u8 = new Uint8Array(this.buffer, this.byteOffset, this.byteLength);
this.superblock = new SuperBlock(this.buffer, this.byteOffset);
}
*keys() {
const keys = new Set();
for (let block = this.superblock.metadata; block; block = block.previous) {
block.waitUnlocked();
for (const entry of block.items) {
if (!entry.offset || keys.has(entry.id))
continue;
keys.add(entry.id);
yield entry.id;
}
}
}
get(id) {
for (let block = this.superblock.metadata; block; block = block.previous) {
block.waitUnlocked();
for (const entry of block.items) {
if (!entry.offset || entry.id != id)
continue;
const off = this.byteOffset + entry.offset;
return new Uint8Array(this.buffer.slice(off, off + entry.size));
}
}
}
set(id, data) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
if (id === 0 && data.length < sizeof(Inode))
throw alert(withErrno('EIO', `sbfs: tried to set ${data.length} bytes for id 0!`));
for (let block = this.superblock.metadata; block; block = block.previous) {
block.waitUnlocked();
for (const entry of block.items) {
const env_2 = { stack: [], error: void 0, hasError: false };
try {
if (!entry.offset || entry.id != id)
continue;
const lock = __addDisposableResource(env_2, block.lock(), false);
if (data.length == entry.size) {
this._u8.set(data, entry.offset);
return;
}
if (data.length < entry.size || this.superblock.isUnused(entry.offset, data.length)) {
this._u8.set(data, entry.offset);
entry.size = data.length;
_update(block);
return;
}
entry.offset = Number(Atomics.add(this.superblock, usedBytes, BigInt(data.length)));
entry.size = data.length;
this._u8.set(data, entry.offset);
_update(block);
_update(this.superblock);
return;
}
catch (e_1) {
env_2.error = e_1;
env_2.hasError = true;
}
finally {
__disposeResources(env_2);
}
}
}
let entry = Array.from(this.superblock.metadata.items).find(e => !e.offset);
if (!entry) {
this.superblock.rotateMetadata();
entry = this.superblock.metadata.items[0];
}
const lock = __addDisposableResource(env_1, this.superblock.metadata.lock(), false);
const offset = Number(Atomics.add(this.superblock, usedBytes, BigInt(data.length)));
entry.id = id;
entry.offset = offset;
entry.size = data.length;
this._u8.set(data, offset);
_update(this.superblock.metadata);
_update(this.superblock);
}
catch (e_2) {
env_1.error = e_2;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
}
delete(id) {
for (let block = this.superblock.metadata; block; block = block.previous) {
block.waitUnlocked();
for (const entry of block.items) {
if (entry.id != id)
continue;
entry.offset = 0;
entry.size = 0;
entry.id = 0;
_update(block);
return;
}
}
}
_fs;
get fs() {
return this._fs;
}
set fs(fs) {
if (this.buffer.constructor.name === 'SharedArrayBuffer')
fs?.attributes.set('no_id_tables', true);
this._fs = fs;
}
sync() {
return Promise.resolve();
}
usage() {
return {
totalSpace: Number(this.superblock.total_bytes),
freeSpace: Number(this.superblock.total_bytes - this.superblock.used_bytes),
};
}
transaction() {
return new SyncMapTransaction(this);
}
}
const _SingleBuffer = {
name: 'SingleBuffer',
options: {
buffer: { type: 'object', required: true },
},
create(opt) {
const fs = new StoreFS(ArrayBuffer.isView(opt.buffer)
? new SingleBufferStore(opt.buffer.buffer, opt.buffer.byteOffset, opt.buffer.byteLength)
: new SingleBufferStore(opt.buffer));
fs.checkRootSync();
return fs;
},
};
/**
* A backend that uses a single buffer for storing data
* @category Backends and Configuration
*/
export const SingleBuffer = _SingleBuffer;