Skip to content

Commit 53117fa

Browse files
committed
Fix some error handling
1 parent fb23761 commit 53117fa

File tree

8 files changed

+50
-67
lines changed

8 files changed

+50
-67
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"@types/node": "^22.10.1 <22.13.7",
7171
"buffer": "^6.0.3",
7272
"eventemitter3": "^5.0.1",
73-
"kerium": "^1.3.3",
73+
"kerium": "^1.3.4",
7474
"memium": "^0.1.10",
7575
"readable-stream": "^4.5.2",
7676
"utilium": "^2.2.3"

src/internal/error.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export function wrap<const FS, const Prop extends keyof FS & string>(fs: FS, pro
2828
if (typeof fn !== 'function') throw new TypeError(`${prop} is not a function`);
2929
return function (...args: Parameters<typeof fn>) {
3030
try {
31-
return fn(...args);
31+
return fn.call(fs, ...args);
3232
} catch (e: any) {
33-
throw setUVMessage(Object.assign(e, { path, dest }));
33+
throw setUVMessage(Object.assign(e, { path, dest, syscall: prop.endsWith('Sync') ? prop.slice(0, -4) : prop }));
3434
}
3535
} as FS[Prop];
3636
}

src/vfs/ioctl.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
- include/uapi/linux/fs.h (`FS_IOC_*`)
66
*/
77

8-
import { Errno, Exception, UV } from 'kerium';
8+
import { Errno, Exception, setUVMessage, UV } from 'kerium';
99
import { sizeof, struct, types as t } from 'memium';
1010
import { _throw } from 'utilium';
1111
import { BufferView } from 'utilium/buffer.js';
1212
import type { V_Context } from '../context.js';
1313
import { Inode, InodeFlags } from '../internal/inode.js';
1414
import { normalizePath } from '../utils.js';
15-
import { fixError, resolveMount } from './shared.js';
15+
import { resolveMount } from './shared.js';
1616

1717
/*
1818
* Flags for the fsxattr.xflags field
@@ -202,7 +202,7 @@ export async function ioctl<const Command extends number, const Args extends __i
202202
return `/sys/fs/${fs.name}/${fs.uuid}` as _rt;
203203
}
204204
} catch (e: any) {
205-
throw fixError(e, { [resolved]: path });
205+
throw setUVMessage(Object.assign(e, { syscall: 'ioctl', path }));
206206
}
207207

208208
throw UV('ENOTSUP', 'ioctl', path);
@@ -267,7 +267,7 @@ export function ioctlSync<const Command extends number, const Args extends __ioc
267267
return `/sys/fs/${fs.name}/${fs.uuid}` as _rt;
268268
}
269269
} catch (e: any) {
270-
throw fixError(e, { [resolved]: path });
270+
throw setUVMessage(Object.assign(e, { syscall: 'ioctl', path }));
271271
}
272272

273273
throw UV('ENOTSUP', 'ioctl', path);

src/vfs/promises.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,13 @@ export async function mkdir(
789789
const __create = async (path: string, parent: InodeLike) => {
790790
if (checkAccess && !hasAccess(this, parent, constants.W_OK)) throw UV('EACCES', 'mkdir', dirname(path));
791791

792-
const inode = await fs.mkdir(path, {
793-
mode,
794-
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
795-
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
796-
});
792+
const inode = await fs
793+
.mkdir(path, {
794+
mode,
795+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
796+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
797+
})
798+
.catch(rethrow({ syscall: 'mkdir', path }));
797799
emitChange(this, 'rename', path);
798800
return inode;
799801
};
@@ -926,7 +928,7 @@ export async function link(this: V_Context, path: fs.PathLike, dest: fs.PathLike
926928
// We need to use the VFS here since the link path may be a mount point
927929
if (checkAccess && !(await stat.call(this, dirname(dest))).hasAccess(constants.W_OK, this)) throw UV('EACCES', 'link', dirname(dest));
928930

929-
if (checkAccess && !hasAccess(this, await fs.stat(resolved), constants.R_OK)) throw UV('EACCES', $ex);
931+
if (checkAccess && !hasAccess(this, await fs.stat(resolved).catch(rethrow($ex)), constants.R_OK)) throw UV('EACCES', $ex);
930932

931933
return await fs.link(resolved, dst.path).catch(rethrow($ex));
932934
}
@@ -1066,7 +1068,12 @@ async function _resolve($: V_Context, path: string, preserveSymlinks?: boolean):
10661068
const maybePath = join(realDir, base);
10671069
const resolved = resolveMount(maybePath, $);
10681070

1069-
const stats = await resolved.fs.stat(resolved.path).catch(rethrow({ syscall: 'stat', path: maybePath }));
1071+
const stats = await resolved.fs.stat(resolved.path).catch((e: Exception) => {
1072+
if (e.code == 'ENOENT') return;
1073+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
1074+
});
1075+
1076+
if (!stats) return { ...resolved, fullPath: path };
10701077
if (!isSymbolicLink(stats)) {
10711078
return { ...resolved, fullPath: maybePath, stats };
10721079
}

src/vfs/shared.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -102,31 +102,6 @@ export function resolveMount(path: string, ctx: V_Context): ResolvedMount {
102102
throw alert(new Exception(Errno.EIO, 'No file system for ' + path));
103103
}
104104

105-
/**
106-
* Reverse maps the paths in text from the mounted FileSystem to the global path
107-
* @internal @hidden
108-
*/
109-
export function fixPaths(text: string, paths: Record<string, string>): string {
110-
for (const [from, to] of Object.entries(paths)) {
111-
text = text?.replaceAll(from, to);
112-
}
113-
return text;
114-
}
115-
116-
/**
117-
* Fix paths in error stacks
118-
* @internal @hidden
119-
*/
120-
export function fixError<E extends Exception>(e: E, paths: Record<string, string>): E {
121-
try {
122-
e.message = fixPaths(e.message, paths);
123-
} catch {
124-
// `message` is read only
125-
}
126-
if (e.path) e.path = fixPaths(e.path, paths);
127-
return e;
128-
}
129-
130105
/**
131106
* @internal @hidden
132107
*/

