]> git.proxmox.com Git - grub2.git/blob - grub-core/fs/btrfs.c
* grub-core/fs/btrfs.c (grub_btrfs_lzo_decompress): Random fixes and some
[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 <
630 grub_le_to_cpu64 (key->offset) + 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, uncompressed = 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 off -= GRUB_BTRFS_LZO_BLOCK_SIZE;
913 ibuf += cblock_size;
914 }
915
916 while (osize > 0)
917 {
918 lzo_uint usize = GRUB_BTRFS_LZO_BLOCK_SIZE;
919
920 cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
921 ibuf += sizeof (cblock_size);
922
923 /* Block partially filled with requested data. */
924 if (off > 0 || osize < GRUB_BTRFS_LZO_BLOCK_SIZE)
925 {
926 grub_size_t to_copy = grub_min(osize, GRUB_BTRFS_LZO_BLOCK_SIZE - off);
927
928 if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, buf, &usize,
929 NULL) != LZO_E_OK)
930 return -1;
931
932 grub_memcpy(obuf, buf + off, to_copy);
933
934 osize -= to_copy;
935 uncompressed += to_copy;
936 obuf += to_copy;
937 ibuf += cblock_size;
938 off = 0;
939 continue;
940 }
941
942 /* 'Main' case, decompress whole block directly to output buffer. */
943 if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, (lzo_bytep)obuf,
944 &usize, NULL) != LZO_E_OK)
945 return -1;
946
947 osize -= usize;
948 uncompressed += usize;
949 obuf += usize;
950 ibuf += cblock_size;
951 }
952
953 return uncompressed;
954 }
955
956 static grub_ssize_t
957 grub_btrfs_extent_read (struct grub_btrfs_data *data,
958 grub_uint64_t ino, grub_uint64_t tree,
959 grub_off_t pos0, char *buf, grub_size_t len)
960 {
961 grub_off_t pos = pos0;
962 while (len)
963 {
964 grub_size_t csize;
965 grub_err_t err;
966 grub_off_t extoff;
967 if (!data->extent || data->extstart > pos || data->extino != ino
968 || data->exttree != tree || data->extend <= pos)
969 {
970 struct grub_btrfs_key key_in, key_out;
971 grub_disk_addr_t elemaddr;
972 grub_size_t elemsize;
973
974 grub_free (data->extent);
975 key_in.object_id = ino;
976 key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
977 key_in.offset = grub_cpu_to_le64 (pos);
978 err = lower_bound (data, &key_in, &key_out, tree,
979 &elemaddr, &elemsize, NULL);
980 if (err)
981 return -1;
982 if (key_out.object_id != ino
983 || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
984 {
985 grub_error (GRUB_ERR_BAD_FS, "extent not found");
986 return -1;
987 }
988 data->extstart = grub_le_to_cpu64 (key_out.offset);
989 data->extsize = elemsize;
990 data->extent = grub_malloc (elemsize);
991 data->extino = ino;
992 data->exttree = tree;
993 if (!data->extent)
994 return grub_errno;
995
996 err = grub_btrfs_read_logical (data, elemaddr, data->extent,
997 elemsize);
998 if (err)
999 return err;
1000
1001 data->extend = data->extstart + grub_le_to_cpu64 (data->extent->size);
1002 if (data->extent->type == GRUB_BTRFS_EXTENT_REGULAR
1003 && (char *) &data->extent + elemsize >=
1004 (char *) &data->extent->filled + sizeof (data->extent->filled))
1005 data->extend =
1006 data->extstart + grub_le_to_cpu64 (data->extent->filled);
1007
1008 grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%"
1009 PRIxGRUB_UINT64_T " (0x%"
1010 PRIxGRUB_UINT64_T ")\n",
1011 grub_le_to_cpu64 (key_out.offset),
1012 grub_le_to_cpu64 (data->extent->size),
1013 grub_le_to_cpu64 (data->extent->filled));
1014 if (data->extend <= pos)
1015 {
1016 grub_error (GRUB_ERR_BAD_FS, "extent not found");
1017 return -1;
1018 }
1019 }
1020 csize = data->extend - pos;
1021 extoff = pos - data->extstart;
1022 if (csize > len)
1023 csize = len;
1024
1025 if (data->extent->encryption)
1026 {
1027 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1028 "encryption not supported");
1029 return -1;
1030 }
1031
1032 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
1033 && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB
1034 && data->extent->compression != GRUB_BTRFS_COMPRESSION_LZO)
1035 {
1036 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1037 "compression type 0x%x not supported",
1038 data->extent->compression);
1039 return -1;
1040 }
1041
1042 if (data->extent->encoding)
1043 {
1044 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "encoding not supported");
1045 return -1;
1046 }
1047
1048 switch (data->extent->type)
1049 {
1050 case GRUB_BTRFS_EXTENT_INLINE:
1051 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
1052 {
1053 if (grub_zlib_decompress (data->extent->inl, data->extsize -
1054 ((grub_uint8_t *) data->extent->inl
1055 - (grub_uint8_t *) data->extent),
1056 extoff, buf, csize)
1057 != (grub_ssize_t) csize)
1058 return -1;
1059 }
1060 else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO)
1061 {
1062 if (grub_btrfs_lzo_decompress(data->extent->inl, data->extsize -
1063 ((grub_uint8_t *) data->extent->inl
1064 - (grub_uint8_t *) data->extent),
1065 extoff, buf, csize)
1066 != (grub_ssize_t) csize)
1067 return -1;
1068 }
1069 else
1070 grub_memcpy (buf, data->extent->inl + extoff, csize);
1071 break;
1072 case GRUB_BTRFS_EXTENT_REGULAR:
1073 if (!data->extent->laddr)
1074 {
1075 grub_memset (buf, 0, csize);
1076 break;
1077 }
1078
1079 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE)
1080 {
1081 char *tmp;
1082 grub_uint64_t zsize;
1083 grub_ssize_t ret;
1084
1085 zsize = grub_le_to_cpu64 (data->extent->compressed_size);
1086 tmp = grub_malloc (zsize);
1087 if (!tmp)
1088 return -1;
1089 err = grub_btrfs_read_logical (data,
1090 grub_le_to_cpu64 (data->extent->laddr),
1091 tmp, zsize);
1092 if (err)
1093 {
1094 grub_free (tmp);
1095 return -1;
1096 }
1097
1098 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
1099 ret = grub_zlib_decompress (tmp, zsize, extoff
1100 + grub_le_to_cpu64 (data->extent->offset),
1101 buf, csize);
1102 else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO)
1103 ret = grub_btrfs_lzo_decompress (tmp, zsize, extoff
1104 + grub_le_to_cpu64 (data->extent->offset),
1105 buf, csize);
1106 else
1107 ret = -1;
1108
1109 grub_free(tmp);
1110
1111 if (ret != (grub_ssize_t) csize)
1112 return -1;
1113
1114 break;
1115 }
1116 err = grub_btrfs_read_logical (data,
1117 grub_le_to_cpu64 (data->extent->laddr)
1118 + grub_le_to_cpu64 (data->extent->offset)
1119 + extoff, buf, csize);
1120 if (err)
1121 return -1;
1122 break;
1123 default:
1124 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1125 "unsupported extent type 0x%x", data->extent->type);
1126 return -1;
1127 }
1128 buf += csize;
1129 pos += csize;
1130 len -= csize;
1131 }
1132 return pos - pos0;
1133 }
1134
1135 static grub_err_t
1136 find_path (struct grub_btrfs_data *data,
1137 const char *path, struct grub_btrfs_key *key,
1138 grub_uint64_t * tree, grub_uint8_t * type)
1139 {
1140 const char *slash = path;
1141 grub_err_t err;
1142 grub_disk_addr_t elemaddr;
1143 grub_size_t elemsize;
1144 grub_size_t allocated = 0;
1145 struct grub_btrfs_dir_item *direl = NULL;
1146 struct grub_btrfs_key key_out;
1147 int skip_default;
1148 const char *ctoken;
1149 grub_size_t ctokenlen;
1150 char *path_alloc = NULL;
1151 char *origpath = NULL;
1152 unsigned symlinks_max = 32;
1153
1154 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1155 *tree = data->sblock.root_tree;
1156 key->object_id = data->sblock.root_dir_objectid;
1157 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1158 key->offset = 0;
1159 skip_default = 1;
1160 origpath = grub_strdup (path);
1161 if (!origpath)
1162 return grub_errno;
1163
1164 while (1)
1165 {
1166 if (!skip_default)
1167 {
1168 while (path[0] == '/')
1169 path++;
1170 if (!path[0])
1171 break;
1172 slash = grub_strchr (path, '/');
1173 if (!slash)
1174 slash = path + grub_strlen (path);
1175 ctoken = path;
1176 ctokenlen = slash - path;
1177 }
1178 else
1179 {
1180 ctoken = "default";
1181 ctokenlen = sizeof ("default") - 1;
1182 }
1183
1184 if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1185 {
1186 grub_free (path_alloc);
1187 grub_free (origpath);
1188 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1189 }
1190
1191 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1192 key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
1193
1194 err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize,
1195 NULL);
1196 if (err)
1197 {
1198 grub_free (direl);
1199 grub_free (path_alloc);
1200 grub_free (origpath);
1201 return err;
1202 }
1203 if (key_cmp (key, &key_out) != 0)
1204 {
1205 grub_free (direl);
1206 grub_free (path_alloc);
1207 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
1208 grub_free (origpath);
1209 return err;
1210 }
1211
1212 struct grub_btrfs_dir_item *cdirel;
1213 if (elemsize > allocated)
1214 {
1215 allocated = 2 * elemsize;
1216 grub_free (direl);
1217 direl = grub_malloc (allocated + 1);
1218 if (!direl)
1219 {
1220 grub_free (path_alloc);
1221 grub_free (origpath);
1222 return grub_errno;
1223 }
1224 }
1225
1226 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
1227 if (err)
1228 {
1229 grub_free (direl);
1230 grub_free (path_alloc);
1231 grub_free (origpath);
1232 return err;
1233 }
1234
1235 for (cdirel = direl;
1236 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1237 < (grub_ssize_t) elemsize;
1238 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1239 + grub_le_to_cpu16 (cdirel->n)
1240 + grub_le_to_cpu16 (cdirel->m)))
1241 {
1242 if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
1243 && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
1244 break;
1245 }
1246 if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1247 >= (grub_ssize_t) elemsize)
1248 {
1249 grub_free (direl);
1250 grub_free (path_alloc);
1251 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
1252 grub_free (origpath);
1253 return err;
1254 }
1255
1256 if (!skip_default)
1257 path = slash;
1258 skip_default = 0;
1259 if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
1260 {
1261 struct grub_btrfs_inode inode;
1262 char *tmp;
1263 if (--symlinks_max == 0)
1264 {
1265 grub_free (direl);
1266 grub_free (path_alloc);
1267 grub_free (origpath);
1268 return grub_error (GRUB_ERR_SYMLINK_LOOP,
1269 "too deep nesting of symlinks");
1270 }
1271
1272 err = grub_btrfs_read_inode (data, &inode,
1273 cdirel->key.object_id, *tree);
1274 if (err)
1275 {
1276 grub_free (direl);
1277 grub_free (path_alloc);
1278 grub_free (origpath);
1279 return err;
1280 }
1281 tmp = grub_malloc (grub_le_to_cpu64 (inode.size)
1282 + grub_strlen (path) + 1);
1283 if (!tmp)
1284 {
1285 grub_free (direl);
1286 grub_free (path_alloc);
1287 grub_free (origpath);
1288 return grub_errno;
1289 }
1290
1291 if (grub_btrfs_extent_read (data, cdirel->key.object_id,
1292 *tree, 0, tmp,
1293 grub_le_to_cpu64 (inode.size))
1294 != (grub_ssize_t) grub_le_to_cpu64 (inode.size))
1295 {
1296 grub_free (direl);
1297 grub_free (path_alloc);
1298 grub_free (origpath);
1299 grub_free (tmp);
1300 return grub_errno;
1301 }
1302 grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path,
1303 grub_strlen (path) + 1);
1304 grub_free (path_alloc);
1305 grub_free (origpath);
1306 path = path_alloc = tmp;
1307 if (path[0] == '/')
1308 {
1309 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
1310 *tree = data->sblock.root_tree;
1311 key->object_id = data->sblock.root_dir_objectid;
1312 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1313 key->offset = 0;
1314 skip_default = 1;
1315 }
1316 continue;
1317 }
1318 *type = cdirel->type;
1319
1320 switch (cdirel->key.type)
1321 {
1322 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
1323 {
1324 struct grub_btrfs_root_item ri;
1325 err = lower_bound (data, &cdirel->key, &key_out,
1326 data->sblock.root_tree,
1327 &elemaddr, &elemsize, NULL);
1328 if (err)
1329 {
1330 grub_free (direl);
1331 grub_free (path_alloc);
1332 grub_free (origpath);
1333 return err;
1334 }
1335 if (cdirel->key.object_id != key_out.object_id
1336 || cdirel->key.type != key_out.type)
1337 {
1338 grub_free (direl);
1339 grub_free (path_alloc);
1340 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
1341 grub_free (origpath);
1342 return err;
1343 }
1344 err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri));
1345 if (err)
1346 {
1347 grub_free (direl);
1348 grub_free (path_alloc);
1349 grub_free (origpath);
1350 return err;
1351 }
1352 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1353 key->offset = 0;
1354 key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
1355 *tree = grub_le_to_cpu64 (ri.tree);
1356 break;
1357 }
1358 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM:
1359 if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1360 {
1361 grub_free (direl);
1362 grub_free (path_alloc);
1363 err = grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", origpath);
1364 grub_free (origpath);
1365 return err;
1366 }
1367 *key = cdirel->key;
1368 if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1369 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1370 break;
1371 default:
1372 grub_free (path_alloc);
1373 grub_free (origpath);
1374 grub_free (direl);
1375 return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x",
1376 cdirel->key.type);
1377 }
1378 }
1379
1380 grub_free (direl);
1381 return GRUB_ERR_NONE;
1382 }
1383
1384 static grub_err_t
1385 grub_btrfs_dir (grub_device_t device, const char *path,
1386 int (*hook) (const char *filename,
1387 const struct grub_dirhook_info * info))
1388 {
1389 struct grub_btrfs_data *data = grub_btrfs_mount (device);
1390 struct grub_btrfs_key key_in, key_out;
1391 grub_err_t err;
1392 grub_disk_addr_t elemaddr;
1393 grub_size_t elemsize;
1394 grub_size_t allocated = 0;
1395 struct grub_btrfs_dir_item *direl = NULL;
1396 struct grub_btrfs_leaf_descriptor desc;
1397 int r = 0;
1398 grub_uint64_t tree;
1399 grub_uint8_t type;
1400
1401 if (!data)
1402 return grub_errno;
1403
1404 err = find_path (data, path, &key_in, &tree, &type);
1405 if (err)
1406 return err;
1407 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1408 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1409
1410 err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, &desc);
1411 if (err)
1412 return err;
1413 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1414 || key_out.object_id != key_in.object_id)
1415 {
1416 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1417 if (r <= 0)
1418 {
1419 free_iterator (&desc);
1420 return -r;
1421 }
1422 }
1423 do
1424 {
1425 struct grub_btrfs_dir_item *cdirel;
1426 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1427 || key_out.object_id != key_in.object_id)
1428 {
1429 r = 0;
1430 break;
1431 }
1432 if (elemsize > allocated)
1433 {
1434 allocated = 2 * elemsize;
1435 grub_free (direl);
1436 direl = grub_malloc (allocated + 1);
1437 if (!direl)
1438 {
1439 free_iterator (&desc);
1440 return grub_errno;
1441 }
1442 }
1443
1444 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
1445 if (err)
1446 return err;
1447
1448 for (cdirel = direl;
1449 (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1450 < (grub_ssize_t) elemsize;
1451 cdirel = (void *) ((grub_uint8_t *) (direl + 1)
1452 + grub_le_to_cpu16 (cdirel->n)
1453 + grub_le_to_cpu16 (cdirel->m)))
1454 {
1455 char c;
1456 struct grub_btrfs_inode inode;
1457 struct grub_dirhook_info info;
1458 err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
1459 tree);
1460 grub_memset (&info, 0, sizeof (info));
1461 if (err)
1462 grub_errno = GRUB_ERR_NONE;
1463 else
1464 {
1465 info.mtime = inode.mtime.sec;
1466 info.mtimeset = 1;
1467 }
1468 c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
1469 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
1470 info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
1471 if (hook (cdirel->name, &info))
1472 goto out;
1473 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1474 }
1475 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1476 }
1477 while (r > 0);
1478
1479 out:
1480 grub_free (direl);
1481
1482 free_iterator (&desc);
1483 grub_btrfs_unmount (data);
1484
1485 return -r;
1486 }
1487
1488 static grub_err_t
1489 grub_btrfs_open (struct grub_file *file, const char *name)
1490 {
1491 struct grub_btrfs_data *data = grub_btrfs_mount (file->device);
1492 grub_err_t err;
1493 struct grub_btrfs_inode inode;
1494 grub_uint8_t type;
1495 struct grub_btrfs_key key_in;
1496
1497 if (!data)
1498 return grub_errno;
1499
1500 err = find_path (data, name, &key_in, &data->tree, &type);
1501 if (err)
1502 {
1503 grub_btrfs_unmount (data);
1504 return err;
1505 }
1506 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1507 {
1508 grub_btrfs_unmount (data);
1509 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
1510 }
1511
1512 data->inode = key_in.object_id;
1513 err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
1514 if (err)
1515 {
1516 grub_btrfs_unmount (data);
1517 return err;
1518 }
1519
1520 file->data = data;
1521 file->size = grub_le_to_cpu64 (inode.size);
1522
1523 return err;
1524 }
1525
1526 static grub_err_t
1527 grub_btrfs_close (grub_file_t file)
1528 {
1529 grub_btrfs_unmount (file->data);
1530
1531 return GRUB_ERR_NONE;
1532 }
1533
1534 static grub_ssize_t
1535 grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
1536 {
1537 struct grub_btrfs_data *data = file->data;
1538
1539 return grub_btrfs_extent_read (data, data->inode,
1540 data->tree, file->offset, buf, len);
1541 }
1542
1543 static grub_err_t
1544 grub_btrfs_uuid (grub_device_t device, char **uuid)
1545 {
1546 struct grub_btrfs_data *data;
1547
1548 *uuid = NULL;
1549
1550 data = grub_btrfs_mount (device);
1551 if (!data)
1552 return grub_errno;
1553
1554 *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1555 grub_be_to_cpu16 (data->sblock.uuid[0]),
1556 grub_be_to_cpu16 (data->sblock.uuid[1]),
1557 grub_be_to_cpu16 (data->sblock.uuid[2]),
1558 grub_be_to_cpu16 (data->sblock.uuid[3]),
1559 grub_be_to_cpu16 (data->sblock.uuid[4]),
1560 grub_be_to_cpu16 (data->sblock.uuid[5]),
1561 grub_be_to_cpu16 (data->sblock.uuid[6]),
1562 grub_be_to_cpu16 (data->sblock.uuid[7]));
1563
1564 grub_btrfs_unmount (data);
1565
1566 return grub_errno;
1567 }
1568
1569 static grub_err_t
1570 grub_btrfs_label (grub_device_t device, char **label)
1571 {
1572 struct grub_btrfs_data *data;
1573
1574 *label = NULL;
1575
1576 data = grub_btrfs_mount (device);
1577 if (!data)
1578 return grub_errno;
1579
1580 *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
1581
1582 grub_btrfs_unmount (data);
1583
1584 return grub_errno;
1585 }
1586
1587 static struct grub_fs grub_btrfs_fs = {
1588 .name = "btrfs",
1589 .dir = grub_btrfs_dir,
1590 .open = grub_btrfs_open,
1591 .read = grub_btrfs_read,
1592 .close = grub_btrfs_close,
1593 .uuid = grub_btrfs_uuid,
1594 .label = grub_btrfs_label,
1595 #ifdef GRUB_UTIL
1596 .reserved_first_sector = 1,
1597 #endif
1598 };
1599
1600 GRUB_MOD_INIT (btrfs)
1601 {
1602 grub_fs_register (&grub_btrfs_fs);
1603 }
1604
1605 GRUB_MOD_FINI (btrfs)
1606 {
1607 grub_fs_unregister (&grub_btrfs_fs);
1608 }