Last active
November 17, 2024 07:33
-
-
Save keithchambers/d15b629bcc2f65ac478ed1363824b4fc to your computer and use it in GitHub Desktop.
macvolumes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // macvolumes is a tool to classify macOS volumes by their physical connectivity | |
| // and whether they are permanently installed or removable. It distinguishes between: | |
| // - System volumes (the macOS boot volume) | |
| // - Fixed volumes (built-in or permanently installed drives) | |
| // - Removable volumes (external drives that can be disconnected) | |
| // | |
| // The tool uses macOS's standardized disk management structures to ensure | |
| // consistent behavior across different Mac models. | |
| package main | |
| import ( | |
| "context" | |
| "fmt" | |
| "os" | |
| "os/exec" | |
| "regexp" | |
| "strings" | |
| "time" | |
| ) | |
| // ConnectionType represents a volume's physical connection characteristics | |
| type ConnectionType struct { | |
| Attachment string // "fixed" or "removable" | |
| Transport string // "system", "fabric", "pcie", "thunderbolt", "usb" | |
| } | |
| // Constants representing different transport types | |
| const ( | |
| appleFabric = "apple fabric" | |
| pcie = "pcie" | |
| nvme = "nvme" | |
| thunderbolt = "thunderbolt" | |
| usb = "usb" | |
| ) | |
| var diskIDPattern = regexp.MustCompile(disk\d+) | |
| // runCommand executes a system command with a timeout and returns its output | |
| func runCommand(name string, args ...string) (string, error) { | |
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | |
| defer cancel() | |
| cmd := exec.CommandContext(ctx, name, args...) | |
| output, err := cmd.Output() | |
| if ctx.Err() == context.DeadlineExceeded { | |
| return "", fmt.Errorf("command %s timed out", name) | |
| } | |
| if err != nil { | |
| return "", fmt.Errorf("error running %s: %v", name, err) | |
| } | |
| return string(output), nil | |
| } | |
| // getDiskInfo retrieves and parses diskutil info for a given disk | |
| // Returns a map of lowercase key-value pairs from diskutil output | |
| func getDiskInfo(diskID string) (map[string]string, error) { | |
| info := make(map[string]string) | |
| output, err := runCommand("diskutil", "info", diskID) | |
| if err != nil { | |
| return nil, err | |
| } | |
| lines := strings.Split(output, "\n") | |
| for _, line := range lines { | |
| line = strings.TrimSpace(line) | |
| if strings.Contains(line, ":") { | |
| parts := strings.SplitN(line, ":", 2) | |
| key := strings.TrimSpace(strings.ToLower(parts[0])) | |
| value := strings.TrimSpace(strings.ToLower(parts[1])) | |
| info[key] = value | |
| } | |
| } | |
| return info, nil | |
| } | |
| // isTimeMachineVolume checks if a volume is used for Time Machine backups | |
| // This checks both the volume name and specific Time Machine flags | |
| func isTimeMachineVolume(diskID string) bool { | |
| info, err := getDiskInfo(diskID) | |
| if err != nil { | |
| fmt.Fprintf(os.Stderr, "Error getting disk info for %s: %v\n", diskID, err) | |
| return false | |
| } | |
| // Check volume name and properties | |
| volName := strings.ToLower(info["volume name"]) | |
| return strings.Contains(volName, "time machine") || | |
| strings.Contains(volName, "backup") || | |
| strings.Contains(info["file system personality"], "com.apple.backup") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment