diff options
Diffstat (limited to 'e2fsprogs/e2fsck/journal.c')
-rw-r--r-- | e2fsprogs/e2fsck/journal.c | 960 |
1 files changed, 960 insertions, 0 deletions
diff --git a/e2fsprogs/e2fsck/journal.c b/e2fsprogs/e2fsck/journal.c new file mode 100644 index 000000000..14e774d50 --- /dev/null +++ b/e2fsprogs/e2fsck/journal.c | |||
@@ -0,0 +1,960 @@ | |||
1 | /* | ||
2 | * journal.c --- code for handling the "ext3" journal | ||
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. | ||
13 | */ | ||
14 | |||
15 | #ifdef HAVE_SYS_MOUNT_H | ||
16 | #include <sys/param.h> | ||
17 | #include <sys/mount.h> | ||
18 | #define MNT_FL (MS_MGC_VAL | MS_RDONLY) | ||
19 | #endif | ||
20 | #ifdef HAVE_SYS_STAT_H | ||
21 | #include <sys/stat.h> | ||
22 | #endif | ||
23 | |||
24 | #define E2FSCK_INCLUDE_INLINE_FUNCS | ||
25 | #include "jfs_user.h" | ||
26 | #include "problem.h" | ||
27 | #include "uuid/uuid.h" | ||
28 | |||
29 | #ifdef __CONFIG_JBD_DEBUG__E2FS /* Enabled by configure --enable-jfs-debug */ | ||
30 | static int bh_count = 0; | ||
31 | #endif | ||
32 | |||
33 | /* | ||
34 | * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths. | ||
35 | * This creates a larger static binary, and a smaller binary using | ||
36 | * shared libraries. It's also probably slightly less CPU-efficient, | ||
37 | * which is why it's not on by default. But, it's a good way of | ||
38 | * testing the functions in inode_io.c and fileio.c. | ||
39 | */ | ||
40 | #undef USE_INODE_IO | ||
41 | |||
42 | /* Kernel compatibility functions for handling the journal. These allow us | ||
43 | * to use the recovery.c file virtually unchanged from the kernel, so we | ||
44 | * don't have to do much to keep kernel and user recovery in sync. | ||
45 | */ | ||
46 | int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys) | ||
47 | { | ||
48 | #ifdef USE_INODE_IO | ||
49 | *phys = block; | ||
50 | return 0; | ||
51 | #else | ||
52 | struct inode *inode = journal->j_inode; | ||
53 | errcode_t retval; | ||
54 | blk_t pblk; | ||
55 | |||
56 | if (!inode) { | ||
57 | *phys = block; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino, | ||
62 | &inode->i_ext2, NULL, 0, block, &pblk); | ||
63 | *phys = pblk; | ||
64 | return (retval); | ||
65 | #endif | ||
66 | } | ||
67 | |||
68 | struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize) | ||
69 | { | ||
70 | struct buffer_head *bh; | ||
71 | |||
72 | bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer"); | ||
73 | if (!bh) | ||
74 | return NULL; | ||
75 | |||
76 | jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n", | ||
77 | (unsigned long) blocknr, blocksize, ++bh_count); | ||
78 | |||
79 | bh->b_ctx = kdev->k_ctx; | ||
80 | if (kdev->k_dev == K_DEV_FS) | ||
81 | bh->b_io = kdev->k_ctx->fs->io; | ||
82 | else | ||
83 | bh->b_io = kdev->k_ctx->journal_io; | ||
84 | bh->b_size = blocksize; | ||
85 | bh->b_blocknr = blocknr; | ||
86 | |||
87 | return bh; | ||
88 | } | ||
89 | |||
90 | void sync_blockdev(kdev_t kdev) | ||
91 | { | ||
92 | io_channel io; | ||
93 | |||
94 | if (kdev->k_dev == K_DEV_FS) | ||
95 | io = kdev->k_ctx->fs->io; | ||
96 | else | ||
97 | io = kdev->k_ctx->journal_io; | ||
98 | |||
99 | io_channel_flush(io); | ||
100 | } | ||
101 | |||
102 | void ll_rw_block(int rw, int nr, struct buffer_head *bhp[]) | ||
103 | { | ||
104 | int retval; | ||
105 | struct buffer_head *bh; | ||
106 | |||
107 | for (; nr > 0; --nr) { | ||
108 | bh = *bhp++; | ||
109 | if (rw == READ && !bh->b_uptodate) { | ||
110 | jfs_debug(3, "reading block %lu/%p\n", | ||
111 | (unsigned long) bh->b_blocknr, (void *) bh); | ||
112 | retval = io_channel_read_blk(bh->b_io, | ||
113 | bh->b_blocknr, | ||
114 | 1, bh->b_data); | ||
115 | if (retval) { | ||
116 | com_err(bh->b_ctx->device_name, retval, | ||
117 | "while reading block %lu\n", | ||
118 | (unsigned long) bh->b_blocknr); | ||
119 | bh->b_err = retval; | ||
120 | continue; | ||
121 | } | ||
122 | bh->b_uptodate = 1; | ||
123 | } else if (rw == WRITE && bh->b_dirty) { | ||
124 | jfs_debug(3, "writing block %lu/%p\n", | ||
125 | (unsigned long) bh->b_blocknr, (void *) bh); | ||
126 | retval = io_channel_write_blk(bh->b_io, | ||
127 | bh->b_blocknr, | ||
128 | 1, bh->b_data); | ||
129 | if (retval) { | ||
130 | com_err(bh->b_ctx->device_name, retval, | ||
131 | "while writing block %lu\n", | ||
132 | (unsigned long) bh->b_blocknr); | ||
133 | bh->b_err = retval; | ||
134 | continue; | ||
135 | } | ||
136 | bh->b_dirty = 0; | ||
137 | bh->b_uptodate = 1; | ||
138 | } else { | ||
139 | jfs_debug(3, "no-op %s for block %lu\n", | ||
140 | rw == READ ? "read" : "write", | ||
141 | (unsigned long) bh->b_blocknr); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | void mark_buffer_dirty(struct buffer_head *bh) | ||
147 | { | ||
148 | bh->b_dirty = 1; | ||
149 | } | ||
150 | |||
151 | static void mark_buffer_clean(struct buffer_head * bh) | ||
152 | { | ||
153 | bh->b_dirty = 0; | ||
154 | } | ||
155 | |||
156 | void brelse(struct buffer_head *bh) | ||
157 | { | ||
158 | if (bh->b_dirty) | ||
159 | ll_rw_block(WRITE, 1, &bh); | ||
160 | jfs_debug(3, "freeing block %lu/%p (total %d)\n", | ||
161 | (unsigned long) bh->b_blocknr, (void *) bh, --bh_count); | ||
162 | ext2fs_free_mem(&bh); | ||
163 | } | ||
164 | |||
165 | int buffer_uptodate(struct buffer_head *bh) | ||
166 | { | ||
167 | return bh->b_uptodate; | ||
168 | } | ||
169 | |||
170 | void mark_buffer_uptodate(struct buffer_head *bh, int val) | ||
171 | { | ||
172 | bh->b_uptodate = val; | ||
173 | } | ||
174 | |||
175 | void wait_on_buffer(struct buffer_head *bh) | ||
176 | { | ||
177 | if (!bh->b_uptodate) | ||
178 | ll_rw_block(READ, 1, &bh); | ||
179 | } | ||
180 | |||
181 | |||
182 | static void e2fsck_clear_recover(e2fsck_t ctx, int error) | ||
183 | { | ||
184 | ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; | ||
185 | |||
186 | /* if we had an error doing journal recovery, we need a full fsck */ | ||
187 | if (error) | ||
188 | ctx->fs->super->s_state &= ~EXT2_VALID_FS; | ||
189 | ext2fs_mark_super_dirty(ctx->fs); | ||
190 | } | ||
191 | |||
192 | static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) | ||
193 | { | ||
194 | struct ext2_super_block *sb = ctx->fs->super; | ||
195 | struct ext2_super_block jsuper; | ||
196 | struct problem_context pctx; | ||
197 | struct buffer_head *bh; | ||
198 | struct inode *j_inode = NULL; | ||
199 | struct kdev_s *dev_fs = NULL, *dev_journal; | ||
200 | const char *journal_name = 0; | ||
201 | journal_t *journal = NULL; | ||
202 | errcode_t retval = 0; | ||
203 | io_manager io_ptr = 0; | ||
204 | unsigned long start = 0; | ||
205 | blk_t blk; | ||
206 | int ext_journal = 0; | ||
207 | int tried_backup_jnl = 0; | ||
208 | int i; | ||
209 | |||
210 | clear_problem_context(&pctx); | ||
211 | |||
212 | journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); | ||
213 | if (!journal) { | ||
214 | return EXT2_ET_NO_MEMORY; | ||
215 | } | ||
216 | |||
217 | dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); | ||
218 | if (!dev_fs) { | ||
219 | retval = EXT2_ET_NO_MEMORY; | ||
220 | goto errout; | ||
221 | } | ||
222 | dev_journal = dev_fs+1; | ||
223 | |||
224 | dev_fs->k_ctx = dev_journal->k_ctx = ctx; | ||
225 | dev_fs->k_dev = K_DEV_FS; | ||
226 | dev_journal->k_dev = K_DEV_JOURNAL; | ||
227 | |||
228 | journal->j_dev = dev_journal; | ||
229 | journal->j_fs_dev = dev_fs; | ||
230 | journal->j_inode = NULL; | ||
231 | journal->j_blocksize = ctx->fs->blocksize; | ||
232 | |||
233 | if (uuid_is_null(sb->s_journal_uuid)) { | ||
234 | if (!sb->s_journal_inum) | ||
235 | return EXT2_ET_BAD_INODE_NUM; | ||
236 | j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), | ||
237 | "journal inode"); | ||
238 | if (!j_inode) { | ||
239 | retval = EXT2_ET_NO_MEMORY; | ||
240 | goto errout; | ||
241 | } | ||
242 | |||
243 | j_inode->i_ctx = ctx; | ||
244 | j_inode->i_ino = sb->s_journal_inum; | ||
245 | |||
246 | if ((retval = ext2fs_read_inode(ctx->fs, | ||
247 | sb->s_journal_inum, | ||
248 | &j_inode->i_ext2))) { | ||
249 | try_backup_journal: | ||
250 | if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || | ||
251 | tried_backup_jnl) | ||
252 | goto errout; | ||
253 | memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); | ||
254 | memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, | ||
255 | EXT2_N_BLOCKS*4); | ||
256 | j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; | ||
257 | j_inode->i_ext2.i_links_count = 1; | ||
258 | j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; | ||
259 | tried_backup_jnl++; | ||
260 | } | ||
261 | if (!j_inode->i_ext2.i_links_count || | ||
262 | !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { | ||
263 | retval = EXT2_ET_NO_JOURNAL; | ||
264 | goto try_backup_journal; | ||
265 | } | ||
266 | if (j_inode->i_ext2.i_size / journal->j_blocksize < | ||
267 | JFS_MIN_JOURNAL_BLOCKS) { | ||
268 | retval = EXT2_ET_JOURNAL_TOO_SMALL; | ||
269 | goto try_backup_journal; | ||
270 | } | ||
271 | for (i=0; i < EXT2_N_BLOCKS; i++) { | ||
272 | blk = j_inode->i_ext2.i_block[i]; | ||
273 | if (!blk) { | ||
274 | if (i < EXT2_NDIR_BLOCKS) { | ||
275 | retval = EXT2_ET_JOURNAL_TOO_SMALL; | ||
276 | goto try_backup_journal; | ||
277 | } | ||
278 | continue; | ||
279 | } | ||
280 | if (blk < sb->s_first_data_block || | ||
281 | blk >= sb->s_blocks_count) { | ||
282 | retval = EXT2_ET_BAD_BLOCK_NUM; | ||
283 | goto try_backup_journal; | ||
284 | } | ||
285 | } | ||
286 | journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize; | ||
287 | |||
288 | #ifdef USE_INODE_IO | ||
289 | retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, | ||
290 | &j_inode->i_ext2, | ||
291 | &journal_name); | ||
292 | if (retval) | ||
293 | goto errout; | ||
294 | |||
295 | io_ptr = inode_io_manager; | ||
296 | #else | ||
297 | journal->j_inode = j_inode; | ||
298 | ctx->journal_io = ctx->fs->io; | ||
299 | if ((retval = journal_bmap(journal, 0, &start)) != 0) | ||
300 | goto errout; | ||
301 | #endif | ||
302 | } else { | ||
303 | ext_journal = 1; | ||
304 | if (!ctx->journal_name) { | ||
305 | char uuid[37]; | ||
306 | |||
307 | uuid_unparse(sb->s_journal_uuid, uuid); | ||
308 | ctx->journal_name = blkid_get_devname(ctx->blkid, | ||
309 | "UUID", uuid); | ||
310 | if (!ctx->journal_name) | ||
311 | ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); | ||
312 | } | ||
313 | journal_name = ctx->journal_name; | ||
314 | |||
315 | if (!journal_name) { | ||
316 | fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); | ||
317 | return EXT2_ET_LOAD_EXT_JOURNAL; | ||
318 | } | ||
319 | |||
320 | jfs_debug(1, "Using journal file %s\n", journal_name); | ||
321 | io_ptr = unix_io_manager; | ||
322 | } | ||
323 | |||
324 | #if 0 | ||
325 | test_io_backing_manager = io_ptr; | ||
326 | io_ptr = test_io_manager; | ||
327 | #endif | ||
328 | #ifndef USE_INODE_IO | ||
329 | if (ext_journal) | ||
330 | #endif | ||
331 | retval = io_ptr->open(journal_name, IO_FLAG_RW, | ||
332 | &ctx->journal_io); | ||
333 | if (retval) | ||
334 | goto errout; | ||
335 | |||
336 | io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); | ||
337 | |||
338 | if (ext_journal) { | ||
339 | if (ctx->fs->blocksize == 1024) | ||
340 | start = 1; | ||
341 | bh = getblk(dev_journal, start, ctx->fs->blocksize); | ||
342 | if (!bh) { | ||
343 | retval = EXT2_ET_NO_MEMORY; | ||
344 | goto errout; | ||
345 | } | ||
346 | ll_rw_block(READ, 1, &bh); | ||
347 | if ((retval = bh->b_err) != 0) | ||
348 | goto errout; | ||
349 | memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024, | ||
350 | sizeof(jsuper)); | ||
351 | brelse(bh); | ||
352 | #ifdef EXT2FS_ENABLE_SWAPFS | ||
353 | if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) | ||
354 | ext2fs_swap_super(&jsuper); | ||
355 | #endif | ||
356 | if (jsuper.s_magic != EXT2_SUPER_MAGIC || | ||
357 | !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { | ||
358 | fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); | ||
359 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | ||
360 | goto errout; | ||
361 | } | ||
362 | /* Make sure the journal UUID is correct */ | ||
363 | if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, | ||
364 | sizeof(jsuper.s_uuid))) { | ||
365 | fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); | ||
366 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | ||
367 | goto errout; | ||
368 | } | ||
369 | |||
370 | journal->j_maxlen = jsuper.s_blocks_count; | ||
371 | start++; | ||
372 | } | ||
373 | |||
374 | if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { | ||
375 | retval = EXT2_ET_NO_MEMORY; | ||
376 | goto errout; | ||
377 | } | ||
378 | |||
379 | journal->j_sb_buffer = bh; | ||
380 | journal->j_superblock = (journal_superblock_t *)bh->b_data; | ||
381 | |||
382 | #ifdef USE_INODE_IO | ||
383 | if (j_inode) | ||
384 | ext2fs_free_mem(&j_inode); | ||
385 | #endif | ||
386 | |||
387 | *ret_journal = journal; | ||
388 | return 0; | ||
389 | |||
390 | errout: | ||
391 | if (dev_fs) | ||
392 | ext2fs_free_mem(&dev_fs); | ||
393 | if (j_inode) | ||
394 | ext2fs_free_mem(&j_inode); | ||
395 | if (journal) | ||
396 | ext2fs_free_mem(&journal); | ||
397 | return retval; | ||
398 | |||
399 | } | ||
400 | |||
401 | static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx, | ||
402 | struct problem_context *pctx) | ||
403 | { | ||
404 | struct ext2_super_block *sb = ctx->fs->super; | ||
405 | int recover = ctx->fs->super->s_feature_incompat & | ||
406 | EXT3_FEATURE_INCOMPAT_RECOVER; | ||
407 | int has_journal = ctx->fs->super->s_feature_compat & | ||
408 | EXT3_FEATURE_COMPAT_HAS_JOURNAL; | ||
409 | |||
410 | if (has_journal || sb->s_journal_inum) { | ||
411 | /* The journal inode is bogus, remove and force full fsck */ | ||
412 | pctx->ino = sb->s_journal_inum; | ||
413 | if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) { | ||
414 | if (has_journal && sb->s_journal_inum) | ||
415 | printf("*** ext3 journal has been deleted - " | ||
416 | "filesystem is now ext2 only ***\n\n"); | ||
417 | sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; | ||
418 | sb->s_journal_inum = 0; | ||
419 | ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */ | ||
420 | e2fsck_clear_recover(ctx, 1); | ||
421 | return 0; | ||
422 | } | ||
423 | return EXT2_ET_BAD_INODE_NUM; | ||
424 | } else if (recover) { | ||
425 | if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) { | ||
426 | e2fsck_clear_recover(ctx, 1); | ||
427 | return 0; | ||
428 | } | ||
429 | return EXT2_ET_UNSUPP_FEATURE; | ||
430 | } | ||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | #define V1_SB_SIZE 0x0024 | ||
435 | static void clear_v2_journal_fields(journal_t *journal) | ||
436 | { | ||
437 | e2fsck_t ctx = journal->j_dev->k_ctx; | ||
438 | struct problem_context pctx; | ||
439 | |||
440 | clear_problem_context(&pctx); | ||
441 | |||
442 | if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx)) | ||
443 | return; | ||
444 | |||
445 | memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, | ||
446 | ctx->fs->blocksize-V1_SB_SIZE); | ||
447 | mark_buffer_dirty(journal->j_sb_buffer); | ||
448 | } | ||
449 | |||
450 | |||
451 | static errcode_t e2fsck_journal_load(journal_t *journal) | ||
452 | { | ||
453 | e2fsck_t ctx = journal->j_dev->k_ctx; | ||
454 | journal_superblock_t *jsb; | ||
455 | struct buffer_head *jbh = journal->j_sb_buffer; | ||
456 | struct problem_context pctx; | ||
457 | |||
458 | clear_problem_context(&pctx); | ||
459 | |||
460 | ll_rw_block(READ, 1, &jbh); | ||
461 | if (jbh->b_err) { | ||
462 | com_err(ctx->device_name, jbh->b_err, | ||
463 | _("reading journal superblock\n")); | ||
464 | return jbh->b_err; | ||
465 | } | ||
466 | |||
467 | jsb = journal->j_superblock; | ||
468 | /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ | ||
469 | if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) | ||
470 | return e2fsck_journal_fix_bad_inode(ctx, &pctx); | ||
471 | |||
472 | switch (ntohl(jsb->s_header.h_blocktype)) { | ||
473 | case JFS_SUPERBLOCK_V1: | ||
474 | journal->j_format_version = 1; | ||
475 | if (jsb->s_feature_compat || | ||
476 | jsb->s_feature_incompat || | ||
477 | jsb->s_feature_ro_compat || | ||
478 | jsb->s_nr_users) | ||
479 | clear_v2_journal_fields(journal); | ||
480 | break; | ||
481 | |||
482 | case JFS_SUPERBLOCK_V2: | ||
483 | journal->j_format_version = 2; | ||
484 | if (ntohl(jsb->s_nr_users) > 1 && | ||
485 | uuid_is_null(ctx->fs->super->s_journal_uuid)) | ||
486 | clear_v2_journal_fields(journal); | ||
487 | if (ntohl(jsb->s_nr_users) > 1) { | ||
488 | fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx); | ||
489 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; | ||
490 | } | ||
491 | break; | ||
492 | |||
493 | /* | ||
494 | * These should never appear in a journal super block, so if | ||
495 | * they do, the journal is badly corrupted. | ||
496 | */ | ||
497 | case JFS_DESCRIPTOR_BLOCK: | ||
498 | case JFS_COMMIT_BLOCK: | ||
499 | case JFS_REVOKE_BLOCK: | ||
500 | return EXT2_ET_CORRUPT_SUPERBLOCK; | ||
501 | |||
502 | /* If we don't understand the superblock major type, but there | ||
503 | * is a magic number, then it is likely to be a new format we | ||
504 | * just don't understand, so leave it alone. */ | ||
505 | default: | ||
506 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; | ||
507 | } | ||
508 | |||
509 | if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) | ||
510 | return EXT2_ET_UNSUPP_FEATURE; | ||
511 | |||
512 | if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) | ||
513 | return EXT2_ET_RO_UNSUPP_FEATURE; | ||
514 | |||
515 | /* We have now checked whether we know enough about the journal | ||
516 | * format to be able to proceed safely, so any other checks that | ||
517 | * fail we should attempt to recover from. */ | ||
518 | if (jsb->s_blocksize != htonl(journal->j_blocksize)) { | ||
519 | com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK, | ||
520 | _("%s: no valid journal superblock found\n"), | ||
521 | ctx->device_name); | ||
522 | return EXT2_ET_CORRUPT_SUPERBLOCK; | ||
523 | } | ||
524 | |||
525 | if (ntohl(jsb->s_maxlen) < journal->j_maxlen) | ||
526 | journal->j_maxlen = ntohl(jsb->s_maxlen); | ||
527 | else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) { | ||
528 | com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK, | ||
529 | _("%s: journal too short\n"), | ||
530 | ctx->device_name); | ||
531 | return EXT2_ET_CORRUPT_SUPERBLOCK; | ||
532 | } | ||
533 | |||
534 | journal->j_tail_sequence = ntohl(jsb->s_sequence); | ||
535 | journal->j_transaction_sequence = journal->j_tail_sequence; | ||
536 | journal->j_tail = ntohl(jsb->s_start); | ||
537 | journal->j_first = ntohl(jsb->s_first); | ||
538 | journal->j_last = ntohl(jsb->s_maxlen); | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb, | ||
544 | journal_t *journal) | ||
545 | { | ||
546 | char *p; | ||
547 | union { | ||
548 | uuid_t uuid; | ||
549 | __u32 val[4]; | ||
550 | } u; | ||
551 | __u32 new_seq = 0; | ||
552 | int i; | ||
553 | |||
554 | /* Leave a valid existing V1 superblock signature alone. | ||
555 | * Anything unrecognisable we overwrite with a new V2 | ||
556 | * signature. */ | ||
557 | |||
558 | if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) || | ||
559 | jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) { | ||
560 | jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); | ||
561 | jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); | ||
562 | } | ||
563 | |||
564 | /* Zero out everything else beyond the superblock header */ | ||
565 | |||
566 | p = ((char *) jsb) + sizeof(journal_header_t); | ||
567 | memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t)); | ||
568 | |||
569 | jsb->s_blocksize = htonl(ctx->fs->blocksize); | ||
570 | jsb->s_maxlen = htonl(journal->j_maxlen); | ||
571 | jsb->s_first = htonl(1); | ||
572 | |||
573 | /* Initialize the journal sequence number so that there is "no" | ||
574 | * chance we will find old "valid" transactions in the journal. | ||
575 | * This avoids the need to zero the whole journal (slow to do, | ||
576 | * and risky when we are just recovering the filesystem). | ||
577 | */ | ||
578 | uuid_generate(u.uuid); | ||
579 | for (i = 0; i < 4; i ++) | ||
580 | new_seq ^= u.val[i]; | ||
581 | jsb->s_sequence = htonl(new_seq); | ||
582 | |||
583 | mark_buffer_dirty(journal->j_sb_buffer); | ||
584 | ll_rw_block(WRITE, 1, &journal->j_sb_buffer); | ||
585 | } | ||
586 | |||
587 | static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx, | ||
588 | journal_t *journal, | ||
589 | struct problem_context *pctx) | ||
590 | { | ||
591 | struct ext2_super_block *sb = ctx->fs->super; | ||
592 | int recover = ctx->fs->super->s_feature_incompat & | ||
593 | EXT3_FEATURE_INCOMPAT_RECOVER; | ||
594 | |||
595 | if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) { | ||
596 | if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) { | ||
597 | e2fsck_journal_reset_super(ctx, journal->j_superblock, | ||
598 | journal); | ||
599 | journal->j_transaction_sequence = 1; | ||
600 | e2fsck_clear_recover(ctx, recover); | ||
601 | return 0; | ||
602 | } | ||
603 | return EXT2_ET_CORRUPT_SUPERBLOCK; | ||
604 | } else if (e2fsck_journal_fix_bad_inode(ctx, pctx)) | ||
605 | return EXT2_ET_CORRUPT_SUPERBLOCK; | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal, | ||
611 | int reset, int drop) | ||
612 | { | ||
613 | journal_superblock_t *jsb; | ||
614 | |||
615 | if (drop) | ||
616 | mark_buffer_clean(journal->j_sb_buffer); | ||
617 | else if (!(ctx->options & E2F_OPT_READONLY)) { | ||
618 | jsb = journal->j_superblock; | ||
619 | jsb->s_sequence = htonl(journal->j_transaction_sequence); | ||
620 | if (reset) | ||
621 | jsb->s_start = 0; /* this marks the journal as empty */ | ||
622 | mark_buffer_dirty(journal->j_sb_buffer); | ||
623 | } | ||
624 | brelse(journal->j_sb_buffer); | ||
625 | |||
626 | if (ctx->journal_io) { | ||
627 | if (ctx->fs && ctx->fs->io != ctx->journal_io) | ||
628 | io_channel_close(ctx->journal_io); | ||
629 | ctx->journal_io = 0; | ||
630 | } | ||
631 | |||
632 | #ifndef USE_INODE_IO | ||
633 | if (journal->j_inode) | ||
634 | ext2fs_free_mem(&journal->j_inode); | ||
635 | #endif | ||
636 | if (journal->j_fs_dev) | ||
637 | ext2fs_free_mem(&journal->j_fs_dev); | ||
638 | ext2fs_free_mem(&journal); | ||
639 | } | ||
640 | |||
641 | /* | ||
642 | * This function makes sure that the superblock fields regarding the | ||
643 | * journal are consistent. | ||
644 | */ | ||
645 | int e2fsck_check_ext3_journal(e2fsck_t ctx) | ||
646 | { | ||
647 | struct ext2_super_block *sb = ctx->fs->super; | ||
648 | journal_t *journal; | ||
649 | int recover = ctx->fs->super->s_feature_incompat & | ||
650 | EXT3_FEATURE_INCOMPAT_RECOVER; | ||
651 | struct problem_context pctx; | ||
652 | problem_t problem; | ||
653 | int reset = 0, force_fsck = 0; | ||
654 | int retval; | ||
655 | |||
656 | /* If we don't have any journal features, don't do anything more */ | ||
657 | if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && | ||
658 | !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 && | ||
659 | uuid_is_null(sb->s_journal_uuid)) | ||
660 | return 0; | ||
661 | |||
662 | clear_problem_context(&pctx); | ||
663 | pctx.num = sb->s_journal_inum; | ||
664 | |||
665 | retval = e2fsck_get_journal(ctx, &journal); | ||
666 | if (retval) { | ||
667 | if ((retval == EXT2_ET_BAD_INODE_NUM) || | ||
668 | (retval == EXT2_ET_BAD_BLOCK_NUM) || | ||
669 | (retval == EXT2_ET_JOURNAL_TOO_SMALL) || | ||
670 | (retval == EXT2_ET_NO_JOURNAL)) | ||
671 | return e2fsck_journal_fix_bad_inode(ctx, &pctx); | ||
672 | return retval; | ||
673 | } | ||
674 | |||
675 | retval = e2fsck_journal_load(journal); | ||
676 | if (retval) { | ||
677 | if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) || | ||
678 | ((retval == EXT2_ET_UNSUPP_FEATURE) && | ||
679 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT, | ||
680 | &pctx))) || | ||
681 | ((retval == EXT2_ET_RO_UNSUPP_FEATURE) && | ||
682 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT, | ||
683 | &pctx))) || | ||
684 | ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) && | ||
685 | (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx)))) | ||
686 | retval = e2fsck_journal_fix_corrupt_super(ctx, journal, | ||
687 | &pctx); | ||
688 | e2fsck_journal_release(ctx, journal, 0, 1); | ||
689 | return retval; | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * We want to make the flags consistent here. We will not leave with | ||
694 | * needs_recovery set but has_journal clear. We can't get in a loop | ||
695 | * with -y, -n, or -p, only if a user isn't making up their mind. | ||
696 | */ | ||
697 | no_has_journal: | ||
698 | if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { | ||
699 | recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER; | ||
700 | pctx.str = "inode"; | ||
701 | if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) { | ||
702 | if (recover && | ||
703 | !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx)) | ||
704 | goto no_has_journal; | ||
705 | /* | ||
706 | * Need a full fsck if we are releasing a | ||
707 | * journal stored on a reserved inode. | ||
708 | */ | ||
709 | force_fsck = recover || | ||
710 | (sb->s_journal_inum < EXT2_FIRST_INODE(sb)); | ||
711 | /* Clear all of the journal fields */ | ||
712 | sb->s_journal_inum = 0; | ||
713 | sb->s_journal_dev = 0; | ||
714 | memset(sb->s_journal_uuid, 0, | ||
715 | sizeof(sb->s_journal_uuid)); | ||
716 | e2fsck_clear_recover(ctx, force_fsck); | ||
717 | } else if (!(ctx->options & E2F_OPT_READONLY)) { | ||
718 | sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; | ||
719 | ext2fs_mark_super_dirty(ctx->fs); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL && | ||
724 | !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) && | ||
725 | journal->j_superblock->s_start != 0) { | ||
726 | /* Print status information */ | ||
727 | fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx); | ||
728 | if (ctx->superblock) | ||
729 | problem = PR_0_JOURNAL_RUN_DEFAULT; | ||
730 | else | ||
731 | problem = PR_0_JOURNAL_RUN; | ||
732 | if (fix_problem(ctx, problem, &pctx)) { | ||
733 | ctx->options |= E2F_OPT_FORCE; | ||
734 | sb->s_feature_incompat |= | ||
735 | EXT3_FEATURE_INCOMPAT_RECOVER; | ||
736 | ext2fs_mark_super_dirty(ctx->fs); | ||
737 | } else if (fix_problem(ctx, | ||
738 | PR_0_JOURNAL_RESET_JOURNAL, &pctx)) { | ||
739 | reset = 1; | ||
740 | sb->s_state &= ~EXT2_VALID_FS; | ||
741 | ext2fs_mark_super_dirty(ctx->fs); | ||
742 | } | ||
743 | /* | ||
744 | * If the user answers no to the above question, we | ||
745 | * ignore the fact that journal apparently has data; | ||
746 | * accidentally replaying over valid data would be far | ||
747 | * worse than skipping a questionable recovery. | ||
748 | * | ||
749 | * XXX should we abort with a fatal error here? What | ||
750 | * will the ext3 kernel code do if a filesystem with | ||
751 | * !NEEDS_RECOVERY but with a non-zero | ||
752 | * journal->j_superblock->s_start is mounted? | ||
753 | */ | ||
754 | } | ||
755 | |||
756 | e2fsck_journal_release(ctx, journal, reset, 0); | ||
757 | return retval; | ||
758 | } | ||
759 | |||
760 | static errcode_t recover_ext3_journal(e2fsck_t ctx) | ||
761 | { | ||
762 | journal_t *journal; | ||
763 | int retval; | ||
764 | |||
765 | journal_init_revoke_caches(); | ||
766 | retval = e2fsck_get_journal(ctx, &journal); | ||
767 | if (retval) | ||
768 | return retval; | ||
769 | |||
770 | retval = e2fsck_journal_load(journal); | ||
771 | if (retval) | ||
772 | goto errout; | ||
773 | |||
774 | retval = journal_init_revoke(journal, 1024); | ||
775 | if (retval) | ||
776 | goto errout; | ||
777 | |||
778 | retval = -journal_recover(journal); | ||
779 | if (retval) | ||
780 | goto errout; | ||
781 | |||
782 | if (journal->j_superblock->s_errno) { | ||
783 | ctx->fs->super->s_state |= EXT2_ERROR_FS; | ||
784 | ext2fs_mark_super_dirty(ctx->fs); | ||
785 | journal->j_superblock->s_errno = 0; | ||
786 | mark_buffer_dirty(journal->j_sb_buffer); | ||
787 | } | ||
788 | |||
789 | errout: | ||
790 | journal_destroy_revoke(journal); | ||
791 | journal_destroy_revoke_caches(); | ||
792 | e2fsck_journal_release(ctx, journal, 1, 0); | ||
793 | return retval; | ||
794 | } | ||
795 | |||
796 | int e2fsck_run_ext3_journal(e2fsck_t ctx) | ||
797 | { | ||
798 | io_manager io_ptr = ctx->fs->io->manager; | ||
799 | int blocksize = ctx->fs->blocksize; | ||
800 | errcode_t retval, recover_retval; | ||
801 | |||
802 | printf(_("%s: recovering journal\n"), ctx->device_name); | ||
803 | if (ctx->options & E2F_OPT_READONLY) { | ||
804 | printf(_("%s: won't do journal recovery while read-only\n"), | ||
805 | ctx->device_name); | ||
806 | return EXT2_ET_FILE_RO; | ||
807 | } | ||
808 | |||
809 | if (ctx->fs->flags & EXT2_FLAG_DIRTY) | ||
810 | ext2fs_flush(ctx->fs); /* Force out any modifications */ | ||
811 | |||
812 | recover_retval = recover_ext3_journal(ctx); | ||
813 | |||
814 | /* | ||
815 | * Reload the filesystem context to get up-to-date data from disk | ||
816 | * because journal recovery will change the filesystem under us. | ||
817 | */ | ||
818 | ext2fs_close(ctx->fs); | ||
819 | retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW, | ||
820 | ctx->superblock, blocksize, io_ptr, | ||
821 | &ctx->fs); | ||
822 | |||
823 | if (retval) { | ||
824 | com_err(ctx->program_name, retval, | ||
825 | _("while trying to re-open %s"), | ||
826 | ctx->device_name); | ||
827 | fatal_error(ctx, 0); | ||
828 | } | ||
829 | ctx->fs->priv_data = ctx; | ||
830 | |||
831 | /* Set the superblock flags */ | ||
832 | e2fsck_clear_recover(ctx, recover_retval); | ||
833 | return recover_retval; | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * This function will move the journal inode from a visible file in | ||
838 | * the filesystem directory hierarchy to the reserved inode if necessary. | ||
839 | */ | ||
840 | static const char * const journal_names[] = { | ||
841 | ".journal", "journal", ".journal.dat", "journal.dat", 0 }; | ||
842 | |||
843 | void e2fsck_move_ext3_journal(e2fsck_t ctx) | ||
844 | { | ||
845 | struct ext2_super_block *sb = ctx->fs->super; | ||
846 | struct problem_context pctx; | ||
847 | struct ext2_inode inode; | ||
848 | ext2_filsys fs = ctx->fs; | ||
849 | ext2_ino_t ino; | ||
850 | errcode_t retval; | ||
851 | const char * const * cpp; | ||
852 | int group, mount_flags; | ||
853 | |||
854 | clear_problem_context(&pctx); | ||
855 | |||
856 | /* | ||
857 | * If the filesystem is opened read-only, or there is no | ||
858 | * journal, then do nothing. | ||
859 | */ | ||
860 | if ((ctx->options & E2F_OPT_READONLY) || | ||
861 | (sb->s_journal_inum == 0) || | ||
862 | !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) | ||
863 | return; | ||
864 | |||
865 | /* | ||
866 | * Read in the journal inode | ||
867 | */ | ||
868 | if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0) | ||
869 | return; | ||
870 | |||
871 | /* | ||
872 | * If it's necessary to backup the journal inode, do so. | ||
873 | */ | ||
874 | if ((sb->s_jnl_backup_type == 0) || | ||
875 | ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) && | ||
876 | memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) { | ||
877 | if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) { | ||
878 | memcpy(sb->s_jnl_blocks, inode.i_block, | ||
879 | EXT2_N_BLOCKS*4); | ||
880 | sb->s_jnl_blocks[16] = inode.i_size; | ||
881 | sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; | ||
882 | ext2fs_mark_super_dirty(fs); | ||
883 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * If the journal is already the hidden inode, then do nothing | ||
889 | */ | ||
890 | if (sb->s_journal_inum == EXT2_JOURNAL_INO) | ||
891 | return; | ||
892 | |||
893 | /* | ||
894 | * The journal inode had better have only one link and not be readable. | ||
895 | */ | ||
896 | if (inode.i_links_count != 1) | ||
897 | return; | ||
898 | |||
899 | /* | ||
900 | * If the filesystem is mounted, or we can't tell whether | ||
901 | * or not it's mounted, do nothing. | ||
902 | */ | ||
903 | retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags); | ||
904 | if (retval || (mount_flags & EXT2_MF_MOUNTED)) | ||
905 | return; | ||
906 | |||
907 | /* | ||
908 | * If we can't find the name of the journal inode, then do | ||
909 | * nothing. | ||
910 | */ | ||
911 | for (cpp = journal_names; *cpp; cpp++) { | ||
912 | retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp, | ||
913 | strlen(*cpp), 0, &ino); | ||
914 | if ((retval == 0) && (ino == sb->s_journal_inum)) | ||
915 | break; | ||
916 | } | ||
917 | if (*cpp == 0) | ||
918 | return; | ||
919 | |||
920 | /* We need the inode bitmap to be loaded */ | ||
921 | retval = ext2fs_read_bitmaps(fs); | ||
922 | if (retval) | ||
923 | return; | ||
924 | |||
925 | pctx.str = *cpp; | ||
926 | if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx)) | ||
927 | return; | ||
928 | |||
929 | /* | ||
930 | * OK, we've done all the checks, let's actually move the | ||
931 | * journal inode. Errors at this point mean we need to force | ||
932 | * an ext2 filesystem check. | ||
933 | */ | ||
934 | if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0) | ||
935 | goto err_out; | ||
936 | if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0) | ||
937 | goto err_out; | ||
938 | sb->s_journal_inum = EXT2_JOURNAL_INO; | ||
939 | ext2fs_mark_super_dirty(fs); | ||
940 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; | ||
941 | inode.i_links_count = 0; | ||
942 | inode.i_dtime = time(0); | ||
943 | if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0) | ||
944 | goto err_out; | ||
945 | |||
946 | group = ext2fs_group_of_ino(fs, ino); | ||
947 | ext2fs_unmark_inode_bitmap(fs->inode_map, ino); | ||
948 | ext2fs_mark_ib_dirty(fs); | ||
949 | fs->group_desc[group].bg_free_inodes_count++; | ||
950 | fs->super->s_free_inodes_count++; | ||
951 | return; | ||
952 | |||
953 | err_out: | ||
954 | pctx.errcode = retval; | ||
955 | fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx); | ||
956 | fs->super->s_state &= ~EXT2_VALID_FS; | ||
957 | ext2fs_mark_super_dirty(fs); | ||
958 | return; | ||
959 | } | ||
960 | |||