UNPKG

@zenfs/core

Version:

A filesystem, anywhere

253 lines (252 loc) 12 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; }; // SPDX-License-Identifier: LGPL-3.0-or-later /* Access Control Lists. At the moment, they are only intended for internal use. They also are not checked for permissions yet. Please use a namespace import for the best experience. */ import { withErrno } from 'kerium'; import { err } from 'kerium/log'; import { sizeof } from 'memium'; import { $from, struct, types as t } from 'memium/decorators'; import { BufferView } from 'utilium/buffer'; import { R_OK, S_IRWXG, S_IRWXO, S_IRWXU, W_OK, X_OK } from '../constants.js'; import { contextOf } from '../internal/contexts.js'; import { Attributes } from '../internal/inode.js'; import * as xattr from './xattr.js'; const version = 2; export var Type; (function (Type) { Type[Type["Access"] = 32768] = "Access"; Type[Type["Default"] = 16384] = "Default"; })(Type || (Type = {})); export var Tag; (function (Tag) { Tag[Tag["UserObj"] = 1] = "UserObj"; Tag[Tag["User"] = 2] = "User"; Tag[Tag["GroupObj"] = 4] = "GroupObj"; Tag[Tag["Group"] = 8] = "Group"; Tag[Tag["Mask"] = 16] = "Mask"; Tag[Tag["Other"] = 32] = "Other"; /** * @internal @hidden */ Tag[Tag["_None"] = 0] = "_None"; })(Tag || (Tag = {})); let Entry = (() => { var _a, _b, _c; let _classDecorators = [struct.packed()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; let _classSuper = $from(BufferView); let _tag_decorators; let _tag_initializers = []; let _tag_extraInitializers = []; let _perm_decorators; let _perm_initializers = []; let _perm_extraInitializers = []; let _id_decorators; let _id_initializers = []; let _id_extraInitializers = []; var Entry = class extends _classSuper { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; _tag_decorators = [(_a = t).uint16.bind(_a)]; _perm_decorators = [(_b = t).uint16.bind(_b)]; _id_decorators = [(_c = t).uint32.bind(_c)]; __esDecorate(this, null, _tag_decorators, { kind: "accessor", name: "tag", static: false, private: false, access: { has: obj => "tag" in obj, get: obj => obj.tag, set: (obj, value) => { obj.tag = value; } }, metadata: _metadata }, _tag_initializers, _tag_extraInitializers); __esDecorate(this, null, _perm_decorators, { kind: "accessor", name: "perm", static: false, private: false, access: { has: obj => "perm" in obj, get: obj => obj.perm, set: (obj, value) => { obj.perm = value; } }, metadata: _metadata }, _perm_initializers, _perm_extraInitializers); __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(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); Entry = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); } static name = 'ACL.Entry'; #tag_accessor_storage = __runInitializers(this, _tag_initializers, void 0); get tag() { return this.#tag_accessor_storage; } set tag(value) { this.#tag_accessor_storage = value; } #perm_accessor_storage = (__runInitializers(this, _tag_extraInitializers), __runInitializers(this, _perm_initializers, void 0)); get perm() { return this.#perm_accessor_storage; } set perm(value) { this.#perm_accessor_storage = value; } #id_accessor_storage = (__runInitializers(this, _perm_extraInitializers), __runInitializers(this, _id_initializers, void 0)); get id() { return this.#id_accessor_storage; } set id(value) { this.#id_accessor_storage = value; } constructor() { super(...arguments); __runInitializers(this, _id_extraInitializers); } static { __runInitializers(_classThis, _classExtraInitializers); } }; return Entry = _classThis; })(); export { Entry }; let ACL = (() => { var _a; let _classDecorators = [struct.packed()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; let _classSuper = $from(BufferView); let _version_decorators; let _version_initializers = []; let _version_extraInitializers = []; var ACL = class extends _classSuper { static { _classThis = this; } static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; _version_decorators = [(_a = t).uint32.bind(_a)]; __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(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); ACL = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); } static name = 'ACL'; #version_accessor_storage = __runInitializers(this, _version_initializers, void 0); get version() { return this.#version_accessor_storage; } set version(value) { this.#version_accessor_storage = value; } entries = (__runInitializers(this, _version_extraInitializers), []); constructor(...args) { super(...args); this.version ||= version; if (this.version != version) throw err(withErrno('EINVAL', 'Invalid ACL version')); for (let offset = sizeof(ACL); offset < this.byteLength; offset += sizeof(Entry)) { if (offset + sizeof(Entry) > this.byteLength) throw err(withErrno('EIO', 'Invalid ACL data')); this.entries.push(new Entry(this.buffer, offset)); } } static { __runInitializers(_classThis, _classExtraInitializers); } }; return ACL = _classThis; })(); export { ACL }; export function fromMode(mode) { const acl = new ACL(); acl.entries.push(Object.assign(new Entry(), { tag: Tag.UserObj, perm: (mode & S_IRWXU) >> 6 }), Object.assign(new Entry(), { tag: Tag.GroupObj, perm: (mode & S_IRWXG) >> 3 }), Object.assign(new Entry(), { tag: Tag.Other, perm: mode & S_IRWXO })); return acl; } export function toMode(acl) { let mode = 0; for (const entry of acl.entries) { switch (entry.tag) { case Tag.UserObj: mode |= entry.perm << 6; break; case Tag.GroupObj: mode |= entry.perm << 3; break; case Tag.Other: mode |= entry.perm; break; case Tag.User: case Tag.Group: case Tag.Mask: case Tag._None: continue; } } return mode; } export async function get($, path) { return new ACL(await xattr.get.call($, path, 'system.posix_acl_access')); } export function getSync($, path) { return new ACL(xattr.getSync.call($, path, 'system.posix_acl_access')); } export async function set($, path, acl) { await xattr.set.call($, path, 'system.posix_acl_access', new Uint8Array(acl.buffer, acl.byteOffset, acl.byteLength)); } export function setSync($, path, acl) { xattr.setSync.call($, path, 'system.posix_acl_access', new Uint8Array(acl.buffer, acl.byteOffset, acl.byteLength)); } export let shouldCheck = true; export function setChecks(enabled) { shouldCheck = enabled; } /** * Checks if a given user/group has access to this item * @param access The requested access, combination of `W_OK`, `R_OK`, and `X_OK` */ export function check($, inode, access) { if (!shouldCheck) return true; inode.attributes ??= new Attributes(); const { euid, egid } = contextOf($).credentials; const data = inode.attributes.get('system.posix_acl_access'); if (!data) return true; const acl = new ACL(data); let mask = R_OK | W_OK | X_OK; let result = false; for (const entry of acl.entries) { switch (entry.tag) { case Tag.UserObj: if (inode.uid == euid && (entry.perm & access) === access) result = true; break; case Tag.User: if (entry.id == euid && (entry.perm & mask & access) === access) result = true; break; case Tag.GroupObj: if (inode.gid == egid && (entry.perm & mask & access) === access) result = true; break; case Tag.Group: if (entry.id == egid && (entry.perm & mask & access) === access) result = true; break; case Tag.Mask: mask = entry.perm; break; case Tag.Other: if ((entry.perm & mask & access) === access) result = true; break; case Tag._None: continue; } } return result; }