Check ashift validity in 'zpool add'

df83110 added the ability to specify a custom "ashift" value from the command
line in 'zpool add' and 'zpool attach'. This commit adds additional checks to
the provided ashift to prevent invalid values from being used, which could
result in disastrous consequences for the whole pool.

Additionally provide ASHIFT_MAX and ASHIFT_MIN definitions in spa.h.

Reviewed-by: Giuseppe Di Natale <dinatale2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #5878
This commit is contained in:
LOLi 2017-03-29 02:21:11 +02:00 committed by Brian Behlendorf
parent 12aec7dcd9
commit ff61d1a495
8 changed files with 153 additions and 9 deletions

View File

@ -70,6 +70,7 @@
#include <libintl.h> #include <libintl.h>
#include <libnvpair.h> #include <libnvpair.h>
#include <limits.h> #include <limits.h>
#include <sys/spa.h>
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include <scsi/sg.h> #include <scsi/sg.h>
#include <stdio.h> #include <stdio.h>
@ -721,8 +722,18 @@ make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
char *value = NULL; char *value = NULL;
if (nvlist_lookup_string(props, if (nvlist_lookup_string(props,
zpool_prop_to_name(ZPOOL_PROP_ASHIFT), &value) == 0) zpool_prop_to_name(ZPOOL_PROP_ASHIFT), &value) == 0) {
zfs_nicestrtonum(NULL, value, &ashift); zfs_nicestrtonum(NULL, value, &ashift);
if (ashift != 0 &&
(ashift < ASHIFT_MIN || ashift > ASHIFT_MAX)) {
(void) fprintf(stderr,
gettext("invalid 'ashift=%" PRIu64 "' "
"property: only values between %" PRId32 " "
"and %" PRId32 " are allowed.\n"),
ashift, ASHIFT_MIN, ASHIFT_MAX);
return (NULL);
}
}
} }
/* /*

View File

@ -732,7 +732,8 @@ typedef enum vdev_aux {
VDEV_AUX_IO_FAILURE, /* experienced I/O failure */ VDEV_AUX_IO_FAILURE, /* experienced I/O failure */
VDEV_AUX_BAD_LOG, /* cannot read log chain(s) */ VDEV_AUX_BAD_LOG, /* cannot read log chain(s) */
VDEV_AUX_EXTERNAL, /* external diagnosis */ VDEV_AUX_EXTERNAL, /* external diagnosis */
VDEV_AUX_SPLIT_POOL /* vdev was split off into another pool */ VDEV_AUX_SPLIT_POOL, /* vdev was split off into another pool */
VDEV_AUX_BAD_ASHIFT /* vdev ashift is invalid */
} vdev_aux_t; } vdev_aux_t;
/* /*

View File

@ -121,6 +121,17 @@ _NOTE(CONSTCOND) } while (0)
#define SPA_OLD_MAXBLOCKSIZE (1ULL << SPA_OLD_MAXBLOCKSHIFT) #define SPA_OLD_MAXBLOCKSIZE (1ULL << SPA_OLD_MAXBLOCKSHIFT)
#define SPA_MAXBLOCKSIZE (1ULL << SPA_MAXBLOCKSHIFT) #define SPA_MAXBLOCKSIZE (1ULL << SPA_MAXBLOCKSHIFT)
/*
* Alignment Shift (ashift) is an immutable, internal top-level vdev property
* which can only be set at vdev creation time. Physical writes are always done
* according to it, which makes 2^ashift the smallest possible IO on a vdev.
*
* We currently allow values ranging from 512 bytes (2^9 = 512) to 8 KiB
* (2^13 = 8,192).
*/
#define ASHIFT_MIN 9
#define ASHIFT_MAX 13
/* /*
* Size of block to hold the configuration data (a packed nvlist) * Size of block to hold the configuration data (a packed nvlist)
*/ */

View File

@ -538,10 +538,13 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
goto error; goto error;
} }
if (intval != 0 && (intval < 9 || intval > 13)) { if (intval != 0 &&
(intval < ASHIFT_MIN || intval > ASHIFT_MAX)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s' number %d is invalid."), "invalid '%s=%d' property: only values "
propname, intval); "between %" PRId32 " and %" PRId32 " "
"are allowed.\n"),
propname, intval, ASHIFT_MIN, ASHIFT_MAX);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf); (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error; goto error;
} }

View File

