@zenfs/core
Version:
A filesystem, anywhere
197 lines (196 loc) • 6.74 kB
JavaScript
// SPDX-License-Identifier: LGPL-3.0-or-later
import { Buffer } from 'buffer';
import { rethrow, setUVMessage, UV } from 'kerium';
import { Attributes, hasAccess } from '../internal/inode.js';
import { normalizePath } from '../utils.js';
import { checkAccess } from './config.js';
import { R_OK, W_OK } from '../constants.js';
import { resolveMount } from './shared.js';
const _allowedRestrictedNames = [];
/**
* Check permission for the attribute name.
* For now, only attributes in the 'user' namespace are supported.
* @throws ENOTSUP for attributes in namespaces other than 'user'
*/
function checkName($, name, path, syscall) {
if (!name.startsWith('user.') && !_allowedRestrictedNames.includes(name))
throw UV('ENOTSUP', syscall, path);
}
export async function get(path, name, opt = {}) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
checkName(this, name, path, 'xattr.get');
const inode = await fs.stat(resolved).catch(rethrow('xattr.get', path));
if (checkAccess && !hasAccess(this, inode, R_OK))
throw UV('EACCES', 'xattr.get', path);
inode.attributes ??= new Attributes();
const value = inode.attributes.get(name);
if (!value)
throw UV('ENODATA', 'xattr.get', path);
const buffer = Buffer.from(value);
return opt.encoding == 'buffer' || !opt.encoding ? buffer : buffer.toString(opt.encoding);
}
export function getSync(path, name, opt = {}) {
path = normalizePath(path);
checkName(this, name, path, 'xattr.get');
const { fs, path: resolved } = resolveMount(path, this);
let inode;
try {
inode = fs.statSync(resolved);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
if (checkAccess && !hasAccess(this, inode, R_OK))
throw UV('EACCES', 'xattr.get', path);
inode.attributes ??= new Attributes();
const value = inode.attributes.get(name);
if (!value)
throw UV('ENODATA', 'xattr.get', path);
const buffer = Buffer.from(value);
return opt.encoding == 'buffer' || !opt.encoding ? buffer : buffer.toString(opt.encoding);
}
/**
* Sets the value of an extended attribute.
*
* @param path Path to the file
* @param name Name of the attribute to set
* @param value Value to set
* @param opt Options for the operation
*/
export async function set(path, name, value, opt = {}) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
checkName(this, name, path, 'xattr.set');
const inode = await fs.stat(resolved).catch(rethrow('xattr.set', path));
if (checkAccess && !hasAccess(this, inode, W_OK))
throw UV('EACCES', 'xattr.set', path);
inode.attributes ??= new Attributes();
const attr = inode.attributes.get(name);
if (opt.create && attr)
throw UV('EEXIST', 'xattr.set', path);
if (opt.replace && !attr)
throw UV('ENODATA', 'xattr.set', path);
inode.attributes.set(name, Buffer.from(value));
await fs.touch(resolved, inode).catch(rethrow('xattr.set', path));
}
/**
* Synchronously sets the value of an extended attribute.
*
* @param path Path to the file
* @param name Name of the attribute to set
* @param value Value to set
* @param opt Options for the operation
*/
export function setSync(path, name, value, opt = {}) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
checkName(this, name, path, 'xattr.set');
let inode;
try {
inode = fs.statSync(resolved);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
if (checkAccess && !hasAccess(this, inode, W_OK))
throw UV('EACCES', 'xattr.set', path);
inode.attributes ??= new Attributes();
const attr = inode.attributes.get(name);
if (opt.create && attr)
throw UV('EEXIST', 'xattr.set', path);
if (opt.replace && !attr)
throw UV('ENODATA', 'xattr.set', path);
inode.attributes.set(name, Buffer.from(value));
try {
fs.touchSync(resolved, inode);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
}
/**
* Removes an extended attribute from a file.
*
* @param path Path to the file
* @param name Name of the attribute to remove
*/
export async function remove(path, name) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
checkName(this, name, path, 'xattr.remove');
const inode = await fs.stat(resolved).catch(rethrow('xattr.remove', path));
if (checkAccess && !hasAccess(this, inode, W_OK))
throw UV('EACCES', 'xattr.remove', path);
inode.attributes ??= new Attributes();
const attr = inode.attributes.get(name);
if (!attr)
throw UV('ENODATA', 'xattr.remove', path);
inode.attributes.remove(name);
await fs.touch(resolved, inode);
}
/**
* Synchronously removes an extended attribute from a file.
*
* @param path Path to the file
* @param name Name of the attribute to remove
*/
export function removeSync(path, name) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
checkName(this, name, path, 'xattr.remove');
let inode;
try {
inode = fs.statSync(resolved);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
if (checkAccess && !hasAccess(this, inode, W_OK))
throw UV('EACCES', 'xattr.remove', path);
inode.attributes ??= new Attributes();
const attr = inode.attributes.get(name);
if (!attr)
throw UV('ENODATA', 'xattr.remove', path);
inode.attributes.remove(name);
try {
fs.touchSync(resolved, inode);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
}
/**
* Lists all extended attributes of a file.
*
* @param path Path to the file
* @returns Array of attribute names
*/
export async function list(path) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
const inode = await fs.stat(resolved).catch(rethrow('xattr.list', path));
if (!inode.attributes)
return [];
return inode.attributes.keys().toArray();
}
/**
* Synchronously lists all extended attributes of a file.
*
* @param path Path to the file
* @returns Array of attribute names
*/
export function listSync(path) {
path = normalizePath(path);
const { fs, path: resolved } = resolveMount(path, this);
let inode;
try {
inode = fs.statSync(resolved);
}
catch (e) {
throw setUVMessage(Object.assign(e, { path }));
}
if (!inode.attributes)
return [];
return inode.attributes.keys().toArray();
}