diff options
| author | Jeff Carr <[email protected]> | 2025-08-17 17:43:34 -0500 | 
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-08-17 22:57:06 -0500 | 
| commit | 145244f63d2589203b1450af61987f16fb87aff3 (patch) | |
| tree | 3b7a481b7a793f51a58a6633f3260ef512a3deca | |
| parent | 25c604aeee7cd821d27dfd4c09bf0626e9312535 (diff) | |
more sanity checks
| -rw-r--r-- | doGui.go | 40 | ||||
| -rw-r--r-- | hasPartitionTable.go | 34 | ||||
| -rw-r--r-- | isDriveInUse.go | 95 | 
3 files changed, 164 insertions, 5 deletions
@@ -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 <device> 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 +}  | 
