mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
vendor: update hcsshim to v0.6.11
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
588e088799
commit
b6b47649e0
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -114,8 +114,8 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Microsoft/hcsshim",
|
||||
"Comment": "V0.6.3",
|
||||
"Rev": "6ea7fe54f719d95721e7d9b26ac0add224c9b923"
|
||||
"Comment": "v0.6.11",
|
||||
"Rev": "800683ae704ac360b2f3f47fa88f3a6c8c9091b5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/NYTimes/gziphandler",
|
||||
|
3
vendor/github.com/Microsoft/hcsshim/BUILD
generated
vendored
3
vendor/github.com/Microsoft/hcsshim/BUILD
generated
vendored
@ -29,10 +29,13 @@ go_library(
|
||||
"layerexists.go",
|
||||
"layerutils.go",
|
||||
"legacy.go",
|
||||
"legacy18.go",
|
||||
"legacy19.go",
|
||||
"nametoguid.go",
|
||||
"preparelayer.go",
|
||||
"process.go",
|
||||
"processimage.go",
|
||||
"safeopen.go",
|
||||
"unpreparelayer.go",
|
||||
"utils.go",
|
||||
"version.go",
|
||||
|
33
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
33
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
@ -4,9 +4,30 @@ This package supports launching Windows Server containers from Go. It is
|
||||
primarily used in the [Docker Engine](https://github.com/docker/docker) project,
|
||||
but it can be freely used by other projects as well.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||
see the [Code of Conduct
|
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||
questions or comments.
|
||||
|
||||
## Contributing
|
||||
---------------
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
Security issues and bugs should be reported privately, via email, to the Microsoft Security
|
||||
Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
|
||||
receive a response within 24 hours. If for some reason you do not, please follow up via
|
||||
email to ensure we received your original message. Further information, including the
|
||||
[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in
|
||||
the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
|
||||
|
||||
-------------------------------------------
|
||||
Copyright (c) 2018 Microsoft Corp. All rights reserved.
|
||||
|
54
vendor/github.com/Microsoft/hcsshim/baselayer.go
generated
vendored
54
vendor/github.com/Microsoft/hcsshim/baselayer.go
generated
vendored
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type baseLayerWriter struct {
|
||||
root string
|
||||
root *os.File
|
||||
f *os.File
|
||||
bw *winio.BackupFileWriter
|
||||
err error
|
||||
@ -26,10 +26,10 @@ type dirInfo struct {
|
||||
// reapplyDirectoryTimes reapplies directory modification, creation, etc. times
|
||||
// after processing of the directory tree has completed. The times are expected
|
||||
// to be ordered such that parent directories come before child directories.
|
||||
func reapplyDirectoryTimes(dis []dirInfo) error {
|
||||
func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error {
|
||||
for i := range dis {
|
||||
di := &dis[len(dis)-i-1] // reverse order: process child directories first
|
||||
f, err := winio.OpenForBackup(di.path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING)
|
||||
f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -75,12 +75,6 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
|
||||
w.hasUtilityVM = true
|
||||
}
|
||||
|
||||
path := filepath.Join(w.root, name)
|
||||
path, err = makeLongAbsPath(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
defer func() {
|
||||
if f != nil {
|
||||
@ -88,27 +82,23 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
|
||||
}
|
||||
}()
|
||||
|
||||
createmode := uint32(syscall.CREATE_NEW)
|
||||
extraFlags := uint32(0)
|
||||
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
err := os.Mkdir(path, 0)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
createmode = syscall.OPEN_EXISTING
|
||||
extraFlags |= _FILE_DIRECTORY_FILE
|
||||
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo})
|
||||
w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo})
|
||||
}
|
||||
}
|
||||
|
||||
mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
|
||||
f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
|
||||
f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags)
|
||||
if err != nil {
|
||||
return makeError(err, "Failed to OpenForBackup", path)
|
||||
return makeError(err, "Failed to openRelative", name)
|
||||
}
|
||||
|
||||
err = winio.SetFileBasicInfo(f, fileInfo)
|
||||
if err != nil {
|
||||
return makeError(err, "Failed to SetFileBasicInfo", path)
|
||||
return makeError(err, "Failed to SetFileBasicInfo", name)
|
||||
}
|
||||
|
||||
w.f = f
|
||||
@ -129,17 +119,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
linkpath, err := makeLongAbsPath(filepath.Join(w.root, name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Link(linktarget, linkpath)
|
||||
return linkRelative(target, w.root, name, w.root)
|
||||
}
|
||||
|
||||
func (w *baseLayerWriter) Remove(name string) error {
|
||||
@ -155,6 +135,10 @@ func (w *baseLayerWriter) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (w *baseLayerWriter) Close() error {
|
||||
defer func() {
|
||||
w.root.Close()
|
||||
w.root = nil
|
||||
}()
|
||||
err := w.closeCurrentFile()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -162,18 +146,22 @@ func (w *baseLayerWriter) Close() error {
|
||||
if w.err == nil {
|
||||
// Restore the file times of all the directories, since they may have
|
||||
// been modified by creating child directories.
|
||||
err = reapplyDirectoryTimes(w.dirInfo)
|
||||
err = reapplyDirectoryTimes(w.root, w.dirInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ProcessBaseLayer(w.root)
|
||||
err = ProcessBaseLayer(w.root.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if w.hasUtilityVM {
|
||||
err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM"))
|
||||
err := ensureNotReparsePointRelative("UtilityVM", w.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
6
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
@ -201,12 +201,18 @@ func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON strin
|
||||
|
||||
if createError == nil || IsPending(createError) {
|
||||
if err := container.registerCallback(); err != nil {
|
||||
// Terminate the container if it still exists. We're okay to ignore a failure here.
|
||||
container.Terminate()
|
||||
return nil, makeContainerError(container, operation, "", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout)
|
||||
if err != nil {
|
||||
if err == ErrTimeout {
|
||||
// Terminate the container if it still exists. We're okay to ignore a failure here.
|
||||
container.Terminate()
|
||||
}
|
||||
return nil, makeContainerError(container, operation, configuration, err)
|
||||
}
|
||||
|
||||
|
22
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
22
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
@ -72,6 +72,22 @@ var (
|
||||
ErrPlatformNotSupported = errors.New("unsupported platform request")
|
||||
)
|
||||
|
||||
type EndpointNotFoundError struct {
|
||||
EndpointName string
|
||||
}
|
||||
|
||||
func (e EndpointNotFoundError) Error() string {
|
||||
return fmt.Sprintf("Endpoint %s not found", e.EndpointName)
|
||||
}
|
||||
|
||||
type NetworkNotFoundError struct {
|
||||
NetworkName string
|
||||
}
|
||||
|
||||
func (e NetworkNotFoundError) Error() string {
|
||||
return fmt.Sprintf("Network %s not found", e.NetworkName)
|
||||
}
|
||||
|
||||
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||
type ProcessError struct {
|
||||
Process *process
|
||||
@ -174,6 +190,12 @@ func makeProcessError(process *process, operation string, extraInfo string, err
|
||||
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||
func IsNotExist(err error) bool {
|
||||
err = getInnerError(err)
|
||||
if _, ok := err.(EndpointNotFoundError); ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := err.(NetworkNotFoundError); ok {
|
||||
return true
|
||||
}
|
||||
return err == ErrComputeSystemDoesNotExist ||
|
||||
err == ErrElementNotFound ||
|
||||
err == ErrProcNotFound
|
||||
|
2
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
2
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go
|
||||
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go
|
||||
|
||||
//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
|
||||
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
|
||||
|
17
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
17
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
@ -2,7 +2,6 @@ package hcsshim
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -135,7 +134,7 @@ func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
|
||||
return &hnsEndpoint, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Endpoint %v not found", endpointName)
|
||||
return nil, EndpointNotFoundError{EndpointName: endpointName}
|
||||
}
|
||||
|
||||
// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
|
||||
@ -192,18 +191,24 @@ func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error {
|
||||
return modifyNetworkEndpoint(containerID, endpoint.Id, Remove)
|
||||
}
|
||||
|
||||
// ApplyACLPolicy applies Acl Policy on the Endpoint
|
||||
func (endpoint *HNSEndpoint) ApplyACLPolicy(policy *ACLPolicy) error {
|
||||
// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
|
||||
func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
|
||||
operation := "ApplyACLPolicy"
|
||||
title := "HCSShim::HNSEndpoint::" + operation
|
||||
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||
|
||||
for _, policy := range policies {
|
||||
if policy == nil {
|
||||
continue
|
||||
}
|
||||
jsonString, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint.Policies[0] = jsonString
|
||||
_, err = endpoint.Update()
|
||||
endpoint.Policies = append(endpoint.Policies, jsonString)
|
||||
}
|
||||
|
||||
_, err := endpoint.Update()
|
||||
return err
|
||||
}
|
||||
|
||||
|
3
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
3
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
@ -2,7 +2,6 @@ package hcsshim
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -90,7 +89,7 @@ func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
|
||||
return &hnsnetwork, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Network %v not found", networkName)
|
||||
return nil, NetworkNotFoundError{NetworkName: networkName}
|
||||
}
|
||||
|
||||
// Create Network by sending NetworkRequest to HNS.
|
||||
|
5
vendor/github.com/Microsoft/hcsshim/hnspolicy.go
generated
vendored
5
vendor/github.com/Microsoft/hcsshim/hnspolicy.go
generated
vendored
@ -80,12 +80,11 @@ type ACLPolicy struct {
|
||||
InternalPort uint16
|
||||
Action ActionType
|
||||
Direction DirectionType
|
||||
LocalAddress string
|
||||
RemoteAddress string
|
||||
LocalAddresses string
|
||||
RemoteAddresses string
|
||||
LocalPort uint16
|
||||
RemotePort uint16
|
||||
RuleType RuleType `json:"RuleType,omitempty"`
|
||||
|
||||
Priority uint16
|
||||
ServiceName string
|
||||
}
|
||||
|
38
vendor/github.com/Microsoft/hcsshim/importlayer.go
generated
vendored
38
vendor/github.com/Microsoft/hcsshim/importlayer.go
generated
vendored
@ -129,37 +129,39 @@ type legacyLayerWriterWrapper struct {
|
||||
}
|
||||
|
||||
func (r *legacyLayerWriterWrapper) Close() error {
|
||||
defer os.RemoveAll(r.root)
|
||||
defer os.RemoveAll(r.root.Name())
|
||||
defer r.legacyLayerWriter.CloseRoots()
|
||||
err := r.legacyLayerWriter.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use the original path here because ImportLayer does not support long paths for the source in TP5.
|
||||
// But do use a long path for the destination to work around another bug with directories
|
||||
// with MAX_PATH - 12 < length < MAX_PATH.
|
||||
info := r.info
|
||||
fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID))
|
||||
if err != nil {
|
||||
info.HomeDir = ""
|
||||
if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info.HomeDir = ""
|
||||
if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil {
|
||||
for _, name := range r.Tombstones {
|
||||
if err = removeRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Add any hard links that were collected.
|
||||
for _, lnk := range r.PendingLinks {
|
||||
if err = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) {
|
||||
if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err = os.Link(lnk.Target, lnk.Path); err != nil {
|
||||
if err = linkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Prepare the utility VM for use if one is present in the layer.
|
||||
if r.HasUtilityVM {
|
||||
err = ProcessUtilityVMImage(filepath.Join(fullPath, "UtilityVM"))
|
||||
err := ensureNotReparsePointRelative("UtilityVM", r.destRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -173,8 +175,12 @@ func (r *legacyLayerWriterWrapper) Close() error {
|
||||
func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
|
||||
if len(parentLayerPaths) == 0 {
|
||||
// This is a base layer. It gets imported differently.
|
||||
f, err := openRoot(filepath.Join(info.HomeDir, layerID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &baseLayerWriter{
|
||||
root: filepath.Join(info.HomeDir, layerID),
|
||||
root: f,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -185,8 +191,12 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &legacyLayerWriterWrapper{
|
||||
legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)),
|
||||
legacyLayerWriter: w,
|
||||
info: info,
|
||||
layerID: layerID,
|
||||
path: path,
|
||||
|
1
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
1
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
@ -35,6 +35,7 @@ type MappedDir struct {
|
||||
ReadOnly bool
|
||||
BandwidthMaximum uint64
|
||||
IOPSMaximum uint64
|
||||
CreateInUtilityVM bool
|
||||
}
|
||||
|
||||
type MappedPipe struct {
|
||||
|
288
vendor/github.com/Microsoft/hcsshim/legacy.go
generated
vendored
288
vendor/github.com/Microsoft/hcsshim/legacy.go
generated
vendored
@ -121,6 +121,16 @@ func (r *legacyLayerReader) walkUntilCancelled() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
|
||||
// Handle failure from what may be a golang bug in the conversion of
|
||||
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
|
||||
// which is called by filepath.Walk will fail when a filename contains
|
||||
// unicode characters. Skip the recycle bin regardless which is goodness.
|
||||
if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
|
||||
return nil
|
||||
}
|
||||
@ -326,59 +336,79 @@ func (r *legacyLayerReader) Close() error {
|
||||
|
||||
type pendingLink struct {
|
||||
Path, Target string
|
||||
TargetRoot *os.File
|
||||
}
|
||||
|
||||
type pendingDir struct {
|
||||
Path string
|
||||
Root *os.File
|
||||
}
|
||||
|
||||
type legacyLayerWriter struct {
|
||||
root string
|
||||
parentRoots []string
|
||||
destRoot string
|
||||
root *os.File
|
||||
destRoot *os.File
|
||||
parentRoots []*os.File
|
||||
currentFile *os.File
|
||||
currentFileName string
|
||||
currentFileRoot *os.File
|
||||
backupWriter *winio.BackupFileWriter
|
||||
tombstones []string
|
||||
pathFixed bool
|
||||
Tombstones []string
|
||||
HasUtilityVM bool
|
||||
uvmDi []dirInfo
|
||||
addedFiles map[string]bool
|
||||
PendingLinks []pendingLink
|
||||
pendingDirs []pendingDir
|
||||
currentIsDir bool
|
||||
}
|
||||
|
||||
// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
|
||||
// transport format to disk.
|
||||
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) *legacyLayerWriter {
|
||||
return &legacyLayerWriter{
|
||||
root: root,
|
||||
parentRoots: parentRoots,
|
||||
destRoot: destRoot,
|
||||
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
|
||||
w = &legacyLayerWriter{
|
||||
addedFiles: make(map[string]bool),
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
w.CloseRoots()
|
||||
w = nil
|
||||
}
|
||||
}()
|
||||
w.root, err = openRoot(root)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
w.destRoot, err = openRoot(destRoot)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, r := range parentRoots {
|
||||
f, err := openRoot(r)
|
||||
if err != nil {
|
||||
return w, err
|
||||
}
|
||||
w.parentRoots = append(w.parentRoots, f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) init() error {
|
||||
if !w.pathFixed {
|
||||
path, err := makeLongAbsPath(w.root)
|
||||
if err != nil {
|
||||
return err
|
||||
func (w *legacyLayerWriter) CloseRoots() {
|
||||
if w.root != nil {
|
||||
w.root.Close()
|
||||
w.root = nil
|
||||
}
|
||||
for i, p := range w.parentRoots {
|
||||
w.parentRoots[i], err = makeLongAbsPath(p)
|
||||
if err != nil {
|
||||
return err
|
||||
if w.destRoot != nil {
|
||||
w.destRoot.Close()
|
||||
w.destRoot = nil
|
||||
}
|
||||
for i := range w.parentRoots {
|
||||
w.parentRoots[i].Close()
|
||||
}
|
||||
destPath, err := makeLongAbsPath(w.destRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.root = path
|
||||
w.destRoot = destPath
|
||||
w.pathFixed = true
|
||||
}
|
||||
return nil
|
||||
w.parentRoots = nil
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) initUtilityVM() error {
|
||||
if !w.HasUtilityVM {
|
||||
err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0)
|
||||
err := mkdirRelative(utilityVMPath, w.destRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -386,7 +416,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
|
||||
// clone the utility VM from the parent layer into this layer. Use hard
|
||||
// links to avoid unnecessary copying, since most of the files are
|
||||
// immutable.
|
||||
err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles)
|
||||
err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
|
||||
}
|
||||
@ -395,7 +425,40 @@ func (w *legacyLayerWriter) initUtilityVM() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) reset() {
|
||||
func (w *legacyLayerWriter) reset() error {
|
||||
if w.currentIsDir {
|
||||
r := w.currentFile
|
||||
br := winio.NewBackupStreamReader(r)
|
||||
// Seek to the beginning of the backup stream, skipping the fileattrs
|
||||
if _, err := r.Seek(4, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
bhdr, err := br.Next()
|
||||
if err == io.EOF {
|
||||
// end of backupstream data
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch bhdr.Id {
|
||||
case winio.BackupReparseData:
|
||||
// The current file is a `.$wcidirs$` metadata file that
|
||||
// describes a directory reparse point. Delete the placeholder
|
||||
// directory to prevent future files being added into the
|
||||
// destination of the reparse point during the ImportLayer call
|
||||
if err := removeRelative(w.currentFileName, w.currentFileRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
|
||||
default:
|
||||
// ignore all other stream types, as we only care about directory reparse points
|
||||
}
|
||||
}
|
||||
w.currentIsDir = false
|
||||
}
|
||||
if w.backupWriter != nil {
|
||||
w.backupWriter.Close()
|
||||
w.backupWriter = nil
|
||||
@ -403,21 +466,21 @@ func (w *legacyLayerWriter) reset() {
|
||||
if w.currentFile != nil {
|
||||
w.currentFile.Close()
|
||||
w.currentFile = nil
|
||||
w.currentFileName = ""
|
||||
w.currentFileRoot = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
|
||||
func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
|
||||
createDisposition := uint32(syscall.CREATE_NEW)
|
||||
if isDir {
|
||||
err = os.Mkdir(destPath, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createDisposition = syscall.OPEN_EXISTING
|
||||
}
|
||||
|
||||
src, err := openFileOrDir(srcPath, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.OPEN_EXISTING)
|
||||
func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
|
||||
src, err := openRelative(
|
||||
subPath,
|
||||
srcRoot,
|
||||
syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
|
||||
syscall.FILE_SHARE_READ,
|
||||
_FILE_OPEN,
|
||||
_FILE_OPEN_REPARSE_POINT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -430,7 +493,17 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dest, err := openFileOrDir(destPath, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
|
||||
extraFlags := uint32(0)
|
||||
if isDir {
|
||||
extraFlags |= _FILE_DIRECTORY_FILE
|
||||
}
|
||||
dest, err := openRelative(
|
||||
subPath,
|
||||
destRoot,
|
||||
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
|
||||
syscall.FILE_SHARE_READ,
|
||||
_FILE_CREATE,
|
||||
extraFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -459,39 +532,49 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
|
||||
|
||||
// cloneTree clones a directory tree using hard links. It skips hard links for
|
||||
// the file names in the provided map and just copies those files.
|
||||
func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
|
||||
func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
|
||||
var di []dirInfo
|
||||
err := filepath.Walk(srcPath, func(srcFilePath string, info os.FileInfo, err error) error {
|
||||
err := ensureNotReparsePointRelative(subPath, srcRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(srcPath, srcFilePath)
|
||||
relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
destFilePath := filepath.Join(destPath, relPath)
|
||||
|
||||
fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||
// Directories, reparse points, and files that will be mutated during
|
||||
// utility VM import must be copied. All other files can be hard linked.
|
||||
isReparsePoint := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||
if info.IsDir() || isReparsePoint || mutatedFiles[relPath] {
|
||||
fi, err := copyFileWithMetadata(srcFilePath, destFilePath, info.IsDir())
|
||||
isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||
// In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
|
||||
// See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc
|
||||
// Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly
|
||||
isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
|
||||
if isDir || isReparsePoint || mutatedFiles[relPath] {
|
||||
fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() && !isReparsePoint {
|
||||
di = append(di, dirInfo{path: destFilePath, fileInfo: *fi})
|
||||
if isDir && !isReparsePoint {
|
||||
di = append(di, dirInfo{path: relPath, fileInfo: *fi})
|
||||
}
|
||||
} else {
|
||||
err = os.Link(srcFilePath, destFilePath)
|
||||
err = linkRelative(relPath, srcRoot, relPath, destRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Don't recurse on reparse points.
|
||||
if info.IsDir() && isReparsePoint {
|
||||
// Don't recurse on reparse points in go1.8 and older. Filepath.Walk
|
||||
// handles this in go1.9 and newer.
|
||||
if isDir && isReparsePoint && shouldSkipDirectoryReparse {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
@ -501,13 +584,11 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return reapplyDirectoryTimes(di)
|
||||
return reapplyDirectoryTimes(destRoot, di)
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
|
||||
w.reset()
|
||||
err := w.init()
|
||||
if err != nil {
|
||||
if err := w.reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -515,6 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
||||
return w.initUtilityVM()
|
||||
}
|
||||
|
||||
name = filepath.Clean(name)
|
||||
if hasPathPrefix(name, utilityVMPath) {
|
||||
if !w.HasUtilityVM {
|
||||
return errors.New("missing UtilityVM directory")
|
||||
@ -522,10 +604,9 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
||||
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
|
||||
return errors.New("invalid UtilityVM layer")
|
||||
}
|
||||
path := filepath.Join(w.destRoot, name)
|
||||
createDisposition := uint32(syscall.OPEN_EXISTING)
|
||||
createDisposition := uint32(_FILE_OPEN)
|
||||
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||
st, err := os.Lstat(path)
|
||||
st, err := lstatRelative(name, w.destRoot)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
@ -533,37 +614,44 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
||||
// Delete the existing file/directory if it is not the same type as this directory.
|
||||
existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||
if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
|
||||
if err = os.RemoveAll(path); err != nil {
|
||||
if err = removeAllRelative(name, w.destRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
st = nil
|
||||
}
|
||||
}
|
||||
if st == nil {
|
||||
if err = os.Mkdir(path, 0); err != nil {
|
||||
if err = mkdirRelative(name, w.destRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
w.uvmDi = append(w.uvmDi, dirInfo{path: path, fileInfo: *fileInfo})
|
||||
w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
|
||||
}
|
||||
} else {
|
||||
// Overwrite any existing hard link.
|
||||
err = os.Remove(path)
|
||||
err := removeRelative(name, w.destRoot)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
createDisposition = syscall.CREATE_NEW
|
||||
createDisposition = _FILE_CREATE
|
||||
}
|
||||
|
||||
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
|
||||
f, err := openRelative(
|
||||
name,
|
||||
w.destRoot,
|
||||
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
|
||||
syscall.FILE_SHARE_READ,
|
||||
createDisposition,
|
||||
_FILE_OPEN_REPARSE_POINT,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if f != nil {
|
||||
f.Close()
|
||||
os.Remove(path)
|
||||
removeRelative(name, w.destRoot)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -574,28 +662,31 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
||||
|
||||
w.backupWriter = winio.NewBackupFileWriter(f, true)
|
||||
w.currentFile = f
|
||||
w.currentFileName = name
|
||||
w.currentFileRoot = w.destRoot
|
||||
w.addedFiles[name] = true
|
||||
f = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
path := filepath.Join(w.root, name)
|
||||
fname := name
|
||||
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||
err := os.Mkdir(path, 0)
|
||||
err := mkdirRelative(name, w.root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path += ".$wcidirs$"
|
||||
fname += ".$wcidirs$"
|
||||
w.currentIsDir = true
|
||||
}
|
||||
|
||||
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.CREATE_NEW)
|
||||
f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if f != nil {
|
||||
f.Close()
|
||||
os.Remove(path)
|
||||
removeRelative(fname, w.root)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -617,19 +708,20 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
|
||||
}
|
||||
|
||||
w.currentFile = f
|
||||
w.currentFileName = name
|
||||
w.currentFileRoot = w.root
|
||||
w.addedFiles[name] = true
|
||||
f = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||
w.reset()
|
||||
err := w.init()
|
||||
if err != nil {
|
||||
if err := w.reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var roots []string
|
||||
target = filepath.Clean(target)
|
||||
var roots []*os.File
|
||||
if hasPathPrefix(target, filesPath) {
|
||||
// Look for cross-layer hard link targets in the parent layers, since
|
||||
// nothing is in the destination path yet.
|
||||
@ -638,7 +730,7 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||
// Since the utility VM is fully cloned into the destination path
|
||||
// already, look for cross-layer hard link targets directly in the
|
||||
// destination path.
|
||||
roots = []string{w.destRoot}
|
||||
roots = []*os.File{w.destRoot}
|
||||
}
|
||||
|
||||
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
|
||||
@ -647,12 +739,12 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||
|
||||
// Find to try the target of the link in a previously added file. If that
|
||||
// fails, search in parent layers.
|
||||
var selectedRoot string
|
||||
var selectedRoot *os.File
|
||||
if _, ok := w.addedFiles[target]; ok {
|
||||
selectedRoot = w.destRoot
|
||||
} else {
|
||||
for _, r := range roots {
|
||||
if _, err = os.Lstat(filepath.Join(r, target)); err != nil {
|
||||
if _, err := lstatRelative(target, r); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
@ -661,22 +753,25 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
if selectedRoot == "" {
|
||||
if selectedRoot == nil {
|
||||
return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
|
||||
}
|
||||
}
|
||||
|
||||
// The link can't be written until after the ImportLayer call.
|
||||
w.PendingLinks = append(w.PendingLinks, pendingLink{
|
||||
Path: filepath.Join(w.destRoot, name),
|
||||
Target: filepath.Join(selectedRoot, target),
|
||||
Path: name,
|
||||
Target: target,
|
||||
TargetRoot: selectedRoot,
|
||||
})
|
||||
w.addedFiles[name] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) Remove(name string) error {
|
||||
name = filepath.Clean(name)
|
||||
if hasPathPrefix(name, filesPath) {
|
||||
w.tombstones = append(w.tombstones, name[len(filesPath)+1:])
|
||||
w.Tombstones = append(w.Tombstones, name)
|
||||
} else if hasPathPrefix(name, utilityVMFilesPath) {
|
||||
err := w.initUtilityVM()
|
||||
if err != nil {
|
||||
@ -685,11 +780,10 @@ func (w *legacyLayerWriter) Remove(name string) error {
|
||||
// Make sure the path exists; os.RemoveAll will not fail if the file is
|
||||
// already gone, and this needs to be a fatal error for diagnostics
|
||||
// purposes.
|
||||
path := filepath.Join(w.destRoot, name)
|
||||
if _, err := os.Lstat(path); err != nil {
|
||||
if _, err := lstatRelative(name, w.destRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.RemoveAll(path)
|
||||
err = removeAllRelative(name, w.destRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -711,28 +805,20 @@ func (w *legacyLayerWriter) Write(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (w *legacyLayerWriter) Close() error {
|
||||
w.reset()
|
||||
err := w.init()
|
||||
if err != nil {
|
||||
if err := w.reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
tf, err := os.Create(filepath.Join(w.root, "tombstones.txt"))
|
||||
if err != nil {
|
||||
if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
defer tf.Close()
|
||||
_, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, t := range w.tombstones {
|
||||
_, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n"))
|
||||
for _, pd := range w.pendingDirs {
|
||||
err := mkdirRelative(pd.Path, pd.Root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if w.HasUtilityVM {
|
||||
err = reapplyDirectoryTimes(w.uvmDi)
|
||||
err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
7
vendor/github.com/Microsoft/hcsshim/legacy18.go
generated
vendored
Normal file
7
vendor/github.com/Microsoft/hcsshim/legacy18.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// +build !go1.9
|
||||
|
||||
package hcsshim
|
||||
|
||||
// Due to a bug in go1.8 and before, directory reparse points need to be skipped
|
||||
// during filepath.Walk. This is fixed in go1.9
|
||||
var shouldSkipDirectoryReparse = true
|
7
vendor/github.com/Microsoft/hcsshim/legacy19.go
generated
vendored
Normal file
7
vendor/github.com/Microsoft/hcsshim/legacy19.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// +build go1.9
|
||||
|
||||
package hcsshim
|
||||
|
||||
// Due to a bug in go1.8 and before, directory reparse points need to be skipped
|
||||
// during filepath.Walk. This is fixed in go1.9
|
||||
var shouldSkipDirectoryReparse = false
|
427
vendor/github.com/Microsoft/hcsshim/safeopen.go
generated
vendored
Normal file
427
vendor/github.com/Microsoft/hcsshim/safeopen.go
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
package hcsshim
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
winio "github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
//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
|
||||
//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile
|
||||
//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
|
||||
//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
|
||||
//sys localFree(ptr uintptr) = kernel32.LocalFree
|
||||
|
||||
type ioStatusBlock struct {
|
||||
Status, Information uintptr
|
||||
}
|
||||
|
||||
type objectAttributes struct {
|
||||
Length uintptr
|
||||
RootDirectory uintptr
|
||||
ObjectName uintptr
|
||||
Attributes uintptr
|
||||
SecurityDescriptor uintptr
|
||||
SecurityQoS uintptr
|
||||
}
|
||||
|
||||
type unicodeString struct {
|
||||
Length uint16
|
||||
MaximumLength uint16
|
||||
Buffer uintptr
|
||||
}
|
||||
|
||||
type fileLinkInformation struct {
|
||||
ReplaceIfExists bool
|
||||
RootDirectory uintptr
|
||||
FileNameLength uint32
|
||||
FileName [1]uint16
|
||||
}
|
||||
|
||||
type fileDispositionInformationEx struct {
|
||||
Flags uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
_FileLinkInformation = 11
|
||||
_FileDispositionInformationEx = 64
|
||||
|
||||
_FILE_READ_ATTRIBUTES = 0x0080
|
||||
_FILE_WRITE_ATTRIBUTES = 0x0100
|
||||
_DELETE = 0x10000
|
||||
|
||||
_FILE_OPEN = 1
|
||||
_FILE_CREATE = 2
|
||||
|
||||
_FILE_DIRECTORY_FILE = 0x00000001
|
||||
_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
|
||||
_FILE_DELETE_ON_CLOSE = 0x00001000
|
||||
_FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000
|
||||
_FILE_OPEN_REPARSE_POINT = 0x00200000
|
||||
|
||||
_FILE_DISPOSITION_DELETE = 0x00000001
|
||||
|
||||
_OBJ_DONT_REPARSE = 0x1000
|
||||
|
||||
_STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B
|
||||
)
|
||||
|
||||
func openRoot(path string) (*os.File, error) {
|
||||
longpath, err := makeLongAbsPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING)
|
||||
}
|
||||
|
||||
func ntRelativePath(path string) ([]uint16, error) {
|
||||
path = filepath.Clean(path)
|
||||
if strings.Contains(":", path) {
|
||||
// Since alternate data streams must follow the file they
|
||||
// are attached to, finding one here (out of order) is invalid.
|
||||
return nil, errors.New("path contains invalid character `:`")
|
||||
}
|
||||
fspath := filepath.FromSlash(path)
|
||||
if len(fspath) > 0 && fspath[0] == '\\' {
|
||||
return nil, errors.New("expected relative path")
|
||||
}
|
||||
|
||||
path16 := utf16.Encode(([]rune)(fspath))
|
||||
if len(path16) > 32767 {
|
||||
return nil, syscall.ENAMETOOLONG
|
||||
}
|
||||
|
||||
return path16, nil
|
||||
}
|
||||
|
||||
// openRelativeInternal opens a relative path from the given root, failing if
|
||||
// any of the intermediate path components are reparse points.
|
||||
func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
|
||||
var (
|
||||
h uintptr
|
||||
iosb ioStatusBlock
|
||||
oa objectAttributes
|
||||
)
|
||||
|
||||
path16, err := ntRelativePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if root == nil || root.Fd() == 0 {
|
||||
return nil, errors.New("missing root directory")
|
||||
}
|
||||
|
||||
upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2)
|
||||
defer localFree(upathBuffer)
|
||||
|
||||
upath := (*unicodeString)(unsafe.Pointer(upathBuffer))
|
||||
upath.Length = uint16(len(path16) * 2)
|
||||
upath.MaximumLength = upath.Length
|
||||
upath.Buffer = upathBuffer + unsafe.Sizeof(*upath)
|
||||
copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16)
|
||||
|
||||
oa.Length = unsafe.Sizeof(oa)
|
||||
oa.ObjectName = upathBuffer
|
||||
oa.RootDirectory = uintptr(root.Fd())
|
||||
oa.Attributes = _OBJ_DONT_REPARSE
|
||||
status := ntCreateFile(
|
||||
&h,
|
||||
accessMask|syscall.SYNCHRONIZE,
|
||||
&oa,
|
||||
&iosb,
|
||||
nil,
|
||||
0,
|
||||
shareFlags,
|
||||
createDisposition,
|
||||
_FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags,
|
||||
nil,
|
||||
0,
|
||||
)
|
||||
if status != 0 {
|
||||
return nil, rtlNtStatusToDosError(status)
|
||||
}
|
||||
|
||||
fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path))
|
||||
if err != nil {
|
||||
syscall.Close(syscall.Handle(h))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.NewFile(h, fullPath), nil
|
||||
}
|
||||
|
||||
// openRelative opens a relative path from the given root, failing if
|
||||
// any of the intermediate path components are reparse points.
|
||||
func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
|
||||
f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags)
|
||||
if err != nil {
|
||||
err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err}
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
|
||||
// linkRelative creates a hard link from oldname to newname (relative to oldroot
|
||||
// and newroot), failing if any of the intermediate path components are reparse
|
||||
// points.
|
||||
func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error {
|
||||
// Open the old file.
|
||||
oldf, err := openRelativeInternal(
|
||||
oldname,
|
||||
oldroot,
|
||||
syscall.FILE_WRITE_ATTRIBUTES,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_OPEN,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err}
|
||||
}
|
||||
defer oldf.Close()
|
||||
|
||||
// Open the parent of the new file.
|
||||
var parent *os.File
|
||||
parentPath := filepath.Dir(newname)
|
||||
if parentPath != "." {
|
||||
parent, err = openRelativeInternal(
|
||||
parentPath,
|
||||
newroot,
|
||||
syscall.GENERIC_READ,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_OPEN,
|
||||
_FILE_DIRECTORY_FILE)
|
||||
if err != nil {
|
||||
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err}
|
||||
}
|
||||
defer parent.Close()
|
||||
|
||||
fi, err := winio.GetFileBasicInfo(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
|
||||
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)}
|
||||
}
|
||||
|
||||
} else {
|
||||
parent = newroot
|
||||
}
|
||||
|
||||
// Issue an NT call to create the link. This will be safe because NT will
|
||||
// not open any more directories to create the link, so it cannot walk any
|
||||
// more reparse points.
|
||||
newbase := filepath.Base(newname)
|
||||
newbase16, err := ntRelativePath(newbase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2
|
||||
linkinfoBuffer := localAlloc(0, size)
|
||||
defer localFree(linkinfoBuffer)
|
||||
linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer))
|
||||
linkinfo.RootDirectory = parent.Fd()
|
||||
linkinfo.FileNameLength = uint32(len(newbase16) * 2)
|
||||
copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16)
|
||||
|
||||
var iosb ioStatusBlock
|
||||
status := ntSetInformationFile(
|
||||
oldf.Fd(),
|
||||
&iosb,
|
||||
linkinfoBuffer,
|
||||
uint32(size),
|
||||
_FileLinkInformation,
|
||||
)
|
||||
if status != 0 {
|
||||
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteOnClose marks a file to be deleted when the handle is closed.
|
||||
func deleteOnClose(f *os.File) error {
|
||||
disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE}
|
||||
var iosb ioStatusBlock
|
||||
status := ntSetInformationFile(
|
||||
f.Fd(),
|
||||
&iosb,
|
||||
uintptr(unsafe.Pointer(&disposition)),
|
||||
uint32(unsafe.Sizeof(disposition)),
|
||||
_FileDispositionInformationEx,
|
||||
)
|
||||
if status != 0 {
|
||||
return rtlNtStatusToDosError(status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// clearReadOnly clears the readonly attribute on a file.
|
||||
func clearReadOnly(f *os.File) error {
|
||||
bi, err := winio.GetFileBasicInfo(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 {
|
||||
return nil
|
||||
}
|
||||
sbi := winio.FileBasicInfo{
|
||||
FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY,
|
||||
}
|
||||
if sbi.FileAttributes == 0 {
|
||||
sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL
|
||||
}
|
||||
return winio.SetFileBasicInfo(f, &sbi)
|
||||
}
|
||||
|
||||
// removeRelative removes a file or directory relative to a root, failing if any
|
||||
// intermediate path components are reparse points.
|
||||
func removeRelative(path string, root *os.File) error {
|
||||
f, err := openRelativeInternal(
|
||||
path,
|
||||
root,
|
||||
_FILE_READ_ATTRIBUTES|_FILE_WRITE_ATTRIBUTES|_DELETE,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_OPEN,
|
||||
_FILE_OPEN_REPARSE_POINT)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
err = deleteOnClose(f)
|
||||
if err == syscall.ERROR_ACCESS_DENIED {
|
||||
// Maybe the file is marked readonly. Clear the bit and retry.
|
||||
clearReadOnly(f)
|
||||
err = deleteOnClose(f)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeAllRelative removes a directory tree relative to a root, failing if any
|
||||
// intermediate path components are reparse points.
|
||||
func removeAllRelative(path string, root *os.File) error {
|
||||
fi, err := lstatRelative(path, root)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||
if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
// If this is a reparse point, it can't have children. Simple remove will do.
|
||||
err := removeRelative(path, root)
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// It is necessary to use os.Open as Readdirnames does not work with
|
||||
// openRelative. This is safe because the above lstatrelative fails
|
||||
// if the target is outside the root, and we know this is not a
|
||||
// symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check.
|
||||
fd, err := os.Open(filepath.Join(root.Name(), path))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Race. It was deleted between the Lstat and Open.
|
||||
// Return nil per RemoveAll's docs.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove contents & return first error.
|
||||
for {
|
||||
names, err1 := fd.Readdirnames(100)
|
||||
for _, name := range names {
|
||||
err1 := removeAllRelative(path+string(os.PathSeparator)+name, root)
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
}
|
||||
if err1 == io.EOF {
|
||||
break
|
||||
}
|
||||
// If Readdirnames returned an error, use it.
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
// Remove directory.
|
||||
err1 := removeRelative(path, root)
|
||||
if err1 == nil || os.IsNotExist(err1) {
|
||||
return nil
|
||||
}
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// mkdirRelative creates a directory relative to a root, failing if any
|
||||
// intermediate path components are reparse points.
|
||||
func mkdirRelative(path string, root *os.File) error {
|
||||
f, err := openRelativeInternal(
|
||||
path,
|
||||
root,
|
||||
0,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_CREATE,
|
||||
_FILE_DIRECTORY_FILE)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
} else {
|
||||
err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// lstatRelative performs a stat operation on a file relative to a root, failing
|
||||
// if any intermediate path components are reparse points.
|
||||
func lstatRelative(path string, root *os.File) (os.FileInfo, error) {
|
||||
f, err := openRelativeInternal(
|
||||
path,
|
||||
root,
|
||||
_FILE_READ_ATTRIBUTES,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_OPEN,
|
||||
_FILE_OPEN_REPARSE_POINT)
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err}
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Stat()
|
||||
}
|
||||
|
||||
// ensureNotReparsePointRelative validates that a given file (relative to a
|
||||
// root) and all intermediate path components are not a reparse points.
|
||||
func ensureNotReparsePointRelative(path string, root *os.File) error {
|
||||
// Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT.
|
||||
f, err := openRelative(
|
||||
path,
|
||||
root,
|
||||
0,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
_FILE_OPEN,
|
||||
0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
return nil
|
||||
}
|
38
vendor/github.com/Microsoft/hcsshim/zhcsshim.go
generated
vendored
38
vendor/github.com/Microsoft/hcsshim/zhcsshim.go
generated
vendored
@ -41,6 +41,8 @@ var (
|
||||
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
|
||||
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||
procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId")
|
||||
@ -94,6 +96,11 @@ var (
|
||||
procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback")
|
||||
procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings")
|
||||
procHNSCall = modvmcompute.NewProc("HNSCall")
|
||||
procNtCreateFile = modntdll.NewProc("NtCreateFile")
|
||||
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
|
||||
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||
)
|
||||
|
||||
func coTaskMemFree(buffer unsafe.Pointer) {
|
||||
@ -1040,3 +1047,34 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
status = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) {
|
||||
r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0)
|
||||
status = uint32(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func rtlNtStatusToDosError(status uint32) (winerr error) {
|
||||
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
|
||||
if r0 != 0 {
|
||||
winerr = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localAlloc(flags uint32, size int) (ptr uintptr) {
|
||||
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0)
|
||||
ptr = uintptr(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func localFree(ptr uintptr) {
|
||||
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0)
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user