Compare commits

..
5 Commits
Author SHA1 Message Date
lgcosta e760d19017 Fix second use-after-free in the rollback path; clang battery re-validated
build / build (clang, ) (push) Failing after 6s
build / build (clang, --enable-warnings) (push) Failing after 6s
build / build (gcc, ) (push) Failing after 6s
build / build (gcc, --enable-io-failure-emulation) (push) Failing after 6s
build / build (gcc, --enable-warnings) (push) Failing after 7s
build / out-of-tree (push) Failing after 6s
build / sanitizers (push) Failing after 5s
ASan with halt_on_error caught a second dangling read on the exact path
--rollback-fsck-changes exits through: fsck_rollback() called freemem(fs)
at fsck/main.c:1313 and then printed the closing banner via
fsck_progress(...), which dereferences fs (fsck_data(fs) etc.).

Print the banner before the cleanup, matching the order the_end() and the
rebuild paths already use. No behavior change otherwise -- the same bytes
are emitted, only before the free.

Re-validated after the fix:
- clang+ASan/UBSan with halt_on_error: rollback save/restore, garbage-magic
  rejection, resize 64->128 MiB, mkfs, check, tune, debugreiserfs -- zero
  sanitizer errors; restore still prints "blocks restored" and the final
  banner.
- host functional battery (gcc, --enable-warnings): 12/12.
- host clang out-of-tree build: 0 warnings; clang rollback path exercised
  without sanitizers: exit semantics unchanged.
- Debian VM, CC=clang battery on this exact tree: 12/12, 0 warnings.

tests/vm/RESULTS.md documents the finding (item 6 in the clang section).
2026-11-12 09:11:55 -03:00
lgcosta 7b5421ce49 Support clang builds; fix the defects clang and its sanitizers exposed
build / build (clang, ) (push) Failing after 7s
build / build (clang, --enable-warnings) (push) Failing after 5s
build / build (gcc, ) (push) Failing after 5s
build / build (gcc, --enable-io-failure-emulation) (push) Failing after 5s
build / build (gcc, --enable-warnings) (push) Failing after 6s
build / out-of-tree (push) Failing after 6s
build / sanitizers (push) Failing after 5s
Clang (22.1.8 on the host, 14.0.6 in the Debian VM) now builds the tree
warning-free with --enable-warnings, in-tree and out-of-tree, with -j, and
the full VM battery passes with CC=clang: 12/12. ASan/UBSan builds run the
real tool paths (mkfs, check, tune, resize, rollback save/restore/reject)
with halt_on_error and report zero errors. Documented in tests/vm/RESULTS.md;
the CI gained clang+warnings, out-of-tree and sanitizer jobs.

Fixes found by this work:

