]> git.proxmox.com Git - grub2.git/blame - grub-core/fs/btrfs.c
merge mainline into butter
[grub2.git] / grub-core / fs / btrfs.c
CommitLineData
4dfbc574
RM
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>
b18610fe 27#include <grub/lib/crc.h>
3be8e5ea 28#include <grub/deflate.h>
4dfbc574 29
0e761d3d 30#define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
4dfbc574 31
0e761d3d
VS
32typedef grub_uint8_t grub_btrfs_checksum_t[0x20];
33typedef grub_uint16_t grub_btrfs_uuid_t[8];
b18610fe
VS
34
35struct grub_btrfs_device
36{
37 grub_uint64_t device_id;
38 grub_uint8_t dummy[0x62 - 8];
39} __attribute__ ((packed));
40
0e761d3d 41struct grub_btrfs_superblock
4dfbc574 42{
0e761d3d
VS
43 grub_btrfs_checksum_t checksum;
44 grub_btrfs_uuid_t uuid;
b18610fe 45 grub_uint8_t dummy[0x10];
0e761d3d 46 grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1];
b18610fe
VS
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];
db51e201 53 struct grub_btrfs_device this_device;
b18610fe
VS
54 char label[0x100];
55 grub_uint8_t dummy4[0x100];
56 grub_uint8_t bootstrap_mapping[0x800];
57} __attribute__ ((packed));
58
59struct btrfs_header
60{
0e761d3d
VS
61 grub_btrfs_checksum_t checksum;
62 grub_btrfs_uuid_t uuid;
b18610fe
VS
63 grub_uint8_t dummy[0x30];
64 grub_uint32_t nitems;
65 grub_uint8_t level;
4dfbc574
RM
66} __attribute__ ((packed));
67
db51e201
VS
68struct grub_btrfs_device_desc
69{
70 grub_device_t dev;
71 grub_uint64_t id;
72};
73
4dfbc574
RM
74struct grub_btrfs_data
75{
0e761d3d 76 struct grub_btrfs_superblock sblock;
b18610fe
VS
77 grub_uint64_t tree;
78 grub_uint64_t inode;
34018a7d 79
db51e201
VS
80 struct grub_btrfs_device_desc *devices_attached;
81 unsigned n_devices_attached;
82 unsigned n_devices_allocated;
83
34018a7d 84 /* Cached extent data. */
a43c4bc5 85 grub_uint64_t extstart;
6a01f54a 86 grub_uint64_t extend;
34018a7d
VS
87 grub_uint64_t extino;
88 grub_uint64_t exttree;
3be8e5ea 89 grub_size_t extsize;
a43c4bc5 90 struct grub_btrfs_extent_data *extent;
4dfbc574
RM
91};
92
b18610fe
VS
93struct grub_btrfs_key
94{
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
102 grub_uint8_t type;
103 grub_uint64_t offset;
104} __attribute__ ((packed));
105
106struct grub_btrfs_chunk_item
107{
108 grub_uint64_t size;
109 grub_uint64_t dummy;
110 grub_uint64_t stripe_length;
db51e201
VS
111 grub_uint64_t type;
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
6333f1e9 117#define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40
db51e201 118 grub_uint8_t dummy2[0xc];
b18610fe 119 grub_uint16_t nstripes;
db51e201 120 grub_uint16_t nsubstripes;
b18610fe
VS
121} __attribute__ ((packed));
122
123struct grub_btrfs_chunk_stripe
124{
125 grub_uint64_t device_id;
126 grub_uint64_t offset;
0e761d3d 127 grub_btrfs_uuid_t device_uuid;
b18610fe
VS
128} __attribute__ ((packed));
129
130struct grub_btrfs_leaf_node
131{
132 struct grub_btrfs_key key;
133 grub_uint32_t offset;
134 grub_uint32_t size;
135} __attribute__ ((packed));
136
355b3eed
VS
137struct grub_btrfs_internal_node
138{
139 struct grub_btrfs_key key;
0e761d3d 140 grub_uint64_t addr;
355b3eed
VS
141 grub_uint64_t dummy;
142} __attribute__ ((packed));
143
b18610fe
VS
144struct grub_btrfs_dir_item
145{
146 struct grub_btrfs_key key;
147 grub_uint8_t dummy[8];
148 grub_uint16_t m;
149 grub_uint16_t n;
df80cd06
VS
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
b18610fe
VS
153 grub_uint8_t type;
154 char name[0];
155} __attribute__ ((packed));
156
157struct grub_btrfs_leaf_descriptor
158{
159 unsigned depth;
160 unsigned allocated;
161 struct {
162 grub_disk_addr_t addr;
163 unsigned iter;
164 unsigned maxiter;
165 int leaf;
166 } *data;
167};
168
169struct grub_btrfs_root_item
170{
171 grub_uint8_t dummy[0xb0];
172 grub_uint64_t tree;
173 grub_uint64_t inode;
174};
175
d6f07b29
VS
176struct grub_btrfs_time
177{
178 grub_int64_t sec;
179 grub_uint32_t nanosec;
180} __attribute__ ((aligned(4)));
181
b18610fe
VS
182struct grub_btrfs_inode
183{
d6f07b29 184 grub_uint8_t dummy1[0x10];
b18610fe 185 grub_uint64_t size;
d6f07b29
VS
186 grub_uint8_t dummy2[0x70];
187 struct grub_btrfs_time mtime;
b18610fe
VS
188} __attribute__ ((packed));
189
190struct grub_btrfs_extent_data
191{
192 grub_uint64_t dummy;
193 grub_uint64_t size;
194 grub_uint8_t compression;
195 grub_uint8_t encryption;
196 grub_uint16_t encoding;
197 grub_uint8_t type;
198 union
199 {
200 char inl[0];
3be8e5ea
VS
201 struct
202 {
203 grub_uint64_t laddr;
204 grub_uint64_t compressed_size;
205 grub_uint64_t offset;
6a01f54a 206 grub_uint64_t filled;
3be8e5ea 207 };
b18610fe
VS
208 };
209} __attribute__ ((packed));
210
211#define GRUB_BTRFS_EXTENT_INLINE 0
212#define GRUB_BTRFS_EXTENT_REGULAR 1
213
3be8e5ea
VS
214#define GRUB_BTRFS_COMPRESSION_NONE 0
215#define GRUB_BTRFS_COMPRESSION_ZLIB 1
b18610fe
VS
216
217#define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
218
219static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
220 256 * 1048576 * 2,
221 1048576ULL * 1048576ULL * 2 };
222
223static grub_err_t
224grub_btrfs_read_logical (struct grub_btrfs_data *data,
db51e201
VS
225 grub_disk_addr_t addr, void *buf, grub_size_t size);
226
227static grub_err_t
228read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb)
229{
230 unsigned i;
231 grub_err_t err = GRUB_ERR_NONE;
232 for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++)
233 {
234 struct grub_btrfs_superblock sblock;
235 err = grub_disk_read (disk, superblock_sectors[i], 0,
236 sizeof (sblock), &sblock);
237 if (err == GRUB_ERR_OUT_OF_RANGE)
238 break;
239
240 if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE,
241 sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0)
242 break;
243 if (i == 0 || grub_le_to_cpu64 (sblock.generation)
244 > grub_le_to_cpu64 (sb->generation))
245 grub_memcpy (sb, &sblock, sizeof (sblock));
246 }
247
248 if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0)
249 return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem");
250
251 if (err == GRUB_ERR_OUT_OF_RANGE)
252 grub_errno = err = GRUB_ERR_NONE;
253
254 return err;
255}
b18610fe
VS
256
257static int
258key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b)
259{
260 if (grub_cpu_to_le64 (a->object_id) < grub_cpu_to_le64 (b->object_id))
261 return -1;
262 if (grub_cpu_to_le64 (a->object_id) > grub_cpu_to_le64 (b->object_id))
263 return +1;
264
265 if (a->type < b->type)
266 return -1;
267 if (a->type > b->type)
268 return +1;
269
270 if (grub_cpu_to_le64 (a->offset) < grub_cpu_to_le64 (b->offset))
271 return -1;
272 if (grub_cpu_to_le64 (a->offset) > grub_cpu_to_le64 (b->offset))
273 return +1;
274 return 0;
275}
276
277static void
278free_iterator (struct grub_btrfs_leaf_descriptor *desc)
279{
280 grub_free (desc->data);
281}
282
355b3eed
VS
283static grub_err_t
284save_ref (struct grub_btrfs_leaf_descriptor *desc,
285 grub_disk_addr_t addr, unsigned i, unsigned m, int l)
286{
287 desc->depth++;
98042add 288 if (desc->allocated < desc->depth)
355b3eed
VS
289 {
290 void *newdata;
291 desc->allocated *= 2;
292 newdata = grub_realloc (desc->data, sizeof (desc->data[0])
293 * desc->allocated);
294 if (!newdata)
295 return grub_errno;
296 desc->data = newdata;
297 }
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;
303}
304
b18610fe 305static int
db51e201 306next (struct grub_btrfs_data *data,
b18610fe
VS
307 struct grub_btrfs_leaf_descriptor *desc,
308 grub_disk_addr_t *outaddr, grub_size_t *outsize,
309 struct grub_btrfs_key *key_out)
310{
b18610fe
VS
311 grub_err_t err;
312 struct grub_btrfs_leaf_node leaf;
313
98042add 314 for (; desc->depth > 0; desc->depth--)
b18610fe 315 {
98042add
VS
316 desc->data[desc->depth - 1].iter++;
317 if (desc->data[desc->depth - 1].iter
b18610fe
VS
318 < desc->data[desc->depth - 1].maxiter)
319 break;
b18610fe 320 }
98042add 321 if (desc->depth == 0)
b18610fe
VS
322 return 0;
323 while (!desc->data[desc->depth - 1].leaf)
324 {
355b3eed
VS
325 struct grub_btrfs_internal_node node;
326 struct btrfs_header head;
327
db51e201 328 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
355b3eed
VS
329 * sizeof (node)
330 + sizeof (struct btrfs_header)
331 + desc->data[desc->depth - 1].addr, &node,
332 sizeof (node));
333 if (err)
334 return -err;
335
db51e201 336 err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
355b3eed
VS
337 sizeof (head));
338 if (err)
339 return -err;
340
0e761d3d 341 save_ref (desc, grub_le_to_cpu64 (node.addr), 0,
355b3eed 342 grub_le_to_cpu32 (head.nitems), !head.level);
b18610fe 343 }
db51e201 344 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
b18610fe
VS
345 * sizeof (leaf)
346 + sizeof (struct btrfs_header)
347 + desc->data[desc->depth - 1].addr, &leaf,
348 sizeof (leaf));
349 if (err)
350 return -err;
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);
354 *key_out = leaf.key;
355 return 1;
356}
357
b18610fe 358static grub_err_t
db51e201 359lower_bound (struct grub_btrfs_data *data,
b18610fe
VS
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)
365{
366 grub_disk_addr_t addr = root;
355b3eed 367 int depth = -1;
b18610fe
VS
368
369 if (desc)
370 {
371 desc->allocated = 16;
372 desc->depth = 0;
373 desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
374 if (!desc->data)
375 return grub_errno;
376 }
377
355b3eed
VS
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);
382
b18610fe
VS
383 while (1)
384 {
355b3eed
VS
385 grub_err_t err;
386 struct btrfs_header head;
387
388 reiter:
389 depth++;
390 /* FIXME: preread few nodes into buffer. */
db51e201 391 err = grub_btrfs_read_logical (data, addr, &head, sizeof (head));
b18610fe
VS
392 if (err)
393 return err;
b18610fe 394 addr += sizeof (head);
355b3eed 395 if (head.level)
b18610fe 396 {
355b3eed
VS
397 unsigned i;
398 struct grub_btrfs_internal_node node, node_last;
399 int have_last = 0;
ac5dcabe 400 grub_memset (&node_last, 0, sizeof (node_last));
355b3eed 401 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
b18610fe 402 {
db51e201 403 err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
355b3eed
VS
404 &node, sizeof (node));
405 if (err)
406 return err;
407
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);
412
413 if (key_cmp (&node.key, key_in) == 0)
414 {
415 err = GRUB_ERR_NONE;
416 if (desc)
417 err = save_ref (desc, addr - sizeof (head), i,
418 grub_le_to_cpu32 (head.nitems), 0);
419 if (err)
420 return err;
0e761d3d 421 addr = grub_le_to_cpu64 (node.addr);
355b3eed
VS
422 goto reiter;
423 }
424 if (key_cmp (&node.key, key_in) > 0)
425 break;
426 node_last = node;
427 have_last = 1;
428 }
429 if (have_last)
430 {
355b3eed 431 err = GRUB_ERR_NONE;
b18610fe 432 if (desc)
355b3eed
VS
433 err = save_ref (desc, addr - sizeof (head), i - 1,
434 grub_le_to_cpu32 (head.nitems), 0);
435 if (err)
436 return err;
0e761d3d 437 addr = grub_le_to_cpu64 (node_last.addr);
355b3eed 438 goto reiter;
b18610fe 439 }
355b3eed
VS
440 *outsize = 0;
441 *outaddr = 0;
442 grub_memset (key_out, 0, sizeof (*key_out));
b18610fe 443 if (desc)
355b3eed
VS
444 return save_ref (desc, addr - sizeof (head), -1,
445 grub_le_to_cpu32 (head.nitems), 0);
446 return GRUB_ERR_NONE;
b18610fe 447 }
355b3eed
VS
448 {
449 unsigned i;
450 struct grub_btrfs_leaf_node leaf, leaf_last;
451 int have_last = 0;
452 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
453 {
db51e201 454 err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
355b3eed
VS
455 &leaf, sizeof (leaf));
456 if (err)
457 return err;
458
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);
463
464 if (key_cmp (&leaf.key, key_in) == 0)
465 {
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);
469 if (desc)
470 return save_ref (desc, addr - sizeof (head), i,
471 grub_le_to_cpu32 (head.nitems), 1);
472 return GRUB_ERR_NONE;
473 }
474
475 if (key_cmp (&leaf.key, key_in) > 0)
476 break;
477
478 have_last = 1;
479 leaf_last = leaf;
480 }
481
482 if (have_last)
483 {
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);
487 if (desc)
488 return save_ref (desc, addr - sizeof (head), i - 1,
489 grub_le_to_cpu32 (head.nitems), 1);
490 return GRUB_ERR_NONE;
491 }
492 *outsize = 0;
493 *outaddr = 0;
494 grub_memset (key_out, 0, sizeof (*key_out));
495 if (desc)
496 return save_ref (desc, addr - sizeof (head), -1,
497 grub_le_to_cpu32 (head.nitems), 1);
498 return GRUB_ERR_NONE;
499 }
b18610fe
VS
500 }
501}
502
db51e201 503static grub_device_t
79282228
VS
504find_device (struct grub_btrfs_data *data, grub_uint64_t id,
505 int do_rescan)
db51e201
VS
506{
507 grub_device_t dev_found = NULL;
508 auto int hook (const char *name);
509 int hook (const char *name)
510 {
511 grub_device_t dev;
512 grub_err_t err;
513 struct grub_btrfs_superblock sb;
514 dev = grub_device_open (name);
515 if (!dev)
516 return 0;
517 if (!dev->disk)
518 {
519 grub_device_close (dev);
520 return 0;
521 }
522 err = read_sblock (dev->disk, &sb);
523 if (err == GRUB_ERR_BAD_FS)
524 {
525 grub_device_close (dev);
526 grub_errno = GRUB_ERR_NONE;
527 return 0;
528 }
529 if (err)
530 {
531 grub_device_close (dev);
532 grub_print_error ();
533 return 0;
534 }
535 if (grub_memcmp (data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0
536 || sb.this_device.device_id != id)
537 {
538 grub_device_close (dev);
539 return 0;
540 }
541
542 dev_found = dev;
543 return 1;
544 }
545
546 unsigned i;
547
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;
79282228
VS
551 if (do_rescan)
552 grub_device_iterate (hook);
db51e201
VS
553 if (!dev_found)
554 {
555 grub_error (GRUB_ERR_BAD_FS, "couldn't find a member device");
556 return NULL;
557 }
558 data->n_devices_attached++;
559 if (data->n_devices_attached > data->n_devices_allocated)
560 {
561 void *tmp;
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)
568 {
569 grub_device_close (dev_found);
570 data->devices_attached = tmp;
571 return NULL;
572 }
573 }
574 data->devices_attached[data->n_devices_attached - 1].id = id;
575 data->devices_attached[data->n_devices_attached - 1].dev = dev_found;
576 return dev_found;
577}
578
b18610fe
VS
579static grub_err_t
580grub_btrfs_read_logical (struct grub_btrfs_data *data,
db51e201 581 grub_disk_addr_t addr,
b18610fe
VS
582 void *buf, grub_size_t size)
583{
584 while (size > 0)
585 {
586 grub_uint8_t *ptr;
587 struct grub_btrfs_key *key;
588 struct grub_btrfs_chunk_item *chunk;
db51e201 589 grub_ssize_t csize;
b18610fe 590 grub_err_t err;
b18610fe
VS
591 struct grub_btrfs_key key_out;
592 int challoc = 0;
db51e201 593 grub_device_t dev;
ec25b87d
VS
594 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n",
595 addr);
b18610fe
VS
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);
600 )
601 {
602 key = (struct grub_btrfs_key *) ptr;
603 if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK)
604 break;
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))
612 goto chunk_found;
613 ptr += sizeof (*key) + sizeof (*chunk)
79282228
VS
614 + sizeof (struct grub_btrfs_chunk_stripe)
615 * grub_le_to_cpu16 (chunk->nstripes);
b18610fe
VS
616 }
617 struct grub_btrfs_key key_in;
618 grub_size_t chsize;
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;
db51e201 623 err = lower_bound (data, &key_in, &key_out,
b18610fe
VS
624 grub_le_to_cpu64 (data->sblock.chunk_tree),
625 &chaddr, &chsize, NULL);
626 if (err)
627 return err;
628 key = &key_out;
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");
633
634 chunk = grub_malloc (chsize);
635 if (!chunk)
636 return grub_errno;
637
638 challoc = 1;
db51e201 639 err = grub_btrfs_read_logical (data, chaddr, chunk, chsize);
b18610fe
VS
640 if (err)
641 {
642 grub_free (chunk);
643 return err;
644 }
b18610fe
VS
645
646 chunk_found:
db51e201
VS
647 {
648 grub_uint32_t stripen;
649 grub_uint32_t stripe_offset;
650 grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset);
79282228
VS
651 unsigned redundancy = 1;
652 unsigned i, j;
db51e201 653
ec25b87d
VS
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));
663
db51e201
VS
664 switch (grub_le_to_cpu64 (chunk->type)
665 & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE)
666 {
667 case GRUB_BTRFS_CHUNK_TYPE_SINGLE:
668 {
669 grub_uint32_t stripe_length;
670 stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
671 grub_le_to_cpu16 (chunk->nstripes),
672 NULL);
673 stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
674 csize = (stripen + 1) * stripe_length - off;
675 break;
676 }
677 case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED:
678 case GRUB_BTRFS_CHUNK_TYPE_RAID1:
db51e201 679 {
db51e201
VS
680 stripen = 0;
681 stripe_offset = off;
099821e9 682 csize = grub_le_to_cpu64 (chunk->size) - off;
79282228 683 redundancy = 2;
db51e201
VS
684 break;
685 }
686 case GRUB_BTRFS_CHUNK_TYPE_RAID0:
687 {
688 grub_uint64_t middle, high;
689 grub_uint32_t low;
690 middle = grub_divmod64 (off,
691 grub_le_to_cpu64 (chunk->stripe_length),
692 &low);
693
694 high = grub_divmod64 (middle, grub_le_to_cpu16 (chunk->nstripes),
695 &stripen);
696 stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
697 * high;
698 csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
699 break;
700 }
6333f1e9 701 case GRUB_BTRFS_CHUNK_TYPE_RAID10:
6333f1e9
VS
702 {
703 grub_uint64_t middle, high;
704 grub_uint32_t low;
705 middle = grub_divmod64 (off,
706 grub_le_to_cpu64 (chunk->stripe_length),
707 &low);
708
709 high = grub_divmod64 (middle,
710 grub_le_to_cpu16 (chunk->nsubstripes),
711 &stripen);
712 stripen *= grub_le_to_cpu16 (chunk->nstripes)
713 / grub_le_to_cpu16 (chunk->nsubstripes);
79282228
VS
714 redundancy = grub_le_to_cpu16 (chunk->nstripes)
715 / grub_le_to_cpu16 (chunk->nsubstripes);
6333f1e9
VS
716 stripe_offset = low + grub_le_to_cpu64 (chunk->stripe_length)
717 * high;
718 csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
719 break;
720 }
db51e201 721 default:
db51e201
VS
722 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
723 "unsupported RAID flags %" PRIxGRUB_UINT64_T,
724 grub_le_to_cpu64 (chunk->type));
725 }
db51e201
VS
726 if (csize <= 0)
727 return grub_error (GRUB_ERR_BAD_FS,
728 "couldn't find the chunk descriptor");
729 if ((grub_size_t) csize > size)
730 csize = size;
79282228
VS
731
732 for (j = 0; j < 2; j++)
733 {
734 for (i = 0; i < redundancy; i++)
735 {
736 struct grub_btrfs_chunk_stripe *stripe;
737 grub_disk_addr_t paddr;
738
739 stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1);
5870a4a0 740 /* Right now the redundancy handling is easy.
79282228
VS
741 With RAID5-like it will be more difficult. */
742 stripe += stripen + i;
743
744 paddr = stripe->offset + stripe_offset;
745
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,
758 addr);
759
760 dev = find_device (data, stripe->device_id, j);
761 if (!dev)
762 {
763 err = grub_errno;
764 grub_errno = GRUB_ERR_NONE;
765 continue;
766 }
767
768 err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS,
769 paddr & (GRUB_DISK_SECTOR_SIZE - 1),
770 csize, buf);
771 if (!err)
772 break;
773 grub_errno = GRUB_ERR_NONE;
774 }
775 if (i != redundancy)
776 break;
777 }
db51e201 778 if (err)
79282228 779 return grub_errno = err;
db51e201 780 }
b18610fe
VS
781 size -= csize;
782 buf = (grub_uint8_t *) buf + csize;
783 addr += csize;
784 if (challoc)
785 grub_free (chunk);
786 }
787 return GRUB_ERR_NONE;
788}
789
4dfbc574 790static struct grub_btrfs_data *
db51e201 791grub_btrfs_mount (grub_device_t dev)
4dfbc574 792{
db51e201
VS
793 struct grub_btrfs_data *data;
794 grub_err_t err;
b18610fe 795
db51e201
VS
796 if (!dev->disk)
797 {
798 grub_error (GRUB_ERR_BAD_FS, "not BtrFS");
799 return NULL;
800 }
801
802 data = grub_zalloc (sizeof (*data));
4dfbc574
RM
803 if (! data)
804 return NULL;
805
db51e201
VS
806 err = read_sblock (dev->disk, &data->sblock);
807 if (err)
b18610fe 808 {
db51e201
VS
809 grub_free (data);
810 return NULL;
b18610fe
VS
811 }
812
db51e201
VS
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)
4dfbc574 817 {
db51e201
VS
818 grub_free (data);
819 return NULL;
4dfbc574 820 }
db51e201
VS
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;
b18610fe 824
4dfbc574 825 return data;
db51e201 826}
4dfbc574 827
db51e201
VS
828static void
829grub_btrfs_unmount (struct grub_btrfs_data *data)
830{
831 unsigned i;
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);
4dfbc574 837 grub_free (data);
4dfbc574
RM
838}
839
34018a7d 840static grub_err_t
db51e201 841grub_btrfs_read_inode (struct grub_btrfs_data *data,
34018a7d
VS
842 struct grub_btrfs_inode *inode, grub_uint64_t num,
843 grub_uint64_t tree)
844{
845 struct grub_btrfs_key key_in, key_out;
846 grub_disk_addr_t elemaddr;
847 grub_size_t elemsize;
848 grub_err_t err;
849
850 key_in.object_id = num;
851 key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
852 key_in.offset = 0;
853
db51e201 854 err = lower_bound (data, &key_in, &key_out, tree,
34018a7d
VS
855 &elemaddr, &elemsize, NULL);
856 if (err)
857 return err;
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");
861
db51e201 862 return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode));
34018a7d
VS
863}
864
865static grub_ssize_t
db51e201 866grub_btrfs_extent_read (struct grub_btrfs_data *data,
34018a7d
VS
867 grub_uint64_t ino, grub_uint64_t tree,
868 grub_off_t pos0, char *buf, grub_size_t len)
869{
870 grub_off_t pos = pos0;
871 while (len)
872 {
873 grub_size_t csize;
874 grub_err_t err;
875 grub_off_t extoff;
876 if (!data->extent || data->extstart > pos || data->extino != ino
6a01f54a 877 || data->exttree != tree || data->extend <= pos)
34018a7d
VS
878 {
879 struct grub_btrfs_key key_in, key_out;
880 grub_disk_addr_t elemaddr;
881 grub_size_t elemsize;
6a01f54a 882
34018a7d
VS
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);
db51e201 887 err = lower_bound (data, &key_in, &key_out, tree,
34018a7d
VS
888 &elemaddr, &elemsize, NULL);
889 if (err)
890 return -1;
891 if (key_out.object_id != ino
892 || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
893 {
894 grub_error (GRUB_ERR_BAD_FS, "extent not found");
895 return -1;
896 }
897 data->extstart = grub_le_to_cpu64 (key_out.offset);
3be8e5ea 898 data->extsize = elemsize;
34018a7d
VS
899 data->extent = grub_malloc (elemsize);
900 data->extino = ino;
901 data->exttree = tree;
902 if (!data->extent)
903 return grub_errno;
904
db51e201 905 err = grub_btrfs_read_logical (data, elemaddr,
34018a7d
VS
906 data->extent, elemsize);
907 if (err)
908 return err;
6a01f54a
VS
909
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);
918
34018a7d 919 grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%"
6a01f54a
VS
920 PRIxGRUB_UINT64_T " (0x%"
921 PRIxGRUB_UINT64_T ")\n",
34018a7d 922 grub_le_to_cpu64 (key_out.offset),
6a01f54a
VS
923 grub_le_to_cpu64 (data->extent->size),
924 grub_le_to_cpu64 (data->extent->filled));
925 if (data->extend <= pos)
926 {
927 grub_error (GRUB_ERR_BAD_FS, "extent not found");
928 return -1;
929 }
34018a7d 930 }
6a01f54a 931 csize = data->extend - pos;
34018a7d
VS
932 extoff = pos - data->extstart;
933 if (csize > len)
934 csize = len;
935
936 if (data->extent->encryption)
937 {
938 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
939 "encryption not supported");
940 return -1;
941 }
942
3be8e5ea
VS
943 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
944 && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB)
34018a7d
VS
945 {
946 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3be8e5ea
VS
947 "compression type 0x%x not supported",
948 data->extent->compression);
34018a7d
VS
949 return -1;
950 }
951
34018a7d
VS
952 if (data->extent->encoding)
953 {
954 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
955 "encoding not supported");
956 return -1;
957 }
958
959 switch (data->extent->type)
960 {
961 case GRUB_BTRFS_EXTENT_INLINE:
3be8e5ea
VS
962 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
963 {
964 if (grub_zlib_decompress (data->extent->inl, data->extsize -
965 ((grub_uint8_t *) data->extent->inl
966 - (grub_uint8_t *) data->extent),
967 extoff, buf, csize)
968 != (grub_ssize_t) csize)
969 return -1;
970 }
971 else
972 grub_memcpy (buf, data->extent->inl + extoff, csize);
34018a7d
VS
973 break;
974 case GRUB_BTRFS_EXTENT_REGULAR:
975 if (!data->extent->laddr)
976 {
977 grub_memset (buf, 0, csize);
978 break;
979 }
3be8e5ea
VS
980 if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB)
981 {
982 char *tmp;
983 grub_uint64_t zsize;
984 zsize = grub_le_to_cpu64 (data->extent->compressed_size);
985 tmp = grub_malloc (zsize);
986 if (!tmp)
987 return -1;
988 err = grub_btrfs_read_logical (data,
989 grub_le_to_cpu64 (data->extent->laddr),
990 tmp, zsize);
991 if (err)
992 {
993 grub_free (tmp);
994 return -1;
995 }
996 if (grub_zlib_decompress (tmp, zsize, extoff
997 + grub_le_to_cpu64 (data->extent->offset),
998 buf, csize) != (grub_ssize_t) csize)
999 {
1000 grub_free (tmp);
1001 return -1;
1002 }
1003 grub_free (tmp);
1004 break;
1005 }
db51e201 1006 err = grub_btrfs_read_logical (data,
34018a7d 1007 grub_le_to_cpu64 (data->extent->laddr)
565f0763 1008 + grub_le_to_cpu64 (data->extent->offset)
34018a7d
VS
1009 + extoff,
1010 buf, csize);
1011 if (err)
1012 return -1;
1013 break;
1014 default:
1015 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
1016 "unsupported extent type 0x%x", data->extent->type);
1017 return -1;
1018 }
1019 buf += csize;
1020 pos += csize;
1021 len -= csize;
1022 }
1023 return pos - pos0;
1024}
1025
4dfbc574 1026static grub_err_t
b18610fe 1027find_path (struct grub_btrfs_data *data,
b18610fe 1028 const char *path, struct grub_btrfs_key *key,
df80cd06 1029 grub_uint64_t *tree, grub_uint8_t *type)
4dfbc574 1030{
d9865a25 1031 const char *slash = path;
b18610fe
VS
1032 grub_err_t err;
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;
34018a7d 1038 int skip_default;
d9865a25
VS
1039 const char *ctoken;
1040 grub_size_t ctokenlen;
34018a7d 1041 char *path_alloc = NULL;
db51e201 1042 unsigned symlinks_max = 32;
b18610fe 1043
df80cd06 1044 *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
b18610fe
VS
1045 *tree = data->sblock.root_tree;
1046 key->object_id = data->sblock.root_dir_objectid;
1f60e353
VS
1047 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1048 key->offset = 0;
34018a7d 1049 skip_default = 1;
b18610fe
VS
1050
1051 while (1)
1052 {
d9865a25
VS
1053 if (!skip_default)
1054 {
1055 while (path[0] == '/')
1056 path++;
1057 if (!path[0])
1058 break;
1059 slash = grub_strchr (path, '/');
1060 if (!slash)
1061 slash = path + grub_strlen (path);
1062 ctoken = path;
1063 ctokenlen = slash - path;
1064 }
1065 else
1066 {
1067 ctoken = "default";
1068 ctokenlen = sizeof ("default") - 1;
1069 }
df80cd06
VS
1070
1071 if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
34018a7d
VS
1072 {
1073 grub_free (path_alloc);
1074 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1075 }
df80cd06
VS
1076
1077 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
d9865a25 1078 key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
b18610fe 1079
db51e201 1080 err = lower_bound (data, key, &key_out, *tree,
b18610fe
VS
1081 &elemaddr, &elemsize, NULL);
1082 if (err)
1083 {
1084 grub_free (direl);
34018a7d 1085 grub_free (path_alloc);
b18610fe
VS
1086 return err;
1087 }
1088 if (key_cmp (key, &key_out) != 0)
1089 {
1090 grub_free (direl);
34018a7d 1091 grub_free (path_alloc);
b18610fe
VS
1092 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1093 }
1094
1095 struct grub_btrfs_dir_item *cdirel;
1096 if (elemsize > allocated)
1097 {
1098 allocated = 2 * elemsize;
1099 grub_free (direl);
1100 direl = grub_malloc (allocated + 1);
1101 if (!direl)
34018a7d
VS
1102 {
1103 grub_free (path_alloc);
1104 return grub_errno;
1105 }
b18610fe
VS
1106 }
1107
db51e201 1108 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
b18610fe
VS
1109 if (err)
1110 {
1111 grub_free (direl);
34018a7d 1112 grub_free (path_alloc);
b18610fe
VS
1113 return err;
1114 }
1115
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)))
1122 {
228f95a2
VS
1123 if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
1124 && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
b18610fe 1125 break;
b18610fe
VS
1126 }
1127 if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1128 >= (grub_ssize_t) elemsize)
1129 {
1130 grub_free (direl);
34018a7d 1131 grub_free (path_alloc);
b18610fe
VS
1132 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1133 }
1134
d9865a25
VS
1135 if (!skip_default)
1136 path = slash;
1137 skip_default = 0;
34018a7d 1138 if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
df80cd06 1139 {
34018a7d
VS
1140 struct grub_btrfs_inode inode;
1141 char *tmp;
db51e201
VS
1142 if (--symlinks_max == 0)
1143 {
1144 grub_free (direl);
1145 grub_free (path_alloc);
1146 return grub_error (GRUB_ERR_SYMLINK_LOOP,
1147 "too deep nesting of symlinks");
1148 }
1149
1150 err = grub_btrfs_read_inode (data, &inode,
34018a7d
VS
1151 cdirel->key.object_id, *tree);
1152 if (err)
1153 {
1154 grub_free (direl);
1155 grub_free (path_alloc);
1156 return err;
1157 }
1158 tmp = grub_malloc (grub_le_to_cpu64 (inode.size)
1159 + grub_strlen (path) + 1);
1160 if (!tmp)
1161 {
1162 grub_free (direl);
1163 grub_free (path_alloc);
1164 return grub_errno;
1165 }
1166
db51e201 1167 if (grub_btrfs_extent_read (data, cdirel->key.object_id,
34018a7d
VS
1168 *tree, 0, tmp,
1169 grub_le_to_cpu64 (inode.size))
1170 != (grub_ssize_t) grub_le_to_cpu64 (inode.size))
1171 {
1172 grub_free (direl);
1173 grub_free (path_alloc);
1174 grub_free (tmp);
1175 return grub_errno;
1176 }
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;
1181 if (path[0] == '/')
1182 {
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;
1187 key->offset = 0;
1188 skip_default = 1;
1189 }
1190 continue;
df80cd06 1191 }
34018a7d 1192 *type = cdirel->type;
93e0c7a7 1193
b18610fe
VS
1194 switch (cdirel->key.type)
1195 {
1196 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
1197 {
1198 struct grub_btrfs_root_item ri;
db51e201 1199 err = lower_bound (data, &cdirel->key, &key_out,
93e0c7a7 1200 data->sblock.root_tree,
b18610fe
VS
1201 &elemaddr, &elemsize, NULL);
1202 if (err)
34018a7d
VS
1203 {
1204 grub_free (direl);
1205 grub_free (path_alloc);
1206 return err;
1207 }
b18610fe
VS
1208 if (cdirel->key.object_id != key_out.object_id
1209 || cdirel->key.type != key_out.type)
34018a7d
VS
1210 {
1211 grub_free (direl);
1212 grub_free (path_alloc);
1213 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1214 }
db51e201 1215 err = grub_btrfs_read_logical (data, elemaddr,
b18610fe
VS
1216 &ri, sizeof (ri));
1217 if (err)
34018a7d
VS
1218 {
1219 grub_free (direl);
1220 grub_free (path_alloc);
1221 return err;
1222 }
b18610fe
VS
1223 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1224 key->offset = 0;
1225 key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
1226 *tree = grub_le_to_cpu64 (ri.tree);
1227 break;
1228 }
1229 case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM:
9b4cb862 1230 if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
34018a7d
VS
1231 {
1232 grub_free (direl);
1233 grub_free (path_alloc);
1234 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1235 }
b18610fe 1236 *key = cdirel->key;
9b4cb862 1237 if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
34018a7d 1238 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
b18610fe
VS
1239 break;
1240 default:
34018a7d
VS
1241 grub_free (path_alloc);
1242 grub_free (direl);
b18610fe
VS
1243 return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x",
1244 cdirel->key.type);
1245 }
1246 }
1247
1248 grub_free (direl);
b18610fe 1249 return GRUB_ERR_NONE;
4dfbc574
RM
1250}
1251
1252static grub_err_t
eb82b856 1253grub_btrfs_dir (grub_device_t device, const char *path,
4dfbc574 1254 int (*hook) (const char *filename,
b18610fe 1255 const struct grub_dirhook_info *info))
4dfbc574 1256{
db51e201 1257 struct grub_btrfs_data *data = grub_btrfs_mount (device);
b18610fe
VS
1258 struct grub_btrfs_key key_in, key_out;
1259 grub_err_t err;
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;
a3d1fcfb 1265 int r = 0;
b18610fe 1266 grub_uint64_t tree;
df80cd06 1267 grub_uint8_t type;
b18610fe
VS
1268
1269 if (!data)
4dfbc574
RM
1270 return grub_errno;
1271
db51e201 1272 err = find_path (data, path, &key_in, &tree, &type);
b18610fe
VS
1273 if (err)
1274 return err;
df80cd06
VS
1275 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1276 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
b18610fe 1277
db51e201 1278 err = lower_bound (data, &key_in, &key_out, tree,
b18610fe
VS
1279 &elemaddr, &elemsize, &desc);
1280 if (err)
1281 return err;
1282 if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM
1283 || key_out.object_id != key_in.object_id)
1284 {
db51e201 1285 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
b18610fe
VS
1286 if (r <= 0)
1287 {
1288 free_iterator (&desc);
1289 return -r;
1290 }
1291 }
1292 do
1293 {
b18610fe
VS
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)
1297 {
1298 r = 0;
1299 break;
1300 }
1301 if (elemsize > allocated)
1302 {
1303 allocated = 2 * elemsize;
1304 grub_free (direl);
1305 direl = grub_malloc (allocated + 1);
1306 if (!direl)
1307 {
1308 free_iterator (&desc);
1309 return grub_errno;
1310 }
1311 }
1312
db51e201 1313 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
b18610fe
VS
1314 if (err)
1315 return err;
1316
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)))
1323 {
1324 char c;
d6f07b29
VS
1325 struct grub_btrfs_inode inode;
1326 struct grub_dirhook_info info;
1327 err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
1328 tree);
1329 grub_memset (&info, 0, sizeof (info));
1330 if (err)
1331 grub_errno = GRUB_ERR_NONE;
1332 else
1333 {
1334 info.mtime = inode.mtime.sec;
1335 info.mtimeset = 1;
1336 }
b18610fe
VS
1337 c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
1338 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
df80cd06 1339 info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
b18610fe
VS
1340 if (hook (cdirel->name, &info))
1341 goto out;
1342 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1343 }
db51e201 1344 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
b18610fe
VS
1345 }
1346 while (r > 0);
1347
1348 out:
1349 grub_free (direl);
1350
1351 free_iterator (&desc);
db51e201 1352 grub_btrfs_unmount (data);
4dfbc574 1353
b18610fe
VS
1354 return -r;
1355}
1356
1357static grub_err_t
1358grub_btrfs_open (struct grub_file *file, const char *name)
1359{
db51e201 1360 struct grub_btrfs_data *data = grub_btrfs_mount (file->device);
b18610fe 1361 grub_err_t err;
b18610fe 1362 struct grub_btrfs_inode inode;
df80cd06 1363 grub_uint8_t type;
34018a7d 1364 struct grub_btrfs_key key_in;
b18610fe
VS
1365
1366 if (!data)
1367 return grub_errno;
1368
db51e201 1369 err = find_path (data, name, &key_in, &data->tree, &type);
b18610fe
VS
1370 if (err)
1371 {
db51e201 1372 grub_btrfs_unmount (data);
b18610fe
VS
1373 return err;
1374 }
df80cd06 1375 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
34018a7d 1376 {
db51e201 1377 grub_btrfs_unmount (data);
34018a7d
VS
1378 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
1379 }
b18610fe 1380
34018a7d 1381 data->inode = key_in.object_id;
db51e201 1382 err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
b18610fe 1383 if (err)
34018a7d 1384 {
db51e201 1385 grub_btrfs_unmount (data);
34018a7d
VS
1386 return err;
1387 }
b18610fe
VS
1388
1389 file->data = data;
1390 file->size = grub_le_to_cpu64 (inode.size);
1391
34018a7d 1392 return err;
b18610fe
VS
1393}
1394
1395static grub_err_t
1396grub_btrfs_close (grub_file_t file)
1397{
db51e201 1398 grub_btrfs_unmount (file->data);
b18610fe 1399
4dfbc574
RM
1400 return GRUB_ERR_NONE;
1401}
1402
b18610fe
VS
1403static grub_ssize_t
1404grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
1405{
1406 struct grub_btrfs_data *data = file->data;
b18610fe 1407
db51e201 1408 return grub_btrfs_extent_read (data, data->inode,
34018a7d 1409 data->tree, file->offset, buf, len);
b18610fe
VS
1410}
1411
4dfbc574
RM
1412static grub_err_t
1413grub_btrfs_uuid (grub_device_t device, char **uuid)
1414{
1415 struct grub_btrfs_data *data;
1416
1417 *uuid = NULL;
1418
db51e201 1419 data = grub_btrfs_mount (device);
4dfbc574
RM
1420 if (! data)
1421 return grub_errno;
1422
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]));
1432
db51e201 1433 grub_btrfs_unmount (data);
4dfbc574
RM
1434
1435 return grub_errno;
1436}
1437
b18610fe
VS
1438static grub_err_t
1439grub_btrfs_label (grub_device_t device, char **label)
1440{
1441 struct grub_btrfs_data *data;
1442
1443 *label = NULL;
1444
db51e201 1445 data = grub_btrfs_mount (device);
b18610fe
VS
1446 if (! data)
1447 return grub_errno;
1448
1449 *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
1450
db51e201 1451 grub_btrfs_unmount (data);
b18610fe
VS
1452
1453 return grub_errno;
1454}
1455
4dfbc574
RM
1456static struct grub_fs grub_btrfs_fs =
1457 {
1458 .name = "btrfs",
1459 .dir = grub_btrfs_dir,
1460 .open = grub_btrfs_open,
b18610fe
VS
1461 .read = grub_btrfs_read,
1462 .close = grub_btrfs_close,
4dfbc574 1463 .uuid = grub_btrfs_uuid,
b18610fe 1464 .label = grub_btrfs_label,
4dfbc574
RM
1465 };
1466
1467GRUB_MOD_INIT(btrfs)
1468{
1469 grub_fs_register (&grub_btrfs_fs);
1470}
1471
1472GRUB_MOD_FINI(btrfs)
1473{
1474 grub_fs_unregister (&grub_btrfs_fs);
1475}