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
|
// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
package btrfs
import (
"fmt"
"reflect"
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/fmtutil"
)
type Superblock struct {
Checksum btrfssum.CSum `bin:"off=0x0, siz=0x20"` // Checksum of everything past this field (from 0x20 to 0x1000)
FSUUID UUID `bin:"off=0x20, siz=0x10"` // FS UUID
Self btrfsvol.PhysicalAddr `bin:"off=0x30, siz=0x8"` // physical address of this block (different for mirrors)
Flags uint64 `bin:"off=0x38, siz=0x8"` // flags
Magic [8]byte `bin:"off=0x40, siz=0x8"` // magic ('_BHRfS_M')
Generation Generation `bin:"off=0x48, siz=0x8"`
RootTree btrfsvol.LogicalAddr `bin:"off=0x50, siz=0x8"` // logical address of the root tree root
ChunkTree btrfsvol.LogicalAddr `bin:"off=0x58, siz=0x8"` // logical address of the chunk tree root
LogTree btrfsvol.LogicalAddr `bin:"off=0x60, siz=0x8"` // logical address of the log tree root
LogRootTransID uint64 `bin:"off=0x68, siz=0x8"` // log_root_transid
TotalBytes uint64 `bin:"off=0x70, siz=0x8"` // total_bytes
BytesUsed uint64 `bin:"off=0x78, siz=0x8"` // bytes_used
RootDirObjectID ObjID `bin:"off=0x80, siz=0x8"` // root_dir_objectid (usually 6)
NumDevices uint64 `bin:"off=0x88, siz=0x8"` // num_devices
SectorSize uint32 `bin:"off=0x90, siz=0x4"`
NodeSize uint32 `bin:"off=0x94, siz=0x4"`
LeafSize uint32 `bin:"off=0x98, siz=0x4"` // unused; must be the same as NodeSize
StripeSize uint32 `bin:"off=0x9c, siz=0x4"`
SysChunkArraySize uint32 `bin:"off=0xa0, siz=0x4"`
ChunkRootGeneration Generation `bin:"off=0xa4, siz=0x8"`
CompatFlags uint64 `bin:"off=0xac, siz=0x8"` // compat_flags
CompatROFlags uint64 `bin:"off=0xb4, siz=0x8"` // compat_ro_flags - only implementations that support the flags can write to the filesystem
IncompatFlags IncompatFlags `bin:"off=0xbc, siz=0x8"` // incompat_flags - only implementations that support the flags can use the filesystem
ChecksumType btrfssum.CSumType `bin:"off=0xc4, siz=0x2"`
RootLevel uint8 `bin:"off=0xc6, siz=0x1"` // root_level
ChunkLevel uint8 `bin:"off=0xc7, siz=0x1"` // chunk_root_level
LogLevel uint8 `bin:"off=0xc8, siz=0x1"` // log_root_level
DevItem btrfsitem.Dev `bin:"off=0xc9, siz=0x62"` // DEV_ITEM data for this device
Label [0x100]byte `bin:"off=0x12b, siz=0x100"` // label (may not contain '/' or '\\')
CacheGeneration Generation `bin:"off=0x22b, siz=0x8"`
UUIDTreeGeneration Generation `bin:"off=0x233, siz=0x8"`
// FeatureIncompatMetadataUUID
MetadataUUID UUID `bin:"off=0x23b, siz=0x10"`
// FeatureIncompatExtentTreeV2
NumGlobalRoots uint64 `bin:"off=0x24b, siz=0x8"`
// FeatureIncompatExtentTreeV2
BlockGroupRoot btrfsvol.LogicalAddr `bin:"off=0x253, siz=0x8"`
BlockGroupRootGeneration Generation `bin:"off=0x25b, siz=0x8"`
BlockGroupRootLevel uint8 `bin:"off=0x263, siz=0x1"`
Reserved [199]byte `bin:"off=0x264, siz=0xc7"` // future expansion
SysChunkArray [0x800]byte `bin:"off=0x32b, siz=0x800"` // sys_chunk_array:(n bytes valid) Contains (KEY . CHUNK_ITEM) pairs for all SYSTEM chunks. This is needed to bootstrap the mapping from logical addresses to physical.
SuperRoots [4]RootBackup `bin:"off=0xb2b, siz=0x2a0"`
// Padded to 4096 bytes
Padding [565]byte `bin:"off=0xdcb, siz=0x235"`
binstruct.End `bin:"off=0x1000"`
}
func (sb Superblock) CalculateChecksum() (btrfssum.CSum, error) {
data, err := binstruct.Marshal(sb)
if err != nil {
return btrfssum.CSum{}, err
}
return sb.ChecksumType.Sum(data[binstruct.StaticSize(btrfssum.CSum{}):])
}
func (sb Superblock) ValidateChecksum() error {
stored := sb.Checksum
calced, err := sb.CalculateChecksum()
if err != nil {
return err
}
if calced != stored {
return fmt.Errorf("superblock checksum mismatch: stored=%v calculated=%v",
stored, calced)
}
return nil
}
func (a Superblock) Equal(b Superblock) bool {
a.Checksum = btrfssum.CSum{}
a.Self = 0
b.Checksum = btrfssum.CSum{}
b.Self = 0
return reflect.DeepEqual(a, b)
}
func (sb Superblock) EffectiveMetadataUUID() UUID {
if !sb.IncompatFlags.Has(FeatureIncompatMetadataUUID) {
return sb.FSUUID
}
return sb.MetadataUUID
}
type SysChunk struct {
Key Key
Chunk btrfsitem.Chunk
}
func (sc SysChunk) MarshalBinary() ([]byte, error) {
dat, err := binstruct.Marshal(sc.Key)
if err != nil {
return dat, err
}
_dat, err := binstruct.Marshal(sc.Chunk)
dat = append(dat, _dat...)
if err != nil {
return dat, err
}
return dat, nil
}
func (sc *SysChunk) UnmarshalBinary(dat []byte) (int, error) {
n, err := binstruct.Unmarshal(dat, &sc.Key)
if err != nil {
return n, err
}
_n, err := binstruct.Unmarshal(dat[n:], &sc.Chunk)
n += _n
if err != nil {
return n, err
}
return n, nil
}
func (sb Superblock) ParseSysChunkArray() ([]SysChunk, error) {
dat := sb.SysChunkArray[:sb.SysChunkArraySize]
var ret []SysChunk
for len(dat) > 0 {
var pair SysChunk
n, err := binstruct.Unmarshal(dat, &pair)
dat = dat[n:]
if err != nil {
return nil, err
}
ret = append(ret, pair)
}
return ret, nil
}
type RootBackup struct {
TreeRoot ObjID `bin:"off=0x0, siz=0x8"`
TreeRootGen Generation `bin:"off=0x8, siz=0x8"`
ChunkRoot ObjID `bin:"off=0x10, siz=0x8"`
ChunkRootGen Generation `bin:"off=0x18, siz=0x8"`
ExtentRoot ObjID `bin:"off=0x20, siz=0x8"`
ExtentRootGen Generation `bin:"off=0x28, siz=0x8"`
FSRoot ObjID `bin:"off=0x30, siz=0x8"`
FSRootGen Generation `bin:"off=0x38, siz=0x8"`
DevRoot ObjID `bin:"off=0x40, siz=0x8"`
DevRootGen Generation `bin:"off=0x48, siz=0x8"`
ChecksumRoot ObjID `bin:"off=0x50, siz=0x8"`
ChecksumRootGen Generation `bin:"off=0x58, siz=0x8"`
TotalBytes uint64 `bin:"off=0x60, siz=0x8"`
BytesUsed uint64 `bin:"off=0x68, siz=0x8"`
NumDevices uint64 `bin:"off=0x70, siz=0x8"`
Unused [8 * 4]byte `bin:"off=0x78, siz=0x20"`
TreeRootLevel uint8 `bin:"off=0x98, siz=0x1"`
ChunkRootLevel uint8 `bin:"off=0x99, siz=0x1"`
ExtentRootLevel uint8 `bin:"off=0x9a, siz=0x1"`
FSRootLevel uint8 `bin:"off=0x9b, siz=0x1"`
DevRootLevel uint8 `bin:"off=0x9c, siz=0x1"`
ChecksumRootLevel uint8 `bin:"off=0x9d, siz=0x1"`
Padding [10]byte `bin:"off=0x9e, siz=0xa"`
binstruct.End `bin:"off=0xa8"`
}
type IncompatFlags uint64
const (
FeatureIncompatMixedBackref = IncompatFlags(1 << iota)
FeatureIncompatDefaultSubvol
FeatureIncompatMixedGroups
FeatureIncompatCompressLZO
FeatureIncompatCompressZSTD
FeatureIncompatBigMetadata // buggy
FeatureIncompatExtendedIRef
FeatureIncompatRAID56
FeatureIncompatSkinnyMetadata
FeatureIncompatNoHoles
FeatureIncompatMetadataUUID
FeatureIncompatRAID1C34
FeatureIncompatZoned
FeatureIncompatExtentTreeV2
)
var incompatFlagNames = []string{
"FeatureIncompatMixedBackref",
"FeatureIncompatDefaultSubvol",
"FeatureIncompatMixedGroups",
"FeatureIncompatCompressLZO",
"FeatureIncompatCompressZSTD",
"FeatureIncompatBigMetadata ",
"FeatureIncompatExtendedIRef",
"FeatureIncompatRAID56",
"FeatureIncompatSkinnyMetadata",
"FeatureIncompatNoHoles",
"FeatureIncompatMetadataUUID",
"FeatureIncompatRAID1C34",
"FeatureIncompatZoned",
"FeatureIncompatExtentTreeV2",
}
func (f IncompatFlags) Has(req IncompatFlags) bool { return f&req == req }
func (f IncompatFlags) String() string {
return fmtutil.BitfieldString(f, incompatFlagNames, fmtutil.HexLower)
}
|