File: bind_linux.go

package info (click to toggle)
golang-github-containers-buildah 1.39.3%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,724 kB
  • sloc: sh: 2,398; makefile: 236; perl: 187; asm: 16; awk: 12; ansic: 1
file content (102 lines) | stat: -rw-r--r-- 2,948 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package volumes

import (
	"errors"
	"fmt"
	"os"

	"github.com/containers/buildah/internal/open"
	"github.com/containers/storage/pkg/mount"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/unix"
)

// bindFromChroot opens "path" inside of "root" using a chrooted subprocess
// that returns a descriptor, then creates a uniquely-named temporary directory
// or file under "tmp" and bind-mounts the opened descriptor to it, returning
// the path of the temporary file or directory.  The caller is responsible for
// unmounting and removing the temporary.
func bindFromChroot(root, path, tmp string) (string, error) {
	fd, _, err := open.InChroot(root, "", path, unix.O_DIRECTORY|unix.O_RDONLY, 0)
	if err != nil {
		if !errors.Is(err, unix.ENOTDIR) {
			return "", fmt.Errorf("opening directory %q under %q: %w", path, root, err)
		}
		fd, _, err = open.InChroot(root, "", path, unix.O_RDWR, 0)
		if err != nil {
			return "", fmt.Errorf("opening non-directory %q under %q: %w", path, root, err)
		}
	}
	defer func() {
		if err := unix.Close(fd); err != nil {
			logrus.Debugf("closing %q under %q: %v", path, root, err)
		}
	}()

	succeeded := false
	var dest string
	var destF *os.File
	defer func() {
		if !succeeded {
			if destF != nil {
				if err := destF.Close(); err != nil {
					logrus.Debugf("closing bind target %q: %v", dest, err)
				}
			}
			if dest != "" {
				if err := os.Remove(dest); err != nil {
					logrus.Debugf("removing bind target %q: %v", dest, err)
				}
			}
		}
	}()

	var st unix.Stat_t
	if err = unix.Fstat(fd, &st); err != nil {
		return "", fmt.Errorf("checking if %q under %q was a directory: %w", path, root, err)
	}

	if st.Mode&unix.S_IFDIR == unix.S_IFDIR {
		if dest, err = os.MkdirTemp(tmp, "bind"); err != nil {
			return "", fmt.Errorf("creating a bind target directory: %w", err)
		}
	} else {
		if destF, err = os.CreateTemp(tmp, "bind"); err != nil {
			return "", fmt.Errorf("creating a bind target non-directory: %w", err)
		}
		if err := destF.Close(); err != nil {
			logrus.Debugf("closing bind target %q: %v", dest, err)
		}
		dest = destF.Name()
	}
	defer func() {
		if !succeeded {
			if err := os.Remove(dest); err != nil {
				logrus.Debugf("removing bind target %q: %v", dest, err)
			}
		}
	}()

	if err := unix.Mount(fmt.Sprintf("/proc/self/fd/%d", fd), dest, "bind", unix.MS_BIND, ""); err != nil {
		return "", fmt.Errorf("bind-mounting passed-in descriptor to %q: %w", dest, err)
	}
	defer func() {
		if !succeeded {
			if err := mount.Unmount(dest); err != nil {
				logrus.Debugf("unmounting bound target %q: %v", dest, err)
			}
		}
	}()

	var st2 unix.Stat_t
	if err = unix.Stat(dest, &st2); err != nil {
		return "", fmt.Errorf("looking up device/inode of newly-bind-mounted %q: %w", dest, err)
	}

	if st2.Dev != st.Dev || st2.Ino != st.Ino {
		return "", fmt.Errorf("device/inode weren't what we expected after bind mounting: %w", err)
	}

	succeeded = true
	return dest, nil
}