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