~daniel-mehrmann/e2fsprogs/master

« back to all changes in this revision

Viewing changes to debugfs/do_journal.c

  • Committer: Daniel Mehrmann
  • Date: 2014-12-16 09:16:59 UTC
  • mfrom: (1.2.25)
  • Revision ID: daniel.mehrmann@gmx.de-20141216091659-ymhbl4ualba43vuc
Tags: 1.43-SN-2014-12-16-0ubuntu1
* Merge in snapshot from the maint branch 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * do_journal.c --- Scribble onto the journal!
 
3
 *
 
4
 * Copyright (C) 2014 Oracle.  This file may be redistributed
 
5
 * under the terms of the GNU Public License.
 
6
 */
 
7
 
 
8
#include "config.h"
 
9
#include <stdio.h>
 
10
#ifdef HAVE_GETOPT_H
 
11
#include <getopt.h>
 
12
#else
 
13
extern int optind;
 
14
extern char *optarg;
 
15
#endif
 
16
#include <ctype.h>
 
17
#include <unistd.h>
 
18
#ifdef HAVE_SYS_TIME_H
 
19
#include <sys/time.h>
 
20
#endif
 
21
 
 
22
#include "debugfs.h"
 
23
#include "jfs_user.h"
 
24
#include "ext2fs/kernel-jbd.h"
 
25
 
 
26
/* journal.c */
 
27
errcode_t ext2fs_open_journal(ext2_filsys fs, journal_t **j);
 
28
errcode_t ext2fs_close_journal(ext2_filsys fs, journal_t **j);
 
29
errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
 
30
void jbd2_commit_block_csum_set(journal_t *j, struct buffer_head *bh);
 
31
void jbd2_revoke_csum_set(journal_t *j, struct buffer_head *bh);
 
32
void jbd2_descr_block_csum_set(journal_t *j, struct buffer_head *bh);
 
33
void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag,
 
34
                             struct buffer_head *bh, __u32 sequence);
 
35
 
 
36
#undef DEBUG
 
37
 
 
38
#ifdef DEBUG
 
39
# define dbg_printf(f, a...)  do {printf("JFS DEBUG: " f, ## a); \
 
40
        fflush(stdout); \
 
41
} while (0)
 
42
#else
 
43
# define dbg_printf(f, a...)
 
44
#endif
 
45
 
 
46
#define JOURNAL_CHECK_TRANS_MAGIC(x)    \
 
47
        do { \
 
48
                if ((x)->magic != J_TRANS_MAGIC) \
 
49
                        return EXT2_ET_INVALID_ARGUMENT; \
 
50
        } while (0)
 
51
 
 
52
#define J_TRANS_MAGIC           0xD15EA5ED
 
53
#define J_TRANS_OPEN            1
 
54
#define J_TRANS_COMMITTED       2
 
55
struct journal_transaction_s {
 
56
        unsigned int magic;
 
57
        ext2_filsys fs;
 
58
        journal_t *journal;
 
59
        blk64_t block;
 
60
        blk64_t start, end;
 
61
        tid_t tid;
 
62
        int flags;
 
63
};
 
64
 
 
65
typedef struct journal_transaction_s journal_transaction_t;
 
66
 
 
67
static journal_t *current_journal = NULL;
 
68
 
 
69
static void journal_dump_trans(journal_transaction_t *trans, const char *tag)
 
70
{
 
71
        dbg_printf("TRANS %p(%s): tid=%d start=%llu block=%llu end=%llu "
 
72
                   "flags=0x%x\n", trans, tag, trans->tid, trans->start,
 
73
                   trans->block, trans->end, trans->flags);
 
74
}
 
75
 
 
76
static errcode_t journal_commit_trans(journal_transaction_t *trans)
 
