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