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

Use kernel mount of squashfs if running privledged. #361

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions squashfs/squashfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"path"
"strings"
"sync"
"syscall"

"github.com/pkg/errors"
"golang.org/x/sys/unix"
Expand All @@ -20,6 +21,9 @@ import (
var checkZstdSupported sync.Once
var zstdIsSuspported bool

var tryKernelMountSquash bool = true
var kernelSquashMountFailed error = errors.New("kernel squash mount failed")

// ExcludePaths represents a list of paths to exclude in a squashfs listing.
// Users should do something like filepath.Walk() over the whole filesystem,
// calling AddExclude() or AddInclude() based on whether they want to include
Expand Down Expand Up @@ -162,12 +166,66 @@ func MakeSquashfs(tempdir string, rootfs string, eps *ExcludePaths, verity Verit
return blob, GenerateSquashfsMediaType(compression, verity), rootHash, nil
}

// maybeKernelSquashMount - try to mount squashfile with kernel mount
//
// if global tryKernelMountSquash is false, do not try
// if environment variable STACKER_ALLOW_SQUASHFS_KERNEL_MOUNTS is "false", do not try.
// try. If it fails, log message and set tryKernelMountSquash=false.
func maybeKernelSquashMount(squashFile, extractDir string) (bool, error) {
if !tryKernelMountSquash {
return false, nil
}

const strTrue, strFalse = "true", "false"
const envName = "STACKER_ALLOW_SQUASHFS_KERNEL_MOUNTS"
envVal := os.Getenv(envName)
if envVal == strFalse {
log.Debugf("Not trying kernel mounts per %s=%s", envName, envVal)
tryKernelMountSquash = false
return false, nil
} else if envVal != strTrue && envVal != "" {
return false, errors.Errorf("%s must be '%s' or '%s', found '%s'", envName, strTrue, strFalse, envVal)
}

ecmd := []string{"mount", "-tsquashfs", "-oloop,ro", squashFile, extractDir}
var output bytes.Buffer
cmd := exec.Command(ecmd[0], ecmd[1:]...)
cmd.Stdin = nil
cmd.Stdout = &output
cmd.Stderr = cmd.Stdout
err := cmd.Run()
if err == nil {
return true, nil
}
exitError, ok := err.(*exec.ExitError)
if !ok {
tryKernelMountSquash = false
return false, errors.Errorf("Unexpected error (no-rc), in exec (%v): %v", ecmd, err)
}

status, ok := exitError.Sys().(syscall.WaitStatus)
if !ok {
tryKernelMountSquash = false
return false, errors.Errorf("Unexpected error (no-status) in exec (%v): %v", ecmd, err)
}

// we can't really tell why the mount failed. mount(8) does not give a lot specific rc exits.
log.Debugf("maybeKernelSquashMount(%s) exited %d: %s\n", squashFile, status.ExitStatus(), output.String())
return false, kernelSquashMountFailed
}

func ExtractSingleSquash(squashFile string, extractDir string, storageType string) error {
err := os.MkdirAll(extractDir, 0755)
if err != nil {
return err
}

if mounted, err := maybeKernelSquashMount(squashFile, extractDir); err == nil && mounted {
return nil
} else if err != kernelSquashMountFailed {
return err
}

findSqfusePath := func() string {
if p := which("squashfuse_ll"); p != "" {
return p
Expand Down