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