Skip to content

Commit 4c61e12

Browse files
committed
mount: support mount call for darwin with new darwin snapshotter
Signed-off-by: Hajime Tazaki <[email protected]>
1 parent 45e0e5a commit 4c61e12

File tree

5 files changed

+340
-2
lines changed

5 files changed

+340
-2
lines changed

mount/hdiutil_darwin.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mount
18+
19+
import (
20+
"os"
21+
"os/exec"
22+
"strconv"
23+
24+
"github.com/containerd/containerd/log"
25+
"github.com/pkg/errors"
26+
)
27+
28+
func CreateSparseBundle(image string, fileSizeMB int) error {
29+
// create a disk image file from a snapshot
30+
args := []string{
31+
"create",
32+
"-size", strconv.Itoa(fileSizeMB) + "m",
33+
image,
34+
"-ov",
35+
"-type", "SPARSEBUNDLE",
36+
"-fs", "HFS+",
37+
}
38+
39+
if err := hdiutilCommand(args...); err != nil {
40+
return err
41+
}
42+
43+
return nil
44+
}
45+
46+
func AttachSparseBundle(target, source string, options []string) error {
47+
args := []string{
48+
"attach",
49+
source,
50+
"-mountpoint", target,
51+
"-nobrowse",
52+
"-owners", "on",
53+
"-noverify",
54+
"-noautofsck",
55+
}
56+
args = append(args, options...)
57+
58+
err := hdiutilCommand(args...)
59+
if err != nil {
60+
return err
61+
}
62+
63+
return nil
64+
}
65+
66+
func DetachSparseBundle(target string, flags int) error {
67+
if _, err := os.Stat(target); err != nil {
68+
return nil
69+
}
70+
71+
return hdiutilCommand("detach", target)
72+
}
73+
74+
func hdiutilCommand(args ...string) error {
75+
log.L.Debugf("hdiutil %s", args)
76+
cmd := exec.Command("hdiutil", args...)
77+
78+
out, err := cmd.CombinedOutput()
79+
if err != nil {
80+
return errors.Errorf("%v: %s", err, out)
81+
}
82+
83+
return nil
84+
}

mount/hdiutil_darwin_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mount
18+
19+
import (
20+
"io/ioutil"
21+
"os"
22+
"path/filepath"
23+
"testing"
24+
25+
"github.com/containerd/continuity/testutil"
26+
)
27+
28+
var randomData = []byte("randomdata")
29+
30+
func createTempDir(t *testing.T) string {
31+
t.Helper()
32+
33+
dir, err := ioutil.TempDir("", "test-image")
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
return dir
39+
}
40+
41+
func testSparseBundle(t *testing.T, option string, testerFn func(file string)) {
42+
tmpDir := createTempDir(t)
43+
defer os.RemoveAll(tmpDir)
44+
45+
mountPoint := filepath.Join(tmpDir, "mount"+option)
46+
sparseBundle := filepath.Join(tmpDir, "test"+option+".sparsebundle")
47+
err := CreateSparseBundle(sparseBundle, 64)
48+
if err != nil {
49+
t.Fatal(err)
50+
}
51+
52+
defer func() {
53+
if err := os.RemoveAll(sparseBundle); err != nil {
54+
t.Fatal(err)
55+
}
56+
}()
57+
58+
err = AttachSparseBundle(mountPoint, sparseBundle, []string{option})
59+
if err != nil {
60+
t.Fatal(err)
61+
}
62+
63+
testfile := filepath.Join(mountPoint, "testfile")
64+
testerFn(testfile)
65+
66+
err = DetachSparseBundle(mountPoint, 0)
67+
if err != nil {
68+
t.Fatal(err)
69+
}
70+
71+
}
72+
73+
func TestSparseRO(t *testing.T) {
74+
testerFn := func(testfile string) {
75+
if err := ioutil.WriteFile(testfile, randomData, 0777); err != nil {
76+
t.Logf("write %q failed with %v (EROFS is expected)", testfile, err)
77+
} else {
78+
t.Fatalf("write %q should fail (EROFS) but did not fail", testfile)
79+
}
80+
}
81+
testSparseBundle(t, "-readonly", testerFn)
82+
}
83+
84+
func TestSparseRW(t *testing.T) {
85+
testutil.RequiresRoot(t)
86+
testerFn := func(testfile string) {
87+
if err := ioutil.WriteFile(testfile, randomData, 0777); err != nil {
88+
t.Fatalf("write to %q failed", testfile)
89+
}
90+
}
91+
testSparseBundle(t, "-readwrite", testerFn)
92+
}

