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