Skip to content

Commit cae19b1

Browse files
authored
Merge pull request containerd#10658 from darwin-containers/reorganize-mount-unmount
Reorganize mount/unmount code so it is easier to add Darwin-specific implementation
2 parents 58aa92d + bfc1465 commit cae19b1

File tree

13 files changed

+200
-221
lines changed

13 files changed

+200
-221
lines changed

core/diff/apply/apply_darwin.go

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

core/diff/apply/apply_other.go

Lines changed: 17 additions & 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.
@@ -21,13 +21,29 @@ package apply
2121
import (
2222
"context"
2323
"io"
24+
"os"
2425

2526
"github.com/containerd/containerd/v2/core/mount"
2627
"github.com/containerd/containerd/v2/pkg/archive"
2728
)
2829

2930
func apply(ctx context.Context, mounts []mount.Mount, r io.Reader, _sync bool) error {
3031
// TODO: for windows, how to sync?
32+
33+
if !mount.HasBindMounts && len(mounts) == 1 && mounts[0].Type == "bind" {
34+
opts := []archive.ApplyOpt{}
35+
36+
if os.Getuid() != 0 {
37+
opts = append(opts, archive.WithNoSameOwner())
38+
}
39+
40+
path := mounts[0].Source
41+
_, err := archive.Apply(ctx, path, r, opts...)
42+
return err
43+
44+
// TODO: Do we need to sync all the filesystems?
45+
}
46+
3147
return mount.WithTempMount(ctx, mounts, func(root string) error {
3248
_, err := archive.Apply(ctx, root, r)
3349
return err

core/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+
}

core/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+
}

core/mount/mount.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,16 @@ package mount
1919
import (
2020
"fmt"
2121
"path/filepath"
22+
"runtime"
2223
"strings"
2324

2425
"github.com/containerd/containerd/api/types"
2526
"github.com/containerd/continuity/fs"
2627
)
2728

29+
// HasBindMounts This is a flag to conditionally disable code that relies on working bind-mount support, so such code is easier to find across codebase.
30+
const HasBindMounts = runtime.GOOS != "darwin" && runtime.GOOS != "openbsd"
31+
2832
// Mount is the lingua franca of containerd. A mount represents a
2933
// serialized mount syscall. Components either emit or consume mounts.
3034
type Mount struct {

core/mount/mount_darwin.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 "github.com/containerd/errdefs"
20+
21+
// Mount to the provided target.
22+
func (m *Mount) mount(target string) error {
23+
return errdefs.ErrNotImplemented
24+
}

core/mount/mount_freebsd.go

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,11 @@ package mount
1919
import (
2020
"errors"
2121
"fmt"
22-
"os"
2322
"os/exec"
24-
"time"
2523

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-
}

core/mount/mount_linux.go

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"runtime"
2727
"strconv"
2828
"strings"
29-
"time"
3029

3130
"github.com/containerd/log"
3231
"github.com/moby/sys/userns"
@@ -274,92 +273,6 @@ func doPrepareIDMappedOverlay(lowerDirs []string, usernsFd int) (tmpLowerDirs []
274273
return tmpLowerDirs, cleanUp, nil
275274
}
276275

277-
// Unmount the provided mount path with the flags
278-
func Unmount(target string, flags int) error {
279-
if err := unmount(target, flags); err != nil && err != unix.EINVAL {
280-
return err
281-
}
282-
return nil
283-
}
284-
285-
// fuseSuperMagic is defined in statfs(2)
286-
const fuseSuperMagic = 0x65735546
287-
288-
func isFUSE(dir string) bool {
289-
var st unix.Statfs_t
290-
if err := unix.Statfs(dir, &st); err != nil {
291-
return false
292-
}
293-
return st.Type == fuseSuperMagic
294-
}
295-
296-
// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary.
297-
//
298-
// For FUSE mounts, using these helper binaries is preferred, see:
299-
// https://github.com/containerd/containerd/pull/3765#discussion_r342083514
300-
func unmountFUSE(target string) error {
301-
var err error
302-
for _, helperBinary := range []string{"fusermount3", "fusermount"} {
303-
cmd := exec.Command(helperBinary, "-u", target)
304-
err = cmd.Run()
305-
if err == nil {
306-
return nil
307-
}
308-
}
309-
return err
310-
}
311-
312-
func unmount(target string, flags int) error {
313-
if isFUSE(target) {
314-
if err := unmountFUSE(target); err == nil {
315-
return nil
316-
}
317-
}
318-
for i := 0; i < 50; i++ {
319-
if err := unix.Unmount(target, flags); err != nil {
320-
switch err {
321-
case unix.EBUSY:
322-
time.Sleep(50 * time.Millisecond)
323-
continue
324-
default:
325-
return err
326-
}
327-
}
328-
return nil
329-
}
330-
return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY)
331-
}
332-
333-
// UnmountAll repeatedly unmounts the given mount point until there
334-
// are no mounts remaining (EINVAL is returned by mount), which is
335-
// useful for undoing a stack of mounts on the same mount point.
336-
// UnmountAll all is noop when the first argument is an empty string.
337-
// This is done when the containerd client did not specify any rootfs
338-
// mounts (e.g. because the rootfs is managed outside containerd)
339-
// UnmountAll is noop when the mount path does not exist.
340-
func UnmountAll(mount string, flags int) error {
341-
if mount == "" {
342-
return nil
343-
}
344-
if _, err := os.Stat(mount); os.IsNotExist(err) {
345-
return nil
346-
}
347-
348-
for {
349-
if err := unmount(mount, flags); err != nil {
350-
// EINVAL is returned if the target is not a
351-
// mount point, indicating that we are
352-
// done. It can also indicate a few other
353-
// things (such as invalid flags) which we
354-
// unfortunately end up squelching here too.
355-
if err == unix.EINVAL {
356-
return nil
357-
}
358-
return err
359-
}
360-
}
361-
}
362-
363276
// parseMountOptions takes fstab style mount options and parses them for
364277
// use with a standard mount() syscall
365278
func parseMountOptions(options []string) (opt mountOpt) {

0 commit comments

Comments
 (0)