77
{
 
78
        struct buffer_head *bh, *cbh = NULL;
 
79
        struct commit_header *commit;
 
80
#ifdef HAVE_SYS_TIME_H
 
81
        struct timeval tv;
 
82
#endif
 
83
        errcode_t err;
 
84
 
 
85
        JOURNAL_CHECK_TRANS_MAGIC(trans);
 
86
 
 
87
        if ((trans->flags & J_TRANS_COMMITTED) ||
 
88
            !(trans->flags & J_TRANS_OPEN))
 
89
                return EXT2_ET_INVALID_ARGUMENT;
 
90
 
 
91
        bh = getblk(trans->journal->j_dev, 0, trans->journal->j_blocksize);
 
92
        if (bh == NULL)
 
93
                return ENOMEM;
 
94
 
 
95
        /* write the descriptor block header */
 
96
        commit = (struct commit_header *)bh->b_data;
 
97
        commit->h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
 
98
        commit->h_blocktype = ext2fs_cpu_to_be32(JFS_COMMIT_BLOCK);
 
99
        commit->h_sequence = ext2fs_cpu_to_be32(trans->tid);
 
100
        if (JFS_HAS_COMPAT_FEATURE(trans->journal,
 
101
                                   JFS_FEATURE_COMPAT_CHECKSUM)) {
 
102
                __u32 csum_v1 = ~0;
 
103
                blk64_t cblk;
 
104
 
 
105
                cbh = getblk(trans->journal->j_dev, 0,
 
106
                             trans->journal->j_blocksize);
 
107
                if (cbh == NULL) {
 
108
                        err = ENOMEM;
 
109
                        goto error;
 
110
                }
 
111
 
 
112
                for (cblk = trans->start; cblk < trans->block; cblk++) {
 
113
                        err = journal_bmap(trans->journal, cblk,
 
114
                                           &cbh->b_blocknr);
 
115
                        if (err)
 
116
                                goto error;
 
117
                        mark_buffer_uptodate(cbh, 0);
 
118
                        ll_rw_block(READ, 1, &cbh);
 
119
                        err = cbh->b_err;
 
120
                        if (err)
 
121
                                goto error;
 
122
                        csum_v1 = ext2fs_crc32_be(csum_v1,
 
123
                                        (unsigned char const *)cbh->b_data,
 
124
                                        cbh->b_size);
 
125
                }
 
126
 
 
127
                commit->h_chksum_type = JFS_CRC32_CHKSUM;
 
128
                commit->h_chksum_size = JFS_CRC32_CHKSUM_SIZE;
 
129
                commit->h_chksum[0] = ext2fs_cpu_to_be32(csum_v1);
 
130
        } else {
 
131
                commit->h_chksum_type = 0;
 
132
                commit->h_chksum_size = 0;
 
133
                commit->h_chksum[0] = 0;
 
134
        }
 
135
#ifdef HAVE_SYS_TIME_H
 
136
        gettimeofday(&tv, NULL);
 
137
        commit->h_commit_sec = ext2fs_cpu_to_be32(tv.tv_sec);
 
138
        commit->h_commit_nsec = ext2fs_cpu_to_be32(tv.tv_usec * 1000);
 
139
#else
 
140
        commit->h_commit_sec = 0;
 
141
        commit->h_commit_nsec = 0;
 
142
#endif
 
143
 
 
144
        /* Write block */
 
145
        jbd2_commit_block_csum_set(trans->journal, bh);
 
146
        err = journal_bmap(trans->journal, trans->block, &bh->b_blocknr);
 
147
        if (err)
 
148
                goto error;
 
149
 
 
150
        dbg_printf("Writing commit block at %llu:%llu\n", trans->block,
 
151
                   bh->b_blocknr);
 
152
        mark_buffer_dirty(bh);
 
153
        ll_rw_block(WRITE, 1, &bh);
 
154
        err = bh->b_err;
 
155
        if (err)
 
156
                goto error;
 
157
        trans->flags |= J_TRANS_COMMITTED;
 
158
        trans->flags &= ~J_TRANS_OPEN;
 
159
        trans->block++;
 
160
 
 
161
        trans->fs->super->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER;
 
162
        ext2fs_mark_super_dirty(trans->fs);
 
163
error:
 
164
        if (cbh)
 
165
                brelse(cbh);
 
166
        brelse(bh);
 
167
        return err;
 
168
}
 
169
 
 
170
static errcode_t journal_add_revoke_to_trans(journal_transaction_t *trans,
 
171
                                             blk64_t *revoke_list,
 
172
                                             size_t revoke_len)
 
173
{
 
174
        journal_revoke_header_t *jrb;
 
175
        void *buf;
 
176
        size_t i, offset;
 
177
        blk64_t curr_blk;
 
178
        int csum_size = 0;
 
179
        struct buffer_head *bh;
 
180
        errcode_t err;
 
181
 
 
182
        JOURNAL_CHECK_TRANS_MAGIC(trans);
 
183
 
 
184
        if ((trans->flags & J_TRANS_COMMITTED) ||
 
185
            !(trans->flags & J_TRANS_OPEN))
 
186
                return EXT2_ET_INVALID_ARGUMENT;
 
187
 
 
188
        if (revoke_len == 0)
 
189
                return 0;
 
190
 
 
191
        /* Do we need to leave space at the end for a checksum? */
 
192
        if (journal_has_csum_v2or3(trans->journal))
 
193
                csum_size = sizeof(struct journal_revoke_tail);
 
194
 
 
195
        curr_blk = trans->block;
 
196
 
 
197
        bh = getblk(trans->journal->j_dev, curr_blk,
 
198
                    trans->journal->j_blocksize);
 
199
        if (bh == NULL)
 
200
                return ENOMEM;
 
201
        jrb = buf = bh->b_data;
 
202
        jrb->r_header.h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
 
203
        jrb->r_header.h_blocktype = ext2fs_cpu_to_be32(JFS_REVOKE_BLOCK);
 
204
        jrb->r_header.h_sequence = ext2fs_cpu_to_be32(trans->tid);
 
205
        offset = sizeof(*jrb);
 
206
 
 
207
        for (i = 0; i < revoke_len; i++) {
 
208
                /* Block full, write to journal */
 
209
                if (offset > trans->journal->j_blocksize - csum_size) {
 
210
                        jrb->r_count = ext2fs_cpu_to_be32(offset);
 
211
                        jbd2_revoke_csum_set(trans->journal, bh);
 
212
 
 
213
                        err = journal_bmap(trans->journal, curr_blk,
 
214
                                           &bh->b_blocknr);
 
215
                        if (err)
 
216
                                goto error;
 
217
                        dbg_printf("Writing revoke block at %llu:%llu\n",
 
218
                                   curr_blk, bh->b_blocknr);
 
219
                        mark_buffer_dirty(bh);
 
220
                        ll_rw_block(WRITE, 1, &bh);
 
221
                        err = bh->b_err;
 
222
                        if (err)
 
223
                                goto error;
 
224
 
 
225
                        offset = sizeof(*jrb);
 
226
                        curr_blk++;
 
227
                }
 
228
 
 
229
                if (revoke_list[i] >=
 
230
                    ext2fs_blocks_count(trans->journal->j_fs_dev->k_fs->super)) {
 
231
                        err = EXT2_ET_BAD_BLOCK_NUM;
 
232
                        goto error;
 
233
                }
 
234
 
 
235
                if (JFS_HAS_INCOMPAT_FEATURE(trans->journal,
 
236
                                             JFS_FEATURE_INCOMPAT_64BIT)) {
 
237
                        *((__u64 *)(&((char *)buf)[offset])) =
 
238
                                ext2fs_cpu_to_be64(revoke_list[i]);
 
239
                        offset += 8;
 
240
 
 
241
                } else {
 
242
                        *((__u32 *)(&((char *)buf)[offset])) =
 
243
                                ext2fs_cpu_to_be32(revoke_list[i]);
 
244
                        offset += 4;
 
245
                }
 
246
        }
 
247
 
 
248
        if (offset > 0) {
 
249
                jrb->r_count = ext2fs_cpu_to_be32(offset);
 
250
                jbd2_revoke_csum_set(trans->journal, bh);
 
251
 
 
252
                err = journal_bmap(trans->journal, curr_blk, &bh->b_blocknr);
 
253
                if (err)
 
254
                        goto error;
 
255
                dbg_printf("Writing revoke block at %llu:%llu\n",
 
256
                           curr_blk, bh->b_blocknr);
 
257
                mark_buffer_dirty(bh);
 
258
                ll_rw_block(WRITE, 1, &bh);
 
259
                err = bh->b_err;
 
260
                if (err)
 
261
                        goto error;
 
262
                curr_blk++;
 
263
        }
 
264
 
 
265
error:
 
266
        trans->block = curr_blk;
 
267
        brelse(bh);
 
268
        return err;
 
269
}
 
