Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkg/overlay: add limited support for FreeBSD #4888

Merged
merged 1 commit into from Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
69 changes: 0 additions & 69 deletions pkg/overlay/overlay.go
Expand Up @@ -6,7 +6,6 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"

"errors"

Expand Down Expand Up @@ -146,74 +145,6 @@ func mountWithMountProgram(mountProgram, overlayOptions, mergeDir string) error
return nil
}

// MountWithOptions creates a subdir of the contentDir based on the source directory
// from the source system. It then mounts up the source directory on to the
// generated mount point and returns the mount point to the caller.
// But allows api to set custom workdir, upperdir and other overlay options
// Following API is being used by podman at the moment
func MountWithOptions(contentDir, source, dest string, opts *Options) (mount specs.Mount, Err error) {
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 layer.
lowerTwo := filepath.Join(contentDir, "lower")
if err := os.Mkdir(lowerTwo, 0755); 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
upperDir = opts.UpperDirOptionFragment
}

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

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, ",")

return mount, nil
}

// Convert ":" to "\:", the path which will be overlay mounted need to be escaped
func escapeColon(source string) string {
return strings.ReplaceAll(source, ":", "\\:")
Expand Down
31 changes: 31 additions & 0 deletions pkg/overlay/overlay_freebsd.go
@@ -0,0 +1,31 @@
package overlay

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

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

// MountWithOptions creates a subdir of the contentDir based on the source directory
// from the source system. It then mounts up the source directory on to the
// generated mount point and returns the mount point to the caller.
// But allows api to set custom workdir, upperdir and other overlay options
// Following API is being used by podman at the moment
func MountWithOptions(contentDir, source, dest string, opts *Options) (mount specs.Mount, Err error) {
if opts.ReadOnly {
// Read-only overlay mounts can be simulated with nullfs
mount.Source = source
mount.Destination = dest
mount.Type = "nullfs"
mount.Options = []string{"ro"}
return mount, nil
} else {
return mount, errors.New("read/write overlay mounts not supported on freebsd")
}
}
80 changes: 80 additions & 0 deletions pkg/overlay/overlay_linux.go
@@ -0,0 +1,80 @@
package overlay

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

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

// MountWithOptions creates a subdir of the contentDir based on the source directory
// from the source system. It then mounts up the source directory on to the
// generated mount point and returns the mount point to the caller.
// But allows api to set custom workdir, upperdir and other overlay options
// Following API is being used by podman at the moment
func MountWithOptions(contentDir, source, dest string, opts *Options) (mount specs.Mount, Err error) {
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 layer.
lowerTwo := filepath.Join(contentDir, "lower")
if err := os.Mkdir(lowerTwo, 0755); 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
upperDir = opts.UpperDirOptionFragment
}

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

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, ",")

return mount, nil
}
36 changes: 35 additions & 1 deletion run_freebsd.go
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal"
"github.com/containers/buildah/pkg/jail"
"github.com/containers/buildah/pkg/overlay"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/buildah/util"
"github.com/containers/common/libnetwork/resolvconf"
Expand Down Expand Up @@ -322,13 +323,22 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
}

parseMount := func(mountType, host, container string, options []string) (specs.Mount, error) {
var foundrw, foundro bool
var foundrw, foundro, foundO bool
var upperDir string
for _, opt := range options {
switch opt {
case "rw":
foundrw = true
case "ro":
foundro = true
case "O":
foundO = true
}
if strings.HasPrefix(opt, "upperdir") {
splitOpt := strings.SplitN(opt, "=", 2)
if len(splitOpt) > 1 {
upperDir = splitOpt[1]
}
}
}
if !foundrw && !foundro {
Expand All @@ -337,6 +347,30 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
if mountType == "bind" || mountType == "rbind" {
mountType = "nullfs"
}
if foundO {
containerDir, err := b.store.ContainerDirectory(b.ContainerID)
if err != nil {
return specs.Mount{}, err
}

contentDir, err := overlay.TempDir(containerDir, idMaps.rootUID, idMaps.rootGID)
if err != nil {
return specs.Mount{}, fmt.Errorf("failed to create TempDir in the %s directory: %w", containerDir, err)
}

overlayOpts := overlay.Options{
RootUID: idMaps.rootUID,
RootGID: idMaps.rootGID,
UpperDirOptionFragment: upperDir,
GraphOpts: b.store.GraphOptions(),
}

overlayMount, err := overlay.MountWithOptions(contentDir, host, container, &overlayOpts)
if err == nil {
b.TempVolumes[contentDir] = true
}
return overlayMount, err
}
return specs.Mount{
Destination: container,
Type: mountType,
Expand Down