Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ jobs:

- name: Build
run: npm run build

- name: Unit tests
run: npx zenfs-test -vn --common
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ dist
docs
*.log
tmp
build
build
.*
!.github
!.vscode
!.gitignore
!.prettierignore
!.gitattributes
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint src",
"lint": "eslint src tests",
"test": "npx zenfs-test -bcn",
"build": "tsc -p tsconfig.json",
"build:docs": "typedoc --out docs --name 'ZenFS Emscripten' src/index.ts",
"prepublishOnly": "npm run build"
Expand Down
145 changes: 145 additions & 0 deletions src/errno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import { Errno } from 'kerium';

/**
* ZenFS and Emscripten do not number their errnos the same way, and this is the translation between them.
*/
const linuxToEmscripten: Record<number, number> = {
[Errno.EPERM]: 63,
[Errno.ENOENT]: 44,
[Errno.ESRCH]: 71,
[Errno.EINTR]: 27,
[Errno.EIO]: 29,
[Errno.ENXIO]: 60,
[Errno.E2BIG]: 1,
[Errno.ENOEXEC]: 45,
[Errno.EBADF]: 8,
[Errno.ECHILD]: 12,
[Errno.EAGAIN]: 6,
[Errno.ENOMEM]: 48,
[Errno.EACCES]: 2,
[Errno.EFAULT]: 21,
[Errno.ENOTBLK]: 105,
[Errno.EBUSY]: 10,
[Errno.EEXIST]: 20,
[Errno.EXDEV]: 75,
[Errno.ENODEV]: 43,
[Errno.ENOTDIR]: 54,
[Errno.EISDIR]: 31,
[Errno.EINVAL]: 28,
[Errno.ENFILE]: 41,
[Errno.EMFILE]: 33,
[Errno.ETXTBSY]: 74,
[Errno.EFBIG]: 22,
[Errno.ENOSPC]: 51,
[Errno.ESPIPE]: 70,
[Errno.EROFS]: 69,
[Errno.EMLINK]: 34,
[Errno.EPIPE]: 64,
[Errno.EDOM]: 18,
[Errno.ERANGE]: 68,
[Errno.EDEADLK]: 16,
[Errno.ENAMETOOLONG]: 37,
[Errno.ENOLCK]: 46,
[Errno.ENOSYS]: 52,
[Errno.ENOTEMPTY]: 55,
[Errno.ELOOP]: 32,
[Errno.ENOMSG]: 49,
[Errno.EIDRM]: 24,
[Errno.ECHRNG]: 106,
[Errno.EL2NSYNC]: 156,
[Errno.EL3HLT]: 107,
[Errno.EL3RST]: 108,
[Errno.ENRNG]: 109, // ELNRNG on Linux
[Errno.EUNATCH]: 110,
[Errno.ECSI]: 111, // ENOCSI on Linux
[Errno.EL2HLT]: 112,
[Errno.EBADE]: 113,
[Errno.EBADR]: 114,
[Errno.EXFULL]: 115,
[Errno.ENOANO]: 104,
[Errno.EBADRQC]: 103,
[Errno.EBADSLT]: 102,
[Errno.EBFONT]: 101,
[Errno.ENOSTR]: 100,
[Errno.ENODATA]: 116,
[Errno.ETIME]: 117,
[Errno.ENOSR]: 118,
[Errno.ENONET]: 119,
[Errno.ENOPKG]: 120,
[Errno.EREMOTE]: 121,
[Errno.ENOLINK]: 47,
[Errno.EADV]: 122,
[Errno.ESRMNT]: 123,
[Errno.ECOMM]: 124,
[Errno.EPROTO]: 65,
[Errno.EMULTIHOP]: 36,
[Errno.EDOTDOT]: 125,
[Errno.EBADMSG]: 9,
[Errno.EOVERFLOW]: 61,
[Errno.ENOTUNIQ]: 126,
[Errno.EBADFD]: 127,
[Errno.EREMCHG]: 128,
[Errno.ELIBACC]: 129,
[Errno.ELIBBAD]: 130,
[Errno.ELIBSCN]: 131,
[Errno.ELIBMAX]: 132,
[Errno.ELIBEXEC]: 133,
[Errno.EILSEQ]: 25,
[Errno.ERESTART]: 134,
[Errno.ESTRPIPE]: 135,
[Errno.EUSERS]: 136,
[Errno.ENOTSOCK]: 57,
[Errno.EDESTADDRREQ]: 17,
[Errno.EMSGSIZE]: 35,
[Errno.EPROTOTYPE]: 67,
[Errno.ENOPROTOOPT]: 50,
[Errno.EPROTONOSUPPORT]: 66,
[Errno.ESOCKTNOSUPPORT]: 137,
[Errno.ENOTSUP]: 138,
[Errno.EPFNOSUPPORT]: 139,
[Errno.EAFNOSUPPORT]: 5,
[Errno.EADDRINUSE]: 3,
[Errno.EADDRNOTAVAIL]: 4,
[Errno.ENETDOWN]: 38,
[Errno.ENETUNREACH]: 40,
[Errno.ENETRESET]: 39,
[Errno.ECONNABORTED]: 13,
[Errno.ECONNRESET]: 15,
[Errno.ENOBUFS]: 42,
[Errno.EISCONN]: 30,
[Errno.ENOTCONN]: 53,
[Errno.ESHUTDOWN]: 140,
[Errno.ETOOMANYREFS]: 141,
[Errno.ETIMEDOUT]: 73,
[Errno.ECONNREFUSED]: 14,
[Errno.EHOSTDOWN]: 142,
[Errno.EHOSTUNREACH]: 23,
[Errno.EALREADY]: 7,
[Errno.EINPROGRESS]: 26,
[Errno.ESTALE]: 72,
[Errno.EEUCLEAN]: 143, // EUCLEAN on Linux
[Errno.ENOTNAM]: 144,
[Errno.ENAVAIL]: 145,
[Errno.EISNAM]: 146,
[Errno.EREMOTEIO]: 147,
[Errno.EDQUOT]: 19,
[Errno.ENOMEDIUM]: 148,
[Errno.EMEDIUMTYPE]: 149,
[Errno.ECANCELED]: 11,
[Errno.ENOKEY]: 150,
[Errno.EKEYEXPIRED]: 151,
[Errno.EKEYREVOKED]: 152,
[Errno.EKEYREJECTED]: 153,
[Errno.EOWNERDEAD]: 62,
[Errno.ENOTRECOVERABLE]: 56,
[Errno.ERFKILL]: 154,
[Errno.EHWPOISON]: 155,
} satisfies Record<Errno, number>;

