Fix directory blocks checksumming.

Reviewed by:    pfg
MFC after:      3 months

Differential Revision:    https://reviews.freebsd.org/D15396
This commit is contained in:
Fedor Uporov 2018-05-13 19:48:30 +00:00
parent c4aa9a026d
commit 6d4a4ed747
6 changed files with 114 additions and 81 deletions

View File

@ -154,12 +154,37 @@ ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp)
header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header);
}
static struct ext2fs_direct_tail *
ext2_get_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
void
ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
{
struct ext2fs_direct_tail *tp;
memset(tp, 0, sizeof(struct ext2fs_direct_tail));
tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail);
tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
}
tp = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
struct ext2fs_direct_tail *
ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep)
{
struct ext2fs_direct_2 *dep;
void *top;
struct ext2fs_direct_tail *tp;
unsigned int rec_len;
dep = ep;
top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize);
rec_len = dep->e2d_reclen;
while (rec_len && !(rec_len & 0x3)) {
dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len);
if ((void *)dep >= top)
break;
rec_len = dep->e2d_reclen;
}
if (dep != top)
return (NULL);
tp = (struct ext2fs_direct_tail *)dep;
if (tp->e2dt_reserved_zero1 ||
tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) ||
tp->e2dt_reserved_zero2 ||
@ -189,13 +214,13 @@ ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size)
return (crc);
}
static int
int
ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
{
uint32_t calculated;
struct ext2fs_direct_tail *tp;
tp = ext2_get_dirent_tail(ip, ep);
tp = ext2_dirent_get_tail(ip, ep);
if (tp == NULL)
return (0);
@ -263,7 +288,7 @@ ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset,
return (crc);
}
static int
int
ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep)
{
uint32_t calculated;
@ -304,7 +329,7 @@ ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
ep = (struct ext2fs_direct_2 *)bp->b_data;
if (ext2_get_dirent_tail(ip, ep) != NULL)
if (ext2_dirent_get_tail(ip, ep) != NULL)
error = ext2_dirent_csum_verify(ip, ep);
else if (ext2_get_dx_count(ip, ep, NULL) != NULL)
error = ext2_dx_csum_verify(ip, ep);
@ -316,12 +341,18 @@ ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp)
return (error);
}
static void
void
ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
{
struct m_ext2fs *fs;
struct ext2fs_direct_tail *tp;
tp = ext2_get_dirent_tail(ip, ep);
fs = ip->i_e2fs;
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
return;
tp = ext2_dirent_get_tail(ip, ep);
if (tp == NULL)
return;
@ -329,13 +360,19 @@ ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep);
}
static void
void
ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
{
struct m_ext2fs *fs;
struct ext2fs_htree_count *cp;
struct ext2fs_htree_tail *tp;
int count_offset, limit, count;
fs = ip->i_e2fs;
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
return;
cp = ext2_get_dx_count(ip, ep, &count_offset);
if (cp == NULL)
return;
@ -350,35 +387,6 @@ ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep)
tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp);
}
void
ext2_dir_blk_csum_set_mem(struct inode *ip, char *buf, int size)
{
struct m_ext2fs *fs;
struct ext2fs_direct_2 *ep;
fs = ip->i_e2fs;
if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
return;
ep = (struct ext2fs_direct_2 *)buf;
if (ext2_htree_has_idx(ip)) {
if (ext2_get_dx_count(ip, ep, NULL) != NULL)
ext2_dx_csum_set(ip, ep);
} else {
if (ext2_get_dirent_tail(ip, ep) != NULL)
ext2_dirent_csum_set(ip, ep);
}
}
void
ext2_dir_blk_csum_set(struct inode *ip, struct buf *bp)
{
ext2_dir_blk_csum_set_mem(ip, bp->b_data, bp->b_bufsize);
}
static uint32_t
ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp)
{
@ -543,9 +551,9 @@ ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei)
offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo);
csum_size = sizeof(dummy_csum);
inum = ip->i_number;
gen = ip->i_gen;
crc = calculate_crc32c(fs->e2fs_csum_seed,
(uint8_t *)&inum, sizeof(inum));
gen = ip->i_gen;
inode_csum_seed = calculate_crc32c(crc,
(uint8_t *)&gen, sizeof(gen));

View File

