2 * csum.c --- checksumming of ext3 structures
4 * Copyright (C) 2006 Cluster File Systems, Inc.
5 * Copyright (C) 2006, 2007 by Andreas Dilger <adilger@clusterfs.com>
8 * This file may be redistributed under the terms of the GNU Library
9 * General Public License, version 2.
15 #include <sys/types.h>
24 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
33 void ext2fs_init_csum_seed(ext2_filsys fs
)
35 if (ext2fs_has_feature_csum_seed(fs
->super
))
36 fs
->csum_seed
= fs
->super
->s_checksum_seed
;
37 else if (ext2fs_has_feature_metadata_csum(fs
->super
) ||
38 ext2fs_has_feature_ea_inode(fs
->super
))
39 fs
->csum_seed
= ext2fs_crc32c_le(~0, fs
->super
->s_uuid
,
40 sizeof(fs
->super
->s_uuid
));
43 static __u32
ext2fs_mmp_csum(ext2_filsys fs
, struct mmp_struct
*mmp
)
45 int offset
= offsetof(struct mmp_struct
, mmp_checksum
);
47 return ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)mmp
, offset
);
50 int ext2fs_mmp_csum_verify(ext2_filsys fs
, struct mmp_struct
*mmp
)
54 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
57 calculated
= ext2fs_mmp_csum(fs
, mmp
);
59 return ext2fs_le32_to_cpu(mmp
->mmp_checksum
) == calculated
;
62 errcode_t
ext2fs_mmp_csum_set(ext2_filsys fs
, struct mmp_struct
*mmp
)
66 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
69 crc
= ext2fs_mmp_csum(fs
, mmp
);
70 mmp
->mmp_checksum
= ext2fs_cpu_to_le32(crc
);
75 int ext2fs_verify_csum_type(ext2_filsys fs
, struct ext2_super_block
*sb
)
77 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
80 return sb
->s_checksum_type
== EXT2_CRC32C_CHKSUM
;
83 static __u32
ext2fs_superblock_csum(ext2_filsys fs
EXT2FS_ATTR((unused
)),
84 struct ext2_super_block
*sb
)
86 int offset
= offsetof(struct ext2_super_block
, s_checksum
);
88 return ext2fs_crc32c_le(~0, (unsigned char *)sb
, offset
);
91 /* NOTE: The input to this function MUST be in LE order */
92 int ext2fs_superblock_csum_verify(ext2_filsys fs
, struct ext2_super_block
*sb
)
94 __u32 flag
, calculated
;
96 if (fs
->flags
& EXT2_FLAG_SWAP_BYTES
)
97 flag
= EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
;
99 flag
= ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
);
101 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs
->super
, flag
))
104 calculated
= ext2fs_superblock_csum(fs
, sb
);
106 return ext2fs_le32_to_cpu(sb
->s_checksum
) == calculated
;
109 /* NOTE: The input to this function MUST be in LE order */
110 errcode_t
ext2fs_superblock_csum_set(ext2_filsys fs
,
111 struct ext2_super_block
*sb
)
115 if (fs
->flags
& EXT2_FLAG_SWAP_BYTES
)
116 flag
= EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
;
118 flag
= ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM
);
120 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs
->super
, flag
))
123 crc
= ext2fs_superblock_csum(fs
, sb
);
124 sb
->s_checksum
= ext2fs_cpu_to_le32(crc
);
129 static errcode_t
ext2fs_ext_attr_block_csum(ext2_filsys fs
,
130 ext2_ino_t inum
EXT2FS_ATTR((unused
)),
132 struct ext2_ext_attr_header
*hdr
,
135 char *buf
= (char *)hdr
;
136 __u32 old_crc
= hdr
->h_checksum
;
139 block
= ext2fs_cpu_to_le64(block
);
140 *crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&block
,
142 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)buf
, fs
->blocksize
);
143 hdr
->h_checksum
= old_crc
;
148 int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
150 struct ext2_ext_attr_header
*hdr
)
155 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
158 retval
= ext2fs_ext_attr_block_csum(fs
, inum
, block
, hdr
, &calculated
);
162 return ext2fs_le32_to_cpu(hdr
->h_checksum
) == calculated
;
165 errcode_t
ext2fs_ext_attr_block_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
167 struct ext2_ext_attr_header
*hdr
)
172 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
175 retval
= ext2fs_ext_attr_block_csum(fs
, inum
, block
, hdr
, &crc
);
178 hdr
->h_checksum
= ext2fs_cpu_to_le32(crc
);
182 static __u16
do_nothing16(__u16 x
)
187 static __u16
disk_to_host16(__u16 x
)
189 return ext2fs_le16_to_cpu(x
);
192 static errcode_t
__get_dx_countlimit(ext2_filsys fs
,
193 struct ext2_dir_entry
*dirent
,
194 struct ext2_dx_countlimit
**cc
,
198 struct ext2_dir_entry
*dp
;
199 struct ext2_dx_root_info
*root
;
200 struct ext2_dx_countlimit
*c
;
201 int count_offset
, max_sane_entries
;
202 unsigned int rec_len
;
203 __u16 (*translate
)(__u16
) = (need_swab
? disk_to_host16
: do_nothing16
);
205 rec_len
= translate(dirent
->rec_len
);
207 if (rec_len
== fs
->blocksize
&& translate(dirent
->name_len
) == 0)
209 else if (rec_len
== 12) {
210 dp
= (struct ext2_dir_entry
*)(((char *)dirent
) + rec_len
);
211 rec_len
= translate(dp
->rec_len
);
212 if (rec_len
!= fs
->blocksize
- 12)
213 return EXT2_ET_DB_NOT_FOUND
;
214 root
= (struct ext2_dx_root_info
*)(((char *)dp
+ 12));
215 if (root
->reserved_zero
||
216 root
->info_length
!= sizeof(struct ext2_dx_root_info
))
217 return EXT2_ET_DB_NOT_FOUND
;
220 return EXT2_ET_DB_NOT_FOUND
;
222 c
= (struct ext2_dx_countlimit
*)(((char *)dirent
) + count_offset
);
223 max_sane_entries
= (fs
->blocksize
- count_offset
) /
224 sizeof(struct ext2_dx_entry
);
225 if (ext2fs_le16_to_cpu(c
->limit
) > max_sane_entries
||
226 ext2fs_le16_to_cpu(c
->count
) > max_sane_entries
)
227 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
230 *offset
= count_offset
;
237 errcode_t
ext2fs_get_dx_countlimit(ext2_filsys fs
,
238 struct ext2_dir_entry
*dirent
,
239 struct ext2_dx_countlimit
**cc
,
242 return __get_dx_countlimit(fs
, dirent
, cc
, offset
, 0);
245 void ext2fs_initialize_dirent_tail(ext2_filsys fs
,
246 struct ext2_dir_entry_tail
*t
)
248 memset(t
, 0, sizeof(struct ext2_dir_entry_tail
));
249 ext2fs_set_rec_len(fs
, sizeof(struct ext2_dir_entry_tail
),
250 (struct ext2_dir_entry
*)t
);
251 t
->det_reserved_name_len
= EXT2_DIR_NAME_LEN_CSUM
;
254 static errcode_t
__get_dirent_tail(ext2_filsys fs
,
255 struct ext2_dir_entry
*dirent
,
256 struct ext2_dir_entry_tail
**tt
,
259 struct ext2_dir_entry
*d
;
261 struct ext2_dir_entry_tail
*t
;
262 unsigned int rec_len
;
263 errcode_t retval
= 0;
264 __u16 (*translate
)(__u16
) = (need_swab
? disk_to_host16
: do_nothing16
);
266 if (fs
->blocksize
< 1024)
267 return EXT2_FILSYS_CORRUPTED
; /* Should never happen */
270 top
= EXT2_DIRENT_TAIL(dirent
, fs
->blocksize
);
272 while ((void *) d
< top
) {
273 rec_len
= translate(d
->rec_len
);
274 if ((rec_len
< 8) || (rec_len
& 0x03))
275 return EXT2_ET_DIR_CORRUPTED
;
276 d
= (struct ext2_dir_entry
*)(((char *)d
) + rec_len
);
279 if ((char *)d
> ((char *)dirent
+ fs
->blocksize
))
280 return EXT2_ET_DIR_CORRUPTED
;
282 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
284 t
= (struct ext2_dir_entry_tail
*)d
;
285 if (t
->det_reserved_zero1
||
286 translate(t
->det_rec_len
) != sizeof(struct ext2_dir_entry_tail
) ||
287 translate(t
->det_reserved_name_len
) != EXT2_DIR_NAME_LEN_CSUM
)
288 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
295 int ext2fs_dirent_has_tail(ext2_filsys fs
, struct ext2_dir_entry
*dirent
)
297 return __get_dirent_tail(fs
, dirent
, NULL
, 0) !=
298 EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
301 static errcode_t
ext2fs_dirent_csum(ext2_filsys fs
, ext2_ino_t inum
,
302 struct ext2_dir_entry
*dirent
, __u32
*crc
,
306 char *buf
= (char *)dirent
;
308 struct ext2_inode inode
;
310 retval
= ext2fs_read_inode(fs
, inum
, &inode
);
314 inum
= ext2fs_cpu_to_le32(inum
);
315 gen
= ext2fs_cpu_to_le32(inode
.i_generation
);
316 *crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&inum
,
318 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)&gen
, sizeof(gen
));
319 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)buf
, size
);
324 int ext2fs_dirent_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
325 struct ext2_dir_entry
*dirent
)
329 struct ext2_dir_entry_tail
*t
;
331 retval
= __get_dirent_tail(fs
, dirent
, &t
, 1);
336 * The checksum field is overlaid with the dirent->name field
337 * so the swapfs.c functions won't change the endianness.
339 retval
= ext2fs_dirent_csum(fs
, inum
, dirent
, &calculated
,
340 (char *)t
- (char *)dirent
);
343 return ext2fs_le32_to_cpu(t
->det_checksum
) == calculated
;
346 static errcode_t
ext2fs_dirent_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
347 struct ext2_dir_entry
*dirent
)
351 struct ext2_dir_entry_tail
*t
;
353 retval
= __get_dirent_tail(fs
, dirent
, &t
, 1);
357 /* swapfs.c functions don't change the checksum endianness */
358 retval
= ext2fs_dirent_csum(fs
, inum
, dirent
, &crc
,
359 (char *)t
- (char *)dirent
);
362 t
->det_checksum
= ext2fs_cpu_to_le32(crc
);
366 errcode_t
ext2fs_dx_csum(ext2_filsys fs
, ext2_ino_t inum
,
367 struct ext2_dir_entry
*dirent
,
368 __u32
*crc
, struct ext2_dx_tail
**ret_t
)
371 char *buf
= (char *)dirent
;
373 __u32 gen
, dummy_csum
= 0;
374 struct ext2_inode inode
;
375 struct ext2_dx_tail
*t
;
376 struct ext2_dx_countlimit
*c
;
377 int count_offset
, limit
, count
;
379 retval
= __get_dx_countlimit(fs
, dirent
, &c
, &count_offset
, 1);
382 limit
= ext2fs_le16_to_cpu(c
->limit
);
383 count
= ext2fs_le16_to_cpu(c
->count
);
384 if (count_offset
+ (limit
* sizeof(struct ext2_dx_entry
)) >
385 fs
->blocksize
- sizeof(struct ext2_dx_tail
))
386 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
387 /* htree structs are accessed in LE order */
388 t
= (struct ext2_dx_tail
*)(((struct ext2_dx_entry
*)c
) + limit
);
390 size
= count_offset
+ (count
* sizeof(struct ext2_dx_entry
));
392 retval
= ext2fs_read_inode(fs
, inum
, &inode
);
396 inum
= ext2fs_cpu_to_le32(inum
);
397 gen
= ext2fs_cpu_to_le32(inode
.i_generation
);
398 *crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&inum
,
400 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)&gen
, sizeof(gen
));
401 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)buf
, size
);
402 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)t
, 4);
403 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)&dummy_csum
, 4);
410 static int ext2fs_dx_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
411 struct ext2_dir_entry
*dirent
)
415 struct ext2_dx_tail
*t
;
417 retval
= ext2fs_dx_csum(fs
, inum
, dirent
, &calculated
, &t
);
421 return ext2fs_le32_to_cpu(t
->dt_checksum
) == calculated
;
424 static errcode_t
ext2fs_dx_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
425 struct ext2_dir_entry
*dirent
)
428 errcode_t retval
= 0;
429 struct ext2_dx_tail
*t
;
431 retval
= ext2fs_dx_csum(fs
, inum
, dirent
, &crc
, &t
);
434 t
->dt_checksum
= ext2fs_cpu_to_le32(crc
);
438 int ext2fs_dir_block_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
439 struct ext2_dir_entry
*dirent
)
441 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
444 if (__get_dirent_tail(fs
, dirent
, NULL
, 1) == 0)
445 return ext2fs_dirent_csum_verify(fs
, inum
, dirent
);
446 if (__get_dx_countlimit(fs
, dirent
, NULL
, NULL
, 1) == 0)
447 return ext2fs_dx_csum_verify(fs
, inum
, dirent
);
452 errcode_t
ext2fs_dir_block_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
453 struct ext2_dir_entry
*dirent
)
455 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
458 if (__get_dirent_tail(fs
, dirent
, NULL
, 1) == 0)
459 return ext2fs_dirent_csum_set(fs
, inum
, dirent
);
460 if (__get_dx_countlimit(fs
, dirent
, NULL
, NULL
, 1) == 0)
461 return ext2fs_dx_csum_set(fs
, inum
, dirent
);
463 if (fs
->flags
& EXT2_FLAG_IGNORE_CSUM_ERRORS
)
465 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM
;
468 #define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
469 (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
471 static struct ext3_extent_tail
*get_extent_tail(struct ext3_extent_header
*h
)
473 return (struct ext3_extent_tail
*)(((char *)h
) +
474 EXT3_EXTENT_TAIL_OFFSET(h
));
477 static errcode_t
ext2fs_extent_block_csum(ext2_filsys fs
, ext2_ino_t inum
,
478 struct ext3_extent_header
*eh
,
484 struct ext2_inode inode
;
486 size
= EXT3_EXTENT_TAIL_OFFSET(eh
) + offsetof(struct ext3_extent_tail
,
489 retval
= ext2fs_read_inode(fs
, inum
, &inode
);
492 inum
= ext2fs_cpu_to_le32(inum
);
493 gen
= ext2fs_cpu_to_le32(inode
.i_generation
);
494 *crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&inum
,
496 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)&gen
, sizeof(gen
));
497 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)eh
, size
);
502 int ext2fs_extent_block_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
503 struct ext3_extent_header
*eh
)
506 __u32 provided
, calculated
;
507 struct ext3_extent_tail
*t
= get_extent_tail(eh
);
510 * The extent tree structures are accessed in LE order, so we must
511 * swap the checksum bytes here.
513 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
516 provided
= ext2fs_le32_to_cpu(t
->et_checksum
);
517 retval
= ext2fs_extent_block_csum(fs
, inum
, eh
, &calculated
);
521 return provided
== calculated
;
524 errcode_t
ext2fs_extent_block_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
525 struct ext3_extent_header
*eh
)
529 struct ext3_extent_tail
*t
= get_extent_tail(eh
);
531 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
535 * The extent tree structures are accessed in LE order, so we must
536 * swap the checksum bytes here.
538 retval
= ext2fs_extent_block_csum(fs
, inum
, eh
, &crc
);
541 t
->et_checksum
= ext2fs_cpu_to_le32(crc
);
545 int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs
, dgrp_t group
,
546 char *bitmap
, int size
)
548 struct ext4_group_desc
*gdp
= (struct ext4_group_desc
*)
549 ext2fs_group_desc(fs
, fs
->group_desc
, group
);
550 __u32 provided
, calculated
;
552 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
554 provided
= gdp
->bg_inode_bitmap_csum_lo
;
555 calculated
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)bitmap
,
557 if (EXT2_DESC_SIZE(fs
->super
) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END
)
558 provided
|= (__u32
)gdp
->bg_inode_bitmap_csum_hi
<< 16;
560 calculated
&= 0xFFFF;
562 return provided
== calculated
;
565 errcode_t
ext2fs_inode_bitmap_csum_set(ext2_filsys fs
, dgrp_t group
,
566 char *bitmap
, int size
)
569 struct ext4_group_desc
*gdp
= (struct ext4_group_desc
*)
570 ext2fs_group_desc(fs
, fs
->group_desc
, group
);
572 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
575 crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)bitmap
, size
);
576 gdp
->bg_inode_bitmap_csum_lo
= crc
& 0xFFFF;
577 if (EXT2_DESC_SIZE(fs
->super
) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END
)
578 gdp
->bg_inode_bitmap_csum_hi
= crc
>> 16;
583 int ext2fs_block_bitmap_csum_verify(ext2_filsys fs
, dgrp_t group
,
584 char *bitmap
, int size
)
586 struct ext4_group_desc
*gdp
= (struct ext4_group_desc
*)
587 ext2fs_group_desc(fs
, fs
->group_desc
, group
);
588 __u32 provided
, calculated
;
590 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
592 provided
= gdp
->bg_block_bitmap_csum_lo
;
593 calculated
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)bitmap
,
595 if (EXT2_DESC_SIZE(fs
->super
) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION
)
596 provided
|= (__u32
)gdp
->bg_block_bitmap_csum_hi
<< 16;
598 calculated
&= 0xFFFF;
600 return provided
== calculated
;
603 errcode_t
ext2fs_block_bitmap_csum_set(ext2_filsys fs
, dgrp_t group
,
604 char *bitmap
, int size
)
607 struct ext4_group_desc
*gdp
= (struct ext4_group_desc
*)
608 ext2fs_group_desc(fs
, fs
->group_desc
, group
);
610 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
613 crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)bitmap
, size
);
614 gdp
->bg_block_bitmap_csum_lo
= crc
& 0xFFFF;
615 if (EXT2_DESC_SIZE(fs
->super
) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION
)
616 gdp
->bg_block_bitmap_csum_hi
= crc
>> 16;
621 static errcode_t
ext2fs_inode_csum(ext2_filsys fs
, ext2_ino_t inum
,
622 struct ext2_inode_large
*inode
,
623 __u32
*crc
, int has_hi
)
626 struct ext2_inode_large
*desc
= inode
;
627 size_t size
= EXT2_INODE_SIZE(fs
->super
);
631 old_lo
= inode
->i_checksum_lo
;
632 inode
->i_checksum_lo
= 0;
634 old_hi
= inode
->i_checksum_hi
;
635 inode
->i_checksum_hi
= 0;
638 inum
= ext2fs_cpu_to_le32(inum
);
639 gen
= inode
->i_generation
;
640 *crc
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&inum
,
642 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)&gen
, sizeof(gen
));
643 *crc
= ext2fs_crc32c_le(*crc
, (unsigned char *)desc
, size
);
645 inode
->i_checksum_lo
= old_lo
;
647 inode
->i_checksum_hi
= old_hi
;
651 int ext2fs_inode_csum_verify(ext2_filsys fs
, ext2_ino_t inum
,
652 struct ext2_inode_large
*inode
)
655 __u32 provided
, calculated
;
656 unsigned int i
, has_hi
;
659 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
662 has_hi
= (EXT2_INODE_SIZE(fs
->super
) > EXT2_GOOD_OLD_INODE_SIZE
&&
663 inode
->i_extra_isize
>= EXT4_INODE_CSUM_HI_EXTRA_END
);
665 provided
= ext2fs_le16_to_cpu(inode
->i_checksum_lo
);
666 retval
= ext2fs_inode_csum(fs
, inum
, inode
, &calculated
, has_hi
);
670 __u32 hi
= ext2fs_le16_to_cpu(inode
->i_checksum_hi
);
671 provided
|= hi
<< 16;
673 calculated
&= 0xFFFF;
675 if (provided
== calculated
)
679 * If the checksum didn't match, it's possible it was due to
680 * the inode being all zero's. It's unlikely this is the
681 * case, but it can happen. So check for it here. (We only
682 * check the base inode since that's good enough, and it's not
683 * worth the bother to figure out how much of the extended
684 * inode, if any, is present.)
686 for (cp
= (char *) inode
, i
= 0;
687 i
< sizeof(struct ext2_inode
);
691 return 1; /* Inode must have been all zero's */
694 errcode_t
ext2fs_inode_csum_set(ext2_filsys fs
, ext2_ino_t inum
,
695 struct ext2_inode_large
*inode
)
701 if (!ext2fs_has_feature_metadata_csum(fs
->super
))
704 has_hi
= (EXT2_INODE_SIZE(fs
->super
) > EXT2_GOOD_OLD_INODE_SIZE
&&
705 inode
->i_extra_isize
>= EXT4_INODE_CSUM_HI_EXTRA_END
);
707 retval
= ext2fs_inode_csum(fs
, inum
, inode
, &crc
, has_hi
);
710 inode
->i_checksum_lo
= ext2fs_cpu_to_le16(crc
& 0xFFFF);
712 inode
->i_checksum_hi
= ext2fs_cpu_to_le16(crc
>> 16);
716 __u16
ext2fs_group_desc_csum(ext2_filsys fs
, dgrp_t group
)
718 struct ext2_group_desc
*desc
= ext2fs_group_desc(fs
, fs
->group_desc
,
720 size_t offset
, size
= EXT2_DESC_SIZE(fs
->super
);
722 #ifdef WORDS_BIGENDIAN
723 struct ext4_group_desc swabdesc
;
724 size_t save_size
= size
;
725 const size_t ext4_bg_size
= sizeof(struct ext4_group_desc
);
726 struct ext2_group_desc
*save_desc
= desc
;
728 /* Have to swab back to little-endian to do the checksum */
729 if (size
> ext4_bg_size
)
731 memcpy(&swabdesc
, desc
, size
);
732 ext2fs_swap_group_desc2(fs
, (struct ext2_group_desc
*) &swabdesc
);
733 desc
= (struct ext2_group_desc
*) &swabdesc
;
734 group
= ext2fs_swab32(group
);
737 if (ext2fs_has_feature_metadata_csum(fs
->super
)) {
738 /* new metadata csum code */
742 old_crc
= desc
->bg_checksum
;
743 desc
->bg_checksum
= 0;
744 crc32
= ext2fs_crc32c_le(fs
->csum_seed
, (unsigned char *)&group
,
746 crc32
= ext2fs_crc32c_le(crc32
, (unsigned char *)desc
,
748 desc
->bg_checksum
= old_crc
;
749 #ifdef WORDS_BIGENDIAN
750 if (save_size
> ext4_bg_size
)
751 crc32
= ext2fs_crc32c_le(crc32
,
752 (unsigned char *)save_desc
+ ext4_bg_size
,
753 save_size
- ext4_bg_size
);
755 crc
= crc32
& 0xFFFF;
760 offset
= offsetof(struct ext2_group_desc
, bg_checksum
);
761 crc
= ext2fs_crc16(~0, fs
->super
->s_uuid
,
762 sizeof(fs
->super
->s_uuid
));
763 crc
= ext2fs_crc16(crc
, &group
, sizeof(group
));
764 crc
= ext2fs_crc16(crc
, desc
, offset
);
765 offset
+= sizeof(desc
->bg_checksum
); /* skip checksum */
766 /* for checksum of struct ext4_group_desc do the rest...*/
768 crc
= ext2fs_crc16(crc
, (char *)desc
+ offset
,
771 #ifdef WORDS_BIGENDIAN
773 * If the size of the bg descriptor is greater than 64
774 * bytes, which is the size of the traditional ext4 bg
775 * descriptor, checksum the rest of the descriptor here
777 if (save_size
> ext4_bg_size
)
778 crc
= ext2fs_crc16(crc
, (char *)save_desc
+ ext4_bg_size
,
779 save_size
- ext4_bg_size
);
786 int ext2fs_group_desc_csum_verify(ext2_filsys fs
, dgrp_t group
)
788 if (ext2fs_has_group_desc_csum(fs
) &&
789 (ext2fs_bg_checksum(fs
, group
) !=
790 ext2fs_group_desc_csum(fs
, group
)))
796 void ext2fs_group_desc_csum_set(ext2_filsys fs
, dgrp_t group
)
798 if (!ext2fs_has_group_desc_csum(fs
))
801 /* ext2fs_bg_checksum_set() sets the actual checksum field but
802 * does not calculate the checksum itself. */
803 ext2fs_bg_checksum_set(fs
, group
, ext2fs_group_desc_csum(fs
, group
));
806 static __u32
find_last_inode_ingrp(ext2fs_inode_bitmap bitmap
,
807 __u32 inodes_per_grp
, dgrp_t grp_no
)
809 ext2_ino_t i
, start_ino
, end_ino
;
811 start_ino
= grp_no
* inodes_per_grp
+ 1;
812 end_ino
= start_ino
+ inodes_per_grp
- 1;
814 for (i
= end_ino
; i
>= start_ino
; i
--) {
815 if (ext2fs_fast_test_inode_bitmap2(bitmap
, i
))
816 return i
- start_ino
+ 1;
818 return inodes_per_grp
;
821 /* update the bitmap flags, set the itable high watermark, and calculate
822 * checksums for the group descriptors */
823 errcode_t
ext2fs_set_gdt_csum(ext2_filsys fs
)
825 struct ext2_super_block
*sb
= fs
->super
;
830 return EXT2_ET_NO_INODE_BITMAP
;
832 if (!ext2fs_has_group_desc_csum(fs
))
835 for (i
= 0; i
< fs
->group_desc_count
; i
++) {
836 __u32 old_csum
= ext2fs_bg_checksum(fs
, i
);
837 __u32 old_unused
= ext2fs_bg_itable_unused(fs
, i
);
838 __u32 old_flags
= ext2fs_bg_flags(fs
, i
);
839 __u32 old_free_inodes_count
= ext2fs_bg_free_inodes_count(fs
, i
);
840 __u32 old_free_blocks_count
= ext2fs_bg_free_blocks_count(fs
, i
);
842 if (old_free_blocks_count
== sb
->s_blocks_per_group
&&
843 i
!= fs
->group_desc_count
- 1)
844 ext2fs_bg_flags_set(fs
, i
, EXT2_BG_BLOCK_UNINIT
);
846 if (old_free_inodes_count
== sb
->s_inodes_per_group
) {
847 ext2fs_bg_flags_set(fs
, i
, EXT2_BG_INODE_UNINIT
);
848 ext2fs_bg_itable_unused_set(fs
, i
, sb
->s_inodes_per_group
);
851 sb
->s_inodes_per_group
-
852 find_last_inode_ingrp(fs
->inode_map
,
853 sb
->s_inodes_per_group
, i
);
855 ext2fs_bg_flags_clear(fs
, i
, EXT2_BG_INODE_UNINIT
);
856 ext2fs_bg_itable_unused_set(fs
, i
, unused
);
859 ext2fs_group_desc_csum_set(fs
, i
);
860 if (old_flags
!= ext2fs_bg_flags(fs
, i
))
862 if (old_unused
!= ext2fs_bg_itable_unused(fs
, i
))
864 if (old_csum
!= ext2fs_bg_checksum(fs
, i
))
868 ext2fs_mark_super_dirty(fs
);
875 void print_csum(const char *msg
, ext2_filsys fs
, dgrp_t group
)
877 __u16 crc1
, crc2
, crc3
;
879 struct ext2_group_desc
*desc
= ext2fs_group_desc(fs
, fs
->group_desc
,
881 size_t size
= EXT2_DESC_SIZE(fs
->super
);
882 struct ext2_super_block
*sb
= fs
->super
;
883 int offset
= offsetof(struct ext2_group_desc
, bg_checksum
);
884 #ifdef WORDS_BIGENDIAN
885 struct ext4_group_desc swabdesc
;
886 struct ext2_group_desc
*save_desc
= desc
;
887 const size_t ext4_bg_size
= sizeof(struct ext4_group_desc
);
888 size_t save_size
= size
;
891 #ifdef WORDS_BIGENDIAN
892 /* Have to swab back to little-endian to do the checksum */
893 if (size
> ext4_bg_size
)
895 memcpy(&swabdesc
, desc
, size
);
896 ext2fs_swap_group_desc2(fs
, (struct ext2_group_desc
*) &swabdesc
);
897 desc
= (struct ext2_group_desc
*) &swabdesc
;
899 swabgroup
= ext2fs_swab32(group
);
904 crc1
= ext2fs_crc16(~0, sb
->s_uuid
, sizeof(fs
->super
->s_uuid
));
905 crc2
= ext2fs_crc16(crc1
, &swabgroup
, sizeof(swabgroup
));
906 crc3
= ext2fs_crc16(crc2
, desc
, offset
);
907 offset
+= sizeof(desc
->bg_checksum
); /* skip checksum */
908 /* for checksum of struct ext4_group_desc do the rest...*/
910 crc3
= ext2fs_crc16(crc3
, (char *)desc
+ offset
, size
- offset
);
911 #ifdef WORDS_BIGENDIAN
912 if (save_size
> ext4_bg_size
)
913 crc3
= ext2fs_crc16(crc3
, (char *)save_desc
+ ext4_bg_size
,
914 save_size
- ext4_bg_size
);
917 printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n",
918 msg
, e2p_uuid2str(sb
->s_uuid
), crc1
, group
, crc2
, crc3
,
919 ext2fs_group_desc_csum(fs
, group
));
922 unsigned char sb_uuid
[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23,
923 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb };
925 int main(int argc
, char **argv
)
927 struct ext2_super_block param
;
931 __u16 csum1
, csum2
, csum_known
= 0xd3a4;
933 memset(¶m
, 0, sizeof(param
));
934 ext2fs_blocks_count_set(¶m
, 32768);
936 param
.s_feature_incompat
|= EXT4_FEATURE_INCOMPAT_64BIT
;
937 param
.s_desc_size
= 128;
941 retval
= ext2fs_initialize("test fs", EXT2_FLAG_64BITS
, ¶m
,
942 test_io_manager
, &fs
);
944 com_err("setup", retval
,
945 "While initializing filesystem");
948 memcpy(fs
->super
->s_uuid
, sb_uuid
, 16);
949 fs
->super
->s_feature_ro_compat
= EXT4_FEATURE_RO_COMPAT_GDT_CSUM
;
951 for (i
=0; i
< fs
->group_desc_count
; i
++) {
952 ext2fs_block_bitmap_loc_set(fs
, i
, 124);
953 ext2fs_inode_bitmap_loc_set(fs
, i
, 125);
954 ext2fs_inode_table_loc_set(fs
, i
, 126);
955 ext2fs_bg_free_blocks_count_set(fs
, i
, 31119);
956 ext2fs_bg_free_inodes_count_set(fs
, i
, 15701);
957 ext2fs_bg_used_dirs_count_set(fs
, i
, 2);
958 ext2fs_bg_flags_zap(fs
, i
);
961 csum1
= ext2fs_group_desc_csum(fs
, 0);
962 print_csum("csum0000", fs
, 0);
964 if (csum1
!= csum_known
) {
965 printf("checksum for group 0 should be %04x\n", csum_known
);
968 csum2
= ext2fs_group_desc_csum(fs
, 1);
969 print_csum("csum0001", fs
, 1);
970 if (csum1
== csum2
) {
971 printf("checksums for different groups shouldn't match\n");
974 csum2
= ext2fs_group_desc_csum(fs
, 2);
975 print_csum("csumffff", fs
, 2);
976 if (csum1
== csum2
) {
977 printf("checksums for different groups shouldn't match\n");
980 ext2fs_bg_checksum_set(fs
, 0, csum1
);
981 csum2
= ext2fs_group_desc_csum(fs
, 0);
982 print_csum("csum_set", fs
, 0);
983 if (csum1
!= csum2
) {
984 printf("checksums should not depend on checksum field\n");
987 if (!ext2fs_group_desc_csum_verify(fs
, 0)) {
988 printf("checksums should verify against gd_checksum\n");
991 memset(fs
->super
->s_uuid
, 0x30, sizeof(fs
->super
->s_uuid
));
992 print_csum("new_uuid", fs
, 0);
993 if (ext2fs_group_desc_csum_verify(fs
, 0) != 0) {
994 printf("checksums for different filesystems shouldn't match\n");
997 csum1
= ext2fs_group_desc_csum(fs
, 0);
998 ext2fs_bg_checksum_set(fs
, 0, csum1
);
999 print_csum("csum_new", fs
, 0);
1000 ext2fs_bg_free_blocks_count_set(fs
, 0, 1);
1001 csum2
= ext2fs_group_desc_csum(fs
, 0);
1002 print_csum("csum_blk", fs
, 0);
1003 if (csum1
== csum2
) {
1004 printf("checksums for different data shouldn't match\n");