]> git.proxmox.com Git - grub2.git/blob - grub-core/fs/btrfs.c
Add dprintfs to report chunk lookups
[grub2.git] / grub-core / fs / btrfs.c
1 /* btrfs.c - B-tree file system. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010 Free Software Foundation, Inc.
5 *
6 * GRUB 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, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/lib/crc.h>
28 #include <grub/deflate.h>
29
30 #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
31
32 typedef grub_uint8_t grub_btrfs_checksum_t[0x20];
33 typedef grub_uint16_t grub_btrfs_uuid_t[8];
34
35 struct grub_btrfs_device
36 {
37 grub_uint64_t device_id;
38 grub_uint8_t dummy[0x62 - 8];
39 } __attribute__ ((packed));
40
41 struct grub_btrfs_superblock
42 {
43 grub_btrfs_checksum_t checksum;
44 grub_btrfs_uuid_t uuid;
45 grub_uint8_t dummy[0x10];
46 grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1];
47 grub_uint64_t generation;
48 grub_uint64_t root_tree;
49 grub_uint64_t chunk_tree;
50 grub_uint8_t dummy2[0x20];
51 grub_uint64_t root_dir_objectid;
52 grub_uint8_t dummy3[0x41];
53 struct grub_btrfs_device this_device;
54 char label[0x100];
55 grub_uint8_t dummy4[0x100];
56 grub_uint8_t bootstrap_mapping[0x800];
57 } __attribute__ ((packed));
58
59 struct btrfs_header
60 {
61 grub_btrfs_checksum_t checksum;
62 grub_btrfs_uuid_t uuid;
63 grub_uint8_t dummy[0x30];
64 grub_uint32_t nitems;
65 grub_uint8_t level;
66 } __attribute__ ((packed));
67
68 struct grub_btrfs_device_desc
69 {
70 grub_device_t dev;
71 grub_uint64_t id;
72 };
73
74 struct grub_btrfs_data
75 {
76 struct grub_btrfs_superblock sblock;
77 grub_uint64_t tree;
78 grub_uint64_t inode;
79
80 struct grub_btrfs_device_desc *devices_attached;
81 unsigned n_devices_attached;
82 unsigned n_devices_allocated;
83
84 /* Cached extent data. */
85 grub_uint64_t extstart;
86 grub_uint64_t extino;
87 grub_uint64_t exttree;
88 grub_size_t extsize;
89 struct grub_btrfs_extent_data *extent;
90 };
91
92 struct grub_btrfs_key
93 {
94 grub_uint64_t object_id;
95 #define GRUB_BTRFS_ITEM_TYPE_INODE_ITEM 0x01
96 #define GRUB_BTRFS_ITEM_TYPE_DIR_ITEM 0x54
97 #define GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM 0x6c
98 #define GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM 0x84
99 #define GRUB_BTRFS_ITEM_TYPE_DEVICE 0xd8
100 #define GRUB_BTRFS_ITEM_TYPE_CHUNK 0xe4
101 grub_uint8_t type;
102 grub_uint64_t offset;
103 } __attribute__ ((packed));
104
105 struct grub_btrfs_chunk_item
106 {
107 grub_uint64_t size;
108 grub_uint64_t dummy;
109 grub_uint64_t stripe_length;
110 grub_uint64_t type;
111 #define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07
112 #define GRUB_BTRFS_CHUNK_TYPE_SINGLE 0x00
113 #define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08
114 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
115 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20
116 #define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40
117 grub_uint8_t dummy2[0xc];
118 grub_uint16_t nstripes;
119 grub_uint16_t nsubstripes;
120 } __attribute__ ((packed));
121
122 struct grub_btrfs_chunk_stripe
123 {
124 grub_uint64_t device_id;
125 grub_uint64_t offset;
126 grub_btrfs_uuid_t device_uuid;
127 } __attribute__ ((packed));
128
129 struct grub_btrfs_leaf_node
130 {
131 struct grub_btrfs_key key;
132 grub_uint32_t offset;
133 grub_uint32_t size;
134 } __attribute__ ((packed));
135
136 struct grub_btrfs_internal_node
137 {
138 struct grub_btrfs_key key;
139 grub_uint64_t addr;
140 grub_uint64_t dummy;
141 } __attribute__ ((packed));
142
143 struct grub_btrfs_dir_item
144 {
145 struct grub_btrfs_key key;
146 grub_uint8_t dummy[8];
147 grub_uint16_t m;
148 grub_uint16_t n;
149 #define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1
150 #define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2
151 #define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7
152 grub_uint8_t type;
153 char name[0];
154 } __attribute__ ((packed));
155
156 struct grub_btrfs_leaf_descriptor
157 {
158 unsigned depth;
159 unsigned allocated;
160 struct {
161 grub_disk_addr_t addr;
162 unsigned iter;
163 unsigned maxiter;
164 int leaf;
165 } *data;
166 };
167
168 struct grub_btrfs_root_item
169 {
170 grub_uint8_t dummy[0xb0];
171 grub_uint64_t tree;
172 grub_uint64_t inode;
173 };
174
175 struct grub_btrfs_time
176 {
177 grub_int64_t sec;
178 grub_uint32_t nanosec;
179 } __attribute__ ((aligned(4)));
180
181 struct grub_btrfs_inode
182 {
183 grub_uint8_t dummy1[0x10];
184 grub_uint64_t size;
185 grub_uint8_t dummy2[0x70];
186 struct grub_btrfs_time mtime;
187 } __attribute__ ((packed));
188
189 struct grub_btrfs_extent_data
190 {
191 grub_uint64_t dummy;
192 grub_uint64_t size;
193 grub_uint8_t compression;
194 grub_uint8_t encryption;
195 grub_uint16_t encoding;
196 grub_uint8_t type;
197 union
198 {
199 char inl[0];
200 struct
201 {
202 grub_uint64_t laddr;
203 grub_uint64_t compressed_size;
204 grub_uint64_t offset;
205 };
206 };
207 } __attribute__ ((packed));
208
209 #define GRUB_BTRFS_EXTENT_INLINE 0
210 #define GRUB_BTRFS_EXTENT_REGULAR 1
211
212 #define GRUB_BTRFS_COMPRESSION_NONE 0
213 #define GRUB_BTRFS_COMPRESSION_ZLIB 1
214
215 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
216
217 static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
218 256 * 1048576 * 2,
219 1048576ULL * 1048576ULL * 2 };
220
221 static grub_err_t
222 grub_btrfs_read_logical (struct grub_btrfs_data *data,
223 grub_disk_addr_t addr, void *buf, grub_size_t size);
224
225 static grub_err_t
226 read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
227 {
228 unsigned i;
229 grub_err_t err = GRUB_ERR_NONE;
230 for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++)
231 {
232 struct grub_btrfs_superblock sblock;
233 err = grub_disk_read (disk, superblock_sectors[i], 0,
234 sizeof (sblock), &sblock);
235 if (err == GRUB_ERR_OUT_OF_RANGE)
236 break;
237
238 if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE,
239 sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0)
240 break;
241 if (i == 0 || grub_le_to_cpu64 (sblock.generation)
242 > grub_le_to_cpu64 (sb->generation))
243 grub_memcpy (sb, &sblock, sizeof (sblock));
244 }
245
246 if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0)
247 return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem");
248
249 if (err == GRUB_ERR_OUT_OF_RANGE)
250 grub_errno = err = GRUB_ERR_NONE;
251
252 return err;
253 }
254
255 static int
256 key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b)
257 {
258 if (grub_cpu_to_le64 (a->object_id) < grub_cpu_to_le64 (b->object_id))
259 return -1;
260 if (grub_cpu_to_le64 (a->object_id) > grub_cpu_to_le64 (b->object_id))
261 return +1;
262
263 if (a->type < b->type)
264 return -1;
265 if (a->type > b->type)
266 return +1;
267
268 if (grub_cpu_to_le64 (a->offset) < grub_cpu_to_le64 (b->offset))
269 return -1;
270 if (grub_cpu_to_le64 (a->offset) > grub_cpu_to_le64 (b->offset))
271 return +1;
272 return 0;
273 }
274
275 static void
276 free_iterator (struct grub_btrfs_leaf_descriptor *desc)
277 {
278 grub_free (desc->data);
279 }
280
281 static grub_err_t
282 save_ref (struct grub_btrfs_leaf_descriptor *desc,
283 grub_disk_addr_t addr, unsigned i, unsigned m, int l)
284 {
285 desc->depth++;
286 if (desc->allocated < desc->depth)
287 {
288 void *newdata;
289 desc->allocated *= 2;
290 newdata = grub_realloc (desc->data, sizeof (desc->data[0])
291 * desc->allocated);
292 if (!newdata)
293 return grub_errno;
294 desc->data = newdata;
295 }
296 desc->data[desc->depth - 1].addr = addr;
297 desc->data[desc->depth - 1].iter = i;
298 desc->data[desc->depth - 1].maxiter = m;
299 desc->data[desc->depth - 1].leaf = l;
300 return GRUB_ERR_NONE;
301 }
302
303 static int
304 next (struct grub_btrfs_data *data,
305 struct grub_btrfs_leaf_descriptor *desc,
306 grub_disk_addr_t *outaddr, grub_size_t *outsize,
307 struct grub_btrfs_key *key_out)
308 {
309 grub_err_t err;
310 struct grub_btrfs_leaf_node leaf;
311
312 for (; desc->depth > 0; desc->depth--)
313 {
314 desc->data[desc->depth - 1].iter++;
315 if (desc->data[desc->depth - 1].iter
316 < desc->data[desc->depth - 1].maxiter)
317 break;
318 }
319 if (desc->depth == 0)
320 return 0;
321 while (!desc->data[desc->depth - 1].leaf)
322 {
323 struct grub_btrfs_internal_node node;
324 struct btrfs_header head;
325
326 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
327 * sizeof (node)
328 + sizeof (struct btrfs_header)
329 + desc->data[desc->depth - 1].addr, &node,
330 sizeof (node));
331 if (err)
332 return -err;
333
334 err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
335 sizeof (head));
336 if (err)
337 return -err;
338
339 save_ref (desc, grub_le_to_cpu64 (node.addr), 0,
340 grub_le_to_cpu32 (head.nitems), !head.level);
341 }
342 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
343 * sizeof (leaf)
344 + sizeof (struct btrfs_header)
345 + desc->data[desc->depth - 1].addr, &leaf,
346 sizeof (leaf));
347 if (err)
348 return -err;
349 *outsize = grub_le_to_cpu32 (leaf.size);
350 *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header)
351 + grub_le_to_cpu32 (leaf.offset);
352 *key_out = leaf.key;
353 return 1;
354 }
355
356 static grub_err_t
357 lower_bound (struct grub_btrfs_data *data,
358 const struct grub_btrfs_key *key_in,
359 struct grub_btrfs_key *key_out,
360 grub_disk_addr_t root,
361 grub_disk_addr_t *outaddr, grub_size_t *outsize,
362 struct grub_btrfs_leaf_descriptor *desc)
363 {
364 grub_disk_addr_t addr = root;
365 int depth = -1;
366
367 if (desc)
368 {
369 desc->allocated = 16;
370 desc->depth = 0;
371 desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
372 if (!desc->data)
373 return grub_errno;
374 }
375
376 grub_dprintf ("btrfs",
377 "retrieving %" PRIxGRUB_UINT64_T
378 " %x %" PRIxGRUB_UINT64_T "\n",
379 key_in->object_id, key_in->type, key_in->offset);
380
381 while (1)
382 {
383 grub_err_t err;
384 struct btrfs_header head;
385
386 reiter:
387 depth++;
388 /* FIXME: preread few nodes into buffer. */
389 err = grub_btrfs_read_logical (data, addr, &head, sizeof (head));
390 if (err)
391 return err;
392 addr += sizeof (head);
393 if (head.level)
394 {
395 unsigned i;
396 struct grub_btrfs_internal_node node, node_last;
397 int have_last = 0;
398 grub_memset (&node_last, 0, sizeof (node_last));
399 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
400 {
401 err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
402 &node, sizeof (node));
403 if (err)
404 return err;
405
406 grub_dprintf ("btrfs",
407 "internal node (depth %d) %" PRIxGRUB_UINT64_T
408 " %x %" PRIxGRUB_UINT64_T "\n", depth,
409 node.key.object_id, node.key.type, node.key.offset);
410
411 if (key_cmp (&node.key, key_in) == 0)
412 {
413 err = GRUB_ERR_NONE;
414 if (desc)
415 err = save_ref (desc, addr - sizeof (head), i,
416 grub_le_to_cpu32 (head.nitems), 0);
417 if (err)
418 return err;
419 addr = grub_le_to_cpu64 (node.addr);
420 goto reiter;
421 }
422 if (key_cmp (&node.key, key_in) > 0)
423 break;
424 node_last = node;
425 have_last = 1;
426 }
427 if (have_last)
428 {
429 err = GRUB_ERR_NONE;
430 if (desc)
431 err = save_ref (desc, addr - sizeof (head), i - 1,
432 grub_le_to_cpu32 (head.nitems), 0);
433 if (err)
434 return err;
435 addr = grub_le_to_cpu64 (node_last.addr);
436 goto reiter;
437 }
438 *outsize = 0;
439 *outaddr = 0;
440 grub_memset (key_out, 0, sizeof (*key_out));
441 if (desc)
442 return save_ref (desc, addr - sizeof (head), -1,
443 grub_le_to_cpu32 (head.nitems), 0);
444 return GRUB_ERR_NONE;
445 }
446 {
447 unsigned i;
448 struct grub_btrfs_leaf_node leaf, leaf_last;
449 int have_last = 0;
450 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
451 {
452 err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
453 &leaf, sizeof (leaf));
454 if (err)
455 return err;
456
457 grub_dprintf ("btrfs",
458 "leaf (depth %d) %" PRIxGRUB_UINT64_T
459 " %x %" PRIxGRUB_UINT64_T "\n", depth,
460 leaf.key.object_id, leaf.key.type, leaf.key.offset);
461
462 if (key_cmp (&leaf.key, key_in) == 0)
463 {
464 grub_memcpy (key_out, &leaf.key, sizeof(*key_out));
465 *outsize = grub_le_to_cpu32 (leaf.size);
466 *outaddr = addr + grub_le_to_cpu32 (leaf.offset);
467 if (desc)
468 return save_ref (desc, addr - sizeof (head), i,
469 grub_le_to_cpu32 (head.nitems), 1);
470 return GRUB_ERR_NONE;
471 }
472
473 if (key_cmp (&leaf.key, key_in) > 0)
474 break;
475
476 have_last = 1;
477 leaf_last = leaf;
478 }
479
480 if (have_last)
481 {
482 grub_memcpy (key_out, &leaf_last.key, sizeof(*key_out));
483 *outsize = grub_le_to_cpu32 (leaf_last.size);
484 *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset);
485 if (desc)
486 return save_ref (desc, addr - sizeof (head), i - 1,
487 grub_le_to_cpu32 (head.nitems), 1);
488 return GRUB_ERR_NONE;
489 }
490 *outsize = 0;
491 *outaddr = 0;
492 grub_memset (key_out, 0, sizeof (*key_out));
493 if (desc)
494 return save_ref (desc, addr - sizeof (head), -1,
495 grub_le_to_cpu32 (head.nitems), 1);
496 return GRUB_ERR_NONE;
497 }
498 }
499 }
500
501 static grub_device_t
502 find_device (struct grub_btrfs_data *data, grub_uint64_t id,
503 int do_rescan)
504 {
505 grub_device_t dev_found = NULL;
506 auto int hook (const char *name);
507 int hook (const char *name)
508 {
509 grub_device_t dev;
510 grub_err_t err;
511 struct grub_btrfs_superblock sb;
512 dev = grub_device_open (name);
513 if (!dev)
514 return 0;
515 if (!dev->disk)
516 {
517 grub_device_close (dev);
518 return 0;
519 }
520 err = read_sblock (dev->disk, &sb);
521 if (err == GRUB_ERR_BAD_FS)
522 {
523 grub_device_close (dev);
524 grub_errno = GRUB_ERR_NONE;
525 return 0;
526 }
527 if (err)
528 {
529 grub_device_close (dev);
530 grub_print_error ();
531 return 0;
532 }
533 if (grub_memcmp (data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0
534 || sb.this_device.device_id != id)
535 {
536 grub_device_close (dev);
537 return 0;
538 }
539
540 dev_found = dev;
541 return 1;
542 }
543
544 unsigned i;
545
546 for (i = 0; i < data->n_devices_attached; i++)
547 if (id == data->devices_attached[i].id)
548 return data->devices_attached[i].dev;
549 if (do_rescan)
550 grub_device_iterate (hook);
551 if (!dev_found)
552 {
553 grub_error (GRUB_ERR_BAD_FS, "couldn't find a member device");
554 return NULL;
555 }
556 data->n_devices_attached++;
557 if (data->n_devices_attached > data->n_devices_allocated)
558 {
559 void *tmp;
560 data->n_devices_allocated = 2 * data->n_devices_attached + 1;
561 data->devices_attached
562 = grub_realloc (tmp = data->devices_attached,
563 data->n_devices_allocated
564 * sizeof (data->devices_attached[0]));
565 if (!data->devices_attached)
566 {
567 grub_device_close (dev_found);
568 data->devices_attached = tmp;
569 return NULL;
570 }
571 }
572 data->devices_attached[data->n_devices_attached - 1].id = id;
573 data->devices_attached[data->n_devices_attached - 1].dev = dev_found;
574 return dev_found;
575 }
576
577 static grub_err_t
578 grub_btrfs_read_logical (struct grub_btrfs_data *data,
579 grub_disk_addr_t addr,
580 void *buf, grub_size_t size)
581 {
582 while (size > 0)
583 {
584 grub_uint8_t *ptr;
585 struct grub_btrfs_key *key;
586 struct grub_btrfs_chunk_item *chunk;
587 grub_ssize_t csize;
588 grub_err_t err;
589 struct grub_btrfs_key key_out;
590 int challoc = 0;
591 grub_device_t dev;
592 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n",
593 addr);
594 for (ptr = data->sblock.bootstrap_mapping;
595 ptr < data->sblock.bootstrap_mapping
596 + sizeof (data->sblock.bootstrap_mapping)
597 - sizeof (struct grub_btrfs_key);
598 )
599 {
600 key = (struct grub_btrfs_key *) ptr;
601 if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK)
602 break;
603 chunk = (struct grub_btrfs_chunk_item *) (key + 1);
604 grub_dprintf ("btrfs", "%" PRIxGRUB_UINT64_T " %" PRIxGRUB_UINT64_T " \n",
605 grub_le_to_cpu64 (key->offset),
606 grub_le_to_cpu64 (chunk->size));
607 if (grub_le_to_cpu64 (key->offset) <= addr
608 && addr < grub_le_to_cpu64 (key->offset)
609 + grub_le_to_cpu64 (chunk->size))
610 goto chunk_found;
611 ptr += sizeof (*key) + sizeof (*chunk)
612 + sizeof (struct grub_btrfs_chunk_stripe)
613 * grub_le_to_cpu16 (chunk->nstripes);
614 }
615 struct grub_btrfs_key key_in;
616 grub_size_t chsize;
617 grub_disk_addr_t chaddr;
618 key_in.object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
619 key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK;
620 key_in.offset = addr;
621 err = lower_bound (data, &key_in, &key_out,
622 grub_le_to_cpu64 (data->sblock.chunk_tree),
623 &chaddr, &chsize, NULL);
624 if (err)
625 return err;
626 key = &key_out;
627 if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK
628 || !(grub_le_to_cpu64 (key->offset) <= addr))
629 return grub_error (GRUB_ERR_BAD_FS,
630 "couldn't find the chunk descriptor");
631
632 chunk = grub_malloc (chsize);
633 if (!chunk)
634 return grub_errno;
635
636 challoc = 1;
637 err = grub_btrfs_read_logical (data, chaddr, chunk, chsize);
638 if (err)
639 {
640 grub_free (chunk);
641 return err;
642 }
643
644 chunk_found:
645 {
646 grub_uint32_t stripen;
647 grub_uint32_t stripe_offset;
648 grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset);
649 unsigned redundancy = 1;
650 unsigned i, j;
651
652 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
653 "+0x%" PRIxGRUB_UINT64_T
654 " (%d stripes (%d substripes) of %"
655 PRIxGRUB_UINT64_T ")\n",
656 grub_le_to_cpu64 (key->offset),
657 grub_le_to_cpu64 (chunk->size),
658 grub_le_to_cpu16 (chunk->nstripes),
659 grub_le_to_cpu16 (chunk->nsubstripes),
660 grub_le_to_cpu64 (chunk->stripe_length));
661
662 switch (grub_le_to_cpu64 (chunk->type)
663 & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE)
664 {
665 case GRUB_BTRFS_CHUNK_TYPE_SINGLE:
666 {
667 grub_uint32_t stripe_length;
668 stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
669 grub_le_to_cpu16 (chunk->nstripes),
670 NULL);
671 stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
672 csize = (stripen + 1) * stripe_length - off;
673 break;
674 }
675 case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED:
676 case GRUB_BTRFS_CHUNK_TYPE_RAID1:
677 {
678 stripen = 0;
679 stripe_offset = off;
680 csize = grub_le_to_cpu64 (chunk->size) - off;
681 redundancy = 2;
682 break;
683 }
684 case GRUB_BTRFS_CHUNK_TYPE_RAID0:
685 {
686 grub_uint64_t middle, high;
687 grub_uint32_t low;
688 middle = grub_divmod64 (off,
689 grub_le_to_cpu64 (chunk->stripe_length),
690 &low);
691
692 high = grub_divmod64 (middle, grub_le_to_cpu16 (chunk->nstripes),
693 &stripen);
694 stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
695 * high;
696 csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
697 break;
698 }
699 case GRUB_BTRFS_CHUNK_TYPE_RAID10:
700 {
701 grub_uint64_t middle, high;
702 grub_uint32_t low;
703 middle = grub_divmod64 (off,
704 grub_le_to_cpu64 (chunk->stripe_length),
705 &low);
706
707 high = grub_divmod64 (middle,
708 grub_le_to_cpu16 (chunk->nsubstripes),
709 &stripen);
710 stripen *= grub_le_to_cpu16 (chunk->nstripes)
711 / grub_le_to_cpu16 (chunk->nsubstripes);
712 redundancy = grub_le_to_cpu16 (chunk->nstripes)
713 / grub_le_to_cpu16 (chunk->nsubstripes);
714 stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
715 * high;
716 csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
717 break;
718 }
719 default:
720 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
721 "unsupported RAID flags %" PRIxGRUB_UINT64_T,
722 grub_le_to_cpu64 (chunk->type));
723 }
724 if (csize <= 0)
725 return grub_error (GRUB_ERR_BAD_FS,
726 "couldn't find the chunk descriptor");
727 if ((grub_size_t) csize > size)
728 csize = size;
729
730 for (j = 0; j < 2; j++)
731 {
732 for (i = 0; i < redundancy; i++)
733 {
734 struct grub_btrfs_chunk_stripe *stripe;
735 grub_disk_addr_t paddr;
736
737 stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
738 /* Right now the redundancy handling is easy.
739 With RAID5-like it will be more difficult. */
740 stripe += stripen + i;
741
742 paddr = stripe->offset + stripe_offset;
743
744 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
745 "+0x%" PRIxGRUB_UINT64_T " (%d stripes (%d substripes) of %"
746 PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT32_T
747 " maps to 0x%" PRIxGRUB_UINT64_T "\n",
748 grub_le_to_cpu64 (key->offset),
749 grub_le_to_cpu64 (chunk->size),
750 grub_le_to_cpu16 (chunk->nstripes),
751 grub_le_to_cpu16 (chunk->nsubstripes),
752 grub_le_to_cpu64 (chunk->stripe_length),
753 stripen, stripe->offset);
754 grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
755 " for laddr 0x%" PRIxGRUB_UINT64_T"\n", paddr,
756 addr);
757
758 dev = find_device (data, stripe->device_id, j);
759 if (!dev)
760 {
761 err = grub_errno;
762 grub_errno = GRUB_ERR_NONE;
763 continue;
764 }
765
766 err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
767 paddr & (GRUB_DISK_SECTOR_SIZE - 1),
768 csize, buf);
769 if (!err)
770 break;
771 grub_errno = GRUB_ERR_NONE;
772 }
773 if (i != redundancy)
774 break;
775 }
776 if (err)
777 return grub_errno = err;
778 }
779 size -= csize;
780 buf = (grub_uint8_t *) buf + csize;
781 addr += csize;
782 if (challoc)
783 grub_free (chunk);
784 }
785 return GRUB_ERR_NONE;
786 }
787
788 static struct grub_btrfs_data *
789 grub_btrfs_mount (grub_device_t dev)
790 {
791 struct grub_btrfs_data *data;
792 grub_err_t err;
793
794 if (!dev->disk)
795 {
796 grub_error (GRUB_ERR_BAD_FS, "not BtrFS");
797 return NULL;
798 }
799
800 data = grub_zalloc (sizeof (*data));
801 if (! data)
802 return NULL;
803
804 err = read_sblock (dev->disk, &data->sblock);
805 if (err)
806 {
807 grub_free (data);
808 return NULL;
809 }
810
811 data->n_devices_allocated = 16;
812 data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
813 * data->n_devices_allocated);
814 if (!data->devices_attached)
815 {
816 grub_free (data);
817 return NULL;
818 }
819 data->n_devices_attached = 1;
820 data->devices_attached[0].dev = dev;
821 data->devices_attached[0].id = data->sblock.this_device.device_id;
822
823 return data;
824 }
825
826 static void
827 grub_btrfs_unmount (struct grub_btrfs_data *data)
828 {
829 unsigned i;
830 /* The device 0 is closed one layer upper. */
831 for (i = 1; i < data->n_devices_attached; i++)
832 grub_device_close (data->devices_attached[i].dev);
833 grub_free (data->devices_attached);
834 grub_free (data->extent);
835 grub_free (data);
836 }
837
838 static grub_err_t
839 grub_btrfs_read_inode (struct grub_btrfs_data *data,
840 struct grub_btrfs_inode *inode, grub_uint64_t num,
841 grub_uint64_t tree)
842 {
843 struct grub_btrfs_key key_in, key_out;
844 grub_disk_addr_t elemaddr;
845 grub_size_t elemsize;
846 grub_err_t err;
847
848 key_in.object_id = num;
849 key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
850 key_in.offset = 0;
851
852 err = lower_bound (data, &key_in, &key_out, tree,
853 &elemaddr, &elemsize, NULL);
854 if (err)
855 return err;
856 if (num != key_out.object_id
857 || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM)
858 return grub_error (GRUB_ERR_BAD_FS, "inode not found");
859
860 return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode));
861 }
862
863 static grub_ssize_t
864 grub_btrfs_extent_read (struct grub_btrfs_data *data,
865 grub_uint64_t ino, grub_uint64_t tree,
866 grub_off_t pos0, char *buf, grub_size_t len)
867 {
868 grub_off_t pos = pos0;
869 while (len)
870 {
871 grub_size_t csize;
872 grub_err_t err;
873 grub_off_t extoff;
874 if (!data->extent || data->extstart > pos || data->extino != ino
875 || data->exttree != tree
876 || grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
877 {
878 struct grub_btrfs_key key_in, key_out;
879 grub_disk_addr_t elemaddr;
880 grub_size_t elemsize;
881 grub_free (data->extent);
882 key_in.object_id = ino;
883 key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
884 key_in.offset = grub_cpu_to_le64 (pos);
885 err = lower_bound (data, &key_in, &key_out, tree,
886 &elemaddr, &elemsize, NULL);
887 if (err)
888 return -1;
889 if (key_out.object_id != ino
890 || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
891 {
892 grub_error (GRUB_ERR_BAD_FS, "extent not found");
893 return -1;
894 }
895 data->extstart = grub_le_to_cpu64 (key_out.offset);
896 data->extsize = elemsize;
897 data->extent = grub_malloc (elemsize);
898 data->extino = ino;
899 data->exttree = tree;
900 if (!data->extent)
901 return grub_errno;
902
903 err = grub_btrfs_read_logical (data, elemaddr,
904 data->extent, elemsize);
905 if (err)
906 return err;
907 if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
908 return grub_error (GRUB_ERR_BAD_FS, "extent not found");
909 grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%"
910 PRIxGRUB_UINT64_T "\n",
911 grub_le_to_cpu64 (key_out.offset),
912 grub_le_to_cpu64 (data->extent->size));
913 }
914 csize = grub_le_to_cpu64 (data->extent->size)
915 + grub_le_to_cpu64 (data->extstart) - pos;
916 extoff = pos - data->extstart;
917 if (csize > len)
918 csize = len;
919
920 if (data->extent->encryption)
921 {
922 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
923 "encryption not supported");
924 return -1;
925 }
926
927 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
928 && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB)
929 {
930 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
931 "compression type 0x%x not supported",
932 data->extent->compression);
933 return -1;
934 }
935
936 if (data->extent->encoding)
937 {
938 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
939 "encoding not supported");
940 return -1;
941 }
942
943 switch (data->extent->type)
944 {
945 case GRUB_BTRFS_EXTENT_INLINE:
946 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
947 {
948 if (grub_zlib_decompress (data->extent->inl, data->extsize -
949 ((grub_uint8_t *) data->extent->inl
950 - (grub_uint8_t *) data->extent),
951 extoff, buf, csize)
952 != (grub_ssize_t) csize)
953 return -1;
954 }
955 else
956 grub_memcpy (buf, data->extent->inl + extoff, csize);
957 break;
958 case GRUB_BTRFS_EXTENT_REGULAR:
959 if (!data->extent->laddr)
960 {
961 grub_memset (buf, 0, csize);
962 break;
963 }
964 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
965 {
966 char *tmp;
967 grub_uint64_t zsize;
968 zsize = grub_le_to_cpu64 (data->extent->compressed_size);
969 tmp = grub_malloc (zsize);
970 if (!tmp)
971 return -1;
972 err = grub_btrfs_read_logical (data,
973 grub_le_to_cpu64 (data->extent->laddr),
974 tmp, zsize);
975 if (err)
976 {
977 grub_free (tmp);
978 return -1;
979 }
980 if (grub_zlib_decompress (tmp, zsize, extoff
981 + grub_le_to_cpu64 (data->extent->offset),
982 buf, csize) != (grub_ssize_t) csize)
983 {
984 grub_free (tmp);
985 return -1;
986 }
987 grub_free (tmp);
988 break;
989 }
990 err = grub_btrfs_read_logical (data,
991 grub_le_to_cpu64 (data->extent->laddr)
992 + extoff,
993 buf, csize);
994 if (err)
995 return -1;
996 break;
997 default:
998 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
999 "unsupported extent type 0x%x", data->extent->type);
1000 return -1;
1001 }
1002 buf += csize;
1003 pos += csize;
1004 len -= csize;
1005 }
1006 return pos - pos0;
1007 }
1008
1009 static grub_err_t
1010 find_path (struct grub_btrfs_data *data,
1011 const char *path, struct grub_btrfs_key *key,
1012 grub_uint64_t *tree, grub_uint8_t *type)
1013 {
1014 const char *slash = path;
1015 grub_err_t err;
1016 grub_disk_addr_t elemaddr;
1017 grub_size_t elemsize;
1018 grub_size_t allocated = 0;
1019 struct grub_btrfs_dir_item *direl = NULL;
1020 struct grub_btrfs_key key_out;
1021 int skip_default;
1022 const char *ctoken;
1023 grub_size_t ctokenlen;
1024 char *path_alloc = NULL;
1025 unsigned symlinks_max = 32;
1026
1027 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1028 *tree = data->sblock.root_tree;
1029 key->object_id = data->sblock.root_dir_objectid;
1030 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1031 key->offset = 0;
1032 skip_default = 1;
1033
1034 while (1)
1035 {
1036 if (!skip_default)
1037 {
1038 while (path[0] == '/')
1039 path++;
1040 if (!path[0])
1041 break;
1042 slash = grub_strchr (path, '/');
1043 if (!slash)
1044 slash = path + grub_strlen (path);
1045 ctoken = path;
1046 ctokenlen = slash - path;
1047 }
1048 else
1049 {
1050 ctoken = "default";
1051 ctokenlen = sizeof ("default") - 1;
1052 }
1053
1054 if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1055 {
1056 grub_free (path_alloc);
1057 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1058 }
1059
1060 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1061 key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
1062
1063 err = lower_bound (data, key, &key_out, *tree,
1064 &elemaddr, &elemsize, NULL);
1065 if (err)
1066 {
1067 grub_free (direl);
1068 grub_free (path_alloc);
1069 return err;
1070 }
1071 if (key_cmp (key, &key_out) != 0)
1072 {
1073 grub_free (direl);
1074 grub_free (path_alloc);
1075 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1076 }
1077
1078 struct grub_btrfs_dir_item *cdirel;
1079 if (elemsize > allocated)
1080 {
1081 allocated = 2 * elemsize;
1082 grub_free (direl);
1083 direl = grub_malloc (allocated + 1);
1084 if (!direl)
1085 {
1086 grub_free (path_alloc);
1087 return grub_errno;
1088 }
1089 }
1090
1091 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
1092 if (err)
1093 {
1094 grub_free (direl);
1095 grub_free (path_alloc);
1096 return err;
1097 }
1098
1099 for (cdirel = direl;
1100 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1101 < (grub_ssize_t) elemsize;
1102 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1103 + grub_le_to_cpu16 (cdirel->n)
1104 + grub_le_to_cpu16 (cdirel->m)))
1105 {
1106 char c;
1107 c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
1108 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
1109 if (grub_strncmp (cdirel->name, ctoken, ctokenlen) == 0)
1110 break;
1111 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1112 }
1113 if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1114 >= (grub_ssize_t) elemsize)
1115 {
1116 grub_free (direl);
1117 grub_free (path_alloc);
1118 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1119 }
1120
1121 if (!skip_default)
1122 path = slash;
1123 skip_default = 0;
1124 if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
1125 {
1126 struct grub_btrfs_inode inode;
1127 char *tmp;
1128 if (--symlinks_max == 0)
1129 {
1130 grub_free (direl);
1131 grub_free (path_alloc);
1132 return grub_error (GRUB_ERR_SYMLINK_LOOP,
1133 "too deep nesting of symlinks");
1134 }
1135
1136 err = grub_btrfs_read_inode (data, &inode,
1137 cdirel->key.object_id, *tree);
1138 if (err)
1139 {
1140 grub_free (direl);
1141 grub_free (path_alloc);
1142 return err;
1143 }
1144 tmp = grub_malloc (grub_le_to_cpu64 (inode.size)
1145 + grub_strlen (path) + 1);
1146 if (!tmp)
1147 {
1148 grub_free (direl);
1149 grub_free (path_alloc);
1150 return grub_errno;
1151 }
1152
1153 if (grub_btrfs_extent_read (data, cdirel->key.object_id,
1154 *tree, 0, tmp,
1155 grub_le_to_cpu64 (inode.size))
1156 != (grub_ssize_t) grub_le_to_cpu64 (inode.size))
1157 {
1158 grub_free (direl);
1159 grub_free (path_alloc);
1160 grub_free (tmp);
1161 return grub_errno;
1162 }
1163 grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path,
1164 grub_strlen (path) + 1);
1165 grub_free (path_alloc);
1166 path = path_alloc = tmp;
1167 if (path[0] == '/')
1168 {
1169 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1170 *tree = data->sblock.root_tree;
1171 key->object_id = data->sblock.root_dir_objectid;
1172 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1173 key->offset = 0;
1174 skip_default = 1;
1175 }
1176 continue;
1177 }
1178 *type = cdirel->type;
1179
1180 switch (cdirel->key.type)
1181 {
1182 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
1183 {
1184 struct grub_btrfs_root_item ri;
1185 err = lower_bound (data, &cdirel->key, &key_out,
1186 data->sblock.root_tree,
1187 &elemaddr, &elemsize, NULL);
1188 if (err)
1189 {
1190 grub_free (direl);
1191 grub_free (path_alloc);
1192 return err;
1193 }
1194 if (cdirel->key.object_id != key_out.object_id
1195 || cdirel->key.type != key_out.type)
1196 {
1197 grub_free (direl);
1198 grub_free (path_alloc);
1199 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1200 }
1201 err = grub_btrfs_read_logical (data, elemaddr,
1202 &ri, sizeof (ri));
1203 if (err)
1204 {
1205 grub_free (direl);
1206 grub_free (path_alloc);
1207 return err;
1208 }
1209 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1210 key->offset = 0;
1211 key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
1212 *tree = grub_le_to_cpu64 (ri.tree);
1213 break;
1214 }
1215 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM:
1216 if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1217 {
1218 grub_free (direl);
1219 grub_free (path_alloc);
1220 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1221 }
1222 *key = cdirel->key;
1223 if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1224 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1225 break;
1226 default:
1227 grub_free (path_alloc);
1228 grub_free (direl);
1229 return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x",
1230 cdirel->key.type);
1231 }
1232 }
1233
1234 grub_free (direl);
1235 return GRUB_ERR_NONE;
1236 }
1237
1238 static grub_err_t
1239 grub_btrfs_dir (grub_device_t device, const char *path,
1240 int (*hook) (const char *filename,
1241 const struct grub_dirhook_info *info))
1242 {
1243 struct grub_btrfs_data *data = grub_btrfs_mount (device);
1244 struct grub_btrfs_key key_in, key_out;
1245 grub_err_t err;
1246 grub_disk_addr_t elemaddr;
1247 grub_size_t elemsize;
1248 grub_size_t allocated = 0;
1249 struct grub_btrfs_dir_item *direl = NULL;
1250 struct grub_btrfs_leaf_descriptor desc;
1251 int r = 0;
1252 grub_uint64_t tree;
1253 grub_uint8_t type;
1254
1255 if (!data)
1256 return grub_errno;
1257
1258 err = find_path (data, path, &key_in, &tree, &type);
1259 if (err)
1260 return err;
1261 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1262 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1263
1264 err = lower_bound (data, &key_in, &key_out, tree,
1265 &elemaddr, &elemsize, &desc);
1266 if (err)
1267 return err;
1268 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1269 || key_out.object_id != key_in.object_id)
1270 {
1271 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1272 if (r <= 0)
1273 {
1274 free_iterator (&desc);
1275 return -r;
1276 }
1277 }
1278 do
1279 {
1280 struct grub_btrfs_dir_item *cdirel;
1281 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1282 || key_out.object_id != key_in.object_id)
1283 {
1284 r = 0;
1285 break;
1286 }
1287 if (elemsize > allocated)
1288 {
1289 allocated = 2 * elemsize;
1290 grub_free (direl);
1291 direl = grub_malloc (allocated + 1);
1292 if (!direl)
1293 {
1294 free_iterator (&desc);
1295 return grub_errno;
1296 }
1297 }
1298
1299 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
1300 if (err)
1301 return err;
1302
1303 for (cdirel = direl;
1304 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1305 < (grub_ssize_t) elemsize;
1306 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1307 + grub_le_to_cpu16 (cdirel->n)
1308 + grub_le_to_cpu16 (cdirel->m)))
1309 {
1310 char c;
1311 struct grub_btrfs_inode inode;
1312 struct grub_dirhook_info info;
1313 err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
1314 tree);
1315 grub_memset (&info, 0, sizeof (info));
1316 if (err)
1317 grub_errno = GRUB_ERR_NONE;
1318 else
1319 {
1320 info.mtime = inode.mtime.sec;
1321 info.mtimeset = 1;
1322 }
1323 c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
1324 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
1325 info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
1326 if (hook (cdirel->name, &info))
1327 goto out;
1328 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1329 }
1330 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1331 }
1332 while (r > 0);
1333
1334 out:
1335 grub_free (direl);
1336
1337 free_iterator (&desc);
1338 grub_btrfs_unmount (data);
1339
1340 return -r;
1341 }
1342
1343 static grub_err_t
1344 grub_btrfs_open (struct grub_file *file, const char *name)
1345 {
1346 struct grub_btrfs_data *data = grub_btrfs_mount (file->device);
1347 grub_err_t err;
1348 struct grub_btrfs_inode inode;
1349 grub_uint8_t type;
1350 struct grub_btrfs_key key_in;
1351
1352 if (!data)
1353 return grub_errno;
1354
1355 err = find_path (data, name, &key_in, &data->tree, &type);
1356 if (err)
1357 {
1358 grub_btrfs_unmount (data);
1359 return err;
1360 }
1361 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1362 {
1363 grub_btrfs_unmount (data);
1364 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
1365 }
1366
1367 data->inode = key_in.object_id;
1368 err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
1369 if (err)
1370 {
1371 grub_btrfs_unmount (data);
1372 return err;
1373 }
1374
1375 file->data = data;
1376 file->size = grub_le_to_cpu64 (inode.size);
1377
1378 return err;
1379 }
1380
1381 static grub_err_t
1382 grub_btrfs_close (grub_file_t file)
1383 {
1384 grub_btrfs_unmount (file->data);
1385
1386 return GRUB_ERR_NONE;
1387 }
1388
1389 static grub_ssize_t
1390 grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
1391 {
1392 struct grub_btrfs_data *data = file->data;
1393
1394 return grub_btrfs_extent_read (data, data->inode,
1395 data->tree, file->offset, buf, len);
1396 }
1397
1398 static grub_err_t
1399 grub_btrfs_uuid (grub_device_t device, char **uuid)
1400 {
1401 struct grub_btrfs_data *data;
1402
1403 *uuid = NULL;
1404
1405 data = grub_btrfs_mount (device);
1406 if (! data)
1407 return grub_errno;
1408
1409 *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1410 grub_be_to_cpu16 (data->sblock.uuid[0]),
1411 grub_be_to_cpu16 (data->sblock.uuid[1]),
1412 grub_be_to_cpu16 (data->sblock.uuid[2]),
1413 grub_be_to_cpu16 (data->sblock.uuid[3]),
1414 grub_be_to_cpu16 (data->sblock.uuid[4]),
1415 grub_be_to_cpu16 (data->sblock.uuid[5]),
1416 grub_be_to_cpu16 (data->sblock.uuid[6]),
1417 grub_be_to_cpu16 (data->sblock.uuid[7]));
1418
1419 grub_btrfs_unmount (data);
1420
1421 return grub_errno;
1422 }
1423
1424 static grub_err_t
1425 grub_btrfs_label (grub_device_t device, char **label)
1426 {
1427 struct grub_btrfs_data *data;
1428
1429 *label = NULL;
1430
1431 data = grub_btrfs_mount (device);
1432 if (! data)
1433 return grub_errno;
1434
1435 *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
1436
1437 grub_btrfs_unmount (data);
1438
1439 return grub_errno;
1440 }
1441
1442 static struct grub_fs grub_btrfs_fs =
1443 {
1444 .name = "btrfs",
1445 .dir = grub_btrfs_dir,
1446 .open = grub_btrfs_open,
1447 .read = grub_btrfs_read,
1448 .close = grub_btrfs_close,
1449 .uuid = grub_btrfs_uuid,
1450 .label = grub_btrfs_label,
1451 };
1452
1453 GRUB_MOD_INIT(btrfs)
1454 {
1455 grub_fs_register (&grub_btrfs_fs);
1456 }
1457
1458 GRUB_MOD_FINI(btrfs)
1459 {
1460 grub_fs_unregister (&grub_btrfs_fs);
1461 }