Files
lgcosta 293e6a7280
build / build (clang, ) (push) Failing after 12s
build / build (gcc, ) (push) Failing after 9s
build / build (gcc, --enable-io-failure-emulation) (push) Failing after 8s
build / build (gcc, --enable-warnings) (push) Failing after 6s
Fix real bugs and clean up the build with -Wall -Wextra
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.
2026-11-11 22:02:38 -03:00

421 lines
11 KiB
C

/*
* Copyright 2000-2004 by Hans Reiser, licensing governed by
* reiserfsprogs/README
*/
#include "fsck.h"
#include "misc.h"
void erase_name(int len);
void print_name(char *name, int len);
void modify_item(struct item_head *ih, void *item);
/* fixme: search_by_key is not needed after any add_entry */
static __u64 _look_for_lost(reiserfs_filsys_t fs, int link_lost_dirs)
{
struct reiserfs_key key, prev_key, *rdkey;
INITIALIZE_REISERFS_PATH(path);
int item_pos;
struct buffer_head *bh;
struct item_head *ih;
unsigned long leaves;
int is_it_dir;
static int lost_files = 0; /* looking for lost dirs we calculate amount of
lost files, so that when we will look for
lost files we will be able to stop when
there are no lost files anymore */
int retval;
__u64 size;
key = root_dir_key;
if (!link_lost_dirs && !lost_files) {
/* we have to look for lost files but we know already that there are
no any */
return 0;
}
fsck_progress("Looking for lost %s:\n",
link_lost_dirs ? "directories" : "files");
leaves = 0;
/* total size of added entries */
size = 0;
while (1) {
retval = reiserfs_search_by_key_4(fs, &key, &path);
/* fixme: we assume path ends up with a leaf */
bh = get_bh(&path);
item_pos = get_item_pos(&path);
if (retval != ITEM_FOUND) {
if (item_pos == get_blkh_nr_items(B_BLK_HEAD(bh))) {
rdkey = uget_rkey(&path);
if (!rdkey) {
pathrelse(&path);
break;
}
key = *rdkey;
pathrelse(&path);
continue;
}
/* we are on the item in the buffer */
}
/* print ~ how many leaves were scanned and how fast it was */
if (!fsck_quiet(fs))
print_how_fast(leaves++, 0, 50, 0);
for (ih = tp_item_head(&path);
item_pos < get_blkh_nr_items(B_BLK_HEAD(bh));
item_pos++, ih++, PATH_LAST_POSITION(&path)++) {
if (is_item_reachable(ih))
continue;
/* found item which can not be reached */
if (!is_direntry_ih(ih) && !is_stat_data_ih(ih)) {
continue;
}
if (is_direntry_ih(ih)) {
/* if this directory has no stat data - try to recover it */
struct reiserfs_key sd;
struct reiserfs_path tmp;
sd = ih->ih_key;
set_type_and_offset(KEY_FORMAT_1, &sd,
SD_OFFSET, TYPE_STAT_DATA);
if (reiserfs_search_by_key_4(fs, &sd, &tmp) ==
ITEM_FOUND) {
/* should not happen - because if there were a stat data -
we would have done with the whole directory */
pathrelse(&tmp);
continue;
}
lost_found_pass_stat(fs)->dir_recovered++;
create_dir_sd(fs, &tmp, &sd, modify_item);
id_map_mark(proper_id_map(fs),
get_key_objectid(&sd));
key = sd;
pathrelse(&path);
goto cont;
}
/* stat data marked "not having name" found */
if (is_stat_data_ih(tp_item_head(&path)))
fix_obviously_wrong_sd_mode(&path);
is_it_dir =
((not_a_directory(ih_item_body(bh, ih))) ? 0 : 1);
if (is_it_dir) {
struct reiserfs_key tmp_key;
INITIALIZE_REISERFS_PATH(tmp_path);
struct item_head *tmp_ih;
/* there is no need to link empty lost directories into /lost+found */
tmp_key = ih->ih_key;
set_type_and_offset(KEY_FORMAT_1, &tmp_key,
0xffffffff, TYPE_DIRENTRY);
reiserfs_search_by_key_4(fs, &tmp_key,
&tmp_path);
tmp_ih = tp_item_head(&tmp_path);
tmp_ih--;
if (not_of_one_file(&tmp_key, tmp_ih))
reiserfs_panic("not directory found");
if (!is_direntry_ih(tmp_ih) ||
(get_deh_offset
(B_I_DEH(get_bh(&tmp_path), tmp_ih) +
get_ih_entry_count(tmp_ih) - 1) ==
DOT_DOT_OFFSET)) {
/* last directory item is either stat data or empty
directory item - do not link this dir into lost+found */
lost_found_pass_stat(fs)->
empty_lost_dirs++;
pathrelse(&tmp_path);
continue;
}
pathrelse(&tmp_path);
}
if (link_lost_dirs && !is_it_dir) {
/* we are looking for directories and it is not a dir */
lost_files++;
continue;
}
lost_found_pass_stat(fs)->lost_found++;
{
struct reiserfs_key obj_key =
{ 0, 0, {{0, 0},} };
char *lost_name;
struct item_head tmp_ih;
/* key to continue */
key = ih->ih_key;
set_key_objectid(&key,
get_key_objectid(&key) + 1);
tmp_ih = *ih;
if (id_map_test
(semantic_id_map(fs),
get_key_objectid(&ih->ih_key))) {
/* objectid is used, relocate an object */
lost_found_pass_stat(fs)->oid_sharing++;
if (is_it_dir) {
relocate_dir(&tmp_ih, 1);
lost_found_pass_stat(fs)->
oid_sharing_dirs_relocated++;
} else {
rewrite_file(&tmp_ih, 1, 1);
lost_found_pass_stat(fs)->
oid_sharing_files_relocated++;
}
linked_already(&tmp_ih.ih_key);
} else {
if (!is_it_dir)
id_map_mark(semantic_id_map(fs),
get_key_objectid
(&ih->ih_key));
}
xasprintf(&lost_name, "%u_%u",
get_key_dirid(&tmp_ih.ih_key),
get_key_objectid(&tmp_ih.ih_key));
/* entry in lost+found directory will point to this key */
set_key_dirid(&obj_key,
get_key_dirid(&tmp_ih.ih_key));
set_key_objectid(&obj_key,
get_key_objectid(&tmp_ih.
ih_key));
pathrelse(&path);
/* 0 does not mean anyting - item with "." and ".." already
exists and reached, so only name will be added */
size +=
reiserfs_add_entry(fs, &lost_found_dir_key,
lost_name,
name_length(lost_name,
lost_found_dir_format),
&obj_key,
0 /*fsck_need */ );
if (is_it_dir) {
/* fixme: we hope that if we will try to pull all the
directory right now - then there will be less
lost_found things */
print_name(lost_name,
strlen(lost_name));
/*fsck_progress ("\tChecking lost dir \"%s\":", lost_name); */
rebuild_semantic_pass(&obj_key,
&lost_found_dir_key,
/*dot_dot */ 0,
/*reloc_ih */ NULL);
erase_name(strlen(lost_name));
/*fsck_progress ("finished\n"); */
lost_found_pass_stat(fs)->
lost_found_dirs++;
} else {
if (reiserfs_search_by_key_4
(fs, &obj_key, &path) != ITEM_FOUND)
reiserfs_panic
("look_for_lost: lost file stat data %K not found",
&obj_key);
/* check_regular_file does not mark stat data reachable */
mark_item_reachable(tp_item_head(&path),
get_bh(&path));
rebuild_check_regular_file(&path,
tp_item_body
(&path),
NULL
/*reloc_ih */
);
pathrelse(&path);
lost_found_pass_stat(fs)->
lost_found_files++;
lost_files--;
}
free(lost_name);
goto cont;
}
} /* for */
prev_key = key;
PATH_LAST_POSITION(&path) = item_pos - 1;
rdkey = reiserfs_next_key(&path);
if (rdkey)
key = *rdkey;
else
break;
if (comp_keys(&prev_key, &key) != -1)
reiserfs_panic
("pass_3a: key must grow 2: prev=%k next=%k",
&prev_key, &key);
pathrelse(&path);
cont:
if (!link_lost_dirs && !lost_files) {
break;
}
}
pathrelse(&path);
#if 0
/* check names added we just have added to/lost+found. Those names are
marked DEH_Lost_found flag */
fsck_progress("Checking lost+found directory..");
fflush(stdout);
check_semantic_tree(&lost_found_dir_key, &root_dir_key, 0,
1 /* lost+found */ );
fsck_progress("finished\n");
#endif
return size;
}
static void save_lost_found_result(reiserfs_filsys_t fs)
{
FILE *file;
int retval;
/* save bitmaps with which we will be able start reiserfs from
pass 1 */
file = open_file("temp_fsck_file.deleteme", "w+");
if (!file)
return;
reiserfs_begin_stage_info_save(file, LOST_FOUND_DONE);
reiserfs_end_stage_info_save(file);
close_file(file);
retval = rename("temp_fsck_file.deleteme", state_dump_file(fs));
if (retval != 0)
fsck_progress
("pass 0: Could not rename the temporary file temp_fsck_file.deleteme to %s",
state_dump_file(fs));
}
/* we have nothing to load from a state file, but we have to fetch
on-disk bitmap, copy it to allocable bitmap, and fetch objectid
map */
void load_lost_found_result(reiserfs_filsys_t fs)
{
fsck_new_bitmap(fs) =
reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb));
reiserfs_bitmap_copy(fsck_new_bitmap(fs), fs->fs_bitmap2);
fsck_allocable_bitmap(fs) =
reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb));
reiserfs_bitmap_copy(fsck_allocable_bitmap(fs), fs->fs_bitmap2);
fs->block_allocator = reiserfsck_reiserfs_new_blocknrs;
fs->block_deallocator = reiserfsck_reiserfs_free_block;
/* we need objectid map on semantic pass to be able to relocate files */
proper_id_map(fs) = id_map_init();
/* Not implemented yet.
fetch_objectid_map (proper_id_map (fs), fs);
*/
}
static void after_lost_found(reiserfs_filsys_t fs)
{
/* update super block: objectid map, fsck state */
set_sb_fs_state(fs->fs_ondisk_sb, LOST_FOUND_DONE);
mark_buffer_dirty(fs->fs_super_bh);
/* write all dirty blocks */
fsck_progress("Flushing..");
id_map_flush(proper_id_map(fs), fs);
fs->fs_dirt = 1;
reiserfs_flush_to_ondisk_bitmap(fsck_new_bitmap(fs), fs);
reiserfs_flush(fs);
fsck_progress("finished\n");
stage_report(0x3a, fs);
if (!fsck_run_one_step(fs)) {
if (fsck_user_confirmed(fs, "Continue? (Yes):", "Yes\n", 1))
/* reiserfsck continues */
return;
}
save_lost_found_result(fs);
id_map_free(proper_id_map(fs));
proper_id_map(fs) = NULL;
fs->fs_dirt = 1;
reiserfs_close(fs);
exit(EXIT_OK);
}
void pass_3a_look_for_lost(reiserfs_filsys_t fs)
{
INITIALIZE_REISERFS_PATH(path);
struct item_head *ih;
void *sd;
__u64 size, sd_size;
__u32 blocks;
__u16 mode;
__u32 objectid;
unsigned int gen_counter;
fsck_progress("Pass 3a (looking for lost dir/files):\n");
/* when warnings go not to stderr - separate them in the log */
if (fsck_log_file(fs) != stderr)
fsck_log("####### Pass 3a (lost+found pass) #########\n");
/* look for lost dirs first */
size = _look_for_lost(fs, 1);
/* link files which are still lost */
size += _look_for_lost(fs, 0);
/* update /lost+found sd_size and sd_blocks (nlink is correct already) */
objectid = reiserfs_find_entry(fs, &root_dir_key, "lost+found",
&gen_counter, &lost_found_dir_key);
if (!objectid) {
reiserfs_panic
("look_for_lost: The entry 'lost+found' could not be found in the root directory.");
}
if (reiserfs_search_by_key_4(fs, &lost_found_dir_key, &path) !=
ITEM_FOUND)
reiserfs_panic
("look_for_lost: The StatData of the 'lost+found' directory %K could not be found",
&lost_found_dir_key);
ih = tp_item_head(&path);
sd = tp_item_body(&path);
get_sd_size(ih, sd, &sd_size);
size += sd_size;
blocks = dir_size2st_blocks(size);
set_sd_size(ih, sd, &size);
set_sd_blocks(ih, sd, &blocks);
/* make lost+found to be drwx------ */
mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
set_sd_mode(ih, sd, &mode);
mark_buffer_dirty(get_bh(&path));
pathrelse(&path);
after_lost_found(fs);
}