Browse code

Update hcsshim to v0.6.10

Signed-off-by: Darren Stahl <darst@microsoft.com>

Darren Stahl authored on 2018/05/03 02:15:52
Showing 13 changed files
... ...
@@ -1,6 +1,6 @@
1 1
 # the following lines are in sorted order, FYI
2 2
 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
3
-github.com/Microsoft/hcsshim v0.6.8
3
+github.com/Microsoft/hcsshim v0.6.10
4 4
 github.com/Microsoft/go-winio v0.4.6
5 5
 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
6 6
 github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
... ...
@@ -10,7 +10,7 @@ import (
10 10
 )
11 11
 
12 12
 type baseLayerWriter struct {
13
-	root         string
13
+	root         *os.File
14 14
 	f            *os.File
15 15
 	bw           *winio.BackupFileWriter
16 16
 	err          error
... ...
@@ -26,10 +26,10 @@ type dirInfo struct {
26 26
 // reapplyDirectoryTimes reapplies directory modification, creation, etc. times
27 27
 // after processing of the directory tree has completed. The times are expected
28 28
 // to be ordered such that parent directories come before child directories.
29
-func reapplyDirectoryTimes(dis []dirInfo) error {
29
+func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error {
30 30
 	for i := range dis {
31 31
 		di := &dis[len(dis)-i-1] // reverse order: process child directories first
32
-		f, err := winio.OpenForBackup(di.path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING)
32
+		f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE)
33 33
 		if err != nil {
34 34
 			return err
35 35
 		}
... ...
@@ -75,12 +75,6 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
75 75
 		w.hasUtilityVM = true
76 76
 	}
77 77
 
78
-	path := filepath.Join(w.root, name)
79
-	path, err = makeLongAbsPath(path)
80
-	if err != nil {
81
-		return err
82
-	}
83
-
84 78
 	var f *os.File
85 79
 	defer func() {
86 80
 		if f != nil {
... ...
@@ -88,27 +82,23 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
88 88
 		}
89 89
 	}()
90 90
 
91
-	createmode := uint32(syscall.CREATE_NEW)
91
+	extraFlags := uint32(0)
92 92
 	if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
93
-		err := os.Mkdir(path, 0)
94
-		if err != nil && !os.IsExist(err) {
95
-			return err
96
-		}
97
-		createmode = syscall.OPEN_EXISTING
93
+		extraFlags |= _FILE_DIRECTORY_FILE
98 94
 		if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
99
-			w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo})
95
+			w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo})
100 96
 		}
101 97
 	}
102 98
 
103 99
 	mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
104
-	f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
100
+	f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags)
105 101
 	if err != nil {
106
-		return makeError(err, "Failed to OpenForBackup", path)
102
+		return makeError(err, "Failed to openRelative", name)
107 103
 	}
108 104
 
109 105
 	err = winio.SetFileBasicInfo(f, fileInfo)
110 106
 	if err != nil {
111
-		return makeError(err, "Failed to SetFileBasicInfo", path)
107
+		return makeError(err, "Failed to SetFileBasicInfo", name)
112 108
 	}
113 109
 
114 110
 	w.f = f
... ...
@@ -129,17 +119,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
129 129
 		return err
130 130
 	}
131 131
 
132
-	linkpath, err := makeLongAbsPath(filepath.Join(w.root, name))
133
-	if err != nil {
134
-		return err
135
-	}
136
-
137
-	linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
138
-	if err != nil {
139
-		return err
140
-	}
141
-
142
-	return os.Link(linktarget, linkpath)
132
+	return linkRelative(target, w.root, name, w.root)
143 133
 }
144 134
 
