@zenfs/core
Advanced tools
Comparing version 1.2.8 to 1.2.9
@@ -645,13 +645,13 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
try { | ||
const tx = __addDisposableResource(env_15, this.store.transaction(), true); | ||
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath); | ||
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath); | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root should path == '/'. | ||
we will create a file with name '' in root if path is '/'. | ||
*/ | ||
if (path === '/') { | ||
if (path == '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
const tx = __addDisposableResource(env_15, this.store.transaction(), true); | ||
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath); | ||
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath); | ||
// Check if file already exists. | ||
@@ -697,13 +697,13 @@ if (listing[fname]) { | ||
try { | ||
const tx = __addDisposableResource(env_16, this.store.transaction(), false); | ||
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath); | ||
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath); | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root should p == '/'. | ||
we will create a file with name '' in root if path is '/'. | ||
*/ | ||
if (path === '/') { | ||
if (path == '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
const tx = __addDisposableResource(env_16, this.store.transaction(), false); | ||
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath); | ||
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath); | ||
// Check if file already exists. | ||
@@ -710,0 +710,0 @@ if (listing[fname]) { |
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js'; | ||
import { credentials } from './credentials.js'; | ||
import { DeviceFS, fullDevice, nullDevice, randomDevice, zeroDevice } from './devices.js'; | ||
import { DeviceFS } from './devices.js'; | ||
import * as cache from './emulation/cache.js'; | ||
import { config } from './emulation/config.js'; | ||
import * as fs from './emulation/index.js'; | ||
import { config } from './emulation/config.js'; | ||
import { Errno, ErrnoError } from './error.js'; | ||
@@ -64,2 +64,22 @@ import { FileSystem } from './filesystem.js'; | ||
/** | ||
* Like `fs.mount`, but it also creates missing directories. | ||
* @privateRemarks | ||
* This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files. | ||
* @internal | ||
*/ | ||
async function mount(path, mount) { | ||
if (path == '/') { | ||
fs.mount(path, mount); | ||
return; | ||
} | ||
const stats = await fs.promises.stat(path).catch(() => null); | ||
if (!stats) { | ||
await fs.promises.mkdir(path, { recursive: true }); | ||
} | ||
else if (!stats.isDirectory()) { | ||
throw ErrnoError.With('ENOTDIR', path, 'configure'); | ||
} | ||
fs.mount(path, mount); | ||
} | ||
/** | ||
* Configures ZenFS with `configuration` | ||
@@ -78,8 +98,5 @@ * @see Configuration | ||
const devfs = new DeviceFS(); | ||
devfs.createDevice('/null', nullDevice); | ||
devfs.createDevice('/zero', zeroDevice); | ||
devfs.createDevice('/full', fullDevice); | ||
devfs.createDevice('/random', randomDevice); | ||
devfs.addDefaults(); | ||
await devfs.ready(); | ||
fs.mount('/dev', devfs); | ||
await mount('/dev', devfs); | ||
} | ||
@@ -89,2 +106,4 @@ if (!configuration.mounts) { | ||
} | ||
const toMount = []; | ||
let unmountRoot = false; | ||
for (const [point, mountConfig] of Object.entries(configuration.mounts)) { | ||
@@ -97,5 +116,9 @@ if (!point.startsWith('/')) { | ||
} | ||
configuration.mounts[point] = await resolveMountConfig(mountConfig); | ||
if (point == '/') | ||
unmountRoot = true; | ||
toMount.push([point, await resolveMountConfig(mountConfig)]); | ||
} | ||
fs.mountObject(configuration.mounts); | ||
if (unmountRoot) | ||
fs.umount('/'); | ||
await Promise.all(toMount.map(([point, fs]) => mount(point, fs))); | ||
} |
@@ -15,2 +15,5 @@ /** | ||
export declare const credentials: Credentials; | ||
/** | ||
* @deprecated | ||
*/ | ||
export declare const rootCredentials: Credentials; |
@@ -9,2 +9,5 @@ export const credentials = { | ||
}; | ||
/** | ||
* @deprecated | ||
*/ | ||
export const rootCredentials = { | ||
@@ -11,0 +14,0 @@ uid: 0, |
@@ -127,2 +127,6 @@ import type { FileReadResult } from 'node:fs/promises'; | ||
createDevice<TData = any>(path: string, driver: DeviceDriver<TData>): Device<TData | Record<string, never>>; | ||
/** | ||
* Adds default devices | ||
*/ | ||
addDefaults(): void; | ||
constructor(); | ||
@@ -129,0 +133,0 @@ rename(oldPath: string, newPath: string): Promise<void>; |
@@ -171,2 +171,11 @@ /* | ||
} | ||
/** | ||
* Adds default devices | ||
*/ | ||
addDefaults() { | ||
this.createDevice('/null', nullDevice); | ||
this.createDevice('/zero', zeroDevice); | ||
this.createDevice('/full', fullDevice); | ||
this.createDevice('/random', randomDevice); | ||
} | ||
constructor() { | ||
@@ -173,0 +182,0 @@ super(new InMemoryStore('devfs')); |
@@ -56,7 +56,7 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
import * as cache from './cache.js'; | ||
import { config } from './config.js'; | ||
import * as constants from './constants.js'; | ||
import { Dir, Dirent } from './dir.js'; | ||
import { dirname, join, parse } from './path.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js'; | ||
import { config } from './config.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js'; | ||
import { ReadStream, WriteStream } from './streams.js'; | ||
@@ -589,3 +589,3 @@ import { FSWatcher, emitChange } from './watchers.js'; | ||
if (!stats) { | ||
throw ErrnoError.With('ENOENT', path, 'readdir'); | ||
throw ErrnoError.With('ENOENT', path, 'rmdir'); | ||
} | ||
@@ -660,12 +660,2 @@ if (!stats.isDirectory()) { | ||
const entries = await fs.readdir(resolved).catch(handleError); | ||
for (const point of mounts.keys()) { | ||
if (point.startsWith(path)) { | ||
const entry = point.slice(path.length); | ||
if (entry.includes('/') || entry.length == 0) { | ||
// ignore FSs mounted in subdirectories and any FS mounted to `path`. | ||
continue; | ||
} | ||
entries.push(entry); | ||
} | ||
} | ||
const values = []; | ||
@@ -672,0 +662,0 @@ const addEntry = async (entry) => { |
@@ -41,2 +41,5 @@ import type { BigIntStatsFs, StatsFs } from 'node:fs'; | ||
export declare function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E; | ||
/** | ||
* @deprecated | ||
*/ | ||
export declare function mountObject(mounts: MountObject): void; | ||
@@ -43,0 +46,0 @@ /** |
@@ -93,2 +93,5 @@ // Utilities and shared data | ||
} | ||
/** | ||
* @deprecated | ||
*/ | ||
export function mountObject(mounts) { | ||
@@ -95,0 +98,0 @@ if ('/' in mounts) { |
@@ -53,9 +53,9 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js'; | ||
import * as cache from './cache.js'; | ||
import { config } from './config.js'; | ||
import * as constants from './constants.js'; | ||
import { Dir, Dirent } from './dir.js'; | ||
import { dirname, join, parse } from './path.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js'; | ||
import { config } from './config.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js'; | ||
import { emitChange } from './watchers.js'; | ||
import * as cache from './cache.js'; | ||
export function renameSync(oldPath, newPath) { | ||
@@ -463,13 +463,2 @@ oldPath = normalizePath(oldPath); | ||
} | ||
for (const mount of mounts.keys()) { | ||
if (!mount.startsWith(path)) { | ||
continue; | ||
} | ||
const entry = mount.slice(path.length); | ||
if (entry.includes('/') || entry.length == 0) { | ||
// ignore FSs mounted in subdirectories and any FS mounted to `path`. | ||
continue; | ||
} | ||
entries.push(entry); | ||
} | ||
// Iterate over entries and handle recursive case if needed | ||
@@ -476,0 +465,0 @@ const values = []; |
@@ -93,4 +93,4 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { | ||
const promises = []; | ||
for (const key of sync.keysSync()) { | ||
promises.push(async.set(key, sync.getSync(key))); | ||
for (const key of await async.keys()) { | ||
promises.push(async.get(key).then(data => sync.setSync(key, data))); | ||
} | ||
@@ -97,0 +97,0 @@ await Promise.all(promises); |
{ | ||
"name": "@zenfs/core", | ||
"version": "1.2.8", | ||
"version": "1.2.9", | ||
"description": "A filesystem, anywhere", | ||
@@ -61,2 +61,3 @@ "funding": { | ||
"test": "tsx --test --experimental-test-coverage", | ||
"pretest": "npm run build", | ||
"build": "tsc -p tsconfig.json", | ||
@@ -63,0 +64,0 @@ "build:docs": "typedoc", |
@@ -537,2 +537,11 @@ import { credentials } from '../../credentials.js'; | ||
private async commitNew(path: string, type: FileType, mode: number, data: Uint8Array): Promise<Inode> { | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root if path is '/'. | ||
*/ | ||
if (path == '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
await using tx = this.store.transaction(); | ||
@@ -545,11 +554,2 @@ const parentPath = dirname(path), | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root should path == '/'. | ||
*/ | ||
if (path === '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
// Check if file already exists. | ||
@@ -586,2 +586,11 @@ if (listing[fname]) { | ||
protected commitNewSync(path: string, type: FileType, mode: number, data: Uint8Array = new Uint8Array()): Inode { | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root if path is '/'. | ||
*/ | ||
if (path == '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
using tx = this.store.transaction(); | ||
@@ -594,11 +603,2 @@ const parentPath = dirname(path), | ||
/* | ||
The root always exists. | ||
If we don't check this prior to taking steps below, | ||
we will create a file with name '' in root should p == '/'. | ||
*/ | ||
if (path === '/') { | ||
throw ErrnoError.With('EEXIST', path, 'commitNew'); | ||
} | ||
// Check if file already exists. | ||
@@ -605,0 +605,0 @@ if (listing[fname]) { |
import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js'; | ||
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js'; | ||
import { credentials } from './credentials.js'; | ||
import { DeviceFS, fullDevice, nullDevice, randomDevice, zeroDevice } from './devices.js'; | ||
import { DeviceFS } from './devices.js'; | ||
import * as cache from './emulation/cache.js'; | ||
import { config } from './emulation/config.js'; | ||
import * as fs from './emulation/index.js'; | ||
import type { AbsolutePath } from './emulation/path.js'; | ||
import { type MountObject } from './emulation/shared.js'; | ||
import { config } from './emulation/config.js'; | ||
import { Errno, ErrnoError } from './error.js'; | ||
@@ -155,2 +154,23 @@ import { FileSystem } from './filesystem.js'; | ||
/** | ||
* Like `fs.mount`, but it also creates missing directories. | ||
* @privateRemarks | ||
* This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files. | ||
* @internal | ||
*/ | ||
async function mount(path: string, mount: FileSystem): Promise<void> { | ||
if (path == '/') { | ||
fs.mount(path, mount); | ||
return; | ||
} | ||
const stats = await fs.promises.stat(path).catch(() => null); | ||
if (!stats) { | ||
await fs.promises.mkdir(path, { recursive: true }); | ||
} else if (!stats.isDirectory()) { | ||
throw ErrnoError.With('ENOTDIR', path, 'configure'); | ||
} | ||
fs.mount(path, mount); | ||
} | ||
/** | ||
* Configures ZenFS with `configuration` | ||
@@ -172,8 +192,5 @@ * @see Configuration | ||
const devfs = new DeviceFS(); | ||
devfs.createDevice('/null', nullDevice); | ||
devfs.createDevice('/zero', zeroDevice); | ||
devfs.createDevice('/full', fullDevice); | ||
devfs.createDevice('/random', randomDevice); | ||
devfs.addDefaults(); | ||
await devfs.ready(); | ||
fs.mount('/dev', devfs); | ||
await mount('/dev', devfs); | ||
} | ||
@@ -185,2 +202,5 @@ | ||
const toMount: [string, FileSystem][] = []; | ||
let unmountRoot = false; | ||
for (const [point, mountConfig] of Object.entries(configuration.mounts)) { | ||
@@ -195,6 +215,9 @@ if (!point.startsWith('/')) { | ||
configuration.mounts[point as keyof T & `/${string}`] = await resolveMountConfig(mountConfig); | ||
if (point == '/') unmountRoot = true; | ||
toMount.push([point, await resolveMountConfig(mountConfig)]); | ||
} | ||
fs.mountObject(configuration.mounts as MountObject); | ||
if (unmountRoot) fs.umount('/'); | ||
await Promise.all(toMount.map(([point, fs]) => mount(point, fs))); | ||
} |
@@ -24,2 +24,5 @@ /** | ||
/** | ||
* @deprecated | ||
*/ | ||
export const rootCredentials: Credentials = { | ||
@@ -26,0 +29,0 @@ uid: 0, |
@@ -249,2 +249,12 @@ /* | ||
/** | ||
* Adds default devices | ||
*/ | ||
public addDefaults(): void { | ||
this.createDevice('/null', nullDevice); | ||
this.createDevice('/zero', zeroDevice); | ||
this.createDevice('/full', fullDevice); | ||
this.createDevice('/random', randomDevice); | ||
} | ||
public constructor() { | ||
@@ -251,0 +261,0 @@ super(new InMemoryStore('devfs')); |
@@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ | ||
import * as cache from './cache.js'; | ||
import { config } from './config.js'; | ||
import * as constants from './constants.js'; | ||
import { Dir, Dirent } from './dir.js'; | ||
import { dirname, join, parse } from './path.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js'; | ||
import { config } from './config.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js'; | ||
import { ReadStream, WriteStream } from './streams.js'; | ||
@@ -629,3 +629,3 @@ import { FSWatcher, emitChange } from './watchers.js'; | ||
if (!stats) { | ||
throw ErrnoError.With('ENOENT', path, 'readdir'); | ||
throw ErrnoError.With('ENOENT', path, 'rmdir'); | ||
} | ||
@@ -741,13 +741,2 @@ if (!stats.isDirectory()) { | ||
for (const point of mounts.keys()) { | ||
if (point.startsWith(path)) { | ||
const entry = point.slice(path.length); | ||
if (entry.includes('/') || entry.length == 0) { | ||
// ignore FSs mounted in subdirectories and any FS mounted to `path`. | ||
continue; | ||
} | ||
entries.push(entry); | ||
} | ||
} | ||
const values: (string | Dirent | Buffer)[] = []; | ||
@@ -754,0 +743,0 @@ const addEntry = async (entry: string) => { |
@@ -109,2 +109,5 @@ // Utilities and shared data | ||
/** | ||
* @deprecated | ||
*/ | ||
export function mountObject(mounts: MountObject): void { | ||
@@ -111,0 +114,0 @@ if ('/' in mounts) { |
@@ -9,9 +9,9 @@ import { Buffer } from 'buffer'; | ||
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js'; | ||
import * as cache from './cache.js'; | ||
import { config } from './config.js'; | ||
import * as constants from './constants.js'; | ||
import { Dir, Dirent } from './dir.js'; | ||
import { dirname, join, parse } from './path.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js'; | ||
import { config } from './config.js'; | ||
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js'; | ||
import { emitChange } from './watchers.js'; | ||
import * as cache from './cache.js'; | ||
@@ -476,14 +476,2 @@ export function renameSync(oldPath: fs.PathLike, newPath: fs.PathLike): void { | ||
for (const mount of mounts.keys()) { | ||
if (!mount.startsWith(path)) { | ||
continue; | ||
} | ||
const entry = mount.slice(path.length); | ||
if (entry.includes('/') || entry.length == 0) { | ||
// ignore FSs mounted in subdirectories and any FS mounted to `path`. | ||
continue; | ||
} | ||
entries.push(entry); | ||
} | ||
// Iterate over entries and handle recursive case if needed | ||
@@ -490,0 +478,0 @@ const values: (string | Dirent | Buffer)[] = []; |
@@ -83,4 +83,4 @@ import { StoreFS } from '../backends/store/fs.js'; | ||
const promises = []; | ||
for (const key of sync.keysSync()) { | ||
promises.push(async.set(key, sync.getSync(key))); | ||
for (const key of await async.keys()) { | ||
promises.push(async.get(key).then(data => sync.setSync(key, data))); | ||
} | ||
@@ -87,0 +87,0 @@ |
@@ -6,3 +6,18 @@ import assert from 'node:assert'; | ||
suite('Directory', () => { | ||
const testDir = 'test-dir'; | ||
const testFiles = ['file1.txt', 'file2.txt', 'file3.txt']; | ||
const testDirectories = ['subdir1', 'subdir2']; | ||
await fs.promises.mkdir(testDir); | ||
for (const file of testFiles) { | ||
await fs.promises.writeFile(`${testDir}/${file}`, 'Sample content'); | ||
} | ||
for (const dir of testDirectories) { | ||
await fs.promises.mkdir(`${testDir}/${dir}`); | ||
for (const file of ['file4.txt', 'file5.txt']) { | ||
await fs.promises.writeFile(`${testDir}/${dir}/${file}`, 'Sample content'); | ||
} | ||
} | ||
suite('Directories', () => { | ||
test('mkdir', async () => { | ||
@@ -130,2 +145,69 @@ await fs.promises.mkdir('/one', 0o755); | ||
}); | ||
test('readdir returns files and directories', async () => { | ||
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true }); | ||
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name); | ||
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name); | ||
assert(testFiles.every(file => files.includes(file))); | ||
assert(testDirectories.every(dir => dirs.includes(dir))); | ||
}); | ||
test('readdirSync returns files and directories', () => { | ||
const dirents = fs.readdirSync(testDir, { withFileTypes: true }); | ||
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name); | ||
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name); | ||
assert(testFiles.every(file => files.includes(file))); | ||
assert(testDirectories.every(dir => dirs.includes(dir))); | ||
}); | ||
test('readdir returns Dirent objects', async () => { | ||
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true }); | ||
assert(dirents[0] instanceof fs.Dirent); | ||
}); | ||
test('readdirSync returns Dirent objects', () => { | ||
const dirents = fs.readdirSync(testDir, { withFileTypes: true }); | ||
assert(dirents[0] instanceof fs.Dirent); | ||
}); | ||
test('readdir works without withFileTypes option', async () => { | ||
const files = await fs.promises.readdir(testDir); | ||
assert(testFiles.every(entry => files.includes(entry))); | ||
assert(testDirectories.every(entry => files.includes(entry))); | ||
}); | ||
test('readdirSync works without withFileTypes option', () => { | ||
const files = fs.readdirSync(testDir); | ||
assert(testFiles.every(entry => files.includes(entry))); | ||
assert(testDirectories.every(entry => files.includes(entry))); | ||
}); | ||
test('readdir returns files recursively', async () => { | ||
const entries = await fs.promises.readdir(testDir, { recursive: true }); | ||
assert(entries.includes('file1.txt')); | ||
assert(entries.includes('subdir1/file4.txt')); | ||
assert(entries.includes('subdir2/file5.txt')); | ||
}); | ||
test('readdir returns Dirent recursively', async () => { | ||
const entries = await fs.promises.readdir(testDir, { recursive: true, withFileTypes: true }); | ||
assert(entries.find(entry => entry.path === 'file1.txt')); | ||
assert(entries.find(entry => entry.path === 'subdir1/file4.txt')); | ||
assert(entries.find(entry => entry.path === 'subdir2/file5.txt')); | ||
}); | ||
// New test for readdirSync with recursive: true | ||
test('readdirSync returns files recursively', () => { | ||
const entries = fs.readdirSync(testDir, { recursive: true }); | ||
assert(entries.includes('file1.txt')); | ||
assert(entries.includes('subdir1/file4.txt')); | ||
assert(entries.includes('subdir2/file5.txt')); | ||
}); | ||
test('Cyrillic file names', () => { | ||
fs.writeFileSync('/мой-файл.txt', 'HELLO!', 'utf-8'); | ||
assert(fs.readdirSync('/').includes('мой-файл.txt')); | ||
}); | ||
}); |
import assert from 'node:assert'; | ||
import { suite, test } from 'node:test'; | ||
import { credentials } from '../../dist/credentials.js'; | ||
import { R_OK, W_OK, X_OK } from '../../dist/emulation/constants.js'; | ||
@@ -9,3 +10,42 @@ import { join } from '../../dist/emulation/path.js'; | ||
const asyncMode = 0o777; | ||
const syncMode = 0o644; | ||
const file = 'a.js'; | ||
suite('Permissions', () => { | ||
test('chmod', async () => { | ||
await fs.promises.chmod(file, asyncMode.toString(8)); | ||
const stats = await fs.promises.stat(file); | ||
assert.equal(stats.mode & 0o777, asyncMode); | ||
fs.chmodSync(file, syncMode); | ||
assert.equal(fs.statSync(file).mode & 0o777, syncMode); | ||
}); | ||
test('fchmod', async () => { | ||
const handle = await fs.promises.open(file, 'a', 0o644); | ||
await handle.chmod(asyncMode); | ||
const stats = await handle.stat(); | ||
assert.equal(stats.mode & 0o777, asyncMode); | ||
fs.fchmodSync(handle.fd, syncMode); | ||
assert.equal(fs.statSync(file).mode & 0o777, syncMode); | ||
}); | ||
test('lchmod', async () => { | ||
const link = 'symbolic-link'; | ||
await fs.promises.symlink(file, link); | ||
await fs.promises.lchmod(link, asyncMode); | ||
const stats = await fs.promises.lstat(link); | ||
assert.equal(stats.mode & 0o777, asyncMode); | ||
fs.lchmodSync(link, syncMode); | ||
assert.equal(fs.lstatSync(link).mode & 0o777, syncMode); | ||
}); | ||
async function test_item(path: string): Promise<void> { | ||
@@ -31,3 +71,3 @@ const stats = await fs.promises.stat(path).catch((error: ErrnoError) => { | ||
for (const dir of await fs.promises.readdir(path)) { | ||
await test_item(join(path, dir)); | ||
await test('Access controls: ' + join(path, dir), () => test_item(join(path, dir))); | ||
} | ||
@@ -53,3 +93,6 @@ } else { | ||
test('recursive', () => test_item('/')); | ||
const copy = { ...credentials }; | ||
Object.assign(credentials, { uid: 1000, gid: 1000, euid: 1000, egid: 1000 }); | ||
test('Access controls: /', () => test_item('/')); | ||
Object.assign(credentials, copy); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
874081
23268
170