blob: 56b0eeb568ed71855304a4fa3fe5a6f90d22ad4b [file] [log] [blame]
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +05301/*
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%
Theodore Ts'o543547a2010-05-17 21:31:56 -04009 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +053011 * %End-Header%
12 */
13
14#define _LARGEFILE_SOURCE
15#define _LARGEFILE64_SOURCE
16
Theodore Ts'od1154eb2011-09-18 17:34:37 -040017#include "config.h"
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +053018#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
Theodore Ts'o8895f432008-06-07 11:53:56 -040046#ifdef __GNUC__
47#define ATTR(x) __attribute__(x)
48#else
49#define ATTR(x)
50#endif
51
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +053052/*
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);
Theodore Ts'o1adc3842009-07-12 00:53:57 -040077static 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);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +053081static 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);
Theodore Ts'o1adc3842009-07-12 00:53:57 -040090static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +053091
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,
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400102 undo_set_option,
103 undo_get_stats,
104 undo_read_blk64,
105 undo_write_blk64,
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530106};
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
Theodore Ts'o8895f432008-06-07 11:53:56 -0400113static unsigned char mtime_key[] = "filesystem MTIME";
114static unsigned char blksize_key[] = "filesystem BLKSIZE";
115static unsigned char uuid_key[] = "filesystem UUID";
116
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530117errcode_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);
Valerie Aurora Henson24a117a2009-09-07 21:14:24 -0400152 retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530153 if (retval)
154 goto err_out;
155
156 /* Write to tdb file in the file system byte order */
Theodore Ts'o8895f432008-06-07 11:53:56 -0400157 tdb_key.dptr = mtime_key;
158 tdb_key.dsize = sizeof(mtime_key);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530159 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
Theodore Ts'o8895f432008-06-07 11:53:56 -0400168 tdb_key.dptr = uuid_key;
169 tdb_key.dsize = sizeof(uuid_key);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530170 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
Theodore Ts'o8895f432008-06-07 11:53:56 -0400188 tdb_key.dptr = blksize_key;
189 tdb_key.dsize = sizeof(blksize_key);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530190 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,
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400202 unsigned long long block, int count)
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530203
204{
Theodore Ts'o8895f432008-06-07 11:53:56 -0400205 int size, sz;
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400206 unsigned long long block_num, backing_blk_num;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530207 errcode_t retval = 0;
208 ext2_loff_t offset;
209 struct undo_private_data *data;
210 TDB_DATA tdb_key, tdb_data;
Theodore Ts'o8895f432008-06-07 11:53:56 -0400211 unsigned char *read_ptr;
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400212 unsigned long long end_block;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530213
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;
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400278 retval = io_channel_read_blk64(data->real, backing_blk_num,
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530279 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
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400297 printf("Printing with key %lld data %x and size %d\n",
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530298 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
Theodore Ts'o8895f432008-06-07 11:53:56 -0400334static 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)))
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530341{
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;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530356
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)
Eric Sandeen624e8eb2011-09-16 15:49:28 -0500361 goto cleanup;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530362 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 */
Eric Sandeen665563b2011-09-16 15:49:21 -0500404 if (data->real)
405 undo_err_handler_init(data->real);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530406
407 *channel = io;
408 return 0;
409
410cleanup:
Eric Sandeen624e8eb2011-09-16 15:49:28 -0500411 if (data && data->real)
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530412 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;
Theodore Ts'o8895f432008-06-07 11:53:56 -0400450 errcode_t retval = 0;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530451
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
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400468static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530469 int count, void *buf)
470{
Theodore Ts'o8895f432008-06-07 11:53:56 -0400471 errcode_t retval = 0;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530472 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)
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400479 retval = io_channel_read_blk64(data->real, block, count, buf);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530480
481 return retval;
482}
483
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400484static 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,
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530491 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)
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400506 retval = io_channel_write_blk64(data->real, block, count, buf);
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530507
508 return retval;
509}
510
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400511static 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
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530517static 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;
Aneesh Kumar K.V72a168b2007-08-13 15:56:21 +0530522 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}
Theodore Ts'o1adc3842009-07-12 00:53:57 -0400609
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}