Beispiel #1
0
// extractFile extracts the file described by hdr from the given tarball into
// the target directory.
// If overwrite is true, existing files will be overwritten.
func extractFile(tr *tar.Reader, target string, hdr *tar.Header, overwrite bool, editor FilePermissionsEditor) error {
	p := filepath.Join(target, hdr.Name)
	fi := hdr.FileInfo()
	typ := hdr.Typeflag
	if overwrite {
		info, err := os.Lstat(p)
		switch {
		case os.IsNotExist(err):
		case err == nil:
			// If the old and new paths are both dirs do nothing or
			// RemoveAll will remove all dir's contents
			if !info.IsDir() || typ != tar.TypeDir {
				err := os.RemoveAll(p)
				if err != nil {
					return err
				}
			}
		default:
			return err
		}
	}

	// Create parent dir if it doesn't exist
	if err := os.MkdirAll(filepath.Dir(p), DEFAULT_DIR_MODE); err != nil {
		return err
	}
	switch {
	case typ == tar.TypeReg || typ == tar.TypeRegA:
		f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode())
		if err != nil {
			return err
		}
		_, err = io.Copy(f, tr)
		if err != nil {
			f.Close()
			return err
		}
		f.Close()
	case typ == tar.TypeDir:
		if err := os.MkdirAll(p, fi.Mode()); err != nil {
			return err
		}
		dir, err := os.Open(p)
		if err != nil {
			return err
		}
		if err := dir.Chmod(fi.Mode()); err != nil {
			dir.Close()
			return err
		}
		dir.Close()
	case typ == tar.TypeLink:
		dest := filepath.Join(target, hdr.Linkname)
		if err := os.Link(dest, p); err != nil {
			return err
		}
	case typ == tar.TypeSymlink:
		if err := os.Symlink(hdr.Linkname, p); err != nil {
			return err
		}
	case typ == tar.TypeChar:
		dev := device.Makedev(uint(hdr.Devmajor), uint(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFCHR
		if err := syscall.Mknod(p, mode, int(dev)); err != nil {
			return err
		}
	case typ == tar.TypeBlock:
		dev := device.Makedev(uint(hdr.Devmajor), uint(hdr.Devminor))
		mode := uint32(fi.Mode()) | syscall.S_IFBLK
		if err := syscall.Mknod(p, mode, int(dev)); err != nil {
			return err
		}
	case typ == tar.TypeFifo:
		if err := syscall.Mkfifo(p, uint32(fi.Mode())); err != nil {
			return err
		}
	// TODO(jonboulle): implement other modes
	default:
		return fmt.Errorf("unsupported type: %v", typ)
	}

	if editor != nil {
		if err := editor(p, hdr.Uid, hdr.Gid, hdr.Typeflag, fi); err != nil {
			return err
		}
	}

	// Restore entry atime and mtime.
	// Use special function LUtimesNano not available on go's syscall package because we
	// have to restore symlink's times and not the referenced file times.
	ts := HdrToTimespec(hdr)
	if hdr.Typeflag != tar.TypeSymlink {
		if err := syscall.UtimesNano(p, ts); err != nil {
			return err
		}
	} else {
		if err := fileutil.LUtimesNano(p, ts); err != nil && err != ErrNotSupportedPlatform {
			return err
		}
	}

	return nil
}
Beispiel #2
0
func CopyTree(src, dest string, uidRange *user.UidRange) error {
	cleanSrc := filepath.Clean(src)

	dirs := make(map[string][]syscall.Timespec)
	copyWalker := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		rootLess := path[len(cleanSrc):]
		target := filepath.Join(dest, rootLess)
		mode := info.Mode()
		switch {
		case mode.IsDir():
			err := os.Mkdir(target, mode.Perm())
			if err != nil {
				return err
			}

			dir, err := os.Open(target)
			if err != nil {
				return err
			}
			if err := dir.Chmod(mode); err != nil {
				dir.Close()
				return err
			}
			dir.Close()
		case mode.IsRegular():
			if err := CopyRegularFile(path, target); err != nil {
				return err
			}
		case mode&os.ModeSymlink == os.ModeSymlink:
			if err := CopySymlink(path, target); err != nil {
				return err
			}
		case mode&os.ModeCharDevice == os.ModeCharDevice:
			stat := syscall.Stat_t{}
			if err := syscall.Stat(path, &stat); err != nil {
				return err
			}

			dev := device.Makedev(device.Major(stat.Rdev), device.Minor(stat.Rdev))
			mode := uint32(mode) | syscall.S_IFCHR
			if err := syscall.Mknod(target, mode, int(dev)); err != nil {
				return err
			}
		case mode&os.ModeDevice == os.ModeDevice:
			stat := syscall.Stat_t{}
			if err := syscall.Stat(path, &stat); err != nil {
				return err
			}

			dev := device.Makedev(device.Major(stat.Rdev), device.Minor(stat.Rdev))
			mode := uint32(mode) | syscall.S_IFBLK
			if err := syscall.Mknod(target, mode, int(dev)); err != nil {
				return err
			}
		case mode&os.ModeNamedPipe == os.ModeNamedPipe:
			if err := syscall.Mkfifo(target, uint32(mode)); err != nil {
				return err
			}
		default:
			return fmt.Errorf("unsupported mode: %v", mode)
		}

		var srcUid = info.Sys().(*syscall.Stat_t).Uid
		var srcGid = info.Sys().(*syscall.Stat_t).Gid

		shiftedUid, shiftedGid, err := uidRange.ShiftRange(srcUid, srcGid)
		if err != nil {
			return err
		}

		if err := os.Lchown(target, int(shiftedUid), int(shiftedGid)); err != nil {
			return err
		}

		// lchown(2) says that, depending on the linux kernel version, it
		// can change the file's mode also if executed as root. So call
		// os.Chmod after it.
		if mode&os.ModeSymlink != os.ModeSymlink {
			if err := os.Chmod(target, mode); err != nil {
				return err
			}
		}

		ts, err := pathToTimespec(path)
		if err != nil {
			return err
		}

		if mode.IsDir() {
			dirs[target] = ts
		}
		if mode&os.ModeSymlink != os.ModeSymlink {
			if err := syscall.UtimesNano(target, ts); err != nil {
				return err
			}
		} else {
			if err := LUtimesNano(target, ts); err != nil {
				return err
			}
		}

		return nil
	}

	if err := filepath.Walk(cleanSrc, copyWalker); err != nil {
		return err
	}

	// Restore dirs atime and mtime. This has to be done after copying
	// as a file copying will change its parent directory's times.
	for dirPath, ts := range dirs {
		if err := syscall.UtimesNano(dirPath, ts); err != nil {
			return err
		}
	}

	return nil
}
Beispiel #3
0
func main() {
	globalFlagset.Parse(os.Args[1:])
	args := globalFlagset.Args()
	if len(args) > 0 {
		fmt.Fprintln(os.Stderr, "Wrong parameters")
		os.Exit(1)
	}

	if globalFlags.PrintNoNewPrivs {
		r1, _, err := syscall.Syscall(
			syscall.SYS_PRCTL,
			uintptr(unix.PR_GET_NO_NEW_PRIVS),
			uintptr(0), uintptr(0),
		)

		fmt.Printf("no_new_privs: %v err: %v\n", r1, err)
	}

	if globalFlags.CheckMknod != "" {
		/* format: c:5:2:name */
		dev := strings.SplitN(globalFlags.CheckMknod, ":", 4)
		if len(dev) < 4 {
			fmt.Fprintln(os.Stderr, "Not enough parameters for mknod")
			os.Exit(1)
		}
		typ := dev[0]
		major, err := strconv.Atoi(dev[1])
		if err != nil {
			fmt.Fprintln(os.Stderr, "Wrong major")
			os.Exit(1)
		}
		minor, err := strconv.Atoi(dev[2])
		if err != nil {
			fmt.Fprintln(os.Stderr, "Wrong minor")
			os.Exit(1)
		}
		nodeName := dev[3]

		majorMinor := device.Makedev(uint(major), uint(minor))
		mode := uint32(0777)
		switch typ {
		case "c":
			mode |= syscall.S_IFCHR
		case "b":
			mode |= syscall.S_IFBLK
		default:
			fmt.Fprintln(os.Stderr, "Wrong device node type")
			os.Exit(1)
		}

		if err := syscall.Mknod(nodeName, mode, int(majorMinor)); err != nil {
			fmt.Fprintf(os.Stderr, "mknod %s: fail: %v\n", nodeName, err)
			os.Exit(1)
		} else {
			fmt.Printf("mknod %s: succeed\n", nodeName)
			os.Exit(0)
		}
	}

	if globalFlags.SilentSigterm {
		terminateCh := make(chan os.Signal, 1)
		signal.Notify(terminateCh, syscall.SIGTERM)
		go func() {
			<-terminateCh
			os.Exit(0)
		}()
	}

	if globalFlags.PreSleep >= 0 {
		time.Sleep(time.Duration(globalFlags.PreSleep) * time.Second)
	}

	if globalFlags.ReadStdin {
		reader := bufio.NewReader(os.Stdin)
		fmt.Printf("Enter text:\n")
		text, _ := reader.ReadString('\n')
		fmt.Printf("Received text: %s\n", text)
	}

	if globalFlags.CheckTty {
		fd := int(os.Stdin.Fd())
		var termios syscall.Termios
		_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), syscall.TCGETS, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
		if err == 0 {
			fmt.Printf("stdin is a terminal\n")
		} else {
			fmt.Printf("stdin is not a terminal\n")
		}
	}

	if globalFlags.CheckPath {
		envBytes, err := ioutil.ReadFile("/proc/self/environ")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading environment from \"/proc/self/environ\": %v\n", err)
			os.Exit(1)
		}
		for _, v := range bytes.Split(envBytes, []byte{0}) {
			if len(v) == 0 {
				continue
			}
			if strings.HasPrefix(string(v), "PATH=") {
				if strings.Contains(string(v), "\n") {
					fmt.Fprintf(os.Stderr, "Malformed PATH: found new line")
					os.Exit(1)
				} else {
					fmt.Printf("PATH is good\n")
					os.Exit(0)
				}
			} else {
				continue
			}
		}
		fmt.Fprintf(os.Stderr, "PATH not found")
		os.Exit(1)
	}

	if globalFlags.PrintExec {
		fmt.Fprintf(os.Stdout, "inspect execed as: %s\n", os.Args[0])
	}

	if globalFlags.PrintMsg != "" {
		fmt.Fprintf(os.Stdout, "%s\n", globalFlags.PrintMsg)
		messageLoopStr := os.Getenv("MESSAGE_LOOP")
		messageLoop, err := strconv.Atoi(messageLoopStr)
		if err == nil {
			for i := 0; i < messageLoop; i++ {
				time.Sleep(time.Second)
				fmt.Fprintf(os.Stdout, "%s\n", globalFlags.PrintMsg)
			}
		}
	}

	if globalFlags.PrintEnv != "" {
		fmt.Fprintf(os.Stdout, "%s=%s\n", globalFlags.PrintEnv, os.Getenv(globalFlags.PrintEnv))
	}

	if globalFlags.PrintCapsPid >= 0 {
		caps, err := capability.NewPid(globalFlags.PrintCapsPid)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot get caps: %v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Capability set: effective: %s (%s)\n", caps.StringCap(capability.EFFECTIVE), globalFlags.SuffixMsg)
		fmt.Printf("Capability set: permitted: %s (%s)\n", caps.StringCap(capability.PERMITTED), globalFlags.SuffixMsg)
		fmt.Printf("Capability set: inheritable: %s (%s)\n", caps.StringCap(capability.INHERITABLE), globalFlags.SuffixMsg)
		fmt.Printf("Capability set: bounding: %s (%s)\n", caps.StringCap(capability.BOUNDING), globalFlags.SuffixMsg)

		if capStr := os.Getenv("CAPABILITY"); capStr != "" {
			capInt, err := strconv.Atoi(capStr)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Environment variable $CAPABILITY is not a valid capability number: %v\n", err)
				os.Exit(1)
			}
			c := capability.Cap(capInt)
			if caps.Get(capability.BOUNDING, c) {
				fmt.Printf("%v=enabled (%s)\n", c.String(), globalFlags.SuffixMsg)
			} else {
				fmt.Printf("%v=disabled (%s)\n", c.String(), globalFlags.SuffixMsg)
			}
		}
	}

	if globalFlags.PrintUser {
		fmt.Printf("User: uid=%d euid=%d gid=%d egid=%d\n", os.Getuid(), os.Geteuid(), os.Getgid(), os.Getegid())
	}

	if globalFlags.PrintGroups {
		gids, err := os.Getgroups()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error getting groups: %v\n", err)
			os.Exit(1)
		}
		// getgroups(2): It is unspecified whether the effective group ID of
		// the calling process is included in the returned list. (Thus, an
		// application should also call getegid(2) and add or remove the
		// resulting value.)
		egid := os.Getegid()
		if !in(gids, egid) {
			gids = append(gids, egid)
			sort.Ints(gids)
		}
		var b bytes.Buffer
		for _, gid := range gids {
			b.WriteString(fmt.Sprintf("%d ", gid))
		}
		fmt.Printf("Groups: %s\n", b.String())
	}

	if globalFlags.WriteFile {
		fileName := os.Getenv("FILE")
		if globalFlags.FileName != "" {
			fileName = globalFlags.FileName
		}
		content := os.Getenv("CONTENT")
		if globalFlags.Content != "" {
			content = globalFlags.Content
		}

		err := ioutil.WriteFile(fileName, []byte(content), 0600)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot write to file %q: %v\n", fileName, err)
			os.Exit(1)
		}
	}

	if globalFlags.ReadFile {
		fileName := os.Getenv("FILE")
		if globalFlags.FileName != "" {
			fileName = globalFlags.FileName
		}

		dat, err := ioutil.ReadFile(fileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot read file %q: %v\n", fileName, err)
			os.Exit(1)
		}
		fmt.Print("<<<")
		fmt.Print(string(dat))
		fmt.Print(">>>\n")
	}

	if globalFlags.StatFile {
		fileName := os.Getenv("FILE")
		if globalFlags.FileName != "" {
			fileName = globalFlags.FileName
		}

		fi, err := os.Stat(fileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot stat file %q: %v\n", fileName, err)
			os.Exit(1)
		}
		fmt.Printf("%s: mode: %s\n", fileName, fi.Mode().String())
		fmt.Printf("%s: user: %v\n", fileName, fi.Sys().(*syscall.Stat_t).Uid)
		fmt.Printf("%s: group: %v\n", fileName, fi.Sys().(*syscall.Stat_t).Gid)
	}

	if globalFlags.PrintCwd {
		wd, err := os.Getwd()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Cannot get working directory: %v\n", err)
			os.Exit(1)
		}
		fmt.Printf("cwd: %s\n", wd)
	}

	if globalFlags.Sleep >= 0 {
		time.Sleep(time.Duration(globalFlags.Sleep) * time.Second)
	}

	if globalFlags.PrintMemoryLimit {
		memCgroupPath, err := cgroup.GetOwnCgroupPath("memory")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error getting own memory cgroup path: %v\n", err)
			os.Exit(1)
		}
		// we use /proc/1/root to escape the chroot we're in and read our
		// memory limit
		limitPath := filepath.Join("/proc/1/root/sys/fs/cgroup/memory", memCgroupPath, "memory.limit_in_bytes")
		limit, err := ioutil.ReadFile(limitPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Can't read memory.limit_in_bytes\n")
			os.Exit(1)
		}

		fmt.Printf("Memory Limit: %s\n", string(limit))
	}

	if globalFlags.PrintCPUQuota {
		cpuCgroupPath, err := cgroup.GetOwnCgroupPath("cpu")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error getting own cpu cgroup path: %v\n", err)
			os.Exit(1)
		}
		// we use /proc/1/root to escape the chroot we're in and read our
		// cpu quota
		periodPath := filepath.Join("/proc/1/root/sys/fs/cgroup/cpu", cpuCgroupPath, "cpu.cfs_period_us")
		periodBytes, err := ioutil.ReadFile(periodPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Can't read cpu.cpu_period_us\n")
			os.Exit(1)
		}
		quotaPath := filepath.Join("/proc/1/root/sys/fs/cgroup/cpu", cpuCgroupPath, "cpu.cfs_quota_us")
		quotaBytes, err := ioutil.ReadFile(quotaPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Can't read cpu.cpu_quota_us\n")
			os.Exit(1)
		}

		period, err := strconv.Atoi(strings.Trim(string(periodBytes), "\n"))
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		quota, err := strconv.Atoi(strings.Trim(string(quotaBytes), "\n"))
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}

		quotaMilliCores := quota * 1000 / period
		fmt.Printf("CPU Quota: %s\n", strconv.Itoa(quotaMilliCores))
	}

	if globalFlags.CheckCgroupMounts {
		rootCgroupPath := "/proc/1/root/sys/fs/cgroup"
		testPaths := []string{rootCgroupPath}

		// test a couple of controllers if they're available
		if _, err := os.Stat(filepath.Join(rootCgroupPath, "memory")); err == nil {
			testPaths = append(testPaths, filepath.Join(rootCgroupPath, "memory"))
		}
		if _, err := os.Stat(filepath.Join(rootCgroupPath, "cpu")); err == nil {
			testPaths = append(testPaths, filepath.Join(rootCgroupPath, "cpu"))
		}

		for _, p := range testPaths {
			if err := syscall.Mkdir(filepath.Join(p, "test"), 0600); err == nil || err != syscall.EROFS {
				fmt.Fprintf(os.Stderr, "check-cgroups: FAIL (%v)", err)
				os.Exit(1)
			}
		}

		fmt.Println("check-cgroups: SUCCESS")
	}

	if globalFlags.PrintNetNS {
		ns, err := os.Readlink("/proc/self/ns/net")
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("NetNS: %s\n", ns)
	}

	if globalFlags.PrintIPv4 != "" {
		iface := globalFlags.PrintIPv4
		ips, err := testutils.GetIPsv4(iface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		if len(ips) == 0 {
			fmt.Fprintf(os.Stderr, "No IPv4 found for interface %+v:\n", iface)
			os.Exit(1)
		}
		fmt.Printf("%v IPv4: %s\n", iface, ips[0])
	}

	if globalFlags.PrintDefaultGWv4 {
		gw, err := testutils.GetDefaultGWv4()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("DefaultGWv4: %s\n", gw)
	}

	if globalFlags.PrintDefaultGWv6 {
		gw, err := testutils.GetDefaultGWv6()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("DefaultGWv6: %s\n", gw)
	}

	if globalFlags.PrintGWv4 != "" {
		// TODO: GetGW not implemented yet
		iface := globalFlags.PrintGWv4
		gw, err := testutils.GetGWv4(iface)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("%v GWv4: %s\n", iface, gw)
	}

	if globalFlags.PrintIPv6 != "" {
		// TODO
	}

	if globalFlags.PrintGWv6 != "" {
		// TODO
	}

	if globalFlags.PrintHostname {
		hostname, err := os.Hostname()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Hostname: %s\n", hostname)
	}

	if globalFlags.ServeHTTP != "" {
		err := testutils.HTTPServe(globalFlags.ServeHTTP, globalFlags.ServeHTTPTimeout)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
	}

	if globalFlags.GetHTTP != "" {
		body, err := testutils.HTTPGet(globalFlags.GetHTTP)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("HTTP-Get received: %s\n", body)
	}

	if globalFlags.PrintIfaceCount {
		ifaceCount, err := testutils.GetIfaceCount()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Interface count: %d\n", ifaceCount)
	}

	if globalFlags.PrintAppAnnotation != "" {
		mdsUrl, appName := os.Getenv("AC_METADATA_URL"), os.Getenv("AC_APP_NAME")
		body, err := testutils.HTTPGet(fmt.Sprintf("%s/acMetadata/v1/apps/%s/annotations/%s", mdsUrl, appName, globalFlags.PrintAppAnnotation))
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		fmt.Printf("Annotation %s=%s\n", globalFlags.PrintAppAnnotation, body)
	}

	if globalFlags.CheckMountNS {
		appMountNS, err := os.Readlink("/proc/self/ns/mnt")
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		s1MountNS, err := os.Readlink("/proc/1/ns/mnt")
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		if appMountNS != s1MountNS {
			fmt.Println("check-mountns: DIFFERENT")
		} else {
			fmt.Println("check-mountns: IDENTICAL")
			os.Exit(1)
		}
	}

	os.Exit(globalFlags.ExitCode)
}