Revert "Add support for mounts on Darwin"
This reverts commit 2799b28e61.
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
			
			
This commit is contained in:
		| @@ -1,50 +0,0 @@ | |||||||
| /* |  | ||||||
|    Copyright The containerd Authors. |  | ||||||
|  |  | ||||||
|    Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|    you may not use this file except in compliance with the License. |  | ||||||
|    You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|        http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|    Unless required by applicable law or agreed to in writing, software |  | ||||||
|    distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|    See the License for the specific language governing permissions and |  | ||||||
|    limitations under the License. |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| package mount |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"os/exec" |  | ||||||
|  |  | ||||||
| 	"golang.org/x/sys/unix" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // fuseSuperMagic is defined in statfs(2) |  | ||||||
| const fuseSuperMagic = 0x65735546 |  | ||||||
|  |  | ||||||
| func isFUSE(dir string) bool { |  | ||||||
| 	var st unix.Statfs_t |  | ||||||
| 	if err := unix.Statfs(dir, &st); err != nil { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	return st.Type == fuseSuperMagic |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary. |  | ||||||
| // |  | ||||||
| // For FUSE mounts, using these helper binaries is preferred, see: |  | ||||||
| // https://github.com/containerd/containerd/pull/3765#discussion_r342083514 |  | ||||||
| func unmountFUSE(target string) error { |  | ||||||
| 	var err error |  | ||||||
| 	for _, helperBinary := range []string{"fusermount3", "fusermount"} { |  | ||||||
| 		cmd := exec.Command(helperBinary, "-u", target) |  | ||||||
| 		err = cmd.Run() |  | ||||||
| 		if err == nil { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| //go:build !linux && !windows |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|    Copyright The containerd Authors. |  | ||||||
|  |  | ||||||
|    Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|    you may not use this file except in compliance with the License. |  | ||||||
|    You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|        http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|    Unless required by applicable law or agreed to in writing, software |  | ||||||
|    distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|    See the License for the specific language governing permissions and |  | ||||||
|    limitations under the License. |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| package mount |  | ||||||
|  |  | ||||||
| import "fmt" |  | ||||||
|  |  | ||||||
| func isFUSE(dir string) bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // unmountFUSE is not implemented on this platform |  | ||||||
| func unmountFUSE(target string) error { |  | ||||||
| 	return fmt.Errorf("FUSE is not supported on this platform") |  | ||||||
| } |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| /* |  | ||||||
|    Copyright The containerd Authors. |  | ||||||
|  |  | ||||||
|    Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|    you may not use this file except in compliance with the License. |  | ||||||
|    You may obtain a copy of the License at |  | ||||||
|  |  | ||||||
|        http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  |  | ||||||
|    Unless required by applicable law or agreed to in writing, software |  | ||||||
|    distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|    See the License for the specific language governing permissions and |  | ||||||
|    limitations under the License. |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| package mount |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"os/exec" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Mount to the provided target. |  | ||||||
| func (m *Mount) mount(target string) error { |  | ||||||
| 	// See https://github.com/slonopotamus/containerd-darwin-mount-helper for reference implementation |  | ||||||
| 	const commandName = "containerd-darwin-mount-helper" |  | ||||||
|  |  | ||||||
| 	args := []string{"-t", m.Type} |  | ||||||
| 	for _, option := range m.Options { |  | ||||||
| 		args = append(args, "-o", option) |  | ||||||
| 	} |  | ||||||
| 	args = append(args, m.Source, target) |  | ||||||
|  |  | ||||||
| 	cmd := exec.Command(commandName, args...) |  | ||||||
| 	output, err := cmd.CombinedOutput() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("%s [%v] failed: %q: %w", commandName, args, string(output), err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| @@ -19,11 +19,18 @@ package mount | |||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os/exec" | 	"os" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	exec "golang.org/x/sys/execabs" | ||||||
| 	"golang.org/x/sys/unix" | 	"golang.org/x/sys/unix" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrNotImplementOnUnix is returned for methods that are not implemented | ||||||
|  | 	ErrNotImplementOnUnix = errors.New("not implemented under unix") | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Mount to the provided target. | // Mount to the provided target. | ||||||
| // | // | ||||||
| // The "syscall" and "golang.org/x/sys/unix" packages do not define a Mount | // The "syscall" and "golang.org/x/sys/unix" packages do not define a Mount | ||||||
| @@ -70,3 +77,57 @@ func (m *Mount) mount(target string) error { | |||||||
| 	} | 	} | ||||||
| 	return fmt.Errorf("mount [%v] failed with ECHILD (retried %d times)", args, retriesOnECHILD) | 	return fmt.Errorf("mount [%v] failed with ECHILD (retried %d times)", args, retriesOnECHILD) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Unmount the provided mount path with the flags | ||||||
|  | func Unmount(target string, flags int) error { | ||||||
|  | 	if err := unmount(target, flags); err != nil && err != unix.EINVAL { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func unmount(target string, flags int) error { | ||||||
|  | 	for i := 0; i < 50; i++ { | ||||||
|  | 		if err := unix.Unmount(target, flags); err != nil { | ||||||
|  | 			switch err { | ||||||
|  | 			case unix.EBUSY: | ||||||
|  | 				time.Sleep(50 * time.Millisecond) | ||||||
|  | 				continue | ||||||
|  | 			default: | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UnmountAll repeatedly unmounts the given mount point until there | ||||||
|  | // are no mounts remaining (EINVAL is returned by mount), which is | ||||||
|  | // useful for undoing a stack of mounts on the same mount point. | ||||||
|  | // UnmountAll all is noop when the first argument is an empty string. | ||||||
|  | // This is done when the containerd client did not specify any rootfs | ||||||
|  | // mounts (e.g. because the rootfs is managed outside containerd) | ||||||
|  | // UnmountAll is noop when the mount path does not exist. | ||||||
|  | func UnmountAll(mount string, flags int) error { | ||||||
|  | 	if mount == "" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if _, err := os.Stat(mount); os.IsNotExist(err) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if err := unmount(mount, flags); err != nil { | ||||||
|  | 			// EINVAL is returned if the target is not a | ||||||
|  | 			// mount point, indicating that we are | ||||||
|  | 			// done. It can also indicate a few other | ||||||
|  | 			// things (such as invalid flags) which we | ||||||
|  | 			// unfortunately end up squelching here too. | ||||||
|  | 			if err == unix.EINVAL { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	"path" | 	"path" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	exec "golang.org/x/sys/execabs" | 	exec "golang.org/x/sys/execabs" | ||||||
| 	"golang.org/x/sys/unix" | 	"golang.org/x/sys/unix" | ||||||
| @@ -119,6 +120,92 @@ func (m *Mount) mount(target string) (err error) { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Unmount the provided mount path with the flags | ||||||
|  | func Unmount(target string, flags int) error { | ||||||
|  | 	if err := unmount(target, flags); err != nil && err != unix.EINVAL { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // fuseSuperMagic is defined in statfs(2) | ||||||
|  | const fuseSuperMagic = 0x65735546 | ||||||
|  |  | ||||||
|  | func isFUSE(dir string) bool { | ||||||
|  | 	var st unix.Statfs_t | ||||||
|  | 	if err := unix.Statfs(dir, &st); err != nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return st.Type == fuseSuperMagic | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary. | ||||||
|  | // | ||||||
|  | // For FUSE mounts, using these helper binaries is preferred, see: | ||||||
|  | // https://github.com/containerd/containerd/pull/3765#discussion_r342083514 | ||||||
|  | func unmountFUSE(target string) error { | ||||||
|  | 	var err error | ||||||
|  | 	for _, helperBinary := range []string{"fusermount3", "fusermount"} { | ||||||
|  | 		cmd := exec.Command(helperBinary, "-u", target) | ||||||
|  | 		err = cmd.Run() | ||||||
|  | 		if err == nil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func unmount(target string, flags int) error { | ||||||
|  | 	if isFUSE(target) { | ||||||
|  | 		if err := unmountFUSE(target); err == nil { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < 50; i++ { | ||||||
|  | 		if err := unix.Unmount(target, flags); err != nil { | ||||||
|  | 			switch err { | ||||||
|  | 			case unix.EBUSY: | ||||||
|  | 				time.Sleep(50 * time.Millisecond) | ||||||
|  | 				continue | ||||||
|  | 			default: | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UnmountAll repeatedly unmounts the given mount point until there | ||||||
|  | // are no mounts remaining (EINVAL is returned by mount), which is | ||||||
|  | // useful for undoing a stack of mounts on the same mount point. | ||||||
|  | // UnmountAll all is noop when the first argument is an empty string. | ||||||
|  | // This is done when the containerd client did not specify any rootfs | ||||||
|  | // mounts (e.g. because the rootfs is managed outside containerd) | ||||||
|  | // UnmountAll is noop when the mount path does not exist. | ||||||
|  | func UnmountAll(mount string, flags int) error { | ||||||
|  | 	if mount == "" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if _, err := os.Stat(mount); os.IsNotExist(err) { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for { | ||||||
|  | 		if err := unmount(mount, flags); err != nil { | ||||||
|  | 			// EINVAL is returned if the target is not a | ||||||
|  | 			// mount point, indicating that we are | ||||||
|  | 			// done. It can also indicate a few other | ||||||
|  | 			// things (such as invalid flags) which we | ||||||
|  | 			// unfortunately end up squelching here too. | ||||||
|  | 			if err == unix.EINVAL { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // parseMountOptions takes fstab style mount options and parses them for | // parseMountOptions takes fstab style mount options and parses them for | ||||||
| // use with a standard mount() syscall | // use with a standard mount() syscall | ||||||
| func parseMountOptions(options []string) (int, []string, bool) { | func parseMountOptions(options []string) (int, []string, bool) { | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| //go:build !windows && !openbsd | //go:build !windows && !darwin && !openbsd | ||||||
|  |  | ||||||
| /* | /* | ||||||
|    Copyright The containerd Authors. |    Copyright The containerd Authors. | ||||||
| @@ -19,13 +19,9 @@ | |||||||
| package mount | package mount | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/moby/sys/mountinfo" | 	"github.com/moby/sys/mountinfo" | ||||||
| 	"golang.org/x/sys/unix" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // UnmountRecursive unmounts the target and all mounts underneath, starting | // UnmountRecursive unmounts the target and all mounts underneath, starting | ||||||
| @@ -63,64 +59,3 @@ func UnmountRecursive(target string, flags int) error { | |||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func unmount(target string, flags int) error { |  | ||||||
| 	if isFUSE(target) { |  | ||||||
| 		// TODO: Why error is ignored? |  | ||||||
| 		// Shouldn't this just be "return unmountFUSE(target)"? |  | ||||||
| 		if err := unmountFUSE(target); err == nil { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	for i := 0; i < 50; i++ { |  | ||||||
| 		if err := unix.Unmount(target, flags); err != nil { |  | ||||||
| 			switch err { |  | ||||||
| 			case unix.EBUSY: |  | ||||||
| 				time.Sleep(50 * time.Millisecond) |  | ||||||
| 				continue |  | ||||||
| 			default: |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return fmt.Errorf("failed to unmount target %s: %w", target, unix.EBUSY) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Unmount the provided mount path with the flags |  | ||||||
| func Unmount(target string, flags int) error { |  | ||||||
| 	if err := unmount(target, flags); err != nil && err != unix.EINVAL { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UnmountAll repeatedly unmounts the given mount point until there |  | ||||||
| // are no mounts remaining (EINVAL is returned by mount), which is |  | ||||||
| // useful for undoing a stack of mounts on the same mount point. |  | ||||||
| // UnmountAll all is noop when the first argument is an empty string. |  | ||||||
| // This is done when the containerd client did not specify any rootfs |  | ||||||
| // mounts (e.g. because the rootfs is managed outside containerd) |  | ||||||
| // UnmountAll is noop when the mount path does not exist. |  | ||||||
| func UnmountAll(mount string, flags int) error { |  | ||||||
| 	if mount == "" { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if _, err := os.Stat(mount); os.IsNotExist(err) { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		if err := unmount(mount, flags); err != nil { |  | ||||||
| 			// EINVAL is returned if the target is not a |  | ||||||
| 			// mount point, indicating that we are |  | ||||||
| 			// done. It can also indicate a few other |  | ||||||
| 			// things (such as invalid flags) which we |  | ||||||
| 			// unfortunately end up squelching here too. |  | ||||||
| 			if err == unix.EINVAL { |  | ||||||
| 				return nil |  | ||||||
| 			} |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| //go:build openbsd | //go:build darwin || openbsd | ||||||
|  |  | ||||||
| /* | /* | ||||||
|    Copyright The containerd Authors. |    Copyright The containerd Authors. | ||||||
| @@ -18,26 +18,29 @@ | |||||||
|  |  | ||||||
| package mount | package mount | ||||||
|  |  | ||||||
| import ( | import "errors" | ||||||
| 	"github.com/containerd/containerd/errdefs" |  | ||||||
|  | var ( | ||||||
|  | 	// ErrNotImplementOnUnix is returned for methods that are not implemented | ||||||
|  | 	ErrNotImplementOnUnix = errors.New("not implemented under unix") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Mount is not implemented on this platform | // Mount is not implemented on this platform | ||||||
| func (m *Mount) mount(target string) error { | func (m *Mount) mount(target string) error { | ||||||
| 	return errdefs.ErrNotImplemented | 	return ErrNotImplementOnUnix | ||||||
| } | } | ||||||
|  |  | ||||||
| // Unmount is not implemented on this platform | // Unmount is not implemented on this platform | ||||||
| func Unmount(mount string, flags int) error { | func Unmount(mount string, flags int) error { | ||||||
| 	return errdefs.ErrNotImplemented | 	return ErrNotImplementOnUnix | ||||||
| } | } | ||||||
|  |  | ||||||
| // UnmountAll is not implemented on this platform | // UnmountAll is not implemented on this platform | ||||||
| func UnmountAll(mount string, flags int) error { | func UnmountAll(mount string, flags int) error { | ||||||
| 	return errdefs.ErrNotImplemented | 	return ErrNotImplementOnUnix | ||||||
| } | } | ||||||
|  |  | ||||||
| // UnmountRecursive is not implemented on this platform | // UnmountRecursive is not implemented on this platform | ||||||
| func UnmountRecursive(mount string, flags int) error { | func UnmountRecursive(mount string, flags int) error { | ||||||
| 	return errdefs.ErrNotImplemented | 	return ErrNotImplementOnUnix | ||||||
| } | } | ||||||
|   | |||||||
| @@ -33,6 +33,11 @@ import ( | |||||||
|  |  | ||||||
| const sourceStreamName = "containerd.io-source" | const sourceStreamName = "containerd.io-source" | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ErrNotImplementOnWindows is returned when an action is not implemented for windows | ||||||
|  | 	ErrNotImplementOnWindows = errors.New("not implemented under windows") | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Mount to the provided target. | // Mount to the provided target. | ||||||
| func (m *Mount) mount(target string) (retErr error) { | func (m *Mount) mount(target string) (retErr error) { | ||||||
| 	if m.Type != "windows-layer" { | 	if m.Type != "windows-layer" { | ||||||
|   | |||||||
| @@ -19,13 +19,12 @@ | |||||||
| package os | package os | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/containerd/containerd/errdefs" |  | ||||||
| 	"github.com/containerd/containerd/mount" | 	"github.com/containerd/containerd/mount" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Mount will call unix.Mount to mount the file. | // Mount will call unix.Mount to mount the file. | ||||||
| func (RealOS) Mount(source string, target string, fstype string, flags uintptr, data string) error { | func (RealOS) Mount(source string, target string, fstype string, flags uintptr, data string) error { | ||||||
| 	return errdefs.ErrNotImplemented | 	return mount.ErrNotImplementOnUnix | ||||||
| } | } | ||||||
|  |  | ||||||
| // Unmount will call Unmount to unmount the file. | // Unmount will call Unmount to unmount the file. | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | 	"runtime" | ||||||
|  |  | ||||||
| 	"github.com/containerd/containerd/identifiers" | 	"github.com/containerd/containerd/identifiers" | ||||||
| 	"github.com/containerd/containerd/mount" | 	"github.com/containerd/containerd/mount" | ||||||
| @@ -128,8 +129,10 @@ type Bundle struct { | |||||||
| func (b *Bundle) Delete() error { | func (b *Bundle) Delete() error { | ||||||
| 	work, werr := os.Readlink(filepath.Join(b.Path, "work")) | 	work, werr := os.Readlink(filepath.Join(b.Path, "work")) | ||||||
| 	rootfs := filepath.Join(b.Path, "rootfs") | 	rootfs := filepath.Join(b.Path, "rootfs") | ||||||
| 	if err := mount.UnmountRecursive(rootfs, 0); err != nil { | 	if runtime.GOOS != "darwin" { | ||||||
| 		return fmt.Errorf("unmount rootfs %s: %w", rootfs, err) | 		if err := mount.UnmountRecursive(rootfs, 0); err != nil { | ||||||
|  | 			return fmt.Errorf("unmount rootfs %s: %w", rootfs, err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	if err := os.Remove(rootfs); err != nil && !os.IsNotExist(err) { | 	if err := os.Remove(rootfs); err != nil && !os.IsNotExist(err) { | ||||||
| 		return fmt.Errorf("failed to remove bundle rootfs: %w", err) | 		return fmt.Errorf("failed to remove bundle rootfs: %w", err) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Akihiro Suda
					Akihiro Suda