145 135
 func (w *baseLayerWriter) Remove(name string) error {
... ...
@@ -155,6 +135,10 @@ func (w *baseLayerWriter) Write(b []byte) (int, error) {
155 155
 }
156 156
 
157 157
 func (w *baseLayerWriter) Close() error {
158
+	defer func() {
159
+		w.root.Close()
160
+		w.root = nil
161
+	}()
158 162
 	err := w.closeCurrentFile()
159 163
 	if err != nil {
160 164
 		return err
... ...
@@ -162,18 +146,22 @@ func (w *baseLayerWriter) Close() error {
162 162
 	if w.err == nil {
163 163
 		// Restore the file times of all the directories, since they may have
164 164
 		// been modified by creating child directories.
165
-		err = reapplyDirectoryTimes(w.dirInfo)
165
+		err = reapplyDirectoryTimes(w.root, w.dirInfo)
166 166
 		if err != nil {
167 167
 			return err
168 168
 		}
169 169
 
170
-		err = ProcessBaseLayer(w.root)
170
+		err = ProcessBaseLayer(w.root.Name())
171 171
 		if err != nil {
172 172
 			return err
173 173
 		}
174 174
 
175 175
 		if w.hasUtilityVM {
176
-			err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM"))
176
+			err := ensureNotReparsePointRelative("UtilityVM", w.root)
177
+			if err != nil {
178
+				return err
179
+			}
180
+			err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
177 181
 			if err != nil {
178 182
 				return err
179 183
 			}
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"github.com/sirupsen/logrus"
12 12
 )
13 13
 
14
-//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go
14
+//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go
15 15
 
16 16
 //sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
17 17
 //sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
... ...
@@ -1,323 +1,323 @@
1
-package hcsshim
2
-
3
-import (
4
-	"encoding/json"
5
-	"net"
6
-
7
-	"github.com/sirupsen/logrus"
8
-)
9
-
10
-// HNSEndpoint represents a network endpoint in HNS
11
-type HNSEndpoint struct {
12
-	Id                 string            `json:"ID,omitempty"`
13
-	Name               string            `json:",omitempty"`
14
-	VirtualNetwork     string            `json:",omitempty"`
15
-	VirtualNetworkName string            `json:",omitempty"`
16
-	Policies           []json.RawMessage `json:",omitempty"`
17
-	MacAddress         string            `json:",omitempty"`
18
-	IPAddress          net.IP            `json:",omitempty"`
19
-	DNSSuffix          string            `json:",omitempty"`
20
-	DNSServerList      string            `json:",omitempty"`
21
-	GatewayAddress     string            `json:",omitempty"`
22
-	EnableInternalDNS  bool              `json:",omitempty"`
23
-	DisableICC         bool              `json:",omitempty"`
24
-	PrefixLength       uint8             `json:",omitempty"`
25
-	IsRemoteEndpoint   bool              `json:",omitempty"`
26
-}
27
-
28
-//SystemType represents the type of the system on which actions are done
29
-type SystemType string
30
-
31
-// SystemType const
32
-const (
33
-	ContainerType      SystemType = "Container"
34
-	VirtualMachineType SystemType = "VirtualMachine"
35
-	HostType           SystemType = "Host"
36
-)
37
-
38
-// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system
39
-// Supported resource types are Network and Request Types are Add/Remove
40
-type EndpointAttachDetachRequest struct {
41
-	ContainerID    string     `json:"ContainerId,omitempty"`
42
-	SystemType     SystemType `json:"SystemType"`
43
-	CompartmentID  uint16     `json:"CompartmentId,omitempty"`
44
-	VirtualNICName string     `json:"VirtualNicName,omitempty"`
45
-}
46
-
47
-// EndpointResquestResponse is object to get the endpoint request response
48
-type EndpointResquestResponse struct {
49
-	Success bool
50
-	Error   string
51
-}
52
-
53
-// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
54
-func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
55
-	endpoint := &HNSEndpoint{}
56
-	err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
57
-	if err != nil {
58
-		return nil, err
59
-	}
60
-
61
-	return endpoint, nil
62
-}
63
-
64
-// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
65
-func HNSListEndpointRequest() ([]HNSEndpoint, error) {
66
-	var endpoint []HNSEndpoint
67
-	err := hnsCall("GET", "/endpoints/", "", &endpoint)
68
-	if err != nil {
69
-		return nil, err
70
-	}
71
-
72
-	return endpoint, nil
73
-}
74
-
75
-// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
76
-func HotAttachEndpoint(containerID string, endpointID string) error {
77
-	return modifyNetworkEndpoint(containerID, endpointID, Add)
78
-}
79
-
80
-// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
81
-func HotDetachEndpoint(containerID string, endpointID string) error {
82
-	return modifyNetworkEndpoint(containerID, endpointID, Remove)
83
-}
84
-
85
-// ModifyContainer corresponding to the container id, by sending a request
86
-func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
87
-	container, err := OpenContainer(id)
88
-	if err != nil {
89
-		if IsNotExist(err) {
90
-			return ErrComputeSystemDoesNotExist
91
-		}
92
-		return getInnerError(err)
93
-	}
94
-	defer container.Close()
95
-	err = container.Modify(request)
96
-	if err != nil {
97
-		if IsNotSupported(err) {
98
-			return ErrPlatformNotSupported
99
-		}
100
-		return getInnerError(err)
101
-	}
102
-
103
-	return nil
104
-}
105
-
106
-func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
107
-	requestMessage := &ResourceModificationRequestResponse{
108
-		Resource: Network,
109
-		Request:  request,
110
-		Data:     endpointID,
111
-	}
112
-	err := modifyContainer(containerID, requestMessage)
113
-
114
-	if err != nil {
115
-		return err
116
-	}
117
-
118
-	return nil
119
-}
120
-
121
-// GetHNSEndpointByID get the Endpoint by ID
122
-func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
123
-	return HNSEndpointRequest("GET", endpointID, "")
124
-}
125
-
126
-// GetHNSEndpointByName gets the endpoint filtered by Name
127
-func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
128
-	hnsResponse, err := HNSListEndpointRequest()
129
-	if err != nil {
130
-		return nil, err
131
-	}
132
-	for _, hnsEndpoint := range hnsResponse {
133
-		if hnsEndpoint.Name == endpointName {
134
-			return &hnsEndpoint, nil
135
-		}
136
-	}
137
-	return nil, EndpointNotFoundError{EndpointName: endpointName}
138
-}
139
-
140
-// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
141
-func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
142
-	operation := "Create"
143
-	title := "HCSShim::HNSEndpoint::" + operation
144
-	logrus.Debugf(title+" id=%s", endpoint.Id)
145
-
146
-	jsonString, err := json.Marshal(endpoint)
147
-	if err != nil {
148
-		return nil, err
149
-	}
150
-	return HNSEndpointRequest("POST", "", string(jsonString))
151
-}
152
-
153
-// Delete Endpoint by sending EndpointRequest to HNS
154
-func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
155
-	operation := "Delete"
156
-	title := "HCSShim::HNSEndpoint::" + operation
157
-	logrus.Debugf(title+" id=%s", endpoint.Id)
158
-
159
-	return HNSEndpointRequest("DELETE", endpoint.Id, "")
160
-}
161
-
162
-// Update Endpoint
163
-func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
164
-	operation := "Update"
165
-	title := "HCSShim::HNSEndpoint::" + operation
166
-	logrus.Debugf(title+" id=%s", endpoint.Id)
167
-	jsonString, err := json.Marshal(endpoint)
168
-	if err != nil {
169
-		return nil, err
170
-	}
171
-	err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
172
-
173
-	return endpoint, err
174
-}
175
-
176
-// ContainerHotAttach attaches an endpoint to a running container
177
-func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error {
178
-	operation := "ContainerHotAttach"
179
-	title := "HCSShim::HNSEndpoint::" + operation
180
-	logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
181
-
182
-	return modifyNetworkEndpoint(containerID, endpoint.Id, Add)
183
-}
184
-
185
-// ContainerHotDetach detaches an endpoint from a running container
186
-func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error {
187
-	operation := "ContainerHotDetach"
188
-	title := "HCSShim::HNSEndpoint::" + operation
189
-	logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
190
-
191
-	return modifyNetworkEndpoint(containerID, endpoint.Id, Remove)
192
-}
193
-
194
-// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
195
-func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
196
-	operation := "ApplyACLPolicy"
197
-	title := "HCSShim::HNSEndpoint::" + operation
198
-	logrus.Debugf(title+" id=%s", endpoint.Id)
199
-
200
-	for _, policy := range policies {
201
-		if policy == nil {
202
-			continue
203
-		}
204
-		jsonString, err := json.Marshal(policy)
205
-		if err != nil {
206
-			return err
207
-		}
208
-		endpoint.Policies = append(endpoint.Policies, jsonString)
209
-	}
210
-
211
-	_, err := endpoint.Update()
212
-	return err
213
-}
214
-
215
-// ContainerAttach attaches an endpoint to container
216
-func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
217
-	operation := "ContainerAttach"
218
-	title := "HCSShim::HNSEndpoint::" + operation
219
-	logrus.Debugf(title+" id=%s", endpoint.Id)
220
-
221
-	requestMessage := &EndpointAttachDetachRequest{
222
-		ContainerID:   containerID,
223
-		CompartmentID: compartmentID,
224
-		SystemType:    ContainerType,
225
-	}
226
-	response := &EndpointResquestResponse{}
227
-	jsonString, err := json.Marshal(requestMessage)
228
-	if err != nil {
229
-		return err
230
-	}
231
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
232
-}
233
-
234
-// ContainerDetach detaches an endpoint from container
235
-func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
236
-	operation := "ContainerDetach"
237
-	title := "HCSShim::HNSEndpoint::" + operation
238
-	logrus.Debugf(title+" id=%s", endpoint.Id)
239
-
240
-	requestMessage := &EndpointAttachDetachRequest{
241
-		ContainerID: containerID,
242
-		SystemType:  ContainerType,
243
-	}
244
-	response := &EndpointResquestResponse{}
245
-
246
-	jsonString, err := json.Marshal(requestMessage)
247
-	if err != nil {
248
-		return err
249
-	}
250
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
251
-}
252
-
253
-// HostAttach attaches a nic on the host
254
-func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
255
-	operation := "HostAttach"
256
-	title := "HCSShim::HNSEndpoint::" + operation
257
-	logrus.Debugf(title+" id=%s", endpoint.Id)
258
-	requestMessage := &EndpointAttachDetachRequest{
259
-		CompartmentID: compartmentID,
260
-		SystemType:    HostType,
261
-	}
262
-	response := &EndpointResquestResponse{}
263
-
264
-	jsonString, err := json.Marshal(requestMessage)
265
-	if err != nil {
266
-		return err
267
-	}
268
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
269
-
270
-}
271
-
272
-// HostDetach detaches a nic on the host
273
-func (endpoint *HNSEndpoint) HostDetach() error {
274
-	operation := "HostDetach"
275
-	title := "HCSShim::HNSEndpoint::" + operation
276
-	logrus.Debugf(title+" id=%s", endpoint.Id)
277
-	requestMessage := &EndpointAttachDetachRequest{
278
-		SystemType: HostType,
279
-	}
280
-	response := &EndpointResquestResponse{}
281
-
282
-	jsonString, err := json.Marshal(requestMessage)
283
-	if err != nil {
284
-		return err
285
-	}
286
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
287
-}
288
-
289
-// VirtualMachineNICAttach attaches a endpoint to a virtual machine
290
-func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
291
-	operation := "VirtualMachineNicAttach"
292
-	title := "HCSShim::HNSEndpoint::" + operation
293
-	logrus.Debugf(title+" id=%s", endpoint.Id)
294
-	requestMessage := &EndpointAttachDetachRequest{
295
-		VirtualNICName: virtualMachineNICName,
296
-		SystemType:     VirtualMachineType,
297
-	}
298
-	response := &EndpointResquestResponse{}
299
-
300
-	jsonString, err := json.Marshal(requestMessage)
301
-	if err != nil {
302
-		return err
303
-	}
304
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
305
-}
306
-
307
-// VirtualMachineNICDetach detaches a endpoint  from a virtual machine
308
-func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
309
-	operation := "VirtualMachineNicDetach"
310
-	title := "HCSShim::HNSEndpoint::" + operation
311
-	logrus.Debugf(title+" id=%s", endpoint.Id)
312
-
313
-	requestMessage := &EndpointAttachDetachRequest{
314
-		SystemType: VirtualMachineType,
315
-	}
316
-	response := &EndpointResquestResponse{}
317
-
318
-	jsonString, err := json.Marshal(requestMessage)
319
-	if err != nil {
320
-		return err
321
-	}
322
-	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
323
-}
1
+package hcsshim
2
+
3
+import (
4
+	"encoding/json"
5
+	"net"
6
+
7
+	"github.com/sirupsen/logrus"
8
+)
9
+
10
+// HNSEndpoint represents a network endpoint in HNS
11
+type HNSEndpoint struct {
12
+	Id                 string            `json:"ID,omitempty"`
13
+	Name               string            `json:",omitempty"`
14
+	VirtualNetwork     string            `json:",omitempty"`
15
+	VirtualNetworkName string            `json:",omitempty"`
16
+	Policies           []json.RawMessage `json:",omitempty"`
17
+	MacAddress         string            `json:",omitempty"`
18
+	IPAddress          net.IP            `json:",omitempty"`
19
+	DNSSuffix          string            `json:",omitempty"`
20
+	DNSServerList      string            `json:",omitempty"`
21
+	GatewayAddress     string            `json:",omitempty"`
22
+	EnableInternalDNS  bool              `json:",omitempty"`
23
+	DisableICC         bool              `json:",omitempty"`
24
+	PrefixLength       uint8             `json:",omitempty"`
25
+	IsRemoteEndpoint   bool              `json:",omitempty"`
26
+}
27
+
28
+//SystemType represents the type of the system on which actions are done
29
+type SystemType string
30
+
31
+// SystemType const
32
+const (
33
+	ContainerType      SystemType = "Container"
34
+	VirtualMachineType SystemType = "VirtualMachine"
35
+	HostType           SystemType = "Host"
36
+)
37
+
38
+// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system
39
+// Supported resource types are Network and Request Types are Add/Remove
40
+type EndpointAttachDetachRequest struct {
41
+	ContainerID    string     `json:"ContainerId,omitempty"`
42
+	SystemType     SystemType `json:"SystemType"`
43
+	CompartmentID  uint16     `json:"CompartmentId,omitempty"`
44
+	VirtualNICName string     `json:"VirtualNicName,omitempty"`
45
+}
46
+
47
+// EndpointResquestResponse is object to get the endpoint request response
48
+type EndpointResquestResponse struct {
49
+	Success bool
50
+	Error   string
51
+}
52
+
53
+// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
54
+func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
55
+	endpoint := &HNSEndpoint{}
56
+	err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
57
+	if err != nil {
58
+		return nil, err
59
+	}
60
+
61
+	return endpoint, nil
62
+}
63
+
64
+// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
65
+func HNSListEndpointRequest() ([]HNSEndpoint, error) {
66
+	var endpoint []HNSEndpoint
67
+	err := hnsCall("GET", "/endpoints/", "", &endpoint)
68
+	if err != nil {
69
+		return nil, err
70
+	}
71
+
72
+	return endpoint, nil
73
+}
74
+
75
+// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
76
+func HotAttachEndpoint(containerID string, endpointID string) error {
77
+	return modifyNetworkEndpoint(containerID, endpointID, Add)
78
+}
79
+
80
+// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
81
+func HotDetachEndpoint(containerID string, endpointID string) error {
82
+	return modifyNetworkEndpoint(containerID, endpointID, Remove)
83
+}
84
+
85
+// ModifyContainer corresponding to the container id, by sending a request
86
+func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
87
+	container, err := OpenContainer(id)
88
+	if err != nil {
89
+		if IsNotExist(err) {
90
+			return ErrComputeSystemDoesNotExist
91
+		}
92
+		return getInnerError(err)
93
+	}
94
+	defer container.Close()
95
+	err = container.Modify(request)
96
+	if err != nil {
97
+		if IsNotSupported(err) {
98
+			return ErrPlatformNotSupported
99
+		}
100
+		return getInnerError(err)
101
+	}
102
+
103
+	return nil
104
+}
105
+
106
+func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
107
+	requestMessage := &ResourceModificationRequestResponse{
108
+		Resource: Network,
109
+		Request:  request,
110
+		Data:     endpointID,
111
+	}
112
+	err := modifyContainer(containerID, requestMessage)
113
+
114
+	if err != nil {
115
+		return err
116
+	}
117
+
118
+	return nil
119
+}
120
+
121
+// GetHNSEndpointByID get the Endpoint by ID
122
+func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
123
+	return HNSEndpointRequest("GET", endpointID, "")
124
+}
125
+
126
+// GetHNSEndpointByName gets the endpoint filtered by Name
127
+func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
128
+	hnsResponse, err := HNSListEndpointRequest()
129
+	if err != nil {
130
+		return nil, err
131
+	}
132
+	for _, hnsEndpoint := range hnsResponse {
133
+		if hnsEndpoint.Name == endpointName {
134
+			return &hnsEndpoint, nil
135
+		}
136
+	}
137
+	return nil, EndpointNotFoundError{EndpointName: endpointName}
138
+}
139
+
140
+// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
141
+func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
142
+	operation := "Create"
143
+	title := "HCSShim::HNSEndpoint::" + operation
144
+	logrus.Debugf(title+" id=%s", endpoint.Id)
145
+
146
+	jsonString, err := json.Marshal(endpoint)
147
+	if err != nil {
148
+		return nil, err
149
+	}
150
+	return HNSEndpointRequest("POST", "", string(jsonString))
151
+}
152
+
153
+// Delete Endpoint by sending EndpointRequest to HNS
154
+func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
155
+	operation := "Delete"
156
+	title := "HCSShim::HNSEndpoint::" + operation
157
+	logrus.Debugf(title+" id=%s", endpoint.Id)
158
+
159
+	return HNSEndpointRequest("DELETE", endpoint.Id, "")
160
+}
161
+
162
+// Update Endpoint
163
+func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
164
+	operation := "Update"
165
+	title := "HCSShim::HNSEndpoint::" + operation
166
+	logrus.Debugf(title+" id=%s", endpoint.Id)
167
+	jsonString, err := json.Marshal(endpoint)
168
+	if err != nil {
169
+		return nil, err
170
+	}
171
+	err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
172
+
173
+	return endpoint, err
174
+}
175
+
176
+// ContainerHotAttach attaches an endpoint to a running container
177
+func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error {
178
+	operation := "ContainerHotAttach"
179
+	title := "HCSShim::HNSEndpoint::" + operation
180
+	logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
181
+
182
+	return modifyNetworkEndpoint(containerID, endpoint.Id, Add)
183
+}
184
+
185
+// ContainerHotDetach detaches an endpoint from a running container
186
+func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error {
187
+	operation := "ContainerHotDetach"
188
+	title := "HCSShim::HNSEndpoint::" + operation
189
+	logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
190
+
191
+	return modifyNetworkEndpoint(containerID, endpoint.Id, Remove)
192
+}
193
+
194
+// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
195
+func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
196
+	operation := "ApplyACLPolicy"
197
+	title := "HCSShim::HNSEndpoint::" + operation
198
+	logrus.Debugf(title+" id=%s", endpoint.Id)
199
+
200
+	for _, policy := range policies {
201
+		if policy == nil {
202
+			continue
203
+		}
204
+		jsonString, err := json.Marshal(policy)
205
+		if err != nil {
206
+			return err
207
+		}
208
+		endpoint.Policies = append(endpoint.Policies, jsonString)
209
+	}
210
+
211
+	_, err := endpoint.Update()
212
+	return err
213
+}
214
+
215
+// ContainerAttach attaches an endpoint to container
216
+func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
217
+	operation := "ContainerAttach"
218
+	title := "HCSShim::HNSEndpoint::" + operation
219
+	logrus.Debugf(title+" id=%s", endpoint.Id)
220
+
221
+	requestMessage := &EndpointAttachDetachRequest{
222
+		ContainerID:   containerID,
223
+		CompartmentID: compartmentID,
224
+		SystemType:    ContainerType,
225
+	}
226
+	response := &EndpointResquestResponse{}
227
+	jsonString, err := json.Marshal(requestMessage)
228
+	if err != nil {
229
+		return err
230
+	}
231
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
232
+}
233
+
234
+// ContainerDetach detaches an endpoint from container
235
+func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
236
+	operation := "ContainerDetach"
237
+	title := "HCSShim::HNSEndpoint::" + operation
238
+	logrus.Debugf(title+" id=%s", endpoint.Id)
239
+
240
+	requestMessage := &EndpointAttachDetachRequest{
241
+		ContainerID: containerID,
242
+		SystemType:  ContainerType,
243
+	}
244
+	response := &EndpointResquestResponse{}
245
+
246
+	jsonString, err := json.Marshal(requestMessage)
247
+	if err != nil {
248
+		return err
249
+	}
250
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
251
+}
252
+
253
+// HostAttach attaches a nic on the host
254
+func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
255
+	operation := "HostAttach"
256
+	title := "HCSShim::HNSEndpoint::" + operation
257
+	logrus.Debugf(title+" id=%s", endpoint.Id)
258
+	requestMessage := &EndpointAttachDetachRequest{
259
+		CompartmentID: compartmentID,
260
+		SystemType:    HostType,
261
+	}
262
+	response := &EndpointResquestResponse{}
263
+
264
+	jsonString, err := json.Marshal(requestMessage)
265
+	if err != nil {
266
+		return err
267
+	}
268
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
269
+
270
+}
271
+
272
+// HostDetach detaches a nic on the host
273
+func (endpoint *HNSEndpoint) HostDetach() error {
274
+	operation := "HostDetach"
275
+	title := "HCSShim::HNSEndpoint::" + operation
276
+	logrus.Debugf(title+" id=%s", endpoint.Id)
277
+	requestMessage := &EndpointAttachDetachRequest{
278
+		SystemType: HostType,
279
+	}
280
+	response := &EndpointResquestResponse{}
281
+
282
+	jsonString, err := json.Marshal(requestMessage)
283
+	if err != nil {
284
+		return err
285
+	}
286
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
287
+}
288
+
289
+// VirtualMachineNICAttach attaches a endpoint to a virtual machine
290
+func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
291
+	operation := "VirtualMachineNicAttach"
292
+	title := "HCSShim::HNSEndpoint::" + operation
293
+	logrus.Debugf(title+" id=%s", endpoint.Id)
294
+	requestMessage := &EndpointAttachDetachRequest{
295
+		VirtualNICName: virtualMachineNICName,
296
+		SystemType:     VirtualMachineType,
297
+	}
298
+	response := &EndpointResquestResponse{}
299
+
300
+	jsonString, err := json.Marshal(requestMessage)
301
+	if err != nil {
302
+		return err
303
+	}
304
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
305
+}
306
+
307
+// VirtualMachineNICDetach detaches a endpoint  from a virtual machine
308
+func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
309
+	operation := "VirtualMachineNicDetach"
310
+	title := "HCSShim::HNSEndpoint::" + operation
311
+	logrus.Debugf(title+" id=%s", endpoint.Id)
312
+
313
+	requestMessage := &EndpointAttachDetachRequest{
314
+		SystemType: VirtualMachineType,
315
+	}
316
+	response := &EndpointResquestResponse{}
317
+
318
+	jsonString, err := json.Marshal(requestMessage)
319
+	if err != nil {
320
+		return err
321
+	}
322
+	return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
323
+}
... ...
@@ -1,141 +1,141 @@
1
-package hcsshim
2
-
3
-import (
4
-	"encoding/json"
5
-	"net"
6
-
7
-	"github.com/sirupsen/logrus"
8
-)
9
-
10
-// Subnet is assoicated with a network and represents a list
11
-// of subnets available to the network
12
-type Subnet struct {
13
-	AddressPrefix  string            `json:",omitempty"`
14
-	GatewayAddress string            `json:",omitempty"`
15
-	Policies       []json.RawMessage `json:",omitempty"`
16
-}
17
-
18
-// MacPool is assoicated with a network and represents a list
19
-// of macaddresses available to the network
20
-type MacPool struct {
21
-	StartMacAddress string `json:",omitempty"`
22
-	EndMacAddress   string `json:",omitempty"`
23
-}
24
-
25
-// HNSNetwork represents a network in HNS
26
-type HNSNetwork struct {
27
-	Id                   string            `json:"ID,omitempty"`
28
-	Name                 string            `json:",omitempty"`
29
-	Type                 string            `json:",omitempty"`
30
-	NetworkAdapterName   string            `json:",omitempty"`
31
-	SourceMac            string            `json:",omitempty"`
32
-	Policies             []json.RawMessage `json:",omitempty"`
33
-	MacPools             []MacPool         `json:",omitempty"`
34
-	Subnets              []Subnet          `json:",omitempty"`
35
-	DNSSuffix            string            `json:",omitempty"`
36
-	DNSServerList        string            `json:",omitempty"`
37
-	DNSServerCompartment uint32            `json:",omitempty"`
38
-	ManagementIP         string            `json:",omitempty"`
39
-	AutomaticDNS         bool              `json:",omitempty"`
40
-}
41
-
42
-type hnsNetworkResponse struct {
43
-	Success bool
44
-	Error   string
45
-	Output  HNSNetwork
46
-}
47
-
48
-type hnsResponse struct {
49
-	Success bool
50
-	Error   string
51
-	Output  json.RawMessage
52
-}
53
-
54
-// HNSNetworkRequest makes a call into HNS to update/query a single network
55
-func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
56
-	var network HNSNetwork
57
-	err := hnsCall(method, "/networks/"+path, request, &network)
58
-	if err != nil {
59
-		return nil, err
60
-	}
61
-
62
-	return &network, nil
63
-}
64
-
65
-// HNSListNetworkRequest makes a HNS call to query the list of available networks
66
-func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
67
-	var network []HNSNetwork
68
-	err := hnsCall(method, "/networks/"+path, request, &network)
69
-	if err != nil {
70
-		return nil, err
71
-	}
72
-
73
-	return network, nil
74
-}
75
-
76
-// GetHNSNetworkByID
77
-func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
78
-	return HNSNetworkRequest("GET", networkID, "")
79
-}
80
-
81
-// GetHNSNetworkName filtered by Name
82
-func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
83
-	hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
84
-	if err != nil {
85
-		return nil, err
86
-	}
87
-	for _, hnsnetwork := range hsnnetworks {
88
-		if hnsnetwork.Name == networkName {
89
-			return &hnsnetwork, nil
90
-		}
91
-	}
92
-	return nil, NetworkNotFoundError{NetworkName: networkName}
93
-}
94
-
95
-// Create Network by sending NetworkRequest to HNS.
96
-func (network *HNSNetwork) Create() (*HNSNetwork, error) {
97
-	operation := "Create"
98
-	title := "HCSShim::HNSNetwork::" + operation
99
-	logrus.Debugf(title+" id=%s", network.Id)
100
-
101
-	jsonString, err := json.Marshal(network)
102
-	if err != nil {
103
-		return nil, err
104
-	}
105
-	return HNSNetworkRequest("POST", "", string(jsonString))
106
-}
107
-
108
-// Delete Network by sending NetworkRequest to HNS
109
-func (network *HNSNetwork) Delete() (*HNSNetwork, error) {
110
-	operation := "Delete"
111
-	title := "HCSShim::HNSNetwork::" + operation
112
-	logrus.Debugf(title+" id=%s", network.Id)
113
-
114
-	return HNSNetworkRequest("DELETE", network.Id, "")
115
-}
116
-
117
-// Creates an endpoint on the Network.
118
-func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint {
119
-	return &HNSEndpoint{
120
-		VirtualNetwork: network.Id,
121
-		IPAddress:      ipAddress,
122
-		MacAddress:     string(macAddress),
123
-	}
124
-}
125
-
126
-func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
127
-	operation := "CreateEndpoint"
128
-	title := "HCSShim::HNSNetwork::" + operation
129
-	logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id)
130
-
131
-	endpoint.VirtualNetwork = network.Id
132
-	return endpoint.Create()
133
-}
134
-
135
-func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
136
-	operation := "CreateRemoteEndpoint"
137
-	title := "HCSShim::HNSNetwork::" + operation
138
-	logrus.Debugf(title+" id=%s", network.Id)
139
-	endpoint.IsRemoteEndpoint = true
140
-	return network.CreateEndpoint(endpoint)
141
-}
1
+package hcsshim
2
+
3
+import (
4
+	"encoding/json"
5
+	"net"
6
+
7
+	"github.com/sirupsen/logrus"
8
+)
9
+
10
+// Subnet is assoicated with a network and represents a list
11
+// of subnets available to the network
12
+type Subnet struct {
13
+	AddressPrefix  string            `json:",omitempty"`
14
+	GatewayAddress string            `json:",omitempty"`
15
+	Policies       []json.RawMessage `json:",omitempty"`
16
+}
17
+
18
+// MacPool is assoicated with a network and represents a list
19
+// of macaddresses available to the network
20
+type MacPool struct {
21
+	StartMacAddress string `json:",omitempty"`
22
+	EndMacAddress   string `json:",omitempty"`
23
+}
24
+
25
+// HNSNetwork represents a network in HNS
26
+type HNSNetwork struct {
27
+	Id                   string            `json:"ID,omitempty"`
28
+	Name                 string            `json:",omitempty"`
29
+	Type                 string            `json:",omitempty"`
30
+	NetworkAdapterName   string            `json:",omitempty"`
31
+	SourceMac            string            `json:",omitempty"`
32
+	Policies             []json.RawMessage `json:",omitempty"`
33
+	MacPools             []MacPool         `json:",omitempty"`
34
+	Subnets              []Subnet          `json:",omitempty"`
35
+	DNSSuffix            string            `json:",omitempty"`
36
+	DNSServerList        string            `json:",omitempty"`
37
+	DNSServerCompartment uint32            `json:",omitempty"`
38
+	ManagementIP         string            `json:",omitempty"`
39
+	AutomaticDNS         bool              `json:",omitempty"`
40
+}
41
+
42
+type hnsNetworkResponse struct {
43
+	Success bool
44
+	Error   string
45
+	Output  HNSNetwork
46
+}
47
+
48
+type hnsResponse struct {
49
+	Success bool
50
+	Error   string
51
+	Output  json.RawMessage
52
+}
53
+
54
+// HNSNetworkRequest makes a call into HNS to update/query a single network
55
+func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
56
+	var network HNSNetwork
57
+	err := hnsCall(method, "/networks/"+path, request, &network)
58
+	if err != nil {
59
+		return nil, err
60
+	}
61
+
62
+	return &network, nil
63
+}
64
+
65
+// HNSListNetworkRequest makes a HNS call to query the list of available networks
66
+func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
67
+	var network []HNSNetwork
68
+	err := hnsCall(method, "/networks/"+path, request, &network)
69
+	if err != nil {
70
+		return nil, err
71
+	}
72
+
73
+	return network, nil
74
+}
75
+
76
+// GetHNSNetworkByID
77
+func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
78
+	return HNSNetworkRequest("GET", networkID, "")
79
+}
80
+
81
+// GetHNSNetworkName filtered by Name
82
+func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
83
+	hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	for _, hnsnetwork := range hsnnetworks {
88
+		if hnsnetwork.Name == networkName {
89
+			return &hnsnetwork, nil
90
+		}
91
+	}
92
+	return nil, NetworkNotFoundError{NetworkName: networkName}
93
+}
94
+
95
+// Create Network by sending NetworkRequest to HNS.
96
+func (network *HNSNetwork) Create() (*HNSNetwork, error) {
97
+	operation := "Create"
98
+	title := "HCSShim::HNSNetwork::" + operation
99
+	logrus.Debugf(title+" id=%s", network.Id)
100
+
101
+	jsonString, err := json.Marshal(network)
102
+	if err != nil {
103
+		return nil, err
104
+	}
105
+	return HNSNetworkRequest("POST", "", string(jsonString))
106
+}
107
+
108
+// Delete Network by sending NetworkRequest to HNS
109
+func (network *HNSNetwork) Delete() (*HNSNetwork, error) {
110
+	operation := "Delete"
111
+	title := "HCSShim::HNSNetwork::" + operation
112
+	logrus.Debugf(title+" id=%s", network.Id)
113
+
114
+	return HNSNetworkRequest("DELETE", network.Id, "")
115
+}
116
+
117
+// Creates an endpoint on the Network.
118
+func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint {
119
+	return &HNSEndpoint{
120
+		VirtualNetwork: network.Id,
121
+		IPAddress:      ipAddress,
122
+		MacAddress:     string(macAddress),
123
+	}
124
+}
125
+
126
+func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
127
+	operation := "CreateEndpoint"
128
+	title := "HCSShim::HNSNetwork::" + operation
129
+	logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id)
130
+
131
+	endpoint.VirtualNetwork = network.Id
132
+	return endpoint.Create()
133
+}
134
+
135
+func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
136
+	operation := "CreateRemoteEndpoint"
137
+	title := "HCSShim::HNSNetwork::" + operation
138
+	logrus.Debugf(title+" id=%s", network.Id)
139
+	endpoint.IsRemoteEndpoint = true
140
+	return network.CreateEndpoint(endpoint)
141
+}
... ...
@@ -1,94 +1,94 @@
1
-package hcsshim
2
-
3
-// Type of Request Support in ModifySystem
4
-type PolicyType string
5
-
6
-// RequestType const
7
-const (
8
-	Nat                  PolicyType = "NAT"
9
-	ACL                  PolicyType = "ACL"
10
-	PA                   PolicyType = "PA"
11
-	VLAN                 PolicyType = "VLAN"
12
-	VSID                 PolicyType = "VSID"
13
-	VNet                 PolicyType = "VNET"
14
-	L2Driver             PolicyType = "L2Driver"
15
-	Isolation            PolicyType = "Isolation"
16
-	QOS                  PolicyType = "QOS"
17
-	OutboundNat          PolicyType = "OutBoundNAT"
18
-	ExternalLoadBalancer PolicyType = "ELB"
19
-	Route                PolicyType = "ROUTE"
20
-)
21
-
22
-type NatPolicy struct {
23
-	Type         PolicyType `json:"Type"`
24
-	Protocol     string
25
-	InternalPort uint16
26
-	ExternalPort uint16
27
-}
28
-
29
-type QosPolicy struct {
30
-	Type                            PolicyType `json:"Type"`
31
-	MaximumOutgoingBandwidthInBytes uint64
32
-}
33
-
34
-type IsolationPolicy struct {
35
-	Type               PolicyType `json:"Type"`
36
-	VLAN               uint
37
-	VSID               uint
38
-	InDefaultIsolation bool
39
-}
40
-
41
-type VlanPolicy struct {
42
-	Type PolicyType `json:"Type"`
43
-	VLAN uint
44
-}
45
-
46
-type VsidPolicy struct {
47
-	Type PolicyType `json:"Type"`
48
-	VSID uint
49
-}
50
-
51
-type PaPolicy struct {
52
-	Type PolicyType `json:"Type"`
53
-	PA   string     `json:"PA"`
54
-}
55
-
56
-type OutboundNatPolicy struct {
57
-	Policy
58
-	VIP        string   `json:"VIP,omitempty"`
59
-	Exceptions []string `json:"ExceptionList,omitempty"`
60
-}
61
-
62
-type ActionType string
63
-type DirectionType string
64
-type RuleType string
65
-
66
-const (
67
-	Allow ActionType = "Allow"
68
-	Block ActionType = "Block"
69
-
70
-	In  DirectionType = "In"
71
-	Out DirectionType = "Out"
72
-
73
-	Host   RuleType = "Host"
74
-	Switch RuleType = "Switch"
75
-)
76
-
77
-type ACLPolicy struct {
78
-	Type            PolicyType `json:"Type"`
79
-	Protocol        uint16
80
-	InternalPort    uint16
81
-	Action          ActionType
82
-	Direction       DirectionType
83
-	LocalAddresses  string
84
-	RemoteAddresses string
85
-	LocalPort       uint16
86
-	RemotePort      uint16
87
-	RuleType        RuleType `json:"RuleType,omitempty"`
88
-	Priority        uint16
89
-	ServiceName     string
90
-}
91
-
92
-type Policy struct {
93
-	Type PolicyType `json:"Type"`
94
-}
1
+package hcsshim
2
+
3
+// Type of Request Support in ModifySystem
4
+type PolicyType string
5
+
6
+// RequestType const
7
+const (
8
+	Nat                  PolicyType = "NAT"
9
+	ACL                  PolicyType = "ACL"
10
+	PA                   PolicyType = "PA"
11
+	VLAN                 PolicyType = "VLAN"
12
+	VSID                 PolicyType = "VSID"
13
+	VNet                 PolicyType = "VNET"
14
+	L2Driver             PolicyType = "L2Driver"
15
+	Isolation            PolicyType = "Isolation"
16
+	QOS                  PolicyType = "QOS"
17
+	OutboundNat          PolicyType = "OutBoundNAT"
18
+	ExternalLoadBalancer PolicyType = "ELB"
19
+	Route                PolicyType = "ROUTE"
20
+)
21
+
22
+type NatPolicy struct {
23
+	Type         PolicyType `json:"Type"`
24
+	Protocol     string
25
+	InternalPort uint16
26
+	ExternalPort uint16
27
+}
28
+
29
+type QosPolicy struct {
30
+	Type                            PolicyType `json:"Type"`
31
+	MaximumOutgoingBandwidthInBytes uint64
32
+}
33
+
34
+type IsolationPolicy struct {
35
+	Type               PolicyType `json:"Type"`
36
+	VLAN               uint
37
+	VSID               uint
38
+	InDefaultIsolation bool
39
+}
40
+
41
+type VlanPolicy struct {
42
+	Type PolicyType `json:"Type"`
43
+	VLAN uint
44
+}
45
+
46
+type VsidPolicy struct {
47
+	Type PolicyType `json:"Type"`
48
+	VSID uint
49
+}
50
+
51
+type PaPolicy struct {
52
+	Type PolicyType `json:"Type"`
53
+	PA   string     `json:"PA"`
54
+}
55
+
56
+type OutboundNatPolicy struct {
57
+	Policy
58
+	VIP        string   `json:"VIP,omitempty"`
59
+	Exceptions []string `json:"ExceptionList,omitempty"`
60
+}
61
+
62
+type ActionType string
63
+type DirectionType string
64
+type RuleType string
65
+
66
+const (
67
+	Allow ActionType = "Allow"
68
+	Block ActionType = "Block"
69
+
70
+	In  DirectionType = "In"
71
+	Out DirectionType = "Out"
72
+
73
+	Host   RuleType = "Host"
74
+	Switch RuleType = "Switch"
75
+)
76
+
77
+type ACLPolicy struct {
78
+	Type            PolicyType `json:"Type"`
79
+	Protocol        uint16
80
+	InternalPort    uint16
81
+	Action          ActionType
82
+	Direction       DirectionType
83
+	LocalAddresses  string
84
+	RemoteAddresses string
85
+	LocalPort       uint16
86
+	RemotePort      uint16
87
+	RuleType        RuleType `json:"RuleType,omitempty"`
88
+	Priority        uint16
89
+	ServiceName     string
90
+}
91
+
92
+type Policy struct {
93
+	Type PolicyType `json:"Type"`
94
+}
... ...
@@ -1,200 +1,200 @@
1
-package hcsshim
2
-
3
-import (
4
-	"encoding/json"
5
-
6
-	"github.com/sirupsen/logrus"
7
-)
8
-
9
-// RoutePolicy is a structure defining schema for Route based Policy
10
-type RoutePolicy struct {
11
-	Policy
12
-	DestinationPrefix string `json:"DestinationPrefix,omitempty"`
13
-	NextHop           string `json:"NextHop,omitempty"`
14
-	EncapEnabled      bool   `json:"NeedEncap,omitempty"`
15
-}
16
-
17
-// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
18
-type ELBPolicy struct {
19
-	LBPolicy
20
-	SourceVIP string   `json:"SourceVIP,omitempty"`
21
-	VIPs      []string `json:"VIPs,omitempty"`
22
-	ILB       bool     `json:"ILB,omitempty"`
23
-}
24
-
25
-// LBPolicy is a structure defining schema for LoadBalancing based Policy
26
-type LBPolicy struct {
27
-	Policy
28
-	Protocol     uint16 `json:"Protocol,omitempty"`
29
-	InternalPort uint16
30
-	ExternalPort uint16
31
-}
32
-
33
-// PolicyList is a structure defining schema for Policy list request
34
-type PolicyList struct {
35
-	ID                 string            `json:"ID,omitempty"`
36
-	EndpointReferences []string          `json:"References,omitempty"`
37
-	Policies           []json.RawMessage `json:"Policies,omitempty"`
38
-}
39
-
40
-// HNSPolicyListRequest makes a call into HNS to update/query a single network
41
-func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
42
-	var policy PolicyList
43
-	err := hnsCall(method, "/policylists/"+path, request, &policy)
44
-	if err != nil {
45
-		return nil, err
46
-	}
47
-
48
-	return &policy, nil
49
-}
50
-
51
-// HNSListPolicyListRequest gets all the policy list
52
-func HNSListPolicyListRequest() ([]PolicyList, error) {
53
-	var plist []PolicyList
54
-	err := hnsCall("GET", "/policylists/", "", &plist)
55
-	if err != nil {
56
-		return nil, err
57
-	}
58
-
59
-	return plist, nil
60
-}
61
-
62
-// PolicyListRequest makes a HNS call to modify/query a network policy list
63
-func PolicyListRequest(method, path, request string) (*PolicyList, error) {
64
-	policylist := &PolicyList{}
65
-	err := hnsCall(method, "/policylists/"+path, request, &policylist)
66
-	if err != nil {
67
-		return nil, err
68
-	}
69
-
70
-	return policylist, nil
71
-}
72
-
73
-// GetPolicyListByID get the policy list by ID
74
-func GetPolicyListByID(policyListID string) (*PolicyList, error) {
75
-	return PolicyListRequest("GET", policyListID, "")
76
-}
77
-
78
-// Create PolicyList by sending PolicyListRequest to HNS.
79
-func (policylist *PolicyList) Create() (*PolicyList, error) {
80
-	operation := "Create"
81
-	title := "HCSShim::PolicyList::" + operation
82
-	logrus.Debugf(title+" id=%s", policylist.ID)
83
-	jsonString, err := json.Marshal(policylist)
84
-	if err != nil {
85
-		return nil, err
86
-	}
87
-	return PolicyListRequest("POST", "", string(jsonString))
88
-}
89
-
90
-// Delete deletes PolicyList
91
-func (policylist *PolicyList) Delete() (*PolicyList, error) {
92
-	operation := "Delete"
93
-	title := "HCSShim::PolicyList::" + operation
94
-	logrus.Debugf(title+" id=%s", policylist.ID)
95
-
96
-	return PolicyListRequest("DELETE", policylist.ID, "")
97
-}
98
-
99
-// AddEndpoint add an endpoint to a Policy List
100
-func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
101
-	operation := "AddEndpoint"
102
-	title := "HCSShim::PolicyList::" + operation
103
-	logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
104
-
105
-	_, err := policylist.Delete()
106
-	if err != nil {
107
-		return nil, err
108
-	}
109
-
110
-	// Add Endpoint to the Existing List
111
-	policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
112
-
113
-	return policylist.Create()
114
-}
115
-
116
-// RemoveEndpoint removes an endpoint from the Policy List
117
-func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
118
-	operation := "RemoveEndpoint"
119
-	title := "HCSShim::PolicyList::" + operation
120
-	logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
121
-
122
-	_, err := policylist.Delete()
123
-	if err != nil {
124
-		return nil, err
125
-	}
126
-
127
-	elementToRemove := "/endpoints/" + endpoint.Id
128
-
129
-	var references []string
130
-
131
-	for _, endpointReference := range policylist.EndpointReferences {
132
-		if endpointReference == elementToRemove {
133
-			continue
134
-		}
135
-		references = append(references, endpointReference)
136
-	}
137
-	policylist.EndpointReferences = references
138
-	return policylist.Create()
139
-}
140
-
141
-// AddLoadBalancer policy list for the specified endpoints
142
-func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
143
-	operation := "AddLoadBalancer"
144
-	title := "HCSShim::PolicyList::" + operation
145
-	logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort)
146
-
147
-	policylist := &PolicyList{}
148
-
149
-	elbPolicy := &ELBPolicy{
150
-		SourceVIP: sourceVIP,
151
-		ILB:       isILB,
152
-	}
153
-
154
-	if len(vip) > 0 {
155
-		elbPolicy.VIPs = []string{vip}
156
-	}
157
-	elbPolicy.Type = ExternalLoadBalancer
158
-	elbPolicy.Protocol = protocol
159
-	elbPolicy.InternalPort = internalPort
160
-	elbPolicy.ExternalPort = externalPort
161
-
162
-	for _, endpoint := range endpoints {
163
-		policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
164
-	}
165
-
166
-	jsonString, err := json.Marshal(elbPolicy)
167
-	if err != nil {
168
-		return nil, err
169
-	}
170
-	policylist.Policies = append(policylist.Policies, jsonString)
171
-	return policylist.Create()
172
-}
173
-
174
-// AddRoute adds route policy list for the specified endpoints
175
-func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
176
-	operation := "AddRoute"
177
-	title := "HCSShim::PolicyList::" + operation
178
-	logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix)
179
-
180
-	policylist := &PolicyList{}
181
-
182
-	rPolicy := &RoutePolicy{
183
-		DestinationPrefix: destinationPrefix,
184
-		NextHop:           nextHop,
185
-		EncapEnabled:      encapEnabled,
186
-	}
187
-	rPolicy.Type = Route
188
-
189
-	for _, endpoint := range endpoints {
190
-		policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
191
-	}
192
-
193
-	jsonString, err := json.Marshal(rPolicy)
194
-	if err != nil {
195
-		return nil, err
196
-	}
197
-
198
-	policylist.Policies = append(policylist.Policies, jsonString)
199
-	return policylist.Create()
200
-}
1
+package hcsshim
2
+
3
+import (
4
+	"encoding/json"
5
+
6
+	"github.com/sirupsen/logrus"
7
+)
8
+
9
+// RoutePolicy is a structure defining schema for Route based Policy
10
+type RoutePolicy struct {
11
+	Policy
12
+	DestinationPrefix string `json:"DestinationPrefix,omitempty"`
13
+	NextHop           string `json:"NextHop,omitempty"`
14
+	EncapEnabled      bool   `json:"NeedEncap,omitempty"`
15
+}
16
+
17
+// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
18
+type ELBPolicy struct {
19
+	LBPolicy
20
+	SourceVIP string   `json:"SourceVIP,omitempty"`
21
+	VIPs      []string `json:"VIPs,omitempty"`
22
+	ILB       bool     `json:"ILB,omitempty"`
23
+}
24
+
25
+// LBPolicy is a structure defining schema for LoadBalancing based Policy
26
+type LBPolicy struct {
27
+	Policy
28
+	Protocol     uint16 `json:"Protocol,omitempty"`
29
+	InternalPort uint16
30
+	ExternalPort uint16
31
+}
32
+
33
+// PolicyList is a structure defining schema for Policy list request
34
+type PolicyList struct {
35
+	ID                 string            `json:"ID,omitempty"`
36
+	EndpointReferences []string          `json:"References,omitempty"`
37
+	Policies           []json.RawMessage `json:"Policies,omitempty"`
38
+}
39
+
40
+// HNSPolicyListRequest makes a call into HNS to update/query a single network
41
+func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
42
+	var policy PolicyList
43
+	err := hnsCall(method, "/policylists/"+path, request, &policy)
44
+	if err != nil {
45
+		return nil, err
46
+	}
47
+
48
+	return &policy, nil
49
+}
50
+
51
+// HNSListPolicyListRequest gets all the policy list
52
+func HNSListPolicyListRequest() ([]PolicyList, error) {
53
+	var plist []PolicyList
54
+	err := hnsCall("GET", "/policylists/", "", &plist)
55
+	if err != nil {
56
+		return nil, err
57
+	}
58
+
59
+	return plist, nil
60
+}
61
+
62
+// PolicyListRequest makes a HNS call to modify/query a network policy list
63
+func PolicyListRequest(method, path, request string) (*PolicyList, error) {
64
+	policylist := &PolicyList{}
65
+	err := hnsCall(method, "/policylists/"+path, request, &policylist)
66
+	if err != nil {
67
+		return nil, err
68
+	}
69
+
70
+	return policylist, nil
71
+}
72
+
73
+// GetPolicyListByID get the policy list by ID
74
+func GetPolicyListByID(policyListID string) (*PolicyList, error) {
75
+	return PolicyListRequest("GET", policyListID, "")
76
+}
77
+
78
+// Create PolicyList by sending PolicyListRequest to HNS.
79
+func (policylist *PolicyList) Create() (*PolicyList, error) {
80
+	operation := "Create"
81
+	title := "HCSShim::PolicyList::" + operation
82
+	logrus.Debugf(title+" id=%s", policylist.ID)
83
+	jsonString, err := json.Marshal(policylist)
84
+	if err != nil {
85
+		return nil, err
86
+	}
87
+	return PolicyListRequest("POST", "", string(jsonString))
88
+}
89
+
90
+// Delete deletes PolicyList
91
+func (policylist *PolicyList) Delete() (*PolicyList, error) {
92
+	operation := "Delete"
93
+	title := "HCSShim::PolicyList::" + operation
94
+	logrus.Debugf(title+" id=%s", policylist.ID)
95
+
96
+	return PolicyListRequest("DELETE", policylist.ID, "")
97
+}
98
+
99
+// AddEndpoint add an endpoint to a Policy List
100
+func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
101
+	operation := "AddEndpoint"
102
+	title := "HCSShim::PolicyList::" + operation
103
+	logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
104
+
105
+	_, err := policylist.Delete()
106
+	if err != nil {
107
+		return nil, err
108
+	}
109
+
110
+	// Add Endpoint to the Existing List
111
+	policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
112
+
113
+	return policylist.Create()
114
+}
115
+
116
+// RemoveEndpoint removes an endpoint from the Policy List
117
+func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
118
+	operation := "RemoveEndpoint"
119
+	title := "HCSShim::PolicyList::" + operation
120
+	logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
121
+
122
+	_, err := policylist.Delete()
123
+	if err != nil {
124
+		return nil, err
125
+	}
126
+
127
+	elementToRemove := "/endpoints/" + endpoint.Id
128
+
129
+	var references []string
130
+
131
+	for _, endpointReference := range policylist.EndpointReferences {
132
+		if endpointReference == elementToRemove {
133
+			continue
134
+		}
135
+		references = append(references, endpointReference)
136
+	}
137
+	policylist.EndpointReferences = references
138
+	return policylist.Create()
139
+}
140
+
141
+// AddLoadBalancer policy list for the specified endpoints
142
+func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
143
+	operation := "AddLoadBalancer"
144
+	title := "HCSShim::PolicyList::" + operation
145
+	logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort)
146
+
147
+	policylist := &PolicyList{}
148
+
149
+	elbPolicy := &ELBPolicy{
150
+		SourceVIP: sourceVIP,
151
+		ILB:       isILB,
152
+	}
153
+
154
+	if len(vip) > 0 {
155
+		elbPolicy.VIPs = []string{vip}
156
+	}
157
+	elbPolicy.Type = ExternalLoadBalancer
158
+	elbPolicy.Protocol = protocol
159
+	elbPolicy.InternalPort = internalPort
160
+	elbPolicy.ExternalPort = externalPort
161
+
162
+	for _, endpoint := range endpoints {
163
+		policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
164
+	}
165
+
166
+	jsonString, err := json.Marshal(elbPolicy)
167
+	if err != nil {
168
+		return nil, err
169
+	}
170
+	policylist.Policies = append(policylist.Policies, jsonString)
171
+	return policylist.Create()
172
+}
173
+
174
+// AddRoute adds route policy list for the specified endpoints
175
+func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
176
+	operation := "AddRoute"
177
+	title := "HCSShim::PolicyList::" + operation
178
+	logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix)
179
+
180
+	policylist := &PolicyList{}
181
+
182
+	rPolicy := &RoutePolicy{
183
+		DestinationPrefix: destinationPrefix,
184
+		NextHop:           nextHop,
185
+		EncapEnabled:      encapEnabled,
186
+	}
187
+	rPolicy.Type = Route
188
+
189
+	for _, endpoint := range endpoints {
190
+		policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
191
+	}
192
+
193
+	jsonString, err := json.Marshal(rPolicy)
194
+	if err != nil {
195
+		return nil, err
196
+	}
197
+
198
+	policylist.Policies = append(policylist.Policies, jsonString)
199
+	return policylist.Create()
200
+}
... ...
@@ -129,37 +129,39 @@ type legacyLayerWriterWrapper struct {
129 129
 }
