baselayerreader.go 4.3 KB

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