• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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