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 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 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_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 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 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 409cleanup: 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 419static 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 446static 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 467static 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 483static 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 489static 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 510static 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 516static 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 */ 550static 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 565static 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 609static 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