BFS implementation based on the specification.
* grub-core/fs/bfs.c: New file. * Makefile.util.def (libgrubmods): Add bfs.c. * grub-core/Makefile.core.def (bfs): New module.
This commit is contained in:
parent
42b2a706bf
commit
5825b3794b
4 changed files with 962 additions and 0 deletions
|
@ -1,3 +1,11 @@
|
|||
2011-10-30 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
BFS implementation based on the specification.
|
||||
|
||||
* grub-core/fs/bfs.c: New file.
|
||||
* Makefile.util.def (libgrubmods): Add bfs.c.
|
||||
* grub-core/Makefile.core.def (bfs): New module.
|
||||
|
||||
2011-10-30 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
* util/grub-fstest.c (cmd_cp): Clarify error message.
|
||||
|
|
|
@ -55,6 +55,7 @@ library = {
|
|||
common = grub-core/disk/raid6_recover.c;
|
||||
common = grub-core/disk/raid.c;
|
||||
common = grub-core/fs/affs.c;
|
||||
common = grub-core/fs/bfs.c;
|
||||
common = grub-core/fs/btrfs.c;
|
||||
common = grub-core/fs/cpio.c;
|
||||
common = grub-core/fs/ext2.c;
|
||||
|
|
|
@ -970,6 +970,11 @@ module = {
|
|||
common = fs/affs.c;
|
||||
};
|
||||
|
||||
module = {
|
||||
name = bfs;
|
||||
common = fs/bfs.c;
|
||||
};
|
||||
|
||||
module = {
|
||||
name = btrfs;
|
||||
common = fs/btrfs.c;
|
||||
|
|
948
grub-core/fs/bfs.c
Normal file
948
grub-core/fs/bfs.c
Normal file
|
@ -0,0 +1,948 @@
|
|||
/* 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>
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#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 SUPER_BLOCK_MAGIC1 0x42465331
|
||||
#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;
|
||||
} __attribute__ ((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;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_bfs_inode
|
||||
{
|
||||
grub_uint8_t unused[20];
|
||||
grub_uint32_t mode;
|
||||
grub_uint32_t flags;
|
||||
grub_uint8_t unused2[8];
|
||||
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];
|
||||
} __attribute__ ((packed));
|
||||
char inplace_link[144];
|
||||
} __attribute__ ((packed));
|
||||
grub_uint8_t small_data[0];
|
||||
} __attribute__ ((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;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_bfs_btree_header
|
||||
{
|
||||
grub_uint32_t magic;
|
||||
grub_uint32_t node_size;
|
||||
grub_uint32_t level;
|
||||
grub_uint32_t unused;
|
||||
grub_uint64_t root;
|
||||
grub_uint32_t unused2[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_bfs_btree_node
|
||||
{
|
||||
grub_uint64_t unused;
|
||||
grub_uint64_t right;
|
||||
grub_uint64_t overflow;
|
||||
grub_uint16_t count_keys;
|
||||
grub_uint16_t total_key_len;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct grub_bfs_data
|
||||
{
|
||||
struct grub_bfs_superblock sb;
|
||||
struct grub_bfs_inode ino[0];
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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) - 9)
|
||||
, byteoff,
|
||||
len, buf);
|
||||
}
|
||||
|
||||
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,
|
||||
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
|
||||
unsigned offset, unsigned length))
|
||||
{
|
||||
if (len == 0)
|
||||
return GRUB_ERR_NONE;
|
||||
|
||||
if (off + len > grub_bfs_to_cpu64 (ino->size))
|
||||
return grub_error (GRUB_ERR_OUT_OF_RANGE, "attempt to read past the end of file");
|
||||
|
||||
if (off < grub_bfs_to_cpu64 (ino->max_direct_range))
|
||||
{
|
||||
unsigned i;
|
||||
grub_uint64_t pos = 0;
|
||||
for (i = 0; i < ARRAY_SIZE (ino->direct); i++)
|
||||
{
|
||||
grub_uint64_t newpos;
|
||||
newpos = pos + (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;
|
||||
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))
|
||||
return grub_error (GRUB_ERR_BAD_FS, "incorrect direct blocks");
|
||||
|
||||
if (off < grub_bfs_to_cpu64 (ino->max_indirect_range))
|
||||
{
|
||||
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);
|
||||
nentries = (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_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;
|
||||
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))
|
||||
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_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_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;
|
||||
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;
|
||||
}
|
||||
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_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);
|
||||
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);
|
||||
if (err)
|
||||
return 0;
|
||||
err = read_bfs_file (disk, sb, ino, node_off
|
||||
+ ALIGN_UP (sizeof (node) + grub_bfs_to_cpu16 (node.total_key_len),
|
||||
8)
|
||||
+ grub_bfs_to_cpu16 (node.count_keys) * 2,
|
||||
&key_value, sizeof (grub_uint64_t), 0);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
node_off = grub_bfs_to_cpu64 (key_value);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct grub_bfs_btree_node node;
|
||||
err = read_bfs_file (disk, sb, ino, node_off, &node, sizeof (node), 0);
|
||||
if (err)
|
||||
return 0;
|
||||
{
|
||||
char key_data[grub_bfs_to_cpu16 (node.total_key_len) + 1];
|
||||
grub_uint16_t keylen_idx[grub_bfs_to_cpu16 (node.count_keys)];
|
||||
grub_uint64_t key_values[grub_bfs_to_cpu16 (node.count_keys)];
|
||||
unsigned i;
|
||||
grub_uint16_t start = 0, end = 0;
|
||||
|
||||
err = read_bfs_file (disk, sb, ino, node_off + sizeof (node), key_data,
|
||||
grub_bfs_to_cpu16 (node.total_key_len), 0);
|
||||
if (err)
|
||||
return 0;
|
||||
key_data[grub_bfs_to_cpu16 (node.total_key_len)] = 0;
|
||||
err = read_bfs_file (disk, sb, ino, node_off
|
||||
+ ALIGN_UP (sizeof (node) + grub_bfs_to_cpu16 (node.total_key_len),
|
||||
8),
|
||||
keylen_idx, grub_bfs_to_cpu16 (node.count_keys)
|
||||
* 2, 0);
|
||||
if (err)
|
||||
return 0;
|
||||
err = read_bfs_file (disk, sb, ino, node_off
|
||||
+ ALIGN_UP (sizeof (node) + grub_bfs_to_cpu16 (node.total_key_len),
|
||||
8)
|
||||
+ grub_bfs_to_cpu16 (node.count_keys) * 2,
|
||||
key_values, grub_bfs_to_cpu16 (node.count_keys)
|
||||
* 8, 0);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < grub_bfs_to_cpu16 (node.count_keys); i++)
|
||||
{
|
||||
char c;
|
||||
start = end;
|
||||
end = grub_bfs_to_cpu16 (keylen_idx[i]);
|
||||
c = key_data[end];
|
||||
key_data[end] = 0;
|
||||
if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i])))
|
||||
return 1;
|
||||
key_data[end] = c;
|
||||
}
|
||||
node_off = grub_bfs_to_cpu64 (node.right);
|
||||
if (node_off == POINTER_INVALID)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
err = read_bfs_file (disk, sb, ino, node_off, &node, sizeof (node), 0);
|
||||
if (err)
|
||||
return err;
|
||||
if (node.count_keys == 0)
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found",
|
||||
name);
|
||||
{
|
||||
char key_data[grub_bfs_to_cpu16 (node.total_key_len) + 1];
|
||||
grub_uint16_t keylen_idx[grub_bfs_to_cpu16 (node.count_keys)];
|
||||
grub_uint64_t key_values[grub_bfs_to_cpu16 (node.count_keys)];
|
||||
unsigned i;
|
||||
grub_uint16_t start = 0, end = 0;
|
||||
err = read_bfs_file (disk, sb, ino, node_off + sizeof (node), key_data,
|
||||
grub_bfs_to_cpu16 (node.total_key_len), 0);
|
||||
if (err)
|
||||
return err;
|
||||
key_data[grub_bfs_to_cpu16 (node.total_key_len)] = 0;
|
||||
err = read_bfs_file (disk, sb, ino, node_off
|
||||
+
|
||||
ALIGN_UP (sizeof (node) +grub_bfs_to_cpu16 (node.total_key_len),
|
||||
8),
|
||||
keylen_idx, grub_bfs_to_cpu16 (node.count_keys)
|
||||
* 2, 0);
|
||||
if (err)
|
||||
return err;
|
||||
err = read_bfs_file (disk, sb, ino, node_off
|
||||
|
||||
+ ALIGN_UP (sizeof (node) + grub_bfs_to_cpu16 (node.total_key_len),
|
||||
8)
|
||||
+ grub_bfs_to_cpu16 (node.count_keys) * 2,
|
||||
key_values, grub_bfs_to_cpu16 (node.count_keys)
|
||||
* 8, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < grub_bfs_to_cpu16 (node.count_keys); i++)
|
||||
{
|
||||
int cmp;
|
||||
char c;
|
||||
start = end;
|
||||
end = grub_bfs_to_cpu16 (keylen_idx[i]);
|
||||
if (grub_bfs_to_cpu16 (node.total_key_len) <= end)
|
||||
end = grub_bfs_to_cpu16 (node.total_key_len);
|
||||
c = key_data[end];
|
||||
key_data[end] = 0;
|
||||
cmp = grub_strcmp (key_data + start, name);
|
||||
key_data[end] = c;
|
||||
if (cmp == 0 && level == 0)
|
||||
{
|
||||
*res = grub_bfs_to_cpu64 (key_values[i]);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
if (cmp >= 0 && level != 0)
|
||||
{
|
||||
node_off = grub_bfs_to_cpu64 (key_values[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < grub_bfs_to_cpu16 (node.count_keys))
|
||||
{
|
||||
level--;
|
||||
continue;
|
||||
}
|
||||
if (node.overflow != POINTER_INVALID)
|
||||
{
|
||||
node_off = grub_bfs_to_cpu64 (node.overflow);
|
||||
/* This level-- isn't specified but is needed. */
|
||||
level--;
|
||||
continue;
|
||||
}
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found",
|
||||
name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
hop_level (grub_disk_t disk,
|
||||
const struct grub_bfs_superblock *sb,
|
||||
struct grub_bfs_inode *ino,
|
||||
const char *name)
|
||||
{
|
||||
grub_err_t err;
|
||||
grub_uint64_t res;
|
||||
|
||||
if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) != ATTR_DIR))
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
err = find_in_b_tree (disk, sb, ino, name, &res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return grub_disk_read (disk, res
|
||||
<< (grub_bfs_to_cpu32 (sb->log2_bsize) - 9), 0,
|
||||
grub_bfs_to_cpu32 (sb->bsize), (char *) ino);
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
find_file (const char *path, grub_disk_t disk,
|
||||
const struct grub_bfs_superblock *sb,
|
||||
struct grub_bfs_inode *ino)
|
||||
{
|
||||
char *ptr, *ptr2;
|
||||
char *alloc = NULL;
|
||||
grub_err_t err;
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb->bsize)];
|
||||
} old_ino;
|
||||
unsigned symlinks_max = 32;
|
||||
|
||||
err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
|
||||
grub_bfs_to_cpu32 (sb->bsize));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ptr = (char *) path;
|
||||
while (1)
|
||||
{
|
||||
while (*ptr == '/')
|
||||
ptr++;
|
||||
if (*ptr == 0)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
ptr2 = grub_strchr (ptr, '/');
|
||||
grub_memcpy (&old_ino, ino, grub_bfs_to_cpu32 (sb->bsize));
|
||||
if (ptr2)
|
||||
{
|
||||
char component[ptr2 - ptr + 1];
|
||||
grub_memcpy (component, ptr, ptr2 - ptr);
|
||||
component[ptr2 - ptr] = 0;
|
||||
err = hop_level (disk, sb, ino, component);
|
||||
}
|
||||
else
|
||||
err = hop_level (disk, sb, ino, ptr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) == ATTR_LNK))
|
||||
{
|
||||
char *old_alloc = alloc;
|
||||
if (--symlinks_max == 0)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return grub_error (GRUB_ERR_SYMLINK_LOOP,
|
||||
"too deep nesting of symlinks");
|
||||
}
|
||||
|
||||
if (grub_bfs_to_cpu32 (ino->flags) & LONG_SYMLINK)
|
||||
{
|
||||
grub_size_t symsize = grub_bfs_to_cpu64 (ino->size);
|
||||
alloc = grub_malloc ((ptr2 ? grub_strlen (ptr2) : 0)
|
||||
|
||||
+ symsize + 1);
|
||||
if (!alloc)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return grub_errno;
|
||||
}
|
||||
grub_free (old_alloc);
|
||||
err = read_bfs_file (disk, sb, ino,
|
||||
0, alloc, symsize, 0);
|
||||
if (err)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return err;
|
||||
}
|
||||
alloc[symsize] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc = grub_malloc ((ptr2 ? grub_strlen (ptr2) : 0)
|
||||
+ sizeof (ino->inplace_link) + 1);
|
||||
if (!alloc)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return grub_errno;
|
||||
}
|
||||
grub_free (old_alloc);
|
||||
grub_memcpy (alloc, ino->inplace_link,
|
||||
sizeof (ino->inplace_link));
|
||||
alloc[sizeof (ino->inplace_link)] = 0;
|
||||
}
|
||||
if (alloc[0] == '/')
|
||||
{
|
||||
err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
|
||||
grub_bfs_to_cpu32 (sb->bsize));
|
||||
if (err)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else
|
||||
grub_memcpy (ino, &old_ino, grub_bfs_to_cpu32 (sb->bsize));
|
||||
ptr = alloc + grub_strlen (alloc);
|
||||
if (ptr2)
|
||||
ptr = grub_stpcpy (ptr, ptr2);
|
||||
*ptr = 0;
|
||||
ptr = ptr2 = alloc;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ptr2)
|
||||
{
|
||||
grub_free (alloc);
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
ptr = ptr2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
mount (grub_disk_t disk, struct grub_bfs_superblock *sb)
|
||||
{
|
||||
grub_err_t err;
|
||||
err = grub_disk_read (disk, 1, 0,
|
||||
sizeof (*sb), sb);
|
||||
if (err == GRUB_ERR_OUT_OF_RANGE)
|
||||
return grub_error (GRUB_ERR_BAD_FS, "not a BFS filesystem");
|
||||
if (err)
|
||||
return err;
|
||||
if (grub_bfs_to_cpu32 (sb->magic1) != SUPER_BLOCK_MAGIC1
|
||||
|| grub_bfs_to_cpu32 (sb->magic2) != SUPER_BLOCK_MAGIC2
|
||||
|| grub_bfs_to_cpu32 (sb->magic3) != SUPER_BLOCK_MAGIC3
|
||||
|| (grub_bfs_to_cpu32 (sb->bsize)
|
||||
!= (1U << grub_bfs_to_cpu32 (sb->log2_bsize))))
|
||||
return grub_error (GRUB_ERR_BAD_FS, "not a BFS filesystem");
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_bfs_dir (grub_device_t device, const char *path,
|
||||
int (*hook_in) (const char *filename,
|
||||
const struct grub_dirhook_info *info))
|
||||
{
|
||||
struct grub_bfs_superblock sb;
|
||||
grub_err_t err;
|
||||
auto int hook (const char *name, grub_uint64_t value);
|
||||
|
||||
int hook (const char *name, grub_uint64_t value)
|
||||
{
|
||||
grub_err_t err2;
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
|
||||
} ino;
|
||||
struct grub_dirhook_info info;
|
||||
|
||||
err2 = grub_disk_read (device->disk, value
|
||||
<< (grub_bfs_to_cpu32 (sb.log2_bsize) - 9)
|
||||
, 0,
|
||||
grub_bfs_to_cpu32 (sb.bsize), (char *) ino.raw);
|
||||
if (err2)
|
||||
{
|
||||
grub_print_error ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
info.mtimeset = 1;
|
||||
info.mtime = grub_bfs_to_cpu64 (ino.ino.mtime) >> 16;
|
||||
info.dir = ((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) == ATTR_DIR);
|
||||
return hook_in (name, &info);
|
||||
}
|
||||
err = mount (device->disk, &sb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
{
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
|
||||
} ino;
|
||||
err = find_file (path, device->disk, &sb, &ino.ino);
|
||||
if (err)
|
||||
return err;
|
||||
if (((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) != ATTR_DIR))
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
iterate_in_b_tree (device->disk, &sb, &ino.ino, hook);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
{
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
|
||||
} ino;
|
||||
struct grub_bfs_data *data;
|
||||
err = find_file (name, file->device->disk, &sb, &ino.ino);
|
||||
if (err)
|
||||
return err;
|
||||
if (((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) != ATTR_REG))
|
||||
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
|
||||
|
||||
data = grub_zalloc (sizeof (struct grub_bfs_data)
|
||||
+ grub_bfs_to_cpu32 (sb.bsize));
|
||||
if (!data)
|
||||
return grub_errno;
|
||||
data->sb = sb;
|
||||
grub_memcpy (&data->ino, &ino, grub_bfs_to_cpu32 (sb.bsize));
|
||||
file->data = data;
|
||||
file->size = grub_bfs_to_cpu64 (ino.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);
|
||||
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;
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
read_bfs_attr (grub_disk_t disk,
|
||||
const struct grub_bfs_superblock *sb,
|
||||
const 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)
|
||||
{
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb->bsize)];
|
||||
} ino2;
|
||||
grub_size_t read;
|
||||
grub_err_t err;
|
||||
grub_uint64_t res;
|
||||
|
||||
err = read_extent (disk, sb, &ino->attr, 0, 0, ino2.raw,
|
||||
grub_bfs_to_cpu32 (sb->bsize));
|
||||
if (err)
|
||||
return -1;
|
||||
|
||||
err = find_in_b_tree (disk, sb, &ino2.ino, name, &res);
|
||||
if (err)
|
||||
return -1;
|
||||
grub_disk_read (disk, res
|
||||
<< (grub_bfs_to_cpu32 (sb->log2_bsize) - 9)
|
||||
, 0,
|
||||
grub_bfs_to_cpu32 (sb->bsize), (char *) &ino2);
|
||||
read = grub_bfs_to_cpu64 (ino2.ino.size);
|
||||
if (read > len)
|
||||
read = len;
|
||||
|
||||
err = read_bfs_file (disk, sb, &ino2.ino, 0, buf, read, 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;
|
||||
|
||||
*uuid = 0;
|
||||
|
||||
err = mount (device->disk, &sb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
{
|
||||
union {
|
||||
struct grub_bfs_inode ino;
|
||||
grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
|
||||
} ino;
|
||||
grub_uint64_t vid;
|
||||
|
||||
err = read_extent (device->disk, &sb, &sb.root_dir, 0, 0,
|
||||
&ino, grub_bfs_to_cpu32 (sb.bsize));
|
||||
if (err)
|
||||
return err;
|
||||
if (read_bfs_attr (device->disk, &sb, &ino.ino, "be:volume_id",
|
||||
&vid, 8) == 8)
|
||||
*uuid = grub_xasprintf ("%016" PRIxGRUB_UINT64_T, grub_bfs_to_cpu64 (vid));
|
||||
}
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
static struct grub_fs grub_bfs_fs = {
|
||||
.name = "bfs",
|
||||
.dir = grub_bfs_dir,
|
||||
.open = grub_bfs_open,
|
||||
.read = grub_bfs_read,
|
||||
.close = grub_bfs_close,
|
||||
.label = grub_bfs_label,
|
||||
.uuid = grub_bfs_uuid,
|
||||
#ifdef GRUB_UTIL
|
||||
.reserved_first_sector = 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
GRUB_MOD_INIT (bfs)
|
||||
{
|
||||
COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE == sizeof (struct grub_bfs_extent));
|
||||
grub_fs_register (&grub_bfs_fs);
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI (bfs)
|
||||
{
|
||||
grub_fs_unregister (&grub_bfs_fs);
|
||||
}
|
Loading…
Reference in a new issue