1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126 |
- /* bfs.c - The Bee File System. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2010,2011 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- /*
- Based on the book "Practical File System Design by Dominic Giampaolo
- with corrections and completitions based on Haiku code.
- */
- #include <grub/err.h>
- #include <grub/file.h>
- #include <grub/mm.h>
- #include <grub/misc.h>
- #include <grub/disk.h>
- #include <grub/dl.h>
- #include <grub/types.h>
- #include <grub/i18n.h>
- #include <grub/fshelp.h>
- #include <grub/lockdown.h>
- GRUB_MOD_LICENSE ("GPLv3+");
- #ifdef MODE_AFS
- #define BTREE_ALIGN 4
- #define SUPERBLOCK 2
- #else
- #define BTREE_ALIGN 8
- #define SUPERBLOCK 1
- #endif
- #define grub_bfs_to_cpu16 grub_le_to_cpu16
- #define grub_bfs_to_cpu32 grub_le_to_cpu32
- #define grub_bfs_to_cpu64 grub_le_to_cpu64
- #define grub_cpu_to_bfs32_compile_time grub_cpu_to_le32_compile_time
- #ifdef MODE_AFS
- #define grub_bfs_to_cpu_treehead grub_bfs_to_cpu32
- #else
- #define grub_bfs_to_cpu_treehead grub_bfs_to_cpu16
- #endif
- #ifdef MODE_AFS
- #define SUPER_BLOCK_MAGIC1 0x41465331
- #else
- #define SUPER_BLOCK_MAGIC1 0x42465331
- #endif
- #define SUPER_BLOCK_MAGIC2 0xdd121031
- #define SUPER_BLOCK_MAGIC3 0x15b6830e
- #define POINTER_INVALID 0xffffffffffffffffULL
- #define ATTR_TYPE 0160000
- #define ATTR_REG 0100000
- #define ATTR_DIR 0040000
- #define ATTR_LNK 0120000
- #define DOUBLE_INDIRECT_SHIFT 2
- #define LOG_EXTENT_SIZE 3
- struct grub_bfs_extent
- {
- grub_uint32_t ag;
- grub_uint16_t start;
- grub_uint16_t len;
- } GRUB_PACKED;
- struct grub_bfs_superblock
- {
- char label[32];
- grub_uint32_t magic1;
- grub_uint32_t unused1;
- grub_uint32_t bsize;
- grub_uint32_t log2_bsize;
- grub_uint8_t unused[20];
- grub_uint32_t magic2;
- grub_uint32_t unused2;
- grub_uint32_t log2_ag_size;
- grub_uint8_t unused3[32];
- grub_uint32_t magic3;
- struct grub_bfs_extent root_dir;
- } GRUB_PACKED;
- struct grub_bfs_inode
- {
- grub_uint8_t unused[20];
- grub_uint32_t mode;
- grub_uint32_t flags;
- #ifdef MODE_AFS
- grub_uint8_t unused2[12];
- #else
- grub_uint8_t unused2[8];
- #endif
- grub_uint64_t mtime;
- grub_uint8_t unused3[8];
- struct grub_bfs_extent attr;
- grub_uint8_t unused4[12];
- union
- {
- struct
- {
- struct grub_bfs_extent direct[12];
- grub_uint64_t max_direct_range;
- struct grub_bfs_extent indirect;
- grub_uint64_t max_indirect_range;
- struct grub_bfs_extent double_indirect;
- grub_uint64_t max_double_indirect_range;
- grub_uint64_t size;
- grub_uint32_t pad[4];
- } GRUB_PACKED;
- char inplace_link[144];
- } GRUB_PACKED;
- grub_uint8_t small_data[0];
- } GRUB_PACKED;
- enum
- {
- LONG_SYMLINK = 0x40
- };
- struct grub_bfs_small_data_element_header
- {
- grub_uint32_t type;
- grub_uint16_t name_len;
- grub_uint16_t value_len;
- } GRUB_PACKED;
- struct grub_bfs_btree_header
- {
- grub_uint32_t magic;
- #ifdef MODE_AFS
- grub_uint64_t root;
- grub_uint32_t level;
- grub_uint32_t node_size;
- grub_uint32_t unused;
- #else
- grub_uint32_t node_size;
- grub_uint32_t level;
- grub_uint32_t unused;
- grub_uint64_t root;
- #endif
- grub_uint32_t unused2[2];
- } GRUB_PACKED;
- struct grub_bfs_btree_node
- {
- grub_uint64_t unused;
- grub_uint64_t right;
- grub_uint64_t overflow;
- #ifdef MODE_AFS
- grub_uint32_t count_keys;
- grub_uint32_t total_key_len;
- #else
- grub_uint16_t count_keys;
- grub_uint16_t total_key_len;
- #endif
- } GRUB_PACKED;
- struct grub_bfs_data
- {
- struct grub_bfs_superblock sb;
- struct grub_bfs_inode ino;
- };
- /* Context for grub_bfs_dir. */
- struct grub_bfs_dir_ctx
- {
- grub_device_t device;
- grub_fs_dir_hook_t hook;
- void *hook_data;
- struct grub_bfs_superblock sb;
- };
- static grub_err_t
- read_extent (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- const struct grub_bfs_extent *in,
- grub_off_t off, grub_off_t byteoff, void *buf, grub_size_t len)
- {
- #ifdef MODE_AFS
- return grub_disk_read (disk, ((grub_bfs_to_cpu32 (in->ag)
- << (grub_bfs_to_cpu32 (sb->log2_ag_size)
- - GRUB_DISK_SECTOR_BITS))
- + ((grub_bfs_to_cpu16 (in->start) + off)
- << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - GRUB_DISK_SECTOR_BITS))),
- byteoff, len, buf);
- #else
- return grub_disk_read (disk, (((grub_bfs_to_cpu32 (in->ag)
- << grub_bfs_to_cpu32 (sb->log2_ag_size))
- + grub_bfs_to_cpu16 (in->start) + off)
- << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - GRUB_DISK_SECTOR_BITS)),
- byteoff, len, buf);
- #endif
- }
- #ifdef MODE_AFS
- #define RANGE_SHIFT grub_bfs_to_cpu32 (sb->log2_bsize)
- #else
- #define RANGE_SHIFT 0
- #endif
- static grub_err_t
- read_bfs_file (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- const struct grub_bfs_inode *ino,
- grub_off_t off, void *buf, grub_size_t len,
- grub_disk_read_hook_t read_hook, void *read_hook_data)
- {
- if (len == 0)
- return GRUB_ERR_NONE;
- if (off + len > grub_bfs_to_cpu64 (ino->size))
- return grub_error (GRUB_ERR_OUT_OF_RANGE,
- N_("attempt to read past the end of file"));
- if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
- {
- unsigned i;
- grub_uint64_t pos = 0;
- for (i = 0; i < ARRAY_SIZE (ino->direct); i++)
- {
- grub_uint64_t newpos;
- newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (ino->direct[i].len))
- << grub_bfs_to_cpu32 (sb->log2_bsize));
- if (newpos > off)
- {
- grub_size_t read_size;
- grub_err_t err;
- read_size = newpos - off;
- if (read_size > len)
- read_size = len;
- disk->read_hook = read_hook;
- disk->read_hook_data = read_hook_data;
- err = read_extent (disk, sb, &ino->direct[i], 0, off - pos,
- buf, read_size);
- disk->read_hook = 0;
- if (err)
- return err;
- off += read_size;
- len -= read_size;
- buf = (char *) buf + read_size;
- if (len == 0)
- return GRUB_ERR_NONE;
- }
- pos = newpos;
- }
- }
- if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
- return grub_error (GRUB_ERR_BAD_FS, "incorrect direct blocks");
- if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
- {
- unsigned i;
- struct grub_bfs_extent *entries;
- grub_size_t nentries;
- grub_err_t err;
- grub_uint64_t pos = (grub_bfs_to_cpu64 (ino->max_direct_range)
- << RANGE_SHIFT);
- nentries = (((grub_size_t) grub_bfs_to_cpu16 (ino->indirect.len))
- << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
- entries = grub_malloc (nentries << LOG_EXTENT_SIZE);
- if (!entries)
- return grub_errno;
- err = read_extent (disk, sb, &ino->indirect, 0, 0,
- entries, nentries << LOG_EXTENT_SIZE);
- for (i = 0; i < nentries; i++)
- {
- grub_uint64_t newpos;
- newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (entries[i].len))
- << grub_bfs_to_cpu32 (sb->log2_bsize));
- if (newpos > off)
- {
- grub_size_t read_size;
- read_size = newpos - off;
- if (read_size > len)
- read_size = len;
- disk->read_hook = read_hook;
- disk->read_hook_data = read_hook_data;
- err = read_extent (disk, sb, &entries[i], 0, off - pos,
- buf, read_size);
- disk->read_hook = 0;
- if (err)
- {
- grub_free (entries);
- return err;
- }
- off += read_size;
- len -= read_size;
- buf = (char *) buf + read_size;
- if (len == 0)
- {
- grub_free (entries);
- return GRUB_ERR_NONE;
- }
- }
- pos = newpos;
- }
- grub_free (entries);
- }
- if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
- return grub_error (GRUB_ERR_BAD_FS, "incorrect indirect blocks");
- {
- struct grub_bfs_extent *l1_entries, *l2_entries;
- grub_size_t nl1_entries, nl2_entries;
- grub_off_t last_l1n = ~0ULL;
- grub_err_t err;
- nl1_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (ino->double_indirect.len))
- << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
- l1_entries = grub_malloc (nl1_entries << LOG_EXTENT_SIZE);
- if (!l1_entries)
- return grub_errno;
- nl2_entries = 0;
- l2_entries = grub_malloc (1 << (DOUBLE_INDIRECT_SHIFT
- + grub_bfs_to_cpu32 (sb->log2_bsize)));
- if (!l2_entries)
- {
- grub_free (l1_entries);
- return grub_errno;
- }
- err = read_extent (disk, sb, &ino->double_indirect, 0, 0,
- l1_entries, nl1_entries << LOG_EXTENT_SIZE);
- if (err)
- {
- grub_free (l1_entries);
- grub_free (l2_entries);
- return err;
- }
- while (len > 0)
- {
- grub_off_t boff, l2n, l1n;
- grub_size_t read_size;
- grub_off_t double_indirect_offset;
- double_indirect_offset = off
- - grub_bfs_to_cpu64 (ino->max_indirect_range);
- boff = (double_indirect_offset
- & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
- + DOUBLE_INDIRECT_SHIFT)) - 1));
- l2n = ((double_indirect_offset >> (grub_bfs_to_cpu32 (sb->log2_bsize)
- + DOUBLE_INDIRECT_SHIFT))
- & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE
- + DOUBLE_INDIRECT_SHIFT)) - 1));
- l1n =
- (double_indirect_offset >>
- (2 * grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE +
- 2 * DOUBLE_INDIRECT_SHIFT));
- if (l1n > nl1_entries)
- {
- grub_free (l1_entries);
- grub_free (l2_entries);
- return grub_error (GRUB_ERR_BAD_FS,
- "incorrect double-indirect block");
- }
- if (l1n != last_l1n)
- {
- nl2_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (l1_entries[l1n].len))
- << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - LOG_EXTENT_SIZE));
- if (nl2_entries > (1U << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - LOG_EXTENT_SIZE
- + DOUBLE_INDIRECT_SHIFT)))
- nl2_entries = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - LOG_EXTENT_SIZE
- + DOUBLE_INDIRECT_SHIFT));
- err = read_extent (disk, sb, &l1_entries[l1n], 0, 0,
- l2_entries, nl2_entries << LOG_EXTENT_SIZE);
- if (err)
- {
- grub_free (l1_entries);
- grub_free (l2_entries);
- return err;
- }
- last_l1n = l1n;
- }
- if (l2n > nl2_entries)
- {
- grub_free (l1_entries);
- grub_free (l2_entries);
- return grub_error (GRUB_ERR_BAD_FS,
- "incorrect double-indirect block");
- }
- read_size = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize)
- + DOUBLE_INDIRECT_SHIFT)) - boff;
- if (read_size > len)
- read_size = len;
- disk->read_hook = read_hook;
- disk->read_hook_data = read_hook_data;
- err = read_extent (disk, sb, &l2_entries[l2n], 0, boff,
- buf, read_size);
- disk->read_hook = 0;
- if (err)
- {
- grub_free (l1_entries);
- grub_free (l2_entries);
- return err;
- }
- off += read_size;
- len -= read_size;
- buf = (char *) buf + read_size;
- }
- grub_free (l1_entries);
- grub_free (l2_entries);
- return GRUB_ERR_NONE;
- }
- }
- static grub_err_t
- read_b_node (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- const struct grub_bfs_inode *ino,
- grub_uint64_t node_off,
- struct grub_bfs_btree_node **node,
- char **key_data, grub_uint16_t **keylen_idx,
- grub_unaligned_uint64_t **key_values)
- {
- void *ret;
- struct grub_bfs_btree_node node_head;
- grub_size_t total_size;
- grub_err_t err;
- *node = NULL;
- *key_data = NULL;
- *keylen_idx = NULL;
- *key_values = NULL;
- err = read_bfs_file (disk, sb, ino, node_off, &node_head, sizeof (node_head),
- 0, 0);
- if (err)
- return err;
- total_size = ALIGN_UP (sizeof (node_head) +
- grub_bfs_to_cpu_treehead
- (node_head.total_key_len),
- BTREE_ALIGN) +
- grub_bfs_to_cpu_treehead (node_head.count_keys) *
- sizeof (grub_uint16_t)
- + grub_bfs_to_cpu_treehead (node_head.count_keys) *
- sizeof (grub_uint64_t);
- ret = grub_malloc (total_size);
- if (!ret)
- return grub_errno;
- err = read_bfs_file (disk, sb, ino, node_off, ret, total_size, 0, 0);
- if (err)
- {
- grub_free (ret);
- return err;
- }
- *node = ret;
- *key_data = (char *) ret + sizeof (node_head);
- *keylen_idx = (grub_uint16_t *) ret
- + ALIGN_UP (sizeof (node_head) +
- grub_bfs_to_cpu_treehead (node_head.total_key_len),
- BTREE_ALIGN) / 2;
- *key_values = (grub_unaligned_uint64_t *)
- (*keylen_idx +
- grub_bfs_to_cpu_treehead (node_head.count_keys));
- return GRUB_ERR_NONE;
- }
- static int
- iterate_in_b_tree (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- const struct grub_bfs_inode *ino,
- int (*hook) (const char *name, grub_uint64_t value,
- struct grub_bfs_dir_ctx *ctx),
- struct grub_bfs_dir_ctx *ctx)
- {
- struct grub_bfs_btree_header head;
- grub_err_t err;
- int level;
- grub_uint64_t node_off;
- err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
- if (err)
- return 0;
- node_off = grub_bfs_to_cpu64 (head.root);
- level = grub_bfs_to_cpu32 (head.level) - 1;
- while (level--)
- {
- struct grub_bfs_btree_node node;
- grub_uint64_t key_value;
- err = read_bfs_file (disk, sb, ino, node_off, &node, sizeof (node),
- 0, 0);
- if (err)
- return 0;
- err = read_bfs_file (disk, sb, ino, node_off
- + ALIGN_UP (sizeof (node) +
- grub_bfs_to_cpu_treehead (node.
- total_key_len),
- BTREE_ALIGN) +
- grub_bfs_to_cpu_treehead (node.count_keys) *
- sizeof (grub_uint16_t), &key_value,
- sizeof (grub_uint64_t), 0, 0);
- if (err)
- return 0;
- node_off = grub_bfs_to_cpu64 (key_value);
- }
- while (1)
- {
- struct grub_bfs_btree_node *node;
- char *key_data;
- grub_uint16_t *keylen_idx;
- grub_unaligned_uint64_t *key_values;
- unsigned i;
- grub_uint16_t start = 0, end = 0;
- err = read_b_node (disk, sb, ino,
- node_off,
- &node,
- &key_data,
- &keylen_idx,
- &key_values);
- if (err)
- return 0;
- for (i = 0; i < grub_bfs_to_cpu_treehead (node->count_keys); i++)
- {
- char c;
- start = end;
- end = grub_bfs_to_cpu16 (keylen_idx[i]);
- if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
- end = grub_bfs_to_cpu_treehead (node->total_key_len);
- c = key_data[end];
- key_data[end] = 0;
- if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i].val),
- ctx))
- {
- grub_free (node);
- return 1;
- }
- key_data[end] = c;
- }
- node_off = grub_bfs_to_cpu64 (node->right);
- grub_free (node);
- if (node_off == POINTER_INVALID)
- return 0;
- }
- }
- static int
- bfs_strcmp (const char *a, const char *b, grub_size_t alen)
- {
- char ac, bc;
- while (*b && alen)
- {
- if (*a != *b)
- break;
- a++;
- b++;
- alen--;
- }
- ac = alen ? *a : 0;
- bc = *b;
- #ifdef MODE_AFS
- return (int) (grub_int8_t) ac - (int) (grub_int8_t) bc;
- #else
- return (int) (grub_uint8_t) ac - (int) (grub_uint8_t) bc;
- #endif
- }
- static grub_err_t
- find_in_b_tree (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- const struct grub_bfs_inode *ino, const char *name,
- grub_uint64_t * res)
- {
- struct grub_bfs_btree_header head;
- grub_err_t err;
- int level;
- grub_uint64_t node_off;
- err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
- if (err)
- return err;
- node_off = grub_bfs_to_cpu64 (head.root);
- level = grub_bfs_to_cpu32 (head.level) - 1;
- while (1)
- {
- struct grub_bfs_btree_node *node;
- char *key_data;
- grub_uint16_t *keylen_idx;
- grub_unaligned_uint64_t *key_values;
- int lg, j;
- unsigned i;
- err = read_b_node (disk, sb, ino, node_off, &node, &key_data, &keylen_idx, &key_values);
- if (err)
- return err;
- if (node->count_keys == 0)
- {
- grub_free (node);
- return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
- name);
- }
- for (lg = 0; grub_bfs_to_cpu_treehead (node->count_keys) >> lg; lg++);
- i = 0;
- for (j = lg - 1; j >= 0; j--)
- {
- int cmp;
- grub_uint16_t start = 0, end = 0;
- if ((i | (1 << j)) >= grub_bfs_to_cpu_treehead (node->count_keys))
- continue;
- start = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j)) - 1]);
- end = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]);
- if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
- end = grub_bfs_to_cpu_treehead (node->total_key_len);
- cmp = bfs_strcmp (key_data + start, name, end - start);
- if (cmp == 0 && level == 0)
- {
- *res = grub_bfs_to_cpu64 (key_values[i | (1 << j)].val);
- grub_free (node);
- return GRUB_ERR_NONE;
- }
- #ifdef MODE_AFS
- if (cmp <= 0)
- #else
- if (cmp < 0)
- #endif
- i |= (1 << j);
- }
- if (i == 0)
- {
- grub_uint16_t end = 0;
- int cmp;
- end = grub_bfs_to_cpu16 (keylen_idx[0]);
- if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
- end = grub_bfs_to_cpu_treehead (node->total_key_len);
- cmp = bfs_strcmp (key_data, name, end);
- if (cmp == 0 && level == 0)
- {
- *res = grub_bfs_to_cpu64 (key_values[0].val);
- grub_free (node);
- return GRUB_ERR_NONE;
- }
- #ifdef MODE_AFS
- if (cmp > 0 && level != 0)
- #else
- if (cmp >= 0 && level != 0)
- #endif
- {
- node_off = grub_bfs_to_cpu64 (key_values[0].val);
- level--;
- grub_free (node);
- continue;
- }
- else if (level != 0
- && grub_bfs_to_cpu_treehead (node->count_keys) >= 2)
- {
- node_off = grub_bfs_to_cpu64 (key_values[1].val);
- level--;
- grub_free (node);
- continue;
- }
- }
- else if (level != 0
- && i + 1 < grub_bfs_to_cpu_treehead (node->count_keys))
- {
- node_off = grub_bfs_to_cpu64 (key_values[i + 1].val);
- level--;
- grub_free (node);
- continue;
- }
- if (node->overflow != POINTER_INVALID)
- {
- node_off = grub_bfs_to_cpu64 (node->overflow);
- /* This level-- isn't specified but is needed. */
- level--;
- grub_free (node);
- continue;
- }
- grub_free (node);
- return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
- name);
- }
- }
- struct grub_fshelp_node
- {
- grub_disk_t disk;
- const struct grub_bfs_superblock *sb;
- struct grub_bfs_inode ino;
- };
- static grub_err_t
- lookup_file (grub_fshelp_node_t dir,
- const char *name,
- grub_fshelp_node_t *foundnode,
- enum grub_fshelp_filetype *foundtype)
- {
- grub_err_t err;
- struct grub_bfs_inode *new_ino;
- grub_uint64_t res = 0;
- err = find_in_b_tree (dir->disk, dir->sb, &dir->ino, name, &res);
- if (err)
- return err;
- *foundnode = grub_malloc (sizeof (struct grub_fshelp_node));
- if (!*foundnode)
- return grub_errno;
- (*foundnode)->disk = dir->disk;
- (*foundnode)->sb = dir->sb;
- new_ino = &(*foundnode)->ino;
- if (grub_disk_read (dir->disk, res
- << (grub_bfs_to_cpu32 (dir->sb->log2_bsize)
- - GRUB_DISK_SECTOR_BITS), 0,
- sizeof (*new_ino), (char *) new_ino))
- {
- grub_free (*foundnode);
- return grub_errno;
- }
- switch (grub_bfs_to_cpu32 (new_ino->mode) & ATTR_TYPE)
- {
- default:
- case ATTR_REG:
- *foundtype = GRUB_FSHELP_REG;
- break;
- case ATTR_DIR:
- *foundtype = GRUB_FSHELP_DIR;
- break;
- case ATTR_LNK:
- *foundtype = GRUB_FSHELP_SYMLINK;
- break;
- }
- return GRUB_ERR_NONE;
- }
- static char *
- read_symlink (grub_fshelp_node_t node)
- {
- char *alloc = NULL;
- grub_err_t err;
- #ifndef MODE_AFS
- if (!(grub_bfs_to_cpu32 (node->ino.flags) & LONG_SYMLINK))
- {
- alloc = grub_malloc (sizeof (node->ino.inplace_link) + 1);
- if (!alloc)
- {
- return NULL;
- }
- grub_memcpy (alloc, node->ino.inplace_link,
- sizeof (node->ino.inplace_link));
- alloc[sizeof (node->ino.inplace_link)] = 0;
- }
- else
- #endif
- {
- grub_size_t symsize = grub_bfs_to_cpu64 (node->ino.size);
- alloc = grub_malloc (symsize + 1);
- if (!alloc)
- return NULL;
- err = read_bfs_file (node->disk, node->sb, &node->ino, 0, alloc, symsize, 0, 0);
- if (err)
- {
- grub_free (alloc);
- return NULL;
- }
- alloc[symsize] = 0;
- }
- return alloc;
- }
- static grub_err_t
- find_file (const char *path, grub_disk_t disk,
- const struct grub_bfs_superblock *sb, struct grub_bfs_inode *ino,
- enum grub_fshelp_filetype exptype)
- {
- grub_err_t err;
- struct grub_fshelp_node root = {
- .disk = disk,
- .sb = sb,
- };
- struct grub_fshelp_node *found = NULL;
- err = read_extent (disk, sb, &sb->root_dir, 0, 0, &root.ino,
- sizeof (root.ino));
- if (err)
- return err;
- err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, read_symlink, exptype);
- if (!err)
- grub_memcpy (ino, &found->ino, sizeof (*ino));
- if (&root != found)
- grub_free (found);
- return err;
- }
- static grub_err_t
- mount (grub_disk_t disk, struct grub_bfs_superblock *sb)
- {
- grub_err_t err;
- err = grub_disk_read (disk, SUPERBLOCK, 0, sizeof (*sb), sb);
- if (err == GRUB_ERR_OUT_OF_RANGE)
- return grub_error (GRUB_ERR_BAD_FS,
- #ifdef MODE_AFS
- "not an AFS filesystem"
- #else
- "not a BFS filesystem"
- #endif
- );
- if (err)
- return err;
- if (sb->magic1 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC1)
- || sb->magic2 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC2)
- || sb->magic3 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC3)
- || sb->bsize == 0
- || (grub_bfs_to_cpu32 (sb->bsize)
- != (1U << grub_bfs_to_cpu32 (sb->log2_bsize)))
- || grub_bfs_to_cpu32 (sb->log2_bsize) < GRUB_DISK_SECTOR_BITS)
- return grub_error (GRUB_ERR_BAD_FS,
- #ifdef MODE_AFS
- "not an AFS filesystem"
- #else
- "not a BFS filesystem"
- #endif
- );
- return GRUB_ERR_NONE;
- }
- /* Helper for grub_bfs_dir. */
- static int
- grub_bfs_dir_iter (const char *name, grub_uint64_t value,
- struct grub_bfs_dir_ctx *ctx)
- {
- grub_err_t err2;
- struct grub_bfs_inode ino;
- struct grub_dirhook_info info;
- err2 = grub_disk_read (ctx->device->disk, value
- << (grub_bfs_to_cpu32 (ctx->sb.log2_bsize)
- - GRUB_DISK_SECTOR_BITS), 0,
- sizeof (ino), (char *) &ino);
- if (err2)
- {
- grub_print_error ();
- return 0;
- }
- info.mtimeset = 1;
- #ifdef MODE_AFS
- info.mtime =
- grub_divmod64 (grub_bfs_to_cpu64 (ino.mtime), 1000000, 0);
- #else
- info.mtime = grub_bfs_to_cpu64 (ino.mtime) >> 16;
- #endif
- info.dir = ((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) == ATTR_DIR);
- return ctx->hook (name, &info, ctx->hook_data);
- }
- static grub_err_t
- grub_bfs_dir (grub_device_t device, const char *path,
- grub_fs_dir_hook_t hook, void *hook_data)
- {
- struct grub_bfs_dir_ctx ctx = {
- .device = device,
- .hook = hook,
- .hook_data = hook_data
- };
- grub_err_t err;
- err = mount (device->disk, &ctx.sb);
- if (err)
- return err;
- {
- struct grub_bfs_inode ino;
- err = find_file (path, device->disk, &ctx.sb, &ino, GRUB_FSHELP_DIR);
- if (err)
- return err;
- iterate_in_b_tree (device->disk, &ctx.sb, &ino, grub_bfs_dir_iter,
- &ctx);
- }
- return grub_errno;
- }
- static grub_err_t
- grub_bfs_open (struct grub_file *file, const char *name)
- {
- struct grub_bfs_superblock sb;
- grub_err_t err;
- err = mount (file->device->disk, &sb);
- if (err)
- return err;
- {
- struct grub_bfs_inode ino;
- struct grub_bfs_data *data;
- err = find_file (name, file->device->disk, &sb, &ino, GRUB_FSHELP_REG);
- if (err)
- return err;
- data = grub_zalloc (sizeof (struct grub_bfs_data));
- if (!data)
- return grub_errno;
- data->sb = sb;
- grub_memcpy (&data->ino, &ino, sizeof (data->ino));
- file->data = data;
- file->size = grub_bfs_to_cpu64 (ino.size);
- }
- return GRUB_ERR_NONE;
- }
- static grub_err_t
- grub_bfs_close (grub_file_t file)
- {
- grub_free (file->data);
- return GRUB_ERR_NONE;
- }
- static grub_ssize_t
- grub_bfs_read (grub_file_t file, char *buf, grub_size_t len)
- {
- grub_err_t err;
- struct grub_bfs_data *data = file->data;
- err = read_bfs_file (file->device->disk, &data->sb,
- &data->ino, file->offset, buf, len,
- file->read_hook, file->read_hook_data);
- if (err)
- return -1;
- return len;
- }
- static grub_err_t
- grub_bfs_label (grub_device_t device, char **label)
- {
- struct grub_bfs_superblock sb;
- grub_err_t err;
- *label = 0;
- err = mount (device->disk, &sb);
- if (err)
- return err;
- *label = grub_strndup (sb.label, sizeof (sb.label));
- return GRUB_ERR_NONE;
- }
- #ifndef MODE_AFS
- static grub_ssize_t
- read_bfs_attr (grub_disk_t disk,
- const struct grub_bfs_superblock *sb,
- struct grub_bfs_inode *ino,
- const char *name, void *buf, grub_size_t len)
- {
- grub_uint8_t *ptr = (grub_uint8_t *) ino->small_data;
- grub_uint8_t *end = ((grub_uint8_t *) ino + grub_bfs_to_cpu32 (sb->bsize));
- while (ptr + sizeof (struct grub_bfs_small_data_element_header) < end)
- {
- struct grub_bfs_small_data_element_header *el;
- char *el_name;
- grub_uint8_t *data;
- el = (struct grub_bfs_small_data_element_header *) ptr;
- if (el->name_len == 0)
- break;
- el_name = (char *) (el + 1);
- data = (grub_uint8_t *) el_name + grub_bfs_to_cpu16 (el->name_len) + 3;
- ptr = data + grub_bfs_to_cpu16 (el->value_len) + 1;
- if (grub_memcmp (name, el_name, grub_bfs_to_cpu16 (el->name_len)) == 0
- && name[el->name_len] == 0)
- {
- grub_size_t copy;
- copy = len;
- if (grub_bfs_to_cpu16 (el->value_len) > copy)
- copy = grub_bfs_to_cpu16 (el->value_len);
- grub_memcpy (buf, data, copy);
- return copy;
- }
- }
- if (ino->attr.len != 0)
- {
- grub_size_t read;
- grub_err_t err;
- grub_uint64_t res;
- err = read_extent (disk, sb, &ino->attr, 0, 0, ino,
- grub_bfs_to_cpu32 (sb->bsize));
- if (err)
- return -1;
- err = find_in_b_tree (disk, sb, ino, name, &res);
- if (err)
- return -1;
- grub_disk_read (disk, res
- << (grub_bfs_to_cpu32 (sb->log2_bsize)
- - GRUB_DISK_SECTOR_BITS), 0,
- grub_bfs_to_cpu32 (sb->bsize), (char *) ino);
- read = grub_bfs_to_cpu64 (ino->size);
- if (read > len)
- read = len;
- err = read_bfs_file (disk, sb, ino, 0, buf, read, 0, 0);
- if (err)
- return -1;
- return read;
- }
- return -1;
- }
- static grub_err_t
- grub_bfs_uuid (grub_device_t device, char **uuid)
- {
- struct grub_bfs_superblock sb;
- grub_err_t err;
- struct grub_bfs_inode *ino;
- grub_uint64_t vid;
- *uuid = 0;
- err = mount (device->disk, &sb);
- if (err)
- return err;
- ino = grub_malloc (grub_bfs_to_cpu32 (sb.bsize));
- if (!ino)
- return grub_errno;
- err = read_extent (device->disk, &sb, &sb.root_dir, 0, 0,
- ino, grub_bfs_to_cpu32 (sb.bsize));
- if (err)
- {
- grub_free (ino);
- return err;
- }
- if (read_bfs_attr (device->disk, &sb, ino, "be:volume_id",
- &vid, sizeof (vid)) == sizeof (vid))
- *uuid =
- grub_xasprintf ("%016" PRIxGRUB_UINT64_T, grub_bfs_to_cpu64 (vid));
- grub_free (ino);
- return GRUB_ERR_NONE;
- }
- #endif
- static struct grub_fs grub_bfs_fs = {
- #ifdef MODE_AFS
- .name = "afs",
- #else
- .name = "bfs",
- #endif
- .fs_dir = grub_bfs_dir,
- .fs_open = grub_bfs_open,
- .fs_read = grub_bfs_read,
- .fs_close = grub_bfs_close,
- .fs_label = grub_bfs_label,
- #ifndef MODE_AFS
- .fs_uuid = grub_bfs_uuid,
- #endif
- #ifdef GRUB_UTIL
- .reserved_first_sector = 1,
- .blocklist_install = 1,
- #endif
- };
- #ifdef MODE_AFS
- GRUB_MOD_INIT (afs)
- #else
- GRUB_MOD_INIT (bfs)
- #endif
- {
- COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE ==
- sizeof (struct grub_bfs_extent));
- if (!grub_is_lockdown ())
- {
- grub_bfs_fs.mod = mod;
- grub_fs_register (&grub_bfs_fs);
- }
- }
- #ifdef MODE_AFS
- GRUB_MOD_FINI (afs)
- #else
- GRUB_MOD_FINI (bfs)
- #endif
- {
- if (!grub_is_lockdown ())
- grub_fs_unregister (&grub_bfs_fs);
- }
|