1// +build !windows 2 3package archive // import "github.com/ory/dockertest/docker/pkg/archive" 4 5import ( 6 "archive/tar" 7 "bufio" 8 "errors" 9 "fmt" 10 "os" 11 "path/filepath" 12 "syscall" 13 14 "github.com/ory/dockertest/docker/pkg/idtools" 15 "github.com/ory/dockertest/docker/pkg/system" 16 "golang.org/x/sys/unix" 17) 18 19// fixVolumePathPrefix does platform specific processing to ensure that if 20// the path being passed in is not in a volume path format, convert it to one. 21func fixVolumePathPrefix(srcPath string) string { 22 return srcPath 23} 24 25// getWalkRoot calculates the root path when performing a TarWithOptions. 26// We use a separate function as this is platform specific. On Linux, we 27// can't use filepath.Join(srcPath,include) because this will clean away 28// a trailing "." or "/" which may be important. 29func getWalkRoot(srcPath string, include string) string { 30 return srcPath + string(filepath.Separator) + include 31} 32 33// CanonicalTarNameForPath returns platform-specific filepath 34// to canonical posix-style path for tar archival. p is relative 35// path. 36func CanonicalTarNameForPath(p string) (string, error) { 37 return p, nil // already unix-style 38} 39 40// chmodTarEntry is used to adjust the file permissions used in tar header based 41// on the platform the archival is done. 42 43func chmodTarEntry(perm os.FileMode) os.FileMode { 44 return perm // noop for unix as golang APIs provide perm bits correctly 45} 46 47func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (err error) { 48 s, ok := stat.(*syscall.Stat_t) 49 50 if ok { 51 // Currently go does not fill in the major/minors 52 if s.Mode&unix.S_IFBLK != 0 || 53 s.Mode&unix.S_IFCHR != 0 { 54 hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) // nolint: unconvert 55 hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) // nolint: unconvert 56 } 57 } 58 59 return 60} 61 62func getInodeFromStat(stat interface{}) (inode uint64, err error) { 63 s, ok := stat.(*syscall.Stat_t) 64 65 if ok { 66 inode = s.Ino 67 } 68 69 return 70} 71 72func getFileUIDGID(stat interface{}) (idtools.IDPair, error) { 73 s, ok := stat.(*syscall.Stat_t) 74 75 if !ok { 76 return idtools.IDPair{}, errors.New("cannot convert stat value to syscall.Stat_t") 77 } 78 return idtools.IDPair{UID: int(s.Uid), GID: int(s.Gid)}, nil 79} 80 81// handleTarTypeBlockCharFifo is an OS-specific helper function used by 82// createTarFile to handle the following types of header: Block; Char; Fifo 83func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { 84 if runningInUserNS() { 85 // cannot create a device if running in user namespace 86 return nil 87 } 88 89 mode := uint32(hdr.Mode & 07777) 90 switch hdr.Typeflag { 91 case tar.TypeBlock: 92 mode |= unix.S_IFBLK 93 case tar.TypeChar: 94 mode |= unix.S_IFCHR 95 case tar.TypeFifo: 96 mode |= unix.S_IFIFO 97 } 98 99 return system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))) 100} 101 102func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { 103 if hdr.Typeflag == tar.TypeLink { 104 if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { 105 if err := os.Chmod(path, hdrInfo.Mode()); err != nil { 106 return err 107 } 108 } 109 } else if hdr.Typeflag != tar.TypeSymlink { 110 if err := os.Chmod(path, hdrInfo.Mode()); err != nil { 111 return err 112 } 113 } 114 return nil 115} 116 117// runningInUserNS detects whether we are currently running in a user namespace. 118// Copied from github.com/opencontainers/runc/libcontainer/system/linux.go 119// Copied from github.com/lxc/lxd/shared/util.go 120func runningInUserNS() bool { 121 file, err := os.Open("/proc/self/uid_map") 122 if err != nil { 123 // This kernel-provided file only exists if user namespaces are supported 124 return false 125 } 126 defer file.Close() 127 128 buf := bufio.NewReader(file) 129 l, _, err := buf.ReadLine() 130 if err != nil { 131 return false 132 } 133 134 line := string(l) 135 var a, b, c int64 136 fmt.Sscanf(line, "%d %d %d", &a, &b, &c) 137 /* 138 * We assume we are in the initial user namespace if we have a full 139 * range - 4294967295 uids starting at uid 0. 140 */ 141 if a == 0 && b == 0 && c == 4294967295 { 142 return false 143 } 144 return true 145} 146