]>
Commit | Line | Data |
---|---|---|
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 | |
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 | ||
53c5d606 DW |
161 | trans->fs->super->s_feature_incompat |= EXT3_FEATURE_INCOMPAT_RECOVER; |
162 | ext2fs_mark_super_dirty(trans->fs); | |
463eb921 DW |
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, ¤t_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, ¤t_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(¤t_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 | } |