overlaydir_test.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // Copyright 2019 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package reboot_test
  5. import (
  6. "io"
  7. "io/fs"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. )
  12. // overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
  13. //
  14. // TODO: Once we no longer need to support the misc module in GOPATH mode,
  15. // factor this function out into a package to reduce duplication.
  16. func overlayDir(dstRoot, srcRoot string) error {
  17. dstRoot = filepath.Clean(dstRoot)
  18. if err := os.MkdirAll(dstRoot, 0777); err != nil {
  19. return err
  20. }
  21. srcRoot, err := filepath.Abs(srcRoot)
  22. if err != nil {
  23. return err
  24. }
  25. return filepath.WalkDir(srcRoot, func(srcPath string, entry fs.DirEntry, err error) error {
  26. if err != nil || srcPath == srcRoot {
  27. return err
  28. }
  29. if filepath.Base(srcPath) == "testdata" {
  30. // We're just building, so no need to copy those.
  31. return fs.SkipDir
  32. }
  33. suffix := strings.TrimPrefix(srcPath, srcRoot)
  34. for len(suffix) > 0 && suffix[0] == filepath.Separator {
  35. suffix = suffix[1:]
  36. }
  37. dstPath := filepath.Join(dstRoot, suffix)
  38. info, err := entry.Info()
  39. perm := info.Mode() & os.ModePerm
  40. if info.Mode()&os.ModeSymlink != 0 {
  41. info, err = os.Stat(srcPath)
  42. if err != nil {
  43. return err
  44. }
  45. perm = info.Mode() & os.ModePerm
  46. }
  47. // Always make copies of directories.
  48. // If we add a file in the overlay, we don't want to add it in the original.
  49. if info.IsDir() {
  50. return os.MkdirAll(dstPath, perm|0200)
  51. }
  52. // If we can use a hard link, do that instead of copying bytes.
  53. // Go builds don't like symlinks in some cases, such as go:embed.
  54. if err := os.Link(srcPath, dstPath); err == nil {
  55. return nil
  56. }
  57. // Otherwise, copy the bytes.
  58. src, err := os.Open(srcPath)
  59. if err != nil {
  60. return err
  61. }
  62. defer src.Close()
  63. dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
  64. if err != nil {
  65. return err
  66. }
  67. _, err = io.Copy(dst, src)
  68. if closeErr := dst.Close(); err == nil {
  69. err = closeErr
  70. }
  71. return err
  72. })
  73. }