270
 
 
271
static errcode_t journal_add_blocks_to_trans(journal_transaction_t *trans,
 
272
                                      blk64_t *block_list, size_t block_len,
 
273
                                      FILE *fp)
 
274
{
 
275
        blk64_t curr_blk, jdb_blk;
 
276
        size_t i, j;
 
277
        int csum_size = 0;
 
278
        journal_header_t *jdb;
 
279
        journal_block_tag_t *jdbt;
 
280
        int tag_bytes;
 
281
        void *buf = NULL, *jdb_buf = NULL;
 
282
        struct buffer_head *bh = NULL, *data_bh;
 
283
        errcode_t err;
 
284
 
 
285
        JOURNAL_CHECK_TRANS_MAGIC(trans);
 
286
 
 
287
        if ((trans->flags & J_TRANS_COMMITTED) ||
 
288
            !(trans->flags & J_TRANS_OPEN))
 
289
                return EXT2_ET_INVALID_ARGUMENT;
 
290
 
 
291
        if (block_len == 0)
 
292
                return 0;
 
293
 
 
294
        /* Do we need to leave space at the end for a checksum? */
 
295
        if (journal_has_csum_v2or3(trans->journal))
 
296
                csum_size = sizeof(struct journal_block_tail);
 
297
 
 
298
        curr_blk = jdb_blk = trans->block;
 
299
 
 
300
        data_bh = getblk(trans->journal->j_dev, curr_blk,
 
301
                         trans->journal->j_blocksize);
 
302
        if (data_bh == NULL)
 
303
                return ENOMEM;
 
304
        buf = data_bh->b_data;
 
305
 
 
306
        /* write the descriptor block header */
 
307
        bh = getblk(trans->journal->j_dev, curr_blk,
 
308
                    trans->journal->j_blocksize);
 
309
        if (bh == NULL) {
 
310
                err = ENOMEM;
 
311
                goto error;
 
312
        }
 
313
        jdb = jdb_buf = bh->b_data;
 
314
        jdb->h_magic = ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER);
 
315
        jdb->h_blocktype = ext2fs_cpu_to_be32(JFS_DESCRIPTOR_BLOCK);
 
316
        jdb->h_sequence = ext2fs_cpu_to_be32(trans->tid);
 
317
        jdbt = (journal_block_tag_t *)(jdb + 1);
 
318
 
 
319
        curr_blk++;
 
320
        for (i = 0; i < block_len; i++) {
 
321
                j = fread(data_bh->b_data, trans->journal->j_blocksize, 1, fp);
 
322
                if (j != 1) {
 
323
                        err = errno;
 
324
                        goto error;
 
325
                }
 
326
 
 
327
                tag_bytes = journal_tag_bytes(trans->journal);
 
328
 
 
329
                /* No space left in descriptor block, write it out */
 
330
                if ((char *)jdbt + tag_bytes >
 
331
                    (char *)jdb_buf + trans->journal->j_blocksize - csum_size) {
 
332
                        jbd2_descr_block_csum_set(trans->journal, bh);
 
333
                        err = journal_bmap(trans->journal, jdb_blk,
 
334
                                           &bh->b_blocknr);
 
335
                        if (err)
 
336
                                goto error;
 
337
                        dbg_printf("Writing descriptor block at %llu:%llu\n",
 
338
                                   jdb_blk, bh->b_blocknr);
 
339
                        mark_buffer_dirty(bh);
 
340
                        ll_rw_block(WRITE, 1, &bh);
 
341
                        err = bh->b_err;
 
342
                        if (err)
 
343
                                goto error;
 
344
 
 
345
                        jdbt = (journal_block_tag_t *)(jdb + 1);
 
346
                        jdb_blk = curr_blk;
 
347
                        curr_blk++;
 
348
                }
 
349
 
 
350
                if (block_list[i] >=
 
351
                    ext2fs_blocks_count(trans->journal->j_fs_dev->k_fs->super)) {
 
352
                        err = EXT2_ET_BAD_BLOCK_NUM;
 
353
                        goto error;
 
354
                }
 
355
 
 
356
                /* Fill out the block tag */
 
357
                jdbt->t_blocknr = ext2fs_cpu_to_be32(block_list[i] & 0xFFFFFFFF);
 
358
                jdbt->t_flags = 0;
 
359
                if (jdbt != (journal_block_tag_t *)(jdb + 1))
 
360
                        jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_SAME_UUID);
 
