]>
Commit | Line | Data |
---|---|---|
759c46cf DW |
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 | #include "config.h" | |
16 | #ifdef HAVE_SYS_MOUNT_H | |
17 | #include <sys/param.h> | |
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 | |
24 | ||
25 | #define E2FSCK_INCLUDE_INLINE_FUNCS | |
759c46cf | 26 | #include "uuid/uuid.h" |
df0b907e | 27 | #include "journal.h" |
759c46cf DW |
28 | |
29 | #ifdef CONFIG_JBD_DEBUG /* Enabled by configure --enable-jfs-debug */ | |
30 | static int bh_count = 0; | |
31 | #endif | |
32 | ||
33 | #if EXT2_FLAT_INCLUDES | |
34 | #include "blkid.h" | |
35 | #else | |
36 | #include "blkid/blkid.h" | |
37 | #endif | |
38 | ||
39 | /* | |
40 | * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths. | |
41 | * This creates a larger static binary, and a smaller binary using | |
42 | * shared libraries. It's also probably slightly less CPU-efficient, | |
43 | * which is why it's not on by default. But, it's a good way of | |
44 | * testing the functions in inode_io.c and fileio.c. | |
45 | */ | |
46 | #undef USE_INODE_IO | |
47 | ||
48 | /* Checksumming functions */ | |
49 | static int ext2fs_journal_verify_csum_type(journal_t *j, | |
50 | journal_superblock_t *jsb) | |
51 | { | |
52 | if (!journal_has_csum_v2or3(j)) | |
53 | return 1; | |
54 | ||
55 | return jsb->s_checksum_type == JBD2_CRC32C_CHKSUM; | |
56 | } | |
57 | ||
58 | static __u32 ext2fs_journal_sb_csum(journal_superblock_t *jsb) | |
59 | { | |
60 | __u32 crc, old_crc; | |
61 | ||
62 | old_crc = jsb->s_checksum; | |
63 | jsb->s_checksum = 0; | |
64 | crc = ext2fs_crc32c_le(~0, (unsigned char *)jsb, | |
65 | sizeof(journal_superblock_t)); | |
66 | jsb->s_checksum = old_crc; | |
67 | ||
68 | return crc; | |
69 | } | |
70 | ||
71 | static int ext2fs_journal_sb_csum_verify(journal_t *j, | |
72 | journal_superblock_t *jsb) | |
73 | { | |
74 | __u32 provided, calculated; | |
75 | ||
76 | if (!journal_has_csum_v2or3(j)) | |
77 | return 1; | |
78 | ||
79 | provided = ext2fs_be32_to_cpu(jsb->s_checksum); | |
80 | calculated = ext2fs_journal_sb_csum(jsb); | |
81 | ||
82 | return provided == calculated; | |
83 | } | |
84 | ||
85 | static errcode_t ext2fs_journal_sb_csum_set(journal_t *j, | |
86 | journal_superblock_t *jsb) | |
87 | { | |
88 | __u32 crc; | |
89 | ||
90 | if (!journal_has_csum_v2or3(j)) | |
91 | return 0; | |
92 | ||
93 | crc = ext2fs_journal_sb_csum(jsb); | |
94 | jsb->s_checksum = ext2fs_cpu_to_be32(crc); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | /* Kernel compatibility functions for handling the journal. These allow us | |
99 | * to use the recovery.c file virtually unchanged from the kernel, so we | |
100 | * don't have to do much to keep kernel and user recovery in sync. | |
101 | */ | |
102 | int journal_bmap(journal_t *journal, blk64_t block, unsigned long long *phys) | |
103 | { | |
104 | #ifdef USE_INODE_IO | |
105 | *phys = block; | |
106 | return 0; | |
107 | #else | |
108 | struct inode *inode = journal->j_inode; | |
109 | errcode_t retval; | |
110 | blk64_t pblk; | |
111 | ||
112 | if (!inode) { | |
113 | *phys = block; | |
114 | return 0; | |
115 | } | |
116 | ||
117 | retval = ext2fs_bmap2(inode->i_fs, inode->i_ino, | |
118 | &inode->i_ext2, NULL, 0, block, 0, &pblk); | |
119 | *phys = pblk; | |
120 | return (int) retval; | |
121 | #endif | |
122 | } | |
123 | ||
124 | struct buffer_head *getblk(kdev_t kdev, blk64_t blocknr, int blocksize) | |
125 | { | |
126 | struct buffer_head *bh; | |
127 | int bufsize = sizeof(*bh) + kdev->k_fs->blocksize - | |
128 | sizeof(bh->b_data); | |
129 | errcode_t retval; | |
130 | ||
131 | retval = ext2fs_get_memzero(bufsize, &bh); | |
132 | if (retval) | |
133 | return NULL; | |
134 | ||
135 | #ifdef CONFIG_JBD_DEBUG | |
136 | if (journal_enable_debug >= 3) | |
137 | bh_count++; | |
138 | #endif | |
139 | jfs_debug(4, "getblk for block %llu (%d bytes)(total %d)\n", | |
140 | (unsigned long long) blocknr, blocksize, bh_count); | |
141 | ||
142 | bh->b_fs = kdev->k_fs; | |
143 | if (kdev->k_dev == K_DEV_FS) | |
144 | bh->b_io = kdev->k_fs->io; | |
145 | else | |
146 | bh->b_io = kdev->k_fs->journal_io; | |
147 | bh->b_size = blocksize; | |
148 | bh->b_blocknr = blocknr; | |
149 | ||
150 | return bh; | |
151 | } | |
152 | ||
153 | int sync_blockdev(kdev_t kdev) | |
154 | { | |
155 | io_channel io; | |
156 | ||
157 | if (kdev->k_dev == K_DEV_FS) | |
158 | io = kdev->k_fs->io; | |
159 | else | |
160 | io = kdev->k_fs->journal_io; | |
161 | ||
162 | return io_channel_flush(io) ? EIO : 0; | |
163 | } | |
164 | ||
165 | void ll_rw_block(int rw, int nr, struct buffer_head *bhp[]) | |
166 | { | |
167 | errcode_t retval; | |
168 | struct buffer_head *bh; | |
169 | ||
170 | for (; nr > 0; --nr) { | |
171 | bh = *bhp++; | |
172 | if (rw == READ && !bh->b_uptodate) { | |
173 | jfs_debug(3, "reading block %llu/%p\n", | |
174 | bh->b_blocknr, (void *) bh); | |
175 | retval = io_channel_read_blk64(bh->b_io, | |
176 | bh->b_blocknr, | |
177 | 1, bh->b_data); | |
178 | if (retval) { | |
179 | com_err(bh->b_fs->device_name, retval, | |
180 | "while reading block %llu\n", | |
181 | bh->b_blocknr); | |
182 | bh->b_err = (int) retval; | |
183 | continue; | |
184 | } | |
185 | bh->b_uptodate = 1; | |
186 | } else if (rw == WRITE && bh->b_dirty) { | |
187 | jfs_debug(3, "writing block %llu/%p\n", | |
188 | bh->b_blocknr, | |
189 | (void *) bh); | |
190 | retval = io_channel_write_blk64(bh->b_io, | |
191 | bh->b_blocknr, | |
192 | 1, bh->b_data); | |
193 | if (retval) { | |
194 | com_err(bh->b_fs->device_name, retval, | |
195 | "while writing block %llu\n", | |
196 | bh->b_blocknr); | |
197 | bh->b_err = (int) retval; | |
198 | continue; | |
199 | } | |
200 | bh->b_dirty = 0; | |
201 | bh->b_uptodate = 1; | |
202 | } else { | |
203 | jfs_debug(3, "no-op %s for block %llu\n", | |
204 | rw == READ ? "read" : "write", | |
205 | bh->b_blocknr); | |
206 | } | |
207 | } | |
208 | } | |
209 | ||
210 | void mark_buffer_dirty(struct buffer_head *bh) | |
211 | { | |
212 | bh->b_dirty = 1; | |
213 | } | |
214 | ||
215 | static void mark_buffer_clean(struct buffer_head *bh) | |
216 | { | |
217 | bh->b_dirty = 0; | |
218 | } | |
219 | ||
220 | void brelse(struct buffer_head *bh) | |
221 | { | |
222 | if (bh->b_dirty) | |
223 | ll_rw_block(WRITE, 1, &bh); | |
224 | jfs_debug(3, "freeing block %llu/%p (total %d)\n", | |
225 | bh->b_blocknr, (void *) bh, --bh_count); | |
226 | ext2fs_free_mem(&bh); | |
227 | } | |
228 | ||
229 | int buffer_uptodate(struct buffer_head *bh) | |
230 | { | |
231 | return bh->b_uptodate; | |
232 | } | |
233 | ||
234 | void mark_buffer_uptodate(struct buffer_head *bh, int val) | |
235 | { | |
236 | bh->b_uptodate = val; | |
237 | } | |
238 | ||
239 | void wait_on_buffer(struct buffer_head *bh) | |
240 | { | |
241 | if (!bh->b_uptodate) | |
242 | ll_rw_block(READ, 1, &bh); | |
243 | } | |
244 | ||
245 | ||
246 | static void ext2fs_clear_recover(ext2_filsys fs, int error) | |
247 | { | |
4ee26699 | 248 | ext2fs_clear_feature_journal_needs_recovery(fs->super); |
759c46cf DW |
249 | |
250 | /* if we had an error doing journal recovery, we need a full fsck */ | |
251 | if (error) | |
252 | fs->super->s_state &= ~EXT2_VALID_FS; | |
60f032bb TT |
253 | /* |
254 | * If we replayed the journal by definition the file system | |
255 | * was mounted since the last time it was checked | |
256 | */ | |
257 | if (fs->super->s_lastcheck >= fs->super->s_mtime) | |
258 | fs->super->s_lastcheck = fs->super->s_mtime - 1; | |
759c46cf DW |
259 | ext2fs_mark_super_dirty(fs); |
260 | } | |
261 | ||
262 | /* | |
263 | * This is a helper function to check the validity of the journal. | |
264 | */ | |
265 | struct process_block_struct { | |
266 | e2_blkcnt_t last_block; | |
267 | }; | |
268 | ||
269 | static int process_journal_block(ext2_filsys fs, | |
270 | blk64_t *block_nr, | |
271 | e2_blkcnt_t blockcnt, | |
272 | blk64_t ref_block EXT2FS_ATTR((unused)), | |
273 | int ref_offset EXT2FS_ATTR((unused)), | |
274 | void *priv_data) | |
275 | { | |
276 | struct process_block_struct *p; | |
277 | blk64_t blk = *block_nr; | |
278 | ||
279 | p = (struct process_block_struct *) priv_data; | |
280 | ||
281 | if (!blk || blk < fs->super->s_first_data_block || | |
282 | blk >= ext2fs_blocks_count(fs->super)) | |
283 | return BLOCK_ABORT; | |
284 | ||
285 | if (blockcnt >= 0) | |
286 | p->last_block = blockcnt; | |
287 | return 0; | |
288 | } | |
289 | ||
290 | static errcode_t ext2fs_get_journal(ext2_filsys fs, journal_t **ret_journal) | |
291 | { | |
292 | struct process_block_struct pb; | |
293 | struct ext2_super_block *sb = fs->super; | |
294 | struct ext2_super_block jsuper; | |
295 | struct buffer_head *bh; | |
296 | struct inode *j_inode = NULL; | |
297 | struct kdev_s *dev_fs = NULL, *dev_journal; | |
298 | const char *journal_name = 0; | |
299 | journal_t *journal = NULL; | |
300 | errcode_t retval = 0; | |
301 | io_manager io_ptr = 0; | |
302 | unsigned long long start = 0; | |
303 | int ext_journal = 0; | |
304 | int tried_backup_jnl = 0; | |
305 | ||
306 | retval = ext2fs_get_memzero(sizeof(journal_t), &journal); | |
307 | if (retval) | |
308 | return retval; | |
309 | ||
310 | retval = ext2fs_get_memzero(2 * sizeof(struct kdev_s), &dev_fs); | |
311 | if (retval) | |
312 | goto errout; | |
313 | dev_journal = dev_fs+1; | |
314 | ||
315 | dev_fs->k_fs = dev_journal->k_fs = fs; | |
316 | dev_fs->k_dev = K_DEV_FS; | |
317 | dev_journal->k_dev = K_DEV_JOURNAL; | |
318 | ||
319 | journal->j_dev = dev_journal; | |
320 | journal->j_fs_dev = dev_fs; | |
321 | journal->j_inode = NULL; | |
322 | journal->j_blocksize = fs->blocksize; | |
323 | ||
324 | if (uuid_is_null(sb->s_journal_uuid)) { | |
325 | if (!sb->s_journal_inum) { | |
326 | retval = EXT2_ET_BAD_INODE_NUM; | |
327 | goto errout; | |
328 | } | |
329 | retval = ext2fs_get_memzero(sizeof(*j_inode), &j_inode); | |
330 | if (retval) | |
331 | goto errout; | |
332 | ||
333 | j_inode->i_fs = fs; | |
334 | j_inode->i_ino = sb->s_journal_inum; | |
335 | ||
336 | retval = ext2fs_read_inode(fs, sb->s_journal_inum, | |
337 | &j_inode->i_ext2); | |
338 | if (retval) { | |
339 | try_backup_journal: | |
340 | if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || | |
341 | tried_backup_jnl) | |
342 | goto errout; | |
343 | memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); | |
344 | memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, | |
345 | EXT2_N_BLOCKS*4); | |
346 | j_inode->i_ext2.i_size_high = sb->s_jnl_blocks[15]; | |
347 | j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; | |
348 | j_inode->i_ext2.i_links_count = 1; | |
349 | j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; | |
350 | tried_backup_jnl++; | |
351 | } | |
352 | if (!j_inode->i_ext2.i_links_count || | |
353 | !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { | |
354 | retval = EXT2_ET_NO_JOURNAL; | |
355 | goto try_backup_journal; | |
356 | } | |
357 | if (EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize < | |
358 | JFS_MIN_JOURNAL_BLOCKS) { | |
359 | retval = EXT2_ET_JOURNAL_TOO_SMALL; | |
360 | goto try_backup_journal; | |
361 | } | |
362 | pb.last_block = -1; | |
363 | retval = ext2fs_block_iterate3(fs, j_inode->i_ino, | |
364 | BLOCK_FLAG_HOLE, 0, | |
365 | process_journal_block, &pb); | |
366 | if ((pb.last_block + 1) * fs->blocksize < | |
367 | (int) EXT2_I_SIZE(&j_inode->i_ext2)) { | |
368 | retval = EXT2_ET_JOURNAL_TOO_SMALL; | |
369 | goto try_backup_journal; | |
370 | } | |
371 | if (tried_backup_jnl && (fs->flags & EXT2_FLAG_RW)) { | |
372 | retval = ext2fs_write_inode(fs, sb->s_journal_inum, | |
373 | &j_inode->i_ext2); | |
374 | if (retval) | |
375 | goto errout; | |
376 | } | |
377 | ||
378 | journal->j_maxlen = EXT2_I_SIZE(&j_inode->i_ext2) / | |
379 | journal->j_blocksize; | |
380 | ||
381 | #ifdef USE_INODE_IO | |
382 | retval = ext2fs_inode_io_intern2(fs, sb->s_journal_inum, | |
383 | &j_inode->i_ext2, | |
384 | &journal_name); | |
385 | if (retval) | |
386 | goto errout; | |
387 | ||
388 | io_ptr = inode_io_manager; | |
389 | #else | |
390 | journal->j_inode = j_inode; | |
391 | fs->journal_io = fs->io; | |
392 | retval = (errcode_t)journal_bmap(journal, 0, &start); | |
393 | if (retval) | |
394 | goto errout; | |
395 | #endif | |
396 | } else { | |
397 | ext_journal = 1; | |
398 | if (!fs->journal_name) { | |
399 | char uuid[37]; | |
400 | blkid_cache blkid; | |
401 | ||
402 | blkid_get_cache(&blkid, NULL); | |
403 | uuid_unparse(sb->s_journal_uuid, uuid); | |
404 | fs->journal_name = blkid_get_devname(blkid, | |
405 | "UUID", uuid); | |
406 | if (!fs->journal_name) | |
407 | fs->journal_name = blkid_devno_to_devname(sb->s_journal_dev); | |
408 | blkid_put_cache(blkid); | |
409 | } | |
410 | journal_name = fs->journal_name; | |
411 | ||
412 | if (!journal_name) { | |
413 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | |
414 | goto errout; | |
415 | } | |
416 | ||
417 | jfs_debug(1, "Using journal file %s\n", journal_name); | |
418 | io_ptr = unix_io_manager; | |
419 | } | |
420 | ||
421 | #if 0 | |
422 | test_io_backing_manager = io_ptr; | |
423 | io_ptr = test_io_manager; | |
424 | #endif | |
425 | #ifndef USE_INODE_IO | |
426 | if (ext_journal) | |
427 | #endif | |
428 | { | |
429 | retval = io_ptr->open(journal_name, fs->flags & EXT2_FLAG_RW, | |
430 | &fs->journal_io); | |
431 | } | |
432 | if (retval) | |
433 | goto errout; | |
434 | ||
435 | io_channel_set_blksize(fs->journal_io, fs->blocksize); | |
436 | ||
437 | if (ext_journal) { | |
438 | blk64_t maxlen; | |
439 | ||
440 | start = ext2fs_journal_sb_start(fs->blocksize) - 1; | |
441 | bh = getblk(dev_journal, start, fs->blocksize); | |
442 | if (!bh) { | |
443 | retval = EXT2_ET_NO_MEMORY; | |
444 | goto errout; | |
445 | } | |
446 | ll_rw_block(READ, 1, &bh); | |
447 | retval = bh->b_err; | |
448 | if (retval) { | |
449 | brelse(bh); | |
450 | goto errout; | |
451 | } | |
452 | memcpy(&jsuper, start ? bh->b_data : | |
453 | bh->b_data + SUPERBLOCK_OFFSET, | |
454 | sizeof(jsuper)); | |
455 | #ifdef WORDS_BIGENDIAN | |
456 | if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) | |
457 | ext2fs_swap_super(&jsuper); | |
458 | #endif | |
459 | if (jsuper.s_magic != EXT2_SUPER_MAGIC || | |
4ee26699 | 460 | !ext2fs_has_feature_journal_dev(&jsuper)) { |
759c46cf DW |
461 | retval = EXT2_ET_LOAD_EXT_JOURNAL; |
462 | brelse(bh); | |
463 | goto errout; | |
464 | } | |
465 | /* Make sure the journal UUID is correct */ | |
466 | if (memcmp(jsuper.s_uuid, fs->super->s_journal_uuid, | |
467 | sizeof(jsuper.s_uuid))) { | |
468 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | |
469 | brelse(bh); | |
470 | goto errout; | |
471 | } | |
472 | ||
473 | /* Check the superblock checksum */ | |
4ee26699 | 474 | if (ext2fs_has_feature_metadata_csum(&jsuper)) { |
759c46cf DW |
475 | struct struct_ext2_filsys fsx; |
476 | struct ext2_super_block superx; | |
477 | void *p; | |
478 | ||
479 | p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; | |
480 | memcpy(&fsx, fs, sizeof(fsx)); | |
481 | memcpy(&superx, fs->super, sizeof(superx)); | |
482 | fsx.super = &superx; | |
4ee26699 | 483 | ext2fs_set_feature_metadata_csum(fsx.super); |
759c46cf DW |
484 | if (!ext2fs_superblock_csum_verify(&fsx, p)) { |
485 | retval = EXT2_ET_LOAD_EXT_JOURNAL; | |
486 | brelse(bh); | |
487 | goto errout; | |
488 | } | |
489 | } | |
490 | brelse(bh); | |
491 | ||
492 | maxlen = ext2fs_blocks_count(&jsuper); | |
493 | journal->j_maxlen = (maxlen < 1ULL << 32) ? maxlen : | |
494 | (1ULL << 32) - 1; | |
495 | start++; | |
496 | } | |
497 | ||
498 | bh = getblk(dev_journal, start, journal->j_blocksize); | |
499 | if (!bh) { | |
500 | retval = EXT2_ET_NO_MEMORY; | |
501 | goto errout; | |
502 | } | |
503 | ||
504 | journal->j_sb_buffer = bh; | |
505 | journal->j_superblock = (journal_superblock_t *)bh->b_data; | |
506 | ||
507 | #ifdef USE_INODE_IO | |
508 | if (j_inode) | |
509 | ext2fs_free_mem(&j_inode); | |
510 | #endif | |
511 | ||
512 | *ret_journal = journal; | |
513 | return 0; | |
514 | ||
515 | errout: | |
516 | if (dev_fs) | |
517 | ext2fs_free_mem(&dev_fs); | |
518 | if (j_inode) | |
519 | ext2fs_free_mem(&j_inode); | |
520 | if (journal) | |
521 | ext2fs_free_mem(&journal); | |
522 | return retval; | |
523 | } | |
524 | ||
525 | static errcode_t ext2fs_journal_fix_bad_inode(ext2_filsys fs) | |
526 | { | |
527 | struct ext2_super_block *sb = fs->super; | |
4ee26699 DW |
528 | int recover = ext2fs_has_feature_journal_needs_recovery(fs->super); |
529 | int has_journal = ext2fs_has_feature_journal(fs->super); | |
759c46cf DW |
530 | |
531 | if (has_journal || sb->s_journal_inum) { | |
532 | /* The journal inode is bogus, remove and force full fsck */ | |
533 | return EXT2_ET_BAD_INODE_NUM; | |
534 | } else if (recover) { | |
535 | return EXT2_ET_UNSUPP_FEATURE; | |
536 | } | |
537 | return 0; | |
538 | } | |
539 | ||
540 | #define V1_SB_SIZE 0x0024 | |
541 | static void clear_v2_journal_fields(journal_t *journal) | |
542 | { | |
543 | ext2_filsys fs = journal->j_dev->k_fs; | |
544 | ||
545 | memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0, | |
546 | fs->blocksize-V1_SB_SIZE); | |
547 | mark_buffer_dirty(journal->j_sb_buffer); | |
548 | } | |
549 | ||
550 | ||
551 | static errcode_t ext2fs_journal_load(journal_t *journal) | |
552 | { | |
553 | ext2_filsys fs = journal->j_dev->k_fs; | |
554 | journal_superblock_t *jsb; | |
555 | struct buffer_head *jbh = journal->j_sb_buffer; | |
556 | ||
557 | ll_rw_block(READ, 1, &jbh); | |
558 | if (jbh->b_err) | |
559 | return jbh->b_err; | |
560 | ||
561 | jsb = journal->j_superblock; | |
562 | /* If we don't even have JFS_MAGIC, we probably have a wrong inode */ | |
563 | if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER)) | |
564 | return ext2fs_journal_fix_bad_inode(fs); | |
565 | ||
566 | switch (ntohl(jsb->s_header.h_blocktype)) { | |
567 | case JFS_SUPERBLOCK_V1: | |
568 | journal->j_format_version = 1; | |
569 | if (jsb->s_feature_compat || | |
570 | jsb->s_feature_incompat || | |
571 | jsb->s_feature_ro_compat || | |
572 | jsb->s_nr_users) | |
573 | clear_v2_journal_fields(journal); | |
574 | break; | |
575 | ||
576 | case JFS_SUPERBLOCK_V2: | |
577 | journal->j_format_version = 2; | |
578 | if (ntohl(jsb->s_nr_users) > 1 && | |
579 | uuid_is_null(fs->super->s_journal_uuid)) | |
580 | clear_v2_journal_fields(journal); | |
581 | if (ntohl(jsb->s_nr_users) > 1) | |
582 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; | |
583 | break; | |
584 | ||
585 | /* | |
586 | * These should never appear in a journal super block, so if | |
587 | * they do, the journal is badly corrupted. | |
588 | */ | |
589 | case JFS_DESCRIPTOR_BLOCK: | |
590 | case JFS_COMMIT_BLOCK: | |
591 | case JFS_REVOKE_BLOCK: | |
d37026ea | 592 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
593 | |
594 | /* If we don't understand the superblock major type, but there | |
595 | * is a magic number, then it is likely to be a new format we | |
596 | * just don't understand, so leave it alone. */ | |
597 | default: | |
598 | return EXT2_ET_JOURNAL_UNSUPP_VERSION; | |
599 | } | |
600 | ||
601 | if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES)) | |
602 | return EXT2_ET_UNSUPP_FEATURE; | |
603 | ||
604 | if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES)) | |
605 | return EXT2_ET_RO_UNSUPP_FEATURE; | |
606 | ||
607 | /* Checksum v1-3 are mutually exclusive features. */ | |
4ee26699 | 608 | if (jfs_has_feature_csum2(journal) && jfs_has_feature_csum3(journal)) |
d37026ea | 609 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
610 | |
611 | if (journal_has_csum_v2or3(journal) && | |
4ee26699 | 612 | jfs_has_feature_checksum(journal)) |
d37026ea | 613 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
614 | |
615 | if (!ext2fs_journal_verify_csum_type(journal, jsb) || | |
616 | !ext2fs_journal_sb_csum_verify(journal, jsb)) | |
d37026ea | 617 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
618 | |
619 | if (journal_has_csum_v2or3(journal)) | |
620 | journal->j_csum_seed = jbd2_chksum(journal, ~0, jsb->s_uuid, | |
621 | sizeof(jsb->s_uuid)); | |
622 | ||
623 | /* We have now checked whether we know enough about the journal | |
624 | * format to be able to proceed safely, so any other checks that | |
625 | * fail we should attempt to recover from. */ | |
626 | if (jsb->s_blocksize != htonl(journal->j_blocksize)) | |
d37026ea | 627 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
628 | |
629 | if (ntohl(jsb->s_maxlen) < journal->j_maxlen) | |
630 | journal->j_maxlen = ntohl(jsb->s_maxlen); | |
631 | else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) | |
d37026ea | 632 | return EXT2_ET_CORRUPT_JOURNAL_SB; |
759c46cf DW |
633 | |
634 | journal->j_tail_sequence = ntohl(jsb->s_sequence); | |
635 | journal->j_transaction_sequence = journal->j_tail_sequence; | |
636 | journal->j_tail = ntohl(jsb->s_start); | |
637 | journal->j_first = ntohl(jsb->s_first); | |
638 | journal->j_last = ntohl(jsb->s_maxlen); | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
df0b907e | 643 | static void ext2fs_journal_release(ext2_filsys fs, journal_t *journal, |
759c46cf DW |
644 | int reset, int drop) |
645 | { | |
646 | journal_superblock_t *jsb; | |
647 | ||
648 | if (drop) | |
649 | mark_buffer_clean(journal->j_sb_buffer); | |
650 | else if (fs->flags & EXT2_FLAG_RW) { | |
651 | jsb = journal->j_superblock; | |
652 | jsb->s_sequence = htonl(journal->j_tail_sequence); | |
653 | if (reset) | |
654 | jsb->s_start = 0; /* this marks the journal as empty */ | |
655 | ext2fs_journal_sb_csum_set(journal, jsb); | |
656 | mark_buffer_dirty(journal->j_sb_buffer); | |
657 | } | |
658 | brelse(journal->j_sb_buffer); | |
659 | ||
d4daf0d8 DW |
660 | if (fs && fs->journal_io) { |
661 | if (fs->io != fs->journal_io) | |
759c46cf | 662 | io_channel_close(fs->journal_io); |
d4daf0d8 | 663 | fs->journal_io = NULL; |
a54733d2 TT |
664 | free(fs->journal_name); |
665 | fs->journal_name = NULL; | |
759c46cf DW |
666 | } |
667 | ||
668 | #ifndef USE_INODE_IO | |
669 | if (journal->j_inode) | |
670 | ext2fs_free_mem(&journal->j_inode); | |
671 | #endif | |
672 | if (journal->j_fs_dev) | |
673 | ext2fs_free_mem(&journal->j_fs_dev); | |
674 | ext2fs_free_mem(&journal); | |
675 | } | |
676 | ||
677 | /* | |
678 | * This function makes sure that the superblock fields regarding the | |
679 | * journal are consistent. | |
680 | */ | |
df0b907e | 681 | static errcode_t ext2fs_check_ext3_journal(ext2_filsys fs) |
759c46cf DW |
682 | { |
683 | struct ext2_super_block *sb = fs->super; | |
684 | journal_t *journal; | |
4ee26699 | 685 | int recover = ext2fs_has_feature_journal_needs_recovery(fs->super); |
759c46cf DW |
686 | errcode_t retval; |
687 | ||
688 | /* If we don't have any journal features, don't do anything more */ | |
4ee26699 | 689 | if (!ext2fs_has_feature_journal(sb) && |
759c46cf DW |
690 | !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 && |
691 | uuid_is_null(sb->s_journal_uuid)) | |
692 | return 0; | |
693 | ||
694 | retval = ext2fs_get_journal(fs, &journal); | |
695 | if (retval) | |
696 | return retval; | |
697 | ||
698 | retval = ext2fs_journal_load(journal); | |
d4daf0d8 DW |
699 | if (retval) |
700 | goto err; | |
759c46cf DW |
701 | |
702 | /* | |
703 | * We want to make the flags consistent here. We will not leave with | |
704 | * needs_recovery set but has_journal clear. We can't get in a loop | |
705 | * with -y, -n, or -p, only if a user isn't making up their mind. | |
706 | */ | |
4ee26699 | 707 | if (!ext2fs_has_feature_journal(sb)) { |
d4daf0d8 DW |
708 | retval = EXT2_ET_JOURNAL_FLAGS_WRONG; |
709 | goto err; | |
710 | } | |
759c46cf | 711 | |
4ee26699 DW |
712 | if (ext2fs_has_feature_journal(sb) && |
713 | !ext2fs_has_feature_journal_needs_recovery(sb) && | |
d4daf0d8 DW |
714 | journal->j_superblock->s_start != 0) { |
715 | retval = EXT2_ET_JOURNAL_FLAGS_WRONG; | |
716 | goto err; | |
717 | } | |
759c46cf DW |
718 | |
719 | /* | |
720 | * If we don't need to do replay the journal, check to see if | |
721 | * the journal's errno is set; if so, we need to mark the file | |
722 | * system as being corrupt and clear the journal's s_errno. | |
723 | */ | |
4ee26699 | 724 | if (!ext2fs_has_feature_journal_needs_recovery(sb) && |
759c46cf DW |
725 | journal->j_superblock->s_errno) { |
726 | fs->super->s_state |= EXT2_ERROR_FS; | |
727 | ext2fs_mark_super_dirty(fs); | |
728 | journal->j_superblock->s_errno = 0; | |
729 | ext2fs_journal_sb_csum_set(journal, journal->j_superblock); | |
730 | mark_buffer_dirty(journal->j_sb_buffer); | |
731 | } | |
732 | ||
d4daf0d8 DW |
733 | err: |
734 | ext2fs_journal_release(fs, journal, 0, retval ? 1 : 0); | |
759c46cf DW |
735 | return retval; |
736 | } | |
737 | ||
738 | static errcode_t recover_ext3_journal(ext2_filsys fs) | |
739 | { | |
740 | journal_t *journal; | |
741 | errcode_t retval; | |
742 | ||
743 | journal_init_revoke_caches(); | |
744 | retval = ext2fs_get_journal(fs, &journal); | |
745 | if (retval) | |
746 | return retval; | |
747 | ||
748 | retval = ext2fs_journal_load(journal); | |
749 | if (retval) | |
750 | goto errout; | |
751 | ||
752 | retval = journal_init_revoke(journal, 1024); | |
753 | if (retval) | |
754 | goto errout; | |
755 | ||
756 | retval = -journal_recover(journal); | |
757 | if (retval) | |
758 | goto errout; | |
759 | ||
760 | if (journal->j_failed_commit) { | |
761 | journal->j_superblock->s_errno = -EINVAL; | |
762 | mark_buffer_dirty(journal->j_sb_buffer); | |
763 | } | |
764 | ||
765 | errout: | |
766 | journal_destroy_revoke(journal); | |
767 | journal_destroy_revoke_caches(); | |
768 | ext2fs_journal_release(fs, journal, 1, 0); | |
769 | return retval; | |
770 | } | |
771 | ||
772 | errcode_t ext2fs_run_ext3_journal(ext2_filsys *fsp) | |
773 | { | |
774 | ext2_filsys fs = *fsp; | |
775 | io_manager io_ptr = fs->io->manager; | |
776 | errcode_t retval, recover_retval; | |
777 | io_stats stats = 0; | |
778 | unsigned long long kbytes_written = 0; | |
779 | char *fsname; | |
780 | int fsflags; | |
781 | int fsblocksize; | |
782 | ||
783 | if (!(fs->flags & EXT2_FLAG_RW)) | |
784 | return EXT2_ET_FILE_RO; | |
785 | ||
786 | if (fs->flags & EXT2_FLAG_DIRTY) | |
787 | ext2fs_flush(fs); /* Force out any modifications */ | |
788 | ||
789 | recover_retval = recover_ext3_journal(fs); | |
790 | ||
791 | /* | |
792 | * Reload the filesystem context to get up-to-date data from disk | |
793 | * because journal recovery will change the filesystem under us. | |
794 | */ | |
795 | if (fs->super->s_kbytes_written && | |
796 | fs->io->manager->get_stats) | |
797 | fs->io->manager->get_stats(fs->io, &stats); | |
798 | if (stats && stats->bytes_written) | |
799 | kbytes_written = stats->bytes_written >> 10; | |
800 | ||
801 | ext2fs_mmp_stop(fs); | |
6ae16a68 TT |
802 | fsname = fs->device_name; |
803 | fs->device_name = NULL; | |
759c46cf DW |
804 | fsflags = fs->flags; |
805 | fsblocksize = fs->blocksize; | |
806 | ext2fs_free(fs); | |
6ae16a68 TT |
807 | *fsp = NULL; |
808 | retval = ext2fs_open(fsname, fsflags, 0, fsblocksize, io_ptr, fsp); | |
809 | ext2fs_free_mem(&fsname); | |
759c46cf DW |
810 | if (retval) |
811 | return retval; | |
812 | ||
813 | fs = *fsp; | |
814 | fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; | |
815 | fs->super->s_kbytes_written += kbytes_written; | |
816 | ||
817 | /* Set the superblock flags */ | |
818 | ext2fs_clear_recover(fs, recover_retval != 0); | |
819 | ||
820 | /* | |
821 | * Do one last sanity check, and propagate journal->s_errno to | |
822 | * the EXT2_ERROR_FS flag in the fs superblock if needed. | |
823 | */ | |
824 | retval = ext2fs_check_ext3_journal(fs); | |
825 | return retval ? retval : recover_retval; | |
826 | } | |
827 | ||
828 | errcode_t ext2fs_open_journal(ext2_filsys fs, journal_t **j) | |
829 | { | |
830 | journal_t *journal; | |
831 | errcode_t retval; | |
832 | ||
833 | journal_init_revoke_caches(); | |
834 | retval = ext2fs_get_journal(fs, &journal); | |
835 | if (retval) | |
836 | return retval; | |
837 | ||
838 | retval = ext2fs_journal_load(journal); | |
839 | if (retval) | |
840 | goto errout; | |
841 | ||
842 | retval = journal_init_revoke(journal, 1024); | |
843 | if (retval) | |
844 | goto errout; | |
845 | ||
846 | if (journal->j_failed_commit) { | |
847 | journal->j_superblock->s_errno = -EINVAL; | |
848 | mark_buffer_dirty(journal->j_sb_buffer); | |
849 | } | |
850 | ||
851 | *j = journal; | |
852 | return 0; | |
853 | ||
854 | errout: | |
855 | journal_destroy_revoke(journal); | |
856 | journal_destroy_revoke_caches(); | |
857 | ext2fs_journal_release(fs, journal, 1, 0); | |
858 | return retval; | |
859 | } | |
860 | ||
861 | errcode_t ext2fs_close_journal(ext2_filsys fs, journal_t **j) | |
862 | { | |
863 | journal_t *journal = *j; | |
864 | ||
865 | journal_destroy_revoke(journal); | |
866 | journal_destroy_revoke_caches(); | |
867 | ext2fs_journal_release(fs, journal, 0, 0); | |
868 | *j = NULL; | |
869 | ||
870 | return 0; | |
871 | } | |
872 | ||
873 | void jbd2_commit_block_csum_set(journal_t *j, struct buffer_head *bh) | |
874 | { | |
875 | struct commit_header *h; | |
876 | __u32 csum; | |
877 | ||
878 | if (!journal_has_csum_v2or3(j)) | |
879 | return; | |
880 | ||
881 | h = (struct commit_header *)(bh->b_data); | |
882 | h->h_chksum_type = 0; | |
883 | h->h_chksum_size = 0; | |
884 | h->h_chksum[0] = 0; | |
885 | csum = jbd2_chksum(j, j->j_csum_seed, bh->b_data, j->j_blocksize); | |
886 | h->h_chksum[0] = ext2fs_cpu_to_be32(csum); | |
887 | } | |
888 | ||
889 | void jbd2_revoke_csum_set(journal_t *j, struct buffer_head *bh) | |
890 | { | |
891 | struct journal_revoke_tail *tail; | |
892 | __u32 csum; | |
893 | ||
894 | if (!journal_has_csum_v2or3(j)) | |
895 | return; | |
896 | ||
897 | tail = (struct journal_revoke_tail *)(bh->b_data + j->j_blocksize - | |
898 | sizeof(struct journal_revoke_tail)); | |
899 | tail->r_checksum = 0; | |
900 | csum = jbd2_chksum(j, j->j_csum_seed, bh->b_data, j->j_blocksize); | |
901 | tail->r_checksum = ext2fs_cpu_to_be32(csum); | |
902 | } | |
903 | ||
904 | void jbd2_descr_block_csum_set(journal_t *j, struct buffer_head *bh) | |
905 | { | |
906 | struct journal_block_tail *tail; | |
907 | __u32 csum; | |
908 | ||
909 | if (!journal_has_csum_v2or3(j)) | |
910 | return; | |
911 | ||
912 | tail = (struct journal_block_tail *)(bh->b_data + j->j_blocksize - | |
913 | sizeof(struct journal_block_tail)); | |
914 | tail->t_checksum = 0; | |
915 | csum = jbd2_chksum(j, j->j_csum_seed, bh->b_data, j->j_blocksize); | |
916 | tail->t_checksum = ext2fs_cpu_to_be32(csum); | |
917 | } | |
918 | ||
919 | void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, | |
920 | struct buffer_head *bh, __u32 sequence) | |
921 | { | |
922 | journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; | |
923 | __u32 csum32; | |
924 | __be32 seq; | |
925 | ||
926 | if (!journal_has_csum_v2or3(j)) | |
927 | return; | |
928 | ||
929 | seq = ext2fs_cpu_to_be32(sequence); | |
930 | csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); | |
931 | csum32 = jbd2_chksum(j, csum32, bh->b_data, bh->b_size); | |
932 | ||
4ee26699 | 933 | if (jfs_has_feature_csum3(j)) |
759c46cf DW |
934 | tag3->t_checksum = ext2fs_cpu_to_be32(csum32); |
935 | else | |
936 | tag->t_checksum = ext2fs_cpu_to_be16(csum32); | |
937 | } | |
938 |