Skip to content

Commit

Permalink
mount: support FUSE helper
Browse files Browse the repository at this point in the history
When m.Type starts with either `fuse.` or `fuse3`, the
mount helper binary `mount.fuse` or `mount.fuse3` is executed.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Dec 31, 2019
1 parent 537afb1 commit e739314
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 2 deletions.
57 changes: 55 additions & 2 deletions mount/mount_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package mount
import (
"fmt"
"os"
"os/exec"
"path"
"strings"
"time"
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
45 changes: 45 additions & 0 deletions mount/mount_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}

0 comments on commit e739314

Please sign in to comment.