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