legacy.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. //go:build windows
  2. package wclayer
  3. import (
  4. "bufio"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "syscall"
  13. "github.com/Microsoft/go-winio"
  14. "github.com/Microsoft/hcsshim/internal/longpath"
  15. "github.com/Microsoft/hcsshim/internal/safefile"
  16. "github.com/Microsoft/hcsshim/internal/winapi"
  17. )
  18. var errorIterationCanceled = errors.New("")
  19. var mutatedUtilityVMFiles = map[string]bool{
  20. `EFI\Microsoft\Boot\BCD`: true,
  21. `EFI\Microsoft\Boot\BCD.LOG`: true,
  22. `EFI\Microsoft\Boot\BCD.LOG1`: true,
  23. `EFI\Microsoft\Boot\BCD.LOG2`: true,
  24. }
  25. const (
  26. filesPath = `Files`
  27. hivesPath = `Hives`
  28. utilityVMPath = `UtilityVM`
  29. utilityVMFilesPath = `UtilityVM\Files`
  30. )
  31. func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
  32. return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition)
  33. }
  34. func hasPathPrefix(p, prefix string) bool {
  35. return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\'
  36. }
  37. type fileEntry struct {
  38. path string
  39. fi os.FileInfo
  40. err error
  41. }
  42. type legacyLayerReader struct {
  43. root string
  44. result chan *fileEntry
  45. proceed chan bool
  46. currentFile *os.File
  47. backupReader *winio.BackupFileReader
  48. }
  49. // newLegacyLayerReader returns a new LayerReader that can read the Windows
  50. // container layer transport format from disk.
  51. func newLegacyLayerReader(root string) *legacyLayerReader {
  52. r := &legacyLayerReader{
  53. root: root,
  54. result: make(chan *fileEntry),
  55. proceed: make(chan bool),
  56. }
  57. go r.walk()
  58. return r
  59. }
  60. func readTombstones(path string) (map[string]([]string), error) {
  61. tf, err := os.Open(filepath.Join(path, "tombstones.txt"))
  62. if err != nil {
  63. return nil, err
  64. }
  65. defer tf.Close()
  66. s := bufio.NewScanner(tf)
  67. if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" {
  68. return nil, errors.New("invalid tombstones file")
  69. }
  70. ts := make(map[string]([]string))
  71. for s.Scan() {
  72. t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\`
  73. dir := filepath.Dir(t)
  74. ts[dir] = append(ts[dir], t)
  75. }
  76. if err = s.Err(); err != nil {
  77. return nil, err
  78. }
  79. return ts, nil
  80. }
  81. func (r *legacyLayerReader) walkUntilCancelled() error {
  82. root, err := longpath.LongAbs(r.root)
  83. if err != nil {
  84. return err
  85. }
  86. r.root = root
  87. ts, err := readTombstones(r.root)
  88. if err != nil {
  89. return err
  90. }
  91. err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error {
  92. if err != nil {
  93. return err
  94. }
  95. // Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
  96. // Handle failure from what may be a golang bug in the conversion of
  97. // UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
  98. // which is called by filepath.Walk will fail when a filename contains
  99. // unicode characters. Skip the recycle bin regardless which is goodness.
  100. if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
  101. return filepath.SkipDir
  102. }
  103. if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
  104. return nil
  105. }
  106. r.result <- &fileEntry{path, info, nil}
  107. if !<-r.proceed {
  108. return errorIterationCanceled
  109. }
  110. // List all the tombstones.
  111. if info.IsDir() {
  112. relPath, err := filepath.Rel(r.root, path)
  113. if err != nil {
  114. return err
  115. }
  116. if dts, ok := ts[relPath]; ok {
  117. for _, t := range dts {
  118. r.result <- &fileEntry{filepath.Join(r.root, t), nil, nil}
  119. if !<-r.proceed {
  120. return errorIterationCanceled
  121. }
  122. }
  123. }
  124. }
  125. return nil
  126. })
  127. if err == errorIterationCanceled {
  128. return nil
  129. }
  130. if err == nil {
  131. return io.EOF
  132. }
  133. return err
  134. }
  135. func (r *legacyLayerReader) walk() {
  136. defer close(r.result)
  137. if !<-r.proceed {
  138. return
  139. }
  140. err := r.walkUntilCancelled()
  141. if err != nil {
  142. for {
  143. r.result <- &fileEntry{err: err}
  144. if !<-r.proceed {
  145. return
  146. }
  147. }
  148. }
  149. }
  150. func (r *legacyLayerReader) reset() {
  151. if r.backupReader != nil {
  152. r.backupReader.Close()
  153. r.backupReader = nil
  154. }
  155. if r.currentFile != nil {
  156. r.currentFile.Close()
  157. r.currentFile = nil
  158. }
  159. }
  160. func findBackupStreamSize(r io.Reader) (int64, error) {
  161. br := winio.NewBackupStreamReader(r)
  162. for {
  163. hdr, err := br.Next()
  164. if err != nil {
  165. if err == io.EOF {
  166. err = nil
  167. }
  168. return 0, err
  169. }
  170. if hdr.Id == winio.BackupData {
  171. return hdr.Size, nil
  172. }
  173. }
  174. }
  175. func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
  176. r.reset()
  177. r.proceed <- true
  178. fe := <-r.result
  179. if fe == nil {
  180. err = errors.New("LegacyLayerReader closed")
  181. return
  182. }
  183. if fe.err != nil {
  184. err = fe.err
  185. return
  186. }
  187. path, err = filepath.Rel(r.root, fe.path)
  188. if err != nil {
  189. return
  190. }
  191. if fe.fi == nil {
  192. // This is a tombstone. Return a nil fileInfo.
  193. return
  194. }
  195. if fe.fi.IsDir() && hasPathPrefix(path, filesPath) {
  196. fe.path += ".$wcidirs$"
  197. }
  198. f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
  199. if err != nil {
  200. return
  201. }
  202. defer func() {
  203. if f != nil {
  204. f.Close()
  205. }
  206. }()
  207. fileInfo, err = winio.GetFileBasicInfo(f)
  208. if err != nil {
  209. return
  210. }
  211. if !hasPathPrefix(path, filesPath) {
  212. size = fe.fi.Size()
  213. r.backupReader = winio.NewBackupFileReader(f, false)
  214. if path == hivesPath || path == filesPath {
  215. // The Hives directory has a non-deterministic file time because of the
  216. // nature of the import process. Use the times from System_Delta.
  217. var g *os.File
  218. g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`))
  219. if err != nil {
  220. return
  221. }
  222. attr := fileInfo.FileAttributes
  223. fileInfo, err = winio.GetFileBasicInfo(g)
  224. g.Close()
  225. if err != nil {
  226. return
  227. }
  228. fileInfo.FileAttributes = attr
  229. }
  230. // The creation time and access time get reset for files outside of the Files path.
  231. fileInfo.CreationTime = fileInfo.LastWriteTime
  232. fileInfo.LastAccessTime = fileInfo.LastWriteTime
  233. } else {
  234. // The file attributes are written before the backup stream.
  235. var attr uint32
  236. err = binary.Read(f, binary.LittleEndian, &attr)
  237. if err != nil {
  238. return
  239. }
  240. fileInfo.FileAttributes = attr
  241. beginning := int64(4)
  242. // Find the accurate file size.
  243. if !fe.fi.IsDir() {
  244. size, err = findBackupStreamSize(f)
  245. if err != nil {
  246. err = &os.PathError{Op: "findBackupStreamSize", Path: fe.path, Err: err}
  247. return
  248. }
  249. }
  250. // Return back to the beginning of the backup stream.
  251. _, err = f.Seek(beginning, 0)
  252. if err != nil {
  253. return
  254. }
  255. }
  256. r.currentFile = f
  257. f = nil
  258. return
  259. }
  260. func (r *legacyLayerReader) LinkInfo() (uint32, *winio.FileIDInfo, error) {
  261. fileStandardInfo, err := winio.GetFileStandardInfo(r.currentFile)
  262. if err != nil {
  263. return 0, nil, err
  264. }
  265. fileIDInfo, err := winio.GetFileID(r.currentFile)
  266. if err != nil {
  267. return 0, nil, err
  268. }
  269. return fileStandardInfo.NumberOfLinks, fileIDInfo, nil
  270. }
  271. func (r *legacyLayerReader) Read(b []byte) (int, error) {
  272. if r.backupReader == nil {
  273. if r.currentFile == nil {
  274. return 0, io.EOF
  275. }
  276. return r.currentFile.Read(b)
  277. }
  278. return r.backupReader.Read(b)
  279. }
  280. func (r *legacyLayerReader) Seek(offset int64, whence int) (int64, error) {
  281. if r.backupReader == nil {
  282. if r.currentFile == nil {
  283. return 0, errors.New("no current file")
  284. }
  285. return r.currentFile.Seek(offset, whence)
  286. }
  287. return 0, errors.New("seek not supported on this stream")
  288. }
  289. func (r *legacyLayerReader) Close() error {
  290. r.proceed <- false
  291. <-r.result
  292. r.reset()
  293. return nil
  294. }
  295. type pendingLink struct {
  296. Path, Target string
  297. TargetRoot *os.File
  298. }
  299. type pendingDir struct {
  300. Path string
  301. Root *os.File
  302. }
  303. type legacyLayerWriter struct {
  304. root *os.File
  305. destRoot *os.File
  306. parentRoots []*os.File
  307. currentFile *os.File
  308. bufWriter *bufio.Writer
  309. currentFileName string
  310. currentFileRoot *os.File
  311. backupWriter *winio.BackupFileWriter
  312. Tombstones []string
  313. HasUtilityVM bool
  314. changedDi []dirInfo
  315. addedFiles map[string]bool
  316. PendingLinks []pendingLink
  317. pendingDirs []pendingDir
  318. currentIsDir bool
  319. }
  320. // newLegacyLayerWriter returns a LayerWriter that can write the container layer
  321. // transport format to disk.
  322. func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
  323. w = &legacyLayerWriter{
  324. addedFiles: make(map[string]bool),
  325. }
  326. defer func() {
  327. if err != nil {
  328. w.CloseRoots()
  329. w = nil
  330. }
  331. }()
  332. w.root, err = safefile.OpenRoot(root)
  333. if err != nil {
  334. return
  335. }
  336. w.destRoot, err = safefile.OpenRoot(destRoot)
  337. if err != nil {
  338. return
  339. }
  340. for _, r := range parentRoots {
  341. f, err := safefile.OpenRoot(r)
  342. if err != nil {
  343. return w, err
  344. }
  345. w.parentRoots = append(w.parentRoots, f)
  346. }
  347. w.bufWriter = bufio.NewWriterSize(io.Discard, 65536)
  348. return
  349. }
  350. func (w *legacyLayerWriter) CloseRoots() {
  351. if w.root != nil {
  352. w.root.Close()
  353. w.root = nil
  354. }
  355. if w.destRoot != nil {
  356. w.destRoot.Close()
  357. w.destRoot = nil
  358. }
  359. for i := range w.parentRoots {
  360. _ = w.parentRoots[i].Close()
  361. }
  362. w.parentRoots = nil
  363. }
  364. func (w *legacyLayerWriter) initUtilityVM() error {
  365. if !w.HasUtilityVM {
  366. err := safefile.MkdirRelative(utilityVMPath, w.destRoot)
  367. if err != nil {
  368. return err
  369. }
  370. // Server 2016 does not support multiple layers for the utility VM, so
  371. // clone the utility VM from the parent layer into this layer. Use hard
  372. // links to avoid unnecessary copying, since most of the files are
  373. // immutable.
  374. err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
  375. if err != nil {
  376. return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
  377. }
  378. w.HasUtilityVM = true
  379. }
  380. return nil
  381. }
  382. func (w *legacyLayerWriter) reset() error {
  383. err := w.bufWriter.Flush()
  384. if err != nil {
  385. return err
  386. }
  387. w.bufWriter.Reset(io.Discard)
  388. if w.currentIsDir {
  389. r := w.currentFile
  390. br := winio.NewBackupStreamReader(r)
  391. // Seek to the beginning of the backup stream, skipping the fileattrs
  392. if _, err := r.Seek(4, io.SeekStart); err != nil {
  393. return err
  394. }
  395. for {
  396. bhdr, err := br.Next()
  397. if err == io.EOF {
  398. // end of backupstream data
  399. break
  400. }
  401. if err != nil {
  402. return err
  403. }
  404. switch bhdr.Id {
  405. case winio.BackupReparseData:
  406. // The current file is a `.$wcidirs$` metadata file that
  407. // describes a directory reparse point. Delete the placeholder
  408. // directory to prevent future files being added into the
  409. // destination of the reparse point during the ImportLayer call
  410. if err := safefile.RemoveRelative(w.currentFileName, w.currentFileRoot); err != nil {
  411. return err
  412. }
  413. w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
  414. default:
  415. // ignore all other stream types, as we only care about directory reparse points
  416. }
  417. }
  418. w.currentIsDir = false
  419. }
  420. if w.backupWriter != nil {
  421. w.backupWriter.Close()
  422. w.backupWriter = nil
  423. }
  424. if w.currentFile != nil {
  425. w.currentFile.Close()
  426. w.currentFile = nil
  427. w.currentFileName = ""
  428. w.currentFileRoot = nil
  429. }
  430. return nil
  431. }
  432. // copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
  433. func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
  434. src, err := safefile.OpenRelative(
  435. subPath,
  436. srcRoot,
  437. syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
  438. syscall.FILE_SHARE_READ,
  439. winapi.FILE_OPEN,
  440. winapi.FILE_OPEN_REPARSE_POINT)
  441. if err != nil {
  442. return nil, err
  443. }
  444. defer src.Close()
  445. srcr := winio.NewBackupFileReader(src, true)
  446. defer srcr.Close()
  447. fileInfo, err = winio.GetFileBasicInfo(src)
  448. if err != nil {
  449. return nil, err
  450. }
  451. extraFlags := uint32(0)
  452. if isDir {
  453. extraFlags |= winapi.FILE_DIRECTORY_FILE
  454. }
  455. dest, err := safefile.OpenRelative(
  456. subPath,
  457. destRoot,
  458. syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
  459. syscall.FILE_SHARE_READ,
  460. winapi.FILE_CREATE,
  461. extraFlags)
  462. if err != nil {
  463. return nil, err
  464. }
  465. defer dest.Close()
  466. err = winio.SetFileBasicInfo(dest, fileInfo)
  467. if err != nil {
  468. return nil, err
  469. }
  470. destw := winio.NewBackupFileWriter(dest, true)
  471. defer func() {
  472. cerr := destw.Close()
  473. if err == nil {
  474. err = cerr
  475. }
  476. }()
  477. _, err = io.Copy(destw, srcr)
  478. if err != nil {
  479. return nil, err
  480. }
  481. return fileInfo, nil
  482. }
  483. // cloneTree clones a directory tree using hard links. It skips hard links for
  484. // the file names in the provided map and just copies those files.
  485. func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
  486. var di []dirInfo
  487. err := safefile.EnsureNotReparsePointRelative(subPath, srcRoot)
  488. if err != nil {
  489. return err
  490. }
  491. err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
  492. if err != nil {
  493. return err
  494. }
  495. relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
  496. if err != nil {
  497. return err
  498. }
  499. fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
  500. // Directories, reparse points, and files that will be mutated during
  501. // utility VM import must be copied. All other files can be hard linked.
  502. isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
  503. // In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
  504. // See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc
  505. // Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly
  506. isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
  507. if isDir || isReparsePoint || mutatedFiles[relPath] {
  508. fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
  509. if err != nil {
  510. return err
  511. }
  512. if isDir {
  513. di = append(di, dirInfo{path: relPath, fileInfo: *fi})
  514. }
  515. } else {
  516. err = safefile.LinkRelative(relPath, srcRoot, relPath, destRoot)
  517. if err != nil {
  518. return err
  519. }
  520. }
  521. return nil
  522. })
  523. if err != nil {
  524. return err
  525. }
  526. return reapplyDirectoryTimes(destRoot, di)
  527. }
  528. func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
  529. if err := w.reset(); err != nil {
  530. return err
  531. }
  532. if name == utilityVMPath {
  533. return w.initUtilityVM()
  534. }
  535. if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
  536. w.changedDi = append(w.changedDi, dirInfo{path: name, fileInfo: *fileInfo})
  537. }
  538. name = filepath.Clean(name)
  539. if hasPathPrefix(name, utilityVMPath) {
  540. if !w.HasUtilityVM {
  541. return errors.New("missing UtilityVM directory")
  542. }
  543. if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
  544. return errors.New("invalid UtilityVM layer")
  545. }
  546. createDisposition := uint32(winapi.FILE_OPEN)
  547. if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
  548. st, err := safefile.LstatRelative(name, w.destRoot)
  549. if err != nil && !os.IsNotExist(err) {
  550. return err
  551. }
  552. if st != nil {
  553. // Delete the existing file/directory if it is not the same type as this directory.
  554. existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
  555. if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
  556. if err = safefile.RemoveAllRelative(name, w.destRoot); err != nil {
  557. return err
  558. }
  559. st = nil
  560. }
  561. }
  562. if st == nil {
  563. if err = safefile.MkdirRelative(name, w.destRoot); err != nil {
  564. return err
  565. }
  566. }
  567. } else {
  568. // Overwrite any existing hard link.
  569. err := safefile.RemoveRelative(name, w.destRoot)
  570. if err != nil && !os.IsNotExist(err) {
  571. return err
  572. }
  573. createDisposition = winapi.FILE_CREATE
  574. }
  575. f, err := safefile.OpenRelative(
  576. name,
  577. w.destRoot,
  578. syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
  579. syscall.FILE_SHARE_READ,
  580. createDisposition,
  581. winapi.FILE_OPEN_REPARSE_POINT,
  582. )
  583. if err != nil {
  584. return err
  585. }
  586. defer func() {
  587. if f != nil {
  588. f.Close()
  589. _ = safefile.RemoveRelative(name, w.destRoot)
  590. }
  591. }()
  592. err = winio.SetFileBasicInfo(f, fileInfo)
  593. if err != nil {
  594. return err
  595. }
  596. w.backupWriter = winio.NewBackupFileWriter(f, true)
  597. w.bufWriter.Reset(w.backupWriter)
  598. w.currentFile = f
  599. w.currentFileName = name
  600. w.currentFileRoot = w.destRoot
  601. w.addedFiles[name] = true
  602. f = nil
  603. return nil
  604. }
  605. fname := name
  606. if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
  607. err := safefile.MkdirRelative(name, w.root)
  608. if err != nil {
  609. return err
  610. }
  611. fname += ".$wcidirs$"
  612. w.currentIsDir = true
  613. }
  614. f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, winapi.FILE_CREATE, 0)
  615. if err != nil {
  616. return err
  617. }
  618. defer func() {
  619. if f != nil {
  620. f.Close()
  621. _ = safefile.RemoveRelative(fname, w.root)
  622. }
  623. }()
  624. strippedFi := *fileInfo
  625. strippedFi.FileAttributes = 0
  626. err = winio.SetFileBasicInfo(f, &strippedFi)
  627. if err != nil {
  628. return err
  629. }
  630. if hasPathPrefix(name, hivesPath) {
  631. w.backupWriter = winio.NewBackupFileWriter(f, false)
  632. w.bufWriter.Reset(w.backupWriter)
  633. } else {
  634. w.bufWriter.Reset(f)
  635. // The file attributes are written before the stream.
  636. err = binary.Write(w.bufWriter, binary.LittleEndian, uint32(fileInfo.FileAttributes))
  637. if err != nil {
  638. w.bufWriter.Reset(io.Discard)
  639. return err
  640. }
  641. }
  642. w.currentFile = f
  643. w.currentFileName = name
  644. w.currentFileRoot = w.root
  645. w.addedFiles[name] = true
  646. f = nil
  647. return nil
  648. }
  649. func (w *legacyLayerWriter) AddLink(name string, target string) error {
  650. if err := w.reset(); err != nil {
  651. return err
  652. }
  653. target = filepath.Clean(target)
  654. var roots []*os.File
  655. if hasPathPrefix(target, filesPath) {
  656. // Look for cross-layer hard link targets in the parent layers, since
  657. // nothing is in the destination path yet.
  658. roots = w.parentRoots
  659. } else if hasPathPrefix(target, utilityVMFilesPath) {
  660. // Since the utility VM is fully cloned into the destination path
  661. // already, look for cross-layer hard link targets directly in the
  662. // destination path.
  663. roots = []*os.File{w.destRoot}
  664. }
  665. if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
  666. return errors.New("invalid hard link in layer")
  667. }
  668. // Try to find the target of the link in a previously added file. If that
  669. // fails, search in parent layers.
  670. var selectedRoot *os.File
  671. if _, ok := w.addedFiles[target]; ok {
  672. selectedRoot = w.destRoot
  673. } else {
  674. for _, r := range roots {
  675. if _, err := safefile.LstatRelative(target, r); err != nil {
  676. if !os.IsNotExist(err) {
  677. return err
  678. }
  679. } else {
  680. selectedRoot = r
  681. break
  682. }
  683. }
  684. if selectedRoot == nil {
  685. return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
  686. }
  687. }
  688. // The link can't be written until after the ImportLayer call.
  689. w.PendingLinks = append(w.PendingLinks, pendingLink{
  690. Path: name,
  691. Target: target,
  692. TargetRoot: selectedRoot,
  693. })
  694. w.addedFiles[name] = true
  695. return nil
  696. }
  697. func (w *legacyLayerWriter) Remove(name string) error {
  698. name = filepath.Clean(name)
  699. if hasPathPrefix(name, filesPath) {
  700. w.Tombstones = append(w.Tombstones, name)
  701. } else if hasPathPrefix(name, utilityVMFilesPath) {
  702. err := w.initUtilityVM()
  703. if err != nil {
  704. return err
  705. }
  706. // Make sure the path exists; os.RemoveAll will not fail if the file is
  707. // already gone, and this needs to be a fatal error for diagnostics
  708. // purposes.
  709. if _, err := safefile.LstatRelative(name, w.destRoot); err != nil {
  710. return err
  711. }
  712. err = safefile.RemoveAllRelative(name, w.destRoot)
  713. if err != nil {
  714. return err
  715. }
  716. } else {
  717. return fmt.Errorf("invalid tombstone %s", name)
  718. }
  719. return nil
  720. }
  721. func (w *legacyLayerWriter) Write(b []byte) (int, error) {
  722. if w.backupWriter == nil && w.currentFile == nil {
  723. return 0, errors.New("closed")
  724. }
  725. return w.bufWriter.Write(b)
  726. }
  727. func (w *legacyLayerWriter) Close() error {
  728. if err := w.reset(); err != nil {
  729. return err
  730. }
  731. if err := safefile.RemoveRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
  732. return err
  733. }
  734. for _, pd := range w.pendingDirs {
  735. err := safefile.MkdirRelative(pd.Path, pd.Root)
  736. if err != nil {
  737. return err
  738. }
  739. }
  740. return nil
  741. }