// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "bufio" "log" "os" "strings" "time" "golang.org/x/sys/unix" ) // UnmountAll attempts to unmount all the mounted filesystems via "self" mountinfo. func UnmountAll() error { // timeout in seconds const timeout = 10 ticker := time.NewTicker(time.Second) defer ticker.Stop() for i := 0; i < timeout; i++ { mounts, err := readMountInfo() if err != nil { return err } failedUnmounts := 0 for _, mountInfo := range mounts { if mountInfo.MountPoint == "" { continue } if strings.HasPrefix(mountInfo.MountSource, "/dev/") { err = unix.Unmount(mountInfo.MountPoint, 0) if err == nil { log.Printf("unmounted %s (%s)", mountInfo.MountPoint, mountInfo.MountSource) } else { log.Printf("failed unmounting %s: %s", mountInfo.MountPoint, err) failedUnmounts++ } } } if failedUnmounts == 0 { break } log.Printf("retrying %d unmount operations...", failedUnmounts) <-ticker.C } return nil } type mountInfo struct { MountPoint string MountSource string } func readMountInfo() ([]mountInfo, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { return nil, err } defer f.Close() //nolint:errcheck var mounts []mountInfo scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() parts := strings.SplitN(line, " - ", 2) if len(parts) < 2 { continue } var mntInfo mountInfo pre := strings.Fields(parts[0]) post := strings.Fields(parts[1]) if len(pre) >= 5 { mntInfo.MountPoint = pre[4] } if len(post) >= 1 { mntInfo.MountSource = post[1] } mounts = append(mounts, mntInfo) } return mounts, scanner.Err() }