130 130
 
131 131
 func (r *legacyLayerWriterWrapper) Close() error {
132
-	defer os.RemoveAll(r.root)
132
+	defer os.RemoveAll(r.root.Name())
133
+	defer r.legacyLayerWriter.CloseRoots()
133 134
 	err := r.legacyLayerWriter.Close()
134 135
 	if err != nil {
135 136
 		return err
136 137
 	}
137 138
 
138
-	// Use the original path here because ImportLayer does not support long paths for the source in TP5.
139
-	// But do use a long path for the destination to work around another bug with directories
140
-	// with MAX_PATH - 12 < length < MAX_PATH.
141 139
 	info := r.info
142
-	fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID))
143
-	if err != nil {
144
-		return err
145
-	}
146
-
147 140
 	info.HomeDir = ""
148
-	if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil {
141
+	if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
149 142
 		return err
150 143
 	}
144
+	for _, name := range r.Tombstones {
145
+		if err = removeRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) {
146
+			return err
147
+		}
148
+	}
151 149
 	// Add any hard links that were collected.
152 150
 	for _, lnk := range r.PendingLinks {
153
-		if err = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) {
151
+		if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
154 152
 			return err
155 153
 		}
156
-		if err = os.Link(lnk.Target, lnk.Path); err != nil {
154
+		if err = linkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
157 155
 			return err
158 156
 		}
