generated from soypat/go-module-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgpt.go
265 lines (217 loc) · 7.9 KB
/
gpt.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
package gpt implements GUID Partition Table scheme.
*/
package gpt
import (
"encoding/binary"
"errors"
"github.com/soypat/tinyboot/internal/utf16x"
)
const (
pteNameOff = 56
pteNameLen = 72
)
type Header struct {
data []byte
}
func ToHeader(start []byte) (Header, error) {
if len(start) < 92 {
return Header{}, errors.New("gpt header too short")
}
h := Header{
data: start[:92:92],
}
return h, nil
}
// Signature returns the 8-byte signature at the start of the GPT header.
// Expect it to be 0x5452415020494645, which is "EFI PART" in little-endian.
func (h Header) Signature() (sig uint64) {
return binary.LittleEndian.Uint64(h.data[0:8])
}
// Revision returns the GPT Header revision number. [0,0,1,0] for UEFI 2.10.
func (h Header) Revision() uint32 {
return binary.LittleEndian.Uint32(h.data[8:12])
}
// Size returns the size of the GPT header in bytes, usually 92.
func (h Header) Size() uint32 {
return binary.LittleEndian.Uint32(h.data[12:16])
}
// SetSize sets the size of the GPT header in bytes.
func (h Header) SetSize(size uint32) {
binary.LittleEndian.PutUint32(h.data[12:16], size)
}
// CRC returns the CRC32 of the GPT header.
func (h Header) CRC() uint32 {
return binary.LittleEndian.Uint32(h.data[16:20])
}
// SetCRC sets the CRC32 of the GPT header.
func (h Header) SetCRC(crc uint32) {
binary.LittleEndian.PutUint32(h.data[16:20], crc)
}
// Bytes 20..24 are reserved and should be zero.
// CurrentLBA returns the LBA of the current GPT header.
func (h Header) CurrentLBA() int64 {
return int64(binary.LittleEndian.Uint64(h.data[24:32]))
}
// SetCurrentLBA sets the LBA of the current GPT header.
func (h Header) SetCurrentLBA(lba int64) {
binary.LittleEndian.PutUint64(h.data[24:32], uint64(lba))
}
// BackupLBA returns the LBA of the backup GPT header.
func (h Header) BackupLBA() int64 {
return int64(binary.LittleEndian.Uint64(h.data[32:40]))
}
// SetBackupLBA sets the LBA of the backup GPT header.
func (h Header) SetBackupLBA(lba int64) {
binary.LittleEndian.PutUint64(h.data[32:40], uint64(lba))
}
// FirstUsableLBA returns the first LBA that is not used by the GPT header, partition table and partition entries.
func (h Header) FirstUsableLBA() int64 {
return int64(binary.LittleEndian.Uint64(h.data[40:48]))
}
// SetFirstUsableLBA sets the first LBA that is not used by the GPT header, partition table and partition entries.
func (h Header) SetFirstUsableLBA(lba int64) {
binary.LittleEndian.PutUint64(h.data[40:48], uint64(lba))
}
// LastUsableLBA returns the last LBA that is not used by the GPT header, partition table and partition entries.
func (h Header) LastUsableLBA() int64 {
return int64(binary.LittleEndian.Uint64(h.data[48:56]))
}
// SetLastUsableLBA sets the last LBA that is not used by the GPT header, partition table and partition entries.
func (h Header) SetLastUsableLBA(lba int64) {
binary.LittleEndian.PutUint64(h.data[48:56], uint64(lba))
}
// DiskGUID returns the GUID of the disk.
func (h Header) DiskGUID() (guid [16]byte) {
copy(guid[:], h.data[56:72])
return guid
}
// SetDiskGUID sets the GUID of the disk.
func (h Header) SetDiskGUID(guid [16]byte) {
copy(h.data[56:72], guid[:])
}
// PartitionEntryLBA returns the LBA of the start of the partition table.
// This field is usually 2 for compatibility with MBR paritioning.
// This is because 0 is used for the protective MBR and 1 is used for the GPT header.
func (h Header) PartitionEntryLBA() int64 {
return int64(binary.LittleEndian.Uint64(h.data[72:80]))
}
// SetPartitionEntryLBA sets the LBA of the start of the partition table.
func (h Header) SetPartitionEntryLBA(lba int64) {
binary.LittleEndian.PutUint64(h.data[72:80], uint64(lba))
}
// NumberOfPartitionEntries returns the number of partition entries in the partition table.
func (h Header) NumberOfPartitionEntries() uint32 {
return binary.LittleEndian.Uint32(h.data[80:84])
}
// SetNumberOfPartitionEntries sets the number of partition entries in the partition table.
func (h Header) SetNumberOfPartitionEntries(n uint32) {
binary.LittleEndian.PutUint32(h.data[80:84], n)
}
// SizeOfPartitionEntry returns the size of each partition entry in the partition table.
// Is usually 128.
func (h Header) SizeOfPartitionEntry() uint32 {
return binary.LittleEndian.Uint32(h.data[84:88])
}
// SetSizeOfPartitionEntry sets the size of each partition entry in the partition table.
func (h Header) SetSizeOfPartitionEntry(size uint32) {
binary.LittleEndian.PutUint32(h.data[84:88], size)
}
// CRCOfPartitionEntries returns the CRC32 of the partition entries in the partition table.
func (h Header) CRCOfPartitionEntries() uint32 {
return binary.LittleEndian.Uint32(h.data[88:92])
}
// SetCRCOfPartitionEntries sets the CRC32 of the partition entries in the partition table.
func (h Header) SetCRCOfPartitionEntries(crc uint32) {
binary.LittleEndian.PutUint32(h.data[88:92], crc)
}
// PartitionEntry represents a single partition entry in the GPT partition table. Usually of size 128 bytes.
type PartitionEntry struct {
data []byte
}
type PartitionAttributes uint64
func ToPartitionEntry(start []byte) (PartitionEntry, error) {
if len(start) < 128 {
return PartitionEntry{}, errors.New("gpt partition entry too short")
}
p := PartitionEntry{
data: start[:128:128],
}
return p, nil
}
// PartitionTypeGUID returns the GUID of the partition type.
func (p PartitionEntry) PartitionTypeGUID() (guid [16]byte) {
copy(guid[:], p.data[0:16])
return
}
// SetPartitionTypeGUID sets the GUID of the partition type.
func (p PartitionEntry) SetPartitionTypeGUID(guid [16]byte) {
copy(p.data[0:16], guid[:])
}
// UniquePartitionGUID returns the GUID of the partition.
func (p PartitionEntry) UniquePartitionGUID() (guid [16]byte) {
copy(guid[:], p.data[16:32])
return
}
// SetUniquePartitionGUID sets the GUID of the partition.
func (p PartitionEntry) SetUniquePartitionGUID(guid [16]byte) {
copy(p.data[16:32], guid[:])
}
// FirstLBA returns the first LBA of the partition.
// To calculate total LBAs: (LastLBA - FirstLBA) + 1
func (p PartitionEntry) FirstLBA() int64 {
return int64(binary.LittleEndian.Uint64(p.data[32:40]))
}
// SetFirstLBA sets the first LBA of the partition.
func (p PartitionEntry) SetFirstLBA(lba int64) {
binary.LittleEndian.PutUint64(p.data[32:40], uint64(lba))
}
// LastLBA returns the last LBA of the partition (inclusive).
// To calculate total LBAs: (LastLBA - FirstLBA) + 1
func (p PartitionEntry) LastLBA() int64 {
return int64(binary.LittleEndian.Uint64(p.data[40:48]))
}
// SetLastLBA sets the last LBA of the partition (inclusive).
func (p PartitionEntry) SetLastLBA(lba int64) {
binary.LittleEndian.PutUint64(p.data[40:48], uint64(lba))
}
// Attributes returns the attributes of the partition.
func (p PartitionEntry) Attributes() PartitionAttributes {
return PartitionAttributes(binary.LittleEndian.Uint64(p.data[48:56]))
}
// SetAttributes sets the attributes of the partition.
func (p PartitionEntry) SetAttributes(attr PartitionAttributes) {
binary.LittleEndian.PutUint64(p.data[48:56], uint64(attr))
}
// ReadNameAsUTF8 reads the partition name from the partition entry and
// encodes it as utf-8 into the provided slice. The number of bytes
// read is returned along with any error.
func (p PartitionEntry) ReadNameAsUTF8(b []byte) (int, error) {
// Find the length of the name.
nameLen := 0
for nameLen < pteNameLen && p.data[pteNameOff+nameLen] != 0 {
nameLen++
}
n, err := utf16x.ToUTF8(b, p.data[pteNameOff:pteNameOff+nameLen], binary.LittleEndian)
if err != nil {
return n, err
}
return n, nil
}
func (p PartitionEntry) ClearName() {
p.clearNameAfter(0)
}
// SetNameUTF8 writes a utf-8 encoded string as the Partition Entry's name.
func (p PartitionEntry) SetNameUTF8(name []byte) error {
n, err := utf16x.FromUTF8(p.data[pteNameOff:pteNameOff+pteNameLen], name, binary.LittleEndian)
if err != nil {
return err
}
p.clearNameAfter(n)
return nil
}
func (p PartitionEntry) clearNameAfter(idx int) {
for i := idx; i < pteNameLen; i++ {
p.data[pteNameOff+i] = 0
}
}