Fixed dd /dev/zero truncation in packages/bare-os-coreutils/src/dd.js

if=/dev/zero with finite count*bs now produces requested size directly (not limited by VFS 64KiB pseudo-file).
Raw stdout now uses bareOsEmitRaw, improving pipeline correctness.
Fixed pipeline capture path for FIFO-relevant commands

echo and cat now emit via bareOsEmitRaw (packages/bare-os-coreutils/src/echo.js, packages/bare-os-coreutils/src/cat.js).
Shell pipeline now injects bareOsBinWrite into child contexts when output is captured, so raw writes are captured (packages/bare-os-booter/lib/shell.js).
Improved xattr behavior in packages/bare-os-coreutils/src/xattr.js

Added -p/--print NAME.
-w now logs written attribute name for visible success feedback.
Roundtrip behaviors preserved with sidecar metadata format.
Improved ACL UX in:

packages/bare-os-coreutils/src/getfacl.js
Explicit source header for sidecar and mode fallback (non-compact mode).
packages/bare-os-coreutils/src/setfacl.js
Added -m/--modify ENTRY in addition to stdin blob and -b.
Implemented dynamic ulimit output in packages/bare-os-coreutils/src/ulimit.js

Reads /proc/bare_os/rlimits.json when available.
Supports -a, -n, -Sn, -Hn.
Added shuf -e support in packages/bare-os-coreutils/src/shuf.js.

