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>
30 #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
32 typedef grub_uint8_t grub_btrfs_checksum_t
[0x20];
33 typedef grub_uint16_t grub_btrfs_uuid_t
[8];
35 struct grub_btrfs_device
37 grub_uint64_t device_id
;
38 grub_uint8_t dummy
[0x62 - 8];
39 } __attribute__ ((packed
));
41 struct grub_btrfs_superblock
43 grub_btrfs_checksum_t checksum
;
44 grub_btrfs_uuid_t uuid
;
45 grub_uint8_t dummy
[0x10];
46 grub_uint8_t signature
[sizeof (GRUB_BTRFS_SIGNATURE
) - 1];
47 grub_uint64_t generation
;
48 grub_uint64_t root_tree
;
49 grub_uint64_t chunk_tree
;
50 grub_uint8_t dummy2
[0x20];
51 grub_uint64_t root_dir_objectid
;
52 grub_uint8_t dummy3
[0x41];
53 struct grub_btrfs_device this_device
;
55 grub_uint8_t dummy4
[0x100];
56 grub_uint8_t bootstrap_mapping
[0x800];
57 } __attribute__ ((packed
));
61 grub_btrfs_checksum_t checksum
;
62 grub_btrfs_uuid_t uuid
;
63 grub_uint8_t dummy
[0x30];
66 } __attribute__ ((packed
));
68 struct grub_btrfs_device_desc
74 struct grub_btrfs_data
76 struct grub_btrfs_superblock sblock
;
80 struct grub_btrfs_device_desc
*devices_attached
;
81 unsigned n_devices_attached
;
82 unsigned n_devices_allocated
;
84 /* Cached extent data. */
85 grub_uint64_t extstart
;
88 grub_uint64_t exttree
;
90 struct grub_btrfs_extent_data
*extent
;
95 grub_uint64_t object_id
;
96 #define GRUB_BTRFS_ITEM_TYPE_INODE_ITEM 0x01
97 #define GRUB_BTRFS_ITEM_TYPE_DIR_ITEM 0x54
98 #define GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM 0x6c
99 #define GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM 0x84
100 #define GRUB_BTRFS_ITEM_TYPE_DEVICE 0xd8
101 #define GRUB_BTRFS_ITEM_TYPE_CHUNK 0xe4
103 grub_uint64_t offset
;
104 } __attribute__ ((packed
));
106 struct grub_btrfs_chunk_item
110 grub_uint64_t stripe_length
;
112 #define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07
113 #define GRUB_BTRFS_CHUNK_TYPE_SINGLE 0x00
114 #define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08
115 #define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10
116 #define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20
117 #define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40
118 grub_uint8_t dummy2
[0xc];
119 grub_uint16_t nstripes
;
120 grub_uint16_t nsubstripes
;
121 } __attribute__ ((packed
));
123 struct grub_btrfs_chunk_stripe
125 grub_uint64_t device_id
;
126 grub_uint64_t offset
;
127 grub_btrfs_uuid_t device_uuid
;
128 } __attribute__ ((packed
));
130 struct grub_btrfs_leaf_node
132 struct grub_btrfs_key key
;
133 grub_uint32_t offset
;
135 } __attribute__ ((packed
));
137 struct grub_btrfs_internal_node
139 struct grub_btrfs_key key
;
142 } __attribute__ ((packed
));
144 struct grub_btrfs_dir_item
146 struct grub_btrfs_key key
;
147 grub_uint8_t dummy
[8];
150 #define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1
151 #define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2
152 #define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7
155 } __attribute__ ((packed
));
157 struct grub_btrfs_leaf_descriptor
162 grub_disk_addr_t addr
;
169 struct grub_btrfs_root_item
171 grub_uint8_t dummy
[0xb0];
176 struct grub_btrfs_time
179 grub_uint32_t nanosec
;
180 } __attribute__ ((aligned(4)));
182 struct grub_btrfs_inode
184 grub_uint8_t dummy1
[0x10];
186 grub_uint8_t dummy2
[0x70];
187 struct grub_btrfs_time mtime
;
188 } __attribute__ ((packed
));
190 struct grub_btrfs_extent_data
194 grub_uint8_t compression
;
195 grub_uint8_t encryption
;
196 grub_uint16_t encoding
;
204 grub_uint64_t compressed_size
;
205 grub_uint64_t offset
;
206 grub_uint64_t filled
;
209 } __attribute__ ((packed
));
211 #define GRUB_BTRFS_EXTENT_INLINE 0
212 #define GRUB_BTRFS_EXTENT_REGULAR 1
214 #define GRUB_BTRFS_COMPRESSION_NONE 0
215 #define GRUB_BTRFS_COMPRESSION_ZLIB 1
217 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
219 static grub_disk_addr_t superblock_sectors
[] = { 64 * 2, 64 * 1024 * 2,
221 1048576ULL * 1048576ULL * 2 };
224 grub_btrfs_read_logical (struct grub_btrfs_data
*data
,
225 grub_disk_addr_t addr
, void *buf
, grub_size_t size
);
228 read_sblock (grub_disk_t disk
, struct grub_btrfs_superblock
*sb
)
231 grub_err_t err
= GRUB_ERR_NONE
;
232 for (i
= 0; i
< ARRAY_SIZE (superblock_sectors
); i
++)
234 struct grub_btrfs_superblock sblock
;
235 err
= grub_disk_read (disk
, superblock_sectors
[i
], 0,
236 sizeof (sblock
), &sblock
);
237 if (err
== GRUB_ERR_OUT_OF_RANGE
)
240 if (grub_memcmp ((char *) sblock
.signature
, GRUB_BTRFS_SIGNATURE
,
241 sizeof (GRUB_BTRFS_SIGNATURE
) - 1) != 0)
243 if (i
== 0 || grub_le_to_cpu64 (sblock
.generation
)
244 > grub_le_to_cpu64 (sb
->generation
))
245 grub_memcpy (sb
, &sblock
, sizeof (sblock
));
248 if ((err
== GRUB_ERR_OUT_OF_RANGE
|| !err
) && i
== 0)
249 return grub_error (GRUB_ERR_BAD_FS
, "not a Btrfs filesystem");
251 if (err
== GRUB_ERR_OUT_OF_RANGE
)
252 grub_errno
= err
= GRUB_ERR_NONE
;
258 key_cmp (const struct grub_btrfs_key
*a
, const struct grub_btrfs_key
*b
)
260 if (grub_cpu_to_le64 (a
->object_id
) < grub_cpu_to_le64 (b
->object_id
))
262 if (grub_cpu_to_le64 (a
->object_id
) > grub_cpu_to_le64 (b
->object_id
))
265 if (a
->type
< b
->type
)
267 if (a
->type
> b
->type
)
270 if (grub_cpu_to_le64 (a
->offset
) < grub_cpu_to_le64 (b
->offset
))
272 if (grub_cpu_to_le64 (a
->offset
) > grub_cpu_to_le64 (b
->offset
))
278 free_iterator (struct grub_btrfs_leaf_descriptor
*desc
)
280 grub_free (desc
->data
);
284 save_ref (struct grub_btrfs_leaf_descriptor
*desc
,
285 grub_disk_addr_t addr
, unsigned i
, unsigned m
, int l
)
288 if (desc
->allocated
< desc
->depth
)
291 desc
->allocated
*= 2;
292 newdata
= grub_realloc (desc
->data
, sizeof (desc
->data
[0])
296 desc
->data
= newdata
;
298 desc
->data
[desc
->depth
- 1].addr
= addr
;
299 desc
->data
[desc
->depth
- 1].iter
= i
;
300 desc
->data
[desc
->depth
- 1].maxiter
= m
;
301 desc
->data
[desc
->depth
- 1].leaf
= l
;
302 return GRUB_ERR_NONE
;
306 next (struct grub_btrfs_data
*data
,
307 struct grub_btrfs_leaf_descriptor
*desc
,
308 grub_disk_addr_t
*outaddr
, grub_size_t
*outsize
,
309 struct grub_btrfs_key
*key_out
)
312 struct grub_btrfs_leaf_node leaf
;
314 for (; desc
->depth
> 0; desc
->depth
--)
316 desc
->data
[desc
->depth
- 1].iter
++;
317 if (desc
->data
[desc
->depth
- 1].iter
318 < desc
->data
[desc
->depth
- 1].maxiter
)
321 if (desc
->depth
== 0)
323 while (!desc
->data
[desc
->depth
- 1].leaf
)
325 struct grub_btrfs_internal_node node
;
326 struct btrfs_header head
;
328 err
= grub_btrfs_read_logical (data
, desc
->data
[desc
->depth
- 1].iter
330 + sizeof (struct btrfs_header
)
331 + desc
->data
[desc
->depth
- 1].addr
, &node
,
336 err
= grub_btrfs_read_logical (data
, grub_le_to_cpu64 (node
.addr
), &head
,
341 save_ref (desc
, grub_le_to_cpu64 (node
.addr
), 0,
342 grub_le_to_cpu32 (head
.nitems
), !head
.level
);
344 err
= grub_btrfs_read_logical (data
, desc
->data
[desc
->depth
- 1].iter
346 + sizeof (struct btrfs_header
)
347 + desc
->data
[desc
->depth
- 1].addr
, &leaf
,
351 *outsize
= grub_le_to_cpu32 (leaf
.size
);
352 *outaddr
= desc
->data
[desc
->depth
- 1].addr
+ sizeof (struct btrfs_header
)
353 + grub_le_to_cpu32 (leaf
.offset
);
359 lower_bound (struct grub_btrfs_data
*data
,
360 const struct grub_btrfs_key
*key_in
,
361 struct grub_btrfs_key
*key_out
,
362 grub_disk_addr_t root
,
363 grub_disk_addr_t
*outaddr
, grub_size_t
*outsize
,
364 struct grub_btrfs_leaf_descriptor
*desc
)
366 grub_disk_addr_t addr
= root
;
371 desc
->allocated
= 16;
373 desc
->data
= grub_malloc (sizeof (desc
->data
[0]) * desc
->allocated
);
378 grub_dprintf ("btrfs",
379 "retrieving %" PRIxGRUB_UINT64_T
380 " %x %" PRIxGRUB_UINT64_T
"\n",
381 key_in
->object_id
, key_in
->type
, key_in
->offset
);
386 struct btrfs_header head
;
390 /* FIXME: preread few nodes into buffer. */
391 err
= grub_btrfs_read_logical (data
, addr
, &head
, sizeof (head
));
394 addr
+= sizeof (head
);
398 struct grub_btrfs_internal_node node
, node_last
;
400 grub_memset (&node_last
, 0, sizeof (node_last
));
401 for (i
= 0; i
< grub_le_to_cpu32 (head
.nitems
); i
++)
403 err
= grub_btrfs_read_logical (data
, addr
+ i
* sizeof (node
),
404 &node
, sizeof (node
));
408 grub_dprintf ("btrfs",
409 "internal node (depth %d) %" PRIxGRUB_UINT64_T
410 " %x %" PRIxGRUB_UINT64_T
"\n", depth
,
411 node
.key
.object_id
, node
.key
.type
, node
.key
.offset
);
413 if (key_cmp (&node
.key
, key_in
) == 0)
417 err
= save_ref (desc
, addr
- sizeof (head
), i
,
418 grub_le_to_cpu32 (head
.nitems
), 0);
421 addr
= grub_le_to_cpu64 (node
.addr
);
424 if (key_cmp (&node
.key
, key_in
) > 0)
433 err
= save_ref (desc
, addr
- sizeof (head
), i
- 1,
434 grub_le_to_cpu32 (head
.nitems
), 0);
437 addr
= grub_le_to_cpu64 (node_last
.addr
);
442 grub_memset (key_out
, 0, sizeof (*key_out
));
444 return save_ref (desc
, addr
- sizeof (head
), -1,
445 grub_le_to_cpu32 (head
.nitems
), 0);
446 return GRUB_ERR_NONE
;
450 struct grub_btrfs_leaf_node leaf
, leaf_last
;
452 for (i
= 0; i
< grub_le_to_cpu32 (head
.nitems
); i
++)
454 err
= grub_btrfs_read_logical (data
, addr
+ i
* sizeof (leaf
),
455 &leaf
, sizeof (leaf
));
459 grub_dprintf ("btrfs",
460 "leaf (depth %d) %" PRIxGRUB_UINT64_T
461 " %x %" PRIxGRUB_UINT64_T
"\n", depth
,
462 leaf
.key
.object_id
, leaf
.key
.type
, leaf
.key
.offset
);
464 if (key_cmp (&leaf
.key
, key_in
) == 0)
466 grub_memcpy (key_out
, &leaf
.key
, sizeof(*key_out
));
467 *outsize
= grub_le_to_cpu32 (leaf
.size
);
468 *outaddr
= addr
+ grub_le_to_cpu32 (leaf
.offset
);
470 return save_ref (desc
, addr
- sizeof (head
), i
,
471 grub_le_to_cpu32 (head
.nitems
), 1);
472 return GRUB_ERR_NONE
;
475 if (key_cmp (&leaf
.key
, key_in
) > 0)
484 grub_memcpy (key_out
, &leaf_last
.key
, sizeof(*key_out
));
485 *outsize
= grub_le_to_cpu32 (leaf_last
.size
);
486 *outaddr
= addr
+ grub_le_to_cpu32 (leaf_last
.offset
);
488 return save_ref (desc
, addr
- sizeof (head
), i
- 1,
489 grub_le_to_cpu32 (head
.nitems
), 1);
490 return GRUB_ERR_NONE
;
494 grub_memset (key_out
, 0, sizeof (*key_out
));
496 return save_ref (desc
, addr
- sizeof (head
), -1,
497 grub_le_to_cpu32 (head
.nitems
), 1);
498 return GRUB_ERR_NONE
;
504 find_device (struct grub_btrfs_data
*data
, grub_uint64_t id
,
507 grub_device_t dev_found
= NULL
;
508 auto int hook (const char *name
);
509 int hook (const char *name
)
513 struct grub_btrfs_superblock sb
;
514 dev
= grub_device_open (name
);
519 grub_device_close (dev
);
522 err
= read_sblock (dev
->disk
, &sb
);
523 if (err
== GRUB_ERR_BAD_FS
)
525 grub_device_close (dev
);
526 grub_errno
= GRUB_ERR_NONE
;
531 grub_device_close (dev
);
535 if (grub_memcmp (data
->sblock
.uuid
, sb
.uuid
, sizeof (sb
.uuid
)) != 0
536 || sb
.this_device
.device_id
!= id
)
538 grub_device_close (dev
);
548 for (i
= 0; i
< data
->n_devices_attached
; i
++)
549 if (id
== data
->devices_attached
[i
].id
)
550 return data
->devices_attached
[i
].dev
;
552 grub_device_iterate (hook
);
555 grub_error (GRUB_ERR_BAD_FS
, "couldn't find a member device");
558 data
->n_devices_attached
++;
559 if (data
->n_devices_attached
> data
->n_devices_allocated
)
562 data
->n_devices_allocated
= 2 * data
->n_devices_attached
+ 1;
563 data
->devices_attached
564 = grub_realloc (tmp
= data
->devices_attached
,
565 data
->n_devices_allocated
566 * sizeof (data
->devices_attached
[0]));
567 if (!data
->devices_attached
)
569 grub_device_close (dev_found
);
570 data
->devices_attached
= tmp
;
574 data
->devices_attached
[data
->n_devices_attached
- 1].id
= id
;
575 data
->devices_attached
[data
->n_devices_attached
- 1].dev
= dev_found
;
580 grub_btrfs_read_logical (struct grub_btrfs_data
*data
,
581 grub_disk_addr_t addr
,
582 void *buf
, grub_size_t size
)
587 struct grub_btrfs_key
*key
;
588 struct grub_btrfs_chunk_item
*chunk
;
591 struct grub_btrfs_key key_out
;
594 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T
"\n",
596 for (ptr
= data
->sblock
.bootstrap_mapping
;
597 ptr
< data
->sblock
.bootstrap_mapping
598 + sizeof (data
->sblock
.bootstrap_mapping
)
599 - sizeof (struct grub_btrfs_key
);
602 key
= (struct grub_btrfs_key
*) ptr
;
603 if (key
->type
!= GRUB_BTRFS_ITEM_TYPE_CHUNK
)
605 chunk
= (struct grub_btrfs_chunk_item
*) (key
+ 1);
606 grub_dprintf ("btrfs", "%" PRIxGRUB_UINT64_T
" %" PRIxGRUB_UINT64_T
" \n",
607 grub_le_to_cpu64 (key
->offset
),
608 grub_le_to_cpu64 (chunk
->size
));
609 if (grub_le_to_cpu64 (key
->offset
) <= addr
610 && addr
< grub_le_to_cpu64 (key
->offset
)
611 + grub_le_to_cpu64 (chunk
->size
))
613 ptr
+= sizeof (*key
) + sizeof (*chunk
)
614 + sizeof (struct grub_btrfs_chunk_stripe
)
615 * grub_le_to_cpu16 (chunk
->nstripes
);
617 struct grub_btrfs_key key_in
;
619 grub_disk_addr_t chaddr
;
620 key_in
.object_id
= GRUB_BTRFS_OBJECT_ID_CHUNK
;
621 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_CHUNK
;
622 key_in
.offset
= addr
;
623 err
= lower_bound (data
, &key_in
, &key_out
,
624 grub_le_to_cpu64 (data
->sblock
.chunk_tree
),
625 &chaddr
, &chsize
, NULL
);
629 if (key
->type
!= GRUB_BTRFS_ITEM_TYPE_CHUNK
630 || !(grub_le_to_cpu64 (key
->offset
) <= addr
))
631 return grub_error (GRUB_ERR_BAD_FS
,
632 "couldn't find the chunk descriptor");
634 chunk
= grub_malloc (chsize
);
639 err
= grub_btrfs_read_logical (data
, chaddr
, chunk
, chsize
);
648 grub_uint32_t stripen
;
649 grub_uint32_t stripe_offset
;
650 grub_uint64_t off
= addr
- grub_le_to_cpu64 (key
->offset
);
651 unsigned redundancy
= 1;
654 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
655 "+0x%" PRIxGRUB_UINT64_T
656 " (%d stripes (%d substripes) of %"
657 PRIxGRUB_UINT64_T
")\n",
658 grub_le_to_cpu64 (key
->offset
),
659 grub_le_to_cpu64 (chunk
->size
),
660 grub_le_to_cpu16 (chunk
->nstripes
),
661 grub_le_to_cpu16 (chunk
->nsubstripes
),
662 grub_le_to_cpu64 (chunk
->stripe_length
));
664 switch (grub_le_to_cpu64 (chunk
->type
)
665 & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE
)
667 case GRUB_BTRFS_CHUNK_TYPE_SINGLE
:
669 grub_uint32_t stripe_length
;
670 stripe_length
= grub_divmod64 (grub_le_to_cpu64 (chunk
->size
),
671 grub_le_to_cpu16 (chunk
->nstripes
),
673 stripen
= grub_divmod64 (off
, stripe_length
, &stripe_offset
);
674 csize
= (stripen
+ 1) * stripe_length
- off
;
677 case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED
:
678 case GRUB_BTRFS_CHUNK_TYPE_RAID1
:
682 csize
= grub_le_to_cpu64 (chunk
->size
) - off
;
686 case GRUB_BTRFS_CHUNK_TYPE_RAID0
:
688 grub_uint64_t middle
, high
;
690 middle
= grub_divmod64 (off
,
691 grub_le_to_cpu64 (chunk
->stripe_length
),
694 high
= grub_divmod64 (middle
, grub_le_to_cpu16 (chunk
->nstripes
),
696 stripe_offset
= low
+ grub_le_to_cpu64 (chunk
->stripe_length
)
698 csize
= grub_le_to_cpu64 (chunk
->stripe_length
) - low
;
701 case GRUB_BTRFS_CHUNK_TYPE_RAID10
:
703 grub_uint64_t middle
, high
;
705 middle
= grub_divmod64 (off
,
706 grub_le_to_cpu64 (chunk
->stripe_length
),
709 high
= grub_divmod64 (middle
,
710 grub_le_to_cpu16 (chunk
->nsubstripes
),
712 stripen
*= grub_le_to_cpu16 (chunk
->nstripes
)
713 / grub_le_to_cpu16 (chunk
->nsubstripes
);
714 redundancy
= grub_le_to_cpu16 (chunk
->nstripes
)
715 / grub_le_to_cpu16 (chunk
->nsubstripes
);
716 stripe_offset
= low
+ grub_le_to_cpu64 (chunk
->stripe_length
)
718 csize
= grub_le_to_cpu64 (chunk
->stripe_length
) - low
;
722 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
723 "unsupported RAID flags %" PRIxGRUB_UINT64_T
,
724 grub_le_to_cpu64 (chunk
->type
));
727 return grub_error (GRUB_ERR_BAD_FS
,
728 "couldn't find the chunk descriptor");
729 if ((grub_size_t
) csize
> size
)
732 for (j
= 0; j
< 2; j
++)
734 for (i
= 0; i
< redundancy
; i
++)
736 struct grub_btrfs_chunk_stripe
*stripe
;
737 grub_disk_addr_t paddr
;
739 stripe
= (struct grub_btrfs_chunk_stripe
*) (chunk
+ 1);
740 /* Right now the redundancy handling is easy.
741 With RAID5-like it will be more difficult. */
742 stripe
+= stripen
+ i
;
744 paddr
= stripe
->offset
+ stripe_offset
;
746 grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
747 "+0x%" PRIxGRUB_UINT64_T
" (%d stripes (%d substripes) of %"
748 PRIxGRUB_UINT64_T
") stripe %" PRIxGRUB_UINT32_T
749 " maps to 0x%" PRIxGRUB_UINT64_T
"\n",
750 grub_le_to_cpu64 (key
->offset
),
751 grub_le_to_cpu64 (chunk
->size
),
752 grub_le_to_cpu16 (chunk
->nstripes
),
753 grub_le_to_cpu16 (chunk
->nsubstripes
),
754 grub_le_to_cpu64 (chunk
->stripe_length
),
755 stripen
, stripe
->offset
);
756 grub_dprintf ("btrfs", "reading paddr 0x%" PRIxGRUB_UINT64_T
757 " for laddr 0x%" PRIxGRUB_UINT64_T
"\n", paddr
,
760 dev
= find_device (data
, stripe
->device_id
, j
);
764 grub_errno
= GRUB_ERR_NONE
;
768 err
= grub_disk_read (dev
->disk
, paddr
>> GRUB_DISK_SECTOR_BITS
,
769 paddr
& (GRUB_DISK_SECTOR_SIZE
- 1),
773 grub_errno
= GRUB_ERR_NONE
;
779 return grub_errno
= err
;
782 buf
= (grub_uint8_t
*) buf
+ csize
;
787 return GRUB_ERR_NONE
;
790 static struct grub_btrfs_data
*
791 grub_btrfs_mount (grub_device_t dev
)
793 struct grub_btrfs_data
*data
;
798 grub_error (GRUB_ERR_BAD_FS
, "not BtrFS");
802 data
= grub_zalloc (sizeof (*data
));
806 err
= read_sblock (dev
->disk
, &data
->sblock
);
813 data
->n_devices_allocated
= 16;
814 data
->devices_attached
= grub_malloc (sizeof (data
->devices_attached
[0])
815 * data
->n_devices_allocated
);
816 if (!data
->devices_attached
)
821 data
->n_devices_attached
= 1;
822 data
->devices_attached
[0].dev
= dev
;
823 data
->devices_attached
[0].id
= data
->sblock
.this_device
.device_id
;
829 grub_btrfs_unmount (struct grub_btrfs_data
*data
)
832 /* The device 0 is closed one layer upper. */
833 for (i
= 1; i
< data
->n_devices_attached
; i
++)
834 grub_device_close (data
->devices_attached
[i
].dev
);
835 grub_free (data
->devices_attached
);
836 grub_free (data
->extent
);
841 grub_btrfs_read_inode (struct grub_btrfs_data
*data
,
842 struct grub_btrfs_inode
*inode
, grub_uint64_t num
,
845 struct grub_btrfs_key key_in
, key_out
;
846 grub_disk_addr_t elemaddr
;
847 grub_size_t elemsize
;
850 key_in
.object_id
= num
;
851 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
;
854 err
= lower_bound (data
, &key_in
, &key_out
, tree
,
855 &elemaddr
, &elemsize
, NULL
);
858 if (num
!= key_out
.object_id
859 || key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
)
860 return grub_error (GRUB_ERR_BAD_FS
, "inode not found");
862 return grub_btrfs_read_logical (data
, elemaddr
, inode
, sizeof (*inode
));
866 grub_btrfs_extent_read (struct grub_btrfs_data
*data
,
867 grub_uint64_t ino
, grub_uint64_t tree
,
868 grub_off_t pos0
, char *buf
, grub_size_t len
)
870 grub_off_t pos
= pos0
;
876 if (!data
->extent
|| data
->extstart
> pos
|| data
->extino
!= ino
877 || data
->exttree
!= tree
|| data
->extend
<= pos
)
879 struct grub_btrfs_key key_in
, key_out
;
880 grub_disk_addr_t elemaddr
;
881 grub_size_t elemsize
;
883 grub_free (data
->extent
);
884 key_in
.object_id
= ino
;
885 key_in
.type
= GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM
;
886 key_in
.offset
= grub_cpu_to_le64 (pos
);
887 err
= lower_bound (data
, &key_in
, &key_out
, tree
,
888 &elemaddr
, &elemsize
, NULL
);
891 if (key_out
.object_id
!= ino
892 || key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM
)
894 grub_error (GRUB_ERR_BAD_FS
, "extent not found");
897 data
->extstart
= grub_le_to_cpu64 (key_out
.offset
);
898 data
->extsize
= elemsize
;
899 data
->extent
= grub_malloc (elemsize
);
901 data
->exttree
= tree
;
905 err
= grub_btrfs_read_logical (data
, elemaddr
,
906 data
->extent
, elemsize
);
910 data
->extend
= data
->extstart
911 + grub_le_to_cpu64 (data
->extent
->size
);
912 if (data
->extent
->type
== GRUB_BTRFS_EXTENT_REGULAR
913 && (char *) &data
->extent
+ elemsize
914 >= (char *) &data
->extent
->filled
915 + sizeof (data
->extent
->filled
))
916 data
->extend
= data
->extstart
917 + grub_le_to_cpu64 (data
->extent
->filled
);
919 grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T
"+0x%"
920 PRIxGRUB_UINT64_T
" (0x%"
921 PRIxGRUB_UINT64_T
")\n",
922 grub_le_to_cpu64 (key_out
.offset
),
923 grub_le_to_cpu64 (data
->extent
->size
),
924 grub_le_to_cpu64 (data
->extent
->filled
));
925 if (data
->extend
<= pos
)
927 grub_error (GRUB_ERR_BAD_FS
, "extent not found");
931 csize
= data
->extend
- pos
;
932 extoff
= pos
- data
->extstart
;
936 if (data
->extent
->encryption
)
938 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
939 "encryption not supported");
943 if (data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_NONE
944 && data
->extent
->compression
!= GRUB_BTRFS_COMPRESSION_ZLIB
)
946 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
947 "compression type 0x%x not supported",
948 data
->extent
->compression
);
952 if (data
->extent
->encoding
)
954 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
955 "encoding not supported");
959 switch (data
->extent
->type
)
961 case GRUB_BTRFS_EXTENT_INLINE
:
962 if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_ZLIB
)
964 if (grub_zlib_decompress (data
->extent
->inl
, data
->extsize
-
965 ((grub_uint8_t
*) data
->extent
->inl
966 - (grub_uint8_t
*) data
->extent
),
968 != (grub_ssize_t
) csize
)
972 grub_memcpy (buf
, data
->extent
->inl
+ extoff
, csize
);
974 case GRUB_BTRFS_EXTENT_REGULAR
:
975 if (!data
->extent
->laddr
)
977 grub_memset (buf
, 0, csize
);
980 if (data
->extent
->compression
== GRUB_BTRFS_COMPRESSION_ZLIB
)
984 zsize
= grub_le_to_cpu64 (data
->extent
->compressed_size
);
985 tmp
= grub_malloc (zsize
);
988 err
= grub_btrfs_read_logical (data
,
989 grub_le_to_cpu64 (data
->extent
->laddr
),
996 if (grub_zlib_decompress (tmp
, zsize
, extoff
997 + grub_le_to_cpu64 (data
->extent
->offset
),
998 buf
, csize
) != (grub_ssize_t
) csize
)
1006 err
= grub_btrfs_read_logical (data
,
1007 grub_le_to_cpu64 (data
->extent
->laddr
)
1008 + grub_le_to_cpu64 (data
->extent
->offset
)
1015 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1016 "unsupported extent type 0x%x", data
->extent
->type
);
1027 find_path (struct grub_btrfs_data
*data
,
1028 const char *path
, struct grub_btrfs_key
*key
,
1029 grub_uint64_t
*tree
, grub_uint8_t
*type
)
1031 const char *slash
= path
;
1033 grub_disk_addr_t elemaddr
;
1034 grub_size_t elemsize
;
1035 grub_size_t allocated
= 0;
1036 struct grub_btrfs_dir_item
*direl
= NULL
;
1037 struct grub_btrfs_key key_out
;
1040 grub_size_t ctokenlen
;
1041 char *path_alloc
= NULL
;
1042 unsigned symlinks_max
= 32;
1044 *type
= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
;
1045 *tree
= data
->sblock
.root_tree
;
1046 key
->object_id
= data
->sblock
.root_dir_objectid
;
1047 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1055 while (path
[0] == '/')
1059 slash
= grub_strchr (path
, '/');
1061 slash
= path
+ grub_strlen (path
);
1063 ctokenlen
= slash
- path
;
1068 ctokenlen
= sizeof ("default") - 1;
1071 if (*type
!= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1073 grub_free (path_alloc
);
1074 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
1077 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1078 key
->offset
= grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken
, ctokenlen
));
1080 err
= lower_bound (data
, key
, &key_out
, *tree
,
1081 &elemaddr
, &elemsize
, NULL
);
1085 grub_free (path_alloc
);
1088 if (key_cmp (key
, &key_out
) != 0)
1091 grub_free (path_alloc
);
1092 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
1095 struct grub_btrfs_dir_item
*cdirel
;
1096 if (elemsize
> allocated
)
1098 allocated
= 2 * elemsize
;
1100 direl
= grub_malloc (allocated
+ 1);
1103 grub_free (path_alloc
);
1108 err
= grub_btrfs_read_logical (data
, elemaddr
, direl
, elemsize
);
1112 grub_free (path_alloc
);
1116 for (cdirel
= direl
;
1117 (grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1118 < (grub_ssize_t
) elemsize
;
1119 cdirel
= (void *) ((grub_uint8_t
*) (direl
+ 1)
1120 + grub_le_to_cpu16 (cdirel
->n
)
1121 + grub_le_to_cpu16 (cdirel
->m
)))
1123 if (ctokenlen
== grub_le_to_cpu16 (cdirel
->n
)
1124 && grub_memcmp (cdirel
->name
, ctoken
, ctokenlen
) == 0)
1127 if ((grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1128 >= (grub_ssize_t
) elemsize
)
1131 grub_free (path_alloc
);
1132 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
1138 if (cdirel
->type
== GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK
)
1140 struct grub_btrfs_inode inode
;
1142 if (--symlinks_max
== 0)
1145 grub_free (path_alloc
);
1146 return grub_error (GRUB_ERR_SYMLINK_LOOP
,
1147 "too deep nesting of symlinks");
1150 err
= grub_btrfs_read_inode (data
, &inode
,
1151 cdirel
->key
.object_id
, *tree
);
1155 grub_free (path_alloc
);
1158 tmp
= grub_malloc (grub_le_to_cpu64 (inode
.size
)
1159 + grub_strlen (path
) + 1);
1163 grub_free (path_alloc
);
1167 if (grub_btrfs_extent_read (data
, cdirel
->key
.object_id
,
1169 grub_le_to_cpu64 (inode
.size
))
1170 != (grub_ssize_t
) grub_le_to_cpu64 (inode
.size
))
1173 grub_free (path_alloc
);
1177 grub_memcpy (tmp
+ grub_le_to_cpu64 (inode
.size
), path
,
1178 grub_strlen (path
) + 1);
1179 grub_free (path_alloc
);
1180 path
= path_alloc
= tmp
;
1183 *type
= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
;
1184 *tree
= data
->sblock
.root_tree
;
1185 key
->object_id
= data
->sblock
.root_dir_objectid
;
1186 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1192 *type
= cdirel
->type
;
1194 switch (cdirel
->key
.type
)
1196 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM
:
1198 struct grub_btrfs_root_item ri
;
1199 err
= lower_bound (data
, &cdirel
->key
, &key_out
,
1200 data
->sblock
.root_tree
,
1201 &elemaddr
, &elemsize
, NULL
);
1205 grub_free (path_alloc
);
1208 if (cdirel
->key
.object_id
!= key_out
.object_id
1209 || cdirel
->key
.type
!= key_out
.type
)
1212 grub_free (path_alloc
);
1213 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
1215 err
= grub_btrfs_read_logical (data
, elemaddr
,
1220 grub_free (path_alloc
);
1223 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1225 key
->object_id
= GRUB_BTRFS_OBJECT_ID_CHUNK
;
1226 *tree
= grub_le_to_cpu64 (ri
.tree
);
1229 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM
:
1230 if (*slash
&& *type
== GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR
)
1233 grub_free (path_alloc
);
1234 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
1237 if (*type
== GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1238 key
->type
= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
;
1241 grub_free (path_alloc
);
1243 return grub_error (GRUB_ERR_BAD_FS
, "unrecognised object type 0x%x",
1249 return GRUB_ERR_NONE
;
1253 grub_btrfs_dir (grub_device_t device
, const char *path
,
1254 int (*hook
) (const char *filename
,
1255 const struct grub_dirhook_info
*info
))
1257 struct grub_btrfs_data
*data
= grub_btrfs_mount (device
);
1258 struct grub_btrfs_key key_in
, key_out
;
1260 grub_disk_addr_t elemaddr
;
1261 grub_size_t elemsize
;
1262 grub_size_t allocated
= 0;
1263 struct grub_btrfs_dir_item
*direl
= NULL
;
1264 struct grub_btrfs_leaf_descriptor desc
;
1272 err
= find_path (data
, path
, &key_in
, &tree
, &type
);
1275 if (type
!= GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
)
1276 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
1278 err
= lower_bound (data
, &key_in
, &key_out
, tree
,
1279 &elemaddr
, &elemsize
, &desc
);
1282 if (key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1283 || key_out
.object_id
!= key_in
.object_id
)
1285 r
= next (data
, &desc
, &elemaddr
, &elemsize
, &key_out
);
1288 free_iterator (&desc
);
1294 struct grub_btrfs_dir_item
*cdirel
;
1295 if (key_out
.type
!= GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1296 || key_out
.object_id
!= key_in
.object_id
)
1301 if (elemsize
> allocated
)
1303 allocated
= 2 * elemsize
;
1305 direl
= grub_malloc (allocated
+ 1);
1308 free_iterator (&desc
);
1313 err
= grub_btrfs_read_logical (data
, elemaddr
, direl
, elemsize
);
1317 for (cdirel
= direl
;
1318 (grub_uint8_t
*) cdirel
- (grub_uint8_t
*) direl
1319 < (grub_ssize_t
) elemsize
;
1320 cdirel
= (void *) ((grub_uint8_t
*) (direl
+ 1)
1321 + grub_le_to_cpu16 (cdirel
->n
)
1322 + grub_le_to_cpu16 (cdirel
->m
)))
1325 struct grub_btrfs_inode inode
;
1326 struct grub_dirhook_info info
;
1327 err
= grub_btrfs_read_inode (data
, &inode
, cdirel
->key
.object_id
,
1329 grub_memset (&info
, 0, sizeof (info
));
1331 grub_errno
= GRUB_ERR_NONE
;
1334 info
.mtime
= inode
.mtime
.sec
;
1337 c
= cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)];
1338 cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)] = 0;
1339 info
.dir
= (cdirel
->type
== GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY
);
1340 if (hook (cdirel
->name
, &info
))
1342 cdirel
->name
[grub_le_to_cpu16 (cdirel
->n
)] = c
;
1344 r
= next (data
, &desc
, &elemaddr
, &elemsize
, &key_out
);
1351 free_iterator (&desc
);
1352 grub_btrfs_unmount (data
);
1358 grub_btrfs_open (struct grub_file
*file
, const char *name
)
1360 struct grub_btrfs_data
*data
= grub_btrfs_mount (file
->device
);
1362 struct grub_btrfs_inode inode
;
1364 struct grub_btrfs_key key_in
;
1369 err
= find_path (data
, name
, &key_in
, &data
->tree
, &type
);
1372 grub_btrfs_unmount (data
);
1375 if (type
!= GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR
)
1377 grub_btrfs_unmount (data
);
1378 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a regular file");
1381 data
->inode
= key_in
.object_id
;
1382 err
= grub_btrfs_read_inode (data
, &inode
, data
->inode
, data
->tree
);
1385 grub_btrfs_unmount (data
);
1390 file
->size
= grub_le_to_cpu64 (inode
.size
);
1396 grub_btrfs_close (grub_file_t file
)
1398 grub_btrfs_unmount (file
->data
);
1400 return GRUB_ERR_NONE
;
1404 grub_btrfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
1406 struct grub_btrfs_data
*data
= file
->data
;
1408 return grub_btrfs_extent_read (data
, data
->inode
,
1409 data
->tree
, file
->offset
, buf
, len
);
1413 grub_btrfs_uuid (grub_device_t device
, char **uuid
)
1415 struct grub_btrfs_data
*data
;
1419 data
= grub_btrfs_mount (device
);
1423 *uuid
= grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
1424 grub_be_to_cpu16 (data
->sblock
.uuid
[0]),
1425 grub_be_to_cpu16 (data
->sblock
.uuid
[1]),
1426 grub_be_to_cpu16 (data
->sblock
.uuid
[2]),
1427 grub_be_to_cpu16 (data
->sblock
.uuid
[3]),
1428 grub_be_to_cpu16 (data
->sblock
.uuid
[4]),
1429 grub_be_to_cpu16 (data
->sblock
.uuid
[5]),
1430 grub_be_to_cpu16 (data
->sblock
.uuid
[6]),
1431 grub_be_to_cpu16 (data
->sblock
.uuid
[7]));
1433 grub_btrfs_unmount (data
);
1439 grub_btrfs_label (grub_device_t device
, char **label
)
1441 struct grub_btrfs_data
*data
;
1445 data
= grub_btrfs_mount (device
);
1449 *label
= grub_strndup (data
->sblock
.label
, sizeof (data
->sblock
.label
));
1451 grub_btrfs_unmount (data
);
1456 static struct grub_fs grub_btrfs_fs
=
1459 .dir
= grub_btrfs_dir
,
1460 .open
= grub_btrfs_open
,
1461 .read
= grub_btrfs_read
,
1462 .close
= grub_btrfs_close
,
1463 .uuid
= grub_btrfs_uuid
,
1464 .label
= grub_btrfs_label
,
1467 GRUB_MOD_INIT(btrfs
)
1469 grub_fs_register (&grub_btrfs_fs
);
1472 GRUB_MOD_FINI(btrfs
)
1474 grub_fs_unregister (&grub_btrfs_fs
);