diff options
author | David Sterba <dsterba@suse.cz> | 2013-03-06 09:57:46 -0500 |
---|---|---|
committer | Chris Mason <chris.mason@fusionio.com> | 2013-05-07 10:50:27 -0400 |
commit | 1104a8855109a4051d74977f819a13b4516aa11e (patch) | |
tree | c81894968089f010c293bbba501a18e6331204e1 /fs/btrfs | |
parent | b6919a58f09db5daaa29b0326d53513ee418b84b (diff) |
btrfs: enhance superblock checks
The superblock checksum is not verified upon mount. <awkward silence>
Add that check and also reorder existing checks to a more logical
order.
Current mkfs.btrfs does not calculate the correct checksum of
super_block and thus a freshly created filesytem will fail to mount when
this patch is applied.
First transaction commit calculates correct superblock checksum and
saves it to disk.
Reproducer:
$ mfks.btrfs /dev/sda
$ mount /dev/sda /mnt
$ btrfs scrub start /mnt
$ sleep 5
$ btrfs scrub status /mnt
... super:2 ...
Signed-off-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.h | 6 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 82 |
2 files changed, 71 insertions, 17 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 78b9d457d723..63c328a9ce95 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -2793,8 +2793,10 @@ BTRFS_SETGET_STACK_FUNCS(super_cache_generation, struct btrfs_super_block, | |||
2793 | 2793 | ||
2794 | static inline int btrfs_super_csum_size(struct btrfs_super_block *s) | 2794 | static inline int btrfs_super_csum_size(struct btrfs_super_block *s) |
2795 | { | 2795 | { |
2796 | int t = btrfs_super_csum_type(s); | 2796 | u16 t = btrfs_super_csum_type(s); |
2797 | BUG_ON(t >= ARRAY_SIZE(btrfs_csum_sizes)); | 2797 | /* |
2798 | * csum type is validated at mount time | ||
2799 | */ | ||
2798 | return btrfs_csum_sizes[t]; | 2800 | return btrfs_csum_sizes[t]; |
2799 | } | 2801 | } |
2800 | 2802 | ||
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 2bc1ecf5e840..bc423f7eddce 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -357,6 +357,44 @@ out: | |||
357 | } | 357 | } |
358 | 358 | ||
359 | /* | 359 | /* |
360 | * Return 0 if the superblock checksum type matches the checksum value of that | ||
361 | * algorithm. Pass the raw disk superblock data. | ||
362 | */ | ||
363 | static int btrfs_check_super_csum(char *raw_disk_sb) | ||
364 | { | ||
365 | struct btrfs_super_block *disk_sb = | ||
366 | (struct btrfs_super_block *)raw_disk_sb; | ||
367 | u16 csum_type = btrfs_super_csum_type(disk_sb); | ||
368 | int ret = 0; | ||
369 | |||
370 | if (csum_type == BTRFS_CSUM_TYPE_CRC32) { | ||
371 | u32 crc = ~(u32)0; | ||
372 | const int csum_size = sizeof(crc); | ||
373 | char result[csum_size]; | ||
374 | |||
375 | /* | ||
376 | * The super_block structure does not span the whole | ||
377 | * BTRFS_SUPER_INFO_SIZE range, we expect that the unused space | ||
378 | * is filled with zeros and is included in the checkum. | ||
379 | */ | ||
380 | crc = btrfs_csum_data(raw_disk_sb + BTRFS_CSUM_SIZE, | ||
381 | crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); | ||
382 | btrfs_csum_final(crc, result); | ||
383 | |||
384 | if (memcmp(raw_disk_sb, result, csum_size)) | ||
385 | ret = 1; | ||
386 | } | ||
387 | |||
388 | if (csum_type >= ARRAY_SIZE(btrfs_csum_sizes)) { | ||
389 | printk(KERN_ERR "btrfs: unsupported checksum algorithm %u\n", | ||
390 | csum_type); | ||
391 | ret = 1; | ||
392 | } | ||
393 | |||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | /* | ||
360 | * helper to read a given tree block, doing retries as required when | 398 | * helper to read a given tree block, doing retries as required when |
361 | * the checksums don't match and we have alternate mirrors to try. | 399 | * the checksums don't match and we have alternate mirrors to try. |
362 | */ | 400 | */ |
@@ -2249,12 +2287,31 @@ int open_ctree(struct super_block *sb, | |||
2249 | fs_info, BTRFS_ROOT_TREE_OBJECTID); | 2287 | fs_info, BTRFS_ROOT_TREE_OBJECTID); |
2250 | 2288 | ||
2251 | invalidate_bdev(fs_devices->latest_bdev); | 2289 | invalidate_bdev(fs_devices->latest_bdev); |
2290 | |||
2291 | /* | ||
2292 | * Read super block and check the signature bytes only | ||
2293 | */ | ||
2252 | bh = btrfs_read_dev_super(fs_devices->latest_bdev); | 2294 | bh = btrfs_read_dev_super(fs_devices->latest_bdev); |
2253 | if (!bh) { | 2295 | if (!bh) { |
2254 | err = -EINVAL; | 2296 | err = -EINVAL; |
2255 | goto fail_alloc; | 2297 | goto fail_alloc; |
2256 | } | 2298 | } |
2257 | 2299 | ||
2300 | /* | ||
2301 | * We want to check superblock checksum, the type is stored inside. | ||
2302 | * Pass the whole disk block of size BTRFS_SUPER_INFO_SIZE (4k). | ||
2303 | */ | ||
2304 | if (btrfs_check_super_csum(bh->b_data)) { | ||
2305 | printk(KERN_ERR "btrfs: superblock checksum mismatch\n"); | ||
2306 | err = -EINVAL; | ||
2307 | goto fail_alloc; | ||
2308 | } | ||
2309 | |||
2310 | /* | ||
2311 | * super_copy is zeroed at allocation time and we never touch the | ||
2312 | * following bytes up to INFO_SIZE, the checksum is calculated from | ||
2313 | * the whole block of INFO_SIZE | ||
2314 | */ | ||
2258 | memcpy(fs_info->super_copy, bh->b_data, sizeof(*fs_info->super_copy)); | 2315 | memcpy(fs_info->super_copy, bh->b_data, sizeof(*fs_info->super_copy)); |
2259 | memcpy(fs_info->super_for_commit, fs_info->super_copy, | 2316 | memcpy(fs_info->super_for_commit, fs_info->super_copy, |
2260 | sizeof(*fs_info->super_for_commit)); | 2317 | sizeof(*fs_info->super_for_commit)); |
@@ -2262,6 +2319,13 @@ int open_ctree(struct super_block *sb, | |||
2262 | 2319 | ||
2263 | memcpy(fs_info->fsid, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); | 2320 | memcpy(fs_info->fsid, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); |
2264 | 2321 | ||
2322 | ret = btrfs_check_super_valid(fs_info, sb->s_flags & MS_RDONLY); | ||
2323 | if (ret) { | ||
2324 | printk(KERN_ERR "btrfs: superblock contains fatal errors\n"); | ||
2325 | err = -EINVAL; | ||
2326 | goto fail_alloc; | ||
2327 | } | ||
2328 | |||
2265 | disk_super = fs_info->super_copy; | 2329 | disk_super = fs_info->super_copy; |
2266 | if (!btrfs_super_root(disk_super)) | 2330 | if (!btrfs_super_root(disk_super)) |
2267 | goto fail_alloc; | 2331 | goto fail_alloc; |
@@ -2270,13 +2334,6 @@ int open_ctree(struct super_block *sb, | |||
2270 | if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR) | 2334 | if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR) |
2271 | set_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state); | 2335 | set_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state); |
2272 | 2336 | ||
2273 | ret = btrfs_check_super_valid(fs_info, sb->s_flags & MS_RDONLY); | ||
2274 | if (ret) { | ||
2275 | printk(KERN_ERR "btrfs: superblock contains fatal errors\n"); | ||
2276 | err = ret; | ||
2277 | goto fail_alloc; | ||
2278 | } | ||
2279 | |||
2280 | /* | 2337 | /* |
2281 | * run through our array of backup supers and setup | 2338 | * run through our array of backup supers and setup |
2282 | * our ring pointer to the oldest one | 2339 | * our ring pointer to the oldest one |
@@ -3561,14 +3618,9 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) | |||
3561 | static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info, | 3618 | static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info, |
3562 | int read_only) | 3619 | int read_only) |
3563 | { | 3620 | { |
3564 | if (btrfs_super_csum_type(fs_info->super_copy) >= ARRAY_SIZE(btrfs_csum_sizes)) { | 3621 | /* |
3565 | printk(KERN_ERR "btrfs: unsupported checksum algorithm\n"); | 3622 | * Placeholder for checks |
3566 | return -EINVAL; | 3623 | */ |
3567 | } | ||
3568 | |||
3569 | if (read_only) | ||
3570 | return 0; | ||
3571 | |||
3572 | return 0; | 3624 | return 0; |
3573 | } | 3625 | } |
3574 | 3626 | ||