aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libufs/inode.c7
-rw-r--r--lib/libufs/libufs.h2
-rw-r--r--sbin/fsck_ffs/inode.c30
-rw-r--r--sbin/fsck_ffs/main.c2
-rw-r--r--sbin/fsirand/fsirand.c1
-rw-r--r--sbin/newfs/mkfs.c2
-rw-r--r--sys/sys/param.h3
-rw-r--r--sys/ufs/ffs/ffs_extern.h2
-rw-r--r--sys/ufs/ffs/ffs_inode.c1
-rw-r--r--sys/ufs/ffs/ffs_snapshot.c2
-rw-r--r--sys/ufs/ffs/ffs_softdep.c11
-rw-r--r--sys/ufs/ffs/ffs_subr.c47
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c2
-rw-r--r--sys/ufs/ufs/dinode.h3
14 files changed, 107 insertions, 8 deletions
diff --git a/lib/libufs/inode.c b/lib/libufs/inode.c
index 11e263f08ad5..497ff4c854f6 100644
--- a/lib/libufs/inode.c
+++ b/lib/libufs/inode.c
@@ -90,7 +90,10 @@ gotit: switch (disk->d_ufs) {
disk->d_dp.dp2 = &((struct ufs2_dinode *)inoblock)[inum - min];
if (dp != NULL)
*dp = disk->d_dp;
- return (0);
+ if (ffs_verify_dinode_ckhash(fs, disk->d_dp.dp2) == 0)
+ return (0);
+ ERROR(disk, "check-hash failed for inode read from disk");
+ return (-1);
default:
break;
}
@@ -108,6 +111,8 @@ putinode(struct uufsd *disk)
ERROR(disk, "No inode block allocated");
return (-1);
}
+ if (disk->d_ufs == 2)
+ ffs_update_dinode_ckhash(fs, disk->d_dp.dp2);
if (bwrite(disk, fsbtodb(fs, ino_to_fsba(&disk->d_fs, disk->d_inomin)),
disk->d_inoblock, disk->d_fs.fs_bsize) <= 0)
return (-1);
diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h
index 9d1e7355c2f7..44eaed83ccbd 100644
--- a/lib/libufs/libufs.h
+++ b/lib/libufs/libufs.h
@@ -116,6 +116,8 @@ int ffs_sbget(void *, struct fs **, off_t, char *,
int (*)(void *, off_t, void **, int));
int ffs_sbput(void *, struct fs *, off_t,
int (*)(void *, off_t, void *, int));
+void ffs_update_dinode_ckhash(struct fs *, struct ufs2_dinode *);
+int ffs_verify_dinode_ckhash(struct fs *, struct ufs2_dinode *);
/*
* Request standard superblock location in ffs_sbget
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index a914be8b87a5..6957a41f70c0 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -286,6 +286,7 @@ union dinode *
ginode(ino_t inumber)
{
ufs2_daddr_t iblk;
+ union dinode *dp;
if (inumber < UFS_ROOTINO || inumber > maxino)
errx(EEXIT, "bad inode number %ju to ginode",
@@ -301,7 +302,17 @@ ginode(ino_t inumber)
if (sblock.fs_magic == FS_UFS1_MAGIC)
return ((union dinode *)
&pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]);
- return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]);
+ dp = (union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)];
+ if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp) != 0) {
+ pwarn("INODE CHECK-HASH FAILED");
+ prtinode(inumber, dp);
+ if (preen || reply("FIX") != 0) {
+ if (preen)
+ printf(" (FIXED)\n");
+ inodirty(dp);
+ }
+ }
+ return (dp);
}
/*
@@ -347,6 +358,21 @@ getnextinode(ino_t inumber, int rebuildcg)
nextinop += sizeof(struct ufs1_dinode);
else
nextinop += sizeof(struct ufs2_dinode);
+ if ((ckhashadd & CK_INODE) != 0) {
+ ffs_update_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp);
+ dirty(&inobuf);
+ }
+ if (ffs_verify_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp) != 0) {
+ pwarn("INODE CHECK-HASH FAILED");
+ prtinode(inumber, dp);
+ if (preen || reply("FIX") != 0) {
+ if (preen)
+ printf(" (FIXED)\n");
+ ffs_update_dinode_ckhash(&sblock,
+ (struct ufs2_dinode *)dp);
+ dirty(&inobuf);
+ }
+ }
if (rebuildcg && (char *)dp == inobuf.b_un.b_buf) {
/*
* Try to determine if we have reached the end of the
@@ -522,6 +548,8 @@ void
inodirty(union dinode *dp)
{
+ if (sblock.fs_magic == FS_UFS2_MAGIC)
+ ffs_update_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp);
dirty(pbp);
}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index ce4d0a89a36a..47bd3987c8e8 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -468,13 +468,13 @@ checkfilesys(char *filesys)
ckhashadd |= CK_SUPERBLOCK;
sblock.fs_metackhash |= CK_SUPERBLOCK;
}
-#ifdef notyet
if ((sblock.fs_metackhash & CK_INODE) == 0 &&
getosreldate() >= P_OSREL_CK_INODE &&
reply("ADD INODE CHECK-HASH PROTECTION") != 0) {
ckhashadd |= CK_INODE;
sblock.fs_metackhash |= CK_INODE;
}
+#ifdef notyet
if ((sblock.fs_metackhash & CK_INDIR) == 0 &&
getosreldate() >= P_OSREL_CK_INDIR &&
reply("ADD INDIRECT BLOCK CHECK-HASH PROTECTION") != 0) {
diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c
index 8675f7c3fd33..f24d34705b72 100644
--- a/sbin/fsirand/fsirand.c
+++ b/sbin/fsirand/fsirand.c
@@ -202,6 +202,7 @@ fsirand(char *device)
dp1++;
} else {
dp2->di_gen = arc4random();
+ ffs_update_dinode_ckhash(sblock, dp2);
dp2++;
}
}
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
index 2bea89cf2bc9..96849a791714 100644
--- a/sbin/newfs/mkfs.c
+++ b/sbin/newfs/mkfs.c
@@ -499,6 +499,8 @@ restart:
sblock.fs_metackhash |= CK_CYLGRP;
if (getosreldate() >= P_OSREL_CK_SUPERBLOCK)
sblock.fs_metackhash |= CK_SUPERBLOCK;
+ if (getosreldate() >= P_OSREL_CK_INODE)
+ sblock.fs_metackhash |= CK_INODE;
}
/*
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 64aa65bef7b0..a28bc884d25c 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -60,7 +60,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
-#define __FreeBSD_version 1300004 /* Master, propagated to newvers */
+#define __FreeBSD_version 1300005 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
@@ -89,6 +89,7 @@
#define P_OSREL_CK_CYLGRP 1200046
#define P_OSREL_VMTOTAL64 1200054
#define P_OSREL_CK_SUPERBLOCK 1300000
+#define P_OSREL_CK_INODE 1300005
#define P_OSREL_MAJOR(x) ((x) / 100000)
#endif
diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h
index 16f517d5f12e..f7d95404c046 100644
--- a/sys/ufs/ffs/ffs_extern.h
+++ b/sys/ufs/ffs/ffs_extern.h
@@ -108,6 +108,8 @@ void ffs_sync_snap(struct mount *, int);
int ffs_syncvnode(struct vnode *vp, int waitfor, int flags);
int ffs_truncate(struct vnode *, off_t, int, struct ucred *);
int ffs_update(struct vnode *, int);
+void ffs_update_dinode_ckhash(struct fs *, struct ufs2_dinode *);
+int ffs_verify_dinode_ckhash(struct fs *, struct ufs2_dinode *);
int ffs_valloc(struct vnode *, int, struct ucred *, struct vnode **);
int ffs_vfree(struct vnode *, ino_t, int);
vfs_vget_t ffs_vget;
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c
index d146d14980c3..bbacaaa59e5f 100644
--- a/sys/ufs/ffs/ffs_inode.c
+++ b/sys/ufs/ffs/ffs_inode.c
@@ -154,6 +154,7 @@ loop:
*/
random_harvest_queue(&(ip->i_din1), sizeof(ip->i_din1), RANDOM_FS_ATIME);
} else {
+ ffs_update_dinode_ckhash(fs, ip->i_din2);
*((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
/*
diff --git a/sys/ufs/ffs/ffs_snapshot.c b/sys/ufs/ffs/ffs_snapshot.c
index e230d4b0c6c8..092720a9cbeb 100644
--- a/sys/ufs/ffs/ffs_snapshot.c
+++ b/sys/ufs/ffs/ffs_snapshot.c
@@ -1340,6 +1340,8 @@ expunge_ufs2(snapvp, cancelip, fs, acctfunc, expungetype, clearmode)
bzero(&dip->di_db[0], (UFS_NDADDR + UFS_NIADDR) * sizeof(ufs2_daddr_t));
if (clearmode || cancelip->i_effnlink == 0)
dip->di_mode = 0;
+ else
+ ffs_update_dinode_ckhash(fs, dip);
bdwrite(bp);
/*
* Now go through and expunge all the blocks in the file
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
index aad19c6014cd..eb093d4bf419 100644
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -6702,6 +6702,7 @@ softdep_journal_freeblocks(ip, cred, length, flags)
*((struct ufs1_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1;
} else {
+ ffs_update_dinode_ckhash(fs, ip->i_din2);
*((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2;
}
@@ -6957,6 +6958,7 @@ softdep_setup_freeblocks(ip, length, flags)
dp2 = ((struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, ip->i_number));
ip->i_din2->di_freelink = dp2->di_freelink;
+ ffs_update_dinode_ckhash(fs, ip->i_din2);
*dp2 = *ip->i_din2;
}
/*
@@ -9752,6 +9754,7 @@ clear_unlinked_inodedep(inodedep)
dip = (struct ufs2_dinode *)bp->b_data +
ino_to_fsbo(fs, pino);
dip->di_freelink = nino;
+ ffs_update_dinode_ckhash(fs, dip);
}
/*
* If the bwrite fails we have no recourse to recover. The
@@ -10392,6 +10395,7 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
inon = TAILQ_NEXT(inodedep, id_unlinked);
dp->di_freelink = inon ? inon->id_ino : 0;
+ ffs_update_dinode_ckhash(fs, dp);
}
/*
* If the bitmap is not yet written, then the allocated
@@ -10553,6 +10557,7 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
#endif /* INVARIANTS */
dp->di_ib[i] = 0;
}
+ ffs_update_dinode_ckhash(fs, dp);
return;
}
/*
@@ -10581,6 +10586,7 @@ initiate_write_inodeblock_ufs2(inodedep, bp)
*/
for (; adp; adp = TAILQ_NEXT(adp, ad_next))
dp->di_ib[adp->ad_offset - UFS_NDADDR] = 0;
+ ffs_update_dinode_ckhash(fs, dp);
}
/*
@@ -11619,8 +11625,11 @@ handle_written_inodeblock(inodedep, bp, flags)
* marked dirty so that its will eventually get written back in
* its correct form.
*/
- if (hadchanges)
+ if (hadchanges) {
+ if (fstype == UFS2)
+ ffs_update_dinode_ckhash(inodedep->id_fs, dp2);
bdirty(bp);
+ }
bufwait:
/*
* If the write did not succeed, we have done all the roll-forward
diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c
index 64354bbd5383..2891c8d8fe96 100644
--- a/sys/ufs/ffs/ffs_subr.c
+++ b/sys/ufs/ffs/ffs_subr.c
@@ -119,6 +119,7 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino)
{
struct ufs1_dinode *dip1;
struct ufs2_dinode *dip2;
+ int error;
if (I_IS_UFS1(ip)) {
dip1 = ip->i_din1;
@@ -142,11 +143,55 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struct fs *fs, ino_t ino)
ip->i_gen = dip2->di_gen;
ip->i_uid = dip2->di_uid;
ip->i_gid = dip2->di_gid;
- return (0);
+ if ((error = ffs_verify_dinode_ckhash(fs, dip2)) != 0)
+ printf("Inode %jd: check-hash failed\n", (intmax_t)ino);
+ return (error);
}
#endif /* _KERNEL */
/*
+ * Verify an inode check-hash.
+ */
+int
+ffs_verify_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip)
+{
+ uint32_t save_ckhash;
+
+ /*
+ * Return success if unallocated or we are not doing inode check-hash.
+ */
+ if (dip->di_mode == 0 || (fs->fs_metackhash & CK_INODE) == 0)
+ return (0);
+ /*
+ * Exclude di_ckhash from the crc32 calculation, e.g., always use
+ * a check-hash value of zero when calculating the check-hash.
+ */
+ save_ckhash = dip->di_ckhash;
+ dip->di_ckhash = 0;
+ if (save_ckhash != calculate_crc32c(~0L, (void *)dip, sizeof(*dip)))
+ return (EINVAL);
+ dip->di_ckhash = save_ckhash;
+ return (0);
+}
+
+/*
+ * Update an inode check-hash.
+ */
+void
+ffs_update_dinode_ckhash(struct fs *fs, struct ufs2_dinode *dip)
+{
+
+ if (dip->di_mode == 0 || (fs->fs_metackhash & CK_INODE) == 0)
+ return;
+ /*
+ * Exclude old di_ckhash from the crc32 calculation, e.g., always use
+ * a check-hash value of zero when calculating the new check-hash.
+ */
+ dip->di_ckhash = 0;
+ dip->di_ckhash = calculate_crc32c(~0L, (void *)dip, sizeof(*dip));
+}
+
+/*
* These are the low-level functions that actually read and write
* the superblock and its associated data.
*/
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 7cb81cfa450a..15b208c5c06a 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -817,7 +817,7 @@ ffs_mountfs(devvp, mp, td)
if ((error = ffs_sbget(devvp, &fs, loc, M_UFSMNT, ffs_use_bread)) != 0)
goto out;
/* none of these types of check-hashes are maintained by this kernel */
- fs->fs_metackhash &= ~(CK_INODE | CK_INDIR | CK_DIR);
+ fs->fs_metackhash &= ~(CK_INDIR | CK_DIR);
/* no support for any undefined flags */
fs->fs_flags &= FS_SUPPORTED;
fs->fs_flags &= ~FS_UNCLEAN;
diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h
index 0a201ce0b43b..1f0f25c4d5ec 100644
--- a/sys/ufs/ufs/dinode.h
+++ b/sys/ufs/ufs/dinode.h
@@ -149,7 +149,8 @@ struct ufs2_dinode {
ufs2_daddr_t di_ib[UFS_NIADDR]; /* 208: Indirect disk blocks. */
u_int64_t di_modrev; /* 232: i_modrev for NFSv4 */
uint32_t di_freelink; /* 240: SUJ: Next unlinked inode. */
- uint32_t di_spare[3]; /* 244: Reserved; currently unused */
+ uint32_t di_ckhash; /* 244: if CK_INODE, its check-hash */
+ uint32_t di_spare[2]; /* 248: Reserved; currently unused */
};
/*