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 }