123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- 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
- }
- 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
- }
- }
- }
- }
- 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
- }
|