]> git.proxmox.com Git - grub2.git/blob - grub-core/fs/btrfs.c
merge mainline into butter
[grub2.git] / grub-core / fs / btrfs.c
1 /* btrfs.c - B-tree file system. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27 #include <grub/lib/crc.h>
28 #include <grub/deflate.h>
29
30 #define GRUB_BTRFS_SIGNATURE "_BHRfS_M"
31
32 typedef grub_uint8_t grub_btrfs_checksum_t[0x20];
33 typedef grub_uint16_t grub_btrfs_uuid_t[8];
34
35 struct grub_btrfs_device
36 {
37 grub_uint64_t device_id;
38 grub_uint8_t dummy[0x62 - 8];
39 } __attribute__ ((packed));
40
41 struct grub_btrfs_superblock
42 {
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;
54 char label[0x100];
55 grub_uint8_t dummy4[0x100];
56 grub_uint8_t bootstrap_mapping[0x800];
57 } __attribute__ ((packed));
58
59 struct btrfs_header
60 {
61 grub_btrfs_checksum_t checksum;
62 grub_btrfs_uuid_t uuid;
63 grub_uint8_t dummy[0x30];
64 grub_uint32_t nitems;
65 grub_uint8_t level;
66 } __attribute__ ((packed));
67
68 struct grub_btrfs_device_desc
69 {
70 grub_device_t dev;
71 grub_uint64_t id;
72 };
73
74 struct grub_btrfs_data
75 {
76 struct grub_btrfs_superblock sblock;
77 grub_uint64_t tree;
78 grub_uint64_t inode;
79
80 struct grub_btrfs_device_desc *devices_attached;
81 unsigned n_devices_attached;
82 unsigned n_devices_allocated;
83
84 /* Cached extent data. */
85 grub_uint64_t extstart;
86 grub_uint64_t extend;
87 grub_uint64_t extino;
88 grub_uint64_t exttree;
89 grub_size_t extsize;
90 struct grub_btrfs_extent_data *extent;
91 };
92
93 struct 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
106 struct grub_btrfs_chunk_item
107 {
108 grub_uint64_t size;
109 grub_uint64_t dummy;
110 grub_uint64_t stripe_length;
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
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));
122
123 struct grub_btrfs_chunk_stripe
124 {
125 grub_uint64_t device_id;
126 grub_uint64_t offset;
127 grub_btrfs_uuid_t device_uuid;
128 } __attribute__ ((packed));
129
130 struct 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
137 struct grub_btrfs_internal_node
138 {
139 struct grub_btrfs_key key;
140 grub_uint64_t addr;
141 grub_uint64_t dummy;
142 } __attribute__ ((packed));
143
144 struct 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;
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
153 grub_uint8_t type;
154 char name[0];
155 } __attribute__ ((packed));
156
157 struct 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
169 struct grub_btrfs_root_item
170 {
171 grub_uint8_t dummy[0xb0];
172 grub_uint64_t tree;
173 grub_uint64_t inode;
174 };
175
176 struct grub_btrfs_time
177 {
178 grub_int64_t sec;
179 grub_uint32_t nanosec;
180 } __attribute__ ((aligned(4)));
181
182 struct grub_btrfs_inode
183 {
184 grub_uint8_t dummy1[0x10];
185 grub_uint64_t size;
186 grub_uint8_t dummy2[0x70];
187 struct grub_btrfs_time mtime;
188 } __attribute__ ((packed));
189
190 struct 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];
201 struct
202 {
203 grub_uint64_t laddr;
204 grub_uint64_t compressed_size;
205 grub_uint64_t offset;
206 grub_uint64_t filled;
207 };
208 };
209 } __attribute__ ((packed));
210
211 #define GRUB_BTRFS_EXTENT_INLINE 0
212 #define GRUB_BTRFS_EXTENT_REGULAR 1
213
214 #define GRUB_BTRFS_COMPRESSION_NONE 0
215 #define GRUB_BTRFS_COMPRESSION_ZLIB 1
216
217 #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100
218
219 static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2,
220 256 * 1048576 * 2,
221 1048576ULL * 1048576ULL * 2 };
222
223 static grub_err_t
224 grub_btrfs_read_logical (struct grub_btrfs_data *data,
225 grub_disk_addr_t addr, void *buf, grub_size_t size);
226
227 static grub_err_t
228 read_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 }
256
257 static int
258 key_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
277 static void
278 free_iterator (struct grub_btrfs_leaf_descriptor *desc)
279 {
280 grub_free (desc->data);
281 }
282
283 static grub_err_t
284 save_ref (struct grub_btrfs_leaf_descriptor *desc,
285 grub_disk_addr_t addr, unsigned i, unsigned m, int l)
286 {
287 desc->depth++;
288 if (desc->allocated < desc->depth)
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
305 static int
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)
310 {
311 grub_err_t err;
312 struct grub_btrfs_leaf_node leaf;
313
314 for (; desc->depth > 0; desc->depth--)
315 {
316 desc->data[desc->depth - 1].iter++;
317 if (desc->data[desc->depth - 1].iter
318 < desc->data[desc->depth - 1].maxiter)
319 break;
320 }
321 if (desc->depth == 0)
322 return 0;
323 while (!desc->data[desc->depth - 1].leaf)
324 {
325 struct grub_btrfs_internal_node node;
326 struct btrfs_header head;
327
328 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
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
336 err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), &head,
337 sizeof (head));
338 if (err)
339 return -err;
340
341 save_ref (desc, grub_le_to_cpu64 (node.addr), 0,
342 grub_le_to_cpu32 (head.nitems), !head.level);
343 }
344 err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter
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
358 static grub_err_t
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)
365 {
366 grub_disk_addr_t addr = root;
367 int depth = -1;
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
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
383 while (1)
384 {
385 grub_err_t err;
386 struct btrfs_header head;
387
388 reiter:
389 depth++;
390 /* FIXME: preread few nodes into buffer. */
391 err = grub_btrfs_read_logical (data, addr, &head, sizeof (head));
392 if (err)
393 return err;
394 addr += sizeof (head);
395 if (head.level)
396 {
397 unsigned i;
398 struct grub_btrfs_internal_node node, node_last;
399 int have_last = 0;
400 grub_memset (&node_last, 0, sizeof (node_last));
401 for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++)
402 {
403 err = grub_btrfs_read_logical (data, addr + i * sizeof (node),
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;
421 addr = grub_le_to_cpu64 (node.addr);
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 {
431 err = GRUB_ERR_NONE;
432 if (desc)
433 err = save_ref (desc, addr - sizeof (head), i - 1,
434 grub_le_to_cpu32 (head.nitems), 0);
435 if (err)
436 return err;
437 addr = grub_le_to_cpu64 (node_last.addr);
438 goto reiter;
439 }
440 *outsize = 0;
441 *outaddr = 0;
442 grub_memset (key_out, 0, sizeof (*key_out));
443 if (desc)
444 return save_ref (desc, addr - sizeof (head), -1,
445 grub_le_to_cpu32 (head.nitems), 0);
446 return GRUB_ERR_NONE;
447 }
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 {
454 err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf),
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 }
500 }
501 }
502
503 static grub_device_t
504 find_device (struct grub_btrfs_data *data, grub_uint64_t id,
505 int do_rescan)
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;
551 if (do_rescan)
552 grub_device_iterate (hook);
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
579 static grub_err_t
580 grub_btrfs_read_logical (struct grub_btrfs_data *data,
581 grub_disk_addr_t addr,
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;
589 grub_ssize_t csize;
590 grub_err_t err;
591 struct grub_btrfs_key key_out;
592 int challoc = 0;
593 grub_device_t dev;
594 grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n",
595 addr);
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)
614 + sizeof (struct grub_btrfs_chunk_stripe)
615 * grub_le_to_cpu16 (chunk->nstripes);
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;
623 err = lower_bound (data, &key_in, &key_out,
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;
639 err = grub_btrfs_read_logical (data, chaddr, chunk, chsize);
640 if (err)
641 {
642 grub_free (chunk);
643 return err;
644 }
645
646 chunk_found:
647 {
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;
652 unsigned i, j;
653
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
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:
679 {
680 stripen = 0;
681 stripe_offset = off;
682 csize = grub_le_to_cpu64 (chunk->size) - off;
683 redundancy = 2;
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 }
701 case GRUB_BTRFS_CHUNK_TYPE_RAID10:
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);
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)
717 * high;
718 csize = grub_le_to_cpu64 (chunk->stripe_length) - low;
719 break;
720 }
721 default:
722 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
723 "unsupported RAID flags %" PRIxGRUB_UINT64_T,
724 grub_le_to_cpu64 (chunk->type));
725 }
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;
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);
740 /* Right now the redundancy handling is easy.
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 }
778 if (err)
779 return grub_errno = err;
780 }
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
790 static struct grub_btrfs_data *
791 grub_btrfs_mount (grub_device_t dev)
792 {
793 struct grub_btrfs_data *data;
794 grub_err_t err;
795
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));
803 if (! data)
804 return NULL;
805
806 err = read_sblock (dev->disk, &data->sblock);
807 if (err)
808 {
809 grub_free (data);
810 return NULL;
811 }
812
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)
817 {
818 grub_free (data);
819 return NULL;
820 }
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;
824
825 return data;
826 }
827
828 static void
829 grub_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);
837 grub_free (data);
838 }
839
840 static grub_err_t
841 grub_btrfs_read_inode (struct grub_btrfs_data *data,
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
854 err = lower_bound (data, &key_in, &key_out, tree,
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
862 return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode));
863 }
864
865 static grub_ssize_t
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)
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
877 || data->exttree != tree || data->extend <= pos)
878 {
879 struct grub_btrfs_key key_in, key_out;
880 grub_disk_addr_t elemaddr;
881 grub_size_t elemsize;
882
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);
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);
898 data->extsize = elemsize;
899 data->extent = grub_malloc (elemsize);
900 data->extino = ino;
901 data->exttree = tree;
902 if (!data->extent)
903 return grub_errno;
904
905 err = grub_btrfs_read_logical (data, elemaddr,
906 data->extent, elemsize);
907 if (err)
908 return err;
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
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)
926 {
927 grub_error (GRUB_ERR_BAD_FS, "extent not found");
928 return -1;
929 }
930 }
931 csize = data->extend - pos;
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
943 if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE
944 && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB)
945 {
946 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
947 "compression type 0x%x not supported",
948 data->extent->compression);
949 return -1;
950 }
951
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:
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);
973 break;
974 case GRUB_BTRFS_EXTENT_REGULAR:
975 if (!data->extent->laddr)
976 {
977 grub_memset (buf, 0, csize);
978 break;
979 }
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 }
1006 err = grub_btrfs_read_logical (data,
1007 grub_le_to_cpu64 (data->extent->laddr)
1008 + grub_le_to_cpu64 (data->extent->offset)
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
1026 static grub_err_t
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)
1030 {
1031 const char *slash = path;
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;
1038 int skip_default;
1039 const char *ctoken;
1040 grub_size_t ctokenlen;
1041 char *path_alloc = NULL;
1042 unsigned symlinks_max = 32;
1043
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;
1048 key->offset = 0;
1049 skip_default = 1;
1050
1051 while (1)
1052 {
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 }
1070
1071 if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1072 {
1073 grub_free (path_alloc);
1074 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1075 }
1076
1077 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1078 key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
1079
1080 err = lower_bound (data, key, &key_out, *tree,
1081 &elemaddr, &elemsize, NULL);
1082 if (err)
1083 {
1084 grub_free (direl);
1085 grub_free (path_alloc);
1086 return err;
1087 }
1088 if (key_cmp (key, &key_out) != 0)
1089 {
1090 grub_free (direl);
1091 grub_free (path_alloc);
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)
1102 {
1103 grub_free (path_alloc);
1104 return grub_errno;
1105 }
1106 }
1107
1108 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
1109 if (err)
1110 {
1111 grub_free (direl);
1112 grub_free (path_alloc);
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 {
1123 if (ctokenlen == grub_le_to_cpu16 (cdirel->n)
1124 && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0)
1125 break;
1126 }
1127 if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl
1128 >= (grub_ssize_t) elemsize)
1129 {
1130 grub_free (direl);
1131 grub_free (path_alloc);
1132 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1133 }
1134
1135 if (!skip_default)
1136 path = slash;
1137 skip_default = 0;
1138 if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
1139 {
1140 struct grub_btrfs_inode inode;
1141 char *tmp;
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,
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
1167 if (grub_btrfs_extent_read (data, cdirel->key.object_id,
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;
1191 }
1192 *type = cdirel->type;
1193
1194 switch (cdirel->key.type)
1195 {
1196 case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM:
1197 {
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);
1202 if (err)
1203 {
1204 grub_free (direl);
1205 grub_free (path_alloc);
1206 return err;
1207 }
1208 if (cdirel->key.object_id != key_out.object_id
1209 || cdirel->key.type != key_out.type)
1210 {
1211 grub_free (direl);
1212 grub_free (path_alloc);
1213 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1214 }
1215 err = grub_btrfs_read_logical (data, elemaddr,
1216 &ri, sizeof (ri));
1217 if (err)
1218 {
1219 grub_free (direl);
1220 grub_free (path_alloc);
1221 return err;
1222 }
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:
1230 if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1231 {
1232 grub_free (direl);
1233 grub_free (path_alloc);
1234 return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
1235 }
1236 *key = cdirel->key;
1237 if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1238 key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
1239 break;
1240 default:
1241 grub_free (path_alloc);
1242 grub_free (direl);
1243 return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x",
1244 cdirel->key.type);
1245 }
1246 }
1247
1248 grub_free (direl);
1249 return GRUB_ERR_NONE;
1250 }
1251
1252 static grub_err_t
1253 grub_btrfs_dir (grub_device_t device, const char *path,
1254 int (*hook) (const char *filename,
1255 const struct grub_dirhook_info *info))
1256 {
1257 struct grub_btrfs_data *data = grub_btrfs_mount (device);
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;
1265 int r = 0;
1266 grub_uint64_t tree;
1267 grub_uint8_t type;
1268
1269 if (!data)
1270 return grub_errno;
1271
1272 err = find_path (data, path, &key_in, &tree, &type);
1273 if (err)
1274 return err;
1275 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
1276 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
1277
1278 err = lower_bound (data, &key_in, &key_out, tree,
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 {
1285 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1286 if (r <= 0)
1287 {
1288 free_iterator (&desc);
1289 return -r;
1290 }
1291 }
1292 do
1293 {
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
1313 err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize);
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;
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 }
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))
1341 goto out;
1342 cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
1343 }
1344 r = next (data, &desc, &elemaddr, &elemsize, &key_out);
1345 }
1346 while (r > 0);
1347
1348 out:
1349 grub_free (direl);
1350
1351 free_iterator (&desc);
1352 grub_btrfs_unmount (data);
1353
1354 return -r;
1355 }
1356
1357 static grub_err_t
1358 grub_btrfs_open (struct grub_file *file, const char *name)
1359 {
1360 struct grub_btrfs_data *data = grub_btrfs_mount (file->device);
1361 grub_err_t err;
1362 struct grub_btrfs_inode inode;
1363 grub_uint8_t type;
1364 struct grub_btrfs_key key_in;
1365
1366 if (!data)
1367 return grub_errno;
1368
1369 err = find_path (data, name, &key_in, &data->tree, &type);
1370 if (err)
1371 {
1372 grub_btrfs_unmount (data);
1373 return err;
1374 }
1375 if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
1376 {
1377 grub_btrfs_unmount (data);
1378 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
1379 }
1380
1381 data->inode = key_in.object_id;
1382 err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree);
1383 if (err)
1384 {
1385 grub_btrfs_unmount (data);
1386 return err;
1387 }
1388
1389 file->data = data;
1390 file->size = grub_le_to_cpu64 (inode.size);
1391
1392 return err;
1393 }
1394
1395 static grub_err_t
1396 grub_btrfs_close (grub_file_t file)
1397 {
1398 grub_btrfs_unmount (file->data);
1399
1400 return GRUB_ERR_NONE;
1401 }
1402
1403 static grub_ssize_t
1404 grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
1405 {
1406 struct grub_btrfs_data *data = file->data;
1407
1408 return grub_btrfs_extent_read (data, data->inode,
1409 data->tree, file->offset, buf, len);
1410 }
1411
1412 static grub_err_t
1413 grub_btrfs_uuid (grub_device_t device, char **uuid)
1414 {
1415 struct grub_btrfs_data *data;
1416
1417 *uuid = NULL;
1418
1419 data = grub_btrfs_mount (device);
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
1433 grub_btrfs_unmount (data);
1434
1435 return grub_errno;
1436 }
1437
1438 static grub_err_t
1439 grub_btrfs_label (grub_device_t device, char **label)
1440 {
1441 struct grub_btrfs_data *data;
1442
1443 *label = NULL;
1444
1445 data = grub_btrfs_mount (device);
1446 if (! data)
1447 return grub_errno;
1448
1449 *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label));
1450
1451 grub_btrfs_unmount (data);
1452
1453 return grub_errno;
1454 }
1455
1456 static struct grub_fs grub_btrfs_fs =
1457 {
1458 .name = "btrfs",
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,
1465 };
1466
1467 GRUB_MOD_INIT(btrfs)
1468 {
1469 grub_fs_register (&grub_btrfs_fs);
1470 }
1471
1472 GRUB_MOD_FINI(btrfs)
1473 {
1474 grub_fs_unregister (&grub_btrfs_fs);
1475 }