]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - debugfs/do_journal.c
misc: fix minor testcase problems
[thirdparty/e2fsprogs.git] / debugfs / do_journal.c
CommitLineData
463eb921
DW
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
13extern int optind;
14extern 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 */
27errcode_t ext2fs_open_journal(ext2_filsys fs, journal_t **j);
28errcode_t ext2fs_close_journal(ext2_filsys fs, journal_t **j);
29errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
30void jbd2_commit_block_csum_set(journal_t *j, struct buffer_head *bh);
31void jbd2_revoke_csum_set(journal_t *j, struct buffer_head *bh);
32void jbd2_descr_block_csum_set(journal_t *j, struct buffer_head *bh);
33void 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
55struct 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
65typedef struct journal_transaction_s journal_transaction_t;
66
67static journal_t *current_journal = NULL;
68
69static 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
76static 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
53c5d606
DW
161 trans->fs->super->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER;
162 ext2fs_mark_super_dirty(trans->fs);
463eb921
DW
163error:
164 if (cbh)
165 brelse(cbh);
166 brelse(bh);
167 return err;
168}
169
170static 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
265error:
266 trans->block = curr_blk;
267 brelse(bh);
268 return err;
269}
270
271static 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
412error:
413 trans->block = curr_blk;
414 if (bh)
415 brelse(bh);
416 brelse(data_bh);
417 return err;
418}
419
420static 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
446static 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
474static 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
509static 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;
546error:
547 return err;
548}
549
550void 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);
614out:
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) \
623do { \
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
632static 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
659errcode_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
764err:
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
775static 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
827static 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
862static 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
887void 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
956void 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
966void 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");
53c5d606
DW
984 else {
985 current_fs->super->s_feature_incompat &=
986 ~EXT3_FEATURE_INCOMPAT_RECOVER;
987 ext2fs_mark_super_dirty(current_fs);
988 }
463eb921 989}