__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;
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));
unsigned char *read_ptr;
unsigned long long end_block;
unsigned long long data_size;
- void *data_ptr;
struct undo_key *key;
__u32 blk_crc;
* 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;
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? */
int undo_fd = -1;
errcode_t retval;
+ /* We don't support multi-threading, at least for now */
+ flags &= ~IO_FLAG_THREADS;
if (name == 0)
return EXT2_ET_BAD_DEVICE_NAME;
retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
return retval;
}
+/*
+ * Flush data buffers to disk and cleanup the cache.
+ */
+static errcode_t undo_flush_cleanup(io_channel channel)
+{
+ errcode_t retval = 0;
+ struct undo_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_flush_cleanup(data->real);
+
+ return retval;
+}
+
static errcode_t undo_set_option(io_channel channel, const char *option,
const char *arg)
{
.read_blk = undo_read_blk,
.write_blk = undo_write_blk,
.flush = undo_flush,
+ .flush_cleanup = undo_flush_cleanup,
.write_byte = undo_write_byte,
.set_option = undo_set_option,
.get_stats = undo_get_stats,