1use crate::{
13 raw::{
14 BTRFS_BLOCK_GROUP_ITEM_KEY, BTRFS_CHUNK_ITEM_KEY,
15 BTRFS_CHUNK_TREE_OBJECTID, BTRFS_EXTENT_TREE_OBJECTID,
16 BTRFS_FIRST_CHUNK_TREE_OBJECTID,
17 },
18 space::BlockGroupFlags,
19 tree_search::{Key, SearchFilter, tree_search},
20};
21use btrfs_disk::items::ChunkItem;
22use std::os::unix::io::BorrowedFd;
23
24#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct DeviceAllocation {
32 pub devid: u64,
34 pub flags: BlockGroupFlags,
37 pub bytes: u64,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ChunkEntry {
49 pub devid: u64,
51 pub physical_start: u64,
53 pub logical_start: u64,
55 pub length: u64,
58 pub flags: BlockGroupFlags,
60 pub used: u64,
63}
64
65pub fn device_chunk_allocations(
82 fd: BorrowedFd,
83) -> nix::Result<Vec<DeviceAllocation>> {
84 let mut allocs: Vec<DeviceAllocation> = Vec::new();
85
86 tree_search(
87 fd,
88 SearchFilter::for_type(
89 u64::from(BTRFS_CHUNK_TREE_OBJECTID),
90 BTRFS_CHUNK_ITEM_KEY,
91 ),
92 |_hdr, data| {
93 if let Some((stripe_len, flags, stripes)) = parse_chunk(data) {
94 for devid in stripes {
95 accumulate(&mut allocs, devid, flags, stripe_len);
96 }
97 }
98 Ok(())
99 },
100 )?;
101
102 Ok(allocs)
103}
104
105pub fn chunk_list(fd: BorrowedFd) -> nix::Result<Vec<ChunkEntry>> {
119 let mut entries: Vec<ChunkEntry> = Vec::new();
120
121 tree_search(
122 fd,
123 SearchFilter::for_objectid_range(
124 u64::from(BTRFS_CHUNK_TREE_OBJECTID),
125 BTRFS_CHUNK_ITEM_KEY,
126 u64::from(BTRFS_FIRST_CHUNK_TREE_OBJECTID),
127 u64::from(BTRFS_FIRST_CHUNK_TREE_OBJECTID),
128 ),
129 |hdr, data| {
130 if let Some(chunk) = ChunkItem::parse(data) {
131 let logical_start = hdr.offset;
132 let flags = BlockGroupFlags::from_bits_truncate(
133 chunk.chunk_type.bits(),
134 );
135 let used = block_group_used(fd, logical_start).unwrap_or(0);
136 for stripe in &chunk.stripes {
137 entries.push(ChunkEntry {
138 devid: stripe.devid,
139 physical_start: stripe.offset,
140 logical_start,
141 length: chunk.length,
142 flags,
143 used,
144 });
145 }
146 }
147 Ok(())
148 },
149 )?;
150
151 Ok(entries)
152}
153
154fn block_group_used(fd: BorrowedFd, logical_start: u64) -> Option<u64> {
159 let mut used: Option<u64> = None;
160 tree_search(
161 fd,
162 SearchFilter {
163 tree_id: u64::from(BTRFS_EXTENT_TREE_OBJECTID),
164 start: Key {
165 objectid: logical_start,
166 item_type: BTRFS_BLOCK_GROUP_ITEM_KEY,
167 offset: 0,
168 },
169 end: Key {
170 objectid: logical_start,
171 item_type: BTRFS_BLOCK_GROUP_ITEM_KEY,
172 offset: u64::MAX,
173 },
174 min_transid: 0,
175 max_transid: u64::MAX,
176 },
177 |_hdr, data| {
178 if let Some(bg) = btrfs_disk::items::BlockGroupItem::parse(data) {
179 used = Some(bg.used);
180 }
181 Ok(())
182 },
183 )
184 .ok()?;
185 used
186}
187
188fn parse_chunk(data: &[u8]) -> Option<(u64, BlockGroupFlags, Vec<u64>)> {
193 let chunk = ChunkItem::parse(data)?;
194 let flags = BlockGroupFlags::from_bits_truncate(chunk.chunk_type.bits());
195 let devids: Vec<u64> = chunk.stripes.iter().map(|s| s.devid).collect();
196 Some((chunk.stripe_len, flags, devids))
197}
198
199fn accumulate(
202 allocs: &mut Vec<DeviceAllocation>,
203 devid: u64,
204 flags: BlockGroupFlags,
205 bytes: u64,
206) {
207 if let Some(entry) = allocs
208 .iter_mut()
209 .find(|a| a.devid == devid && a.flags == flags)
210 {
211 entry.bytes += bytes;
212 } else {
213 allocs.push(DeviceAllocation {
214 devid,
215 flags,
216 bytes,
217 });
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 fn build_chunk_buf(
228 length: u64,
229 stripe_len: u64,
230 type_bits: u64,
231 num_stripes: u16,
232 stripes: &[(u64, u64)], ) -> Vec<u8> {
234 let mut buf = Vec::new();
235 buf.extend_from_slice(&length.to_le_bytes());
236 buf.extend_from_slice(&0u64.to_le_bytes()); buf.extend_from_slice(&stripe_len.to_le_bytes());
238 buf.extend_from_slice(&type_bits.to_le_bytes()); buf.extend_from_slice(&4096u32.to_le_bytes()); buf.extend_from_slice(&4096u32.to_le_bytes()); buf.extend_from_slice(&4096u32.to_le_bytes()); buf.extend_from_slice(&num_stripes.to_le_bytes());
243 buf.extend_from_slice(&0u16.to_le_bytes()); for &(devid, offset) in stripes {
245 buf.extend_from_slice(&devid.to_le_bytes());
246 buf.extend_from_slice(&offset.to_le_bytes());
247 buf.extend_from_slice(&[0u8; 16]); }
249 buf
250 }
251
252 #[test]
253 fn parse_chunk_single_stripe() {
254 let data_flags = BlockGroupFlags::DATA.bits();
255 let buf = build_chunk_buf(1024 * 1024, 65536, data_flags, 1, &[(1, 0)]);
256 let (stripe_len, flags, devids) = parse_chunk(&buf).unwrap();
257 assert_eq!(stripe_len, 65536);
258 assert_eq!(flags, BlockGroupFlags::DATA);
259 assert_eq!(devids, vec![1]);
260 }
261
262 #[test]
263 fn parse_chunk_two_stripes() {
264 let flags_bits =
265 (BlockGroupFlags::DATA | BlockGroupFlags::RAID1).bits();
266 let buf = build_chunk_buf(
267 1 << 30,
268 1 << 30,
269 flags_bits,
270 2,
271 &[(1, 0), (2, 4096)],
272 );
273 let (_, flags, devids) = parse_chunk(&buf).unwrap();
274 assert_eq!(flags, BlockGroupFlags::DATA | BlockGroupFlags::RAID1);
275 assert_eq!(devids, vec![1, 2]);
276 }
277
278 #[test]
279 fn parse_chunk_too_short() {
280 let buf = vec![0u8; 10];
281 assert!(parse_chunk(&buf).is_none());
282 }
283
284 #[test]
285 fn parse_chunk_claims_more_stripes_than_fit() {
286 let buf = build_chunk_buf(1024, 1024, 0, 5, &[(1, 0)]);
288 let result = parse_chunk(&buf);
290 assert!(result.is_some());
291 let (_, _, devids) = result.unwrap();
292 assert_eq!(devids.len(), 1);
293 }
294
295 #[test]
298 fn accumulate_new_entry() {
299 let mut allocs = Vec::new();
300 accumulate(&mut allocs, 1, BlockGroupFlags::DATA, 1000);
301 assert_eq!(allocs.len(), 1);
302 assert_eq!(allocs[0].devid, 1);
303 assert_eq!(allocs[0].bytes, 1000);
304 }
305
306 #[test]
307 fn accumulate_merge_same_devid_flags() {
308 let mut allocs = Vec::new();
309 accumulate(&mut allocs, 1, BlockGroupFlags::DATA, 1000);
310 accumulate(&mut allocs, 1, BlockGroupFlags::DATA, 2000);
311 assert_eq!(allocs.len(), 1);
312 assert_eq!(allocs[0].bytes, 3000);
313 }
314
315 #[test]
316 fn accumulate_separate_different_flags() {
317 let mut allocs = Vec::new();
318 accumulate(&mut allocs, 1, BlockGroupFlags::DATA, 1000);
319 accumulate(&mut allocs, 1, BlockGroupFlags::METADATA, 2000);
320 assert_eq!(allocs.len(), 2);
321 }
322
323 #[test]
324 fn accumulate_separate_different_devids() {
325 let mut allocs = Vec::new();
326 accumulate(&mut allocs, 1, BlockGroupFlags::DATA, 1000);
327 accumulate(&mut allocs, 2, BlockGroupFlags::DATA, 2000);
328 assert_eq!(allocs.len(), 2);
329 }
330}