123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- //go:build windows
- package wclayer
- import (
- "context"
- "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 := oc.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, "")
- }
- 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 := oc.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 := os.MkdirTemp("", "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
- }
|