Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / btrfs.c
blob9cffa91fafb081c4a161b38e0710b28c19f48aa0
1 /* btrfs.c - B-tree file system. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010,2011,2012,2013 Free Software Foundation, Inc.
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.
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.
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/>.
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 #include <minilzo.h>
30 #include <grub/i18n.h>
31 #include <grub/btrfs.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
37 /* From http://www.oberhumer.com/opensource/lzo/lzofaq.php
38 * LZO will expand incompressible data by a little amount. I still haven't
39 * computed the exact values, but I suggest using these formulas for
40 * a worst-case expansion calculation:
42 * output_block_size = input_block_size + (input_block_size / 16) + 64 + 3
43 * */
44 #define GRUB_BTRFS_LZO_BLOCK_SIZE 4096
45 #define GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE (GRUB_BTRFS_LZO_BLOCK_SIZE + \
46 (GRUB_BTRFS_LZO_BLOCK_SIZE / 16) + 64 + 3)
48 typedef grub_uint8_t grub_btrfs_checksum_t[0x20];
49 typedef grub_uint16_t grub_btrfs_uuid_t[8];
51 struct grub_btrfs_device
53 grub_uint64_t device_id;
54 grub_uint64_t size;
55 grub_uint8_t dummy[0x62 - 0x10];
56 } GRUB_PACKED;
58 struct grub_btrfs_superblock
60 grub_btrfs_checksum_t checksum;
61 grub_btrfs_uuid_t uuid;
62 grub_uint8_t dummy[0x10];
63 grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1];
64 grub_uint64_t generation;
65 grub_uint64_t root_tree;
66 grub_uint64_t chunk_tree;
67 grub_uint8_t dummy2[0x20];
68 grub_uint64_t root_dir_objectid;
69 grub_uint8_t dummy3[0x41];
70 struct grub_btrfs_device this_device;
71 char label[0x100];
72 grub_uint8_t dummy4[0x100];
73 grub_uint8_t bootstrap_mapping[0x800];
74 } GRUB_PACKED;
76 struct btrfs_header
78 grub_btrfs_checksum_t checksum;
79 grub_btrfs_uuid_t uuid;
80 grub_uint8_t dummy[0x30];
81 grub_uint32_t nitems;
82 grub_uint8_t level;
83 } GRUB_PACKED;
85 struct grub_btrfs_device_desc
87 grub_device_t dev;
88 grub_uint64_t id;
91 struct grub_btrfs_data
93 struct grub_btrfs_superblock sblock;
94 grub_uint64_t tree;
95 grub_uint64_t inode;
97 struct grub_btrfs_device_desc *devices_attached;
98 unsigned n_devices_attached;
99 unsigned n_devices_allocated;
101 /* Cached extent data. */
102 grub_uint64_t extstart;
103 grub_uint64_t extend;
104 grub_uint64_t extino;
105 grub_uint64_t exttree;
106 grub_size_t extsize;
107 struct grub_btrfs_extent_data *extent;
110 struct grub_btrfs_chunk_item
112 grub_uint64_t size;
113 grub_uint64_t dummy;
114 grub_uint64_t stripe_length;
115 grub_uint64_t type;
116 #define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07
117 #define GRUB_BTRFS_CHUNK_TYPE_SINGLE 0x00
118 #define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08
119 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
120 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20
121 #define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40
122 grub_uint8_t dummy2[0xc];
123 grub_uint16_t nstripes;
124 grub_uint16_t nsubstripes;
125 } GRUB_PACKED;
127 struct grub_btrfs_chunk_stripe
129 grub_uint64_t device_id;
130 grub_uint64_t offset;
131 grub_btrfs_uuid_t device_uuid;
132 } GRUB_PACKED;
134 struct grub_btrfs_leaf_node
136 struct grub_btrfs_key key;
137 grub_uint32_t offset;
138 grub_uint32_t size;
139 } GRUB_PACKED;
141 struct grub_btrfs_internal_node
143 struct grub_btrfs_key key;
144 grub_uint64_t addr;
145 grub_uint64_t dummy;
146 } GRUB_PACKED;
148 struct grub_btrfs_dir_item
150 struct grub_btrfs_key key;
151 grub_uint8_t dummy[8];
152 grub_uint16_t m;
153 grub_uint16_t n;
154 #define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1
155 #define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2
156 #define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7
157 grub_uint8_t type;
158 char name[0];
159 } GRUB_PACKED;
161 struct grub_btrfs_leaf_descriptor
163 unsigned depth;
164 unsigned allocated;
165 struct
167 grub_disk_addr_t addr;
168 unsigned iter;
169 unsigned maxiter;
170 int leaf;
171 } *data;
174 struct grub_btrfs_time
176 grub_int64_t sec;
177 grub_uint32_t nanosec;
178 } __attribute__ ((aligned (4)));
180 struct grub_btrfs_inode
182 grub_uint8_t dummy1[0x10];
183 grub_uint64_t size;
184 grub_uint8_t dummy2[0x70];
185 struct grub_btrfs_time mtime;
186 } GRUB_PACKED;
188 struct grub_btrfs_extent_data
190 grub_uint64_t dummy;
191 grub_uint64_t size;
192 grub_uint8_t compression;
193 grub_uint8_t encryption;
194 grub_uint16_t encoding;
195 grub_uint8_t type;
196 union
198 char inl[0];
199 struct
201 grub_uint64_t laddr;
202 grub_uint64_t compressed_size;
203 grub_uint64_t offset;
204 grub_uint64_t filled;
207 } GRUB_PACKED;
209 #define GRUB_BTRFS_EXTENT_INLINE 0
210 #define GRUB_BTRFS_EXTENT_REGULAR 1
212 #define GRUB_BTRFS_COMPRESSION_NONE 0
213 #define GRUB_BTRFS_COMPRESSION_ZLIB 1
214 #define GRUB_BTRFS_COMPRESSION_LZO 2
216 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
218 static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
219 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2
222 static grub_err_t
223 grub_btrfs_read_logical (struct grub_btrfs_data *data,
224 grub_disk_addr_t addr, void *buf, grub_size_t size,
225 int recursion_depth);
227 static grub_err_t
228 read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
230 unsigned i;
231 grub_err_t err = GRUB_ERR_NONE;
232 for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++)
234 struct grub_btrfs_superblock sblock;
235 /* Don't try additional superblocks beyond device size. */
236 if (i && (grub_le_to_cpu64 (sblock.this_device.size)
237 >> GRUB_DISK_SECTOR_BITS) <= superblock_sectors[i])
238 break;
239 err = grub_disk_read (disk, superblock_sectors[i], 0,
240 sizeof (sblock), &sblock);
241 if (err == GRUB_ERR_OUT_OF_RANGE)
242 break;
244 if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE,
245 sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0)
246 break;
247 if (i == 0 || grub_le_to_cpu64 (sblock.generation)
248 > grub_le_to_cpu64 (sb->generation))
249 grub_memcpy (sb, &sblock, sizeof (sblock));
252 if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0)
253 return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem");
255 if (err == GRUB_ERR_OUT_OF_RANGE)
256 grub_errno = err = GRUB_ERR_NONE;
258 return err;
261 static int
262 key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b)
264 if (grub_le_to_cpu64 (a->object_id) < grub_le_to_cpu64 (b->object_id))
265 return -1;
266 if (grub_le_to_cpu64 (a->object_id) > grub_le_to_cpu64 (b->object_id))
267 return +1;
269 if (a->type < b->type)
270 return -1;
271 if (a->type > b->type)
272 return +1;
274 if (grub_le_to_cpu64 (a->offset) < grub_le_to_cpu64 (b->offset))
275 return -1;
276 if (grub_le_to_cpu64 (a->offset) > grub_le_to_cpu64 (b->offset))
277 return +1;
278 return 0;
281 static void
282 free_iterator (struct grub_btrfs_leaf_descriptor *desc)
284 grub_free (desc->data);
287 static grub_err_t
288 save_ref (struct grub_btrfs_leaf_descriptor *desc,
289 grub_disk_addr_t addr, unsigned i, unsigned m, int l)
291 desc->depth++;
292 if (desc->allocated < desc->depth)
294 void *newdata;
295 desc->allocated *= 2;
296 newdata = grub_realloc (desc->data, sizeof (desc->data[0])
297 * desc->allocated);
298 if (!newdata)
299 return grub_errno;
300 desc->data = newdata;
302 desc->data[desc->depth - 1].addr = addr;
303 desc->data[desc->depth - 1].iter = i;
304 desc->data[desc->depth - 1].maxiter = m;
305 desc->data[desc->depth - 1].leaf = l;
306 return GRUB_ERR_NONE;
309 static int
310 next (struct grub_btrfs_data *data,
311 struct grub_btrfs_leaf_descriptor *desc,
312 grub_disk_addr_t * outaddr, grub_size_t * outsize,
313 struct grub_btrfs_key *key_out)
315 grub_err_t err;
316 struct grub_btrfs_leaf_node leaf;
318 for (; desc->depth > 0; desc->depth--)
320 desc->data[desc->depth - 1].iter++;
321 if (desc->data[desc->depth - 1].iter
322 < desc->data[desc->depth - 1].maxiter)
323 break;
325 if (desc->depth == 0)
326 return 0;
327 while (!desc->data[desc->depth - 1].leaf)
329 struct grub_btrfs_internal_node node;
330 struct btrfs_header head;
332 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
333 * sizeof (node)
334 + sizeof (struct btrfs_header)
335 + desc->data[desc->depth - 1].addr,
336 &node, sizeof (node), 0);
337 if (err)
338 return -err;
340 err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr),
341 &head, sizeof (head), 0);
342 if (err)
343 return -err;
345 save_ref (desc, grub_le_to_cpu64 (node.addr), 0,
346 grub_le_to_cpu32 (head.nitems), !head.level);
348 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
349 * sizeof (leaf)
350 + sizeof (struct btrfs_header)
351 + desc->data[desc->depth - 1].addr, &leaf,
352 sizeof (leaf), 0);
353 if (err)
354 return -err;
355 *outsize = grub_le_to_cpu32 (leaf.size);
356 *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header)
357 + grub_le_to_cpu32 (leaf.offset);
358 *key_out = leaf.key;
359 return 1;
362 static grub_err_t
363 lower_bound (struct grub_btrfs_data *data,
364 const struct grub_btrfs_key *key_in,
365 struct grub_btrfs_key *key_out,
366 grub_uint64_t root,
367 grub_disk_addr_t *outaddr, grub_size_t *outsize,
368 struct grub_btrfs_leaf_descriptor *desc,
369 int recursion_depth)
371 grub_disk_addr_t addr = grub_le_to_cpu64 (root);
372 int depth = -1;
374 if (desc)
376 desc->allocated = 16;
377 desc->depth = 0;
378 desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
379 if (!desc->data)
380 return grub_errno;
383 /* > 2 would work as well but be robust and allow a bit more just in case.
385 if (recursion_depth > 10)
386 return grub_error (GRUB_ERR_BAD_FS, "too deep btrfs virtual nesting");
388 grub_dprintf ("btrfs",
389 "retrieving %" PRIxGRUB_UINT64_T
390 " %x %" PRIxGRUB_UINT64_T "\n",
391 key_in->object_id, key_in->type, key_in->offset);
393 while (1)
395 grub_err_t err;
396 struct btrfs_header head;
398 reiter:
399 depth++;
400 /* FIXME: preread few nodes into buffer. */
401 err = grub_btrfs_read_logical (data, addr, &head, sizeof (head),
402 recursion_depth + 1);
403 if (err)
404 return err;
405 addr += sizeof (head);
406 if (head.level)
408 unsigned i;
409 struct grub_btrfs_internal_node node, node_last;
410 int have_last = 0;
411 grub_memset (&node_last, 0, sizeof (node_last));
412 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
414 err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
415 &node, sizeof (node),
416 recursion_depth + 1);
417 if (err)
418 return err;
420 grub_dprintf ("btrfs",
421 "internal node (depth %d) %" PRIxGRUB_UINT64_T
422 " %x %" PRIxGRUB_UINT64_T "\n", depth,
423 node.key.object_id, node.key.type,
424 node.key.offset);
426 if (key_cmp (&node.key, key_in) == 0)
428 err = GRUB_ERR_NONE;
429 if (desc)
430 err = save_ref (desc, addr - sizeof (head), i,
431 grub_le_to_cpu32 (head.nitems), 0);
432 if (err)
433 return err;
434 addr = grub_le_to_cpu64 (node.addr);
435 goto reiter;
437 if (key_cmp (&node.key, key_in) > 0)
438 break;
439 node_last = node;
440 have_last = 1;
442 if (have_last)
444 err = GRUB_ERR_NONE;
445 if (desc)
446 err = save_ref (desc, addr - sizeof (head), i - 1,
447 grub_le_to_cpu32 (head.nitems), 0);
448 if (err)
449 return err;
450 addr = grub_le_to_cpu64 (node_last.addr);
451 goto reiter;
453 *outsize = 0;
454 *outaddr = 0;
455 grub_memset (key_out, 0, sizeof (*key_out));
456 if (desc)
457 return save_ref (desc, addr - sizeof (head), -1,
458 grub_le_to_cpu32 (head.nitems), 0);
459 return GRUB_ERR_NONE;
462 unsigned i;
463 struct grub_btrfs_leaf_node leaf, leaf_last;
464 int have_last = 0;
465 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
467 err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
468 &leaf, sizeof (leaf),
469 recursion_depth + 1);
470 if (err)
471 return err;
473 grub_dprintf ("btrfs",
474 "leaf (depth %d) %" PRIxGRUB_UINT64_T
475 " %x %" PRIxGRUB_UINT64_T "\n", depth,
476 leaf.key.object_id, leaf.key.type, leaf.key.offset);
478 if (key_cmp (&leaf.key, key_in) == 0)
480 grub_memcpy (key_out, &leaf.key, sizeof (*key_out));
481 *outsize = grub_le_to_cpu32 (leaf.size);
482 *outaddr = addr + grub_le_to_cpu32 (leaf.offset);
483 if (desc)
484 return save_ref (desc, addr - sizeof (head), i,
485 grub_le_to_cpu32 (head.nitems), 1);
486 return GRUB_ERR_NONE;
489 if (key_cmp (&leaf.key, key_in) > 0)
490 break;
492 have_last = 1;
493 leaf_last = leaf;
496 if (have_last)
498 grub_memcpy (key_out, &leaf_last.key, sizeof (*key_out));
499 *outsize = grub_le_to_cpu32 (leaf_last.size);
500 *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset);
501 if (desc)
502 return save_ref (desc, addr - sizeof (head), i - 1,
503 grub_le_to_cpu32 (head.nitems), 1);
504 return GRUB_ERR_NONE;
506 *outsize = 0;
507 *outaddr = 0;
508 grub_memset (key_out, 0, sizeof (*key_out));
509 if (desc)
510 return save_ref (desc, addr - sizeof (head), -1,
511 grub_le_to_cpu32 (head.nitems), 1);
512 return GRUB_ERR_NONE;
517 /* Context for find_device. */
518 struct find_device_ctx
520 struct grub_btrfs_data *data;
521 grub_uint64_t id;
522 grub_device_t dev_found;
525 /* Helper for find_device. */
526 static int
527 find_device_iter (const char *name, void *data)
529 struct find_device_ctx *ctx = data;
530 grub_device_t dev;
531 grub_err_t err;
532 struct grub_btrfs_superblock sb;
534 dev = grub_device_open (name);
535 if (!dev)
536 return 0;
537 if (!dev->disk)
539 grub_device_close (dev);
540 return 0;
542 err = read_sblock (dev->disk, &sb);
543 if (err == GRUB_ERR_BAD_FS)
545 grub_device_close (dev);
546 grub_errno = GRUB_ERR_NONE;
547 return 0;
549 if (err)
551 grub_device_close (dev);
552 grub_print_error ();
553 return 0;
555 if (grub_memcmp (ctx->data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0
556 || sb.this_device.device_id != ctx->id)
558 grub_device_close (dev);
559 return 0;
562 ctx->dev_found = dev;
563 return 1;
566 static grub_device_t
567 find_device (struct grub_btrfs_data *data, grub_uint64_t id, int do_rescan)
569 struct find_device_ctx ctx = {
570 .data = data,
571 .id = id,
572 .dev_found = NULL
574 unsigned i;
576 for (i = 0; i < data->n_devices_attached; i++)
577 if (id == data->devices_attached[i].id)
578 return data->devices_attached[i].dev;
579 if (do_rescan)
580 grub_device_iterate (find_device_iter, &ctx);
581 if (!ctx.dev_found)
583 grub_error (GRUB_ERR_BAD_FS,
584 N_("couldn't find a necessary member device "
585 "of multi-device filesystem"));
586 return NULL;
588 data->n_devices_attached++;
589 if (data->n_devices_attached > data->n_devices_allocated)
591 void *tmp;
592 data->n_devices_allocated = 2 * data->n_devices_attached + 1;
593 data->devices_attached
594 = grub_realloc (tmp = data->devices_attached,
595 data->n_devices_allocated
596 * sizeof (data->devices_attached[0]));
597 if (!data->devices_attached)
599 grub_device_close (ctx.dev_found);
600 data->devices_attached = tmp;
601 return NULL;
604 data->devices_attached[data->n_devices_attached - 1].id = id;
605 data->devices_attached[data->n_devices_attached - 1].dev = ctx.dev_found;
606 return ctx.dev_found;
609 static grub_err_t
610 grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
611 void *buf, grub_size_t size, int recursion_depth)
613 while (size > 0)
615 grub_uint8_t *ptr;
616 struct grub_btrfs_key *key;
617 struct grub_btrfs_chunk_item *chunk;
618 grub_uint64_t csize;
619 grub_err_t err = 0;
620 struct grub_btrfs_key key_out;
621 int challoc = 0;
622 grub_device_t dev;
623 struct grub_btrfs_key key_in;
624 grub_size_t chsize;
625 grub_disk_addr_t chaddr;
627 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n",
628 addr);
629 for (ptr = data->sblock.bootstrap_mapping;
630 ptr < data->sblock.bootstrap_mapping
631 + sizeof (data->sblock.bootstrap_mapping)
632 - sizeof (struct grub_btrfs_key);)
634 key = (struct grub_btrfs_key *) ptr;
635 if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK)
636 break;
637 chunk = (struct grub_btrfs_chunk_item *) (key + 1);
638 grub_dprintf ("btrfs",
639 "%" PRIxGRUB_UINT64_T " %" PRIxGRUB_UINT64_T " \n",
640 grub_le_to_cpu64 (key->offset),
641 grub_le_to_cpu64 (chunk->size));
642 if (grub_le_to_cpu64 (key->offset) <= addr
643 && addr < grub_le_to_cpu64 (key->offset)
644 + grub_le_to_cpu64 (chunk->size))
645 goto chunk_found;
646 ptr += sizeof (*key) + sizeof (*chunk)
647 + sizeof (struct grub_btrfs_chunk_stripe)
648 * grub_le_to_cpu16 (chunk->nstripes);
651 key_in.object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
652 key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK;
653 key_in.offset = grub_cpu_to_le64 (addr);
654 err = lower_bound (data, &key_in, &key_out,
655 data->sblock.chunk_tree,
656 &chaddr, &chsize, NULL, recursion_depth);
657 if (err)
658 return err;
659 key = &key_out;
660 if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK
661 || !(grub_le_to_cpu64 (key->offset) <= addr))
662 return grub_error (GRUB_ERR_BAD_FS,
663 "couldn't find the chunk descriptor");
665 chunk = grub_malloc (chsize);
666 if (!chunk)
667 return grub_errno;
669 challoc = 1;
670 err = grub_btrfs_read_logical (data, chaddr, chunk, chsize,
671 recursion_depth);
672 if (err)
674 grub_free (chunk);
675 return err;
678 chunk_found:
680 grub_uint64_t stripen;
681 grub_uint64_t stripe_offset;
682 grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset);
683 grub_uint64_t chunk_stripe_length;
684 grub_uint16_t nstripes;
685 unsigned redundancy = 1;
686 unsigned i, j;
688 if (grub_le_to_cpu64 (chunk->size) <= off)
690 grub_dprintf ("btrfs", "no chunk\n");
691 return grub_error (GRUB_ERR_BAD_FS,
692 "couldn't find the chunk descriptor");
695 nstripes = grub_le_to_cpu16 (chunk->nstripes) ? : 1;
696 chunk_stripe_length = grub_le_to_cpu64 (chunk->stripe_length) ? : 512;
697 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
698 "+0x%" PRIxGRUB_UINT64_T
699 " (%d stripes (%d substripes) of %"
700 PRIxGRUB_UINT64_T ")\n",
701 grub_le_to_cpu64 (key->offset),
702 grub_le_to_cpu64 (chunk->size),
703 nstripes,
704 grub_le_to_cpu16 (chunk->nsubstripes),
705 chunk_stripe_length);
707 switch (grub_le_to_cpu64 (chunk->type)
708 & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE)
710 case GRUB_BTRFS_CHUNK_TYPE_SINGLE:
712 grub_uint64_t stripe_length;
713 grub_dprintf ("btrfs", "single\n");
714 stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
715 nstripes,
716 NULL);
717 if (stripe_length == 0)
718 stripe_length = 512;
719 stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
720 csize = (stripen + 1) * stripe_length - off;
721 break;
723 case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED:
724 case GRUB_BTRFS_CHUNK_TYPE_RAID1:
726 grub_dprintf ("btrfs", "RAID1\n");
727 stripen = 0;
728 stripe_offset = off;
729 csize = grub_le_to_cpu64 (chunk->size) - off;
730 redundancy = 2;
731 break;
733 case GRUB_BTRFS_CHUNK_TYPE_RAID0:
735 grub_uint64_t middle, high;
736 grub_uint64_t low;
737 grub_dprintf ("btrfs", "RAID0\n");
738 middle = grub_divmod64 (off,
739 chunk_stripe_length,
740 &low);
742 high = grub_divmod64 (middle, nstripes,
743 &stripen);
744 stripe_offset =
745 low + chunk_stripe_length * high;
746 csize = chunk_stripe_length - low;
747 break;
749 case GRUB_BTRFS_CHUNK_TYPE_RAID10:
751 grub_uint64_t middle, high;
752 grub_uint64_t low;
753 grub_uint16_t nsubstripes;
754 nsubstripes = grub_le_to_cpu16 (chunk->nsubstripes) ? : 1;
755 middle = grub_divmod64 (off,
756 chunk_stripe_length,
757 &low);
759 high = grub_divmod64 (middle,
760 nstripes / nsubstripes ? : 1,
761 &stripen);
762 stripen *= nsubstripes;
763 redundancy = nsubstripes;
764 stripe_offset = low + chunk_stripe_length
765 * high;
766 csize = chunk_stripe_length - low;
767 break;
769 default:
770 grub_dprintf ("btrfs", "unsupported RAID\n");
771 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
772 "unsupported RAID flags %" PRIxGRUB_UINT64_T,
773 grub_le_to_cpu64 (chunk->type));
775 if (csize == 0)
776 return grub_error (GRUB_ERR_BUG,
777 "couldn't find the chunk descriptor");
778 if (csize > (grub_uint64_t) size)
779 csize = size;
781 for (j = 0; j < 2; j++)
783 for (i = 0; i < redundancy; i++)
785 struct grub_btrfs_chunk_stripe *stripe;
786 grub_disk_addr_t paddr;
788 stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
789 /* Right now the redundancy handling is easy.
790 With RAID5-like it will be more difficult. */
791 stripe += stripen + i;
793 paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset;
795 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
796 "+0x%" PRIxGRUB_UINT64_T
797 " (%d stripes (%d substripes) of %"
798 PRIxGRUB_UINT64_T ") stripe %" PRIxGRUB_UINT64_T
799 " maps to 0x%" PRIxGRUB_UINT64_T "\n",
800 grub_le_to_cpu64 (key->offset),
801 grub_le_to_cpu64 (chunk->size),
802 grub_le_to_cpu16 (chunk->nstripes),
803 grub_le_to_cpu16 (chunk->nsubstripes),
804 grub_le_to_cpu64 (chunk->stripe_length),
805 stripen, stripe->offset);
806 grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
807 " for laddr 0x%" PRIxGRUB_UINT64_T "\n", paddr,
808 addr);
810 dev = find_device (data, stripe->device_id, j);
811 if (!dev)
813 err = grub_errno;
814 grub_errno = GRUB_ERR_NONE;
815 continue;
818 err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
819 paddr & (GRUB_DISK_SECTOR_SIZE - 1),
820 csize, buf);
821 if (!err)
822 break;
823 grub_errno = GRUB_ERR_NONE;
825 if (i != redundancy)
826 break;
828 if (err)
829 return grub_errno = err;
831 size -= csize;
832 buf = (grub_uint8_t *) buf + csize;
833 addr += csize;
834 if (challoc)
835 grub_free (chunk);
837 return GRUB_ERR_NONE;
840 static struct grub_btrfs_data *
841 grub_btrfs_mount (grub_device_t dev)
843 struct grub_btrfs_data *data;
844 grub_err_t err;
846 if (!dev->disk)
848 grub_error (GRUB_ERR_BAD_FS, "not BtrFS");
849 return NULL;
852 data = grub_zalloc (sizeof (*data));
853 if (!data)
854 return NULL;
856 err = read_sblock (dev->disk, &data->sblock);
857 if (err)
859 grub_free (data);
860 return NULL;
863 data->n_devices_allocated = 16;
864 data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
865 * data->n_devices_allocated);
866 if (!data->devices_attached)
868 grub_free (data);
869 return NULL;
871 data->n_devices_attached = 1;
872 data->devices_attached[0].dev = dev;
873 data->devices_attached[0].id = data->sblock.this_device.device_id;
875 return data;
878 static void
879 grub_btrfs_unmount (struct grub_btrfs_data *data)
881 unsigned i;
882 /* The device 0 is closed one layer upper. */
883 for (i = 1; i < data->n_devices_attached; i++)
884 grub_device_close (data->devices_attached[i].dev);
885 grub_free (data->devices_attached);
886 grub_free (data->extent);
887 grub_free (data);
890 static grub_err_t
891 grub_btrfs_read_inode (struct grub_btrfs_data *data,
892 struct grub_btrfs_inode *inode, grub_uint64_t num,
893 grub_uint64_t tree)
895 struct grub_btrfs_key key_in, key_out;
896 grub_disk_addr_t elemaddr;
897 grub_size_t elemsize;
898 grub_err_t err;
900 key_in.object_id = num;
901 key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
902 key_in.offset = 0;
904 err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, NULL,
906 if (err)
907 return err;
908 if (num != key_out.object_id
909 || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM)
910 return grub_error (GRUB_ERR_BAD_FS, "inode not found");
912 return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode), 0);
915 static grub_ssize_t
916 grub_btrfs_lzo_decompress(char *ibuf, grub_size_t isize, grub_off_t off,
917 char *obuf, grub_size_t osize)
919 grub_uint32_t total_size, cblock_size;
920 grub_size_t ret = 0;
921 char *ibuf0 = ibuf;
923 total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
924 ibuf += sizeof (total_size);
926 if (isize < total_size)
927 return -1;
929 /* Jump forward to first block with requested data. */
930 while (off >= GRUB_BTRFS_LZO_BLOCK_SIZE)
932 /* Don't let following uint32_t cross the page boundary. */
933 if (((ibuf - ibuf0) & 0xffc) == 0xffc)
934 ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0;
936 cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
937 ibuf += sizeof (cblock_size);
939 if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE)
940 return -1;
942 off -= GRUB_BTRFS_LZO_BLOCK_SIZE;
943 ibuf += cblock_size;
946 while (osize > 0)
948 lzo_uint usize = GRUB_BTRFS_LZO_BLOCK_SIZE;
950 /* Don't let following uint32_t cross the page boundary. */
951 if (((ibuf - ibuf0) & 0xffc) == 0xffc)
952 ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0;
954 cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
955 ibuf += sizeof (cblock_size);
957 if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE)
958 return -1;
960 /* Block partially filled with requested data. */
961 if (off > 0 || osize < GRUB_BTRFS_LZO_BLOCK_SIZE)
963 grub_size_t to_copy = GRUB_BTRFS_LZO_BLOCK_SIZE - off;
964 grub_uint8_t *buf;
966 if (to_copy > osize)
967 to_copy = osize;
969 buf = grub_malloc (GRUB_BTRFS_LZO_BLOCK_SIZE);
970 if (!buf)
971 return -1;
973 if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, buf, &usize,
974 NULL) != LZO_E_OK)
976 grub_free (buf);
977 return -1;
980 if (to_copy > usize)
981 to_copy = usize;
982 grub_memcpy(obuf, buf + off, to_copy);
984 osize -= to_copy;
985 ret += to_copy;
986 obuf += to_copy;
987 ibuf += cblock_size;
988 off = 0;
990 grub_free (buf);
991 continue;
994 /* Decompress whole block directly to output buffer. */
995 if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, (lzo_bytep)obuf,
996 &usize, NULL) != LZO_E_OK)
997 return -1;
999 osize -= usize;
1000 ret += usize;
1001 obuf += usize;
1002 ibuf += cblock_size;
1005 return ret;
1008 static grub_ssize_t
1009 grub_btrfs_extent_read (struct grub_btrfs_data *data,
1010 grub_uint64_t ino, grub_uint64_t tree,
1011 grub_off_t pos0, char *buf, grub_size_t len)
1013 grub_off_t pos = pos0;
1014 while (len)
1016 grub_size_t csize;
1017 grub_err_t err;
1018 grub_off_t extoff;
1019 if (!data->extent || data->extstart > pos || data->extino != ino
1020 || data->exttree != tree || data->extend <= pos)
1022 struct grub_btrfs_key key_in, key_out;
1023 grub_disk_addr_t elemaddr;
1024 grub_size_t elemsize;
1026 grub_free (data->extent);
1027 key_in.object_id = ino;
1028 key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
1029 key_in.offset = grub_cpu_to_le64 (pos);
1030 err = lower_bound (data, &key_in, &key_out, tree,
1031 &elemaddr, &elemsize, NULL, 0);
1032 if (err)
1033 return -1;
1034 if (key_out.object_id != ino
1035 || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
1037 grub_error (GRUB_ERR_BAD_FS, "extent not found");
1038 return -1;
1040 if ((grub_ssize_t) elemsize < ((char *) &data->extent->inl
1041 - (char *) data->extent))
1043 grub_error (GRUB_ERR_BAD_FS, "extent descriptor is too short");
1044 return -1;
1046 data->extstart = grub_le_to_cpu64 (key_out.offset);
1047 data->extsize = elemsize;
1048 data->extent = grub_malloc (elemsize);
1049 data->extino = ino;
1050 data->exttree = tree;
1051 if (!data->extent)
1052 return grub_errno;
1054 err = grub_btrfs_read_logical (data, elemaddr, data->extent,
1055 elemsize, 0);
1056 if (err)
1057 return err;
1059 data->extend = data->extstart + grub_le_to_cpu64 (data->extent->size);
1060 if (data->extent->type == GRUB_BTRFS_EXTENT_REGULAR
1061 && (char *) data->extent + elemsize
1062 >= (char *) &data->extent->filled + sizeof (data->extent->filled))
1063 data->extend =
1064 data->extstart + grub_le_to_cpu64 (data->extent->filled);
1066 grub_dprintf ("btrfs", "regular extent 0x%" PRIxGRUB_UINT64_T "+0x%"
1067 PRIxGRUB_UINT64_T "\n",
1068 grub_le_to_cpu64 (key_out.offset),
1069 grub_le_to_cpu64 (data->extent->size));
1070 if (data->extend <= pos)
1072 grub_error (GRUB_ERR_BAD_FS, "extent not found");
1073 return -1;
1076 csize = data->extend - pos;
1077 extoff = pos - data->extstart;
1078 if (csize > len)
1079 csize = len;
1081 if (data->extent->encryption)
1083 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1084 "encryption not supported");
1085 return -1;
1088 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
1089 && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB
1090 && data->extent->compression != GRUB_BTRFS_COMPRESSION_LZO)
1092 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1093 "compression type 0x%x not supported",
1094 data->extent->compression);
1095 return -1;
1098 if (data->extent->encoding)
1100 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "encoding not supported");
1101 return -1;
1104 switch (data->extent->type)
1106 case GRUB_BTRFS_EXTENT_INLINE:
1107 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
1109 if (grub_zlib_decompress (data->extent->inl, data->extsize -
1110 ((grub_uint8_t *) data->extent->inl
1111 - (grub_uint8_t *) data->extent),
1112 extoff, buf, csize)
1113 != (grub_ssize_t) csize)
1115 if (!grub_errno)
1116 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
1117 "premature end of compressed");
1118 return -1;
1121 else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO)
1123 if (grub_btrfs_lzo_decompress(data->extent->inl, data->extsize -
1124 ((grub_uint8_t *) data->extent->inl
1125 - (grub_uint8_t *) data->extent),
1126 extoff, buf, csize)
1127 != (grub_ssize_t) csize)
1128 return -1;
1130 else
1131 grub_memcpy (buf, data->extent->inl + extoff, csize);
1132 break;
1133 case GRUB_BTRFS_EXTENT_REGULAR:
1134 if (!data->extent->laddr)
1136 grub_memset (buf, 0, csize);
1137 break;
1140 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE)
1142 char *tmp;
1143 grub_uint64_t zsize;
1144 grub_ssize_t ret;
1146 zsize = grub_le_to_cpu64 (data->extent->compressed_size);
1147 tmp = grub_malloc (zsize);
1148 if (!tmp)
1149 return -1;
1150 err = grub_btrfs_read_logical (data,
1151 grub_le_to_cpu64 (data->extent->laddr),
1152 tmp, zsize, 0);
1153 if (err)
1155 grub_free (tmp);
1156 return -1;
1159 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
1160 ret = grub_zlib_decompress (tmp, zsize, extoff
1161 + grub_le_to_cpu64 (data->extent->offset),
1162 buf, csize);
1163 else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO)
1164 ret = grub_btrfs_lzo_decompress (tmp, zsize, extoff
1165 + grub_le_to_cpu64 (data->extent->offset),
1166 buf, csize);
1167 else
1168 ret = -1;
1170 grub_free (tmp);
1172 if (ret != (grub_ssize_t) csize)
1174 if (!grub_errno)
1175 grub_error (GRUB_ERR_BAD_COMPRESSED_DATA,
1176 "premature end of compressed");
1177 return -1;
1180 break;
1182 err = grub_btrfs_read_logical (data,
1183 grub_le_to_cpu64 (data->extent->laddr)
1184 + grub_le_to_cpu64 (data->extent->offset)
1185 + extoff, buf, csize, 0);
1186 if (err)
1187 return -1;
1188 break;
1189 default:
1190 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1191 "unsupported extent type 0x%x", data->extent->type);
1192 return -1;
1194 buf += csize;
1195 pos += csize;
1196 len -= csize;
1198 return pos - pos0;
1201 static grub_err_t
1202 get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key,
1203 grub_uint64_t *tree, grub_uint8_t *type)
1205 grub_err_t err;
1206 grub_disk_addr_t elemaddr;
1207 grub_size_t elemsize;
1208 struct grub_btrfs_key key_out, key_in;
1209 struct grub_btrfs_root_item ri;
1211 key_in.object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_ROOT_VOL_OBJECTID);
1212 key_in.offset = 0;
1213 key_in.type = GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM;
1214 err = lower_bound (data, &key_in, &key_out,
1215 data->sblock.root_tree,
1216 &elemaddr, &elemsize, NULL, 0);
1217 if (err)
1218 return err;
1219 if (key_in.object_id != key_out.object_id
1220 || key_in.type != key_out.type
1221 || key_in.offset != key_out.offset)
1222 return grub_error (GRUB_ERR_BAD_FS, "no root");
1223 err = grub_btrfs_read_logical (data, elemaddr, &ri,
1224 sizeof (ri), 0);
1225 if (err)
1226 return err;
1227 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1228 key->offset = 0;
1229 key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
1230 *tree = ri.tree;
1231 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1232 return GRUB_ERR_NONE;
1235 static grub_err_t
1236 find_path (struct grub_btrfs_data *data,
1237 const char *path, struct grub_btrfs_key *key,
1238 grub_uint64_t *tree, grub_uint8_t *type)
1240 const char *slash = path;
1241 grub_err_t err;
1242 grub_disk_addr_t elemaddr;
1243 grub_size_t elemsize;
1244 grub_size_t allocated = 0;
1245 struct grub_btrfs_dir_item *direl = NULL;
1246 struct grub_btrfs_key key_out;
1247 const char *ctoken;
1248 grub_size_t ctokenlen;
1249 char *path_alloc = NULL;
1250 char *origpath = NULL;
1251 unsigned symlinks_max = 32;
1253 err = get_root (data, key, tree, type);
1254 if (err)
1255 return err;
1257 origpath = grub_strdup (path);
1258 if (!origpath)
1259 return grub_errno;
1261 while (1)
1263 while (path[0] == '/')
1264 path++;
1265 if (!path[0])
1266 break;
1267 slash = grub_strchr (path, '/');
1268 if (!slash)
1269 slash = path + grub_strlen (path);
1270 ctoken = path;
1271 ctokenlen = slash - path;
1273 if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1275 grub_free (path_alloc);
1276 grub_free (origpath);
1277 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1280 if (ctokenlen == 1 && ctoken[0] == '.')
1282 path = slash;
1283 continue;
1285 if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.')
1287 key->type = GRUB_BTRFS_ITEM_TYPE_INODE_REF;
1288 key->offset = -1;
1290 err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize,
1291 NULL, 0);
1292 if (err)
1294 grub_free (direl);
1295 grub_free (path_alloc);
1296 grub_free (origpath);
1297 return err;
1300 if (key_out.type != key->type
1301 || key->object_id != key_out.object_id)
1303 grub_free (direl);
1304 grub_free (path_alloc);
1305 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1306 grub_free (origpath);
1307 return err;
1310 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1311 key->object_id = key_out.offset;
1313 path = slash;
1315 continue;
1318 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1319 key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
1321 err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize,
1322 NULL, 0);
1323 if (err)
1325 grub_free (direl);
1326 grub_free (path_alloc);
1327 grub_free (origpath);
1328 return err;
1330 if (key_cmp (key, &key_out) != 0)
1332 grub_free (direl);
1333 grub_free (path_alloc);
1334 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1335 grub_free (origpath);
1336 return err;
1339 struct grub_btrfs_dir_item *cdirel;
1340 if (elemsize > allocated)
1342 allocated = 2 * elemsize;
1343 grub_free (direl);
1344 direl = grub_malloc (allocated + 1);
1345 if (!direl)
1347 grub_free (path_alloc);
1348 grub_free (origpath);
1349 return grub_errno;
1353 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0);
1354 if (err)
1356 grub_free (direl);
1357 grub_free (path_alloc);
1358 grub_free (origpath);
1359 return err;
1362 for (cdirel = direl;
1363 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1364 < (grub_ssize_t) elemsize;
1365 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1366 + grub_le_to_cpu16 (cdirel->n)
1367 + grub_le_to_cpu16 (cdirel->m)))
1369 if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
1370 && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
1371 break;
1373 if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1374 >= (grub_ssize_t) elemsize)
1376 grub_free (direl);
1377 grub_free (path_alloc);
1378 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1379 grub_free (origpath);
1380 return err;
1383 path = slash;
1384 if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
1386 struct grub_btrfs_inode inode;
1387 char *tmp;
1388 if (--symlinks_max == 0)
1390 grub_free (direl);
1391 grub_free (path_alloc);
1392 grub_free (origpath);
1393 return grub_error (GRUB_ERR_SYMLINK_LOOP,
1394 N_("too deep nesting of symlinks"));
1397 err = grub_btrfs_read_inode (data, &inode,
1398 cdirel->key.object_id, *tree);
1399 if (err)
1401 grub_free (direl);
1402 grub_free (path_alloc);
1403 grub_free (origpath);
1404 return err;
1406 tmp = grub_malloc (grub_le_to_cpu64 (inode.size)
1407 + grub_strlen (path) + 1);
1408 if (!tmp)
1410 grub_free (direl);
1411 grub_free (path_alloc);
1412 grub_free (origpath);
1413 return grub_errno;
1416 if (grub_btrfs_extent_read (data, cdirel->key.object_id,
1417 *tree, 0, tmp,
1418 grub_le_to_cpu64 (inode.size))
1419 != (grub_ssize_t) grub_le_to_cpu64 (inode.size))
1421 grub_free (direl);
1422 grub_free (path_alloc);
1423 grub_free (origpath);
1424 grub_free (tmp);
1425 return grub_errno;
1427 grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path,
1428 grub_strlen (path) + 1);
1429 grub_free (path_alloc);
1430 path = path_alloc = tmp;
1431 if (path[0] == '/')
1433 err = get_root (data, key, tree, type);
1434 if (err)
1435 return err;
1437 continue;
1439 *type = cdirel->type;
1441 switch (cdirel->key.type)
1443 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
1445 struct grub_btrfs_root_item ri;
1446 err = lower_bound (data, &cdirel->key, &key_out,
1447 data->sblock.root_tree,
1448 &elemaddr, &elemsize, NULL, 0);
1449 if (err)
1451 grub_free (direl);
1452 grub_free (path_alloc);
1453 grub_free (origpath);
1454 return err;
1456 if (cdirel->key.object_id != key_out.object_id
1457 || cdirel->key.type != key_out.type)
1459 grub_free (direl);
1460 grub_free (path_alloc);
1461 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1462 grub_free (origpath);
1463 return err;
1465 err = grub_btrfs_read_logical (data, elemaddr, &ri,
1466 sizeof (ri), 0);
1467 if (err)
1469 grub_free (direl);
1470 grub_free (path_alloc);
1471 grub_free (origpath);
1472 return err;
1474 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1475 key->offset = 0;
1476 key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK);
1477 *tree = ri.tree;
1478 break;
1480 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM:
1481 if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1483 grub_free (direl);
1484 grub_free (path_alloc);
1485 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
1486 grub_free (origpath);
1487 return err;
1489 *key = cdirel->key;
1490 if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1491 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1492 break;
1493 default:
1494 grub_free (path_alloc);
1495 grub_free (origpath);
1496 grub_free (direl);
1497 return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x",
1498 cdirel->key.type);
1502 grub_free (direl);
1503 grub_free (origpath);
1504 grub_free (path_alloc);
1505 return GRUB_ERR_NONE;
1508 static grub_err_t
1509 grub_btrfs_dir (grub_device_t device, const char *path,
1510 grub_fs_dir_hook_t hook, void *hook_data)
1512 struct grub_btrfs_data *data = grub_btrfs_mount (device);
1513 struct grub_btrfs_key key_in, key_out;
1514 grub_err_t err;
1515 grub_disk_addr_t elemaddr;
1516 grub_size_t elemsize;
1517 grub_size_t allocated = 0;
1518 struct grub_btrfs_dir_item *direl = NULL;
1519 struct grub_btrfs_leaf_descriptor desc;
1520 int r = 0;
1521 grub_uint64_t tree;
1522 grub_uint8_t type;
1524 if (!data)
1525 return grub_errno;
1527 err = find_path (data, path, &key_in, &tree, &type);
1528 if (err)
1530 grub_btrfs_unmount (data);
1531 return err;
1533 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1535 grub_btrfs_unmount (data);
1536 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
1539 err = lower_bound (data, &key_in, &key_out, tree,
1540 &elemaddr, &elemsize, &desc, 0);
1541 if (err)
1543 grub_btrfs_unmount (data);
1544 return err;
1546 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1547 || key_out.object_id != key_in.object_id)
1549 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1550 if (r <= 0)
1551 goto out;
1555 struct grub_btrfs_dir_item *cdirel;
1556 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1557 || key_out.object_id != key_in.object_id)
1559 r = 0;
1560 break;
1562 if (elemsize > allocated)
1564 allocated = 2 * elemsize;
1565 grub_free (direl);
1566 direl = grub_malloc (allocated + 1);
1567 if (!direl)
1569 r = -grub_errno;
1570 break;
1574 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0);
1575 if (err)
1577 r = -err;
1578 break;
1581 for (cdirel = direl;
1582 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1583 < (grub_ssize_t) elemsize;
1584 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1585 + grub_le_to_cpu16 (cdirel->n)
1586 + grub_le_to_cpu16 (cdirel->m)))
1588 char c;
1589 struct grub_btrfs_inode inode;
1590 struct grub_dirhook_info info;
1591 err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
1592 tree);
1593 grub_memset (&info, 0, sizeof (info));
1594 if (err)
1595 grub_errno = GRUB_ERR_NONE;
1596 else
1598 info.mtime = grub_le_to_cpu64 (inode.mtime.sec);
1599 info.mtimeset = 1;
1601 c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
1602 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
1603 info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
1604 if (hook (cdirel->name, &info, hook_data))
1605 goto out;
1606 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1608 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1610 while (r > 0);
1612 out:
1613 grub_free (direl);
1615 free_iterator (&desc);
1616 grub_btrfs_unmount (data);
1618 return -r;
1621 static grub_err_t
1622 grub_btrfs_open (struct grub_file *file, const char *name)
1624 struct grub_btrfs_data *data = grub_btrfs_mount (file->device);
1625 grub_err_t err;
1626 struct grub_btrfs_inode inode;
1627 grub_uint8_t type;
1628 struct grub_btrfs_key key_in;
1630 if (!data)
1631 return grub_errno;
1633 err = find_path (data, name, &key_in, &data->tree, &type);
1634 if (err)
1636 grub_btrfs_unmount (data);
1637 return err;
1639 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1641 grub_btrfs_unmount (data);
1642 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
1645 data->inode = key_in.object_id;
1646 err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
1647 if (err)
1649 grub_btrfs_unmount (data);
1650 return err;
1653 file->data = data;
1654 file->size = grub_le_to_cpu64 (inode.size);
1656 return err;
1659 static grub_err_t
1660 grub_btrfs_close (grub_file_t file)
1662 grub_btrfs_unmount (file->data);
1664 return GRUB_ERR_NONE;
1667 static grub_ssize_t
1668 grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
1670 struct grub_btrfs_data *data = file->data;
1672 return grub_btrfs_extent_read (data, data->inode,
1673 data->tree, file->offset, buf, len);
1676 static grub_err_t
1677 grub_btrfs_uuid (grub_device_t device, char **uuid)
1679 struct grub_btrfs_data *data;
1681 *uuid = NULL;
1683 data = grub_btrfs_mount (device);
1684 if (!data)
1685 return grub_errno;
1687 *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1688 grub_be_to_cpu16 (data->sblock.uuid[0]),
1689 grub_be_to_cpu16 (data->sblock.uuid[1]),
1690 grub_be_to_cpu16 (data->sblock.uuid[2]),
1691 grub_be_to_cpu16 (data->sblock.uuid[3]),
1692 grub_be_to_cpu16 (data->sblock.uuid[4]),
1693 grub_be_to_cpu16 (data->sblock.uuid[5]),
1694 grub_be_to_cpu16 (data->sblock.uuid[6]),
1695 grub_be_to_cpu16 (data->sblock.uuid[7]));
1697 grub_btrfs_unmount (data);
1699 return grub_errno;
1702 static grub_err_t
1703 grub_btrfs_label (grub_device_t device, char **label)
1705 struct grub_btrfs_data *data;
1707 *label = NULL;
1709 data = grub_btrfs_mount (device);
1710 if (!data)
1711 return grub_errno;
1713 *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
1715 grub_btrfs_unmount (data);
1717 return grub_errno;
1720 #ifdef GRUB_UTIL
1721 static grub_err_t
1722 grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
1723 unsigned int *nsectors,
1724 unsigned int max_nsectors,
1725 grub_embed_type_t embed_type,
1726 grub_disk_addr_t **sectors)
1728 unsigned i;
1730 if (embed_type != GRUB_EMBED_PCBIOS)
1731 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1732 "BtrFS currently supports only PC-BIOS embedding");
1734 if (64 * 2 - 1 < *nsectors)
1735 return grub_error (GRUB_ERR_OUT_OF_RANGE,
1736 N_("your core.img is unusually large. "
1737 "It won't fit in the embedding area"));
1739 *nsectors = 64 * 2 - 1;
1740 if (*nsectors > max_nsectors)
1741 *nsectors = max_nsectors;
1742 *sectors = grub_malloc (*nsectors * sizeof (**sectors));
1743 if (!*sectors)
1744 return grub_errno;
1745 for (i = 0; i < *nsectors; i++)
1746 (*sectors)[i] = i + 1;
1748 return GRUB_ERR_NONE;
1750 #endif
1752 static struct grub_fs grub_btrfs_fs = {
1753 .name = "btrfs",
1754 .dir = grub_btrfs_dir,
1755 .open = grub_btrfs_open,
1756 .read = grub_btrfs_read,
1757 .close = grub_btrfs_close,
1758 .uuid = grub_btrfs_uuid,
1759 .label = grub_btrfs_label,
1760 #ifdef GRUB_UTIL
1761 .embed = grub_btrfs_embed,
1762 .reserved_first_sector = 1,
1763 .blocklist_install = 0,
1764 #endif
1767 GRUB_MOD_INIT (btrfs)
1769 grub_fs_register (&grub_btrfs_fs);
1772 GRUB_MOD_FINI (btrfs)
1774 grub_fs_unregister (&grub_btrfs_fs);