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