| package hcsshim |
| |
| import ( |
| "io" |
| "io/ioutil" |
| "os" |
| "runtime" |
| "syscall" |
| |
| "github.com/Microsoft/go-winio" |
| "github.com/Sirupsen/logrus" |
| ) |
| |
| // 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(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { |
| title := "hcsshim::ExportLayer " |
| logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerId, exportFolderPath) |
| |
| // Generate layer descriptors |
| layers, err := layerPathsToDescriptors(parentLayerPaths) |
| if err != nil { |
| return err |
| } |
| |
| // Convert info to API calling convention |
| infop, err := convertDriverInfo(info) |
| if err != nil { |
| logrus.Error(err) |
| return err |
| } |
| |
| err = exportLayer(&infop, layerId, exportFolderPath, layers) |
| if err != nil { |
| err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerId, info.Flavour, exportFolderPath) |
| logrus.Error(err) |
| return err |
| } |
| |
| logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath) |
| return nil |
| } |
| |
| type LayerReader interface { |
| Next() (string, int64, *winio.FileBasicInfo, error) |
| Read(b []byte) (int, error) |
| Close() error |
| } |
| |
| // FilterLayerReader provides an interface for extracting the contents of an on-disk layer. |
| type FilterLayerReader struct { |
| context uintptr |
| } |
| |
| // Next reads the next available file from a layer, ensuring that parent directories are always read |
| // before child files and directories. |
| // |
| // Next returns the file's relative path, size, and basic file metadata. Read() should be used to |
| // extract a Win32 backup stream with the remainder of the metadata and the data. |
| func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) { |
| var fileNamep *uint16 |
| fileInfo := &winio.FileBasicInfo{} |
| var deleted uint32 |
| var fileSize int64 |
| err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted) |
| if err != nil { |
| if err == syscall.ERROR_NO_MORE_FILES { |
| err = io.EOF |
| } else { |
| err = makeError(err, "ExportLayerNext", "") |
| } |
| return "", 0, nil, err |
| } |
| fileName := convertAndFreeCoTaskMemString(fileNamep) |
| if deleted != 0 { |
| fileInfo = nil |
| } |
| if fileName[0] == '\\' { |
| fileName = fileName[1:] |
| } |
| return fileName, fileSize, fileInfo, nil |
| } |
| |
| // Read reads from the current file's Win32 backup stream. |
| func (r *FilterLayerReader) Read(b []byte) (int, error) { |
| var bytesRead uint32 |
| err := exportLayerRead(r.context, b, &bytesRead) |
| if err != nil { |
| return 0, makeError(err, "ExportLayerRead", "") |
| } |
| if bytesRead == 0 { |
| return 0, io.EOF |
| } |
| return int(bytesRead), nil |
| } |
| |
| // Close frees resources associated with the layer reader. It will return an |
| // error if there was an error while reading the layer or of the layer was not |
| // completely read. |
| func (r *FilterLayerReader) Close() (err error) { |
| if r.context != 0 { |
| err = exportLayerEnd(r.context) |
| if err != nil { |
| err = makeError(err, "ExportLayerEnd", "") |
| } |
| r.context = 0 |
| } |
| return |
| } |
| |
| // 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(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { |
| if procExportLayerBegin.Find() != nil { |
| // The new layer reader is not available on this Windows build. Fall back to the |
| // legacy export code path. |
| path, err := ioutil.TempDir("", "hcs") |
| if err != nil { |
| return nil, err |
| } |
| err = ExportLayer(info, layerID, path, parentLayerPaths) |
| if err != nil { |
| os.RemoveAll(path) |
| return nil, err |
| } |
| return &legacyLayerReaderWrapper{newLegacyLayerReader(path)}, nil |
| } |
| |
| layers, err := layerPathsToDescriptors(parentLayerPaths) |
| if err != nil { |
| return nil, err |
| } |
| infop, err := convertDriverInfo(info) |
| if err != nil { |
| return nil, err |
| } |
| r := &FilterLayerReader{} |
| err = exportLayerBegin(&infop, layerID, layers, &r.context) |
| if err != nil { |
| return nil, makeError(err, "ExportLayerBegin", "") |
| } |
| runtime.SetFinalizer(r, func(r *FilterLayerReader) { r.Close() }) |
| return r, err |
| } |
| |
| type legacyLayerReaderWrapper struct { |
| *legacyLayerReader |
| } |
| |
| func (r *legacyLayerReaderWrapper) Close() error { |
| err := r.legacyLayerReader.Close() |
| os.RemoveAll(r.root) |
| return err |
| } |