@@ -554,32 +554,67 @@ export async function copyFileWithinRoot(params: {
554554
555555 let target : SafeWritableOpenResult | null = null ;
556556 let sourceClosedByStream = false ;
557- let targetClosedByStream = false ;
557+ let targetClosedByUs = false ;
558+ let tempHandle : FileHandle | null = null ;
559+ let tempPath : string | null = null ;
560+ let tempClosedByStream = false ;
558561 try {
559562 target = await openWritableFileWithinRoot ( {
560563 rootDir : params . rootDir ,
561564 relativePath : params . relativePath ,
562565 mkdir : params . mkdir ,
566+ truncateExisting : false ,
563567 } ) ;
568+ const destinationPath = target . openedRealPath ;
569+ const targetMode = target . openedStat . mode & 0o777 ;
570+ await target . handle . close ( ) . catch ( ( ) => { } ) ;
571+ targetClosedByUs = true ;
572+
573+ tempPath = buildAtomicWriteTempPath ( destinationPath ) ;
574+ tempHandle = await fs . open ( tempPath , OPEN_WRITE_CREATE_FLAGS , targetMode || 0o600 ) ;
564575 const sourceStream = source . handle . createReadStream ( ) ;
565- const targetStream = target . handle . createWriteStream ( ) ;
576+ const targetStream = tempHandle . createWriteStream ( ) ;
566577 sourceStream . once ( "close" , ( ) => {
567578 sourceClosedByStream = true ;
568579 } ) ;
569580 targetStream . once ( "close" , ( ) => {
570- targetClosedByStream = true ;
581+ tempClosedByStream = true ;
571582 } ) ;
572583 await pipeline ( sourceStream , targetStream ) ;
584+ const writtenStat = await fs . stat ( tempPath ) ;
585+ if ( ! tempClosedByStream ) {
586+ await tempHandle . close ( ) . catch ( ( ) => { } ) ;
587+ tempClosedByStream = true ;
588+ }
589+ tempHandle = null ;
590+ await fs . rename ( tempPath , destinationPath ) ;
591+ tempPath = null ;
592+ try {
593+ await verifyAtomicWriteResult ( {
594+ rootDir : params . rootDir ,
595+ targetPath : destinationPath ,
596+ expectedStat : writtenStat ,
597+ } ) ;
598+ } catch ( err ) {
599+ emitWriteBoundaryWarning ( `post-copy verification failed: ${ String ( err ) } ` ) ;
600+ throw err ;
601+ }
573602 } catch ( err ) {
574603 if ( target ?. createdForWrite ) {
575604 await fs . rm ( target . openedRealPath , { force : true } ) . catch ( ( ) => { } ) ;
576605 }
577606 throw err ;
578607 } finally {
608+ if ( tempPath ) {
609+ await fs . rm ( tempPath , { force : true } ) . catch ( ( ) => { } ) ;
610+ }
579611 if ( ! sourceClosedByStream ) {
580612 await source . handle . close ( ) . catch ( ( ) => { } ) ;
581613 }
582- if ( target && ! targetClosedByStream ) {
614+ if ( tempHandle && ! tempClosedByStream ) {
615+ await tempHandle . close ( ) . catch ( ( ) => { } ) ;
616+ }
617+ if ( target && ! targetClosedByUs ) {
583618 await target . handle . close ( ) . catch ( ( ) => { } ) ;
584619 }
585620 }
0 commit comments