@ -1345,8 +1345,15 @@ vdev_open(vdev_t *vd)
*/ */
vd->vdev_asize = asize; vd->vdev_asize = asize;
vd->vdev_max_asize = max_asize; vd->vdev_max_asize = max_asize;
if (vd->vdev_ashift == 0) if (vd->vdev_ashift == 0) {
vd->vdev_ashift = ashift; vd->vdev_ashift = ashift; /* use detected value */
}
if (vd->vdev_ashift != 0 && (vd->vdev_ashift < ASHIFT_MIN ||
vd->vdev_ashift > ASHIFT_MAX)) {
vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN,
VDEV_AUX_BAD_ASHIFT);
return (SET_ERROR(EDOM));
}
} else { } else {
/* /*
* Detect if the alignment requirement has increased. * Detect if the alignment requirement has increased.
@ -3487,6 +3494,9 @@ vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state, vdev_aux_t aux)
case VDEV_AUX_BAD_LABEL: case VDEV_AUX_BAD_LABEL:
class = FM_EREPORT_ZFS_DEVICE_BAD_LABEL; class = FM_EREPORT_ZFS_DEVICE_BAD_LABEL;
break; break;
case VDEV_AUX_BAD_ASHIFT:
class = FM_EREPORT_ZFS_DEVICE_BAD_ASHIFT;
break;
default: default:
class = FM_EREPORT_ZFS_DEVICE_UNKNOWN; class = FM_EREPORT_ZFS_DEVICE_UNKNOWN;
} }

View File

@ -230,7 +230,8 @@ tests = ['zpool_001_neg', 'zpool_002_pos', 'zpool_003_pos']
# zpool_add_006_pos - https://github.com/zfsonlinux/zfs/issues/3484 # zpool_add_006_pos - https://github.com/zfsonlinux/zfs/issues/3484
[tests/functional/cli_root/zpool_add] [tests/functional/cli_root/zpool_add]
tests = ['zpool_add_001_pos', 'zpool_add_002_pos', 'zpool_add_003_pos', tests = ['zpool_add_001_pos', 'zpool_add_002_pos', 'zpool_add_003_pos',
'zpool_add_007_neg', 'zpool_add_008_neg', 'zpool_add_009_neg'] 'zpool_add_007_neg', 'zpool_add_008_neg', 'zpool_add_009_neg',
'add-o_ashift']
[tests/functional/cli_root/zpool_attach] [tests/functional/cli_root/zpool_attach]
tests = ['zpool_attach_001_neg'] tests = ['zpool_attach_001_neg']

View File

@ -12,4 +12,5 @@ dist_pkgdata_SCRIPTS = \
zpool_add_006_pos.ksh \ zpool_add_006_pos.ksh \
zpool_add_007_neg.ksh \ zpool_add_007_neg.ksh \
zpool_add_008_neg.ksh \ zpool_add_008_neg.ksh \
zpool_add_009_neg.ksh zpool_add_009_neg.ksh \
add-o_ashift.ksh

View File

@ -0,0 +1,106 @@
#!/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 2017, loli10K. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_create/zpool_create.shlib
#
# DESCRIPTION:
# 'zpool add -o ashift=<n> ...' should work with different ashift
# values.
#
# STRATEGY:
# 1. Create a pool with default values.
# 2. Verify 'zpool add -o ashift=<n>' works with allowed values (9-13).
# 3. Verify 'zpool add -o ashift=<n>' doesn't accept other invalid values.
#
verify_runnable "global"
function cleanup
{
poolexists $TESTPOOL && destroy_pool $TESTPOOL
log_must $RM $disk1 $disk2
}
#
# Verify every label in device $1 contains ashift value $2
# $1 device
# $2 ashift value
#
function verify_device_ashift
{
typeset device=$1
typeset value=$2
typeset ashift
$ZDB -e -l $device | $GREP " ashift:" | {
while read ashift ; do
if [[ "ashift: $value" != "$ashift" ]]; then
return 1
fi
done
}
return 0
}
log_assert "zpool add -o ashift=<n>' works with different ashift values"
log_onexit cleanup
disk1=$TEST_BASE_DIR/$FILEDISK0
disk2=$TEST_BASE_DIR/$FILEDISK1
log_must $MKFILE $SIZE $disk1
log_must $MKFILE $SIZE $disk2
typeset ashifts=("9" "10" "11" "12" "13")
for ashift in ${ashifts[@]}
do
log_must $ZPOOL create $TESTPOOL $disk1
log_must $ZPOOL add -o ashift=$ashift $TESTPOOL $disk2
verify_device_ashift $disk2 $ashift
if [[ $? -ne 0 ]]
then
log_fail "Device was added without setting ashift value to "\
"$ashift"
fi
# clean things for the next run
log_must $ZPOOL destroy $TESTPOOL
log_must $ZPOOL labelclear $disk1
log_must $ZPOOL labelclear $disk2
done
typeset badvals=("off" "on" "1" "8" "14" "1b" "ff" "-")
for badval in ${badvals[@]}
do
log_must $ZPOOL create $TESTPOOL $disk1
log_mustnot $ZPOOL add -o ashift="$badval" $disk2
log_must $ZPOOL destroy $TESTPOOL
log_must $ZPOOL labelclear $disk1
log_must $ZPOOL labelclear $disk2
done
log_pass "zpool add -o ashift=<n>' works with different ashift values"