Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / fs / bfs.c
blob145e771863c7fb59a2cd183f28525dc0622c35f0
1 /* bfs.c - The Bee File System. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 Based on the book "Practical File System Design by Dominic Giampaolo
21 with corrections and completitions based on Haiku code.
24 #include <grub/err.h>
25 #include <grub/file.h>
26 #include <grub/mm.h>
27 #include <grub/misc.h>
28 #include <grub/disk.h>
29 #include <grub/dl.h>
30 #include <grub/types.h>
31 #include <grub/i18n.h>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #ifdef MODE_AFS
36 #define BTREE_ALIGN 4
37 #define SUPERBLOCK 2
38 #else
39 #define BTREE_ALIGN 8
40 #define SUPERBLOCK 1
41 #endif
43 #define grub_bfs_to_cpu16 grub_le_to_cpu16
44 #define grub_bfs_to_cpu32 grub_le_to_cpu32
45 #define grub_bfs_to_cpu64 grub_le_to_cpu64
46 #define grub_cpu_to_bfs32_compile_time grub_cpu_to_le32_compile_time
48 #ifdef MODE_AFS
49 #define grub_bfs_to_cpu_treehead grub_bfs_to_cpu32
50 #else
51 #define grub_bfs_to_cpu_treehead grub_bfs_to_cpu16
52 #endif
54 #ifdef MODE_AFS
55 #define SUPER_BLOCK_MAGIC1 0x41465331
56 #else
57 #define SUPER_BLOCK_MAGIC1 0x42465331
58 #endif
59 #define SUPER_BLOCK_MAGIC2 0xdd121031
60 #define SUPER_BLOCK_MAGIC3 0x15b6830e
61 #define POINTER_INVALID 0xffffffffffffffffULL
63 #define ATTR_TYPE 0160000
64 #define ATTR_REG 0100000
65 #define ATTR_DIR 0040000
66 #define ATTR_LNK 0120000
68 #define DOUBLE_INDIRECT_SHIFT 2
70 #define LOG_EXTENT_SIZE 3
71 struct grub_bfs_extent
73 grub_uint32_t ag;
74 grub_uint16_t start;
75 grub_uint16_t len;
76 } GRUB_PACKED;
78 struct grub_bfs_superblock
80 char label[32];
81 grub_uint32_t magic1;
82 grub_uint32_t unused1;
83 grub_uint32_t bsize;
84 grub_uint32_t log2_bsize;
85 grub_uint8_t unused[20];
86 grub_uint32_t magic2;
87 grub_uint32_t unused2;
88 grub_uint32_t log2_ag_size;
89 grub_uint8_t unused3[32];
90 grub_uint32_t magic3;
91 struct grub_bfs_extent root_dir;
92 } GRUB_PACKED;
94 struct grub_bfs_inode
96 grub_uint8_t unused[20];
97 grub_uint32_t mode;
98 grub_uint32_t flags;
99 #ifdef MODE_AFS
100 grub_uint8_t unused2[12];
101 #else
102 grub_uint8_t unused2[8];
103 #endif
104 grub_uint64_t mtime;
105 grub_uint8_t unused3[8];
106 struct grub_bfs_extent attr;
107 grub_uint8_t unused4[12];
109 union
111 struct
113 struct grub_bfs_extent direct[12];
114 grub_uint64_t max_direct_range;
115 struct grub_bfs_extent indirect;
116 grub_uint64_t max_indirect_range;
117 struct grub_bfs_extent double_indirect;
118 grub_uint64_t max_double_indirect_range;
119 grub_uint64_t size;
120 grub_uint32_t pad[4];
121 } GRUB_PACKED;
122 char inplace_link[144];
123 } GRUB_PACKED;
124 grub_uint8_t small_data[0];
125 } GRUB_PACKED;
127 enum
129 LONG_SYMLINK = 0x40
132 struct grub_bfs_small_data_element_header
134 grub_uint32_t type;
135 grub_uint16_t name_len;
136 grub_uint16_t value_len;
137 } GRUB_PACKED;
139 struct grub_bfs_btree_header
141 grub_uint32_t magic;
142 #ifdef MODE_AFS
143 grub_uint64_t root;
144 grub_uint32_t level;
145 grub_uint32_t node_size;
146 grub_uint32_t unused;
147 #else
148 grub_uint32_t node_size;
149 grub_uint32_t level;
150 grub_uint32_t unused;
151 grub_uint64_t root;
152 #endif
153 grub_uint32_t unused2[2];
154 } GRUB_PACKED;
156 struct grub_bfs_btree_node
158 grub_uint64_t unused;
159 grub_uint64_t right;
160 grub_uint64_t overflow;
161 #ifdef MODE_AFS
162 grub_uint32_t count_keys;
163 grub_uint32_t total_key_len;
164 #else
165 grub_uint16_t count_keys;
166 grub_uint16_t total_key_len;
167 #endif
168 } GRUB_PACKED;
170 struct grub_bfs_data
172 struct grub_bfs_superblock sb;
173 struct grub_bfs_inode ino;
176 /* Context for grub_bfs_dir. */
177 struct grub_bfs_dir_ctx
179 grub_device_t device;
180 grub_fs_dir_hook_t hook;
181 void *hook_data;
182 struct grub_bfs_superblock sb;
185 static grub_err_t
186 read_extent (grub_disk_t disk,
187 const struct grub_bfs_superblock *sb,
188 const struct grub_bfs_extent *in,
189 grub_off_t off, grub_off_t byteoff, void *buf, grub_size_t len)
191 #ifdef MODE_AFS
192 return grub_disk_read (disk, ((grub_bfs_to_cpu32 (in->ag)
193 << (grub_bfs_to_cpu32 (sb->log2_ag_size)
194 - GRUB_DISK_SECTOR_BITS))
195 + ((grub_bfs_to_cpu16 (in->start) + off)
196 << (grub_bfs_to_cpu32 (sb->log2_bsize)
197 - GRUB_DISK_SECTOR_BITS))),
198 byteoff, len, buf);
199 #else
200 return grub_disk_read (disk, (((grub_bfs_to_cpu32 (in->ag)
201 << grub_bfs_to_cpu32 (sb->log2_ag_size))
202 + grub_bfs_to_cpu16 (in->start) + off)
203 << (grub_bfs_to_cpu32 (sb->log2_bsize)
204 - GRUB_DISK_SECTOR_BITS)),
205 byteoff, len, buf);
206 #endif
209 #ifdef MODE_AFS
210 #define RANGE_SHIFT grub_bfs_to_cpu32 (sb->log2_bsize)
211 #else
212 #define RANGE_SHIFT 0
213 #endif
215 static grub_err_t
216 read_bfs_file (grub_disk_t disk,
217 const struct grub_bfs_superblock *sb,
218 const struct grub_bfs_inode *ino,
219 grub_off_t off, void *buf, grub_size_t len,
220 grub_disk_read_hook_t read_hook, void *read_hook_data)
222 if (len == 0)
223 return GRUB_ERR_NONE;
225 if (off + len > grub_bfs_to_cpu64 (ino->size))
226 return grub_error (GRUB_ERR_OUT_OF_RANGE,
227 N_("attempt to read past the end of file"));
229 if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
231 unsigned i;
232 grub_uint64_t pos = 0;
233 for (i = 0; i < ARRAY_SIZE (ino->direct); i++)
235 grub_uint64_t newpos;
236 newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (ino->direct[i].len))
237 << grub_bfs_to_cpu32 (sb->log2_bsize));
238 if (newpos > off)
240 grub_size_t read_size;
241 grub_err_t err;
242 read_size = newpos - off;
243 if (read_size > len)
244 read_size = len;
245 disk->read_hook = read_hook;
246 disk->read_hook_data = read_hook_data;
247 err = read_extent (disk, sb, &ino->direct[i], 0, off - pos,
248 buf, read_size);
249 disk->read_hook = 0;
250 if (err)
251 return err;
252 off += read_size;
253 len -= read_size;
254 buf = (char *) buf + read_size;
255 if (len == 0)
256 return GRUB_ERR_NONE;
258 pos = newpos;
262 if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
263 return grub_error (GRUB_ERR_BAD_FS, "incorrect direct blocks");
265 if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
267 unsigned i;
268 struct grub_bfs_extent *entries;
269 grub_size_t nentries;
270 grub_err_t err;
271 grub_uint64_t pos = (grub_bfs_to_cpu64 (ino->max_direct_range)
272 << RANGE_SHIFT);
273 nentries = (((grub_size_t) grub_bfs_to_cpu16 (ino->indirect.len))
274 << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
275 entries = grub_malloc (nentries << LOG_EXTENT_SIZE);
276 if (!entries)
277 return grub_errno;
278 err = read_extent (disk, sb, &ino->indirect, 0, 0,
279 entries, nentries << LOG_EXTENT_SIZE);
280 for (i = 0; i < nentries; i++)
282 grub_uint64_t newpos;
283 newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (entries[i].len))
284 << grub_bfs_to_cpu32 (sb->log2_bsize));
285 if (newpos > off)
287 grub_size_t read_size;
288 read_size = newpos - off;
289 if (read_size > len)
290 read_size = len;
291 disk->read_hook = read_hook;
292 disk->read_hook_data = read_hook_data;
293 err = read_extent (disk, sb, &entries[i], 0, off - pos,
294 buf, read_size);
295 disk->read_hook = 0;
296 if (err)
298 grub_free (entries);
299 return err;
301 off += read_size;
302 len -= read_size;
303 buf = (char *) buf + read_size;
304 if (len == 0)
306 grub_free (entries);
307 return GRUB_ERR_NONE;
310 pos = newpos;
312 grub_free (entries);
315 if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
316 return grub_error (GRUB_ERR_BAD_FS, "incorrect indirect blocks");
319 struct grub_bfs_extent *l1_entries, *l2_entries;
320 grub_size_t nl1_entries, nl2_entries;
321 grub_off_t last_l1n = ~0ULL;
322 grub_err_t err;
323 nl1_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (ino->double_indirect.len))
324 << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
325 l1_entries = grub_malloc (nl1_entries << LOG_EXTENT_SIZE);
326 if (!l1_entries)
327 return grub_errno;
328 nl2_entries = 0;
329 l2_entries = grub_malloc (1 << (DOUBLE_INDIRECT_SHIFT
330 + grub_bfs_to_cpu32 (sb->log2_bsize)));
331 if (!l2_entries)
333 grub_free (l1_entries);
334 return grub_errno;
336 err = read_extent (disk, sb, &ino->double_indirect, 0, 0,
337 l1_entries, nl1_entries << LOG_EXTENT_SIZE);
338 if (err)
340 grub_free (l1_entries);
341 grub_free (l2_entries);
342 return err;
345 while (len > 0)
347 grub_off_t boff, l2n, l1n;
348 grub_size_t read_size;
349 grub_off_t double_indirect_offset;
350 double_indirect_offset = off
351 - grub_bfs_to_cpu64 (ino->max_indirect_range);
352 boff = (double_indirect_offset
353 & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
354 + DOUBLE_INDIRECT_SHIFT)) - 1));
355 l2n = ((double_indirect_offset >> (grub_bfs_to_cpu32 (sb->log2_bsize)
356 + DOUBLE_INDIRECT_SHIFT))
357 & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE
358 + DOUBLE_INDIRECT_SHIFT)) - 1));
359 l1n =
360 (double_indirect_offset >>
361 (2 * grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE +
362 2 * DOUBLE_INDIRECT_SHIFT));
363 if (l1n > nl1_entries)
365 grub_free (l1_entries);
366 grub_free (l2_entries);
367 return grub_error (GRUB_ERR_BAD_FS,
368 "incorrect double-indirect block");
370 if (l1n != last_l1n)
372 nl2_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (l1_entries[l1n].len))
373 << (grub_bfs_to_cpu32 (sb->log2_bsize)
374 - LOG_EXTENT_SIZE));
375 if (nl2_entries > (1U << (grub_bfs_to_cpu32 (sb->log2_bsize)
376 - LOG_EXTENT_SIZE
377 + DOUBLE_INDIRECT_SHIFT)))
378 nl2_entries = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
379 - LOG_EXTENT_SIZE
380 + DOUBLE_INDIRECT_SHIFT));
381 err = read_extent (disk, sb, &l1_entries[l1n], 0, 0,
382 l2_entries, nl2_entries << LOG_EXTENT_SIZE);
383 if (err)
385 grub_free (l1_entries);
386 grub_free (l2_entries);
387 return err;
389 last_l1n = l1n;
391 if (l2n > nl2_entries)
393 grub_free (l1_entries);
394 grub_free (l2_entries);
395 return grub_error (GRUB_ERR_BAD_FS,
396 "incorrect double-indirect block");
399 read_size = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
400 + DOUBLE_INDIRECT_SHIFT)) - boff;
401 if (read_size > len)
402 read_size = len;
403 disk->read_hook = read_hook;
404 disk->read_hook_data = read_hook_data;
405 err = read_extent (disk, sb, &l2_entries[l2n], 0, boff,
406 buf, read_size);
407 disk->read_hook = 0;
408 if (err)
410 grub_free (l1_entries);
411 grub_free (l2_entries);
412 return err;
414 off += read_size;
415 len -= read_size;
416 buf = (char *) buf + read_size;
418 return GRUB_ERR_NONE;
422 static grub_err_t
423 read_b_node (grub_disk_t disk,
424 const struct grub_bfs_superblock *sb,
425 const struct grub_bfs_inode *ino,
426 grub_uint64_t node_off,
427 struct grub_bfs_btree_node **node,
428 char **key_data, grub_uint16_t **keylen_idx,
429 grub_unaligned_uint64_t **key_values)
431 void *ret;
432 struct grub_bfs_btree_node node_head;
433 grub_size_t total_size;
434 grub_err_t err;
436 *node = NULL;
437 *key_data = NULL;
438 *keylen_idx = NULL;
439 *key_values = NULL;
441 err = read_bfs_file (disk, sb, ino, node_off, &node_head, sizeof (node_head),
442 0, 0);
443 if (err)
444 return err;
446 total_size = ALIGN_UP (sizeof (node_head) +
447 grub_bfs_to_cpu_treehead
448 (node_head.total_key_len),
449 BTREE_ALIGN) +
450 grub_bfs_to_cpu_treehead (node_head.count_keys) *
451 sizeof (grub_uint16_t)
452 + grub_bfs_to_cpu_treehead (node_head.count_keys) *
453 sizeof (grub_uint64_t);
455 ret = grub_malloc (total_size);
456 if (!ret)
457 return grub_errno;
459 err = read_bfs_file (disk, sb, ino, node_off, ret, total_size, 0, 0);
460 if (err)
462 grub_free (ret);
463 return err;
466 *node = ret;
467 *key_data = (char *) ret + sizeof (node_head);
468 *keylen_idx = (grub_uint16_t *) ret
469 + ALIGN_UP (sizeof (node_head) +
470 grub_bfs_to_cpu_treehead (node_head.total_key_len),
471 BTREE_ALIGN) / 2;
472 *key_values = (grub_unaligned_uint64_t *)
473 (*keylen_idx +
474 grub_bfs_to_cpu_treehead (node_head.count_keys));
476 return GRUB_ERR_NONE;
479 static int
480 iterate_in_b_tree (grub_disk_t disk,
481 const struct grub_bfs_superblock *sb,
482 const struct grub_bfs_inode *ino,
483 int (*hook) (const char *name, grub_uint64_t value,
484 struct grub_bfs_dir_ctx *ctx),
485 struct grub_bfs_dir_ctx *ctx)
487 struct grub_bfs_btree_header head;
488 grub_err_t err;
489 int level;
490 grub_uint64_t node_off;
492 err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
493 if (err)
494 return 0;
495 node_off = grub_bfs_to_cpu64 (head.root);
497 level = grub_bfs_to_cpu32 (head.level) - 1;
498 while (level--)
500 struct grub_bfs_btree_node node;
501 grub_uint64_t key_value;
502 err = read_bfs_file (disk, sb, ino, node_off, &node, sizeof (node),
503 0, 0);
504 if (err)
505 return 0;
506 err = read_bfs_file (disk, sb, ino, node_off
507 + ALIGN_UP (sizeof (node) +
508 grub_bfs_to_cpu_treehead (node.
509 total_key_len),
510 BTREE_ALIGN) +
511 grub_bfs_to_cpu_treehead (node.count_keys) *
512 sizeof (grub_uint16_t), &key_value,
513 sizeof (grub_uint64_t), 0, 0);
514 if (err)
515 return 0;
517 node_off = grub_bfs_to_cpu64 (key_value);
520 while (1)
522 struct grub_bfs_btree_node *node;
523 char *key_data;
524 grub_uint16_t *keylen_idx;
525 grub_unaligned_uint64_t *key_values;
526 unsigned i;
527 grub_uint16_t start = 0, end = 0;
529 err = read_b_node (disk, sb, ino,
530 node_off,
531 &node,
532 &key_data,
533 &keylen_idx,
534 &key_values);
536 if (err)
537 return 0;
539 for (i = 0; i < grub_bfs_to_cpu_treehead (node->count_keys); i++)
541 char c;
542 start = end;
543 end = grub_bfs_to_cpu16 (keylen_idx[i]);
544 if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
545 end = grub_bfs_to_cpu_treehead (node->total_key_len);
546 c = key_data[end];
547 key_data[end] = 0;
548 if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i].val),
549 ctx))
551 grub_free (node);
552 return 1;
554 key_data[end] = c;
556 node_off = grub_bfs_to_cpu64 (node->right);
557 grub_free (node);
558 if (node_off == POINTER_INVALID)
559 return 0;
563 static int
564 bfs_strcmp (const char *a, const char *b, grub_size_t alen, grub_size_t blen)
566 char ac, bc;
567 while (blen && alen)
569 if (*a != *b)
570 break;
572 a++;
573 b++;
574 alen--;
575 blen--;
578 ac = alen ? *a : 0;
579 bc = blen ? *b : 0;
581 #ifdef MODE_AFS
582 return (int) (grub_int8_t) ac - (int) (grub_int8_t) bc;
583 #else
584 return (int) (grub_uint8_t) ac - (int) (grub_uint8_t) bc;
585 #endif
588 static grub_err_t
589 find_in_b_tree (grub_disk_t disk,
590 const struct grub_bfs_superblock *sb,
591 const struct grub_bfs_inode *ino, const char *name,
592 grub_size_t name_len,
593 grub_uint64_t * res)
595 struct grub_bfs_btree_header head;
596 grub_err_t err;
597 int level;
598 grub_uint64_t node_off;
600 err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
601 if (err)
602 return err;
603 node_off = grub_bfs_to_cpu64 (head.root);
605 level = grub_bfs_to_cpu32 (head.level) - 1;
606 while (1)
608 struct grub_bfs_btree_node *node;
609 char *key_data;
610 grub_uint16_t *keylen_idx;
611 grub_unaligned_uint64_t *key_values;
612 int lg, j;
613 unsigned i;
615 err = read_b_node (disk, sb, ino, node_off, &node, &key_data, &keylen_idx, &key_values);
616 if (err)
617 return err;
619 if (node->count_keys == 0)
621 grub_free (node);
622 return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
623 name);
626 for (lg = 0; grub_bfs_to_cpu_treehead (node->count_keys) >> lg; lg++);
628 i = 0;
630 for (j = lg - 1; j >= 0; j--)
632 int cmp;
633 grub_uint16_t start = 0, end = 0;
634 if ((i | (1 << j)) >= grub_bfs_to_cpu_treehead (node->count_keys))
635 continue;
636 start = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j)) - 1]);
637 end = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]);
638 if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
639 end = grub_bfs_to_cpu_treehead (node->total_key_len);
640 cmp = bfs_strcmp (key_data + start, name, end - start, name_len);
641 if (cmp == 0 && level == 0)
643 *res = grub_bfs_to_cpu64 (key_values[i | (1 << j)].val);
644 grub_free (node);
645 return GRUB_ERR_NONE;
647 #ifdef MODE_AFS
648 if (cmp <= 0)
649 #else
650 if (cmp < 0)
651 #endif
652 i |= (1 << j);
654 if (i == 0)
656 grub_uint16_t end = 0;
657 int cmp;
658 end = grub_bfs_to_cpu16 (keylen_idx[0]);
659 if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
660 end = grub_bfs_to_cpu_treehead (node->total_key_len);
661 cmp = bfs_strcmp (key_data, name, end, name_len);
662 if (cmp == 0 && level == 0)
664 *res = grub_bfs_to_cpu64 (key_values[0].val);
665 grub_free (node);
666 return GRUB_ERR_NONE;
668 #ifdef MODE_AFS
669 if (cmp > 0 && level != 0)
670 #else
671 if (cmp >= 0 && level != 0)
672 #endif
674 node_off = grub_bfs_to_cpu64 (key_values[0].val);
675 level--;
676 grub_free (node);
677 continue;
679 else if (level != 0
680 && grub_bfs_to_cpu_treehead (node->count_keys) >= 2)
682 node_off = grub_bfs_to_cpu64 (key_values[1].val);
683 level--;
684 grub_free (node);
685 continue;
688 else if (level != 0
689 && i + 1 < grub_bfs_to_cpu_treehead (node->count_keys))
691 node_off = grub_bfs_to_cpu64 (key_values[i + 1].val);
692 level--;
693 grub_free (node);
694 continue;
696 if (node->overflow != POINTER_INVALID)
698 node_off = grub_bfs_to_cpu64 (node->overflow);
699 /* This level-- isn't specified but is needed. */
700 level--;
701 grub_free (node);
702 continue;
704 grub_free (node);
705 return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
706 name);
710 static grub_err_t
711 hop_level (grub_disk_t disk,
712 const struct grub_bfs_superblock *sb,
713 struct grub_bfs_inode *ino, const char *name,
714 const char *name_end)
716 grub_err_t err;
717 grub_uint64_t res = 0;
719 if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) != ATTR_DIR))
720 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
722 err = find_in_b_tree (disk, sb, ino, name, name_end - name, &res);
723 if (err)
724 return err;
726 return grub_disk_read (disk, res
727 << (grub_bfs_to_cpu32 (sb->log2_bsize)
728 - GRUB_DISK_SECTOR_BITS), 0,
729 sizeof (*ino), (char *) ino);
732 static grub_err_t
733 find_file (const char *path, grub_disk_t disk,
734 const struct grub_bfs_superblock *sb, struct grub_bfs_inode *ino)
736 const char *ptr, *next = path;
737 char *alloc = NULL;
738 char *wptr;
739 grub_err_t err;
740 struct grub_bfs_inode old_ino;
741 unsigned symlinks_max = 32;
743 err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
744 sizeof (*ino));
745 if (err)
746 return err;
748 while (1)
750 ptr = next;
751 while (*ptr == '/')
752 ptr++;
753 if (*ptr == 0)
755 grub_free (alloc);
756 return GRUB_ERR_NONE;
758 for (next = ptr; *next && *next != '/'; next++);
759 grub_memcpy (&old_ino, ino, sizeof (old_ino));
760 err = hop_level (disk, sb, ino, ptr, next);
761 if (err)
762 return err;
764 if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) == ATTR_LNK))
766 char *old_alloc = alloc;
767 if (--symlinks_max == 0)
769 grub_free (alloc);
770 return grub_error (GRUB_ERR_SYMLINK_LOOP,
771 N_("too deep nesting of symlinks"));
774 #ifndef MODE_AFS
775 if (grub_bfs_to_cpu32 (ino->flags) & LONG_SYMLINK)
776 #endif
778 grub_size_t symsize = grub_bfs_to_cpu64 (ino->size);
779 alloc = grub_malloc (grub_strlen (next)
780 + symsize + 1);
781 if (!alloc)
783 grub_free (alloc);
784 return grub_errno;
786 grub_free (old_alloc);
787 err = read_bfs_file (disk, sb, ino, 0, alloc, symsize, 0, 0);
788 if (err)
790 grub_free (alloc);
791 return err;
793 alloc[symsize] = 0;
795 #ifndef MODE_AFS
796 else
798 alloc = grub_malloc (grub_strlen (next)
799 + sizeof (ino->inplace_link) + 1);
800 if (!alloc)
802 grub_free (alloc);
803 return grub_errno;
805 grub_free (old_alloc);
806 grub_memcpy (alloc, ino->inplace_link,
807 sizeof (ino->inplace_link));
808 alloc[sizeof (ino->inplace_link)] = 0;
810 #endif
811 if (alloc[0] == '/')
813 err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
814 sizeof (*ino));
815 if (err)
817 grub_free (alloc);
818 return err;
821 else
822 grub_memcpy (ino, &old_ino, sizeof (old_ino));
823 wptr = alloc + grub_strlen (alloc);
824 if (next)
825 wptr = grub_stpcpy (wptr, next);
826 *wptr = 0;
827 next = alloc;
828 continue;
833 static grub_err_t
834 mount (grub_disk_t disk, struct grub_bfs_superblock *sb)
836 grub_err_t err;
837 err = grub_disk_read (disk, SUPERBLOCK, 0, sizeof (*sb), sb);
838 if (err == GRUB_ERR_OUT_OF_RANGE)
839 return grub_error (GRUB_ERR_BAD_FS,
840 #ifdef MODE_AFS
841 "not an AFS filesystem"
842 #else
843 "not a BFS filesystem"
844 #endif
846 if (err)
847 return err;
848 if (sb->magic1 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC1)
849 || sb->magic2 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC2)
850 || sb->magic3 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC3)
851 || sb->bsize == 0
852 || (grub_bfs_to_cpu32 (sb->bsize)
853 != (1U << grub_bfs_to_cpu32 (sb->log2_bsize)))
854 || grub_bfs_to_cpu32 (sb->log2_bsize) < GRUB_DISK_SECTOR_BITS)
855 return grub_error (GRUB_ERR_BAD_FS,
856 #ifdef MODE_AFS
857 "not an AFS filesystem"
858 #else
859 "not a BFS filesystem"
860 #endif
862 return GRUB_ERR_NONE;
865 /* Helper for grub_bfs_dir. */
866 static int
867 grub_bfs_dir_iter (const char *name, grub_uint64_t value,
868 struct grub_bfs_dir_ctx *ctx)
870 grub_err_t err2;
871 struct grub_bfs_inode ino;
872 struct grub_dirhook_info info;
874 err2 = grub_disk_read (ctx->device->disk, value
875 << (grub_bfs_to_cpu32 (ctx->sb.log2_bsize)
876 - GRUB_DISK_SECTOR_BITS), 0,
877 sizeof (ino), (char *) &ino);
878 if (err2)
880 grub_print_error ();
881 return 0;
884 info.mtimeset = 1;
885 #ifdef MODE_AFS
886 info.mtime =
887 grub_divmod64 (grub_bfs_to_cpu64 (ino.mtime), 1000000, 0);
888 #else
889 info.mtime = grub_bfs_to_cpu64 (ino.mtime) >> 16;
890 #endif
891 info.dir = ((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) == ATTR_DIR);
892 return ctx->hook (name, &info, ctx->hook_data);
895 static grub_err_t
896 grub_bfs_dir (grub_device_t device, const char *path,
897 grub_fs_dir_hook_t hook, void *hook_data)
899 struct grub_bfs_dir_ctx ctx = {
900 .device = device,
901 .hook = hook,
902 .hook_data = hook_data
904 grub_err_t err;
906 err = mount (device->disk, &ctx.sb);
907 if (err)
908 return err;
911 struct grub_bfs_inode ino;
912 err = find_file (path, device->disk, &ctx.sb, &ino);
913 if (err)
914 return err;
915 if (((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) != ATTR_DIR))
916 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
917 iterate_in_b_tree (device->disk, &ctx.sb, &ino, grub_bfs_dir_iter,
918 &ctx);
921 return grub_errno;
924 static grub_err_t
925 grub_bfs_open (struct grub_file *file, const char *name)
927 struct grub_bfs_superblock sb;
928 grub_err_t err;
930 err = mount (file->device->disk, &sb);
931 if (err)
932 return err;
935 struct grub_bfs_inode ino;
936 struct grub_bfs_data *data;
937 err = find_file (name, file->device->disk, &sb, &ino);
938 if (err)
939 return err;
940 if (((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) != ATTR_REG))
941 return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
943 data = grub_zalloc (sizeof (struct grub_bfs_data));
944 if (!data)
945 return grub_errno;
946 data->sb = sb;
947 grub_memcpy (&data->ino, &ino, sizeof (data->ino));
948 file->data = data;
949 file->size = grub_bfs_to_cpu64 (ino.size);
952 return GRUB_ERR_NONE;
955 static grub_err_t
956 grub_bfs_close (grub_file_t file)
958 grub_free (file->data);
960 return GRUB_ERR_NONE;
963 static grub_ssize_t
964 grub_bfs_read (grub_file_t file, char *buf, grub_size_t len)
966 grub_err_t err;
967 struct grub_bfs_data *data = file->data;
969 err = read_bfs_file (file->device->disk, &data->sb,
970 &data->ino, file->offset, buf, len,
971 file->read_hook, file->read_hook_data);
972 if (err)
973 return -1;
974 return len;
977 static grub_err_t
978 grub_bfs_label (grub_device_t device, char **label)
980 struct grub_bfs_superblock sb;
981 grub_err_t err;
983 *label = 0;
985 err = mount (device->disk, &sb);
986 if (err)
987 return err;
989 *label = grub_strndup (sb.label, sizeof (sb.label));
990 return GRUB_ERR_NONE;
993 #ifndef MODE_AFS
994 static grub_ssize_t
995 read_bfs_attr (grub_disk_t disk,
996 const struct grub_bfs_superblock *sb,
997 struct grub_bfs_inode *ino,
998 const char *name, void *buf, grub_size_t len)
1000 grub_uint8_t *ptr = (grub_uint8_t *) ino->small_data;
1001 grub_uint8_t *end = ((grub_uint8_t *) ino + grub_bfs_to_cpu32 (sb->bsize));
1003 while (ptr + sizeof (struct grub_bfs_small_data_element_header) < end)
1005 struct grub_bfs_small_data_element_header *el;
1006 char *el_name;
1007 grub_uint8_t *data;
1008 el = (struct grub_bfs_small_data_element_header *) ptr;
1009 if (el->name_len == 0)
1010 break;
1011 el_name = (char *) (el + 1);
1012 data = (grub_uint8_t *) el_name + grub_bfs_to_cpu16 (el->name_len) + 3;
1013 ptr = data + grub_bfs_to_cpu16 (el->value_len) + 1;
1014 if (grub_memcmp (name, el_name, grub_bfs_to_cpu16 (el->name_len)) == 0
1015 && name[el->name_len] == 0)
1017 grub_size_t copy;
1018 copy = len;
1019 if (grub_bfs_to_cpu16 (el->value_len) > copy)
1020 copy = grub_bfs_to_cpu16 (el->value_len);
1021 grub_memcpy (buf, data, copy);
1022 return copy;
1026 if (ino->attr.len != 0)
1028 grub_size_t read;
1029 grub_err_t err;
1030 grub_uint64_t res;
1032 err = read_extent (disk, sb, &ino->attr, 0, 0, ino,
1033 grub_bfs_to_cpu32 (sb->bsize));
1034 if (err)
1035 return -1;
1037 err = find_in_b_tree (disk, sb, ino, name, grub_strlen (name), &res);
1038 if (err)
1039 return -1;
1040 grub_disk_read (disk, res
1041 << (grub_bfs_to_cpu32 (sb->log2_bsize)
1042 - GRUB_DISK_SECTOR_BITS), 0,
1043 grub_bfs_to_cpu32 (sb->bsize), (char *) ino);
1044 read = grub_bfs_to_cpu64 (ino->size);
1045 if (read > len)
1046 read = len;
1048 err = read_bfs_file (disk, sb, ino, 0, buf, read, 0, 0);
1049 if (err)
1050 return -1;
1051 return read;
1053 return -1;
1056 static grub_err_t
1057 grub_bfs_uuid (grub_device_t device, char **uuid)
1059 struct grub_bfs_superblock sb;
1060 grub_err_t err;
1061 struct grub_bfs_inode *ino;
1062 grub_uint64_t vid;
1064 *uuid = 0;
1066 err = mount (device->disk, &sb);
1067 if (err)
1068 return err;
1070 ino = grub_malloc (grub_bfs_to_cpu32 (sb.bsize));
1071 if (!ino)
1072 return grub_errno;
1074 err = read_extent (device->disk, &sb, &sb.root_dir, 0, 0,
1075 ino, grub_bfs_to_cpu32 (sb.bsize));
1076 if (err)
1078 grub_free (ino);
1079 return err;
1081 if (read_bfs_attr (device->disk, &sb, ino, "be:volume_id",
1082 &vid, sizeof (vid)) == sizeof (vid))
1083 *uuid =
1084 grub_xasprintf ("%016" PRIxGRUB_UINT64_T, grub_bfs_to_cpu64 (vid));
1086 grub_free (ino);
1088 return GRUB_ERR_NONE;
1090 #endif
1092 static struct grub_fs grub_bfs_fs = {
1093 #ifdef MODE_AFS
1094 .name = "afs",
1095 #else
1096 .name = "bfs",
1097 #endif
1098 .dir = grub_bfs_dir,
1099 .open = grub_bfs_open,
1100 .read = grub_bfs_read,
1101 .close = grub_bfs_close,
1102 .label = grub_bfs_label,
1103 #ifndef MODE_AFS
1104 .uuid = grub_bfs_uuid,
1105 #endif
1106 #ifdef GRUB_UTIL
1107 .reserved_first_sector = 1,
1108 .blocklist_install = 1,
1109 #endif
1112 #ifdef MODE_AFS
1113 GRUB_MOD_INIT (afs)
1114 #else
1115 GRUB_MOD_INIT (bfs)
1116 #endif
1118 COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE ==
1119 sizeof (struct grub_bfs_extent));
1120 grub_fs_register (&grub_bfs_fs);
1123 #ifdef MODE_AFS
1124 GRUB_MOD_FINI (afs)
1125 #else
1126 GRUB_MOD_FINI (bfs)
1127 #endif
1129 grub_fs_unregister (&grub_bfs_fs);