/**
* A Linux errno as Emscripten numbers it. Values outside kerium's `Errno` are passed through.
*/
export function toEmscriptenErrno(errno: number): number {
return linuxToEmscripten[errno] ?? errno;
}
37 changes: 19 additions & 18 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Stats } from '@zenfs/core';
import { fs as zfs } from '@zenfs/core';
import { parse as parseFlag } from '@zenfs/core/vfs/flags.js';
import { Errno } from 'kerium';
import { toEmscriptenErrno } from './errno.js';
import type { EmFS } from './emscripten.js';
import { join, normalize } from '@zenfs/core/path';
import { info } from 'kerium/log';
Expand Down Expand Up @@ -56,7 +57,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {

public createNode(parent: Node | null, name: string, mode: number, rdev?: number): Node {
if (!this.em_fs.isDir(mode) && !this.em_fs.isFile(mode) && !this.em_fs.isLink(mode)) {
throw new this.em_fs.ErrnoError(Errno.EINVAL);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(Errno.EINVAL));
}
const node: Node = new this.em_fs.FSNode(parent!, name, mode, rdev!);
node.node_ops = this.node_ops;
Expand All @@ -73,7 +74,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
return stat.mode;
}
Expand All @@ -99,7 +100,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
return stat;
},
Expand All @@ -123,7 +124,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
// Ignore not supported errors. Emscripten does utimesSync when it
// writes files, but never really requires the value to be set.
if (e.code !== 'ENOTSUP') {
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
}
if (attr.size !== undefined) {
Expand All @@ -133,7 +134,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
}
},
Expand All @@ -158,7 +159,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
return node;
},
Expand All @@ -176,7 +177,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},

Expand All @@ -188,7 +189,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},

Expand All @@ -200,7 +201,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},

Expand All @@ -216,7 +217,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},

Expand All @@ -228,7 +229,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},

Expand All @@ -240,7 +241,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},
};
Expand All @@ -255,7 +256,7 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},
close: (stream: EmFS.FSStream): void => {
Expand All @@ -267,22 +268,22 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
if (!e.code) {
throw e;
}
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},
read: (stream: EmFS.FSStream, buffer: Uint8Array, offset: number, length: number, position: number): number => {
try {
// debugger;
return this.fs.readSync(stream.nfd!, buffer, offset, length, position);
} catch (e: any) {
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},
write: (stream: EmFS.FSStream, buffer: Uint8Array, offset: number, length: number, position: number): number => {
try {
return this.fs.writeSync(stream.nfd!, buffer, offset, length, position);
} catch (e: any) {
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
},
llseek: (stream: EmFS.FSStream, offset: number, whence: number): number => {
Expand All @@ -295,12 +296,12 @@ export default class EmscriptenPlugin implements EmscriptenNodeFS {
try {
position += this.fs.fstatSync(stream.nfd!).size;
} catch (e: any) {
throw new this.em_fs.ErrnoError(e.errno);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(e.errno));
}
}

if (position < 0) {
throw new this.em_fs.ErrnoError(Errno.EINVAL);
throw new this.em_fs.ErrnoError(toEmscriptenErrno(Errno.EINVAL));
}

stream.position = position;
Expand Down
29 changes: 29 additions & 0 deletions tests/errno.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import { Errno } from 'kerium';
import assert from 'node:assert/strict';
import { suite, test } from 'node:test';
import { toEmscriptenErrno } from '@zenfs/emscripten/errno.js';

/** Every distinct value kerium's `Errno` can carry. */
const errnos: number[] = Object.values(Errno).filter(value => typeof value === 'number');

suite('errno translation', () => {
test('ENOENT becomes the 44 that FS.open looks for', () => {
assert.equal(toEmscriptenErrno(Errno.ENOENT), 44);
assert.equal(toEmscriptenErrno(Errno.EACCES), 2);
});

test('no errno translates to itself', () => {
const unchanged = errnos.filter(errno => toEmscriptenErrno(errno) === errno);
assert.deepEqual(unchanged, []);
});

test('translating twice is not translating once', () => {
const once = toEmscriptenErrno(Errno.EACCES);
assert.notEqual(toEmscriptenErrno(once), once);
});

test('a number that is not an errno is left alone', () => {
assert.equal(toEmscriptenErrno(9999), 9999);
});
});
Loading
Loading