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