@ -43,6 +43,7 @@
struct ext2fs_dinode;
struct ext2fs_direct_2;
struct ext2fs_direct_tail;
struct ext2fs_searchslot;
struct indir;
struct inode;
@ -110,10 +111,15 @@ void ext2_sb_csum_set(struct m_ext2fs *);
int ext2_extattr_blk_csum_verify(struct inode *, struct buf *);
void ext2_extattr_blk_csum_set(struct inode *, struct buf *);
int ext2_dir_blk_csum_verify(struct inode *, struct buf *);
void ext2_dir_blk_csum_set(struct inode *, struct buf *);
void ext2_dir_blk_csum_set_mem(struct inode *, char *, int);
struct ext2fs_direct_tail *ext2_dirent_get_tail(struct inode *ip,
struct ext2fs_direct_2 *ep);
void ext2_dirent_csum_set(struct inode *, struct ext2fs_direct_2 *);
int ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep);
void ext2_dx_csum_set(struct inode *, struct ext2fs_direct_2 *);
int ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep);
int ext2_extent_blk_csum_verify(struct inode *, void *);
void ext2_extent_blk_csum_set(struct inode *, void *);
void ext2_init_dirent_tail(struct ext2fs_direct_tail *);
int ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *, int, struct buf *);
void ext2_gd_i_bitmap_csum_set(struct m_ext2fs *, int, struct buf *);
int ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *, int, struct buf *);
@ -123,7 +129,6 @@ void ext2_ei_csum_set(struct inode *, struct ext2fs_dinode *);
int ext2_gd_csum_verify(struct m_ext2fs *, struct cdev *);
void ext2_gd_csum_set(struct m_ext2fs *);
/* Flags to low-level allocation routines.
* The low 16-bits are reserved for IO_ flags from vnode.h.
*/

View File

