summaryrefslogtreecommitdiff
path: root/vendor/github.com/Microsoft/hcsshim/exportlayer.go
blob: d7025f20babafe33fbe9a5ea19e62b4dfe9054e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package hcsshim

import (
	"io"
	"io/ioutil"
	"os"
	"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", "")
	}
	return r, err
}

type legacyLayerReaderWrapper struct {
	*legacyLayerReader
}

func (r *legacyLayerReaderWrapper) Close() error {
	err := r.legacyLayerReader.Close()
	os.RemoveAll(r.root)
	return err
}