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:
@@ -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 */
|
||||
|
||||
@@ -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`)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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--) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user