361
                else {
 
362
                        memcpy(jdbt + tag_bytes,
 
363
                               trans->journal->j_superblock->s_uuid,
 
364
                               sizeof(trans->journal->j_superblock->s_uuid));
 
365
                        tag_bytes += 16;
 
366
                }
 
367
                if (i == block_len - 1)
 
368
                        jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_LAST_TAG);
 
369
                if (*((__u32 *)buf) == ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER)) {
 
370
                        *((__u32 *)buf) = 0;
 
371
                        jdbt->t_flags |= ext2fs_cpu_to_be16(JFS_FLAG_ESCAPE);
 
372
                }
 
373
                if (JFS_HAS_INCOMPAT_FEATURE(trans->journal,
 
374
                                             JFS_FEATURE_INCOMPAT_64BIT))
 
375
                        jdbt->t_blocknr_high = ext2fs_cpu_to_be32(block_list[i] >> 32);
 
376
                jbd2_block_tag_csum_set(trans->journal, jdbt, data_bh,
 
377
                                        trans->tid);
 
378
 
 
379
                /* Write the data block */
 
380
                err = journal_bmap(trans->journal, curr_blk,
 
381
                                   &data_bh->b_blocknr);
 
382
                if (err)
 
383
                        goto error;
 
384
                dbg_printf("Writing data block %llu at %llu:%llu tag %d\n",
 
385
                           block_list[i], curr_blk, data_bh->b_blocknr,
 
386
                           tag_bytes);
 
387
                mark_buffer_dirty(data_bh);
 
388
                ll_rw_block(WRITE, 1, &data_bh);
 
389
                err = data_bh->b_err;
 
390
                if (err)
 
391
                        goto error;
 
392
 
 
393
                curr_blk++;
 
394
                jdbt = (journal_block_tag_t *)(((char *)jdbt) + tag_bytes);
 
395
        }
 
396
 
 
397
        /* Write out the last descriptor block */
 
398
        if (jdbt != (journal_block_tag_t *)(jdb + 1)) {
 
399
                jbd2_descr_block_csum_set(trans->journal, bh);
 
400
                err = journal_bmap(trans->journal, jdb_blk, &bh->b_blocknr);
 
401
                if (err)
 
402
                        goto error;
 
403
                dbg_printf("Writing descriptor block at %llu:%llu\n",
 
404
                           jdb_blk, bh->b_blocknr);
 
405
                mark_buffer_dirty(bh);
 
406
                ll_rw_block(WRITE, 1, &bh);
 
407
                err = bh->b_err;
 
408
                if (err)
 
409
                        goto error;
 
410
        }
 
411
 
 
412
error:
 
413
        trans->block = curr_blk;
 
414
        if (bh)
 
415
                brelse(bh);
 
416
        brelse(data_bh);
 
417
        return err;
 
418
}
 
419
 
 
420
static blk64_t journal_guess_blocks(journal_t *journal, blk64_t data_blocks,
 
421
                                    blk64_t revoke_blocks)
 
422
{
 
423
        blk64_t ret = 1;
 
424
        unsigned int bs, sz;
 
425
 
 
426
        /* Estimate # of revoke blocks */
 
427
        bs = journal->j_blocksize;
 
428
        if (journal_has_csum_v2or3(journal))
 
429
                bs -= sizeof(struct journal_revoke_tail);
 
430
        sz = JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) ?
 
431
                                sizeof(__u64) : sizeof(__u32);
 
432
        ret += revoke_blocks * sz / bs;
 
433
 
 
434
        /* Estimate # of data blocks */
 
435
        bs = journal->j_blocksize - 16;
 
436
        if (journal_has_csum_v2or3(journal))
 
437
                bs -= sizeof(struct journal_block_tail);
 
438
        sz = journal_tag_bytes(journal);
 
439
        ret += data_blocks * sz / bs;
 
440
 
 
441
        ret += data_blocks;
 
442
 
 
443
        return ret;
 
444
}
 
445
 
 
446
static errcode_t journal_open_trans(journal_t *journal,
 
447
                                    journal_transaction_t *trans,
 
448
                                    blk64_t blocks)
 
449
{
 
450
        trans->fs = journal->j_fs_dev->k_fs;
 
451
        trans->journal = journal;
 
452
        trans->flags = J_TRANS_OPEN;
 
453
 
 
454
        if (journal->j_tail == 0) {
 
455
                /* Clean journal, start at the tail */
 
456
                trans->tid = journal->j_tail_sequence;
 
457
                trans->start = journal->j_first;
 
458
        } else {
 
459
                /* Put new transaction at the head of the list */
 
460
                trans->tid = journal->j_transaction_sequence;
 
461
                trans->start = journal->j_head;
 
462
        }
 
463
 
 
464
        trans->block = trans->start;
 
465
        if (trans->start + blocks > journal->j_last)
 
466
                return ENOSPC;
 
467
        trans->end = trans->block + blocks;
 
468
        journal_dump_trans(trans, "new transaction");
 
469
 
 
470
        trans->magic = J_TRANS_MAGIC;
 
471
        return 0;
 
472
}
 
