//go:build linux package storage import ( "fmt" "os" "path/filepath" "strings" "syscall" "time" ) // IsSystemDisk checks if the given device path overlaps with the root filesystem device. // Returns true if the device is (or is the parent of) the system disk. func IsSystemDisk(devicePath string) (bool, error) { // Get the block device major number of the root filesystem var rootStat syscall.Stat_t if err := syscall.Stat("/", &rootStat); err != nil { return false, fmt.Errorf("cannot stat /: %w", err) } // Get block device info of the target device var devStat syscall.Stat_t if err := syscall.Stat(devicePath, &devStat); err != nil { return false, fmt.Errorf("cannot stat %s: %w", devicePath, err) } // Compare major device numbers rootMajor := rootStat.Dev >> 8 & 0xff devMajor := devStat.Rdev >> 8 & 0xff if rootMajor == devMajor { return true, nil } return false, nil } // IsDeviceMounted checks if a device or any of its partitions is currently mounted. func IsDeviceMounted(devicePath string) (bool, error) { data, err := os.ReadFile("/proc/mounts") if err != nil { return false, fmt.Errorf("cannot read /proc/mounts: %w", err) } base := filepath.Base(devicePath) for _, line := range strings.Split(string(data), "\n") { fields := strings.Fields(line) if len(fields) < 2 { continue } dev := fields[0] devBase := filepath.Base(dev) if devBase == base || strings.HasPrefix(devBase, base) { return true, nil } } return false, nil } // IsMountPathInUse checks if a path is already used as a mount point. func IsMountPathInUse(mountPath string) (bool, error) { data, err := os.ReadFile("/proc/mounts") if err != nil { return false, fmt.Errorf("cannot read /proc/mounts: %w", err) } mountPath = filepath.Clean(mountPath) for _, line := range strings.Split(string(data), "\n") { fields := strings.Fields(line) if len(fields) < 2 { continue } if filepath.Clean(fields[1]) == mountPath { return true, nil } } return false, nil } // BackupFstab creates a dated backup of the fstab file. func BackupFstab(fstabPath string) error { data, err := os.ReadFile(fstabPath) if err != nil { return fmt.Errorf("cannot read %s: %w", fstabPath, err) } backupPath := fstabPath + ".bak." + time.Now().Format("20060102") return os.WriteFile(backupPath, data, 0644) } // AppendFstabEntry appends a UUID-based fstab entry. func AppendFstabEntry(fstabPath, uuid, mountPoint, fsType, options string) error { entry := fmt.Sprintf("\nUUID=%s\t%s\t%s\t%s\t0 2\n", uuid, mountPoint, fsType, options) f, err := os.OpenFile(fstabPath, os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("cannot open fstab for writing: %w", err) } defer f.Close() if _, err := f.WriteString(entry); err != nil { return fmt.Errorf("cannot write fstab entry: %w", err) } return nil }