1 /* btrfs.c - B-tree file system. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010 Free Software Foundation, Inc.
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.
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.
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/>.
21 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
26 #include <grub/types.h>
27 #include <grub/lib/crc.h>
28 #include <grub/deflate.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
33 #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
34 #define GRUB_BTRFS_LZO_BLOCK_SIZE 4096
36 typedef grub_uint8_t grub_btrfs_checksum_t
[0x20];
37 typedef grub_uint16_t grub_btrfs_uuid_t
[8];
39 struct grub_btrfs_device
41 grub_uint64_t device_id
;
42 grub_uint8_t dummy
[0x62 - 8];
43 } __attribute__ ((packed
));
45 struct grub_btrfs_superblock
47 grub_btrfs_checksum_t checksum
;
48 grub_btrfs_uuid_t uuid
;
49 grub_uint8_t dummy
[0x10];
50 grub_uint8_t signature
[sizeof (GRUB_BTRFS_SIGNATURE
) - 1];
51 grub_uint64_t generation
;
52 grub_uint64_t root_tree
;
53 grub_uint64_t chunk_tree
;
54 grub_uint8_t dummy2
[0x20];
55 grub_uint64_t root_dir_objectid
;
56 grub_uint8_t dummy3
[0x41];
57 struct grub_btrfs_device this_device
;
59 grub_uint8_t dummy4
[0x100];
60 grub_uint8_t bootstrap_mapping
[0x800];
61 } __attribute__ ((packed
));
65 grub_btrfs_checksum_t checksum
;
66 grub_btrfs_uuid_t uuid
;
67 grub_uint8_t dummy
[0x30];
70 } __attribute__ ((packed
));
72 struct grub_btrfs_device_desc
78 struct grub_btrfs_data
80 struct grub_btrfs_superblock sblock
;
84 struct grub_btrfs_device_desc
*devices_attached
;
85 unsigned n_devices_attached
;
86 unsigned n_devices_allocated
;
88 /* Cached extent data. */
89 grub_uint64_t extstart
;
92 grub_uint64_t exttree
;
94 struct grub_btrfs_extent_data
*extent
;
99 grub_uint64_t object_id
;
100 #define GRUB_BTRFS_ITEM_TYPE_INODE_ITEM 0x01
101 #define GRUB_BTRFS_ITEM_TYPE_DIR_ITEM 0x54
102 #define GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM 0x6c
103 #define GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM 0x84
104 #define GRUB_BTRFS_ITEM_TYPE_DEVICE 0xd8
105 #define GRUB_BTRFS_ITEM_TYPE_CHUNK 0xe4
107 grub_uint64_t offset
;
108 } __attribute__ ((packed
));
110 struct grub_btrfs_chunk_item
114 grub_uint64_t stripe_length
;
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 } __attribute__ ((packed
));
127 struct grub_btrfs_chunk_stripe
129 grub_uint64_t device_id
;
130 grub_uint64_t offset
;
131 grub_btrfs_uuid_t device_uuid
;
132 } __attribute__ ((packed
));
134 struct grub_btrfs_leaf_node
136 struct grub_btrfs_key key
;
137 grub_uint32_t offset
;
139 } __attribute__ ((packed
));
141 struct grub_btrfs_internal_node
143 struct grub_btrfs_key key
;
146 } __attribute__ ((packed
));
148 struct grub_btrfs_dir_item
150 struct grub_btrfs_key key
;
151 grub_uint8_t dummy
[8];
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
159 } __attribute__ ((packed
));
161 struct grub_btrfs_leaf_descriptor
167 grub_disk_addr_t addr
;
174 struct grub_btrfs_root_item
176 grub_uint8_t dummy
[0xb0];
181 struct grub_btrfs_time
184 grub_uint32_t nanosec
;
185 } __attribute__ ((aligned (4)));
187 struct grub_btrfs_inode
189 grub_uint8_t dummy1
[0x10];
191 grub_uint8_t dummy2
[0x70];
192 struct grub_btrfs_time mtime
;
193 } __attribute__ ((packed
));
195 struct grub_btrfs_extent_data
199 grub_uint8_t compression
;
200 grub_uint8_t encryption
;
201 grub_uint16_t encoding
;
209 grub_uint64_t compressed_size
;
210 grub_uint64_t offset
;
211 grub_uint64_t filled
;
214 } __attribute__ ((packed
));
216 #define GRUB_BTRFS_EXTENT_INLINE 0
217 #define GRUB_BTRFS_EXTENT_REGULAR 1
219 #define GRUB_BTRFS_COMPRESSION_NONE 0
220 #define GRUB_BTRFS_COMPRESSION_ZLIB 1
221 #define GRUB_BTRFS_COMPRESSION_LZO 2
223 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
225 static grub_disk_addr_t superblock_sectors
[] = { 64 * 2, 64 * 1024 * 2,
226 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2
230 grub_btrfs_read_logical (struct grub_btrfs_data
*data
,
231 grub_disk_addr_t addr
, void *buf
, grub_size_t size
);
234 read_sblock (grub_disk_t disk
, struct grub_btrfs_superblock
*sb
)
237 grub_err_t err
= GRUB_ERR_NONE
;
238 for (i
= 0; i
< ARRAY_SIZE (superblock_sectors
); i
++)
240 struct grub_btrfs_superblock sblock
;
241 err
= grub_disk_read (disk
, superblock_sectors
[i
], 0,
242 sizeof (sblock
), &sblock
);
243 if (err
== GRUB_ERR_OUT_OF_RANGE
)
246 if (grub_memcmp ((char *) sblock
.signature
, GRUB_BTRFS_SIGNATURE
,
247 sizeof (GRUB_BTRFS_SIGNATURE
) - 1) != 0)
249 if (i
== 0 || grub_le_to_cpu64 (sblock
.generation
)
250 > grub_le_to_cpu64 (sb
->generation
))
251 grub_memcpy (sb
, &sblock
, sizeof (sblock
));
254 if ((err
== GRUB_ERR_OUT_OF_RANGE
|| !err
) && i
== 0)
255 return grub_error (GRUB_ERR_BAD_FS
, "not a Btrfs filesystem");
257 if (err
== GRUB_ERR_OUT_OF_RANGE
)
258 grub_errno
= err
= GRUB_ERR_NONE
;
264 key_cmp (const struct grub_btrfs_key
*a
, const struct grub_btrfs_key
*b
)
266 if (grub_cpu_to_le64 (a
->object_id
) < grub_cpu_to_le64 (b
->object_id
))
268 if (grub_cpu_to_le64 (a
->object_id
) > grub_cpu_to_le64 (b
->object_id
))
271 if (a
->type
< b
->type
)
273 if (a
->type
> b
->type
)
276 if (grub_cpu_to_le64 (a
->offset
) < grub_cpu_to_le64 (b
->offset
))
278 if (grub_cpu_to_le64 (a
->offset
) > grub_cpu_to_le64 (b
->offset
))
284 free_iterator (struct grub_btrfs_leaf_descriptor
*desc
)
286 grub_free (desc
->data
);
290 save_ref (struct grub_btrfs_leaf_descriptor
*desc
,
291 grub_disk_addr_t addr
, unsigned i
, unsigned m
, int l
)
294 if (desc
->allocated
< desc
->depth
)
297 desc
->allocated
*= 2;
298 newdata
= grub_realloc (desc
->data
, sizeof (desc
->data
[0])
302 desc
->data
= newdata
;
304 desc
->data
[desc
->depth
- 1].addr
= addr
;
305 desc
->data
[desc
->depth
- 1].iter
= i
;
306 desc
->data
[desc
->depth
- 1].maxiter
= m
;
307 desc
->data
[desc
->depth
- 1].leaf
= l
;
308 return GRUB_ERR_NONE
;
312 next (struct grub_btrfs_data
*data
,
313 struct grub_btrfs_leaf_descriptor
*desc
,
314 grub_disk_addr_t
* outaddr
, grub_size_t
* outsize
,
315 struct grub_btrfs_key
*key_out
)
318 struct grub_btrfs_leaf_node leaf
;
320 for (; desc
->depth
> 0; desc
->depth
--)
322 desc
->data
[desc
->depth
- 1].iter
++;
323 if (desc
->data
[desc
->depth
- 1].iter
<
324 desc
->data
[desc
->depth
- 1].maxiter
)
327 if (desc
->depth
== 0)
329 while (!desc
->data
[desc
->depth
- 1].leaf
)
331 struct grub_btrfs_internal_node node
;
332 struct btrfs_header head
;
334 err
= grub_btrfs_read_logical (data
, desc
->data
[desc
->depth
- 1].iter
336 + sizeof (struct btrfs_header
)
337 + desc
->data
[desc
->depth
- 1].addr
,
338 &node
, sizeof (node
));
342 err
= grub_btrfs_read_logical (data
, grub_le_to_cpu64 (node
.addr
),
343 &head
, sizeof (head
));
347 save_ref (desc
, grub_le_to_cpu64 (node
.addr
), 0,
348 grub_le_to_cpu32 (head
.nitems
), !head
.level
);
350 err
= grub_btrfs_read_logical (data
, desc
->data
[desc
->depth
- 1].iter
352 + sizeof (struct btrfs_header
)
353 + desc
->data
[desc
->depth
- 1].addr
, &leaf
,
357 *outsize
= grub_le_to_cpu32 (leaf
.size
);
358 *outaddr
= desc
->data
[desc
->depth
- 1].addr
+ sizeof (struct btrfs_header
)
359 + grub_le_to_cpu32 (leaf
.offset
);
365 lower_bound (struct grub_btrfs_data
*data
,
366 const struct grub_btrfs_key
*key_in
,
367 struct grub_btrfs_key
*key_out
,
368 grub_disk_addr_t root
,
369 grub_disk_addr_t
* outaddr
, grub_size_t
* outsize
,
370 struct grub_btrfs_leaf_descriptor
*desc
)
372 grub_disk_addr_t addr
= root
;
377 desc
->allocated
= 16;
379 desc
->data
= grub_malloc (sizeof (desc
->data
[0]) * desc
->allocated
);
384 grub_dprintf ("btrfs",
385 "retrieving %" PRIxGRUB_UINT64_T
386 " %x %" PRIxGRUB_UINT64_T
"\n",
387 key_in
->object_id
, key_in
->type
, key_in
->offset
);
392 struct btrfs_header head
;
396 /* FIXME: preread few nodes into buffer. */
397 err
= grub_btrfs_read_logical (data
, addr
, &head
, sizeof (head
));
400 addr
+= sizeof (head
);
404 struct grub_btrfs_internal_node node
, node_last
;
406 grub_memset (&node_last
, 0, sizeof (node_last
));
407 for (i
= 0; i
< grub_le_to_cpu32 (head
.nitems
); i
++)
409 err
= grub_btrfs_read_logical (data
, addr
+ i
* sizeof (node
),
410 &node
, sizeof (node
));
414 grub_dprintf ("btrfs",
415 "internal node (depth %d) %" PRIxGRUB_UINT64_T
416 " %x %" PRIxGRUB_UINT64_T
"\n", depth
,
417 node
.key
.object_id
, node
.key
.type
,
420 if (key_cmp (&node
.key
, key_in
) == 0)
424 err
= save_ref (desc
, addr
- sizeof (head
), i
,
425 grub_le_to_cpu32 (head
.nitems
), 0);
428 addr
= grub_le_to_cpu64 (node
.addr
);
431 if (key_cmp (&node
.key
, key_in
) > 0)
440 err
= save_ref (desc
, addr
- sizeof (head
), i
- 1,
441 grub_le_to_cpu32 (head
.nitems
), 0);
444 addr
= grub_le_to_cpu64 (node_last
.addr
);
449 grub_memset (key_out
, 0, sizeof (*key_out
));
451 return save_ref (desc
, addr
- sizeof (head
), -1,
452 grub_le_to_cpu32 (head
.nitems
), 0);
453 return GRUB_ERR_NONE
;
457 struct grub_btrfs_leaf_node leaf
, leaf_last
;
459 for (i
= 0; i
< grub_le_to_cpu32 (head
.nitems
); i
++)
461 err
= grub_btrfs_read_logical (data
, addr
+ i
* sizeof (leaf
),
462 &leaf
, sizeof (leaf
));
466 grub_dprintf ("btrfs",
467 "leaf (depth %d) %" PRIxGRUB_UINT64_T
468 " %x %" PRIxGRUB_UINT64_T
"\n", depth
,
469 leaf
.key
.object_id
, leaf
.key
.type
, leaf
.key
.offset
);
471 if (key_cmp (&leaf
.key
, key_in
) == 0)
473 grub_memcpy (key_out
, &leaf
.key
, sizeof (*key_out
));
474 *outsize
= grub_le_to_cpu32 (leaf
.size
);
475 *outaddr
= addr
+ grub_le_to_cpu32 (leaf
.offset
);
477 return save_ref (desc
, addr
- sizeof (head
), i
,
478 grub_le_to_cpu32 (head
.nitems
), 1);
479 return GRUB_ERR_NONE
;
482 if (key_cmp (&leaf
.key
, key_in
) > 0)
491 grub_memcpy (key_out
, &leaf_last
.key
, sizeof (*key_out
));
492 *outsize
= grub_le_to_cpu32 (leaf_last
.size
);
493 *outaddr
= addr
+ grub_le_to_cpu32 (leaf_last
.offset
);
495 return save_ref (desc
, addr
- sizeof (head
), i
- 1,
496 grub_le_to_cpu32 (head
.nitems
), 1);
497 return GRUB_ERR_NONE
;
501 grub_memset (key_out
, 0, sizeof (*key_out
));
503 return save_ref (desc
, addr
- sizeof (head
), -1,
504 grub_le_to_cpu32 (head
.nitems
), 1);
505 return GRUB_ERR_NONE
;
511 find_device (struct grub_btrfs_data
*data
, grub_uint64_t id
, int do_rescan
)
513 grub_device_t dev_found
= NULL
;
514 auto int hook (const char *name
);
515 int hook (const char *name
)
519 struct grub_btrfs_superblock sb
;
520 dev
= grub_device_open (name
);
525 grub_device_close (dev
);
528 err
= read_sblock (dev
->disk
, &sb
);
529 if (err
== GRUB_ERR_BAD_FS
)
531 grub_device_close (dev
);
532 grub_errno
= GRUB_ERR_NONE
;
537 grub_device_close (dev
);
541 if (grub_memcmp (data
->sblock
.uuid
, sb
.uuid
, sizeof (sb
.uuid
)) != 0
542 || sb
.this_device
.device_id
!= id
)
544 grub_device_close (dev
);
554 for (i
= 0; i
< data
->n_devices_attached
; i
++)
555 if (id
== data
->devices_attached
[i
].id
)
556 return data
->devices_attached
[i
].dev
;
558 grub_device_iterate (hook
);
561 grub_error (GRUB_ERR_BAD_FS
, "couldn't find a member device");
564 data
->n_devices_attached
++;
565 if (data
->n_devices_attached
> data
->n_devices_allocated
)
568 data
->n_devices_allocated
= 2 * data
->n_devices_attached
+ 1;
569 data
->devices_attached
570 = grub_realloc (tmp
= data
->devices_attached
,
571 data
->n_devices_allocated
572 * sizeof (data
->devices_attached
[0]));
573 if (!data
->devices_attached
)
575 grub_device_close (dev_found
);
576 data
->devices_attached
= tmp
;
580 data
->devices_attached
[data
->n_devices_attached
- 1].id
= id
;
581 data
->devices_attached
[data
->n_devices_attached
- 1].dev
= dev_found
;
586 grub_btrfs_read_logical (struct grub_btrfs_data
*data
, grub_disk_addr_t addr
,
587 void *buf
, grub_size_t size
)
592 struct grub_btrfs_key
*key
;
593 struct grub_btrfs_chunk_item
*chunk
;
596 struct grub_btrfs_key key_out
;
599 struct grub_btrfs_key key_in
;
601 grub_disk_addr_t chaddr
;
603 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T
"\n",
605 for (ptr
= data
->sblock
.bootstrap_mapping
;
606 ptr
< data
->sblock
.bootstrap_mapping
607 + sizeof (data
->sblock
.bootstrap_mapping
)
608 - sizeof (struct grub_btrfs_key
);)
610 key
= (struct grub_btrfs_key
*) ptr
;
611 if (key
->type
!= GRUB_BTRFS_ITEM_TYPE_CHUNK
)
613 chunk
= (struct grub_btrfs_chunk_item
*) (key
+ 1);
614 grub_dprintf ("btrfs",
615 "%" PRIxGRUB_UINT64_T
" %" PRIxGRUB_UINT64_T
" \n",
616 grub_le_to_cpu64 (key
->offset
),
617 grub_le_to_cpu64 (chunk
->size
));
618 if (grub_le_to_cpu64 (key
->offset
) <= addr
620 grub_le_to_cpu64 (key
->offset
) + grub_le_to_cpu64 (chunk
->size
))
622 ptr
+= sizeof (*key
) + sizeof (*chunk
)
623 + sizeof (struct grub_btrfs_chunk_stripe
)
624 * grub_le_to_cpu16 (chunk
->nstripes
);
627 key_in
.object_id
= GRUB_BTRFS_OBJECT_ID_CHUNK
;
628 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_CHUNK
;
629 key_in
.offset
= addr
;
630 err
= lower_bound (data
, &key_in
, &key_out
,
631 grub_le_to_cpu64 (data
->sblock
.chunk_tree
),
632 &chaddr
, &chsize
, NULL
);
636 if (key
->type
!= GRUB_BTRFS_ITEM_TYPE_CHUNK
637 || !(grub_le_to_cpu64 (key
->offset
) <= addr
))
638 return grub_error (GRUB_ERR_BAD_FS
,
639 "couldn't find the chunk descriptor");
641 chunk
= grub_malloc (chsize
);
646 err
= grub_btrfs_read_logical (data
, chaddr
, chunk
, chsize
);
655 grub_uint64_t stripen
;
656 grub_uint64_t stripe_offset
;
657 grub_uint64_t off
= addr
- grub_le_to_cpu64 (key
->offset
);
658 unsigned redundancy
= 1;
661 if (grub_le_to_cpu64 (chunk
->size
) <= off
)
663 grub_dprintf ("btrfs", "no chunk\n");
664 return grub_error (GRUB_ERR_BAD_FS
,
665 "couldn't find the chunk descriptor");
668 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
669 "+0x%" PRIxGRUB_UINT64_T
670 " (%d stripes (%d substripes) of %"
671 PRIxGRUB_UINT64_T
")\n",
672 grub_le_to_cpu64 (key
->offset
),
673 grub_le_to_cpu64 (chunk
->size
),
674 grub_le_to_cpu16 (chunk
->nstripes
),
675 grub_le_to_cpu16 (chunk
->nsubstripes
),
676 grub_le_to_cpu64 (chunk
->stripe_length
));
678 switch (grub_le_to_cpu64 (chunk
->type
)
679 & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE
)
681 case GRUB_BTRFS_CHUNK_TYPE_SINGLE
:
683 grub_uint64_t stripe_length
;
684 grub_dprintf ("btrfs", "single\n");
685 stripe_length
= grub_divmod64 (grub_le_to_cpu64 (chunk
->size
),
686 grub_le_to_cpu16 (chunk
->nstripes
),
688 stripen
= grub_divmod64 (off
, stripe_length
, &stripe_offset
);
689 csize
= (stripen
+ 1) * stripe_length
- off
;
692 case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED
:
693 case GRUB_BTRFS_CHUNK_TYPE_RAID1
:
695 grub_dprintf ("btrfs", "RAID1\n");
698 csize
= grub_le_to_cpu64 (chunk
->size
) - off
;
702 case GRUB_BTRFS_CHUNK_TYPE_RAID0
:
704 grub_uint64_t middle
, high
;
706 grub_dprintf ("btrfs", "RAID0\n");
707 middle
= grub_divmod64 (off
,
708 grub_le_to_cpu64 (chunk
->stripe_length
),
711 high
= grub_divmod64 (middle
, grub_le_to_cpu16 (chunk
->nstripes
),
714 low
+ grub_le_to_cpu64 (chunk
->stripe_length
) * high
;
715 csize
= grub_le_to_cpu64 (chunk
->stripe_length
) - low
;
718 case GRUB_BTRFS_CHUNK_TYPE_RAID10
:
720 grub_uint64_t middle
, high
;
722 middle
= grub_divmod64 (off
,
723 grub_le_to_cpu64 (chunk
->stripe_length
),
726 high
= grub_divmod64 (middle
,
727 grub_le_to_cpu16 (chunk
->nsubstripes
),
729 stripen
*= grub_le_to_cpu16 (chunk
->nstripes
)
730 / grub_le_to_cpu16 (chunk
->nsubstripes
);
731 redundancy
= grub_le_to_cpu16 (chunk
->nstripes
)
732 / grub_le_to_cpu16 (chunk
->nsubstripes
);
733 stripe_offset
= low
+ grub_le_to_cpu64 (chunk
->stripe_length
)
735 csize
= grub_le_to_cpu64 (chunk
->stripe_length
) - low
;
739 grub_dprintf ("btrfs", "unsupported RAID\n");
740 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
741 "unsupported RAID flags %" PRIxGRUB_UINT64_T
,
742 grub_le_to_cpu64 (chunk
->type
));
745 return grub_error (GRUB_ERR_BUG
,
746 "couldn't find the chunk descriptor");
747 if ((grub_size_t
) csize
> size
)
750 for (j
= 0; j
< 2; j
++)
752 for (i
= 0; i
< redundancy
; i
++)
754 struct grub_btrfs_chunk_stripe
*stripe
;
755 grub_disk_addr_t paddr
;
757 stripe
= (struct grub_btrfs_chunk_stripe
*) (chunk
+ 1);
758 /* Right now the redundancy handling is easy.
759 With RAID5-like it will be more difficult. */
760 stripe
+= stripen
+ i
;
762 paddr
= stripe
->offset
+ stripe_offset
;
764 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
765 "+0x%" PRIxGRUB_UINT64_T
766 " (%d stripes (%d substripes) of %"
767 PRIxGRUB_UINT64_T
") stripe %" PRIxGRUB_UINT64_T
768 " maps to 0x%" PRIxGRUB_UINT64_T
"\n",
769 grub_le_to_cpu64 (key
->offset
),
770 grub_le_to_cpu64 (chunk
->size
),
771 grub_le_to_cpu16 (chunk
->nstripes
),
772 grub_le_to_cpu16 (chunk
->nsubstripes
),
773 grub_le_to_cpu64 (chunk
->stripe_length
),
774 stripen
, stripe
->offset
);
775 grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
776 " for laddr 0x%" PRIxGRUB_UINT64_T
"\n", paddr
,
779 dev
= find_device (data
, stripe
->device_id
, j
);
783 grub_errno
= GRUB_ERR_NONE
;
787 err
= grub_disk_read (dev
->disk
, paddr
>> GRUB_DISK_SECTOR_BITS
,
788 paddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
792 grub_errno
= GRUB_ERR_NONE
;
798 return grub_errno
= err
;
801 buf
= (grub_uint8_t
*) buf
+ csize
;
806 return GRUB_ERR_NONE
;
809 static struct grub_btrfs_data
*
810 grub_btrfs_mount (grub_device_t dev
)
812 struct grub_btrfs_data
*data
;
817 grub_error (GRUB_ERR_BAD_FS
, "not BtrFS");
821 data
= grub_zalloc (sizeof (*data
));
825 err
= read_sblock (dev
->disk
, &data
->sblock
);
832 data
->n_devices_allocated
= 16;
833 data
->devices_attached
= grub_malloc (sizeof (data
->devices_attached
[0])
834 * data
->n_devices_allocated
);
835 if (!data
->devices_attached
)
840 data
->n_devices_attached
= 1;
841 data
->devices_attached
[0].dev
= dev
;
842 data
->devices_attached
[0].id
= data
->sblock
.this_device
.device_id
;
848 grub_btrfs_unmount (struct grub_btrfs_data
*data
)
851 /* The device 0 is closed one layer upper. */
852 for (i
= 1; i
< data
->n_devices_attached
; i
++)
853 grub_device_close (data
->devices_attached
[i
].dev
);
854 grub_free (data
->devices_attached
);
855 grub_free (data
->extent
);
860 grub_btrfs_read_inode (struct grub_btrfs_data
*data
,
861 struct grub_btrfs_inode
*inode
, grub_uint64_t num
,
864 struct grub_btrfs_key key_in
, key_out
;
865 grub_disk_addr_t elemaddr
;
866 grub_size_t elemsize
;
869 key_in
.object_id
= num
;
870 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
;
873 err
= lower_bound (data
, &key_in
, &key_out
, tree
, &elemaddr
, &elemsize
, NULL
);
876 if (num
!= key_out
.object_id
877 || key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
)
878 return grub_error (GRUB_ERR_BAD_FS
, "inode not found");
880 return grub_btrfs_read_logical (data
, elemaddr
, inode
, sizeof (*inode
));
884 grub_btrfs_lzo_decompress(char *ibuf
, grub_size_t isize
, grub_off_t off
,
885 char *obuf
, grub_size_t osize
)
887 grub_uint32_t total_size
, cblock_size
;
888 unsigned char buf
[GRUB_BTRFS_LZO_BLOCK_SIZE
];
890 total_size
= grub_le_to_cpu32 (grub_get_unaligned32 (ibuf
));
891 ibuf
+= sizeof (total_size
);
893 if (isize
< total_size
)
898 lzo_uint usize
= GRUB_BTRFS_LZO_BLOCK_SIZE
;
900 cblock_size
= grub_le_to_cpu32 (grub_get_unaligned32 (ibuf
));
901 ibuf
+= sizeof (cblock_size
);
903 if (cblock_size
> GRUB_BTRFS_LZO_BLOCK_SIZE
)
906 /* Jump forward to first block with requested data. */
907 if (off
>= GRUB_BTRFS_LZO_BLOCK_SIZE
)
909 off
-= GRUB_BTRFS_LZO_BLOCK_SIZE
;
914 /* First block partially filled with requested data. */
917 if (lzo1x_decompress_safe ((lzo_bytep
)ibuf
, cblock_size
, buf
, &usize
,
921 grub_memcpy(obuf
, buf
+ off
, usize
- off
);
923 osize
-= usize
- off
;
930 /* 'Main' case, decompress whole block directly to output buffer. */
931 if (osize
>= GRUB_BTRFS_LZO_BLOCK_SIZE
)
933 if (lzo1x_decompress_safe ((lzo_bytep
)ibuf
, cblock_size
,
934 (lzo_bytep
)obuf
, &usize
, NULL
) != LZO_E_OK
)
941 else /* Last possible block partially filled with requested data. */
943 if (lzo1x_decompress_safe ((lzo_bytep
)ibuf
, cblock_size
, buf
, &usize
,
947 grub_memcpy(obuf
, buf
, osize
);
955 grub_btrfs_extent_read (struct grub_btrfs_data
*data
,
956 grub_uint64_t ino
, grub_uint64_t tree
,
957 grub_off_t pos0
, char *buf
, grub_size_t len
)
959 grub_off_t pos
= pos0
;
965 if (!data
->extent
|| data
->extstart
> pos
|| data
->extino
!= ino
966 || data
->exttree
!= tree
|| data
->extend
<= pos
)
968 struct grub_btrfs_key key_in
, key_out
;
969 grub_disk_addr_t elemaddr
;
970 grub_size_t elemsize
;
972 grub_free (data
->extent
);
973 key_in
.object_id
= ino
;
974 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM
;
975 key_in
.offset
= grub_cpu_to_le64 (pos
);
976 err
= lower_bound (data
, &key_in
, &key_out
, tree
,
977 &elemaddr
, &elemsize
, NULL
);
980 if (key_out
.object_id
!= ino
981 || key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM
)
983 grub_error (GRUB_ERR_BAD_FS
, "extent not found");
986 data
->extstart
= grub_le_to_cpu64 (key_out
.offset
);
987 data
->extsize
= elemsize
;
988 data
->extent
= grub_malloc (elemsize
);
990 data
->exttree
= tree
;
994 err
= grub_btrfs_read_logical (data
, elemaddr
, data
->extent
,
999 data
->extend
= data
->extstart
+ grub_le_to_cpu64 (data
->extent
->size
);
1000 if (data
->extent
->type
== GRUB_BTRFS_EXTENT_REGULAR
1001 && (char *) &data
->extent
+ elemsize
>=
1002 (char *) &data
->extent
->filled
+ sizeof (data
->extent
->filled
))
1004 data
->extstart
+ grub_le_to_cpu64 (data
->extent
->filled
);
1006 grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T
"+0x%"
1007 PRIxGRUB_UINT64_T
" (0x%"
1008 PRIxGRUB_UINT64_T
")\n",
1009 grub_le_to_cpu64 (key_out
.offset
),
1010 grub_le_to_cpu64 (data
->extent
->size
),
1011 grub_le_to_cpu64 (data
->extent
->filled
));
1012 if (data
->extend
<= pos
)
1014 grub_error (GRUB_ERR_BAD_FS
, "extent not found");
1018 csize
= data
->extend
- pos
;
1019 extoff
= pos
- data
->extstart
;
1023 if (data
->extent
->encryption
)
1025 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1026 "encryption not supported");
1030 if (data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_NONE
1031 && data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_ZLIB
1032 && data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_LZO
)
1034 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1035 "compression type 0x%x not supported",
1036 data
->extent
->compression
);
1040 if (data
->extent
->encoding
)
1042 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
, "encoding not supported");
1046 switch (data
->extent
->type
)
1048 case GRUB_BTRFS_EXTENT_INLINE
:
1049 if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_ZLIB
)
1051 if (grub_zlib_decompress (data
->extent
->inl
, data
->extsize
-
1052 ((grub_uint8_t
*) data
->extent
->inl
1053 - (grub_uint8_t
*) data
->extent
),
1055 != (grub_ssize_t
) csize
)
1058 else if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_LZO
)
1060 if (grub_btrfs_lzo_decompress(data
->extent
->inl
, data
->extsize
-
1061 ((grub_uint8_t
*) data
->extent
->inl
1062 - (grub_uint8_t
*) data
->extent
),
1063 extoff
, buf
, csize
) < 0)
1067 grub_memcpy (buf
, data
->extent
->inl
+ extoff
, csize
);
1069 case GRUB_BTRFS_EXTENT_REGULAR
:
1070 if (!data
->extent
->laddr
)
1072 grub_memset (buf
, 0, csize
);
1076 if (data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_NONE
)
1079 grub_uint64_t zsize
;
1080 zsize
= grub_le_to_cpu64 (data
->extent
->compressed_size
);
1081 tmp
= grub_malloc (zsize
);
1084 err
= grub_btrfs_read_logical (data
,
1085 grub_le_to_cpu64 (data
->extent
->laddr
),
1093 if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_ZLIB
)
1097 ret
= grub_zlib_decompress (tmp
, zsize
, extoff
1098 + grub_le_to_cpu64 (data
->extent
->offset
),
1103 if (ret
!= (grub_ssize_t
) csize
)
1108 else if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_LZO
)
1112 ret
= grub_btrfs_lzo_decompress (tmp
, zsize
, extoff
1113 + grub_le_to_cpu64 (data
->extent
->offset
),
1124 err
= grub_btrfs_read_logical (data
,
1125 grub_le_to_cpu64 (data
->extent
->laddr
)
1126 + grub_le_to_cpu64 (data
->extent
->offset
)
1127 + extoff
, buf
, csize
);
1132 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1133 "unsupported extent type 0x%x", data
->extent
->type
);
1144 find_path (struct grub_btrfs_data
*data
,
1145 const char *path
, struct grub_btrfs_key
*key
,
1146 grub_uint64_t
* tree
, grub_uint8_t
* type
)
1148 const char *slash
= path
;
1150 grub_disk_addr_t elemaddr
;
1151 grub_size_t elemsize
;
1152 grub_size_t allocated
= 0;
1153 struct grub_btrfs_dir_item
*direl
= NULL
;
1154 struct grub_btrfs_key key_out
;
1157 grub_size_t ctokenlen
;
1158 char *path_alloc
= NULL
;
1159 char *origpath
= NULL
;
1160 unsigned symlinks_max
= 32;
1162 *type
= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
;
1163 *tree
= data
->sblock
.root_tree
;
1164 key
->object_id
= data
->sblock
.root_dir_objectid
;
1165 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1168 origpath
= grub_strdup (path
);
1176 while (path
[0] == '/')
1180 slash
= grub_strchr (path
, '/');
1182 slash
= path
+ grub_strlen (path
);
1184 ctokenlen
= slash
- path
;
1189 ctokenlen
= sizeof ("default") - 1;
1192 if (*type
!= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1194 grub_free (path_alloc
);
1195 grub_free (origpath
);
1196 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
1199 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1200 key
->offset
= grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken
, ctokenlen
));
1202 err
= lower_bound (data
, key
, &key_out
, *tree
, &elemaddr
, &elemsize
,
1207 grub_free (path_alloc
);
1208 grub_free (origpath
);
1211 if (key_cmp (key
, &key_out
) != 0)
1214 grub_free (path_alloc
);
1215 err
= grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file `%s' not found", origpath
);
1216 grub_free (origpath
);
1220 struct grub_btrfs_dir_item
*cdirel
;
1221 if (elemsize
> allocated
)
1223 allocated
= 2 * elemsize
;
1225 direl
= grub_malloc (allocated
+ 1);
1228 grub_free (path_alloc
);
1229 grub_free (origpath
);
1234 err
= grub_btrfs_read_logical (data
, elemaddr
, direl
, elemsize
);
1238 grub_free (path_alloc
);
1239 grub_free (origpath
);
1243 for (cdirel
= direl
;
1244 (grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1245 < (grub_ssize_t
) elemsize
;
1246 cdirel
= (void *) ((grub_uint8_t
*) (direl
+ 1)
1247 + grub_le_to_cpu16 (cdirel
->n
)
1248 + grub_le_to_cpu16 (cdirel
->m
)))
1250 if (ctokenlen
== grub_le_to_cpu16 (cdirel
->n
)
1251 && grub_memcmp (cdirel
->name
, ctoken
, ctokenlen
) == 0)
1254 if ((grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1255 >= (grub_ssize_t
) elemsize
)
1258 grub_free (path_alloc
);
1259 err
= grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file `%s' not found", origpath
);
1260 grub_free (origpath
);
1267 if (cdirel
->type
== GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK
)
1269 struct grub_btrfs_inode inode
;
1271 if (--symlinks_max
== 0)
1274 grub_free (path_alloc
);
1275 grub_free (origpath
);
1276 return grub_error (GRUB_ERR_SYMLINK_LOOP
,
1277 "too deep nesting of symlinks");
1280 err
= grub_btrfs_read_inode (data
, &inode
,
1281 cdirel
->key
.object_id
, *tree
);
1285 grub_free (path_alloc
);
1286 grub_free (origpath
);
1289 tmp
= grub_malloc (grub_le_to_cpu64 (inode
.size
)
1290 + grub_strlen (path
) + 1);
1294 grub_free (path_alloc
);
1295 grub_free (origpath
);
1299 if (grub_btrfs_extent_read (data
, cdirel
->key
.object_id
,
1301 grub_le_to_cpu64 (inode
.size
))
1302 != (grub_ssize_t
) grub_le_to_cpu64 (inode
.size
))
1305 grub_free (path_alloc
);
1306 grub_free (origpath
);
1310 grub_memcpy (tmp
+ grub_le_to_cpu64 (inode
.size
), path
,
1311 grub_strlen (path
) + 1);
1312 grub_free (path_alloc
);
1313 grub_free (origpath
);
1314 path
= path_alloc
= tmp
;
1317 *type
= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
;
1318 *tree
= data
->sblock
.root_tree
;
1319 key
->object_id
= data
->sblock
.root_dir_objectid
;
1320 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1326 *type
= cdirel
->type
;
1328 switch (cdirel
->key
.type
)
1330 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM
:
1332 struct grub_btrfs_root_item ri
;
1333 err
= lower_bound (data
, &cdirel
->key
, &key_out
,
1334 data
->sblock
.root_tree
,
1335 &elemaddr
, &elemsize
, NULL
);
1339 grub_free (path_alloc
);
1340 grub_free (origpath
);
1343 if (cdirel
->key
.object_id
!= key_out
.object_id
1344 || cdirel
->key
.type
!= key_out
.type
)
1347 grub_free (path_alloc
);
1348 err
= grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file `%s' not found", origpath
);
1349 grub_free (origpath
);
1352 err
= grub_btrfs_read_logical (data
, elemaddr
, &ri
, sizeof (ri
));
1356 grub_free (path_alloc
);
1357 grub_free (origpath
);
1360 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1362 key
->object_id
= GRUB_BTRFS_OBJECT_ID_CHUNK
;
1363 *tree
= grub_le_to_cpu64 (ri
.tree
);
1366 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
:
1367 if (*slash
&& *type
== GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR
)
1370 grub_free (path_alloc
);
1371 err
= grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file `%s' not found", origpath
);
1372 grub_free (origpath
);
1376 if (*type
== GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1377 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1380 grub_free (path_alloc
);
1381 grub_free (origpath
);
1383 return grub_error (GRUB_ERR_BAD_FS
, "unrecognised object type 0x%x",
1389 return GRUB_ERR_NONE
;
1393 grub_btrfs_dir (grub_device_t device
, const char *path
,
1394 int (*hook
) (const char *filename
,
1395 const struct grub_dirhook_info
* info
))
1397 struct grub_btrfs_data
*data
= grub_btrfs_mount (device
);
1398 struct grub_btrfs_key key_in
, key_out
;
1400 grub_disk_addr_t elemaddr
;
1401 grub_size_t elemsize
;
1402 grub_size_t allocated
= 0;
1403 struct grub_btrfs_dir_item
*direl
= NULL
;
1404 struct grub_btrfs_leaf_descriptor desc
;
1412 err
= find_path (data
, path
, &key_in
, &tree
, &type
);
1415 if (type
!= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1416 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
1418 err
= lower_bound (data
, &key_in
, &key_out
, tree
, &elemaddr
, &elemsize
, &desc
);
1421 if (key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1422 || key_out
.object_id
!= key_in
.object_id
)
1424 r
= next (data
, &desc
, &elemaddr
, &elemsize
, &key_out
);
1427 free_iterator (&desc
);
1433 struct grub_btrfs_dir_item
*cdirel
;
1434 if (key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1435 || key_out
.object_id
!= key_in
.object_id
)
1440 if (elemsize
> allocated
)
1442 allocated
= 2 * elemsize
;
1444 direl
= grub_malloc (allocated
+ 1);
1447 free_iterator (&desc
);
1452 err
= grub_btrfs_read_logical (data
, elemaddr
, direl
, elemsize
);
1456 for (cdirel
= direl
;
1457 (grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1458 < (grub_ssize_t
) elemsize
;
1459 cdirel
= (void *) ((grub_uint8_t
*) (direl
+ 1)
1460 + grub_le_to_cpu16 (cdirel
->n
)
1461 + grub_le_to_cpu16 (cdirel
->m
)))
1464 struct grub_btrfs_inode inode
;
1465 struct grub_dirhook_info info
;
1466 err
= grub_btrfs_read_inode (data
, &inode
, cdirel
->key
.object_id
,
1468 grub_memset (&info
, 0, sizeof (info
));
1470 grub_errno
= GRUB_ERR_NONE
;
1473 info
.mtime
= inode
.mtime
.sec
;
1476 c
= cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)];
1477 cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)] = 0;
1478 info
.dir
= (cdirel
->type
== GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
);
1479 if (hook (cdirel
->name
, &info
))
1481 cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)] = c
;
1483 r
= next (data
, &desc
, &elemaddr
, &elemsize
, &key_out
);
1490 free_iterator (&desc
);
1491 grub_btrfs_unmount (data
);
1497 grub_btrfs_open (struct grub_file
*file
, const char *name
)
1499 struct grub_btrfs_data
*data
= grub_btrfs_mount (file
->device
);
1501 struct grub_btrfs_inode inode
;
1503 struct grub_btrfs_key key_in
;
1508 err
= find_path (data
, name
, &key_in
, &data
->tree
, &type
);
1511 grub_btrfs_unmount (data
);
1514 if (type
!= GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR
)
1516 grub_btrfs_unmount (data
);
1517 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a regular file");
1520 data
->inode
= key_in
.object_id
;
1521 err
= grub_btrfs_read_inode (data
, &inode
, data
->inode
, data
->tree
);
1524 grub_btrfs_unmount (data
);
1529 file
->size
= grub_le_to_cpu64 (inode
.size
);
1535 grub_btrfs_close (grub_file_t file
)
1537 grub_btrfs_unmount (file
->data
);
1539 return GRUB_ERR_NONE
;
1543 grub_btrfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1545 struct grub_btrfs_data
*data
= file
->data
;
1547 return grub_btrfs_extent_read (data
, data
->inode
,
1548 data
->tree
, file
->offset
, buf
, len
);
1552 grub_btrfs_uuid (grub_device_t device
, char **uuid
)
1554 struct grub_btrfs_data
*data
;
1558 data
= grub_btrfs_mount (device
);
1562 *uuid
= grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1563 grub_be_to_cpu16 (data
->sblock
.uuid
[0]),
1564 grub_be_to_cpu16 (data
->sblock
.uuid
[1]),
1565 grub_be_to_cpu16 (data
->sblock
.uuid
[2]),
1566 grub_be_to_cpu16 (data
->sblock
.uuid
[3]),
1567 grub_be_to_cpu16 (data
->sblock
.uuid
[4]),
1568 grub_be_to_cpu16 (data
->sblock
.uuid
[5]),
1569 grub_be_to_cpu16 (data
->sblock
.uuid
[6]),
1570 grub_be_to_cpu16 (data
->sblock
.uuid
[7]));
1572 grub_btrfs_unmount (data
);
1578 grub_btrfs_label (grub_device_t device
, char **label
)
1580 struct grub_btrfs_data
*data
;
1584 data
= grub_btrfs_mount (device
);
1588 *label
= grub_strndup (data
->sblock
.label
, sizeof (data
->sblock
.label
));
1590 grub_btrfs_unmount (data
);
1595 static struct grub_fs grub_btrfs_fs
= {
1597 .dir
= grub_btrfs_dir
,
1598 .open
= grub_btrfs_open
,
1599 .read
= grub_btrfs_read
,
1600 .close
= grub_btrfs_close
,
1601 .uuid
= grub_btrfs_uuid
,
1602 .label
= grub_btrfs_label
,
1604 .reserved_first_sector
= 1,
1608 GRUB_MOD_INIT (btrfs
)
1610 grub_fs_register (&grub_btrfs_fs
);
1613 GRUB_MOD_FINI (btrfs
)
1615 grub_fs_unregister (&grub_btrfs_fs
);