473
 
 
474
static errcode_t journal_close_trans(journal_transaction_t *trans)
 
475
{
 
476
        journal_t *journal;
 
477
 
 
478
        JOURNAL_CHECK_TRANS_MAGIC(trans);
 
479
 
 
480
        if (!(trans->flags & J_TRANS_COMMITTED))
 
481
                return 0;
 
482
 
 
483
        journal = trans->journal;
 
484
        if (journal->j_tail == 0) {
 
485
                /* Update the tail */
 
486
                journal->j_tail_sequence = trans->tid;
 
487
                journal->j_tail = trans->start;
 
488
                journal->j_superblock->s_start = ext2fs_cpu_to_be32(trans->start);
 
489
        }
 
490
 
 
491
        /* Update the head */
 
492
        journal->j_head = trans->end + 1;
 
493
        journal->j_transaction_sequence = trans->tid + 1;
 
494
 
 
495
        trans->magic = 0;
 
496
 
 
497
        /* Mark ourselves as needing recovery */
 
498
        if (!(EXT2_HAS_INCOMPAT_FEATURE(trans->fs->super,
 
499
                                        EXT3_FEATURE_INCOMPAT_RECOVER))) {
 
500
                trans->fs->super->s_feature_incompat |=
 
501
                                        EXT3_FEATURE_INCOMPAT_RECOVER;
 
502
                ext2fs_mark_super_dirty(trans->fs);
 
503
        }
 
504
 
 
505
        return 0;
 
506
}
 
507
 
 
508
#define JOURNAL_WRITE_NO_COMMIT         1
 
509
static errcode_t journal_write(journal_t *journal,
 
510
                               int flags, blk64_t *block_list,
 
511
                               size_t block_len, blk64_t *revoke_list,
 
512
                               size_t revoke_len, FILE *fp)
 
513
{
 
514
        blk64_t blocks;
 
515
        journal_transaction_t trans;
 
516
        errcode_t err;
 
517
 
 
518
        if (revoke_len > 0) {
 
519
                journal->j_superblock->s_feature_incompat |=
 
520
                                ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_REVOKE);
 
521
                mark_buffer_dirty(journal->j_sb_buffer);
 
522
        }
 
523
 
 
524
        blocks = journal_guess_blocks(journal, block_len, revoke_len);
 
525
        err = journal_open_trans(journal, &trans, blocks);
 
526
        if (err)
 
527
                goto error;
 
528
 
 
529
        err = journal_add_blocks_to_trans(&trans, block_list, block_len, fp);
 
530
        if (err)
 
531
                goto error;
 
532
 
 
533
        err = journal_add_revoke_to_trans(&trans, revoke_list, revoke_len);
 
534
        if (err)
 
535
                goto error;
 
536
 
 
537
        if (!(flags & JOURNAL_WRITE_NO_COMMIT)) {
 
538
                err = journal_commit_trans(&trans);
 
539
                if (err)
 
540
                        goto error;
 
541
        }
 
542
 
 
543
        err = journal_close_trans(&trans);
 
544
        if (err)
 
545
                goto error;
 
546
error:
 
547
        return err;
 
548
}
 
549
 
 
550
void do_journal_write(int argc, char *argv[])
 
551
{
 
552
        blk64_t *blist = NULL, *rlist = NULL;
 
553
        size_t bn = 0, rn = 0;
 
554
        FILE *fp = NULL;
 
555
        int opt;
 
556
        int flags = 0;
 
557
        errcode_t err;
 
558
 
 
559
        if (current_journal == NULL) {
 
560
                printf("Journal not open.\n");
 
561
                return;
 
562
        }
 
563
 
 
564
        reset_getopt();
 
565
        while ((opt = getopt(argc, argv, "b:r:c")) != -1) {
 
566
                switch (opt) {
 
567
                case 'b':
 
568
                        err = read_list(optarg, &blist, &bn);
 
569
                        if (err)
 
570
                                com_err(argv[0], err,
 
571
                                        "while reading block list");
 
572
                        break;
 
573
                case 'r':
 
574
                        err = read_list(optarg, &rlist, &rn);
 
575
                        if (err)
 
576
                                com_err(argv[0], err,
 
577
                                        "while reading revoke list");
 
578
                        break;
 
579
                case 'c':
 
580
                        flags |= JOURNAL_WRITE_NO_COMMIT;
 
581
                        break;
 
582
                default:
 
583
                        printf("%s [-b blocks] [-r revoke] [-c] file\n",
 
584
                               argv[0]);
 
585
                        printf("-b: Write these blocks into transaction.\n");
 
586
                        printf("-c: Do not commit transaction.\n");
 
587
                        printf("-r: Revoke these blocks from transaction.\n");
 
588
 
 
589
                        goto out;
 
590
                }
 
591
        }
 
592
 
 
593
        if (bn > 0 && optind != argc - 1) {
 
594
                printf("Need a file to read blocks from.\n");
 
595
                return;
 
596
        }
 
597
 
 
598
        if (bn > 0) {
 
599
                fp = fopen(argv[optind], "r");
 
600
                if (fp == NULL) {
 
601
                        com_err(argv[0], errno,
 
602
                                "while opening journal data file");
 
603
                        goto out;
 
604
                }
 
605
        }
 
606
 
 
607
        err = journal_write(current_journal, flags, blist, bn,
 
608
                            rlist, rn, fp);
 
609
        if (err)
 
610
                com_err("journal_write", err, "while writing journal");
 
611
 
 
612
        if (fp)
 
613
                fclose(fp);
 
614
out:
 
615
        if (blist)
 
616
                free(blist);
 
617
        if (rlist)
 
618
                free(rlist);
 
619
}
 
