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_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,
     84 				 const char *arg);
     85 
     86 static struct struct_io_manager struct_undo_manager = {
     87 	EXT2_ET_MAGIC_IO_MANAGER,
     88 	"Undo I/O Manager",
     89 	undo_open,
     90 	undo_close,
     91 	undo_set_blksize,
     92 	undo_read_blk,
     93 	undo_write_blk,
     94 	undo_flush,
     95 	undo_write_byte,
     96 	undo_set_option
     97 };
     98 
     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;
    103 
    104 static unsigned char mtime_key[] = "filesystem MTIME";
    105 static unsigned char blksize_key[] = "filesystem BLKSIZE";
    106 static unsigned char uuid_key[] = "filesystem UUID";
    107 
    108 errcode_t set_undo_io_backing_manager(io_manager manager)
    109 {
    110 	/*
    111 	 * We may want to do some validation later
    112 	 */
    113 	undo_io_backing_manager = manager;
    114 	return 0;
    115 }
    116 
    117 errcode_t set_undo_io_backup_file(char *file_name)
    118 {
    119 	tdb_file = strdup(file_name);
    120 
    121 	if (tdb_file == NULL) {
    122 		return EXT2_ET_NO_MEMORY;
    123 	}
    124 
    125 	return 0;
    126 }
    127 
    128 static errcode_t write_file_system_identity(io_channel undo_channel,
    129 							TDB_CONTEXT *tdb)
    130 {
    131 	errcode_t retval;
    132 	struct ext2_super_block super;
    133 	TDB_DATA tdb_key, tdb_data;
    134 	struct undo_private_data *data;
    135 	io_channel channel;
    136 	int block_size ;
    137 
    138 	data = (struct undo_private_data *) undo_channel->private_data;
    139 	channel = data->real;
    140 	block_size = channel->block_size;
    141 
    142 	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
    143 	retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
    144 	if (retval)
    145 		goto err_out;
    146 
    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);
    152 
    153 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    154 	if (retval == -1) {
    155 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    156 		goto err_out;
    157 	}
    158 
    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);
    163 
    164 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    165 	if (retval == -1) {
    166 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    167 	}
    168 
    169 err_out:
    170 	io_channel_set_blksize(channel, block_size);
    171 	return retval;
    172 }
    173 
    174 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
    175 {
    176 	errcode_t retval;
    177 	TDB_DATA tdb_key, tdb_data;
    178 
    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);
    183 
    184 	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
    185 	if (retval == -1) {
    186 		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
    187 	}
    188 
    189 	return retval;
    190 }
    191 
    192 static errcode_t undo_write_tdb(io_channel channel,
    193 				unsigned long block, int count)
    194 
    195 {
    196 	int size, sz;
    197 	unsigned long block_num, backing_blk_num;
    198 	errcode_t retval = 0;
    199 	ext2_loff_t offset;
    200 	struct undo_private_data *data;
    201 	TDB_DATA tdb_key, tdb_data;
    202 	unsigned char *read_ptr;
    203 	unsigned long end_block;
    204 
    205 	data = (struct undo_private_data *) channel->private_data;
    206 
    207 	if (data->tdb == NULL) {
    208 		/*
    209 		 * Transaction database not initialized
    210 		 */
    211 		return 0;
    212 	}
    213 
    214 	if (count == 1)
    215 		size = channel->block_size;
    216 	else {
    217 		if (count < 0)
    218 			size = -count;
    219 		else
    220 			size = count * channel->block_size;
    221 	}
    222 	/*
    223 	 * Data is stored in tdb database as blocks of tdb_data_size size
    224 	 * This helps in efficient lookup further.
    225 	 *
    226 	 * We divide the disk to blocks of tdb_data_size.
    227 	 */
    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;
    231 
    232 	tdb_transaction_start(data->tdb);
    233 	while (block_num <= end_block ) {
    234 
    235 		tdb_key.dptr = (unsigned char *)&block_num;
    236 		tdb_key.dsize = sizeof(block_num);
    237 		/*
    238 		 * Check if we have the record already
    239 		 */
    240 		if (tdb_exists(data->tdb, tdb_key)) {
    241 			/* Try the next block */
    242 			block_num++;
    243 			continue;
    244 		}
    245 		/*
    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.
    251 		 */
    252 		offset = block_num * data->tdb_data_size;
    253 		backing_blk_num = (offset - data->offset) / channel->block_size;
    254 
    255 		count = data->tdb_data_size +
    256 				((offset - data->offset) % channel->block_size);
    257 		retval = ext2fs_get_mem(count, &read_ptr);
    258 		if (retval) {
    259 			tdb_transaction_cancel(data->tdb);
    260 			return retval;
    261 		}
    262 
    263 		memset(read_ptr, 0, count);
    264 		actual_size = 0;
    265 		if ((count % channel->block_size) == 0)
    266 			sz = count / channel->block_size;
    267 		else
    268 			sz = -count;
    269 		retval = io_channel_read_blk(data->real, backing_blk_num,
    270 					     sz, read_ptr);
    271 		if (retval) {
    272 			if (retval != EXT2_ET_SHORT_READ) {
    273 				free(read_ptr);
    274 				tdb_transaction_cancel(data->tdb);
    275 				return retval;
    276 			}
    277 			/*
    278 			 * short read so update the record size
    279 			 * accordingly
    280 			 */
    281 			tdb_data.dsize = actual_size;
    282 		} else {
    283 			tdb_data.dsize = data->tdb_data_size;
    284 		}
    285 		tdb_data.dptr = read_ptr +
    286 				((offset - data->offset) % channel->block_size);
    287 #ifdef DEBUG
    288 		printf("Printing with key %ld data %x and size %d\n",
    289 		       block_num,
    290 		       tdb_data.dptr,
    291 		       tdb_data.dsize);
    292 #endif
    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);
    298 			if (retval) {
    299 				tdb_transaction_cancel(data->tdb);
    300 				retval = EXT2_ET_TDB_ERR_IO;
    301 				free(read_ptr);
    302 				return retval;
    303 			}
    304 		}
    305 		retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
    306 		if (retval == -1) {
    307 			/*
    308 			 * TDB_ERR_EXISTS cannot happen because we
    309 			 * have already verified it doesn't exist
    310 			 */
    311 			tdb_transaction_cancel(data->tdb);
    312 			retval = EXT2_ET_TDB_ERR_IO;
    313 			free(read_ptr);
    314 			return retval;
    315 		}
    316 		free(read_ptr);
    317 		/* Next block */
    318 		block_num++;
    319 	}
    320 	tdb_transaction_commit(data->tdb);
    321 
    322 	return retval;
    323 }
    324 
    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)),
    330 				    int actual,
    331 				    errcode_t error ATTR((unused)))
    332 {
    333 	actual_size = actual;
    334 	return error;
    335 }
    336 
    337 static void undo_err_handler_init(io_channel channel)
    338 {
    339 	channel->read_error = undo_io_read_error;
    340 }
    341 
    342 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
    343 {
    344 	io_channel	io = NULL;
    345 	struct undo_private_data *data = NULL;
    346 	errcode_t	retval;
    347 
    348 	if (name == 0)
    349 		return EXT2_ET_BAD_DEVICE_NAME;
    350 	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
    351 	if (retval)
    352 		return retval;
    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);
    356 	if (retval)
    357 		goto cleanup;
    358 
    359 	io->manager = undo_io_manager;
    360 	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
    361 	if (retval)
    362 		goto cleanup;
    363 
    364 	strcpy(io->name, name);
    365 	io->private_data = data;
    366 	io->block_size = 1024;
    367 	io->read_error = 0;
    368 	io->write_error = 0;
    369 	io->refcount = 1;
    370 
    371 	memset(data, 0, sizeof(struct undo_private_data));
    372 	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
    373 
    374 	if (undo_io_backing_manager) {
    375 		retval = undo_io_backing_manager->open(name, flags,
    376 						       &data->real);
    377 		if (retval)
    378 			goto cleanup;
    379 	} else {
    380 		data->real = 0;
    381 	}
    382 
    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);
    386 	if (!data->tdb) {
    387 		retval = errno;
    388 		goto cleanup;
    389 	}
    390 
    391 	/*
    392 	 * setup err handler for read so that we know
    393 	 * when the backing manager fails do short read
    394 	 */
    395 	undo_err_handler_init(data->real);
    396 
    397 	*channel = io;
    398 	return 0;
    399 
    400 cleanup:
    401 	if (data->real)
    402 		io_channel_close(data->real);
    403 	if (data)
    404 		ext2fs_free_mem(&data);
    405 	if (io)
    406 		ext2fs_free_mem(&io);
    407 	return retval;
    408 }
    409 
    410 static errcode_t undo_close(io_channel channel)
    411 {
    412 	struct undo_private_data *data;
    413 	errcode_t	retval = 0;
    414 
    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);
    418 
    419 	if (--channel->refcount > 0)
    420 		return 0;
    421 	/* Before closing write the file system identity */
    422 	retval = write_file_system_identity(channel, data->tdb);
    423 	if (retval)
    424 		return retval;
    425 	if (data->real)
    426 		retval = io_channel_close(data->real);
    427 	if (data->tdb)
    428 		tdb_close(data->tdb);
    429 	ext2fs_free_mem(&channel->private_data);
    430 	if (channel->name)
    431 		ext2fs_free_mem(&channel->name);
    432 	ext2fs_free_mem(&channel);
    433 
    434 	return retval;
    435 }
    436 
    437 static errcode_t undo_set_blksize(io_channel channel, int blksize)
    438 {
    439 	struct undo_private_data *data;
    440 	errcode_t		retval = 0;
    441 
    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);
    445 
    446 	if (data->real)
    447 		retval = io_channel_set_blksize(data->real, blksize);
    448 	/*
    449 	 * Set the block size used for tdb
    450 	 */
    451 	if (!data->tdb_data_size) {
    452 		data->tdb_data_size = blksize;
    453 	}
    454 	channel->block_size = blksize;
    455 	return retval;
    456 }
    457 
    458 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
    459 			       int count, void *buf)
    460 {
    461 	errcode_t	retval = 0;
    462 	struct undo_private_data *data;
    463 
    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);
    467 
    468 	if (data->real)
    469 		retval = io_channel_read_blk(data->real, block, count, buf);
    470 
    471 	return retval;
    472 }
    473 
    474 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
    475 				int count, const void *buf)
    476 {
    477 	struct undo_private_data *data;
    478 	errcode_t	retval = 0;
    479 
    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);
    483 	/*
    484 	 * First write the existing content into database
    485 	 */
    486 	retval = undo_write_tdb(channel, block, count);
    487 	if (retval)
    488 		 return retval;
    489 	if (data->real)
    490 		retval = io_channel_write_blk(data->real, block, count, buf);
    491 
    492 	return retval;
    493 }
    494 
    495 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
    496 				 int size, const void *buf)
    497 {
    498 	struct undo_private_data *data;
    499 	errcode_t	retval = 0;
    500 	ext2_loff_t	location;
    501 	unsigned long blk_num, count;;
    502 
    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);
    506 
    507 	location = offset + data->offset;
    508 	blk_num = location/channel->block_size;
    509 	/*
    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
    514 	 */
    515 	count = (size + (location % channel->block_size) +
    516 			channel->block_size  -1)/channel->block_size;
    517 	retval = undo_write_tdb(channel, blk_num, count);
    518 	if (retval)
    519 		return retval;
    520 	if (data->real && data->real->manager->write_byte)
    521 		retval = io_channel_write_byte(data->real, offset, size, buf);
    522 
    523 	return retval;
    524 }
    525 
    526 /*
    527  * Flush data buffers to disk.
    528  */
    529 static errcode_t undo_flush(io_channel channel)
    530 {
    531 	errcode_t	retval = 0;
    532 	struct undo_private_data *data;
    533 
    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);
    537 
    538 	if (data->real)
    539 		retval = io_channel_flush(data->real);
    540 
    541 	return retval;
    542 }
    543 
    544 static errcode_t undo_set_option(io_channel channel, const char *option,
    545 				 const char *arg)
    546 {
    547 	errcode_t	retval = 0;
    548 	struct undo_private_data *data;
    549 	unsigned long tmp;
    550 	char *end;
    551 
    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);
    555 
    556 	if (!strcmp(option, "tdb_data_size")) {
    557 		if (!arg)
    558 			return EXT2_ET_INVALID_ARGUMENT;
    559 
    560 		tmp = strtoul(arg, &end, 0);
    561 		if (*end)
    562 			return EXT2_ET_INVALID_ARGUMENT;
    563 		if (!data->tdb_data_size || !data->tdb_written) {
    564 			data->tdb_data_size = tmp;
    565 		}
    566 		return 0;
    567 	}
    568 	/*
    569 	 * Need to support offset option to work with
    570 	 * Unix I/O manager
    571 	 */
    572 	if (data->real && data->real->manager->set_option) {
    573 		retval = data->real->manager->set_option(data->real,
    574 							option, arg);
    575 	}
    576 	if (!retval && !strcmp(option, "offset")) {
    577 		if (!arg)
    578 			return EXT2_ET_INVALID_ARGUMENT;
    579 
    580 		tmp = strtoul(arg, &end, 0);
    581 		if (*end)
    582 			return EXT2_ET_INVALID_ARGUMENT;
    583 		data->offset = tmp;
    584 	}
    585 	return retval;
    586 }
    587