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