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