~csurbhi/ubuntu/maverick/e2fsprogs/e2fsprogs.fix-505719

« back to all changes in this revision

Viewing changes to lib/ext2fs/undo_io.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2008-08-08 20:32:11 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20080808203211-w72lpsd9q7o3bw6x
Tags: 1.41.0-3ubuntu1
* Merge from Debian unstable (LP: #254152, #246461), remaining changes:
  - Do not build-depend on dietlibc-dev, which is in universe.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 Public
 
10
 * License.
 
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
}