620
 
 
621
/* Make sure we wrap around the log correctly! */
 
622
#define wrap(journal, var)                                              \
 
623
do {                                                                    \
 
624
        if (var >= (journal)->j_last)                                   \
 
625
                var -= ((journal)->j_last - (journal)->j_first);        \
 
626
} while (0)
 
627
 
 
628
/*
 
629
 * Count the number of in-use tags in a journal descriptor block.
 
630
 */
 
631
 
 
632
static int count_tags(journal_t *journal, char *buf)
 
633
{
 
634
        char                    *tagp;
 
635
        journal_block_tag_t     *tag;
 
636
        int                     nr = 0, size = journal->j_blocksize;
 
637
        int                     tag_bytes = journal_tag_bytes(journal);
 
638
 
 
639
        if (journal_has_csum_v2or3(journal))
 
640
                size -= sizeof(struct journal_block_tail);
 
641
 
 
642
        tagp = buf + sizeof(journal_header_t);
 
643
 
 
644
        while ((tagp - buf + tag_bytes) <= size) {
 
645
                tag = (journal_block_tag_t *) tagp;
 
646
 
 
647
                nr++;
 
648
                tagp += tag_bytes;
 
649
                if (!(tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_SAME_UUID)))
 
650
                        tagp += 16;
 
651
 
 
652
                if (tag->t_flags & ext2fs_cpu_to_be16(JFS_FLAG_LAST_TAG))
 
653
                        break;
 
654
        }
 
655
 
 
656
        return nr;
 
657
}
 
658
 
 
659
errcode_t journal_find_head(journal_t *journal)
 
660
{
 
661
        unsigned int            next_commit_ID;
 
662
        blk64_t                 next_log_block, head_block;
 
663
        int                     err;
 
664
        journal_superblock_t    *sb;
 
665
        journal_header_t        *tmp;
 
666
        struct buffer_head      *bh;
 
667
        unsigned int            sequence;
 
668
        int                     blocktype;
 
669
 
 
670
        /*
 
671
         * First thing is to establish what we expect to find in the log
 
672
         * (in terms of transaction IDs), and where (in terms of log
 
673
         * block offsets): query the superblock.
 
674
         */
 
675
 
 
676
        sb = journal->j_superblock;
 
677
        next_commit_ID = ext2fs_be32_to_cpu(sb->s_sequence);
 
678
        next_log_block = ext2fs_be32_to_cpu(sb->s_start);
 
679
        head_block = next_log_block;
 
680
 
 
681
        if (next_log_block == 0)
 
682
                return 0;
 
683
 
 
684
        bh = getblk(journal->j_dev, 0, journal->j_blocksize);
 
685
        if (bh == NULL)
 
686
                return ENOMEM;
 
687
 
 
688
        /*
 
689
         * Now we walk through the log, transaction by transaction,
 
690
         * making sure that each transaction has a commit block in the
 
691
         * expected place.  Each complete transaction gets replayed back
 
692
         * into the main filesystem.
 
693
         */
 
694
        while (1) {
 
695
                dbg_printf("Scanning for sequence ID %u at %lu/%lu\n",
 
696
                          next_commit_ID, (unsigned long)next_log_block,
 
697
                          journal->j_last);
 
698
 
 
699
                /* Skip over each chunk of the transaction looking
 
700
                 * either the next descriptor block or the final commit
 
701
                 * record. */
 
702
                err = journal_bmap(journal, next_log_block, &bh->b_blocknr);
 
703
                if (err)
 
704
                        goto err;
 
705
                mark_buffer_uptodate(bh, 0);
 
706
                ll_rw_block(READ, 1, &bh);
 
707
                err = bh->b_err;
 
708
                if (err)
 
709
                        goto err;
 
710
 
 
711
                next_log_block++;
 
712
                wrap(journal, next_log_block);
 
713
 
 
714
                /* What kind of buffer is it?
 
715
                 *
 
716
                 * If it is a descriptor block, check that it has the
 
717
                 * expected sequence number.  Otherwise, we're all done
 
718
                 * here. */
 
719
 
 
720
                tmp = (journal_header_t *)bh->b_data;
 
721
 
 
722
                if (tmp->h_magic != ext2fs_cpu_to_be32(JFS_MAGIC_NUMBER)) {
 
723
                        dbg_printf("JBD2: wrong magic 0x%x\n", tmp->h_magic);
 
724
                        goto err;
 
725
                }
 
726
 
 
727
                blocktype = ext2fs_be32_to_cpu(tmp->h_blocktype);
 
728
                sequence = ext2fs_be32_to_cpu(tmp->h_sequence);
 
729
                dbg_printf("Found magic %d, sequence %d\n",
 
730
                          blocktype, sequence);
 
731
 
 
732
                if (sequence != next_commit_ID) {
 
733
                        dbg_printf("JBD2: Wrong sequence %d (wanted %d)\n",
 
734
                                   sequence, next_commit_ID);
 
735
                        goto err;
 
736
                }
 
737
 
 
738
                /* OK, we have a valid descriptor block which matches
 
739
                 * all of the sequence number checks.  What are we going
 
740
                 * to do with it?  That depends on the pass... */
 
741
 
 
742
                switch (blocktype) {
 
743
                case JFS_DESCRIPTOR_BLOCK:
 
744
                        next_log_block += count_tags(journal, bh->b_data);
 
745
                        wrap(journal, next_log_block);
 
746
                        continue;
 
747
 
 
748
                case JFS_COMMIT_BLOCK:
 
749
                        head_block = next_log_block;
 
750
                        next_commit_ID++;
 
751
                        continue;
 
752
 
 
753
                case JFS_REVOKE_BLOCK:
 
754
                        continue;
 
755
 
 
756
                default:
 
757
                        dbg_printf("Unrecognised magic %d, end of scan.\n",
 
758
                                  blocktype);
 
759
                        err = -EINVAL;
 
760
                        goto err;
 
761
                }
 
762
        }
 
763
 
 
764
err:
 
765
        if (err == 0) {
 
766
                dbg_printf("head seq=%d blk=%llu\n", next_commit_ID,
 
767
                           head_block);
 
768
                journal->j_transaction_sequence = next_commit_ID;
 
769
                journal->j_head = head_block;
 
770
        }
 
771
        brelse(bh);
 
772
        return err;
 
773
}
 
