This commit is contained in:
Mariusz Zaborski 2024-07-17 07:15:51 +08:00 committed by GitHub
commit 9e0cb8704b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 510 additions and 0 deletions

View File

@ -112,6 +112,7 @@ static int zfs_do_hold(int argc, char **argv);
static int zfs_do_holds(int argc, char **argv); static int zfs_do_holds(int argc, char **argv);
static int zfs_do_release(int argc, char **argv); static int zfs_do_release(int argc, char **argv);
static int zfs_do_diff(int argc, char **argv); static int zfs_do_diff(int argc, char **argv);
static int zfs_do_scrub(int argc, char **argv);
static int zfs_do_bookmark(int argc, char **argv); static int zfs_do_bookmark(int argc, char **argv);
static int zfs_do_channel_program(int argc, char **argv); static int zfs_do_channel_program(int argc, char **argv);
static int zfs_do_load_key(int argc, char **argv); static int zfs_do_load_key(int argc, char **argv);
@ -181,6 +182,7 @@ typedef enum {
HELP_HOLDS, HELP_HOLDS,
HELP_RELEASE, HELP_RELEASE,
HELP_DIFF, HELP_DIFF,
HELP_SCRUB,
HELP_BOOKMARK, HELP_BOOKMARK,
HELP_CHANNEL_PROGRAM, HELP_CHANNEL_PROGRAM,
HELP_LOAD_KEY, HELP_LOAD_KEY,
@ -253,6 +255,7 @@ static zfs_command_t command_table[] = {
{ "holds", zfs_do_holds, HELP_HOLDS }, { "holds", zfs_do_holds, HELP_HOLDS },
{ "release", zfs_do_release, HELP_RELEASE }, { "release", zfs_do_release, HELP_RELEASE },
{ "diff", zfs_do_diff, HELP_DIFF }, { "diff", zfs_do_diff, HELP_DIFF },
{ "scrub", zfs_do_scrub, HELP_SCRUB },
{ "load-key", zfs_do_load_key, HELP_LOAD_KEY }, { "load-key", zfs_do_load_key, HELP_LOAD_KEY },
{ "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY }, { "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY },
{ "change-key", zfs_do_change_key, HELP_CHANGE_KEY }, { "change-key", zfs_do_change_key, HELP_CHANGE_KEY },
@ -401,6 +404,8 @@ get_usage(zfs_help_t idx)
case HELP_DIFF: case HELP_DIFF:
return (gettext("\tdiff [-FHth] <snapshot> " return (gettext("\tdiff [-FHth] <snapshot> "
"[snapshot|filesystem]\n")); "[snapshot|filesystem]\n"));
case HELP_SCRUB:
return (gettext("\tscrub <file>\n"));
case HELP_BOOKMARK: case HELP_BOOKMARK:
return (gettext("\tbookmark <snapshot|bookmark> " return (gettext("\tbookmark <snapshot|bookmark> "
"<newbookmark>\n")); "<newbookmark>\n"));
@ -7938,6 +7943,98 @@ out:
return (err != 0); return (err != 0);
} }
static int
scrub_single_file(zfs_handle_t *zhp, void *data)
{
struct stat64 *filestat = (struct stat64 *)data;
struct stat64 mountstat;
char mountpoint[ZFS_MAXPROPLEN];
int err;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
return (0);
}
if (stat64(mountpoint, &mountstat) < 0) {
return (0);
}
if (mountstat.st_dev != filestat->st_dev) {
return (0);
}
nvlist_t *args = fnvlist_alloc();
fnvlist_add_string(args, "dataset", zfs_get_name(zhp));
fnvlist_add_uint64(args, "object", filestat->st_ino);
err = lzc_scrub(ZFS_IOC_POOL_SCRUB_FILE, zfs_get_pool_name(zhp),
args, NULL);
fnvlist_free(args);
return (err != 0 ? 2 : 1);
}
static int
find_dataset_and_scrub(zfs_handle_t *zhp, void *data)
{
int err;
err = scrub_single_file(zhp, data);
if (err == 0) {
err = zfs_iter_filesystems_v2(zhp, 0, find_dataset_and_scrub,
data);
}
zfs_close(zhp);
return (err);
}
/*
* zfs scrub <file>
*
* Scrubs single file.
*/
static int
zfs_do_scrub(int argc, char **argv)
{
char *filetoscrub = NULL;
struct stat64 filestat;
int err = 0;
if (argc < 1) {
(void) fprintf(stderr,
gettext("must provide a file to scrub\n"));
usage(B_FALSE);
}
if (argc > 2) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}
filetoscrub = argv[1];
if (stat64(filetoscrub, &filestat) < 0) {
(void) fprintf(stderr,
gettext("must provide a file to scrub\n"));
usage(B_FALSE);
}
if (S_ISREG(filestat.st_mode) == 0) {
(void) fprintf(stderr,
gettext("scrub works only on regular files\n"));
usage(B_FALSE);
}
err = zfs_iter_root(g_zfs, find_dataset_and_scrub, &filestat);
if (err == 0) {
(void) fprintf(stderr,
gettext("unable to scrub file %s\n"), filetoscrub);
}
return (err == 1 ? 0 : -1);
}
/* /*
* zfs bookmark <fs@source>|<fs#source> <fs#bookmark> * zfs bookmark <fs@source>|<fs#source> <fs#bookmark>
* *

View File

@ -1512,6 +1512,7 @@ typedef enum zfs_ioc {
ZFS_IOC_VDEV_GET_PROPS, /* 0x5a55 */ ZFS_IOC_VDEV_GET_PROPS, /* 0x5a55 */
ZFS_IOC_VDEV_SET_PROPS, /* 0x5a56 */ ZFS_IOC_VDEV_SET_PROPS, /* 0x5a56 */
ZFS_IOC_POOL_SCRUB, /* 0x5a57 */ ZFS_IOC_POOL_SCRUB, /* 0x5a57 */
ZFS_IOC_POOL_SCRUB_FILE, /* 0x5a58 */
/* /*
* Per-platform (Optional) - 8/128 numbers reserved. * Per-platform (Optional) - 8/128 numbers reserved.

53
man/man8/zfs-scrub.8 Normal file
View File

@ -0,0 +1,53 @@
.\"
.\" CDDL HEADER START
.\"
.\" The contents of this file are subject to the terms of the
.\" Common Development and Distribution License (the "License").
.\" You may not use this file except in compliance with the License.
.\"
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
.\" or https://opensource.org/licenses/CDDL-1.0.
.\" See the License for the specific language governing permissions
.\" and limitations under the License.
.\"
.\" When distributing Covered Code, include this CDDL HEADER in each
.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE.
.\" If applicable, add the following below this CDDL HEADER, with the
.\" fields enclosed by brackets "[]" replaced with your own identifying
.\" information: Portions Copyright [yyyy] [name of copyright owner]
.\"
.\" CDDL HEADER END
.\"
.\" Copyright 2024 Klara, Inc.
.\" Copyright 2024 Mariusz Zaborski <oshogbo@FreeBSD.org>
.\"
.Dd February 21, 2024
.Dt ZFS-SCRUB 8
.Os
.
.Sh NAME
.Nm zfs-scrub
.Nd run scrub of file in ZFS storage pools
.Sh SYNOPSIS
.Nm zfs
.Cm scrub
.Ar filename Ns
.
.Sh DESCRIPTION
Runs a scrub for a single file.
The scrub examines all data associated with a given file.
For replicated
.Pq mirror, raidz, or draid
devices, ZFS automatically repairs any damage discovered during the scrub.
The
.Nm zpool Cm status
command reports the progress of the scrub and summarizes the results of the
scrub upon completion.
For more details about scrubing, refer
.Xr zpool 8 .
.
.Pp
ZFS allows only on scrub process at a time (pool scrub or file scrub).
.Sh SEE ALSO
.Xr zpool-scrub 8 ,
.Xr zpool-status 8

View File

@ -275,6 +275,12 @@ Delegate permissions on the specified filesystem or volume.
Remove delegated permissions on the specified filesystem or volume. Remove delegated permissions on the specified filesystem or volume.
.El .El
. .
.Ss Maintenance
.Bl -tag -width ""
.It Xr zfs-scrub 8
Runs a scrub of single file.
.El
.
.Ss Encryption .Ss Encryption
.Bl -tag -width "" .Bl -tag -width ""
.It Xr zfs-change-key 8 .It Xr zfs-change-key 8
@ -818,6 +824,7 @@ don't wait.
.Xr zfs-release 8 , .Xr zfs-release 8 ,
.Xr zfs-rename 8 , .Xr zfs-rename 8 ,
.Xr zfs-rollback 8 , .Xr zfs-rollback 8 ,
.Xr zfs-scrub 8 ,
.Xr zfs-send 8 , .Xr zfs-send 8 ,
.Xr zfs-set 8 , .Xr zfs-set 8 ,
.Xr zfs-share 8 , .Xr zfs-share 8 ,

View File

@ -154,6 +154,7 @@ timer units are provided.
. .
.Sh SEE ALSO .Sh SEE ALSO
.Xr systemd.timer 5 , .Xr systemd.timer 5 ,
.Xr zfs-scrub 8 ,
.Xr zpool-iostat 8 , .Xr zpool-iostat 8 ,
.Xr zpool-resilver 8 , .Xr zpool-resilver 8 ,
.Xr zpool-status 8 .Xr zpool-status 8

View File

@ -202,6 +202,7 @@
#include <sys/dmu_recv.h> #include <sys/dmu_recv.h>
#include <sys/dmu_send.h> #include <sys/dmu_send.h>
#include <sys/dmu_recv.h> #include <sys/dmu_recv.h>
#include <sys/dbuf.h>
#include <sys/dsl_destroy.h> #include <sys/dsl_destroy.h>
#include <sys/dsl_bookmark.h> #include <sys/dsl_bookmark.h>
#include <sys/dsl_userhold.h> #include <sys/dsl_userhold.h>
@ -1727,6 +1728,131 @@ zfs_ioc_pool_scrub(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
return (error); return (error);
} }
/*
* inputs:
* dataset name of dataset
* object object number
*/
static const zfs_ioc_key_t zfs_keys_pool_scrub_file[] = {
{"dataset", DATA_TYPE_STRING, 0},
{"object", DATA_TYPE_UINT64, 0},
};
static void
scrub_file_traverse(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb)
{
uint64_t blk_birth = bp->blk_birth;
if (blk_birth == 0)
return;
spa_log_error(spa, zb, &blk_birth);
if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) {
arc_flags_t flags = ARC_FLAG_WAIT;
int i;
blkptr_t *cbp;
int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT;
arc_buf_t *buf;
if (arc_read(NULL, spa, bp, arc_getbuf_func, &buf,
ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb))
return;
/* recursively visit blocks below this */
cbp = buf->b_data;
for (i = 0; i < epb; i++, cbp++) {
zbookmark_phys_t czb;
SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object,
zb->zb_level - 1,
zb->zb_blkid * epb + i);
scrub_file_traverse(spa, cbp, &czb);
}
}
}
static int
zfs_ioc_pool_scrub_file(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
{
spa_t *spa;
int error;
const char *dataset;
uint64_t object;
objset_t *os;
dmu_object_info_t doi;
zbookmark_phys_t zb;
dmu_buf_t *db = NULL;
dnode_t *dn;
if (nvlist_lookup_string(innvl, "dataset", &dataset) != 0)
return (SET_ERROR(EINVAL));
if (nvlist_lookup_uint64(innvl, "object", &object) != 0)
return (SET_ERROR(EINVAL));
error = dmu_objset_hold(dataset, FTAG, &os);
if (error != 0) {
return (error);
}
dsl_dataset_long_hold(dmu_objset_ds(os), FTAG);
spa = dmu_objset_spa(os);
if (!spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG)) {
dsl_pool_rele(dmu_objset_pool(os), FTAG);
dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
dsl_dataset_rele_flags(dmu_objset_ds(os), 0, FTAG);
return (SET_ERROR(ENOTSUP));
}
error = dmu_object_info(os, object, &doi);
if (error != 0) {
dsl_pool_rele(dmu_objset_pool(os), FTAG);
dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
dsl_dataset_rele_flags(dmu_objset_ds(os), 0, FTAG);
return (error);
}
if (doi.doi_type != DMU_OT_PLAIN_FILE_CONTENTS) {
dsl_pool_rele(dmu_objset_pool(os), FTAG);
dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
dsl_dataset_rele_flags(dmu_objset_ds(os), 0, FTAG);
return (EINVAL);
}
error = dmu_bonus_hold(os, object, FTAG, &db);
if (error != 0) {
dsl_pool_rele(dmu_objset_pool(os), FTAG);
dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
dsl_dataset_rele_flags(dmu_objset_ds(os), 0, FTAG);
return (error);
}
dn = DB_DNODE((dmu_buf_impl_t *)db);
zb.zb_objset = dmu_objset_id(os);
zb.zb_object = object;
dmu_buf_rele(db, FTAG);
for (int j = 0; j < dn->dn_phys->dn_nblkptr; j++) {
zb.zb_blkid = j;
zb.zb_level = BP_GET_LEVEL(&dn->dn_phys->dn_blkptr[j]);
scrub_file_traverse(spa, &dn->dn_phys->dn_blkptr[j], &zb);
}
dsl_pool_rele(dmu_objset_pool(os), FTAG);
dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
dsl_dataset_rele_flags(dmu_objset_ds(os), 0, FTAG);
txg_wait_synced(spa_get_dsl(spa), 0);
if ((error = spa_open(poolname, &spa, FTAG)) != 0)
return (error);
error = spa_scan(spa, POOL_SCAN_ERRORSCRUB);
spa_close(spa, FTAG);
return (error);
}
static int static int
zfs_ioc_pool_freeze(zfs_cmd_t *zc) zfs_ioc_pool_freeze(zfs_cmd_t *zc)
{ {
@ -7328,6 +7454,11 @@ zfs_ioctl_init(void)
POOL_CHECK_NONE, B_TRUE, B_TRUE, POOL_CHECK_NONE, B_TRUE, B_TRUE,
zfs_keys_pool_scrub, ARRAY_SIZE(zfs_keys_pool_scrub)); zfs_keys_pool_scrub, ARRAY_SIZE(zfs_keys_pool_scrub));
zfs_ioctl_register("scrub_file", ZFS_IOC_POOL_SCRUB_FILE,
zfs_ioc_pool_scrub_file, zfs_secpolicy_config, POOL_NAME,
POOL_CHECK_NONE, B_TRUE, B_TRUE,
zfs_keys_pool_scrub_file, ARRAY_SIZE(zfs_keys_pool_scrub_file));
/* IOCTLS that use the legacy function signature */ /* IOCTLS that use the legacy function signature */
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,

