summaryrefslogtreecommitdiff
path: root/getDrives.go
blob: a818645fceb27e59b6d66b191665f1ae7e1981c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main

import (
	"os"
	"path/filepath"
	"strings"

	"go.wit.com/log"
)

type DevInfo struct {
	Name   string
	IsRaw  bool
	Type   string
	Parent string // raw device if this entry is a partition
}

func doDrives2() {
	parts, err := parseProcPartitions()
	if err != nil {
		log.Printf("Error reading /proc/partitions: %v\n", err)
		os.Exit(1)
	}

	// Determine raw devices and type
	devs := make(map[string]*DevInfo)
	for _, p := range parts {
		devs[p.Name] = &DevInfo{Name: p.Name}
	}

	for _, info := range devs {
		sysPath := filepath.Join("/sys/block", info.Name)
		if exists(sysPath) {
			info.IsRaw = true
			typ := detectType(sysPath)
			info.Type = typ
		} else {
			info.IsRaw = false
			info.Parent = findParent(info.Name, devs)
		}
	}

	for _, info := range devs {
		devPath := "/dev/" + info.Name
		if info.IsRaw {
			s := log.Sprintf("%-12s -> %-8s (raw)", devPath, info.Type)
			log.Info(s)
			me.dd.AddText(s)
		} else {
			if info.Parent != "" {
				log.Printf("%-12s -> partition of /dev/%s\n", devPath, info.Parent)
			} else {
				log.Printf("%-12s -> unknown (no parent found)\n", devPath)
			}
		}
	}
}

/*
// parseProcPartitions parses /proc/partitions entries
func parseProcPartitions() ([]struct {
	Name string
}, error) {
	f, err := os.Open("/proc/partitions")
	if err != nil {
		return nil, err
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	var parts []struct{ Name string }
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, "major") {
			continue
		}
		fields := strings.Fields(line)
		if len(fields) < 4 {
			continue
		}
		parts = append(parts, struct{ Name string }{Name: fields[3]})
	}
	return parts, scanner.Err()
}
*/

// exists checks if the given path exists
func exists(path string) bool {
	_, err := os.Lstat(path)
	return err == nil
}

// detectType determines device type by resolving sysPath and inspecting parent directories
func detectType(sysPath string) string {
	resolved, err := filepath.EvalSymlinks(sysPath)
	if err != nil {
		return "unknown"
	}
	resolved = filepath.ToSlash(resolved)

	switch {
	case strings.Contains(resolved, "/nvme"):
		return "NVMe"
	case strings.Contains(resolved, "/mmc_host") || strings.Contains(resolved, "/mmc"):
		return "MMC/SD"
	case strings.Contains(resolved, "/usb"):
		return "USB"
	case strings.Contains(resolved, "/loop"):
		return "Loop"
	case strings.Contains(resolved, "/ram"):
		return "RAM disk"
	default:
		return "SCSI/SATA"
	}
}

// findParent identifies the raw device for a partition, based on name heuristics
func findParent(name string, devs map[string]*DevInfo) string {
	for candidate := range devs {
		if candidate == name {
			continue
		}
		if strings.HasPrefix(name, candidate) {
			return candidate
		}
	}
	return ""
}