Home | History | Annotate | Download | only in ext2fs
      1 /*
      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
      4  *
      5  * Copyright IBM Corporation, 2007
      6  * Author Aneesh Kumar K.V <aneesh.kumar (at) linux.vnet.ibm.com>
      7  *
      8  * %Begin-Header%
      9  * This file may be redistributed under the terms of the GNU Library
     10  * General Public License, version 2.
     11  * %End-Header%
     12  */
     13 
     14 #define _LARGEFILE_SOURCE
     15 #define _LARGEFILE64_SOURCE
     16 
     17 #include <stdio.h>
     18 #include <string.h>
     19 #if HAVE_UNISTD_H
     20 #include <unistd.h>
     21 #endif
     22 #if HAVE_ERRNO_H
     23 #include <errno.h>
     24 #endif
     25 #include <fcntl.h>
     26 #include <time.h>
     27 #ifdef __linux__
     28 #include <sys/utsname.h>
     29 #endif
     30 #if HAVE_SYS_STAT_H
     31 #include <sys/stat.h>
     32 #endif
     33 #if HAVE_SYS_TYPES_H
     34 #include <sys/types.h>
     35 #endif
     36 #if HAVE_SYS_RESOURCE_H
     37 #include <sys/resource.h>
     38 #endif
     39 
     40 #include "tdb.h"
     41 
     42 #include "ext2_fs.h"
     43 #include "ext2fs.h"
     44 
     45 #ifdef __GNUC__
     46 #define ATTR(x) __attribute__(x)
     47 #else
     48 #define ATTR(x)
     49 #endif
     50 
     51 /*
     52  * For checking structure magic numbers...
     53  */
     54 
     55 #define EXT2_CHECK_MAGIC(struct, code) \
     56 	  if ((struct)->magic != (code)) return (code)
     57 
     58 struct undo_private_data {
     59 	int	magic;
     60 	TDB_CONTEXT *tdb;
     61 	char *tdb_file;
     62 
     63 	/* The backing io channel */
     64 	io_channel real;
     65 
     66 	int tdb_data_size;
     67 	int tdb_written;
     68 
     69 	/* to support offset in unix I/O manager */
     70 	ext2_loff_t offset;
     71 };
     72 
     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_blk64(io_channel channel, unsigned long long block,
     77 				 int count, void *data);
     78 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
     79 				  int count, const void *data);
     80 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
     81 			       int count, void *data);
     82 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
     83 				int count, const void *data);
     84 static errcode_t undo_flush(io_channel channel);
     85 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
     86 				int size, const void *data);
     87 static errcode_t undo_set_option(io_channel channel, const char *option,
     88 				 const char *arg);
     89 static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
     90 
     91 static struct struct_io_manager struct_undo_manager = {
     92 	EXT2_ET_MAGIC_IO_MANAGER,
     93 	"Undo I/O Manager",
     94 	undo_open,
     95 	undo_close,
     96 	undo_set_blksize,
     97 	undo_read_blk,
     98 	undo_write_blk,
     99 	undo_flush,
    100 	undo_write_byte,
    101 	undo_set_option,
    102 	undo_get_stats,
    103 	undo_read_blk64,
    104 	undo_write_blk64,
    105 };
    106 
    107 io_manager undo_io_manager = &struct_undo_manager;
    108 static io_manager undo_io_backing_manager ;
    109 static char *tdb_file;
    110 static int actual_size;
    111 
    112 static unsigned char mtime_key[] = "filesystem MTIME";
    113 static unsigned char blksize_key[] = "filesystem BLKSIZE";
    114 static unsigned char uuid_key[] = "filesystem UUID";
    115 
    116 errcode_t set_undo_io_backing_manager(io_manager manager)
    117 {
    118 	/*
    119 	 * We may want to do some validation later
    120 	 */
    121 	undo_io_backing_manager = manager;
    122 	return 0;
    123 }
    124 
    125 errcode_t set_undo_io_backup_file(char *file_name)
    126 {
    127 	tdb_file = strdup(file_name);
    128 
    129 	if (tdb_file == NULL) {
    130 		return EXT2_ET_NO_MEMORY;
    131 	}
    132 
    133 	return 0;
    134 }
    135 
    136 static errcode_t write_file_system_identity(io_channel undo_channel,
    137 							TDB_CONTEXT *tdb)
    138 {
    139 	errcode_t retval;
    140 	struct ext2_super_block super;
    141 	TDB_DATA tdb_key, tdb_data;
    142 	struct undo_private_data *data;
    143 	io_channel channel;
    144 	int block_size ;
    145 
    146 	data = (struct undo_private_data *) undo_channel->private_data;
    147 	channel = data->real;
    148 	block_size = channel->block_size;
    149 
    150 	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
    151 	retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
    152 	if (retval)
    153 		goto err_out;
    154 
    155 	/* Write to tdb file in the file system byte order */
    156 	tdb_key.dptr = mtime_key;
    157 	tdb_key.dsize = sizeof(mtime_key);
    158 	tdb_data.dptr = (unsigned char *) &(super.s_mtime);
    159 	tdb_data.dsize = sizeof(super.s_mtime);
    160 
    161 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    162 	if (retval == -1) {
    163 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    164 		goto err_out;
    165 	}
    166 
    167 	tdb_key.dptr = uuid_key;
    168 	tdb_key.dsize = sizeof(uuid_key);
    169 	tdb_data.dptr = (unsigned char *)&(super.s_uuid);
    170 	tdb_data.dsize = sizeof(super.s_uuid);
    171 
    172 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    173 	if (retval == -1) {
    174 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    175 	}
    176 
    177 err_out:
    178 	io_channel_set_blksize(channel, block_size);
    179 	return retval;
    180 }
    181 
    182 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
    183 {
    184 	errcode_t retval;
    185 	TDB_DATA tdb_key, tdb_data;
    186 
    187 	tdb_key.dptr = blksize_key;
    188 	tdb_key.dsize = sizeof(blksize_key);
    189 	tdb_data.dptr = (unsigned char *)&(block_size);
    190 	tdb_data.dsize = sizeof(block_size);
    191 
    192 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    193 	if (retval == -1) {
    194 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    195 	}
    196 
    197 	return retval;
    198 }
    199 
    200 static errcode_t undo_write_tdb(io_channel channel,
    201 				unsigned long long block, int count)
    202 
    203 {
    204 	int size, sz;
    205 	unsigned long long block_num, backing_blk_num;
    206 	errcode_t retval = 0;
    207 	ext2_loff_t offset;
    208 	struct undo_private_data *data;
    209 	TDB_DATA tdb_key, tdb_data;
    210 	unsigned char *read_ptr;
    211 	unsigned long long end_block;
    212 
    213 	data = (struct undo_private_data *) channel->private_data;
    214 
    215 	if (data->tdb == NULL) {
    216 		/*
    217 		 * Transaction database not initialized
    218 		 */
    219 		return 0;
    220 	}
    221 
    222 	if (count == 1)
    223 		size = channel->block_size;
    224 	else {
    225 		if (count < 0)
    226 			size = -count;
    227 		else
    228 			size = count * channel->block_size;
    229 	}
    230 	/*
    231 	 * Data is stored in tdb database as blocks of tdb_data_size size
    232 	 * This helps in efficient lookup further.
    233 	 *
    234 	 * We divide the disk to blocks of tdb_data_size.
    235 	 */
    236 	offset = (block * channel->block_size) + data->offset ;
    237 	block_num = offset / data->tdb_data_size;
    238 	end_block = (offset + size) / data->tdb_data_size;
    239 
    240 	tdb_transaction_start(data->tdb);
    241 	while (block_num <= end_block ) {
    242 
    243 		tdb_key.dptr = (unsigned char *)&block_num;
    244 		tdb_key.dsize = sizeof(block_num);
    245 		/*
    246 		 * Check if we have the record already
    247 		 */
    248 		if (tdb_exists(data->tdb, tdb_key)) {
    249 			/* Try the next block */
    250 			block_num++;
    251 			continue;
    252 		}
    253 		/*
    254 		 * Read one block using the backing I/O manager
    255 		 * The backing I/O manager block size may be
    256 		 * different from the tdb_data_size.
    257 		 * Also we need to recalcuate the block number with respect
    258 		 * to the backing I/O manager.
    259 		 */
    260 		offset = block_num * data->tdb_data_size;
    261 		backing_blk_num = (offset - data->offset) / channel->block_size;
    262 
    263 		count = data->tdb_data_size +
    264 				((offset - data->offset) % channel->block_size);
    265 		retval = ext2fs_get_mem(count, &read_ptr);
    266 		if (retval) {
    267 			tdb_transaction_cancel(data->tdb);
    268 			return retval;
    269 		}
    270 
    271 		memset(read_ptr, 0, count);
    272 		actual_size = 0;
    273 		if ((count % channel->block_size) == 0)
    274 			sz = count / channel->block_size;
    275 		else
    276 			sz = -count;
    277 		retval = io_channel_read_blk64(data->real, backing_blk_num,
    278 					     sz, read_ptr);
    279 		if (retval) {
    280 			if (retval != EXT2_ET_SHORT_READ) {
    281 				free(read_ptr);
    282 				tdb_transaction_cancel(data->tdb);
    283 				return retval;
    284 			}
    285 			/*
    286 			 * short read so update the record size
    287 			 * accordingly
    288 			 */
    289 			tdb_data.dsize = actual_size;
    290 		} else {
    291 			tdb_data.dsize = data->tdb_data_size;
    292 		}
    293 		tdb_data.dptr = read_ptr +
    294 				((offset - data->offset) % channel->block_size);
    295 #ifdef DEBUG
    296 		printf("Printing with key %lld data %x and size %d\n",
    297 		       block_num,
    298 		       tdb_data.dptr,
    299 		       tdb_data.dsize);
    300 #endif
    301 		if (!data->tdb_written) {
    302 			data->tdb_written = 1;
    303 			/* Write the blocksize to tdb file */
    304 			retval = write_block_size(data->tdb,
    305 						  data->tdb_data_size);
    306 			if (retval) {
    307 				tdb_transaction_cancel(data->tdb);
    308 				retval = EXT2_ET_TDB_ERR_IO;
    309 				free(read_ptr);
    310 				return retval;
    311 			}
    312 		}
    313 		retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
    314 		if (retval == -1) {
    315 			/*
    316 			 * TDB_ERR_EXISTS cannot happen because we
    317 			 * have already verified it doesn't exist
    318 			 */
    319 			tdb_transaction_cancel(data->tdb);
    320 			retval = EXT2_ET_TDB_ERR_IO;
    321 			free(read_ptr);
    322 			return retval;
    323 		}
    324 		free(read_ptr);
    325 		/* Next block */
    326 		block_num++;
    327 	}
    328 	tdb_transaction_commit(data->tdb);
    329 
    330 	return retval;
    331 }
    332 
    333 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
    334 				    unsigned long block ATTR((unused)),
    335 				    int count ATTR((unused)),
    336 				    void *data ATTR((unused)),
    337 				    size_t size ATTR((unused)),
    338 				    int actual,
    339 				    errcode_t error ATTR((unused)))
    340 {
    341 	actual_size = actual;
    342 	return error;
    343 }
    344 
    345 static void undo_err_handler_init(io_channel channel)
    346 {
    347 	channel->read_error = undo_io_read_error;
    348 }
    349 
    350 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
    351 {
    352 	io_channel	io = NULL;
    353 	struct undo_private_data *data = NULL;
    354 	errcode_t	retval;
    355 
    356 	if (name == 0)
    357 		return EXT2_ET_BAD_DEVICE_NAME;
    358 	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
    359 	if (retval)
    360 		goto cleanup;
    361 	memset(io, 0, sizeof(struct struct_io_channel));
    362 	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
    363 	retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
    364 	if (retval)
    365 		goto cleanup;
    366 
    367 	io->manager = undo_io_manager;
    368 	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
    369 	if (retval)
    370 		goto cleanup;
    371 
    372 	strcpy(io->name, name);
    373 	io->private_data = data;
    374 	io->block_size = 1024;
    375 	io->read_error = 0;
    376 	io->write_error = 0;
    377 	io->refcount = 1;
    378 
    379 	memset(data, 0, sizeof(struct undo_private_data));
    380 	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
    381 
    382 	if (undo_io_backing_manager) {
    383 		retval = undo_io_backing_manager->open(name, flags,
    384 						       &data->real);
    385 		if (retval)
    386 			goto cleanup;
    387 	} else {
    388 		data->real = 0;
    389 	}
    390 
    391 	/* setup the tdb file */
    392 	data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
    393 			     O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
    394 	if (!data->tdb) {
    395 		retval = errno;
    396 		goto cleanup;
    397 	}
    398 
    399 	/*
    400 	 * setup err handler for read so that we know
    401 	 * when the backing manager fails do short read
    402 	 */
    403 	if (data->real)
    404 		undo_err_handler_init(data->real);
    405 
    406 	*channel = io;
    407 	return 0;
    408 
    409 cleanup:
    410 	if (data && data->real)
    411 		io_channel_close(data->real);
    412 	if (data)
    413 		ext2fs_free_mem(&data);
    414 	if (io)
    415 		ext2fs_free_mem(&io);
    416 	return retval;
    417 }
    418 
    419 static errcode_t undo_close(io_channel channel)
    420 {
    421 	struct undo_private_data *data;
    422 	errcode_t	retval = 0;
    423 
    424 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    425 	data = (struct undo_private_data *) channel->private_data;
    426 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    427 
    428 	if (--channel->refcount > 0)
    429 		return 0;
    430 	/* Before closing write the file system identity */
    431 	retval = write_file_system_identity(channel, data->tdb);
    432 	if (retval)
    433 		return retval;
    434 	if (data->real)
    435 		retval = io_channel_close(data->real);
    436 	if (data->tdb)
    437 		tdb_close(data->tdb);
    438 	ext2fs_free_mem(&channel->private_data);
    439 	if (channel->name)
    440 		ext2fs_free_mem(&channel->name);
    441 	ext2fs_free_mem(&channel);
    442 
    443 	return retval;
    444 }
    445 
    446 static errcode_t undo_set_blksize(io_channel channel, int blksize)
    447 {
    448 	struct undo_private_data *data;
    449 	errcode_t		retval = 0;
    450 
    451 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    452 	data = (struct undo_private_data *) channel->private_data;
    453 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    454 
    455 	if (data->real)
    456 		retval = io_channel_set_blksize(data->real, blksize);
    457 	/*
    458 	 * Set the block size used for tdb
    459 	 */
    460 	if (!data->tdb_data_size) {
    461 		data->tdb_data_size = blksize;
    462 	}
    463 	channel->block_size = blksize;
    464 	return retval;
    465 }
    466 
    467 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
    468 			       int count, void *buf)
    469 {
    470 	errcode_t	retval = 0;
    471 	struct undo_private_data *data;
    472 
    473 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    474 	data = (struct undo_private_data *) channel->private_data;
    475 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    476 
    477 	if (data->real)
    478 		retval = io_channel_read_blk64(data->real, block, count, buf);
    479 
    480 	return retval;
    481 }
    482 
    483 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
    484 			       int count, void *buf)
    485 {
    486 	return undo_read_blk64(channel, block, count, buf);
    487 }
    488 
    489 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
    490 				int count, const void *buf)
    491 {
    492 	struct undo_private_data *data;
    493 	errcode_t	retval = 0;
    494 
    495 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    496 	data = (struct undo_private_data *) channel->private_data;
    497 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    498 	/*
    499 	 * First write the existing content into database
    500 	 */
    501 	retval = undo_write_tdb(channel, block, count);
    502 	if (retval)
    503 		 return retval;
    504 	if (data->real)
    505 		retval = io_channel_write_blk64(data->real, block, count, buf);
    506 
    507 	return retval;
    508 }
    509 
    510 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
    511 				int count, const void *buf)
    512 {
    513 	return undo_write_blk64(channel, block, count, buf);
    514 }
    515 
    516 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
    517 				 int size, const void *buf)
    518 {
    519 	struct undo_private_data *data;
    520 	errcode_t	retval = 0;
    521 	ext2_loff_t	location;
    522 	unsigned long blk_num, count;;
    523 
    524 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    525 	data = (struct undo_private_data *) channel->private_data;
    526 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    527 
    528 	location = offset + data->offset;
    529 	blk_num = location/channel->block_size;
    530 	/*
    531 	 * the size specified may spread across multiple blocks
    532 	 * also make sure we account for the fact that block start
    533 	 * offset for tdb is different from the backing I/O manager
    534 	 * due to possible different block size
    535 	 */
    536 	count = (size + (location % channel->block_size) +
    537 			channel->block_size  -1)/channel->block_size;
    538 	retval = undo_write_tdb(channel, blk_num, count);
    539 	if (retval)
    540 		return retval;
    541 	if (data->real && data->real->manager->write_byte)
    542 		retval = io_channel_write_byte(data->real, offset, size, buf);
    543 
    544 	return retval;
    545 }
    546 
    547 /*
    548  * Flush data buffers to disk.
    549  */
    550 static errcode_t undo_flush(io_channel channel)
    551 {
    552 	errcode_t	retval = 0;
    553 	struct undo_private_data *data;
    554 
    555 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    556 	data = (struct undo_private_data *) channel->private_data;
    557 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    558 
    559 	if (data->real)
    560 		retval = io_channel_flush(data->real);
    561 
    562 	return retval;
    563 }
    564 
    565 static errcode_t undo_set_option(io_channel channel, const char *option,
    566 				 const char *arg)
    567 {
    568 	errcode_t	retval = 0;
    569 	struct undo_private_data *data;
    570 	unsigned long tmp;
    571 	char *end;
    572 
    573 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    574 	data = (struct undo_private_data *) channel->private_data;
    575 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    576 
    577 	if (!strcmp(option, "tdb_data_size")) {
    578 		if (!arg)
    579 			return EXT2_ET_INVALID_ARGUMENT;
    580 
    581 		tmp = strtoul(arg, &end, 0);
    582 		if (*end)
    583 			return EXT2_ET_INVALID_ARGUMENT;
    584 		if (!data->tdb_data_size || !data->tdb_written) {
    585 			data->tdb_data_size = tmp;
    586 		}
    587 		return 0;
    588 	}
    589 	/*
    590 	 * Need to support offset option to work with
    591 	 * Unix I/O manager
    592 	 */
    593 	if (data->real && data->real->manager->set_option) {
    594 		retval = data->real->manager->set_option(data->real,
    595 							option, arg);
    596 	}
    597 	if (!retval && !strcmp(option, "offset")) {
    598 		if (!arg)
    599 			return EXT2_ET_INVALID_ARGUMENT;
    600 
    601 		tmp = strtoul(arg, &end, 0);
    602 		if (*end)
    603 			return EXT2_ET_INVALID_ARGUMENT;
    604 		data->offset = tmp;
    605 	}
    606 	return retval;
    607 }
    608 
    609 static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
    610 {
    611 	errcode_t	retval = 0;
    612 	struct undo_private_data *data;
    613 
    614 	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
    615 	data = (struct undo_private_data *) channel->private_data;
    616 	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
    617 
    618 	if (data->real)
    619 		retval = (data->real->manager->get_stats)(data->real, stats);
    620 
    621 	return retval;
    622 }
    623