Skip to content

Commit

Permalink
diff_cb() does not handle encrypted large dnodes
Browse files Browse the repository at this point in the history
Trying to 'zfs diff' a snapshot with large dnodes will incorrectly try to access
its interior slots when dnodesize > sizeof(dnode_phys_t). This is normally not
an issue because the interior slots are zero-filled, which report_dnode()
handles calling report_free_dnode_range(). However this is not the case for
encrypted large dnodes where raw, unencrypted data is interpreted as a
dnode_phys_t.

Signed-off-by: loli10K <[email protected]>
  • Loading branch information
loli10K committed Sep 22, 2019
1 parent afc8f0a commit a31e940
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 2 deletions.
5 changes: 3 additions & 2 deletions module/zfs/dmu_diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
* Copyright (c) 2010, loli10K <[email protected]>. All rights reserved.
*/

#include <sys/dmu.h>
Expand Down Expand Up @@ -131,7 +132,7 @@ diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
dnode_phys_t *blk;
arc_buf_t *abuf;
arc_flags_t aflags = ARC_FLAG_WAIT;
int blksz = BP_GET_LSIZE(bp);
int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;
int zio_flags = ZIO_FLAG_CANFAIL;
int i;

Expand All @@ -143,7 +144,7 @@ diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
return (SET_ERROR(EIO));

blk = abuf->b_data;
for (i = 0; i < blksz >> DNODE_SHIFT; i++) {
for (i = 0; i < epb; i += blk[i].dn_extra_slots + 1) {
uint64_t dnobj = (zb->zb_blkid <<
(DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i;
err = report_dnode(da, dnobj, blk+i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# 1. Create an encrypted dataset
# 2. Create two snapshots of the dataset
# 3. Perform 'zfs diff -Ft' and verify no errors occur
# 4. Perform the same test on a dataset with large dnodes
#

verify_runnable "both"
Expand All @@ -50,4 +51,13 @@ log_must zfs snapshot $TESTPOOL/$TESTFS1@snap2
# 3. Perform 'zfs diff' and verify no errors occur
log_must zfs diff -Ft $TESTPOOL/$TESTFS1@snap1 $TESTPOOL/$TESTFS1@snap2

# 4. Perform the same test on a dataset with large dnodes
log_must eval "echo 'password' | zfs create -o dnodesize=4k \
-o encryption=on -o keyformat=passphrase $TESTPOOL/$TESTFS2"
MNTPOINT="$(get_prop mountpoint $TESTPOOL/$TESTFS2)"
log_must zfs snapshot $TESTPOOL/$TESTFS2@snap1
log_must touch "$MNTPOINT/file"
log_must zfs snapshot $TESTPOOL/$TESTFS2@snap2
log_must zfs diff -Ft $TESTPOOL/$TESTFS2@snap1 $TESTPOOL/$TESTFS2@snap2

log_pass "'zfs diff' works with encrypted datasets"

0 comments on commit a31e940

Please sign in to comment.