215 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package hcsshim
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"runtime"
 | 
						|
 | 
						|
	"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", "")
 | 
						|
	}
 | 
						|
	runtime.SetFinalizer(w, func(w *FilterLayerWriter) { w.Close() })
 | 
						|
	return w, nil
 | 
						|
}
 |