diff --git a/sys/fs/ext2fs/ext2_csum.c b/sys/fs/ext2fs/ext2_csum.c index 881a7b16dfc0..92634306989a 100644 --- a/sys/fs/ext2fs/ext2_csum.c +++ b/sys/fs/ext2fs/ext2_csum.c @@ -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)); diff --git a/sys/fs/ext2fs/ext2_extern.h b/sys/fs/ext2fs/ext2_extern.h index c8a77b6431dc..1c1d4bf8f7f1 100644 --- a/sys/fs/ext2fs/ext2_extern.h +++ b/sys/fs/ext2fs/ext2_extern.h @@ -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. */ diff --git a/sys/fs/ext2fs/ext2_htree.c b/sys/fs/ext2fs/ext2_htree.c index c8165b4aa86d..40615bb8432e 100644 --- a/sys/fs/ext2fs/ext2_htree.c +++ b/sys/fs/ext2fs/ext2_htree.c @@ -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; diff --git a/sys/fs/ext2fs/ext2_inode_cnv.c b/sys/fs/ext2fs/ext2_inode_cnv.c index 558de7b05605..55a8dc6b9726 100644 --- a/sys/fs/ext2fs/ext2_inode_cnv.c +++ b/sys/fs/ext2fs/ext2_inode_cnv.c @@ -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); diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c index 9de4f0343c1a..6d831955eb3c 100644 --- a/sys/fs/ext2fs/ext2_lookup.c +++ b/sys/fs/ext2fs/ext2_lookup.c @@ -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); diff --git a/sys/fs/ext2fs/ext2_vnops.c b/sys/fs/ext2fs/ext2_vnops.c index 575ab91fe4fd..86b986730564 100644 --- a/sys/fs/ext2fs/ext2_vnops.c +++ b/sys/fs/ext2fs/ext2_vnops.c @@ -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,