undo_io.c revision 1adc384bc559da2a2fd3d65e563a89bb7d296094
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@linux.vnet.ibm.com> 7 * 8 * %Begin-Header% 9 * This file may be redistributed under the terms of the GNU Public 10 * License. 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 58struct 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 73static errcode_t undo_open(const char *name, int flags, io_channel *channel); 74static errcode_t undo_close(io_channel channel); 75static errcode_t undo_set_blksize(io_channel channel, int blksize); 76static errcode_t undo_read_blk64(io_channel channel, unsigned long long block, 77 int count, void *data); 78static errcode_t undo_write_blk64(io_channel channel, unsigned long long block, 79 int count, const void *data); 80static errcode_t undo_read_blk(io_channel channel, unsigned long block, 81 int count, void *data); 82static errcode_t undo_write_blk(io_channel channel, unsigned long block, 83 int count, const void *data); 84static errcode_t undo_flush(io_channel channel); 85static errcode_t undo_write_byte(io_channel channel, unsigned long offset, 86 int size, const void *data); 87static errcode_t undo_set_option(io_channel channel, const char *option, 88 const char *arg); 89static errcode_t undo_get_stats(io_channel channel, io_stats *stats); 90 91static 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 107io_manager undo_io_manager = &struct_undo_manager; 108static io_manager undo_io_backing_manager ; 109static char *tdb_file; 110static int actual_size; 111 112static unsigned char mtime_key[] = "filesystem MTIME"; 113static unsigned char blksize_key[] = "filesystem BLKSIZE"; 114static unsigned char uuid_key[] = "filesystem UUID"; 115 116errcode_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 125errcode_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 136static 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_blk(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 177err_out: 178 io_channel_set_blksize(channel, block_size); 179 return retval; 180} 181 182static 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 200static 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 333static 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 345static void undo_err_handler_init(io_channel channel) 346{ 347 channel->read_error = undo_io_read_error; 348} 349 350static 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 return retval; 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 undo_err_handler_init(data->real); 404 405 *channel = io; 406 return 0; 407 408cleanup: 409 if (data->real) 410 io_channel_close(data->real); 411 if (data) 412 ext2fs_free_mem(&data); 413 if (io) 414 ext2fs_free_mem(&io); 415 return retval; 416} 417 418static errcode_t undo_close(io_channel channel) 419{ 420 struct undo_private_data *data; 421 errcode_t retval = 0; 422 423 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 424 data = (struct undo_private_data *) channel->private_data; 425 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 426 427 if (--channel->refcount > 0) 428 return 0; 429 /* Before closing write the file system identity */ 430 retval = write_file_system_identity(channel, data->tdb); 431 if (retval) 432 return retval; 433 if (data->real) 434 retval = io_channel_close(data->real); 435 if (data->tdb) 436 tdb_close(data->tdb); 437 ext2fs_free_mem(&channel->private_data); 438 if (channel->name) 439 ext2fs_free_mem(&channel->name); 440 ext2fs_free_mem(&channel); 441 442 return retval; 443} 444 445static errcode_t undo_set_blksize(io_channel channel, int blksize) 446{ 447 struct undo_private_data *data; 448 errcode_t retval = 0; 449 450 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 451 data = (struct undo_private_data *) channel->private_data; 452 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 453 454 if (data->real) 455 retval = io_channel_set_blksize(data->real, blksize); 456 /* 457 * Set the block size used for tdb 458 */ 459 if (!data->tdb_data_size) { 460 data->tdb_data_size = blksize; 461 } 462 channel->block_size = blksize; 463 return retval; 464} 465 466static errcode_t undo_read_blk64(io_channel channel, unsigned long long block, 467 int count, void *buf) 468{ 469 errcode_t retval = 0; 470 struct undo_private_data *data; 471 472 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 473 data = (struct undo_private_data *) channel->private_data; 474 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 475 476 if (data->real) 477 retval = io_channel_read_blk64(data->real, block, count, buf); 478 479 return retval; 480} 481 482static errcode_t undo_read_blk(io_channel channel, unsigned long block, 483 int count, void *buf) 484{ 485 return undo_read_blk64(channel, block, count, buf); 486} 487 488static errcode_t undo_write_blk64(io_channel channel, unsigned long long block, 489 int count, const void *buf) 490{ 491 struct undo_private_data *data; 492 errcode_t retval = 0; 493 494 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 495 data = (struct undo_private_data *) channel->private_data; 496 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 497 /* 498 * First write the existing content into database 499 */ 500 retval = undo_write_tdb(channel, block, count); 501 if (retval) 502 return retval; 503 if (data->real) 504 retval = io_channel_write_blk64(data->real, block, count, buf); 505 506 return retval; 507} 508 509static errcode_t undo_write_blk(io_channel channel, unsigned long block, 510 int count, const void *buf) 511{ 512 return undo_write_blk64(channel, block, count, buf); 513} 514 515static errcode_t undo_write_byte(io_channel channel, unsigned long offset, 516 int size, const void *buf) 517{ 518 struct undo_private_data *data; 519 errcode_t retval = 0; 520 ext2_loff_t location; 521 unsigned long blk_num, count;; 522 523 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 524 data = (struct undo_private_data *) channel->private_data; 525 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 526 527 location = offset + data->offset; 528 blk_num = location/channel->block_size; 529 /* 530 * the size specified may spread across multiple blocks 531 * also make sure we account for the fact that block start 532 * offset for tdb is different from the backing I/O manager 533 * due to possible different block size 534 */ 535 count = (size + (location % channel->block_size) + 536 channel->block_size -1)/channel->block_size; 537 retval = undo_write_tdb(channel, blk_num, count); 538 if (retval) 539 return retval; 540 if (data->real && data->real->manager->write_byte) 541 retval = io_channel_write_byte(data->real, offset, size, buf); 542 543 return retval; 544} 545 546/* 547 * Flush data buffers to disk. 548 */ 549static errcode_t undo_flush(io_channel channel) 550{ 551 errcode_t retval = 0; 552 struct undo_private_data *data; 553 554 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 555 data = (struct undo_private_data *) channel->private_data; 556 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 557 558 if (data->real) 559 retval = io_channel_flush(data->real); 560 561 return retval; 562} 563 564static errcode_t undo_set_option(io_channel channel, const char *option, 565 const char *arg) 566{ 567 errcode_t retval = 0; 568 struct undo_private_data *data; 569 unsigned long tmp; 570 char *end; 571 572 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 573 data = (struct undo_private_data *) channel->private_data; 574 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 575 576 if (!strcmp(option, "tdb_data_size")) { 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 if (!data->tdb_data_size || !data->tdb_written) { 584 data->tdb_data_size = tmp; 585 } 586 return 0; 587 } 588 /* 589 * Need to support offset option to work with 590 * Unix I/O manager 591 */ 592 if (data->real && data->real->manager->set_option) { 593 retval = data->real->manager->set_option(data->real, 594 option, arg); 595 } 596 if (!retval && !strcmp(option, "offset")) { 597 if (!arg) 598 return EXT2_ET_INVALID_ARGUMENT; 599 600 tmp = strtoul(arg, &end, 0); 601 if (*end) 602 return EXT2_ET_INVALID_ARGUMENT; 603 data->offset = tmp; 604 } 605 return retval; 606} 607 608static errcode_t undo_get_stats(io_channel channel, io_stats *stats) 609{ 610 errcode_t retval = 0; 611 struct undo_private_data *data; 612 613 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); 614 data = (struct undo_private_data *) channel->private_data; 615 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); 616 617 if (data->real) 618 retval = (data->real->manager->get_stats)(data->real, stats); 619 620 return retval; 621} 622