diff --git a/pkg/cleanup/directories.go b/pkg/cleanup/directories.go index 540a9f928a95..2b3a9b459040 100644 --- a/pkg/cleanup/directories.go +++ b/pkg/cleanup/directories.go @@ -21,8 +21,10 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" "k8s.io/mount-utils" ) @@ -46,15 +48,23 @@ func (d *directories) Run() error { var dataDirMounted bool - // search and unmount kubelet volume mounts - for _, v := range procMounts { - if v.Path == filepath.Join(d.Config.dataDir, "kubelet") { + // search and unmount anything under kubelet root or data dir + // in reverse order to handle nested mounts and over mounts + for i := len(procMounts) - 1; i >= 0; i-- { + v := procMounts[i] + // avoid unmount datadir if its mounted on separate partition + if v.Path == d.Config.k0sVars.DataDir { + dataDirMounted = true + continue + } + if isUnderPath(v.Path, filepath.Join(d.Config.dataDir, "kubelet")) || isUnderPath(v.Path, d.Config.k0sVars.DataDir) { logrus.Debugf("%v is mounted! attempting to unmount...", v.Path) if err = mounter.Unmount(v.Path); err != nil { - logrus.Warningf("failed to unmount %v", v.Path) + logrus.Warningf("lazy unmounting %v", v.Path) + if err = unix.Unmount(v.Path, unix.MNT_DETACH); err != nil { + return fmt.Errorf("failed unmount %v", v.Path) + } } - } else if v.Path == d.Config.dataDir { - dataDirMounted = true } } @@ -81,6 +91,12 @@ func (d *directories) Run() error { return nil } +// test if the path is a directory equal to or under base +func isUnderPath(path, base string) bool { + rel, err := filepath.Rel(base, path) + return err == nil && !strings.HasPrefix(rel, "..") && !filepath.IsAbs(rel) +} + // this is for checking if the error retrned by os.RemoveAll is due to // it being a mount point. if it is, we can ignore the error. this way // we can't rely on os.RemoveAll instead of recursively deleting the