@zenfs/core
Version:
A filesystem, anywhere
392 lines (391 loc) • 18.8 kB
JavaScript
// SPDX-License-Identifier: LGPL-3.0-or-later
/*
ioctl stuff. The majority of the code here is ported from Linux
See:
- include/uapi/asm-generic/ioctl.h
- include/uapi/linux/fs.h (`FS_IOC_*`)
*/
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;
};
import { Errno, Exception, setUVMessage, UV } from 'kerium';
import { sizeof } from 'memium';
import { $from, struct, types as t } from 'memium/decorators';
import { _throw } from 'utilium';
import { BufferView } from 'utilium/buffer';
import { Inode, InodeFlags } from '../internal/inode.js';
import { normalizePath } from '../utils.js';
import { resolveMount } from './shared.js';
/*
* Flags for the fsxattr.xflags field
*/
var XFlag;
(function (XFlag) {
/** data in realtime volume */
XFlag[XFlag["RealTime"] = 1] = "RealTime";
/** preallocated file extents */
XFlag[XFlag["PreAlloc"] = 2] = "PreAlloc";
/** file cannot be modified */
XFlag[XFlag["Immutable"] = 8] = "Immutable";
/** all writes append */
XFlag[XFlag["Append"] = 16] = "Append";
/** all writes synchronous */
XFlag[XFlag["Sync"] = 32] = "Sync";
/** do not update access time */
XFlag[XFlag["NoAtime"] = 64] = "NoAtime";
/** do not include in backups */
XFlag[XFlag["NoDump"] = 128] = "NoDump";
/** create with rt bit set */
XFlag[XFlag["RtInherit"] = 256] = "RtInherit";
/** create with parents projid */
XFlag[XFlag["ProjInherit"] = 512] = "ProjInherit";
/** disallow symlink creation */
XFlag[XFlag["NoSymlinks"] = 1024] = "NoSymlinks";
/** extent size allocator hint */
XFlag[XFlag["ExtSize"] = 2048] = "ExtSize";
/** inherit inode extent size */
XFlag[XFlag["ExtSzInherit"] = 4096] = "ExtSzInherit";
/** do not defragment */
XFlag[XFlag["NoDefrag"] = 8192] = "NoDefrag";
/** use filestream allocator */
XFlag[XFlag["FileStream"] = 16384] = "FileStream";
/** use DAX for IO */
XFlag[XFlag["Dax"] = 32768] = "Dax";
/** CoW extent size allocator hint */
XFlag[XFlag["CowExtSize"] = 65536] = "CowExtSize";
/** no DIFLAG for this */
XFlag[XFlag["HasAttr"] = 2147483648] = "HasAttr";
})(XFlag || (XFlag = {}));
let fsxattr = (() => {
var _a, _b, _c, _d, _e;
let _classDecorators = [struct()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = $from(BufferView);
let _xflags_decorators;
let _xflags_initializers = [];
let _xflags_extraInitializers = [];
let _extsize_decorators;
let _extsize_initializers = [];
let _extsize_extraInitializers = [];
let _nextents_decorators;
let _nextents_initializers = [];
let _nextents_extraInitializers = [];
let _projid_decorators;
let _projid_initializers = [];
let _projid_extraInitializers = [];
let _cowextsize_decorators;
let _cowextsize_initializers = [];
let _cowextsize_extraInitializers = [];
let _pad_decorators;
let _pad_initializers = [];
let _pad_extraInitializers = [];
var fsxattr = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_xflags_decorators = [(_a = t).uint32.bind(_a)];
_extsize_decorators = [(_b = t).uint32.bind(_b)];
_nextents_decorators = [(_c = t).uint32.bind(_c)];
_projid_decorators = [(_d = t).uint32.bind(_d)];
_cowextsize_decorators = [(_e = t).uint32.bind(_e)];
_pad_decorators = [t.char(8)];
__esDecorate(this, null, _xflags_decorators, { kind: "accessor", name: "xflags", static: false, private: false, access: { has: obj => "xflags" in obj, get: obj => obj.xflags, set: (obj, value) => { obj.xflags = value; } }, metadata: _metadata }, _xflags_initializers, _xflags_extraInitializers);
__esDecorate(this, null, _extsize_decorators, { kind: "accessor", name: "extsize", static: false, private: false, access: { has: obj => "extsize" in obj, get: obj => obj.extsize, set: (obj, value) => { obj.extsize = value; } }, metadata: _metadata }, _extsize_initializers, _extsize_extraInitializers);
__esDecorate(this, null, _nextents_decorators, { kind: "accessor", name: "nextents", static: false, private: false, access: { has: obj => "nextents" in obj, get: obj => obj.nextents, set: (obj, value) => { obj.nextents = value; } }, metadata: _metadata }, _nextents_initializers, _nextents_extraInitializers);
__esDecorate(this, null, _projid_decorators, { kind: "accessor", name: "projid", static: false, private: false, access: { has: obj => "projid" in obj, get: obj => obj.projid, set: (obj, value) => { obj.projid = value; } }, metadata: _metadata }, _projid_initializers, _projid_extraInitializers);
__esDecorate(this, null, _cowextsize_decorators, { kind: "accessor", name: "cowextsize", static: false, private: false, access: { has: obj => "cowextsize" in obj, get: obj => obj.cowextsize, set: (obj, value) => { obj.cowextsize = value; } }, metadata: _metadata }, _cowextsize_initializers, _cowextsize_extraInitializers);
__esDecorate(this, null, _pad_decorators, { kind: "accessor", name: "pad", static: false, private: false, access: { has: obj => "pad" in obj, get: obj => obj.pad, set: (obj, value) => { obj.pad = value; } }, metadata: _metadata }, _pad_initializers, _pad_extraInitializers);
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
fsxattr = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static name = 'fsxattr';
#xflags_accessor_storage = __runInitializers(this, _xflags_initializers, void 0);
/** xflags field value */
get xflags() { return this.#xflags_accessor_storage; }
set xflags(value) { this.#xflags_accessor_storage = value; }
#extsize_accessor_storage = (__runInitializers(this, _xflags_extraInitializers), __runInitializers(this, _extsize_initializers, void 0));
/** extsize field value */
get extsize() { return this.#extsize_accessor_storage; }
set extsize(value) { this.#extsize_accessor_storage = value; }
#nextents_accessor_storage = (__runInitializers(this, _extsize_extraInitializers), __runInitializers(this, _nextents_initializers, void 0));
/** nextents field value */
get nextents() { return this.#nextents_accessor_storage; }
set nextents(value) { this.#nextents_accessor_storage = value; }
#projid_accessor_storage = (__runInitializers(this, _nextents_extraInitializers), __runInitializers(this, _projid_initializers, void 0));
/** project identifier */
get projid() { return this.#projid_accessor_storage; }
set projid(value) { this.#projid_accessor_storage = value; }
#cowextsize_accessor_storage = (__runInitializers(this, _projid_extraInitializers), __runInitializers(this, _cowextsize_initializers, void 0));
/** CoW extsize field value */
get cowextsize() { return this.#cowextsize_accessor_storage; }
set cowextsize(value) { this.#cowextsize_accessor_storage = value; }
#pad_accessor_storage = (__runInitializers(this, _cowextsize_extraInitializers), __runInitializers(this, _pad_initializers, []));
get pad() { return this.#pad_accessor_storage; }
set pad(value) { this.#pad_accessor_storage = value; }
constructor(inode = _throw(new Exception(Errno.EINVAL, 'fsxattr must be initialized with an inode'))) {
super(new ArrayBuffer(sizeof(fsxattr)));
__runInitializers(this, _pad_extraInitializers);
this.extsize = inode.size;
this.nextents = 1;
this.projid = inode.uid;
this.cowextsize = inode.size;
for (const name of Object.keys(InodeFlags)) {
if (!(inode.flags & InodeFlags[name]))
continue;
if (name in XFlag)
this.xflags |= XFlag[name];
}
}
static {
__runInitializers(_classThis, _classExtraInitializers);
}
};
return fsxattr = _classThis;
})();
/**
* Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS)
* @see `FS_*_FL` in `include/uapi/linux/fs.h` (around L250)
* @experimental
*/
var FileFlag;
(function (FileFlag) {
/** Secure deletion */
FileFlag[FileFlag["SecureRm"] = 1] = "SecureRm";
/** Undelete */
FileFlag[FileFlag["Undelete"] = 2] = "Undelete";
/** Compress file */
FileFlag[FileFlag["Compress"] = 4] = "Compress";
/** Synchronous updates */
FileFlag[FileFlag["Sync"] = 8] = "Sync";
/** Immutable file */
FileFlag[FileFlag["Immutable"] = 16] = "Immutable";
/** Writes to file may only append */
FileFlag[FileFlag["Append"] = 32] = "Append";
/** do not dump file */
FileFlag[FileFlag["NoDump"] = 64] = "NoDump";
/** do not update atime */
FileFlag[FileFlag["NoAtime"] = 128] = "NoAtime";
// Reserved for compression usage...
FileFlag[FileFlag["Dirty"] = 256] = "Dirty";
/** One or more compressed clusters */
FileFlag[FileFlag["CompressBlk"] = 512] = "CompressBlk";
/** Don't compress */
FileFlag[FileFlag["NoCompress"] = 1024] = "NoCompress";
// End compression flags --- maybe not all used
/** Encrypted file */
FileFlag[FileFlag["Encrypt"] = 2048] = "Encrypt";
/** btree format dir */
FileFlag[FileFlag["Btree"] = 4096] = "Btree";
/** hash-indexed directory */
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
FileFlag[FileFlag["Index"] = 4096] = "Index";
/** AFS directory */
FileFlag[FileFlag["IMagic"] = 8192] = "IMagic";
/** Reserved for ext3 */
FileFlag[FileFlag["JournalData"] = 16384] = "JournalData";
/** file tail should not be merged */
FileFlag[FileFlag["NoTail"] = 32768] = "NoTail";
/** dirsync behaviour (directories only) */
FileFlag[FileFlag["DirSync"] = 65536] = "DirSync";
/** Top of directory hierarchies*/
FileFlag[FileFlag["TopDir"] = 131072] = "TopDir";
/** Reserved for ext4 */
FileFlag[FileFlag["HugeFile"] = 262144] = "HugeFile";
/** Extents */
FileFlag[FileFlag["Extent"] = 524288] = "Extent";
/** Verity protected inode */
FileFlag[FileFlag["Verity"] = 1048576] = "Verity";
/** Inode used for large EA */
FileFlag[FileFlag["EaInode"] = 2097152] = "EaInode";
/** Reserved for ext4 */
FileFlag[FileFlag["EofBlocks"] = 4194304] = "EofBlocks";
/** Do not cow file */
FileFlag[FileFlag["NoCow"] = 8388608] = "NoCow";
/** Inode is DAX */
FileFlag[FileFlag["Dax"] = 33554432] = "Dax";
/** Reserved for ext4 */
FileFlag[FileFlag["InlineData"] = 268435456] = "InlineData";
/** Create with parents projid */
FileFlag[FileFlag["ProjInherit"] = 536870912] = "ProjInherit";
/** Folder is case insensitive */
FileFlag[FileFlag["CaseFold"] = 1073741824] = "CaseFold";
/** reserved for ext2 lib */
FileFlag[FileFlag["Reserved"] = 2147483648] = "Reserved";
})(FileFlag || (FileFlag = {}));
/**
* `FS_IOC_*` commands for {@link ioctl | `ioctl`}
* @remarks
* These are computed from a script since constant values are needed for enum member types
*/
export var IOC;
(function (IOC) {
IOC[IOC["GetFlags"] = 2148034049] = "GetFlags";
IOC[IOC["SetFlags"] = 1074292226] = "SetFlags";
IOC[IOC["GetVersion"] = 2148038145] = "GetVersion";
IOC[IOC["SetVersion"] = 1074296322] = "SetVersion";
IOC[IOC["Fiemap"] = 3223348747] = "Fiemap";
IOC[IOC["GetXattr"] = 2149341215] = "GetXattr";
IOC[IOC["SetXattr"] = 1075599392] = "SetXattr";
IOC[IOC["GetLabel"] = 2164298801] = "GetLabel";
IOC[IOC["SetLabel"] = 1090556978] = "SetLabel";
IOC[IOC["GetUUID"] = 2148603136] = "GetUUID";
IOC[IOC["GetSysfsPath"] = 2155943169] = "GetSysfsPath";
})(IOC || (IOC = {}));
/**
* `FS_IOC32_*` commands for {@link ioctl | `ioctl`}
* @remarks
* These are computed from a script since constant values are needed for enum member types
*/
export var IOC32;
(function (IOC32) {
IOC32[IOC32["GetFlags"] = 2147771905] = "GetFlags";
IOC32[IOC32["SetFlags"] = 1074030082] = "SetFlags";
IOC32[IOC32["GetVersion"] = 2147776001] = "GetVersion";
IOC32[IOC32["SetVersion"] = 1074034178] = "SetVersion";
})(IOC32 || (IOC32 = {}));
/** Perform an `ioctl` on a file or file system. */
export async function ioctl(
/** The path to the file or file system to perform the `ioctl` on */
path,
/** The command to perform (uint32) */
command,
/** The arguments to pass to the command */
...args) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
try {
const inode = new Inode(await fs.stat(resolved));
switch (command) {
case IOC.GetFlags:
case IOC32.GetFlags:
return inode.flags;
case IOC.SetFlags:
case IOC32.SetFlags:
inode.flags = args[0];
await fs.touch(resolved, inode);
return undefined;
case IOC.GetVersion:
case IOC32.GetVersion:
return inode.version;
case IOC.SetVersion:
case IOC32.SetVersion:
inode.version = args[0];
await fs.touch(resolved, inode);
return undefined;
case IOC.Fiemap:
break;
case IOC.GetXattr:
return new fsxattr(inode);
case IOC.SetXattr:
break;
case IOC.GetLabel:
return fs.label;
case IOC.SetLabel:
fs.label = args[0];
return undefined;
case IOC.GetUUID:
return fs.uuid;
case IOC.GetSysfsPath:
/**
* Returns the path component under /sys/fs/ that refers to this filesystem;
* also /sys/kernel/debug/ for filesystems with debugfs exports
* @todo Implement sysfs and have each FS implement the /sys/fs/<name> tree
*/
return `/sys/fs/${fs.name}/${fs.uuid}`;
}
}
catch (e) {
throw setUVMessage(Object.assign(e, { syscall: 'ioctl', path }));
}
throw UV('ENOTSUP', 'ioctl', path);
}
/** Perform an `ioctl` on a file or file system */
export function ioctlSync(
/** The path to the file or file system to perform the `ioctl` on */
path,
/** The command to perform (uint32) */
command,
/** The arguments to pass to the command */
...args) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
try {
const inode = new Inode(fs.statSync(resolved));
switch (command) {
case IOC.GetFlags:
case IOC32.GetFlags:
return inode.flags;
case IOC.SetFlags:
case IOC32.SetFlags:
inode.flags = args[0];
fs.touchSync(resolved, inode);
return undefined;
case IOC.GetVersion:
case IOC32.GetVersion:
return inode.version;
case IOC.SetVersion:
case IOC32.SetVersion:
inode.version = args[0];
fs.touchSync(resolved, inode);
return undefined;
case IOC.Fiemap:
break;
case IOC.GetXattr:
return new fsxattr(inode);
case IOC.SetXattr:
break;
case IOC.GetLabel:
return fs.label;
case IOC.SetLabel:
fs.label = args[0];
return undefined;
case IOC.GetUUID:
return fs.uuid;
case IOC.GetSysfsPath:
/**
* Returns the path component under /sys/fs/ that refers to this filesystem;
* also /sys/kernel/debug/ for filesystems with debugfs exports
* @todo Implement sysfs and have each FS implement the /sys/fs/<name> tree
*/
return `/sys/fs/${fs.name}/${fs.uuid}`;
}
}
catch (e) {
throw setUVMessage(Object.assign(e, { syscall: 'ioctl', path }));
}
throw UV('ENOTSUP', 'ioctl', path);
}