| //go:build windows | |
| package wclayer | |
| import ( | |
| "context" | |
| "os" | |
| "strings" | |
| "github.com/Microsoft/go-winio" | |
| "github.com/Microsoft/hcsshim/internal/hcserror" | |
| "github.com/Microsoft/hcsshim/internal/oc" | |
| "go.opencensus.io/trace" | |
| ) | |
| // ExportLayer will create a folder at exportFolderPath and fill that folder with | |
| // the transport format version of the layer identified by layerId. This transport | |
| // format includes any metadata required for later importing the layer (using | |
| // ImportLayer), and requires the full list of parent layer paths in order to | |
| // perform the export. | |
| func ExportLayer(ctx context.Context, path string, exportFolderPath string, parentLayerPaths []string) (err error) { | |
| title := "hcsshim::ExportLayer" | |
| ctx, span := oc.StartSpan(ctx, title) | |
| defer span.End() | |
| defer func() { oc.SetSpanStatus(span, err) }() | |
| span.AddAttributes( | |
| trace.StringAttribute("path", path), | |
| trace.StringAttribute("exportFolderPath", exportFolderPath), | |
| trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) | |
| // Generate layer descriptors | |
| layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) | |
| if err != nil { | |
| return err | |
| } | |
| err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers) | |
| if err != nil { | |
| return hcserror.New(err, title, "") | |
| } | |
| return nil | |
| } | |
| // LayerReader is an interface that supports reading an existing container image layer. | |
| type LayerReader interface { | |
| // Next advances to the next file and returns the name, size, and file info | |
| Next() (string, int64, *winio.FileBasicInfo, error) | |
| // LinkInfo returns the number of links and the file identifier for the current file. | |
| LinkInfo() (uint32, *winio.FileIDInfo, error) | |
| // Read reads data from the current file, in the format of a Win32 backup stream, and | |
| // returns the number of bytes read. | |
| Read(b []byte) (int, error) | |
| // Close finishes the layer reading process and releases any resources. | |
| Close() error | |
| } | |
| // NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. | |
| // The caller must have taken the SeBackupPrivilege privilege | |
| // to call this and any methods on the resulting LayerReader. | |
| func NewLayerReader(ctx context.Context, path string, parentLayerPaths []string) (_ LayerReader, err error) { | |
| ctx, span := oc.StartSpan(ctx, "hcsshim::NewLayerReader") | |
| 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 exported differently. | |
| return newBaseLayerReader(path, span), nil | |
| } | |
| exportPath, err := os.MkdirTemp("", "hcs") | |
| if err != nil { | |
| return nil, err | |
| } | |
| err = ExportLayer(ctx, path, exportPath, parentLayerPaths) | |
| if err != nil { | |
| os.RemoveAll(exportPath) | |
| return nil, err | |
| } | |
| return &legacyLayerReaderWrapper{ | |
| ctx: ctx, | |
| s: span, | |
| legacyLayerReader: newLegacyLayerReader(exportPath), | |
| }, nil | |
| } | |
| type legacyLayerReaderWrapper struct { | |
| ctx context.Context | |
| s *trace.Span | |
| *legacyLayerReader | |
| } | |
| func (r *legacyLayerReaderWrapper) Close() (err error) { | |
| defer r.s.End() | |
| defer func() { oc.SetSpanStatus(r.s, err) }() | |
| err = r.legacyLayerReader.Close() | |
| os.RemoveAll(r.root) | |
| return err | |
| } |