Releasing debian version 4.04+dfsg-9.
[syslinux-debian/hramrach.git] / core / fs / btrfs / btrfs.c
blobb6a14e3b12fbc0df8bb53105170a5028b4f10959
1 /*
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.
14 #include <dprintf.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <cache.h>
18 #include <core.h>
19 #include <disk.h>
20 #include <fs.h>
21 #include <dirent.h>
22 #include "btrfs.h"
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)
31 int low = min;
32 int high = max;
33 int mid;
34 int ret;
35 unsigned long offset;
36 void *item;
38 while (low < high) {
39 mid = (low + high) / 2;
40 offset = mid * item_size;
42 item = ptr + offset;
43 ret = func(item, cmp_item);
45 if (ret < 0)
46 low = mid + 1;
47 else if (ret > 0)
48 high = mid;
49 else {
50 *slot = mid;
51 return 0;
54 *slot = low;
55 return 1;
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;
61 static u64 fs_tree;
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)
67 return 1;
68 if (m1->logical < m2->logical)
69 return -1;
70 return 0;
73 /* insert a new chunk mapping item */
74 static void insert_map(struct btrfs_chunk_map_item *item)
76 int ret;
77 int slot;
78 int i;
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;
86 return;
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 */
92 return;
93 if (chunk_map.cur_length == BTRFS_MAX_CHUNK_ENTRIES) {
94 /* should be impossible */
95 printf("too many chunk items\n");
96 return;
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;
111 int slot, ret;
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);
117 if (ret == 0)
118 slot++;
119 else if (slot == 0)
120 return -1;
121 if (logical >=
122 chunk_map.map[slot-1].logical + chunk_map.map[slot-1].length)
123 return -1;
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)
131 const char *cd;
132 size_t block_size = fs->fs_dev->cache_block_size;
133 size_t off, cnt, total;
134 block_t block;
136 total = count;
137 while (count > 0) {
138 block = offset / block_size;
139 off = offset % block_size;
140 cd = get_cache(fs->fs_dev, block);
141 if (!cd)
142 break;
143 cnt = block_size - off;
144 if (cnt > count)
145 cnt = count;
146 memcpy(buf, cd + off, cnt);
147 count -= cnt;
148 buf += cnt;
149 offset += 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;
158 if (mirror)
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)
166 int i;
167 int ret;
168 u8 fsid[BTRFS_FSID_SIZE];
169 u64 offset;
170 u64 transid = 0;
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)
181 break;
183 ret = btrfs_read(fs, (char *)&buf, offset, sizeof(buf));
184 if (ret < sizeof(buf))
185 break;
187 if (buf.bytenr != offset ||
188 strncmp((char *)(&buf.magic), BTRFS_MAGIC,
189 sizeof(buf.magic)))
190 continue;
192 if (i == 0)
193 memcpy(fsid, buf.fsid, sizeof(fsid));
194 else if (memcmp(fsid, buf.fsid, sizeof(fsid)))
195 continue;
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)
218 return 1;
219 if (k1->objectid < k2->objectid)
220 return -1;
221 if (k1->type > k2->type)
222 return 1;
223 if (k1->type < k2->type)
224 return -1;
225 if (k1->offset > k2->offset)
226 return 1;
227 if (k1->offset < k2->offset)
228 return -1;
229 return 0;
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)
237 return 1;
238 if (k1->objectid < k2->objectid)
239 return -1;
240 if (k1->type > k2->type)
241 return 1;
242 if (k1->type < k2->type)
243 return -1;
244 return 0;
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;
255 int slot, ret;
256 u64 offset;
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])
269 slot--;
270 path->slots[header->level] = slot;
271 ret = search_tree(fs, node->ptrs[slot].blockptr, key, path);
272 } else {/*leaf*/
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])
281 slot--;
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);
288 return ret;
291 /* return 0 if leaf found */
292 static int next_leaf(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path)
294 int slot;
295 int level = 1;
297 while (level < BTRFS_MAX_LEVEL) {
298 if (!path->itemsnr[level]) /* no more nodes */
299 return 1;
300 slot = path->slots[level] + 1;
301 if (slot >= path->itemsnr[level]) {
302 level++;
303 continue;;
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);
308 break;
310 if (level == BTRFS_MAX_LEVEL)
311 return 1;
312 return 0;
315 /* return 0 if slot found */
316 static int next_slot(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path)
318 int slot;
320 if (!path->itemsnr[0])
321 return 1;
322 slot = path->slots[0] + 1;
323 if (slot >= path->itemsnr[0])
324 return 1;
325 path->slots[0] = slot;
326 search_tree(fs, path->offsets[0], key, path);
327 return 0;
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;
338 int cur;
340 /* read chunk array in superblock */
341 cur = 0;
342 while (cur < sb.sys_chunk_array_size) {
343 key = (struct btrfs_disk_key *)(sb.sys_chunk_array + cur);
344 cur += sizeof(*key);
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 */
352 insert_map(&item);
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;
371 clear_path(&path);
372 search_tree(fs, sb.chunk_root, &search_key, &path);
373 do {
374 do {
375 if (btrfs_comp_keys_type(&search_key,
376 &path.item.key))
377 break;
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;
384 insert_map(&item);
385 } while (!next_slot(fs, &search_key, &path));
386 if (btrfs_comp_keys_type(&search_key, &path.item.key))
387 break;
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)
399 struct inode *inode;
400 struct btrfs_inode_item inode_item;
401 struct btrfs_disk_key search_key;
402 struct btrfs_path path;
403 int ret;
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;
410 clear_path(&path);
411 ret = search_tree(fs, fs_tree, &search_key, &path);
412 if (ret)
413 return NULL;
414 inode_item = *(struct btrfs_inode_item *)path.data;
415 if (!(inode = alloc_inode(fs, inr, sizeof(struct btrfs_pvt_inode))))
416 return NULL;
417 inode->ino = inr;
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;
423 u64 offset;
425 /* get file_extent_item */
426 search_key.type = BTRFS_EXTENT_DATA_KEY;
427 search_key.offset = 0;
428 clear_path(&path);
429 ret = search_tree(fs, fs_tree, &search_key, &path);
430 if (ret)
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)
435 + path.item.offset
436 + offsetof(struct btrfs_file_extent_item, disk_bytenr);
437 else
438 offset = extent_item.disk_bytenr;
439 PVT(inode)->offset = offset;
441 return inode;
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;
456 int ret;
458 search_key.objectid = parent->ino;
459 search_key.type = BTRFS_DIR_ITEM_KEY;
460 search_key.offset = btrfs_name_hash(name, strlen(name));
461 clear_path(&path);
462 ret = search_tree(fs, fs_tree, &search_key, &path);
463 if (ret)
464 return NULL;
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';
474 return inode->size;
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;
484 int ret;
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;
494 clear_path(&path);
495 ret = search_tree(fs, fs_tree, &search_key, &path);
497 if (ret) {
498 if (btrfs_comp_keys_type(&search_key, &path.item.key))
499 return -1;
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';
512 return 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;
520 int ret;
521 u64 offset;
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;
529 clear_path(&path);
530 ret = search_tree(fs, fs_tree, &search_key, &path);
531 if (ret) { /* impossible */
532 printf("btrfs: search extent data error!\n");
533 return -1;
535 extent_item = *(struct btrfs_file_extent_item *)path.data;
537 if (extent_item.encryption) {
538 printf("btrfs: found encrypted data, cannot continue!\n");
539 return -1;
541 if (extent_item.compression) {
542 printf("btrfs: found compressed data, cannot continue!\n");
543 return -1;
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)
549 + path.item.offset
550 + offsetof(struct btrfs_file_extent_item, disk_bytenr);
551 inode->next_extent.len =
552 (inode->size + sec_size -1) >> sec_shift;
553 } else {
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;
561 return 0;
564 static uint32_t btrfs_getfssec(struct file *file, char *buf, int sectors,
565 bool *have_more)
567 u32 ret;
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);
577 if (!ret)
578 return ret;
579 off = PVT(file->inode)->offset % SECTOR_SIZE(fs);
580 if (handle_inline) {/* inline file patch */
581 ret -= off;
582 memcpy(buf, buf + off, ret);
584 return 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 */
595 if (*SubvolName) {
596 search_key.objectid = BTRFS_FS_TREE_OBJECTID;
597 search_key.type = BTRFS_ROOT_REF_KEY;
598 search_key.offset = 0;
599 clear_path(&path);
600 if (search_tree(fs, sb.root, &search_key, &path))
601 next_slot(fs, &search_key, &path);
602 do {
603 do {
604 struct btrfs_root_ref *ref;
606 if (btrfs_comp_keys_type(&search_key,
607 &path.item.key))
608 break;
609 ref = (struct btrfs_root_ref *)path.data;
610 if (!strcmp((char*)(ref + 1), SubvolName)) {
611 subvol_ok = true;
612 break;
614 } while (!next_slot(fs, &search_key, &path));
615 if (subvol_ok)
616 break;
617 if (btrfs_comp_keys_type(&search_key, &path.item.key))
618 break;
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 */
624 if (subvol_ok)
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;
630 clear_path(&path);
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;
641 btrfs_init_crc32c();
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)))
653 return -1;
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 = {
662 .fs_name = "btrfs",
663 .fs_flags = 0,
664 .fs_init = btrfs_fs_init,
665 .iget_root = btrfs_iget_root,
666 .iget = btrfs_iget,
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