774
 
 
775
static void update_journal_csum(journal_t *journal, int ver)
 
776
{
 
777
        journal_superblock_t *jsb;
 
778
 
 
779
        if (journal->j_format_version < 2)
 
780
                return;
 
781
 
 
782
        if (journal->j_tail != 0 ||
 
783
            EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super,
 
784
                                      EXT3_FEATURE_INCOMPAT_RECOVER)) {
 
785
                printf("Journal needs recovery, will not add csums.\n");
 
786
                return;
 
787
        }
 
788
 
 
789
        /* metadata_csum implies journal csum v3 */
 
790
        jsb = journal->j_superblock;
 
791
        if (EXT2_HAS_RO_COMPAT_FEATURE(journal->j_fs_dev->k_fs->super,
 
792
                                       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
 
793
                printf("Setting csum v%d\n", ver);
 
794
                switch (ver) {
 
795
                case 2:
 
796
                        journal->j_superblock->s_feature_incompat &=
 
797
                                ext2fs_cpu_to_be32(~JFS_FEATURE_INCOMPAT_CSUM_V3);
 
798
                        journal->j_superblock->s_feature_incompat |=
 
799
                                ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V2);
 
800
                        journal->j_superblock->s_feature_compat &=
 
801
                                ext2fs_cpu_to_be32(~JFS_FEATURE_COMPAT_CHECKSUM);
 
802
                        break;
 
803
                case 3:
 
804
                        journal->j_superblock->s_feature_incompat &=
 
805
                                ext2fs_cpu_to_be32(~JFS_FEATURE_INCOMPAT_CSUM_V2);
 
806
                        journal->j_superblock->s_feature_incompat |=
 
807
                                ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_CSUM_V3);
 
808
                        journal->j_superblock->s_feature_compat &=
 
809
                                ext2fs_cpu_to_be32(~JFS_FEATURE_COMPAT_CHECKSUM);
 
810
                        break;
 
811
                default:
 
812
                        printf("Unknown checksum v%d\n", ver);
 
813
                        break;
 
814
                }
 
815
                journal->j_superblock->s_checksum_type = JBD2_CRC32C_CHKSUM;
 
816
                journal->j_csum_seed = jbd2_chksum(journal, ~0, jsb->s_uuid,
 
817
                                                   sizeof(jsb->s_uuid));
 
818
        } else {
 
819
                journal->j_superblock->s_feature_compat |=
 
820
                        ext2fs_cpu_to_be32(JFS_FEATURE_COMPAT_CHECKSUM);
 
821
                journal->j_superblock->s_feature_incompat &=
 
822
                        ext2fs_cpu_to_be32(~(JFS_FEATURE_INCOMPAT_CSUM_V2 |
 
823
                                             JFS_FEATURE_INCOMPAT_CSUM_V3));
 
824
        }
 
825
}
 
826
 
 
827
static void update_uuid(journal_t *journal)
 
828
{
 
829
        size_t z;
 
830
        ext2_filsys fs;
 
831
 
 
832
        if (journal->j_format_version < 2)
 
833
                return;
 
834
 
 
835
        for (z = 0; z < sizeof(journal->j_superblock->s_uuid); z++)
 
836
                if (journal->j_superblock->s_uuid[z])
 
837
                        break;
 
838
        if (z == 0)
 
839
                return;
 
840
 
 
841
        fs = journal->j_fs_dev->k_fs;
 
842
        if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 
843
                                       EXT4_FEATURE_INCOMPAT_64BIT))
 
844
                return;
 
845
 
 
846
        if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) &&
 
847
            EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 
848
                                      EXT4_FEATURE_INCOMPAT_64BIT))
 
849
                return;
 
850
 
 
851
        if (journal->j_tail != 0 ||
 
852
            EXT2_HAS_INCOMPAT_FEATURE(fs->super,
 
853
                                      EXT3_FEATURE_INCOMPAT_RECOVER)) {
 
854
                printf("Journal needs recovery, will not set 64bit.\n");
 
855
                return;
 
856
        }
 
