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
}
|