Merge pull request #3765 from AkihiroSuda/mount-fuse
mount: support FUSE helper
This commit is contained in:
commit
b9fad5e310
@ -19,6 +19,7 @@ package mount
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
@ -28,14 +29,27 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var pagesize = 4096
|
||||
var (
|
||||
pagesize = 4096
|
||||
allowedHelperBinaries = []string{"mount.fuse", "mount.fuse3"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
pagesize = os.Getpagesize()
|
||||
}
|
||||
|
||||
// Mount to the provided target path
|
||||
// Mount to the provided target path.
|
||||
//
|
||||
// If m.Type starts with "fuse." or "fuse3.", "mount.fuse" or "mount.fuse3"
|
||||
// helper binary is called.
|
||||
func (m *Mount) Mount(target string) error {
|
||||
for _, helperBinary := range allowedHelperBinaries {
|
||||
// helperBinary = "mount.fuse", typePrefix = "fuse."
|
||||
typePrefix := strings.TrimPrefix(helperBinary, "mount.") + "."
|
||||
if strings.HasPrefix(m.Type, typePrefix) {
|
||||
return m.mountWithHelper(helperBinary, typePrefix, target)
|
||||
}
|
||||
}
|
||||
var (
|
||||
chdir string
|
||||
options = m.Options
|
||||
@ -92,7 +106,28 @@ func Unmount(target string, flags int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isFUSE(dir string) (bool, error) {
|
||||
// fuseSuperMagic is defined in statfs(2)
|
||||
const fuseSuperMagic = 0x65735546
|
||||
var st unix.Statfs_t
|
||||
if err := unix.Statfs(dir, &st); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return st.Type == fuseSuperMagic, nil
|
||||
}
|
||||
|
||||
func unmount(target string, flags int) error {
|
||||
// For FUSE mounts, attempting to execute fusermount helper binary is preferred
|
||||
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
|
||||
if ok, err := isFUSE(target); err == nil && ok {
|
||||
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
|
||||
cmd := exec.Command(helperBinary, "-u", target)
|
||||
if err := cmd.Run(); err == nil {
|
||||
return nil
|
||||
}
|
||||
// ignore error and try unix.Unmount
|
||||
}
|
||||
}
|
||||
for i := 0; i < 50; i++ {
|
||||
if err := unix.Unmount(target, flags); err != nil {
|
||||
switch err {
|
||||
@ -317,3 +352,21 @@ func mountAt(chdir string, source, target, fstype string, flags uintptr, data st
|
||||
}
|
||||
return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat")
|
||||
}
|
||||
|
||||
func (m *Mount) mountWithHelper(helperBinary, typePrefix, target string) error {
|
||||
// helperBinary: "mount.fuse3"
|
||||
// target: "/foo/merged"
|
||||
// m.Type: "fuse3.fuse-overlayfs"
|
||||
// command: "mount.fuse3 overlay /foo/merged -o lowerdir=/foo/lower2:/foo/lower1,upperdir=/foo/upper,workdir=/foo/work -t fuse-overlayfs"
|
||||
args := []string{m.Source, target}
|
||||
for _, o := range m.Options {
|
||||
args = append(args, "-o", o)
|
||||
}
|
||||
args = append(args, "-t", strings.TrimPrefix(m.Type, typePrefix))
|
||||
cmd := exec.Command(helperBinary, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "mount helper [%s %v] failed: %q", helperBinary, args, string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -19,8 +19,15 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/continuity/testutil"
|
||||
)
|
||||
|
||||
func TestLongestCommonPrefix(t *testing.T) {
|
||||
@ -92,3 +99,41 @@ func TestCompactLowerdirOption(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFUSEHelper(t *testing.T) {
|
||||
testutil.RequiresRoot(t)
|
||||
const fuseoverlayfsBinary = "fuse-overlayfs"
|
||||
_, err := exec.LookPath(fuseoverlayfsBinary)
|
||||
if err != nil {
|
||||
t.Skip("fuse-overlayfs not installed")
|
||||
}
|
||||
td, err := ioutil.TempDir("", "fuse")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(td); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
|
||||
if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work"))
|
||||
m := Mount{
|
||||
Type: "fuse3." + fuseoverlayfsBinary,
|
||||
Source: "overlay",
|
||||
Options: []string{opts},
|
||||
}
|
||||
dest := filepath.Join(td, "merged")
|
||||
if err := m.Mount(dest); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := UnmountAll(dest, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user