159 157
 	}
160 158
 	// Prepare the utility VM for use if one is present in the layer.
161 159
 	if r.HasUtilityVM {
162
-		err = ProcessUtilityVMImage(filepath.Join(fullPath, "UtilityVM"))
160
+		err := ensureNotReparsePointRelative("UtilityVM", r.destRoot)
161
+		if err != nil {
162
+			return err
163
+		}
164
+		err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
163 165
 		if err != nil {
164 166
 			return err
165 167
 		}
... ...
@@ -173,8 +175,12 @@ func (r *legacyLayerWriterWrapper) Close() error {
173 173
 func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
174 174
 	if len(parentLayerPaths) == 0 {
175 175
 		// This is a base layer. It gets imported differently.
176
+		f, err := openRoot(filepath.Join(info.HomeDir, layerID))
177
+		if err != nil {
178
+			return nil, err
179
+		}
176 180
 		return &baseLayerWriter{
177
-			root: filepath.Join(info.HomeDir, layerID),
181
+			root: f,
178 182
 		}, nil
179 183
 	}
180 184
 
... ...
@@ -185,8 +191,12 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string)
185 185
 		if err != nil {
186 186
 			return nil, err
187 187
 		}
188
+		w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID))
189
+		if err != nil {
190
+			return nil, err
191
+		}
188 192
 		return &legacyLayerWriterWrapper{
189
-			legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)),
193
+			legacyLayerWriter: w,
190 194
 			info:              info,
