Build: add --enable-warnings (-Wall -Wextra); the tree is now warning-free with it, so distributions can opt in without risking a surprise -Werror breakage from a future compiler. Real bugs fixed (each verified by a reproducer): - lib/io.c: open_rollback_file() read 28 bytes into char[28] with no NUL and then strcmp()'d it against a 28-character magic, reading one byte past the array. The condition was also inverted, so a *valid* rollback file was rejected -- but only sometimes: the missing NUL made the result depend on stack garbage (verified: -O0 rejects, -O2 accepts). The whole --save-rollback/--rollback-changes path was unreliable. Now the buffer is sized for the terminator, zeroed, and the comparison is `!= 0`. Verified end to end: save -> reopen -> restore reports "2 blocks restored, 0 skipped"; a random file is rejected. - reiserfscore/reiserfslib.c, reiserfscore/journal.c: asprintf() was called with &fs->fs_file_name / &fs->fs_j_file_name, but those fields are `char *`, not `char **`. Passing a `char **` where `char *` is expected wrote through the wrong pointer -- silent memory corruption on any allocation. Fixed by using the new checked helper. - reiserfscore/node_formats.c: `code < 0` on an unsigned parameter was dead code; the guard never fired. - resize_reiserfs: missing `break` after -j made -j fall into -f, so "-j dev" silently also forced the operation. Sign-compare on the count_blocks() ternary and on count + blocks. - lib/misc.c: strncat with bound == strlen(src) (equivalent to strcat) into char[256] from a char[256] dirent name -- up to 261 bytes. Replaced with snprintf plus an explicit fit check. - lib/misc.c: die() used vsprintf into a fixed 1024-byte buffer. - strncpy with bound == destination size (mkreiserfs, tune, progbar) never terminated; replaced with snprintf. Allocator: drop the homegrown magic-header allocator. It wrote "_mem_begin_" / "mem_end" through strcpy inside the allocation, kept the size in an `int`, and its only safety check (checkmem) was never called -- every call site is commented out. Replaced with a size_t header that is over-aligned via max_align_t, so malloc alignment guarantees still hold. expandmem() now takes the old size from the header instead of a caller-supplied value that could disagree with it. Warnings: annotate the deliberate fallthroughs (fsck pass cascade, unit suffix parsing, percentage parsing), fix the remaining sign-compare and missing-field-initializer sites, and compute addresses of packed members via char* + offsetof instead of &member. Portability: asm/types.h -> linux/types.h, malloc.h and sys/vfs.h dropped from includes.h (nothing used them), sys/vfs.h -> sys/statfs.h for statfs(2). Version: reiserfsprogs.spec said 3.6.23 while configure.ac said 3.6.25. Remove the dead `#if 0` main() from hashes.c. Add .github/workflows/build.yml: gcc, clang, --enable-warnings and --enable-io-failure-emulation, each followed by a format/check/inspect smoke test on a real image. Verified: ./autogen.sh && ./configure --enable-warnings && make is clean from scratch (0 warnings, 51 compile/link steps). mkreiserfs, reiserfsck --check, debugreiserfs -S, reiserfstune -l and a real resize_reiserfs 128M -> 256M all succeed, and fsck reports the resized image clean.
1638 lines
42 KiB
C
1638 lines
42 KiB
C
/*
|
|
* Copyright 2000-2004 by Hans Reiser, licensing governed by
|
|
* reiserfsprogs/README
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "includes.h"
|
|
#include <linux/kdev_t.h>
|
|
#include <time.h>
|
|
|
|
struct reiserfs_key root_dir_key = { 0, 0, {{0, 0},} };
|
|
struct reiserfs_key parent_root_dir_key = { 0, 0, {{0, 0},} };
|
|
struct reiserfs_key lost_found_dir_key = { 0, 0, {{0, 0},} };
|
|
static struct reiserfs_key badblock_key =
|
|
{ constant_cpu_to_le32(BADBLOCK_DIRID),
|
|
constant_cpu_to_le32(BADBLOCK_OBJID),
|
|
{{constant_cpu_to_le32(0), constant_cpu_to_le32(0)},} };
|
|
|
|
__u16 root_dir_format = 0;
|
|
__u16 lost_found_dir_format = 0;
|
|
|
|
static void make_const_keys(void)
|
|
{
|
|
set_key_dirid(&root_dir_key, REISERFS_ROOT_PARENT_OBJECTID);
|
|
set_key_objectid(&root_dir_key, REISERFS_ROOT_OBJECTID);
|
|
|
|
set_key_dirid(&parent_root_dir_key, 0);
|
|
set_key_objectid(&parent_root_dir_key, REISERFS_ROOT_PARENT_OBJECTID);
|
|
}
|
|
|
|
/* reiserfs needs at least: enough blocks for journal, 64 k at the beginning,
|
|
one block for super block, bitmap block and root block. Note that first
|
|
bitmap block must point to all of them */
|
|
int is_block_count_correct(unsigned long journal_offset,
|
|
unsigned int block_size, unsigned long block_count,
|
|
unsigned long journal_size)
|
|
{
|
|
unsigned long blocks;
|
|
|
|
/* RESERVED, MD RAID SBs, super block, bitmap, root, journal size with journal header */
|
|
blocks = journal_offset + journal_size;
|
|
|
|
/* we have a limit: skipped area, super block, journal and root block
|
|
all have to be addressed by one first bitmap */
|
|
if (blocks > block_size * 8)
|
|
return 0;
|
|
|
|
if (blocks > block_count)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* read super block. fixme: only 4k blocks, pre-journaled format
|
|
is refused. Journal and bitmap are to be opened separately.
|
|
skip_check is set to 1 if checks of openned SB should be omitted.*/
|
|
reiserfs_filsys_t reiserfs_open(const char *filename, int flags,
|
|
long *error, void *vp, int check)
|
|
{
|
|
reiserfs_filsys_t fs;
|
|
struct buffer_head *bh;
|
|
struct reiserfs_super_block *sb;
|
|
int fd;
|
|
unsigned int i;
|
|
|
|
/* convert root dir key and parent root dir key to little endian format */
|
|
make_const_keys();
|
|
|
|
*error = 0;
|
|
|
|
fd = open(filename, flags
|
|
#if defined(O_LARGEFILE)
|
|
| O_LARGEFILE
|
|
#endif
|
|
);
|
|
if (fd == -1) {
|
|
*error = errno;
|
|
return NULL;
|
|
}
|
|
|
|
fs = getmem(sizeof(*fs));
|
|
fs->fs_dev = fd;
|
|
fs->fs_vp = vp;
|
|
xasprintf(&fs->fs_file_name, "%s", filename);
|
|
|
|
/* reiserfs super block is either in 16-th or in 2-nd 4k block of the
|
|
device */
|
|
for (i = 2; i < 17; i += 14) {
|
|
bh = bread(fd, i, 4096);
|
|
if (!bh) {
|
|
*error = REISERFS_ET_BREAD_FAILED;
|
|
} else {
|
|
sb = (struct reiserfs_super_block *)bh->b_data;
|
|
|
|
if (is_any_reiserfs_magic_string(sb))
|
|
goto found;
|
|
|
|
/* reiserfs signature is not found at the i-th 4k block */
|
|
brelse(bh);
|
|
}
|
|
}
|
|
|
|
*error = REISERFS_ET_BAD_MAGIC;
|
|
|
|
freemem(fs);
|
|
close(fd);
|
|
fs = NULL;
|
|
return fs;
|
|
|
|
found:
|
|
|
|
if (!is_blocksize_correct(get_sb_block_size(sb))) {
|
|
*error = REISERFS_ET_BAD_SUPER;
|
|
freemem(fs);
|
|
close(fd);
|
|
brelse(bh);
|
|
return NULL;
|
|
}
|
|
|
|
if (check) {
|
|
/* A few checks of found super block. */
|
|
struct buffer_head *tmp_bh;
|
|
|
|
tmp_bh =
|
|
bread(fd, get_sb_block_count(sb) - 1,
|
|
get_sb_block_size(sb));
|
|
|
|
if (!tmp_bh) {
|
|
*error = REISERFS_ET_SMALL_PARTITION;
|
|
freemem(fs);
|
|
close(fd);
|
|
brelse(bh);
|
|
return NULL;
|
|
}
|
|
|
|
brelse(tmp_bh);
|
|
}
|
|
|
|
fs->fs_blocksize = get_sb_block_size(sb);
|
|
|
|
/* check block size on the filesystem */
|
|
if (fs->fs_blocksize != 4096) {
|
|
i = bh->b_blocknr * 4096 / fs->fs_blocksize;
|
|
brelse(bh);
|
|
bh = bread(fd, i, fs->fs_blocksize);
|
|
if (!bh) {
|
|
*error = REISERFS_ET_BREAD_FAILED;
|
|
freemem(fs);
|
|
return NULL;
|
|
}
|
|
sb = (struct reiserfs_super_block *)bh->b_data;
|
|
}
|
|
|
|
fs->fs_hash_function = code2func(get_sb_hash_code(sb));
|
|
fs->fs_super_bh = bh;
|
|
fs->fs_ondisk_sb = sb;
|
|
fs->fs_flags = flags; /* O_RDONLY or O_RDWR */
|
|
|
|
fs->fs_format = get_reiserfs_format(sb);
|
|
|
|
/*reiserfs_read_bitmap_blocks(fs); */
|
|
if (flags & O_RDWR)
|
|
fs->fs_dirt = 1;
|
|
else
|
|
fs->fs_dirt = 0;
|
|
|
|
return fs;
|
|
}
|
|
|
|
/* creates buffer for super block and fills it up with fields which are
|
|
constant for given size and version of a filesystem */
|
|
reiserfs_filsys_t reiserfs_create(const char *filename,
|
|
int version,
|
|
unsigned long block_count,
|
|
int block_size,
|
|
int default_journal, int new_format,
|
|
long *error)
|
|
{
|
|
reiserfs_filsys_t fs;
|
|
time_t now;
|
|
unsigned int bmap_nr = reiserfs_bmap_nr(block_count, block_size);;
|
|
|
|
*error = 0;
|
|
|
|
/* convert root dir key and parent root dir key to little endian format */
|
|
make_const_keys();
|
|
|
|
if (count_blocks(filename, block_size) < block_count) {
|
|
*error = REISERFS_ET_NOT_ENOUGH_BLOCKS;
|
|
return NULL;
|
|
}
|
|
|
|
if (!is_block_count_correct(REISERFS_DISK_OFFSET_IN_BYTES / block_size,
|
|
block_size, block_count, 0)) {
|
|
*error = REISERFS_ET_TOO_SMALL;
|
|
return NULL;
|
|
}
|
|
|
|
fs = getmem(sizeof(*fs));
|
|
if (!fs) {
|
|
*error = errno;
|
|
return NULL;
|
|
}
|
|
|
|
fs->fs_dev = open(filename, O_RDWR | O_EXCL
|
|
#if defined(O_LARGEFILE)
|
|
| O_LARGEFILE
|
|
#endif
|
|
);
|
|
if (fs->fs_dev == -1) {
|
|
*error = errno;
|
|
freemem(fs);
|
|
return NULL;
|
|
}
|
|
|
|
fs->fs_blocksize = block_size;
|
|
xasprintf(&fs->fs_file_name, "%s", filename);
|
|
fs->fs_format = version;
|
|
|
|
if (new_format)
|
|
fs->fs_super_bh = getblk(fs->fs_dev,
|
|
REISERFS_DISK_OFFSET_IN_BYTES /
|
|
block_size, block_size);
|
|
else
|
|
fs->fs_super_bh = getblk(fs->fs_dev,
|
|
REISERFS_OLD_DISK_OFFSET_IN_BYTES /
|
|
block_size, block_size);
|
|
|
|
if (!fs->fs_super_bh) {
|
|
*error = REISERFS_ET_GETBLK_FAILED;
|
|
return NULL;
|
|
}
|
|
|
|
mark_buffer_uptodate(fs->fs_super_bh, 1);
|
|
|
|
fs->fs_ondisk_sb =
|
|
(struct reiserfs_super_block *)fs->fs_super_bh->b_data;
|
|
memset(fs->fs_ondisk_sb, 0, block_size);
|
|
|
|
/* fill super block fields which are constant for given version and block count */
|
|
set_sb_block_count(fs->fs_ondisk_sb, block_count);
|
|
/* sb_free_blocks */
|
|
/* sb_root_block */
|
|
/* sb_journal_1st_block */
|
|
/* sb_journal_dev */
|
|
/* sb_orig_journal_size */
|
|
/* sb_joural_magic */
|
|
/* sb_journal magic_F */
|
|
/* sb_mount_id */
|
|
/* sb_not_used0 */
|
|
/* sb_generation_number */
|
|
set_sb_block_size(fs->fs_ondisk_sb, block_size);
|
|
switch (version) {
|
|
case REISERFS_FORMAT_3_5:
|
|
set_sb_oid_maxsize(fs->fs_ondisk_sb,
|
|
(block_size -
|
|
SB_SIZE_V1) / sizeof(__u32) / 2 * 2);
|
|
/* sb_oid_cursize */
|
|
/* sb_state */
|
|
memcpy(fs->fs_ondisk_sb->s_v1.s_magic,
|
|
REISERFS_3_5_SUPER_MAGIC_STRING,
|
|
strlen(REISERFS_3_5_SUPER_MAGIC_STRING));
|
|
break;
|
|
|
|
case REISERFS_FORMAT_3_6:
|
|
set_sb_oid_maxsize(fs->fs_ondisk_sb,
|
|
(block_size -
|
|
SB_SIZE) / sizeof(__u32) / 2 * 2);
|
|
/* sb_oid_cursize */
|
|
/* sb_state */
|
|
memcpy(fs->fs_ondisk_sb->s_v1.s_magic,
|
|
REISERFS_3_6_SUPER_MAGIC_STRING,
|
|
strlen(REISERFS_3_6_SUPER_MAGIC_STRING));
|
|
break;
|
|
}
|
|
if (!default_journal)
|
|
memcpy(fs->fs_ondisk_sb->s_v1.s_magic,
|
|
REISERFS_JR_SUPER_MAGIC_STRING,
|
|
strlen(REISERFS_JR_SUPER_MAGIC_STRING));
|
|
|
|
/* sb_fsck_state */
|
|
/* sb_hash_function_code */
|
|
/* sb_tree_height */
|
|
|
|
set_sb_bmap_nr(fs->fs_ondisk_sb,
|
|
reiserfs_bmap_over(bmap_nr) ? 0 : bmap_nr);
|
|
|
|
set_sb_version(fs->fs_ondisk_sb, version);
|
|
set_sb_v2_lastcheck(fs->fs_ondisk_sb, time(&now));
|
|
set_sb_v2_check_interval(fs->fs_ondisk_sb, DEFAULT_CHECK_INTERVAL);
|
|
set_sb_v2_mnt_count(fs->fs_ondisk_sb, 1);
|
|
set_sb_v2_max_mnt_count(fs->fs_ondisk_sb, DEFAULT_MAX_MNT_COUNT);
|
|
|
|
/* sb_not_used1 */
|
|
|
|
mark_buffer_dirty(fs->fs_super_bh);
|
|
fs->fs_dirt = 1;
|
|
|
|
return fs;
|
|
}
|
|
|
|
int no_reiserfs_found(reiserfs_filsys_t fs)
|
|
{
|
|
return (fs == NULL || fs->fs_blocksize == 0) ? 1 : 0;
|
|
}
|
|
|
|
int spread_bitmaps(reiserfs_filsys_t fs)
|
|
{
|
|
return fs->fs_super_bh->b_blocknr != 2;
|
|
}
|
|
|
|
/* 0 means: do not guarantee that fs is consistent */
|
|
int reiserfs_is_fs_consistent(reiserfs_filsys_t fs)
|
|
{
|
|
if (get_sb_umount_state(fs->fs_ondisk_sb) == FS_CLEANLY_UMOUNTED &&
|
|
get_sb_fs_state(fs->fs_ondisk_sb) == FS_CONSISTENT)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* flush bitmap, brelse super block, flush all dirty buffers, close and open
|
|
again the device, read super block */
|
|
static void reiserfs_only_reopen(reiserfs_filsys_t fs, int flag)
|
|
{
|
|
unsigned long super_block;
|
|
|
|
/* reiserfs_flush_to_ondisk_bitmap (fs->fs_bitmap2, fs); */
|
|
super_block = fs->fs_super_bh->b_blocknr;
|
|
brelse(fs->fs_super_bh);
|
|
flush_buffers(fs->fs_dev);
|
|
|
|
invalidate_buffers(fs->fs_dev);
|
|
if (close(fs->fs_dev))
|
|
die("reiserfs_reopen: closed failed: %s", strerror(errno));
|
|
|
|
fs->fs_dev = open(fs->fs_file_name, flag
|
|
#if defined(O_LARGEFILE)
|
|
| O_LARGEFILE
|
|
#endif
|
|
);
|
|
if (fs->fs_dev == -1)
|
|
die("reiserfs_reopen: could not reopen device: %s",
|
|
strerror(errno));
|
|
|
|
fs->fs_super_bh = bread(fs->fs_dev, super_block, fs->fs_blocksize);
|
|
if (!fs->fs_super_bh)
|
|
die("reiserfs_reopen: reading super block failed");
|
|
fs->fs_ondisk_sb =
|
|
(struct reiserfs_super_block *)fs->fs_super_bh->b_data;
|
|
fs->fs_flags = flag; /* O_RDONLY or O_RDWR */
|
|
|
|
if (flag & O_RDWR)
|
|
fs->fs_dirt = 1;
|
|
else
|
|
fs->fs_dirt = 0;
|
|
}
|
|
|
|
void reiserfs_reopen(reiserfs_filsys_t fs, int flag)
|
|
{
|
|
reiserfs_only_reopen(fs, flag);
|
|
reiserfs_reopen_journal(fs, flag);
|
|
}
|
|
|
|
int is_opened_rw(reiserfs_filsys_t fs)
|
|
{
|
|
if ((fs->fs_flags) & O_RDWR)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* flush all changes made on a filesystem */
|
|
void reiserfs_flush(reiserfs_filsys_t fs)
|
|
{
|
|
if (fs->fs_dirt) {
|
|
reiserfs_flush_journal(fs);
|
|
flush_buffers(fs->fs_dev);
|
|
}
|
|
fs->fs_dirt = 0;
|
|
}
|
|
|
|
/* free all memory involved into manipulating with filesystem */
|
|
void reiserfs_free(reiserfs_filsys_t fs)
|
|
{
|
|
reiserfs_free_journal(fs);
|
|
reiserfs_free_ondisk_bitmap(fs);
|
|
|
|
/* release super block and memory used by filesystem handler */
|
|
brelse(fs->fs_super_bh);
|
|
fs->fs_super_bh = NULL;
|
|
|
|
free_buffers();
|
|
|
|
free(fs->fs_file_name);
|
|
fs->fs_file_name = NULL;
|
|
freemem(fs);
|
|
}
|
|
|
|
/* this closes everything: journal. bitmap and the fs itself */
|
|
void reiserfs_close(reiserfs_filsys_t fs)
|
|
{
|
|
reiserfs_close_journal(fs);
|
|
reiserfs_close_ondisk_bitmap(fs);
|
|
|
|
reiserfs_flush(fs);
|
|
reiserfs_free(fs);
|
|
fsync(fs->fs_dev);
|
|
}
|
|
|
|
int reiserfs_new_blocknrs(reiserfs_filsys_t fs,
|
|
unsigned long *free_blocknrs,
|
|
unsigned long start, int amount_needed)
|
|
{
|
|
if (fs->block_allocator)
|
|
return fs->block_allocator(fs, free_blocknrs, start,
|
|
amount_needed);
|
|
die("block allocator is not defined\n");
|
|
return 0;
|
|
}
|
|
|
|
int reiserfs_free_block(reiserfs_filsys_t fs, unsigned long block)
|
|
{
|
|
if (fs->block_deallocator)
|
|
return fs->block_deallocator(fs, block);
|
|
die("block deallocator is not defined\n");
|
|
return 0;
|
|
}
|
|
|
|
static int reiserfs_search_by_key_x(reiserfs_filsys_t fs,
|
|
const struct reiserfs_key *key,
|
|
struct reiserfs_path *path, int key_length)
|
|
{
|
|
struct buffer_head *bh;
|
|
unsigned long block;
|
|
struct reiserfs_path_element *curr;
|
|
int retval;
|
|
|
|
block = get_sb_root_block(fs->fs_ondisk_sb);
|
|
if (not_data_block(fs, block))
|
|
return IO_ERROR;
|
|
|
|
path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
while (1) {
|
|
curr = PATH_OFFSET_PELEMENT(path, ++path->path_length);
|
|
bh = curr->pe_buffer =
|
|
bread(fs->fs_dev, block, fs->fs_blocksize);
|
|
if (bh == NULL) {
|
|
path->path_length--;
|
|
pathrelse(path);
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
retval =
|
|
reiserfs_bin_search(key, leaf_key(bh, 0), B_NR_ITEMS(bh),
|
|
is_leaf_node(bh) ? IH_SIZE : KEY_SIZE,
|
|
&curr->pe_position,
|
|
key_length ==
|
|
4 ? comp_keys : comp_keys_3);
|
|
if (retval == POSITION_FOUND) {
|
|
/* key found, return if this is leaf level */
|
|
if (is_leaf_node(bh)) {
|
|
path->pos_in_item = 0;
|
|
return ITEM_FOUND;
|
|
}
|
|
curr->pe_position++;
|
|
} else {
|
|
/* key not found in the node */
|
|
if (is_leaf_node(bh))
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
block = get_dc_child_blocknr(B_N_CHILD(bh, curr->pe_position));
|
|
if (not_data_block(fs, block))
|
|
return IO_ERROR;
|
|
}
|
|
printf("search_by_key: you can not get here\n");
|
|
return ITEM_NOT_FOUND;
|
|
}
|
|
|
|
int reiserfs_search_by_key_3(reiserfs_filsys_t fs, const struct reiserfs_key *key,
|
|
struct reiserfs_path *path)
|
|
{
|
|
return reiserfs_search_by_key_x(fs, key, path, 3);
|
|
}
|
|
|
|
int reiserfs_search_by_key_4(reiserfs_filsys_t fs, const struct reiserfs_key *key,
|
|
struct reiserfs_path *path)
|
|
{
|
|
return reiserfs_search_by_key_x(fs, key, path, 4);
|
|
}
|
|
|
|
/* key is key of byte in the regular file. This searches in tree
|
|
through items and in the found item as well */
|
|
int reiserfs_search_by_position(reiserfs_filsys_t s, struct reiserfs_key *key,
|
|
int version, struct reiserfs_path *path)
|
|
{
|
|
struct buffer_head *bh;
|
|
struct item_head *ih;
|
|
struct reiserfs_key *next_key;
|
|
|
|
if (reiserfs_search_by_key_3(s, key, path) == ITEM_FOUND) {
|
|
ih = tp_item_head(path);
|
|
|
|
if (!is_direct_ih(ih) && !is_indirect_ih(ih))
|
|
return DIRECTORY_FOUND;
|
|
|
|
path->pos_in_item = 0;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
bh = get_bh(path);
|
|
ih = tp_item_head(path);
|
|
|
|
if (PATH_LAST_POSITION(path) == 0) {
|
|
/* previous item does not exist, that means we are in leftmost leaf of
|
|
* the tree */
|
|
if (!not_of_one_file(&ih->ih_key, key)) {
|
|
if (!is_direct_ih(ih) && !is_indirect_ih(ih))
|
|
return DIRECTORY_FOUND;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
/* take previous item */
|
|
PATH_LAST_POSITION(path)--;
|
|
ih--;
|
|
|
|
if (not_of_one_file(&ih->ih_key, key) || is_stat_data_ih(ih)) {
|
|
/* previous item belongs to another object or is a stat data, check
|
|
* next item */
|
|
PATH_LAST_POSITION(path)++;
|
|
if (PATH_LAST_POSITION(path) < B_NR_ITEMS(bh))
|
|
/* next key is in the same node */
|
|
next_key = leaf_key(bh, PATH_LAST_POSITION(path));
|
|
else
|
|
next_key = uget_rkey(path);
|
|
if (next_key == NULL || not_of_one_file(next_key, key)) {
|
|
/* there is no any part of such file in the tree */
|
|
path->pos_in_item = 0;
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
if (is_direntry_key(next_key)) {
|
|
reiserfs_warning(stderr,
|
|
"%s: looking for %k found a directory with the same key\n",
|
|
__func__, next_key);
|
|
return DIRECTORY_FOUND;
|
|
}
|
|
|
|
/* next item is the part of this file */
|
|
path->pos_in_item = 0;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
if (is_direntry_ih(ih)) {
|
|
return DIRECTORY_FOUND;
|
|
}
|
|
|
|
if (is_stat_data_ih(ih)) {
|
|
PATH_LAST_POSITION(path)++;
|
|
return FILE_NOT_FOUND;
|
|
}
|
|
|
|
/* previous item is part of desired file */
|
|
if (I_K_KEY_IN_ITEM(ih, key, bh->b_size)) {
|
|
path->pos_in_item = get_offset(key) - get_offset(&ih->ih_key);
|
|
if (is_indirect_ih(ih))
|
|
path->pos_in_item /= bh->b_size;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
path->pos_in_item =
|
|
is_indirect_ih(ih) ? I_UNFM_NUM(ih) : get_ih_item_len(ih);
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
static int comp_dir_entries(const void *p1, const void *p2)
|
|
{
|
|
__u32 off1, off2;
|
|
|
|
off1 = d32_get((__le32 *) p1, 0);
|
|
off2 = *(__u32 *) p2;
|
|
|
|
if (off1 < off2)
|
|
return -1;
|
|
if (off1 > off2)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
struct reiserfs_key *uget_lkey(const struct reiserfs_path *path)
|
|
{
|
|
int pos, offset = path->path_length;
|
|
const struct buffer_head *bh;
|
|
|
|
if (offset < FIRST_PATH_ELEMENT_OFFSET)
|
|
die("uget_lkey: illegal offset in the path (%d)", offset);
|
|
|
|
/* While not higher in path than first element. */
|
|
while (offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
if (!buffer_uptodate(PATH_OFFSET_PBUFFER(path, offset)))
|
|
die("uget_lkey: parent is not uptodate");
|
|
|
|
/* Parent at the path is not in the tree now. */
|
|
if (!B_IS_IN_TREE(bh = PATH_OFFSET_PBUFFER(path, offset)))
|
|
die("uget_lkey: buffer on the path is not in tree");
|
|
|
|
/* Check whether position in the parent is correct. */
|
|
if ((pos = PATH_OFFSET_POSITION(path, offset)) > B_NR_ITEMS(bh))
|
|
die("uget_lkey: invalid position (%d) in the path",
|
|
pos);
|
|
|
|
/* Check whether parent at the path really points to the child. */
|
|
if (get_dc_child_blocknr(B_N_CHILD(bh, pos)) !=
|
|
PATH_OFFSET_PBUFFER(path, offset + 1)->b_blocknr)
|
|
die("uget_lkey: invalid block number (%d). Must be %ld",
|
|
get_dc_child_blocknr(B_N_CHILD(bh, pos)),
|
|
PATH_OFFSET_PBUFFER(path, offset + 1)->b_blocknr);
|
|
|
|
/* Return delimiting key if position in the parent is not equal to zero. */
|
|
if (pos)
|
|
return internal_key(bh, pos - 1);
|
|
}
|
|
|
|
/* there is no left delimiting key */
|
|
return NULL;
|
|
}
|
|
|
|
struct reiserfs_key *uget_rkey(const struct reiserfs_path *path)
|
|
{
|
|
int pos, offset = path->path_length;
|
|
struct buffer_head *bh;
|
|
|
|
if (offset < FIRST_PATH_ELEMENT_OFFSET)
|
|
die("uget_rkey: illegal offset in the path (%d)", offset);
|
|
|
|
while (offset-- > FIRST_PATH_ELEMENT_OFFSET) {
|
|
if (!buffer_uptodate(PATH_OFFSET_PBUFFER(path, offset)))
|
|
die("uget_rkey: parent is not uptodate");
|
|
|
|
/* Parent at the path is not in the tree now. */
|
|
if (!B_IS_IN_TREE(bh = PATH_OFFSET_PBUFFER(path, offset)))
|
|
die("uget_rkey: buffer on the path is not in tree");
|
|
|
|
/* Check whether position in the parrent is correct. */
|
|
if ((pos = PATH_OFFSET_POSITION(path, offset)) > B_NR_ITEMS(bh))
|
|
die("uget_rkey: invalid position (%d) in the path",
|
|
pos);
|
|
|
|
/* Check whether parent at the path really points to the child. */
|
|
if (get_dc_child_blocknr(B_N_CHILD(bh, pos)) !=
|
|
PATH_OFFSET_PBUFFER(path, offset + 1)->b_blocknr)
|
|
die("uget_rkey: invalid block number (%d). Must be %ld",
|
|
get_dc_child_blocknr(B_N_CHILD(bh, pos)),
|
|
PATH_OFFSET_PBUFFER(path, offset + 1)->b_blocknr);
|
|
|
|
/* Return delimiting key if position in the parent is not the last one. */
|
|
if (pos != B_NR_ITEMS(bh))
|
|
return internal_key(bh, pos);
|
|
}
|
|
|
|
/* there is no right delimiting key */
|
|
return NULL;
|
|
}
|
|
|
|
struct reiserfs_key *reiserfs_next_key(const struct reiserfs_path *path)
|
|
{
|
|
if (get_item_pos(path) + 1 < B_NR_ITEMS(get_bh(path)))
|
|
return leaf_key(get_bh(path), get_item_pos(path) + 1);
|
|
|
|
return uget_rkey(path);
|
|
}
|
|
|
|
/* NOTE: this only should be used to look for keys who exists */
|
|
int reiserfs_search_by_entry_key(reiserfs_filsys_t fs,
|
|
const struct reiserfs_key *key,
|
|
struct reiserfs_path *path)
|
|
{
|
|
struct buffer_head *bh;
|
|
int item_pos;
|
|
struct item_head *ih;
|
|
struct reiserfs_key tmpkey;
|
|
__u32 offset;
|
|
|
|
if (reiserfs_search_by_key_4(fs, key, path) == ITEM_FOUND) {
|
|
path->pos_in_item = 0;
|
|
return POSITION_FOUND;
|
|
}
|
|
|
|
bh = get_bh(path);
|
|
item_pos = get_item_pos(path);
|
|
ih = tp_item_head(path);
|
|
|
|
if (item_pos == 0) {
|
|
/* key is less than the smallest key in the tree */
|
|
if (not_of_one_file(&(ih->ih_key), key))
|
|
/* there are no items of that directory */
|
|
return DIRECTORY_NOT_FOUND;
|
|
|
|
if (!is_direntry_ih(ih)) {
|
|
reiserfs_panic
|
|
("reiserfs_search_by_entry_key: found item "
|
|
"is not of directory type %H", ih);
|
|
}
|
|
|
|
/* key we looked for should be here */
|
|
path->pos_in_item = 0;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
/* take previous item */
|
|
item_pos--;
|
|
ih--;
|
|
PATH_LAST_POSITION(path)--;
|
|
|
|
if (not_of_one_file(&(ih->ih_key), key) || !is_direntry_ih(ih)) {
|
|
/* previous item belongs to another object or is stat data, check next
|
|
item */
|
|
|
|
item_pos++;
|
|
PATH_LAST_POSITION(path)++;
|
|
|
|
if (item_pos < B_NR_ITEMS(bh)) {
|
|
/* next item is in the same node */
|
|
ih++;
|
|
if (not_of_one_file(&(ih->ih_key), key)) {
|
|
/* there are no items of that directory */
|
|
path->pos_in_item = 0;
|
|
return DIRECTORY_NOT_FOUND;
|
|
}
|
|
|
|
if (!is_direntry_ih(ih))
|
|
reiserfs_panic
|
|
("_search_by_entry_key: %k is not a directory",
|
|
key);
|
|
} else {
|
|
/* next item is in right neighboring node */
|
|
struct reiserfs_key *next_key = uget_rkey(path);
|
|
|
|
if (next_key == NULL ||
|
|
not_of_one_file(next_key, key)) {
|
|
/* there are no items of that directory */
|
|
path->pos_in_item = 0;
|
|
return DIRECTORY_NOT_FOUND;
|
|
}
|
|
|
|
if (!is_direntry_key(next_key))
|
|
reiserfs_panic
|
|
("_search_by_entry_key: %k is not a directory",
|
|
key);
|
|
|
|
/* we got right delimiting key - search for it - the entry will be
|
|
pasted in position 0 */
|
|
copy_key(&tmpkey, next_key);
|
|
pathrelse(path);
|
|
if (reiserfs_search_by_key_4(fs, &tmpkey, path) !=
|
|
ITEM_FOUND || PATH_LAST_POSITION(path) != 0)
|
|
reiserfs_panic
|
|
("_search_by_entry_key: item corresponding to delimiting key %k not found",
|
|
&tmpkey);
|
|
}
|
|
|
|
/* next item is the part of this directory */
|
|
path->pos_in_item = 0;
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
/* previous item is part of desired directory */
|
|
offset = get_key_offset_v1(key);
|
|
if (reiserfs_bin_search
|
|
(&offset, B_I_DEH(bh, ih), get_ih_entry_count(ih), DEH_SIZE,
|
|
&(path->pos_in_item), comp_dir_entries) == POSITION_FOUND)
|
|
return POSITION_FOUND;
|
|
|
|
return POSITION_NOT_FOUND;
|
|
}
|
|
|
|
void init_tb_struct(struct tree_balance *tb, reiserfs_filsys_t fs,
|
|
struct reiserfs_path *path, int size)
|
|
{
|
|
memset(tb, '\0', sizeof(struct tree_balance));
|
|
tb->tb_fs = fs;
|
|
tb->tb_path = path;
|
|
|
|
PATH_OFFSET_PBUFFER(path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL;
|
|
PATH_OFFSET_POSITION(path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0;
|
|
tb->insert_size[0] = size;
|
|
}
|
|
|
|
int reiserfs_remove_entry(reiserfs_filsys_t fs, const struct reiserfs_key *key)
|
|
{
|
|
struct reiserfs_path path;
|
|
struct tree_balance tb;
|
|
struct item_head *ih;
|
|
struct reiserfs_de_head *deh;
|
|
|
|
if (reiserfs_search_by_entry_key(fs, key, &path) != POSITION_FOUND) {
|
|
pathrelse(&path);
|
|
return 1;
|
|
}
|
|
|
|
ih = tp_item_head(&path);
|
|
if (get_ih_entry_count(ih) == 1) {
|
|
init_tb_struct(&tb, fs, &path,
|
|
-(IH_SIZE + get_ih_item_len(ih)));
|
|
if (fix_nodes(M_DELETE, &tb, NULL) != CARRY_ON) {
|
|
unfix_nodes(&tb);
|
|
return 1;
|
|
}
|
|
do_balance(&tb, NULL, NULL, M_DELETE, 0);
|
|
return 0;
|
|
}
|
|
|
|
deh = B_I_DEH(get_bh(&path), ih) + path.pos_in_item;
|
|
init_tb_struct(&tb, fs, &path,
|
|
-(DEH_SIZE + entry_length(ih, deh, path.pos_in_item)));
|
|
if (fix_nodes(M_CUT, &tb, NULL) != CARRY_ON) {
|
|
unfix_nodes(&tb);
|
|
return 1;
|
|
}
|
|
do_balance(&tb, NULL, NULL, M_CUT, 0);
|
|
return 0;
|
|
}
|
|
|
|
void reiserfs_paste_into_item(reiserfs_filsys_t fs,
|
|
struct reiserfs_path *path, const void *body,
|
|
int size)
|
|
{
|
|
struct tree_balance tb;
|
|
|
|
init_tb_struct(&tb, fs, path, size);
|
|
|
|
if (fix_nodes(M_PASTE, &tb, NULL) != CARRY_ON)
|
|
reiserfs_panic("reiserfs_paste_into_item: fix_nodes failed");
|
|
|
|
do_balance(&tb, NULL, body, M_PASTE, 0 /* zero num */ );
|
|
}
|
|
|
|
void reiserfs_insert_item(reiserfs_filsys_t fs, struct reiserfs_path *path,
|
|
struct item_head *ih, const void *body)
|
|
{
|
|
struct tree_balance tb;
|
|
|
|
init_tb_struct(&tb, fs, path, IH_SIZE + get_ih_item_len(ih));
|
|
if (fix_nodes(M_INSERT, &tb, ih) != CARRY_ON)
|
|
die("reiserfs_insert_item: fix_nodes failed");
|
|
|
|
do_balance(&tb, ih, body, M_INSERT, 0 /*zero num */ );
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
|
|
__u32 hash_value(hashf_t func, const char *name, int namelen)
|
|
{
|
|
__u32 res;
|
|
|
|
res = func(name, namelen);
|
|
res = GET_HASH_VALUE(res);
|
|
if (res == 0)
|
|
res = 128;
|
|
|
|
return res;
|
|
}
|
|
|
|
/* if name is found in a directory - return 1 and set path to the name,
|
|
otherwise return 0 and pathrelse path */
|
|
int reiserfs_locate_entry(reiserfs_filsys_t fs, struct reiserfs_key *dir,
|
|
const char *name, struct reiserfs_path *path)
|
|
{
|
|
struct reiserfs_key entry_key;
|
|
struct item_head *ih;
|
|
struct reiserfs_de_head *deh;
|
|
int i, retval;
|
|
struct reiserfs_key *rdkey;
|
|
|
|
set_key_dirid(&entry_key, get_key_dirid(dir));
|
|
set_key_objectid(&entry_key, get_key_objectid(dir));
|
|
set_key_offset_v1(&entry_key, 0);
|
|
set_key_uniqueness(&entry_key, DIRENTRY_UNIQUENESS);
|
|
|
|
if (reiserfs_search_by_entry_key(fs, &entry_key, path) ==
|
|
DIRECTORY_NOT_FOUND) {
|
|
pathrelse(path);
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
ih = tp_item_head(path);
|
|
deh = B_I_DEH(get_bh(path), ih) + path->pos_in_item;
|
|
for (i = path->pos_in_item; i < get_ih_entry_count(ih);
|
|
i++, deh++) {
|
|
/* the name in directory has the same hash as the given name */
|
|
if ((name_in_entry_length(ih, deh, i) ==
|
|
(int)strlen(name))
|
|
&& !memcmp(name_in_entry(deh, i), name,
|
|
strlen(name))) {
|
|
path->pos_in_item = i;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
rdkey = uget_rkey(path);
|
|
if (!rdkey || not_of_one_file(rdkey, dir)) {
|
|
pathrelse(path);
|
|
return 0;
|
|
}
|
|
|
|
if (!is_direntry_key(rdkey))
|
|
reiserfs_panic
|
|
("reiserfs_locate_entry: can not find name in broken directory yet");
|
|
|
|
/* first name of that item may be a name we are looking for */
|
|
entry_key = *rdkey;
|
|
pathrelse(path);
|
|
retval = reiserfs_search_by_entry_key(fs, &entry_key, path);
|
|
if (retval != POSITION_FOUND)
|
|
reiserfs_panic
|
|
("reiserfs_locate_entry: wrong delimiting key in the tree");
|
|
|
|
} while (1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* returns 0 if name is not found in a directory and 1 if name is
|
|
found. Stores key found in the entry in 'key'. Returns minimal not used
|
|
generation counter in 'min_gen_counter'. dies if found object is not a
|
|
directory. */
|
|
int reiserfs_find_entry(reiserfs_filsys_t fs, const struct reiserfs_key *dir,
|
|
const char *name, unsigned int *min_gen_counter,
|
|
struct reiserfs_key *key)
|
|
{
|
|
struct reiserfs_key entry_key;
|
|
int retval;
|
|
int i;
|
|
INITIALIZE_REISERFS_PATH(path);
|
|
struct item_head *ih;
|
|
struct reiserfs_de_head *deh;
|
|
struct reiserfs_key *rdkey;
|
|
__u32 hash;
|
|
|
|
set_key_dirid(&entry_key, get_key_dirid(dir));
|
|
set_key_objectid(&entry_key, get_key_objectid(dir));
|
|
if (!strcmp(name, "."))
|
|
hash = DOT_OFFSET;
|
|
else if (!strcmp(name, ".."))
|
|
hash = DOT_DOT_OFFSET;
|
|
else
|
|
hash = hash_value(reiserfs_hash(fs), name, strlen(name));
|
|
set_key_offset_v1(&entry_key, hash);
|
|
set_key_uniqueness(&entry_key, DIRENTRY_UNIQUENESS);
|
|
|
|
*min_gen_counter = 0;
|
|
|
|
if (reiserfs_search_by_entry_key(fs, &entry_key, &path) ==
|
|
DIRECTORY_NOT_FOUND) {
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
ih = tp_item_head(&path);
|
|
deh = B_I_DEH(get_bh(&path), ih) + path.pos_in_item;
|
|
for (i = path.pos_in_item; i < get_ih_entry_count(ih);
|
|
i++, deh++) {
|
|
if (GET_HASH_VALUE(get_deh_offset(deh)) !=
|
|
GET_HASH_VALUE(hash)) {
|
|
/* all entries having the same hash were scanned */
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
if (GET_GENERATION_NUMBER(get_deh_offset(deh)) ==
|
|
*min_gen_counter)
|
|
(*min_gen_counter)++;
|
|
|
|
if ((name_in_entry_length(ih, deh, i) ==
|
|
(int)strlen(name))
|
|
&&
|
|
(!memcmp
|
|
(name_in_entry(deh, i), name, strlen(name)))) {
|
|
/* entry found in the directory */
|
|
if (key) {
|
|
memset(key, 0,
|
|
sizeof(struct reiserfs_key));
|
|
set_key_dirid(key, get_deh_dirid(deh));
|
|
set_key_objectid(key,
|
|
get_deh_objectid(deh));
|
|
}
|
|
pathrelse(&path);
|
|
return 1; //get_deh_objectid (deh) ? get_deh_objectid (deh) : 1;
|
|
}
|
|
}
|
|
|
|
rdkey = uget_rkey(&path);
|
|
if (!rdkey || not_of_one_file(rdkey, dir)) {
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
if (!is_direntry_key(rdkey))
|
|
reiserfs_panic
|
|
("reiserfs_find_entry: can not find name in broken directory yet");
|
|
|
|
/* next item is the item of the directory we are looking name in */
|
|
if (GET_HASH_VALUE(get_offset(rdkey)) != hash) {
|
|
/* but there is no names with given hash */
|
|
pathrelse(&path);
|
|
return 0;
|
|
}
|
|
|
|
/* first name of that item may be a name we are looking for */
|
|
entry_key = *rdkey;
|
|
pathrelse(&path);
|
|
retval = reiserfs_search_by_entry_key(fs, &entry_key, &path);
|
|
if (retval != POSITION_FOUND)
|
|
reiserfs_panic
|
|
("reiserfs_find_entry: wrong delimiting key in the tree");
|
|
|
|
} while (1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* compose directory entry: dir entry head and name itself */
|
|
static char *make_entry(char *entry, const char *name,
|
|
const struct reiserfs_key *key, __u32 offset)
|
|
{
|
|
struct reiserfs_de_head *deh;
|
|
__u16 state;
|
|
|
|
if (!entry)
|
|
entry = getmem(DEH_SIZE + ROUND_UP(strlen(name)));
|
|
|
|
memset(entry, 0, DEH_SIZE + ROUND_UP(strlen(name)));
|
|
deh = (struct reiserfs_de_head *)entry;
|
|
|
|
set_deh_location(deh, 0);
|
|
set_deh_offset(deh, offset);
|
|
state = (1 << DEH_Visible2);
|
|
set_deh_state(deh, state);
|
|
|
|
/* key of object entry will point to */
|
|
set_deh_dirid(deh, get_key_dirid(key));
|
|
set_deh_objectid(deh, get_key_objectid(key));
|
|
|
|
memcpy((char *)(deh + 1), name, strlen(name));
|
|
return entry;
|
|
}
|
|
|
|
/* add new name into a directory. If it exists in a directory - do
|
|
nothing */
|
|
int reiserfs_add_entry(reiserfs_filsys_t fs, const struct reiserfs_key *dir,
|
|
const char *name, int name_len,
|
|
const struct reiserfs_key *key, __u16 fsck_need)
|
|
{
|
|
struct item_head entry_ih = { 0 };
|
|
char *entry;
|
|
int retval;
|
|
INITIALIZE_REISERFS_PATH(path);
|
|
unsigned int gen_counter;
|
|
int item_len;
|
|
__u32 hash;
|
|
|
|
if (reiserfs_find_entry(fs, dir, name, &gen_counter, NULL))
|
|
/* entry is in the directory already or directory was not found */
|
|
return 0;
|
|
|
|
/* compose entry key to look for its place in the tree */
|
|
set_key_dirid(&(entry_ih.ih_key), get_key_dirid(dir));
|
|
set_key_objectid(&(entry_ih.ih_key), get_key_objectid(dir));
|
|
if (!strcmp(name, "."))
|
|
hash = DOT_OFFSET;
|
|
else if (!strcmp(name, ".."))
|
|
hash = DOT_DOT_OFFSET;
|
|
else
|
|
hash =
|
|
hash_value(reiserfs_hash(fs), name,
|
|
strlen(name)) + gen_counter;
|
|
set_key_offset_v1(&(entry_ih.ih_key), hash);
|
|
set_key_uniqueness(&(entry_ih.ih_key), DIRENTRY_UNIQUENESS);
|
|
|
|
set_ih_key_format(&entry_ih, KEY_FORMAT_1);
|
|
set_ih_entry_count(&entry_ih, 1);
|
|
|
|
item_len = DEH_SIZE + name_len;
|
|
/*
|
|
if (get_reiserfs_format (fs->fs_ondisk_sb) == REISERFS_FORMAT_3_5)
|
|
item_len = DEH_SIZE + strlen (name);
|
|
else if (get_reiserfs_format (fs->fs_ondisk_sb) == REISERFS_FORMAT_3_6)
|
|
item_len = DEH_SIZE + ROUND_UP (strlen (name));
|
|
else
|
|
reiserfs_panic ("unknown fs format");
|
|
*/
|
|
|
|
set_ih_item_len(&entry_ih, item_len);
|
|
|
|
/* fsck may need to insert item which was not reached yet */
|
|
set_ih_flags(&entry_ih, fsck_need);
|
|
|
|
entry = make_entry(NULL, name, key, get_offset(&(entry_ih.ih_key)));
|
|
|
|
retval = reiserfs_search_by_entry_key(fs, &(entry_ih.ih_key), &path);
|
|
switch (retval) {
|
|
case POSITION_NOT_FOUND:
|
|
reiserfs_paste_into_item(fs, &path, entry, item_len);
|
|
break;
|
|
|
|
case DIRECTORY_NOT_FOUND:
|
|
set_deh_location((struct reiserfs_de_head *)entry, DEH_SIZE);
|
|
reiserfs_insert_item(fs, &path, &entry_ih, entry);
|
|
break;
|
|
|
|
default:
|
|
reiserfs_panic
|
|
("reiserfs_add_entry: looking for %k (inserting name \"%s\") "
|
|
"search_by_entry_key returned %d", &(entry_ih.ih_key),
|
|
name, retval);
|
|
}
|
|
|
|
freemem(entry);
|
|
return item_len;
|
|
}
|
|
|
|
void copy_key(void *to, const void *from)
|
|
{
|
|
memcpy(to, from, KEY_SIZE);
|
|
}
|
|
|
|
void copy_short_key(void *to, const void *from)
|
|
{
|
|
memcpy(to, from, SHORT_KEY_SIZE);
|
|
}
|
|
|
|
/* inserts new or old stat data of a directory (unreachable, nlinks == 0) */
|
|
int create_dir_sd(reiserfs_filsys_t fs,
|
|
struct reiserfs_path *path, const struct reiserfs_key *key,
|
|
void (*modify_item) (struct item_head *, void *))
|
|
{
|
|
struct item_head ih;
|
|
struct stat_data sd;
|
|
int key_format;
|
|
|
|
if (fs->fs_format == REISERFS_FORMAT_3_5)
|
|
key_format = KEY_FORMAT_1;
|
|
else
|
|
key_format = KEY_FORMAT_2;
|
|
|
|
memset(&sd, 0, sizeof(sd));
|
|
make_dir_stat_data(fs->fs_blocksize, key_format, get_key_dirid(key),
|
|
get_key_objectid(key), &ih, &sd);
|
|
|
|
/* if calling user is not root set the owner of the root entry
|
|
* to the calling user */
|
|
if (getuid()) {
|
|
if (key_format == KEY_FORMAT_1) {
|
|
struct stat_data_v1 *sd_v1 = (struct stat_data_v1 *)&sd;
|
|
set_sd_v1_uid(sd_v1, getuid());
|
|
set_sd_v1_gid(sd_v1, getgid());
|
|
} else {
|
|
set_sd_v2_uid(&sd, getuid());
|
|
set_sd_v2_gid(&sd, getgid());
|
|
}
|
|
}
|
|
|
|
if (modify_item)
|
|
modify_item(&ih, &sd);
|
|
#if 0
|
|
/* set nlink count to 0 and make the item unreachable */
|
|
zero_nlink(&ih, &sd, 0);
|
|
mark_item_unreachable(&ih);
|
|
#endif
|
|
reiserfs_insert_item(fs, path, &ih, &sd);
|
|
return key_format;
|
|
}
|
|
|
|
void make_sure_root_dir_exists(reiserfs_filsys_t fs,
|
|
void (*modify_item) (struct item_head *, void *),
|
|
int ih_flags)
|
|
{
|
|
INITIALIZE_REISERFS_PATH(path);
|
|
|
|
/* is there root's stat data */
|
|
if (reiserfs_search_by_key_4(fs, &root_dir_key, &path) ==
|
|
ITEM_NOT_FOUND) {
|
|
root_dir_format =
|
|
create_dir_sd(fs, &path, &root_dir_key, modify_item);
|
|
} else {
|
|
struct item_head *ih = tp_item_head(&path);
|
|
|
|
if (!is_stat_data_ih(ih))
|
|
reiserfs_panic("It must be root's stat data %k\n",
|
|
&ih->ih_key);
|
|
|
|
root_dir_format =
|
|
(get_ih_item_len(tp_item_head(&path)) ==
|
|
SD_SIZE) ? KEY_FORMAT_2 : KEY_FORMAT_1;
|
|
pathrelse(&path);
|
|
}
|
|
|
|
/* add "." and ".." if any of them do not exist. Last two
|
|
parameters say: 0 - entry is not added on lost_found pass and 1
|
|
- mark item unreachable */
|
|
|
|
reiserfs_add_entry(fs, &root_dir_key, ".",
|
|
name_length(".", root_dir_format), &root_dir_key,
|
|
ih_flags);
|
|
reiserfs_add_entry(fs, &root_dir_key, "..",
|
|
name_length("..", root_dir_format),
|
|
&parent_root_dir_key, ih_flags);
|
|
}
|
|
|
|
/* we only can use a file for filesystem or journal if it is either not
|
|
mounted block device or regular file and we are forced to use it */
|
|
int can_we_format_it(const char *device_name, int force)
|
|
{
|
|
mode_t mode;
|
|
dev_t rdev;
|
|
|
|
if (misc_device_mounted(device_name) > 0) {
|
|
/* device looks mounted */
|
|
reiserfs_warning(stderr, "'%s' looks mounted.", device_name);
|
|
check_forcing_ask_confirmation(force);
|
|
}
|
|
|
|
mode = misc_device_mode(device_name);
|
|
rdev = misc_device_rdev(device_name);
|
|
|
|
if (!S_ISBLK(mode)) {
|
|
/* file is not a block device */
|
|
reiserfs_warning(stderr, "%s is not a block special device\n",
|
|
device_name);
|
|
check_forcing_ask_confirmation(force);
|
|
} else {
|
|
if ((IDE_DISK_MAJOR(major(rdev)) && minor(rdev) % 64 == 0) ||
|
|
(SCSI_BLK_MAJOR(major(rdev)) && minor(rdev) % 16 == 0)) {
|
|
/* /dev/hda or similar */
|
|
reiserfs_warning(stderr,
|
|
"%s is entire device, not just one partition!\n",
|
|
device_name);
|
|
check_forcing_ask_confirmation(force);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int create_badblock_bitmap(reiserfs_filsys_t fs, const char *badblocks_file)
|
|
{
|
|
FILE *fd;
|
|
char buf[128];
|
|
__u32 blocknr;
|
|
int count;
|
|
|
|
fs->fs_badblocks_bm =
|
|
reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb));
|
|
reiserfs_bitmap_zero(fs->fs_badblocks_bm);
|
|
|
|
if (!badblocks_file)
|
|
return 0;
|
|
|
|
fd = fopen(badblocks_file, "r");
|
|
|
|
if (fd == NULL) {
|
|
fprintf(stderr,
|
|
"%s: Failed to open the given badblock file '%s'.\n\n",
|
|
__FUNCTION__, badblocks_file);
|
|
return 1;
|
|
}
|
|
|
|
while (!feof(fd)) {
|
|
if (fgets(buf, sizeof(buf), fd) == NULL)
|
|
break;
|
|
count = sscanf(buf, "%u", &blocknr);
|
|
|
|
if (count <= 0)
|
|
continue;
|
|
|
|
if (blocknr >= get_sb_block_count(fs->fs_ondisk_sb)) {
|
|
fprintf(stderr,
|
|
"%s: block number (%u) points out of fs size "
|
|
"(%u).\n", __FUNCTION__, blocknr,
|
|
get_sb_block_count(fs->fs_ondisk_sb));
|
|
} else if (not_data_block(fs, blocknr)) {
|
|
fprintf(stderr,
|
|
"%s: block number (%u) belongs to system "
|
|
"reiserfs area. It cannot be relocated.\n",
|
|
__FUNCTION__, blocknr);
|
|
return 1;
|
|
} else {
|
|
reiserfs_bitmap_set_bit(fs->fs_badblocks_bm, blocknr);
|
|
}
|
|
}
|
|
|
|
fclose(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void badblock_list(reiserfs_filsys_t fs, badblock_func_t action, void *data)
|
|
{
|
|
struct reiserfs_path badblock_path;
|
|
struct reiserfs_key rd_key = badblock_key;
|
|
struct reiserfs_key *key;
|
|
|
|
badblock_path.path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
|
|
set_type_and_offset(KEY_FORMAT_2, &badblock_key, 1, TYPE_INDIRECT);
|
|
|
|
while (1) {
|
|
if (reiserfs_search_by_key_4(fs, &rd_key, &badblock_path) ==
|
|
IO_ERROR) {
|
|
fprintf(stderr,
|
|
"%s: Some problems while searching by the key "
|
|
"occured. Probably due to tree corruptions.\n",
|
|
__FUNCTION__);
|
|
pathrelse(&badblock_path);
|
|
break;
|
|
}
|
|
|
|
if (get_blkh_nr_items(B_BLK_HEAD(get_bh(&badblock_path))) <=
|
|
PATH_LAST_POSITION(&badblock_path)) {
|
|
pathrelse(&badblock_path);
|
|
break;
|
|
}
|
|
|
|
rd_key = tp_item_head(&badblock_path)->ih_key;
|
|
|
|
if (get_key_dirid(&rd_key) != BADBLOCK_DIRID ||
|
|
get_key_objectid(&rd_key) != BADBLOCK_OBJID ||
|
|
!KEY_IS_INDIRECT_KEY(&rd_key)) {
|
|
pathrelse(&badblock_path);
|
|
break;
|
|
}
|
|
|
|
if ((key = reiserfs_next_key(&badblock_path)))
|
|
rd_key = *key;
|
|
else
|
|
memset(&rd_key, 0, sizeof(rd_key));
|
|
|
|
action(fs, &badblock_path, data);
|
|
|
|
if (get_key_dirid(&rd_key) == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void callback_badblock_rm(reiserfs_filsys_t fs,
|
|
struct reiserfs_path *badblock_path,
|
|
void *data)
|
|
{
|
|
struct tree_balance tb;
|
|
struct item_head *tmp_ih;
|
|
|
|
tmp_ih = tp_item_head(badblock_path);
|
|
memset(tp_item_body(badblock_path), 0, get_ih_item_len(tmp_ih));
|
|
|
|
init_tb_struct(&tb, fs, badblock_path,
|
|
-(IH_SIZE +
|
|
get_ih_item_len(tp_item_head(badblock_path))));
|
|
|
|
if (fix_nodes(M_DELETE, &tb, NULL) != CARRY_ON)
|
|
die("%s: fix_nodes failed", __FUNCTION__);
|
|
|
|
do_balance(&tb, NULL, NULL, M_DELETE, 0 /* zero num */);
|
|
}
|
|
|
|
void mark_badblock(reiserfs_filsys_t fs,
|
|
struct reiserfs_path *badblock_path, void *data)
|
|
{
|
|
struct item_head *tmp_ih;
|
|
__le32 *ind_item;
|
|
__u32 i;
|
|
|
|
if (!fs->fs_badblocks_bm)
|
|
create_badblock_bitmap(fs, NULL);
|
|
|
|
tmp_ih = tp_item_head(badblock_path);
|
|
ind_item = (__le32 *) tp_item_body(badblock_path);
|
|
|
|
for (i = 0; i < I_UNFM_NUM(tmp_ih); i++) {
|
|
reiserfs_bitmap_set_bit(fs->fs_badblocks_bm,
|
|
d32_get(ind_item, i));
|
|
}
|
|
|
|
pathrelse(badblock_path);
|
|
}
|
|
|
|
void add_badblock_list(reiserfs_filsys_t fs, int replace)
|
|
{
|
|
struct tree_balance tb;
|
|
struct reiserfs_path badblock_path;
|
|
struct item_head badblock_ih;
|
|
__le32 ni;
|
|
|
|
__u64 offset;
|
|
__u32 i, j;
|
|
|
|
if (fs->fs_badblocks_bm == NULL)
|
|
return;
|
|
|
|
/* delete all items with badblock_key */
|
|
if (replace)
|
|
badblock_list(fs, callback_badblock_rm, NULL);
|
|
|
|
memset(&badblock_ih, 0, sizeof(badblock_ih));
|
|
set_ih_key_format(&badblock_ih, KEY_FORMAT_2);
|
|
set_ih_item_len(&badblock_ih, UNFM_P_SIZE);
|
|
set_ih_free_space(&badblock_ih, 0);
|
|
set_ih_location(&badblock_ih, 0);
|
|
set_key_dirid(&badblock_ih.ih_key, BADBLOCK_DIRID);
|
|
set_key_objectid(&badblock_ih.ih_key, BADBLOCK_OBJID);
|
|
set_type(KEY_FORMAT_2, &badblock_ih.ih_key, TYPE_INDIRECT);
|
|
|
|
j = 0;
|
|
|
|
/* insert all badblock pointers */
|
|
for (i = 0; i < fs->fs_badblocks_bm->bm_bit_size; i++) {
|
|
int retval;
|
|
|
|
if (!reiserfs_bitmap_test_bit(fs->fs_badblocks_bm, i))
|
|
continue;
|
|
|
|
offset = j * fs->fs_blocksize + 1;
|
|
set_offset(KEY_FORMAT_2, &badblock_ih.ih_key, offset);
|
|
ni = cpu_to_le32(i);
|
|
|
|
retval = reiserfs_search_by_position(fs, &badblock_ih.ih_key,
|
|
key_format(&badblock_ih.ih_key),
|
|
&badblock_path);
|
|
|
|
switch (retval) {
|
|
case (FILE_NOT_FOUND):
|
|
init_tb_struct(&tb, fs, &badblock_path,
|
|
IH_SIZE + get_ih_item_len(&badblock_ih));
|
|
|
|
if (fix_nodes(M_INSERT, &tb, &badblock_ih) != CARRY_ON)
|
|
die("reiserfsck_insert_item: fix_nodes failed");
|
|
|
|
do_balance(&tb, &badblock_ih, (void *)&ni, M_INSERT, 0);
|
|
|
|
break;
|
|
|
|
case (POSITION_NOT_FOUND):
|
|
case (POSITION_FOUND):
|
|
/* Insert the new item to the found position. */
|
|
|
|
init_tb_struct(&tb, fs, &badblock_path, UNFM_P_SIZE);
|
|
|
|
if (fix_nodes(M_PASTE, &tb, NULL) != CARRY_ON)
|
|
die("reiserfsck_paste_into_item: fix_nodes failed");
|
|
|
|
do_balance(&tb, NULL, (const char *)&ni, M_PASTE, 0);
|
|
break;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
}
|
|
|
|
int reiserfs_iterate_file_data(reiserfs_filsys_t fs,
|
|
const struct reiserfs_key *short_key,
|
|
reiserfs_file_iterate_indirect_fn indirect_fn,
|
|
reiserfs_file_iterate_direct_fn direct_fn,
|
|
void *data)
|
|
{
|
|
INITIALIZE_REISERFS_PATH(path);
|
|
struct reiserfs_key key = {
|
|
.k2_dir_id = short_key->k2_dir_id,
|
|
.k2_objectid = short_key->k2_objectid,
|
|
};
|
|
struct item_head *ih;
|
|
__u64 size;
|
|
__u64 position = 0;
|
|
int ret;
|
|
|
|
set_key_type_v2(&key, TYPE_STAT_DATA);
|
|
set_key_offset_v2(&key, 0);
|
|
|
|
ret = reiserfs_search_by_key_3(fs, &key, &path);
|
|
if (ret != ITEM_FOUND) {
|
|
ret = -ENOENT;
|
|
goto fail;
|
|
}
|
|
|
|
ih = tp_item_head(&path);
|
|
if (!is_stat_data_ih(ih)) {
|
|
ret = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
if (get_ih_key_format(ih) == KEY_FORMAT_1) {
|
|
struct stat_data_v1 *sd = tp_item_body(&path);
|
|
size = sd_v1_size(sd);
|
|
} else {
|
|
struct stat_data *sd = tp_item_body(&path);
|
|
size = sd_v2_size(sd);
|
|
}
|
|
|
|
pathrelse(&path);
|
|
|
|
set_key_offset_v2(&key, 1);
|
|
set_key_type_v2(&key, TYPE_DIRECT);
|
|
|
|
while (position < size) {
|
|
struct item_head *ih = tp_item_head(&path);
|
|
__u64 offset;
|
|
ret = reiserfs_search_by_position(fs, &key, 0, &path);
|
|
ih = tp_item_head(&path);
|
|
if (ret != POSITION_FOUND) {
|
|
reiserfs_warning(stderr,
|
|
"found %k instead of %k [%d] (%lu, %lu)\n",
|
|
&ih->ih_key, &key, ret, position, size);
|
|
if (ret != ITEM_NOT_FOUND)
|
|
ret = -EIO;
|
|
goto fail;
|
|
}
|
|
|
|
offset = get_offset(&ih->ih_key);
|
|
|
|
position = offset - 1;
|
|
|
|
if (is_indirect_key(&ih->ih_key)) {
|
|
int num_ptrs = get_ih_item_len(ih) / 4;
|
|
__u32 *ptrs = tp_item_body(&path);
|
|
if (!num_ptrs) {
|
|
reiserfs_warning(stderr,
|
|
"indirect item %k contained 0 block pointers\n",
|
|
&ih->ih_key);
|
|
ret = -EIO;
|
|
goto fail;
|
|
}
|
|
ret = indirect_fn(fs, position, size, num_ptrs,
|
|
ptrs, data);
|
|
if (ret)
|
|
goto fail;
|
|
position += num_ptrs * fs->fs_blocksize;
|
|
} else if (is_direct_key(&ih->ih_key)) {
|
|
int len = get_ih_item_len(ih);
|
|
ret = direct_fn(fs, position, size, tp_item_body(&path),
|
|
len, data);
|
|
if (ret)
|
|
goto fail;
|
|
position += len;
|
|
} else
|
|
break;
|
|
pathrelse(&path);
|
|
set_key_offset_v2(&key, position + 1);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
fail:
|
|
pathrelse(&path);
|
|
return ret;
|
|
}
|
|
|
|
int reiserfs_iterate_dir(reiserfs_filsys_t fs,
|
|
const struct reiserfs_key *dir_short_key,
|
|
const reiserfs_iterate_dir_fn callback, void *data)
|
|
{
|
|
INITIALIZE_REISERFS_PATH(path);
|
|
struct reiserfs_key *next_key;
|
|
struct reiserfs_key min_key = {};
|
|
struct reiserfs_de_head *deh;
|
|
struct reiserfs_key next_item_key;
|
|
int ret;
|
|
__u64 next_pos = DOT_OFFSET;
|
|
|
|
set_key_dirid(&next_item_key, get_key_dirid(dir_short_key));
|
|
set_key_objectid(&next_item_key, get_key_objectid(dir_short_key));
|
|
set_key_offset_v1(&next_item_key, DOT_OFFSET);
|
|
set_key_uniqueness(&next_item_key, DIRENTRY_UNIQUENESS);
|
|
|
|
while (1) {
|
|
__u32 pos;
|
|
int i;
|
|
struct item_head *ih;
|
|
ret = reiserfs_search_by_entry_key(fs, &next_item_key, &path);
|
|
if (ret != POSITION_FOUND) {
|
|
reiserfs_warning(stderr,
|
|
"search by entry key for %k: %d\n",
|
|
&next_item_key, ret);
|
|
break;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
ih = tp_item_head(&path);
|
|
deh = tp_item_body(&path);
|
|
pos = path.pos_in_item;
|
|
|
|
deh += pos;
|
|
for (i = pos; i < get_ih_entry_count(ih); i++, deh++) {
|
|
const char *name;
|
|
size_t name_len;
|
|
if (get_deh_offset(deh) == DOT_OFFSET ||
|
|
get_deh_offset(deh) == DOT_DOT_OFFSET)
|
|
continue;
|
|
|
|
name = tp_item_body(&path) + get_deh_location(deh);
|
|
name_len = entry_length(ih, deh, i);
|
|
if (!name[name_len - 1])
|
|
name_len = strlen(name);
|
|
|
|
ret = callback(fs, dir_short_key, name, name_len,
|
|
get_deh_dirid(deh),
|
|
get_deh_objectid(deh), data);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
next_pos = get_deh_offset(deh) + 1;
|
|
}
|
|
|
|
next_key = uget_rkey(&path);
|
|
if (!next_key)
|
|
break;
|
|
if (!comp_keys(next_key, &min_key)) {
|
|
set_key_offset_v2(&next_item_key, next_pos);
|
|
pathrelse(&path);
|
|
continue;
|
|
}
|
|
|
|
if (comp_short_keys(next_key, &next_item_key))
|
|
break;
|
|
|
|
next_item_key = *next_key;
|
|
pathrelse(&path);
|
|
}
|
|
fail:
|
|
pathrelse(&path);
|
|
return ret;
|
|
}
|
|
|
|
void __attribute__ ((constructor)) init(void)
|
|
{
|
|
initialize_reiserfs_error_table();
|
|
}
|