2 * btrfs.c -- readonly btrfs support for syslinux
3 * Some data structures are derivated from btrfs-tools-0.19 ctree.h
4 * Copyright 2009 Intel Corporation; author: alek.du@intel.com
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
24 /* compare function used for bin_search */
25 typedef int (*cmp_func
)(void *ptr1
, void *ptr2
);
27 /* simple but useful bin search, used for chunk search and btree search */
28 static int bin_search(void *ptr
, int item_size
, void *cmp_item
, cmp_func func
,
29 int min
, int max
, int *slot
)
39 mid
= (low
+ high
) / 2;
40 offset
= mid
* item_size
;
43 ret
= func(item
, cmp_item
);
58 /* XXX: these should go into the filesystem instance structure */
59 static struct btrfs_chunk_map chunk_map
;
60 static struct btrfs_super_block sb
;
63 static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item
*m1
,
64 struct btrfs_chunk_map_item
*m2
)
66 if (m1
->logical
> m2
->logical
)
68 if (m1
->logical
< m2
->logical
)
73 /* insert a new chunk mapping item */
74 static void insert_map(struct btrfs_chunk_map_item
*item
)
80 if (chunk_map
.map
== NULL
) { /* first item */
81 chunk_map
.map_length
= BTRFS_MAX_CHUNK_ENTRIES
;
82 chunk_map
.map
= (struct btrfs_chunk_map_item
*)
83 malloc(chunk_map
.map_length
* sizeof(*chunk_map
.map
));
84 chunk_map
.map
[0] = *item
;
85 chunk_map
.cur_length
= 1;
88 ret
= bin_search(chunk_map
.map
, sizeof(*item
), item
,
89 (cmp_func
)btrfs_comp_chunk_map
, 0,
90 chunk_map
.cur_length
, &slot
);
91 if (ret
== 0)/* already in map */
93 if (chunk_map
.cur_length
== BTRFS_MAX_CHUNK_ENTRIES
) {
94 /* should be impossible */
95 printf("too many chunk items\n");
98 for (i
= chunk_map
.cur_length
; i
> slot
; i
--)
99 chunk_map
.map
[i
] = chunk_map
.map
[i
-1];
100 chunk_map
.map
[slot
] = *item
;
101 chunk_map
.cur_length
++;
105 * from sys_chunk_array or chunk_tree, we can convert a logical address to
106 * a physical address we can not support multi device case yet
108 static u64
logical_physical(u64 logical
)
110 struct btrfs_chunk_map_item item
;
113 item
.logical
= logical
;
114 ret
= bin_search(chunk_map
.map
, sizeof(*chunk_map
.map
), &item
,
115 (cmp_func
)btrfs_comp_chunk_map
, 0,
116 chunk_map
.cur_length
, &slot
);
122 chunk_map
.map
[slot
-1].logical
+ chunk_map
.map
[slot
-1].length
)
124 return chunk_map
.map
[slot
-1].physical
+ logical
-
125 chunk_map
.map
[slot
-1].logical
;
128 /* cache read from disk, offset and count are bytes */
129 static int btrfs_read(struct fs_info
*fs
, char *buf
, u64 offset
, u64 count
)
132 size_t block_size
= fs
->fs_dev
->cache_block_size
;
133 size_t off
, cnt
, total
;
138 block
= offset
/ block_size
;
139 off
= offset
% block_size
;
140 cd
= get_cache(fs
->fs_dev
, block
);
143 cnt
= block_size
- off
;
146 memcpy(buf
, cd
+ off
, cnt
);
151 return total
- count
;
154 /* btrfs has several super block mirrors, need to calculate their location */
155 static inline u64
btrfs_sb_offset(int mirror
)
157 u64 start
= 16 * 1024;
159 return start
<< (BTRFS_SUPER_MIRROR_SHIFT
* mirror
);
160 return BTRFS_SUPER_INFO_OFFSET
;
163 /* find the most recent super block */
164 static void btrfs_read_super_block(struct fs_info
*fs
)
168 u8 fsid
[BTRFS_FSID_SIZE
];
171 struct btrfs_super_block buf
;
173 sb
.total_bytes
= ~0; /* Unknown as of yet */
175 /* find most recent super block */
176 for (i
= 0; i
< BTRFS_SUPER_MIRROR_MAX
; i
++) {
177 offset
= btrfs_sb_offset(i
);
178 dprintf("btrfs super: %llu max %llu\n",
179 offset
, sb
.total_bytes
);
180 if (offset
>= sb
.total_bytes
)
183 ret
= btrfs_read(fs
, (char *)&buf
, offset
, sizeof(buf
));
184 if (ret
< sizeof(buf
))
187 if (buf
.bytenr
!= offset
||
188 strncmp((char *)(&buf
.magic
), BTRFS_MAGIC
,
193 memcpy(fsid
, buf
.fsid
, sizeof(fsid
));
194 else if (memcmp(fsid
, buf
.fsid
, sizeof(fsid
)))
197 if (buf
.generation
> transid
) {
198 memcpy(&sb
, &buf
, sizeof(sb
));
199 transid
= buf
.generation
;
204 static inline unsigned long btrfs_chunk_item_size(int num_stripes
)
206 return sizeof(struct btrfs_chunk
) +
207 sizeof(struct btrfs_stripe
) * (num_stripes
- 1);
210 static void clear_path(struct btrfs_path
*path
)
212 memset(path
, 0, sizeof(*path
));
215 static int btrfs_comp_keys(struct btrfs_disk_key
*k1
, struct btrfs_disk_key
*k2
)
217 if (k1
->objectid
> k2
->objectid
)
219 if (k1
->objectid
< k2
->objectid
)
221 if (k1
->type
> k2
->type
)
223 if (k1
->type
< k2
->type
)
225 if (k1
->offset
> k2
->offset
)
227 if (k1
->offset
< k2
->offset
)
232 /* compare keys but ignore offset, is useful to enumerate all same kind keys */
233 static int btrfs_comp_keys_type(struct btrfs_disk_key
*k1
,
234 struct btrfs_disk_key
*k2
)
236 if (k1
->objectid
> k2
->objectid
)
238 if (k1
->objectid
< k2
->objectid
)
240 if (k1
->type
> k2
->type
)
242 if (k1
->type
< k2
->type
)
247 /* seach tree directly on disk ... */
248 static int search_tree(struct fs_info
*fs
, u64 loffset
,
249 struct btrfs_disk_key
*key
, struct btrfs_path
*path
)
251 u8 buf
[BTRFS_MAX_LEAF_SIZE
];
252 struct btrfs_header
*header
= (struct btrfs_header
*)buf
;
253 struct btrfs_node
*node
= (struct btrfs_node
*)buf
;
254 struct btrfs_leaf
*leaf
= (struct btrfs_leaf
*)buf
;
258 offset
= logical_physical(loffset
);
259 btrfs_read(fs
, (char *)header
, offset
, sizeof(*header
));
260 if (header
->level
) {/*node*/
261 btrfs_read(fs
, (char *)&node
->ptrs
[0], offset
+ sizeof(*header
),
262 sb
.nodesize
- sizeof(*header
));
263 path
->itemsnr
[header
->level
] = header
->nritems
;
264 path
->offsets
[header
->level
] = loffset
;
265 ret
= bin_search(&node
->ptrs
[0], sizeof(struct btrfs_key_ptr
),
266 key
, (cmp_func
)btrfs_comp_keys
,
267 path
->slots
[header
->level
], header
->nritems
, &slot
);
268 if (ret
&& slot
> path
->slots
[header
->level
])
270 path
->slots
[header
->level
] = slot
;
271 ret
= search_tree(fs
, node
->ptrs
[slot
].blockptr
, key
, path
);
273 btrfs_read(fs
, (char *)&leaf
->items
, offset
+ sizeof(*header
),
274 sb
.leafsize
- sizeof(*header
));
275 path
->itemsnr
[header
->level
] = header
->nritems
;
276 path
->offsets
[0] = loffset
;
277 ret
= bin_search(&leaf
->items
[0], sizeof(struct btrfs_item
),
278 key
, (cmp_func
)btrfs_comp_keys
, path
->slots
[0],
279 header
->nritems
, &slot
);
280 if (ret
&& slot
> path
->slots
[header
->level
])
282 path
->slots
[0] = slot
;
283 path
->item
= leaf
->items
[slot
];
284 btrfs_read(fs
, (char *)&path
->data
,
285 offset
+ sizeof(*header
) + leaf
->items
[slot
].offset
,
286 leaf
->items
[slot
].size
);
291 /* return 0 if leaf found */
292 static int next_leaf(struct fs_info
*fs
, struct btrfs_disk_key
*key
, struct btrfs_path
*path
)
297 while (level
< BTRFS_MAX_LEVEL
) {
298 if (!path
->itemsnr
[level
]) /* no more nodes */
300 slot
= path
->slots
[level
] + 1;
301 if (slot
>= path
->itemsnr
[level
]) {
305 path
->slots
[level
] = slot
;
306 path
->slots
[level
-1] = 0; /* reset low level slots info */
307 search_tree(fs
, path
->offsets
[level
], key
, path
);
310 if (level
== BTRFS_MAX_LEVEL
)
315 /* return 0 if slot found */
316 static int next_slot(struct fs_info
*fs
, struct btrfs_disk_key
*key
, struct btrfs_path
*path
)
320 if (!path
->itemsnr
[0])
322 slot
= path
->slots
[0] + 1;
323 if (slot
>= path
->itemsnr
[0])
325 path
->slots
[0] = slot
;
326 search_tree(fs
, path
->offsets
[0], key
, path
);
331 * read chunk_array in super block
333 static void btrfs_read_sys_chunk_array(void)
335 struct btrfs_chunk_map_item item
;
336 struct btrfs_disk_key
*key
;
337 struct btrfs_chunk
*chunk
;
340 /* read chunk array in superblock */
342 while (cur
< sb
.sys_chunk_array_size
) {
343 key
= (struct btrfs_disk_key
*)(sb
.sys_chunk_array
+ cur
);
345 chunk
= (struct btrfs_chunk
*)(sb
.sys_chunk_array
+ cur
);
346 cur
+= btrfs_chunk_item_size(chunk
->num_stripes
);
347 /* insert to mapping table, ignore multi stripes */
348 item
.logical
= key
->offset
;
349 item
.length
= chunk
->length
;
350 item
.devid
= chunk
->stripe
.devid
;
351 item
.physical
= chunk
->stripe
.offset
;/*ignore other stripes */
356 /* read chunk items from chunk_tree and insert them to chunk map */
357 static void btrfs_read_chunk_tree(struct fs_info
*fs
)
359 struct btrfs_disk_key search_key
;
360 struct btrfs_chunk
*chunk
;
361 struct btrfs_chunk_map_item item
;
362 struct btrfs_path path
;
364 if (!(sb
.flags
& BTRFS_SUPER_FLAG_METADUMP
)) {
365 if (sb
.num_devices
> 1)
366 printf("warning: only support single device btrfs\n");
367 /* read chunk from chunk_tree */
368 search_key
.objectid
= BTRFS_FIRST_CHUNK_TREE_OBJECTID
;
369 search_key
.type
= BTRFS_CHUNK_ITEM_KEY
;
370 search_key
.offset
= 0;
372 search_tree(fs
, sb
.chunk_root
, &search_key
, &path
);
375 if (btrfs_comp_keys_type(&search_key
,
378 chunk
= (struct btrfs_chunk
*)(path
.data
);
379 /* insert to mapping table, ignore stripes */
380 item
.logical
= path
.item
.key
.offset
;
381 item
.length
= chunk
->length
;
382 item
.devid
= chunk
->stripe
.devid
;
383 item
.physical
= chunk
->stripe
.offset
;
385 } while (!next_slot(fs
, &search_key
, &path
));
386 if (btrfs_comp_keys_type(&search_key
, &path
.item
.key
))
388 } while (!next_leaf(fs
, &search_key
, &path
));
392 static inline u64
btrfs_name_hash(const char *name
, int len
)
394 return btrfs_crc32c((u32
)~1, name
, len
);
397 static struct inode
*btrfs_iget_by_inr(struct fs_info
*fs
, u64 inr
)
400 struct btrfs_inode_item inode_item
;
401 struct btrfs_disk_key search_key
;
402 struct btrfs_path path
;
405 /* FIXME: some BTRFS inode member are u64, while our logical inode
406 is u32, we may need change them to u64 later */
407 search_key
.objectid
= inr
;
408 search_key
.type
= BTRFS_INODE_ITEM_KEY
;
409 search_key
.offset
= 0;
411 ret
= search_tree(fs
, fs_tree
, &search_key
, &path
);
414 inode_item
= *(struct btrfs_inode_item
*)path
.data
;
415 if (!(inode
= alloc_inode(fs
, inr
, sizeof(struct btrfs_pvt_inode
))))
418 inode
->size
= inode_item
.size
;
419 inode
->mode
= IFTODT(inode_item
.mode
);
421 if (inode
->mode
== DT_REG
|| inode
->mode
== DT_LNK
) {
422 struct btrfs_file_extent_item extent_item
;
425 /* get file_extent_item */
426 search_key
.type
= BTRFS_EXTENT_DATA_KEY
;
427 search_key
.offset
= 0;
429 ret
= search_tree(fs
, fs_tree
, &search_key
, &path
);
431 return NULL
; /* impossible */
432 extent_item
= *(struct btrfs_file_extent_item
*)path
.data
;
433 if (extent_item
.type
== BTRFS_FILE_EXTENT_INLINE
)/* inline file */
434 offset
= path
.offsets
[0] + sizeof(struct btrfs_header
)
436 + offsetof(struct btrfs_file_extent_item
, disk_bytenr
);
438 offset
= extent_item
.disk_bytenr
;
439 PVT(inode
)->offset
= offset
;
444 static struct inode
*btrfs_iget_root(struct fs_info
*fs
)
446 /* BTRFS_FIRST_CHUNK_TREE_OBJECTID(256) actually is first OBJECTID for FS_TREE */
447 return btrfs_iget_by_inr(fs
, BTRFS_FIRST_CHUNK_TREE_OBJECTID
);
450 static struct inode
*btrfs_iget(const char *name
, struct inode
*parent
)
452 struct fs_info
*fs
= parent
->fs
;
453 struct btrfs_disk_key search_key
;
454 struct btrfs_path path
;
455 struct btrfs_dir_item dir_item
;
458 search_key
.objectid
= parent
->ino
;
459 search_key
.type
= BTRFS_DIR_ITEM_KEY
;
460 search_key
.offset
= btrfs_name_hash(name
, strlen(name
));
462 ret
= search_tree(fs
, fs_tree
, &search_key
, &path
);
465 dir_item
= *(struct btrfs_dir_item
*)path
.data
;
467 return btrfs_iget_by_inr(fs
, dir_item
.location
.objectid
);
470 static int btrfs_readlink(struct inode
*inode
, char *buf
)
472 btrfs_read(inode
->fs
, buf
, logical_physical(PVT(inode
)->offset
), inode
->size
);
473 buf
[inode
->size
] = '\0';
477 static int btrfs_readdir(struct file
*file
, struct dirent
*dirent
)
479 struct fs_info
*fs
= file
->fs
;
480 struct inode
*inode
= file
->inode
;
481 struct btrfs_disk_key search_key
;
482 struct btrfs_path path
;
483 struct btrfs_dir_item
*dir_item
;
487 * we use file->offset to store last search key.offset, will will search
488 * key that lower that offset, 0 means first search and we will search
489 * -1UL, which is the biggest possible key
491 search_key
.objectid
= inode
->ino
;
492 search_key
.type
= BTRFS_DIR_ITEM_KEY
;
493 search_key
.offset
= file
->offset
- 1;
495 ret
= search_tree(fs
, fs_tree
, &search_key
, &path
);
498 if (btrfs_comp_keys_type(&search_key
, &path
.item
.key
))
502 dir_item
= (struct btrfs_dir_item
*)path
.data
;
503 file
->offset
= path
.item
.key
.offset
;
504 dirent
->d_ino
= dir_item
->location
.objectid
;
505 dirent
->d_off
= file
->offset
;
506 dirent
->d_reclen
= offsetof(struct dirent
, d_name
)
507 + dir_item
->name_len
+ 1;
508 dirent
->d_type
= IFTODT(dir_item
->type
);
509 memcpy(dirent
->d_name
, dir_item
+ 1, dir_item
->name_len
);
510 dirent
->d_name
[dir_item
->name_len
] = '\0';
515 static int btrfs_next_extent(struct inode
*inode
, uint32_t lstart
)
517 struct btrfs_disk_key search_key
;
518 struct btrfs_file_extent_item extent_item
;
519 struct btrfs_path path
;
522 struct fs_info
*fs
= inode
->fs
;
523 u32 sec_shift
= SECTOR_SHIFT(fs
);
524 u32 sec_size
= SECTOR_SIZE(fs
);
526 search_key
.objectid
= inode
->ino
;
527 search_key
.type
= BTRFS_EXTENT_DATA_KEY
;
528 search_key
.offset
= lstart
<< sec_shift
;
530 ret
= search_tree(fs
, fs_tree
, &search_key
, &path
);
531 if (ret
) { /* impossible */
532 printf("btrfs: search extent data error!\n");
535 extent_item
= *(struct btrfs_file_extent_item
*)path
.data
;
537 if (extent_item
.encryption
) {
538 printf("btrfs: found encrypted data, cannot continue!\n");
541 if (extent_item
.compression
) {
542 printf("btrfs: found compressed data, cannot continue!\n");
546 if (extent_item
.type
== BTRFS_FILE_EXTENT_INLINE
) {/* inline file */
547 /* we fake a extent here, and PVT of inode will tell us */
548 offset
= path
.offsets
[0] + sizeof(struct btrfs_header
)
550 + offsetof(struct btrfs_file_extent_item
, disk_bytenr
);
551 inode
->next_extent
.len
=
552 (inode
->size
+ sec_size
-1) >> sec_shift
;
554 offset
= extent_item
.disk_bytenr
+ extent_item
.offset
;
555 inode
->next_extent
.len
=
556 (extent_item
.num_bytes
+ sec_size
- 1) >> sec_shift
;
558 inode
->next_extent
.pstart
=
559 logical_physical(offset
) >> sec_shift
;
560 PVT(inode
)->offset
= offset
;
564 static uint32_t btrfs_getfssec(struct file
*file
, char *buf
, int sectors
,
568 struct fs_info
*fs
= file
->fs
;
569 u32 off
= PVT(file
->inode
)->offset
% SECTOR_SIZE(fs
);
570 bool handle_inline
= false;
572 if (off
&& !file
->offset
) {/* inline file first read patch */
573 file
->inode
->size
+= off
;
574 handle_inline
= true;
576 ret
= generic_getfssec(file
, buf
, sectors
, have_more
);
579 off
= PVT(file
->inode
)->offset
% SECTOR_SIZE(fs
);
580 if (handle_inline
) {/* inline file patch */
582 memcpy(buf
, buf
+ off
, ret
);
587 static void btrfs_get_fs_tree(struct fs_info
*fs
)
589 struct btrfs_disk_key search_key
;
590 struct btrfs_path path
;
591 struct btrfs_root_item
*tree
;
592 bool subvol_ok
= false;
594 /* check if subvol is filled by installer */
596 search_key
.objectid
= BTRFS_FS_TREE_OBJECTID
;
597 search_key
.type
= BTRFS_ROOT_REF_KEY
;
598 search_key
.offset
= 0;
600 if (search_tree(fs
, sb
.root
, &search_key
, &path
))
601 next_slot(fs
, &search_key
, &path
);
604 struct btrfs_root_ref
*ref
;
606 if (btrfs_comp_keys_type(&search_key
,
609 ref
= (struct btrfs_root_ref
*)path
.data
;
610 if (!strcmp((char*)(ref
+ 1), SubvolName
)) {
614 } while (!next_slot(fs
, &search_key
, &path
));
617 if (btrfs_comp_keys_type(&search_key
, &path
.item
.key
))
619 } while (!next_leaf(fs
, &search_key
, &path
));
620 if (!subvol_ok
) /* should be impossible */
621 printf("no subvol found!\n");
623 /* find fs_tree from tree_root */
625 search_key
.objectid
= path
.item
.key
.offset
;
626 else /* "default" volume */
627 search_key
.objectid
= BTRFS_FS_TREE_OBJECTID
;
628 search_key
.type
= BTRFS_ROOT_ITEM_KEY
;
629 search_key
.offset
= -1;
631 search_tree(fs
, sb
.root
, &search_key
, &path
);
632 tree
= (struct btrfs_root_item
*)path
.data
;
633 fs_tree
= tree
->bytenr
;
636 /* init. the fs meta data, return the block size shift bits. */
637 static int btrfs_fs_init(struct fs_info
*fs
)
639 struct disk
*disk
= fs
->fs_dev
->disk
;
643 fs
->sector_shift
= disk
->sector_shift
;
644 fs
->sector_size
= 1 << fs
->sector_shift
;
645 fs
->block_shift
= BTRFS_BLOCK_SHIFT
;
646 fs
->block_size
= 1 << fs
->block_shift
;
648 /* Initialize the block cache */
649 cache_init(fs
->fs_dev
, fs
->block_shift
);
651 btrfs_read_super_block(fs
);
652 if (strncmp((char *)(&sb
.magic
), BTRFS_MAGIC
, sizeof(sb
.magic
)))
654 btrfs_read_sys_chunk_array();
655 btrfs_read_chunk_tree(fs
);
656 btrfs_get_fs_tree(fs
);
658 return fs
->block_shift
;
661 const struct fs_ops btrfs_fs_ops
= {
664 .fs_init
= btrfs_fs_init
,
665 .iget_root
= btrfs_iget_root
,
667 .readlink
= btrfs_readlink
,
668 .getfssec
= btrfs_getfssec
,
669 .close_file
= generic_close_file
,
670 .mangle_name
= generic_mangle_name
,
671 .next_extent
= btrfs_next_extent
,
672 .readdir
= btrfs_readdir
,
673 .load_config
= generic_load_config