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