tar_unix.go 5.7 KB

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