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