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