summaryrefslogtreecommitdiff
path: root/getDrives.go
blob: 13dacc60b4ffda43caf09afb949912e450675c94 (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
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 {
			addDrive(devPath, log.Sprintf("-> %-8s (raw)", info.Type))
		} 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 ""
}