1118 lines
29 KiB
C
1118 lines
29 KiB
C
/* bfs.c - The Bee File System. */
|
|
/*
|
|
* VasEBoot -- GRand Unified Bootloader
|
|
* Copyright (C) 2010,2011 Free Software Foundation, Inc.
|
|
*
|
|
* VasEBoot 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.
|
|
*
|
|
* VasEBoot 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 VasEBoot. 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 <VasEBoot/err.h>
|
|
#include <VasEBoot/file.h>
|
|
#include <VasEBoot/mm.h>
|
|
#include <VasEBoot/misc.h>
|
|
#include <VasEBoot/disk.h>
|
|
#include <VasEBoot/dl.h>
|
|
#include <VasEBoot/types.h>
|
|
#include <VasEBoot/i18n.h>
|
|
#include <VasEBoot/fshelp.h>
|
|
|
|
VasEBoot_MOD_LICENSE ("GPLv3+");
|
|
|
|
#ifdef MODE_AFS
|
|
#define BTREE_ALIGN 4
|
|
#define SUPERBLOCK 2
|
|
#else
|
|
#define BTREE_ALIGN 8
|
|
#define SUPERBLOCK 1
|
|
#endif
|
|
|
|
#define VasEBoot_bfs_to_cpu16 VasEBoot_le_to_cpu16
|
|
#define VasEBoot_bfs_to_cpu32 VasEBoot_le_to_cpu32
|
|
#define VasEBoot_bfs_to_cpu64 VasEBoot_le_to_cpu64
|
|
#define VasEBoot_cpu_to_bfs32_compile_time VasEBoot_cpu_to_le32_compile_time
|
|
|
|
#ifdef MODE_AFS
|
|
#define VasEBoot_bfs_to_cpu_treehead VasEBoot_bfs_to_cpu32
|
|
#else
|
|
#define VasEBoot_bfs_to_cpu_treehead VasEBoot_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 VasEBoot_bfs_extent
|
|
{
|
|
VasEBoot_uint32_t ag;
|
|
VasEBoot_uint16_t start;
|
|
VasEBoot_uint16_t len;
|
|
} VasEBoot_PACKED;
|
|
|
|
struct VasEBoot_bfs_superblock
|
|
{
|
|
char label[32];
|
|
VasEBoot_uint32_t magic1;
|
|
VasEBoot_uint32_t unused1;
|
|
VasEBoot_uint32_t bsize;
|
|
VasEBoot_uint32_t log2_bsize;
|
|
VasEBoot_uint8_t unused[20];
|
|
VasEBoot_uint32_t magic2;
|
|
VasEBoot_uint32_t unused2;
|
|
VasEBoot_uint32_t log2_ag_size;
|
|
VasEBoot_uint8_t unused3[32];
|
|
VasEBoot_uint32_t magic3;
|
|
struct VasEBoot_bfs_extent root_dir;
|
|
} VasEBoot_PACKED;
|
|
|
|
struct VasEBoot_bfs_inode
|
|
{
|
|
VasEBoot_uint8_t unused[20];
|
|
VasEBoot_uint32_t mode;
|
|
VasEBoot_uint32_t flags;
|
|
#ifdef MODE_AFS
|
|
VasEBoot_uint8_t unused2[12];
|
|
#else
|
|
VasEBoot_uint8_t unused2[8];
|
|
#endif
|
|
VasEBoot_uint64_t mtime;
|
|
VasEBoot_uint8_t unused3[8];
|
|
struct VasEBoot_bfs_extent attr;
|
|
VasEBoot_uint8_t unused4[12];
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
struct VasEBoot_bfs_extent direct[12];
|
|
VasEBoot_uint64_t max_direct_range;
|
|
struct VasEBoot_bfs_extent indirect;
|
|
VasEBoot_uint64_t max_indirect_range;
|
|
struct VasEBoot_bfs_extent double_indirect;
|
|
VasEBoot_uint64_t max_double_indirect_range;
|
|
VasEBoot_uint64_t size;
|
|
VasEBoot_uint32_t pad[4];
|
|
} VasEBoot_PACKED;
|
|
char inplace_link[144];
|
|
} VasEBoot_PACKED;
|
|
VasEBoot_uint8_t small_data[0];
|
|
} VasEBoot_PACKED;
|
|
|
|
enum
|
|
{
|
|
LONG_SYMLINK = 0x40
|
|
};
|
|
|
|
struct VasEBoot_bfs_small_data_element_header
|
|
{
|
|
VasEBoot_uint32_t type;
|
|
VasEBoot_uint16_t name_len;
|
|
VasEBoot_uint16_t value_len;
|
|
} VasEBoot_PACKED;
|
|
|
|
struct VasEBoot_bfs_btree_header
|
|
{
|
|
VasEBoot_uint32_t magic;
|
|
#ifdef MODE_AFS
|
|
VasEBoot_uint64_t root;
|
|
VasEBoot_uint32_t level;
|
|
VasEBoot_uint32_t node_size;
|
|
VasEBoot_uint32_t unused;
|
|
#else
|
|
VasEBoot_uint32_t node_size;
|
|
VasEBoot_uint32_t level;
|
|
VasEBoot_uint32_t unused;
|
|
VasEBoot_uint64_t root;
|
|
#endif
|
|
VasEBoot_uint32_t unused2[2];
|
|
} VasEBoot_PACKED;
|
|
|
|
struct VasEBoot_bfs_btree_node
|
|
{
|
|
VasEBoot_uint64_t unused;
|
|
VasEBoot_uint64_t right;
|
|
VasEBoot_uint64_t overflow;
|
|
#ifdef MODE_AFS
|
|
VasEBoot_uint32_t count_keys;
|
|
VasEBoot_uint32_t total_key_len;
|
|
#else
|
|
VasEBoot_uint16_t count_keys;
|
|
VasEBoot_uint16_t total_key_len;
|
|
#endif
|
|
} VasEBoot_PACKED;
|
|
|
|
struct VasEBoot_bfs_data
|
|
{
|
|
struct VasEBoot_bfs_superblock sb;
|
|
struct VasEBoot_bfs_inode ino;
|
|
};
|
|
|
|
/* Context for VasEBoot_bfs_dir. */
|
|
struct VasEBoot_bfs_dir_ctx
|
|
{
|
|
VasEBoot_device_t device;
|
|
VasEBoot_fs_dir_hook_t hook;
|
|
void *hook_data;
|
|
struct VasEBoot_bfs_superblock sb;
|
|
};
|
|
|
|
static VasEBoot_err_t
|
|
read_extent (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
const struct VasEBoot_bfs_extent *in,
|
|
VasEBoot_off_t off, VasEBoot_off_t byteoff, void *buf, VasEBoot_size_t len)
|
|
{
|
|
#ifdef MODE_AFS
|
|
return VasEBoot_disk_read (disk, ((VasEBoot_bfs_to_cpu32 (in->ag)
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_ag_size)
|
|
- VasEBoot_DISK_SECTOR_BITS))
|
|
+ ((VasEBoot_bfs_to_cpu16 (in->start) + off)
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
- VasEBoot_DISK_SECTOR_BITS))),
|
|
byteoff, len, buf);
|
|
#else
|
|
return VasEBoot_disk_read (disk, (((VasEBoot_bfs_to_cpu32 (in->ag)
|
|
<< VasEBoot_bfs_to_cpu32 (sb->log2_ag_size))
|
|
+ VasEBoot_bfs_to_cpu16 (in->start) + off)
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
- VasEBoot_DISK_SECTOR_BITS)),
|
|
byteoff, len, buf);
|
|
#endif
|
|
}
|
|
|
|
#ifdef MODE_AFS
|
|
#define RANGE_SHIFT VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
#else
|
|
#define RANGE_SHIFT 0
|
|
#endif
|
|
|
|
static VasEBoot_err_t
|
|
read_bfs_file (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
const struct VasEBoot_bfs_inode *ino,
|
|
VasEBoot_off_t off, void *buf, VasEBoot_size_t len,
|
|
VasEBoot_disk_read_hook_t read_hook, void *read_hook_data)
|
|
{
|
|
if (len == 0)
|
|
return VasEBoot_ERR_NONE;
|
|
|
|
if (off + len > VasEBoot_bfs_to_cpu64 (ino->size))
|
|
return VasEBoot_error (VasEBoot_ERR_OUT_OF_RANGE,
|
|
N_("attempt to read past the end of file"));
|
|
|
|
if (off < (VasEBoot_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
|
|
{
|
|
unsigned i;
|
|
VasEBoot_uint64_t pos = 0;
|
|
for (i = 0; i < ARRAY_SIZE (ino->direct); i++)
|
|
{
|
|
VasEBoot_uint64_t newpos;
|
|
newpos = pos + (((VasEBoot_uint64_t) VasEBoot_bfs_to_cpu16 (ino->direct[i].len))
|
|
<< VasEBoot_bfs_to_cpu32 (sb->log2_bsize));
|
|
if (newpos > off)
|
|
{
|
|
VasEBoot_size_t read_size;
|
|
VasEBoot_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 VasEBoot_ERR_NONE;
|
|
}
|
|
pos = newpos;
|
|
}
|
|
}
|
|
|
|
if (off < (VasEBoot_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT))
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS, "incorrect direct blocks");
|
|
|
|
if (off < (VasEBoot_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
|
|
{
|
|
unsigned i;
|
|
struct VasEBoot_bfs_extent *entries;
|
|
VasEBoot_size_t nentries;
|
|
VasEBoot_err_t err;
|
|
VasEBoot_uint64_t pos = (VasEBoot_bfs_to_cpu64 (ino->max_direct_range)
|
|
<< RANGE_SHIFT);
|
|
nentries = (((VasEBoot_size_t) VasEBoot_bfs_to_cpu16 (ino->indirect.len))
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
|
|
entries = VasEBoot_malloc (nentries << LOG_EXTENT_SIZE);
|
|
if (!entries)
|
|
return VasEBoot_errno;
|
|
err = read_extent (disk, sb, &ino->indirect, 0, 0,
|
|
entries, nentries << LOG_EXTENT_SIZE);
|
|
for (i = 0; i < nentries; i++)
|
|
{
|
|
VasEBoot_uint64_t newpos;
|
|
newpos = pos + (((VasEBoot_uint64_t) VasEBoot_bfs_to_cpu16 (entries[i].len))
|
|
<< VasEBoot_bfs_to_cpu32 (sb->log2_bsize));
|
|
if (newpos > off)
|
|
{
|
|
VasEBoot_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)
|
|
{
|
|
VasEBoot_free (entries);
|
|
return err;
|
|
}
|
|
off += read_size;
|
|
len -= read_size;
|
|
buf = (char *) buf + read_size;
|
|
if (len == 0)
|
|
{
|
|
VasEBoot_free (entries);
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
}
|
|
pos = newpos;
|
|
}
|
|
VasEBoot_free (entries);
|
|
}
|
|
|
|
if (off < (VasEBoot_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT))
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS, "incorrect indirect blocks");
|
|
|
|
{
|
|
struct VasEBoot_bfs_extent *l1_entries, *l2_entries;
|
|
VasEBoot_size_t nl1_entries, nl2_entries;
|
|
VasEBoot_off_t last_l1n = ~0ULL;
|
|
VasEBoot_err_t err;
|
|
nl1_entries = (((VasEBoot_uint64_t) VasEBoot_bfs_to_cpu16 (ino->double_indirect.len))
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE));
|
|
l1_entries = VasEBoot_malloc (nl1_entries << LOG_EXTENT_SIZE);
|
|
if (!l1_entries)
|
|
return VasEBoot_errno;
|
|
nl2_entries = 0;
|
|
l2_entries = VasEBoot_malloc (1 << (DOUBLE_INDIRECT_SHIFT
|
|
+ VasEBoot_bfs_to_cpu32 (sb->log2_bsize)));
|
|
if (!l2_entries)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
return VasEBoot_errno;
|
|
}
|
|
err = read_extent (disk, sb, &ino->double_indirect, 0, 0,
|
|
l1_entries, nl1_entries << LOG_EXTENT_SIZE);
|
|
if (err)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
VasEBoot_free (l2_entries);
|
|
return err;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
VasEBoot_off_t boff, l2n, l1n;
|
|
VasEBoot_size_t read_size;
|
|
VasEBoot_off_t double_indirect_offset;
|
|
double_indirect_offset = off
|
|
- VasEBoot_bfs_to_cpu64 (ino->max_indirect_range);
|
|
boff = (double_indirect_offset
|
|
& ((1 << (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
+ DOUBLE_INDIRECT_SHIFT)) - 1));
|
|
l2n = ((double_indirect_offset >> (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
+ DOUBLE_INDIRECT_SHIFT))
|
|
& ((1 << (VasEBoot_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE
|
|
+ DOUBLE_INDIRECT_SHIFT)) - 1));
|
|
l1n =
|
|
(double_indirect_offset >>
|
|
(2 * VasEBoot_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE +
|
|
2 * DOUBLE_INDIRECT_SHIFT));
|
|
if (l1n > nl1_entries)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
VasEBoot_free (l2_entries);
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS,
|
|
"incorrect double-indirect block");
|
|
}
|
|
if (l1n != last_l1n)
|
|
{
|
|
nl2_entries = (((VasEBoot_uint64_t) VasEBoot_bfs_to_cpu16 (l1_entries[l1n].len))
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
- LOG_EXTENT_SIZE));
|
|
if (nl2_entries > (1U << (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
- LOG_EXTENT_SIZE
|
|
+ DOUBLE_INDIRECT_SHIFT)))
|
|
nl2_entries = (1 << (VasEBoot_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)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
VasEBoot_free (l2_entries);
|
|
return err;
|
|
}
|
|
last_l1n = l1n;
|
|
}
|
|
if (l2n > nl2_entries)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
VasEBoot_free (l2_entries);
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS,
|
|
"incorrect double-indirect block");
|
|
}
|
|
|
|
read_size = (1 << (VasEBoot_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)
|
|
{
|
|
VasEBoot_free (l1_entries);
|
|
VasEBoot_free (l2_entries);
|
|
return err;
|
|
}
|
|
off += read_size;
|
|
len -= read_size;
|
|
buf = (char *) buf + read_size;
|
|
}
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
read_b_node (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
const struct VasEBoot_bfs_inode *ino,
|
|
VasEBoot_uint64_t node_off,
|
|
struct VasEBoot_bfs_btree_node **node,
|
|
char **key_data, VasEBoot_uint16_t **keylen_idx,
|
|
VasEBoot_unaligned_uint64_t **key_values)
|
|
{
|
|
void *ret;
|
|
struct VasEBoot_bfs_btree_node node_head;
|
|
VasEBoot_size_t total_size;
|
|
VasEBoot_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) +
|
|
VasEBoot_bfs_to_cpu_treehead
|
|
(node_head.total_key_len),
|
|
BTREE_ALIGN) +
|
|
VasEBoot_bfs_to_cpu_treehead (node_head.count_keys) *
|
|
sizeof (VasEBoot_uint16_t)
|
|
+ VasEBoot_bfs_to_cpu_treehead (node_head.count_keys) *
|
|
sizeof (VasEBoot_uint64_t);
|
|
|
|
ret = VasEBoot_malloc (total_size);
|
|
if (!ret)
|
|
return VasEBoot_errno;
|
|
|
|
err = read_bfs_file (disk, sb, ino, node_off, ret, total_size, 0, 0);
|
|
if (err)
|
|
{
|
|
VasEBoot_free (ret);
|
|
return err;
|
|
}
|
|
|
|
*node = ret;
|
|
*key_data = (char *) ret + sizeof (node_head);
|
|
*keylen_idx = (VasEBoot_uint16_t *) ret
|
|
+ ALIGN_UP (sizeof (node_head) +
|
|
VasEBoot_bfs_to_cpu_treehead (node_head.total_key_len),
|
|
BTREE_ALIGN) / 2;
|
|
*key_values = (VasEBoot_unaligned_uint64_t *)
|
|
(*keylen_idx +
|
|
VasEBoot_bfs_to_cpu_treehead (node_head.count_keys));
|
|
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
static int
|
|
iterate_in_b_tree (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
const struct VasEBoot_bfs_inode *ino,
|
|
int (*hook) (const char *name, VasEBoot_uint64_t value,
|
|
struct VasEBoot_bfs_dir_ctx *ctx),
|
|
struct VasEBoot_bfs_dir_ctx *ctx)
|
|
{
|
|
struct VasEBoot_bfs_btree_header head;
|
|
VasEBoot_err_t err;
|
|
int level;
|
|
VasEBoot_uint64_t node_off;
|
|
|
|
err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
|
|
if (err)
|
|
return 0;
|
|
node_off = VasEBoot_bfs_to_cpu64 (head.root);
|
|
|
|
level = VasEBoot_bfs_to_cpu32 (head.level) - 1;
|
|
while (level--)
|
|
{
|
|
struct VasEBoot_bfs_btree_node node;
|
|
VasEBoot_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) +
|
|
VasEBoot_bfs_to_cpu_treehead (node.
|
|
total_key_len),
|
|
BTREE_ALIGN) +
|
|
VasEBoot_bfs_to_cpu_treehead (node.count_keys) *
|
|
sizeof (VasEBoot_uint16_t), &key_value,
|
|
sizeof (VasEBoot_uint64_t), 0, 0);
|
|
if (err)
|
|
return 0;
|
|
|
|
node_off = VasEBoot_bfs_to_cpu64 (key_value);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
struct VasEBoot_bfs_btree_node *node;
|
|
char *key_data;
|
|
VasEBoot_uint16_t *keylen_idx;
|
|
VasEBoot_unaligned_uint64_t *key_values;
|
|
unsigned i;
|
|
VasEBoot_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 < VasEBoot_bfs_to_cpu_treehead (node->count_keys); i++)
|
|
{
|
|
char c;
|
|
start = end;
|
|
end = VasEBoot_bfs_to_cpu16 (keylen_idx[i]);
|
|
if (VasEBoot_bfs_to_cpu_treehead (node->total_key_len) <= end)
|
|
end = VasEBoot_bfs_to_cpu_treehead (node->total_key_len);
|
|
c = key_data[end];
|
|
key_data[end] = 0;
|
|
if (hook (key_data + start, VasEBoot_bfs_to_cpu64 (key_values[i].val),
|
|
ctx))
|
|
{
|
|
VasEBoot_free (node);
|
|
return 1;
|
|
}
|
|
key_data[end] = c;
|
|
}
|
|
node_off = VasEBoot_bfs_to_cpu64 (node->right);
|
|
VasEBoot_free (node);
|
|
if (node_off == POINTER_INVALID)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
bfs_strcmp (const char *a, const char *b, VasEBoot_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) (VasEBoot_int8_t) ac - (int) (VasEBoot_int8_t) bc;
|
|
#else
|
|
return (int) (VasEBoot_uint8_t) ac - (int) (VasEBoot_uint8_t) bc;
|
|
#endif
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
find_in_b_tree (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
const struct VasEBoot_bfs_inode *ino, const char *name,
|
|
VasEBoot_uint64_t * res)
|
|
{
|
|
struct VasEBoot_bfs_btree_header head;
|
|
VasEBoot_err_t err;
|
|
int level;
|
|
VasEBoot_uint64_t node_off;
|
|
|
|
err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0);
|
|
if (err)
|
|
return err;
|
|
node_off = VasEBoot_bfs_to_cpu64 (head.root);
|
|
|
|
level = VasEBoot_bfs_to_cpu32 (head.level) - 1;
|
|
while (1)
|
|
{
|
|
struct VasEBoot_bfs_btree_node *node;
|
|
char *key_data;
|
|
VasEBoot_uint16_t *keylen_idx;
|
|
VasEBoot_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)
|
|
{
|
|
VasEBoot_free (node);
|
|
return VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
|
|
name);
|
|
}
|
|
|
|
for (lg = 0; VasEBoot_bfs_to_cpu_treehead (node->count_keys) >> lg; lg++);
|
|
|
|
i = 0;
|
|
|
|
for (j = lg - 1; j >= 0; j--)
|
|
{
|
|
int cmp;
|
|
VasEBoot_uint16_t start = 0, end = 0;
|
|
if ((i | (1 << j)) >= VasEBoot_bfs_to_cpu_treehead (node->count_keys))
|
|
continue;
|
|
start = VasEBoot_bfs_to_cpu16 (keylen_idx[(i | (1 << j)) - 1]);
|
|
end = VasEBoot_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]);
|
|
if (VasEBoot_bfs_to_cpu_treehead (node->total_key_len) <= end)
|
|
end = VasEBoot_bfs_to_cpu_treehead (node->total_key_len);
|
|
cmp = bfs_strcmp (key_data + start, name, end - start);
|
|
if (cmp == 0 && level == 0)
|
|
{
|
|
*res = VasEBoot_bfs_to_cpu64 (key_values[i | (1 << j)].val);
|
|
VasEBoot_free (node);
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
#ifdef MODE_AFS
|
|
if (cmp <= 0)
|
|
#else
|
|
if (cmp < 0)
|
|
#endif
|
|
i |= (1 << j);
|
|
}
|
|
if (i == 0)
|
|
{
|
|
VasEBoot_uint16_t end = 0;
|
|
int cmp;
|
|
end = VasEBoot_bfs_to_cpu16 (keylen_idx[0]);
|
|
if (VasEBoot_bfs_to_cpu_treehead (node->total_key_len) <= end)
|
|
end = VasEBoot_bfs_to_cpu_treehead (node->total_key_len);
|
|
cmp = bfs_strcmp (key_data, name, end);
|
|
if (cmp == 0 && level == 0)
|
|
{
|
|
*res = VasEBoot_bfs_to_cpu64 (key_values[0].val);
|
|
VasEBoot_free (node);
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
#ifdef MODE_AFS
|
|
if (cmp > 0 && level != 0)
|
|
#else
|
|
if (cmp >= 0 && level != 0)
|
|
#endif
|
|
{
|
|
node_off = VasEBoot_bfs_to_cpu64 (key_values[0].val);
|
|
level--;
|
|
VasEBoot_free (node);
|
|
continue;
|
|
}
|
|
else if (level != 0
|
|
&& VasEBoot_bfs_to_cpu_treehead (node->count_keys) >= 2)
|
|
{
|
|
node_off = VasEBoot_bfs_to_cpu64 (key_values[1].val);
|
|
level--;
|
|
VasEBoot_free (node);
|
|
continue;
|
|
}
|
|
}
|
|
else if (level != 0
|
|
&& i + 1 < VasEBoot_bfs_to_cpu_treehead (node->count_keys))
|
|
{
|
|
node_off = VasEBoot_bfs_to_cpu64 (key_values[i + 1].val);
|
|
level--;
|
|
VasEBoot_free (node);
|
|
continue;
|
|
}
|
|
if (node->overflow != POINTER_INVALID)
|
|
{
|
|
node_off = VasEBoot_bfs_to_cpu64 (node->overflow);
|
|
/* This level-- isn't specified but is needed. */
|
|
level--;
|
|
VasEBoot_free (node);
|
|
continue;
|
|
}
|
|
VasEBoot_free (node);
|
|
return VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
|
|
name);
|
|
}
|
|
}
|
|
|
|
struct VasEBoot_fshelp_node
|
|
{
|
|
VasEBoot_disk_t disk;
|
|
const struct VasEBoot_bfs_superblock *sb;
|
|
struct VasEBoot_bfs_inode ino;
|
|
};
|
|
|
|
static VasEBoot_err_t
|
|
lookup_file (VasEBoot_fshelp_node_t dir,
|
|
const char *name,
|
|
VasEBoot_fshelp_node_t *foundnode,
|
|
enum VasEBoot_fshelp_filetype *foundtype)
|
|
{
|
|
VasEBoot_err_t err;
|
|
struct VasEBoot_bfs_inode *new_ino;
|
|
VasEBoot_uint64_t res = 0;
|
|
|
|
err = find_in_b_tree (dir->disk, dir->sb, &dir->ino, name, &res);
|
|
if (err)
|
|
return err;
|
|
|
|
*foundnode = VasEBoot_malloc (sizeof (struct VasEBoot_fshelp_node));
|
|
if (!*foundnode)
|
|
return VasEBoot_errno;
|
|
|
|
(*foundnode)->disk = dir->disk;
|
|
(*foundnode)->sb = dir->sb;
|
|
new_ino = &(*foundnode)->ino;
|
|
|
|
if (VasEBoot_disk_read (dir->disk, res
|
|
<< (VasEBoot_bfs_to_cpu32 (dir->sb->log2_bsize)
|
|
- VasEBoot_DISK_SECTOR_BITS), 0,
|
|
sizeof (*new_ino), (char *) new_ino))
|
|
{
|
|
VasEBoot_free (*foundnode);
|
|
return VasEBoot_errno;
|
|
}
|
|
switch (VasEBoot_bfs_to_cpu32 (new_ino->mode) & ATTR_TYPE)
|
|
{
|
|
default:
|
|
case ATTR_REG:
|
|
*foundtype = VasEBoot_FSHELP_REG;
|
|
break;
|
|
case ATTR_DIR:
|
|
*foundtype = VasEBoot_FSHELP_DIR;
|
|
break;
|
|
case ATTR_LNK:
|
|
*foundtype = VasEBoot_FSHELP_SYMLINK;
|
|
break;
|
|
}
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
static char *
|
|
read_symlink (VasEBoot_fshelp_node_t node)
|
|
{
|
|
char *alloc = NULL;
|
|
VasEBoot_err_t err;
|
|
|
|
#ifndef MODE_AFS
|
|
if (!(VasEBoot_bfs_to_cpu32 (node->ino.flags) & LONG_SYMLINK))
|
|
{
|
|
alloc = VasEBoot_malloc (sizeof (node->ino.inplace_link) + 1);
|
|
if (!alloc)
|
|
{
|
|
return NULL;
|
|
}
|
|
VasEBoot_memcpy (alloc, node->ino.inplace_link,
|
|
sizeof (node->ino.inplace_link));
|
|
alloc[sizeof (node->ino.inplace_link)] = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
VasEBoot_size_t symsize = VasEBoot_bfs_to_cpu64 (node->ino.size);
|
|
alloc = VasEBoot_malloc (symsize + 1);
|
|
if (!alloc)
|
|
return NULL;
|
|
err = read_bfs_file (node->disk, node->sb, &node->ino, 0, alloc, symsize, 0, 0);
|
|
if (err)
|
|
{
|
|
VasEBoot_free (alloc);
|
|
return NULL;
|
|
}
|
|
alloc[symsize] = 0;
|
|
}
|
|
|
|
return alloc;
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
find_file (const char *path, VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb, struct VasEBoot_bfs_inode *ino,
|
|
enum VasEBoot_fshelp_filetype exptype)
|
|
{
|
|
VasEBoot_err_t err;
|
|
struct VasEBoot_fshelp_node root = {
|
|
.disk = disk,
|
|
.sb = sb,
|
|
};
|
|
struct VasEBoot_fshelp_node *found;
|
|
|
|
err = read_extent (disk, sb, &sb->root_dir, 0, 0, &root.ino,
|
|
sizeof (root.ino));
|
|
if (err)
|
|
return err;
|
|
err = VasEBoot_fshelp_find_file_lookup (path, &root, &found, lookup_file, read_symlink, exptype);
|
|
if (!err)
|
|
VasEBoot_memcpy (ino, &found->ino, sizeof (*ino));
|
|
|
|
if (&root != found)
|
|
VasEBoot_free (found);
|
|
return err;
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
mount (VasEBoot_disk_t disk, struct VasEBoot_bfs_superblock *sb)
|
|
{
|
|
VasEBoot_err_t err;
|
|
err = VasEBoot_disk_read (disk, SUPERBLOCK, 0, sizeof (*sb), sb);
|
|
if (err == VasEBoot_ERR_OUT_OF_RANGE)
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS,
|
|
#ifdef MODE_AFS
|
|
"not an AFS filesystem"
|
|
#else
|
|
"not a BFS filesystem"
|
|
#endif
|
|
);
|
|
if (err)
|
|
return err;
|
|
if (sb->magic1 != VasEBoot_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC1)
|
|
|| sb->magic2 != VasEBoot_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC2)
|
|
|| sb->magic3 != VasEBoot_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC3)
|
|
|| sb->bsize == 0
|
|
|| (VasEBoot_bfs_to_cpu32 (sb->bsize)
|
|
!= (1U << VasEBoot_bfs_to_cpu32 (sb->log2_bsize)))
|
|
|| VasEBoot_bfs_to_cpu32 (sb->log2_bsize) < VasEBoot_DISK_SECTOR_BITS)
|
|
return VasEBoot_error (VasEBoot_ERR_BAD_FS,
|
|
#ifdef MODE_AFS
|
|
"not an AFS filesystem"
|
|
#else
|
|
"not a BFS filesystem"
|
|
#endif
|
|
);
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
/* Helper for VasEBoot_bfs_dir. */
|
|
static int
|
|
VasEBoot_bfs_dir_iter (const char *name, VasEBoot_uint64_t value,
|
|
struct VasEBoot_bfs_dir_ctx *ctx)
|
|
{
|
|
VasEBoot_err_t err2;
|
|
struct VasEBoot_bfs_inode ino;
|
|
struct VasEBoot_dirhook_info info;
|
|
|
|
err2 = VasEBoot_disk_read (ctx->device->disk, value
|
|
<< (VasEBoot_bfs_to_cpu32 (ctx->sb.log2_bsize)
|
|
- VasEBoot_DISK_SECTOR_BITS), 0,
|
|
sizeof (ino), (char *) &ino);
|
|
if (err2)
|
|
{
|
|
VasEBoot_print_error ();
|
|
return 0;
|
|
}
|
|
|
|
info.mtimeset = 1;
|
|
#ifdef MODE_AFS
|
|
info.mtime =
|
|
VasEBoot_divmod64 (VasEBoot_bfs_to_cpu64 (ino.mtime), 1000000, 0);
|
|
#else
|
|
info.mtime = VasEBoot_bfs_to_cpu64 (ino.mtime) >> 16;
|
|
#endif
|
|
info.dir = ((VasEBoot_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) == ATTR_DIR);
|
|
return ctx->hook (name, &info, ctx->hook_data);
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
VasEBoot_bfs_dir (VasEBoot_device_t device, const char *path,
|
|
VasEBoot_fs_dir_hook_t hook, void *hook_data)
|
|
{
|
|
struct VasEBoot_bfs_dir_ctx ctx = {
|
|
.device = device,
|
|
.hook = hook,
|
|
.hook_data = hook_data
|
|
};
|
|
VasEBoot_err_t err;
|
|
|
|
err = mount (device->disk, &ctx.sb);
|
|
if (err)
|
|
return err;
|
|
|
|
{
|
|
struct VasEBoot_bfs_inode ino;
|
|
err = find_file (path, device->disk, &ctx.sb, &ino, VasEBoot_FSHELP_DIR);
|
|
if (err)
|
|
return err;
|
|
iterate_in_b_tree (device->disk, &ctx.sb, &ino, VasEBoot_bfs_dir_iter,
|
|
&ctx);
|
|
}
|
|
|
|
return VasEBoot_errno;
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
VasEBoot_bfs_open (struct VasEBoot_file *file, const char *name)
|
|
{
|
|
struct VasEBoot_bfs_superblock sb;
|
|
VasEBoot_err_t err;
|
|
|
|
err = mount (file->device->disk, &sb);
|
|
if (err)
|
|
return err;
|
|
|
|
{
|
|
struct VasEBoot_bfs_inode ino;
|
|
struct VasEBoot_bfs_data *data;
|
|
err = find_file (name, file->device->disk, &sb, &ino, VasEBoot_FSHELP_REG);
|
|
if (err)
|
|
return err;
|
|
|
|
data = VasEBoot_zalloc (sizeof (struct VasEBoot_bfs_data));
|
|
if (!data)
|
|
return VasEBoot_errno;
|
|
data->sb = sb;
|
|
VasEBoot_memcpy (&data->ino, &ino, sizeof (data->ino));
|
|
file->data = data;
|
|
file->size = VasEBoot_bfs_to_cpu64 (ino.size);
|
|
}
|
|
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
static VasEBoot_err_t
|
|
VasEBoot_bfs_close (VasEBoot_file_t file)
|
|
{
|
|
VasEBoot_free (file->data);
|
|
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
static VasEBoot_ssize_t
|
|
VasEBoot_bfs_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len)
|
|
{
|
|
VasEBoot_err_t err;
|
|
struct VasEBoot_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 VasEBoot_err_t
|
|
VasEBoot_bfs_label (VasEBoot_device_t device, char **label)
|
|
{
|
|
struct VasEBoot_bfs_superblock sb;
|
|
VasEBoot_err_t err;
|
|
|
|
*label = 0;
|
|
|
|
err = mount (device->disk, &sb);
|
|
if (err)
|
|
return err;
|
|
|
|
*label = VasEBoot_strndup (sb.label, sizeof (sb.label));
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
|
|
#ifndef MODE_AFS
|
|
static VasEBoot_ssize_t
|
|
read_bfs_attr (VasEBoot_disk_t disk,
|
|
const struct VasEBoot_bfs_superblock *sb,
|
|
struct VasEBoot_bfs_inode *ino,
|
|
const char *name, void *buf, VasEBoot_size_t len)
|
|
{
|
|
VasEBoot_uint8_t *ptr = (VasEBoot_uint8_t *) ino->small_data;
|
|
VasEBoot_uint8_t *end = ((VasEBoot_uint8_t *) ino + VasEBoot_bfs_to_cpu32 (sb->bsize));
|
|
|
|
while (ptr + sizeof (struct VasEBoot_bfs_small_data_element_header) < end)
|
|
{
|
|
struct VasEBoot_bfs_small_data_element_header *el;
|
|
char *el_name;
|
|
VasEBoot_uint8_t *data;
|
|
el = (struct VasEBoot_bfs_small_data_element_header *) ptr;
|
|
if (el->name_len == 0)
|
|
break;
|
|
el_name = (char *) (el + 1);
|
|
data = (VasEBoot_uint8_t *) el_name + VasEBoot_bfs_to_cpu16 (el->name_len) + 3;
|
|
ptr = data + VasEBoot_bfs_to_cpu16 (el->value_len) + 1;
|
|
if (VasEBoot_memcmp (name, el_name, VasEBoot_bfs_to_cpu16 (el->name_len)) == 0
|
|
&& name[el->name_len] == 0)
|
|
{
|
|
VasEBoot_size_t copy;
|
|
copy = len;
|
|
if (VasEBoot_bfs_to_cpu16 (el->value_len) > copy)
|
|
copy = VasEBoot_bfs_to_cpu16 (el->value_len);
|
|
VasEBoot_memcpy (buf, data, copy);
|
|
return copy;
|
|
}
|
|
}
|
|
|
|
if (ino->attr.len != 0)
|
|
{
|
|
VasEBoot_size_t read;
|
|
VasEBoot_err_t err;
|
|
VasEBoot_uint64_t res;
|
|
|
|
err = read_extent (disk, sb, &ino->attr, 0, 0, ino,
|
|
VasEBoot_bfs_to_cpu32 (sb->bsize));
|
|
if (err)
|
|
return -1;
|
|
|
|
err = find_in_b_tree (disk, sb, ino, name, &res);
|
|
if (err)
|
|
return -1;
|
|
VasEBoot_disk_read (disk, res
|
|
<< (VasEBoot_bfs_to_cpu32 (sb->log2_bsize)
|
|
- VasEBoot_DISK_SECTOR_BITS), 0,
|
|
VasEBoot_bfs_to_cpu32 (sb->bsize), (char *) ino);
|
|
read = VasEBoot_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 VasEBoot_err_t
|
|
VasEBoot_bfs_uuid (VasEBoot_device_t device, char **uuid)
|
|
{
|
|
struct VasEBoot_bfs_superblock sb;
|
|
VasEBoot_err_t err;
|
|
struct VasEBoot_bfs_inode *ino;
|
|
VasEBoot_uint64_t vid;
|
|
|
|
*uuid = 0;
|
|
|
|
err = mount (device->disk, &sb);
|
|
if (err)
|
|
return err;
|
|
|
|
ino = VasEBoot_malloc (VasEBoot_bfs_to_cpu32 (sb.bsize));
|
|
if (!ino)
|
|
return VasEBoot_errno;
|
|
|
|
err = read_extent (device->disk, &sb, &sb.root_dir, 0, 0,
|
|
ino, VasEBoot_bfs_to_cpu32 (sb.bsize));
|
|
if (err)
|
|
{
|
|
VasEBoot_free (ino);
|
|
return err;
|
|
}
|
|
if (read_bfs_attr (device->disk, &sb, ino, "be:volume_id",
|
|
&vid, sizeof (vid)) == sizeof (vid))
|
|
*uuid =
|
|
VasEBoot_xasprintf ("%016" PRIxVasEBoot_UINT64_T, VasEBoot_bfs_to_cpu64 (vid));
|
|
|
|
VasEBoot_free (ino);
|
|
|
|
return VasEBoot_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
static struct VasEBoot_fs VasEBoot_bfs_fs = {
|
|
#ifdef MODE_AFS
|
|
.name = "afs",
|
|
#else
|
|
.name = "bfs",
|
|
#endif
|
|
.dir = VasEBoot_bfs_dir,
|
|
.open = VasEBoot_bfs_open,
|
|
.read = VasEBoot_bfs_read,
|
|
.close = VasEBoot_bfs_close,
|
|
.label = VasEBoot_bfs_label,
|
|
#ifndef MODE_AFS
|
|
.uuid = VasEBoot_bfs_uuid,
|
|
#endif
|
|
#ifdef VasEBoot_UTIL
|
|
.reserved_first_sector = 1,
|
|
.blocklist_install = 1,
|
|
#endif
|
|
};
|
|
|
|
#ifdef MODE_AFS
|
|
VasEBoot_MOD_INIT (afs)
|
|
#else
|
|
VasEBoot_MOD_INIT (bfs)
|
|
#endif
|
|
{
|
|
COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE ==
|
|
sizeof (struct VasEBoot_bfs_extent));
|
|
VasEBoot_fs_register (&VasEBoot_bfs_fs);
|
|
}
|
|
|
|
#ifdef MODE_AFS
|
|
VasEBoot_MOD_FINI (afs)
|
|
#else
|
|
VasEBoot_MOD_FINI (bfs)
|
|
#endif
|
|
{
|
|
VasEBoot_fs_unregister (&VasEBoot_bfs_fs);
|
|
}
|