2626 * Copyright (c) 2017, 2019, Datto Inc. All rights reserved.
2727 * Copyright (c) 2015, Nexenta Systems, Inc. All rights reserved.
2828 * Copyright 2019 Joyent, Inc.
29+ * Copyright 2025 ConnectWise, Inc.
2930 */
3031
3132#include <sys/dsl_scan.h>
3233#include <sys/dsl_pool.h>
3334#include <sys/dsl_dataset.h>
3435#include <sys/dsl_prop.h>
3536#include <sys/dsl_dir.h>
37+ #include <sys/dsl_crypt.h>
3638#include <sys/dsl_synctask.h>
3739#include <sys/dnode.h>
3840#include <sys/dmu_tx.h>
5658#include <sys/abd.h>
5759#include <sys/range_tree.h>
5860#include <sys/dbuf.h>
61+ #include <sys/fm/fs/zfs.h>
5962#ifdef _KERNEL
6063#include <sys/zfs_vfsops.h>
6164#endif
@@ -246,6 +249,16 @@ static int zfs_free_bpobj_enabled = 1;
246249/* Error blocks to be scrubbed in one txg. */
247250static uint_t zfs_scrub_error_blocks_per_txg = 1 << 12 ;
248251
252+ /*
253+ * When set will cause scrub to decrypt and decompress blocks it reads so that
254+ * it will catch the rare type of corruption where the checksum matches the
255+ * data, but decryption and/or decompression fails. For encrypted datasets keys
256+ * have to be loaded in order to perform thorough scrub. If keys are not loaded
257+ * or unloaded during a thorough scrub the scrub will revert to doing a normal
258+ * scrub for the encrypted dataset.
259+ */
260+ static uint_t zfs_scrub_thorough = 0 ;
261+
249262/* the order has to match pool_scan_type */
250263static scan_cb_t * scan_funcs [POOL_SCAN_FUNCS ] = {
251264 NULL ,
@@ -287,6 +300,10 @@ typedef struct scan_io {
287300 uint64_t sio_birth ;
288301 zio_cksum_t sio_cksum ;
289302 uint32_t sio_nr_dvas ;
303+ uint64_t sio_salt ;
304+ uint64_t sio_iv1 ;
305+ uint64_t sio_iv2 ;
306+
290307
291308 /* fields from zio_t */
292309 uint32_t sio_flags ;
@@ -442,6 +459,11 @@ sio2bp(const scan_io_t *sio, blkptr_t *bp)
442459 BP_SET_PHYSICAL_BIRTH (bp , sio -> sio_phys_birth );
443460 BP_SET_LOGICAL_BIRTH (bp , sio -> sio_birth );
444461 bp -> blk_fill = 1 ; /* we always only work with data pointers */
462+ if (BP_IS_ENCRYPTED (bp )) {
463+ bp -> blk_dva [2 ].dva_word [0 ] = sio -> sio_salt ;
464+ bp -> blk_dva [2 ].dva_word [1 ] = sio -> sio_iv1 ;
465+ BP_SET_IV2 (bp , sio -> sio_iv2 );
466+ }
445467 bp -> blk_cksum = sio -> sio_cksum ;
446468
447469 ASSERT3U (sio -> sio_nr_dvas , > , 0 );
@@ -458,6 +480,11 @@ bp2sio(const blkptr_t *bp, scan_io_t *sio, int dva_i)
458480 sio -> sio_birth = BP_GET_LOGICAL_BIRTH (bp );
459481 sio -> sio_cksum = bp -> blk_cksum ;
460482 sio -> sio_nr_dvas = BP_GET_NDVAS (bp );
483+ if (BP_IS_ENCRYPTED (bp )) {
484+ sio -> sio_salt = bp -> blk_dva [2 ].dva_word [0 ];
485+ sio -> sio_iv1 = bp -> blk_dva [2 ].dva_word [1 ];
486+ sio -> sio_iv2 = BP_GET_IV2 (bp );
487+ }
461488
462489 /*
463490 * Copy the DVAs to the sio. We need all copies of the block so
@@ -4013,8 +4040,11 @@ read_by_block_level(dsl_scan_t *scn, zbookmark_phys_t zb)
40134040 return ;
40144041 }
40154042
4016- int zio_flags = ZIO_FLAG_SCAN_THREAD | ZIO_FLAG_RAW |
4017- ZIO_FLAG_CANFAIL | ZIO_FLAG_SCRUB ;
4043+ int zio_flags = ZIO_FLAG_SCAN_THREAD | ZIO_FLAG_CANFAIL |
4044+ ZIO_FLAG_SCRUB ;
4045+
4046+ if (zfs_scrub_thorough == 0 )
4047+ zio_flags |= ZIO_FLAG_RAW ;
40184048
40194049 /* If it's an intent log block, failure is expected. */
40204050 if (zb .zb_level == ZB_ZIL_LEVEL )
@@ -4814,7 +4844,24 @@ dsl_scan_scrub_cb(dsl_pool_t *dp,
48144844 uint64_t phys_birth = BP_GET_PHYSICAL_BIRTH (bp );
48154845 size_t psize = BP_GET_PSIZE (bp );
48164846 boolean_t needs_io = B_FALSE ;
4817- int zio_flags = ZIO_FLAG_SCAN_THREAD | ZIO_FLAG_RAW | ZIO_FLAG_CANFAIL ;
4847+ int zio_flags = ZIO_FLAG_SCAN_THREAD | ZIO_FLAG_CANFAIL ;
4848+
4849+ if (zfs_scrub_thorough == 0 ) {
4850+ zio_flags |= ZIO_FLAG_RAW ;
4851+ } else if (BP_IS_ENCRYPTED (bp )) {
4852+ /*
4853+ * If we are doing a thorough scrub, the key may be unloaded
4854+ * at any time. So we fall back to a raw scrub if we don't have
4855+ * the key loaded.
4856+ */
4857+ dsl_crypto_key_t * dck = NULL ;
4858+ int err = spa_keystore_lookup_key (spa , zb -> zb_objset , FTAG , & dck );
4859+ if (err == 0 ) {
4860+ spa_keystore_dsl_key_rele (spa , dck , FTAG );
4861+ } else {
4862+ zio_flags |= ZIO_FLAG_RAW ;
4863+ }
4864+ }
48184865
48194866 count_block (dp -> dp_blkstats , bp );
48204867 if (phys_birth <= scn -> scn_phys .scn_min_txg ||
@@ -4891,7 +4938,9 @@ dsl_scan_scrub_done(zio_t *zio)
48914938 }
48924939
48934940 if (zio -> io_error && (zio -> io_error != ECKSUM ||
4894- !(zio -> io_flags & ZIO_FLAG_SPECULATIVE ))) {
4941+ !(zio -> io_flags & ZIO_FLAG_SPECULATIVE )) &&
4942+ !(zio -> io_error == EACCES && (zio -> io_flags & ZIO_FLAG_SCRUB ) &&
4943+ zfs_scrub_thorough != 0 )) {
48954944 if (dsl_errorscrubbing (spa -> spa_dsl_pool ) &&
48964945 !dsl_errorscrub_is_paused (spa -> spa_dsl_pool -> dp_scan )) {
48974946 atomic_inc_64 (& spa -> spa_dsl_pool -> dp_scan
@@ -4916,7 +4965,25 @@ scan_exec_io(dsl_pool_t *dp, const blkptr_t *bp, int zio_flags,
49164965{
49174966 spa_t * spa = dp -> dp_spa ;
49184967 dsl_scan_t * scn = dp -> dp_scan ;
4919- size_t size = BP_GET_PSIZE (bp );
4968+
4969+ /*
4970+ * If we are trying to do a thorough scrub, the key may have
4971+ * been unloaded since we queued the IO. Check if we have the
4972+ * key and if not, fall back to a raw scrub.
4973+ */
4974+ if ((zio_flags & ZIO_FLAG_RAW ) == 0 && BP_IS_ENCRYPTED (bp )) {
4975+ dsl_crypto_key_t * dck = NULL ;
4976+ int err = spa_keystore_lookup_key (spa , zb -> zb_objset ,
4977+ FTAG , & dck );
4978+ if (err == 0 ) {
4979+ spa_keystore_dsl_key_rele (spa , dck , FTAG );
4980+ } else {
4981+ zio_flags |= ZIO_FLAG_RAW ;
4982+ }
4983+ }
4984+
4985+ size_t size = (zio_flags & ZIO_FLAG_RAW ) ?
4986+ BP_GET_PSIZE (bp ) : BP_GET_LSIZE (bp );
49204987 abd_t * data = abd_alloc_for_io (size , B_FALSE );
49214988 zio_t * pio ;
49224989
@@ -5362,3 +5429,6 @@ ZFS_MODULE_PARAM(zfs, zfs_, resilver_defer_percent, UINT, ZMOD_RW,
53625429
53635430ZFS_MODULE_PARAM (zfs , zfs_ , scrub_error_blocks_per_txg , UINT , ZMOD_RW ,
53645431 "Error blocks to be scrubbed in one txg" );
5432+
5433+ ZFS_MODULE_PARAM (zfs , zfs_ , scrub_thorough , UINT , ZMOD_RW ,
5434+ "Scrub will decrypt and decompress blocks" );
0 commit comments