- reiserfscore/Makefile.am: reiserfs_err.c and reiserfs_err.h shared one
  recipe. With -j, make runs the recipe once per stale target, so two
  compile_et invocations race: one deletes reiserfs_err.c ("no input
  files") while another job compiles it. Attached the recipe to
  reiserfs_err.c only; reiserfs_err.h merely depends on it. Reproduced
  with clang+ASan, stable across 5 clang / 3 asan / 3 gcc parallel builds.

- reiserfscore/Makefile.am: the rule read the source-local
  reiserfs_err.et, so VPATH builds could not find it. Use $(srcdir)/.
  make distcheck now passes.

- reiserfscore/reiserfslib.c: reiserfs_close() did fsync(fs->fs_dev) after
  reiserfs_free() released fs -- a 4-byte heap-use-after-free, flagged
  exactly at reiserfslib.c:406/407 by ASan (found by clang+ASan; gcc never
  caught it because the freed heap usually still holds the old value).
  Moved fsync before the free; reiserfs_flush() has already written
  journal and dirty buffers at that point and free_buffers() never
  writes, so the ordering preserves durability semantics.

- fsck/semantic_check.c, fsck/semantic_rebuild.c: three %K arguments passed
  &tmp_deh->deh2_dir_id where a struct reiserfs_key * is expected. It
  printed the right values only because deh2_dir_id sits at offset 4 and
  the key is read from the pointer as if dir_id sat at offset 0. Build a
  proper local key with set_key_dirid()/set_key_objectid() (the same
  idiom debugreiserfs/scan.c already uses). Verified -Waddress-of-packed-
  member (clang-only) is gone and output is unchanged.

- reiserfscore/hashes.c: four unreachable *(int *)0 = 0 stores (guarded by
  len >= 16/12/8/4 tests that cannot hold after the while (len >= 16)
  loop, len <= 15). Removed; clang's -Wnull-dereference goes quiet.
  Behavior verified: keyed_hash/yura_hash/r5_hash outputs for 16 inputs
  from 0 to 17 bytes are byte-identical before and after.

run_in_vm.sh: propagate CC into the guest, force a remount of /mnt/data
(the host rewrites the backing file under the guest), exclude generated
reiserfs_err.[ch] from the source tar. run_tests.sh: report which compiler
the battery actually used.
2026-11-12 08:51:29 -03:00
lgcosta 5a7382185b Add a libvirt/KVM test environment and fix the gcc-12 warning it found
build / build (clang, ) (push) Failing after 6s
build / build (gcc, ) (push) Failing after 6s
build / build (gcc, --enable-io-failure-emulation) (push) Failing after 6s
build / build (gcc, --enable-warnings) (push) Failing after 5s
The VM harness (tests/vm/) builds and exercises the tools on real Debian and
Alpine guests, driving the serial console (no sshd/cloud-init dependency).

Results on Debian 12 (glibc 2.36, gcc 12.2): 12/12 checks pass, 0 warnings
with -Wall -Wextra, including a real 128M -> 256M resize and the rollback
save/restore/reject paths.

Results on Alpine 3.20 (musl 1.2.5): the build cannot start. configure stops
at "reiserfsprogs requires a method to add printf modifiers" because
reiserfscore/prints.c depends on glibc's register_printf_specifier(3) and
<printf.h>, which musl does not provide. This is pre-existing and out of
scope here; measured at 341 use sites across 20+ files, all routed through
reiserfs_warning()/reiserfs_info(). Documented in tests/vm/RESULTS.md.

Fix the one real defect the harness surfaced:

fsck/semantic_check.c: check_check_regular_file() is called with NULL for
new_ih (line 466), so the block that would fill *new_ih is unreachable and
check_semantic_pass() never returns RELOCATED through that path. The
"case RELOCATED:" arm still reads relocated_ih.ih_key, which gcc 12.2
reports as "may be used uninitialized" (-Wmaybe-uninitialized); gcc 16 does
not, because it proves the path dead. Zero-initialise the struct and say why.

Verified: 0 warnings on gcc 12.2 after the change, and the Debian battery
stays at 12/12.
2026-11-12 00:11:31 -03:00
lgcosta 293e6a7280 Fix real bugs and clean up the build with -Wall -Wextra
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
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
lgcosta 41fb30313e README: document bootstrap/build steps
The generated autotools files are not tracked, so a fresh clone must run
autogen.sh before configure. State that and the build dependencies.
2026-11-11 21:30:11 -03:00
37 changed files with 1190 additions and 215 deletions

No files matched your search

+165
View File
@@ -0,0 +1,165 @@
name: build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- cc: gcc
extra: ""
- cc: gcc
extra: "--enable-warnings"
- cc: clang
extra: ""
# clang builds warning-free with -Wall -Wextra; keep this job as
# the regression gate for that guarantee.
- cc: clang
extra: "--enable-warnings"
- cc: gcc
extra: "--enable-io-failure-emulation"
env:
CC: ${{ matrix.cc }}
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
autoconf automake libtool pkg-config \
libcom-err-dev uuid-dev \
build-essential clang
- name: Bootstrap
run: ./autogen.sh
- name: Configure
run: ./configure ${{ matrix.extra }}
- name: Build
run: make -j"$(nproc)"
- name: Fail on any compiler warning
if: matrix.extra == '--enable-warnings'
run: |
make -j"$(nproc)" clean >/dev/null 2>&1 || true
make -j"$(nproc)" > build.log 2>&1 || { cat build.log; exit 1; }
if grep -q "warning:" build.log; then
grep "warning:" build.log
echo "build is not -Wall -Wextra clean"
exit 1
fi
- name: Smoke test
run: |
set -e
dd if=/dev/zero of=smoke.img bs=1M count=64 status=none
./mkreiserfs/mkreiserfs -f -q smoke.img
./fsck/reiserfsck --check -f -q -y smoke.img
./debugreiserfs/debugreiserfs -S smoke.img
out-of-tree:
# Parallel builds from a separate build directory; historically this
# tripped the compile_et target race, so keep it covered.
runs-on: ubuntu-latest
env:
CC: gcc
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
autoconf automake libtool pkg-config \
libcom-err-dev uuid-dev build-essential
- name: Bootstrap
run: ./autogen.sh
- name: Configure (out-of-tree)
run: |
mkdir -p "$RUNNER_TEMP/rfs-build"
cd "$RUNNER_TEMP/rfs-build"
"$GITHUB_WORKSPACE/configure" --enable-warnings
- name: Build
run: make -C "$RUNNER_TEMP/rfs-build" -j"$(nproc)"
- name: Smoke test
run: |
set -e
cd "$RUNNER_TEMP/rfs-build"
dd if=/dev/zero of=smoke.img bs=1M count=64 status=none
./mkreiserfs/mkreiserfs -f -q smoke.img
./fsck/reiserfsck --check -f -q -y smoke.img
sanitizers:
# clang + AddressSanitizer + UndefinedBehaviorSanitizer over the real
# tool paths (format, check, resize, rollback). Leak detection is off:
# the one-shot tools deliberately keep process-lifetime allocations.
runs-on: ubuntu-latest
env:
CC: clang
steps:
- uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
autoconf automake libtool pkg-config \
libcom-err-dev uuid-dev build-essential clang
- name: Bootstrap
run: ./autogen.sh
- name: Configure with sanitizers
run: >
./configure --enable-warnings
CFLAGS="-g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer"
- name: Build
run: make -j"$(nproc)"
- name: Exercise tools under sanitizers
env:
ASAN_OPTIONS: detect_leaks=0:halt_on_error=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: |
set -e
dd if=/dev/zero of=/tmp/s.img bs=1M count=64 status=none
./mkreiserfs/mkreiserfs -f -q -l SAN /tmp/s.img
./fsck/reiserfsck --check -f -q -y /tmp/s.img
./debugreiserfs/debugreiserfs -S /tmp/s.img
./tune/reiserfstune -l SAN2 -f /tmp/s.img
# resize: 64M -> 128M
dd if=/dev/zero of=/tmp/b.img bs=1M count=64 status=none
./mkreiserfs/mkreiserfs -f -q /tmp/b.img
truncate -s 128M /tmp/b.img
./resize_reiserfs/resize_reiserfs -s +64M -f /tmp/b.img
./fsck/reiserfsck --check -f -q -y /tmp/b.img
# rollback save + restore
cp /tmp/s.img /tmp/r.img
./fsck/reiserfsck --fix-fixable -y -q -R /tmp/rb.dat /tmp/r.img
./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rb.dat /tmp/r.img
# rollback file with garbage magic must be rejected
head -c 100 /dev/urandom > /tmp/bad.dat
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/bad.dat /tmp/r.img 2>&1 \
| grep -q "does not look like a rollback file"; then
echo "rollback garbage correctly rejected"
else
echo "rollback garbage was NOT rejected"
exit 1
fi
+24
View File
@@ -53,3 +53,27 @@ the code and documentation are to avoid confusion by users, not to avoid
proper attribution. Reiserfsprogs has been maintained by the ReiserFS proper attribution. Reiserfsprogs has been maintained by the ReiserFS
development community for some time and can be reached at development community for some time and can be reached at
reiserfs-devel@vger.kernel.org. reiserfs-devel@vger.kernel.org.
[BUILDING]
The generated autotools files (configure, Makefile.in, ...) are not kept in
the repository. After a fresh clone, bootstrap them first:
./autogen.sh
./configure
make
Build dependencies: a C compiler, autoconf, automake, libtool, pkg-config,
libcom_err (com_err development files) and, optionally, libuuid.
Supported compilers: gcc and clang. Both build warning-free with
./configure --enable-warnings && make (i.e. -Wall -Wextra).
Out-of-tree (separate build directory) and parallel (-j) builds are
supported. Sanitizer builds are exercised in CI:
CC=clang ./configure --enable-warnings \
CFLAGS="-g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer"
Note: reiserfsprogs requires a libc with printf format extensions
(glibc's register_printf_specifier/register_printf_modifier). It does not
build against musl; see tests/vm/RESULTS.md. A libvirt/KVM test
environment for Debian and Alpine lives in tests/vm/.
+9
View File
@@ -108,6 +108,15 @@ if test "x$ac_cv_func_register_printf_specifier" != xyes -a \
fi fi
dnl -Wall -Wextra is clean as of this release; keep it opt-in so that
dnl distributions building with -Werror are not surprised by a future
dnl compiler adding a new diagnostic.
AC_ARG_ENABLE([warnings],
[AS_HELP_STRING([--enable-warnings], [build with -Wall -Wextra])])
if test "$enable_warnings" = "yes" ; then
CFLAGS="$CFLAGS -Wall -Wextra"
fi
dnl Never enable this. It is for debugging only dnl Never enable this. It is for debugging only
AC_ARG_ENABLE([io-failure-emulation], AC_ARG_ENABLE([io-failure-emulation],
[AS_HELP_STRING([--enable-io-failure-emulation], [Never use this. It is for debugging only])]) [AS_HELP_STRING([--enable-io-failure-emulation], [Never use this. It is for debugging only])])
+5 -4
View File
@@ -4,6 +4,7 @@
*/ */
#include "debugreiserfs.h" #include "debugreiserfs.h"
#include "misc.h"
#include <com_err.h> #include <com_err.h>
reiserfs_filsys_t fs; reiserfs_filsys_t fs;
@@ -264,13 +265,13 @@ static char *parse_options(struct debugreiserfs_data *data,
!= EOF) { != EOF) {
switch (c) { switch (c) {
case 'a': /* -r will read this, -n and -N will write to it */ case 'a': /* -r will read this, -n and -N will write to it */
asprintf(&data->map_file, "%s", optarg); xasprintf(&data->map_file, "%s", optarg);
break; break;
case 'b': case 'b':
/* will load bitmap from a file and read only blocks /* will load bitmap from a file and read only blocks
marked in it. This is for -p and -k */ marked in it. This is for -p and -k */
asprintf(&data->input_bitmap, "%s", optarg); xasprintf(&data->input_bitmap, "%s", optarg);
data->scan_area = EXTERN_BITMAP; data->scan_area = EXTERN_BITMAP;
break; break;
@@ -338,7 +339,7 @@ static char *parse_options(struct debugreiserfs_data *data,
break; break;
case 'r': case 'r':
asprintf(&data->recovery_file, "%s", optarg); xasprintf(&data->recovery_file, "%s", optarg);
data->mode = DO_RECOVER; data->mode = DO_RECOVER;
break; break;
@@ -379,7 +380,7 @@ static char *parse_options(struct debugreiserfs_data *data,
break; break;
case 'B': /*disabled for a while */ case 'B': /*disabled for a while */
asprintf(&badblocks_file, "%s", optarg); xasprintf(&badblocks_file, "%s", optarg);
data->mode = DO_EXTRACT_BADBLOCKS; data->mode = DO_EXTRACT_BADBLOCKS;
break; break;
case 'q': case 'q':
+5 -4
View File
@@ -4,6 +4,7 @@
*/ */
#include "debugreiserfs.h" #include "debugreiserfs.h"
#include "misc.h"
#include <regex.h> #include <regex.h>
#include <obstack.h> #include <obstack.h>
#include <search.h> #include <search.h>
@@ -253,7 +254,7 @@ static struct saved_name *scan_for_key(const struct reiserfs_key *key)
char *name; char *name;
struct saved_name *new, *name_in; struct saved_name *new, *name_in;
asprintf(&name, "%u_%u", get_key_dirid(key), get_key_objectid(key)); xasprintf(&name, "%u_%u", get_key_dirid(key), get_key_objectid(key));
new = new =
obstack_alloc(&name_store, obstack_alloc(&name_store,
sizeof(struct saved_name) + strlen(name)); sizeof(struct saved_name) + strlen(name));
@@ -591,7 +592,7 @@ static void flush_map(reiserfs_filsys_t fs,
__u32 v32; __u32 v32;
if (!map_file(fs)) if (!map_file(fs))
asprintf(&map_file(fs), "%s", ".map"); xasprintf(&map_file(fs), "%s", ".map");
//reiserfs_warning (stderr, "Saving maps into %s\n", map_file (fs)); //reiserfs_warning (stderr, "Saving maps into %s\n", map_file (fs));
fp = fopen(map_file(fs), "a"); fp = fopen(map_file(fs), "a");
@@ -814,7 +815,7 @@ static void make_map(const void *nodep, VISIT value, int level)
if (value == leaf || value == postorder) { if (value == leaf || value == postorder) {
while (name) { while (name) {
if (map_file(fs)) { if (map_file(fs)) {
asprintf(&file_name, "%s.%d", map_file(fs), xasprintf(&file_name, "%s.%d", map_file(fs),
++nr); ++nr);
reiserfs_warning(log_to, reiserfs_warning(log_to,
"%d - (%d): [%K]:\"%s\": stored in the %s\n", "%d - (%d): [%K]:\"%s\": stored in the %s\n",
@@ -1006,7 +1007,7 @@ void do_scan(reiserfs_filsys_t fs)
int type; int type;
char *answer = 0; char *answer = 0;
size_t n = 0; size_t n = 0;
struct reiserfs_key key = { 0, 0, }; struct reiserfs_key key = { 0 };
unsigned long done, total; unsigned long done, total;
if (debug_mode(fs) == DO_LOOK_FOR_NAME) { if (debug_mode(fs) == DO_LOOK_FOR_NAME) {
+2
View File
@@ -514,11 +514,13 @@ void unpack_partition(int fd, int jfd)
case '1': case '1':
fread(c, 1, 1, stdin); /* that was 100%, read in first 0 */ fread(c, 1, 1, stdin); /* that was 100%, read in first 0 */
/* fall through */
case '2': case '2':
case '4': case '4':
case '6': case '6':
case '8': case '8':
fread(c, 1, 1, stdin); fread(c, 1, 1, stdin);
/* fall through */
case '0': case '0':
fread(c + 1, 1, 1, stdin); /* read % */ fread(c + 1, 1, 1, stdin); /* read % */
+12 -12
View File
@@ -52,18 +52,18 @@ int main(int argc, char *argv[]);
* options * options
*/ */
#define OPT_INTERACTIVE 1 << 0 #define OPT_INTERACTIVE 1U << 0
#define OPT_ADJUST_FILE_SIZE 1 << 1 /* not default yet */ #define OPT_ADJUST_FILE_SIZE 1U << 1 /* not default yet */
#define OPT_QUIET 1 << 2 /* no "speed" info */ #define OPT_QUIET 1U << 2 /* no "speed" info */
#define OPT_SILENT 1 << 3 /* no complains about found corruptions */ #define OPT_SILENT 1U << 3 /* no complains about found corruptions */
#define OPT_BACKGROUND 1 << 4 #define OPT_BACKGROUND 1U << 4
#define OPT_SKIP_JOURNAL 1 << 5 #define OPT_SKIP_JOURNAL 1U << 5
#define OPT_HASH_DEFINED 1 << 6 #define OPT_HASH_DEFINED 1U << 6
#define OPT_SAVE_PASSES_DUMP 1 << 7 #define OPT_SAVE_PASSES_DUMP 1U << 7
#define OPT_SAVE_ROLLBACK 1 << 8 #define OPT_SAVE_ROLLBACK 1U << 8
#define OPT_YES 1 << 9 #define OPT_YES 1U << 9
#define BADBLOCKS_FILE 1 << 10 #define BADBLOCKS_FILE 1U << 10
#define OPT_FORCE 1 << 11 #define OPT_FORCE 1U << 11
/* pass0.c */ /* pass0.c */
extern reiserfs_bitmap_t *leaves_bitmap; extern reiserfs_bitmap_t *leaves_bitmap;
+2 -1
View File
@@ -4,6 +4,7 @@
*/ */
#include "fsck.h" #include "fsck.h"
#include "misc.h"
void erase_name(int len); void erase_name(int len);
void print_name(char *name, int len); void print_name(char *name, int len);
@@ -180,7 +181,7 @@ static __u64 _look_for_lost(reiserfs_filsys_t fs, int link_lost_dirs)
(&ih->ih_key)); (&ih->ih_key));
} }
asprintf(&lost_name, "%u_%u", xasprintf(&lost_name, "%u_%u",
get_key_dirid(&tmp_ih.ih_key), get_key_dirid(&tmp_ih.ih_key),
get_key_objectid(&tmp_ih.ih_key)); get_key_objectid(&tmp_ih.ih_key));
+22 -13
View File
@@ -4,6 +4,7 @@
*/ */
#include "fsck.h" #include "fsck.h"
#include "misc.h"
#include <getopt.h> #include <getopt.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/mman.h> #include <sys/mman.h>
@@ -198,7 +199,7 @@ static char *parse_options(struct fsck_data *data, int argc, char *argv[])
break; break;
#endif #endif
case 'd': /* --create-passes-dump */ case 'd': /* --create-passes-dump */
asprintf(&data->rebuild.passes_dump_file_name, "%s", xasprintf(&data->rebuild.passes_dump_file_name, "%s",
optarg); optarg);
data->options |= OPT_SAVE_PASSES_DUMP; data->options |= OPT_SAVE_PASSES_DUMP;
break; break;
@@ -208,7 +209,7 @@ static char *parse_options(struct fsck_data *data, int argc, char *argv[])
break; break;
case 'h': /* --hash: suppose that this hash was used on a filesystem */ case 'h': /* --hash: suppose that this hash was used on a filesystem */
asprintf(&data->rebuild.defined_hash, "%s", optarg); xasprintf(&data->rebuild.defined_hash, "%s", optarg);
if (name2func(data->rebuild.defined_hash) == NULL) if (name2func(data->rebuild.defined_hash) == NULL)
reiserfs_panic reiserfs_panic
("reiserfsck: Unknown hash is specified: %s", ("reiserfsck: Unknown hash is specified: %s",
@@ -221,12 +222,12 @@ static char *parse_options(struct fsck_data *data, int argc, char *argv[])
break; break;
case 'R': /* preparing rollback data */ case 'R': /* preparing rollback data */
asprintf(&data->rebuild.rollback_file, "%s", optarg); xasprintf(&data->rebuild.rollback_file, "%s", optarg);
data->options |= OPT_SAVE_ROLLBACK; data->options |= OPT_SAVE_ROLLBACK;
break; break;
case 'B': /* list of phisically corrupted blocks */ case 'B': /* list of phisically corrupted blocks */
asprintf(&badblocks_file, "%s", optarg); xasprintf(&badblocks_file, "%s", optarg);
data->options |= BADBLOCKS_FILE; data->options |= BADBLOCKS_FILE;
break; break;
@@ -916,25 +917,29 @@ static void rebuild_tree(reiserfs_filsys_t fs)
"reiserfsck --rebuild-tree started at %s" "reiserfsck --rebuild-tree started at %s"
"###########\n", ctime(&t)); "###########\n", ctime(&t));
/* Deliberate cascade: each pass falls into the next so that a rebuild
always runs every stage from the requested starting point on. */
switch (where_to_start_from(fs)) { switch (where_to_start_from(fs)) {
case START_FROM_THE_BEGINNING: case START_FROM_THE_BEGINNING:
reset_super_block(fs); reset_super_block(fs);
pass_0(fs); pass_0(fs);
/* fall through */
case START_FROM_PASS_1: case START_FROM_PASS_1:
reset_super_block(fs); reset_super_block(fs);
pass_1(fs); pass_1(fs);
/* fall through */
case START_FROM_PASS_2: case START_FROM_PASS_2:
pass_2(fs); pass_2(fs);
/* fall through */
case START_FROM_SEMANTIC: case START_FROM_SEMANTIC:
pass_3_semantic(fs); pass_3_semantic(fs);
/* fall through */
/* if --lost+found is set - link unaccessed directories to lost+found /* if --lost+found is set - link unaccessed directories to lost+found
directory */ directory */
case START_FROM_LOST_FOUND: case START_FROM_LOST_FOUND:
pass_3a_look_for_lost(fs); pass_3a_look_for_lost(fs);
/* fall through */
case START_FROM_PASS_4: case START_FROM_PASS_4:
/* 4. look for unaccessed items in the leaves */ /* 4. look for unaccessed items in the leaves */
@@ -1267,7 +1272,7 @@ static int open_devices_for_rollback(char *file_name, struct fsck_data *data)
fs = getmem(sizeof(*fs)); fs = getmem(sizeof(*fs));
fs->fs_dev = fd; fs->fs_dev = fd;
fs->fs_vp = data; fs->fs_vp = data;
asprintf(&fs->fs_file_name, "%s", file_name); xasprintf(&fs->fs_file_name, "%s", file_name);
if (data->journal_dev_name && strcmp(data->journal_dev_name, file_name)) { if (data->journal_dev_name && strcmp(data->journal_dev_name, file_name)) {
fs->fs_journal_dev = open(data->journal_dev_name, fs->fs_journal_dev = open(data->journal_dev_name,
@@ -1298,16 +1303,19 @@ static void fsck_rollback(reiserfs_filsys_t fs)
fsck_progress_file(fs)); fsck_progress_file(fs));
close_rollback_file(); close_rollback_file();
/* print the closing banner while fs is still valid: freemem(fs)
below releases it. Same order as the_end() and the rebuild
paths (ASan caught the opposite order here). */
time(&t);
fsck_progress("###########\n"
"reiserfsck finished at %s" "###########\n", ctime(&t));
close(fs->fs_journal_dev); close(fs->fs_journal_dev);
free(fs->fs_file_name); free(fs->fs_file_name);
fs->fs_file_name = NULL; fs->fs_file_name = NULL;
close(fs->fs_dev); close(fs->fs_dev);
freemem(fs); freemem(fs);
time(&t);
fsck_progress("###########\n"
"reiserfsck finished at %s" "###########\n", ctime(&t));
exit(EXIT_FIXED); exit(EXIT_FIXED);
} }
@@ -1454,7 +1462,8 @@ int main(int argc, char *argv[])
if (auto_check(fs)) if (auto_check(fs))
break; break;
data->mode = FSCK_FIX_FIXABLE; data->mode = FSCK_FIX_FIXABLE;
case FSCK_CHECK: /* fall through */
case FSCK_CHECK: /* and FSCK_FIX_FIXABLE */
case FSCK_FIX_FIXABLE: case FSCK_FIX_FIXABLE:
check_fs(fs); check_fs(fs);
break; break;
+3 -5
View File
@@ -4,6 +4,7 @@
*/ */
#include "fsck.h" #include "fsck.h"
#include "misc.h"
/* on pass2 we take leaves which could not be inserted into tree /* on pass2 we take leaves which could not be inserted into tree
during pass1 and insert each item separately. It is possible that during pass1 and insert each item separately. It is possible that
@@ -74,7 +75,7 @@ static __u64 link_one(struct relocated *file)
struct reiserfs_key obj_key; struct reiserfs_key obj_key;
__u64 len = 0; __u64 len = 0;
asprintf(&name, "%lu,%lu", file->old_dir_id, file->new_objectid); xasprintf(&name, "%lu,%lu", file->old_dir_id, file->new_objectid);
set_key_dirid(&obj_key, file->old_dir_id); set_key_dirid(&obj_key, file->old_dir_id);
set_key_objectid(&obj_key, file->new_objectid); set_key_objectid(&obj_key, file->new_objectid);
@@ -118,15 +119,12 @@ void linked_already(const struct reiserfs_key *new_key /*, link_func_t link_func
void link_relocated_files(void) void link_relocated_files(void)
{ {
struct relocated *tmp; struct relocated *tmp;
int count;
count = 0;
while (relocated_list) { while (relocated_list) {
link_one(relocated_list); link_one(relocated_list);
tmp = relocated_list; tmp = relocated_list;
relocated_list = relocated_list->next; relocated_list = relocated_list->next;
freemem(tmp); freemem(tmp);
count++;
} }
} }
@@ -369,7 +367,7 @@ static void put_directory_item_into_tree(struct item_head *comingih, char *item)
" badly hashed entry", i, namelen, name, " badly hashed entry", i, namelen, name,
&comingih->ih_key); &comingih->ih_key);
asprintf(&buf, "%.*s", namelen, name); xasprintf(&buf, "%.*s", namelen, name);
/* 1 for fsck is important: if there is no any items of this /* 1 for fsck is important: if there is no any items of this
directory in the tree yet - new item will be inserted directory in the tree yet - new item will be inserted
marked not reached */ marked not reached */
+24 -9
View File
@@ -3,6 +3,7 @@
*/ */
#include "fsck.h" #include "fsck.h"
#include "misc.h"
static struct reiserfs_key *trunc_links = NULL; static struct reiserfs_key *trunc_links = NULL;
static __u32 links_num = 0; static __u32 links_num = 0;
@@ -366,7 +367,7 @@ start_again:
memcpy(dir_item, ih_item_body(bh, ih), get_ih_item_len(ih)); memcpy(dir_item, ih_item_body(bh, ih), get_ih_item_len(ih));
/* next item key */ /* next item key */
if (PATH_LAST_POSITION(&path) == (B_NR_ITEMS(bh) - 1) && if (PATH_LAST_POSITION(&path) + 1 == B_NR_ITEMS(bh) &&
(rdkey = uget_rkey(&path))) (rdkey = uget_rkey(&path)))
copy_key(key, rdkey); copy_key(key, rdkey);
else { else {
@@ -550,7 +551,10 @@ static int check_semantic_pass(struct reiserfs_key *key,
for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih); for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih);
i++, deh++) { i++, deh++) {
struct item_head relocated_ih; /* Only meaningful when check_check_regular_file() returns
RELOCATED; that path currently passes NULL for new_ih,
so this stays zero-initialised. */
struct item_head relocated_ih = { 0 };
int ret = OK; int ret = OK;
if (name) { if (name) {
@@ -559,7 +563,7 @@ static int check_semantic_pass(struct reiserfs_key *key,
} }
namelen = name_in_entry_length(&tmp_ih, deh, i); namelen = name_in_entry_length(&tmp_ih, deh, i);
asprintf(&name, "%.*s", namelen, name_in_entry(deh, i)); xasprintf(&name, "%.*s", namelen, name_in_entry(deh, i));
entry_len = entry_length(&tmp_ih, deh, i); entry_len = entry_length(&tmp_ih, deh, i);
get_object_key(deh, &object_key, &entry_key, &tmp_ih); get_object_key(deh, &object_key, &entry_key, &tmp_ih);
@@ -634,6 +638,7 @@ static int check_semantic_pass(struct reiserfs_key *key,
("check_semantic_pass: Name \"%.*s\" in directory %K points to " ("check_semantic_pass: Name \"%.*s\" in directory %K points to "
"nowhere\n", namelen, name, "nowhere\n", namelen, name,
&tmp_ih.ih_key); &tmp_ih.ih_key);
/* fall through */
case LOOP_FOUND: case LOOP_FOUND:
if (fsck_mode(fs) == FSCK_FIX_FIXABLE) { if (fsck_mode(fs) == FSCK_FIX_FIXABLE) {
reiserfs_remove_entry(fs, &entry_key); reiserfs_remove_entry(fs, &entry_key);
@@ -671,16 +676,27 @@ static int check_semantic_pass(struct reiserfs_key *key,
} else { } else {
/* update key dir entry points to */ /* update key dir entry points to */
struct reiserfs_de_head *tmp_deh; struct reiserfs_de_head *tmp_deh;
struct reiserfs_key deh_key;
tmp_deh = tmp_deh =
B_I_DEH(get_bh(&path), B_I_DEH(get_bh(&path),
tp_item_head(&path)) + tp_item_head(&path)) +
path.pos_in_item; path.pos_in_item;
/* Build a proper key from the entry's
dirid/objectid. Passing
&tmp_deh->deh2_dir_id as a
reiserfs_key * only worked by
offset coincidence and trips
-Waddress-of-packed-member. */
set_key_dirid(&deh_key,
get_deh_dirid(tmp_deh));
set_key_objectid(&deh_key,
get_deh_objectid(tmp_deh));
fsck_log fsck_log
("The directory %K pointing to %K (\"%.*s\") updated to point " ("The directory %K pointing to %K (\"%.*s\") updated to point "
"to ", &tmp_ih.ih_key, "to ", &tmp_ih.ih_key, &deh_key,
&tmp_deh->deh2_dir_id, namelen, namelen, name);
name);
set_deh_dirid(tmp_deh, set_deh_dirid(tmp_deh,
get_key_dirid get_key_dirid
(&relocated_ih.ih_key)); (&relocated_ih.ih_key));
@@ -689,9 +705,8 @@ static int check_semantic_pass(struct reiserfs_key *key,
(&relocated_ih. (&relocated_ih.
ih_key)); ih_key));
fsck_log("%K (\"%.*s\")\n", fsck_log("%K (\"%.*s\")\n", &deh_key,
&tmp_deh->deh2_dir_id, namelen, namelen, name);
name);
mark_buffer_dirty(get_bh(&path)); mark_buffer_dirty(get_bh(&path));
} }
dir_size += DEH_SIZE + entry_len; dir_size += DEH_SIZE + entry_len;
+16 -3
View File
@@ -4,6 +4,7 @@
*/ */
#include "fsck.h" #include "fsck.h"
#include "misc.h"
int screen_width; int screen_width;
static int screen_curr_pos; static int screen_curr_pos;
@@ -639,7 +640,7 @@ static char *get_next_directory_item(struct reiserfs_key *key, /* on return this
memcpy(dir_item, ih_item_body(bh, ih), get_ih_item_len(ih)); memcpy(dir_item, ih_item_body(bh, ih), get_ih_item_len(ih));
/* next item key */ /* next item key */
if (PATH_LAST_POSITION(&path) == (B_NR_ITEMS(bh) - 1) && if (PATH_LAST_POSITION(&path) + 1 == B_NR_ITEMS(bh) &&
(rdkey = uget_rkey(&path))) (rdkey = uget_rkey(&path)))
copy_key(key, rdkey); copy_key(key, rdkey);
else { else {
@@ -923,7 +924,7 @@ start_again: /* when directory was relocated */
} }
namelen = name_in_entry_length(&tmp_ih, deh, i); namelen = name_in_entry_length(&tmp_ih, deh, i);
asprintf(&name, "%.*s", namelen, name_in_entry(deh, i)); xasprintf(&name, "%.*s", namelen, name_in_entry(deh, i));
entry_len = entry_length(&tmp_ih, deh, i); entry_len = entry_length(&tmp_ih, deh, i);
@@ -1005,11 +1006,23 @@ start_again: /* when directory was relocated */
} else { } else {
/* update key dir entry points to */ /* update key dir entry points to */
struct reiserfs_de_head *tmp_deh; struct reiserfs_de_head *tmp_deh;
struct reiserfs_key deh_key;
tmp_deh = tmp_deh =
B_I_DEH(get_bh(&path), B_I_DEH(get_bh(&path),
tp_item_head(&path)) + tp_item_head(&path)) +
path.pos_in_item; path.pos_in_item;
/* Build a proper key from the entry's
dirid/objectid. Passing
&tmp_deh->deh2_dir_id as a
reiserfs_key * only worked by
offset coincidence and trips
-Waddress-of-packed-member. */
set_key_dirid(&deh_key,
get_deh_dirid(tmp_deh));
set_key_objectid(&deh_key,
get_deh_objectid(tmp_deh));
fsck_log fsck_log
("The entry %K (\"%.*s\") in directory %K updated to point to ", ("The entry %K (\"%.*s\") in directory %K updated to point to ",
&object_key, namelen, name, &object_key, namelen, name,
@@ -1022,7 +1035,7 @@ start_again: /* when directory was relocated */
(&relocated_ih. (&relocated_ih.
ih_key)); ih_key));
fsck_log("%K\n", &tmp_deh->deh2_dir_id); fsck_log("%K\n", &deh_key);
mark_buffer_dirty(get_bh(&path)); mark_buffer_dirty(get_bh(&path));
} }
dir_size += DEH_SIZE + entry_len; dir_size += DEH_SIZE + entry_len;
+2 -2
View File
@@ -13,8 +13,8 @@ static int do_items_have_the_same_type(const struct item_head *ih,
static int are_items_in_the_same_node(const struct reiserfs_path *path) static int are_items_in_the_same_node(const struct reiserfs_path *path)
{ {
return (PATH_LAST_POSITION(path) < return (PATH_LAST_POSITION(path) + 1 <
B_NR_ITEMS(PATH_PLAST_BUFFER(path)) - 1) ? 1 : 0; B_NR_ITEMS(PATH_PLAST_BUFFER(path))) ? 1 : 0;
} }
static void cut_last_unfm_pointer(struct reiserfs_path *path, static void cut_last_unfm_pointer(struct reiserfs_path *path,
+1 -1
View File
@@ -36,10 +36,10 @@
void check_memory_msg(void); void check_memory_msg(void);
void die(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); void die(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
int xasprintf(char **ret, const char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
void *getmem(int size); void *getmem(int size);
void *mem_alloc(int size); void *mem_alloc(int size);
void freemem(void *p); void freemem(void *p);
void checkmem(const char *p, int size);
void *expandmem(void *p, int size, int by); void *expandmem(void *p, int size, int by);
unsigned int get_mem_size(const char *p); unsigned int get_mem_size(const char *p);
void check_and_free_mem(void); void check_and_free_mem(void);
+14 -2
View File
@@ -32,6 +32,7 @@
#ifndef REISERFSPROGS_FS_H #ifndef REISERFSPROGS_FS_H
#define REISERFSPROGS_FS_H #define REISERFSPROGS_FS_H
#include <stddef.h>
#include <linux/types.h> #include <linux/types.h>
#include "swab.h" #include "swab.h"
@@ -174,7 +175,12 @@ struct reiserfs_super_block_v1 {
#define SB_SIZE_V1 (sizeof(struct reiserfs_super_block_v1)) /* 76 bytes */ #define SB_SIZE_V1 (sizeof(struct reiserfs_super_block_v1)) /* 76 bytes */
#define sb_jp(sb) (&((sb)->s_v1.sb_journal)) /* &(sb)->s_v1.sb_journal would take the address of a member inside a
packed struct, which is legal here (offset 12) but trips
-Waddress-of-packed-member on every use. Spell it out instead. */
#define sb_jp(sb) ((struct journal_params *) \
((char *)(sb) + offsetof(struct reiserfs_super_block, s_v1) + \
offsetof(struct reiserfs_super_block_v1, sb_journal)))
/* values for sb_version field of struct reiserfs_super_block. sb_version is /* values for sb_version field of struct reiserfs_super_block. sb_version is
only reliable on filesystem with non-standard journal */ only reliable on filesystem with non-standard journal */
@@ -636,6 +642,12 @@ struct item_head {
/* size of item header */ /* size of item header */
#define IH_SIZE (sizeof(struct item_head)) #define IH_SIZE (sizeof(struct item_head))
/* Address of the packed ih_format field, computed without forming a pointer
to a member of a packed struct (which trips -Waddress-of-packed-member).
ih_format is naturally aligned, so dereferencing the result is fine. */
#define ih_format_ptr(ih) \
((__le16 *)((char *)(ih) + offsetof(struct item_head, ih_format)))
/* set/get fields of item head on disk with these defines */ /* set/get fields of item head on disk with these defines */
#define get_ih_entry_count(ih) get_le16 (ih, u.ih2_entry_count) #define get_ih_entry_count(ih) get_le16 (ih, u.ih2_entry_count)
#define set_ih_entry_count(ih,val) set_le16 (ih, u.ih2_entry_count, val) #define set_ih_entry_count(ih,val) set_le16 (ih, u.ih2_entry_count, val)
@@ -1061,7 +1073,7 @@ struct reiserfs_path {
}; };
#define INITIALIZE_REISERFS_PATH(var) \ #define INITIALIZE_REISERFS_PATH(var) \
struct reiserfs_path var = {ILLEGAL_PATH_ELEMENT_OFFSET, } struct reiserfs_path var = { .path_length = ILLEGAL_PATH_ELEMENT_OFFSET }
/* Get path element by path and path position. */ /* Get path element by path and path position. */
#define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset)) #define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset))
+3 -3
View File
@@ -172,17 +172,17 @@ typedef int (*reiserfs_file_iterate_direct_fn)(reiserfs_filsys_t fs,
const char *body, size_t len, const char *body, size_t len,
void *data); void *data);
int reiserfs_iterate_file_data(reiserfs_filsys_t fs, int reiserfs_iterate_file_data(reiserfs_filsys_t fs,
const struct reiserfs_key const *short_key, const struct reiserfs_key *short_key,
reiserfs_file_iterate_indirect_fn indirect_fn, reiserfs_file_iterate_indirect_fn indirect_fn,
reiserfs_file_iterate_direct_fn direct_fn, reiserfs_file_iterate_direct_fn direct_fn,
void *data); void *data);
typedef int (*reiserfs_iterate_dir_fn)(reiserfs_filsys_t fs, typedef int (*reiserfs_iterate_dir_fn)(reiserfs_filsys_t fs,
const struct reiserfs_key const *dir_short_key, const struct reiserfs_key *dir_short_key,
const char *name, size_t len, const char *name, size_t len,
__u32 deh_dirid, __u32 deh_objectid, void *data); __u32 deh_dirid, __u32 deh_objectid, void *data);
int reiserfs_iterate_dir(reiserfs_filsys_t fs, int reiserfs_iterate_dir(reiserfs_filsys_t fs,
const struct reiserfs_key const *dir_short_key, const struct reiserfs_key *dir_short_key,
const reiserfs_iterate_dir_fn callback, void *data); const reiserfs_iterate_dir_fn callback, void *data);
+7 -6
View File
@@ -7,7 +7,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <asm/types.h> #include <linux/types.h>
void check_memory_msg(void) void check_memory_msg(void)
{ {
@@ -585,8 +585,8 @@ static void erase_rollback_file(char *rollback_file)
int open_rollback_file(char *rollback_file, FILE * log) int open_rollback_file(char *rollback_file, FILE * log)
{ {
char string[28];
struct stat buf; struct stat buf;
char string[sizeof(ROLLBACK_FILE_START_MAGIC)] = { 0 };
if (rollback_file == NULL) if (rollback_file == NULL)
return -1; return -1;
@@ -603,8 +603,8 @@ int open_rollback_file(char *rollback_file, FILE * log)
return -1; return -1;
} }
fread(string, 28, 1, s_rollback_file); fread(string, sizeof(ROLLBACK_FILE_START_MAGIC) - 1, 1, s_rollback_file);
if (!strcmp(string, ROLLBACK_FILE_START_MAGIC)) { if (strcmp(string, ROLLBACK_FILE_START_MAGIC) != 0) {
fprintf(stderr, fprintf(stderr,
"Specified file (%s) does not look like a rollback file\n", "Specified file (%s) does not look like a rollback file\n",
rollback_file); rollback_file);
@@ -775,8 +775,9 @@ void do_fsck_rollback(int fd_device, int fd_journal_device, FILE * progress)
printf("\n"); printf("\n");
if (log_file) if (log_file)
fprintf(log_file, "rollback: (%u) blocks restored\n", fprintf(log_file,
count_rollbacked); "rollback: (%u) blocks restored, %u skipped\n",
count_rollbacked, count_failed);
} }
/* /*
+82 -84
View File
@@ -9,10 +9,11 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <mntent.h> #include <mntent.h>
#include <sys/vfs.h> #include <sys/statfs.h>
#include <time.h> #include <time.h>
#include <utime.h> #include <utime.h>
#include <ctype.h> #include <ctype.h>
@@ -33,125 +34,117 @@ void die(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsprintf(buf, fmt, args); vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args); va_end(args);
fprintf(stderr, "\n%s\n", buf); fprintf(stderr, "\n%s\n", buf);
abort(); abort();
} }
#define MEM_BEGIN "_mem_begin_" /*
#define MEM_END "mem_end" * Small size-tracking allocator. Every block handed out by getmem()/
#define MEM_FREED "__free_" * mem_alloc()/expandmem() is preceded by a header recording its usable
#define CONTROL_SIZE (strlen (MEM_BEGIN) + 1 + sizeof (int) + strlen (MEM_END) + 1) * size. get_mem_size() reports it back and expandmem() uses it to grow
* in place without the caller having to remember the old size.
*
* The header is over-aligned so the returned pointer keeps malloc's
* alignment guarantees for any type.
*/
union mem_header {
struct {
size_t size;
} h;
max_align_t align;
};
/* asprintf() wrapper: allocation failure is fatal, matching the rest of
this file (die()). Use it where the old code ignored asprintf()'s
return value and then dereferenced the pointer. */
int xasprintf(char **ret, const char *fmt, ...)
{
va_list args;
int rc;
va_start(args, fmt);
rc = vasprintf(ret, fmt, args);
va_end(args);
if (rc < 0)
die("xasprintf: out of memory");
return rc;
}
#define MEM_HEADER_SIZE (sizeof(union mem_header))
static union mem_header *mem_header_of(const void *p)
{
return (union mem_header *)((char *)p - MEM_HEADER_SIZE);
}
unsigned int get_mem_size(const char *p) unsigned int get_mem_size(const char *p)
{ {
const char *begin; return mem_header_of(p)->h.size;
begin = p - strlen(MEM_BEGIN) - 1 - sizeof(int);
return *(int *)(begin + strlen(MEM_BEGIN) + 1);
} }
void checkmem(const char *p, int size) void *mem_alloc(int size)
{ {
const char *begin; union mem_header *hdr;
const char *end; char *mem;
begin = p - strlen(MEM_BEGIN) - 1 - sizeof(int); if (size < 0)
if (strcmp(begin, MEM_BEGIN)) die("mem_alloc: negative size (%d)", size);
die("checkmem: memory corrupted - invalid head sign");
if (*(int *)(begin + strlen(MEM_BEGIN) + 1) != size) hdr = malloc(MEM_HEADER_SIZE + (size_t) size);
die("checkmem: memory corrupted - invalid size"); if (!hdr)
die("mem_alloc: no more memory (%d)", size);
end = begin + size + CONTROL_SIZE - strlen(MEM_END) - 1; hdr->h.size = size;
if (strcmp(end, MEM_END)) mem = (char *)hdr + MEM_HEADER_SIZE;
die("checkmem: memory corrupted - invalid end sign");
return mem;
} }
void *getmem(int size) void *getmem(int size)
{ {
char *mem; char *mem;
if ((mem = mem_alloc(size)) == NULL) mem = mem_alloc(size);
die("getmem: no more memory (%d)", size);
memset(mem, 0, size); memset(mem, 0, size);
// checkmem (mem, size);
return mem;
}
void *mem_alloc(int size)
{
char *p;
char *mem;
p = (char *)malloc(CONTROL_SIZE + size);
if (!p)
die("getmem: no more memory (%d)", size);
/* Write the MEM_BEGIN magic in the beginning of allocated memory. */
strcpy(p, MEM_BEGIN);
p += strlen(MEM_BEGIN) + 1;
/* Write the size after the magic. */
*(int *)p = size;
p += sizeof(int);
mem = p;
p += size;
strcpy(p, MEM_END);
return mem; return mem;
} }
void *expandmem(void *vp, int size, int by) void *expandmem(void *vp, int size, int by)
{ {
int allocated; union mem_header *hdr;
char *mem, *p = vp; char *mem;
int expand_by = by; size_t old_size = vp ? (size_t) mem_header_of(vp)->h.size : 0;
size_t new_size = old_size + by;
if (p) { if (by < 0)
checkmem(p, size); die("expandmem: negative increment (%d)", by);
allocated = CONTROL_SIZE + size;
p -= (strlen(MEM_BEGIN) + 1 + sizeof(int)); hdr = realloc(vp ? mem_header_of(vp) : NULL, MEM_HEADER_SIZE + new_size);
} else { if (!hdr)
allocated = 0;
/* add control bytes to the new allocated area */
expand_by += CONTROL_SIZE;
}
p = realloc(p, allocated + expand_by);
if (!p)
die("expandmem: no more memory (%d)", size); die("expandmem: no more memory (%d)", size);
if (!vp) {
strcpy(p, MEM_BEGIN);
}
mem = p + strlen(MEM_BEGIN) + 1 + sizeof(int);
*(int *)(p + strlen(MEM_BEGIN) + 1) = size + by; mem = (char *)hdr + MEM_HEADER_SIZE;
/* fill new allocated area by 0s */ hdr->h.size = new_size;
/* fill the newly allocated area with 0s */
if (by > 0) if (by > 0)
memset(mem + size, 0, by); memset(mem + old_size, 0, by);
strcpy(mem + size + by, MEM_END);
// checkmem (mem, size + by);
return mem; return mem;
} }
void freemem(void *vp) void freemem(void *vp)
{ {
char *p = vp; if (!vp)
int size;
if (!p)
return; return;
size = get_mem_size(vp);
checkmem(p, size);
p -= (strlen(MEM_BEGIN) + 1 + sizeof(int)); free(mem_header_of(vp));
strcpy(p, MEM_FREED);
strcpy(p + size + CONTROL_SIZE - strlen(MEM_END) - 1, MEM_FREED);
free(p);
} }
typedef int (*func_t) (const char *); typedef int (*func_t) (const char *);
@@ -714,13 +707,18 @@ int prepare_dma_check(dma_info_t *dma_info)
} }
while ((dirent = readdir(dir)) != NULL) { while ((dirent = readdir(dir)) != NULL) {
int n;
if (strncmp(dirent->d_name, ".", 1) == 0 if (strncmp(dirent->d_name, ".", 1) == 0
|| strncmp(dirent->d_name, "..", 2) == 0) || strncmp(dirent->d_name, "..", 2) == 0)
continue; continue;
memset(buf, 0, 256);
strncat(buf, "/dev/", 5); /* "/dev/" + name; skip names that do not fit
strncat(buf, dirent->d_name, rather than truncating into a wrong path */
strlen(dirent->d_name)); n = snprintf(buf, sizeof(buf), "/dev/%s",
dirent->d_name);
if (n < 0 || (size_t) n >= sizeof(buf))
continue;
if (stat(buf, &st)) if (stat(buf, &st))
break; break;
+1 -1
View File
@@ -15,7 +15,7 @@ void progbar_init(struct progbar *ctx, const char *units, FILE * fp)
memset(bar, '=', sizeof(bar) - 1); memset(bar, '=', sizeof(bar) - 1);
if (!spaces[0]) if (!spaces[0])
memset(spaces, ' ', sizeof(spaces) - 1); memset(spaces, ' ', sizeof(spaces) - 1);
strncpy(ctx->units, units, sizeof(ctx->units)); snprintf(ctx->units, sizeof(ctx->units), "%s", units);
ctx->file = fp; ctx->file = fp;
} }
+6 -2
View File
@@ -122,7 +122,11 @@ static void make_super_block(reiserfs_filsys_t fs)
reiserfs_warning(stderr, reiserfs_warning(stderr,
"\nSpecified LABEL is longer then 16 " "\nSpecified LABEL is longer then 16 "
"characters, will be truncated\n\n"); "characters, will be truncated\n\n");
strncpy(fs->fs_ondisk_sb->s_label, LABEL, 16); /* on-disk label is a fixed 16-byte field; keep it
NUL-terminated for the userspace readers below */
snprintf(fs->fs_ondisk_sb->s_label,
sizeof(fs->fs_ondisk_sb->s_label),
"%s", LABEL);
} }
set_sb_v2_flag(fs->fs_ondisk_sb, reiserfs_attrs_cleared); set_sb_v2_flag(fs->fs_ondisk_sb, reiserfs_attrs_cleared);
} }
@@ -643,7 +647,7 @@ int main(int argc, char **argv)
break; break;
case 'B': /* --badblock-list */ case 'B': /* --badblock-list */
asprintf(&badblocks_file, "%s", optarg); xasprintf(&badblocks_file, "%s", optarg);
break; break;
case 'h': /* --hash */ case 'h': /* --hash */
+9 -3
View File
@@ -3,9 +3,15 @@ lib_LTLIBRARIES = libreiserfscore.la
reiserfsdir = $(includedir)/reiserfs reiserfsdir = $(includedir)/reiserfs
reiserfs_HEADERS = reiserfs_err.h reiserfs_HEADERS = reiserfs_err.h
reiserfs_err.c reiserfs_err.h: reiserfs_err.et # compile_et writes reiserfs_err.c AND reiserfs_err.h in one pass. Keep the
$(RM) -f reisefs_err.c reiserfs_err.h # recipe on a single target: when one recipe is shared by two targets, parallel
compile_et reiserfs_err.et # make can run it concurrently for both, and one run deletes the .c while
# another job is still compiling it ("no input files"). reiserfs_err.h only
# depends on reiserfs_err.c, so the recipe always runs exactly once.
reiserfs_err.c: $(srcdir)/reiserfs_err.et
$(RM) -f reiserfs_err.c reiserfs_err.h
compile_et $(srcdir)/reiserfs_err.et
reiserfs_err.h: reiserfs_err.c reiserfs_err.h: reiserfs_err.c
BUILT_SOURCES = reiserfs_err.h reiserfs_err.c BUILT_SOURCES = reiserfs_err.h reiserfs_err.c
+1 -1
View File
@@ -1538,7 +1538,7 @@ void do_balance(struct tree_balance *tb, /* tree_balance structure
insert_ptr[0] = (struct buffer_head *)body; insert_ptr[0] = (struct buffer_head *)body;
/* we must prepare insert_key */ /* we must prepare insert_key */
if (PATH_H_B_ITEM_ORDER(tb->tb_path, 0) /*LAST_POSITION (tb->tb_path) */ if ((int)PATH_H_B_ITEM_ORDER(tb->tb_path, 0) /*LAST_POSITION (tb->tb_path) */
/*item_pos */ == -1) { /*item_pos */ == -1) {
/* get delimiting key from buffer in tree */ /* get delimiting key from buffer in tree */
copy_key(&insert_key[0].ih_key, copy_key(&insert_key[0].ih_key,
+4 -36
View File
@@ -22,7 +22,7 @@
// r5 // r5
// //
#include <asm/types.h> #include <linux/types.h>
#define DELTA 0x9E3779B9 #define DELTA 0x9E3779B9
#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */ #define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */
@@ -83,10 +83,9 @@ u32 keyed_hash(const signed char *msg, int len)
msg += 16; msg += 16;
} }
/* len <= 15 here: the while loop above consumed every full 16-byte
block, so the "len >= 16" test that used to sit here was dead. */
if (len >= 12) { if (len >= 12) {
if (len >= 16)
*(int *)0 = 0;
a = (u32) msg[0] | a = (u32) msg[0] |
(u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24; (u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24;
b = (u32) msg[4] | b = (u32) msg[4] |
@@ -101,8 +100,6 @@ u32 keyed_hash(const signed char *msg, int len)
d |= msg[i]; d |= msg[i];
} }
} else if (len >= 8) { } else if (len >= 8) {
if (len >= 12)
*(int *)0 = 0;
a = (u32) msg[0] | a = (u32) msg[0] |
(u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24; (u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24;
b = (u32) msg[4] | b = (u32) msg[4] |
@@ -114,8 +111,6 @@ u32 keyed_hash(const signed char *msg, int len)
c |= msg[i]; c |= msg[i];
} }
} else if (len >= 4) { } else if (len >= 4) {
if (len >= 8)
*(int *)0 = 0;
a = (u32) msg[0] | a = (u32) msg[0] |
(u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24; (u32) msg[1] << 8 | (u32) msg[2] << 16 | (u32) msg[3] << 24;
@@ -125,8 +120,7 @@ u32 keyed_hash(const signed char *msg, int len)
b |= msg[i]; b |= msg[i];
} }
} else { } else {
if (len >= 4) /* len <= 3 here. */
*(int *)0 = 0;
a = b = c = d = pad; a = b = c = d = pad;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
a <<= 8; a <<= 8;
@@ -190,29 +184,3 @@ u32 r5_hash(const signed char *msg, int len)
} }
return a; return a;
} }
#if 0
#include <stdio.h>
int main(void)
{
char *name = 0;
size_t n = 0;
while (1) {
getline(&name, &n, stdin);
if (!strcmp(name, "\n"))
break;
name[strlen(name) - 1] = 0;
printf("tea %lu\n, r5 %lu\nyura %lu\n",
keyed_hash(name, strlen(name)) & 0x7fffff80,
r5_hash(name, strlen(name)) & 0x7fffff80,
yura_hash(name, strlen(name)) & 0x7fffff80);
free(name);
name = 0;
n = 0;
}
}
#endif
+1 -3
View File
@@ -15,8 +15,6 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <asm/types.h> #include <linux/types.h>
#include <fcntl.h> #include <fcntl.h>
#include <malloc.h>
#include <sys/vfs.h>
#include <time.h> #include <time.h>
+2 -2
View File
@@ -506,7 +506,7 @@ int reiserfs_open_journal(reiserfs_filsys_t fs, const char *j_filename,
if (fs->fs_journal_dev == -1) if (fs->fs_journal_dev == -1)
return -1; return -1;
asprintf(&fs->fs_j_file_name, "%s", j_filename); xasprintf(&fs->fs_j_file_name, "%s", j_filename);
if (get_jp_journal_size(sb_jp(sb)) < JOURNAL_MIN_SIZE) { if (get_jp_journal_size(sb_jp(sb)) < JOURNAL_MIN_SIZE) {
reiserfs_warning(stderr, "Journal of (%lu) block size found on " reiserfs_warning(stderr, "Journal of (%lu) block size found on "
@@ -694,7 +694,7 @@ int reiserfs_create_journal(reiserfs_filsys_t fs, const char *j_device, /* journ
return 0; return 0;
} }
asprintf(&fs->fs_j_file_name, "%s", j_device); xasprintf(&fs->fs_j_file_name, "%s", j_device);
if (len < JOURNAL_MIN_SIZE + 1) { if (len < JOURNAL_MIN_SIZE + 1) {
reiserfs_warning(stderr, reiserfs_warning(stderr,
+5 -5
View File
@@ -610,7 +610,7 @@ int find_hash_in_use(const char *name, int namelen, __u32 offset,
char *code2name(unsigned int code) char *code2name(unsigned int code)
{ {
if (code >= HASH_AMOUNT || code < 0) if (code >= HASH_AMOUNT)
return NULL; return NULL;
return hashes[code].name; return hashes[code].name;
} }
@@ -1031,22 +1031,22 @@ int name_length(const char *name, int key_format)
/* key format is stored in 12 bits starting from 0-th of item_head's ih2_format*/ /* key format is stored in 12 bits starting from 0-th of item_head's ih2_format*/
__u16 get_ih_key_format(const struct item_head * ih) __u16 get_ih_key_format(const struct item_head * ih)
{ {
get_bit_field_XX(16, &ih->ih_format, 0, 12); get_bit_field_XX(16, ih_format_ptr(ih), 0, 12);
} }
__u16 get_ih_flags(const struct item_head *ih) __u16 get_ih_flags(const struct item_head *ih)
{ {
get_bit_field_XX(16, &ih->ih_format, 12, 4); get_bit_field_XX(16, ih_format_ptr(ih), 12, 4);
} }
void set_ih_key_format(struct item_head *ih, __u16 val) void set_ih_key_format(struct item_head *ih, __u16 val)
{ {
set_bit_field_XX(16, &ih->ih_format, val, 0, 12); set_bit_field_XX(16, ih_format_ptr(ih), val, 0, 12);
} }
void set_ih_flags(struct item_head *ih, __u16 val) void set_ih_flags(struct item_head *ih, __u16 val)
{ {
set_bit_field_XX(16, &ih->ih_format, val, 12, 4); set_bit_field_XX(16, ih_format_ptr(ih), val, 12, 4);
} }
/* access to fields of stat data (both v1 and v2) */ /* access to fields of stat data (both v1 and v2) */
+12 -7
View File
@@ -82,7 +82,7 @@ reiserfs_filsys_t reiserfs_open(const char *filename, int flags,
fs = getmem(sizeof(*fs)); fs = getmem(sizeof(*fs));
fs->fs_dev = fd; fs->fs_dev = fd;
fs->fs_vp = vp; fs->fs_vp = vp;
asprintf(&fs->fs_file_name, "%s", filename); xasprintf(&fs->fs_file_name, "%s", filename);
/* reiserfs super block is either in 16-th or in 2-nd 4k block of the /* reiserfs super block is either in 16-th or in 2-nd 4k block of the
device */ device */
@@ -215,7 +215,7 @@ reiserfs_filsys_t reiserfs_create(const char *filename,
} }
fs->fs_blocksize = block_size; fs->fs_blocksize = block_size;
asprintf(&fs->fs_file_name, "%s", filename); xasprintf(&fs->fs_file_name, "%s", filename);
fs->fs_format = version; fs->fs_format = version;
if (new_format) if (new_format)
@@ -403,8 +403,13 @@ void reiserfs_close(reiserfs_filsys_t fs)
reiserfs_close_ondisk_bitmap(fs); reiserfs_close_ondisk_bitmap(fs);
reiserfs_flush(fs); reiserfs_flush(fs);
reiserfs_free(fs); /* fsync must run while fs is still valid: reiserfs_free() releases
the handler, and reading fs->fs_dev after it was a use-after-free.
reiserfs_flush() has already written the journal and every dirty
buffer (free_buffers() inside reiserfs_free only validates and
frees), so the data is on the device by this point. */
fsync(fs->fs_dev); fsync(fs->fs_dev);
reiserfs_free(fs);
} }
int reiserfs_new_blocknrs(reiserfs_filsys_t fs, int reiserfs_new_blocknrs(reiserfs_filsys_t fs,
@@ -664,7 +669,7 @@ struct reiserfs_key *uget_rkey(const struct reiserfs_path *path)
struct reiserfs_key *reiserfs_next_key(const struct reiserfs_path *path) struct reiserfs_key *reiserfs_next_key(const struct reiserfs_path *path)
{ {
if (get_item_pos(path) < B_NR_ITEMS(get_bh(path)) - 1) if (get_item_pos(path) + 1 < B_NR_ITEMS(get_bh(path)))
return leaf_key(get_bh(path), get_item_pos(path) + 1); return leaf_key(get_bh(path), get_item_pos(path) + 1);
return uget_rkey(path); return uget_rkey(path);
@@ -1054,7 +1059,7 @@ int reiserfs_add_entry(reiserfs_filsys_t fs, const struct reiserfs_key *dir,
const char *name, int name_len, const char *name, int name_len,
const struct reiserfs_key *key, __u16 fsck_need) const struct reiserfs_key *key, __u16 fsck_need)
{ {
struct item_head entry_ih = { {0,}, }; struct item_head entry_ih = { 0 };
char *entry; char *entry;
int retval; int retval;
INITIALIZE_REISERFS_PATH(path); INITIALIZE_REISERFS_PATH(path);
@@ -1458,7 +1463,7 @@ void add_badblock_list(reiserfs_filsys_t fs, int replace)
} }
int reiserfs_iterate_file_data(reiserfs_filsys_t fs, int reiserfs_iterate_file_data(reiserfs_filsys_t fs,
const struct reiserfs_key const *short_key, const struct reiserfs_key *short_key,
reiserfs_file_iterate_indirect_fn indirect_fn, reiserfs_file_iterate_indirect_fn indirect_fn,
reiserfs_file_iterate_direct_fn direct_fn, reiserfs_file_iterate_direct_fn direct_fn,
void *data) void *data)
@@ -1555,7 +1560,7 @@ fail:
} }
int reiserfs_iterate_dir(reiserfs_filsys_t fs, int reiserfs_iterate_dir(reiserfs_filsys_t fs,
const struct reiserfs_key const *dir_short_key, const struct reiserfs_key *dir_short_key,
const reiserfs_iterate_dir_fn callback, void *data) const reiserfs_iterate_dir_fn callback, void *data)
{ {
INITIALIZE_REISERFS_PATH(path); INITIALIZE_REISERFS_PATH(path);
+1 -1
View File
@@ -1,4 +1,4 @@
%define reiserfsprogsversion 3.6.23 %define reiserfsprogsversion 3.6.25
Vendor: Hans Reiser Vendor: Hans Reiser
Distribution: Hans Reiser Distribution: Hans Reiser
+5 -2
View File
@@ -48,9 +48,11 @@ static long long int calc_new_fs_size(unsigned long count,
case 'G': case 'G':
case 'g': case 'g':
bytes *= 1024; bytes *= 1024;
/* fall through */
case 'M': case 'M':
case 'm': case 'm':
bytes *= 1024; bytes *= 1024;
/* fall through */
case 'K': case 'K':
case 'k': case 'k':
bytes *= 1024; bytes *= 1024;
@@ -58,7 +60,7 @@ static long long int calc_new_fs_size(unsigned long count,
blocks = bytes / bs; blocks = bytes / bs;
return rel ? count + blocks : blocks; return rel ? (long long int)count + blocks : blocks;
} }
/* print some fs parameters */ /* print some fs parameters */
@@ -215,6 +217,7 @@ int main(int argc, char *argv[])
reiserfs_exit(1, reiserfs_exit(1,
"Missing argument to -j option"); "Missing argument to -j option");
jdevice_name = optarg; jdevice_name = optarg;
break;
case 'f': case 'f':
opt_force = 1; opt_force = 1;
break; break;
@@ -292,7 +295,7 @@ int main(int argc, char *argv[])
block_count_new = bytes_count_str ? block_count_new = bytes_count_str ?
calc_new_fs_size(get_sb_block_count(sb), calc_new_fs_size(get_sb_block_count(sb),
fs->fs_blocksize, bytes_count_str) : fs->fs_blocksize, bytes_count_str) :
count_blocks(devname, fs->fs_blocksize); (long long int)count_blocks(devname, fs->fs_blocksize);
if (resizer_check_fs_size(fs, block_count_new)) if (resizer_check_fs_size(fs, block_count_new))
return 1; return 1;
+106
View File
@@ -0,0 +1,106 @@
# Ambiente de testes em VM
Este documento descreve o ambiente de validação do reiserfsprogs em máquinas
virtuais (libvirt/KVM), usado para exercitar o build e as ferramentas em
distribuições diferentes da máquina de desenvolvimento.
O ambiente é **efêmero**: as VMs ficam em `/var/tmp/vmtest` e não fazem parte
do repositório. Os scripts que o reproduzem estão em `tests/vm/`.
## Requisitos do host
| Componente | Verificação |
|---|---|
| KVM | `ls -l /dev/kvm` |
| libvirt (user session) | `virsh -c qemu:///session list --all` |
| `virt-install`, `qemu-img` | `which virt-install qemu-img` |
| `guestfs-tools` (offline) | `which virt-customize` |
| `mkisofs` ou `xorriso` | `which mkisofs xorriso` |
No Fedora: `sudo dnf install libvirt-daemon-kvm qemu-kvm virt-install
guestfs-tools genisoimage`.
O ambiente usa **qemu:///session** (sem root). Não requer a rede `default`
do libvirt: a conectividade vem do backend `passt` em modo *user*.
## Topologia
```mermaid
graph LR
H[host] -->|qemu:///session| D[rfs-debian<br/>Debian 12 · gcc + clang]
H -->|qemu:///session| A[rfs-alpine<br/>Alpine 3.20 · gcc/musl]
D -->|passt :2222| S[ssh opcional]
A -->|passt| S
H -->|attach-disk| V[vdb: fontes + run_tests.sh]
```
Cada VM recebe:
* imagem cloud (qcow2) redimensionada para 8 GiB;
* um disco de dados `vdb` (ext4) com `rfs-src.tar.gz` e `run_tests.sh`;
* acesso por **console serial** (pty do libvirt), que é o canal de
automação usado — independente de rede.
## Por que console serial e não SSH
Nas imagens cloud o `sshd` demora a subir e o `cloud-init` nem sempre aplica
a senha (o datasource NoCloud exige rótulo `cidata` e o CD precisa estar no
Barramento certo). O console serial está disponível assim que o getty sobe e
é suficiente para: autenticar, copiar arquivos (já estão no `vdb`) e rodar
qualquer comando. Por isso o `tests/vm/guestcmd.py` dirige o pty.
## Ciclo de uso
```sh
cd tests/vm
# 1. preparar imagens e VMs (uma vez)
./setup_env.sh
# 2. rodar a bateria em uma VM
./run_in_vm.sh rfs-debian # gcc do guest
CC=clang ./run_in_vm.sh rfs-debian # clang do guest
./run_in_vm.sh rfs-alpine # esperado FALHAR no configure (musl)
# 3. destruir tudo
./teardown_env.sh
```
No Debian, instale o clang no guest uma vez (a rede user-mode do `passt`
precisa de resolver manual antes do apt):
```sh
python3 guestcmd.py rfs-debian 'ip addr add 192.168.1.5/24 dev enp1s0;
ip link set enp1s0 up; ip route add default via 192.168.1.1;
echo nameserver 8.8.8.8 > /etc/resolv.conf;
apt-get update -qq && DEBIAN_FRONTEND=noninteractive \
apt-get install -y -qq clang'
```
## Compiladores suportados
O projeto compila **sem warnings** com `-Wall -Wextra` em gcc (>= 12) e
clang (>= 14; validado em 14.0.6 e 22.1.8). Os mesmos builds são exercitados
sob **AddressSanitizer + UndefinedBehaviorSanitizer** (`CC=clang
CFLAGS="-g -O1 -fsanitize=address,undefined ..."`) tanto no host (CI) quanto
manualmente; resultados em `RESULTS.md`.
## O que a bateria cobre (`tests/vm/run_tests.sh`)
Executado **dentro** do guest, sobre o código extraído do `vdb`:
1. `autogen.sh && configure --enable-warnings && make -j` — build limpo;
2. contagem de warnings (esperado: 0);
3. `mkreiserfs` em imagem de 64 MiB;
4. `reiserfsck --check` (esperado: *No corruptions found*);
5. `debugreiserfs -S` (superblock consistente);
6. `reiserfstune -l` (troca de label);
7. `resize_reiserfs` 128 MiB → 256 MiB;
8. `reiserfsck` pós-resize;
9. rollback: `-R` grava, `--rollback-fsck-changes` restaura, arquivo
inválido é rejeitado.
## Resultados obtidos
Ver `tests/vm/RESULTS.md`.
+208
View File
@@ -0,0 +1,208 @@
# Resultados dos testes em VM
Bateria executada com `tests/vm/run_in_vm.sh` em duas distribuições, ambas em
KVM (`qemu:///session`), com o código de `293e6a7`.
## Ambiente
| | Debian 12 (bookworm) | Alpine 3.20.3 |
|---|---|---|
| Kernel | 6.1.0-54-cloud-amd64 | 6.6.49-0-virt |
| libc | glibc 2.36 | **musl 1.2.5** |
| gcc | 12.2.0 | 13.2.1 |
| init | systemd + cloud-init | OpenRC + cloud-init |
## Resultado por item
| # | Teste | Debian | Alpine |
|---|---|---|---|
| 1 | `autogen.sh` | passa | passa |
| 1 | `configure --enable-warnings` | passa | **falha** (ver abaixo) |
| 1 | `make -j` | passa, **0 warnings** | não alcançado |
| 2 | `mkreiserfs` | passa | não alcançado |
| 3 | `reiserfsck --check` | passa | não alcançado |
| 4 | `debugreiserfs -S` | passa | não alcançado |
| 5 | `reiserfstune -l` | passa | não alcançado |
| 6 | `resize_reiserfs` 128→256 MiB | passa | não alcançado |
| 7 | `fsck` pós-resize | passa | não alcançado |
| 8 | rollback: grava | passa | não alcançado |
| 8 | rollback: restaura | passa | não alcançado |
| 8 | rollback: magic inválida rejeitada | passa | não alcançado |
| 8 | rollback: arquivo inexistente rejeitado | passa | não alcançado |
**Debian: 12/12.** Alpine: bloqueado no item 1.
## Achado 1 (bloqueante): não compila em musl
O `configure` do Alpine falha com:
```
checking for register_printf_modifier... no
checking for register_printf_specifier... no
configure: error: reiserfsprogs requires a method to add printf modifiers
```
Causa: `reiserfscore/prints.c` usa `register_printf_specifier(3)`, extensão
**glibc-only** (`<printf.h>`). O musl não fornece `printf.h` nem essas
funções. O `configure.ac` trata isso como erro fatal explícito — o
comportamento é correto, mas o projeto **não compila fora da glibc**.
Alcance medido:
| Especificador | Uso |
|---|---|
| `%k` | 139 |
| `%K` | 106 |
| `%H` | 52 |
| `%M` | 25 |
| `%b` | 9 |
| `%U` | 4 |
| `%y` | 2 |
| `%30H`, `%20y`, `%20k`, `%20K` | 4 |
| **total** | **341** |
Todos passam por `reiserfs_warning()` / `reiserfs_info()`. Portar exigiria
substituir o mecanismo por funções de formatação explícitas (por exemplo
`char *format_key(const struct reiserfs_key *)` com `%s`), uma mudança de API
interna ampla — não um ajuste local.
**Impacto prático**: reiserfsprogs fica restrito à glibc. Como o único uso
atual da ferramenta é resgate de dados, isso importa: live images de
recuperação baseadas em Alpine/musl não conseguem compilar o projeto.
## Achado 2 (corrigido nesta rodada): warning real no gcc 12
O gcc 12.2 do Debian acusou, duas vezes:
```
../include/reiserfs_fs.h:63:54: warning: ‘relocated_ih’ may be used uninitialized
```
O gcc 16 do host não acusa. Investigação:
* `fsck/semantic_check.c:620` passa `&relocated_ih` para
`check_semantic_pass()`;
* o bloco que escreveria `*new_ih` está **comentado** (linhas 471–486);
* a chamada em `check_check_regular_file()` (linha 466) passa
`NULL` como `new_ih`, com o comentário `/* relocate ? new_ih : */ NULL`;
* portanto `RELOCATED` nunca é retornado por esse caminho e
`case RELOCATED:` (linha 665) leria `relocated_ih` não inicializado.
Não é alcançável hoje, mas é uma leitura potencial de lixo de pilha atrás de
um `#case` que o compilador não consegue descartar. Correção: inicialização
explícita `= { 0 }` com comentário explicando por que permanece zero.
Verificado: **0 warnings no gcc 12.2 após a correção**.
Em `fsck/semantic_rebuild.c` o caminho equivalente é seguro: o bloco
`if (relocate) { ... *new_ih = *ih; retval = RELOCATED; }` está ativo
(linhas 808–822), então `new_ih` é escrito antes do `case RELOCATED:`
(linha 999).
## Validação com clang
Ferramentaria: Fedora 44 (host) com **clang 22.1.8**, Debian 12 na VM com
**clang 14.0.6**. Todos os builds abaixo usam `--enable-warnings`
(-Wall -Wextra).
| Build | Resultado |
|---|---|
| host clang in-tree, -j | 0/5 falhas, **0 warnings** |
| host clang out-of-tree, -j | passa, 0 warnings |
| host clang + ASan/UBSan out-of-tree, -j | 0/3 falhas, 0 warnings |
| host gcc out-of-tree, -j | 3/3 limpo |
| host `make distcheck` | passa (VPATH + install/uninstall + tarballs) |
| VM Debian clang 14.0.6, bateria completa | **12/12, 0 warnings** |
### O que clang expôs (e foi corrigido)
1. **Corrida de build paralela em `compile_et`** (`reiserfscore/Makefile.am`):
`reiserfs_err.c` e `reiserfs_err.h` eram alvos de UMA mesma receita; com
`-j` o make executa a receita duas vezes em paralelo e um processo apaga
o `.c` enquanto o outro compila → `clang: error: no input files`.
Reproduzido com clang+ASan; a correção vale para qualquer compilador.
A receita ficou atrelada apenas a `reiserfs_err.c`, e
`reiserfs_err.h: reiserfs_err.c` apenas ordena os dois. 5/5 builds
paralelos limpos após a correção.
2. **Build out-of-tree quebrado** (pré-existente, também com gcc): a regra
referenciava `reiserfs_err.et` sem `$(srcdir)/`. Corrigido junto.
3. **`-Waddress-of-packed-member`** (só o clang aponta; 3 sítios):
`fsck/semantic_check.c` e `fsck/semantic_rebuild.c` passavam
`&tmp_deh->deh2_dir_id` como argumento `%K`, que espera
`struct reiserfs_key *`. Funcionava por coincidência de offsets
(`deh2_dir_id` em 4 lido como `k2_dir_id` em 0 do cast). Agora montam uma
`reiserfs_key` local via `set_key_dirid`/`set_key_objectid` — saída
idêntica, sem depender de coincidência.
4. **`-Wnull-dereference` em `reiserfscore/hashes.c`**: quatro stores mortos
`*(int *)0 = 0` protegidos por condições inalcançáveis (após o loop
`while (len >= 16)`, `len <= 15`; os ramos testavam `len >= 16/12/8/4`).
Removidos. Comportamento verificado: saída de `keyed_hash`, `yura_hash` e
`r5_hash` para 16 entradas (vazio a 17 bytes) **byte a byte idêntica**
antes e depois (diff vazio).
5. **heap-use-after-free real, encontrado por ASan com clang**:
`reiserfs_close()` chamava `fsync(fs->fs_dev)` DEPOIS de
`reiserfs_free()` liberar `fs` — leitura pendente de 4 bytes em
memória libertada (o ASan apontou exatamente `reiserfslib.c:406/407`; o
gcc nunca acusou porque o heap ainda "devolve" o valor velho). Corrigido
movendo o `fsync` para antes do free — a escrita já está no dispositivo
neste ponto (`reiserfs_flush()` despeja journal e buffers; os
`free_buffers()` de `reiserfs_free()` apenas validam e libertam, sem
escrever).
6. **segundo heap-use-after-free, também no caminho de rollback**
(`fsck/main.c:1313`, `fsck_rollback()`): a função fazia `freemem(fs)` e
depois imprimia o banner final via `fsck_progress(...)`, que
desreferencia `fs` (`fsck_data(fs)` etc.). Capturado por ASan em uma
reexecução explícita do `--rollback-fsck-changes` com `halt_on_error`
(a rodada inicial de smoke não exercitou esse ramo até o fim).
Corrigido: o banner é impresso antes da limpeza, na mesma ordem usada
por `the_end()` e pelos caminhos de rebuild. Revalidado: save, restore,
rejeição de magic inválida e resize sob ASan+UBSan com `halt_on_error=1`
— **zero erros**, saída funcional inalterada.
Ambos os builds saneados (mkfs, check, tune, debugreiserfs, resize
64→128 MiB e rollback completo) passam com **zero erros**; a bateria
completa no host gcc continua 12/12.
### Não-bugs registrados
- LeakSanitizer: 136 bytes, 1 alocação em `debugreiserfs` (`getmem` no
`main`, de vida de processo; o `exit` recolhe). Aceitável para
ferramenta one-shot; por isso o job de CI com sanitizadores roda com
`detect_leaks=0`.
- `AddressSanitizer: WARNING: unexpected format specifier ... %U`: o
interceptor do ASan não conhece os especificadores customizados do
projeto. Informativo, uma vez por processo.
## Achado 3: limitação do ambiente (não do código)
Nas imagens cloud o `cloud-init` não aplicou a senha do usuário: o datasource
NoCloud exige o rótulo `cidata` e o CD correto, e o `sshd` demora a subir.
O console serial, porém, fica disponível assim que o getty sobe — por isso o
`tests/vm/guestcmd.py` dirige o pty e é o canal de automação usado. Isso não
afeta o produto; afeta apenas como os testes são conduzidos.
Além disso, a rede user-mode (`passt`) não entrega DHCP nem DNS por padrão: a
interface sobe como `enp1s0` mas fica sem IP. Foi necessário configurar IP e
resolver manualmente para instalar pacotes nas VMs.
## Como reproduzir
```sh
cd tests/vm
./setup_env.sh # imagens + VMs (uma vez)
./run_in_vm.sh rfs-debian # gcc: build + bateria completa
CC=clang ./run_in_vm.sh rfs-debian # clang no guest (instalar antes: ver README)
./run_in_vm.sh rfs-alpine # deve falhar no configure (esperado)
./teardown_env.sh
# host: builds paralelos in-tree e out-of-tree, distcheck
make distcheck
CC=clang ./configure --enable-warnings && make -j"$(nproc)"
```
Detalhes do ambiente, topologia e limitações: `tests/vm/README.md`.
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env python3
"""Roda um comando no guest atraves do console serial do libvirt.
O console serial e o unico canal garantido nas imagens cloud minimas: nao
depende de sshd, de cloud-init nem de rede. Este script sincroniza com o
prompt, autentica (root/rfstest) e executa o comando, lendo a saida do pty
em blocos pequenos para evitar a sobreposicao de eco que ocorre em comandos
longos.
Uso:
guestcmd.py <dominio> '<comando>'
guestcmd.py <dominio> -f <arquivo> # executa um script local
"""
import base64
import os
import re
import select
import subprocess
import sys
import time
USER = "root"
PASSWORD = "rfstest"
def console_pty(domain):
xml = subprocess.run(["virsh", "dumpxml", domain],
capture_output=True, text=True).stdout
m = (re.search(r"<console[^>]*>\s*<source path='([^']+)'", xml)
or re.search(r"<serial[^>]*>\s*<source path='([^']+)'", xml))
if not m:
sys.exit(f"console serial nao encontrada para {domain}")
return m.group(1)
class Console:
def __init__(self, path):
self.fd = os.open(path, os.O_RDWR | os.O_NOCTTY)
def send(self, s):
os.write(self.fd, s.encode())
def drain(self, seconds):
buf = b""
end = time.time() + seconds
while time.time() < end:
r, _, _ = select.select([self.fd], [], [], 0.3)
if not r:
continue
try:
chunk = os.read(self.fd, 65536)
except OSError:
break
if not chunk:
break
buf += chunk
return buf
def login(self):
self.send("\n")
time.sleep(1.0)
snap = self.drain(2.5).decode(errors="replace").lower()
if "login:" not in snap:
return # ja autenticado
self.send(USER + "\n")
self.drain(3.0)
self.send(PASSWORD + "\n")
time.sleep(2.0)
self.drain(2.0)
def run(self, command, timeout=120.0):
"""Executa e devolve a saida, sincronizando por marcador no pty."""
out_path = "/tmp/.guestcmd.out"
self.send(f"rm -f {out_path}; ( {command} ) > {out_path} 2>&1; "
f"echo __GC_DONE__\n")
deadline = time.time() + timeout
while time.time() < deadline:
if b"__GC_DONE__" in self.drain(2.0):
break
else:
return "(timeout)\n"
# tamanho em bytes
self.send(f"wc -c < {out_path}\n")
time.sleep(1.2)
raw = self.drain(2.0).decode(errors="replace")
nums = re.findall(r"\d+", raw)
size = int(nums[-1]) if nums else 0
# ler em blocos base64 (evita eco/sobreposicao)
data = b""
while len(data) < size:
self.send(f"dd if={out_path} bs=1 skip={len(data)} count=150 "
f"2>/dev/null | base64 -w0; echo\n")
time.sleep(0.8)
enc = self.drain(2.0).decode(errors="replace")
token = "".join(re.findall(r"[A-Za-z0-9+/=]{4,}", enc))
if not token:
break
try:
data += base64.b64decode(token + "=" * (-len(token) % 4))
except Exception:
break
return data.decode(errors="replace")
def main():
if len(sys.argv) < 3:
sys.exit(__doc__)
domain = sys.argv[1]
console = Console(console_pty(domain))
console.login()
if sys.argv[2] == "-f":
with open(sys.argv[3]) as fh:
script = fh.read().replace("'", "'\\''")
command = f"sh -c '{script}'"
else:
command = sys.argv[2]
sys.stdout.write(console.run(command))
sys.stdout.write("\n")
if __name__ == "__main__":
main()
+58
View File
@@ -0,0 +1,58 @@
#!/bin/sh
# Envia o codigo-fonte e o script de teste para dentro de uma VM e executa.
#
# Uso: run_in_vm.sh <dominio>
# CC=clang run_in_vm.sh rfs-debian # build no guest com clang
# Requer: guestcmd.py (mesmo diretorio), mkisofs, libvirt, sudo (loop mount).
set -e
DOMAIN=${1:?uso: run_in_vm.sh <dominio>}
HERE=$(cd "$(dirname "$0")" && pwd)
REPO=$(cd "$HERE/../.." && pwd)
WORK=${WORK:-/var/tmp/vmtest}
echo "== empacotando o codigo-fonte =="
tar czf "$WORK/rfs-src.tar.gz" -C "$REPO" \
--exclude=.git --exclude='*.o' --exclude='*.lo' --exclude='*.la' \
--exclude='.libs' --exclude=build-aux --exclude=m4 \
--exclude=autom4te.cache --exclude=config.h --exclude=config.log \
--exclude=config.status --exclude=libtool \
--exclude='Makefile' --exclude='Makefile.in' --exclude=configure \
--exclude=aclocal.m4 --exclude='*.8' --exclude=stamp-h1 \
--exclude='reiserfscore/reiserfs_err.c' \
--exclude='reiserfscore/reiserfs_err.h' \
.
echo "== montando o disco de dados =="
rm -rf /tmp/vmdata.$$ && mkdir -p /tmp/vmdata.$$
sudo mount -o loop "$WORK/data.img" /tmp/vmdata.$$
sudo cp "$WORK/rfs-src.tar.gz" "$HERE/run_tests.sh" /tmp/vmdata.$$
sync
sudo umount /tmp/vmdata.$$ && rmdir /tmp/vmdata.$$
echo "== transferindo para $DOMAIN =="
ATTACH=/tmp/vdb.$$.xml
cat > "$ATTACH" <<EOF
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='$WORK/data.img'/>
<target dev='vdb' bus='virtio'/>
</disk>
EOF
virsh attach-device "$DOMAIN" "$ATTACH" --live >/dev/null 2>&1 || true
rm -f "$ATTACH"
echo "== extraindo e rodando os testes ${CC:+com CC=$CC} =="
"$HERE/guestcmd.py" "$DOMAIN" "
set -e
mkdir -p /mnt/data
# remount forcado: o host trocou o conteudo do disco por baixo do guest
umount /mnt/data 2>/dev/null || true
mount -o ro /dev/vdb /mnt/data
rm -rf /root/rfs
mkdir -p /root/rfs
tar xzf /mnt/data/rfs-src.tar.gz -C /root/rfs
cp /mnt/data/run_tests.sh /root/run_tests.sh
chmod +x /root/run_tests.sh
SRC=/root/rfs ${CC:+CC=$CC }sh /root/run_tests.sh
" 1800
+135
View File
@@ -0,0 +1,135 @@
#!/bin/sh
# Bateria de testes do reiserfsprogs, executada DENTRO do guest.
# Espera receber o codigo-fonte em $SRC (padrao: /root/rfs).
#
# Uso (dentro da VM):
# SRC=/root/rfs sh run_tests.sh
set -u
SRC=${SRC:-/root/rfs}
PASS=0
FAIL=0
ok() { echo "PASS: $1"; PASS=$((PASS + 1)); }
bad() { echo "FAIL: $1"; FAIL=$((FAIL + 1)); }
cd "$SRC" || { echo "FAIL: $SRC nao existe"; exit 1; }
echo "=== ambiente ==="
uname -srm
echo "CC=${CC:-cc}"
${CC:-cc} --version 2>/dev/null | head -1 || echo "sem ${CC:-cc}"
ldd --version 2>&1 | head -1
echo
echo "=== 1. bootstrap + build com -Wall -Wextra ==="
make distclean >/dev/null 2>&1
if ./autogen.sh >/tmp/ag.log 2>&1 && ./configure --enable-warnings >/tmp/cfg.log 2>&1 \
&& make -j"$(nproc 2>/dev/null || echo 2)" >/tmp/mk.log 2>&1; then
ok "autogen.sh + configure + make"
else
bad "build"
echo "--- configure ---"; tail -15 /tmp/cfg.log
echo "--- make ---"; tail -25 /tmp/mk.log
echo
echo "RESULTADO: $PASS passaram, $FAIL falharam"
exit 1
fi
# grep -c retorna 1 quando nao ha ocorrencias; capturar so a primeira linha
WARN=$(grep -c "warning:" /tmp/mk.log 2>/dev/null | head -1)
WARN=${WARN:-0}
if [ "$WARN" = "0" ]; then
ok "0 warnings com -Wall -Wextra"
else
bad "$WARN warnings"
grep "warning:" /tmp/mk.log | head -10
fi
echo
echo "=== 2. mkfs ==="
rm -f /tmp/t.img
dd if=/dev/zero of=/tmp/t.img bs=1M count=64 status=none 2>/dev/null
if ./mkreiserfs/mkreiserfs -f -q -l TESTVOL /tmp/t.img >/dev/null 2>&1; then
ok "mkreiserfs"
else
bad "mkreiserfs"
fi
echo
echo "=== 3. reiserfsck --check ==="
if ./fsck/reiserfsck --check -f -q -y /tmp/t.img 2>&1 | grep -q "No corruptions found"; then
ok "reiserfsck --check"
else
bad "reiserfsck --check"
fi
echo
echo "=== 4. debugreiserfs -S ==="
if ./debugreiserfs/debugreiserfs -S /tmp/t.img 2>&1 | grep -q "Filesystem state: consistent"; then
ok "debugreiserfs -S"
else
bad "debugreiserfs -S"
fi
echo
echo "=== 5. reiserfstune -l ==="
if ./tune/reiserfstune -l NEWLABEL -f /tmp/t.img >/dev/null 2>&1; then
ok "reiserfstune -l"
else
bad "reiserfstune -l"
fi
echo
echo "=== 6. resize 128M -> 256M ==="
rm -f /tmp/big.img
dd if=/dev/zero of=/tmp/big.img bs=1M count=128 status=none 2>/dev/null
./mkreiserfs/mkreiserfs -f -q /tmp/big.img >/dev/null 2>&1
truncate -s 256M /tmp/big.img
if ./resize_reiserfs/resize_reiserfs -s +128M -f /tmp/big.img 2>&1 \
| grep -q "Resizing finished successfully"; then
ok "resize_reiserfs"
else
bad "resize_reiserfs"
fi
echo
echo "=== 7. fsck pos-resize ==="
if ./fsck/reiserfsck --check -f -q -y /tmp/big.img 2>&1 | grep -q "No corruptions found"; then
ok "fsck apos resize"
else
bad "fsck apos resize"
fi
echo
echo "=== 8. rollback (save/restore/reject) ==="
cp /tmp/t.img /tmp/rb.img
rm -f /tmp/rb.dat
./fsck/reiserfsck --fix-fixable -y -q -R /tmp/rb.dat /tmp/rb.img >/dev/null 2>&1
if [ -f /tmp/rb.dat ]; then
ok "rollback file criado"
else
bad "rollback file nao criado"
fi
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rb.dat /tmp/rb.img 2>&1 \
| grep -q "blocks restored"; then
ok "rollback-fsck-changes"
else
bad "rollback-fsck-changes"
fi
head -c 100 /dev/urandom > /tmp/rollback-invalido.dat
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/rollback-invalido.dat /tmp/rb.img 2>&1 \
| grep -q "does not look like a rollback file"; then
ok "rollback com magic invalida rejeitado"
else
bad "rollback com magic invalida aceito"
fi
if ./fsck/reiserfsck --rollback-fsck-changes -y -q -R /tmp/nao-existe.dat /tmp/rb.img 2>&1 \
| grep -q "Cannot stat rollback file"; then
ok "rollback inexistente rejeitado"
else
bad "rollback inexistente aceito"
fi
echo
echo "RESULTADO: $PASS passaram, $FAIL falharam"
[ "$FAIL" -eq 0 ]
+91
View File
@@ -0,0 +1,91 @@
#!/bin/sh
# Prepara o ambiente de VMs: imagens, seed e maquinas virtuais.
# Requer: libvirt (qemu:///session), virt-install, qemu-img, mkisofs.
set -e
HERE=$(cd "$(dirname "$0")" && pwd)
WORK=${WORK:-/var/tmp/vmtest}
CONN=qemu:///session
mkdir -p "$WORK"
cd "$WORK"
fetch() {
url=$1; out=$2
[ -s "$out" ] && { echo " $out ja existe"; return; }
echo " baixando $out"
curl -fsSL -o "$out.part" "$url"
mv "$out.part" "$out"
}
echo "== imagens =="
fetch https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 debian.qcow2
fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.3-x86_64-bios-cloudinit-r0.qcow2 alpine.qcow2
echo "== redimensionando =="
for img in debian.qcow2 alpine.qcow2; do
size=$(qemu-img info --output=json "$img" | grep -oP '"virtual-size":\s*\K[0-9]+')
if [ "${size:-0}" -lt 8589934592 ]; then
qemu-img resize "$img" 8G
else
echo " $img ja tem 8G"
fi
done
echo "== disco de dados (fontes + script de teste) =="
if [ ! -s data.img ]; then
dd if=/dev/zero of=data.img bs=1M count=8 status=none
mkfs.ext4 -q -F data.img
fi
echo "== seed cloud-init =="
cat > user-data <<'EOF'
#cloud-config
hostname: rfs-test
ssh_pwauth: true
disable_root: false
users:
- name: root
lock_passwd: false
plain_text_passwd: rfstest
chpasswd:
expire: false
EOF
cat > meta-data <<'EOF'
instance-id: rfs-test-1
local-hostname: rfs-test
EOF
cat > network-config <<'EOF'
version: 2
ethernets:
eth0:
dhcp4: true
EOF
rm -f seed.iso
mkisofs -output seed.iso -volid cidata -joliet -rock \
user-data meta-data network-config >/dev/null 2>&1
echo "== definindo VMs =="
define_vm() {
name=$1; img=$2; variant=$3; mem=$4
if virsh -c "$CONN" dominfo "$name" >/dev/null 2>&1; then
echo " $name ja existe"
return
fi
virt-install -c "$CONN" \
--name "$name" \
--memory "$mem" --vcpus 2 \
--disk "path=$WORK/$img,format=qcow2,bus=virtio" \
--disk "path=$WORK/seed.iso,device=cdrom" \
--os-variant "$variant" \
--network user,model=virtio \
--graphics none --console pty \
--import --noautoconsole >/dev/null
echo " criada: $name"
}
define_vm rfs-debian debian.qcow2 debian12 2048
define_vm rfs-alpine alpine.qcow2 alpinelinux3.20 1024
echo
echo "Pronto. Use tests/vm/run_in_vm.sh <vm> para testar."
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
# Destrói as VMs do ambiente de teste. Nao remove as imagens baixadas.
set -e
CONN=qemu:///session
for d in rfs-debian rfs-alpine; do
if virsh -c "$CONN" dominfo "$d" >/dev/null 2>&1; then
virsh -c "$CONN" destroy "$d" >/dev/null 2>&1 || true
virsh -c "$CONN" undefine "$d" --remove-all-storage >/dev/null 2>&1 || true
echo "removida: $d"
else
echo "ausente: $d"
fi
done
+8 -3
View File
@@ -6,6 +6,7 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "tune.h" #include "tune.h"
#include "misc.h"
#include <getopt.h> #include <getopt.h>
#include <stdarg.h> #include <stdarg.h>
@@ -487,11 +488,11 @@ int main(int argc, char **argv)
Force++; Force++;
break; break;
case 'b': /* --add-badblocks */ case 'b': /* --add-badblocks */
asprintf(&badblocks_file, "%s", optarg); xasprintf(&badblocks_file, "%s", optarg);
Bads = 1; Bads = 1;
break; break;
case 'B': /* --badblocks */ case 'B': /* --badblocks */
asprintf(&badblocks_file, "%s", optarg); xasprintf(&badblocks_file, "%s", optarg);
Bads = 2; Bads = 2;
break; break;
case 'u': case 'u':
@@ -675,7 +676,11 @@ int main(int argc, char **argv)
if (strlen(LABEL) > 16) if (strlen(LABEL) > 16)
message message
("Specified LABEL is longer then 16 characters, will be truncated\n"); ("Specified LABEL is longer then 16 characters, will be truncated\n");
strncpy(fs->fs_ondisk_sb->s_label, LABEL, 16); /* on-disk label is a fixed 16-byte field; keep it
NUL-terminated for the userspace readers below */
snprintf(fs->fs_ondisk_sb->s_label,
sizeof(fs->fs_ondisk_sb->s_label),
"%s", LABEL);
need_dirty = 1; need_dirty = 1;
} }
if (Max_mnt_count && if (Max_mnt_count &&