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