...

Source file src/github.com/Microsoft/hcsshim/internal/lcow/scratch.go

Documentation: github.com/Microsoft/hcsshim/internal/lcow

     1  //go:build windows
     2  
     3  package lcow
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/Microsoft/go-winio/vhd"
    14  	cmdpkg "github.com/Microsoft/hcsshim/internal/cmd"
    15  	"github.com/Microsoft/hcsshim/internal/copyfile"
    16  	"github.com/Microsoft/hcsshim/internal/log"
    17  	"github.com/Microsoft/hcsshim/internal/timeout"
    18  	"github.com/Microsoft/hcsshim/internal/uvm"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  const (
    23  	// DefaultScratchSizeGB is the size of the default LCOW scratch disk in GB
    24  	DefaultScratchSizeGB = 20
    25  
    26  	// defaultVhdxBlockSizeMB is the block-size for the scratch VHDx's this
    27  	// package can create.
    28  	defaultVhdxBlockSizeMB = 1
    29  )
    30  
    31  // CreateScratch uses a utility VM to create an empty scratch disk of a
    32  // requested size. It has a caching capability. If the cacheFile exists, and the
    33  // request is for a default size, a copy of that is made to the target. If the
    34  // size is non-default, or the cache file does not exist, it uses a utility VM
    35  // to create target. It is the responsibility of the caller to synchronize
    36  // simultaneous attempts to create the cache file.
    37  func CreateScratch(ctx context.Context, lcowUVM *uvm.UtilityVM, destFile string, sizeGB uint32, cacheFile string) error {
    38  	if lcowUVM == nil {
    39  		return fmt.Errorf("no uvm")
    40  	}
    41  
    42  	if lcowUVM.OS() != "linux" {
    43  		return errors.New("lcow::CreateScratch requires a linux utility VM to operate")
    44  	}
    45  
    46  	log.G(ctx).WithFields(logrus.Fields{
    47  		"dest":   destFile,
    48  		"sizeGB": sizeGB,
    49  		"cache":  cacheFile,
    50  	}).Debug("lcow::CreateScratch opts")
    51  
    52  	// Retrieve from cache if the default size and already on disk
    53  	if cacheFile != "" && sizeGB == DefaultScratchSizeGB {
    54  		if _, err := os.Stat(cacheFile); err == nil {
    55  			if err := copyfile.CopyFile(ctx, cacheFile, destFile, false); err != nil {
    56  				return fmt.Errorf("failed to copy cached file '%s' to '%s': %s", cacheFile, destFile, err)
    57  			}
    58  			log.G(ctx).WithFields(logrus.Fields{
    59  				"dest":  destFile,
    60  				"cache": cacheFile,
    61  			}).Debug("lcow::CreateScratch copied from cache")
    62  			return nil
    63  		}
    64  	}
    65  
    66  	// Create the VHDX
    67  	if err := vhd.CreateVhdx(destFile, sizeGB, defaultVhdxBlockSizeMB); err != nil {
    68  		return fmt.Errorf("failed to create VHDx %s: %s", destFile, err)
    69  	}
    70  
    71  	var options []string
    72  	scsi, err := lcowUVM.AddSCSI(
    73  		ctx,
    74  		destFile,
    75  		"", // No destination as not formatted
    76  		false,
    77  		lcowUVM.ScratchEncryptionEnabled(),
    78  		options,
    79  		uvm.VMAccessTypeIndividual,
    80  	)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	removeSCSI := true
    85  	defer func() {
    86  		if removeSCSI {
    87  			_ = lcowUVM.RemoveSCSI(ctx, destFile)
    88  		}
    89  	}()
    90  
    91  	log.G(ctx).WithFields(logrus.Fields{
    92  		"dest":       destFile,
    93  		"controller": scsi.Controller,
    94  		"lun":        scsi.LUN,
    95  	}).Debug("lcow::CreateScratch device attached")
    96  
    97  	// Validate /sys/bus/scsi/devices/C:0:0:L exists as a directory
    98  	devicePath := fmt.Sprintf("/sys/bus/scsi/devices/%d:0:0:%d/block", scsi.Controller, scsi.LUN)
    99  	testdCtx, cancel := context.WithTimeout(ctx, timeout.TestDRetryLoop)
   100  	defer cancel()
   101  	for {
   102  		cmd := cmdpkg.CommandContext(testdCtx, lcowUVM, "test", "-d", devicePath)
   103  		err := cmd.Run()
   104  		if err == nil {
   105  			break
   106  		}
   107  		if _, ok := err.(*cmdpkg.ExitError); !ok {
   108  			return fmt.Errorf("failed to run %+v following hot-add %s to utility VM: %s", cmd.Spec.Args, destFile, err)
   109  		}
   110  		time.Sleep(time.Millisecond * 10)
   111  	}
   112  	cancel()
   113  
   114  	// Get the device from under the block subdirectory by doing a simple ls. This will come back as (eg) `sda`
   115  	lsCtx, cancel := context.WithTimeout(ctx, timeout.ExternalCommandToStart)
   116  	cmd := cmdpkg.CommandContext(lsCtx, lcowUVM, "ls", devicePath)
   117  	lsOutput, err := cmd.Output()
   118  	cancel()
   119  	if err != nil {
   120  		return fmt.Errorf("failed to `%+v` following hot-add %s to utility VM: %s", cmd.Spec.Args, destFile, err)
   121  	}
   122  	device := fmt.Sprintf(`/dev/%s`, bytes.TrimSpace(lsOutput))
   123  	log.G(ctx).WithFields(logrus.Fields{
   124  		"dest":   destFile,
   125  		"device": device,
   126  	}).Debug("lcow::CreateScratch device guest location")
   127  
   128  	// Format it ext4
   129  	mkfsCtx, cancel := context.WithTimeout(ctx, timeout.ExternalCommandToStart)
   130  	cmd = cmdpkg.CommandContext(mkfsCtx, lcowUVM, "mkfs.ext4", "-q", "-E", "lazy_itable_init=0,nodiscard", "-O", `^has_journal,sparse_super2,^resize_inode`, device)
   131  	var mkfsStderr bytes.Buffer
   132  	cmd.Stderr = &mkfsStderr
   133  	err = cmd.Run()
   134  	cancel()
   135  	if err != nil {
   136  		return fmt.Errorf("failed to `%+v` following hot-add %s to utility VM: %s", cmd.Spec.Args, destFile, err)
   137  	}
   138  
   139  	// Hot-Remove before we copy it
   140  	removeSCSI = false
   141  	if err := lcowUVM.RemoveSCSI(ctx, destFile); err != nil {
   142  		return fmt.Errorf("failed to hot-remove: %s", err)
   143  	}
   144  
   145  	// Populate the cache.
   146  	if cacheFile != "" && (sizeGB == DefaultScratchSizeGB) {
   147  		if err := copyfile.CopyFile(ctx, destFile, cacheFile, true); err != nil {
   148  			return fmt.Errorf("failed to seed cache '%s' from '%s': %s", destFile, cacheFile, err)
   149  		}
   150  	}
   151  
   152  	log.G(ctx).WithField("dest", destFile).Debug("lcow::CreateScratch created (non-cache)")
   153  	return nil
   154  }
   155  

View as plain text