src/vfs/sync.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import * as constants from './constants.js';
1717
import { Dir, Dirent } from './dir.js';
1818
import { deleteFD, fromFD, SyncHandle, toFD } from './file.js';
1919
import * as flags from './flags.js';
20-
import { _statfs, fixError, resolveMount } from './shared.js';
20+
import { _statfs, resolveMount } from './shared.js';
2121
import { BigIntStats, Stats } from './stats.js';
2222
import { emitChange } from './watchers.js';
2323

@@ -450,16 +450,13 @@ futimesSync satisfies typeof fs.futimesSync;
450450
export function rmdirSync(this: V_Context, path: fs.PathLike): void {
451451
path = normalizePath(path);
452452
const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
453-
try {
454-
const stats = fs.statSync(resolved);
455-
if (!isDirectory(stats)) throw UV('ENOTDIR', 'rmdir');
456-
if (checkAccess && !hasAccess(this, stats, constants.W_OK)) throw UV('EACCES', 'rmdir');
457453

458-
fs.rmdirSync(resolved);
459-
emitChange(this, 'rename', path.toString());
460-
} catch (e: any) {
461-
throw setUVMessage(Object.assign(e, { path }));
462-
}
454+
const stats = wrap(fs, 'statSync', path)(resolved);
455+
if (!isDirectory(stats)) throw UV('ENOTDIR', 'rmdir', path);
456+
if (checkAccess && !hasAccess(this, stats, constants.W_OK)) throw UV('EACCES', 'rmdir', path);
457+
458+
wrap(fs, 'rmdirSync', path)(resolved);
459+
emitChange(this, 'rename', path.toString());
463460
}
464461
rmdirSync satisfies typeof fs.rmdirSync;
465462

@@ -480,7 +477,11 @@ export function mkdirSync(this: V_Context, path: fs.PathLike, options?: fs.Mode
480477
const __create = (resolved: string, path: string, parent: InodeLike) => {
481478
if (checkAccess && !hasAccess(this, parent, constants.W_OK)) throw UV('EACCES', 'mkdir', dirname(path));
482479

483-
const inode = fs.mkdirSync(resolved, {
480+
const inode = wrap(
481+
fs,
482+
'mkdirSync',
483+
path
484+
)(resolved, {
484485
mode,
485486
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
486487
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
@@ -721,20 +722,20 @@ function _resolveSync($: V_Context, path: string, preserveSymlinks?: boolean): R
721722
const maybePath = join(realDir, base);
722723
const resolved = resolveMount(maybePath, $);
723724

725+
let stats: InodeLike | undefined;
724726
try {
725-
const stats = resolved.fs.statSync(resolved.path);
726-
if (!isSymbolicLink(stats)) {
727-
return { ...resolved, fullPath: maybePath, stats };
728-
}
727+
stats = resolved.fs.statSync(resolved.path);
728+
} catch (e: any) {
729+
if (e.code === 'ENOENT') return { ...resolved, fullPath: path };
730+
throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
731+
}
729732

730-
const target = resolve.call($, realDir, readlinkSync.call($, maybePath).toString());
731-
return _resolveSync($, target);
732-
} catch (e) {
733-
if ((e as Exception).code == 'ENOENT') {
734-
return { ...resolved, fullPath: path };
735-
}
736-
throw fixError(e as Exception, { [resolved.path]: maybePath });
733+
if (!isSymbolicLink(stats)) {
734+
return { ...resolved, fullPath: maybePath, stats };
737735
}
736+
737+
const target = resolve.call($, realDir, readlinkSync.call($, maybePath).toString());
738+
return _resolveSync($, target);
738739
}
739740

740741
export function realpathSync(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Buffer;

tests/fs/errors.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ suite('Error messages', () => {
1313

1414
test('stat', () => assert.rejects(() => fs.promises.stat(path), missing));
1515
test('mkdir', () => assert.rejects(() => fs.promises.mkdir(existingFile, 0o666), existing));
16-
test('rmdir', () => assert.rejects(() => fs.promises.rmdir(path), missing));
17-
test('rmdir', () => assert.rejects(() => fs.promises.rmdir(existingFile), notDir));
16+
test('rmdir (missing)', () => assert.rejects(() => fs.promises.rmdir(path), missing));
17+
test('rmdir (existing)', () => assert.rejects(() => fs.promises.rmdir(existingFile), notDir));
1818
test('rename', () => assert.rejects(() => fs.promises.rename(path, 'foo'), missing));
1919
test('open', () => assert.rejects(() => fs.promises.open(path, 'r'), missing));
2020
test('readdir', () => assert.rejects(() => fs.promises.readdir(path), missing));

0 commit comments

Comments
 (0)