@@ -17,6 +17,7 @@ package boot
1717import (
1818 "errors"
1919 "fmt"
20+ "io"
2021 "path"
2122 "strconv"
2223 "sync"
@@ -489,7 +490,7 @@ func (cm *containerManager) PortForward(opts *PortForwardOpts, _ *struct{}) erro
489490
490491// RestoreOpts contains options related to restoring a container's file system.
491492type RestoreOpts struct {
492- // FilePayload contains the state file to be restored, followed in order by :
493+ // FilePayload contains, in order:
493494 // 1. checkpoint state file.
494495 // 2. optional checkpoint pages metadata file.
495496 // 3. optional checkpoint pages file.
@@ -498,6 +499,8 @@ type RestoreOpts struct {
498499 HavePagesFile bool
499500 HaveDeviceFile bool
500501 Background bool
502+
503+ RestoreOptsExtra
501504}
502505
503506// Restore loads a container from a statefile.
@@ -521,27 +524,32 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error {
521524 return fmt .Errorf ("at least one file must be passed to Restore" )
522525 }
523526
524- stateFile , err := o . ReleaseFD ( 0 )
527+ stateFile , pagesMetadata , pagesFile , err := getRestoreReadersImpl ( o )
525528 if err != nil {
526529 return err
527530 }
531+ defer func () {
532+ if stateFile != nil {
533+ stateFile .Close ()
534+ }
535+ if pagesMetadata != nil {
536+ pagesMetadata .Close ()
537+ }
538+ if pagesFile != nil {
539+ pagesFile .Close ()
540+ }
541+ }()
528542
529- var stat unix.Stat_t
530- if err := unix .Fstat (stateFile .FD (), & stat ); err != nil {
531- return err
532- }
533- if stat .Size == 0 {
534- return fmt .Errorf ("statefile cannot be empty" )
535- }
536-
537- reader , metadata , err := state .NewStatefileReader (stateFile , nil )
543+ reader , metadata , err := state .NewStatefileReader (stateFile /* transfers ownership on success */ , nil )
538544 if err != nil {
539545 return fmt .Errorf ("creating statefile reader: %w" , err )
540546 }
547+ stateFile = nil
541548
542549 // Create the main MemoryFile.
543550 mf , err := createMemoryFile (cm .l .root .conf .AppHugePages , cm .l .hostTHP )
544551 if err != nil {
552+ reader .Close ()
545553 return fmt .Errorf ("creating memory file: %v" , err )
546554 }
547555
@@ -559,42 +567,18 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error {
559567 // Release `cm.l.mu`.
560568 cu .Clean ()
561569
562- fileIdx := 1
563570 if o .HavePagesFile {
564- pagesMetadataFD , err := o .ReleaseFD (fileIdx )
565- if err != nil {
566- return err
567- }
568- fileIdx ++
569-
570- pagesFileFD , err := o .ReleaseFD (fileIdx )
571- if err != nil {
572- return err
573- }
574- fileIdx ++
575-
576- // //pkg/state/wire reads one byte at a time; buffer these reads to
577- // avoid making one syscall per read. For the state file, this
578- // buffering is handled by statefile.NewReader() => compressio.Reader
579- // or compressio.NewSimpleReader().
580- pagesMetadata := stateio .NewBufioReadCloser (pagesMetadataFD )
581- // TODO: Allow `runsc restore` to override I/O parameters.
582- pagesFile := stateio .NewPagesFileFDReaderDefault (int32 (pagesFileFD .Release ()))
583-
584571 // This immediately starts loading the main MemoryFile asynchronously.
585- cm .restorer .asyncMFLoader = kernel .NewAsyncMFLoader (pagesMetadata , pagesFile , cm .restorer .mainMF , timer .Fork ("PagesFileLoader" ))
572+ cm .restorer .asyncMFLoader = kernel .NewAsyncMFLoader (pagesMetadata , pagesFile , cm .restorer .mainMF , timer .Fork ("PagesFileLoader" )) // transfers ownership
573+ pagesMetadata = nil
574+ pagesFile = nil
586575 }
587576
588577 if o .HaveDeviceFile {
589- cm .restorer .deviceFile , err = o .ReleaseFD (fileIdx )
578+ cm .restorer .deviceFile , err = o .ReleaseFD (len ( o . Files ) - 1 )
590579 if err != nil {
591580 return err
592581 }
593- fileIdx ++
594- }
595-
596- if fileIdx < len (o .Files ) {
597- return fmt .Errorf ("more files passed to Restore than expected" )
598582 }
599583 timer .Reached ("restorer ok" )
600584
@@ -634,6 +618,45 @@ func (cm *containerManager) Restore(o *RestoreOpts, _ *struct{}) error {
634618 return cm .restorer .restoreContainerInfo (cm .l , & cm .l .root , timer .Fork ("cont:root" ))
635619}
636620
621+ func getRestoreReadersForLocalCheckpointFiles (o * RestoreOpts ) (io.ReadCloser , io.ReadCloser , stateio.AsyncReader , error ) {
622+ stateFile , err := o .ReleaseFD (0 )
623+ if err != nil {
624+ return nil , nil , nil , err
625+ }
626+ cu := cleanup .Make (func () { stateFile .Close () })
627+ defer cu .Clean ()
628+ var stat unix.Stat_t
629+ if err := unix .Fstat (stateFile .FD (), & stat ); err != nil {
630+ return nil , nil , nil , err
631+ }
632+ if stat .Size == 0 {
633+ return nil , nil , nil , fmt .Errorf ("statefile cannot be empty" )
634+ }
635+
636+ if ! o .HavePagesFile {
637+ cu .Release ()
638+ return stateFile , nil , nil , nil
639+ }
640+ pagesMetadataFile , err := o .ReleaseFD (1 )
641+ if err != nil {
642+ return nil , nil , nil , err
643+ }
644+ cu .Add (func () { pagesMetadataFile .Close () })
645+ pagesFile , err := o .ReleaseFD (2 )
646+ if err != nil {
647+ return nil , nil , nil , err
648+ }
649+ cu .Release ()
650+ // //pkg/state/wire reads one byte at a time; buffer reads from
651+ // pagesMetadataFile to avoid making one syscall per read. For the state
652+ // file, this buffering is handled by statefile.NewReader() =>
653+ // compressio.Reader or compressio.NewSimpleReader().
654+ return stateFile ,
655+ stateio .NewBufioReadCloser (pagesMetadataFile ),
656+ stateio .NewPagesFileFDReaderDefault (int32 (pagesFile .Release ())),
657+ nil
658+ }
659+
637660func (cm * containerManager ) onRestoreDone () {
638661 cm .l .mu .Lock ()
639662 cm .l .state = restored
0 commit comments