diff --git a/doGui.go b/doGui.go index 9a902ee..8263587 100644 --- a/doGui.go +++ b/doGui.go @@ -6,7 +6,6 @@ package main // An app to submit patches for the 30 GO GUI repos import ( - "fmt" "os" "strings" "time" @@ -21,7 +20,7 @@ import ( func debug() { for { time.Sleep(10 * time.Second) - log.Info("TODO: use this debug loop for something?") + // log.Info("TODO: use this debug loop for something?") } } @@ -40,17 +39,48 @@ func addDrive(devname string, displayDesc string) *Block { } func switchDrive(blk *Block) { + var inUse bool = false me.currentDev = blk - me.parted.SetText("Partition " + blk.Name) + + if isBlockDeviceInUse(blk.Name) { + log.Info("Is in use?", blk.Name) + me.parted.SetText("Partition " + blk.Name + " (in use)") + me.parted.Disable() + inUse = true + } else { + log.Info("Is probably not in use?", blk.Name) + me.parted.SetText("Partition " + blk.Name + " as gpt boot disk") + me.parted.Enable() + } log.Info("check if", me.currentDev.Name, "is in use") result, err := shell.RunVerbose([]string{"parted", me.currentDev.Name, "print"}) log.Info("result =", result.Stdout, "err =", err) out := strings.Join(result.Stdout, "\n") if err != nil { - out += fmt.Sprintf("err = %v", err) + out += log.Sprintf("err = %v", err) } me.driveInfoBox.SetText(out) + + if inUse { + // if the drive is in use already, you can just stop here + return + } + + if ok, err := hasPartitionTable(blk.Name); err != nil { + out := log.Sprintf("Error checking partition table: %s\n", err) + log.Warn(out) + me.parted.Disable() + me.driveInfoBox.SetText(out) + } else { + if !ok { + log.Printf("%s has no partition table safe for gdisk\n", blk.Name) + } else { + log.Printf("%s already has a partition table\n", blk.Name) + me.parted.SetText("Partition " + blk.Name + " (has partitions)") + me.parted.Disable() + } + } } func doGui() { @@ -95,7 +125,7 @@ func drawWindow(win *gadgets.GenericWindow) { log.Info("You must select a drive first") return } - log.Info("TODO: figure out if the drive is in use") + log.Info("If you got here, gdisk should be safe to run on", me.currentDev.Name) }) grid.NextRow() diff --git a/hasPartitionTable.go b/hasPartitionTable.go new file mode 100644 index 0000000..96ed786 --- /dev/null +++ b/hasPartitionTable.go @@ -0,0 +1,34 @@ +package main + +import ( + "bytes" + "fmt" + "os/exec" + "strings" +) + +// HasPartitionTable runs `parted print` and returns: +// - true if the disk has a recognized partition table +// - false if it's unrecognized or empty +func hasPartitionTable(dev string) (bool, error) { + cmd := exec.Command("parted", "-s", dev, "print") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + + err := cmd.Run() + output := out.String() + + // Check for known "no label" pattern + if strings.Contains(output, "unrecognised disk label") || + strings.Contains(output, "unrecognized disk label") || + strings.Contains(output, "Error") { + return false, nil + } + + if err != nil { + return false, fmt.Errorf("parted failed: %w: %s", err, output) + } + + return true, nil +} diff --git a/isDriveInUse.go b/isDriveInUse.go new file mode 100644 index 0000000..912ae07 --- /dev/null +++ b/isDriveInUse.go @@ -0,0 +1,95 @@ +package main + +import ( + "bufio" + "os" + "path/filepath" + "strings" + + "go.wit.com/log" +) + +func isBlockDeviceInUse(dev string) bool { + base := filepath.Base(dev) + + // 1. Check /proc/mounts + if isMounted(dev, base) { + log.Info(dev, "dev is mounted (/proc/mounts)") + return true + } + + // 2. Check /proc/swaps + if isSwap(dev) { + log.Info(dev, "dev is used by swap (/proc/swaps)") + return true + } + + // 3. Check /proc/mdstat for RAID + if isInRAID(base) { + log.Info(dev, "dev is in software raid (/proc/mdstat)") + return true + } + + // 4. Check sysfs for device-mapper or LVM use + if hasDMHolders(base) { + log.Info(dev, "dev has lvm partitions") + return true + } + + log.Info(dev, "dev appears to be unused") + return false +} + +func isMounted(dev, base string) bool { + f, err := os.Open("/proc/mounts") + if err != nil { + return false + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), dev) || strings.Contains(scanner.Text(), base) { + return true + } + } + return false +} + +func isSwap(dev string) bool { + f, err := os.Open("/proc/swaps") + if err != nil { + return false + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), dev) { + return true + } + } + return false +} + +func isInRAID(base string) bool { + f, err := os.Open("/proc/mdstat") + if err != nil { + return false + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if strings.Contains(scanner.Text(), base) { + return true + } + } + return false +} + +func hasDMHolders(base string) bool { + path := filepath.Join("/sys/block", base, "holders") + entries, err := os.ReadDir(path) + return err == nil && len(entries) > 0 +}