857
 
 
858
        memcpy(journal->j_superblock->s_uuid, fs->super->s_uuid,
 
859
               sizeof(fs->super->s_uuid));
 
860
}
 
861
 
 
862
static void update_64bit_flag(journal_t *journal)
 
863
{
 
864
        if (journal->j_format_version < 2)
 
865
                return;
 
866
 
 
867
        if (!EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super,
 
868
                                       EXT4_FEATURE_INCOMPAT_64BIT))
 
869
                return;
 
870
 
 
871
        if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT) &&
 
872
            EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super,
 
873
                                      EXT4_FEATURE_INCOMPAT_64BIT))
 
874
                return;
 
875
 
 
876
        if (journal->j_tail != 0 ||
 
877
            EXT2_HAS_INCOMPAT_FEATURE(journal->j_fs_dev->k_fs->super,
 
878
                                      EXT3_FEATURE_INCOMPAT_RECOVER)) {
 
879
                printf("Journal needs recovery, will not set 64bit.\n");
 
880
                return;
 
881
        }
 
882
 
 
883
        journal->j_superblock->s_feature_incompat |=
 
884
                                ext2fs_cpu_to_be32(JFS_FEATURE_INCOMPAT_64BIT);
 
885
}
 
886
 
 
887
void do_journal_open(int argc, char *argv[])
 
888
{
 
889
        int opt, enable_csum = 0, csum_ver = 3;
 
890
        journal_t *journal;
 
891
        errcode_t err;
 
892
 
 
893
        if (check_fs_open(argv[0]))
 
894
                return;
 
895
        if (check_fs_read_write(argv[0]))
 
896
                return;
 
897
        if (check_fs_bitmaps(argv[0]))
 
898
                return;
 
899
        if (current_journal) {
 
900
                printf("Journal is already open.\n");
 
901
                return;
 
902
        }
 
903
        if (!EXT2_HAS_COMPAT_FEATURE(current_fs->super,
 
904
                                     EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
 
905
                printf("Journalling is not enabled on this filesystem.\n");
 
906
                return;
 
907
        }
 
908
 
 
909
        reset_getopt();
 
910
        while ((opt = getopt(argc, argv, "cv:f:")) != -1) {
 
911
                switch (opt) {
 
912
                case 'c':
 
913
                        enable_csum = 1;
 
914
                        break;
 
915
                case 'f':
 
916
                        if (current_fs->journal_name)
 
917
                                free(current_fs->journal_name);
 
918
                        current_fs->journal_name = strdup(optarg);
 
919
                        break;
 
920
                case 'v':
 
921
                        csum_ver = atoi(optarg);
 
922
                        if (csum_ver != 2 && csum_ver != 3) {
 
923
                                printf("Unknown journal csum v%d\n", csum_ver);
 
924
                                csum_ver = 3;
 
925
                        }
 
926
                        break;
 
927
                default:
 
928
                        printf("%s: [-c] [-v ver]\n", argv[0]);
 
929
                        printf("-c: Enable journal checksumming.\n");
 
930
                        printf("-v: Use this version checksum format.\n");
 
931
                }
 
932
        }
 
933
 
 
934
        err = ext2fs_open_journal(current_fs, &current_journal);
 
935
        if (err) {
 
936
                com_err(argv[0], err, "while opening journal");
 
937
                return;
 
938
        }
 
939
        journal = current_journal;
 
940
 
 
941
        dbg_printf("JOURNAL: seq=%d tailseq=%d start=%lu first=%lu "
 
942
                   "maxlen=%lu\n", journal->j_tail_sequence,
 
943
                   journal->j_transaction_sequence, journal->j_tail,
 
944
                   journal->j_first, journal->j_last);
 
945
 
 
946
        update_uuid(journal);
 
947
        update_64bit_flag(journal);
 
948
        if (enable_csum)
 
949
                update_journal_csum(journal, csum_ver);
 
950
 
 
951
        err = journal_find_head(journal);
 
952
        if (err)
 
953
                com_err(argv[0], err, "while examining journal");
 
954
}
 
955
 
 
956
void do_journal_close(int argc, char *argv[])
 
957
{
 
958
        if (current_journal == NULL) {
 
959
                printf("Journal not open.\n");
 
960
                return;
 
961
        }
 
962
 
 
963
        ext2fs_close_journal(current_fs, &current_journal);
 
964
}
 
965
 
 
966
void do_journal_run(int argc, char *argv[])
 
967
{
 
968
        errcode_t err;
 
969
 
 
970
        if (check_fs_open(argv[0]))
 
971
                return;
 
972
        if (check_fs_read_write(argv[0]))
 
973
                return;
 
974
        if (check_fs_bitmaps(argv[0]))
 
975
                return;
 
976
        if (current_journal) {
 
977
                printf("Please close the journal before recovering it.\n");
 
978
                return;
 
979
        }
 
980
 
 
981
        err = ext2fs_run_ext3_journal(&current_fs);
 
982
        if (err)
 
983
                com_err("journal_run", err, "while recovering journal");
 
984
        else {
 
985
                current_fs->super->s_feature_incompat &=
 
986
                                ~EXT3_FEATURE_INCOMPAT_RECOVER;
 
987
                ext2fs_mark_super_dirty(current_fs);
 
988
        }
 
989
}