@@ -21,10 +21,13 @@ package lcow
2121import (
2222 "context"
2323 "encoding/json"
24+ "fmt"
2425 "io"
2526 "os"
2627 "path/filepath"
28+ "strconv"
2729 "strings"
30+ "sync"
2831 "syscall"
2932 "time"
3033 "unsafe"
@@ -59,6 +62,8 @@ func init() {
5962type snapshotter struct {
6063 root string
6164 ms * storage.MetaStore
65+
66+ scratchLock sync.Mutex
6267}
6368
6469// NewSnapshotter returns a new windows snapshotter
@@ -315,55 +320,10 @@ func (s *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
315320 if err := os .MkdirAll (snDir , 0700 ); err != nil {
316321 return nil , err
317322 }
318- // Create the scratch.vhdx cache file if it doesn't already exit.
319- scratchPath := filepath .Join (s .root , "scratch.vhdx" )
320- scratchLockPath := filepath .Join (s .root , "scratch.vhdx.lock" )
321- startTime := time .Now ()
322- timeout := 2 * time .Minute
323- var scratchSource * os.File
324- for {
325- var err error
326- scratchSource , err = os .OpenFile (scratchPath , os .O_RDONLY , 0700 )
327- if err != nil {
328- if os .IsNotExist (err ) {
329- // No scratch path. Take the lock and create it.
330- slock , err := os .OpenFile (scratchLockPath , os .O_EXCL | os .O_CREATE , 0700 )
331- if err != nil {
332- if time .Now ().Sub (startTime ) >= timeout {
333- return nil , errors .Wrap (err , "timed out waiting for scratch.vhdx.lock" )
334- }
335- // Couldnt obtain the lock. Sleep and try again.
336- time .Sleep (1 * time .Second )
337- continue
338- }
339- defer slock .Close ()
340-
341- // Create the scratch
342- rhcs := runhcs.Runhcs {
343- Debug : true ,
344- Log : filepath .Join (s .root , "runhcs-scratch.log" ),
345- LogFormat : runhcs .JSON ,
346- Owner : "containerd" ,
347- }
348- if err := rhcs .CreateScratch (ctx , scratchPath ); err != nil {
349- _ = os .Remove (scratchPath )
350- return nil , errors .Wrap (err , "failed to create scratch.vhdx" )
351- }
352-
353- // Successfully created scratch in the cache. Open and copy
354- continue
355- } else {
356- if time .Now ().Sub (startTime ) >= timeout {
357- return nil , errors .Wrap (err , "timed out waiting for scratch.vhdx" )
358- }
359- // Couldnt obtain read access. Sleep and try again. Likely
360- // this case is that scratch.vhdx is in the process of being
361- // written actively.
362- time .Sleep (1 * time .Second )
363- continue
364- }
365- }
366- break
323+
324+ scratchSource , err := s .openOrCreateScratch (ctx )
325+ if err != nil {
326+ return nil , err
367327 }
368328 defer scratchSource .Close ()
369329
@@ -392,6 +352,51 @@ func (s *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
392352 return s .mounts (newSnapshot ), nil
393353}
394354
355+ func (s * snapshotter ) openOrCreateScratch (ctx context.Context ) (_ * os.File , err error ) {
356+ // Create the scratch.vhdx cache file if it doesn't already exit.
357+ s .scratchLock .Lock ()
358+ defer s .scratchLock .Unlock ()
359+
360+ scratchFinalPath := filepath .Join (s .root , "scratch.vhdx" )
361+ scratchSource , err := os .OpenFile (scratchFinalPath , os .O_RDONLY , 0700 )
362+ if err != nil {
363+ if ! os .IsNotExist (err ) {
364+ return nil , errors .Wrap (err , "failed to open scratch.vhdx for read" )
365+ }
366+
367+ log .G (ctx ).Debug ("scratch.vhdx not found, creating a new one" )
368+
369+ // Golang logic for ioutil.TempFile without the file creation
370+ r := uint32 (time .Now ().UnixNano () + int64 (os .Getpid ()))
371+ r = r * 1664525 + 1013904223 // constants from Numerical Recipes
372+
373+ scratchTempName := fmt .Sprintf ("scratch-%s-tmp.vhdx" , strconv .Itoa (int (1e9 + r % 1e9 ))[1 :])
374+ scratchTempPath := filepath .Join (s .root , scratchTempName )
375+
376+ // Create the scratch
377+ rhcs := runhcs.Runhcs {
378+ Debug : true ,
379+ Log : filepath .Join (s .root , "runhcs-scratch.log" ),
380+ LogFormat : runhcs .JSON ,
381+ Owner : "containerd" ,
382+ }
383+ if err := rhcs .CreateScratch (ctx , scratchTempPath ); err != nil {
384+ _ = os .Remove (scratchTempPath )
385+ return nil , errors .Wrapf (err , "failed to create '%s' temp file" , scratchTempName )
386+ }
387+ if err := os .Rename (scratchTempPath , scratchFinalPath ); err != nil {
388+ _ = os .Remove (scratchTempPath )
389+ return nil , errors .Wrapf (err , "failed to rename '%s' temp file to 'scratch.vhdx'" , scratchTempName )
390+ }
391+ scratchSource , err = os .OpenFile (scratchFinalPath , os .O_RDONLY , 0700 )
392+ if err != nil {
393+ _ = os .Remove (scratchFinalPath )
394+ return nil , errors .Wrap (err , "failed to open scratch.vhdx for read after creation" )
395+ }
396+ }
397+ return scratchSource , nil
398+ }
399+
395400func (s * snapshotter ) parentIDsToParentPaths (parentIDs []string ) []string {
396401 var parentLayerPaths []string
397402 for _ , ID := range parentIDs {
0 commit comments