Whamcloud - gitweb
da1cf4525bc42479b11e4cd4f9f8bb29bebd8dc5
[tools/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                 return retval;
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->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 }