191 195
 			layerID:           layerID,
192 196
 			path:              path,
... ...
@@ -127,7 +127,7 @@ func (r *legacyLayerReader) walkUntilCancelled() error {
127 127
 		// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
128 128
 		// which is called by filepath.Walk will fail when a filename contains
129 129
 		// unicode characters. Skip the recycle bin regardless which is goodness.
130
-		if strings.HasPrefix(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) {
130
+		if path == filepath.Join(r.root, `Files\$Recycle.Bin`) && info.IsDir() {
131 131
 			return filepath.SkipDir
132 132
 		}
133 133
 
... ...
@@ -336,59 +336,79 @@ func (r *legacyLayerReader) Close() error {
336 336
 
337 337
 type pendingLink struct {
338 338
 	Path, Target string
339
+	TargetRoot   *os.File
340
+}
341
+
342
+type pendingDir struct {
343
+	Path string
344
+	Root *os.File
339 345
 }
340 346
 
341 347
 type legacyLayerWriter struct {
342
-	root         string
343
-	parentRoots  []string
344
-	destRoot     string
345
-	currentFile  *os.File
346
-	backupWriter *winio.BackupFileWriter
347
-	tombstones   []string
348
-	pathFixed    bool
349
-	HasUtilityVM bool
350
-	uvmDi        []dirInfo
351
-	addedFiles   map[string]bool
352
-	PendingLinks []pendingLink
348
+	root            *os.File
349
+	destRoot        *os.File
350
+	parentRoots     []*os.File
351
+	currentFile     *os.File
352
+	currentFileName string
353
+	currentFileRoot *os.File
354
+	backupWriter    *winio.BackupFileWriter
355
+	Tombstones      []string
356
+	HasUtilityVM    bool
357
+	uvmDi           []dirInfo
358
+	addedFiles      map[string]bool
359
+	PendingLinks    []pendingLink
360
+	pendingDirs     []pendingDir
361
+	currentIsDir    bool
353 362
 }
354 363
 
355 364
 // newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
356 365
 // transport format to disk.
357
-func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) *legacyLayerWriter {
358
-	return &legacyLayerWriter{
359
-		root:        root,
360
-		parentRoots: parentRoots,
361
-		destRoot:    destRoot,
362
-		addedFiles:  make(map[string]bool),
366
+func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
367
+	w = &legacyLayerWriter{
368
+		addedFiles: make(map[string]bool),
363 369
 	}
364
-}
365
-
366
-func (w *legacyLayerWriter) init() error {
367
-	if !w.pathFixed {
368
-		path, err := makeLongAbsPath(w.root)
370
+	defer func() {
369 371
 		if err != nil {
370
-			return err
372
+			w.CloseRoots()
373
+			w = nil
371 374
 		}
372
-		for i, p := range w.parentRoots {
373
-			w.parentRoots[i], err = makeLongAbsPath(p)
374
-			if err != nil {
375
-				return err
376
-			}
377
-		}
378
-		destPath, err := makeLongAbsPath(w.destRoot)
375
+	}()
376
+	w.root, err = openRoot(root)
377
+	if err != nil {
378
+		return
379
+	}
380
+	w.destRoot, err = openRoot(destRoot)
381
+	if err != nil {
382
+		return
383
+	}
384
+	for _, r := range parentRoots {
385
+		f, err := openRoot(r)
379 386
 		if err != nil {
380
-			return err
387
+			return w, err
381 388
 		}
382
-		w.root = path
383
-		w.destRoot = destPath
384
-		w.pathFixed = true
389
+		w.parentRoots = append(w.parentRoots, f)
385 390
 	}
386
-	return nil
391
+	return
392
+}
393
+
394
+func (w *legacyLayerWriter) CloseRoots() {
395
+	if w.root != nil {
396
+		w.root.Close()
397
+		w.root = nil
398
+	}
399
+	if w.destRoot != nil {
400
+		w.destRoot.Close()
401
+		w.destRoot = nil
402
+	}
403
+	for i := range w.parentRoots {
404
+		w.parentRoots[i].Close()
405
+	}
406
+	w.parentRoots = nil
387 407
 }
388 408
 
389 409
 func (w *legacyLayerWriter) initUtilityVM() error {
390 410
 	if !w.HasUtilityVM {
391
-		err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0)
411
+		err := mkdirRelative(utilityVMPath, w.destRoot)
392 412
 		if err != nil {
393 413
 			return err
394 414
 		}
... ...
@@ -396,7 +416,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
396 396
 		// clone the utility VM from the parent layer into this layer. Use hard
397 397
 		// links to avoid unnecessary copying, since most of the files are
398 398
 		// immutable.
399
-		err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles)
399
+		err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
400 400
 		if err != nil {
401 401
 			return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
402 402
 		}
... ...
@@ -405,7 +425,40 @@ func (w *legacyLayerWriter) initUtilityVM() error {
405 405
 	return nil
406 406
 }
407 407
 
408
-func (w *legacyLayerWriter) reset() {
408
+func (w *legacyLayerWriter) reset() error {
409
+	if w.currentIsDir {
410
+		r := w.currentFile
411
+		br := winio.NewBackupStreamReader(r)
412
+		// Seek to the beginning of the backup stream, skipping the fileattrs
413
+		if _, err := r.Seek(4, io.SeekStart); err != nil {
414
+			return err
415
+		}
416
+
417
+		for {
418
+			bhdr, err := br.Next()
419
+			if err == io.EOF {
420
+				// end of backupstream data
421
+				break
422
+			}
423
+			if err != nil {
424
+				return err
425
+			}
426
+			switch bhdr.Id {
427
+			case winio.BackupReparseData:
428
+				// The current file is a `.$wcidirs$` metadata file that
429
+				// describes a directory reparse point. Delete the placeholder
430
+				// directory to prevent future files being added into the
431
+				// destination of the reparse point during the ImportLayer call
432
+				if err := removeRelative(w.currentFileName, w.currentFileRoot); err != nil {
433
+					return err
434
+				}
435
+				w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
436
+			default:
437
+				// ignore all other stream types, as we only care about directory reparse points
438
+			}
439
+		}
440
+		w.currentIsDir = false
441
+	}
409 442
 	if w.backupWriter != nil {
410 443
 		w.backupWriter.Close()
411 444
 		w.backupWriter = nil
... ...
@@ -413,21 +466,21 @@ func (w *legacyLayerWriter) reset() {
413 413
 	if w.currentFile != nil {
414 414
 		w.currentFile.Close()
415 415
 		w.currentFile = nil
416
+		w.currentFileName = ""
417
+		w.currentFileRoot = nil
416 418
 	}
419
+	return nil
417 420
 }
418 421
 
419 422
 // copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
420
-func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
421
-	createDisposition := uint32(syscall.CREATE_NEW)
422
-	if isDir {
423
-		err = os.Mkdir(destPath, 0)
424
-		if err != nil {
425
-			return nil, err
426
-		}
427
-		createDisposition = syscall.OPEN_EXISTING
428
-	}
429
-
430
-	src, err := openFileOrDir(srcPath, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.OPEN_EXISTING)
423
+func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
424
+	src, err := openRelative(
425
+		subPath,
426
+		srcRoot,
427
+		syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
428
+		syscall.FILE_SHARE_READ,
429
+		_FILE_OPEN,
430
+		_FILE_OPEN_REPARSE_POINT)
431 431
 	if err != nil {
432 432
 		return nil, err
433 433
 	}
... ...
@@ -440,7 +493,17 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
440 440
 		return nil, err
441 441
 	}
442 442
 
443
-	dest, err := openFileOrDir(destPath, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
443
+	extraFlags := uint32(0)
444
+	if isDir {
445
+		extraFlags |= _FILE_DIRECTORY_FILE
446
+	}
447
+	dest, err := openRelative(
448
+		subPath,
449
+		destRoot,
450
+		syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
451
+		syscall.FILE_SHARE_READ,
452
+		_FILE_CREATE,
453
+		extraFlags)
444 454
 	if err != nil {
445 455
 		return nil, err
446 456
 	}
... ...
@@ -469,18 +532,21 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
469 469
 
470 470
 // cloneTree clones a directory tree using hard links. It skips hard links for
471 471
 // the file names in the provided map and just copies those files.
472
-func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
472
+func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
473 473
 	var di []dirInfo
474
-	err := filepath.Walk(srcPath, func(srcFilePath string, info os.FileInfo, err error) error {
474
+	err := ensureNotReparsePointRelative(subPath, srcRoot)
475
+	if err != nil {
476
+		return err
477
+	}
478
+	err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
475 479
 		if err != nil {
476 480
 			return err
477 481
 		}
478 482
 
479
-		relPath, err := filepath.Rel(srcPath, srcFilePath)
483
+		relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
480 484
 		if err != nil {
481 485
 			return err
482 486
 		}
483
-		destFilePath := filepath.Join(destPath, relPath)
484 487
 
485 488
 		fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
486 489
 		// Directories, reparse points, and files that will be mutated during
... ...
@@ -492,15 +558,15 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
492 492
 		isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
493 493
 
494 494
 		if isDir || isReparsePoint || mutatedFiles[relPath] {
495
-			fi, err := copyFileWithMetadata(srcFilePath, destFilePath, isDir)
495
+			fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
496 496
 			if err != nil {
497 497
 				return err
498 498
 			}
499 499
 			if isDir && !isReparsePoint {
500
-				di = append(di, dirInfo{path: destFilePath, fileInfo: *fi})
500
+				di = append(di, dirInfo{path: relPath, fileInfo: *fi})
501 501
 			}
502 502
 		} else {
503
-			err = os.Link(srcFilePath, destFilePath)
503
+			err = linkRelative(relPath, srcRoot, relPath, destRoot)
504 504
 			if err != nil {
505 505
 				return err
506 506
 			}
... ...
@@ -518,13 +584,11 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
518 518
 		return err
519 519
 	}
520 520
 
521
-	return reapplyDirectoryTimes(di)
521
+	return reapplyDirectoryTimes(destRoot, di)
522 522
 }
523 523
 
524 524
 func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
525
-	w.reset()
526
-	err := w.init()
527
-	if err != nil {
525
+	if err := w.reset(); err != nil {
528 526
 		return err
529 527
 	}
530 528
 
... ...
@@ -532,6 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
532 532
 		return w.initUtilityVM()
533 533
 	}
534 534
 
535
+	name = filepath.Clean(name)
535 536
 	if hasPathPrefix(name, utilityVMPath) {
536 537
 		if !w.HasUtilityVM {
537 538
 			return errors.New("missing UtilityVM directory")
... ...
@@ -539,10 +604,9 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
539 539
 		if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
540 540
 			return errors.New("invalid UtilityVM layer")
541 541
 		}
542
-		path := filepath.Join(w.destRoot, name)
543
-		createDisposition := uint32(syscall.OPEN_EXISTING)
542
+		createDisposition := uint32(_FILE_OPEN)
544 543
 		if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
545
-			st, err := os.Lstat(path)
544
+			st, err := lstatRelative(name, w.destRoot)
546 545
 			if err != nil && !os.IsNotExist(err) {
547 546
 				return err
548 547
 			}
... ...
@@ -550,37 +614,44 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
550 550
 				// Delete the existing file/directory if it is not the same type as this directory.
551 551
 				existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
552 552
 				if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
553
-					if err = os.RemoveAll(path); err != nil {
553
+					if err = removeAllRelative(name, w.destRoot); err != nil {
554 554
 						return err
555 555
 					}
556 556
 					st = nil
557 557
 				}
558 558
 			}
559 559
 			if st == nil {
560
-				if err = os.Mkdir(path, 0); err != nil {
560
+				if err = mkdirRelative(name, w.destRoot); err != nil {
561 561
 					return err
562 562
 				}
563 563
 			}
564 564
 			if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
565
-				w.uvmDi = append(w.uvmDi, dirInfo{path: path, fileInfo: *fileInfo})
565
+				w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
566 566
 			}
567 567
 		} else {
568 568
 			// Overwrite any existing hard link.
569
-			err = os.Remove(path)
569
+			err := removeRelative(name, w.destRoot)
570 570
 			if err != nil && !os.IsNotExist(err) {
571 571
 				return err
572 572
 			}
573
-			createDisposition = syscall.CREATE_NEW
573
+			createDisposition = _FILE_CREATE
574 574
 		}
575 575
 
576
-		f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
576
+		f, err := openRelative(
577
+			name,
578
+			w.destRoot,
579
+			syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
580
+			syscall.FILE_SHARE_READ,
581
+			createDisposition,
582
+			_FILE_OPEN_REPARSE_POINT,
583
+		)
577 584
 		if err != nil {
578 585
 			return err
579 586
 		}
580 587
 		defer func() {
581 588
 			if f != nil {
582 589
 				f.Close()
583
-				os.Remove(path)
590
+				removeRelative(name, w.destRoot)
584 591
 			}
585 592
 		}()
586 593
 
... ...
@@ -591,28 +662,31 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
591 591
 
592 592
 		w.backupWriter = winio.NewBackupFileWriter(f, true)
593 593
 		w.currentFile = f
594
+		w.currentFileName = name
595
+		w.currentFileRoot = w.destRoot
594 596
 		w.addedFiles[name] = true
595 597
 		f = nil
596 598
 		return nil
597 599
 	}
598 600
 
599
-	path := filepath.Join(w.root, name)
601
+	fname := name
600 602
 	if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
601
-		err := os.Mkdir(path, 0)
603
+		err := mkdirRelative(name, w.root)
602 604
 		if err != nil {
603 605
 			return err
604 606
 		}
605
-		path += ".$wcidirs$"
607
+		fname += ".$wcidirs$"
608
+		w.currentIsDir = true
606 609
 	}
607 610
 
608
-	f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.CREATE_NEW)
611
+	f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0)
609 612
 	if err != nil {
610 613
 		return err
611 614
 	}
612 615
 	defer func() {
613 616
 		if f != nil {
614 617
 			f.Close()
615
-			os.Remove(path)
618
+			removeRelative(fname, w.root)
616 619
 		}
617 620
 	}()
618 621
 
... ...
@@ -634,19 +708,20 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
634 634
 	}
635 635
 
636 636
 	w.currentFile = f
637
+	w.currentFileName = name
638
+	w.currentFileRoot = w.root
637 639
 	w.addedFiles[name] = true
638 640
 	f = nil
639 641
 	return nil
640 642
 }
641 643
 
642 644
 func (w *legacyLayerWriter) AddLink(name string, target string) error {
643
-	w.reset()
644
-	err := w.init()
645
-	if err != nil {
645
+	if err := w.reset(); err != nil {
646 646
 		return err
647 647
 	}
648 648
 
649
-	var roots []string
649
+	target = filepath.Clean(target)
650
+	var roots []*os.File
650 651
 	if hasPathPrefix(target, filesPath) {
651 652
 		// Look for cross-layer hard link targets in the parent layers, since
652 653
 		// nothing is in the destination path yet.
... ...
@@ -655,7 +730,7 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
655 655
 		// Since the utility VM is fully cloned into the destination path
656 656
 		// already, look for cross-layer hard link targets directly in the
657 657
 		// destination path.
658
-		roots = []string{w.destRoot}
658
+		roots = []*os.File{w.destRoot}
659 659
 	}
660 660
 
661 661
 	if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
... ...
@@ -664,12 +739,12 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
664 664
 
665 665
 	// Find to try the target of the link in a previously added file. If that
666 666
 	// fails, search in parent layers.
667
-	var selectedRoot string
667
+	var selectedRoot *os.File
668 668
 	if _, ok := w.addedFiles[target]; ok {
669 669
 		selectedRoot = w.destRoot
670 670
 	} else {
671 671
 		for _, r := range roots {
672
-			if _, err = os.Lstat(filepath.Join(r, target)); err != nil {
672
+			if _, err := lstatRelative(target, r); err != nil {
673 673
 				if !os.IsNotExist(err) {
674 674
 					return err
675 675
 				}
... ...
@@ -678,22 +753,25 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
678 678
 				break
679 679
 			}
680 680
 		}
681
-		if selectedRoot == "" {
681
+		if selectedRoot == nil {
682 682
 			return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
683 683
 		}
684 684
 	}
685
+
685 686
 	// The link can't be written until after the ImportLayer call.
686 687
 	w.PendingLinks = append(w.PendingLinks, pendingLink{
687
-		Path:   filepath.Join(w.destRoot, name),
688
-		Target: filepath.Join(selectedRoot, target),
688
+		Path:       name,
689
+		Target:     target,
690
+		TargetRoot: selectedRoot,
689 691
 	})
690 692
 	w.addedFiles[name] = true
691 693
 	return nil
692 694
 }
693 695
 
694 696
 func (w *legacyLayerWriter) Remove(name string) error {
697
+	name = filepath.Clean(name)
695 698
 	if hasPathPrefix(name, filesPath) {
696
-		w.tombstones = append(w.tombstones, name[len(filesPath)+1:])
699
+		w.Tombstones = append(w.Tombstones, name)
697 700
 	} else if hasPathPrefix(name, utilityVMFilesPath) {
698 701
 		err := w.initUtilityVM()
699 702
 		if err != nil {
... ...
@@ -702,11 +780,10 @@ func (w *legacyLayerWriter) Remove(name string) error {
702 702
 		// Make sure the path exists; os.RemoveAll will not fail if the file is
703 703
 		// already gone, and this needs to be a fatal error for diagnostics
704 704
 		// purposes.
705
-		path := filepath.Join(w.destRoot, name)
706
-		if _, err := os.Lstat(path); err != nil {
705
+		if _, err := lstatRelative(name, w.destRoot); err != nil {
707 706
 			return err
708 707
 		}
709
-		err = os.RemoveAll(path)
708
+		err = removeAllRelative(name, w.destRoot)
710 709
 		if err != nil {
711 710
 			return err
712 711
 		}
... ...
@@ -728,28 +805,20 @@ func (w *legacyLayerWriter) Write(b []byte) (int, error) {
728 728
 }
729 729
 
730 730
 func (w *legacyLayerWriter) Close() error {
731
-	w.reset()
732
-	err := w.init()
733
-	if err != nil {
731
+	if err := w.reset(); err != nil {
734 732
 		return err
735 733
 	}
736
-	tf, err := os.Create(filepath.Join(w.root, "tombstones.txt"))
737
-	if err != nil {
738
-		return err
739
-	}
740
-	defer tf.Close()
741
-	_, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n"))
742
-	if err != nil {
734
+	if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
743 735
 		return err
744 736
 	}
745
-	for _, t := range w.tombstones {
746
-		_, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n"))
737
+	for _, pd := range w.pendingDirs {
738
+		err := mkdirRelative(pd.Path, pd.Root)
747 739
 		if err != nil {
748 740
 			return err
749 741
 		}
750 742
 	}
751 743
 	if w.HasUtilityVM {
752
-		err = reapplyDirectoryTimes(w.uvmDi)
744
+		err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
753 745
 		if err != nil {
754 746
 			return err
755 747
 		}
... ...
@@ -1,7 +1,7 @@
1
-// +build !go1.9
2
-
3
-package hcsshim
4
-
5
-// Due to a bug in go1.8 and before, directory reparse points need to be skipped
6
-// during filepath.Walk. This is fixed in go1.9
7
-var shouldSkipDirectoryReparse = true
1
+// +build !go1.9
2
+
3
+package hcsshim
4
+
5
+// Due to a bug in go1.8 and before, directory reparse points need to be skipped
6
+// during filepath.Walk. This is fixed in go1.9
7
+var shouldSkipDirectoryReparse = true
... ...
@@ -1,7 +1,7 @@
1
-// +build go1.9
2
-
3
-package hcsshim
4
-
5
-// Due to a bug in go1.8 and before, directory reparse points need to be skipped
6
-// during filepath.Walk. This is fixed in go1.9
7
-var shouldSkipDirectoryReparse = false
1
+// +build go1.9
2
+
3
+package hcsshim
4
+
5
+// Due to a bug in go1.8 and before, directory reparse points need to be skipped
6
+// during filepath.Walk. This is fixed in go1.9
7
+var shouldSkipDirectoryReparse = false
8 8
new file mode 100644
... ...
@@ -0,0 +1,427 @@
0
+package hcsshim
1
+
2
+import (
3
+	"errors"
4
+	"io"
5
+	"os"
6
+	"path/filepath"
7
+	"strings"
8
+	"syscall"
9
+	"unicode/utf16"
10
+	"unsafe"
11
+
12
+	winio "github.com/Microsoft/go-winio"
13
+)
14
+
15
+//sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile
16
+//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile
17
+//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
18
+//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
19
+//sys localFree(ptr uintptr) = kernel32.LocalFree
20
+
21
+type ioStatusBlock struct {
22
+	Status, Information uintptr
23
+}
24
+
25
+type objectAttributes struct {
26
+	Length             uintptr
27
+	RootDirectory      uintptr
28
+	ObjectName         uintptr
29
+	Attributes         uintptr
30
+	SecurityDescriptor uintptr
31
+	SecurityQoS        uintptr
32
+}
33
+
34
+type unicodeString struct {
35
+	Length        uint16
36
+	MaximumLength uint16
37
+	Buffer        uintptr
38
+}
39
+
40
+type fileLinkInformation struct {
41
+	ReplaceIfExists bool
42
+	RootDirectory   uintptr
43
+	FileNameLength  uint32
44
+	FileName        [1]uint16
45
+}
46
+
47
+type fileDispositionInformationEx struct {
48
+	Flags uintptr
49
+}
50
+
51
+const (
52
+	_FileLinkInformation          = 11
53
+	_FileDispositionInformationEx = 64
54
+
55
+	_FILE_READ_ATTRIBUTES  = 0x0080
56
+	_FILE_WRITE_ATTRIBUTES = 0x0100
57
+	_DELETE                = 0x10000
58
+
59
+	_FILE_OPEN   = 1
60
+	_FILE_CREATE = 2
61
+
62
+	_FILE_DIRECTORY_FILE          = 0x00000001
63
+	_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
64
+	_FILE_DELETE_ON_CLOSE         = 0x00001000
65
+	_FILE_OPEN_FOR_BACKUP_INTENT  = 0x00004000
66
+	_FILE_OPEN_REPARSE_POINT      = 0x00200000
67
+
68
+	_FILE_DISPOSITION_DELETE = 0x00000001
69
+
70
+	_OBJ_DONT_REPARSE = 0x1000
71
+
72
+	_STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B
73
+)
74
+
75
+func openRoot(path string) (*os.File, error) {
76
+	longpath, err := makeLongAbsPath(path)
77
+	if err != nil {
78
+		return nil, err
79
+	}
80
+	return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING)
81
+}
82
+
83
+func ntRelativePath(path string) ([]uint16, error) {
84
+	path = filepath.Clean(path)
85
+	if strings.Contains(":", path) {
86
+		// Since alternate data streams must follow the file they
87
+		// are attached to, finding one here (out of order) is invalid.
88
+		return nil, errors.New("path contains invalid character `:`")
89
+	}
90
+	fspath := filepath.FromSlash(path)
91
+	if len(fspath) > 0 && fspath[0] == '\\' {
92
+		return nil, errors.New("expected relative path")
93
+	}
94
+
95
+	path16 := utf16.Encode(([]rune)(fspath))
96
+	if len(path16) > 32767 {
97
+		return nil, syscall.ENAMETOOLONG
98
+	}
99
+
100
+	return path16, nil
101
+}
102
+
103
+// openRelativeInternal opens a relative path from the given root, failing if
104
+// any of the intermediate path components are reparse points.
105
+func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
106
+	var (
107
+		h    uintptr
108
+		iosb ioStatusBlock
109
+		oa   objectAttributes
110
+	)
111
+
112
+	path16, err := ntRelativePath(path)
113
+	if err != nil {
114
+		return nil, err
115
+	}
116
+
117
+	if root == nil || root.Fd() == 0 {
118
+		return nil, errors.New("missing root directory")
119
+	}
120
+
121
+	upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2)
122
+	defer localFree(upathBuffer)
123
+
124
+	upath := (*unicodeString)(unsafe.Pointer(upathBuffer))
125
+	upath.Length = uint16(len(path16) * 2)
126
+	upath.MaximumLength = upath.Length
127
+	upath.Buffer = upathBuffer + unsafe.Sizeof(*upath)
128
+	copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16)
129
+
130
+	oa.Length = unsafe.Sizeof(oa)
131
+	oa.ObjectName = upathBuffer
132
+	oa.RootDirectory = uintptr(root.Fd())
133
+	oa.Attributes = _OBJ_DONT_REPARSE
134
+	status := ntCreateFile(
135
+		&h,
136
+		accessMask|syscall.SYNCHRONIZE,
137
+		&oa,
138
+		&iosb,
139
+		nil,
140
+		0,
141
+		shareFlags,
142
+		createDisposition,
143
+		_FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags,
144
+		nil,
145
+		0,
146
+	)
147
+	if status != 0 {
148
+		return nil, rtlNtStatusToDosError(status)
149
+	}
150
+
151
+	fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path))
152
+	if err != nil {
153
+		syscall.Close(syscall.Handle(h))
154
+		return nil, err
155
+	}
156
+
157
+	return os.NewFile(h, fullPath), nil
158
+}
159
+
160
+// openRelative opens a relative path from the given root, failing if
161
+// any of the intermediate path components are reparse points.
162
+func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
163
+	f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags)
164
+	if err != nil {
165
+		err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err}
166
+	}
167
+	return f, err
168
+}
169
+
170
+// linkRelative creates a hard link from oldname to newname (relative to oldroot
171
+// and newroot), failing if any of the intermediate path components are reparse
172
+// points.
173
+func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error {
174
+	// Open the old file.
175
+	oldf, err := openRelativeInternal(
176
+		oldname,
177
+		oldroot,
178
+		syscall.FILE_WRITE_ATTRIBUTES,
179
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
180
+		_FILE_OPEN,
181
+		0,
182
+	)
183
+	if err != nil {
184
+		return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err}
185
+	}
186
+	defer oldf.Close()
187
+
188
+	// Open the parent of the new file.
189
+	var parent *os.File
190
+	parentPath := filepath.Dir(newname)
191
+	if parentPath != "." {
192
+		parent, err = openRelativeInternal(
193
+			parentPath,
194
+			newroot,
195
+			syscall.GENERIC_READ,
196
+			syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
197
+			_FILE_OPEN,
198
+			_FILE_DIRECTORY_FILE)
199
+		if err != nil {
200
+			return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err}
201
+		}
202
+		defer parent.Close()
203
+
204
+		fi, err := winio.GetFileBasicInfo(parent)
205
+		if err != nil {
206
+			return err
207
+		}
208
+		if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
209
+			return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)}
210
+		}
211
+
212
+	} else {
213
+		parent = newroot
214
+	}
215
+
216
+	// Issue an NT call to create the link. This will be safe because NT will
217
+	// not open any more directories to create the link, so it cannot walk any
218
+	// more reparse points.
219
+	newbase := filepath.Base(newname)
220
+	newbase16, err := ntRelativePath(newbase)
221
+	if err != nil {
222
+		return err
223
+	}
224
+
225
+	size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2
226
+	linkinfoBuffer := localAlloc(0, size)
227
+	defer localFree(linkinfoBuffer)
228
+	linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer))
229
+	linkinfo.RootDirectory = parent.Fd()
230
+	linkinfo.FileNameLength = uint32(len(newbase16) * 2)
231
+	copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16)
232
+
233
+	var iosb ioStatusBlock
234
+	status := ntSetInformationFile(
235
+		oldf.Fd(),
236
+		&iosb,
237
+		linkinfoBuffer,
238
+		uint32(size),
239
+		_FileLinkInformation,
240
+	)
241
+	if status != 0 {
242
+		return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)}
243
+	}
244
+
245
+	return nil
246
+}
247
+
248
+// deleteOnClose marks a file to be deleted when the handle is closed.
249
+func deleteOnClose(f *os.File) error {
250
+	disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE}
251
+	var iosb ioStatusBlock
252
+	status := ntSetInformationFile(
253
+		f.Fd(),
254
+		&iosb,
255
+		uintptr(unsafe.Pointer(&disposition)),
256
+		uint32(unsafe.Sizeof(disposition)),
257
+		_FileDispositionInformationEx,
258
+	)
259
+	if status != 0 {
260
+		return rtlNtStatusToDosError(status)
261
+	}
262
+	return nil
263
+}
264
+
265
+// clearReadOnly clears the readonly attribute on a file.
266
+func clearReadOnly(f *os.File) error {
267
+	bi, err := winio.GetFileBasicInfo(f)
268
+	if err != nil {
269
+		return err
270
+	}
271
+	if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 {
272
+		return nil
273
+	}
274
+	sbi := winio.FileBasicInfo{
275
+		FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY,
276
+	}
277
+	if sbi.FileAttributes == 0 {
278
+		sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL
279
+	}
280
+	return winio.SetFileBasicInfo(f, &sbi)
281
+}
282
+
283
+// removeRelative removes a file or directory relative to a root, failing if any
284
+// intermediate path components are reparse points.
285
+func removeRelative(path string, root *os.File) error {
286
+	f, err := openRelativeInternal(
287
+		path,
288
+		root,
289
+		_FILE_READ_ATTRIBUTES|_FILE_WRITE_ATTRIBUTES|_DELETE,
290
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
291
+		_FILE_OPEN,
292
+		_FILE_OPEN_REPARSE_POINT)
293
+	if err == nil {
294
+		defer f.Close()
295
+		err = deleteOnClose(f)
296
+		if err == syscall.ERROR_ACCESS_DENIED {
297
+			// Maybe the file is marked readonly. Clear the bit and retry.
298
+			clearReadOnly(f)
299
+			err = deleteOnClose(f)
300
+		}
301
+	}
302
+	if err != nil {
303
+		return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err}
304
+	}
305
+	return nil
306
+}
307
+
308
+// removeAllRelative removes a directory tree relative to a root, failing if any
309
+// intermediate path components are reparse points.
310
+func removeAllRelative(path string, root *os.File) error {
311
+	fi, err := lstatRelative(path, root)
312
+	if err != nil {
313
+		if os.IsNotExist(err) {
314
+			return nil
315
+		}
316
+		return err
317
+	}
318
+	fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes
319
+	if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
320
+		// If this is a reparse point, it can't have children. Simple remove will do.
321
+		err := removeRelative(path, root)
322
+		if err == nil || os.IsNotExist(err) {
323
+			return nil
324
+		}
325
+		return err
326
+	}
327
+
328
+	// It is necessary to use os.Open as Readdirnames does not work with
329
+	// openRelative. This is safe because the above lstatrelative fails
330
+	// if the target is outside the root, and we know this is not a
331
+	// symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check.
332
+	fd, err := os.Open(filepath.Join(root.Name(), path))
333
+	if err != nil {
334
+		if os.IsNotExist(err) {
335
+			// Race. It was deleted between the Lstat and Open.
336
+			// Return nil per RemoveAll's docs.
337
+			return nil
338
+		}
339
+		return err
340
+	}
341
+
342
+	// Remove contents & return first error.
343
+	for {
344
+		names, err1 := fd.Readdirnames(100)
345
+		for _, name := range names {
346
+			err1 := removeAllRelative(path+string(os.PathSeparator)+name, root)
347
+			if err == nil {
348
+				err = err1
349
+			}
350
+		}
351
+		if err1 == io.EOF {
352
+			break
353
+		}
354
+		// If Readdirnames returned an error, use it.
355
+		if err == nil {
356
+			err = err1
357
+		}
358
+		if len(names) == 0 {
359
+			break
360
+		}
361
+	}
362
+	fd.Close()
363
+
364
+	// Remove directory.
365
+	err1 := removeRelative(path, root)
366
+	if err1 == nil || os.IsNotExist(err1) {
367
+		return nil
368
+	}
369
+	if err == nil {
370
+		err = err1
371
+	}
372
+	return err
373
+}
374
+
375
+// mkdirRelative creates a directory relative to a root, failing if any
376
+// intermediate path components are reparse points.
377
+func mkdirRelative(path string, root *os.File) error {
378
+	f, err := openRelativeInternal(
379
+		path,
380
+		root,
381
+		0,
382
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
383
+		_FILE_CREATE,
384
+		_FILE_DIRECTORY_FILE)
385
+	if err == nil {
386
+		f.Close()
387
+	} else {
388
+		err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err}
389
+	}
390
+	return err
391
+}
392
+
393
+// lstatRelative performs a stat operation on a file relative to a root, failing
394
+// if any intermediate path components are reparse points.
395
+func lstatRelative(path string, root *os.File) (os.FileInfo, error) {
396
+	f, err := openRelativeInternal(
397
+		path,
398
+		root,
399
+		_FILE_READ_ATTRIBUTES,
400
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
401
+		_FILE_OPEN,
402
+		_FILE_OPEN_REPARSE_POINT)
403
+	if err != nil {
404
+		return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err}
405
+	}
406
+	defer f.Close()
407
+	return f.Stat()
408
+}
409
+
410
+// ensureNotReparsePointRelative validates that a given file (relative to a
411
+// root) and all intermediate path components are not a reparse points.
412
+func ensureNotReparsePointRelative(path string, root *os.File) error {
413
+	// Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT.
414
+	f, err := openRelative(
415
+		path,
416
+		root,
417
+		0,
418
+		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
419
+		_FILE_OPEN,
420
+		0)
421
+	if err != nil {
422
+		return err
423
+	}
424
+	f.Close()
425
+	return nil
426
+}
... ...
@@ -41,6 +41,8 @@ var (
41 41
 	modole32     = windows.NewLazySystemDLL("ole32.dll")
42 42
 	modiphlpapi  = windows.NewLazySystemDLL("iphlpapi.dll")
43 43
 	modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
44
+	modntdll     = windows.NewLazySystemDLL("ntdll.dll")
45
+	modkernel32  = windows.NewLazySystemDLL("kernel32.dll")
44 46
 
45 47
 	procCoTaskMemFree                      = modole32.NewProc("CoTaskMemFree")
46 48
 	procSetCurrentThreadCompartmentId      = modiphlpapi.NewProc("SetCurrentThreadCompartmentId")
... ...
@@ -94,6 +96,11 @@ var (
94 94
 	procHcsUnregisterProcessCallback       = modvmcompute.NewProc("HcsUnregisterProcessCallback")
95 95
 	procHcsModifyServiceSettings           = modvmcompute.NewProc("HcsModifyServiceSettings")
96 96
 	procHNSCall                            = modvmcompute.NewProc("HNSCall")
97
+	procNtCreateFile                       = modntdll.NewProc("NtCreateFile")
98
+	procNtSetInformationFile               = modntdll.NewProc("NtSetInformationFile")
99
+	procRtlNtStatusToDosErrorNoTeb         = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
100
+	procLocalAlloc                         = modkernel32.NewProc("LocalAlloc")
101
+	procLocalFree                          = modkernel32.NewProc("LocalFree")
97 102
 )
98 103
 
99 104
 func coTaskMemFree(buffer unsafe.Pointer) {
... ...
@@ -1040,3 +1047,34 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16)
1040 1040
 	}
1041 1041
 	return
1042 1042
 }
1043
+
1044
+func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) {
1045
+	r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0)
1046
+	status = uint32(r0)
1047
+	return
1048
+}
1049
+
1050
+func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) {
1051
+	r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0)
1052
+	status = uint32(r0)
1053
+	return
1054
+}
1055
+
1056
+func rtlNtStatusToDosError(status uint32) (winerr error) {
1057
+	r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
1058
+	if r0 != 0 {
1059
+		winerr = syscall.Errno(r0)
1060
+	}
1061
+	return
1062
+}
1063
+
1064
+func localAlloc(flags uint32, size int) (ptr uintptr) {
1065
+	r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0)
1066
+	ptr = uintptr(r0)
1067
+	return
1068
+}
1069
+
1070
+func localFree(ptr uintptr) {
1071
+	syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0)
1072
+	return
1073
+}