legacy.go 20 KB

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