* %End-Header%
*/
+#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
+#endif
#include "config.h"
#include <stdio.h>
__le32 fs_block_size; /* block size of the target device */
__le32 sb_crc; /* crc32c of the superblock */
__le32 state; /* e2undo state flags */
- __le32 f_compat; /* compatible features (none so far) */
+ __le32 f_compat; /* compatible features */
__le32 f_incompat; /* incompatible features (none so far) */
__le32 f_rocompat; /* ro compatible features (none so far) */
- __u8 padding[448]; /* padding */
+ __le32 pad32; /* padding for fs_offset */
+ __le64 fs_offset; /* filesystem offset */
+ __u8 padding[436]; /* padding */
__le32 header_crc; /* crc32c of this header (but not this field) */
};
__le32 crc; /* block checksum */
__le64 reserved; /* zero */
+#if __STDC_VERSION__ >= 199901L
+ struct undo_key keys[]; /* keys, which come immediately after */
+#else
struct undo_key keys[0]; /* keys, which come immediately after */
+#endif
};
struct undo_private_data {
};
#define KEYS_PER_BLOCK(d) (((d)->tdb_data_size / sizeof(struct undo_key)) - 1)
+#define E2UNDO_FEATURE_COMPAT_FS_OFFSET 0x1 /* the filesystem offset */
+
+static inline void e2undo_set_feature_fs_offset(struct undo_header *header) {
+ header->f_compat |= ext2fs_le32_to_cpu(E2UNDO_FEATURE_COMPAT_FS_OFFSET);
+}
+
+static inline void e2undo_clear_feature_fs_offset(struct undo_header *header) {
+ header->f_compat &= ~ext2fs_le32_to_cpu(E2UNDO_FEATURE_COMPAT_FS_OFFSET);
+}
+
static io_manager undo_io_backing_manager;
static char *tdb_file;
static int actual_size;
return 0;
}
-static errcode_t write_undo_indexes(struct undo_private_data *data)
+static errcode_t write_undo_indexes(struct undo_private_data *data, int flush)
{
errcode_t retval;
struct ext2_super_block super;
1, data->keyb);
if (retval)
return retval;
- memset(data->keyb, 0, data->tdb_data_size);
- data->keys_in_block = 0;
- data->key_blk_num = data->undo_blk_num;
+ /* Move on to the next key block if it's full. */
+ if (data->keys_in_block == KEYS_PER_BLOCK(data)) {
+ memset(data->keyb, 0, data->tdb_data_size);
+ data->keys_in_block = 0;
+ data->key_blk_num = data->undo_blk_num;
+ data->undo_blk_num++;
+ }
}
/* Prepare superblock for write */
data->hdr.key_offset = ext2fs_cpu_to_le64(data->first_key_blk);
data->hdr.fs_block_size = ext2fs_cpu_to_le32(block_size);
data->hdr.sb_crc = ext2fs_cpu_to_le32(sb_crc);
+ data->hdr.fs_offset = ext2fs_cpu_to_le64(data->offset);
+ if (data->offset)
+ e2undo_set_feature_fs_offset(&data->hdr);
+ else
+ e2undo_clear_feature_fs_offset(&data->hdr);
hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&data->hdr,
sizeof(data->hdr) -
sizeof(data->hdr.header_crc));
if (retval)
goto err_out;
- retval = io_channel_flush(data->undo_file);
+ if (flush)
+ retval = io_channel_flush(data->undo_file);
err_out:
io_channel_set_blksize(channel, block_size);
return retval;
retval = ext2fs_get_mem(data->tdb_data_size, &data->keyb);
if (retval)
return retval;
- data->key_blk_num = data->undo_blk_num;
+ data->key_blk_num = data->first_key_blk;
/* Record block size */
dbg_printf("Undo block size %llu\n", data->tdb_data_size);
unsigned char *read_ptr;
unsigned long long end_block;
unsigned long long data_size;
- void *data_ptr;
struct undo_key *key;
__u32 blk_crc;
}
ext2fs_mark_block_bitmap2(data->written_block_map, block_num);
- /* Spit out a key block */
- if (data->keys_in_block == KEYS_PER_BLOCK(data)) {
- retval = write_undo_indexes(data);
- if (retval)
- return retval;
- retval = io_channel_write_blk64(data->undo_file,
- data->key_blk_num, 1,
- data->keyb);
- if (retval)
- return retval;
- }
-
- /* Allocate new key block */
- if (data->keys_in_block == 0)
- data->undo_blk_num++;
-
/*
* Read one block using the backing I/O manager
* The backing I/O manager block size may be
* different from the tdb_data_size.
- * Also we need to recalcuate the block number with respect
+ * Also we need to recalculate the block number with respect
* to the backing I/O manager.
*/
- offset = block_num * data->tdb_data_size;
+ offset = block_num * data->tdb_data_size +
+ (data->offset % data->tdb_data_size);
backing_blk_num = (offset - data->offset) / channel->block_size;
- count = data->tdb_data_size +
- ((offset - data->offset) % channel->block_size);
- retval = ext2fs_get_mem(count, &read_ptr);
+ retval = ext2fs_get_mem(data->tdb_data_size, &read_ptr);
if (retval) {
return retval;
}
- memset(read_ptr, 0, count);
+ memset(read_ptr, 0, data->tdb_data_size);
actual_size = 0;
- if ((count % channel->block_size) == 0)
- sz = count / channel->block_size;
+ if ((data->tdb_data_size % channel->block_size) == 0)
+ sz = data->tdb_data_size / channel->block_size;
else
- sz = -count;
+ sz = -data->tdb_data_size;
retval = io_channel_read_blk64(data->real, backing_blk_num,
sz, read_ptr);
if (retval) {
block_num++;
continue;
}
- dbg_printf("Read %llu bytes from FS block %llu (blk=%llu cnt=%u)\n",
- data_size, backing_blk_num, block, count);
+ dbg_printf("Read %llu bytes from FS block %llu (blk=%llu cnt=%llu)\n",
+ data_size, backing_blk_num, block, data->tdb_data_size);
if ((data_size % data->undo_file->block_size) == 0)
sz = data_size / data->undo_file->block_size;
else
- sz = -actual_size;
- data_ptr = read_ptr + ((offset - data->offset) %
- data->undo_file->block_size);
+ sz = -data_size;;
/* extend this key? */
if (data->keys_in_block) {
key = data->keyb->keys + data->keys_in_block - 1;
keysz = 0;
}
if (key != NULL &&
- ext2fs_le64_to_cpu(key->fsblk) +
- ((keysz + data->tdb_data_size - 1) /
- data->tdb_data_size) == backing_blk_num &&
+ (ext2fs_le64_to_cpu(key->fsblk) * channel->block_size +
+ channel->block_size - 1 +
+ keysz) / channel->block_size == backing_blk_num &&
E2UNDO_MAX_EXTENT_BLOCKS * data->tdb_data_size >
- keysz + sz) {
+ keysz + data_size) {
blk_crc = ext2fs_le32_to_cpu(key->blk_crc);
- blk_crc = ext2fs_crc32c_le(blk_crc,
- (unsigned char *)data_ptr,
- data_size);
+ blk_crc = ext2fs_crc32c_le(blk_crc, read_ptr, data_size);
key->blk_crc = ext2fs_cpu_to_le32(blk_crc);
key->size = ext2fs_cpu_to_le32(keysz + data_size);
} else {
key = data->keyb->keys + data->keys_in_block;
data->keys_in_block++;
key->fsblk = ext2fs_cpu_to_le64(backing_blk_num);
- blk_crc = ext2fs_crc32c_le(~0,
- (unsigned char *)data_ptr,
- data_size);
+ blk_crc = ext2fs_crc32c_le(~0, read_ptr, data_size);
key->blk_crc = ext2fs_cpu_to_le32(blk_crc);
key->size = ext2fs_cpu_to_le32(data_size);
}
data->undo_blk_num,
sz, data->num_keys - 1);
retval = io_channel_write_blk64(data->undo_file,
- data->undo_blk_num, sz, data_ptr);
+ data->undo_blk_num, sz, read_ptr);
if (retval) {
free(read_ptr);
return retval;
}
data->undo_blk_num++;
free(read_ptr);
+
+ /* Write out the key block */
+ retval = write_undo_indexes(data, 0);
+ if (retval)
+ return retval;
+
/* Next block */
block_num++;
}
super_block = ext2fs_le64_to_cpu(hdr.super_offset);
num_keys = ext2fs_le64_to_cpu(hdr.num_keys);
io_channel_set_blksize(data->undo_file, blocksize);
- if (hdr.f_compat || hdr.f_incompat || hdr.f_rocompat)
+ /*
+ * Do not compare hdr.f_compat with the available compatible
+ * features set, because a "missing" compatible feature should
+ * not cause any problems.
+ */
+ if (hdr.f_incompat || hdr.f_rocompat)
goto bad_file;
/* Superblock matches this FS? */
struct undo_private_data *data = p;
errcode_t err;
- err = write_undo_indexes(data);
+ err = write_undo_indexes(data, 1);
io_channel_close(data->undo_file);
com_err(data->tdb_file, err, "while force-closing undo file");
memset(data, 0, sizeof(struct undo_private_data));
data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
data->super_blk_num = 1;
- data->undo_blk_num = data->first_key_blk = 2;
+ data->first_key_blk = 2;
+ data->undo_blk_num = 3;
if (undo_io_backing_manager) {
retval = undo_io_backing_manager->open(name, flags,
/* Before closing write the file system identity */
if (!getenv("UNDO_IO_SIMULATE_UNFINISHED"))
data->hdr.state = ext2fs_cpu_to_le32(E2UNDO_STATE_FINISHED);
- err = write_undo_indexes(data);
+ err = write_undo_indexes(data, 1);
ext2fs_remove_exit_fn(undo_atexit, data);
if (data->real)
retval = io_channel_close(data->real);