1
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
24 DefaultScratchSizeGB = 20
25
26
27
28 defaultVhdxBlockSizeMB = 1
29 )
30
31
32
33
34
35
36
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
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
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 "",
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
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
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
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
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
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