UNPKG

@zenfs/core

Version:

A filesystem, anywhere

730 lines (729 loc) 41 kB
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;