123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package hcsshim
- import (
- "errors"
- "io/ioutil"
- "os"
- "path/filepath"
- "github.com/Microsoft/go-winio"
- "github.com/Sirupsen/logrus"
- )
- // 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(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error {
- title := "hcsshim::ImportLayer "
- logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerID, importFolderPath)
- // 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 = importLayer(&infop, layerID, importFolderPath, layers)
- if err != nil {
- err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerID, info.Flavour, importFolderPath)
- logrus.Error(err)
- return err
- }
- logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerID, importFolderPath)
- 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
- }
- // FilterLayerWriter provides an interface to write the contents of a layer to the file system.
- type FilterLayerWriter struct {
- context uintptr
- }
- // Add adds a file or directory to the layer. The file's parent directory must have already been added.
- //
- // name contains the file's relative path. fileInfo contains file times and file attributes; the rest
- // of the file metadata and the file data must be written as a Win32 backup stream to the Write() method.
- // winio.BackupStreamWriter can be used to facilitate this.
- func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
- if name[0] != '\\' {
- name = `\` + name
- }
- err := importLayerNext(w.context, name, fileInfo)
- if err != nil {
- return makeError(err, "ImportLayerNext", "")
- }
- return nil
- }
- // AddLink adds a hard link to the layer. The target of the link must have already been added.
- func (w *FilterLayerWriter) AddLink(name string, target string) error {
- return errors.New("hard links not yet supported")
- }
- // Remove removes a file from the layer. The file must have been present in the parent layer.
- //
- // name contains the file's relative path.
- func (w *FilterLayerWriter) Remove(name string) error {
- if name[0] != '\\' {
- name = `\` + name
- }
- err := importLayerNext(w.context, name, nil)
- if err != nil {
- return makeError(err, "ImportLayerNext", "")
- }
- return nil
- }
- // Write writes more backup stream data to the current file.
- func (w *FilterLayerWriter) Write(b []byte) (int, error) {
- err := importLayerWrite(w.context, b)
- if err != nil {
- err = makeError(err, "ImportLayerWrite", "")
- return 0, err
- }
- return len(b), err
- }
- // Close completes the layer write operation. The error must be checked to ensure that the
- // operation was successful.
- func (w *FilterLayerWriter) Close() (err error) {
- if w.context != 0 {
- err = importLayerEnd(w.context)
- if err != nil {
- err = makeError(err, "ImportLayerEnd", "")
- }
- w.context = 0
- }
- return
- }
- type legacyLayerWriterWrapper struct {
- *legacyLayerWriter
- info DriverInfo
- layerID string
- path string
- parentLayerPaths []string
- }
- func (r *legacyLayerWriterWrapper) Close() error {
- defer os.RemoveAll(r.root)
- err := r.legacyLayerWriter.Close()
- if err != nil {
- return err
- }
- // Use the original path here because ImportLayer does not support long paths for the source in TP5.
- // But do use a long path for the destination to work around another bug with directories
- // with MAX_PATH - 12 < length < MAX_PATH.
- info := r.info
- fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID))
- if err != nil {
- return err
- }
- info.HomeDir = ""
- if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil {
- return err
- }
- // Add any hard links that were collected.
- for _, lnk := range r.PendingLinks {
- if err = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) {
- return err
- }
- if err = os.Link(lnk.Target, lnk.Path); err != nil {
- return err
- }
- }
- // Prepare the utility VM for use if one is present in the layer.
- if r.HasUtilityVM {
- err = ProcessUtilityVMImage(filepath.Join(fullPath, "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(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
- if len(parentLayerPaths) == 0 {
- // This is a base layer. It gets imported differently.
- return &baseLayerWriter{
- root: filepath.Join(info.HomeDir, layerID),
- }, nil
- }
- if procImportLayerBegin.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
- }
- return &legacyLayerWriterWrapper{
- legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)),
- info: info,
- layerID: layerID,
- path: path,
- parentLayerPaths: parentLayerPaths,
- }, nil
- }
- layers, err := layerPathsToDescriptors(parentLayerPaths)
- if err != nil {
- return nil, err
- }
- infop, err := convertDriverInfo(info)
- if err != nil {
- return nil, err
- }
- w := &FilterLayerWriter{}
- err = importLayerBegin(&infop, layerID, layers, &w.context)
- if err != nil {
- return nil, makeError(err, "ImportLayerStart", "")
- }
- return w, nil
- }
|