Added split byte-suffix parsing (k/K/m/M/g/G) and attached -bSIZE support in packages/bare-os-coreutils/src/split.js.
This commit is contained in:
Raven Scott
2026-04-27 08:15:50 -04:00
parent b795397261
commit beaff551cf
36 changed files with 773 additions and 167 deletions
+2 -6
View File
@@ -1,10 +1,6 @@
function bareCatOut(ctx, s) {
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, s)
} else {
ctx.console.log(s)
}
if (bareOsEmitRaw(ctx, s)) return
ctx.console.log(s)
}
/** @param {string} line @param {{ showTabs: boolean, showEnds: boolean, showNonprinting: boolean }} o */
+33 -16
View File
@@ -111,32 +111,49 @@ async function run(ctx, argv) {
}
}
let src
if (opt.if) {
src = await ctx.vfs.readFile(opt.if)
if (!src) {
ctx.console.error('dd: ' + opt.if + ': No such file or directory')
const finiteCount = opt.count == null ? null : opt.count
const wantedBytes =
finiteCount == null ? null : Math.max(0, finiteCount * opt.bs)
const skipBytes = Math.max(0, opt.skip * opt.bs)
let chunk
if (opt.if === '/dev/zero' && wantedBytes != null) {
chunk = ctx.b4a.alloc(wantedBytes)
} else if (opt.if === '/dev/urandom' && wantedBytes != null) {
const raw = await ctx.vfs.readFile('/dev/urandom')
if (!raw) {
ctx.console.error('dd: /dev/urandom: unavailable')
ctx.exitCode = 1
return
}
const seed = raw instanceof Uint8Array ? raw : ctx.b4a.from(raw)
chunk = ctx.b4a.alloc(wantedBytes)
for (let i = 0; i < wantedBytes; i++) chunk[i] = seed[i % seed.length]
} else {
src = ctx.b4a.from(bareStdin(ctx), 'utf8')
let src
if (opt.if) {
src = await ctx.vfs.readFile(opt.if)
if (!src) {
ctx.console.error('dd: ' + opt.if + ': No such file or directory')
ctx.exitCode = 1
return
}
} else {
src = ctx.b4a.from(bareStdin(ctx), 'utf8')
}
const input = src instanceof Uint8Array ? src : ctx.b4a.from(src)
const start = Math.min(skipBytes, input.byteLength)
const end =
wantedBytes == null
? input.byteLength
: Math.min(input.byteLength, start + wantedBytes)
chunk = input.subarray(start, end)
}
const input = src instanceof Uint8Array ? src : ctx.b4a.from(src)
const start = Math.min(opt.skip * opt.bs, input.byteLength)
const end =
opt.count == null
? input.byteLength
: Math.min(input.byteLength, start + opt.count * opt.bs)
let chunk = input.subarray(start, end)
if (opt.conv.includes('sync')) {
chunk = padToBlock(chunk, opt.bs, ctx.b4a)
}
if (!opt.of) {
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') w.call(globalThis.process.stdout, chunk)
else ctx.console.log(ctx.b4a.toString(chunk))
if (!bareOsEmitRaw(ctx, chunk)) ctx.console.log(ctx.b4a.toString(chunk))
if (opt.status !== 'none') {
ctx.console.error(`${chunk.byteLength} bytes copied`)
}
+3 -6
View File
@@ -6,10 +6,7 @@ async function run(ctx, argv) {
parts.shift()
}
const s = parts.join(' ')
const w = globalThis.process?.stdout?.write
if (typeof w === 'function') {
w.call(globalThis.process.stdout, s + (n ? '' : '\n'))
} else {
ctx.console.log(n ? s : s)
}
const out = s + (n ? '' : '\n')
if (bareOsEmitRaw(ctx, out)) return
ctx.console.log(s)
}
@@ -54,6 +54,7 @@ async function run(ctx, argv) {
const raw = await ctx.vfs.readFile(side)
if (raw && raw.byteLength) {
const text = ctx.b4a.toString(raw)
if (!compact) ctx.console.log('# source: ' + side)
ctx.console.log(text.replace(/\n$/, ''))
return
}
@@ -67,6 +68,7 @@ async function run(ctx, argv) {
'# file: ' + path,
'# owner: synthetic',
'# group: synthetic',
'# source: mode-fallback',
'user::' + u,
'group::' + g,
'other::' + o
+50 -8
View File
@@ -6,6 +6,8 @@
async function run(ctx, argv) {
let clear = false
/** @type {string[]} */
const modify = []
/** @type {string[]} */
const files = []
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
@@ -13,9 +15,20 @@ async function run(ctx, argv) {
clear = true
continue
}
if (a === '-m' || a === '--modify') {
const spec = argv[i + 1]
if (!spec) {
ctx.console.error('setfacl: -m requires ACL entry')
ctx.exitCode = 1
return
}
modify.push(spec)
i++
continue
}
if (a === '--help' || a === '-h') {
ctx.console.log(
'usage: setfacl [-b] PATH\nWrites ACL text from stdin when not clearing.'
'usage: setfacl [-b] [-m ENTRY] PATH\nWrites ACL text from stdin when not clearing.'
)
return
}
@@ -48,14 +61,43 @@ async function run(ctx, argv) {
}
return
}
const text = bareStdin(ctx)
if (!text || !String(text).trim()) {
ctx.console.error('setfacl: ACL text required on stdin')
ctx.exitCode = 1
return
let body = ''
if (modify.length) {
const prev = await ctx.vfs.readFile(side)
const lines = prev
? ctx.b4a
.toString(prev)
.split(/\r?\n/)
.map((x) => x.trim())
.filter(Boolean)
: []
const byKey = new Map()
for (const ln of lines) {
const k = ln.split(':', 2).join(':')
byKey.set(k, ln)
}
for (const m of modify) {
const spec = String(m).trim()
if (!spec.includes(':')) {
ctx.console.error('setfacl: invalid ACL entry ' + spec)
ctx.exitCode = 1
return
}
const key = spec.split(':', 2).join(':')
byKey.set(key, spec)
}
body = [...byKey.values()].join('\n')
} else {
const text = bareStdin(ctx)
if (!text || !String(text).trim()) {
ctx.console.error('setfacl: ACL text required on stdin')
ctx.exitCode = 1
return
}
body = String(text)
}
const body = String(text).endsWith('\n') ? String(text) : String(text) + '\n'
await ctx.vfs.writeFile(side, ctx.b4a.from(body))
const finalBody = body.endsWith('\n') ? body : body + '\n'
await ctx.vfs.writeFile(side, ctx.b4a.from(finalBody))
} catch (e) {
ctx.console.error('setfacl: ' + (e && e.message ? e.message : String(e)))
ctx.exitCode = 1
+26 -10
View File
@@ -28,6 +28,7 @@ function seededRandom(seed) {
async function run(ctx, argv) {
const paths = []
let echoMode = false
for (let i = 1; i < argv.length; i++) {
const a = argv[i]
if (a === '-h' || a === '--help') {
@@ -36,6 +37,10 @@ async function run(ctx, argv) {
)
return
}
if (a === '-e') {
echoMode = true
continue
}
if (a.startsWith('-')) {
ctx.console.error('shuf: unsupported option ' + a)
ctx.exitCode = 1
@@ -50,21 +55,32 @@ async function run(ctx, argv) {
const seed = String(ctx.vfs?.env?.BARE_OS_SHUF_SEED || '').trim()
const rand = seed ? seededRandom(seed) : Math.random
const lines = []
for (const p of paths) {
const L = await readLines(ctx, p)
if (L == null) {
ctx.console.error('shuf: ' + p + ': No such file')
ctx.exitCode = 1
return
}
const trim = L.length && L[L.length - 1] === '' ? L.slice(0, -1) : L
for (const ln of trim) {
if (echoMode) {
for (const p of paths) {
if (lines.length >= cap) {
ctx.console.error('shuf: input exceeds line cap')
ctx.exitCode = 1
return
}
lines.push(ln)
lines.push(p)
}
} else {
for (const p of paths) {
const L = await readLines(ctx, p)
if (L == null) {
ctx.console.error('shuf: ' + p + ': No such file')
ctx.exitCode = 1
return
}
const trim = L.length && L[L.length - 1] === '' ? L.slice(0, -1) : L
for (const ln of trim) {
if (lines.length >= cap) {
ctx.console.error('shuf: input exceeds line cap')
ctx.exitCode = 1
return
}
lines.push(ln)
}
}
}
for (let i = lines.length - 1; i > 0; i--) {
+24 -1
View File
@@ -22,6 +22,23 @@ function splitSuffix(prefix, i) {
return prefix + s
}
function parseByteSize(v) {
const s = String(v || '').trim()
const m = /^(\d+)([kKmMgG]?)$/.exec(s)
if (!m) return null
const n = Number.parseInt(m[1], 10)
if (!Number.isFinite(n) || n < 1) return null
const mul =
m[2] === 'k' || m[2] === 'K'
? 1024
: m[2] === 'm' || m[2] === 'M'
? 1024 * 1024
: m[2] === 'g' || m[2] === 'G'
? 1024 * 1024 * 1024
: 1
return n * mul
}
async function run(ctx, argv) {
let lineCount = 1000
let byteCount = null
@@ -47,7 +64,13 @@ async function run(ctx, argv) {
continue
}
if (a === '-b' && argv[i + 1]) {
byteCount = parseInt(argv[++i], 10)
byteCount = parseByteSize(argv[++i])
lineCount = null
if (!Number.isFinite(byteCount) || byteCount < 1) byteCount = 512
continue
}
if (a.startsWith('-b') && a.length > 2) {
byteCount = parseByteSize(a.slice(2))
lineCount = null
if (!Number.isFinite(byteCount) || byteCount < 1) byteCount = 512
continue
+40 -4
View File
@@ -11,18 +11,54 @@ const LIMITS = [
['virtual memory', '-v', 'unlimited']
]
/**
* @param {Record<string, unknown>} ctx
*/
async function readRuntimeLimits(ctx) {
try {
const b = await ctx.vfs.readFile('/proc/bare_os/rlimits.json')
if (!b) return null
const j = JSON.parse(ctx.b4a.toString(b))
return j && typeof j === 'object' ? j : null
} catch {
return null
}
}
async function run(ctx, argv) {
const args = argv.slice(1).filter((a) => a !== '--')
const rt = await readRuntimeLimits(ctx)
const nofileSoft =
String(
rt?.softNoFile ??
rt?.soft_nofile ??
rt?.nofileSoft ??
rt?.nofile_soft ??
256
) || '256'
const nofileHard =
String(
rt?.hardNoFile ??
rt?.hard_nofile ??
rt?.nofileHard ??
rt?.nofile_hard ??
nofileSoft
) || nofileSoft
if (args.length === 0 || (args.length === 1 && args[0] === '-a')) {
for (const [label, flag, val] of LIMITS) {
ctx.console.log(`${label} (${flag}) ${val}`)
const outVal = flag === '-n' ? nofileSoft : val
ctx.console.log(`${label} (${flag}) ${outVal}`)
}
return
}
if (args.length === 1 && args[0] === '-n') {
ctx.console.log('256')
if (args.length === 1 && (args[0] === '-n' || args[0] === '-Sn')) {
ctx.console.log(nofileSoft)
return
}
ctx.console.error('ulimit: only -a and -n are supported in Bare OS')
if (args.length === 1 && args[0] === '-Hn') {
ctx.console.log(nofileHard)
return
}
ctx.console.error('ulimit: only -a, -n, -Sn, and -Hn are supported in Bare OS')
ctx.exitCode = 1
}
+25 -2
View File
@@ -68,6 +68,8 @@ async function run(ctx, argv) {
let delName = null
/** @type {{ name: string, val: string } | null} */
let writePair = null
/** @type {string | null} */
let printName = null
/** @type {string[]} */
const rest = []
for (let i = 1; i < argv.length; i++) {
@@ -88,6 +90,17 @@ async function run(ctx, argv) {
i += 2
continue
}
if (a === '-p' || a === '--print') {
const name = argv[i + 1]
if (!name) {
ctx.console.error('xattr: -p requires NAME')
ctx.exitCode = 1
return
}
printName = name
i++
continue
}
if (a === '-d' || a === '--delete') {
const name = argv[i + 1]
if (!name) {
@@ -100,7 +113,7 @@ async function run(ctx, argv) {
continue
}
if (a === '--help' || a === '-h') {
ctx.console.log('usage: xattr [-l] [-w NAME VALUE] [-d NAME] PATH')
ctx.console.log('usage: xattr [-l] [-p NAME] [-w NAME VALUE] [-d NAME] PATH')
return
}
if (a.startsWith('-')) {
@@ -111,7 +124,7 @@ async function run(ctx, argv) {
rest.push(a)
}
if (rest.length !== 1) {
ctx.console.error('usage: xattr [-l] [-w NAME VALUE] [-d NAME] PATH')
ctx.console.error('usage: xattr [-l] [-p NAME] [-w NAME VALUE] [-d NAME] PATH')
ctx.exitCode = 1
return
}
@@ -132,6 +145,16 @@ async function run(ctx, argv) {
if (writePair) {
obj[writePair.name] = xattrB64Encode(writePair.val)
await writeXattrMap(ctx, path, obj)
ctx.console.log(writePair.name)
return
}
if (printName) {
if (!Object.prototype.hasOwnProperty.call(obj, printName)) {
ctx.console.error('xattr: ' + printName + ': No such xattr')
ctx.exitCode = 1
return
}
ctx.console.log(xattrB64Decode(obj[printName]))
return
}
const keys = Object.keys(obj).sort()