167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
package wclayer
|
|
|
|
import (
|
|
"context"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/Microsoft/go-winio"
|
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
|
"github.com/Microsoft/hcsshim/internal/oc"
|
|
"github.com/Microsoft/hcsshim/internal/safefile"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
// ImportLayer will take the contents of the folder at importFolderPath and import
|
|
// that into a layer with the id layerId. Note that in order to correctly populate
|
|
// the layer and interperet the transport format, all parent layers must already
|
|
// be present on the system at the paths provided in parentLayerPaths.
|
|
func ImportLayer(ctx context.Context, path string, importFolderPath string, parentLayerPaths []string) (err error) {
|
|
title := "hcsshim::ImportLayer"
|
|
ctx, span := trace.StartSpan(ctx, title)
|
|
defer span.End()
|
|
defer func() { oc.SetSpanStatus(span, err) }()
|
|
span.AddAttributes(
|
|
trace.StringAttribute("path", path),
|
|
trace.StringAttribute("importFolderPath", importFolderPath),
|
|
trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", ")))
|
|
|
|
// Generate layer descriptors
|
|
layers, err := layerPathsToDescriptors(ctx, parentLayerPaths)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = importLayer(&stdDriverInfo, path, importFolderPath, layers)
|
|
if err != nil {
|
|
return hcserror.New(err, title+" - failed", "")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LayerWriter is an interface that supports writing a new container image layer.
|
|
type LayerWriter interface {
|
|
// Add adds a file to the layer with given metadata.
|
|
Add(name string, fileInfo *winio.FileBasicInfo) error
|
|
// AddLink adds a hard link to the layer. The target must already have been added.
|
|
AddLink(name string, target string) error
|
|
// Remove removes a file that was present in a parent layer from the layer.
|
|
Remove(name string) error
|
|
// Write writes data to the current file. The data must be in the format of a Win32
|
|
// backup stream.
|
|
Write(b []byte) (int, error)
|
|
// Close finishes the layer writing process and releases any resources.
|
|
Close() error
|
|
}
|
|
|
|
type legacyLayerWriterWrapper struct {
|
|
ctx context.Context
|
|
s *trace.Span
|
|
|
|
*legacyLayerWriter
|
|
path string
|
|
parentLayerPaths []string
|
|
}
|
|
|
|
func (r *legacyLayerWriterWrapper) Close() (err error) {
|
|
defer r.s.End()
|
|
defer func() { oc.SetSpanStatus(r.s, err) }()
|
|
defer os.RemoveAll(r.root.Name())
|
|
defer r.legacyLayerWriter.CloseRoots()
|
|
|
|
err = r.legacyLayerWriter.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = ImportLayer(r.ctx, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
|
|
return err
|
|
}
|
|
for _, name := range r.Tombstones {
|
|
if err = safefile.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 = safefile.RemoveRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
if err = safefile.LinkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// The reapplyDirectoryTimes must be called AFTER we are done with Tombstone
|
|
// deletion and hard link creation. This is because Tombstone deletion and hard link
|
|
// creation updates the directory last write timestamps so that will change the
|
|
// timestamps added by the `Add` call. Some container applications depend on the
|
|
// correctness of these timestamps and so we should change the timestamps back to
|
|
// the original value (i.e the value provided in the Add call) after this
|
|
// processing is done.
|
|
err = reapplyDirectoryTimes(r.destRoot, r.changedDi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prepare the utility VM for use if one is present in the layer.
|
|
if r.HasUtilityVM {
|
|
err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ProcessUtilityVMImage(r.ctx, filepath.Join(r.destRoot.Name(), "UtilityVM"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewLayerWriter returns a new layer writer for creating a layer on disk.
|
|
// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges
|
|
// to call this and any methods on the resulting LayerWriter.
|
|
func NewLayerWriter(ctx context.Context, path string, parentLayerPaths []string) (_ LayerWriter, err error) {
|
|
ctx, span := trace.StartSpan(ctx, "hcsshim::NewLayerWriter")
|
|
defer func() {
|
|
if err != nil {
|
|
oc.SetSpanStatus(span, err)
|
|
span.End()
|
|
}
|
|
}()
|
|
span.AddAttributes(
|
|
trace.StringAttribute("path", path),
|
|
trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", ")))
|
|
|
|
if len(parentLayerPaths) == 0 {
|
|
// This is a base layer. It gets imported differently.
|
|
f, err := safefile.OpenRoot(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &baseLayerWriter{
|
|
ctx: ctx,
|
|
s: span,
|
|
root: f,
|
|
}, nil
|
|
}
|
|
|
|
importPath, err := ioutil.TempDir("", "hcs")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &legacyLayerWriterWrapper{
|
|
ctx: ctx,
|
|
s: span,
|
|
legacyLayerWriter: w,
|
|
path: importPath,
|
|
parentLayerPaths: parentLayerPaths,
|
|
}, nil
|
|
}
|