X Tutup
Skip to content

Commit e739314

Browse files
committed
mount: support FUSE helper
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>
1 parent 537afb1 commit e739314

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

mount/mount_linux.go

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package mount
1919
import (
2020
"fmt"
2121
"os"
22+
"os/exec"
2223
"path"
2324
"strings"
2425
"time"
@@ -28,14 +29,27 @@ import (
2829
"golang.org/x/sys/unix"
2930
)
3031

31-
var pagesize = 4096
32+
var (
33+
pagesize = 4096
34+
allowedHelperBinaries = []string{"mount.fuse", "mount.fuse3"}
35+
)
3236

3337
func init() {
3438
pagesize = os.Getpagesize()
3539
}
3640

37-
// Mount to the provided target path
41+
// Mount to the provided target path.
42+
//
43+
// If m.Type starts with "fuse." or "fuse3.", "mount.fuse" or "mount.fuse3"
44+
// helper binary is called.
3845
func (m *Mount) Mount(target string) error {
46+
for _, helperBinary := range allowedHelperBinaries {
47+
// helperBinary = "mount.fuse", typePrefix = "fuse."
48+
typePrefix := strings.TrimPrefix(helperBinary, "mount.") + "."
49+
if strings.HasPrefix(m.Type, typePrefix) {
50+
return m.mountWithHelper(helperBinary, typePrefix, target)
51+
}
52+
}
3953
var (
4054
chdir string
4155
options = m.Options
@@ -92,7 +106,28 @@ func Unmount(target string, flags int) error {
92106
return nil
93107
}
94108

109+
func isFUSE(dir string) (bool, error) {
110+
// fuseSuperMagic is defined in statfs(2)
111+
const fuseSuperMagic = 0x65735546
112+
var st unix.Statfs_t
113+
if err := unix.Statfs(dir, &st); err != nil {
114+
return false, err
115+
}
116+
return st.Type == fuseSuperMagic, nil
117+
}
118+
95119
func unmount(target string, flags int) error {
120+
// For FUSE mounts, attempting to execute fusermount helper binary is preferred
121+
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
122+
if ok, err := isFUSE(target); err == nil && ok {
123+
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
124+
cmd := exec.Command(helperBinary, "-u", target)
125+
if err := cmd.Run(); err == nil {
126+
return nil
127+
}
128+
// ignore error and try unix.Unmount
129+
}
130+
}
96131
for i := 0; i < 50; i++ {
97132
if err := unix.Unmount(target, flags); err != nil {
98133
switch err {
@@ -317,3 +352,21 @@ func mountAt(chdir string, source, target, fstype string, flags uintptr, data st
317352
}
318353
return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat")
319354
}
355+
356+
func (m *Mount) mountWithHelper(helperBinary, typePrefix, target string) error {
357+
// helperBinary: "mount.fuse3"
358+
// target: "/foo/merged"
359+
// m.Type: "fuse3.fuse-overlayfs"
360+
// command: "mount.fuse3 overlay /foo/merged -o lowerdir=/foo/lower2:/foo/lower1,upperdir=/foo/upper,workdir=/foo/work -t fuse-overlayfs"
361+
args := []string{m.Source, target}
362+
for _, o := range m.Options {
363+
args = append(args, "-o", o)
364+
}
365+
args = append(args, "-t", strings.TrimPrefix(m.Type, typePrefix))
366+
cmd := exec.Command(helperBinary, args...)
367+
out, err := cmd.CombinedOutput()
368+
if err != nil {
369+
return errors.Wrapf(err, "mount helper [%s %v] failed: %q", helperBinary, args, string(out))
370+
}
371+
return nil
372+
}

mount/mount_linux_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@
1919
package mount
2020

2121
import (
22+
"fmt"
23+
"io/ioutil"
24+
"os"
25+
"os/exec"
26+
"path/filepath"
2227
"reflect"
2328
"testing"
29+
30+
"github.com/containerd/continuity/testutil"
2431
)
2532

2633
func TestLongestCommonPrefix(t *testing.T) {
@@ -92,3 +99,41 @@ func TestCompactLowerdirOption(t *testing.T) {
9299
}
93100
}
94101
}
102+
103+
func TestFUSEHelper(t *testing.T) {
104+
testutil.RequiresRoot(t)
105+
const fuseoverlayfsBinary = "fuse-overlayfs"
106+
_, err := exec.LookPath(fuseoverlayfsBinary)
107+
if err != nil {
108+
t.Skip("fuse-overlayfs not installed")
109+
}
110+
td, err := ioutil.TempDir("", "fuse")
111+
if err != nil {
112+
t.Fatal(err)
113+
}
114+
defer func() {
115+
if err := os.RemoveAll(td); err != nil {
116+
t.Fatal(err)
117+
}
118+
}()
119+
120+
for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} {
121+
if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
122+
t.Fatal(err)
123+
}
124+
}
125+
126+
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"))
127+
m := Mount{
128+
Type: "fuse3." + fuseoverlayfsBinary,
129+
Source: "overlay",
130+
Options: []string{opts},
131+
}
132+
dest := filepath.Join(td, "merged")
133+
if err := m.Mount(dest); err != nil {
134+
t.Fatal(err)
135+
}
136+
if err := UnmountAll(dest, 0); err != nil {
137+
t.Fatal(err)
138+
}
139+
}

0 commit comments

Comments
 (0)
X Tutup