-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdevice.go
163 lines (144 loc) · 3.63 KB
/
device.go
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package main
import (
"fmt"
"os"
"path"
"strings"
"syscall"
)
type device struct {
device string
name string
major int
minor int
rIops uint64
wIops uint64
lastChange int64
lastStandBy int64
exists bool
isSleeping bool
}
type powerMode byte
const (
powerModeStandby powerMode = 0x00
powerModeNvcacheSpindown powerMode = 0x40
powerModeNvcache_spinup powerMode = 0x41
powerModeIdle powerMode = 0x80
powerModeActive powerMode = 0xff
unknown powerMode = 0xe0
)
// Initializes device
func (d *device) initDevice(dev string, hddOnly bool) (*device, error) {
if d == nil {
for dev[len(dev)-1] == '/' {
dev = dev[:len(dev)-1]
}
if len(dev) <= len(devicePrefix) {
return nil, fmt.Errorf("device '%s' cannot be recognized", dev)
}
d = &device{device: dev, name: dev[len(devicePrefix):]}
if d.isPartition() {
return nil, fmt.Errorf("device '%s' is a partition, it could not be initialized", d.device)
}
if hddOnly && !d.isRotational() {
return nil, nil
}
return d, nil
} else {
// something
return d, nil
}
}
// Puts drive to sleep/standby mode with ATA_OP_SLEEPNOW1, eventually ATA_OP_SLEEPNOW2.
func (d *device) putDriveToSleep() error {
resp, err := sgioCommand(d.device, ATA_OP_SLEEPNOW1)
if err != nil {
if resp == EACCESS {
return err
}
_, err = sgioCommand(d.device, ATA_OP_SLEEPNOW2)
}
return err
}
// Gets drive power mode/state with ATA_OP_CHECK_POWER_MODE1, eventually ATA_OP_CHECK_POWER_MODE2.
func (d *device) getDriveState() (powerMode, error) {
val, err := sgioCommand(d.device, ATA_OP_CHECK_POWER_MODE1)
if val == EACCESS {
return unknown, err
} else if val == byte(unknown) {
val, err = sgioCommand(d.device, ATA_OP_CHECK_POWER_MODE2)
}
return powerMode(val), err
}
// Checks if drive power mode/state is standby with ATA_OP_CHECK_POWER_MODE1, eventually ATA_OP_CHECK_POWER_MODE2.
func (d *device) isDriveSleeping() (bool, error) {
state, err := d.getDriveState()
if err != nil {
return false, err
}
return state == powerModeStandby, nil
}
// Updates major and minor from device specification. It handles possible hotswaping.
func (d *device) updateMajorMinor() error {
fileInfo, err := os.Stat(d.device)
if err != nil {
d.reset()
return fmt.Errorf("device '%s' is not available", d.device)
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
d.reset()
return fmt.Errorf("device '%s' is not available", d.device)
}
d.exists = true
d.major = int(uint32(stat.Rdev >> 8))
d.minor = int(uint32(stat.Rdev & 0xff))
return nil
}
// Resets all values except device
func (d *device) reset() {
d.exists = false
d.major = 0
d.minor = 0
d.rIops = 0
d.wIops = 0
d.lastChange = 0
d.lastStandBy = 0
d.name = ""
d.isSleeping = false
}
// Checks if defined device is partition
func (d *device) isPartition() bool {
b, _ := os.Stat(path.Join("/sys/class/block/", d.name, "partition"))
return b != nil
}
// Checks if defined device is rotational
func (d *device) isRotational() bool {
path := path.Join("/sys/class/block/", d.name, "queue/rotational")
b, _ := os.Stat(path)
if b == nil {
return false
}
data, err := os.ReadFile(path)
if err != nil {
return false
}
return strings.TrimSpace(string(data)) == "1"
}
// Converts power mode into human readable text.
func (p *powerMode) stringify() string {
switch *p {
case powerModeStandby:
return "standby"
case powerModeNvcacheSpindown:
return "NVcache_spindown"
case powerModeNvcache_spinup:
return "NVcache_spinup"
case powerModeIdle:
return "idle"
case powerModeActive:
return "active/idle"
default:
return "unknown"
}
}