( db: Database, path: string, bytes: Uint8Array, options: WriteFileOptions, now: () => number, )
| 921 | // to the async path; differs only in that the bytes have already been |
| 922 | // materialized. |
| 923 | export function writeFileSync( |
| 924 | db: Database, |
| 925 | path: string, |
| 926 | bytes: Uint8Array, |
| 927 | options: WriteFileOptions, |
| 928 | now: () => number, |
| 929 | ): void { |
| 930 | const { parts, path: canonical } = canonicalizePath(path); |
| 931 | if (parts.length === 0) { |
| 932 | throw createWorkspaceError("EISDIR", "cannot write to the root directory", canonical); |
| 933 | } |
| 934 | assertNotReadOnly(db, canonical); |
| 935 | const mode = (options.mode ?? 0o644) & 0o7777; |
| 936 | const mtime = now(); |
| 937 | |
| 938 | db.transactionSync(() => { |
| 939 | const parentInode = resolveParent(db, parts, canonical); |
| 940 | const leafName = parts[parts.length - 1]; |
| 941 | const existing = db.one<{ child_inode: number }>( |
| 942 | "SELECT child_inode FROM vfs_dirents WHERE parent_inode = ? AND name = ?", |
| 943 | parentInode, |
| 944 | leafName, |
| 945 | ); |
| 946 | |
| 947 | let inode: number; |
| 948 | if (existing !== undefined) { |
| 949 | if (options.exclusive) { |
| 950 | throw createWorkspaceError("EEXIST", `path exists: ${canonical}`, canonical); |
| 951 | } |
| 952 | const node = db.one<{ type: "file" | "dir" }>( |
| 953 | "SELECT type FROM vfs_nodes WHERE inode = ?", |
| 954 | existing.child_inode, |
| 955 | ); |
| 956 | if (node?.type === "dir") { |
| 957 | throw createWorkspaceError("EISDIR", `path is a directory: ${canonical}`, canonical); |
| 958 | } |
| 959 | inode = existing.child_inode; |
| 960 | // Replace the existing representation. Orphaned blobs (if any) |
| 961 | // are cleaned up by a later gc() pass. |
| 962 | db.run("DELETE FROM vfs_chunks WHERE inode = ?", inode); |
| 963 | } else { |
| 964 | inode = insertFileNode(db, mode, mtime); |
| 965 | insertFileDirent(db, parentInode, leafName, inode, canonical); |
| 966 | } |
| 967 | |
| 968 | const rev = incrementRev(db); |
| 969 | const chunks = chunksOf(bytes); |
| 970 | // Upsert blobs and write the new chunk list. |
| 971 | for (let idx = 0; idx < chunks.length; idx++) { |
| 972 | const chunk = chunks[idx]; |
| 973 | upsertChunkBlob(db, chunk, mtime); |
| 974 | db.run( |
| 975 | "INSERT INTO vfs_chunks (inode, idx, hash, size) VALUES (?, ?, ?, ?)", |
| 976 | inode, |
| 977 | idx, |
| 978 | chunk.hash, |
| 979 | chunk.size, |
| 980 | ); |
no test coverage detected