github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/archive/tar_unix.go (about)

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