mount/mount_darwin.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mount
18+
19+
import (
20+
"fmt"
21+
"os/exec"
22+
23+
"github.com/containerd/containerd/errdefs"
24+
"github.com/containerd/containerd/log"
25+
"github.com/pkg/errors"
26+
)
27+
28+
var (
29+
// ErrNotImplementOnUnix is returned for methods that are not implemented
30+
ErrNotImplementOnUnix = errors.New("not implemented under darwin")
31+
// allowedHelperBinaries is a list of third-party executable, which can
32+
// be extended in future uses, e.g., mount_nfs, mount_9p. `mount_darwin`
33+
// is a special case where we will use the internal darwin snapshotter.
34+
allowedHelperBinaries = []string{"mount_darwin"}
35+
)
36+
37+
// Mount to the provided target path.
38+
//
39+
func (m *Mount) Mount(target string) error {
40+
var allowed = false
41+
log.L.Debugf("mount: %s, src %s, type %s", target, m.Source, m.Type)
42+
43+
binary := fmt.Sprintf("mount_%s", m.Type)
44+
if m.Type != "darwin" {
45+
if _, err := exec.LookPath(binary); err != nil {
46+
return errors.Wrapf(errdefs.ErrNotFound, "mount binary not found: '%s(%s)'", m.Type, binary)
47+
}
48+
}
49+
50+
for _, helperBinary := range allowedHelperBinaries {
51+
if binary == helperBinary {
52+
allowed = true
53+
}
54+
}
55+
if !allowed {
56+
return errors.Wrapf(ErrNotImplementOnUnix, "mount binary isn't allowed to use: '%s(%s)'", m.Type, binary)
57+
}
58+
59+
switch m.Type {
60+
case "darwin":
61+
return AttachSparseBundle(target, m.Source, m.Options)
62+
default:
63+
// if allowedHelperBinaries is extended, handle it here
64+
}
65+
66+
return nil
67+
}
68+
69+
// Unmount the provided mount path with the flags
70+
func Unmount(target string, flags int) error {
71+
return unmount(target, flags)
72+
}
73+
74+
// UnmountAll the provided mount path with the flags
75+
func UnmountAll(target string, flags int) error {
76+
return unmount(target, flags)
77+
}
78+
79+
func unmount(target string, flags int) error {
80+
log.L.Debugf("unmount: %s", target)
81+
if err := DetachSparseBundle(target, flags); err != nil {
82+
log.L.Infof("detach failed (ignored): %s", target)
83+
}
84+
return nil
85+
}

mount/mount_darwin_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mount
18+
19+
import (
20+
"testing"
21+
22+
"github.com/containerd/containerd/errdefs"
23+
"github.com/pkg/errors"
24+
)
25+
26+
func TestMountD(t *testing.T) {
27+
testcases := []struct {
28+
mounts Mount
29+
err error
30+
msg string
31+
}{
32+
{
33+
mounts: Mount{
34+
Type: "nfs",
35+
Source: "source",
36+
},
37+
err: ErrNotImplementOnUnix,
38+
msg: "should fail with non-allowed type",
39+
},
40+
{
41+
mounts: Mount{
42+
Type: "blah",
43+
Source: "source",
44+
},
45+
err: errdefs.ErrNotFound,
46+
msg: "should fail with non-existent binary",
47+
},
48+
{
49+
mounts: Mount{
50+
Type: "darwin",
51+
Source: "source",
52+
},
53+
err: nil,
54+
msg: "should fail with non-existent target",
55+
},
56+
{
57+
mounts: Mount{
58+
Type: "9p",
59+
Source: "source",
60+
},
61+
err: ErrNotImplementOnUnix,
62+
msg: "should fail with non-allowed type",
63+
},
64+
}
65+
66+
for _, tc := range testcases {
67+
err := tc.mounts.Mount("target")
68+
t.Log(err)
69+
if tc.mounts.Type == "darwin" {
70+
if err == nil {
71+
t.Fatalf("%s: %s", tc.msg, tc.mounts.Type)
72+
}
73+
} else if !errors.Is(err, tc.err) {
74+
t.Fatalf("%s: %s", tc.msg, tc.mounts.Type)
75+
}
76+
}
77+
}

mount/mount_unix.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//go:build darwin || openbsd
2-
// +build darwin openbsd
1+
//go:build openbsd
2+
// +build openbsd
33

44
/*
55
Copyright The containerd Authors.

0 commit comments

Comments
 (0)