Skip to content

Commit e5a49e6

Browse files
author
Kazuyoshi Kato
authored
Merge pull request #8789 from slonopotamus/macos-bind-mount
Add support for bind-mounts on Darwin (a.k.a. "make native snapshotter work")
2 parents f86d585 + 6c9c711 commit e5a49e6

File tree

12 files changed

+201
-219
lines changed

12 files changed

+201
-219
lines changed

diff/apply/apply_darwin.go

Lines changed: 0 additions & 47 deletions
This file was deleted.

diff/apply/apply_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build !linux && !darwin
1+
//go:build !linux
22

33
/*
44
Copyright The containerd Authors.

mount/fuse_linux.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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/exec"
21+
22+
"golang.org/x/sys/unix"
23+
)
24+
25+
// fuseSuperMagic is defined in statfs(2)
26+
const fuseSuperMagic = 0x65735546
27+
28+
func isFUSE(dir string) bool {
29+
var st unix.Statfs_t
30+
if err := unix.Statfs(dir, &st); err != nil {
31+
return false
32+
}
33+
return st.Type == fuseSuperMagic
34+
}
35+
36+
// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary.
37+
//
38+
// For FUSE mounts, using these helper binaries is preferred, see:
39+
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
40+
func unmountFUSE(target string) error {
41+
var err error
42+
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
43+
cmd := exec.Command(helperBinary, "-u", target)
44+
err = cmd.Run()
45+
if err == nil {
46+
return nil
47+
}
48+
}
49+
return err
50+
}

mount/fuse_unsupported.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//go:build !linux && !windows
2+
3+
/*
4+
Copyright The containerd Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package mount
20+
21+
import "fmt"
22+
23+
func isFUSE(dir string) bool {
24+
return false
25+
}
26+
27+
// unmountFUSE is not implemented on this platform
28+
func unmountFUSE(target string) error {
29+
return fmt.Errorf("FUSE is not supported on this platform")
30+
}

mount/mount_darwin.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
24+
// Mount to the provided target.
25+
func (m *Mount) mount(target string) error {
26+
// See https://github.com/slonopotamus/containerd-darwin-mount-helper for reference implementation
27+
const commandName = "containerd-darwin-mount-helper"
28+
29+
args := []string{"-t", m.Type}
30+
for _, option := range m.Options {
31+
args = append(args, "-o", option)
32+
}
33+
args = append(args, m.Source, target)
34+
35+
cmd := exec.Command(commandName, args...)
36+
output, err := cmd.CombinedOutput()
37+
if err != nil {
38+
return fmt.Errorf("%s [%v] failed: %q: %w", commandName, args, string(output), err)
39+
}
40+
41+
return nil
42+
}

mount/mount_freebsd.go

Lines changed: 1 addition & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,11 @@ package mount
1919
import (
2020
"errors"
2121
"fmt"
22-
"os"
23-
"time"
22+
"os/exec"
2423

25-
exec "golang.org/x/sys/execabs"
2624
"golang.org/x/sys/unix"
2725
)
2826

29-
var (
30-
// ErrNotImplementOnUnix is returned for methods that are not implemented
31-
ErrNotImplementOnUnix = errors.New("not implemented under unix")
32-
)
33-
3427
// Mount to the provided target.
3528
//
3629
// The "syscall" and "golang.org/x/sys/unix" packages do not define a Mount
@@ -77,57 +70,3 @@ func (m *Mount) mount(target string) error {
7770
}
7871
return fmt.Errorf("mount [%v] failed with ECHILD (retried %d times)", args, retriesOnECHILD)
7972
}
80-
81-
// Unmount the provided mount path with the flags
82-
func Unmount(target string, flags int) error {
83-
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
84-
return err
85-
}
86-
return nil
87-
}
88-
89-
func unmount(target string, flags int) error {
90-
for i := 0; i < 50; i++ {
91-
if err := unix.Unmount(target, flags); err != nil {
92-
switch err {
93-
case unix.EBUSY:
94-
time.Sleep(50 * time.Millisecond)
95-
continue
96-
default:
97-
return err
98-
}
99-
}
100-
return nil
101-
}
102-
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
103-
}
104-
105-
// UnmountAll repeatedly unmounts the given mount point until there
106-
// are no mounts remaining (EINVAL is returned by mount), which is
107-
// useful for undoing a stack of mounts on the same mount point.
108-
// UnmountAll all is noop when the first argument is an empty string.
109-
// This is done when the containerd client did not specify any rootfs
110-
// mounts (e.g. because the rootfs is managed outside containerd)
111-
// UnmountAll is noop when the mount path does not exist.
112-
func UnmountAll(mount string, flags int) error {
113-
if mount == "" {
114-
return nil
115-
}
116-
if _, err := os.Stat(mount); os.IsNotExist(err) {
117-
return nil
118-
}
119-
120-
for {
121-
if err := unmount(mount, flags); err != nil {
122-
// EINVAL is returned if the target is not a
123-
// mount point, indicating that we are
124-
// done. It can also indicate a few other
125-
// things (such as invalid flags) which we
126-
// unfortunately end up squelching here too.
127-
if err == unix.EINVAL {
128-
return nil
129-
}
130-
return err
131-
}
132-
}
133-
}

mount/mount_linux.go

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"path"
2424
"runtime"
2525
"strings"
26-
"time"
2726

2827
exec "golang.org/x/sys/execabs"
2928
"golang.org/x/sys/unix"
@@ -120,92 +119,6 @@ func (m *Mount) mount(target string) (err error) {
120119
return nil
121120
}
122121

123-
// Unmount the provided mount path with the flags
124-
func Unmount(target string, flags int) error {
125-
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
126-
return err
127-
}
128-
return nil
129-
}
130-
131-
// fuseSuperMagic is defined in statfs(2)
132-
const fuseSuperMagic = 0x65735546
133-
134-
func isFUSE(dir string) bool {
135-
var st unix.Statfs_t
136-
if err := unix.Statfs(dir, &st); err != nil {
137-
return false
138-
}
139-
return st.Type == fuseSuperMagic
140-
}
141-
142-
// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary.
143-
//
144-
// For FUSE mounts, using these helper binaries is preferred, see:
145-
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
146-
func unmountFUSE(target string) error {
147-
var err error
148-
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
149-
cmd := exec.Command(helperBinary, "-u", target)
150-
err = cmd.Run()
151-
if err == nil {
152-
return nil
153-
}
154-
}
155-
return err
156-
}
157-
158-
func unmount(target string, flags int) error {
159-
if isFUSE(target) {
160-
if err := unmountFUSE(target); err == nil {
161-
return nil
162-
}
163-
}
164-
for i := 0; i < 50; i++ {
165-
if err := unix.Unmount(target, flags); err != nil {
166-
switch err {
167-
case unix.EBUSY:
168-
time.Sleep(50 * time.Millisecond)
169-
continue
170-
default:
171-
return err
172-
}
173-
}
174-
return nil
175-
}
176-
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
177-
}
178-
179-
// UnmountAll repeatedly unmounts the given mount point until there
180-
// are no mounts remaining (EINVAL is returned by mount), which is
181-
// useful for undoing a stack of mounts on the same mount point.
182-
// UnmountAll all is noop when the first argument is an empty string.
183-
// This is done when the containerd client did not specify any rootfs
184-
// mounts (e.g. because the rootfs is managed outside containerd)
185-
// UnmountAll is noop when the mount path does not exist.
186-
func UnmountAll(mount string, flags int) error {
187-
if mount == "" {
188-
return nil
189-
}
190-
if _, err := os.Stat(mount); os.IsNotExist(err) {
191-
return nil
192-
}
193-
194-
for {
195-
if err := unmount(mount, flags); err != nil {
196-
// EINVAL is returned if the target is not a
197-
// mount point, indicating that we are
198-
// done. It can also indicate a few other
199-
// things (such as invalid flags) which we
200-
// unfortunately end up squelching here too.
201-
if err == unix.EINVAL {
202-
return nil
203-
}
204-
return err
205-
}
206-
}
207-
}
208-
209122
// parseMountOptions takes fstab style mount options and parses them for
210123
// use with a standard mount() syscall
211124
func parseMountOptions(options []string) (int, []string, bool) {

0 commit comments

Comments
 (0)