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