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-2014 Intel Corporation; authors: Alek Du, H. Peter Anvin
5 *
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.
11 *
12 */
13
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 <minmax.h>
23 #include "btrfs.h"
24
25 union tree_buf {
26 struct btrfs_header header;
27 struct btrfs_node node;
28 struct btrfs_leaf leaf;
29 };
30
31 /* filesystem instance structure */
32 struct btrfs_info {
33 u64 fs_tree;
34 struct btrfs_super_block sb;
35 struct btrfs_chunk_map chunk_map;
36 union tree_buf *tree_buf;
37 };
38
39 /* compare function used for bin_search */
40 typedef int (*cmp_func)(const void *ptr1, const void *ptr2);
41
42 /* simple but useful bin search, used for chunk search and btree search */
bin_search(void * ptr,int item_size,void * cmp_item,cmp_func func,int min,int max,int * slot)43 static int bin_search(void *ptr, int item_size, void *cmp_item, cmp_func func,
44 int min, int max, int *slot)
45 {
46 int low = min;
47 int high = max;
48 int mid;
49 int ret;
50 unsigned long offset;
51 void *item;
52
53 while (low < high) {
54 mid = (low + high) / 2;
55 offset = mid * item_size;
56
57 item = ptr + offset;
58 ret = func(item, cmp_item);
59
60 if (ret < 0)
61 low = mid + 1;
62 else if (ret > 0)
63 high = mid;
64 else {
65 *slot = mid;
66 return 0;
67 }
68 }
69 *slot = low;
70 return 1;
71 }
72
btrfs_comp_chunk_map(struct btrfs_chunk_map_item * m1,struct btrfs_chunk_map_item * m2)73 static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1,
74 struct btrfs_chunk_map_item *m2)
75 {
76 if (m1->logical > m2->logical)
77 return 1;
78 if (m1->logical < m2->logical)
79 return -1;
80 return 0;
81 }
82
83 /* insert a new chunk mapping item */
insert_map(struct fs_info * fs,struct btrfs_chunk_map_item * item)84 static void insert_map(struct fs_info *fs, struct btrfs_chunk_map_item *item)
85 {
86 struct btrfs_info * const bfs = fs->fs_info;
87 struct btrfs_chunk_map *chunk_map = &bfs->chunk_map;
88 int ret;
89 int slot;
90 int i;
91
92 if (chunk_map->map == NULL) { /* first item */
93 chunk_map->map_length = BTRFS_MAX_CHUNK_ENTRIES;
94 chunk_map->map = malloc(chunk_map->map_length
95 * sizeof(chunk_map->map[0]));
96 chunk_map->map[0] = *item;
97 chunk_map->cur_length = 1;
98 return;
99 }
100 ret = bin_search(chunk_map->map, sizeof(*item), item,
101 (cmp_func)btrfs_comp_chunk_map, 0,
102 chunk_map->cur_length, &slot);
103 if (ret == 0)/* already in map */
104 return;
105 if (chunk_map->cur_length == BTRFS_MAX_CHUNK_ENTRIES) {
106 /* should be impossible */
107 printf("too many chunk items\n");
108 return;
109 }
110 for (i = chunk_map->cur_length; i > slot; i--)
111 chunk_map->map[i] = chunk_map->map[i-1];
112 chunk_map->map[slot] = *item;
113 chunk_map->cur_length++;
114 }
115
116 /*
117 * from sys_chunk_array or chunk_tree, we can convert a logical address to
118 * a physical address we can not support multi device case yet
119 */
logical_physical(struct fs_info * fs,u64 logical)120 static u64 logical_physical(struct fs_info *fs, u64 logical)
121 {
122 struct btrfs_info * const bfs = fs->fs_info;
123 struct btrfs_chunk_map *chunk_map = &bfs->chunk_map;
124 struct btrfs_chunk_map_item item;
125 int slot, ret;
126
127 item.logical = logical;
128 ret = bin_search(chunk_map->map, sizeof(chunk_map->map[0]), &item,
129 (cmp_func)btrfs_comp_chunk_map, 0,
130 chunk_map->cur_length, &slot);
131 if (ret == 0)
132 slot++;
133 else if (slot == 0)
134 return -1;
135 if (logical >=
136 chunk_map->map[slot-1].logical + chunk_map->map[slot-1].length)
137 return -1;
138 return chunk_map->map[slot-1].physical + logical -
139 chunk_map->map[slot-1].logical;
140 }
141
142 /* btrfs has several super block mirrors, need to calculate their location */
btrfs_sb_offset(int mirror)143 static inline u64 btrfs_sb_offset(int mirror)
144 {
145 u64 start = 16 * 1024;
146 if (mirror)
147 return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror);
148 return BTRFS_SUPER_INFO_OFFSET;
149 }
150
151 /* find the most recent super block */
btrfs_read_super_block(struct fs_info * fs)152 static void btrfs_read_super_block(struct fs_info *fs)
153 {
154 int i;
155 int ret;
156 u8 fsid[BTRFS_FSID_SIZE];
157 u64 offset;
158 u64 transid = 0;
159 struct btrfs_super_block buf;
160 struct btrfs_info * const bfs = fs->fs_info;
161
162 bfs->sb.total_bytes = ~0; /* Unknown as of yet */
163
164 /* find most recent super block */
165 for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
166 offset = btrfs_sb_offset(i);
167 if (offset >= bfs->sb.total_bytes)
168 break;
169
170 ret = cache_read(fs, (char *)&buf, offset, sizeof(buf));
171 if (ret < sizeof(buf))
172 break;
173
174 if (buf.bytenr != offset ||
175 strncmp((char *)(&buf.magic), BTRFS_MAGIC,
176 sizeof(buf.magic)))
177 continue;
178
179 if (i == 0)
180 memcpy(fsid, buf.fsid, sizeof(fsid));
181 else if (memcmp(fsid, buf.fsid, sizeof(fsid)))
182 continue;
183
184 if (buf.generation > transid) {
185 memcpy(&bfs->sb, &buf, sizeof(bfs->sb));
186 transid = buf.generation;
187 }
188 }
189 }
190
btrfs_chunk_item_size(int num_stripes)191 static inline unsigned long btrfs_chunk_item_size(int num_stripes)
192 {
193 return sizeof(struct btrfs_chunk) +
194 sizeof(struct btrfs_stripe) * (num_stripes - 1);
195 }
196
clear_path(struct btrfs_path * path)197 static void clear_path(struct btrfs_path *path)
198 {
199 memset(path, 0, sizeof(*path));
200 }
201
btrfs_comp_keys(const struct btrfs_disk_key * k1,const struct btrfs_disk_key * k2)202 static int btrfs_comp_keys(const struct btrfs_disk_key *k1,
203 const struct btrfs_disk_key *k2)
204 {
205 if (k1->objectid > k2->objectid)
206 return 1;
207 if (k1->objectid < k2->objectid)
208 return -1;
209 if (k1->type > k2->type)
210 return 1;
211 if (k1->type < k2->type)
212 return -1;
213 if (k1->offset > k2->offset)
214 return 1;
215 if (k1->offset < k2->offset)
216 return -1;
217 return 0;
218 }
219
220 /* compare keys but ignore offset, is useful to enumerate all same kind keys */
btrfs_comp_keys_type(const struct btrfs_disk_key * k1,const struct btrfs_disk_key * k2)221 static int btrfs_comp_keys_type(const struct btrfs_disk_key *k1,
222 const struct btrfs_disk_key *k2)
223 {
224 if (k1->objectid > k2->objectid)
225 return 1;
226 if (k1->objectid < k2->objectid)
227 return -1;
228 if (k1->type > k2->type)
229 return 1;
230 if (k1->type < k2->type)
231 return -1;
232 return 0;
233 }
234
235 /* seach tree directly on disk ... */
search_tree(struct fs_info * fs,u64 loffset,struct btrfs_disk_key * key,struct btrfs_path * path)236 static int search_tree(struct fs_info *fs, u64 loffset,
237 struct btrfs_disk_key *key, struct btrfs_path *path)
238 {
239 struct btrfs_info * const bfs = fs->fs_info;
240 union tree_buf *tree_buf = bfs->tree_buf;
241 int slot, ret;
242 u64 offset;
243
244 offset = logical_physical(fs, loffset);
245 cache_read(fs, &tree_buf->header, offset, sizeof(tree_buf->header));
246 if (tree_buf->header.level) {
247 /* inner node */
248 cache_read(fs, (char *)&tree_buf->node.ptrs[0],
249 offset + sizeof tree_buf->header,
250 bfs->sb.nodesize - sizeof tree_buf->header);
251 path->itemsnr[tree_buf->header.level] = tree_buf->header.nritems;
252 path->offsets[tree_buf->header.level] = loffset;
253 ret = bin_search(&tree_buf->node.ptrs[0],
254 sizeof(struct btrfs_key_ptr),
255 key, (cmp_func)btrfs_comp_keys,
256 path->slots[tree_buf->header.level],
257 tree_buf->header.nritems, &slot);
258 if (ret && slot > path->slots[tree_buf->header.level])
259 slot--;
260 path->slots[tree_buf->header.level] = slot;
261 ret = search_tree(fs, tree_buf->node.ptrs[slot].blockptr,
262 key, path);
263 } else {
264 /* leaf node */
265 cache_read(fs, (char *)&tree_buf->leaf.items[0],
266 offset + sizeof tree_buf->header,
267 bfs->sb.leafsize - sizeof tree_buf->header);
268 path->itemsnr[tree_buf->header.level] = tree_buf->header.nritems;
269 path->offsets[tree_buf->header.level] = loffset;
270 ret = bin_search(&tree_buf->leaf.items[0],
271 sizeof(struct btrfs_item),
272 key, (cmp_func)btrfs_comp_keys,
273 path->slots[0],
274 tree_buf->header.nritems, &slot);
275 if (ret && slot > path->slots[tree_buf->header.level])
276 slot--;
277 path->slots[tree_buf->header.level] = slot;
278 path->item = tree_buf->leaf.items[slot];
279 cache_read(fs, (char *)&path->data,
280 offset + sizeof tree_buf->header +
281 tree_buf->leaf.items[slot].offset,
282 tree_buf->leaf.items[slot].size);
283 }
284 return ret;
285 }
286
287 /* return 0 if leaf found */
next_leaf(struct fs_info * fs,struct btrfs_disk_key * key,struct btrfs_path * path)288 static int next_leaf(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path)
289 {
290 int slot;
291 int level = 1;
292
293 while (level < BTRFS_MAX_LEVEL) {
294 if (!path->itemsnr[level]) /* no more nodes */
295 return 1;
296 slot = path->slots[level] + 1;
297 if (slot >= path->itemsnr[level]) {
298 level++;
299 continue;;
300 }
301 path->slots[level] = slot;
302 path->slots[level-1] = 0; /* reset low level slots info */
303 search_tree(fs, path->offsets[level], key, path);
304 break;
305 }
306 if (level == BTRFS_MAX_LEVEL)
307 return 1;
308 return 0;
309 }
310
311 /* return 0 if slot found */
next_slot(struct fs_info * fs,struct btrfs_disk_key * key,struct btrfs_path * path)312 static int next_slot(struct fs_info *fs, struct btrfs_disk_key *key,
313 struct btrfs_path *path)
314 {
315 int slot;
316
317 if (!path->itemsnr[0])
318 return 1;
319 slot = path->slots[0] + 1;
320 if (slot >= path->itemsnr[0])
321 return 1;
322 path->slots[0] = slot;
323 search_tree(fs, path->offsets[0], key, path);
324 return 0;
325 }
326
327 /*
328 * read chunk_array in super block
329 */
btrfs_read_sys_chunk_array(struct fs_info * fs)330 static void btrfs_read_sys_chunk_array(struct fs_info *fs)
331 {
332 struct btrfs_info * const bfs = fs->fs_info;
333 struct btrfs_chunk_map_item item;
334 struct btrfs_disk_key *key;
335 struct btrfs_chunk *chunk;
336 int cur;
337
338 /* read chunk array in superblock */
339 cur = 0;
340 while (cur < bfs->sb.sys_chunk_array_size) {
341 key = (struct btrfs_disk_key *)(bfs->sb.sys_chunk_array + cur);
342 cur += sizeof(*key);
343 chunk = (struct btrfs_chunk *)(bfs->sb.sys_chunk_array + cur);
344 cur += btrfs_chunk_item_size(chunk->num_stripes);
345 /* insert to mapping table, ignore multi stripes */
346 item.logical = key->offset;
347 item.length = chunk->length;
348 item.devid = chunk->stripe.devid;
349 item.physical = chunk->stripe.offset;/*ignore other stripes */
350 insert_map(fs, &item);
351 }
352 }
353
354 /* read chunk items from chunk_tree and insert them to chunk map */
btrfs_read_chunk_tree(struct fs_info * fs)355 static void btrfs_read_chunk_tree(struct fs_info *fs)
356 {
357 struct btrfs_info * const bfs = fs->fs_info;
358 struct btrfs_disk_key search_key;
359 struct btrfs_chunk *chunk;
360 struct btrfs_chunk_map_item item;
361 struct btrfs_path path;
362
363 if (!(bfs->sb.flags & BTRFS_SUPER_FLAG_METADUMP)) {
364 if (bfs->sb.num_devices > 1)
365 printf("warning: only support single device btrfs\n");
366 /* read chunk from chunk_tree */
367 search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
368 search_key.type = BTRFS_CHUNK_ITEM_KEY;
369 search_key.offset = 0;
370 clear_path(&path);
371 search_tree(fs, bfs->sb.chunk_root, &search_key, &path);
372 do {
373 do {
374 if (btrfs_comp_keys_type(&search_key,
375 &path.item.key))
376 break;
377 chunk = (struct btrfs_chunk *)(path.data);
378 /* insert to mapping table, ignore stripes */
379 item.logical = path.item.key.offset;
380 item.length = chunk->length;
381 item.devid = chunk->stripe.devid;
382 item.physical = chunk->stripe.offset;
383 insert_map(fs, &item);
384 } while (!next_slot(fs, &search_key, &path));
385 if (btrfs_comp_keys_type(&search_key, &path.item.key))
386 break;
387 } while (!next_leaf(fs, &search_key, &path));
388 }
389 }
390
btrfs_name_hash(const char * name,int len)391 static inline u64 btrfs_name_hash(const char *name, int len)
392 {
393 return btrfs_crc32c((u32)~1, name, len);
394 }
395
btrfs_iget_by_inr(struct fs_info * fs,u64 inr)396 static struct inode *btrfs_iget_by_inr(struct fs_info *fs, u64 inr)
397 {
398 struct btrfs_info * const bfs = fs->fs_info;
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;
404
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, bfs->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);
420
421 if (inode->mode == DT_REG || inode->mode == DT_LNK) {
422 struct btrfs_file_extent_item extent_item;
423 u64 offset;
424
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, bfs->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;
440 }
441 return inode;
442 }
443
btrfs_iget_root(struct fs_info * fs)444 static struct inode *btrfs_iget_root(struct fs_info *fs)
445 {
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);
448 }
449
btrfs_iget(const char * name,struct inode * parent)450 static struct inode *btrfs_iget(const char *name, struct inode *parent)
451 {
452 struct fs_info * const fs = parent->fs;
453 struct btrfs_info * const bfs = fs->fs_info;
454 struct btrfs_disk_key search_key;
455 struct btrfs_path path;
456 struct btrfs_dir_item dir_item;
457 int ret;
458
459 search_key.objectid = parent->ino;
460 search_key.type = BTRFS_DIR_ITEM_KEY;
461 search_key.offset = btrfs_name_hash(name, strlen(name));
462 clear_path(&path);
463 ret = search_tree(fs, bfs->fs_tree, &search_key, &path);
464 if (ret)
465 return NULL;
466 dir_item = *(struct btrfs_dir_item *)path.data;
467
468 return btrfs_iget_by_inr(fs, dir_item.location.objectid);
469 }
470
btrfs_readlink(struct inode * inode,char * buf)471 static int btrfs_readlink(struct inode *inode, char *buf)
472 {
473 cache_read(inode->fs, buf,
474 logical_physical(inode->fs, PVT(inode)->offset),
475 inode->size);
476 buf[inode->size] = '\0';
477 return inode->size;
478 }
479
btrfs_readdir(struct file * file,struct dirent * dirent)480 static int btrfs_readdir(struct file *file, struct dirent *dirent)
481 {
482 struct fs_info * const fs = file->fs;
483 struct btrfs_info * const bfs = fs->fs_info;
484 struct inode * const inode = file->inode;
485 struct btrfs_disk_key search_key;
486 struct btrfs_path path;
487 struct btrfs_dir_item *dir_item;
488 int ret;
489
490 /*
491 * we use file->offset to store last search key.offset, will will search
492 * key that lower that offset, 0 means first search and we will search
493 * -1UL, which is the biggest possible key
494 */
495 search_key.objectid = inode->ino;
496 search_key.type = BTRFS_DIR_ITEM_KEY;
497 search_key.offset = file->offset - 1;
498 clear_path(&path);
499 ret = search_tree(fs, bfs->fs_tree, &search_key, &path);
500
501 if (ret) {
502 if (btrfs_comp_keys_type(&search_key, &path.item.key))
503 return -1;
504 }
505
506 dir_item = (struct btrfs_dir_item *)path.data;
507 file->offset = path.item.key.offset;
508 dirent->d_ino = dir_item->location.objectid;
509 dirent->d_off = file->offset;
510 dirent->d_reclen = offsetof(struct dirent, d_name)
511 + dir_item->name_len + 1;
512 dirent->d_type = IFTODT(dir_item->type);
513 memcpy(dirent->d_name, dir_item + 1, dir_item->name_len);
514 dirent->d_name[dir_item->name_len] = '\0';
515
516 return 0;
517 }
518
btrfs_next_extent(struct inode * inode,uint32_t lstart)519 static int btrfs_next_extent(struct inode *inode, uint32_t lstart)
520 {
521 struct btrfs_disk_key search_key;
522 struct btrfs_file_extent_item extent_item;
523 struct btrfs_path path;
524 int ret;
525 u64 offset;
526 struct fs_info * const fs = inode->fs;
527 struct btrfs_info * const bfs = fs->fs_info;
528 u32 sec_shift = SECTOR_SHIFT(fs);
529 u32 sec_size = SECTOR_SIZE(fs);
530
531 search_key.objectid = inode->ino;
532 search_key.type = BTRFS_EXTENT_DATA_KEY;
533 search_key.offset = lstart << sec_shift;
534 clear_path(&path);
535 ret = search_tree(fs, bfs->fs_tree, &search_key, &path);
536 if (ret) { /* impossible */
537 printf("btrfs: search extent data error!\n");
538 return -1;
539 }
540 extent_item = *(struct btrfs_file_extent_item *)path.data;
541
542 if (extent_item.encryption) {
543 printf("btrfs: found encrypted data, cannot continue!\n");
544 return -1;
545 }
546 if (extent_item.compression) {
547 printf("btrfs: found compressed data, cannot continue!\n");
548 return -1;
549 }
550
551 if (extent_item.type == BTRFS_FILE_EXTENT_INLINE) {/* inline file */
552 /* we fake a extent here, and PVT of inode will tell us */
553 offset = path.offsets[0] + sizeof(struct btrfs_header)
554 + path.item.offset
555 + offsetof(struct btrfs_file_extent_item, disk_bytenr);
556 inode->next_extent.len =
557 (inode->size + sec_size -1) >> sec_shift;
558 } else {
559 offset = extent_item.disk_bytenr + extent_item.offset;
560 inode->next_extent.len =
561 (extent_item.num_bytes + sec_size - 1) >> sec_shift;
562 }
563 inode->next_extent.pstart = logical_physical(fs, offset) >> sec_shift;
564 PVT(inode)->offset = offset;
565 return 0;
566 }
567
btrfs_getfssec(struct file * file,char * buf,int sectors,bool * have_more)568 static uint32_t btrfs_getfssec(struct file *file, char *buf, int sectors,
569 bool *have_more)
570 {
571 u32 ret;
572 struct fs_info *fs = file->fs;
573 u32 off = PVT(file->inode)->offset % SECTOR_SIZE(fs);
574 bool handle_inline = false;
575
576 if (off && !file->offset) {/* inline file first read patch */
577 file->inode->size += off;
578 handle_inline = true;
579 }
580 ret = generic_getfssec(file, buf, sectors, have_more);
581 if (!ret)
582 return ret;
583 off = PVT(file->inode)->offset % SECTOR_SIZE(fs);
584 if (handle_inline) {/* inline file patch */
585 ret -= off;
586 memcpy(buf, buf + off, ret);
587 }
588 return ret;
589 }
590
btrfs_get_fs_tree(struct fs_info * fs)591 static void btrfs_get_fs_tree(struct fs_info *fs)
592 {
593 struct btrfs_info * const bfs = fs->fs_info;
594 struct btrfs_disk_key search_key;
595 struct btrfs_path path;
596 struct btrfs_root_item *tree;
597 bool subvol_ok = false;
598
599 /* check if subvol is filled by installer */
600 if (*SubvolName) {
601 search_key.objectid = BTRFS_FS_TREE_OBJECTID;
602 search_key.type = BTRFS_ROOT_REF_KEY;
603 search_key.offset = 0;
604 clear_path(&path);
605 if (search_tree(fs, bfs->sb.root, &search_key, &path))
606 next_slot(fs, &search_key, &path);
607 do {
608 do {
609 struct btrfs_root_ref *ref;
610 int pathlen;
611
612 if (btrfs_comp_keys_type(&search_key,
613 &path.item.key))
614 break;
615 ref = (struct btrfs_root_ref *)path.data;
616 pathlen = path.item.size - sizeof(struct btrfs_root_ref);
617
618 if (!strncmp((char*)(ref + 1), SubvolName, pathlen)) {
619 subvol_ok = true;
620 break;
621 }
622 } while (!next_slot(fs, &search_key, &path));
623 if (subvol_ok)
624 break;
625 if (btrfs_comp_keys_type(&search_key, &path.item.key))
626 break;
627 } while (!next_leaf(fs, &search_key, &path));
628 if (!subvol_ok) /* should be impossible */
629 printf("no subvol found!\n");
630 }
631 /* find fs_tree from tree_root */
632 if (subvol_ok)
633 search_key.objectid = path.item.key.offset;
634 else /* "default" volume */
635 search_key.objectid = BTRFS_FS_TREE_OBJECTID;
636 search_key.type = BTRFS_ROOT_ITEM_KEY;
637 search_key.offset = -1;
638 clear_path(&path);
639 search_tree(fs, bfs->sb.root, &search_key, &path);
640 tree = (struct btrfs_root_item *)path.data;
641 bfs->fs_tree = tree->bytenr;
642 }
643
644 /* init. the fs meta data, return the block size shift bits. */
btrfs_fs_init(struct fs_info * fs)645 static int btrfs_fs_init(struct fs_info *fs)
646 {
647 struct disk *disk = fs->fs_dev->disk;
648 struct btrfs_info *bfs;
649
650 btrfs_init_crc32c();
651
652 bfs = zalloc(sizeof(struct btrfs_info));
653 if (!bfs)
654 return -1;
655
656 fs->fs_info = bfs;
657
658 fs->sector_shift = disk->sector_shift;
659 fs->sector_size = 1 << fs->sector_shift;
660 fs->block_shift = BTRFS_BLOCK_SHIFT;
661 fs->block_size = 1 << fs->block_shift;
662
663 /* Initialize the block cache */
664 cache_init(fs->fs_dev, fs->block_shift);
665
666 btrfs_read_super_block(fs);
667 if (bfs->sb.magic != BTRFS_MAGIC_N)
668 return -1;
669 bfs->tree_buf = malloc(max(bfs->sb.nodesize, bfs->sb.leafsize));
670 if (!bfs->tree_buf)
671 return -1;
672 btrfs_read_sys_chunk_array(fs);
673 btrfs_read_chunk_tree(fs);
674 btrfs_get_fs_tree(fs);
675
676 return fs->block_shift;
677 }
678
679 const struct fs_ops btrfs_fs_ops = {
680 .fs_name = "btrfs",
681 .fs_flags = 0,
682 .fs_init = btrfs_fs_init,
683 .iget_root = btrfs_iget_root,
684 .iget = btrfs_iget,
685 .readlink = btrfs_readlink,
686 .getfssec = btrfs_getfssec,
687 .close_file = generic_close_file,
688 .mangle_name = generic_mangle_name,
689 .next_extent = btrfs_next_extent,
690 .readdir = btrfs_readdir,
691 .chdir_start = generic_chdir_start,
692 .open_config = generic_open_config,
693 .fs_uuid = NULL,
694 };
695