2 * undo_io.c --- This is the undo io manager that copies the old data that
3 * copies the old data being overwritten into a tdb database
5 * Copyright IBM Corporation, 2007
6 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
9 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
14 #define _LARGEFILE_SOURCE
15 #define _LARGEFILE64_SOURCE
28 #include <sys/utsname.h>
34 #include <sys/types.h>
36 #if HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
46 #define ATTR(x) __attribute__(x)
52 * For checking structure magic numbers...
55 #define EXT2_CHECK_MAGIC(struct, code) \
56 if ((struct)->magic != (code)) return (code)
58 struct undo_private_data {
63 /* The backing io channel */
69 /* to support offset in unix I/O manager */
73 static errcode_t undo_open(const char *name, int flags, io_channel *channel);
74 static errcode_t undo_close(io_channel channel);
75 static errcode_t undo_set_blksize(io_channel channel, int blksize);
76 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
77 int count, void *data);
78 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
79 int count, const void *data);
80 static errcode_t undo_flush(io_channel channel);
81 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
82 int size, const void *data);
83 static errcode_t undo_set_option(io_channel channel, const char *option,
86 static struct struct_io_manager struct_undo_manager = {
87 EXT2_ET_MAGIC_IO_MANAGER,
99 io_manager undo_io_manager = &struct_undo_manager;
100 static io_manager undo_io_backing_manager ;
101 static char *tdb_file;
102 static int actual_size;
104 static unsigned char mtime_key[] = "filesystem MTIME";
105 static unsigned char blksize_key[] = "filesystem BLKSIZE";
106 static unsigned char uuid_key[] = "filesystem UUID";
108 errcode_t set_undo_io_backing_manager(io_manager manager)
111 * We may want to do some validation later
113 undo_io_backing_manager = manager;
117 errcode_t set_undo_io_backup_file(char *file_name)
119 tdb_file = strdup(file_name);
121 if (tdb_file == NULL) {
122 return EXT2_ET_NO_MEMORY;
128 static errcode_t write_file_system_identity(io_channel undo_channel,
132 struct ext2_super_block super;
133 TDB_DATA tdb_key, tdb_data;
134 struct undo_private_data *data;
138 data = (struct undo_private_data *) undo_channel->private_data;
139 channel = data->real;
140 block_size = channel->block_size;
142 io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
143 retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
147 /* Write to tdb file in the file system byte order */
148 tdb_key.dptr = mtime_key;
149 tdb_key.dsize = sizeof(mtime_key);
150 tdb_data.dptr = (unsigned char *) &(super.s_mtime);
151 tdb_data.dsize = sizeof(super.s_mtime);
153 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
155 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
159 tdb_key.dptr = uuid_key;
160 tdb_key.dsize = sizeof(uuid_key);
161 tdb_data.dptr = (unsigned char *)&(super.s_uuid);
162 tdb_data.dsize = sizeof(super.s_uuid);
164 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
166 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
170 io_channel_set_blksize(channel, block_size);
174 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
177 TDB_DATA tdb_key, tdb_data;
179 tdb_key.dptr = blksize_key;
180 tdb_key.dsize = sizeof(blksize_key);
181 tdb_data.dptr = (unsigned char *)&(block_size);
182 tdb_data.dsize = sizeof(block_size);
184 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
186 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
192 static errcode_t undo_write_tdb(io_channel channel,
193 unsigned long block, int count)
197 unsigned long block_num, backing_blk_num;
198 errcode_t retval = 0;
200 struct undo_private_data *data;
201 TDB_DATA tdb_key, tdb_data;
202 unsigned char *read_ptr;
203 unsigned long end_block;
205 data = (struct undo_private_data *) channel->private_data;
207 if (data->tdb == NULL) {
209 * Transaction database not initialized
215 size = channel->block_size;
220 size = count * channel->block_size;
223 * Data is stored in tdb database as blocks of tdb_data_size size
224 * This helps in efficient lookup further.
226 * We divide the disk to blocks of tdb_data_size.
228 offset = (block * channel->block_size) + data->offset ;
229 block_num = offset / data->tdb_data_size;
230 end_block = (offset + size) / data->tdb_data_size;
232 tdb_transaction_start(data->tdb);
233 while (block_num <= end_block ) {
235 tdb_key.dptr = (unsigned char *)&block_num;
236 tdb_key.dsize = sizeof(block_num);
238 * Check if we have the record already
240 if (tdb_exists(data->tdb, tdb_key)) {
241 /* Try the next block */
246 * Read one block using the backing I/O manager
247 * The backing I/O manager block size may be
248 * different from the tdb_data_size.
249 * Also we need to recalcuate the block number with respect
250 * to the backing I/O manager.
252 offset = block_num * data->tdb_data_size;
253 backing_blk_num = (offset - data->offset) / channel->block_size;
255 count = data->tdb_data_size +
256 ((offset - data->offset) % channel->block_size);
257 retval = ext2fs_get_mem(count, &read_ptr);
259 tdb_transaction_cancel(data->tdb);
263 memset(read_ptr, 0, count);
265 if ((count % channel->block_size) == 0)
266 sz = count / channel->block_size;
269 retval = io_channel_read_blk(data->real, backing_blk_num,
272 if (retval != EXT2_ET_SHORT_READ) {
274 tdb_transaction_cancel(data->tdb);
278 * short read so update the record size
281 tdb_data.dsize = actual_size;
283 tdb_data.dsize = data->tdb_data_size;
285 tdb_data.dptr = read_ptr +
286 ((offset - data->offset) % channel->block_size);
288 printf("Printing with key %ld data %x and size %d\n",
293 if (!data->tdb_written) {
294 data->tdb_written = 1;
295 /* Write the blocksize to tdb file */
296 retval = write_block_size(data->tdb,
297 data->tdb_data_size);
299 tdb_transaction_cancel(data->tdb);
300 retval = EXT2_ET_TDB_ERR_IO;
305 retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
308 * TDB_ERR_EXISTS cannot happen because we
309 * have already verified it doesn't exist
311 tdb_transaction_cancel(data->tdb);
312 retval = EXT2_ET_TDB_ERR_IO;
320 tdb_transaction_commit(data->tdb);
325 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
326 unsigned long block ATTR((unused)),
327 int count ATTR((unused)),
328 void *data ATTR((unused)),
329 size_t size ATTR((unused)),
331 errcode_t error ATTR((unused)))
333 actual_size = actual;
337 static void undo_err_handler_init(io_channel channel)
339 channel->read_error = undo_io_read_error;
342 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
344 io_channel io = NULL;
345 struct undo_private_data *data = NULL;
349 return EXT2_ET_BAD_DEVICE_NAME;
350 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
353 memset(io, 0, sizeof(struct struct_io_channel));
354 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
355 retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
359 io->manager = undo_io_manager;
360 retval = ext2fs_get_mem(strlen(name)+1, &io->name);
364 strcpy(io->name, name);
365 io->private_data = data;
366 io->block_size = 1024;
371 memset(data, 0, sizeof(struct undo_private_data));
372 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
374 if (undo_io_backing_manager) {
375 retval = undo_io_backing_manager->open(name, flags,
383 /* setup the tdb file */
384 data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
385 O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
392 * setup err handler for read so that we know
393 * when the backing manager fails do short read
395 undo_err_handler_init(data->real);
402 io_channel_close(data->real);
404 ext2fs_free_mem(&data);
406 ext2fs_free_mem(&io);
410 static errcode_t undo_close(io_channel channel)
412 struct undo_private_data *data;
413 errcode_t retval = 0;
415 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
416 data = (struct undo_private_data *) channel->private_data;
417 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
419 if (--channel->refcount > 0)
421 /* Before closing write the file system identity */
422 retval = write_file_system_identity(channel, data->tdb);
426 retval = io_channel_close(data->real);
428 tdb_close(data->tdb);
429 ext2fs_free_mem(&channel->private_data);
431 ext2fs_free_mem(&channel->name);
432 ext2fs_free_mem(&channel);
437 static errcode_t undo_set_blksize(io_channel channel, int blksize)
439 struct undo_private_data *data;
440 errcode_t retval = 0;
442 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
443 data = (struct undo_private_data *) channel->private_data;
444 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
447 retval = io_channel_set_blksize(data->real, blksize);
449 * Set the block size used for tdb
451 if (!data->tdb_data_size) {
452 data->tdb_data_size = blksize;
454 channel->block_size = blksize;
458 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
459 int count, void *buf)
461 errcode_t retval = 0;
462 struct undo_private_data *data;
464 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
465 data = (struct undo_private_data *) channel->private_data;
466 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
469 retval = io_channel_read_blk(data->real, block, count, buf);
474 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
475 int count, const void *buf)
477 struct undo_private_data *data;
478 errcode_t retval = 0;
480 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
481 data = (struct undo_private_data *) channel->private_data;
482 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
484 * First write the existing content into database
486 retval = undo_write_tdb(channel, block, count);
490 retval = io_channel_write_blk(data->real, block, count, buf);
495 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
496 int size, const void *buf)
498 struct undo_private_data *data;
499 errcode_t retval = 0;
500 ext2_loff_t location;
501 unsigned long blk_num, count;;
503 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
504 data = (struct undo_private_data *) channel->private_data;
505 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
507 location = offset + data->offset;
508 blk_num = location/channel->block_size;
510 * the size specified may spread across multiple blocks
511 * also make sure we account for the fact that block start
512 * offset for tdb is different from the backing I/O manager
513 * due to possible different block size
515 count = (size + (location % channel->block_size) +
516 channel->block_size -1)/channel->block_size;
517 retval = undo_write_tdb(channel, blk_num, count);
520 if (data->real && data->real->manager->write_byte)
521 retval = io_channel_write_byte(data->real, offset, size, buf);
527 * Flush data buffers to disk.
529 static errcode_t undo_flush(io_channel channel)
531 errcode_t retval = 0;
532 struct undo_private_data *data;
534 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
535 data = (struct undo_private_data *) channel->private_data;
536 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
539 retval = io_channel_flush(data->real);
544 static errcode_t undo_set_option(io_channel channel, const char *option,
547 errcode_t retval = 0;
548 struct undo_private_data *data;
552 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
553 data = (struct undo_private_data *) channel->private_data;
554 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
556 if (!strcmp(option, "tdb_data_size")) {
558 return EXT2_ET_INVALID_ARGUMENT;
560 tmp = strtoul(arg, &end, 0);
562 return EXT2_ET_INVALID_ARGUMENT;
563 if (!data->tdb_data_size || !data->tdb_written) {
564 data->tdb_data_size = tmp;
569 * Need to support offset option to work with
572 if (data->real && data->real->manager->set_option) {
573 retval = data->real->manager->set_option(data->real,
576 if (!retval && !strcmp(option, "offset")) {
578 return EXT2_ET_INVALID_ARGUMENT;
580 tmp = strtoul(arg, &end, 0);
582 return EXT2_ET_INVALID_ARGUMENT;