Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -13,7 +13,43 @@ |
| 13 | 13 |
// operations. The downside of safe-mode is that operations are slower as |
| 14 | 14 |
// a new service utility VM has to be started and torn-down when needed. |
| 15 | 15 |
// |
| 16 |
-// To enable global mode, run with --storage-opt lcow.globalmode=true |
|
| 16 |
+// Options (needs official documentation, but lets get full functionality first...) @jhowardmsft |
|
| 17 |
+// |
|
| 18 |
+// The following options are read by the graphdriver itself: |
|
| 19 |
+// |
|
| 20 |
+// * lcow.globalmode - Enables global service VM Mode |
|
| 21 |
+// -- Possible values: true/false |
|
| 22 |
+// -- Default if omitted: false |
|
| 23 |
+// |
|
| 24 |
+// * lcow.sandboxsize - Specifies a custom sandbox size in GB for starting a container |
|
| 25 |
+// -- Possible values: >= default sandbox size (opengcs defined, currently 20) |
|
| 26 |
+// -- Default if ommitted: 20 |
|
| 27 |
+// |
|
| 28 |
+// The following options are read by opengcs: |
|
| 29 |
+// |
|
| 30 |
+// * lcow.kirdpath - Specifies a custom path to a kernel/initrd pair |
|
| 31 |
+// -- Possible values: Any local path that is not a mapped drive |
|
| 32 |
+// -- Default if ommitted: %ProgramFiles%\Linux Containers |
|
| 33 |
+// |
|
| 34 |
+// * lcow.kernel - Specifies a custom kernel file located in the `lcow.kirdpath` path |
|
| 35 |
+// -- Possible values: Any valid filename |
|
| 36 |
+// -- Default if ommitted: bootx64.efi |
|
| 37 |
+// |
|
| 38 |
+// * lcow.initrd - Specifies a custom initrd file located in the `lcow.kirdpath` path |
|
| 39 |
+// -- Possible values: Any valid filename |
|
| 40 |
+// -- Default if ommitted: initrd.img |
|
| 41 |
+// |
|
| 42 |
+// * lcow.bootparameters - Specifies additional boot parameters for booting in kernel+initrd mode |
|
| 43 |
+// -- Possible values: Any valid linux kernel boot options |
|
| 44 |
+// -- Default if ommitted: <nil> |
|
| 45 |
+// |
|
| 46 |
+// * lcow.vhdx - Specifies a custom vhdx file to boot (instead of a kernel+initrd) |
|
| 47 |
+// -- Possible values: Any valid filename |
|
| 48 |
+// -- Default if ommitted: C:\Program Files\Linux Containers\uvm.vhdx |
|
| 49 |
+// |
|
| 50 |
+// * lcow.timeout - Specifies a timeout for utility VM operations in seconds |
|
| 51 |
+// -- Possible values: >=0 |
|
| 52 |
+// -- Default if ommitted: 300 |
|
| 17 | 53 |
|
| 18 | 54 |
// TODO: Grab logs from SVM at terminate or errors |
| 19 | 55 |
|
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
# the following lines are in sorted order, FYI |
| 2 | 2 |
github.com/Azure/go-ansiterm 19f72df4d05d31cbe1c56bfc8045c96babff6c7e |
| 3 | 3 |
github.com/Microsoft/hcsshim v0.6.1 |
| 4 |
-github.com/Microsoft/go-winio v0.4.2 |
|
| 4 |
+github.com/Microsoft/go-winio v0.4.4 |
|
| 5 | 5 |
github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git |
| 6 | 6 |
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 |
| 7 | 7 |
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| ... | ... |
@@ -23,6 +23,13 @@ type atomicBool int32 |
| 23 | 23 |
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
| 24 | 24 |
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
| 25 | 25 |
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
| 26 |
+func (b *atomicBool) swap(new bool) bool {
|
|
| 27 |
+ var newInt int32 |
|
| 28 |
+ if new {
|
|
| 29 |
+ newInt = 1 |
|
| 30 |
+ } |
|
| 31 |
+ return atomic.SwapInt32((*int32)(b), newInt) == 1 |
|
| 32 |
+} |
|
| 26 | 33 |
|
| 27 | 34 |
const ( |
| 28 | 35 |
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 |
| ... | ... |
@@ -71,7 +78,7 @@ func initIo() {
|
| 71 | 71 |
type win32File struct {
|
| 72 | 72 |
handle syscall.Handle |
| 73 | 73 |
wg sync.WaitGroup |
| 74 |
- closing bool |
|
| 74 |
+ closing atomicBool |
|
| 75 | 75 |
readDeadline deadlineHandler |
| 76 | 76 |
writeDeadline deadlineHandler |
| 77 | 77 |
} |
| ... | ... |
@@ -107,9 +114,9 @@ func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
| 107 | 107 |
|
| 108 | 108 |
// closeHandle closes the resources associated with a Win32 handle |
| 109 | 109 |
func (f *win32File) closeHandle() {
|
| 110 |
- if !f.closing {
|
|
| 110 |
+ // Atomically set that we are closing, releasing the resources only once. |
|
| 111 |
+ if !f.closing.swap(true) {
|
|
| 111 | 112 |
// cancel all IO and wait for it to complete |
| 112 |
- f.closing = true |
|
| 113 | 113 |
cancelIoEx(f.handle, nil) |
| 114 | 114 |
f.wg.Wait() |
| 115 | 115 |
// at this point, no new IO can start |
| ... | ... |
@@ -127,10 +134,10 @@ func (f *win32File) Close() error {
|
| 127 | 127 |
// prepareIo prepares for a new IO operation. |
| 128 | 128 |
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. |
| 129 | 129 |
func (f *win32File) prepareIo() (*ioOperation, error) {
|
| 130 |
- f.wg.Add(1) |
|
| 131 |
- if f.closing {
|
|
| 130 |
+ if f.closing.isSet() {
|
|
| 132 | 131 |
return nil, ErrFileClosed |
| 133 | 132 |
} |
| 133 |
+ f.wg.Add(1) |
|
| 134 | 134 |
c := &ioOperation{}
|
| 135 | 135 |
c.ch = make(chan ioResult) |
| 136 | 136 |
return c, nil |
| ... | ... |
@@ -159,7 +166,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er |
| 159 | 159 |
return int(bytes), err |
| 160 | 160 |
} |
| 161 | 161 |
|
| 162 |
- if f.closing {
|
|
| 162 |
+ if f.closing.isSet() {
|
|
| 163 | 163 |
cancelIoEx(f.handle, &c.o) |
| 164 | 164 |
} |
| 165 | 165 |
|
| ... | ... |
@@ -175,7 +182,7 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er |
| 175 | 175 |
case r = <-c.ch: |
| 176 | 176 |
err = r.err |
| 177 | 177 |
if err == syscall.ERROR_OPERATION_ABORTED {
|
| 178 |
- if f.closing {
|
|
| 178 |
+ if f.closing.isSet() {
|
|
| 179 | 179 |
err = ErrFileClosed |
| 180 | 180 |
} |
| 181 | 181 |
} |
| 182 | 182 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,82 @@ |
| 0 |
+// +build windows |
|
| 1 |
+ |
|
| 2 |
+package vhd |
|
| 3 |
+ |
|
| 4 |
+import "syscall" |
|
| 5 |
+ |
|
| 6 |
+//go:generate go run mksyscall_windows.go -output zvhd.go vhd.go |
|
| 7 |
+ |
|
| 8 |
+//sys createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.CreateVirtualDisk |
|
| 9 |
+ |
|
| 10 |
+type virtualStorageType struct {
|
|
| 11 |
+ DeviceID uint32 |
|
| 12 |
+ VendorID [16]byte |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+const virtualDiskAccessNONE uint32 = 0 |
|
| 16 |
+const virtualDiskAccessATTACHRO uint32 = 65536 |
|
| 17 |
+const virtualDiskAccessATTACHRW uint32 = 131072 |
|
| 18 |
+const virtualDiskAccessDETACH uint32 = 262144 |
|
| 19 |
+const virtualDiskAccessGETINFO uint32 = 524288 |
|
| 20 |
+const virtualDiskAccessCREATE uint32 = 1048576 |
|
| 21 |
+const virtualDiskAccessMETAOPS uint32 = 2097152 |
|
| 22 |
+const virtualDiskAccessREAD uint32 = 851968 |
|
| 23 |
+const virtualDiskAccessALL uint32 = 4128768 |
|
| 24 |
+const virtualDiskAccessWRITABLE uint32 = 3276800 |
|
| 25 |
+ |
|
| 26 |
+const createVirtualDiskFlagNone uint32 = 0 |
|
| 27 |
+const createVirtualDiskFlagFullPhysicalAllocation uint32 = 1 |
|
| 28 |
+const createVirtualDiskFlagPreventWritesToSourceDisk uint32 = 2 |
|
| 29 |
+const createVirtualDiskFlagDoNotCopyMetadataFromParent uint32 = 4 |
|
| 30 |
+ |
|
| 31 |
+type version2 struct {
|
|
| 32 |
+ UniqueID [16]byte // GUID |
|
| 33 |
+ MaximumSize uint64 |
|
| 34 |
+ BlockSizeInBytes uint32 |
|
| 35 |
+ SectorSizeInBytes uint32 |
|
| 36 |
+ ParentPath *uint16 // string |
|
| 37 |
+ SourcePath *uint16 // string |
|
| 38 |
+ OpenFlags uint32 |
|
| 39 |
+ ParentVirtualStorageType virtualStorageType |
|
| 40 |
+ SourceVirtualStorageType virtualStorageType |
|
| 41 |
+ ResiliencyGUID [16]byte // GUID |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+type createVirtualDiskParameters struct {
|
|
| 45 |
+ Version uint32 // Must always be set to 2 |
|
| 46 |
+ Version2 version2 |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// CreateVhdx will create a simple vhdx file at the given path using default values. |
|
| 50 |
+func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error {
|
|
| 51 |
+ var defaultType virtualStorageType |
|
| 52 |
+ |
|
| 53 |
+ parameters := createVirtualDiskParameters{
|
|
| 54 |
+ Version: 2, |
|
| 55 |
+ Version2: version2{
|
|
| 56 |
+ MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024, |
|
| 57 |
+ BlockSizeInBytes: blockSizeInMb * 1024 * 1024, |
|
| 58 |
+ }, |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ var handle syscall.Handle |
|
| 62 |
+ |
|
| 63 |
+ if err := createVirtualDisk( |
|
| 64 |
+ &defaultType, |
|
| 65 |
+ path, |
|
| 66 |
+ virtualDiskAccessNONE, |
|
| 67 |
+ nil, |
|
| 68 |
+ createVirtualDiskFlagNone, |
|
| 69 |
+ 0, |
|
| 70 |
+ ¶meters, |
|
| 71 |
+ nil, |
|
| 72 |
+ &handle); err != nil {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ if err := syscall.CloseHandle(handle); err != nil {
|
|
| 77 |
+ return err |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ return nil |
|
| 81 |
+} |
| 0 | 82 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,64 @@ |
| 0 |
+// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT |
|
| 1 |
+ |
|
| 2 |
+package vhd |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "syscall" |
|
| 6 |
+ "unsafe" |
|
| 7 |
+ |
|
| 8 |
+ "golang.org/x/sys/windows" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var _ unsafe.Pointer |
|
| 12 |
+ |
|
| 13 |
+// Do the interface allocations only once for common |
|
| 14 |
+// Errno values. |
|
| 15 |
+const ( |
|
| 16 |
+ errnoERROR_IO_PENDING = 997 |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+// errnoErr returns common boxed Errno values, to prevent |
|
| 24 |
+// allocations at runtime. |
|
| 25 |
+func errnoErr(e syscall.Errno) error {
|
|
| 26 |
+ switch e {
|
|
| 27 |
+ case 0: |
|
| 28 |
+ return nil |
|
| 29 |
+ case errnoERROR_IO_PENDING: |
|
| 30 |
+ return errERROR_IO_PENDING |
|
| 31 |
+ } |
|
| 32 |
+ // TODO: add more here, after collecting data on the common |
|
| 33 |
+ // error values see on Windows. (perhaps when running |
|
| 34 |
+ // all.bat?) |
|
| 35 |
+ return e |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+var ( |
|
| 39 |
+ modVirtDisk = windows.NewLazySystemDLL("VirtDisk.dll")
|
|
| 40 |
+ |
|
| 41 |
+ procCreateVirtualDisk = modVirtDisk.NewProc("CreateVirtualDisk")
|
|
| 42 |
+) |
|
| 43 |
+ |
|
| 44 |
+func createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
|
| 45 |
+ var _p0 *uint16 |
|
| 46 |
+ _p0, err = syscall.UTF16PtrFromString(path) |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ return |
|
| 49 |
+ } |
|
| 50 |
+ return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, flags, providerSpecificFlags, parameters, o, handle) |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func _createVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) {
|
|
| 54 |
+ r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(flags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(handle))) |
|
| 55 |
+ if r1 != 0 {
|
|
| 56 |
+ if e1 != 0 {
|
|
| 57 |
+ err = errnoErr(e1) |
|
| 58 |
+ } else {
|
|
| 59 |
+ err = syscall.EINVAL |
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ return |
|
| 63 |
+} |