149 lines
3.7 KiB
Go
149 lines
3.7 KiB
Go
package ociwclayer
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bufio"
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
winio "github.com/Microsoft/go-winio"
|
|
"github.com/Microsoft/go-winio/backuptar"
|
|
"github.com/Microsoft/hcsshim"
|
|
)
|
|
|
|
const whiteoutPrefix = ".wh."
|
|
|
|
var (
|
|
// mutatedFiles is a list of files that are mutated by the import process
|
|
// and must be backed up and restored.
|
|
mutatedFiles = map[string]string{
|
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak",
|
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak",
|
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak",
|
|
"UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak",
|
|
}
|
|
)
|
|
|
|
// ImportLayerFromTar reads a layer from an OCI layer tar stream and extracts it to the
|
|
// specified path. The caller must specify the parent layers, if any, ordered
|
|
// from lowest to highest layer.
|
|
//
|
|
// The caller must ensure that the thread or process has acquired backup and
|
|
// restore privileges.
|
|
//
|
|
// This function returns the total size of the layer's files, in bytes.
|
|
func ImportLayerFromTar(ctx context.Context, r io.Reader, path string, parentLayerPaths []string) (int64, error) {
|
|
err := os.MkdirAll(path, 0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
w, err := hcsshim.NewLayerWriter(hcsshim.DriverInfo{}, path, parentLayerPaths)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
n, err := writeLayerFromTar(ctx, r, w, path)
|
|
cerr := w.Close()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if cerr != nil {
|
|
return 0, cerr
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func writeLayerFromTar(ctx context.Context, r io.Reader, w hcsshim.LayerWriter, root string) (int64, error) {
|
|
t := tar.NewReader(r)
|
|
hdr, err := t.Next()
|
|
totalSize := int64(0)
|
|
buf := bufio.NewWriter(nil)
|
|
for err == nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return 0, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
base := path.Base(hdr.Name)
|
|
if strings.HasPrefix(base, whiteoutPrefix) {
|
|
name := path.Join(path.Dir(hdr.Name), base[len(whiteoutPrefix):])
|
|
err = w.Remove(filepath.FromSlash(name))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
hdr, err = t.Next()
|
|
} else if hdr.Typeflag == tar.TypeLink {
|
|
err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
hdr, err = t.Next()
|
|
} else {
|
|
var (
|
|
name string
|
|
size int64
|
|
fileInfo *winio.FileBasicInfo
|
|
)
|
|
name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
err = w.Add(filepath.FromSlash(name), fileInfo)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root)
|
|
totalSize += size
|
|
}
|
|
}
|
|
if err != io.EOF {
|
|
return 0, err
|
|
}
|
|
return totalSize, nil
|
|
}
|
|
|
|
// writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and
|
|
// writes it to a backup stream, and also saves any files that will be mutated
|
|
// by the import layer process to a backup location.
|
|
func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) {
|
|
var bcdBackup *os.File
|
|
var bcdBackupWriter *winio.BackupFileWriter
|
|
if backupPath, ok := mutatedFiles[hdr.Name]; ok {
|
|
bcdBackup, err = os.Create(filepath.Join(root, backupPath))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
cerr := bcdBackup.Close()
|
|
if err == nil {
|
|
err = cerr
|
|
}
|
|
}()
|
|
|
|
bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false)
|
|
defer func() {
|
|
cerr := bcdBackupWriter.Close()
|
|
if err == nil {
|
|
err = cerr
|
|
}
|
|
}()
|
|
|
|
buf.Reset(io.MultiWriter(w, bcdBackupWriter))
|
|
} else {
|
|
buf.Reset(w)
|
|
}
|
|
|
|
defer func() {
|
|
ferr := buf.Flush()
|
|
if err == nil {
|
|
err = ferr
|
|
}
|
|
}()
|
|
|
|
return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
|
|
}
|