File: overlay_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 (109 lines) | stat: -rw-r--r-- 3,386 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
103
104
105
106
107
108
109
package overlay

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/containers/storage/pkg/unshare"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/opencontainers/selinux/go-selinux/label"
)

// MountWithOptions creates ${contentDir}/merge, where ${contentDir} was
// presumably created and returned by a call to TempDir(), and either mounts a
// filesystem there and returns a mounts.Spec which bind-mounts the mountpoint
// to ${dest}, or returns a mounts.Spec which mounts a filesystem at ${dest}.
// Options allows the caller to configure a custom workdir and upperdir,
// indicate whether or not the overlay should be read-only, and provide the
// graph driver options that we'll search to determine whether or not we should
// be using a mount helper (i.e., fuse-overlayfs).
// This API is used by podman.
func MountWithOptions(contentDir, source, dest string, opts *Options) (mount specs.Mount, Err error) {
	if opts == nil {
		opts = &Options{}
	}
	mergeDir := filepath.Join(contentDir, "merge")

	// Create overlay mount options for rw/ro.
	var overlayOptions string
	if opts.ReadOnly {
		// Read-only overlay mounts require two lower layers.
		lowerTwo := filepath.Join(contentDir, "lower")
		if err := os.Mkdir(lowerTwo, 0o755); err != nil {
			return mount, err
		}
		overlayOptions = fmt.Sprintf("lowerdir=%s:%s,private", escapeColon(source), lowerTwo)
	} else {
		// Read-write overlay mounts want a lower, upper and a work layer.
		workDir := filepath.Join(contentDir, "work")
		upperDir := filepath.Join(contentDir, "upper")

		if opts.WorkDirOptionFragment != "" && opts.UpperDirOptionFragment != "" {
			workDir = opts.WorkDirOptionFragment
			if !filepath.IsAbs(workDir) {
				workDir = filepath.Join(contentDir, workDir)
			}
			upperDir = opts.UpperDirOptionFragment
			if !filepath.IsAbs(upperDir) {
				upperDir = filepath.Join(contentDir, upperDir)
			}
		}

		st, err := os.Stat(source)
		if err != nil {
			return mount, err
		}
		if err := os.Chmod(upperDir, st.Mode()); err != nil {
			return mount, err
		}
		if stat, ok := st.Sys().(*syscall.Stat_t); ok {
			if err := os.Chown(upperDir, int(stat.Uid), int(stat.Gid)); err != nil {
				return mount, err
			}
		}
		overlayOptions = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s,private", escapeColon(source), upperDir, workDir)
	}
	if opts.MountLabel != "" {
		overlayOptions = overlayOptions + "," + label.FormatMountLabel("", opts.MountLabel)
	}

	mountProgram := findMountProgram(opts.GraphOpts)
	if mountProgram != "" {
		if err := mountWithMountProgram(mountProgram, overlayOptions, mergeDir); err != nil {
			return mount, err
		}

		mount.Source = mergeDir
		mount.Destination = dest
		mount.Type = "bind"
		mount.Options = []string{"bind", "slave"}
		return mount, nil
	}

	if unshare.IsRootless() {
		// If a mount_program is not specified, fallback to try mounting native overlay.
		overlayOptions = fmt.Sprintf("%s,userxattr", overlayOptions)
	}

	mount.Source = mergeDir
	mount.Destination = dest
	mount.Type = "overlay"
	mount.Options = strings.Split(overlayOptions, ",")

	if opts.ForceMount {
		if err := mountNatively(overlayOptions, mergeDir); err != nil {
			return mount, err
		}

		mount.Source = mergeDir
		mount.Destination = dest
		mount.Type = "bind"
		mount.Options = []string{"bind", "slave"}
		return mount, nil
	}

	return mount, nil
}