@ -56,7 +56,7 @@
static void ext2_append_entry(char *block, uint32_t blksize,
struct ext2fs_direct_2 *last_entry,
struct ext2fs_direct_2 *new_entry);
struct ext2fs_direct_2 *new_entry, int csum_size);
static int ext2_htree_append_block(struct vnode *vp, char *data,
struct componentname *cnp, uint32_t blksize);
static int ext2_htree_check_next(struct inode *ip, uint32_t hash,
@ -82,12 +82,14 @@ static void ext2_htree_set_hash(struct ext2fs_htree_entry *ep,
uint32_t hash);
static void ext2_htree_set_limit(struct ext2fs_htree_entry *ep,
uint16_t limit);
static int ext2_htree_split_dirblock(char *block1, char *block2,
uint32_t blksize, uint32_t *hash_seed, uint8_t hash_version,
static int ext2_htree_split_dirblock(struct inode *ip,
char *block1, char *block2, uint32_t blksize,
uint32_t *hash_seed, uint8_t hash_version,
uint32_t *split_hash, struct ext2fs_direct_2 *entry);
static void ext2_htree_release(struct ext2fs_htree_lookup_info *info);
static uint32_t ext2_htree_root_limit(struct inode *ip, int len);
static int ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info);
static int ext2_htree_writebuf(struct inode *ip,
struct ext2fs_htree_lookup_info *info);
int
ext2_htree_has_idx(struct inode *ip)
@ -207,10 +209,16 @@ ext2_htree_release(struct ext2fs_htree_lookup_info *info)
static uint32_t
ext2_htree_root_limit(struct inode *ip, int len)
{
struct m_ext2fs *fs;
uint32_t space;
fs = ip->i_e2fs;
space = ip->i_e2fs->e2fs_bsize - EXT2_DIR_REC_LEN(1) -
EXT2_DIR_REC_LEN(2) - len;
if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
space -= sizeof(struct ext2fs_htree_tail);
return (space / sizeof(struct ext2fs_htree_entry));
}
@ -223,6 +231,9 @@ ext2_htree_node_limit(struct inode *ip)
fs = ip->i_e2fs;
space = fs->e2fs_bsize - EXT2_DIR_REC_LEN(0);
if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
space -= sizeof(struct ext2fs_htree_tail);
return (space / sizeof(struct ext2fs_htree_entry));
}
@ -417,13 +428,13 @@ ext2_htree_append_block(struct vnode *vp, char *data,
}
static int
ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info)
ext2_htree_writebuf(struct inode* ip, struct ext2fs_htree_lookup_info *info)
{
int i, error;
for (i = 0; i < info->h_levels_num; i++) {
struct buf *bp = info->h_levels[i].h_bp;
ext2_dx_csum_set(ip, (struct ext2fs_direct_2 *)bp->b_data);
error = bwrite(bp);
if (error)
return (error);
@ -487,14 +498,14 @@ ext2_htree_cmp_sort_entry(const void *e1, const void *e2)
static void
ext2_append_entry(char *block, uint32_t blksize,
struct ext2fs_direct_2 *last_entry,
struct ext2fs_direct_2 *new_entry)
struct ext2fs_direct_2 *new_entry, int csum_size)
{
uint16_t entry_len;
entry_len = EXT2_DIR_REC_LEN(last_entry->e2d_namlen);
last_entry->e2d_reclen = entry_len;
last_entry = (struct ext2fs_direct_2 *)((char *)last_entry + entry_len);
new_entry->e2d_reclen = block + blksize - (char *)last_entry;
new_entry->e2d_reclen = block + blksize - (char *)last_entry - csum_size;
memcpy(last_entry, new_entry, EXT2_DIR_REC_LEN(new_entry->e2d_namlen));
}
@ -502,12 +513,13 @@ ext2_append_entry(char *block, uint32_t blksize,
* Move half of entries from the old directory block to the new one.
*/
static int
ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
uint32_t *hash_seed, uint8_t hash_version,
ext2_htree_split_dirblock(struct inode *ip, char *block1, char *block2,
uint32_t blksize, uint32_t *hash_seed, uint8_t hash_version,
uint32_t *split_hash, struct ext2fs_direct_2 *entry)
{
struct m_ext2fs *fs;
int entry_cnt = 0;
int size = 0;
int size = 0, csum_size = 0;
int i, k;
uint32_t offset;
uint16_t entry_len = 0;
@ -516,11 +528,15 @@ ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
char *dest;
struct ext2fs_htree_sort_entry *sort_info;
fs = ip->i_e2fs;
ep = (struct ext2fs_direct_2 *)block1;
dest = block2;
sort_info = (struct ext2fs_htree_sort_entry *)
((char *)block2 + blksize);
if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM))
csum_size = sizeof(struct ext2fs_direct_tail);
/*
* Calculate name hash value for the entry which is to be added.
*/
@ -530,7 +546,7 @@ ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
/*
* Fill in directory entry sort descriptors.
*/
while ((char *)ep < block1 + blksize) {
while ((char *)ep < block1 + blksize - csum_size) {
if (ep->e2d_ino && ep->e2d_namlen) {
entry_cnt++;
sort_info--;
@ -585,7 +601,7 @@ ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
/* Shrink directory entries in block 1. */
last = (struct ext2fs_direct_2 *)block1;
entry_len = 0;
for (offset = 0; offset < blksize; ) {
for (offset = 0; offset < blksize - csum_size; ) {
ep = (struct ext2fs_direct_2 *)(block1 + offset);
offset += ep->e2d_reclen;
if (ep->e2d_ino) {
@ -600,17 +616,22 @@ ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
if (entry_hash >= *split_hash) {
/* Add entry to block 2. */
ext2_append_entry(block2, blksize,
(struct ext2fs_direct_2 *)dest, entry);
(struct ext2fs_direct_2 *)dest, entry, csum_size);
/* Adjust length field of last entry of block 1. */
last->e2d_reclen = block1 + blksize - (char *)last;
last->e2d_reclen = block1 + blksize - (char *)last - csum_size;
} else {
/* Add entry to block 1. */
ext2_append_entry(block1, blksize, last, entry);
ext2_append_entry(block1, blksize, last, entry, csum_size);
/* Adjust length field of last entry of block 2. */
((struct ext2fs_direct_2 *)dest)->e2d_reclen =
block2 + blksize - dest;
block2 + blksize - dest - csum_size;
}
if (csum_size) {
ext2_init_dirent_tail(EXT2_DIRENT_TAIL(block1, blksize));
ext2_init_dirent_tail(EXT2_DIRENT_TAIL(block2, blksize));
}
return (0);
@ -680,13 +701,14 @@ ext2_htree_create_index(struct vnode *vp, struct componentname *cnp,
hash_version = root->h_info.h_hash_version;
if (hash_version <= EXT2_HTREE_TEA)
hash_version += m_fs->e2fs_uhash;
ext2_htree_split_dirblock(buf1, buf2, blksize, fs->e3fs_hash_seed,
ext2_htree_split_dirblock(dp, buf1, buf2, blksize, fs->e3fs_hash_seed,
hash_version, &split_hash, new_entry);
ext2_htree_insert_entry(&info, split_hash, 2);
/*
* Write directory block 0.
*/
ext2_dx_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
if (DOINGASYNC(vp)) {
bdwrite(bp);
error = 0;
@ -700,6 +722,7 @@ ext2_htree_create_index(struct vnode *vp, struct componentname *cnp,
/*
* Write directory block 1.
*/
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)buf1);
error = ext2_htree_append_block(vp, buf1, cnp, blksize);
if (error)
goto out1;
@ -707,6 +730,7 @@ ext2_htree_create_index(struct vnode *vp, struct componentname *cnp,
/*
* Write directory block 2.
*/
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)buf2);
error = ext2_htree_append_block(vp, buf2, cnp, blksize);
free(buf1, M_TEMP);
@ -825,6 +849,8 @@ ext2_htree_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry,
split_hash, blknum);
/* Write new index node to disk */
ext2_dx_csum_set(ip,
(struct ext2fs_direct_2 *)dst_bp->b_data);
error = bwrite(dst_bp);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (error)
@ -863,7 +889,7 @@ ext2_htree_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry,
/* Split target directory block */
newdirblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
ext2_htree_split_dirblock((char *)bp->b_data, newdirblock, blksize,
ext2_htree_split_dirblock(ip, (char *)bp->b_data, newdirblock, blksize,
fs->e3fs_hash_seed, hash_version, &split_hash, entry);
cursize = roundup(ip->i_size, blksize);
dirsize = cursize + blksize;
@ -873,11 +899,13 @@ ext2_htree_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry,
ext2_htree_insert_entry(&info, split_hash, blknum);
/* Write the new directory block to the end of the directory */
ext2_dirent_csum_set(ip, (struct ext2fs_direct_2 *)newdirblock);
error = ext2_htree_append_block(dvp, newdirblock, cnp, blksize);
if (error)
goto finish;
/* Write the target directory block */
ext2_dirent_csum_set(ip, (struct ext2fs_direct_2 *)bp->b_data);
error = bwrite(bp);
ip->i_flag |= IN_CHANGE | IN_UPDATE;
if (error)
@ -885,7 +913,7 @@ ext2_htree_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry,
write_bp = 1;
/* Write the index block */
error = ext2_htree_writebuf(&info);
error = ext2_htree_writebuf(ip, &info);
if (!error)
write_info = 1;

View File

@ -136,6 +136,7 @@ ext2_ei2i(struct ext2fs_dinode *ei, struct inode *ip)
memcpy(ip->i_data, ei->e2di_blocks, sizeof(ei->e2di_blocks));
/* Verify inode csum. */
return (ext2_ei_csum_verify(ip, ei));
}
@ -197,7 +198,7 @@ ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei)
memcpy(ei->e2di_blocks, ip->i_data, sizeof(ei->e2di_blocks));
/* Set inode csum */
/* Set inode csum. */
ext2_ei_csum_set(ip, ei);
return (0);

View File

@ -930,7 +930,7 @@ ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp)
memcpy(bp->b_data, &newdir, sizeof(struct ext2fs_direct_2));
ext2_dir_blk_csum_set(dp, bp);
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
error = bwrite(bp);
if (error)
return (error);
@ -1030,7 +1030,7 @@ ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry)
ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
}
bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize);
ext2_dir_blk_csum_set(dp, bp);
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
if (DOINGASYNC(dvp)) {
bdwrite(bp);
error = 0;
@ -1088,7 +1088,7 @@ ext2_dirremove(struct vnode *dvp, struct componentname *cnp)
else
rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen);
ep->e2d_reclen += rep->e2d_reclen;
ext2_dir_blk_csum_set(dp, bp);
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
if (DOINGASYNC(dvp) && dp->i_count != 0)
bdwrite(bp);
else
@ -1119,7 +1119,7 @@ ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp)
ep->e2d_type = DTTOFT(IFTODT(ip->i_mode));
else
ep->e2d_type = EXT2_FT_UNKNOWN;
ext2_dir_blk_csum_set(dp, bp);
ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
error = bwrite(bp);
dp->i_flag |= IN_CHANGE | IN_UPDATE;
return (error);

