Add cimfs differ and snapshotter
Details about CimFs project are discussed in #8346 Signed-off-by: Amit Barve <ambarve@microsoft.com>
This commit is contained in:
291
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cim_writer_windows.go
generated
vendored
Normal file
291
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cim_writer_windows.go
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// CimFsWriter represents a writer to a single CimFS filesystem instance. On disk, the
|
||||
// image is composed of a filesystem file and several object ID and region files.
|
||||
// Note: The CimFsWriter isn't thread safe!
|
||||
type CimFsWriter struct {
|
||||
// name of this cim. Usually a <name>.cim file will be created to represent this cim.
|
||||
name string
|
||||
// handle is the CIMFS_IMAGE_HANDLE that must be passed when calling CIMFS APIs.
|
||||
handle winapi.FsHandle
|
||||
// name of the active file i.e the file to which we are currently writing.
|
||||
activeName string
|
||||
// stream to currently active file.
|
||||
activeStream winapi.StreamHandle
|
||||
// amount of bytes that can be written to the activeStream.
|
||||
activeLeft uint64
|
||||
}
|
||||
|
||||
// Create creates a new cim image. The CimFsWriter returned can then be used to do
|
||||
// operations on this cim.
|
||||
func Create(imagePath string, oldFSName string, newFSName string) (_ *CimFsWriter, err error) {
|
||||
var oldNameBytes *uint16
|
||||
// CimCreateImage API call has different behavior if the value of oldNameBytes / newNameBytes
|
||||
// is empty than if it is nil. So we have to convert those strings into *uint16 here.
|
||||
fsName := oldFSName
|
||||
if oldFSName != "" {
|
||||
oldNameBytes, err = windows.UTF16PtrFromString(oldFSName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var newNameBytes *uint16
|
||||
if newFSName != "" {
|
||||
fsName = newFSName
|
||||
newNameBytes, err = windows.UTF16PtrFromString(newFSName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var handle winapi.FsHandle
|
||||
if err := winapi.CimCreateImage(imagePath, oldNameBytes, newNameBytes, &handle); err != nil {
|
||||
return nil, fmt.Errorf("failed to create cim image at path %s, oldName: %s, newName: %s: %w", imagePath, oldFSName, newFSName, err)
|
||||
}
|
||||
return &CimFsWriter{handle: handle, name: filepath.Join(imagePath, fsName)}, nil
|
||||
}
|
||||
|
||||
// CreateAlternateStream creates alternate stream of given size at the given path inside the cim. This will
|
||||
// replace the current active stream. Always, finish writing current active stream and then create an
|
||||
// alternate stream.
|
||||
func (c *CimFsWriter) CreateAlternateStream(path string, size uint64) (err error) {
|
||||
err = c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCreateAlternateStream(c.handle, path, size, &c.activeStream)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create alternate stream for path %s: %w", path, err)
|
||||
}
|
||||
c.activeName = path
|
||||
return nil
|
||||
}
|
||||
|
||||
// closes the currently active stream.
|
||||
func (c *CimFsWriter) closeStream() error {
|
||||
if c.activeStream == 0 {
|
||||
return nil
|
||||
}
|
||||
err := winapi.CimCloseStream(c.activeStream)
|
||||
if err == nil && c.activeLeft > 0 {
|
||||
// Validate here because CimCloseStream does not and this improves error
|
||||
// reporting. Otherwise the error will occur in the context of
|
||||
// cimWriteStream.
|
||||
err = fmt.Errorf("incomplete write, %d bytes left in the stream %s", c.activeLeft, c.activeName)
|
||||
}
|
||||
if err != nil {
|
||||
err = &PathError{Cim: c.name, Op: "closeStream", Path: c.activeName, Err: err}
|
||||
}
|
||||
c.activeLeft = 0
|
||||
c.activeStream = 0
|
||||
c.activeName = ""
|
||||
return err
|
||||
}
|
||||
|
||||
// AddFile adds a new file to the image. The file is added at the specified path. After
|
||||
// calling this function, the file is set as the active stream for the image, so data can
|
||||
// be written by calling `Write`.
|
||||
func (c *CimFsWriter) AddFile(path string, info *winio.FileBasicInfo, fileSize int64, securityDescriptor []byte, extendedAttributes []byte, reparseData []byte) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileMetadata := &winapi.CimFsFileMetadata{
|
||||
Attributes: info.FileAttributes,
|
||||
FileSize: fileSize,
|
||||
CreationTime: info.CreationTime,
|
||||
LastWriteTime: info.LastWriteTime,
|
||||
ChangeTime: info.ChangeTime,
|
||||
LastAccessTime: info.LastAccessTime,
|
||||
}
|
||||
if len(securityDescriptor) == 0 {
|
||||
// Passing an empty security descriptor creates a CIM in a weird state.
|
||||
// Pass the NULL DACL.
|
||||
securityDescriptor = nullSd
|
||||
}
|
||||
fileMetadata.SecurityDescriptorBuffer = unsafe.Pointer(&securityDescriptor[0])
|
||||
fileMetadata.SecurityDescriptorSize = uint32(len(securityDescriptor))
|
||||
if len(reparseData) > 0 {
|
||||
fileMetadata.ReparseDataBuffer = unsafe.Pointer(&reparseData[0])
|
||||
fileMetadata.ReparseDataSize = uint32(len(reparseData))
|
||||
}
|
||||
if len(extendedAttributes) > 0 {
|
||||
fileMetadata.ExtendedAttributes = unsafe.Pointer(&extendedAttributes[0])
|
||||
fileMetadata.EACount = uint32(len(extendedAttributes))
|
||||
}
|
||||
// remove the trailing `\` if present, otherwise it trips off the cim writer
|
||||
path = strings.TrimSuffix(path, "\\")
|
||||
err = winapi.CimCreateFile(c.handle, path, fileMetadata, &c.activeStream)
|
||||
if err != nil {
|
||||
return &PathError{Cim: c.name, Op: "addFile", Path: path, Err: err}
|
||||
}
|
||||
c.activeName = path
|
||||
if info.FileAttributes&(windows.FILE_ATTRIBUTE_DIRECTORY) == 0 {
|
||||
c.activeLeft = uint64(fileSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes bytes to the active stream.
|
||||
func (c *CimFsWriter) Write(p []byte) (int, error) {
|
||||
if c.activeStream == 0 {
|
||||
return 0, fmt.Errorf("no active stream")
|
||||
}
|
||||
if uint64(len(p)) > c.activeLeft {
|
||||
return 0, &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: fmt.Errorf("wrote too much")}
|
||||
}
|
||||
err := winapi.CimWriteStream(c.activeStream, uintptr(unsafe.Pointer(&p[0])), uint32(len(p)))
|
||||
if err != nil {
|
||||
err = &PathError{Cim: c.name, Op: "write", Path: c.activeName, Err: err}
|
||||
return 0, err
|
||||
}
|
||||
c.activeLeft -= uint64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// AddLink adds a hard link from `oldPath` to `newPath` in the image.
|
||||
func (c *CimFsWriter) AddLink(oldPath string, newPath string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCreateHardLink(c.handle, newPath, oldPath)
|
||||
if err != nil {
|
||||
err = &LinkError{Cim: c.name, Op: "addLink", Old: oldPath, New: newPath, Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unlink deletes the file at `path` from the image.
|
||||
func (c *CimFsWriter) Unlink(path string) error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//TODO(ambarve): CimDeletePath currently returns an error if the file isn't found but we ideally want
|
||||
// to put a tombstone at that path so that when cims are merged it removes that file from the lower
|
||||
// layer
|
||||
err = winapi.CimDeletePath(c.handle, path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
err = &PathError{Cim: c.name, Op: "unlink", Path: path, Err: err}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CimFsWriter) commit() error {
|
||||
err := c.closeStream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winapi.CimCommitImage(c.handle)
|
||||
if err != nil {
|
||||
err = &OpError{Cim: c.name, Op: "commit", Err: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the CimFS filesystem.
|
||||
func (c *CimFsWriter) Close() error {
|
||||
if c.handle == 0 {
|
||||
return fmt.Errorf("invalid writer")
|
||||
}
|
||||
if err := c.commit(); err != nil {
|
||||
return &OpError{Cim: c.name, Op: "commit", Err: err}
|
||||
}
|
||||
if err := winapi.CimCloseImage(c.handle); err != nil {
|
||||
return &OpError{Cim: c.name, Op: "close", Err: err}
|
||||
}
|
||||
c.handle = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroyCim finds out the region files, object files of this cim and then delete
|
||||
// the region files, object files and the <layer-id>.cim file itself.
|
||||
func DestroyCim(ctx context.Context, cimPath string) (retErr error) {
|
||||
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("get region files for cim %s", cimPath)
|
||||
if retErr == nil { //nolint:govet // nilness: consistency with below
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("get objectid file for cim %s", cimPath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
|
||||
log.G(ctx).WithFields(logrus.Fields{
|
||||
"cimPath": cimPath,
|
||||
"regionFiles": regionFilePaths,
|
||||
"objectFiles": objectFilePaths,
|
||||
}).Debug("destroy cim")
|
||||
|
||||
for _, regFilePath := range regionFilePaths {
|
||||
if err := os.Remove(regFilePath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", regFilePath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, objFilePath := range objectFilePaths {
|
||||
if err := os.Remove(objFilePath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", objFilePath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Remove(cimPath); err != nil {
|
||||
log.G(ctx).WithError(err).Warnf("remove file %s", cimPath)
|
||||
if retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
// GetCimUsage returns the total disk usage in bytes by the cim at path `cimPath`.
|
||||
func GetCimUsage(ctx context.Context, cimPath string) (uint64, error) {
|
||||
regionFilePaths, err := getRegionFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("get region file paths for cim %s: %w", cimPath, err)
|
||||
}
|
||||
objectFilePaths, err := getObjectIDFilePaths(ctx, cimPath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("get objectid file for cim %s: %w", cimPath, err)
|
||||
}
|
||||
|
||||
var totalUsage uint64
|
||||
for _, f := range append(regionFilePaths, objectFilePaths...) {
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("stat file %s: %w", f, err)
|
||||
}
|
||||
totalUsage += uint64(fi.Size())
|
||||
}
|
||||
return totalUsage, nil
|
||||
}
|
||||
17
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cimfs.go
generated
vendored
Normal file
17
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/cimfs.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/hcsshim/osversion"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func IsCimFSSupported() bool {
|
||||
rv, err := osversion.BuildRevision()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Warn("get build revision")
|
||||
}
|
||||
return osversion.Build() == 20348 && rv >= 2031
|
||||
}
|
||||
134
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/common.go
generated
vendored
Normal file
134
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/common.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/pkg/cimfs/format"
|
||||
)
|
||||
|
||||
var (
|
||||
// Equivalent to SDDL of "D:NO_ACCESS_CONTROL".
|
||||
nullSd = []byte{1, 0, 4, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
)
|
||||
|
||||
type OpError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *OpError) Error() string {
|
||||
s := "cim " + e.Op + " " + e.Cim
|
||||
s += ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
// PathError is the error type returned by most functions in this package.
|
||||
type PathError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PathError) Error() string {
|
||||
s := "cim " + e.Op + " " + e.Cim
|
||||
s += ":" + e.Path
|
||||
s += ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
type LinkError struct {
|
||||
Cim string
|
||||
Op string
|
||||
Old string
|
||||
New string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *LinkError) Error() string {
|
||||
return "cim " + e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
func validateHeader(h *format.CommonHeader) error {
|
||||
if !bytes.Equal(h.Magic[:], format.MagicValue[:]) {
|
||||
return fmt.Errorf("not a cim file")
|
||||
}
|
||||
if h.Version.Major > format.CurrentVersion.Major || h.Version.Major < format.MinSupportedVersion.Major {
|
||||
return fmt.Errorf("unsupported cim version. cim version %v must be between %v & %v", h.Version, format.MinSupportedVersion, format.CurrentVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFilesystemHeader(f *os.File) (format.FilesystemHeader, error) {
|
||||
var fsh format.FilesystemHeader
|
||||
|
||||
if err := binary.Read(f, binary.LittleEndian, &fsh); err != nil {
|
||||
return fsh, fmt.Errorf("reading filesystem header: %w", err)
|
||||
}
|
||||
|
||||
if err := validateHeader(&fsh.Common); err != nil {
|
||||
return fsh, fmt.Errorf("validating filesystem header: %w", err)
|
||||
}
|
||||
return fsh, nil
|
||||
}
|
||||
|
||||
// Returns the paths of all the objectID files associated with the cim at `cimPath`.
|
||||
func getObjectIDFilePaths(ctx context.Context, cimPath string) ([]string, error) {
|
||||
f, err := os.Open(cimPath)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fsh, err := readFilesystemHeader(f)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("readingp cim header: %w", err)
|
||||
}
|
||||
|
||||
paths := []string{}
|
||||
for i := 0; i < int(fsh.Regions.Count); i++ {
|
||||
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.ObjectIDFileName, fsh.Regions.ID, i))
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
} else {
|
||||
log.G(ctx).WithError(err).Warnf("stat for object file %s", path)
|
||||
}
|
||||
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// Returns the paths of all the region files associated with the cim at `cimPath`.
|
||||
func getRegionFilePaths(ctx context.Context, cimPath string) ([]string, error) {
|
||||
f, err := os.Open(cimPath)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("open cim file %s: %w", cimPath, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fsh, err := readFilesystemHeader(f)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("reading cim header: %w", err)
|
||||
}
|
||||
|
||||
paths := []string{}
|
||||
for i := 0; i < int(fsh.Regions.Count); i++ {
|
||||
path := filepath.Join(filepath.Dir(cimPath), fmt.Sprintf("%s_%v_%d", format.RegionFileName, fsh.Regions.ID, i))
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
} else {
|
||||
log.G(ctx).WithError(err).Warnf("stat for region file %s", path)
|
||||
}
|
||||
}
|
||||
return paths, nil
|
||||
}
|
||||
3
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/doc.go
generated
vendored
Normal file
3
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// This package provides simple go wrappers on top of the win32 CIMFS mount APIs.
|
||||
// The mounting/unmount of cim layers is done by the cim mount functions the internal/wclayer/cim package.
|
||||
package cimfs
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/doc.go
generated
vendored
Normal file
4
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/doc.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// format package maintains some basic structures to allows us to read header of a cim file. This is mostly
|
||||
// required to understand the region & objectid files associated with a particular cim. Otherwise, we don't
|
||||
// need to parse the cim format.
|
||||
package format
|
||||
61
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/format.go
generated
vendored
Normal file
61
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package format
|
||||
|
||||
import "github.com/Microsoft/go-winio/pkg/guid"
|
||||
|
||||
const (
|
||||
RegionFileName = "region"
|
||||
ObjectIDFileName = "objectid"
|
||||
)
|
||||
|
||||
// Magic specifies the magic number at the beginning of a file.
|
||||
type Magic [8]uint8
|
||||
|
||||
var MagicValue = Magic([8]uint8{'c', 'i', 'm', 'f', 'i', 'l', 'e', '0'})
|
||||
|
||||
type Version struct {
|
||||
Major, Minor uint32
|
||||
}
|
||||
|
||||
var CurrentVersion = Version{3, 0}
|
||||
|
||||
var MinSupportedVersion = Version{2, 0}
|
||||
|
||||
type FileType uint8
|
||||
|
||||
// RegionOffset encodes an offset to objects as index of the region file
|
||||
// containing the object and the byte offset within that file.
|
||||
type RegionOffset uint64
|
||||
|
||||
// CommonHeader is the common header for all CIM-related files.
|
||||
type CommonHeader struct {
|
||||
Magic Magic
|
||||
HeaderLength uint32
|
||||
Type FileType
|
||||
Reserved uint8
|
||||
Reserved2 uint16
|
||||
Version Version
|
||||
Reserved3 uint64
|
||||
}
|
||||
|
||||
type RegionSet struct {
|
||||
ID guid.GUID
|
||||
Count uint16
|
||||
Reserved uint16
|
||||
Reserved1 uint32
|
||||
}
|
||||
|
||||
// FilesystemHeader is the header for a filesystem file.
|
||||
//
|
||||
// The filesystem file points to the filesystem object inside a region
|
||||
// file and specifies regions sets.
|
||||
type FilesystemHeader struct {
|
||||
Common CommonHeader
|
||||
Regions RegionSet
|
||||
FilesystemOffset RegionOffset
|
||||
Reserved uint32
|
||||
Reserved1 uint16
|
||||
ParentCount uint16
|
||||
}
|
||||
65
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/mount_cim.go
generated
vendored
Normal file
65
vendor/github.com/Microsoft/hcsshim/pkg/cimfs/mount_cim.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cimfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio/pkg/guid"
|
||||
"github.com/Microsoft/hcsshim/internal/winapi"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type MountError struct {
|
||||
Cim string
|
||||
Op string
|
||||
VolumeGUID guid.GUID
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MountError) Error() string {
|
||||
s := "cim " + e.Op
|
||||
if e.Cim != "" {
|
||||
s += " " + e.Cim
|
||||
}
|
||||
s += " " + e.VolumeGUID.String() + ": " + e.Err.Error()
|
||||
return s
|
||||
}
|
||||
|
||||
// Mount mounts the given cim at a volume with given GUID. Returns the full volume
|
||||
// path if mount is successful.
|
||||
func Mount(cimPath string, volumeGUID guid.GUID, mountFlags uint32) (string, error) {
|
||||
if err := winapi.CimMountImage(filepath.Dir(cimPath), filepath.Base(cimPath), mountFlags, &volumeGUID); err != nil {
|
||||
return "", &MountError{Cim: cimPath, Op: "Mount", VolumeGUID: volumeGUID, Err: err}
|
||||
}
|
||||
return fmt.Sprintf("\\\\?\\Volume{%s}\\", volumeGUID.String()), nil
|
||||
}
|
||||
|
||||
// Unmount unmounts the cim at mounted at path `volumePath`.
|
||||
func Unmount(volumePath string) error {
|
||||
// The path is expected to be in the \\?\Volume{GUID}\ format
|
||||
if volumePath[len(volumePath)-1] != '\\' {
|
||||
volumePath += "\\"
|
||||
}
|
||||
|
||||
if !(strings.HasPrefix(volumePath, "\\\\?\\Volume{") && strings.HasSuffix(volumePath, "}\\")) {
|
||||
return errors.Errorf("volume path %s is not in the expected format", volumePath)
|
||||
}
|
||||
|
||||
trimmedStr := strings.TrimPrefix(volumePath, "\\\\?\\Volume{")
|
||||
trimmedStr = strings.TrimSuffix(trimmedStr, "}\\")
|
||||
|
||||
volGUID, err := guid.FromString(trimmedStr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "guid parsing failed for %s", trimmedStr)
|
||||
}
|
||||
|
||||
if err := winapi.CimDismountImage(&volGUID); err != nil {
|
||||
return &MountError{VolumeGUID: volGUID, Op: "Unmount", Err: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
10
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go
generated
vendored
10
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go
generated
vendored
@@ -89,7 +89,7 @@ func putBuf(b *bytes.Buffer) {
|
||||
bytesBufferPool.Put(b)
|
||||
}
|
||||
|
||||
// Runhcs is the client to the runhcs cli
|
||||
// Runhcs is the client to the runhcs cli.
|
||||
type Runhcs struct {
|
||||
// Debug enables debug output for logging.
|
||||
Debug bool
|
||||
@@ -130,8 +130,8 @@ func (r *Runhcs) args() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Runhcs) command(context context.Context, args ...string) *exec.Cmd {
|
||||
cmd := exec.CommandContext(context, getCommandPath(), append(r.args(), args...)...)
|
||||
func (r *Runhcs) command(ctx context.Context, args ...string) *exec.Cmd {
|
||||
cmd := exec.CommandContext(ctx, getCommandPath(), append(r.args(), args...)...)
|
||||
cmd.Env = os.Environ()
|
||||
return cmd
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (r *Runhcs) command(context context.Context, args ...string) *exec.Cmd {
|
||||
// runOrError will run the provided command. If an error is
|
||||
// encountered and neither Stdout or Stderr was set the error and the
|
||||
// stderr of the command will be returned in the format of <error>:
|
||||
// <stderr>
|
||||
// <stderr>.
|
||||
func (r *Runhcs) runOrError(cmd *exec.Cmd) error {
|
||||
if cmd.Stdout != nil || cmd.Stderr != nil {
|
||||
ec, err := runc.Monitor.Start(cmd)
|
||||
@@ -154,7 +154,7 @@ func (r *Runhcs) runOrError(cmd *exec.Cmd) error {
|
||||
}
|
||||
data, err := cmdOutput(cmd, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", err, data)
|
||||
return fmt.Errorf("%s: %s", err, data) //nolint:errorlint // legacy code
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
8
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go
generated
vendored
8
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go
generated
vendored
@@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
// CreateScratch creates a scratch vhdx at 'destpath' that is ext4 formatted.
|
||||
func (r *Runhcs) CreateScratch(context context.Context, destpath string) error {
|
||||
return r.CreateScratchWithOpts(context, destpath, nil)
|
||||
func (r *Runhcs) CreateScratch(ctx context.Context, destpath string) error {
|
||||
return r.CreateScratchWithOpts(ctx, destpath, nil)
|
||||
}
|
||||
|
||||
// CreateScratchOpts is the set of options that can be used with the
|
||||
@@ -43,7 +43,7 @@ func (opt *CreateScratchOpts) args() ([]string, error) {
|
||||
|
||||
// CreateScratchWithOpts creates a scratch vhdx at 'destpath' that is ext4
|
||||
// formatted based on `opts`.
|
||||
func (r *Runhcs) CreateScratchWithOpts(context context.Context, destpath string, opts *CreateScratchOpts) error {
|
||||
func (r *Runhcs) CreateScratchWithOpts(ctx context.Context, destpath string, opts *CreateScratchOpts) error {
|
||||
args := []string{"create-scratch", "--destpath", destpath}
|
||||
if opts != nil {
|
||||
oargs, err := opts.args()
|
||||
@@ -52,5 +52,5 @@ func (r *Runhcs) CreateScratchWithOpts(context context.Context, destpath string,
|
||||
}
|
||||
args = append(args, oargs...)
|
||||
}
|
||||
return r.runOrError(r.command(context, args...))
|
||||
return r.runOrError(r.command(ctx, args...))
|
||||
}
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go
generated
vendored
@@ -64,7 +64,7 @@ func (opt *CreateOpts) args() ([]string, error) {
|
||||
|
||||
// Create creates a new container and returns its pid if it was created
|
||||
// successfully.
|
||||
func (r *Runhcs) Create(context context.Context, id, bundle string, opts *CreateOpts) error {
|
||||
func (r *Runhcs) Create(ctx context.Context, id, bundle string, opts *CreateOpts) error {
|
||||
args := []string{"create", "--bundle", bundle}
|
||||
if opts != nil {
|
||||
oargs, err := opts.args()
|
||||
@@ -73,14 +73,14 @@ func (r *Runhcs) Create(context context.Context, id, bundle string, opts *Create
|
||||
}
|
||||
args = append(args, oargs...)
|
||||
}
|
||||
cmd := r.command(context, append(args, id)...)
|
||||
cmd := r.command(ctx, append(args, id)...)
|
||||
if opts != nil && opts.IO != nil {
|
||||
opts.Set(cmd)
|
||||
}
|
||||
if cmd.Stdout == nil && cmd.Stderr == nil {
|
||||
data, err := cmdOutput(cmd, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", err, data)
|
||||
return fmt.Errorf("%s: %s", err, data) //nolint:errorlint // legacy code
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go
generated
vendored
@@ -22,7 +22,7 @@ func (opt *DeleteOpts) args() ([]string, error) {
|
||||
|
||||
// Delete any resources held by the container often used with detached
|
||||
// containers.
|
||||
func (r *Runhcs) Delete(context context.Context, id string, opts *DeleteOpts) error {
|
||||
func (r *Runhcs) Delete(ctx context.Context, id string, opts *DeleteOpts) error {
|
||||
args := []string{"delete"}
|
||||
if opts != nil {
|
||||
oargs, err := opts.args()
|
||||
@@ -31,5 +31,5 @@ func (r *Runhcs) Delete(context context.Context, id string, opts *DeleteOpts) er
|
||||
}
|
||||
args = append(args, oargs...)
|
||||
}
|
||||
return r.runOrError(r.command(context, append(args, id)...))
|
||||
return r.runOrError(r.command(ctx, append(args, id)...))
|
||||
}
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go
generated
vendored
@@ -51,7 +51,7 @@ func (opt *ExecOpts) args() ([]string, error) {
|
||||
|
||||
// Exec executes an additional process inside the container based on the
|
||||
// oci.Process spec found at processFile.
|
||||
func (r *Runhcs) Exec(context context.Context, id, processFile string, opts *ExecOpts) error {
|
||||
func (r *Runhcs) Exec(ctx context.Context, id, processFile string, opts *ExecOpts) error {
|
||||
args := []string{"exec", "--process", processFile}
|
||||
if opts != nil {
|
||||
oargs, err := opts.args()
|
||||
@@ -60,14 +60,14 @@ func (r *Runhcs) Exec(context context.Context, id, processFile string, opts *Exe
|
||||
}
|
||||
args = append(args, oargs...)
|
||||
}
|
||||
cmd := r.command(context, append(args, id)...)
|
||||
cmd := r.command(ctx, append(args, id)...)
|
||||
if opts != nil && opts.IO != nil {
|
||||
opts.Set(cmd)
|
||||
}
|
||||
if cmd.Stdout == nil && cmd.Stderr == nil {
|
||||
data, err := cmdOutput(cmd, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", err, data)
|
||||
return fmt.Errorf("%s: %s", err, data) //nolint:errorlint // legacy code
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go
generated
vendored
@@ -8,6 +8,6 @@ import (
|
||||
|
||||
// Kill sends the specified signal (default: SIGTERM) to the container's init
|
||||
// process.
|
||||
func (r *Runhcs) Kill(context context.Context, id, signal string) error {
|
||||
return r.runOrError(r.command(context, "kill", id, signal))
|
||||
func (r *Runhcs) Kill(ctx context.Context, id, signal string) error {
|
||||
return r.runOrError(r.command(ctx, "kill", id, signal))
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go
generated
vendored
@@ -17,8 +17,8 @@ type ContainerState = irunhcs.ContainerState
|
||||
//
|
||||
// Note: This is specific to the Runhcs.Root namespace provided in the global
|
||||
// settings.
|
||||
func (r *Runhcs) List(context context.Context) ([]*ContainerState, error) {
|
||||
data, err := cmdOutput(r.command(context, "list", "--format=json"), false)
|
||||
func (r *Runhcs) List(ctx context.Context) ([]*ContainerState, error) {
|
||||
data, err := cmdOutput(r.command(ctx, "list", "--format=json"), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go
generated
vendored
@@ -7,6 +7,6 @@ import (
|
||||
)
|
||||
|
||||
// Pause suspends all processes inside the container.
|
||||
func (r *Runhcs) Pause(context context.Context, id string) error {
|
||||
return r.runOrError(r.command(context, "pause", id))
|
||||
func (r *Runhcs) Pause(ctx context.Context, id string) error {
|
||||
return r.runOrError(r.command(ctx, "pause", id))
|
||||
}
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go
generated
vendored
@@ -9,10 +9,10 @@ import (
|
||||
)
|
||||
|
||||
// Ps displays the processes running inside a container.
|
||||
func (r *Runhcs) Ps(context context.Context, id string) ([]int, error) {
|
||||
data, err := cmdOutput(r.command(context, "ps", "--format=json", id), true)
|
||||
func (r *Runhcs) Ps(ctx context.Context, id string) ([]int, error) {
|
||||
data, err := cmdOutput(r.command(ctx, "ps", "--format=json", id), true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", err, data)
|
||||
return nil, fmt.Errorf("%s: %s", err, data) //nolint:errorlint // legacy code
|
||||
}
|
||||
var out []int
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go
generated
vendored
@@ -22,7 +22,7 @@ func (opt *ResizeTTYOpts) args() ([]string, error) {
|
||||
}
|
||||
|
||||
// ResizeTTY updates the terminal size for a container process.
|
||||
func (r *Runhcs) ResizeTTY(context context.Context, id string, width, height uint16, opts *ResizeTTYOpts) error {
|
||||
func (r *Runhcs) ResizeTTY(ctx context.Context, id string, width, height uint16, opts *ResizeTTYOpts) error {
|
||||
args := []string{"resize-tty"}
|
||||
if opts != nil {
|
||||
oargs, err := opts.args()
|
||||
@@ -31,5 +31,5 @@ func (r *Runhcs) ResizeTTY(context context.Context, id string, width, height uin
|
||||
}
|
||||
args = append(args, oargs...)
|
||||
}
|
||||
return r.runOrError(r.command(context, append(args, id, strconv.FormatUint(uint64(width), 10), strconv.FormatUint(uint64(height), 10))...))
|
||||
return r.runOrError(r.command(ctx, append(args, id, strconv.FormatUint(uint64(width), 10), strconv.FormatUint(uint64(height), 10))...))
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go
generated
vendored
@@ -7,6 +7,6 @@ import (
|
||||
)
|
||||
|
||||
// Resume resumes all processes that have been previously paused.
|
||||
func (r *Runhcs) Resume(context context.Context, id string) error {
|
||||
return r.runOrError(r.command(context, "resume", id))
|
||||
func (r *Runhcs) Resume(ctx context.Context, id string) error {
|
||||
return r.runOrError(r.command(ctx, "resume", id))
|
||||
}
|
||||
|
||||
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go
generated
vendored
4
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go
generated
vendored
@@ -7,6 +7,6 @@ import (
|
||||
)
|
||||
|
||||
// Start will start an already created container.
|
||||
func (r *Runhcs) Start(context context.Context, id string) error {
|
||||
return r.runOrError(r.command(context, "start", id))
|
||||
func (r *Runhcs) Start(ctx context.Context, id string) error {
|
||||
return r.runOrError(r.command(ctx, "start", id))
|
||||
}
|
||||
|
||||
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go
generated
vendored
6
vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go
generated
vendored
@@ -9,10 +9,10 @@ import (
|
||||
)
|
||||
|
||||
// State outputs the state of a container.
|
||||
func (r *Runhcs) State(context context.Context, id string) (*ContainerState, error) {
|
||||
data, err := cmdOutput(r.command(context, "state", id), true)
|
||||
func (r *Runhcs) State(ctx context.Context, id string) (*ContainerState, error) {
|
||||
data, err := cmdOutput(r.command(ctx, "state", id), true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %s", err, data)
|
||||
return nil, fmt.Errorf("%s: %s", err, data) //nolint:errorlint // legacy code
|
||||
}
|
||||
var out ContainerState
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
|
||||
166
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/cim/import.go
generated
vendored
Normal file
166
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/cim/import.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cim
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio/backuptar"
|
||||
"github.com/Microsoft/hcsshim/internal/log"
|
||||
"github.com/Microsoft/hcsshim/internal/wclayer/cim"
|
||||
"github.com/Microsoft/hcsshim/pkg/ociwclayer"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// ImportCimLayerFromTar reads a layer from an OCI layer tar stream and extracts it into
|
||||
// the CIM format at the specified path. The caller must specify the parent layers, if
|
||||
// any, ordered from lowest to highest layer.
|
||||
// This function expects that the layer paths (both the layer that is being imported & the parent layers) are
|
||||
// formatted like `.../snapshots/<id>` and the corresponding layer CIMs are located/will be created at
|
||||
// `.../snapshots/cim-layers/<id>.cim`. Each CIM file also has corresponding region & objectID files and those
|
||||
// files will also be stored inside the `cim-layers` directory.
|
||||
//
|
||||
// This function returns the total size of the layer's files, in bytes.
|
||||
func ImportCimLayerFromTar(ctx context.Context, r io.Reader, layerPath string, parentLayerPaths []string) (int64, error) {
|
||||
err := os.MkdirAll(layerPath, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
w, err := cim.NewCimLayerWriter(ctx, layerPath, parentLayerPaths)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := writeCimLayerFromTar(ctx, r, w, layerPath)
|
||||
cerr := w.Close(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if cerr != nil {
|
||||
return 0, cerr
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func writeCimLayerFromTar(ctx context.Context, r io.Reader, w *cim.CimLayerWriter, layerPath string) (int64, error) {
|
||||
tr := tar.NewReader(r)
|
||||
buf := bufio.NewWriter(w)
|
||||
size := int64(0)
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
hdr, loopErr := tr.Next()
|
||||
for loopErr == nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return 0, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Note: path is used instead of filepath to prevent OS specific handling
|
||||
// of the tar path
|
||||
base := path.Base(hdr.Name)
|
||||
if strings.HasPrefix(base, ociwclayer.WhiteoutPrefix) {
|
||||
name := path.Join(path.Dir(hdr.Name), base[len(ociwclayer.WhiteoutPrefix):])
|
||||
if rErr := w.Remove(filepath.FromSlash(name)); rErr != nil {
|
||||
return 0, rErr
|
||||
}
|
||||
hdr, loopErr = tr.Next()
|
||||
} else if hdr.Typeflag == tar.TypeLink {
|
||||
if linkErr := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)); linkErr != nil {
|
||||
return 0, linkErr
|
||||
}
|
||||
hdr, loopErr = tr.Next()
|
||||
} else {
|
||||
name, fileSize, fileInfo, err := backuptar.FileInfoFromHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sddl, err := backuptar.SecurityDescriptorFromTarHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
eadata, err := backuptar.ExtendedAttributesFromTarHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var reparse []byte
|
||||
// As of now the only valid reparse data in a layer will be for a symlink. If file is
|
||||
// a symlink set reparse attribute and ensure reparse data buffer isn't
|
||||
// empty. Otherwise remove the reparse attributed.
|
||||
fileInfo.FileAttributes &^= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
if hdr.Typeflag == tar.TypeSymlink {
|
||||
reparse = backuptar.EncodeReparsePointFromTarHeader(hdr)
|
||||
if len(reparse) > 0 {
|
||||
fileInfo.FileAttributes |= uint32(windows.FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
}
|
||||
}
|
||||
|
||||
if addErr := w.Add(filepath.FromSlash(name), fileInfo, fileSize, sddl, eadata, reparse); addErr != nil {
|
||||
return 0, addErr
|
||||
}
|
||||
if hdr.Typeflag == tar.TypeReg {
|
||||
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
|
||||
return 0, cpErr
|
||||
}
|
||||
}
|
||||
size += fileSize
|
||||
|
||||
// Copy all the alternate data streams and return the next non-ADS header.
|
||||
var ahdr *tar.Header
|
||||
for {
|
||||
ahdr, loopErr = tr.Next()
|
||||
if loopErr != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") {
|
||||
hdr = ahdr
|
||||
break
|
||||
}
|
||||
|
||||
// stream names have following format: '<filename>:<stream name>:$DATA'
|
||||
// $DATA is one of the valid types of streams. We currently only support
|
||||
// data streams so fail if this is some other type of stream.
|
||||
if !strings.HasSuffix(ahdr.Name, ":$DATA") {
|
||||
return 0, fmt.Errorf("stream types other than $DATA are not supported, found: %s", ahdr.Name)
|
||||
}
|
||||
|
||||
if addErr := w.AddAlternateStream(filepath.FromSlash(ahdr.Name), uint64(ahdr.Size)); addErr != nil {
|
||||
return 0, addErr
|
||||
}
|
||||
|
||||
if _, cpErr := io.Copy(buf, tr); cpErr != nil {
|
||||
return 0, cpErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flushErr := buf.Flush(); flushErr != nil {
|
||||
if loopErr == nil {
|
||||
loopErr = flushErr
|
||||
} else {
|
||||
log.G(ctx).WithError(flushErr).Warn("flush buffer during layer write failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !errors.Is(loopErr, io.EOF) {
|
||||
return 0, loopErr
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func DestroyCimLayer(layerPath string) error {
|
||||
return cim.DestroyCimLayer(context.Background(), layerPath)
|
||||
}
|
||||
3
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/export.go
generated
vendored
3
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/export.go
generated
vendored
@@ -5,6 +5,7 @@ package ociwclayer
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
@@ -62,7 +63,7 @@ func writeTarFromLayer(ctx context.Context, r wclayer.LayerReader, w io.Writer)
|
||||
}
|
||||
|
||||
name, size, fileInfo, err := r.Next()
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
3
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/import.go
generated
vendored
3
vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/import.go
generated
vendored
@@ -6,6 +6,7 @@ import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -102,7 +103,7 @@ func writeLayerFromTar(ctx context.Context, r io.Reader, w wclayer.LayerWriter,
|
||||
totalSize += size
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
return 0, err
|
||||
}
|
||||
return totalSize, nil
|
||||
|
||||
Reference in New Issue
Block a user