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