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.
217 lines
5.7 KiB
JavaScript
217 lines
5.7 KiB
JavaScript
/**
|
|
* getfacl / setfacl / xattr against an in-memory vfs stub.
|
|
*/
|
|
import { readFile } from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import test from 'brittle'
|
|
import b4a from 'b4a'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
|
|
|
/**
|
|
* @param {string} name
|
|
*/
|
|
async function loadBin(name) {
|
|
const runtime = await readFile(
|
|
path.join(__dirname, '../lib/runtime.js'),
|
|
'utf8'
|
|
)
|
|
const body = await readFile(
|
|
path.join(__dirname, `../src/${name}.js`),
|
|
'utf8'
|
|
)
|
|
return new AsyncFunction(
|
|
'ctx',
|
|
'argv',
|
|
`${runtime}\n${body}\nif (typeof run === 'function') return await run(ctx, argv)\n`
|
|
)
|
|
}
|
|
|
|
function createCtx(store) {
|
|
const logs = []
|
|
const errs = []
|
|
return {
|
|
b4a,
|
|
exitCode: 0,
|
|
shellStdin: '',
|
|
console: {
|
|
log: (m) => logs.push(String(m)),
|
|
error: (m) => errs.push(String(m))
|
|
},
|
|
vfs: {
|
|
async stat(p) {
|
|
if (!Object.prototype.hasOwnProperty.call(store.files, p)) return null
|
|
return store.files[p].stat
|
|
},
|
|
async readFile(p) {
|
|
const ent = store.files[p]
|
|
if (!ent) return null
|
|
return ent.body
|
|
},
|
|
async writeFile(p, body) {
|
|
store.files[p] = {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: Date.now() },
|
|
body: body instanceof Uint8Array ? body : b4a.from(body)
|
|
}
|
|
},
|
|
async unlink(p) {
|
|
delete store.files[p]
|
|
}
|
|
},
|
|
_logs: logs,
|
|
_errs: errs
|
|
}
|
|
}
|
|
|
|
test('getfacl synthesizes mode triples and reads sidecar', async (t) => {
|
|
const store = {
|
|
files: {
|
|
'/a': {
|
|
stat: { mode: 0o755, type: 'file', mtimeMs: 1 },
|
|
body: null
|
|
},
|
|
'/b': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: null
|
|
},
|
|
'/b.bare_acl': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: b4a.from('user::rw-\n')
|
|
}
|
|
}
|
|
}
|
|
const runA = await loadBin('getfacl')
|
|
const ctxA = createCtx(store)
|
|
await runA(ctxA, ['getfacl', '-c', '/a'])
|
|
t.is(ctxA.exitCode, 0)
|
|
t.is(ctxA._logs.join('\n'), 'user::rwx\ngroup::r-x\nother::r-x')
|
|
|
|
const runB = await loadBin('getfacl')
|
|
const ctxB = createCtx(store)
|
|
await runB(ctxB, ['getfacl', '/b'])
|
|
t.is(ctxB.exitCode, 0)
|
|
t.ok(ctxB._logs.join('\n').includes('user::rw-'))
|
|
})
|
|
|
|
test('getfacl documents mask-style sidecars for VFS enforcement', async (t) => {
|
|
const store = {
|
|
files: {
|
|
'/c': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: null
|
|
},
|
|
'/c.bare_acl': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: b4a.from('user::rw-\nother::---\n')
|
|
}
|
|
}
|
|
}
|
|
const run = await loadBin('getfacl')
|
|
const ctx = createCtx(store)
|
|
await run(ctx, ['getfacl', '-c', '/c'])
|
|
t.is(ctx.exitCode, 0)
|
|
t.ok(ctx._logs.join('\n').includes('other::---'))
|
|
})
|
|
|
|
test('setfacl -b, -m and stdin write', async (t) => {
|
|
const store = {
|
|
files: {
|
|
'/x': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: null
|
|
},
|
|
'/x.bare_acl': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: b4a.from('old\n')
|
|
}
|
|
}
|
|
}
|
|
const runClear = await loadBin('setfacl')
|
|
const ctx1 = createCtx(store)
|
|
await runClear(ctx1, ['setfacl', '-b', '/x'])
|
|
t.is(ctx1.exitCode, 0)
|
|
t.absent(store.files['/x.bare_acl'])
|
|
|
|
const runSet = await loadBin('setfacl')
|
|
const ctx2 = createCtx(store)
|
|
ctx2.shellStdin = 'user::r--\n'
|
|
await runSet(ctx2, ['setfacl', '/x'])
|
|
t.is(ctx2.exitCode, 0)
|
|
t.is(b4a.toString(store.files['/x.bare_acl'].body), 'user::r--\n')
|
|
|
|
const runMod = await loadBin('setfacl')
|
|
const ctx3 = createCtx(store)
|
|
await runMod(ctx3, ['setfacl', '-m', 'group::r-x', '/x'])
|
|
t.is(ctx3.exitCode, 0)
|
|
t.ok(b4a.toString(store.files['/x.bare_acl'].body).includes('group::r-x'))
|
|
})
|
|
|
|
test('xattr -w -l -d round trip', async (t) => {
|
|
const store = {
|
|
files: {
|
|
'/f': {
|
|
stat: { mode: 0o644, type: 'file', mtimeMs: 1 },
|
|
body: null
|
|
}
|
|
}
|
|
}
|
|
const runW = await loadBin('xattr')
|
|
const ctxW = createCtx(store)
|
|
await runW(ctxW, ['xattr', '-w', 'user.t', 'hi', '/f'])
|
|
t.is(ctxW.exitCode, 0)
|
|
t.is(ctxW._logs.join('\n'), 'user.t')
|
|
|
|
const runL = await loadBin('xattr')
|
|
const ctxL = createCtx(store)
|
|
await runL(ctxL, ['xattr', '-l', '/f'])
|
|
t.is(ctxL.exitCode, 0)
|
|
t.is(ctxL._logs.join('\n'), 'user.t: hi')
|
|
|
|
const runP = await loadBin('xattr')
|
|
const ctxP = createCtx(store)
|
|
await runP(ctxP, ['xattr', '-p', 'user.t', '/f'])
|
|
t.is(ctxP.exitCode, 0)
|
|
t.is(ctxP._logs.join('\n'), 'hi')
|
|
|
|
const runD = await loadBin('xattr')
|
|
const ctxD = createCtx(store)
|
|
await runD(ctxD, ['xattr', '-d', 'user.t', '/f'])
|
|
t.is(ctxD.exitCode, 0)
|
|
|
|
const runNames = await loadBin('xattr')
|
|
const ctxN = createCtx(store)
|
|
await runNames(ctxN, ['xattr', '/f'])
|
|
t.is(ctxN.exitCode, 0)
|
|
t.is(ctxN._logs.length, 0)
|
|
})
|
|
|
|
test('getfacl and xattr on personal-drive-shaped metadata paths', async (t) => {
|
|
const meta = '/.bare/os/registry.json'
|
|
const store = {
|
|
files: {
|
|
[meta]: {
|
|
stat: { mode: 0o600, type: 'file', mtimeMs: 1 },
|
|
body: b4a.from('{}')
|
|
},
|
|
[`${meta}.bare_acl`]: {
|
|
stat: { mode: 0o600, type: 'file', mtimeMs: 1 },
|
|
body: b4a.from('user::rw-\nother::---\n')
|
|
}
|
|
}
|
|
}
|
|
const runG = await loadBin('getfacl')
|
|
const ctxG = createCtx(store)
|
|
await runG(ctxG, ['getfacl', '-c', meta])
|
|
t.is(ctxG.exitCode, 0)
|
|
t.ok(ctxG._logs.join('\n').includes('other::---'))
|
|
|
|
const runX = await loadBin('xattr')
|
|
const ctxX = createCtx(store)
|
|
await runX(ctxX, ['xattr', '-w', 'user.meta', 'v1', meta])
|
|
t.is(ctxX.exitCode, 0)
|
|
t.ok(store.files[`${meta}.bare_xattr.json`])
|
|
})
|