How File Locks Work Across the `read`, `ls`, `find`, `grep`, `write`, `edit`, and `delete` Tool Sets in Cloudflare Computer
File locks in Cloudflare Computer are implemented as read‑only mount guards at the virtual filesystem (VFS) layer, allowing read operations to proceed while blocking any write, edit, or delete attempts on locked paths.
All filesystem operations in Cloudflare Computer—whether executed through read, ls, find, grep, write, edit, or delete—flow through a SQLite‑backed VFS that enforces logical file locking via the readOnlyRootFor guard. This mechanism ensures that when a path is locked for exclusive access, the entire subtree becomes read‑only across both sides of the sync protocol (the Durable Object and the computerd daemon).
The Mount Guard Architecture
File locking in Cloudflare Computer does not rely on OS‑level primitives. Instead, it uses a logical mount guard system centralized in packages/dofs/src/fs/mount-guard.ts.
The readOnlyRootFor function queries the VFS metadata to determine whether a given path falls under a blocked mount. If a match exists, the operation receives a read‑only root identifier that triggers appropriate access restrictions.
How Each Tool Set Operation Handles Locks
Read, ls, find, grep: Permitted with Read‑Only Access
All read‑oriented operations consult readOnlyRootFor before proceeding. When a lock is active, these tools receive a read‑only mount root and continue execution without modification capability.
read– Retrieves file contents from the blocked path's read‑only view; no write access is granted.ls– Lists directory entries under the read‑only root; metadata likemtimeandsizeremain accessible.find– Recursively traverses the directory tree using the same read‑only mount point.grep– Searches file contents through the read‑only VFS interface.
// From mount-guard.ts — returns the blocked root or null if writable
import { readOnlyRootFor } from "@cloudflare/dofs/src/fs/mount-guard.js";
function canRead(db: SQLiteDatabase, path: string): boolean {
const blockedRoot = readOnlyRootFor(db, path);
// If blockedRoot exists, reads proceed via the read‑only mount
// If null, the path is fully writable
return true; // Reads are always allowed, but writes check blockedRoot
}
Write and Edit: Blocked When Locked
Write operations are rejected at the sync driver layer in packages/dofs/src/sync/apply.ts. Before applying any batch of changes, the driver checks readOnlyRootFor for the target path. A non‑null result triggers an access denial.
The edit tool, which opens files for modification, implicitly acquires an exclusive lock. This lock propagates across the sync channel, forcing the remote side into read‑only mode via the same guard mechanism.
// Conceptual guard check in apply.ts
function applyWriteBatch(db, operations: WriteOp[]) {
for (const op of operations) {
const blocked = readOnlyRootFor(db, op.path);
if (blocked) {
throw new Error(`Write blocked: ${op.path} is locked under ${blocked}`);
}
// Execute UPDATE/INSERT into SQLite VFS tables
}
}
Delete: Blocked Under Active Locks
Deletion follows identical logic to writes. Both unlink (file removal) and rmdir (directory removal) operations in apply.ts validate the target path against readOnlyRootFor. A locked path cannot be deleted until the guard is released.
Cross‑Side Lock Synchronization
The lock state is symmetric across both endpoints of the sync protocol. When a lock is established on one side:
- The locking side marks the path as blocked in its VFS.
- A watermark containing the blocked path is transmitted to the remote side.
- The remote VFS updates its mount guard state, creating a matching read‑only root.
- Both sides now enforce identical restrictions without race conditions.
This design ensures that a container performing an edit via workspace.runtime.exec automatically locks out writes from the Durable Object side—and vice versa.
Lock Lifecycle and Characteristics
| Aspect | Behavior |
|---|---|
| Granularity | Per‑path subtree; all nested files and directories inherit the lock |
| Duration | Transient, tied to the sync transaction or explicit edit session |
| Storage | Logical state in SQLite metadata (blocks table, mount guard cache) |
| Release | Automatic on transaction commit/abort or explicit session close |
| Conflict resolution | First‑lock‑wins; subsequent write attempts receive EACCES‑style errors |
Code Example: Safe File Operations with Lock Awareness
import { readOnlyRootFor } from "@cloudflare/dofs/src/fs/mount-guard.js";
async function guardedWrite(db, path: string, data: Buffer) {
const blockedRoot = readOnlyRootFor(db, path);
if (blockedRoot) {
throw new Error(
`Cannot write ${path}: locked under read‑only mount ${blockedRoot}`
);
}
// Safe to proceed — no active lock
await db.run(
`INSERT INTO blocks (path, data, mtime) VALUES (?, ?, ?)`,
path, data, Date.now()
);
}
// Read operations skip the guard check for blocking
async function safeRead(db, path: string): Promise<Buffer> {
// readOnlyRootFor may return a blocked root, but reads continue
const row = await db.get(`SELECT data FROM blocks WHERE path = ?`, path);
return row ? row.data : null;
}
Key Implementation Files
| File | Purpose | GitHub Link |
|---|---|---|
mount-guard.ts |
Core readOnlyRootFor logic and mount guard state |
source |
apply.ts |
Sync driver that enforces locks before writes/deletes | source |
shim.ts |
computerd shim blocking until VFS state is consistent |
source |
vfs.ts |
POSIX attribute mapping and lock state propagation | source |
Summary
- File locks are logical mount guards, not OS file locks, implemented in
mount-guard.tsviareadOnlyRootFor. - Read tools (
read,ls,find,grep) operate through read‑only mounts when locks are active. - Write tools (
write,edit) and delete are blocked at the sync driver layer inapply.tsif the target path is locked. - Locks synchronize bidirectionally across the Durable Object and
computerddaemon through watermark propagation. - The entire system rides on SQLite‑backed metadata without native filesystem dependencies.
Frequently Asked Questions
How does a file lock affect concurrent read operations?
Concurrent reads proceed normally through a read‑only mount view. The readOnlyRootFor guard returns the blocked root identifier, which read‑oriented tools use to access file contents and metadata without modification rights. No read operation is ever rejected due to a lock.
What happens if I attempt to write to a locked file?
The apply.ts sync driver checks readOnlyRootFor before executing any write batch. If the target path matches a blocked root, the driver throws an error equivalent to EACCES and aborts the transaction. The write never reaches the SQLite blocks table.
How long does a file lock persist?
Locks are transient and bound to the lifecycle of the exclusive operation that created them. An edit session holds the lock until explicitly closed or until its transaction commits or aborts. Once released, the mount guard removes the blocked root entry and full write access resumes on both sides of the sync channel.
Can locks apply to entire directories?
Yes. The mount guard operates on path prefixes. Locking a directory automatically locks all nested files and subdirectories under that path subtree. This behavior ensures consistent isolation for recursive operations like find or bulk delete.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →