]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/ext2fs/undo_io.c
df55abf3e04f8175acc2181477ae1db7efb50e9f
[thirdparty/e2fsprogs.git] / lib / ext2fs / undo_io.c
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 <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 }