Whamcloud - gitweb
libext2fs: add pseudo bitmap backend type EXT2FS_BMAP64_AUTODIR
[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 "config.h"
18 #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
46 #ifdef __GNUC__
47 #define ATTR(x) __attribute__(x)
48 #else
49 #define ATTR(x)
50 #endif
51
52 /*
53  * For checking structure magic numbers...
54  */
55
56 #define EXT2_CHECK_MAGIC(struct, code) \
57           if ((struct)->magic != (code)) return (code)
58
59 struct 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
74 static errcode_t undo_open(const char *name, int flags, io_channel *channel);
75 static errcode_t undo_close(io_channel channel);
76 static errcode_t undo_set_blksize(io_channel channel, int blksize);
77 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
78                                  int count, void *data);
79 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
80                                   int count, const void *data);
81 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
82                                int count, void *data);
83 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
84                                 int count, const void *data);
85 static errcode_t undo_flush(io_channel channel);
86 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
87                                 int size, const void *data);
88 static errcode_t undo_set_option(io_channel channel, const char *option,
89                                  const char *arg);
90 static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
91
92 static 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,
102         undo_set_option,
103         undo_get_stats,
104         undo_read_blk64,
105         undo_write_blk64,
106 };
107
108 io_manager undo_io_manager = &struct_undo_manager;
109 static io_manager undo_io_backing_manager ;
110 static char *tdb_file;
111 static int actual_size;
112
113 static unsigned char mtime_key[] = "filesystem MTIME";
114 static unsigned char blksize_key[] = "filesystem BLKSIZE";
115 static unsigned char uuid_key[] = "filesystem UUID";
116
117 errcode_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
126 errcode_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
137 static 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);
152         retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
153         if (retval)
154                 goto err_out;
155
156         /* Write to tdb file in the file system byte order */
157         tdb_key.dptr = mtime_key;
158         tdb_key.dsize = sizeof(mtime_key);
159         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
168         tdb_key.dptr = uuid_key;
169         tdb_key.dsize = sizeof(uuid_key);
170         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
178 err_out:
179         io_channel_set_blksize(channel, block_size);
180         return retval;
181 }
182
183 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
184 {
185         errcode_t retval;
186         TDB_DATA tdb_key, tdb_data;
187
188         tdb_key.dptr = blksize_key;
189         tdb_key.dsize = sizeof(blksize_key);
190         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
201 static errcode_t undo_write_tdb(io_channel channel,
202                                 unsigned long long block, int count)
203
204 {
205         int size, sz;
206         unsigned long long block_num, backing_blk_num;
207         errcode_t retval = 0;
208         ext2_loff_t offset;
209         struct undo_private_data *data;
210         TDB_DATA tdb_key, tdb_data;
211         unsigned char *read_ptr;
212         unsigned long long end_block;
213
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;
278                 retval = io_channel_read_blk64(data->real, backing_blk_num,
279                                              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
297                 printf("Printing with key %lld data %x and size %d\n",
298                        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
334 static 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)))
341 {
342         actual_size = actual;
343         return error;
344 }
345
346 static void undo_err_handler_init(io_channel channel)
347 {
348         channel->read_error = undo_io_read_error;
349 }
350
351 static 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;
356
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)
361                 goto cleanup;
362         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          */
404         if (data->real)
405                 undo_err_handler_init(data->real);
406
407         *channel = io;
408         return 0;
409
410 cleanup:
411         if (data && data->real)
412                 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
420 static 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
447 static errcode_t undo_set_blksize(io_channel channel, int blksize)
448 {
449         struct undo_private_data *data;
450         errcode_t               retval = 0;
451
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
468 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
469                                int count, void *buf)
470 {
471         errcode_t       retval = 0;
472         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)
479                 retval = io_channel_read_blk64(data->real, block, count, buf);
480
481         return retval;
482 }
483
484 static 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
490 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
491                                 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)
506                 retval = io_channel_write_blk64(data->real, block, count, buf);
507
508         return retval;
509 }
510
511 static 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
517 static 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;
522         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  */
551 static 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
566 static 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 }
609
610 static 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 }