Guest isolation

This commit is contained in:
Raven Scott
2026-04-03 18:31:22 -04:00
parent b727177842
commit c02ffc6021
9 changed files with 232 additions and 34 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ The following are set on `ctx` before the kernel starts (unless noted as overwri
| **`bareOsCtxApiVersion`** | String semver for the documented **`ctx`** contract (e.g. **`1.0.0`**). Bump in [`bare-os-ctx-api.js`](../packages/bare-os-booter/lib/bare-os-ctx-api.js) when you make breaking changes to stable fields. | | **`bareOsCtxApiVersion`** | String semver for the documented **`ctx`** contract (e.g. **`1.0.0`**). Bump in [`bare-os-ctx-api.js`](../packages/bare-os-booter/lib/bare-os-ctx-api.js) when you make breaking changes to stable fields. |
| **`disk`** | Disk bundle used during boot (includes drives and helpers); advanced use | | **`disk`** | Disk bundle used during boot (includes drives and helpers); advanced use |
| **`drive`** | **System** Hyperdrive (`ctx.drive` is the OS image: `/bin`, `/boot`, …) | | **`drive`** | **System** Hyperdrive (`ctx.drive` is the OS image: `/bin`, `/boot`, …) |
| **`personalDrive`** | **Personal** Hyperdrive (mutable per-user state; also reachable via VFS under `$HOME`) | | **`personalDrive`** | **Personal** Hyperdrive (mutable per-user state; VFS maps **`$HOME`** to **`/.bare-os/home/<HOME-basename>/…`** and session **`/var/log`** to **`/.bare-os/var/log/<basename>/…`** so guest vs unlocked trees do not share the same keys) |
| **`vfs`** | Path layer: resolves logical paths, routes to system vs personal drive, implements `mkdir`, `readFile`, etc. See [`vfs.js`](../packages/bare-os-booter/lib/vfs.js) | | **`vfs`** | Path layer: resolves logical paths, routes to system vs personal drive, implements `mkdir`, `readFile`, etc. See [`vfs.js`](../packages/bare-os-booter/lib/vfs.js) |
| **`env`** | Shell environment object (`HOME`, `PATH`, `USER`, …), same object as **`vfs.env`**. Mutated by builtins (`export`, `cd` updates `PWD`, identity unlock updates user fields). After each **`execLine`**, **`BARE_OS_EXIT_STATUS`** holds the last commands exit code as a decimal string (POSIX **`$?`** parity); use **`$?`** or **`${?}`** in shell words for expansion. | | **`env`** | Shell environment object (`HOME`, `PATH`, `USER`, …), same object as **`vfs.env`**. Mutated by builtins (`export`, `cd` updates `PWD`, identity unlock updates user fields). After each **`execLine`**, **`BARE_OS_EXIT_STATUS`** holds the last commands exit code as a decimal string (POSIX **`$?`** parity); use **`$?`** or **`${?}`** in shell words for expansion. |
| **`b4a`** | **`b4a`** module (byte helpers); used to convert Hyperdrive buffers to strings | | **`b4a`** | **`b4a`** module (byte helpers); used to convert Hyperdrive buffers to strings |
+1 -1
View File
@@ -33,7 +33,7 @@ Cleanup path closes swarm/drives and calls **`session.cleanup()`**, which runs *
`lib/vfs.js` implements **`resolveLogical`** with **`unix-path-resolve(cwd, userPath)`** (two arguments only—important when reading the code). `lib/vfs.js` implements **`resolveLogical`** with **`unix-path-resolve(cwd, userPath)`** (two arguments only—important when reading the code).
- Paths under **`$HOME`** resolve to the **personal** Hyperdrive (mutable `writeFile` / `unlink` where policy allows). - Paths under **`$HOME`** resolve to the **personal** Hyperdrive under **`/.bare-os/home/<HOME-basename>/…`** (mutable `writeFile` / `unlink` where policy allows); writable **`/var/log`** uses **`/.bare-os/var/log/<same-basename>/…`** on that drive.
- Other absolute paths hit the **system** drive (OS image). - Other absolute paths hit the **system** drive (OS image).
- Hyperdrive rejects **`/`** as a filename; the VFS special-cases **logical root** for `stat`, `chdir`, `exists`. - Hyperdrive rejects **`/`** as a filename; the VFS special-cases **logical root** for `stat`, `chdir`, `exists`.
+2
View File
@@ -15,6 +15,8 @@ On boot, **`applyGuestEnv`** sets:
The personal drive still persists: guest data is **not** anonymous to the drive—it is simply the **unauthenticated** profile. The personal drive still persists: guest data is **not** anonymous to the drive—it is simply the **unauthenticated** profile.
**Home and session logs on disk:** logical **`$HOME`** and **`/var/log`** map to the personal Hyperdrive under **`/.bare-os/home/<basename>`** and **`/.bare-os/var/log/<basename>`**, where **`<basename>`** is the first segment of **`HOME`** (e.g. `guest` or the 12-hex display name). That keeps guest and unlocked trees separate on the same drive. Shared machine metadata (**`/.bare/account`**, **`/.bare/hdms/`**, vault blobs, etc.) stays outside those prefixes. On first boot after an upgrade from older booters, a best-effort migration may move non-reserved files from the personal drive root into the current sessions home prefix when that prefix is still empty.
--- ---
## Account blob: `/.bare/account` ## Account blob: `/.bare/account`
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@ export const INITD_LOG = `${BARE_OS_VAR_LOG_DIR}/initd.log`
const README_REL = `${BARE_OS_VAR_LOG_DIR}/README` const README_REL = `${BARE_OS_VAR_LOG_DIR}/README`
const README_TEXT = `Bare OS session logs (mirrored on your personal drive under /.bare-os/var/log). const README_TEXT = `Bare OS session logs (mirrored on your personal drive under /.bare-os/var/log/<HOME-basename>/).
kernel-console.log — console.log / console.error from the kernel session kernel-console.log — console.log / console.error from the kernel session
cron.log — bare-cron job errors cron.log — bare-cron job errors
initd.log — bare-initd service start failures initd.log — bare-initd service start failures
+105 -1
View File
@@ -80,6 +80,7 @@ export async function applyUnlockedEnv(ctx, publicKey, secretKey) {
env.LOGNAME = name env.LOGNAME = name
env.HOME = home env.HOME = home
env.PWD = home env.PWD = home
await migrateLegacyPersonalHomeIfNeeded(ctx)
const uid = uidFromPublicKey(publicKey) const uid = uidFromPublicKey(publicKey)
env.UID = uid env.UID = uid
env.GID = uid env.GID = uid
@@ -145,9 +146,10 @@ export async function ensureBareDir(ctx) {
*/ */
export async function ensureGuestHome(ctx) { export async function ensureGuestHome(ctx) {
await ensureBareDir(ctx) await ensureBareDir(ctx)
await migrateLegacyPersonalHomeIfNeeded(ctx)
const drive = ctx.personalDrive const drive = ctx.personalDrive
if (!drive || typeof drive.put !== 'function') return if (!drive || typeof drive.put !== 'function') return
const keep = '/.keep_guest' const keep = '/.bare-os/home/guest/.keep_guest'
try { try {
const existing = await drive.get(keep) const existing = await drive.get(keep)
if (existing) return if (existing) return
@@ -217,6 +219,8 @@ const VAULT_EXCLUDE_PREFIXES = [
'bare', 'bare',
'.bare/', '.bare/',
'.bare', '.bare',
'.bare-os/var/',
'.bare-os/var',
'.vault/', '.vault/',
'.vault', '.vault',
'bin/', 'bin/',
@@ -238,6 +242,106 @@ function shouldVaultSkip(short) {
return false return false
} }
/**
* Top-level personal-drive names we do not lift from `/` into `/.bare-os/home/<seg>/`
* during legacy migration (machine metadata, new layout root, old guest marker).
* @param {string} name single path segment (no slashes)
*/
function legacyRootMigrateSkip(name) {
if (shouldVaultSkip(name)) return true
if (name === '.bare-os' || name === '.keep_guest') return true
return false
}
/**
* @param {import('hyperdrive').default} drive
* @param {string} prefix absolute e.g. /.bare-os/home/guest
*/
async function personalHomePrefixIsEmpty(drive, prefix) {
try {
const names = await driveReaddirNames(drive, prefix)
return names.length === 0
} catch {
return true
}
}
/**
* @param {import('hyperdrive').default} drive
* @param {string} fromAbs
* @param {string} toAbs
*/
async function moveDriveSubtree(drive, fromAbs, toAbs) {
const ent = await drive.entry(fromAbs, { follow: false })
if (!ent?.value) return
const v = ent.value
if (v.linkname != null) {
await drive.putEntry(toAbs, {
linkname: v.linkname,
executable: !!v.executable,
metadata: v.metadata ?? null
})
await drive.del(fromAbs)
return
}
if (v.blob) {
const data = await drive.get(fromAbs, { follow: true })
if (data) {
await drive.put(toAbs, data, {
executable: !!v.executable,
metadata: v.metadata ?? null
})
}
await drive.del(fromAbs)
return
}
const names = await driveReaddirNames(drive, fromAbs)
for (const n of names) {
const f = fromAbs === '/' ? `/${n}` : `${fromAbs}/${n}`
const t = toAbs === '/' ? `/${n}` : `${toAbs}/${n}`
await moveDriveSubtree(drive, f, t)
}
try {
await drive.del(fromAbs)
} catch {
/* directory may have no tombstone */
}
}
/**
* One-time best-effort: flat personal-root files from older booters → `/.bare-os/home/<seg>/`.
* @param {Record<string, unknown>} ctx
*/
export async function migrateLegacyPersonalHomeIfNeeded(ctx) {
const drive = ctx.personalDrive
const vfs = ctx.vfs
if (!drive || typeof drive.put !== 'function' || !vfs?.env) return
const home = vfs.env.HOME
if (typeof home !== 'string' || !home.startsWith('/home/')) return
const seg = home.slice('/home/'.length).split('/')[0]
if (!seg) return
const prefix = `/.bare-os/home/${seg}`
if (!(await personalHomePrefixIsEmpty(drive, prefix))) return
const rootNames = await driveReaddirNames(drive, '/')
for (const name of rootNames) {
if (legacyRootMigrateSkip(name)) continue
const from = `/${name}`
const to = `${prefix}/${name}`
try {
await moveDriveSubtree(drive, from, to)
} catch {
/* best-effort */
}
}
try {
const oldGuest = await drive.get('/.keep_guest')
if (oldGuest) await drive.del('/.keep_guest')
} catch {
/* ignore */
}
}
/** /**
* @param {import('hyperdrive').default} drive * @param {import('hyperdrive').default} drive
* @param {string} dir * @param {string} dir
+34 -14
View File
@@ -18,13 +18,10 @@ import {
/** Empty-directory marker (must match {@link ./git-fs-adapter.js}). */ /** Empty-directory marker (must match {@link ./git-fs-adapter.js}). */
const DIR_MARKER = '.bareos_empty' const DIR_MARKER = '.bareos_empty'
/** Personal-drive backing for logical `/var/log/…` (system drive is read-only). */
const VAR_LOG_STORAGE = '/.bare-os/var/log'
/** /**
* Unified path view: system Hyperdrive for OS paths, personal Hyperdrive under $HOME, * Unified path view: system Hyperdrive for OS paths, personal Hyperdrive under $HOME,
* optional HDMS mounts under /mnt/<label>/…, virtual /var with writable /var/log/… * optional HDMS mounts under /mnt/<label>/…, virtual /var with writable /var/log/…
* on the personal drive at /.bare-os/var/log/ * on the personal drive under /.bare-os/var/log/<home-seg>/… (session-isolated).
* @param {import('hyperdrive').default} systemDrive * @param {import('hyperdrive').default} systemDrive
* @param {import('hyperdrive').default} personalDrive * @param {import('hyperdrive').default} personalDrive
* @param {Record<string, string>} env * @param {Record<string, string>} env
@@ -47,6 +44,18 @@ export function createVfs(systemDrive, personalDrive, env, mntRef = null) {
return seg || null return seg || null
} }
/** Session home tree on the personal drive (isolates guest vs unlocked users). */
function personalHomeStorageRoot() {
const seg = activeHomeBasename()
return seg ? `/.bare-os/home/${seg}` : '/.bare-os/home/_nosession'
}
/** Personal-drive backing for logical `/var/log/…` (per session segment). */
function varLogStorageRoot() {
const seg = activeHomeBasename()
return seg ? `/.bare-os/var/log/${seg}` : '/.bare-os/var/log/_nosession'
}
/** /**
* `cd ~`, `touch ~/x`, etc. must map to $HOME — otherwise `unix-path-resolve(cwd, '~')` * `cd ~`, `touch ~/x`, etc. must map to $HOME — otherwise `unix-path-resolve(cwd, '~')`
* becomes `/~` on the system drive (read-only). * becomes `/~` on the system drive (read-only).
@@ -107,12 +116,13 @@ export function createVfs(systemDrive, personalDrive, env, mntRef = null) {
if (varNorm === '/var') { if (varNorm === '/var') {
return { virtualVarRoot: true } return { virtualVarRoot: true }
} }
const varLogRoot = varLogStorageRoot()
if (varNorm === '/var/log' || absPath === '/var/log/') { if (varNorm === '/var/log' || absPath === '/var/log/') {
return { drive: personalDrive, path: VAR_LOG_STORAGE } return { drive: personalDrive, path: varLogRoot }
} }
if (absPath.startsWith('/var/log/')) { if (absPath.startsWith('/var/log/')) {
const rel = absPath.slice('/var/log/'.length).replace(/^\/+/, '') const rel = absPath.slice('/var/log/'.length).replace(/^\/+/, '')
const p = rel ? unixPathResolve(VAR_LOG_STORAGE, rel) : VAR_LOG_STORAGE const p = rel ? unixPathResolve(varLogRoot, rel) : varLogRoot
return { drive: personalDrive, path: p } return { drive: personalDrive, path: p }
} }
if (absPath.startsWith('/var/')) { if (absPath.startsWith('/var/')) {
@@ -121,6 +131,7 @@ export function createVfs(systemDrive, personalDrive, env, mntRef = null) {
const h = normalizeHome() const h = normalizeHome()
const activeSeg = activeHomeBasename() const activeSeg = activeHomeBasename()
const homeRoot = personalHomeStorageRoot()
if (activeSeg && absPath === '/home') { if (activeSeg && absPath === '/home') {
return { virtualHomeDir: true } return { virtualHomeDir: true }
@@ -132,19 +143,22 @@ export function createVfs(systemDrive, personalDrive, env, mntRef = null) {
const seg = slash === -1 ? after : after.slice(0, slash) const seg = slash === -1 ? after : after.slice(0, slash)
const rest = slash === -1 ? '' : after.slice(slash + 1) const rest = slash === -1 ? '' : after.slice(slash + 1)
if (seg === activeSeg) { if (seg === activeSeg) {
const sub = rest ? '/' + rest.replace(/^\//, '') : '/' if (!rest) {
const p = unixPathResolve('/', sub) return { drive: personalDrive, path: homeRoot }
}
const rel = rest.replace(/^\/+/, '')
const p = unixPathResolve(homeRoot, rel)
return { drive: personalDrive, path: p } return { drive: personalDrive, path: p }
} }
return { drive: systemDrive, path: absPath } return { drive: systemDrive, path: absPath }
} }
if (absPath === h || absPath.startsWith(h + '/')) { if (absPath === h || absPath.startsWith(h + '/')) {
const sub = if (absPath === h) {
absPath === h return { drive: personalDrive, path: homeRoot }
? '/' }
: '/' + absPath.slice(h.length + 1).replace(/^\//, '') const rel = absPath.slice(h.length + 1).replace(/^\/+/, '')
const p = unixPathResolve('/', sub) const p = unixPathResolve(homeRoot, rel)
return { drive: personalDrive, path: p } return { drive: personalDrive, path: p }
} }
return { drive: systemDrive, path: absPath } return { drive: systemDrive, path: absPath }
@@ -277,10 +291,16 @@ export function createVfs(systemDrive, personalDrive, env, mntRef = null) {
if ( if (
normLog === '/var/log' && normLog === '/var/log' &&
r.drive === personalDrive && r.drive === personalDrive &&
p === VAR_LOG_STORAGE p === varLogStorageRoot()
) { ) {
return { ...synthesizeStat(abs, true, env, 'directory'), path: abs } return { ...synthesizeStat(abs, true, env, 'directory'), path: abs }
} }
const h = normalizeHome()
const homeNorm = h.replace(/\/+$/, '') || h
const absNorm = abs.replace(/\/+$/, '') || abs
if (activeSeg && absNorm === homeNorm) {
return { ...synthesizeStat(absNorm, true, env, 'directory'), path: abs }
}
return null return null
} }
+86 -14
View File
@@ -92,6 +92,13 @@ function testCtx(drive, personal, env) {
} }
} }
/** Personal-drive path backing logical `/home/<seg>/…` (matches `vfs.js`). */
function personalHomeBacking(homePath, rel = '') {
const seg = homePath.replace(/^\/home\//, '').split('/')[0] || 'user'
const r = String(rel).replace(/^\//, '')
return r ? `/.bare-os/home/${seg}/${r}` : `/.bare-os/home/${seg}`
}
test('runKernelFromSource invokes start(ctx)', async (t) => { test('runKernelFromSource invokes start(ctx)', async (t) => {
const calls = [] const calls = []
const source = ` const source = `
@@ -135,7 +142,7 @@ test('runBinCommand runs bare hello.js from cwd on personal drive', async (t) =>
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await personal.put( await personal.put(
'/hello.js', personalHomeBacking('/home/user', 'hello.js'),
b4a.from(` b4a.from(`
async function run(ctx, argv) { async function run(ctx, argv) {
ctx.out.push(argv.join(' ')) ctx.out.push(argv.join(' '))
@@ -159,7 +166,7 @@ test('runBinCommand strips shebang from user script', async (t) => {
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await personal.put( await personal.put(
'/x.js', personalHomeBacking('/home/user', 'x.js'),
b4a.from(`#!/usr/bin/env bare b4a.from(`#!/usr/bin/env bare
async function run(ctx) { ctx.out.push('ok') } async function run(ctx) { ctx.out.push('ok') }
`) `)
@@ -180,7 +187,10 @@ test('runBinCommand user script error is caught and logged', async (t) => {
const personal = new Hyperdrive(store.namespace('ptj')) const personal = new Hyperdrive(store.namespace('ptj'))
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await personal.put('/bad.js', b4a.from(`async function run() { test() }`)) await personal.put(
personalHomeBacking('/home/user', 'bad.js'),
b4a.from(`async function run() { test() }`)
)
const errs = [] const errs = []
const ctx = testCtx(drive, personal) const ctx = testCtx(drive, personal)
ctx.console = { ctx.console = {
@@ -206,8 +216,8 @@ test('ls hides dotfiles unless -a', async (t) => {
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await drive.put('/bin/ls', b4a.from(lsSrc)) await drive.put('/bin/ls', b4a.from(lsSrc))
await personal.put('/shown.txt', b4a.from('')) await personal.put(personalHomeBacking('/home/user', 'shown.txt'), b4a.from(''))
await personal.put('/.hidden', b4a.from('')) await personal.put(personalHomeBacking('/home/user', '.hidden'), b4a.from(''))
const lines = [] const lines = []
const ctx = testCtx(drive, personal) const ctx = testCtx(drive, personal)
ctx.console = { ctx.console = {
@@ -346,7 +356,7 @@ test('vfs routes HOME to personal drive', async (t) => {
const env = { HOME: '/home/user', PWD: '/home/user', PATH: '/bin' } const env = { HOME: '/home/user', PWD: '/home/user', PATH: '/bin' }
const vfs = createVfs(sys, personal, env) const vfs = createVfs(sys, personal, env)
await vfs.writeFile('f.txt', b4a.from('hi')) await vfs.writeFile('f.txt', b4a.from('hi'))
const buf = await personal.get('/f.txt') const buf = await personal.get(personalHomeBacking('/home/user', 'f.txt'))
t.ok(buf) t.ok(buf)
t.is(b4a.toString(buf), 'hi') t.is(b4a.toString(buf), 'hi')
const sysTry = await sys.get('/home/user/f.txt') const sysTry = await sys.get('/home/user/f.txt')
@@ -387,7 +397,7 @@ test('vfs expands ~ and ~/ to $HOME (not read-only /~)', async (t) => {
await vfs.chdir('~') await vfs.chdir('~')
t.is(vfs.getcwd(), '/home/zuser') t.is(vfs.getcwd(), '/home/zuser')
await vfs.writeFile('~/tilde.txt', b4a.from('ok')) await vfs.writeFile('~/tilde.txt', b4a.from('ok'))
const buf = await personal.get('/tilde.txt') const buf = await personal.get(personalHomeBacking('/home/zuser', 'tilde.txt'))
t.ok(buf) t.ok(buf)
t.is(b4a.toString(buf), 'ok') t.is(b4a.toString(buf), 'ok')
await store.close() await store.close()
@@ -416,12 +426,66 @@ test('vfs lists virtual /home at root; /home shows only active session dir', asy
t.alike(await vfsU.readdir('/home'), ['eeb18de988e9']) t.alike(await vfsU.readdir('/home'), ['eeb18de988e9'])
t.is(await vfsU.stat('/home/guest'), null) t.is(await vfsU.stat('/home/guest'), null)
await vfsU.writeFile('/home/eeb18de988e9/x', b4a.from('1')) await vfsU.writeFile('/home/eeb18de988e9/x', b4a.from('1'))
const buf = await personal.get('/x') const buf = await personal.get(
personalHomeBacking('/home/eeb18de988e9', 'x')
)
t.ok(buf) t.ok(buf)
await store.close() await store.close()
rmSync(dir, { recursive: true, force: true }) rmSync(dir, { recursive: true, force: true })
}) })
test('vfs isolates home and /var/log per HOME basename on personal drive', async (t) => {
const dir = testCorestoreDir('vfsisol')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pvis'))
await sys.ready()
await personal.ready()
const vfsGuest = createVfs(sys, personal, {
HOME: '/home/guest',
PWD: '/home/guest',
PATH: '/bin'
})
await vfsGuest.writeFile('shared-name.txt', b4a.from('guest-data'))
await vfsGuest.writeFile('/var/log/bare-os/g.log', b4a.from('glog'))
const vfsUser = createVfs(sys, personal, {
HOME: '/home/alice12hex',
PWD: '/home/alice12hex',
PATH: '/bin'
})
t.is(await vfsUser.readFile('/home/alice12hex/shared-name.txt'), null)
await vfsUser.writeFile('shared-name.txt', b4a.from('user-data'))
t.is(
b4a.toString(
await personal.get(personalHomeBacking('/home/guest', 'shared-name.txt'))
),
'guest-data'
)
t.is(
b4a.toString(
await personal.get(
personalHomeBacking('/home/alice12hex', 'shared-name.txt')
)
),
'user-data'
)
t.is(
b4a.toString(
await personal.get('/.bare-os/var/log/guest/bare-os/g.log')
),
'glog'
)
await vfsUser.writeFile('/var/log/bare-os/u.log', b4a.from('ulog'))
t.is(
b4a.toString(
await personal.get('/.bare-os/var/log/alice12hex/bare-os/u.log')
),
'ulog'
)
await store.close()
rmSync(dir, { recursive: true, force: true })
})
test('vfs /mnt lists HDMS mounts and allows writable put', async (t) => { test('vfs /mnt lists HDMS mounts and allows writable put', async (t) => {
const dir = testCorestoreDir('vfsmnt') const dir = testCorestoreDir('vfsmnt')
const store = new Corestore(dir) const store = new Corestore(dir)
@@ -464,10 +528,12 @@ test('vfs /var in root readdir; /var/log empty lstat; writes map to personal', a
t.is(stLog.type, 'directory') t.is(stLog.type, 'directory')
await vfs.mkdir('/var/log/bare-os', { recursive: true }) await vfs.mkdir('/var/log/bare-os', { recursive: true })
await vfs.writeFile('/var/log/bare-os/test-vfs.log', b4a.from('hello')) await vfs.writeFile('/var/log/bare-os/test-vfs.log', b4a.from('hello'))
const buf = await personal.get('/.bare-os/var/log/bare-os/test-vfs.log') const buf = await personal.get(
'/.bare-os/var/log/u/bare-os/test-vfs.log'
)
t.ok(buf) t.ok(buf)
t.is(b4a.toString(buf), 'hello') t.is(b4a.toString(buf), 'hello')
const sysTry = await sys.get('/.bare-os/var/log/bare-os/test-vfs.log') const sysTry = await sys.get('/.bare-os/var/log/u/bare-os/test-vfs.log')
t.is(sysTry, null) t.is(sysTry, null)
await store.close() await store.close()
rmSync(dir, { recursive: true, force: true }) rmSync(dir, { recursive: true, force: true })
@@ -577,7 +643,7 @@ test('loadBarerc applies export and alias from personal ~/.barerc', async (t) =>
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await personal.put( await personal.put(
'/.barerc', personalHomeBacking('/home/user', '.barerc'),
b4a.from('export MYRC=1\nalias dog=echo woof\n') b4a.from('export MYRC=1\nalias dog=echo woof\n')
) )
const ctx = testCtx(drive, personal) const ctx = testCtx(drive, personal)
@@ -1418,8 +1484,14 @@ test('tier-1 rm -rf removes directory tree on personal drive', async (t) => {
const ctx = testCtx(drive, personal) const ctx = testCtx(drive, personal)
ctx.exitCode = 0 ctx.exitCode = 0
ctx.console = { log() {}, error() {} } ctx.console = { log() {}, error() {} }
await personal.put('/nest/leaf/x.txt', b4a.from('x')) await personal.put(
await personal.put('/nest/other/y.txt', b4a.from('y')) personalHomeBacking('/home/user', 'nest/leaf/x.txt'),
b4a.from('x')
)
await personal.put(
personalHomeBacking('/home/user', 'nest/other/y.txt'),
b4a.from('y')
)
t.ok((await ctx.vfs.readdir('nest')).includes('leaf')) t.ok((await ctx.vfs.readdir('nest')).includes('leaf'))
await runBinCommand(ctx, ['rm', '-rf', 'nest']) await runBinCommand(ctx, ['rm', '-rf', 'nest'])
t.is(ctx.exitCode, 0) t.is(ctx.exitCode, 0)
@@ -1482,7 +1554,7 @@ test('runBinCommand runs ./git from cwd instead of booter delegate', async (t) =
await drive.ready() await drive.ready()
await personal.ready() await personal.ready()
await personal.put( await personal.put(
'/git', personalHomeBacking('/home/user', 'git'),
b4a.from(` b4a.from(`
async function run(ctx) { async function run(ctx) {
ctx.out.push('local-git-script') ctx.out.push('local-git-script')
File diff suppressed because one or more lines are too long