View File

@ -302,6 +302,10 @@ tests = ['zfs_rollback_001_pos', 'zfs_rollback_002_pos',
'zfs_rollback_003_neg', 'zfs_rollback_004_neg'] 'zfs_rollback_003_neg', 'zfs_rollback_004_neg']
tags = ['functional', 'cli_root', 'zfs_rollback'] tags = ['functional', 'cli_root', 'zfs_rollback']
[tests/functional/cli_root/zfs_scrub]
tests = ['zfs_error_scrub_001_pos', 'zfs_error_scrub_002_pos']
tags = ['functional', 'cli_root', 'zfs_scrub']
[tests/functional/cli_root/zfs_send] [tests/functional/cli_root/zfs_send]
tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos', tests = ['zfs_send_001_pos', 'zfs_send_002_pos', 'zfs_send_003_pos',
'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos', 'zfs_send_004_neg', 'zfs_send_005_pos', 'zfs_send_006_pos',

View File

@ -1989,6 +1989,7 @@ function check_pool_status # pool token keyword <verbose>
# #
# The following functions are instance of check_pool_status() # The following functions are instance of check_pool_status()
# if_pool_without_errors - to check if the pool has errors
# is_pool_resilvering - to check if the pool resilver is in progress # is_pool_resilvering - to check if the pool resilver is in progress
# is_pool_resilvered - to check if the pool resilver is completed # is_pool_resilvered - to check if the pool resilver is completed
# is_pool_scrubbing - to check if the pool scrub is in progress # is_pool_scrubbing - to check if the pool scrub is in progress
@ -2000,6 +2001,13 @@ function check_pool_status # pool token keyword <verbose>
# is_pool_discarding - to check if the pool checkpoint is being discarded # is_pool_discarding - to check if the pool checkpoint is being discarded
# is_pool_replacing - to check if the pool is performing a replacement # is_pool_replacing - to check if the pool is performing a replacement
# #
function is_pool_without_errors #pool <verbose>
{
check_pool_status "$1" "errors" "No known data errors" $2
return $?
}
function is_pool_resilvering #pool <verbose> function is_pool_resilvering #pool <verbose>
{ {
check_pool_status "$1" "scan" \ check_pool_status "$1" "scan" \

View File

@ -853,6 +853,10 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh \ functional/cli_root/zfs_rollback/zfs_rollback_002_pos.ksh \
functional/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh \ functional/cli_root/zfs_rollback/zfs_rollback_003_neg.ksh \
functional/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh \ functional/cli_root/zfs_rollback/zfs_rollback_004_neg.ksh \
functional/cli_root/zfs_scrub/cleanup.ksh \
functional/cli_root/zfs_scrub/setup.ksh \
functional/cli_root/zfs_scrub/zfs_error_scrub_001_pos.ksh \
functional/cli_root/zfs_scrub/zfs_error_scrub_002_pos.ksh \
functional/cli_root/zfs_send/cleanup.ksh \ functional/cli_root/zfs_send/cleanup.ksh \
functional/cli_root/zfs_send/setup.ksh \ functional/cli_root/zfs_send/setup.ksh \
functional/cli_root/zfs_send/zfs_send_001_pos.ksh \ functional/cli_root/zfs_send/zfs_send_001_pos.ksh \

View File

@ -0,0 +1,21 @@
#!/bin/ksh -p
#
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2024 Klara, Inc.
# Copyright 2024 Mariusz Zaborski <oshogbo@FreeBSD.org>
#
. $STF_SUITE/include/libtest.shlib
default_cleanup

View File

@ -0,0 +1,22 @@
#!/bin/ksh -p
#
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2024 Klara, Inc.
# Copyright 2024 Mariusz Zaborski <oshogbo@FreeBSD.org>
#
. $STF_SUITE/include/libtest.shlib
DISK=${DISKS%% *}
default_setup $DISK

View File

@ -0,0 +1,89 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2024 Klara, Inc.
# Copyright 2024 Mariusz Zaborski <oshogbo@FreeBSD.org>
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Verify scrubbing a single file feature. One file uses indirect
# blocks, and second doesn't.
#
# STRATEGY:
# 1. Create a pool.
# 2. Create a 10MB file in it (not using indirect blocks).
# 3. Create a 1GB file in it (using indirect blocks).
# 4. Inject write errors on the small file.
# 5. Start a scrub on a 10MB file.
# 6. Verify that the file was reported as a buggy.
# 7. Clear errors.
# 8. Inject write errors on the small file.
# 9. Start a scrub on a 1GB file.
# 10. Verify that the file was reported as a buggy.
#
verify_runnable "global"
function cleanup
{
log_must zinject -c all
rm -f /$TESTPOOL/10m_file
log_must zpool clear $TESTPOOL
}
log_onexit cleanup
log_assert "Verify small and large file scrub"
# To automatically determine the pool in which a file resides, access to the
# list of pools is required.
unset __ZFS_POOL_EXCLUDE
export __ZFS_POOL_RESTRICT="$TESTPOOL"
log_must fio --rw=write --name=job --size=10M --filename=/$TESTPOOL/10m_file
log_must fio --rw=write --name=job --size=1G --filename=/$TESTPOOL/1G_file
log_must sync_pool $TESTPOOL
log_must zinject -t data -e checksum -f 100 -am /$TESTPOOL/10m_file
# check that small file is faulty
log_must is_pool_without_errors $TESTPOOL true
log_must zfs scrub /$TESTPOOL/10m_file
log_must zpool wait -t scrub $TESTPOOL
log_mustnot is_pool_without_errors $TESTPOOL true
# clear errors on small file
log_must zinject -c all
log_must zfs scrub /$TESTPOOL/10m_file
log_must zpool wait -t scrub $TESTPOOL
# check that large file is faulty
log_must zinject -t data -e checksum -f 100 -am /$TESTPOOL/1G_file
log_must is_pool_without_errors $TESTPOOL true
log_must zfs scrub /$TESTPOOL/1G_file
log_must zpool wait -t scrub $TESTPOOL
log_mustnot is_pool_without_errors $TESTPOOL true
log_pass "Verified file scrub shows expected status."

View File

@ -0,0 +1,72 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2024 Klara, Inc.
# Copyright 2024 Mariusz Zaborski <oshogbo@FreeBSD.org>
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Scrub a single file and self-healing using additional copies.
#
# STRATEGY:
# 1. Create a dataset with copies=3
# 2. Write a file to the dataset
# 3. zinject errors into the first and second DVAs of that file
# 4. Scrub single file and verify the scrub repaired all errors
# 5. Remove the zinject handler
#
verify_runnable "global"
function cleanup
{
log_must zinject -c all
destroy_dataset $TESTPOOL/$TESTFS2
log_must zpool clear $TESTPOOL
}
log_onexit cleanup
log_assert "Verify scrubing a single file with additional copies"
# To automatically determine the pool in which a file resides, access to the
# list of pools is required.
unset __ZFS_POOL_EXCLUDE
export __ZFS_POOL_RESTRICT="$TESTPOOL"
log_must zfs create -o copies=3 $TESTPOOL/$TESTFS2
typeset mntpnt=$(get_prop mountpoint $TESTPOOL/$TESTFS2)
log_must fio --rw=write --name=job --size=10M --filename=$mntpnt/file
log_must sync_pool $TESTPOOL
log_must zinject -a -t data -C 0,1 -e io $mntpnt/file
log_must zfs scrub $mntpnt/file
log_must is_pool_without_errors $TESTPOOL true
log_must fio --rw=write --name=job --size=10M --filename=$mntpnt/file
log_must is_pool_without_errors $TESTPOOL true
log_pass "Verified file scrub shows expected status."