Skip to content

Commit 575ee5e

Browse files
committed
More work on tasks
Add stub for `get_cwd` Add license header check/write script
1 parent f18379d commit 575ee5e

File tree

12 files changed

+306
-14
lines changed

12 files changed

+306
-14
lines changed

eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export default [
88
'@typescript-eslint/no-unsafe-call': 'off',
99
'@typescript-eslint/no-implied-eval': 'off',
1010
'@typescript-eslint/no-empty-object-type': 'off',
11+
'@typescript-eslint/no-duplicate-enum-values': 'off',
12+
'@typescript-eslint/only-throw-error': 'off',
1113
},
1214
},
1315
];

package-lock.json

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

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"version": "2.0.0-devel-0",
44
"description": "Complete cross-host emulation of Linux in TypeScript",
55
"author": "James Prevett <jp@jamespre.dev> (https://jamespre.dev)",
6+
"bin": {
7+
"devel-check-licenses": "scripts/license.js"
8+
},
69
"repository": {
710
"type": "git",
811
"url": "git+https://github.com/james-pre/kerium.git"

scripts/license.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env node
2+
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
3+
// Copyright (c) 2025 James Prevett
4+
5+
import { readdirSync } from 'node:fs';
6+
import { readFile, writeFile } from 'node:fs/promises';
7+
import { join, relative } from 'node:path';
8+
import { parseArgs, styleText } from 'node:util';
9+
10+
const {
11+
positionals: dirs,
12+
values: { license: expectedLicense, write, verbose, force },
13+
} = parseArgs({
14+
allowPositionals: true,
15+
options: {
16+
license: { type: 'string', short: 'L' },
17+
write: { type: 'boolean', short: 'w', default: false },
18+
verbose: { type: 'boolean', short: 'v', default: false },
19+
force: { type: 'boolean', short: 'f', default: false },
20+
},
21+
});
22+
23+
if (write && !expectedLicense) {
24+
console.error(styleText('red', 'You must specify a license to write with --license/-L'));
25+
process.exit(1);
26+
}
27+
28+
const licenseSpec = /^\s*\/(?:\/|\*) SPDX-License-Identifier: (.+)/;
29+
30+
async function check_file(path, display) {
31+
const content = await readFile(path, 'utf-8');
32+
33+
const match = licenseSpec.exec(content);
34+
35+
if (!match) {
36+
console.error(styleText('red', 'Missing:'), display);
37+
return 'missing';
38+
}
39+
40+
if (!expectedLicense) {
41+
if (verbose) console.log(styleText(['dim'], 'Found: ') + display);
42+
return 'with license';
43+
}
44+
45+
const [, license] = match;
46+
47+
if (license == expectedLicense) {
48+
if (verbose) console.log(styleText(['green', 'dim'], 'Correct: ') + display);
49+
return 'correct';
50+
}
51+
52+
console.warn(styleText('yellow', 'Mismatch:'), display);
53+
return 'mismatched';
54+
}
55+
56+
async function write_file(path, display) {
57+
const content = await readFile(path, 'utf-8');
58+
59+
const match = licenseSpec.exec(content);
60+
61+
if (!match) {
62+
await writeFile(path, `// SPDX-License-Identifier: ${expectedLicense}\n${content}`, 'utf-8');
63+
64+
console.log('Added: ' + display);
65+
return 'added';
66+
}
67+
68+
const [, license] = match;
69+
70+
if (license == expectedLicense) {
71+
if (verbose) console.log(styleText(['green', 'dim'], 'Correct: ') + display);
72+
return 'correct';
73+
}
74+
75+
process.stdout.write(styleText('yellow', 'Mismatch: ') + display);
76+
77+
if (!force) {
78+
console.log(styleText('whiteBright', ' (skipped)'));
79+
return 'skipped';
80+
}
81+
82+
await writeFile(path, content.replace(licenseSpec, `// SPDX-License-Identifier: ${expectedLicense}`), 'utf-8');
83+
console.log(styleText('whiteBright', ' (overwritten)'));
84+
return 'overwritten';
85+
}
86+
87+
function check_dir(dir, display) {
88+
const entries = readdirSync(dir, { withFileTypes: true });
89+
90+
const results = [];
91+
92+
for (const entry of entries) {
93+
if (entry.isDirectory()) {
94+
results.push(...check_dir(join(dir, entry.name), join(display, entry.name)));
95+
continue;
96+
}
97+
98+
if (!entry.isFile()) continue;
99+
100+
const op = write ? write_file : check_file;
101+
results.push(op(join(dir, entry.name), join(display, entry.name)));
102+
}
103+
104+
return results;
105+
}
106+
107+
if (!dirs.length) dirs.push(join(import.meta.dirname, '../src'));
108+
109+
if (verbose) console.log('Checking:', dirs.join(', '));
110+
111+
const promises = [];
112+
113+
for (const dir of dirs) {
114+
promises.push(...check_dir(dir, relative(join(import.meta.dirname, '..'), dir)));
115+
}
116+
117+
const styles = Object.assign(Object.create(null), {
118+
missing: 'red',
119+
'with license': 'white',
120+
correct: 'green',
121+
mismatched: 'yellow',
122+
skipped: 'white',
123+
added: 'cyan',
124+
overwritten: 'magenta',
125+
});
126+
127+
try {
128+
const raw_results = await Promise.all(promises);
129+
const results = Object.create(null);
130+
for (const result of raw_results) {
131+
if (!(result in results)) results[result] = 0;
132+
results[result]++;
133+
}
134+
console.log(
135+
write ? 'Wrote' : 'Checked',
136+
styleText('blueBright', raw_results.length.toString()),
137+
'files:',
138+
Object.entries(results)
139+
.map(([key, value]) => `${key in styles ? styleText(styles[key], value.toString()) : value} ${key}`)
140+
.join(', ')
141+
);
142+
} catch (error) {
143+
console.error(styleText('red', error.toString()));
144+
process.exit(1);
145+
}

src/credentials.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Credentials used for various operations.
66
* Similar to Linux's cred struct.
77
* @see Linux include/linux/cred.h
8+
* @todo capabilities?
89
*/
910
export interface Credentials {
1011
uid: number;
@@ -13,6 +14,8 @@ export interface Credentials {
1314
sgid: number;
1415
euid: number;
1516
egid: number;
17+
fsuid: number;
18+
fsgid: number;
1619
/**
1720
* List of group IDs.
1821
*/
@@ -35,6 +38,8 @@ export function createCredentials(source: CredentialsInit): Credentials {
3538
sgid: source.gid,
3639
euid: source.uid,
3740
egid: source.gid,
41+
fsuid: source.uid,
42+
fsgid: source.gid,
3843
groups: [],
3944
...source,
4045
};

src/fs/dentry.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@
33
import type { Inode } from './inode.js';
44
import type { Superblock } from './super.js';
55

6-
export interface DEntry {
6+
export interface Dentry {
7+
readonly op: DentryOperations;
78
name: string;
89
inode: Inode;
9-
parent: DEntry;
10-
children: DEntry[];
10+
parent: Dentry;
11+
children: Dentry[];
1112
superblock: Superblock;
1213
}
1314

15+
/**
16+
* @todo
17+
*/
1418
export enum DentryFlags {}
19+
20+
/**
21+
* @todo
22+
*/
23+
export interface DentryOperations {
24+
delete?(dir: Readonly<Dentry>): void;
25+
init?(dir: Dentry): void;
26+
}

src/fs/inode.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
22
// Copyright (c) 2025 James Prevett
33
import * as c from './constants.js';
4-
import type { DEntry } from './dentry.js';
4+
import type { Dentry } from './dentry.js';
55
import type { Superblock } from './super.js';
66

77
/**
@@ -84,7 +84,7 @@ export interface Inode {
8484
}
8585

8686
export interface InodeOperations {
87-
lookup(dir: Inode, _dentry: DEntry, flags: number): Promise<DEntry>;
87+
lookup(dir: Inode, _dentry: Dentry, flags: number): Promise<Dentry>;
8888
/**
8989
* @todo finish this
9090
*/

src/fs/mount.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
22
// Copyright (c) 2025 James Prevett
3-
import type { DEntry } from './dentry.js';
3+
import type { Dentry } from './dentry.js';
44
import type { MountIDMap } from './idmapping.js';
55
import type { Superblock } from './super.js';
66

77
/**
88
* @see Linux include/linux/mount.h `vfsmount`
99
*/
10-
export interface Mount {
11-
root: DEntry;
10+
export interface VFSMount {
11+
root: Dentry;
1212
sb: Superblock;
1313
flags: number;
1414
/**
1515
* @todo look into `idmap`s more
1616
*/
1717
idMap: MountIDMap;
1818
}
19+
20+
/**
21+
* @todo
22+
*/
23+
export interface Mountpoint {
24+
dentry: Dentry;
25+
count: number;
26+
}
27+
28+
/**
29+
* @todo
30+
*/
31+
export interface Mount {
32+
parent: Mount;
33+
point: Dentry;
34+
mnt: VFSMount;
35+
mp: Mountpoint;
36+
id: number;
37+
}

src/fs/path.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
22
// Copyright (c) 2025 James Prevett
3-
import type { DEntry } from './dentry.js';
4-
import type { Mount } from './mount.js';
3+
import { Errno as E } from '../error.js';
4+
import { define_syscall } from '../syscalls.js';
5+
import type { Task } from '../task.js';
6+
import type { Dentry } from './dentry.js';
7+
import type { VFSMount } from './mount.js';
58

69
export interface Path {
7-
mnt: Mount;
8-
dentry: DEntry;
10+
mnt: VFSMount;
11+
dentry: Dentry;
912
}
13+
14+
declare module '../syscalls.js' {
15+
interface Syscalls {
16+
get_cwd(): string;
17+
}
18+
}
19+
20+
define_syscall<'get_cwd'>(function get_cwd(this: Task): string {
21+
/*
22+
using _ = lock(this.fs);
23+
24+
if (is_unlinked(this.pwd.dentry)) throw ENOENT;
25+
return join(root, pwd);
26+
*/
27+
return '';
28+
});

src/signal.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later WITH EXCEPTIONS
2+
// Copyright (c) 2025 James Prevett
3+
4+
const _NSIG = 64;
5+
6+
export enum Sig {
7+
HUP = 1,
8+
INT = 2,
9+
QUIT = 3,
10+
ILL = 4,
11+
TRAP = 5,
12+
ABRT = 6,
13+
IOT = 6,
14+
BUS = 7,
15+
FPE = 8,
16+
KILL = 9,
17+
USR1 = 10,
18+
SEGV = 11,
19+
USR2 = 12,
20+
PIPE = 13,
21+
ALRM = 14,
22+
TERM = 15,
23+
STKFLT = 16,
24+
CHLD = 17,
25+
CONT = 18,
26+
STOP = 19,
27+
TSTP = 20,
28+
TTIN = 21,
29+
TTOU = 22,
30+
URG = 23,
31+
XCPU = 24,
32+
XFSZ = 25,
33+
VTALRM = 26,
34+
PROF = 27,
35+
WINCH = 28,
36+
IO = 29,
37+
POLL = IO,
38+
/*
39+
#define SIGLOST 29
40+
*/
41+
PWR = 30,
42+
SYS = 31,
43+
UNUSED = 31,
44+
}
45+
46+
/* These should not be considered constants from userland. */
47+
const SIG_RT_MIN = 32;
48+
const SIG_RT_MAX = _NSIG;

0 commit comments

Comments
 (0)