Skip to content
Merged
2 changes: 1 addition & 1 deletion cmd/wclayer/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var exportCommand = cli.Command{
return err
}

layers, err := normalizeLayers(cliContext.StringSlice("layer"), true)
layers, err := normalizeLayers(cliContext.StringSlice("layer"), false)
if err != nil {
return err
}
Expand Down
24 changes: 24 additions & 0 deletions cmd/wclayer/makebaselayer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"path/filepath"

"github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/internal/appargs"
"github.com/urfave/cli"
)

var makeBaseLayerCommand = cli.Command{
Name: "makebaselayer",
Usage: "converts a directory containing 'Files/' into a base layer",
ArgsUsage: "<layer path>",
Before: appargs.Validate(appargs.NonEmptyString),
Action: func(context *cli.Context) error {
path, err := filepath.Abs(context.Args().First())
if err != nil {
return err
}

return hcsshim.ConvertToBaseLayer(path)
},
}
1 change: 1 addition & 0 deletions cmd/wclayer/wclayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func main() {
createCommand,
exportCommand,
importCommand,
makeBaseLayerCommand,
mountCommand,
removeCommand,
unmountCommand,
Expand Down
216 changes: 216 additions & 0 deletions internal/wclayer/baselayerreader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package wclayer

import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"syscall"

"github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/internal/longpath"
"github.com/Microsoft/hcsshim/internal/oc"
"go.opencensus.io/trace"
)

type baseLayerReader struct {
s *trace.Span
root string
result chan *fileEntry
proceed chan bool
currentFile *os.File
backupReader *winio.BackupFileReader
}

func newBaseLayerReader(root string, s *trace.Span) (r *baseLayerReader) {
r = &baseLayerReader{
s: s,
root: root,
result: make(chan *fileEntry),
proceed: make(chan bool),
}
go r.walk()
return r
}

func (r *baseLayerReader) walkUntilCancelled() error {
root, err := longpath.LongAbs(r.root)
if err != nil {
return err
}

r.root = root

err = filepath.Walk(filepath.Join(r.root, filesPath), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
// Handle failure from what may be a golang bug in the conversion of
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
// which is called by filepath.Walk will fail when a filename contains
// unicode characters. Skip the recycle bin regardless which is goodness.
if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
return filepath.SkipDir
}

r.result <- &fileEntry{path, info, nil}
if !<-r.proceed {
return errorIterationCanceled
}

return nil
})

if err == errorIterationCanceled {
return nil
}

if err != nil {
return err
}

utilityVMAbsPath := filepath.Join(r.root, utilityVMPath)
utilityVMFilesAbsPath := filepath.Join(r.root, utilityVMFilesPath)

// Ignore a UtilityVM without Files, that's not _really_ a UtiltyVM
if _, err = os.Lstat(utilityVMFilesAbsPath); err != nil {
if os.IsNotExist(err) {
return io.EOF
}
return err
}

err = filepath.Walk(utilityVMAbsPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if path != utilityVMAbsPath && path != utilityVMFilesAbsPath && !hasPathPrefix(path, utilityVMFilesAbsPath) {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
Comment thread
ambarve marked this conversation as resolved.

r.result <- &fileEntry{path, info, nil}
if !<-r.proceed {
return errorIterationCanceled
}

return nil
})

if err == errorIterationCanceled {
return nil
}

if err != nil {
return err
}

return io.EOF
}

func (r *baseLayerReader) walk() {
defer close(r.result)
if !<-r.proceed {
return
}

err := r.walkUntilCancelled()
if err != nil {
for {
r.result <- &fileEntry{err: err}
if !<-r.proceed {
return
}
Comment thread
helsaawy marked this conversation as resolved.
}
}
}

func (r *baseLayerReader) reset() {
if r.backupReader != nil {
r.backupReader.Close()
r.backupReader = nil
}
if r.currentFile != nil {
r.currentFile.Close()
r.currentFile = nil
}
}

func (r *baseLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
r.reset()
r.proceed <- true
fe := <-r.result
if fe == nil {
err = errors.New("BaseLayerReader closed")
return
}
if fe.err != nil {
err = fe.err
return
}

path, err = filepath.Rel(r.root, fe.path)
if err != nil {
return
}

f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
if err != nil {
return
}
defer func() {
if f != nil {
f.Close()
}
}()

fileInfo, err = winio.GetFileBasicInfo(f)
if err != nil {
return
}

size = fe.fi.Size()
r.backupReader = winio.NewBackupFileReader(f, true)

r.currentFile = f
f = nil
return
}

func (r *baseLayerReader) LinkInfo() (uint32, *winio.FileIDInfo, error) {
fileStandardInfo, err := winio.GetFileStandardInfo(r.currentFile)
if err != nil {
return 0, nil, err
}
fileIDInfo, err := winio.GetFileID(r.currentFile)
if err != nil {
return 0, nil, err
}
return fileStandardInfo.NumberOfLinks, fileIDInfo, nil
}

func (r *baseLayerReader) Read(b []byte) (int, error) {
if r.backupReader == nil {
return 0, io.EOF
}
return r.backupReader.Read(b)
}

func (r *baseLayerReader) Close() (err error) {
defer r.s.End()
defer func() {
oc.SetSpanStatus(r.s, err)
close(r.proceed)
}()
r.proceed <- false
// The r.result channel will be closed once walk() returns
<-r.result
r.reset()
return nil
}
File renamed without changes.
Loading