package node import ( "bufio" "context" "fmt" "os" "path/filepath" "strings" "time" monov1alpha1 "example.com/monok8s/pkg/apis/monok8s/v1alpha1" system "example.com/monok8s/pkg/system" "k8s.io/klog/v2" ) const ( altRootMount = "/run/altrootfs" altImageStoreSrc = "/run/altrootfs/usr/lib/monok8s/imagestore" ) func MountAltImageStore(ctx context.Context, nctx *NodeContext) error { // Make mountpoints first. if err := os.MkdirAll(altRootMount, 0o755); err != nil { return fmt.Errorf("mkdir %s: %w", altRootMount, err) } altDev := monov1alpha1.AltPartDeviceLink if mounted, err := isMounted(altRootMount); err != nil { return fmt.Errorf("check mount %s: %w", altRootMount, err) } else if !mounted { klog.InfoS("mounting alt rootfs", "device", altDev, "target", altRootMount) if _, err := nctx.SystemRunner.RunWithOptions( ctx, "mount", []string{"-o", "ro", altDev, altRootMount}, system.RunOptions{Timeout: 30 * time.Second}, ); err != nil { klog.Errorf("mount alt rootfs %s on %s: %w", altDev, altRootMount, err) } } // If the alt imagestore doesn't exist, don't fail hard unless you want strict behavior. st, err := os.Stat(altImageStoreSrc) if err != nil { // Unmount immediately _ = safeUnmount(ctx, nctx, altRootMount) if os.IsNotExist(err) { klog.InfoS( "alt imagestore not found; proceeding without it", "path", altImageStoreSrc, ) err = writeCRIOStorageConfig(ctx, nctx, "") if err != nil { return err } return nil } return fmt.Errorf("stat alt imagestore %s: %w", altImageStoreSrc, err) } if !st.IsDir() { return fmt.Errorf("alt imagestore exists but is not a directory: %s", altImageStoreSrc) } err = writeCRIOStorageConfig(ctx, nctx, altImageStoreSrc) if err != nil { return err } return nil } func UnmountAltImageStore(ctx context.Context, nctx *NodeContext) error { mounted, err := isMounted(altRootMount) if err != nil { return err } if mounted { if err := writeCRIOStorageConfig(ctx, nctx, ""); err != nil { return err } err := RestartCRIO(ctx, nctx) if err != nil { return err } } var errs []string if err := safeUnmount(ctx, nctx, altRootMount); err != nil { errs = append(errs, err.Error()) } if len(errs) > 0 { return fmt.Errorf(strings.Join(errs, "; ")) } return nil } func isMounted(path string) (bool, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { return false, fmt.Errorf("open /proc/self/mountinfo: %w", err) } defer f.Close() target := filepath.Clean(path) scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) < 5 { continue } mountPoint, err := unescapeMountInfo(fields[4]) if err != nil { return false, fmt.Errorf("decode mountpoint %q: %w", fields[4], err) } if filepath.Clean(mountPoint) == target { return true, nil } } if err := scanner.Err(); err != nil { return false, fmt.Errorf("scan /proc/self/mountinfo: %w", err) } return false, nil } func safeUnmount(ctx context.Context, nctx *NodeContext, path string) error { mounted, err := isMounted(path) if err != nil { return fmt.Errorf("check mountpoint %s: %w", path, err) } if !mounted { return nil } klog.InfoS("unmounting", "target", path) _, err = nctx.SystemRunner.RunWithOptions( ctx, "umount", []string{path}, system.RunOptions{Timeout: 30 * time.Second}, ) if err != nil { return fmt.Errorf("umount %s: %w", path, err) } return nil } func unescapeMountInfo(s string) (string, error) { var b strings.Builder b.Grow(len(s)) for i := 0; i < len(s); i++ { if s[i] != '\\' { b.WriteByte(s[i]) continue } if i+3 >= len(s) { return "", fmt.Errorf("truncated escape sequence") } esc := s[i+1 : i+4] switch esc { case "040": b.WriteByte(' ') case "011": b.WriteByte('\t') case "012": b.WriteByte('\n') case "134": b.WriteByte('\\') default: return "", fmt.Errorf("unsupported escape sequence \\%s", esc) } i += 3 } return b.String(), nil }