Core error propagation hardening:

Updated kernel-runner delegate deny path to emit on stderr in packages/bare-os-booter/lib/kernel-runner.js.
Added structured errno metadata (err.code) via vfsErr(...) and applied it to key VFS traversal/permission throws in packages/bare-os-booter/lib/vfs.js (ENOENT/EACCES paths).
Critical command exit-code fixes:

packages/bare-os-coreutils/src/ls.js
Tracks access/stat failures and sets nonzero exit when any operand fails.
packages/bare-os-coreutils/src/grep.js
Recursive walker now reports traversal/stat errors back to main flow so fatal status becomes 2.
packages/bare-os-coreutils/src/rm.js
-f only suppresses not-found style errors; permission/deny failures stay nonzero.
packages/bare-os-coreutils/src/chmod.js
Treats falsy/no-op backend chmod result as failure (nonzero).
packages/bare-os-coreutils/src/find.js
Added root-path preflight so missing root now reports explicit error + nonzero.
Minor quirks:

packages/bare-os-coreutils/src/xargs.js
-P0 now maps to bounded parallel cap (env/default cap), not forced serial.
packages/bare-os-coreutils/src/ulimit.js
-f with value now returns explicit unsupported-setter diagnostic + nonzero.
-f alone reports unlimited.
Regression tests:

Added packages/bare-os-coreutils/test/error-propagation.test.mjs covering:
grep missing file => exit 2
rm -f permission error => nonzero
find missing root => nonzero + diagnostic
ulimit -f 1M => explicit unsupported + nonzero
xargs -P0 bounded behavior
This commit is contained in:
Raven Scott
2026-04-27 08:31:31 -04:00
parent 237e0ef63a
commit dd5890b98c
32 changed files with 302 additions and 51 deletions
+5 -1
View File
@@ -153,7 +153,11 @@ async function run(ctx, argv) {
}
perm &= 0o777
}
await ctx.vfs.chmod(f, perm)
const r = await ctx.vfs.chmod(f, perm)
if (r === false || r == null) {
ctx.console.error('chmod: ' + f + ': Operation failed')
ctx.exitCode = 1
}
} catch (e) {
ctx.console.error('chmod: ' + f + ': ' + (e.message || e))
ctx.exitCode = 1
+12
View File
@@ -497,6 +497,18 @@ async function run(ctx, argv) {
if (Number.isFinite(n)) execMax = Math.min(4096, Math.max(1, n))
}
const abs = ctx.vfs.resolveLogical(root)
try {
const stRoot = await ctx.vfs.lstat(abs)
if (!stRoot) {
ctx.console.error('find: ' + root + ': No such file or directory')
ctx.exitCode = 1
return
}
} catch (e) {
ctx.console.error('find: ' + root + ': ' + ((e && e.message) || String(e)))
ctx.exitCode = 1
return
}
const xdevRootDev = xdev ? findRouteDevId(ctx, abs) : null
const o = {
maxDepth,
+10 -3
View File
@@ -436,7 +436,7 @@ async function run(ctx, argv) {
continue
}
if (st.type === 'directory') {
await grepWalkFiles(ctx, p, acc, suppressErrors, {
const walkErr = await grepWalkFiles(ctx, p, acc, suppressErrors, {
includeGlobs,
excludeGlobs,
excludeDirGlobs,
@@ -445,6 +445,7 @@ async function run(ctx, argv) {
10
) || 32
})
if (walkErr) fatal = true
} else {
acc.push(p)
}
@@ -672,6 +673,7 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
const exc = (opts.excludeGlobs || []).slice(0, cap)
const excd = (opts.excludeDirGlobs || []).slice(0, cap)
/** @param {string} d @param {number} depth */
let hadError = false
const walk = async (d, depth) => {
if (depth > GREP_RECURSE_MAX_DEPTH) return
let names = []
@@ -680,6 +682,7 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
} catch (e) {
if (!suppressErrors)
ctx.console.error('grep: ' + d + ': ' + (e.message || e))
hadError = true
return
}
for (const n of names) {
@@ -688,7 +691,10 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
let st
try {
st = await vfs.lstat(sub)
} catch {
} catch (e) {
if (!suppressErrors)
ctx.console.error('grep: ' + sub + ': ' + (e.message || e))
hadError = true
continue
}
if (!st) continue
@@ -708,12 +714,13 @@ async function grepWalkFiles(ctx, dir, acc, suppressErrors, opts = {}) {
acc.push(sub)
}
} catch {
/* skip */
hadError = true
}
}
}
}
await walk(dir, 0)
return hadError
}
/** @param {number[][]} iv */
+10 -1
View File
@@ -600,6 +600,7 @@ async function run(ctx, argv) {
paths.push(a)
}
const targets = paths.length ? paths : ['.']
let hadError = false
const useColor = bareLsUseColor(ctx, colorMode)
const onePerLine = singleColumn || ctx.bareOsStdoutCaptured === true
@@ -619,6 +620,7 @@ async function run(ctx, argv) {
}
} catch (e) {
ctx.console.error('ls: cannot access ' + t + ': ' + (e.message || e))
hadError = true
continue
}
if (!showAll) names = names.filter((n) => !n.startsWith('.'))
@@ -682,7 +684,13 @@ async function run(ctx, argv) {
: t === '.' || t === './'
? n
: t.replace(/\/$/, '') + '/' + n
const st = await vfs.lstat(sub)
let st = null
try {
st = await vfs.lstat(sub)
} catch (e) {
ctx.console.error('ls: cannot access ' + sub + ': ' + (e.message || e))
hadError = true
}
if (!st) {
rows.push({
modeStr: '?---------',
@@ -755,4 +763,5 @@ async function run(ctx, argv) {
}
}
}
if (hadError && (ctx.exitCode == null || ctx.exitCode === 0)) ctx.exitCode = 1
}
+5 -1
View File
@@ -190,6 +190,10 @@ async function run(ctx, argv) {
if (recursive) return rmTreeManual(vfs, p, force)
return vfs.unlink(p)
}
const isNotFoundErr = (e) => {
const msg = String((e && e.message) || e || '')
return /ENOENT|No such file|not found/i.test(msg)
}
for (const f of files) {
try {
if (dirEmptyOnly) {
@@ -215,7 +219,7 @@ async function run(ctx, argv) {
}
await doRm(f)
} catch (e) {
if (force) continue
if (force && isNotFoundErr(e)) continue
ctx.console.error('rm: ' + f + ': ' + (e.message || e))
ctx.exitCode = 1
}
+1 -1
View File
@@ -364,7 +364,7 @@ function bareTelnetParseArgs(argv) {
reconnectDelayMs: 1000,
nawsCols: 80,
nawsRows: 24,
ttype: 'bare-os'
ttype: 'bare-os',
strictClose: true
}
var pos = []
+9
View File
@@ -148,6 +148,15 @@ async function run(ctx, argv) {
ctx.console.log(nofileHard)
return
}
if (args[0] === '-f') {
if (args.length > 1) {
ctx.console.error('ulimit: setting file size limit is not supported in Bare OS')
ctx.exitCode = 1
return
}
ctx.console.log('unlimited')
return
}
ctx.console.error('ulimit: only -a, -n, -Sn, and -Hn are supported in Bare OS')
ctx.exitCode = 1
}
+2 -2
View File
@@ -156,7 +156,7 @@ async function run(ctx, argv) {
return
}
const raw = Number(n)
pCap = Math.min(envPCap, Math.max(1, raw))
pCap = raw === 0 ? envPCap : Math.min(envPCap, Math.max(1, raw))
if (raw > envPCap) {
ctx.console.error(
'xargs: -P ' +
@@ -173,7 +173,7 @@ async function run(ctx, argv) {
}
if (a.startsWith('-P') && a.length > 2 && /^\d+$/.test(a.slice(2))) {
const raw = Number(a.slice(2))
pCap = Math.min(envPCap, Math.max(1, raw))
pCap = raw === 0 ? envPCap : Math.min(envPCap, Math.max(1, raw))
if (raw > envPCap) {
ctx.console.error(
'xargs: -P exceeds cap ' +