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