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
5 * Copyright IBM Corporation, 2007
6 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
9 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
14 #define _LARGEFILE_SOURCE
15 #define _LARGEFILE64_SOURCE
28 #include <sys/utsname.h>
34 #include <sys/types.h>
36 #if HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
46 #define ATTR(x) __attribute__(x)
52 * For checking structure magic numbers...
55 #define EXT2_CHECK_MAGIC(struct, code) \
56 if ((struct)->magic != (code)) return (code)
58 struct undo_private_data
{
63 /* The backing io channel */
69 /* to support offset in unix I/O manager */
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
,
89 static errcode_t
undo_get_stats(io_channel channel
, io_stats
*stats
);
91 static struct struct_io_manager struct_undo_manager
= {
92 EXT2_ET_MAGIC_IO_MANAGER
,
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
;
112 static unsigned char mtime_key
[] = "filesystem MTIME";
113 static unsigned char blksize_key
[] = "filesystem BLKSIZE";
114 static unsigned char uuid_key
[] = "filesystem UUID";
116 errcode_t
set_undo_io_backing_manager(io_manager manager
)
119 * We may want to do some validation later
121 undo_io_backing_manager
= manager
;
125 errcode_t
set_undo_io_backup_file(char *file_name
)
127 tdb_file
= strdup(file_name
);
129 if (tdb_file
== NULL
) {
130 return EXT2_ET_NO_MEMORY
;
136 static errcode_t
write_file_system_identity(io_channel undo_channel
,
140 struct ext2_super_block super
;
141 TDB_DATA tdb_key
, tdb_data
;
142 struct undo_private_data
*data
;
146 data
= (struct undo_private_data
*) undo_channel
->private_data
;
147 channel
= data
->real
;
148 block_size
= channel
->block_size
;
150 io_channel_set_blksize(channel
, SUPERBLOCK_OFFSET
);
151 retval
= io_channel_read_blk64(channel
, 1, -SUPERBLOCK_SIZE
, &super
);
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
);
161 retval
= tdb_store(tdb
, tdb_key
, tdb_data
, TDB_INSERT
);
163 retval
= EXT2_ET_TDB_SUCCESS
+ tdb_error(tdb
);
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
);
172 retval
= tdb_store(tdb
, tdb_key
, tdb_data
, TDB_INSERT
);
174 retval
= EXT2_ET_TDB_SUCCESS
+ tdb_error(tdb
);
178 io_channel_set_blksize(channel
, block_size
);
182 static errcode_t
write_block_size(TDB_CONTEXT
*tdb
, int block_size
)
185 TDB_DATA tdb_key
, tdb_data
;
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
);
192 retval
= tdb_store(tdb
, tdb_key
, tdb_data
, TDB_INSERT
);
194 retval
= EXT2_ET_TDB_SUCCESS
+ tdb_error(tdb
);
200 static errcode_t
undo_write_tdb(io_channel channel
,
201 unsigned long long block
, int count
)
205 unsigned long long block_num
, backing_blk_num
;
206 errcode_t retval
= 0;
208 struct undo_private_data
*data
;
209 TDB_DATA tdb_key
, tdb_data
;
210 unsigned char *read_ptr
;
211 unsigned long long end_block
;
213 data
= (struct undo_private_data
*) channel
->private_data
;
215 if (data
->tdb
== NULL
) {
217 * Transaction database not initialized
223 size
= channel
->block_size
;
228 size
= count
* channel
->block_size
;
231 * Data is stored in tdb database as blocks of tdb_data_size size
232 * This helps in efficient lookup further.
234 * We divide the disk to blocks of tdb_data_size.
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
;
240 tdb_transaction_start(data
->tdb
);
241 while (block_num
<= end_block
) {
243 tdb_key
.dptr
= (unsigned char *)&block_num
;
244 tdb_key
.dsize
= sizeof(block_num
);
246 * Check if we have the record already
248 if (tdb_exists(data
->tdb
, tdb_key
)) {
249 /* Try the next block */
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.
260 offset
= block_num
* data
->tdb_data_size
;
261 backing_blk_num
= (offset
- data
->offset
) / channel
->block_size
;
263 count
= data
->tdb_data_size
+
264 ((offset
- data
->offset
) % channel
->block_size
);
265 retval
= ext2fs_get_mem(count
, &read_ptr
);
267 tdb_transaction_cancel(data
->tdb
);
271 memset(read_ptr
, 0, count
);
273 if ((count
% channel
->block_size
) == 0)
274 sz
= count
/ channel
->block_size
;
277 retval
= io_channel_read_blk64(data
->real
, backing_blk_num
,
280 if (retval
!= EXT2_ET_SHORT_READ
) {
282 tdb_transaction_cancel(data
->tdb
);
286 * short read so update the record size
289 tdb_data
.dsize
= actual_size
;
291 tdb_data
.dsize
= data
->tdb_data_size
;
293 tdb_data
.dptr
= read_ptr
+
294 ((offset
- data
->offset
) % channel
->block_size
);
296 printf("Printing with key %lld data %x and size %d\n",
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
);
307 tdb_transaction_cancel(data
->tdb
);
308 retval
= EXT2_ET_TDB_ERR_IO
;
313 retval
= tdb_store(data
->tdb
, tdb_key
, tdb_data
, TDB_INSERT
);
316 * TDB_ERR_EXISTS cannot happen because we
317 * have already verified it doesn't exist
319 tdb_transaction_cancel(data
->tdb
);
320 retval
= EXT2_ET_TDB_ERR_IO
;
328 tdb_transaction_commit(data
->tdb
);
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
)),
339 errcode_t error
ATTR((unused
)))
341 actual_size
= actual
;
345 static void undo_err_handler_init(io_channel channel
)
347 channel
->read_error
= undo_io_read_error
;
350 static errcode_t
undo_open(const char *name
, int flags
, io_channel
*channel
)
352 io_channel io
= NULL
;
353 struct undo_private_data
*data
= NULL
;
357 return EXT2_ET_BAD_DEVICE_NAME
;
358 retval
= ext2fs_get_mem(sizeof(struct struct_io_channel
), &io
);
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
);
367 io
->manager
= undo_io_manager
;
368 retval
= ext2fs_get_mem(strlen(name
)+1, &io
->name
);
372 strcpy(io
->name
, name
);
373 io
->private_data
= data
;
374 io
->block_size
= 1024;
379 memset(data
, 0, sizeof(struct undo_private_data
));
380 data
->magic
= EXT2_ET_MAGIC_UNIX_IO_CHANNEL
;
382 if (undo_io_backing_manager
) {
383 retval
= undo_io_backing_manager
->open(name
, flags
,
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);
400 * setup err handler for read so that we know
401 * when the backing manager fails do short read
404 undo_err_handler_init(data
->real
);
410 if (data
&& data
->real
)
411 io_channel_close(data
->real
);
413 ext2fs_free_mem(&data
);
415 ext2fs_free_mem(&io
);
419 static errcode_t
undo_close(io_channel channel
)
421 struct undo_private_data
*data
;
422 errcode_t retval
= 0;
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
);
428 if (--channel
->refcount
> 0)
430 /* Before closing write the file system identity */
431 retval
= write_file_system_identity(channel
, data
->tdb
);
435 retval
= io_channel_close(data
->real
);
437 tdb_close(data
->tdb
);
438 ext2fs_free_mem(&channel
->private_data
);
440 ext2fs_free_mem(&channel
->name
);
441 ext2fs_free_mem(&channel
);
446 static errcode_t
undo_set_blksize(io_channel channel
, int blksize
)
448 struct undo_private_data
*data
;
449 errcode_t retval
= 0;
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
);
456 retval
= io_channel_set_blksize(data
->real
, blksize
);
458 * Set the block size used for tdb
460 if (!data
->tdb_data_size
) {
461 data
->tdb_data_size
= blksize
;
463 channel
->block_size
= blksize
;
467 static errcode_t
undo_read_blk64(io_channel channel
, unsigned long long block
,
468 int count
, void *buf
)
470 errcode_t retval
= 0;
471 struct undo_private_data
*data
;
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
);
478 retval
= io_channel_read_blk64(data
->real
, block
, count
, buf
);
483 static errcode_t
undo_read_blk(io_channel channel
, unsigned long block
,
484 int count
, void *buf
)
486 return undo_read_blk64(channel
, block
, count
, buf
);
489 static errcode_t
undo_write_blk64(io_channel channel
, unsigned long long block
,
490 int count
, const void *buf
)
492 struct undo_private_data
*data
;
493 errcode_t retval
= 0;
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
);
499 * First write the existing content into database
501 retval
= undo_write_tdb(channel
, block
, count
);
505 retval
= io_channel_write_blk64(data
->real
, block
, count
, buf
);
510 static errcode_t
undo_write_blk(io_channel channel
, unsigned long block
,
511 int count
, const void *buf
)
513 return undo_write_blk64(channel
, block
, count
, buf
);
516 static errcode_t
undo_write_byte(io_channel channel
, unsigned long offset
,
517 int size
, const void *buf
)
519 struct undo_private_data
*data
;
520 errcode_t retval
= 0;
521 ext2_loff_t location
;
522 unsigned long blk_num
, count
;;
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
);
528 location
= offset
+ data
->offset
;
529 blk_num
= location
/channel
->block_size
;
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
536 count
= (size
+ (location
% channel
->block_size
) +
537 channel
->block_size
-1)/channel
->block_size
;
538 retval
= undo_write_tdb(channel
, blk_num
, count
);
541 if (data
->real
&& data
->real
->manager
->write_byte
)
542 retval
= io_channel_write_byte(data
->real
, offset
, size
, buf
);
548 * Flush data buffers to disk.
550 static errcode_t
undo_flush(io_channel channel
)
552 errcode_t retval
= 0;
553 struct undo_private_data
*data
;
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
);
560 retval
= io_channel_flush(data
->real
);
565 static errcode_t
undo_set_option(io_channel channel
, const char *option
,
568 errcode_t retval
= 0;
569 struct undo_private_data
*data
;
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
);
577 if (!strcmp(option
, "tdb_data_size")) {
579 return EXT2_ET_INVALID_ARGUMENT
;
581 tmp
= strtoul(arg
, &end
, 0);
583 return EXT2_ET_INVALID_ARGUMENT
;
584 if (!data
->tdb_data_size
|| !data
->tdb_written
) {
585 data
->tdb_data_size
= tmp
;
590 * Need to support offset option to work with
593 if (data
->real
&& data
->real
->manager
->set_option
) {
594 retval
= data
->real
->manager
->set_option(data
->real
,
597 if (!retval
&& !strcmp(option
, "offset")) {
599 return EXT2_ET_INVALID_ARGUMENT
;
601 tmp
= strtoul(arg
, &end
, 0);
603 return EXT2_ET_INVALID_ARGUMENT
;
609 static errcode_t
undo_get_stats(io_channel channel
, io_stats
*stats
)
611 errcode_t retval
= 0;
612 struct undo_private_data
*data
;
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
);
619 retval
= (data
->real
->manager
->get_stats
)(data
->real
, stats
);