tar_unix.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // +build !windows
  2. /*
  3. Copyright The containerd Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package archive
  15. import (
  16. "archive/tar"
  17. "os"
  18. "strings"
  19. "syscall"
  20. "github.com/containerd/containerd/sys"
  21. "github.com/containerd/continuity/fs"
  22. "github.com/containerd/continuity/sysx"
  23. "github.com/pkg/errors"
  24. "golang.org/x/sys/unix"
  25. )
  26. func tarName(p string) (string, error) {
  27. return p, nil
  28. }
  29. func chmodTarEntry(perm os.FileMode) os.FileMode {
  30. return perm
  31. }
  32. func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) error {
  33. s, ok := fi.Sys().(*syscall.Stat_t)
  34. if !ok {
  35. return errors.New("unsupported stat type")
  36. }
  37. // Rdev is int32 on darwin/bsd, int64 on linux/solaris
  38. rdev := uint64(s.Rdev) // nolint: unconvert
  39. // Currently go does not fill in the major/minors
  40. if s.Mode&syscall.S_IFBLK != 0 ||
  41. s.Mode&syscall.S_IFCHR != 0 {
  42. hdr.Devmajor = int64(unix.Major(rdev))
  43. hdr.Devminor = int64(unix.Minor(rdev))
  44. }
  45. return nil
  46. }
  47. func open(p string) (*os.File, error) {
  48. return os.Open(p)
  49. }
  50. func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
  51. f, err := os.OpenFile(name, flag, perm)
  52. if err != nil {
  53. return nil, err
  54. }
  55. // Call chmod to avoid permission mask
  56. if err := os.Chmod(name, perm); err != nil {
  57. return nil, err
  58. }
  59. return f, err
  60. }
  61. func mkdir(path string, perm os.FileMode) error {
  62. if err := os.Mkdir(path, perm); err != nil {
  63. return err
  64. }
  65. // Only final created directory gets explicit permission
  66. // call to avoid permission mask
  67. return os.Chmod(path, perm)
  68. }
  69. func skipFile(hdr *tar.Header) bool {
  70. switch hdr.Typeflag {
  71. case tar.TypeBlock, tar.TypeChar:
  72. // cannot create a device if running in user namespace
  73. return sys.RunningInUserNS()
  74. default:
  75. return false
  76. }
  77. }
  78. // handleTarTypeBlockCharFifo is an OS-specific helper function used by
  79. // createTarFile to handle the following types of header: Block; Char; Fifo.
  80. // This function must not be called for Block and Char when running in userns.
  81. // (skipFile() should return true for them.)
  82. func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
  83. mode := uint32(hdr.Mode & 07777)
  84. switch hdr.Typeflag {
  85. case tar.TypeBlock:
  86. mode |= unix.S_IFBLK
  87. case tar.TypeChar:
  88. mode |= unix.S_IFCHR
  89. case tar.TypeFifo:
  90. mode |= unix.S_IFIFO
  91. }
  92. return unix.Mknod(path, mode, int(unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))))
  93. }
  94. func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
  95. if hdr.Typeflag == tar.TypeLink {
  96. if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
  97. if err := os.Chmod(path, hdrInfo.Mode()); err != nil && !os.IsNotExist(err) {
  98. return err
  99. }
  100. }
  101. } else if hdr.Typeflag != tar.TypeSymlink {
  102. if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
  103. return err
  104. }
  105. }
  106. return nil
  107. }
  108. func getxattr(path, attr string) ([]byte, error) {
  109. b, err := sysx.LGetxattr(path, attr)
  110. if err == unix.ENOTSUP || err == sysx.ENODATA {
  111. return nil, nil
  112. }
  113. return b, err
  114. }
  115. func setxattr(path, key, value string) error {
  116. // Do not set trusted attributes
  117. if strings.HasPrefix(key, "trusted.") {
  118. return errors.Wrap(unix.ENOTSUP, "admin attributes from archive not supported")
  119. }
  120. return unix.Lsetxattr(path, key, []byte(value), 0)
  121. }
  122. func copyDirInfo(fi os.FileInfo, path string) error {
  123. st := fi.Sys().(*syscall.Stat_t)
  124. if err := os.Lchown(path, int(st.Uid), int(st.Gid)); err != nil {
  125. if os.IsPermission(err) {
  126. // Normally if uid/gid are the same this would be a no-op, but some
  127. // filesystems may still return EPERM... for instance NFS does this.
  128. // In such a case, this is not an error.
  129. if dstStat, err2 := os.Lstat(path); err2 == nil {
  130. st2 := dstStat.Sys().(*syscall.Stat_t)
  131. if st.Uid == st2.Uid && st.Gid == st2.Gid {
  132. err = nil
  133. }
  134. }
  135. }
  136. if err != nil {
  137. return errors.Wrapf(err, "failed to chown %s", path)
  138. }
  139. }
  140. if err := os.Chmod(path, fi.Mode()); err != nil {
  141. return errors.Wrapf(err, "failed to chmod %s", path)
  142. }
  143. timespec := []unix.Timespec{
  144. unix.NsecToTimespec(syscall.TimespecToNsec(fs.StatAtime(st))),
  145. unix.NsecToTimespec(syscall.TimespecToNsec(fs.StatMtime(st))),
  146. }
  147. if err := unix.UtimesNanoAt(unix.AT_FDCWD, path, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
  148. return errors.Wrapf(err, "failed to utime %s", path)
  149. }
  150. return nil
  151. }
  152. func copyUpXAttrs(dst, src string) error {
  153. xattrKeys, err := sysx.LListxattr(src)
  154. if err != nil {
  155. if err == unix.ENOTSUP || err == sysx.ENODATA {
  156. return nil
  157. }
  158. return errors.Wrapf(err, "failed to list xattrs on %s", src)
  159. }
  160. for _, xattr := range xattrKeys {
  161. // Do not copy up trusted attributes
  162. if strings.HasPrefix(xattr, "trusted.") {
  163. continue
  164. }
  165. data, err := sysx.LGetxattr(src, xattr)
  166. if err != nil {
  167. if err == unix.ENOTSUP || err == sysx.ENODATA {
  168. continue
  169. }
  170. return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
  171. }
  172. if err := unix.Lsetxattr(dst, xattr, data, unix.XATTR_CREATE); err != nil {
  173. if err == unix.ENOTSUP || err == unix.ENODATA || err == unix.EEXIST {
  174. continue
  175. }
  176. return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
  177. }
  178. }
  179. return nil
  180. }