Skip to content

Commit 8778526

Browse files
TBBlePaul "Hampy" Hampson
authored andcommitted
Simple baseLayerReader to export parentless layers
This is the inverse of the baseLayerWriter: It walks Files/ and UtilityVM/Files/ (if present) and ignores the rest of the layer data, as it will be recreated when the layer is imported. Signed-off-by: Paul "TBBle" Hampson <[email protected]>
1 parent acdf144 commit 8778526

7 files changed

Lines changed: 423 additions & 1 deletion

File tree

cmd/wclayer/export.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ var exportCommand = cli.Command{
4040
return err
4141
}
4242

43-
layers, err := normalizeLayers(cliContext.StringSlice("layer"), true)
43+
layers, err := normalizeLayers(cliContext.StringSlice("layer"), false)
4444
if err != nil {
4545
return err
4646
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package wclayer
2+
3+
import (
4+
"context"
5+
"errors"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
"syscall"
11+
12+
"github.com/Microsoft/go-winio"
13+
"github.com/Microsoft/hcsshim/internal/longpath"
14+
"github.com/Microsoft/hcsshim/internal/oc"
15+
"go.opencensus.io/trace"
16+
)
17+
18+
type baseLayerReader struct {
19+
ctx context.Context
20+
s *trace.Span
21+
root string
22+
result chan *fileEntry
23+
proceed chan bool
24+
currentFile *os.File
25+
backupReader *winio.BackupFileReader
26+
}
27+
28+
func newBaseLayerReader(ctx context.Context, root string, s *trace.Span) (r *baseLayerReader) {
29+
r = &baseLayerReader{
30+
ctx: ctx,
31+
s: s,
32+
root: root,
33+
result: make(chan *fileEntry),
34+
proceed: make(chan bool),
35+
}
36+
go r.walk()
37+
return r
38+
}
39+
40+
func (r *baseLayerReader) walkUntilCancelled() error {
41+
root, err := longpath.LongAbs(r.root)
42+
if err != nil {
43+
return err
44+
}
45+
46+
r.root = root
47+
48+
err = filepath.Walk(filepath.Join(r.root, filesPath), func(path string, info os.FileInfo, err error) error {
49+
if err != nil {
50+
return err
51+
}
52+
53+
// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
54+
// Handle failure from what may be a golang bug in the conversion of
55+
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
56+
// which is called by filepath.Walk will fail when a filename contains
57+
// unicode characters. Skip the recycle bin regardless which is goodness.
58+
if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
59+
return filepath.SkipDir
60+
}
61+
62+
r.result <- &fileEntry{path, info, nil}
63+
if !<-r.proceed {
64+
return errorIterationCanceled
65+
}
66+
67+
return nil
68+
})
69+
70+
if err == errorIterationCanceled {
71+
return nil
72+
}
73+
74+
if err != nil {
75+
return err
76+
}
77+
78+
utilityVMAbsPath := filepath.Join(r.root, utilityVMPath)
79+
utilityVMFilesAbsPath := filepath.Join(r.root, utilityVMFilesPath)
80+
81+
// Ignore a UtilityVM without Files, that's not _really_ a UtiltyVM
82+
if _, err = os.Lstat(utilityVMFilesAbsPath); err != nil {
83+
if os.IsNotExist(err) {
84+
return io.EOF
85+
}
86+
return err
87+
}
88+
89+
err = filepath.Walk(utilityVMAbsPath, func(path string, info os.FileInfo, err error) error {
90+
if err != nil {
91+
return err
92+
}
93+
94+
if path != utilityVMAbsPath && path != utilityVMFilesAbsPath && !hasPathPrefix(path, utilityVMFilesAbsPath) {
95+
if info.IsDir() {
96+
return filepath.SkipDir
97+
}
98+
return nil
99+
}
100+
101+
r.result <- &fileEntry{path, info, nil}
102+
if !<-r.proceed {
103+
return errorIterationCanceled
104+
}
105+
106+
return nil
107+
})
108+
109+
if err == errorIterationCanceled {
110+
return nil
111+
}
112+
113+
if err != nil {
114+
return err
115+
}
116+
117+
return io.EOF
118+
}
119+
120+
func (r *baseLayerReader) walk() {
121+
defer close(r.result)
122+
if !<-r.proceed {
123+
return
124+
}
125+
126+
err := r.walkUntilCancelled()
127+
if err != nil {
128+
for {
129+
r.result <- &fileEntry{err: err}
130+
if !<-r.proceed {
131+
return
132+
}
133+
}
134+
}
135+
}
136+
137+
func (r *baseLayerReader) reset() {
138+
if r.backupReader != nil {
139+
r.backupReader.Close()
140+
r.backupReader = nil
141+
}
142+
if r.currentFile != nil {
143+
r.currentFile.Close()
144+
r.currentFile = nil
145+
}
146+
}
147+
148+
func (r *baseLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
149+
r.reset()
150+
r.proceed <- true
151+
fe := <-r.result
152+
if fe == nil {
153+
err = errors.New("BaseLayerReader closed")
154+
return
155+
}
156+
if fe.err != nil {
157+
err = fe.err
158+
return
159+
}
160+
161+
path, err = filepath.Rel(r.root, fe.path)
162+
if err != nil {
163+
return
164+
}
165+
166+
f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
167+
if err != nil {
168+
return
169+
}
170+
defer func() {
171+
if f != nil {
172+
f.Close()
173+
}
174+
}()
175+
176+
fileInfo, err = winio.GetFileBasicInfo(f)
177+
if err != nil {
178+
return
179+
}
180+
181+
size = fe.fi.Size()
182+
r.backupReader = winio.NewBackupFileReader(f, true)
183+
184+
r.currentFile = f
185+
f = nil
186+
return
187+
}
188+
189+
func (r *baseLayerReader) Read(b []byte) (int, error) {
190+
if r.backupReader == nil {
191+
if r.currentFile == nil {
192+
return 0, io.EOF
193+
}
194+
return r.currentFile.Read(b)
195+
}
196+
return r.backupReader.Read(b)
197+
}
198+
199+
func (r *baseLayerReader) Close() (err error) {
200+
defer r.s.End()
201+
defer func() { oc.SetSpanStatus(r.s, err) }()
202+
r.proceed <- false
203+
<-r.result
204+
r.reset()
205+
return nil
206+
}

internal/wclayer/exportlayer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ func NewLayerReader(ctx context.Context, path string, parentLayerPaths []string)
6868
trace.StringAttribute("path", path),
6969
trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", ")))
7070

71+
if len(parentLayerPaths) == 0 {
72+
// This is a base layer. It gets exported differently.
73+
return newBaseLayerReader(ctx, path, span), nil
74+
}
75+
7176
exportPath, err := ioutil.TempDir("", "hcs")
7277
if err != nil {
7378
return nil, err

0 commit comments

Comments
 (0)