@zenfs/core
Version:
A filesystem, anywhere
97 lines (96 loc) • 3.81 kB
JavaScript
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
import { credentials } from './credentials.js';
import { DeviceFS, fullDevice, nullDevice, randomDevice, zeroDevice } from './devices.js';
import * as cache from './emulation/cache.js';
import * as fs from './emulation/index.js';
import { config } from './emulation/config.js';
import { Errno, ErrnoError } from './error.js';
import { FileSystem } from './filesystem.js';
function isMountConfig(arg) {
return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;
}
/**
* Retrieve a file system with `configuration`.
* @see MountConfiguration
*/
export async function resolveMountConfig(configuration, _depth = 0) {
if (typeof configuration !== 'object' || configuration == null) {
throw new ErrnoError(Errno.EINVAL, 'Invalid options on mount configuration');
}
if (!isMountConfig(configuration)) {
throw new ErrnoError(Errno.EINVAL, 'Invalid mount configuration');
}
if (configuration instanceof FileSystem) {
await configuration.ready();
return configuration;
}
if (isBackend(configuration)) {
configuration = { backend: configuration };
}
for (const [key, value] of Object.entries(configuration)) {
if (key == 'backend') {
continue;
}
if (!isMountConfig(value)) {
continue;
}
if (_depth > 10) {
throw new ErrnoError(Errno.EINVAL, 'Invalid configuration, too deep and possibly infinite');
}
configuration[key] = await resolveMountConfig(value, ++_depth);
}
const { backend } = configuration;
if (!(await backend.isAvailable())) {
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
}
await checkOptions(backend, configuration);
const mount = (await backend.create(configuration));
mount._disableSync = configuration.disableAsyncCache || false;
await mount.ready();
return mount;
}
/**
* Configures ZenFS with single mount point /
*/
export async function configureSingle(configuration) {
if (!isBackendConfig(configuration)) {
throw new TypeError('Invalid single mount point configuration');
}
const resolved = await resolveMountConfig(configuration);
fs.umount('/');
fs.mount('/', resolved);
}
/**
* Configures ZenFS with `configuration`
* @see Configuration
*/
export async function configure(configuration) {
const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
cache.setEnabled(configuration.cacheStats ?? false);
config.checkAccess = !configuration.disableAccessChecks;
config.syncOnRead = !configuration.disableSyncOnRead;
if (configuration.addDevices) {
const devfs = new DeviceFS();
devfs.createDevice('/null', nullDevice);
devfs.createDevice('/zero', zeroDevice);
devfs.createDevice('/full', fullDevice);
devfs.createDevice('/random', randomDevice);
await devfs.ready();
fs.mount('/dev', devfs);
}
if (!configuration.mounts) {
return;
}
for (const [point, mountConfig] of Object.entries(configuration.mounts)) {
if (!point.startsWith('/')) {
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
}
if (isBackendConfig(mountConfig)) {
mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
}
configuration.mounts[point] = await resolveMountConfig(mountConfig);
}
fs.mountObject(configuration.mounts);
}