1package hcsshim 2 3import ( 4 "io" 5 "io/ioutil" 6 "os" 7 "syscall" 8 9 "github.com/Microsoft/go-winio" 10 "github.com/sirupsen/logrus" 11) 12 13// ExportLayer will create a folder at exportFolderPath and fill that folder with 14// the transport format version of the layer identified by layerId. This transport 15// format includes any metadata required for later importing the layer (using 16// ImportLayer), and requires the full list of parent layer paths in order to 17// perform the export. 18func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { 19 title := "hcsshim::ExportLayer " 20 logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerId, exportFolderPath) 21 22 // Generate layer descriptors 23 layers, err := layerPathsToDescriptors(parentLayerPaths) 24 if err != nil { 25 return err 26 } 27 28 // Convert info to API calling convention 29 infop, err := convertDriverInfo(info) 30 if err != nil { 31 logrus.Error(err) 32 return err 33 } 34 35 err = exportLayer(&infop, layerId, exportFolderPath, layers) 36 if err != nil { 37 err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerId, info.Flavour, exportFolderPath) 38 logrus.Error(err) 39 return err 40 } 41 42 logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath) 43 return nil 44} 45 46type LayerReader interface { 47 Next() (string, int64, *winio.FileBasicInfo, error) 48 Read(b []byte) (int, error) 49 Close() error 50} 51 52// FilterLayerReader provides an interface for extracting the contents of an on-disk layer. 53type FilterLayerReader struct { 54 context uintptr 55} 56 57// Next reads the next available file from a layer, ensuring that parent directories are always read 58// before child files and directories. 59// 60// Next returns the file's relative path, size, and basic file metadata. Read() should be used to 61// extract a Win32 backup stream with the remainder of the metadata and the data. 62func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) { 63 var fileNamep *uint16 64 fileInfo := &winio.FileBasicInfo{} 65 var deleted uint32 66 var fileSize int64 67 err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted) 68 if err != nil { 69 if err == syscall.ERROR_NO_MORE_FILES { 70 err = io.EOF 71 } else { 72 err = makeError(err, "ExportLayerNext", "") 73 } 74 return "", 0, nil, err 75 } 76 fileName := convertAndFreeCoTaskMemString(fileNamep) 77 if deleted != 0 { 78 fileInfo = nil 79 } 80 if fileName[0] == '\\' { 81 fileName = fileName[1:] 82 } 83 return fileName, fileSize, fileInfo, nil 84} 85 86// Read reads from the current file's Win32 backup stream. 87func (r *FilterLayerReader) Read(b []byte) (int, error) { 88 var bytesRead uint32 89 err := exportLayerRead(r.context, b, &bytesRead) 90 if err != nil { 91 return 0, makeError(err, "ExportLayerRead", "") 92 } 93 if bytesRead == 0 { 94 return 0, io.EOF 95 } 96 return int(bytesRead), nil 97} 98 99// Close frees resources associated with the layer reader. It will return an 100// error if there was an error while reading the layer or of the layer was not 101// completely read. 102func (r *FilterLayerReader) Close() (err error) { 103 if r.context != 0 { 104 err = exportLayerEnd(r.context) 105 if err != nil { 106 err = makeError(err, "ExportLayerEnd", "") 107 } 108 r.context = 0 109 } 110 return 111} 112 113// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. 114// The caller must have taken the SeBackupPrivilege privilege 115// to call this and any methods on the resulting LayerReader. 116func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { 117 if procExportLayerBegin.Find() != nil { 118 // The new layer reader is not available on this Windows build. Fall back to the 119 // legacy export code path. 120 path, err := ioutil.TempDir("", "hcs") 121 if err != nil { 122 return nil, err 123 } 124 err = ExportLayer(info, layerID, path, parentLayerPaths) 125 if err != nil { 126 os.RemoveAll(path) 127 return nil, err 128 } 129 return &legacyLayerReaderWrapper{newLegacyLayerReader(path)}, nil 130 } 131 132 layers, err := layerPathsToDescriptors(parentLayerPaths) 133 if err != nil { 134 return nil, err 135 } 136 infop, err := convertDriverInfo(info) 137 if err != nil { 138 return nil, err 139 } 140 r := &FilterLayerReader{} 141 err = exportLayerBegin(&infop, layerID, layers, &r.context) 142 if err != nil { 143 return nil, makeError(err, "ExportLayerBegin", "") 144 } 145 return r, err 146} 147 148type legacyLayerReaderWrapper struct { 149 *legacyLayerReader 150} 151 152func (r *legacyLayerReaderWrapper) Close() error { 153 err := r.legacyLayerReader.Close() 154 os.RemoveAll(r.root) 155 return err 156} 157