Signed-off-by: Darren Stahl <darst@microsoft.com>
| ... | ... |
@@ -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 |
+} |