Files
reiserfsprogs/lib/io.c
T
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

1017 lines
25 KiB
C

/*
* Copyright 1996-2004 by Hans Reiser, licensing governed by
* reiserfsprogs/README
*/
#include "io.h"
#include <string.h>
#include <errno.h>
#include <linux/types.h>
void check_memory_msg(void)
{
fprintf(stderr,
"\nThe problem has occurred looks like a hardware problem (perhaps\n"
"memory). Send us the bug report only if the second run dies at\n"
"the same place with the same block number.\n");
}
static void check_hd_msg(void)
{
fprintf(stderr,
"\nThe problem has occurred looks like a hardware problem. If you have\n"
"bad blocks, we advise you to get a new hard drive, because once you\n"
"get one bad block that the disk drive internals cannot hide from\n"
"your sight,the chances of getting more are generally said to become\n"
"much higher (precise statistics are unknown to us), and this disk\n"
"drive is probably not expensive enough for you to you to risk your\n"
"time and data on it. If you don't want to follow that follow that\n"
"advice then if you have just a few bad blocks, try writing to the\n"
"bad blocks and see if the drive remaps the bad blocks (that means\n"
"it takes a block it has in reserve and allocates it for use for\n"
"of that block number). If it cannot remap the block, use badblock\n"
"option (-B) with reiserfs utils to handle this block correctly.\n");
}
static int is_bad_block(unsigned long block)
{
#ifdef IO_FAILURE_EMULATION
/* this array similates bad blocks on the device */
unsigned long bad_blocks[] = {
8208, 8209, 8210
/*, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19*/
};
int i;
for (i = 0; i < sizeof(bad_blocks) / sizeof(bad_blocks[0]); i++)
if (bad_blocks[i] == block)
return 1;
#endif
return 0;
}
/* All buffers are in double linked cycled list. If getblk found buffer with
wanted block number in hash queue it moves buffer to the end of list. */
static int g_nr_buffers;
static unsigned long buffers_memory;
/* create buffers until we spend this fraction of system memory, this
** is a hard limit on the amount of buffer ram used
*/
#define BUFFER_MEMORY_FRACTION 10
/* number of bytes in local buffer cache before we start forcing syncs
** of dirty data and reusing unused buffers instead of allocating new
** ones. If a flush doesn't find reusable buffers, new ones are
** still allocated up to the BUFFER_MEMORY_FRACTION percentage
**
*/
#define BUFFER_SOFT_LIMIT (500 * 1024)
static unsigned long buffer_soft_limit = BUFFER_SOFT_LIMIT;
#define NR_HASH_QUEUES 4096
static struct buffer_head *g_a_hash_queues[NR_HASH_QUEUES];
static struct buffer_head *Buffer_list_head;
static struct buffer_head *g_free_buffers = NULL;
static struct buffer_head *g_buffer_heads;
static int buffer_hits = 0;
static int buffer_misses = 0;
static int buffer_reads = 0;
static int buffer_writes = 0;
static void _show_buffers(struct buffer_head **list, int dev,
unsigned long size)
{
int all = 0;
int dirty = 0;
int in_use = 0; /* count != 0 */
int free = 0;
struct buffer_head *next;
next = *list;
if (!next)
return;
for (;;) {
if (next->b_dev == dev && next->b_size == size) {
all++;
if (next->b_count != 0) {
in_use++;
}
if (buffer_dirty(next)) {
dirty++;
}
if (buffer_clean(next) && next->b_count == 0) {
free++;
}
}
next = next->b_next;
if (next == *list)
break;
}
printf
("show_buffers (dev %d, size %lu): free %d, count != 0 %d, dirty %d, "
"all %d\n", dev, size, free, in_use, dirty, all);
}
static void show_buffers(int dev, int size)
{
_show_buffers(&Buffer_list_head, dev, size);
_show_buffers(&g_free_buffers, dev, size);
}
static void insert_into_hash_queue(struct buffer_head *bh)
{
int index = bh->b_blocknr % NR_HASH_QUEUES;
if (bh->b_hash_prev || bh->b_hash_next)
die("insert_into_hash_queue: hash queue corrupted");
if (g_a_hash_queues[index]) {
g_a_hash_queues[index]->b_hash_prev = bh;
bh->b_hash_next = g_a_hash_queues[index];
}
g_a_hash_queues[index] = bh;
}
static void remove_from_hash_queue(struct buffer_head *bh)
{
if (bh->b_hash_next == NULL && bh->b_hash_prev == NULL
&& bh != g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES])
/* (b_dev == -1) ? */
return;
if (bh == g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES]) {
if (bh->b_hash_prev)
die("remove_from_hash_queue: hash queue corrupted");
g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES] =
bh->b_hash_next;
}
if (bh->b_hash_next)
bh->b_hash_next->b_hash_prev = bh->b_hash_prev;
if (bh->b_hash_prev)
bh->b_hash_prev->b_hash_next = bh->b_hash_next;
bh->b_hash_prev = bh->b_hash_next = NULL;
}
static void put_buffer_list_end(struct buffer_head **list,
struct buffer_head *bh)
{
struct buffer_head *last = NULL;
if (bh->b_prev || bh->b_next)
die("put_buffer_list_end: buffer list corrupted");
if (*list == NULL) {
bh->b_next = bh;
bh->b_prev = bh;
*list = bh;
} else {
last = (*list)->b_prev;
bh->b_next = last->b_next;
bh->b_prev = last;
last->b_next->b_prev = bh;
last->b_next = bh;
}
}
static void remove_from_buffer_list(struct buffer_head **list,
struct buffer_head *bh)
{
if (bh == bh->b_next) {
*list = NULL;
} else {
bh->b_prev->b_next = bh->b_next;
bh->b_next->b_prev = bh->b_prev;
if (bh == *list)
*list = bh->b_next;
}
bh->b_next = bh->b_prev = NULL;
}
static void put_buffer_list_head(struct buffer_head **list,
struct buffer_head *bh)
{
put_buffer_list_end(list, bh);
*list = bh;
}
/*
#include <sys/mman.h>
static size_t estimate_memory_amount (void)
{
size_t len = 1;
size_t max = 0;
void * addr;
while (len > 0) {
addr = mmap (0, len, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED) {
if (errno != ENOMEM)
die ("mmap failed: %s\n", strerror(errno));
break;
}
if (mlock (addr, len) != 0) {
if (errno == EPERM)
die ("No permission to run mlock");
break;
}
munlock (addr, len);
munmap (addr, len);
max = len;
len *= 2;
}
// * If we've looped, we don't want to return 0, we want to return the
// * last successful len before we looped. In the event that mmap/mlock
// * failed for len = 1, max will still be 0, so we don't get an invalid
// * result
return max;
}
*/
#define GROW_BUFFERS__NEW_BUFERS_PER_CALL 10
/* creates number of new buffers and insert them into head of buffer list */
static int grow_buffers(int size)
{
int i;
struct buffer_head *bh, *tmp;
/* get memory for array of buffer heads */
bh = (struct buffer_head *)getmem(GROW_BUFFERS__NEW_BUFERS_PER_CALL *
sizeof(struct buffer_head) +
sizeof(struct buffer_head *));
if (!g_buffer_heads)
g_buffer_heads = bh;
else {
/* link new array to the end of array list */
tmp = g_buffer_heads;
while (*(struct buffer_head **)
(tmp + GROW_BUFFERS__NEW_BUFERS_PER_CALL) != NULL)
tmp =
*(struct buffer_head **)(tmp +
GROW_BUFFERS__NEW_BUFERS_PER_CALL);
*(struct buffer_head **)(tmp +
GROW_BUFFERS__NEW_BUFERS_PER_CALL) =
bh;
}
for (i = 0; i < GROW_BUFFERS__NEW_BUFERS_PER_CALL; i++) {
tmp = bh + i;
memset(tmp, 0, sizeof(struct buffer_head));
tmp->b_data = getmem(size);
if (!tmp->b_data)
die("grow_buffers: no memory for new buffer data");
tmp->b_dev = -1;
tmp->b_size = size;
put_buffer_list_head(&g_free_buffers, tmp);
}
buffers_memory += GROW_BUFFERS__NEW_BUFERS_PER_CALL * size;
g_nr_buffers += GROW_BUFFERS__NEW_BUFERS_PER_CALL;
return GROW_BUFFERS__NEW_BUFERS_PER_CALL;
}
struct buffer_head *find_buffer(int dev, unsigned long block,
unsigned long size)
{
struct buffer_head *next;
next = g_a_hash_queues[block % NR_HASH_QUEUES];
for (;;) {
struct buffer_head *tmp = next;
if (!next)
break;
next = tmp->b_hash_next;
if (tmp->b_blocknr != block || tmp->b_size != size
|| tmp->b_dev != dev)
continue;
next = tmp;
break;
}
return next;
}
static struct buffer_head *get_free_buffer(struct buffer_head **list,
unsigned long size)
{
struct buffer_head *next;
next = *list;
if (!next)
return NULL;
for (;;) {
if (!next)
die("get_free_buffer: buffer list is corrupted");
if (next->b_count == 0 && buffer_clean(next)
&& next->b_size == size) {
remove_from_hash_queue(next);
remove_from_buffer_list(list, next);
return next;
}
next = next->b_next;
if (next == *list)
break;
}
return NULL;
}
/* to_write == 0 when all blocks have to be flushed. Otherwise - write only
buffers with b_count == 0 */
static int sync_buffers(struct buffer_head **list, int dev, int to_write)
{
struct buffer_head *next;
int written = 0;
restart:
next = *list;
if (!next)
return 0;
for (;;) {
if (!next)
die("sync_buffers: buffer list is corrupted");
if (next->b_dev == dev && buffer_dirty(next)
&& buffer_uptodate(next)) {
if ((to_write == 0 || next->b_count == 0)
&& !buffer_do_not_flush(next)) {
bwrite(next);
}
}
/* if this buffer is reusable, put it onto the end of the free list */
if (next->b_count == 0 && buffer_clean(next)) {
remove_from_hash_queue(next);
remove_from_buffer_list(list, next);
put_buffer_list_end(&g_free_buffers, next);
written++;
if (written == to_write)
return written;
goto restart;
}
if (to_write && written >= to_write)
return written;
next = next->b_next;
if (next == *list)
break;
}
return written;
}
void flush_buffers(int dev)
{
if (dev == -1)
die("flush_buffers: device is not specified");
sync_buffers(&Buffer_list_head, dev, 0 /*all */ );
buffer_soft_limit = BUFFER_SOFT_LIMIT;
}
struct buffer_head *getblk(int dev, unsigned long block, int size)
{
struct buffer_head *bh;
bh = find_buffer(dev, block, size);
if (bh) {
/* move the buffer to the end of list */
/*checkmem (bh->b_data, bh->b_size); */
remove_from_buffer_list(&Buffer_list_head, bh);
put_buffer_list_end(&Buffer_list_head, bh);
bh->b_count++;
buffer_hits++;
return bh;
}
buffer_misses++;
bh = get_free_buffer(&g_free_buffers, size);
if (bh == NULL) {
if (buffers_memory >= buffer_soft_limit) {
if (sync_buffers(&Buffer_list_head, dev, 32) == 0) {
grow_buffers(size);
buffer_soft_limit = buffers_memory +
GROW_BUFFERS__NEW_BUFERS_PER_CALL * size;
}
} else {
if (grow_buffers(size) == 0)
sync_buffers(&Buffer_list_head, dev, 32);
}
bh = get_free_buffer(&g_free_buffers, size);
if (bh == NULL) {
show_buffers(dev, size);
die("getblk: no free buffers after grow_buffers "
"and refill (%d)", g_nr_buffers);
}
}
bh->b_count = 1;
bh->b_dev = dev;
bh->b_size = size;
bh->b_blocknr = block;
bh->b_end_io = NULL;
memset(bh->b_data, 0, size);
misc_clear_bit(BH_Dirty, &bh->b_state);
misc_clear_bit(BH_Uptodate, &bh->b_state);
put_buffer_list_end(&Buffer_list_head, bh);
insert_into_hash_queue(bh);
/*checkmem (bh->b_data, bh->b_size); */
return bh;
}
void brelse(struct buffer_head *bh)
{
if (!bh)
return;
if (bh->b_count == 0)
die("brelse: can not free a free buffer %lu", bh->b_blocknr);
/*checkmem (bh->b_data, get_mem_size (bh->b_data)); */
bh->b_count--;
}
void bforget(struct buffer_head *bh)
{
if (bh) {
bh->b_state = 0;
brelse(bh);
remove_from_hash_queue(bh);
remove_from_buffer_list(&Buffer_list_head, bh);
put_buffer_list_head(&Buffer_list_head, bh);
}
}
/* Returns 0 on success; 1 - end of file; 0 - OK. */
static int f_read(struct buffer_head *bh)
{
unsigned long long offset;
ssize_t bytes;
buffer_reads++;
offset = (unsigned long long)bh->b_size * bh->b_blocknr;
if (lseek(bh->b_dev, offset, SEEK_SET) < 0)
return -1;
bytes = read(bh->b_dev, bh->b_data, bh->b_size);
return bytes < 0 ? -1 : (bytes != (ssize_t) bh->b_size ? 1 : 0);
}
struct buffer_head *bread(int dev, unsigned long block, size_t size)
{
struct buffer_head *bh;
int ret;
if (is_bad_block(block))
return NULL;
bh = getblk(dev, block, size);
/*checkmem (bh->b_data, get_mem_size(bh->b_data)); */
if (buffer_uptodate(bh))
return bh;
ret = f_read(bh);
if (ret > 0) {
die("%s: End of file, cannot read the block (%lu).\n",
__FUNCTION__, block);
} else if (ret < 0) {
/* BAD BLOCK LIST SUPPORT
* die ("%s: Cannot read a block # %lu. Specify list of badblocks\n",*/
if (errno == EIO) {
check_hd_msg();
die("%s: Cannot read the block (%lu): (%s).\n",
__FUNCTION__, block, strerror(errno));
} else {
fprintf(stderr,
"%s: Cannot read the block (%lu): (%s).\n",
__FUNCTION__, block, strerror(errno));
return NULL;
}
}
mark_buffer_uptodate(bh, 0);
return bh;
}
#define ROLLBACK_FILE_START_MAGIC "_RollBackFileForReiserfsFSCK"
static struct block_handler *rollback_blocks_array;
static __u32 rollback_blocks_number = 0;
static FILE *s_rollback_file = NULL;
static FILE *log_file;
static int do_rollback = 0;
static char *rollback_data;
static int rollback_blocksize;
void init_rollback_file(char *rollback_file, unsigned int *blocksize,
FILE * log)
{
char *string;
struct stat buf;
if (rollback_file == NULL)
return;
stat(rollback_file, &buf);
s_rollback_file = fopen(rollback_file, "w+");
if (s_rollback_file == NULL) {
fprintf(stderr,
"Cannot create file %s, work without a rollback file\n",
rollback_file);
return;
}
rollback_blocksize = *blocksize;
string = ROLLBACK_FILE_START_MAGIC;
fwrite(string, 28, 1, s_rollback_file);
fwrite(&rollback_blocksize, sizeof(rollback_blocksize), 1,
s_rollback_file);
fwrite(&rollback_blocks_number, sizeof(rollback_blocks_number), 1,
s_rollback_file);
fflush(s_rollback_file);
rollback_data = getmem(rollback_blocksize);
// printf("\ncheckmem1");
// fflush (stdout);
// checkmem (rollback_data, get_mem_size((char *)rollback_data));
// printf(" OK");
log_file = log;
if (log_file)
fprintf(log_file, "rollback: file (%s) initialize\n",
rollback_file);
do_rollback = 0;
}
#if 0
static void erase_rollback_file(char *rollback_file)
{
close_rollback_file();
unlink(rollback_file);
}
#endif
int open_rollback_file(char *rollback_file, FILE * log)
{
struct stat buf;
char string[sizeof(ROLLBACK_FILE_START_MAGIC)] = { 0 };
if (rollback_file == NULL)
return -1;
if (stat(rollback_file, &buf)) {
fprintf(stderr, "Cannot stat rollback file (%s)\n",
rollback_file);
return -1;
}
s_rollback_file = fopen(rollback_file, "r+");
if (s_rollback_file == NULL) {
fprintf(stderr, "Cannot open file (%s)\n", rollback_file);
return -1;
}
fread(string, sizeof(ROLLBACK_FILE_START_MAGIC) - 1, 1, s_rollback_file);
if (strcmp(string, ROLLBACK_FILE_START_MAGIC) != 0) {
fprintf(stderr,
"Specified file (%s) does not look like a rollback file\n",
rollback_file);
fclose(s_rollback_file);
s_rollback_file = NULL;
return -1;
}
fread(&rollback_blocksize, sizeof(rollback_blocksize), 1,
s_rollback_file);
if (rollback_blocksize <= 0) {
fprintf(stderr, "rollback: wrong rollback blocksize, exit\n");
return -1;
}
log_file = log;
if (log_file)
fprintf(log_file, "rollback: file (%s) opened\n",
rollback_file);
do_rollback = 1;
return 0;
}
void close_rollback_file(void)
{
if (!s_rollback_file)
return;
if (!do_rollback) {
if (fseek(s_rollback_file, 28 + sizeof(int), SEEK_SET) ==
(loff_t) - 1)
return;
fwrite(&rollback_blocks_number, sizeof(rollback_blocksize), 1,
s_rollback_file);
if (log_file)
fprintf(log_file, "rollback: %u blocks backed up\n",
rollback_blocks_number);
}
fclose(s_rollback_file);
freemem(rollback_data);
freemem(rollback_blocks_array);
// fprintf (stdout, "rollback: (%u) blocks saved, \n", rollback_blocks_number);
/* for (i = 0; i < rollback_blocks_number; i++)
fprintf(stdout, "device (%Lu), block number (%u)\n",
rollback_blocks_array [i].device,
rollback_blocks_array [i].blocknr);
fprintf(stdout, "\n");
*/
}
void do_fsck_rollback(int fd_device, int fd_journal_device, FILE * progress)
{
long long int offset;
struct stat buf;
int descriptor;
ssize_t retval;
int count_failed = 0;
int count_rollbacked = 0;
int b_dev;
int n_dev = 0;
int n_journal_dev = 0;
unsigned long done = 0;
if (fd_device == 0) {
fprintf(stderr, "rollback: unspecified device, exit\n");
return;
}
if (fd_journal_device) {
if (!fstat(fd_journal_device, &buf)) {
n_journal_dev = buf.st_rdev;
} else {
fprintf(stderr,
"rollback: specified journal device cannot be stated\n");
}
}
if (!fstat(fd_device, &buf)) {
n_dev = buf.st_rdev;
} else {
fprintf(stderr,
"rollback: specified device cannot be stated, exit\n");
return;
}
rollback_data = getmem(rollback_blocksize);
// printf("\ncheckmem2");
// fflush (stdout);
// checkmem (rollback_data, get_mem_size((char *)rollback_data));
// printf(" OK");
fread(&rollback_blocks_number, sizeof(rollback_blocks_number), 1,
s_rollback_file);
while (1) {
print_how_far(progress, &done, rollback_blocks_number, 1,
0 /*not quiet */ );
descriptor = 0;
if ((retval =
fread(&b_dev, sizeof(b_dev), 1, s_rollback_file)) <= 0) {
if (retval)
fprintf(stderr, "rollback: fread: %s\n",
strerror(errno));
break;
}
if ((retval =
fread(&offset, sizeof(offset), 1, s_rollback_file)) <= 0) {
if (retval)
fprintf(stderr, "rollback: fread: %s\n",
strerror(errno));
break;
}
if ((retval =
fread(rollback_data, rollback_blocksize, 1,
s_rollback_file)) <= 0) {
if (retval)
fprintf(stderr, "rollback: fread: %s\n",
strerror(errno));
break;
}
if (n_dev == b_dev)
descriptor = fd_device;
if ((n_journal_dev) && (n_journal_dev == b_dev))
descriptor = fd_journal_device;
if (descriptor == 0) {
fprintf(stderr,
"rollback: block from unknown device, skip block\n");
count_failed++;
continue;
}
if (lseek(descriptor, offset, SEEK_SET) == (loff_t) - 1) {
fprintf(stderr,
"device cannot be lseeked, skip block\n");
count_failed++;
continue;
}
if (write(descriptor, rollback_data, rollback_blocksize) == -1) {
fprintf(stderr,
"rollback: write %d bytes returned error "
"(block=%lld, dev=%d): %s\n",
rollback_blocksize, offset / rollback_blocksize,
b_dev, strerror(errno));
count_failed++;
} else {
count_rollbacked++;
/*if you want to know what gets rollbacked, uncomment it */
/* if (log_file != 0 && log_file != stdout)
fprintf (log_file, "rollback: block %Lu of device %Lu was restored\n",
(loff_t)offset/rollback_blocksize, b_dev);
*/
// fprintf (stdout, "rollback: block (%Ld) written\n", (loff_t)offset/rollback_blocksize);
}
}
printf("\n");
if (log_file)
fprintf(log_file,
"rollback: (%u) blocks restored, %u skipped\n",
count_rollbacked, count_failed);
}
/*
static void rollback__mark_block_saved (struct block_handler * rb_e) {
if (rollback_blocks_array == NULL)
rollback_blocks_array = getmem (ROLLBACK__INCREASE_BLOCK_NUMBER * sizeof (*rb_e));
if (rollback_blocks_number == get_mem_size ((void *)rollback_blocks_array) / sizeof (*rb_e))
rollback_blocks_array = expandmem (rollback_blocks_array, get_mem_size((void *)rollback_blocks_array),
ROLLBACK__INCREASE_BLOCK_NUMBER * sizeof (*rb_e));
// checkmem ((char *)rollback_blocks_array, get_mem_size((char *)rollback_blocks_array));
rollback_blocks_array[rollback_blocks_number] = *rb_e;
rollback_blocks_number ++;
qsort (rollback_blocks_array, rollback_blocks_number, sizeof (*rb_e), rollback_compare);
// printf("\ncheckmem3");
// fflush (stdout);
// checkmem ((char *)rollback_blocks_array, get_mem_size((char *)rollback_blocks_array));
// printf(" OK");
}
*/
/* for now - just make sure that bad blocks did not get here */
int bwrite(struct buffer_head *bh)
{
unsigned long long offset;
long long bytes, size;
if (is_bad_block(bh->b_blocknr)) {
fprintf(stderr,
"bwrite: bad block is going to be written: %lu\n",
bh->b_blocknr);
exit(8);
}
if (!buffer_dirty(bh) || !buffer_uptodate(bh))
return 0;
buffer_writes++;
if (bh->b_start_io)
/* this is used by undo feature of reiserfsck */
bh->b_start_io(bh->b_blocknr);
size = bh->b_size;
offset = (loff_t) size *(loff_t) bh->b_blocknr;
if (lseek(bh->b_dev, offset, SEEK_SET) == (loff_t) - 1) {
fprintf(stderr,
"bwrite: lseek to position %llu (block=%lu, dev=%d): %s\n",
offset, bh->b_blocknr, bh->b_dev, strerror(errno));
exit(8); /* File system errors left uncorrected */
}
if (s_rollback_file != NULL
&& bh->b_size == (unsigned long)rollback_blocksize) {
struct stat buf;
__u32 position;
struct block_handler block_h;
/*log previous content into the log */
if (!fstat(bh->b_dev, &buf)) {
block_h.blocknr = bh->b_blocknr;
block_h.device = buf.st_rdev;
if (reiserfs_bin_search(&block_h, rollback_blocks_array,
rollback_blocks_number,
sizeof(block_h), &position,
blockdev_list_compare)
!= POSITION_FOUND) {
/*read initial data from the disk */
if (read(bh->b_dev, rollback_data, bh->b_size)
== (long long)bh->b_size) {
fwrite(&buf.st_rdev,
sizeof(buf.st_rdev), 1,
s_rollback_file);
fwrite(&offset, sizeof(offset), 1,
s_rollback_file);
fwrite(rollback_data,
rollback_blocksize, 1,
s_rollback_file);
fflush(s_rollback_file);
blocklist__insert_in_position(&block_h,
(void
*)
(&rollback_blocks_array),
&rollback_blocks_number,
sizeof
(block_h),
&position);
/*if you want to know what gets saved, uncomment it */
/* if (log_file != 0 && log_file != stdout) {
fprintf (log_file, "rollback: block %lu of device %Lu was "
"backed up\n", bh->b_blocknr, buf.st_rdev);
}
*/
} else {
fprintf(stderr,
"bwrite: read (block=%lu, dev=%d): %s\n",
bh->b_blocknr, bh->b_dev,
strerror(errno));
exit(8);
}
if (lseek(bh->b_dev, offset, SEEK_SET) ==
(loff_t) - 1) {
fprintf(stderr,
"bwrite: lseek to position %llu (block=%lu, "
"dev=%d): %s\n", offset,
bh->b_blocknr, bh->b_dev,
strerror(errno));
exit(8);
}
}
} else {
fprintf(stderr,
"bwrite: fstat of (%d) returned -1: %s\n",
bh->b_dev, strerror(errno));
}
} else if (s_rollback_file != NULL) {
fprintf(stderr,
"rollback: block (%lu) has the size different from "
"the fs uses, block skipped\n", bh->b_blocknr);
}
bytes = write(bh->b_dev, bh->b_data, size);
if (bytes != size) {
fprintf(stderr,
"bwrite: write %lld bytes returned %lld (block=%ld, "
"dev=%d): %s\n", size, bytes, bh->b_blocknr, bh->b_dev,
strerror(errno));
exit(8);
}
mark_buffer_clean(bh);
if (bh->b_end_io) {
bh->b_end_io(bh, 1);
}
return 0;
}
static int _check_and_free_buffer_list(struct buffer_head *list)
{
struct buffer_head *next = list;
int count = 0;
if (!list)
return 0;
for (;;) {
if (next->b_count != 0)
fprintf(stderr,
"check_and_free_buffer_mem: not free buffer "
"(%d, %ld, %ld, %d)\n", next->b_dev,
next->b_blocknr, next->b_size, next->b_count);
if (buffer_dirty(next) && buffer_uptodate(next))
fprintf(stderr,
"check_and_free_buffer_mem: dirty buffer "
"(%d %lu) found\n", next->b_dev,
next->b_blocknr);
freemem(next->b_data);
count++;
next = next->b_next;
if (next == list)
break;
}
return count;
}
static void check_and_free_buffer_mem(void)
{
int count = 0;
struct buffer_head *next;
// printf("check and free buffer mem, hits %d misses %d reads %d writes %d\n",
// buffer_hits, buffer_misses, buffer_reads, buffer_writes) ;
/*sync_buffers (0, 0); */
count = _check_and_free_buffer_list(Buffer_list_head);
count += _check_and_free_buffer_list(g_free_buffers);
if (count != g_nr_buffers)
die("check_and_free_buffer_mem: found %d buffers, must be %d",
count, g_nr_buffers);
/* free buffer heads */
while ((next = g_buffer_heads)) {
g_buffer_heads = *(struct buffer_head **)
(next + GROW_BUFFERS__NEW_BUFERS_PER_CALL);
freemem(next);
}
return;
}
/* */
void free_buffers(void)
{
check_and_free_buffer_mem();
}
static void _invalidate_buffer_list(struct buffer_head *list, int dev)
{
struct buffer_head *next;
if (!list)
return;
next = list;
for (;;) {
if (next->b_dev == dev) {
if (buffer_dirty(next) || next->b_count)
fprintf(stderr,
"invalidate_buffers: dirty buffer or used buffer (%d %lu) found\n",
next->b_count, next->b_blocknr);
next->b_state = 0;
remove_from_hash_queue(next);
}
next = next->b_next;
if (next == list)
break;
}
}
/* forget all buffers of the given device */
void invalidate_buffers(int dev)
{
_invalidate_buffer_list(Buffer_list_head, dev);
_invalidate_buffer_list(g_free_buffers, dev);
}