Whamcloud - gitweb
libe2p, libext2fs: Update file copyright permission states to match COPYING
[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_blk(io_channel channel, unsigned long block,
77                                int count, void *data);
78 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
79                                 int count, const void *data);
80 static errcode_t undo_flush(io_channel channel);
81 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
82                                 int size, const void *data);
83 static errcode_t undo_set_option(io_channel channel, const char *option,
84                                  const char *arg);
85
86 static struct struct_io_manager struct_undo_manager = {
87         EXT2_ET_MAGIC_IO_MANAGER,
88         "Undo I/O Manager",
89         undo_open,
90         undo_close,
91         undo_set_blksize,
92         undo_read_blk,
93         undo_write_blk,
94         undo_flush,
95         undo_write_byte,
96         undo_set_option
97 };
98
99 io_manager undo_io_manager = &struct_undo_manager;
100 static io_manager undo_io_backing_manager ;
101 static char *tdb_file;
102 static int actual_size;
103
104 static unsigned char mtime_key[] = "filesystem MTIME";
105 static unsigned char blksize_key[] = "filesystem BLKSIZE";
106 static unsigned char uuid_key[] = "filesystem UUID";
107
108 errcode_t set_undo_io_backing_manager(io_manager manager)
109 {
110         /*
111          * We may want to do some validation later
112          */
113         undo_io_backing_manager = manager;
114         return 0;
115 }
116
117 errcode_t set_undo_io_backup_file(char *file_name)
118 {
119         tdb_file = strdup(file_name);
120
121         if (tdb_file == NULL) {
122                 return EXT2_ET_NO_MEMORY;
123         }
124
125         return 0;
126 }
127
128 static errcode_t write_file_system_identity(io_channel undo_channel,
129                                                         TDB_CONTEXT *tdb)
130 {
131         errcode_t retval;
132         struct ext2_super_block super;
133         TDB_DATA tdb_key, tdb_data;
134         struct undo_private_data *data;
135         io_channel channel;
136         int block_size ;
137
138         data = (struct undo_private_data *) undo_channel->private_data;
139         channel = data->real;
140         block_size = channel->block_size;
141
142         io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
143         retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
144         if (retval)
145                 goto err_out;
146
147         /* Write to tdb file in the file system byte order */
148         tdb_key.dptr = mtime_key;
149         tdb_key.dsize = sizeof(mtime_key);
150         tdb_data.dptr = (unsigned char *) &(super.s_mtime);
151         tdb_data.dsize = sizeof(super.s_mtime);
152
153         retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
154         if (retval == -1) {
155                 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
156                 goto err_out;
157         }
158
159         tdb_key.dptr = uuid_key;
160         tdb_key.dsize = sizeof(uuid_key);
161         tdb_data.dptr = (unsigned char *)&(super.s_uuid);
162         tdb_data.dsize = sizeof(super.s_uuid);
163
164         retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
165         if (retval == -1) {
166                 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
167         }
168
169 err_out:
170         io_channel_set_blksize(channel, block_size);
171         return retval;
172 }
173
174 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
175 {
176         errcode_t retval;
177         TDB_DATA tdb_key, tdb_data;
178
179         tdb_key.dptr = blksize_key;
180         tdb_key.dsize = sizeof(blksize_key);
181         tdb_data.dptr = (unsigned char *)&(block_size);
182         tdb_data.dsize = sizeof(block_size);
183
184         retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
185         if (retval == -1) {
186                 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
187         }
188
189         return retval;
190 }
191
192 static errcode_t undo_write_tdb(io_channel channel,
193                                 unsigned long block, int count)
194
195 {
196         int size, sz;
197         unsigned long block_num, backing_blk_num;
198         errcode_t retval = 0;
199         ext2_loff_t offset;
200         struct undo_private_data *data;
201         TDB_DATA tdb_key, tdb_data;
202         unsigned char *read_ptr;
203         unsigned long end_block;
204
205         data = (struct undo_private_data *) channel->private_data;
206
207         if (data->tdb == NULL) {
208                 /*
209                  * Transaction database not initialized
210                  */
211                 return 0;
212         }
213
214         if (count == 1)
215                 size = channel->block_size;
216         else {
217                 if (count < 0)
218                         size = -count;
219                 else
220                         size = count * channel->block_size;
221         }
222         /*
223          * Data is stored in tdb database as blocks of tdb_data_size size
224          * This helps in efficient lookup further.
225          *
226          * We divide the disk to blocks of tdb_data_size.
227          */
228         offset = (block * channel->block_size) + data->offset ;
229         block_num = offset / data->tdb_data_size;
230         end_block = (offset + size) / data->tdb_data_size;
231
232         tdb_transaction_start(data->tdb);
233         while (block_num <= end_block ) {
234
235                 tdb_key.dptr = (unsigned char *)&block_num;
236                 tdb_key.dsize = sizeof(block_num);
237                 /*
238                  * Check if we have the record already
239                  */
240                 if (tdb_exists(data->tdb, tdb_key)) {
241                         /* Try the next block */
242                         block_num++;
243                         continue;
244                 }
245                 /*
246                  * Read one block using the backing I/O manager
247                  * The backing I/O manager block size may be
248                  * different from the tdb_data_size.
249                  * Also we need to recalcuate the block number with respect
250                  * to the backing I/O manager.
251                  */
252                 offset = block_num * data->tdb_data_size;
253                 backing_blk_num = (offset - data->offset) / channel->block_size;
254
255                 count = data->tdb_data_size +
256                                 ((offset - data->offset) % channel->block_size);
257                 retval = ext2fs_get_mem(count, &read_ptr);
258                 if (retval) {
259                         tdb_transaction_cancel(data->tdb);
260                         return retval;
261                 }
262
263                 memset(read_ptr, 0, count);
264                 actual_size = 0;
265                 if ((count % channel->block_size) == 0)
266                         sz = count / channel->block_size;
267                 else
268                         sz = -count;
269                 retval = io_channel_read_blk(data->real, backing_blk_num,
270                                              sz, read_ptr);
271                 if (retval) {
272                         if (retval != EXT2_ET_SHORT_READ) {
273                                 free(read_ptr);
274                                 tdb_transaction_cancel(data->tdb);
275                                 return retval;
276                         }
277                         /*
278                          * short read so update the record size
279                          * accordingly
280                          */
281                         tdb_data.dsize = actual_size;
282                 } else {
283                         tdb_data.dsize = data->tdb_data_size;
284                 }
285                 tdb_data.dptr = read_ptr +
286                                 ((offset - data->offset) % channel->block_size);
287 #ifdef DEBUG
288                 printf("Printing with key %ld data %x and size %d\n",
289                        block_num,
290                        tdb_data.dptr,
291                        tdb_data.dsize);
292 #endif
293                 if (!data->tdb_written) {
294                         data->tdb_written = 1;
295                         /* Write the blocksize to tdb file */
296                         retval = write_block_size(data->tdb,
297                                                   data->tdb_data_size);
298                         if (retval) {
299                                 tdb_transaction_cancel(data->tdb);
300                                 retval = EXT2_ET_TDB_ERR_IO;
301                                 free(read_ptr);
302                                 return retval;
303                         }
304                 }
305                 retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
306                 if (retval == -1) {
307                         /*
308                          * TDB_ERR_EXISTS cannot happen because we
309                          * have already verified it doesn't exist
310                          */
311                         tdb_transaction_cancel(data->tdb);
312                         retval = EXT2_ET_TDB_ERR_IO;
313                         free(read_ptr);
314                         return retval;
315                 }
316                 free(read_ptr);
317                 /* Next block */
318                 block_num++;
319         }
320         tdb_transaction_commit(data->tdb);
321
322         return retval;
323 }
324
325 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
326                                     unsigned long block ATTR((unused)),
327                                     int count ATTR((unused)),
328                                     void *data ATTR((unused)),
329                                     size_t size ATTR((unused)),
330                                     int actual,
331                                     errcode_t error ATTR((unused)))
332 {
333         actual_size = actual;
334         return error;
335 }
336
337 static void undo_err_handler_init(io_channel channel)
338 {
339         channel->read_error = undo_io_read_error;
340 }
341
342 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
343 {
344         io_channel      io = NULL;
345         struct undo_private_data *data = NULL;
346         errcode_t       retval;
347
348         if (name == 0)
349                 return EXT2_ET_BAD_DEVICE_NAME;
350         retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
351         if (retval)
352                 return retval;
353         memset(io, 0, sizeof(struct struct_io_channel));
354         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
355         retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
356         if (retval)
357                 goto cleanup;
358
359         io->manager = undo_io_manager;
360         retval = ext2fs_get_mem(strlen(name)+1, &io->name);
361         if (retval)
362                 goto cleanup;
363
364         strcpy(io->name, name);
365         io->private_data = data;
366         io->block_size = 1024;
367         io->read_error = 0;
368         io->write_error = 0;
369         io->refcount = 1;
370
371         memset(data, 0, sizeof(struct undo_private_data));
372         data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
373
374         if (undo_io_backing_manager) {
375                 retval = undo_io_backing_manager->open(name, flags,
376                                                        &data->real);
377                 if (retval)
378                         goto cleanup;
379         } else {
380                 data->real = 0;
381         }
382
383         /* setup the tdb file */
384         data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
385                              O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
386         if (!data->tdb) {
387                 retval = errno;
388                 goto cleanup;
389         }
390
391         /*
392          * setup err handler for read so that we know
393          * when the backing manager fails do short read
394          */
395         undo_err_handler_init(data->real);
396
397         *channel = io;
398         return 0;
399
400 cleanup:
401         if (data->real)
402                 io_channel_close(data->real);
403         if (data)
404                 ext2fs_free_mem(&data);
405         if (io)
406                 ext2fs_free_mem(&io);
407         return retval;
408 }
409
410 static errcode_t undo_close(io_channel channel)
411 {
412         struct undo_private_data *data;
413         errcode_t       retval = 0;
414
415         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
416         data = (struct undo_private_data *) channel->private_data;
417         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
418
419         if (--channel->refcount > 0)
420                 return 0;
421         /* Before closing write the file system identity */
422         retval = write_file_system_identity(channel, data->tdb);
423         if (retval)
424                 return retval;
425         if (data->real)
426                 retval = io_channel_close(data->real);
427         if (data->tdb)
428                 tdb_close(data->tdb);
429         ext2fs_free_mem(&channel->private_data);
430         if (channel->name)
431                 ext2fs_free_mem(&channel->name);
432         ext2fs_free_mem(&channel);
433
434         return retval;
435 }
436
437 static errcode_t undo_set_blksize(io_channel channel, int blksize)
438 {
439         struct undo_private_data *data;
440         errcode_t               retval = 0;
441
442         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
443         data = (struct undo_private_data *) channel->private_data;
444         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
445
446         if (data->real)
447                 retval = io_channel_set_blksize(data->real, blksize);
448         /*
449          * Set the block size used for tdb
450          */
451         if (!data->tdb_data_size) {
452                 data->tdb_data_size = blksize;
453         }
454         channel->block_size = blksize;
455         return retval;
456 }
457
458 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
459                                int count, void *buf)
460 {
461         errcode_t       retval = 0;
462         struct undo_private_data *data;
463
464         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
465         data = (struct undo_private_data *) channel->private_data;
466         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
467
468         if (data->real)
469                 retval = io_channel_read_blk(data->real, block, count, buf);
470
471         return retval;
472 }
473
474 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
475                                 int count, const void *buf)
476 {
477         struct undo_private_data *data;
478         errcode_t       retval = 0;
479
480         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
481         data = (struct undo_private_data *) channel->private_data;
482         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
483         /*
484          * First write the existing content into database
485          */
486         retval = undo_write_tdb(channel, block, count);
487         if (retval)
488                  return retval;
489         if (data->real)
490                 retval = io_channel_write_blk(data->real, block, count, buf);
491
492         return retval;
493 }
494
495 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
496                                  int size, const void *buf)
497 {
498         struct undo_private_data *data;
499         errcode_t       retval = 0;
500         ext2_loff_t     location;
501         unsigned long blk_num, count;;
502
503         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
504         data = (struct undo_private_data *) channel->private_data;
505         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
506
507         location = offset + data->offset;
508         blk_num = location/channel->block_size;
509         /*
510          * the size specified may spread across multiple blocks
511          * also make sure we account for the fact that block start
512          * offset for tdb is different from the backing I/O manager
513          * due to possible different block size
514          */
515         count = (size + (location % channel->block_size) +
516                         channel->block_size  -1)/channel->block_size;
517         retval = undo_write_tdb(channel, blk_num, count);
518         if (retval)
519                 return retval;
520         if (data->real && data->real->manager->write_byte)
521                 retval = io_channel_write_byte(data->real, offset, size, buf);
522
523         return retval;
524 }
525
526 /*
527  * Flush data buffers to disk.
528  */
529 static errcode_t undo_flush(io_channel channel)
530 {
531         errcode_t       retval = 0;
532         struct undo_private_data *data;
533
534         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
535         data = (struct undo_private_data *) channel->private_data;
536         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
537
538         if (data->real)
539                 retval = io_channel_flush(data->real);
540
541         return retval;
542 }
543
544 static errcode_t undo_set_option(io_channel channel, const char *option,
545                                  const char *arg)
546 {
547         errcode_t       retval = 0;
548         struct undo_private_data *data;
549         unsigned long tmp;
550         char *end;
551
552         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
553         data = (struct undo_private_data *) channel->private_data;
554         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
555
556         if (!strcmp(option, "tdb_data_size")) {
557                 if (!arg)
558                         return EXT2_ET_INVALID_ARGUMENT;
559
560                 tmp = strtoul(arg, &end, 0);
561                 if (*end)
562                         return EXT2_ET_INVALID_ARGUMENT;
563                 if (!data->tdb_data_size || !data->tdb_written) {
564                         data->tdb_data_size = tmp;
565                 }
566                 return 0;
567         }
568         /*
569          * Need to support offset option to work with
570          * Unix I/O manager
571          */
572         if (data->real && data->real->manager->set_option) {
573                 retval = data->real->manager->set_option(data->real,
574                                                         option, arg);
575         }
576         if (!retval && !strcmp(option, "offset")) {
577                 if (!arg)
578                         return EXT2_ET_INVALID_ARGUMENT;
579
580                 tmp = strtoul(arg, &end, 0);
581                 if (*end)
582                         return EXT2_ET_INVALID_ARGUMENT;
583                 data->offset = tmp;
584         }
585         return retval;
586 }