]>
Commit | Line | Data |
---|---|---|
17390c04 TT |
1 | /* |
2 | * journal.c --- code for handling the "ext3" journal | |
3b5386dc TT |
3 | * |
4 | * Copyright (C) 2000 Andreas Dilger | |
5 | * Copyright (C) 2000 Theodore Ts'o | |
6 | * | |
7 | * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie | |
8 | * Copyright (C) 1999 Red Hat Software | |
9 | * | |
10 | * This file may be redistributed under the terms of the | |
11 | * GNU General Public License version 2 or at your discretion | |
12 | * any later version. | |
17390c04 TT |
13 | */ |
14 | ||
d1154eb4 | 15 | #include "config.h" |
3b5386dc | 16 | #ifdef HAVE_SYS_MOUNT_H |
b34cbddb | 17 | #include <sys/param.h> |
3b5386dc TT |
18 | #include <sys/mount.h> |
19 | #define MNT_FL (MS_MGC_VAL | MS_RDONLY) | |
20 | #endif | |
21 | #ifdef HAVE_SYS_STAT_H | |
22 | #include <sys/stat.h> | |
23 | #endif | |
17390c04 | 24 | |
53ef44c4 | 25 | #define E2FSCK_INCLUDE_INLINE_FUNCS |
0e8a9560 | 26 | #include "jfs_user.h" |
3b5386dc TT |
27 | #include "problem.h" |
28 | #include "uuid/uuid.h" | |
17390c04 | 29 | |
3b5386dc | 30 | static int bh_count = 0; |
3b5386dc | 31 | |
d1a2182a TT |
32 | /* |
33 | * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths. | |
34 | * This creates a larger static binary, and a smaller binary using | |
35 | * shared libraries. It's also probably slightly less CPU-efficient, | |
36 | * which is why it's not on by default. But, it's a good way of | |
37 | * testing the functions in inode_io.c and fileio.c. | |
38 | */ | |
39 | #undef USE_INODE_IO | |
40 | ||
b2795949 | 41 | /* Checksumming functions */ |
fd9ca825 TT |
42 | static int e2fsck_journal_verify_csum_type(journal_t *j, |
43 | journal_superblock_t *jsb) | |
b2795949 | 44 | { |
8335d3c5 | 45 | if (!jbd2_journal_has_csum_v2or3(j)) |
b2795949 DW |
46 | return 1; |
47 | ||
48 | return jsb->s_checksum_type == JBD2_CRC32C_CHKSUM; | |
49 | } | |
50 | ||
fd9ca825 | 51 | static __u32 e2fsck_journal_sb_csum(journal_superblock_t *jsb) |
b2795949 DW |
52 | { |
53 | __u32 crc, old_crc; | |
54 | ||
55 | old_crc = jsb->s_checksum; | |
56 | jsb->s_checksum = 0; | |
57 | crc = ext2fs_crc32c_le(~0, (unsigned char *)jsb, | |
58 | sizeof(journal_superblock_t)); | |
59 | jsb->s_checksum = old_crc; | |
60 | ||
61 | return crc; | |
62 | } | |
63 | ||
fd9ca825 TT |
64 | static int e2fsck_journal_sb_csum_verify(journal_t *j, |
65 | journal_superblock_t *jsb) | |
b2795949 DW |
66 | { |
67 | __u32 provided, calculated; | |
68 | ||
8335d3c5 | 69 | if (!jbd2_journal_has_csum_v2or3(j)) |
b2795949 DW |
70 | return 1; |
71 | ||
72 | provided = ext2fs_be32_to_cpu(jsb->s_checksum); | |
fd9ca825 | 73 | calculated = e2fsck_journal_sb_csum(jsb); |
b2795949 DW |
74 | |
75 | return provided == calculated; | |
76 | } | |
77 | ||
fd9ca825 TT |
78 | static errcode_t e2fsck_journal_sb_csum_set(journal_t *j, |
79 | journal_superblock_t *jsb) | |
b2795949 DW |
80 | { |
81 | __u32 crc; | |
82 | ||
8335d3c5 | 83 | if (!jbd2_journal_has_csum_v2or3(j)) |
b2795949 DW |
84 | return 0; |
85 | ||
fd9ca825 | 86 | crc = e2fsck_journal_sb_csum(jsb); |
b2795949 DW |
87 | jsb->s_checksum = ext2fs_cpu_to_be32(crc); |
88 | return 0; | |
89 | } | |
90 | ||
b2f93192 TT |
91 | /* Kernel compatibility functions for handling the journal. These allow us |
92 | * to use the recovery.c file virtually unchanged from the kernel, so we | |
93 | * don't have to do much to keep kernel and user recovery in sync. | |
94 | */ | |
4305084d | 95 | int jbd2_journal_bmap(journal_t *journal, unsigned long block, |
8335d3c5 | 96 | unsigned long long *phys) |
3b5386dc | 97 | { |
d1a2182a TT |
98 | #ifdef USE_INODE_IO |
99 | *phys = block; | |
100 | return 0; | |
101 | #else | |
8cf93332 TT |
102 | struct inode *inode = journal->j_inode; |
103 | errcode_t retval; | |
6dc64392 | 104 | blk64_t pblk; |
3b5386dc | 105 | |
8cf93332 TT |
106 | if (!inode) { |
107 | *phys = block; | |
108 | return 0; | |
109 | } | |
3b5386dc | 110 | |
6dc64392 | 111 | retval= ext2fs_bmap2(inode->i_ctx->fs, inode->i_ino, |
4305084d TT |
112 | &inode->i_ext2, NULL, 0, (blk64_t) block, |
113 | 0, &pblk); | |
8cf93332 | 114 | *phys = pblk; |
d8cbb803 | 115 | return -1 * ((int) retval); |
d1a2182a | 116 | #endif |
3b5386dc TT |
117 | } |
118 | ||
4305084d TT |
119 | struct buffer_head *getblk(kdev_t kdev, unsigned long long blocknr, |
120 | int blocksize) | |
3b5386dc TT |
121 | { |
122 | struct buffer_head *bh; | |
e35d548b TT |
123 | int bufsize = sizeof(*bh) + kdev->k_ctx->fs->blocksize - |
124 | sizeof(bh->b_data); | |
3b5386dc | 125 | |
e35d548b | 126 | bh = e2fsck_allocate_memory(kdev->k_ctx, bufsize, "block buffer"); |
3b5386dc TT |
127 | if (!bh) |
128 | return NULL; | |
129 | ||
185c4aea TT |
130 | if (journal_enable_debug >= 3) |
131 | bh_count++; | |
3b693d0b | 132 | jfs_debug(4, "getblk for block %llu (%d bytes)(total %d)\n", |
4305084d | 133 | blocknr, blocksize, bh_count); |
3b5386dc | 134 | |
8cf93332 TT |
135 | bh->b_ctx = kdev->k_ctx; |
136 | if (kdev->k_dev == K_DEV_FS) | |
137 | bh->b_io = kdev->k_ctx->fs->io; | |
efc6f628 | 138 | else |
8cf93332 | 139 | bh->b_io = kdev->k_ctx->journal_io; |
3b5386dc TT |
140 | bh->b_size = blocksize; |
141 | bh->b_blocknr = blocknr; | |
142 | ||
143 | return bh; | |
144 | } | |
145 | ||
13af4b93 | 146 | int sync_blockdev(kdev_t kdev) |
93effaa4 TT |
147 | { |
148 | io_channel io; | |
149 | ||
150 | if (kdev->k_dev == K_DEV_FS) | |
151 | io = kdev->k_ctx->fs->io; | |
efc6f628 | 152 | else |
93effaa4 TT |
153 | io = kdev->k_ctx->journal_io; |
154 | ||
d8cbb803 | 155 | return io_channel_flush(io) ? -EIO : 0; |
93effaa4 TT |
156 | } |
157 | ||
b5f2be81 EB |
158 | void ll_rw_block(int rw, int op_flags EXT2FS_ATTR((unused)), int nr, |
159 | struct buffer_head *bhp[]) | |
3b5386dc | 160 | { |
974d57d3 | 161 | errcode_t retval; |
0e8a9560 | 162 | struct buffer_head *bh; |
3b5386dc | 163 | |
0e8a9560 TT |
164 | for (; nr > 0; --nr) { |
165 | bh = *bhp++; | |
db113c0e | 166 | if (rw == REQ_OP_READ && !bh->b_uptodate) { |
3b693d0b TT |
167 | jfs_debug(3, "reading block %llu/%p\n", |
168 | bh->b_blocknr, (void *) bh); | |
24a117ab | 169 | retval = io_channel_read_blk64(bh->b_io, |
0e8a9560 TT |
170 | bh->b_blocknr, |
171 | 1, bh->b_data); | |
172 | if (retval) { | |
173 | com_err(bh->b_ctx->device_name, retval, | |
3b693d0b TT |
174 | "while reading block %llu\n", |
175 | bh->b_blocknr); | |
974d57d3 | 176 | bh->b_err = (int) retval; |
0e8a9560 TT |
177 | continue; |
178 | } | |
179 | bh->b_uptodate = 1; | |
db113c0e | 180 | } else if (rw == REQ_OP_WRITE && bh->b_dirty) { |
3b693d0b TT |
181 | jfs_debug(3, "writing block %llu/%p\n", |
182 | bh->b_blocknr, | |
183 | (void *) bh); | |
24a117ab | 184 | retval = io_channel_write_blk64(bh->b_io, |
0e8a9560 TT |
185 | bh->b_blocknr, |
186 | 1, bh->b_data); | |
187 | if (retval) { | |
188 | com_err(bh->b_ctx->device_name, retval, | |
3b693d0b TT |
189 | "while writing block %llu\n", |
190 | bh->b_blocknr); | |
974d57d3 | 191 | bh->b_err = (int) retval; |
0e8a9560 TT |
192 | continue; |
193 | } | |
194 | bh->b_dirty = 0; | |
195 | bh->b_uptodate = 1; | |
b969b1b8 | 196 | } else { |
3b693d0b | 197 | jfs_debug(3, "no-op %s for block %llu\n", |
db113c0e | 198 | rw == REQ_OP_READ ? "read" : "write", |
3b693d0b | 199 | bh->b_blocknr); |
b969b1b8 | 200 | } |
0e8a9560 | 201 | } |
3b5386dc TT |
202 | } |
203 | ||
8cf93332 | 204 | void mark_buffer_dirty(struct buffer_head *bh) |
3b5386dc | 205 | { |
8cf93332 | 206 | bh->b_dirty = 1; |
3b5386dc TT |
207 | } |
208 | ||
6a6d3d44 TT |
209 | static void mark_buffer_clean(struct buffer_head * bh) |
210 | { | |
211 | bh->b_dirty = 0; | |
212 | } | |
213 | ||
3b5386dc TT |
214 | void brelse(struct buffer_head *bh) |
215 | { | |
216 | if (bh->b_dirty) | |
db113c0e | 217 | ll_rw_block(REQ_OP_WRITE, 0, 1, &bh); |
3b693d0b TT |
218 | jfs_debug(3, "freeing block %llu/%p (total %d)\n", |
219 | bh->b_blocknr, (void *) bh, --bh_count); | |
c4e3d3f3 | 220 | ext2fs_free_mem(&bh); |
3b5386dc TT |
221 | } |
222 | ||
223 | int buffer_uptodate(struct buffer_head *bh) | |
224 | { | |
225 | return bh->b_uptodate; | |
226 | } | |
227 | ||
15986f79 TT |
228 | void mark_buffer_uptodate(struct buffer_head *bh, int val) |
229 | { | |
230 | bh->b_uptodate = val; | |
231 | } | |
232 | ||
3b5386dc TT |
233 | void wait_on_buffer(struct buffer_head *bh) |
234 | { | |
235 | if (!bh->b_uptodate) | |
db113c0e | 236 | ll_rw_block(REQ_OP_READ, 0, 1, &bh); |
3b5386dc TT |
237 | } |
238 | ||
80bfaa3e | 239 | |
3b5386dc TT |
240 | static void e2fsck_clear_recover(e2fsck_t ctx, int error) |
241 | { | |
86f3b6cf | 242 | ext2fs_clear_feature_journal_needs_recovery(ctx->fs->super); |
3b5386dc TT |
243 | |
244 | /* if we had an error doing journal recovery, we need a full fsck */ | |
245 | if (error) | |
5dd8f963 | 246 | ctx->fs->super->s_state &= ~EXT2_VALID_FS; |
3b5386dc TT |
247 | ext2fs_mark_super_dirty(ctx->fs); |
248 | } | |
249 | ||
051afbe0 TT |
250 | /* |
251 | * This is a helper function to check the validity of the journal. | |
252 | */ | |
253 | struct process_block_struct { | |
254 | e2_blkcnt_t last_block; | |
255 | }; | |
256 | ||
257 | static int process_journal_block(ext2_filsys fs, | |
6dc64392 | 258 | blk64_t *block_nr, |
051afbe0 | 259 | e2_blkcnt_t blockcnt, |
6dc64392 | 260 | blk64_t ref_block EXT2FS_ATTR((unused)), |
051afbe0 TT |
261 | int ref_offset EXT2FS_ATTR((unused)), |
262 | void *priv_data) | |
263 | { | |
264 | struct process_block_struct *p; | |
6dc64392 | 265 | blk64_t blk = *block_nr; |
051afbe0 TT |
266 | |
267 | p = (struct process_block_struct *) priv_data; | |
268 | ||
5750e5f9 | 269 | if (!blk || blk < fs->super->s_first_data_block || |
4efbac6f | 270 | blk >= ext2fs_blocks_count(fs->super)) |
051afbe0 TT |
271 | return BLOCK_ABORT; |
272 | ||
273 | if (blockcnt >= 0) | |
274 | p->last_block = blockcnt; | |
275 | return 0; | |
276 | } | |
277 | ||
81a6b010 HS |
278 | static int ext4_fc_replay_scan(journal_t *j, struct buffer_head *bh, |
279 | int off, tid_t expected_tid) | |
280 | { | |
281 | e2fsck_t ctx = j->j_fs_dev->k_ctx; | |
282 | struct e2fsck_fc_replay_state *state; | |
283 | int ret = JBD2_FC_REPLAY_CONTINUE; | |
86fe64e4 | 284 | struct ext4_fc_add_range ext; |
45780b37 | 285 | struct ext4_fc_tl tl; |
1e0c8ca7 | 286 | struct ext4_fc_tail tail; |
45780b37 | 287 | __u8 *start, *cur, *end, *val; |
1e0c8ca7 | 288 | struct ext4_fc_head head; |
5ba3e164 | 289 | struct ext2fs_extent ext2fs_ex = {0}; |
81a6b010 HS |
290 | |
291 | state = &ctx->fc_replay_state; | |
292 | ||
293 | start = (__u8 *)bh->b_data; | |
294 | end = (__u8 *)bh->b_data + j->j_blocksize - 1; | |
295 | ||
296 | jbd_debug(1, "Scan phase starting, expected %d", expected_tid); | |
297 | if (state->fc_replay_expected_off == 0) { | |
298 | memset(state, 0, sizeof(*state)); | |
299 | /* Check if we can stop early */ | |
300 | if (le16_to_cpu(((struct ext4_fc_tl *)start)->fc_tag) | |
301 | != EXT4_FC_TAG_HEAD) { | |
302 | jbd_debug(1, "Ending early!, not a head tag"); | |
303 | return 0; | |
304 | } | |
305 | } | |
306 | ||
307 | if (off != state->fc_replay_expected_off) { | |
308 | ret = -EFSCORRUPTED; | |
309 | goto out_err; | |
310 | } | |
311 | ||
312 | state->fc_replay_expected_off++; | |
45780b37 HS |
313 | for (cur = start; cur < end; cur = cur + le16_to_cpu(tl.fc_len) + sizeof(tl)) { |
314 | memcpy(&tl, cur, sizeof(tl)); | |
315 | val = cur + sizeof(tl); | |
316 | ||
81a6b010 | 317 | jbd_debug(3, "Scan phase, tag:%s, blk %lld\n", |
45780b37 HS |
318 | tag2str(le16_to_cpu(tl.fc_tag)), bh->b_blocknr); |
319 | switch (le16_to_cpu(tl.fc_tag)) { | |
81a6b010 | 320 | case EXT4_FC_TAG_ADD_RANGE: |
86fe64e4 TT |
321 | memcpy(&ext, val, sizeof(ext)); |
322 | ret = ext2fs_decode_extent(&ext2fs_ex, | |
323 | (void *)&ext.fc_ex, | |
324 | sizeof(ext.fc_ex)); | |
81a6b010 HS |
325 | if (ret) |
326 | ret = JBD2_FC_REPLAY_STOP; | |
327 | else | |
328 | ret = JBD2_FC_REPLAY_CONTINUE; | |
11797844 | 329 | /* fallthrough */ |
81a6b010 HS |
330 | case EXT4_FC_TAG_DEL_RANGE: |
331 | case EXT4_FC_TAG_LINK: | |
332 | case EXT4_FC_TAG_UNLINK: | |
333 | case EXT4_FC_TAG_CREAT: | |
334 | case EXT4_FC_TAG_INODE: | |
335 | case EXT4_FC_TAG_PAD: | |
336 | state->fc_cur_tag++; | |
45780b37 HS |
337 | state->fc_crc = jbd2_chksum(j, state->fc_crc, cur, |
338 | sizeof(tl) + ext4_fc_tag_len(&tl)); | |
81a6b010 HS |
339 | break; |
340 | case EXT4_FC_TAG_TAIL: | |
341 | state->fc_cur_tag++; | |
45780b37 HS |
342 | memcpy(&tail, val, sizeof(tail)); |
343 | state->fc_crc = jbd2_chksum(j, state->fc_crc, cur, | |
344 | sizeof(tl) + | |
81a6b010 HS |
345 | offsetof(struct ext4_fc_tail, |
346 | fc_crc)); | |
347 | jbd_debug(1, "tail tid %d, expected %d\n", | |
1e0c8ca7 TT |
348 | le32_to_cpu(tail.fc_tid), expected_tid); |
349 | if (le32_to_cpu(tail.fc_tid) == expected_tid && | |
350 | le32_to_cpu(tail.fc_crc) == state->fc_crc) { | |
81a6b010 HS |
351 | state->fc_replay_num_tags = state->fc_cur_tag; |
352 | } else { | |
353 | ret = state->fc_replay_num_tags ? | |
354 | JBD2_FC_REPLAY_STOP : -EFSBADCRC; | |
355 | } | |
356 | state->fc_crc = 0; | |
357 | break; | |
358 | case EXT4_FC_TAG_HEAD: | |
45780b37 | 359 | memcpy(&head, val, sizeof(head)); |
1e0c8ca7 TT |
360 | if (le32_to_cpu(head.fc_features) & |
361 | ~EXT4_FC_SUPPORTED_FEATURES) { | |
81a6b010 HS |
362 | ret = -EOPNOTSUPP; |
363 | break; | |
364 | } | |
1e0c8ca7 | 365 | if (le32_to_cpu(head.fc_tid) != expected_tid) { |
81a6b010 HS |
366 | ret = -EINVAL; |
367 | break; | |
368 | } | |
369 | state->fc_cur_tag++; | |
45780b37 HS |
370 | state->fc_crc = jbd2_chksum(j, state->fc_crc, cur, |
371 | sizeof(tl) + ext4_fc_tag_len(&tl)); | |
81a6b010 HS |
372 | break; |
373 | default: | |
374 | ret = state->fc_replay_num_tags ? | |
375 | JBD2_FC_REPLAY_STOP : -ECANCELED; | |
376 | } | |
377 | if (ret < 0 || ret == JBD2_FC_REPLAY_STOP) | |
378 | break; | |
379 | } | |
380 | ||
381 | out_err: | |
382 | return ret; | |
383 | } | |
63b7192c HS |
384 | |
385 | static int __errcode_to_errno(errcode_t err, const char *func, int line) | |
386 | { | |
387 | if (err == 0) | |
388 | return 0; | |
389 | fprintf(stderr, "Error \"%s\" encountered in function %s at line %d\n", | |
390 | error_message(err), func, line); | |
391 | if (err <= 256) | |
392 | return -err; | |
393 | return -EFAULT; | |
394 | } | |
395 | ||
396 | #define errcode_to_errno(err) __errcode_to_errno(err, __func__, __LINE__) | |
397 | ||
f5de3d7c HS |
398 | #define ex_end(__ex) ((__ex)->e_lblk + (__ex)->e_len - 1) |
399 | #define ex_pend(__ex) ((__ex)->e_pblk + (__ex)->e_len - 1) | |
400 | ||
401 | static int make_room(struct extent_list *list, int i) | |
402 | { | |
403 | int ret; | |
404 | ||
405 | if (list->count == list->size) { | |
406 | unsigned int new_size = (list->size + 341) * | |
407 | sizeof(struct ext2fs_extent); | |
408 | ret = errcode_to_errno(ext2fs_resize_mem(0, new_size, &list->extents)); | |
409 | if (ret) | |
410 | return ret; | |
411 | list->size += 341; | |
412 | } | |
413 | ||
414 | memmove(&list->extents[i + 1], &list->extents[i], | |
415 | sizeof(list->extents[0]) * (list->count - i)); | |
416 | list->count++; | |
417 | return 0; | |
418 | } | |
419 | ||
420 | static int ex_compar(const void *arg1, const void *arg2) | |
421 | { | |
f158f896 TT |
422 | const struct ext2fs_extent *ex1 = (const struct ext2fs_extent *)arg1; |
423 | const struct ext2fs_extent *ex2 = (const struct ext2fs_extent *)arg2; | |
f5de3d7c HS |
424 | |
425 | if (ex1->e_lblk < ex2->e_lblk) | |
426 | return -1; | |
427 | if (ex1->e_lblk > ex2->e_lblk) | |
428 | return 1; | |
429 | return ex1->e_len - ex2->e_len; | |
430 | } | |
431 | ||
432 | static int ex_len_compar(const void *arg1, const void *arg2) | |
433 | { | |
f158f896 TT |
434 | const struct ext2fs_extent *ex1 = (const struct ext2fs_extent *)arg1; |
435 | const struct ext2fs_extent *ex2 = (const struct ext2fs_extent *)arg2; | |
f5de3d7c HS |
436 | |
437 | if (ex1->e_len < ex2->e_len) | |
438 | return 1; | |
439 | ||
440 | if (ex1->e_lblk > ex2->e_lblk) | |
441 | return -1; | |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
b5f2be81 | 446 | static void ex_sort_and_merge(struct extent_list *list) |
f5de3d7c | 447 | { |
f158f896 | 448 | unsigned int i, j; |
f5de3d7c HS |
449 | |
450 | if (list->count < 2) | |
451 | return; | |
452 | ||
453 | /* | |
454 | * Reverse sort by length, that way we strip off all the 0 length | |
455 | * extents | |
456 | */ | |
457 | qsort(list->extents, list->count, sizeof(struct ext2fs_extent), | |
458 | ex_len_compar); | |
459 | ||
460 | for (i = 0; i < list->count; i++) { | |
461 | if (list->extents[i].e_len == 0) { | |
462 | list->count = i; | |
463 | break; | |
464 | } | |
465 | } | |
466 | ||
54183fea HS |
467 | if (list->count == 0) |
468 | return; | |
469 | ||
f5de3d7c HS |
470 | /* Now sort by logical offset */ |
471 | qsort(list->extents, list->count, sizeof(list->extents[0]), | |
472 | ex_compar); | |
473 | ||
474 | /* Merge adjacent extents if they are logically and physically contiguous */ | |
475 | i = 0; | |
476 | while (i < list->count - 1) { | |
477 | if (ex_end(&list->extents[i]) + 1 != list->extents[i + 1].e_lblk || | |
478 | ex_pend(&list->extents[i]) + 1 != list->extents[i + 1].e_pblk || | |
479 | (list->extents[i].e_flags & EXT2_EXTENT_FLAGS_UNINIT) != | |
480 | (list->extents[i + 1].e_flags & EXT2_EXTENT_FLAGS_UNINIT)) { | |
481 | i++; | |
482 | continue; | |
483 | } | |
484 | ||
485 | list->extents[i].e_len += list->extents[i + 1].e_len; | |
486 | for (j = i + 1; j < list->count - 1; j++) | |
487 | list->extents[j] = list->extents[j + 1]; | |
488 | list->count--; | |
489 | } | |
490 | } | |
491 | ||
492 | /* must free blocks that are released */ | |
493 | static int ext4_modify_extent_list(e2fsck_t ctx, struct extent_list *list, | |
494 | struct ext2fs_extent *ex, int del) | |
495 | { | |
f158f896 TT |
496 | int ret, offset; |
497 | unsigned int i; | |
beb863f1 | 498 | struct ext2fs_extent add_ex = *ex; |
f5de3d7c HS |
499 | |
500 | /* First let's create a hole from ex->e_lblk of length ex->e_len */ | |
501 | for (i = 0; i < list->count; i++) { | |
502 | if (ex_end(&list->extents[i]) < add_ex.e_lblk) | |
503 | continue; | |
504 | ||
505 | /* Case 1: No overlap */ | |
506 | if (list->extents[i].e_lblk > ex_end(&add_ex)) | |
507 | break; | |
508 | /* | |
509 | * Unmark all the blocks in bb now. All the blocks get marked | |
510 | * before we exit this function. | |
511 | */ | |
512 | ext2fs_unmark_block_bitmap_range2(ctx->fs->block_map, | |
513 | list->extents[i].e_pblk, list->extents[i].e_len); | |
514 | /* Case 2: Split */ | |
515 | if (list->extents[i].e_lblk < add_ex.e_lblk && | |
516 | ex_end(&list->extents[i]) > ex_end(&add_ex)) { | |
517 | ret = make_room(list, i + 1); | |
518 | if (ret) | |
519 | return ret; | |
520 | list->extents[i + 1] = list->extents[i]; | |
521 | offset = ex_end(&add_ex) + 1 - list->extents[i].e_lblk; | |
522 | list->extents[i + 1].e_lblk += offset; | |
523 | list->extents[i + 1].e_pblk += offset; | |
524 | list->extents[i + 1].e_len -= offset; | |
525 | list->extents[i].e_len = | |
526 | add_ex.e_lblk - list->extents[i].e_lblk; | |
527 | break; | |
528 | } | |
529 | ||
530 | /* Case 3: Exact overlap */ | |
531 | if (add_ex.e_lblk <= list->extents[i].e_lblk && | |
532 | ex_end(&list->extents[i]) <= ex_end(&add_ex)) { | |
533 | ||
534 | list->extents[i].e_len = 0; | |
535 | continue; | |
536 | } | |
537 | ||
538 | /* Case 4: Partial overlap */ | |
539 | if (ex_end(&list->extents[i]) > ex_end(&add_ex)) { | |
540 | offset = ex_end(&add_ex) + 1 - list->extents[i].e_lblk; | |
541 | list->extents[i].e_lblk += offset; | |
542 | list->extents[i].e_pblk += offset; | |
543 | list->extents[i].e_len -= offset; | |
544 | break; | |
545 | } | |
546 | ||
547 | if (ex_end(&add_ex) >= ex_end(&list->extents[i])) | |
548 | list->extents[i].e_len = | |
549 | add_ex.e_lblk > list->extents[i].e_lblk ? | |
550 | add_ex.e_lblk - list->extents[i].e_lblk : 0; | |
551 | } | |
552 | ||
553 | if (add_ex.e_len && !del) { | |
554 | make_room(list, list->count); | |
555 | list->extents[list->count - 1] = add_ex; | |
556 | } | |
557 | ||
b5f2be81 | 558 | ex_sort_and_merge(list); |
f5de3d7c HS |
559 | |
560 | /* Mark all occupied blocks allocated */ | |
561 | for (i = 0; i < list->count; i++) | |
562 | ext2fs_mark_block_bitmap_range2(ctx->fs->block_map, | |
563 | list->extents[i].e_pblk, list->extents[i].e_len); | |
564 | ext2fs_mark_bb_dirty(ctx->fs); | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
569 | static int ext4_add_extent_to_list(e2fsck_t ctx, struct extent_list *list, | |
570 | struct ext2fs_extent *ex) | |
571 | { | |
572 | return ext4_modify_extent_list(ctx, list, ex, 0 /* add */); | |
573 | } | |
574 | ||
575 | static int ext4_del_extent_from_list(e2fsck_t ctx, struct extent_list *list, | |
576 | struct ext2fs_extent *ex) | |
577 | { | |
578 | return ext4_modify_extent_list(ctx, list, ex, 1 /* delete */); | |
579 | } | |
580 | ||
8e2e5a51 | 581 | static int ext4_fc_read_extents(e2fsck_t ctx, ext2_ino_t ino) |
f5de3d7c HS |
582 | { |
583 | struct extent_list *extent_list = &ctx->fc_replay_state.fc_extent_list; | |
584 | ||
585 | if (extent_list->ino == ino) | |
586 | return 0; | |
587 | ||
588 | extent_list->ino = ino; | |
589 | return errcode_to_errno(e2fsck_read_extents(ctx, extent_list)); | |
590 | } | |
591 | ||
592 | /* | |
593 | * Flush extents in replay state on disk. @ino is the inode that is going | |
594 | * to be processed next. So, we hold back flushing of the extent list | |
595 | * if the next inode that's going to be processed is same as the one with | |
596 | * cached extents in our replay state. That allows us to gather multiple extents | |
597 | * for the inode so that we can flush all of them at once and it also saves us | |
598 | * from continuously growing and shrinking the extent tree. | |
599 | */ | |
8e2e5a51 | 600 | static void ext4_fc_flush_extents(e2fsck_t ctx, ext2_ino_t ino) |
f5de3d7c HS |
601 | { |
602 | struct extent_list *extent_list = &ctx->fc_replay_state.fc_extent_list; | |
603 | ||
604 | if (extent_list->ino == ino || extent_list->ino == 0) | |
605 | return; | |
606 | e2fsck_rewrite_extent_tree(ctx, extent_list); | |
607 | ext2fs_free_mem(&extent_list->extents); | |
608 | memset(extent_list, 0, sizeof(*extent_list)); | |
609 | } | |
610 | ||
63b7192c HS |
611 | /* Helper struct for dentry replay routines */ |
612 | struct dentry_info_args { | |
8e2e5a51 AD |
613 | ext2_ino_t parent_ino; |
614 | ext2_ino_t ino; | |
615 | int dname_len; | |
616 | char *dname; | |
63b7192c HS |
617 | }; |
618 | ||
1e0c8ca7 | 619 | static inline int tl_to_darg(struct dentry_info_args *darg, |
45780b37 | 620 | struct ext4_fc_tl *tl, __u8 *val) |
63b7192c | 621 | { |
1e0c8ca7 | 622 | struct ext4_fc_dentry_info fcd; |
63b7192c | 623 | |
45780b37 | 624 | memcpy(&fcd, val, sizeof(fcd)); |
63b7192c | 625 | |
1e0c8ca7 TT |
626 | darg->parent_ino = le32_to_cpu(fcd.fc_parent_ino); |
627 | darg->ino = le32_to_cpu(fcd.fc_ino); | |
63b7192c HS |
628 | darg->dname_len = ext4_fc_tag_len(tl) - |
629 | sizeof(struct ext4_fc_dentry_info); | |
630 | darg->dname = malloc(darg->dname_len + 1); | |
1e0c8ca7 TT |
631 | if (!darg->dname) |
632 | return -ENOMEM; | |
633 | memcpy(darg->dname, | |
45780b37 | 634 | val + sizeof(struct ext4_fc_dentry_info), |
1e0c8ca7 | 635 | darg->dname_len); |
63b7192c | 636 | darg->dname[darg->dname_len] = 0; |
8e2e5a51 | 637 | jbd_debug(1, "%s: %s, ino %u, parent %u\n", |
9d8b56b3 AD |
638 | le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_CREAT ? "create" : |
639 | (le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_LINK ? "link" : | |
640 | (le16_to_cpu(tl->fc_tag) == EXT4_FC_TAG_UNLINK ? "unlink" : | |
641 | "error")), darg->dname, darg->ino, darg->parent_ino); | |
1e0c8ca7 | 642 | return 0; |
63b7192c HS |
643 | } |
644 | ||
45780b37 | 645 | static int ext4_fc_handle_unlink(e2fsck_t ctx, struct ext4_fc_tl *tl, __u8 *val) |
63b7192c | 646 | { |
63b7192c | 647 | struct dentry_info_args darg; |
63b7192c HS |
648 | int ret; |
649 | ||
45780b37 | 650 | ret = tl_to_darg(&darg, tl, val); |
1e0c8ca7 TT |
651 | if (ret) |
652 | return ret; | |
f5de3d7c | 653 | ext4_fc_flush_extents(ctx, darg.ino); |
9d8b56b3 AD |
654 | ret = errcode_to_errno(ext2fs_unlink(ctx->fs, darg.parent_ino, |
655 | darg.dname, darg.ino, 0)); | |
63b7192c HS |
656 | /* It's okay if the above call fails */ |
657 | free(darg.dname); | |
9d8b56b3 | 658 | |
63b7192c HS |
659 | return ret; |
660 | } | |
661 | ||
45780b37 | 662 | static int ext4_fc_handle_link_and_create(e2fsck_t ctx, struct ext4_fc_tl *tl, __u8 *val) |
63b7192c HS |
663 | { |
664 | struct dentry_info_args darg; | |
665 | ext2_filsys fs = ctx->fs; | |
666 | struct ext2_inode_large inode_large; | |
667 | int ret, filetype, mode; | |
668 | ||
45780b37 | 669 | ret = tl_to_darg(&darg, tl, val); |
1e0c8ca7 TT |
670 | if (ret) |
671 | return ret; | |
f5de3d7c | 672 | ext4_fc_flush_extents(ctx, 0); |
63b7192c HS |
673 | ret = errcode_to_errno(ext2fs_read_inode(fs, darg.ino, |
674 | (struct ext2_inode *)&inode_large)); | |
675 | if (ret) | |
676 | goto out; | |
677 | ||
678 | mode = inode_large.i_mode; | |
679 | ||
680 | if (LINUX_S_ISREG(mode)) | |
681 | filetype = EXT2_FT_REG_FILE; | |
682 | else if (LINUX_S_ISDIR(mode)) | |
683 | filetype = EXT2_FT_DIR; | |
684 | else if (LINUX_S_ISCHR(mode)) | |
685 | filetype = EXT2_FT_CHRDEV; | |
686 | else if (LINUX_S_ISBLK(mode)) | |
687 | filetype = EXT2_FT_BLKDEV; | |
688 | else if (LINUX_S_ISLNK(mode)) | |
689 | return EXT2_FT_SYMLINK; | |
690 | else if (LINUX_S_ISFIFO(mode)) | |
691 | filetype = EXT2_FT_FIFO; | |
692 | else if (LINUX_S_ISSOCK(mode)) | |
693 | filetype = EXT2_FT_SOCK; | |
694 | else { | |
695 | ret = -EINVAL; | |
696 | goto out; | |
697 | } | |
698 | ||
699 | /* | |
700 | * Forcefully unlink if the same name is present and ignore the error | |
701 | * if any, since this dirent might not exist | |
702 | */ | |
703 | ext2fs_unlink(fs, darg.parent_ino, darg.dname, darg.ino, | |
704 | EXT2FS_UNLINK_FORCE); | |
705 | ||
706 | ret = errcode_to_errno( | |
707 | ext2fs_link(fs, darg.parent_ino, darg.dname, darg.ino, | |
708 | filetype)); | |
709 | out: | |
710 | free(darg.dname); | |
711 | return ret; | |
712 | ||
713 | } | |
f5de3d7c HS |
714 | |
715 | /* This function fixes the i_blocks field in the replayed indoe */ | |
716 | static void ext4_fc_replay_fixup_iblocks(struct ext2_inode_large *ondisk_inode, | |
717 | struct ext2_inode_large *fc_inode) | |
718 | { | |
4e95b372 | 719 | if (ondisk_inode->i_flags & EXT4_EXTENTS_FL) { |
f5de3d7c HS |
720 | struct ext3_extent_header *eh; |
721 | ||
722 | eh = (struct ext3_extent_header *)(&ondisk_inode->i_block[0]); | |
4e95b372 | 723 | if (le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) { |
f5de3d7c | 724 | memset(eh, 0, sizeof(*eh)); |
4e95b372 | 725 | eh->eh_magic = cpu_to_le16(EXT3_EXT_MAGIC); |
f5de3d7c HS |
726 | eh->eh_max = cpu_to_le16( |
727 | (sizeof(ondisk_inode->i_block) - | |
728 | sizeof(struct ext3_extent_header)) / | |
4e95b372 | 729 | sizeof(struct ext3_extent)); |
f5de3d7c | 730 | } |
4e95b372 | 731 | } else if (ondisk_inode->i_flags & EXT4_INLINE_DATA_FL) { |
f5de3d7c HS |
732 | memcpy(ondisk_inode->i_block, fc_inode->i_block, |
733 | sizeof(fc_inode->i_block)); | |
734 | } | |
735 | } | |
736 | ||
45780b37 | 737 | static int ext4_fc_handle_inode(e2fsck_t ctx, __u8 *val) |
f5de3d7c | 738 | { |
f5de3d7c | 739 | int ino, inode_len = EXT2_GOOD_OLD_INODE_SIZE; |
4e95b372 | 740 | struct ext2_inode_large *inode = NULL, *fc_inode = NULL; |
1e0c8ca7 TT |
741 | __le32 fc_ino; |
742 | __u8 *fc_raw_inode; | |
f5de3d7c HS |
743 | errcode_t err; |
744 | blk64_t blks; | |
745 | ||
45780b37 HS |
746 | memcpy(&fc_ino, val, sizeof(fc_ino)); |
747 | fc_raw_inode = val + sizeof(fc_ino); | |
1e0c8ca7 | 748 | ino = le32_to_cpu(fc_ino); |
f5de3d7c | 749 | |
64d576a8 TT |
750 | if (EXT2_INODE_SIZE(ctx->fs->super) > EXT2_GOOD_OLD_INODE_SIZE) { |
751 | __u16 extra_isize = ext2fs_le16_to_cpu( | |
1e0c8ca7 | 752 | ((struct ext2_inode_large *)fc_raw_inode)->i_extra_isize); |
64d576a8 TT |
753 | |
754 | if ((extra_isize < (sizeof(inode->i_extra_isize) + | |
755 | sizeof(inode->i_checksum_hi))) || | |
756 | (extra_isize > (EXT2_INODE_SIZE(ctx->fs->super) - | |
757 | EXT2_GOOD_OLD_INODE_SIZE))) { | |
758 | err = EFSCORRUPTED; | |
759 | goto out; | |
760 | } | |
761 | inode_len += extra_isize; | |
762 | } | |
f5de3d7c HS |
763 | err = ext2fs_get_mem(inode_len, &inode); |
764 | if (err) | |
4e95b372 HS |
765 | goto out; |
766 | err = ext2fs_get_mem(inode_len, &fc_inode); | |
767 | if (err) | |
768 | goto out; | |
f5de3d7c HS |
769 | ext4_fc_flush_extents(ctx, ino); |
770 | ||
771 | err = ext2fs_read_inode_full(ctx->fs, ino, (struct ext2_inode *)inode, | |
772 | inode_len); | |
773 | if (err) | |
774 | goto out; | |
1e0c8ca7 | 775 | memcpy(fc_inode, fc_raw_inode, inode_len); |
86fe64e4 TT |
776 | #ifdef WORDS_BIGENDIAN |
777 | ext2fs_swap_inode_full(ctx->fs, fc_inode, fc_inode, 0, inode_len); | |
4e95b372 HS |
778 | #endif |
779 | memcpy(inode, fc_inode, offsetof(struct ext2_inode_large, i_block)); | |
780 | memcpy(&inode->i_generation, &fc_inode->i_generation, | |
f5de3d7c | 781 | inode_len - offsetof(struct ext2_inode_large, i_generation)); |
4e95b372 | 782 | ext4_fc_replay_fixup_iblocks(inode, fc_inode); |
f5de3d7c HS |
783 | err = ext2fs_count_blocks(ctx->fs, ino, EXT2_INODE(inode), &blks); |
784 | if (err) | |
785 | goto out; | |
786 | ext2fs_iblk_set(ctx->fs, EXT2_INODE(inode), blks); | |
787 | ext2fs_inode_csum_set(ctx->fs, ino, inode); | |
788 | ||
789 | err = ext2fs_write_inode_full(ctx->fs, ino, (struct ext2_inode *)inode, | |
790 | inode_len); | |
791 | if (err) | |
792 | goto out; | |
793 | if (inode->i_links_count) | |
794 | ext2fs_mark_inode_bitmap2(ctx->fs->inode_map, ino); | |
795 | else | |
796 | ext2fs_unmark_inode_bitmap2(ctx->fs->inode_map, ino); | |
797 | ext2fs_mark_ib_dirty(ctx->fs); | |
798 | ||
799 | out: | |
800 | ext2fs_free_mem(&inode); | |
4e95b372 | 801 | ext2fs_free_mem(&fc_inode); |
f5de3d7c HS |
802 | return errcode_to_errno(err); |
803 | } | |
804 | ||
805 | /* | |
806 | * Handle add extent replay tag. | |
807 | */ | |
45780b37 | 808 | static int ext4_fc_handle_add_extent(e2fsck_t ctx, __u8 *val) |
f5de3d7c HS |
809 | { |
810 | struct ext2fs_extent extent; | |
1e0c8ca7 | 811 | struct ext4_fc_add_range add_range; |
8e2e5a51 | 812 | ext2_ino_t ino; |
f158f896 | 813 | int ret = 0; |
f5de3d7c | 814 | |
45780b37 | 815 | memcpy(&add_range, val, sizeof(add_range)); |
1e0c8ca7 | 816 | ino = le32_to_cpu(add_range.fc_ino); |
f5de3d7c HS |
817 | ext4_fc_flush_extents(ctx, ino); |
818 | ||
819 | ret = ext4_fc_read_extents(ctx, ino); | |
820 | if (ret) | |
821 | return ret; | |
822 | memset(&extent, 0, sizeof(extent)); | |
823 | ret = errcode_to_errno(ext2fs_decode_extent( | |
1e0c8ca7 TT |
824 | &extent, (void *)add_range.fc_ex, |
825 | sizeof(add_range.fc_ex))); | |
f5de3d7c HS |
826 | if (ret) |
827 | return ret; | |
828 | return ext4_add_extent_to_list(ctx, | |
829 | &ctx->fc_replay_state.fc_extent_list, &extent); | |
830 | } | |
831 | ||
832 | /* | |
833 | * Handle delete logical range replay tag. | |
834 | */ | |
45780b37 | 835 | static int ext4_fc_handle_del_range(e2fsck_t ctx, __u8 *val) |
f5de3d7c HS |
836 | { |
837 | struct ext2fs_extent extent; | |
1e0c8ca7 | 838 | struct ext4_fc_del_range del_range; |
f5de3d7c HS |
839 | int ret, ino; |
840 | ||
45780b37 | 841 | memcpy(&del_range, val, sizeof(del_range)); |
1e0c8ca7 | 842 | ino = le32_to_cpu(del_range.fc_ino); |
f5de3d7c HS |
843 | ext4_fc_flush_extents(ctx, ino); |
844 | ||
845 | memset(&extent, 0, sizeof(extent)); | |
1e0c8ca7 TT |
846 | extent.e_lblk = le32_to_cpu(del_range.fc_lblk); |
847 | extent.e_len = le32_to_cpu(del_range.fc_len); | |
f5de3d7c HS |
848 | ret = ext4_fc_read_extents(ctx, ino); |
849 | if (ret) | |
850 | return ret; | |
851 | return ext4_del_extent_from_list(ctx, | |
852 | &ctx->fc_replay_state.fc_extent_list, &extent); | |
853 | } | |
854 | ||
c8a097c2 HS |
855 | /* |
856 | * Main recovery path entry point. This function returns JBD2_FC_REPLAY_CONTINUE | |
857 | * to indicate that it is expecting more fast commit blocks. It returns | |
858 | * JBD2_FC_REPLAY_STOP to indicate that replay is done. | |
859 | */ | |
860 | static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, | |
861 | enum passtype pass, int off, tid_t expected_tid) | |
862 | { | |
81a6b010 HS |
863 | e2fsck_t ctx = journal->j_fs_dev->k_ctx; |
864 | struct e2fsck_fc_replay_state *state = &ctx->fc_replay_state; | |
3ba3ec0b | 865 | int ret = JBD2_FC_REPLAY_CONTINUE; |
45780b37 HS |
866 | struct ext4_fc_tl tl; |
867 | __u8 *start, *end, *cur, *val; | |
81a6b010 HS |
868 | |
869 | if (pass == PASS_SCAN) { | |
870 | state->fc_current_pass = PASS_SCAN; | |
871 | return ext4_fc_replay_scan(journal, bh, off, expected_tid); | |
872 | } | |
3ba3ec0b HS |
873 | |
874 | if (state->fc_replay_num_tags == 0) | |
875 | goto replay_done; | |
876 | ||
877 | if (state->fc_current_pass != pass) { | |
878 | /* Starting replay phase */ | |
879 | state->fc_current_pass = pass; | |
880 | /* We will reset checksums */ | |
881 | ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; | |
f5de3d7c | 882 | ret = errcode_to_errno(ext2fs_read_bitmaps(ctx->fs)); |
3ba3ec0b HS |
883 | if (ret) { |
884 | jbd_debug(1, "Error %d while reading bitmaps\n", ret); | |
885 | return ret; | |
886 | } | |
887 | state->fc_super_state = ctx->fs->super->s_state; | |
888 | /* | |
889 | * Mark the file system to indicate it contains errors. That's | |
890 | * because the updates performed by fast commit replay code are | |
20654197 | 891 | * not atomic and may result in inconsistent file system if it |
3ba3ec0b HS |
892 | * crashes before the replay is complete. |
893 | */ | |
894 | ctx->fs->super->s_state |= EXT2_ERROR_FS; | |
895 | ctx->fs->super->s_state |= EXT4_FC_REPLAY; | |
896 | ext2fs_mark_super_dirty(ctx->fs); | |
897 | ext2fs_flush(ctx->fs); | |
898 | } | |
899 | ||
900 | start = (__u8 *)bh->b_data; | |
901 | end = (__u8 *)bh->b_data + journal->j_blocksize - 1; | |
902 | ||
45780b37 HS |
903 | for (cur = start; cur < end; cur = cur + le16_to_cpu(tl.fc_len) + sizeof(tl)) { |
904 | memcpy(&tl, cur, sizeof(tl)); | |
905 | val = cur + sizeof(tl); | |
906 | ||
3ba3ec0b HS |
907 | if (state->fc_replay_num_tags == 0) |
908 | goto replay_done; | |
909 | jbd_debug(3, "Replay phase processing %s tag\n", | |
45780b37 | 910 | tag2str(le16_to_cpu(tl.fc_tag))); |
3ba3ec0b | 911 | state->fc_replay_num_tags--; |
45780b37 | 912 | switch (le16_to_cpu(tl.fc_tag)) { |
3ba3ec0b HS |
913 | case EXT4_FC_TAG_CREAT: |
914 | case EXT4_FC_TAG_LINK: | |
45780b37 | 915 | ret = ext4_fc_handle_link_and_create(ctx, &tl, val); |
63b7192c | 916 | break; |
3ba3ec0b | 917 | case EXT4_FC_TAG_UNLINK: |
45780b37 | 918 | ret = ext4_fc_handle_unlink(ctx, &tl, val); |
63b7192c | 919 | break; |
3ba3ec0b | 920 | case EXT4_FC_TAG_ADD_RANGE: |
45780b37 | 921 | ret = ext4_fc_handle_add_extent(ctx, val); |
f5de3d7c | 922 | break; |
3ba3ec0b | 923 | case EXT4_FC_TAG_DEL_RANGE: |
45780b37 | 924 | ret = ext4_fc_handle_del_range(ctx, val); |
f5de3d7c | 925 | break; |
3ba3ec0b | 926 | case EXT4_FC_TAG_INODE: |
45780b37 | 927 | ret = ext4_fc_handle_inode(ctx, val); |
f5de3d7c | 928 | break; |
3ba3ec0b | 929 | case EXT4_FC_TAG_TAIL: |
f5de3d7c | 930 | ext4_fc_flush_extents(ctx, 0); |
3ba3ec0b HS |
931 | case EXT4_FC_TAG_PAD: |
932 | case EXT4_FC_TAG_HEAD: | |
933 | break; | |
934 | default: | |
935 | ret = -ECANCELED; | |
936 | break; | |
937 | } | |
938 | if (ret < 0) | |
939 | break; | |
940 | ret = JBD2_FC_REPLAY_CONTINUE; | |
941 | } | |
942 | return ret; | |
943 | replay_done: | |
944 | jbd_debug(1, "End of fast commit replay\n"); | |
945 | if (state->fc_current_pass != pass) | |
946 | return JBD2_FC_REPLAY_STOP; | |
947 | ||
948 | ext2fs_calculate_summary_stats(ctx->fs, 0 /* update bg also */); | |
949 | ext2fs_write_block_bitmap(ctx->fs); | |
950 | ext2fs_write_inode_bitmap(ctx->fs); | |
951 | ext2fs_mark_super_dirty(ctx->fs); | |
952 | ext2fs_set_gdt_csum(ctx->fs); | |
953 | ctx->fs->super->s_state = state->fc_super_state; | |
954 | ext2fs_flush(ctx->fs); | |
955 | ||
c8a097c2 HS |
956 | return JBD2_FC_REPLAY_STOP; |
957 | } | |
958 | ||
d1a2182a | 959 | static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) |
3b5386dc | 960 | { |
051afbe0 | 961 | struct process_block_struct pb; |
d1a2182a TT |
962 | struct ext2_super_block *sb = ctx->fs->super; |
963 | struct ext2_super_block jsuper; | |
964 | struct problem_context pctx; | |
965 | struct buffer_head *bh; | |
966 | struct inode *j_inode = NULL; | |
967 | struct kdev_s *dev_fs = NULL, *dev_journal; | |
3e699064 | 968 | const char *journal_name = 0; |
d1a2182a | 969 | journal_t *journal = NULL; |
3e699064 TT |
970 | errcode_t retval = 0; |
971 | io_manager io_ptr = 0; | |
3b693d0b | 972 | unsigned long long start = 0; |
d8cbb803 | 973 | int ret; |
d1a2182a | 974 | int ext_journal = 0; |
a435ec34 | 975 | int tried_backup_jnl = 0; |
2bfe0bdb | 976 | |
d1a2182a | 977 | clear_problem_context(&pctx); |
2bfe0bdb | 978 | |
d1a2182a TT |
979 | journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); |
980 | if (!journal) { | |
3b5386dc TT |
981 | return EXT2_ET_NO_MEMORY; |
982 | } | |
983 | ||
8cf93332 TT |
984 | dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); |
985 | if (!dev_fs) { | |
986 | retval = EXT2_ET_NO_MEMORY; | |
987 | goto errout; | |
988 | } | |
989 | dev_journal = dev_fs+1; | |
2bfe0bdb | 990 | |
8cf93332 TT |
991 | dev_fs->k_ctx = dev_journal->k_ctx = ctx; |
992 | dev_fs->k_dev = K_DEV_FS; | |
993 | dev_journal->k_dev = K_DEV_JOURNAL; | |
2bfe0bdb | 994 | |
d1a2182a TT |
995 | journal->j_dev = dev_journal; |
996 | journal->j_fs_dev = dev_fs; | |
997 | journal->j_inode = NULL; | |
998 | journal->j_blocksize = ctx->fs->blocksize; | |
3b5386dc | 999 | |
d1a2182a | 1000 | if (uuid_is_null(sb->s_journal_uuid)) { |
40196f3b TT |
1001 | /* |
1002 | * The full set of superblock sanity checks haven't | |
1003 | * been performed yet, so we need to do some basic | |
1004 | * checks here to avoid potential array overruns. | |
1005 | */ | |
1006 | if (!sb->s_journal_inum || | |
1007 | (sb->s_journal_inum > | |
1008 | (ctx->fs->group_desc_count * sb->s_inodes_per_group))) { | |
2bfe0bdb BB |
1009 | retval = EXT2_ET_BAD_INODE_NUM; |
1010 | goto errout; | |
1011 | } | |
d1a2182a TT |
1012 | j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), |
1013 | "journal inode"); | |
1014 | if (!j_inode) { | |
1015 | retval = EXT2_ET_NO_MEMORY; | |
1016 | goto errout; | |
1017 | } | |
3b5386dc | 1018 | |
d1a2182a TT |
1019 | j_inode->i_ctx = ctx; |
1020 | j_inode->i_ino = sb->s_journal_inum; | |
2bfe0bdb | 1021 | |
d1a2182a TT |
1022 | if ((retval = ext2fs_read_inode(ctx->fs, |
1023 | sb->s_journal_inum, | |
a435ec34 TT |
1024 | &j_inode->i_ext2))) { |
1025 | try_backup_journal: | |
1026 | if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || | |
1027 | tried_backup_jnl) | |
1028 | goto errout; | |
1029 | memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); | |
efc6f628 | 1030 | memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, |
a435ec34 | 1031 | EXT2_N_BLOCKS*4); |
931b58e1 | 1032 | j_inode->i_ext2.i_size_high = sb->s_jnl_blocks[15]; |
a435ec34 TT |
1033 | j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; |
1034 | j_inode->i_ext2.i_links_count = 1; | |
1035 | j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; | |
051afbe0 TT |
1036 | e2fsck_use_inode_shortcuts(ctx, 1); |
1037 | ctx->stashed_ino = j_inode->i_ino; | |
1038 | ctx->stashed_inode = &j_inode->i_ext2; | |
a435ec34 TT |
1039 | tried_backup_jnl++; |
1040 | } | |
d1a2182a | 1041 | if (!j_inode->i_ext2.i_links_count || |
b0cd09e5 EB |
1042 | !LINUX_S_ISREG(j_inode->i_ext2.i_mode) || |
1043 | (j_inode->i_ext2.i_flags & EXT4_ENCRYPT_FL)) { | |
d1a2182a | 1044 | retval = EXT2_ET_NO_JOURNAL; |
a435ec34 | 1045 | goto try_backup_journal; |
d1a2182a | 1046 | } |
931b58e1 | 1047 | if (EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize < |
8335d3c5 | 1048 | JBD2_MIN_JOURNAL_BLOCKS) { |
d1a2182a | 1049 | retval = EXT2_ET_JOURNAL_TOO_SMALL; |
a435ec34 TT |
1050 | goto try_backup_journal; |
1051 | } | |
051afbe0 | 1052 | pb.last_block = -1; |
6dc64392 | 1053 | retval = ext2fs_block_iterate3(ctx->fs, j_inode->i_ino, |
efc6f628 | 1054 | BLOCK_FLAG_HOLE, 0, |
051afbe0 | 1055 | process_journal_block, &pb); |
931b58e1 | 1056 | if ((pb.last_block + 1) * ctx->fs->blocksize < |
68477355 | 1057 | (int) EXT2_I_SIZE(&j_inode->i_ext2)) { |
051afbe0 TT |
1058 | retval = EXT2_ET_JOURNAL_TOO_SMALL; |
1059 | goto try_backup_journal; | |
d1a2182a | 1060 | } |
ded28ac2 KS |
1061 | if (tried_backup_jnl && !(ctx->options & E2F_OPT_READONLY)) { |
1062 | retval = ext2fs_write_inode(ctx->fs, sb->s_journal_inum, | |
1063 | &j_inode->i_ext2); | |
1064 | if (retval) | |
1065 | goto errout; | |
1066 | } | |
1067 | ||
7ed2b5d0 | 1068 | journal->j_total_len = EXT2_I_SIZE(&j_inode->i_ext2) / |
931b58e1 | 1069 | journal->j_blocksize; |
3b5386dc | 1070 | |
d1a2182a | 1071 | #ifdef USE_INODE_IO |
a435ec34 TT |
1072 | retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, |
1073 | &j_inode->i_ext2, | |
1074 | &journal_name); | |
d1a2182a TT |
1075 | if (retval) |
1076 | goto errout; | |
3b5386dc | 1077 | |
d1a2182a TT |
1078 | io_ptr = inode_io_manager; |
1079 | #else | |
1080 | journal->j_inode = j_inode; | |
1081 | ctx->journal_io = ctx->fs->io; | |
8335d3c5 | 1082 | if ((ret = jbd2_journal_bmap(journal, 0, &start)) != 0) { |
d8cbb803 | 1083 | retval = (errcode_t) (-1 * ret); |
d1a2182a | 1084 | goto errout; |
d8cbb803 | 1085 | } |
d1a2182a TT |
1086 | #endif |
1087 | } else { | |
1088 | ext_journal = 1; | |
f364093b TT |
1089 | if (!ctx->journal_name) { |
1090 | char uuid[37]; | |
1091 | ||
1092 | uuid_unparse(sb->s_journal_uuid, uuid); | |
1093 | ctx->journal_name = blkid_get_devname(ctx->blkid, | |
1094 | "UUID", uuid); | |
1095 | if (!ctx->journal_name) | |
1096 | ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); | |
d1a2182a | 1097 | } |
f364093b | 1098 | journal_name = ctx->journal_name; |
2bfe0bdb | 1099 | |
d1a2182a TT |
1100 | if (!journal_name) { |
1101 | fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); | |
2bfe0bdb BB |
1102 | retval = EXT2_ET_LOAD_EXT_JOURNAL; |
1103 | goto errout; | |
d1a2182a | 1104 | } |
2bfe0bdb | 1105 | |
d1a2182a TT |
1106 | jfs_debug(1, "Using journal file %s\n", journal_name); |
1107 | io_ptr = unix_io_manager; | |
3b5386dc TT |
1108 | } |
1109 | ||
d1a2182a TT |
1110 | #if 0 |
1111 | test_io_backing_manager = io_ptr; | |
adee8d75 | 1112 | io_ptr = test_io_manager; |
adee8d75 | 1113 | #endif |
d1a2182a TT |
1114 | #ifndef USE_INODE_IO |
1115 | if (ext_journal) | |
1116 | #endif | |
26991d02 TT |
1117 | { |
1118 | int flags = IO_FLAG_RW; | |
1119 | if (!(ctx->mount_flags & EXT2_MF_ISROOT && | |
1120 | ctx->mount_flags & EXT2_MF_READONLY)) | |
1121 | flags |= IO_FLAG_EXCLUSIVE; | |
1122 | if ((ctx->mount_flags & EXT2_MF_READONLY) && | |
1123 | (ctx->options & E2F_OPT_FORCE)) | |
1124 | flags &= ~IO_FLAG_EXCLUSIVE; | |
1125 | ||
1126 | ||
1127 | retval = io_ptr->open(journal_name, flags, | |
d1a2182a | 1128 | &ctx->journal_io); |
26991d02 | 1129 | } |
6d222f32 | 1130 | if (retval) |
d1a2182a | 1131 | goto errout; |
adee8d75 | 1132 | |
d1a2182a TT |
1133 | io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); |
1134 | ||
1135 | if (ext_journal) { | |
27dc24de TT |
1136 | blk64_t maxlen; |
1137 | ||
7f33024a | 1138 | start = ext2fs_journal_sb_start(ctx->fs->blocksize) - 1; |
d1a2182a TT |
1139 | bh = getblk(dev_journal, start, ctx->fs->blocksize); |
1140 | if (!bh) { | |
1141 | retval = EXT2_ET_NO_MEMORY; | |
1142 | goto errout; | |
1143 | } | |
db113c0e | 1144 | ll_rw_block(REQ_OP_READ, 0, 1, &bh); |
2aa362f5 TT |
1145 | if ((retval = bh->b_err) != 0) { |
1146 | brelse(bh); | |
d1a2182a | 1147 | goto errout; |
2aa362f5 | 1148 | } |
7f33024a | 1149 | memcpy(&jsuper, start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET, |
d1a2182a | 1150 | sizeof(jsuper)); |
2eae0930 | 1151 | #ifdef WORDS_BIGENDIAN |
efc6f628 | 1152 | if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) |
d1a2182a | 1153 | ext2fs_swap_super(&jsuper); |
adee8d75 | 1154 | #endif |
d1a2182a | 1155 | if (jsuper.s_magic != EXT2_SUPER_MAGIC || |
86f3b6cf | 1156 | !ext2fs_has_feature_journal_dev(&jsuper)) { |
d1a2182a TT |
1157 | fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); |
1158 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | |
6aabb754 | 1159 | brelse(bh); |
d1a2182a TT |
1160 | goto errout; |
1161 | } | |
1162 | /* Make sure the journal UUID is correct */ | |
1163 | if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, | |
1164 | sizeof(jsuper.s_uuid))) { | |
1165 | fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); | |
1166 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | |
6aabb754 | 1167 | brelse(bh); |
d1a2182a TT |
1168 | goto errout; |
1169 | } | |
2bfe0bdb | 1170 | |
6aabb754 | 1171 | /* Check the superblock checksum */ |
86f3b6cf | 1172 | if (ext2fs_has_feature_metadata_csum(&jsuper)) { |
6aabb754 DW |
1173 | struct struct_ext2_filsys fsx; |
1174 | struct ext2_super_block superx; | |
1175 | void *p; | |
1176 | ||
1177 | p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; | |
1178 | memcpy(&fsx, ctx->fs, sizeof(fsx)); | |
1179 | memcpy(&superx, ctx->fs->super, sizeof(superx)); | |
1180 | fsx.super = &superx; | |
86f3b6cf | 1181 | ext2fs_set_feature_metadata_csum(fsx.super); |
6aabb754 DW |
1182 | if (!ext2fs_superblock_csum_verify(&fsx, p) && |
1183 | fix_problem(ctx, PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID, | |
1184 | &pctx)) { | |
1185 | ext2fs_superblock_csum_set(&fsx, p); | |
1186 | mark_buffer_dirty(bh); | |
1187 | } | |
1188 | } | |
1189 | brelse(bh); | |
1190 | ||
4dbfd79d | 1191 | maxlen = ext2fs_blocks_count(&jsuper); |
7ed2b5d0 | 1192 | journal->j_total_len = (maxlen < 1ULL << 32) ? maxlen : (1ULL << 32) - 1; |
d1a2182a | 1193 | start++; |
3b5386dc TT |
1194 | } |
1195 | ||
d1a2182a | 1196 | if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { |
adee8d75 TT |
1197 | retval = EXT2_ET_NO_MEMORY; |
1198 | goto errout; | |
1199 | } | |
2bfe0bdb | 1200 | |
d1a2182a TT |
1201 | journal->j_sb_buffer = bh; |
1202 | journal->j_superblock = (journal_superblock_t *)bh->b_data; | |
c8a097c2 HS |
1203 | if (ext2fs_has_feature_fast_commit(ctx->fs->super)) |
1204 | journal->j_fc_replay_callback = ext4_fc_replay; | |
1205 | else | |
1206 | journal->j_fc_replay_callback = NULL; | |
2bfe0bdb | 1207 | |
d1a2182a TT |
1208 | #ifdef USE_INODE_IO |
1209 | if (j_inode) | |
c4e3d3f3 | 1210 | ext2fs_free_mem(&j_inode); |
d1a2182a TT |
1211 | #endif |
1212 | ||
1213 | *ret_journal = journal; | |
051afbe0 | 1214 | e2fsck_use_inode_shortcuts(ctx, 0); |
adee8d75 TT |
1215 | return 0; |
1216 | ||
1217 | errout: | |
051afbe0 | 1218 | e2fsck_use_inode_shortcuts(ctx, 0); |
d1a2182a | 1219 | if (dev_fs) |
c4e3d3f3 | 1220 | ext2fs_free_mem(&dev_fs); |
d1a2182a | 1221 | if (j_inode) |
c4e3d3f3 | 1222 | ext2fs_free_mem(&j_inode); |
d1a2182a | 1223 | if (journal) |
c4e3d3f3 | 1224 | ext2fs_free_mem(&journal); |
adee8d75 | 1225 | return retval; |
3b5386dc TT |
1226 | } |
1227 | ||
6a6d3d44 TT |
1228 | static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx, |
1229 | struct problem_context *pctx) | |
3b5386dc | 1230 | { |
5dd8f963 | 1231 | struct ext2_super_block *sb = ctx->fs->super; |
86f3b6cf DW |
1232 | int recover = ext2fs_has_feature_journal_needs_recovery(ctx->fs->super); |
1233 | int has_journal = ext2fs_has_feature_journal(ctx->fs->super); | |
3b5386dc | 1234 | |
5dd8f963 | 1235 | if (has_journal || sb->s_journal_inum) { |
3b5386dc | 1236 | /* The journal inode is bogus, remove and force full fsck */ |
7e92dfae | 1237 | pctx->ino = sb->s_journal_inum; |
3b5386dc | 1238 | if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) { |
5dd8f963 | 1239 | if (has_journal && sb->s_journal_inum) |
b151d346 | 1240 | printf("*** journal has been deleted ***\n\n"); |
86f3b6cf | 1241 | ext2fs_clear_feature_journal(sb); |
5dd8f963 | 1242 | sb->s_journal_inum = 0; |
e690eae5 | 1243 | memset(sb->s_jnl_blocks, 0, sizeof(sb->s_jnl_blocks)); |
5107d0d1 | 1244 | ctx->flags |= E2F_FLAG_JOURNAL_INODE; |
0cfce7f7 | 1245 | ctx->fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
3b5386dc TT |
1246 | e2fsck_clear_recover(ctx, 1); |
1247 | return 0; | |
1248 | } | |
d37026ea | 1249 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
3b5386dc TT |
1250 | } else if (recover) { |
1251 | if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) { | |
1252 | e2fsck_clear_recover(ctx, 1); | |
1253 | return 0; | |
1254 | } | |
1255 | return EXT2_ET_UNSUPP_FEATURE; | |
1256 | } | |
1257 | return 0; | |
1258 | } | |
1259 | ||
62e3e7fe TT |
1260 | #define V1_SB_SIZE 0x0024 |
1261 | static void clear_v2_journal_fields(journal_t *journal) | |
1262 | { | |
8cf93332 | 1263 | e2fsck_t ctx = journal->j_dev->k_ctx; |
62e3e7fe TT |
1264 | struct problem_context pctx; |
1265 | ||
1266 | clear_problem_context(&pctx); | |
1267 | ||
1268 | if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx)) | |
1269 | return; | |
1270 | ||
bf9f3b6d | 1271 | ctx->flags |= E2F_FLAG_PROBLEMS_FIXED; |
62e3e7fe TT |
1272 | memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, |
1273 | ctx->fs->blocksize-V1_SB_SIZE); | |
8cf93332 | 1274 | mark_buffer_dirty(journal->j_sb_buffer); |
62e3e7fe TT |
1275 | } |
1276 | ||
1277 | ||
6a6d3d44 | 1278 | static errcode_t e2fsck_journal_load(journal_t *journal) |
3b5386dc | 1279 | { |
8cf93332 | 1280 | e2fsck_t ctx = journal->j_dev->k_ctx; |
3b5386dc TT |
1281 | journal_superblock_t *jsb; |
1282 | struct buffer_head *jbh = journal->j_sb_buffer; | |
1283 | struct problem_context pctx; | |
1284 | ||
1285 | clear_problem_context(&pctx); | |
1286 | ||
db113c0e | 1287 | ll_rw_block(REQ_OP_READ, 0, 1, &jbh); |
3b5386dc | 1288 | if (jbh->b_err) { |
45ff69ff | 1289 | com_err(ctx->device_name, jbh->b_err, "%s", |
3b5386dc TT |
1290 | _("reading journal superblock\n")); |
1291 | return jbh->b_err; | |
1292 | } | |
1293 | ||
1294 | jsb = journal->j_superblock; | |
8335d3c5 TT |
1295 | /* If we don't even have JBD2_MAGIC, we probably have a wrong inode */ |
1296 | if (jsb->s_header.h_magic != htonl(JBD2_MAGIC_NUMBER)) | |
3b5386dc TT |
1297 | return e2fsck_journal_fix_bad_inode(ctx, &pctx); |
1298 | ||
0e8a9560 | 1299 | switch (ntohl(jsb->s_header.h_blocktype)) { |
8335d3c5 | 1300 | case JBD2_SUPERBLOCK_V1: |
0e8a9560 | 1301 | journal->j_format_version = 1; |
62e3e7fe TT |
1302 | if (jsb->s_feature_compat || |
1303 | jsb->s_feature_incompat || | |
1304 | jsb->s_feature_ro_compat || | |
1305 | jsb->s_nr_users) | |
1306 | clear_v2_journal_fields(journal); | |
0e8a9560 | 1307 | break; |
efc6f628 | 1308 | |
8335d3c5 | 1309 | case JBD2_SUPERBLOCK_V2: |
0e8a9560 | 1310 | journal->j_format_version = 2; |
b3b3d465 | 1311 | if (ntohl(jsb->s_nr_users) > 1 && |
a435ec34 | 1312 | uuid_is_null(ctx->fs->super->s_journal_uuid)) |
62e3e7fe | 1313 | clear_v2_journal_fields(journal); |
adee8d75 TT |
1314 | if (ntohl(jsb->s_nr_users) > 1) { |
1315 | fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx); | |
1316 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; | |
1317 | } | |
0e8a9560 | 1318 | break; |
c7f23364 TT |
1319 | |
1320 | /* | |
1321 | * These should never appear in a journal super block, so if | |
1322 | * they do, the journal is badly corrupted. | |
1323 | */ | |
8335d3c5 TT |
1324 | case JBD2_DESCRIPTOR_BLOCK: |
1325 | case JBD2_COMMIT_BLOCK: | |
1326 | case JBD2_REVOKE_BLOCK: | |
d37026ea | 1327 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
efc6f628 | 1328 | |
0e8a9560 TT |
1329 | /* If we don't understand the superblock major type, but there |
1330 | * is a magic number, then it is likely to be a new format we | |
1331 | * just don't understand, so leave it alone. */ | |
1332 | default: | |
c7f23364 | 1333 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; |
0e8a9560 TT |
1334 | } |
1335 | ||
8335d3c5 | 1336 | if (JBD2_HAS_INCOMPAT_FEATURE(journal, ~JBD2_KNOWN_INCOMPAT_FEATURES)) |
2f686ace | 1337 | return EXT2_ET_UNSUPP_FEATURE; |
efc6f628 | 1338 | |
8335d3c5 | 1339 | if (JBD2_HAS_RO_COMPAT_FEATURE(journal, ~JBD2_KNOWN_ROCOMPAT_FEATURES)) |
2f686ace | 1340 | return EXT2_ET_RO_UNSUPP_FEATURE; |
3b5386dc | 1341 | |
38d5adf3 | 1342 | /* Checksum v1-3 are mutually exclusive features. */ |
8335d3c5 | 1343 | if (jbd2_has_feature_csum2(journal) && jbd2_has_feature_csum3(journal)) |
d37026ea | 1344 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
38d5adf3 | 1345 | |
8335d3c5 TT |
1346 | if (jbd2_journal_has_csum_v2or3(journal) && |
1347 | jbd2_has_feature_checksum(journal)) | |
d37026ea | 1348 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
b2795949 DW |
1349 | |
1350 | if (!e2fsck_journal_verify_csum_type(journal, jsb) || | |
1351 | !e2fsck_journal_sb_csum_verify(journal, jsb)) | |
d37026ea | 1352 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
b2795949 | 1353 | |
8335d3c5 | 1354 | if (jbd2_journal_has_csum_v2or3(journal)) |
13af4b93 DW |
1355 | journal->j_csum_seed = jbd2_chksum(journal, ~0, jsb->s_uuid, |
1356 | sizeof(jsb->s_uuid)); | |
1357 | ||
0e8a9560 TT |
1358 | /* We have now checked whether we know enough about the journal |
1359 | * format to be able to proceed safely, so any other checks that | |
1360 | * fail we should attempt to recover from. */ | |
1361 | if (jsb->s_blocksize != htonl(journal->j_blocksize)) { | |
d37026ea | 1362 | com_err(ctx->program_name, EXT2_ET_CORRUPT_JOURNAL_SB, |
0e8a9560 TT |
1363 | _("%s: no valid journal superblock found\n"), |
1364 | ctx->device_name); | |
d37026ea | 1365 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
3b5386dc TT |
1366 | } |
1367 | ||
7ed2b5d0 HS |
1368 | if (ntohl(jsb->s_maxlen) < journal->j_total_len) |
1369 | journal->j_total_len = ntohl(jsb->s_maxlen); | |
1370 | else if (ntohl(jsb->s_maxlen) > journal->j_total_len) { | |
d37026ea | 1371 | com_err(ctx->program_name, EXT2_ET_CORRUPT_JOURNAL_SB, |
0e8a9560 TT |
1372 | _("%s: journal too short\n"), |
1373 | ctx->device_name); | |
d37026ea | 1374 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
3b5386dc TT |
1375 | } |
1376 | ||
1377 | journal->j_tail_sequence = ntohl(jsb->s_sequence); | |
ecf1b776 | 1378 | journal->j_transaction_sequence = journal->j_tail_sequence; |
3b5386dc TT |
1379 | journal->j_tail = ntohl(jsb->s_start); |
1380 | journal->j_first = ntohl(jsb->s_first); | |
7ed2b5d0 | 1381 | if (jbd2_has_feature_fast_commit(journal)) { |
3cc4f867 | 1382 | if (ntohl(jsb->s_maxlen) - jbd2_journal_get_num_fc_blks(jsb) |
7ed2b5d0 HS |
1383 | < JBD2_MIN_JOURNAL_BLOCKS) { |
1384 | com_err(ctx->program_name, EXT2_ET_CORRUPT_JOURNAL_SB, | |
1385 | _("%s: incorrect fast commit blocks\n"), | |
1386 | ctx->device_name); | |
1387 | return EXT2_ET_CORRUPT_JOURNAL_SB; | |
1388 | } | |
1389 | journal->j_fc_last = ntohl(jsb->s_maxlen); | |
1390 | journal->j_last = journal->j_fc_last - | |
3cc4f867 | 1391 | jbd2_journal_get_num_fc_blks(jsb); |
7ed2b5d0 HS |
1392 | journal->j_fc_first = journal->j_last + 1; |
1393 | } else { | |
1394 | journal->j_last = ntohl(jsb->s_maxlen); | |
1395 | } | |
3b5386dc TT |
1396 | |
1397 | return 0; | |
1398 | } | |
1399 | ||
53ef44c4 | 1400 | static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb, |
6a6d3d44 | 1401 | journal_t *journal) |
3b5386dc | 1402 | { |
0e8a9560 | 1403 | char *p; |
424cd2be TT |
1404 | union { |
1405 | uuid_t uuid; | |
1406 | __u32 val[4]; | |
1407 | } u; | |
1408 | __u32 new_seq = 0; | |
1409 | int i; | |
1410 | ||
0e8a9560 TT |
1411 | /* Leave a valid existing V1 superblock signature alone. |
1412 | * Anything unrecognisable we overwrite with a new V2 | |
1413 | * signature. */ | |
efc6f628 | 1414 | |
8335d3c5 TT |
1415 | if (jsb->s_header.h_magic != htonl(JBD2_MAGIC_NUMBER) || |
1416 | jsb->s_header.h_blocktype != htonl(JBD2_SUPERBLOCK_V1)) { | |
1417 | jsb->s_header.h_magic = htonl(JBD2_MAGIC_NUMBER); | |
1418 | jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2); | |
0e8a9560 TT |
1419 | } |
1420 | ||
1421 | /* Zero out everything else beyond the superblock header */ | |
efc6f628 | 1422 | |
0e8a9560 TT |
1423 | p = ((char *) jsb) + sizeof(journal_header_t); |
1424 | memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t)); | |
1425 | ||
3b5386dc | 1426 | jsb->s_blocksize = htonl(ctx->fs->blocksize); |
7ed2b5d0 | 1427 | jsb->s_maxlen = htonl(journal->j_total_len); |
0e8a9560 | 1428 | jsb->s_first = htonl(1); |
0e8a9560 | 1429 | |
424cd2be TT |
1430 | /* Initialize the journal sequence number so that there is "no" |
1431 | * chance we will find old "valid" transactions in the journal. | |
1432 | * This avoids the need to zero the whole journal (slow to do, | |
1433 | * and risky when we are just recovering the filesystem). | |
1434 | */ | |
1435 | uuid_generate(u.uuid); | |
1436 | for (i = 0; i < 4; i ++) | |
1437 | new_seq ^= u.val[i]; | |
1438 | jsb->s_sequence = htonl(new_seq); | |
b2795949 | 1439 | e2fsck_journal_sb_csum_set(journal, jsb); |
0e8a9560 | 1440 | |
8cf93332 | 1441 | mark_buffer_dirty(journal->j_sb_buffer); |
db113c0e | 1442 | ll_rw_block(REQ_OP_WRITE, 0, 1, &journal->j_sb_buffer); |
3b5386dc TT |
1443 | } |
1444 | ||
6a6d3d44 TT |
1445 | static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, |
1446 | journal_t *journal, | |
1447 | struct problem_context *pctx) | |
3b5386dc | 1448 | { |
5dd8f963 | 1449 | struct ext2_super_block *sb = ctx->fs->super; |
86f3b6cf | 1450 | int recover = ext2fs_has_feature_journal_needs_recovery(ctx->fs->super); |
3b5386dc | 1451 | |
86f3b6cf | 1452 | if (ext2fs_has_feature_journal(sb)) { |
3b5386dc | 1453 | if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) { |
b2f93192 TT |
1454 | e2fsck_journal_reset_super(ctx, journal->j_superblock, |
1455 | journal); | |
3b5386dc TT |
1456 | journal->j_transaction_sequence = 1; |
1457 | e2fsck_clear_recover(ctx, recover); | |
1458 | return 0; | |
1459 | } | |
d37026ea | 1460 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
3b5386dc | 1461 | } else if (e2fsck_journal_fix_bad_inode(ctx, pctx)) |
d37026ea | 1462 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
3b5386dc TT |
1463 | |
1464 | return 0; | |
1465 | } | |
1466 | ||
6a6d3d44 TT |
1467 | static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, |
1468 | int reset, int drop) | |
3b5386dc TT |
1469 | { |
1470 | journal_superblock_t *jsb; | |
1471 | ||
6a6d3d44 TT |
1472 | if (drop) |
1473 | mark_buffer_clean(journal->j_sb_buffer); | |
1474 | else if (!(ctx->options & E2F_OPT_READONLY)) { | |
3b5386dc | 1475 | jsb = journal->j_superblock; |
dad8fc5f | 1476 | jsb->s_sequence = htonl(journal->j_tail_sequence); |
3b5386dc TT |
1477 | if (reset) |
1478 | jsb->s_start = 0; /* this marks the journal as empty */ | |
b2795949 | 1479 | e2fsck_journal_sb_csum_set(journal, jsb); |
8cf93332 | 1480 | mark_buffer_dirty(journal->j_sb_buffer); |
3b5386dc TT |
1481 | } |
1482 | brelse(journal->j_sb_buffer); | |
1483 | ||
6d222f32 TT |
1484 | if (ctx->journal_io) { |
1485 | if (ctx->fs && ctx->fs->io != ctx->journal_io) | |
1486 | io_channel_close(ctx->journal_io); | |
1487 | ctx->journal_io = 0; | |
1488 | } | |
efc6f628 | 1489 | |
d1a2182a | 1490 | #ifndef USE_INODE_IO |
3b5386dc | 1491 | if (journal->j_inode) |
c4e3d3f3 | 1492 | ext2fs_free_mem(&journal->j_inode); |
d1a2182a TT |
1493 | #endif |
1494 | if (journal->j_fs_dev) | |
c4e3d3f3 TT |
1495 | ext2fs_free_mem(&journal->j_fs_dev); |
1496 | ext2fs_free_mem(&journal); | |
3b5386dc TT |
1497 | } |
1498 | ||
9b565759 TT |
1499 | /* |
1500 | * This function makes sure that the superblock fields regarding the | |
1501 | * journal are consistent. | |
1502 | */ | |
974d57d3 | 1503 | errcode_t e2fsck_check_ext3_journal(e2fsck_t ctx) |
3b5386dc | 1504 | { |
5dd8f963 | 1505 | struct ext2_super_block *sb = ctx->fs->super; |
3b5386dc | 1506 | journal_t *journal; |
86f3b6cf | 1507 | int recover = ext2fs_has_feature_journal_needs_recovery(ctx->fs->super); |
3b5386dc | 1508 | struct problem_context pctx; |
d37066a9 | 1509 | problem_t problem; |
d3f35b64 | 1510 | int reset = 0, force_fsck = 0; |
974d57d3 | 1511 | errcode_t retval; |
3b5386dc TT |
1512 | |
1513 | /* If we don't have any journal features, don't do anything more */ | |
86f3b6cf | 1514 | if (!ext2fs_has_feature_journal(sb) && |
5dd8f963 TT |
1515 | !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 && |
1516 | uuid_is_null(sb->s_journal_uuid)) | |
9b565759 | 1517 | return 0; |
3b5386dc TT |
1518 | |
1519 | clear_problem_context(&pctx); | |
5dd8f963 | 1520 | pctx.num = sb->s_journal_inum; |
3b5386dc TT |
1521 | |
1522 | retval = e2fsck_get_journal(ctx, &journal); | |
1523 | if (retval) { | |
f2d5c937 | 1524 | if ((retval == EXT2_ET_BAD_INODE_NUM) || |
6e4fbbeb | 1525 | (retval == EXT2_ET_BAD_BLOCK_NUM) || |
f2d5c937 TT |
1526 | (retval == EXT2_ET_JOURNAL_TOO_SMALL) || |
1527 | (retval == EXT2_ET_NO_JOURNAL)) | |
3b5386dc TT |
1528 | return e2fsck_journal_fix_bad_inode(ctx, &pctx); |
1529 | return retval; | |
1530 | } | |
1531 | ||
1532 | retval = e2fsck_journal_load(journal); | |
1533 | if (retval) { | |
d37026ea | 1534 | if ((retval == EXT2_ET_CORRUPT_JOURNAL_SB) || |
2f686ace TT |
1535 | ((retval == EXT2_ET_UNSUPP_FEATURE) && |
1536 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT, | |
1537 | &pctx))) || | |
1538 | ((retval == EXT2_ET_RO_UNSUPP_FEATURE) && | |
1539 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT, | |
1540 | &pctx))) || | |
c7f23364 TT |
1541 | ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) && |
1542 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx)))) | |
6a6d3d44 TT |
1543 | retval = e2fsck_journal_fix_corrupt_super(ctx, journal, |
1544 | &pctx); | |
1545 | e2fsck_journal_release(ctx, journal, 0, 1); | |
3b5386dc TT |
1546 | return retval; |
1547 | } | |
1548 | ||
1549 | /* | |
1550 | * We want to make the flags consistent here. We will not leave with | |
1551 | * needs_recovery set but has_journal clear. We can't get in a loop | |
1552 | * with -y, -n, or -p, only if a user isn't making up their mind. | |
1553 | */ | |
1554 | no_has_journal: | |
86f3b6cf DW |
1555 | if (!ext2fs_has_feature_journal(sb)) { |
1556 | recover = ext2fs_has_feature_journal_needs_recovery(sb); | |
3b5386dc TT |
1557 | if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) { |
1558 | if (recover && | |
1559 | !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx)) | |
1560 | goto no_has_journal; | |
d3f35b64 TT |
1561 | /* |
1562 | * Need a full fsck if we are releasing a | |
8c2dc521 | 1563 | * journal stored on a reserved inode. |
d3f35b64 TT |
1564 | */ |
1565 | force_fsck = recover || | |
1566 | (sb->s_journal_inum < EXT2_FIRST_INODE(sb)); | |
1567 | /* Clear all of the journal fields */ | |
5dd8f963 | 1568 | sb->s_journal_inum = 0; |
d3f35b64 TT |
1569 | sb->s_journal_dev = 0; |
1570 | memset(sb->s_journal_uuid, 0, | |
1571 | sizeof(sb->s_journal_uuid)); | |
1572 | e2fsck_clear_recover(ctx, force_fsck); | |
3b5386dc | 1573 | } else if (!(ctx->options & E2F_OPT_READONLY)) { |
86f3b6cf | 1574 | ext2fs_set_feature_journal(sb); |
0cfce7f7 | 1575 | ctx->fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
3b5386dc TT |
1576 | ext2fs_mark_super_dirty(ctx->fs); |
1577 | } | |
1578 | } | |
1579 | ||
86f3b6cf DW |
1580 | if (ext2fs_has_feature_journal(sb) && |
1581 | !ext2fs_has_feature_journal_needs_recovery(sb) && | |
3b5386dc | 1582 | journal->j_superblock->s_start != 0) { |
d37066a9 TT |
1583 | /* Print status information */ |
1584 | fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx); | |
1585 | if (ctx->superblock) | |
1586 | problem = PR_0_JOURNAL_RUN_DEFAULT; | |
1587 | else | |
1588 | problem = PR_0_JOURNAL_RUN; | |
1589 | if (fix_problem(ctx, problem, &pctx)) { | |
1590 | ctx->options |= E2F_OPT_FORCE; | |
86f3b6cf | 1591 | ext2fs_set_feature_journal_needs_recovery(sb); |
d37066a9 TT |
1592 | ext2fs_mark_super_dirty(ctx->fs); |
1593 | } else if (fix_problem(ctx, | |
1594 | PR_0_JOURNAL_RESET_JOURNAL, &pctx)) { | |
3b5386dc | 1595 | reset = 1; |
d3f35b64 TT |
1596 | sb->s_state &= ~EXT2_VALID_FS; |
1597 | ext2fs_mark_super_dirty(ctx->fs); | |
1598 | } | |
1599 | /* | |
1600 | * If the user answers no to the above question, we | |
1601 | * ignore the fact that journal apparently has data; | |
1602 | * accidentally replaying over valid data would be far | |
1603 | * worse than skipping a questionable recovery. | |
efc6f628 | 1604 | * |
d3f35b64 TT |
1605 | * XXX should we abort with a fatal error here? What |
1606 | * will the ext3 kernel code do if a filesystem with | |
1607 | * !NEEDS_RECOVERY but with a non-zero | |
1608 | * journal->j_superblock->s_start is mounted? | |
1609 | */ | |
3b5386dc TT |
1610 | } |
1611 | ||
6d75685e TT |
1612 | /* |
1613 | * If we don't need to do replay the journal, check to see if | |
1614 | * the journal's errno is set; if so, we need to mark the file | |
1615 | * system as being corrupt and clear the journal's s_errno. | |
1616 | */ | |
86f3b6cf | 1617 | if (!ext2fs_has_feature_journal_needs_recovery(sb) && |
6d75685e TT |
1618 | journal->j_superblock->s_errno) { |
1619 | ctx->fs->super->s_state |= EXT2_ERROR_FS; | |
1620 | ext2fs_mark_super_dirty(ctx->fs); | |
1621 | journal->j_superblock->s_errno = 0; | |
b2795949 | 1622 | e2fsck_journal_sb_csum_set(journal, journal->j_superblock); |
6d75685e TT |
1623 | mark_buffer_dirty(journal->j_sb_buffer); |
1624 | } | |
1625 | ||
6a6d3d44 | 1626 | e2fsck_journal_release(ctx, journal, reset, 0); |
3b5386dc TT |
1627 | return retval; |
1628 | } | |
1629 | ||
6a6d3d44 | 1630 | static errcode_t recover_ext3_journal(e2fsck_t ctx) |
3b5386dc | 1631 | { |
185c4aea | 1632 | struct problem_context pctx; |
3b5386dc | 1633 | journal_t *journal; |
974d57d3 | 1634 | errcode_t retval; |
3b5386dc | 1635 | |
185c4aea TT |
1636 | clear_problem_context(&pctx); |
1637 | ||
2faf75d2 TT |
1638 | retval = jbd2_journal_init_revoke_record_cache(); |
1639 | if (retval) | |
1640 | return retval; | |
1641 | ||
1642 | retval = jbd2_journal_init_revoke_table_cache(); | |
1643 | if (retval) | |
1644 | return retval; | |
1645 | ||
3b5386dc TT |
1646 | retval = e2fsck_get_journal(ctx, &journal); |
1647 | if (retval) | |
ecf1b776 TT |
1648 | return retval; |
1649 | ||
3b5386dc TT |
1650 | retval = e2fsck_journal_load(journal); |
1651 | if (retval) | |
6a6d3d44 | 1652 | goto errout; |
3b5386dc | 1653 | |
8335d3c5 | 1654 | retval = jbd2_journal_init_revoke(journal, 1024); |
0e8a9560 | 1655 | if (retval) |
6a6d3d44 | 1656 | goto errout; |
efc6f628 | 1657 | |
8335d3c5 | 1658 | retval = -jbd2_journal_recover(journal); |
c0a083fa TT |
1659 | if (retval) |
1660 | goto errout; | |
efc6f628 | 1661 | |
185c4aea TT |
1662 | if (journal->j_failed_commit) { |
1663 | pctx.ino = journal->j_failed_commit; | |
1664 | fix_problem(ctx, PR_0_JNL_TXN_CORRUPT, &pctx); | |
63b3913d | 1665 | journal->j_superblock->s_errno = -EINVAL; |
8cf93332 | 1666 | mark_buffer_dirty(journal->j_sb_buffer); |
c0a083fa | 1667 | } |
efc6f628 | 1668 | |
32448f50 DJ |
1669 | journal->j_tail_sequence = journal->j_transaction_sequence; |
1670 | ||
6a6d3d44 | 1671 | errout: |
8335d3c5 | 1672 | jbd2_journal_destroy_revoke(journal); |
2faf75d2 TT |
1673 | jbd2_journal_destroy_revoke_record_cache(); |
1674 | jbd2_journal_destroy_revoke_table_cache(); | |
6a6d3d44 | 1675 | e2fsck_journal_release(ctx, journal, 1, 0); |
3b5386dc TT |
1676 | return retval; |
1677 | } | |
1678 | ||
974d57d3 | 1679 | errcode_t e2fsck_run_ext3_journal(e2fsck_t ctx) |
80bfaa3e TT |
1680 | { |
1681 | io_manager io_ptr = ctx->fs->io->manager; | |
1682 | int blocksize = ctx->fs->blocksize; | |
ecf1b776 | 1683 | errcode_t retval, recover_retval; |
27a407df TT |
1684 | io_stats stats = 0; |
1685 | unsigned long long kbytes_written = 0; | |
6ab579ee | 1686 | __u16 s_error_state; |
8394902e TT |
1687 | |
1688 | printf(_("%s: recovering journal\n"), ctx->device_name); | |
ecf1b776 | 1689 | if (ctx->options & E2F_OPT_READONLY) { |
8394902e | 1690 | printf(_("%s: won't do journal recovery while read-only\n"), |
ecf1b776 TT |
1691 | ctx->device_name); |
1692 | return EXT2_ET_FILE_RO; | |
1693 | } | |
1694 | ||
0f2cfe25 | 1695 | if (ctx->fs->flags & EXT2_FLAG_DIRTY) |
3c6b8977 | 1696 | ext2fs_flush(ctx->fs); /* Force out any modifications */ |
d0515212 | 1697 | |
ecf1b776 | 1698 | recover_retval = recover_ext3_journal(ctx); |
efc6f628 | 1699 | |
80bfaa3e TT |
1700 | /* |
1701 | * Reload the filesystem context to get up-to-date data from disk | |
1702 | * because journal recovery will change the filesystem under us. | |
1703 | */ | |
27a407df TT |
1704 | if (ctx->fs->super->s_kbytes_written && |
1705 | ctx->fs->io->manager->get_stats) | |
1706 | ctx->fs->io->manager->get_stats(ctx->fs->io, &stats); | |
1707 | if (stats && stats->bytes_written) | |
1708 | kbytes_written = stats->bytes_written >> 10; | |
6ab579ee | 1709 | s_error_state = ctx->fs->super->s_state & EXT2_ERROR_FS; |
0f5eba75 AD |
1710 | |
1711 | ext2fs_mmp_stop(ctx->fs); | |
27a407df | 1712 | ext2fs_free(ctx->fs); |
0d19ccbd | 1713 | retval = ext2fs_open(ctx->filesystem_name, ctx->openfs_flags, |
80bfaa3e TT |
1714 | ctx->superblock, blocksize, io_ptr, |
1715 | &ctx->fs); | |
80bfaa3e TT |
1716 | if (retval) { |
1717 | com_err(ctx->program_name, retval, | |
1718 | _("while trying to re-open %s"), | |
1719 | ctx->device_name); | |
99a2cc96 | 1720 | fatal_error(ctx, 0); |
80bfaa3e TT |
1721 | } |
1722 | ctx->fs->priv_data = ctx; | |
8dceb924 | 1723 | ctx->fs->now = ctx->now; |
058ad1c7 | 1724 | ctx->fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; |
27a407df | 1725 | ctx->fs->super->s_kbytes_written += kbytes_written; |
6ab579ee | 1726 | ctx->fs->super->s_state |= s_error_state; |
17390c04 | 1727 | |
ecf1b776 | 1728 | /* Set the superblock flags */ |
974d57d3 | 1729 | e2fsck_clear_recover(ctx, recover_retval != 0); |
63b3913d TT |
1730 | |
1731 | /* | |
1732 | * Do one last sanity check, and propagate journal->s_errno to | |
1733 | * the EXT2_ERROR_FS flag in the fs superblock if needed. | |
1734 | */ | |
1735 | retval = e2fsck_check_ext3_journal(ctx); | |
1736 | return retval ? retval : recover_retval; | |
17390c04 | 1737 | } |
773fd8a1 TT |
1738 | |
1739 | /* | |
1740 | * This function will move the journal inode from a visible file in | |
1741 | * the filesystem directory hierarchy to the reserved inode if necessary. | |
1742 | */ | |
546a1ff1 | 1743 | static const char * const journal_names[] = { |
773fd8a1 TT |
1744 | ".journal", "journal", ".journal.dat", "journal.dat", 0 }; |
1745 | ||
1746 | void e2fsck_move_ext3_journal(e2fsck_t ctx) | |
1747 | { | |
1748 | struct ext2_super_block *sb = ctx->fs->super; | |
1749 | struct problem_context pctx; | |
1750 | struct ext2_inode inode; | |
1751 | ext2_filsys fs = ctx->fs; | |
1752 | ext2_ino_t ino; | |
1753 | errcode_t retval; | |
1754 | const char * const * cpp; | |
3971bfe8 TT |
1755 | dgrp_t group; |
1756 | int mount_flags; | |
efc6f628 | 1757 | |
a435ec34 TT |
1758 | clear_problem_context(&pctx); |
1759 | ||
773fd8a1 TT |
1760 | /* |
1761 | * If the filesystem is opened read-only, or there is no | |
a435ec34 | 1762 | * journal, then do nothing. |
773fd8a1 TT |
1763 | */ |
1764 | if ((ctx->options & E2F_OPT_READONLY) || | |
1765 | (sb->s_journal_inum == 0) || | |
86f3b6cf | 1766 | !ext2fs_has_feature_journal(sb)) |
773fd8a1 TT |
1767 | return; |
1768 | ||
a435ec34 TT |
1769 | /* |
1770 | * Read in the journal inode | |
1771 | */ | |
1772 | if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0) | |
1773 | return; | |
1774 | ||
1775 | /* | |
1776 | * If it's necessary to backup the journal inode, do so. | |
1777 | */ | |
1778 | if ((sb->s_jnl_backup_type == 0) || | |
1779 | ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) && | |
1780 | memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) { | |
1781 | if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) { | |
1782 | memcpy(sb->s_jnl_blocks, inode.i_block, | |
1783 | EXT2_N_BLOCKS*4); | |
931b58e1 | 1784 | sb->s_jnl_blocks[15] = inode.i_size_high; |
a435ec34 TT |
1785 | sb->s_jnl_blocks[16] = inode.i_size; |
1786 | sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; | |
1787 | ext2fs_mark_super_dirty(fs); | |
27479eb2 | 1788 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
a435ec34 TT |
1789 | } |
1790 | } | |
1791 | ||
1792 | /* | |
1793 | * If the journal is already the hidden inode, then do nothing | |
1794 | */ | |
1795 | if (sb->s_journal_inum == EXT2_JOURNAL_INO) | |
1796 | return; | |
efc6f628 | 1797 | |
a435ec34 TT |
1798 | /* |
1799 | * The journal inode had better have only one link and not be readable. | |
1800 | */ | |
1801 | if (inode.i_links_count != 1) | |
1802 | return; | |
1803 | ||
773fd8a1 TT |
1804 | /* |
1805 | * If the filesystem is mounted, or we can't tell whether | |
1806 | * or not it's mounted, do nothing. | |
1807 | */ | |
1808 | retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); | |
1809 | if (retval || (mount_flags & EXT2_MF_MOUNTED)) | |
1810 | return; | |
1811 | ||
1812 | /* | |
1813 | * If we can't find the name of the journal inode, then do | |
1814 | * nothing. | |
1815 | */ | |
1816 | for (cpp = journal_names; *cpp; cpp++) { | |
1817 | retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp, | |
1818 | strlen(*cpp), 0, &ino); | |
1819 | if ((retval == 0) && (ino == sb->s_journal_inum)) | |
1820 | break; | |
1821 | } | |
1822 | if (*cpp == 0) | |
1823 | return; | |
1824 | ||
773fd8a1 TT |
1825 | /* We need the inode bitmap to be loaded */ |
1826 | retval = ext2fs_read_bitmaps(fs); | |
1827 | if (retval) | |
1828 | return; | |
1829 | ||
773fd8a1 TT |
1830 | pctx.str = *cpp; |
1831 | if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx)) | |
1832 | return; | |
efc6f628 | 1833 | |
773fd8a1 TT |
1834 | /* |
1835 | * OK, we've done all the checks, let's actually move the | |
1836 | * journal inode. Errors at this point mean we need to force | |
1837 | * an ext2 filesystem check. | |
1838 | */ | |
1839 | if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0) | |
1840 | goto err_out; | |
1841 | if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0) | |
1842 | goto err_out; | |
1843 | sb->s_journal_inum = EXT2_JOURNAL_INO; | |
1844 | ext2fs_mark_super_dirty(fs); | |
27479eb2 | 1845 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
773fd8a1 | 1846 | inode.i_links_count = 0; |
f590d714 | 1847 | ext2fs_set_dtime(fs, &inode); |
773fd8a1 TT |
1848 | if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) |
1849 | goto err_out; | |
1850 | ||
1851 | group = ext2fs_group_of_ino(fs, ino); | |
c5d2f50d | 1852 | ext2fs_unmark_inode_bitmap2(fs->inode_map, ino); |
773fd8a1 | 1853 | ext2fs_mark_ib_dirty(fs); |
d7cca6b0 | 1854 | ext2fs_bg_free_inodes_count_set(fs, group, ext2fs_bg_free_inodes_count(fs, group) + 1); |
49a7360b | 1855 | ext2fs_group_desc_csum_set(fs, group); |
773fd8a1 TT |
1856 | fs->super->s_free_inodes_count++; |
1857 | return; | |
1858 | ||
1859 | err_out: | |
1860 | pctx.errcode = retval; | |
1861 | fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx); | |
1862 | fs->super->s_state &= ~EXT2_VALID_FS; | |
1863 | ext2fs_mark_super_dirty(fs); | |
1864 | return; | |
1865 | } | |
1866 | ||
b1c52b26 TT |
1867 | /* |
1868 | * This function makes sure the superblock hint for the external | |
1869 | * journal is correct. | |
1870 | */ | |
1871 | int e2fsck_fix_ext3_journal_hint(e2fsck_t ctx) | |
1872 | { | |
1873 | struct ext2_super_block *sb = ctx->fs->super; | |
1874 | struct problem_context pctx; | |
1875 | char uuid[37], *journal_name; | |
1876 | struct stat st; | |
b1c52b26 | 1877 | |
86f3b6cf | 1878 | if (!ext2fs_has_feature_journal(sb) || |
b1c52b26 TT |
1879 | uuid_is_null(sb->s_journal_uuid)) |
1880 | return 0; | |
1881 | ||
1882 | uuid_unparse(sb->s_journal_uuid, uuid); | |
1883 | journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); | |
1884 | if (!journal_name) | |
1885 | return 0; | |
1886 | ||
f0131bdc DW |
1887 | if (stat(journal_name, &st) < 0) { |
1888 | free(journal_name); | |
b1c52b26 | 1889 | return 0; |
f0131bdc | 1890 | } |
b1c52b26 TT |
1891 | |
1892 | if (st.st_rdev != sb->s_journal_dev) { | |
1893 | clear_problem_context(&pctx); | |
1894 | pctx.num = st.st_rdev; | |
1895 | if (fix_problem(ctx, PR_0_EXTERNAL_JOURNAL_HINT, &pctx)) { | |
1896 | sb->s_journal_dev = st.st_rdev; | |
1897 | ext2fs_mark_super_dirty(ctx->fs); | |
1898 | } | |
1899 | } | |
1900 | ||
1901 | free(journal_name); | |
1902 | return 0; | |
1903 | } |