Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 1 | /* |
| 2 | * csum.c --- checksumming of ext3 structures |
| 3 | * |
| 4 | * Copyright (C) 2006 Cluster File Systems, Inc. |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 5 | * Copyright (C) 2006, 2007 by Andreas Dilger <adilger@clusterfs.com> |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 6 | * |
| 7 | * %Begin-Header% |
Theodore Ts'o | 543547a | 2010-05-17 21:31:56 -0400 | [diff] [blame] | 8 | * This file may be redistributed under the terms of the GNU Library |
| 9 | * General Public License, version 2. |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 10 | * %End-Header% |
| 11 | */ |
| 12 | |
Theodore Ts'o | 0eeec8a | 2008-09-12 09:10:39 -0400 | [diff] [blame] | 13 | #if HAVE_SYS_TYPES_H |
| 14 | #include <sys/types.h> |
| 15 | #endif |
| 16 | |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 17 | #include "ext2_fs.h" |
| 18 | #include "ext2fs.h" |
| 19 | #include "crc16.h" |
| 20 | #include <assert.h> |
| 21 | |
| 22 | #ifndef offsetof |
| 23 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
| 24 | #endif |
| 25 | |
| 26 | #ifdef DEBUG |
| 27 | #define STATIC |
| 28 | #else |
| 29 | #define STATIC static |
| 30 | #endif |
| 31 | |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 32 | __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 33 | { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 34 | struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, |
| 35 | group); |
| 36 | size_t size = EXT2_DESC_SIZE(fs->super); |
| 37 | size_t offset; |
| 38 | __u16 crc; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 39 | |
| 40 | if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 41 | size_t offset = offsetof(struct ext2_group_desc, bg_checksum); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 42 | |
| 43 | #ifdef WORDS_BIGENDIAN |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 44 | struct ext4_group_desc swabdesc; |
| 45 | size_t save_size = size; |
| 46 | const size_t ext4_bg_size = sizeof(struct ext4_group_desc); |
| 47 | struct ext2_group_desc *save_desc = desc; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 48 | |
| 49 | /* Have to swab back to little-endian to do the checksum */ |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 50 | if (size > ext4_bg_size) |
| 51 | size = ext4_bg_size; |
| 52 | memcpy(&swabdesc, desc, size); |
| 53 | ext2fs_swap_group_desc2(fs, |
| 54 | (struct ext2_group_desc *) &swabdesc); |
| 55 | desc = (struct ext2_group_desc *) &swabdesc; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 56 | |
| 57 | group = ext2fs_swab32(group); |
| 58 | #endif |
Theodore Ts'o | c4dcb1c | 2008-08-24 22:44:33 -0400 | [diff] [blame] | 59 | crc = ext2fs_crc16(~0, fs->super->s_uuid, |
| 60 | sizeof(fs->super->s_uuid)); |
| 61 | crc = ext2fs_crc16(crc, &group, sizeof(group)); |
| 62 | crc = ext2fs_crc16(crc, desc, offset); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 63 | offset += sizeof(desc->bg_checksum); /* skip checksum */ |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 64 | /* for checksum of struct ext4_group_desc do the rest...*/ |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 65 | if (offset < size) { |
Theodore Ts'o | c4dcb1c | 2008-08-24 22:44:33 -0400 | [diff] [blame] | 66 | crc = ext2fs_crc16(crc, (char *)desc + offset, |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 67 | size - offset); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 68 | } |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 69 | #ifdef WORDS_BIGENDIAN |
| 70 | /* |
| 71 | * If the size of the bg descriptor is greater than 64 |
| 72 | * bytes, which is the size of the traditional ext4 bg |
| 73 | * descriptor, checksum the rest of the descriptor here |
| 74 | */ |
| 75 | if (save_size > ext4_bg_size) |
| 76 | crc = ext2fs_crc16(crc, |
| 77 | (char *)save_desc + ext4_bg_size, |
| 78 | save_size - ext4_bg_size); |
| 79 | #endif |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 80 | } |
| 81 | |
| 82 | return crc; |
| 83 | } |
| 84 | |
| 85 | int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) |
| 86 | { |
Theodore Ts'o | 4729455 | 2008-07-13 19:03:59 -0400 | [diff] [blame] | 87 | if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
| 88 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 89 | (ext2fs_bg_checksum(fs, group) != |
Theodore Ts'o | 4729455 | 2008-07-13 19:03:59 -0400 | [diff] [blame] | 90 | ext2fs_group_desc_csum(fs, group))) |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 91 | return 0; |
| 92 | |
| 93 | return 1; |
| 94 | } |
| 95 | |
| 96 | void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) |
| 97 | { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 98 | if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
| 99 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) |
| 100 | return; |
| 101 | |
| 102 | /* ext2fs_bg_checksum_set() sets the actual checksum field but |
| 103 | * does not calculate the checksum itself. */ |
| 104 | ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, |
| 108 | __u32 inodes_per_grp, dgrp_t grp_no) |
| 109 | { |
| 110 | ext2_ino_t i, start_ino, end_ino; |
| 111 | |
| 112 | start_ino = grp_no * inodes_per_grp + 1; |
| 113 | end_ino = start_ino + inodes_per_grp - 1; |
| 114 | |
| 115 | for (i = end_ino; i >= start_ino; i--) { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 116 | if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 117 | return i - start_ino + 1; |
| 118 | } |
| 119 | return inodes_per_grp; |
| 120 | } |
| 121 | |
| 122 | /* update the bitmap flags, set the itable high watermark, and calculate |
| 123 | * checksums for the group descriptors */ |
Andreas Dilger | f628ace | 2008-03-31 10:50:19 -0400 | [diff] [blame] | 124 | errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 125 | { |
| 126 | struct ext2_super_block *sb = fs->super; |
Theodore Ts'o | 8895f43 | 2008-06-07 11:53:56 -0400 | [diff] [blame] | 127 | int dirty = 0; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 128 | dgrp_t i; |
| 129 | |
Andreas Dilger | f628ace | 2008-03-31 10:50:19 -0400 | [diff] [blame] | 130 | if (!fs->inode_map) |
| 131 | return EXT2_ET_NO_INODE_BITMAP; |
| 132 | |
Theodore Ts'o | 16b851c | 2008-04-20 23:33:34 -0400 | [diff] [blame] | 133 | if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
| 134 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) |
Andreas Dilger | f628ace | 2008-03-31 10:50:19 -0400 | [diff] [blame] | 135 | return 0; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 136 | |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 137 | for (i = 0; i < fs->group_desc_count; i++) { |
| 138 | __u32 old_csum = ext2fs_bg_checksum(fs, i); |
| 139 | __u32 old_unused = ext2fs_bg_itable_unused(fs, i); |
| 140 | __u32 old_flags = ext2fs_bg_flags(fs, i); |
| 141 | __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 142 | |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 143 | if (old_free_inodes_count == sb->s_inodes_per_group) { |
| 144 | ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); |
| 145 | ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); |
Theodore Ts'o | 16b851c | 2008-04-20 23:33:34 -0400 | [diff] [blame] | 146 | } else { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 147 | int unused = |
| 148 | sb->s_inodes_per_group - |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 149 | find_last_inode_ingrp(fs->inode_map, |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 150 | sb->s_inodes_per_group, i); |
| 151 | |
| 152 | ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); |
| 153 | ext2fs_bg_itable_unused_set(fs, i, unused); |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 154 | } |
| 155 | |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 156 | ext2fs_group_desc_csum_set(fs, i); |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 157 | if (old_flags != ext2fs_bg_flags(fs, i)) |
Andreas Dilger | 80fc4e6 | 2008-03-31 00:40:51 -0400 | [diff] [blame] | 158 | dirty = 1; |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 159 | if (old_unused != ext2fs_bg_itable_unused(fs, i)) |
Andreas Dilger | 80fc4e6 | 2008-03-31 00:40:51 -0400 | [diff] [blame] | 160 | dirty = 1; |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 161 | if (old_csum != ext2fs_bg_checksum(fs, i)) |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 162 | dirty = 1; |
| 163 | } |
| 164 | if (dirty) |
| 165 | ext2fs_mark_super_dirty(fs); |
Andreas Dilger | f628ace | 2008-03-31 10:50:19 -0400 | [diff] [blame] | 166 | return 0; |
Jose R. Santos | ca2634a | 2007-10-21 21:03:19 -0500 | [diff] [blame] | 167 | } |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 168 | |
| 169 | #ifdef DEBUG |
Eric Sandeen | c816ecb | 2010-12-14 13:00:01 -0600 | [diff] [blame] | 170 | #include "e2p/e2p.h" |
| 171 | |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 172 | void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) |
| 173 | { |
| 174 | __u16 crc1, crc2, crc3; |
| 175 | dgrp_t swabgroup; |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 176 | struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, |
| 177 | group); |
| 178 | size_t size = EXT2_DESC_SIZE(fs->super); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 179 | struct ext2_super_block *sb = fs->super; |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 180 | int offset = offsetof(struct ext2_group_desc, bg_checksum); |
| 181 | #ifdef WORDS_BIGENDIAN |
| 182 | struct ext4_group_desc swabdesc; |
| 183 | struct ext2_group_desc *save_desc = desc; |
| 184 | const size_t ext4_bg_size = sizeof(struct ext4_group_desc); |
| 185 | size_t save_size = size; |
| 186 | #endif |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 187 | |
Theodore Ts'o | 1d18a55 | 2010-02-15 09:51:28 -0500 | [diff] [blame] | 188 | #ifdef WORDS_BIGENDIAN |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 189 | /* Have to swab back to little-endian to do the checksum */ |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 190 | if (size > ext4_bg_size) |
| 191 | size = ext4_bg_size; |
| 192 | memcpy(&swabdesc, desc, size); |
| 193 | ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); |
| 194 | desc = (struct ext2_group_desc *) &swabdesc; |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 195 | |
| 196 | swabgroup = ext2fs_swab32(group); |
| 197 | #else |
| 198 | swabgroup = group; |
| 199 | #endif |
| 200 | |
| 201 | crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); |
| 202 | crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 203 | crc3 = ext2fs_crc16(crc2, desc, offset); |
| 204 | offset += sizeof(desc->bg_checksum); /* skip checksum */ |
| 205 | /* for checksum of struct ext4_group_desc do the rest...*/ |
| 206 | if (offset < size) |
| 207 | crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); |
| 208 | #ifdef WORDS_BIGENDIAN |
| 209 | if (save_size > ext4_bg_size) |
| 210 | crc3 = ext2fs_crc16(crc3, (char *)save_desc + ext4_bg_size, |
| 211 | save_size - ext4_bg_size); |
| 212 | #endif |
| 213 | |
| 214 | printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n", |
| 215 | msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, |
Eric Sandeen | c816ecb | 2010-12-14 13:00:01 -0600 | [diff] [blame] | 216 | ext2fs_group_desc_csum(fs, group)); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 217 | } |
| 218 | |
| 219 | unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, |
| 220 | 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; |
| 221 | |
| 222 | int main(int argc, char **argv) |
| 223 | { |
| 224 | struct ext2_super_block param; |
| 225 | errcode_t retval; |
| 226 | ext2_filsys fs; |
| 227 | int i; |
| 228 | __u16 csum1, csum2, csum_known = 0xd3a4; |
| 229 | |
| 230 | memset(¶m, 0, sizeof(param)); |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 231 | ext2fs_blocks_count_set(¶m, 32768); |
| 232 | #if 0 |
| 233 | param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT; |
| 234 | param.s_desc_size = 128; |
| 235 | csum_known = 0x5b6e; |
| 236 | #endif |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 237 | |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 238 | retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 239 | test_io_manager, &fs); |
| 240 | if (retval) { |
| 241 | com_err("setup", retval, |
| 242 | "While initializing filesystem"); |
| 243 | exit(1); |
| 244 | } |
| 245 | memcpy(fs->super->s_uuid, sb_uuid, 16); |
| 246 | fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; |
| 247 | |
| 248 | for (i=0; i < fs->group_desc_count; i++) { |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 249 | ext2fs_block_bitmap_loc_set(fs, i, 124); |
| 250 | ext2fs_inode_bitmap_loc_set(fs, i, 125); |
| 251 | ext2fs_inode_table_loc_set(fs, i, 126); |
| 252 | ext2fs_bg_free_blocks_count_set(fs, i, 31119); |
| 253 | ext2fs_bg_free_inodes_count_set(fs, i, 15701); |
| 254 | ext2fs_bg_used_dirs_count_set(fs, i, 2); |
| 255 | ext2fs_bg_flags_zap(fs, i); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 256 | }; |
| 257 | |
| 258 | csum1 = ext2fs_group_desc_csum(fs, 0); |
| 259 | print_csum("csum0000", fs, 0); |
| 260 | |
| 261 | if (csum1 != csum_known) { |
| 262 | printf("checksum for group 0 should be %04x\n", csum_known); |
| 263 | exit(1); |
| 264 | } |
| 265 | csum2 = ext2fs_group_desc_csum(fs, 1); |
| 266 | print_csum("csum0001", fs, 1); |
| 267 | if (csum1 == csum2) { |
| 268 | printf("checksums for different groups shouldn't match\n"); |
| 269 | exit(1); |
| 270 | } |
| 271 | csum2 = ext2fs_group_desc_csum(fs, 2); |
| 272 | print_csum("csumffff", fs, 2); |
| 273 | if (csum1 == csum2) { |
| 274 | printf("checksums for different groups shouldn't match\n"); |
| 275 | exit(1); |
| 276 | } |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 277 | ext2fs_bg_checksum_set(fs, 0, csum1); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 278 | csum2 = ext2fs_group_desc_csum(fs, 0); |
| 279 | print_csum("csum_set", fs, 0); |
| 280 | if (csum1 != csum2) { |
| 281 | printf("checksums should not depend on checksum field\n"); |
| 282 | exit(1); |
| 283 | } |
| 284 | if (!ext2fs_group_desc_csum_verify(fs, 0)) { |
| 285 | printf("checksums should verify against gd_checksum\n"); |
| 286 | exit(1); |
| 287 | } |
| 288 | memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); |
| 289 | print_csum("new_uuid", fs, 0); |
| 290 | if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { |
| 291 | printf("checksums for different filesystems shouldn't match\n"); |
| 292 | exit(1); |
| 293 | } |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 294 | csum1 = ext2fs_group_desc_csum(fs, 0); |
| 295 | ext2fs_bg_checksum_set(fs, 0, csum1); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 296 | print_csum("csum_new", fs, 0); |
JP Abgrall | e0ed740 | 2014-03-19 19:08:39 -0700 | [diff] [blame] | 297 | ext2fs_bg_free_blocks_count_set(fs, 0, 1); |
Theodore Ts'o | 470e737 | 2009-05-29 11:01:22 -0400 | [diff] [blame] | 298 | csum2 = ext2fs_group_desc_csum(fs, 0); |
| 299 | print_csum("csum_blk", fs, 0); |
| 300 | if (csum1 == csum2) { |
| 301 | printf("checksums for different data shouldn't match\n"); |
| 302 | exit(1); |
| 303 | } |
| 304 | |
| 305 | return 0; |
| 306 | } |
| 307 | #endif |