@@ -21,6 +21,7 @@ const (
2121 folderFormatName = "agentsview-normalized-artifacts"
2222 folderFormatVersion = 3
2323 folderMarkerTempPrefix = ".agentsview-artifacts.tmp-"
24+ folderMarkerMaxTemps = 128
2425 folderExchangeLockName = ".agentsview-artifacts.lock"
2526 folderExchangeMaxObjects = 128
2627 folderExchangeMaxBytes = int64 (64 << 20 )
@@ -225,10 +226,16 @@ func (t *folderTransport) prepareMarker() error {
225226 return err
226227 }
227228 if ! empty {
228- return fmt .Errorf (
229- "%w: target is not an agentsview artifact target" ,
230- ErrArtifactInvalid ,
231- )
229+ recovered , err := recoverFolderMarkerTemporaries (t .root )
230+ if err != nil {
231+ return err
232+ }
233+ if ! recovered {
234+ return fmt .Errorf (
235+ "%w: target is not an agentsview artifact target" ,
236+ ErrArtifactInvalid ,
237+ )
238+ }
232239 }
233240 if err := createFolderMarker (t .root ); err != nil {
234241 return fmt .Errorf ("initializing agentsview artifact target: %w" , err )
@@ -406,6 +413,64 @@ func folderRootEmpty(root *os.Root) (bool, error) {
406413 return len (entries ) == 0 , nil
407414}
408415
416+ func recoverFolderMarkerTemporaries (root * os.Root ) (_ bool , retErr error ) {
417+ directory , err := root .Open ("." )
418+ if err != nil {
419+ return false , fmt .Errorf ("opening artifact target directory: %w" , err )
420+ }
421+ defer func () { retErr = errors .Join (retErr , directory .Close ()) }()
422+
423+ names := make ([]string , 0 , 1 )
424+ for {
425+ entries , err := directory .ReadDir (1 )
426+ if errors .Is (err , io .EOF ) {
427+ break
428+ }
429+ if err != nil {
430+ return false , fmt .Errorf ("reading artifact target directory: %w" , err )
431+ }
432+ name := entries [0 ].Name ()
433+ if ! isFolderMarkerTemporaryName (name ) ||
434+ len (names ) >= folderMarkerMaxTemps {
435+ return false , nil
436+ }
437+ info , err := root .Lstat (name )
438+ if err != nil {
439+ return false , fmt .Errorf ("stating marker temporary: %w" , err )
440+ }
441+ if ! info .Mode ().IsRegular () {
442+ return false , nil
443+ }
444+ names = append (names , name )
445+ }
446+ if len (names ) == 0 {
447+ return true , nil
448+ }
449+ for _ , name := range names {
450+ if err := removeFolderFile (root , name ); err != nil {
451+ return false , fmt .Errorf ("removing marker temporary: %w" , err )
452+ }
453+ }
454+ if err := syncFolderDirectory (root ); err != nil {
455+ return false , fmt .Errorf ("syncing recovered artifact target: %w" , err )
456+ }
457+ return true , nil
458+ }
459+
460+ func isFolderMarkerTemporaryName (name string ) bool {
461+ suffix , found := strings .CutPrefix (name , folderMarkerTempPrefix )
462+ if ! found || len (suffix ) != 16 {
463+ return false
464+ }
465+ for _ , character := range suffix {
466+ if (character < '0' || character > '9' ) &&
467+ (character < 'a' || character > 'f' ) {
468+ return false
469+ }
470+ }
471+ return true
472+ }
473+
409474func validateFolderMarker (root * os.Root ) error {
410475 _ , err := readFolderMarker (root )
411476 return err
@@ -494,6 +559,20 @@ func createFolderMarker(root *os.Root) (retErr error) {
494559}
495560
496561func createFolderMarkerExclusive (root * os.Root , body []byte ) error {
562+ return createFolderMarkerExclusiveWithWriter (
563+ root ,
564+ body ,
565+ func (file * os.File , body []byte ) (int , error ) {
566+ return file .Write (body )
567+ },
568+ )
569+ }
570+
571+ func createFolderMarkerExclusiveWithWriter (
572+ root * os.Root ,
573+ body []byte ,
574+ write func (* os.File , []byte ) (int , error ),
575+ ) (retErr error ) {
497576 file , err := root .OpenFile (
498577 folderMarkerName ,
499578 os .O_WRONLY | os .O_CREATE | os .O_EXCL ,
@@ -505,10 +584,36 @@ func createFolderMarkerExclusive(root *os.Root, body []byte) error {
505584 }
506585 return err
507586 }
508- if _ , err := file .Write (body ); err != nil {
509- return errors .Join (err , file .Close ())
587+ closed := false
588+ keep := false
589+ defer func () {
590+ if ! closed {
591+ retErr = errors .Join (retErr , file .Close ())
592+ }
593+ if ! keep {
594+ retErr = errors .Join (
595+ retErr ,
596+ removeFolderFile (root , folderMarkerName ),
597+ )
598+ }
599+ }()
600+ written , err := write (file , body )
601+ if err != nil {
602+ return err
603+ }
604+ if written != len (body ) {
605+ return io .ErrShortWrite
510606 }
511- return errors .Join (file .Sync (), file .Close ())
607+ if err := file .Sync (); err != nil {
608+ return err
609+ }
610+ closeErr := file .Close ()
611+ closed = true
612+ if closeErr != nil {
613+ return closeErr
614+ }
615+ keep = true
616+ return nil
512617}
513618
514619type folderTransportPersistedState struct {
0 commit comments