summaryrefslogtreecommitdiff
path: root/pkg/btrfs/structs.go
blob: 92db216655414a0f59d25e5f634ced4bafb1007d (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
package btrfs

import (
	"time"

	"lukeshu.com/btrfs-tools/pkg/binstruct"
)

type (
	PhysicalAddr int64
	LogicalAddr  int64
)

type Key struct {
	ObjectID      ObjID  `bin:"off=0, siz=8"` // Object ID. Each tree has its own set of Object IDs.
	ItemType      uint8  `bin:"off=8, siz=1"` // Item type.
	Offset        uint64 `bin:"off=9, siz=8"` // Offset. The meaning depends on the item type.
	binstruct.End `bin:"off=11"`
}

type Time struct {
	Sec           int64  `bin:"off=0, siz=8"` // Number of seconds since 1970-01-01T00:00:00Z.
	NSec          uint64 `bin:"off=8, siz=4"` // Number of nanoseconds since the beginning of the second.
	binstruct.End `bin:"off=c"`
}

func (t Time) ToStd() time.Time {
	return time.Unix(t.Sec, int64(t.NSec))
}

type Superblock struct {
	Checksum   CSum         `bin:"off=0,  siz=20"` // Checksum of everything past this field (from 20 to 1000)
	FSUUID     UUID         `bin:"off=20, siz=10"` // FS UUID
	Self       PhysicalAddr `bin:"off=30, siz=8"`  // physical address of this block (different for mirrors)
	Flags      uint64       `bin:"off=38, siz=8"`  // flags
	Magic      [8]byte      `bin:"off=40, siz=8"`  // magic ('_BHRfS_M')
	Generation uint64       `bin:"off=48, siz=8"`  // generation

	RootTree  LogicalAddr `bin:"off=50, siz=8"` // logical address of the root tree root
	ChunkTree LogicalAddr `bin:"off=58, siz=8"` // logical address of the chunk tree root
	LogTree   LogicalAddr `bin:"off=60, siz=8"` // logical address of the log tree root

	LogRootTransID  uint64 `bin:"off=68, siz=8"` // log_root_transid
	TotalBytes      uint64 `bin:"off=70, siz=8"` // total_bytes
	BytesUsed       uint64 `bin:"off=78, siz=8"` // bytes_used
	RootDirObjectID ObjID  `bin:"off=80, siz=8"` // root_dir_objectid (usually 6)
	NumDevices      uint64 `bin:"off=88, siz=8"` // num_devices

	SectorSize        uint32 `bin:"off=90, siz=4"` // sectorsize
	NodeSize          uint32 `bin:"off=94, siz=4"` // nodesize
	LeafSize          uint32 `bin:"off=98, siz=4"` // leafsize
	StripeSize        uint32 `bin:"off=9c, siz=4"` // stripesize
	SysChunkArraySize uint32 `bin:"off=a0, siz=4"` // sys_chunk_array_size

	ChunkRootGeneration uint64        `bin:"off=a4, siz=8"` // chunk_root_generation
	CompatFlags         uint64        `bin:"off=ac, siz=8"` // compat_flags
	CompatROFlags       uint64        `bin:"off=b4, siz=8"` // compat_ro_flags - only implementations that support the flags can write to the filesystem
	IncompatFlags       IncompatFlags `bin:"off=bc, siz=8"` // incompat_flags - only implementations that support the flags can use the filesystem
	ChecksumType        uint16        `bin:"off=c4, siz=2"` // csum_type - Btrfs currently uses the CRC32c little-endian hash function with seed -1.

	RootLevel  uint8 `bin:"off=c6, siz=1"` // root_level
	ChunkLevel uint8 `bin:"off=c7, siz=1"` // chunk_root_level
	LogLevel   uint8 `bin:"off=c8, siz=1"` // log_root_level

	DevItem            DevItem     `bin:"off=c9,  siz=62"`  // DEV_ITEM data for this device
	Label              [0x100]byte `bin:"off=12b, siz=100"` // label (may not contain '/' or '\\')
	CacheGeneration    uint64      `bin:"off=22b, siz=8"`   // cache_generation
	UUIDTreeGeneration uint64      `bin:"off=233, siz=8"`   // uuid_tree_generation

	// FeatureIncompatMetadataUUID
	MetadataUUID UUID `bin:"off=23b, siz=10"`

	// FeatureIncompatExtentTreeV2
	NumGlobalRoots uint64 `bin:"off=24b, siz=8"`

	// FeatureIncompatExtentTreeV2
	BlockGroupRoot           uint64 `bin:"off=253, siz=8"`
	BlockGroupRootGeneration uint64 `bin:"off=25b, siz=8"`
	BlockGroupRootLevel      uint8  `bin:"off=263, siz=1"`

	Reserved [199]byte `bin:"off=264, siz=c7"` // future expansion

	SysChunkArray  [0x800]byte `bin:"off=32b, siz=800"` // 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.
	TODOSuperRoots [0x2a0]byte `bin:"off=b2b, siz=2a0"` // Contain super_roots (4 btrfs_root_backup)

	// Padded to 4096 bytes
	Padding       [565]byte `bin:"off=dcb, siz=235"`
	binstruct.End `bin:"off=1000"`
}

func (sb Superblock) CalculateChecksum() (CSum, error) {
	data, err := binstruct.Marshal(sb)
	if err != nil {
		return CSum{}, err
	}
	return CRC32c(data[0x20:]), nil
}

func (sb Superblock) EffectiveMetadataUUID() UUID {
	if !sb.IncompatFlags.Has(FeatureIncompatMetadataUUID) {
		return sb.FSUUID
	}
	return sb.MetadataUUID
}

type SysChunk struct {
	Key           `bin:"off=0, siz=11"`
	ChunkItem     `bin:"off=11, siz=30"`
	binstruct.End `bin:"off=41"`
}

func (sb Superblock) ParseSysChunkArray() ([]SysChunk, error) {
	dat := sb.SysChunkArray[:sb.SysChunkArraySize]
	var ret []SysChunk
	for len(dat) > 0 {
		var pair SysChunk
		if err := binstruct.Unmarshal(dat, &pair); err != nil {
			return nil, err
		}
		dat = dat[0x41:]

		for i := 0; i < int(pair.ChunkItem.NumStripes); i++ {
			var stripe ChunkItemStripe
			if err := binstruct.Unmarshal(dat, &stripe); err != nil {
				return nil, err
			}
			pair.ChunkItem.Stripes = append(pair.ChunkItem.Stripes, stripe)
			dat = dat[0x20:]
		}

		ret = append(ret, pair)
	}
	return ret, nil
}

type NodeHeader struct {
	Checksum      CSum        `bin:"off=0,  siz=20"` // Checksum of everything after this field (from 20 to the end of the node)
	MetadataUUID  UUID        `bin:"off=20, siz=10"` // FS UUID
	Addr          LogicalAddr `bin:"off=30, siz=8"`  // Logical address of this node
	Flags         uint64      `bin:"off=38, siz=8"`  // Flags
	ChunkTreeUUID UUID        `bin:"off=40, siz=10"` // Chunk tree UUID
	Generation    uint64      `bin:"off=50, siz=8"`  // Generation
	OwnerTree     TreeObjID   `bin:"off=58, siz=8"`  // The ID of the tree that contains this node
	NumItems      uint32      `bin:"off=60, siz=4"`  // Number of items
	Level         uint8       `bin:"off=64, siz=1"`  // Level (0 for leaf nodes)
	binstruct.End `bin:"off=65"`
}

type InternalNode struct {
	NodeHeader
	Body []KeyPointer
}

type KeyPointer struct {
	Key           Key    `bin:"off=0, siz=11"`
	BlockNumber   uint64 `bin:"off=11, siz=8"`
	Generation    uint64 `bin:"off=19, siz=8"`
	binstruct.End `bin:"off=21"`
}

type LeafNode struct {
	NodeHeader
	Body []Item
}

type Item struct {
	Key           Key    `bin:"off=0, siz=11"`
	DataOffset    uint32 `bin:"off=11, siz=4"` // relative to the end of the header (0x65)
	DataSize      uint32 `bin:"off=15, siz=4"`
	binstruct.End `bin:"off=19"`
}

type DevItem struct {
	DeviceID ObjID `bin:"off=0,    siz=8"` // device ID

	NumBytes     uint64 `bin:"off=8,    siz=8"` // number of bytes
	NumBytesUsed uint64 `bin:"off=10,   siz=8"` // number of bytes used

	IOOptimalAlign uint32 `bin:"off=18,   siz=4"` // optimal I/O align
	IOOptimalWidth uint32 `bin:"off=1c,   siz=4"` // optimal I/O width
	IOMinSize      uint32 `bin:"off=20,   siz=4"` // minimal I/O size (sector size)

	Type        uint64 `bin:"off=24,   siz=8"` // type
	Generation  uint64 `bin:"off=2c,   siz=8"` // generation
	StartOffset uint64 `bin:"off=34,   siz=8"` // start offset
	DevGroup    uint32 `bin:"off=3c,   siz=4"` // dev group
	SeekSpeed   uint8  `bin:"off=40,   siz=1"` // seek speed
	Bandwidth   uint8  `bin:"off=41,   siz=1"` // bandwidth

	DevUUID UUID `bin:"off=42,   siz=10"` // device UUID
	FSUUID  UUID `bin:"off=52,   siz=10"` // FS UUID

	binstruct.End `bin:"off=62"`
}

type ChunkItem struct {
	// Maps logical address to physical.
	Size           uint64 `bin:"off=0,  siz=8"` // size of chunk (bytes)
	Root           ObjID  `bin:"off=8,  siz=8"` // root referencing this chunk (2)
	StripeLen      uint64 `bin:"off=10, siz=8"` // stripe length
	Type           uint64 `bin:"off=18, siz=8"` // type (same as flags for block group?)
	IOOptimalAlign uint32 `bin:"off=20, siz=4"` // optimal io alignment
	IOOptimalWidth uint32 `bin:"off=24, siz=4"` // optimal io width
	IoMinSize      uint32 `bin:"off=28, siz=4"` // minimal io size (sector size)
	NumStripes     uint16 `bin:"off=2c, siz=2"` // number of stripes
	SubStripes     uint16 `bin:"off=2e, siz=2"` // sub stripes
	binstruct.End  `bin:"off=30"`
	Stripes        []ChunkItemStripe `bin:"-"`
}

type ChunkItemStripe struct {
	// Stripes follow (for each number of stripes):
	DeviceID      ObjID  `bin:"off=0,  siz=8"`  // device ID
	Offset        uint64 `bin:"off=8,  siz=8"`  // offset
	DeviceUUID    UUID   `bin:"off=10, siz=10"` // device UUID
	binstruct.End `bin:"off=20"`
}