summaryrefslogtreecommitdiff
path: root/lib/btrfs/types_superblock.go
blob: 68c5d560f891bb178794604f5be32029793f7e3c (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
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)
}