View File

@ -1091,9 +1091,8 @@ ext2_rename(struct vop_rename_args *ap)
"rename: mangled dir");
} else {
dirbuf->dotdot_ino = newparent;
ext2_dir_blk_csum_set_mem(ip,
(char *)dirbuf,
ip->i_e2fs->e2fs_bsize);
ext2_dirent_csum_set(ip,
(struct ext2fs_direct_2 *)dirbuf);
(void)vn_rdwr(UIO_WRITE, fvp,
(caddr_t)dirbuf,
ip->i_e2fs->e2fs_bsize,
@ -1284,14 +1283,6 @@ ext2_do_posix1e_acl_inheritance_file(struct vnode *dvp, struct vnode *tvp,
#endif /* UFS_ACL */
static void
ext2_init_dirent_tail(struct ext2fs_direct_tail *tp)
{
memset(tp, 0, sizeof(struct ext2fs_direct_tail));
tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail);
tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM;
}
/*
* Mkdir system call
*/
@ -1400,7 +1391,7 @@ ext2_mkdir(struct vop_mkdir_args *ap)
ext2_init_dirent_tail(EXT2_DIRENT_TAIL(buf, DIRBLKSIZ));
}
memcpy(buf, &dirtemplate, sizeof(dirtemplate));
ext2_dir_blk_csum_set_mem(ip, buf, DIRBLKSIZ);
ext2_dirent_csum_set(ip, (struct ext2fs_direct_2 *)buf);
error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)buf,
DIRBLKSIZ, (off_t)0, UIO_SYSSPACE,
IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED,