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:
+5
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -364,7 +364,7 @@ function bareTelnetParseArgs(argv) {
|
||||
reconnectDelayMs: 1000,
|
||||
nawsCols: 80,
|
||||
nawsRows: 24,
|
||||
ttype: 'bare-os'
|
||||
ttype: 'bare-os',
|
||||
strictClose: true
|
||||
}
|
||||
var pos = []
|
||||
|
||||
@@ -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
@@ -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 ' +
|
||||
|
||||
Reference in New Issue
Block a user