| 
									
										
										
										
											2022-05-20 19:03:03 +08:00
										 |  |  | //go:build linux
 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package bind | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2025-01-23 22:27:47 +08:00
										 |  |  | 	"slices" | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	"syscall" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-18 03:20:16 +08:00
										 |  |  | 	"github.com/containers/buildah/util" | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	"github.com/opencontainers/runtime-spec/specs-go" | 
					
						
							|  |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2025-08-29 20:55:12 +08:00
										 |  |  | 	"go.podman.io/storage/pkg/idtools" | 
					
						
							|  |  |  | 	"go.podman.io/storage/pkg/mount" | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	"golang.org/x/sys/unix" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetupIntermediateMountNamespace creates a new mount namespace and bind
 | 
					
						
							|  |  |  | // mounts all bind-mount sources into a subdirectory of bundlePath that can
 | 
					
						
							|  |  |  | // only be reached by the root user of the container's user namespace, except
 | 
					
						
							|  |  |  | // for Mounts which include the NoBindOption option in their options list.  The
 | 
					
						
							|  |  |  | // NoBindOption will then merely be removed.
 | 
					
						
							|  |  |  | func SetupIntermediateMountNamespace(spec *specs.Spec, bundlePath string) (unmountAll func() error, err error) { | 
					
						
							|  |  |  | 	defer stripNoBindOption(spec) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// We expect a root directory to be defined.
 | 
					
						
							|  |  |  | 	if spec.Root == nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return nil, errors.New("configuration has no root filesystem?") | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	rootPath := spec.Root.Path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a new mount namespace in which to do the things we're doing.
 | 
					
						
							|  |  |  | 	if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("creating new mount namespace for %v: %w", spec.Process.Args, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make all of our mounts private to our namespace.
 | 
					
						
							|  |  |  | 	if err := mount.MakeRPrivate("/"); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("making mounts private to mount namespace for %v: %w", spec.Process.Args, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make sure the bundle directory is searchable.  We created it with
 | 
					
						
							|  |  |  | 	// TempDir(), so it should have started with permissions set to 0700.
 | 
					
						
							|  |  |  | 	info, err := os.Stat(bundlePath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("checking permissions on %q: %w", bundlePath, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-08-16 00:50:07 +08:00
										 |  |  | 	if err = os.Chmod(bundlePath, info.Mode()|0o111); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("loosening permissions on %q: %w", bundlePath, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Figure out who needs to be able to reach these bind mounts in order
 | 
					
						
							|  |  |  | 	// for the container to be started.
 | 
					
						
							|  |  |  | 	rootUID, rootGID, err := util.GetHostRootIDs(spec) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Hand back a callback that the caller can use to clean up everything
 | 
					
						
							|  |  |  | 	// we're doing here.
 | 
					
						
							|  |  |  | 	unmount := []string{} | 
					
						
							|  |  |  | 	unmountAll = func() (err error) { | 
					
						
							|  |  |  | 		for _, mountpoint := range unmount { | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			// Unmount it and anything under it.
 | 
					
						
							|  |  |  | 			if err2 := UnmountMountpoints(mountpoint, nil); err2 != nil { | 
					
						
							|  |  |  | 				logrus.Warnf("pkg/bind: error unmounting %q: %v", mountpoint, err2) | 
					
						
							|  |  |  | 				if err == nil { | 
					
						
							|  |  |  | 					err = err2 | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			if err2 := unix.Unmount(mountpoint, unix.MNT_DETACH); err2 != nil { | 
					
						
							|  |  |  | 				if errno, ok := err2.(syscall.Errno); !ok || errno != syscall.EINVAL { | 
					
						
							|  |  |  | 					logrus.Warnf("pkg/bind: error detaching %q: %v", mountpoint, err2) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						err = err2 | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			// Remove just the mountpoint.
 | 
					
						
							|  |  |  | 			retry := 10 | 
					
						
							|  |  |  | 			remove := unix.Unlink | 
					
						
							|  |  |  | 			err2 := remove(mountpoint) | 
					
						
							|  |  |  | 			for err2 != nil && retry > 0 { | 
					
						
							|  |  |  | 				if errno, ok := err2.(syscall.Errno); ok { | 
					
						
							|  |  |  | 					switch errno { | 
					
						
							|  |  |  | 					default: | 
					
						
							|  |  |  | 						retry = 0 | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					case syscall.EISDIR: | 
					
						
							|  |  |  | 						remove = unix.Rmdir | 
					
						
							|  |  |  | 						err2 = remove(mountpoint) | 
					
						
							|  |  |  | 					case syscall.EBUSY: | 
					
						
							|  |  |  | 						if err3 := unix.Unmount(mountpoint, unix.MNT_DETACH); err3 == nil { | 
					
						
							|  |  |  | 							err2 = remove(mountpoint) | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 					retry-- | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			if err2 != nil { | 
					
						
							|  |  |  | 				logrus.Warnf("pkg/bind: error removing %q: %v", mountpoint, err2) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 				if err == nil { | 
					
						
							|  |  |  | 					err = err2 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a top-level directory that the "root" user will be able to
 | 
					
						
							|  |  |  | 	// access, that "root" from containers which use different mappings, or
 | 
					
						
							|  |  |  | 	// other unprivileged users outside of containers, shouldn't be able to
 | 
					
						
							|  |  |  | 	// access.
 | 
					
						
							|  |  |  | 	mnt := filepath.Join(bundlePath, "mnt") | 
					
						
							| 
									
										
										
										
											2024-08-16 00:50:07 +08:00
										 |  |  | 	if err = idtools.MkdirAndChown(mnt, 0o100, idtools.IDPair{UID: int(rootUID), GID: int(rootGID)}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return unmountAll, fmt.Errorf("creating %q owned by the container's root user: %w", mnt, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Make that directory private, and add it to the list of locations we
 | 
					
						
							|  |  |  | 	// unmount at cleanup time.
 | 
					
						
							|  |  |  | 	if err = mount.MakeRPrivate(mnt); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return unmountAll, fmt.Errorf("marking filesystem at %q as private: %w", mnt, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	unmount = append([]string{mnt}, unmount...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a bind mount for the root filesystem and add it to the list.
 | 
					
						
							|  |  |  | 	rootfs := filepath.Join(mnt, "rootfs") | 
					
						
							| 
									
										
										
										
											2024-08-16 00:50:07 +08:00
										 |  |  | 	if err = os.Mkdir(rootfs, 0o000); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return unmountAll, fmt.Errorf("creating directory %q: %w", rootfs, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if err = unix.Mount(rootPath, rootfs, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return unmountAll, fmt.Errorf("bind mounting root filesystem from %q to %q: %w", rootPath, rootfs, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-30 23:36:52 +08:00
										 |  |  | 	logrus.Debugf("bind mounted %q to %q", rootPath, rootfs) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 	unmount = append([]string{rootfs}, unmount...) | 
					
						
							|  |  |  | 	spec.Root.Path = rootfs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Do the same for everything we're binding in.
 | 
					
						
							|  |  |  | 	mounts := make([]specs.Mount, 0, len(spec.Mounts)) | 
					
						
							|  |  |  | 	for i := range spec.Mounts { | 
					
						
							|  |  |  | 		// If we're not using an intermediate, leave it in the list.
 | 
					
						
							|  |  |  | 		if leaveBindMountAlone(spec.Mounts[i]) { | 
					
						
							|  |  |  | 			mounts = append(mounts, spec.Mounts[i]) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check if the source is a directory or something else.
 | 
					
						
							|  |  |  | 		info, err := os.Stat(spec.Mounts[i].Source) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-27 03:27:30 +08:00
										 |  |  | 			if errors.Is(err, os.ErrNotExist) { | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 				logrus.Warnf("couldn't find %q on host to bind mount into container", spec.Mounts[i].Source) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return unmountAll, fmt.Errorf("checking if %q is a directory: %w", spec.Mounts[i].Source, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		stage := filepath.Join(mnt, fmt.Sprintf("buildah-bind-target-%d", i)) | 
					
						
							|  |  |  | 		if info.IsDir() { | 
					
						
							|  |  |  | 			// If the source is a directory, make one to use as the
 | 
					
						
							|  |  |  | 			// mount target.
 | 
					
						
							| 
									
										
										
										
											2024-08-16 00:50:07 +08:00
										 |  |  | 			if err = os.Mkdir(stage, 0o000); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 				return unmountAll, fmt.Errorf("creating directory %q: %w", stage, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If the source is not a directory, create an empty
 | 
					
						
							|  |  |  | 			// file to use as the mount target.
 | 
					
						
							| 
									
										
										
										
											2024-08-16 00:50:07 +08:00
										 |  |  | 			file, err := os.OpenFile(stage, os.O_WRONLY|os.O_CREATE, 0o000) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 				return unmountAll, fmt.Errorf("creating file %q: %w", stage, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			file.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Bind mount the source from wherever it is to a place where
 | 
					
						
							|  |  |  | 		// we know the runtime helper will be able to get to it...
 | 
					
						
							|  |  |  | 		if err = unix.Mount(spec.Mounts[i].Source, stage, "", unix.MS_BIND|unix.MS_REC|unix.MS_PRIVATE, ""); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return unmountAll, fmt.Errorf("bind mounting bind object from %q to %q: %w", spec.Mounts[i].Source, stage, err) | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		logrus.Debugf("bind mounted %q to %q", spec.Mounts[i].Source, stage) | 
					
						
							|  |  |  | 		spec.Mounts[i].Source = stage | 
					
						
							|  |  |  | 		// ... and update the source location that we'll pass to the
 | 
					
						
							|  |  |  | 		// runtime to our intermediate location.
 | 
					
						
							|  |  |  | 		mounts = append(mounts, spec.Mounts[i]) | 
					
						
							|  |  |  | 		unmount = append([]string{stage}, unmount...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	spec.Mounts = mounts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return unmountAll, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Decide if the mount should not be redirected to an intermediate location first.
 | 
					
						
							|  |  |  | func leaveBindMountAlone(mount specs.Mount) bool { | 
					
						
							|  |  |  | 	// If we know we shouldn't do a redirection for this mount, skip it.
 | 
					
						
							| 
									
										
										
										
											2024-01-31 23:03:20 +08:00
										 |  |  | 	if slices.Contains(mount.Options, NoBindOption) { | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If we're not bind mounting it in, we don't need to do anything for it.
 | 
					
						
							| 
									
										
										
										
											2024-01-31 23:03:20 +08:00
										 |  |  | 	if mount.Type != "bind" && !slices.Contains(mount.Options, "bind") && !slices.Contains(mount.Options, "rbind") { | 
					
						
							| 
									
										
										
										
											2018-06-07 23:25:47 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // UnmountMountpoints unmounts the given mountpoints and anything that's hanging
 | 
					
						
							|  |  |  | // off of them, rather aggressively.  If a mountpoint also appears in the
 | 
					
						
							|  |  |  | // mountpointsToRemove slice, the mountpoints are removed after they are
 | 
					
						
							|  |  |  | // unmounted.
 | 
					
						
							|  |  |  | func UnmountMountpoints(mountpoint string, mountpointsToRemove []string) error { | 
					
						
							|  |  |  | 	mounts, err := mount.GetMounts() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return fmt.Errorf("retrieving list of mounts: %w", err) | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// getChildren returns the list of mount IDs that hang off of the
 | 
					
						
							|  |  |  | 	// specified ID.
 | 
					
						
							|  |  |  | 	getChildren := func(id int) []int { | 
					
						
							|  |  |  | 		var list []int | 
					
						
							|  |  |  | 		for _, info := range mounts { | 
					
						
							|  |  |  | 			if info.Parent == id { | 
					
						
							|  |  |  | 				list = append(list, info.ID) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return list | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// getTree returns the list of mount IDs that hang off of the specified
 | 
					
						
							|  |  |  | 	// ID, and off of those mount IDs, etc.
 | 
					
						
							|  |  |  | 	getTree := func(id int) []int { | 
					
						
							|  |  |  | 		mounts := []int{id} | 
					
						
							|  |  |  | 		i := 0 | 
					
						
							|  |  |  | 		for i < len(mounts) { | 
					
						
							|  |  |  | 			children := getChildren(mounts[i]) | 
					
						
							|  |  |  | 			mounts = append(mounts, children...) | 
					
						
							|  |  |  | 			i++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return mounts | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// getMountByID looks up the mount info with the specified ID
 | 
					
						
							|  |  |  | 	getMountByID := func(id int) *mount.Info { | 
					
						
							|  |  |  | 		for i := range mounts { | 
					
						
							|  |  |  | 			if mounts[i].ID == id { | 
					
						
							|  |  |  | 				return mounts[i] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// getMountByPoint looks up the mount info with the specified mountpoint
 | 
					
						
							|  |  |  | 	getMountByPoint := func(mountpoint string) *mount.Info { | 
					
						
							|  |  |  | 		for i := range mounts { | 
					
						
							|  |  |  | 			if mounts[i].Mountpoint == mountpoint { | 
					
						
							|  |  |  | 				return mounts[i] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// find the top of the tree we're unmounting
 | 
					
						
							|  |  |  | 	top := getMountByPoint(mountpoint) | 
					
						
							|  |  |  | 	if top == nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("%q is not mounted: %w", mountpoint, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// add all of the mounts that are hanging off of it
 | 
					
						
							|  |  |  | 	tree := getTree(top.ID) | 
					
						
							|  |  |  | 	// unmount each mountpoint, working from the end of the list (leaf nodes) to the top
 | 
					
						
							|  |  |  | 	for i := range tree { | 
					
						
							|  |  |  | 		var st unix.Stat_t | 
					
						
							|  |  |  | 		id := tree[len(tree)-i-1] | 
					
						
							|  |  |  | 		mount := getMountByID(id) | 
					
						
							|  |  |  | 		// check if this mountpoint is mounted
 | 
					
						
							|  |  |  | 		if err := unix.Lstat(mount.Mountpoint, &st); err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-27 03:27:30 +08:00
										 |  |  | 			if errors.Is(err, os.ErrNotExist) { | 
					
						
							| 
									
										
										
										
											2019-11-22 04:32:41 +08:00
										 |  |  | 				logrus.Debugf("mountpoint %q is not present(?), skipping", mount.Mountpoint) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return fmt.Errorf("checking if %q is mounted: %w", mount.Mountpoint, err) | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-01-18 10:43:36 +08:00
										 |  |  | 		if uint64(mount.Major) != uint64(st.Dev) || uint64(mount.Minor) != uint64(st.Dev) { //nolint:unconvert // (required for some OS/arch combinations)
 | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			logrus.Debugf("%q is apparently not really mounted, skipping", mount.Mountpoint) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// do the unmount
 | 
					
						
							|  |  |  | 		if err := unix.Unmount(mount.Mountpoint, 0); err != nil { | 
					
						
							|  |  |  | 			// if it was busy, detach it
 | 
					
						
							|  |  |  | 			if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY { | 
					
						
							|  |  |  | 				err = unix.Unmount(mount.Mountpoint, unix.MNT_DETACH) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				// if it was invalid (not mounted), hide the error, else return it
 | 
					
						
							|  |  |  | 				if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EINVAL { | 
					
						
							|  |  |  | 					logrus.Warnf("error unmounting %q: %v", mount.Mountpoint, err) | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// if we're also supposed to remove this thing, do that, too
 | 
					
						
							| 
									
										
										
										
											2024-01-31 23:03:20 +08:00
										 |  |  | 		if slices.Contains(mountpointsToRemove, mount.Mountpoint) { | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			if err := os.Remove(mount.Mountpoint); err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 				return fmt.Errorf("removing %q: %w", mount.Mountpoint, err) | 
					
						
							| 
									
										
										
										
											2018-06-12 09:50:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } |