@@ -154,7 +154,7 @@ interface ChunkRef {
154154}
155155
156156type WriteTarget =
157- | { kind : "existing" ; inode : number }
157+ | { kind : "existing" ; inode : number ; canonicalPath : string }
158158 | { kind : "create" ; parentInode : number ; leafName : string ; canonicalPath : string } ;
159159
160160interface DirectWriteTarget {
@@ -275,7 +275,11 @@ function resolveWriteTarget(
275275 ) ;
276276 }
277277 if ( node . type !== "symlink" ) {
278- return { kind : "existing" , inode : direct . existingInode } ;
278+ return {
279+ kind : "existing" ,
280+ inode : direct . existingInode ,
281+ canonicalPath : direct . canonicalPath ,
282+ } ;
279283 }
280284
281285 countSymlinkFollow ( follows , canonical ) ;
@@ -603,7 +607,10 @@ function resolveFileInode(db: Database, path: string): { inode: number; mode: nu
603607 return { inode : node . inode , mode : node . mode } ;
604608}
605609
606- function resolveWritableFileInode ( db : Database , path : string ) : { inode : number ; mode : number } {
610+ function resolveWritableFileInode (
611+ db : Database ,
612+ path : string ,
613+ ) : { inode : number ; mode : number ; canonicalPath : string } {
607614 const { parts, path : canonical } = canonicalizePath ( path ) ;
608615 if ( parts . length === 0 ) {
609616 throw createWorkspaceError ( "EISDIR" , "cannot write to the root directory" , canonical ) ;
@@ -616,7 +623,7 @@ function resolveWritableFileInode(db: Database, path: string): { inode: number;
616623 if ( mode === undefined ) {
617624 throw createWorkspaceError ( "ENOENT" , `no such file: ${ canonical } ` , canonical ) ;
618625 }
619- return { inode : target . inode , mode } ;
626+ return { inode : target . inode , mode, canonicalPath : target . canonicalPath } ;
620627}
621628
622629function directTargetForPath ( db : Database , path : string ) : DirectWriteTarget {
@@ -820,7 +827,11 @@ export function releaseWriteBufferSync(db: Database, path: string, now: () => nu
820827 const buffered = entry . buf . subarray ( 0 , entry . size ) ;
821828
822829 try {
823- resolveWritableFileInode ( db , path ) ;
830+ if ( entry . dirtyPath === undefined || entry . dirtyTargetPath === undefined ) {
831+ throw createWorkspaceError ( "EIO" , `buffer has no writable path: ${ canonical } ` , canonical ) ;
832+ }
833+ assertNotReadOnly ( db , entry . dirtyPath ) ;
834+ assertNotReadOnly ( db , entry . dirtyTargetPath ) ;
824835 db . transactionSync ( ( ) => {
825836 if ( entry . size === 0 ) {
826837 // An empty file owns no chunk rows; clear any old ones the
@@ -931,6 +942,7 @@ function commitPendingBuffer(db: Database, entry: WriteBufferEntry, now: () => n
931942 throw error ;
932943 }
933944 promotePendingToInode ( db , pendingInode , realInode ) ;
945+ entry . dirty = false ;
934946 return realInode ;
935947}
936948
@@ -1028,7 +1040,11 @@ export function writeRangeSync(
10281040 return bytes . byteLength ;
10291041 }
10301042
1031- const { inode, mode : existingMode } = resolveWritableFileInode ( db , path ) ;
1043+ const {
1044+ inode,
1045+ mode : existingMode ,
1046+ canonicalPath : targetPath ,
1047+ } = resolveWritableFileInode ( db , path ) ;
10321048 const mode = ( options . mode ?? existingMode ) & 0o7777 ;
10331049 const buffered = getWriteBuffer ( db , inode ) ;
10341050
@@ -1046,6 +1062,8 @@ export function writeRangeSync(
10461062 if ( writeEnd > buffered . size ) buffered . size = writeEnd ;
10471063 buffered . mode = mode ;
10481064 buffered . dirty = true ;
1065+ buffered . dirtyPath = canonical ;
1066+ buffered . dirtyTargetPath = targetPath ;
10491067 return bytes . byteLength ;
10501068 }
10511069
@@ -1103,7 +1121,7 @@ export function truncateFileSync(
11031121 return ;
11041122 }
11051123
1106- const { inode, mode } = resolveWritableFileInode ( db , path ) ;
1124+ const { inode, mode, canonicalPath : targetPath } = resolveWritableFileInode ( db , path ) ;
11071125 const buffered = getWriteBuffer ( db , inode ) ;
11081126
11091127 if ( buffered !== undefined ) {
@@ -1114,6 +1132,8 @@ export function truncateFileSync(
11141132 }
11151133 buffered . size = size ;
11161134 buffered . dirty = true ;
1135+ buffered . dirtyPath = canonical ;
1136+ buffered . dirtyTargetPath = targetPath ;
11171137 return ;
11181138 }
11191139
0 commit comments