Add -t option to zfs destroy
Resolves #9522 by adding an optional [-t type] option gate to the `zfs destroy` command. Checks the type of the supplied argument, then checks if the supplied type is the same or one of the aliases that are also used in `zfs list`. If the type doesn't match, it prints an error to stderr and exits. Signed-off-by: Chris Simons <chris@simons.network>
This commit is contained in:
parent
8f2f6cd2ac
commit
c3a968c2f0
|
@ -287,7 +287,8 @@ get_usage(zfs_help_t idx)
|
||||||
"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
|
"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
|
||||||
"-V <size> <volume>\n"));
|
"-V <size> <volume>\n"));
|
||||||
case HELP_DESTROY:
|
case HELP_DESTROY:
|
||||||
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
return (gettext("\tdestroy [-fnpRrv] [-t type] "
|
||||||
|
"<filesystem|volume>\n"
|
||||||
"\tdestroy [-dnpRrv] "
|
"\tdestroy [-dnpRrv] "
|
||||||
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
|
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
|
||||||
"\tdestroy <filesystem|volume>#<bookmark>\n"));
|
"\tdestroy <filesystem|volume>#<bookmark>\n"));
|
||||||
|
@ -1642,9 +1643,10 @@ zfs_do_destroy(int argc, char **argv)
|
||||||
zfs_handle_t *zhp = NULL;
|
zfs_handle_t *zhp = NULL;
|
||||||
char *at, *pound;
|
char *at, *pound;
|
||||||
zfs_type_t type = ZFS_TYPE_DATASET;
|
zfs_type_t type = ZFS_TYPE_DATASET;
|
||||||
|
char *type_filter = NULL;
|
||||||
|
|
||||||
/* check options */
|
/* check options */
|
||||||
while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
|
while ((c = getopt(argc, argv, "vpndfrRt:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'v':
|
case 'v':
|
||||||
cb.cb_verbose = B_TRUE;
|
cb.cb_verbose = B_TRUE;
|
||||||
|
@ -1670,6 +1672,9 @@ zfs_do_destroy(int argc, char **argv)
|
||||||
cb.cb_recurse = B_TRUE;
|
cb.cb_recurse = B_TRUE;
|
||||||
cb.cb_doclones = B_TRUE;
|
cb.cb_doclones = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case 't':
|
||||||
|
type_filter = optarg;
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
||||||
|
@ -1691,6 +1696,54 @@ zfs_do_destroy(int argc, char **argv)
|
||||||
usage(B_FALSE);
|
usage(B_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check type before destroying if specified */
|
||||||
|
if (type_filter != NULL) {
|
||||||
|
zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
|
||||||
|
if (zhp != NULL) {
|
||||||
|
zfs_type_t type = zfs_get_type(zhp);
|
||||||
|
boolean_t type_matches = B_FALSE;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ZFS_TYPE_FILESYSTEM:
|
||||||
|
type_matches = (
|
||||||
|
(strcmp(type_filter, "fs") == 0) ||
|
||||||
|
(strcmp(type_filter, "filesystem") == 0));
|
||||||
|
break;
|
||||||
|
case ZFS_TYPE_VOLUME:
|
||||||
|
type_matches = (
|
||||||
|
(strcmp(type_filter, "vol") == 0) ||
|
||||||
|
(strcmp(type_filter, "volume") == 0));
|
||||||
|
break;
|
||||||
|
case ZFS_TYPE_SNAPSHOT:
|
||||||
|
type_matches = (
|
||||||
|
(strcmp(type_filter, "snap") == 0) ||
|
||||||
|
(strcmp(type_filter, "snapshot") == 0));
|
||||||
|
break;
|
||||||
|
case ZFS_TYPE_BOOKMARK:
|
||||||
|
type_matches =
|
||||||
|
(strcmp(type_filter, "bookmark") == 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type_matches = B_FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
zfs_close(zhp);
|
||||||
|
|
||||||
|
if (!type_matches) {
|
||||||
|
(void) fprintf(stderr,
|
||||||
|
gettext("cannot destroy '%s': "
|
||||||
|
"type does not match '%s'\n"),
|
||||||
|
argv[0], type_filter);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
at = strchr(argv[0], '@');
|
at = strchr(argv[0], '@');
|
||||||
pound = strchr(argv[0], '#');
|
pound = strchr(argv[0], '#');
|
||||||
if (at != NULL) {
|
if (at != NULL) {
|
||||||
|
|
|
@ -40,10 +40,12 @@
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm destroy
|
.Cm destroy
|
||||||
.Op Fl Rfnprv
|
.Op Fl Rfnprv
|
||||||
|
.Oo Fl t Ar type Ns Oc
|
||||||
.Ar filesystem Ns | Ns Ar volume
|
.Ar filesystem Ns | Ns Ar volume
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm destroy
|
.Cm destroy
|
||||||
.Op Fl Rdnprv
|
.Op Fl Rdnprv
|
||||||
|
.Oo Fl t Ar type Ns Oc
|
||||||
.Ar filesystem Ns | Ns Ar volume Ns @ Ns Ar snap Ns
|
.Ar filesystem Ns | Ns Ar volume Ns @ Ns Ar snap Ns
|
||||||
.Oo % Ns Ar snap Ns Oo , Ns Ar snap Ns Oo % Ns Ar snap Oc Oc Oc Ns …
|
.Oo % Ns Ar snap Ns Oo , Ns Ar snap Ns Oo % Ns Ar snap Oc Oc Oc Ns …
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
|
@ -56,6 +58,7 @@
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm destroy
|
.Cm destroy
|
||||||
.Op Fl Rfnprv
|
.Op Fl Rfnprv
|
||||||
|
.Oo Fl t Ar type Ns Oc
|
||||||
.Ar filesystem Ns | Ns Ar volume
|
.Ar filesystem Ns | Ns Ar volume
|
||||||
.Xc
|
.Xc
|
||||||
Destroys the given dataset.
|
Destroys the given dataset.
|
||||||
|
@ -84,6 +87,28 @@ flags to determine what data would be deleted.
|
||||||
Print machine-parsable verbose information about the deleted data.
|
Print machine-parsable verbose information about the deleted data.
|
||||||
.It Fl r
|
.It Fl r
|
||||||
Recursively destroy all children.
|
Recursively destroy all children.
|
||||||
|
.It Fl t Ar type
|
||||||
|
The specified type to destroy, where
|
||||||
|
.Ar type
|
||||||
|
is one of
|
||||||
|
.Sy filesystem ,
|
||||||
|
.Sy snapshot ,
|
||||||
|
.Sy volume ,
|
||||||
|
or
|
||||||
|
.Sy bookmark .
|
||||||
|
For example, specifying
|
||||||
|
.Fl t Sy snapshot
|
||||||
|
will not destroy data if the supplied argument is not a
|
||||||
|
.Sy snapshot .
|
||||||
|
.Sy fs ,
|
||||||
|
.Sy snap ,
|
||||||
|
or
|
||||||
|
.Sy vol
|
||||||
|
can be used as aliases for
|
||||||
|
.Sy filesystem ,
|
||||||
|
.Sy snapshot ,
|
||||||
|
or
|
||||||
|
.Sy volume .
|
||||||
.It Fl v
|
.It Fl v
|
||||||
Print verbose information about the deleted data.
|
Print verbose information about the deleted data.
|
||||||
.El
|
.El
|
||||||
|
@ -98,6 +123,7 @@ behavior for mounted file systems in use.
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm destroy
|
.Cm destroy
|
||||||
.Op Fl Rdnprv
|
.Op Fl Rdnprv
|
||||||
|
.Oo Fl t Ar type Ns Oc
|
||||||
.Ar filesystem Ns | Ns Ar volume Ns @ Ns Ar snap Ns
|
.Ar filesystem Ns | Ns Ar volume Ns @ Ns Ar snap Ns
|
||||||
.Oo % Ns Ar snap Ns Oo , Ns Ar snap Ns Oo % Ns Ar snap Oc Oc Oc Ns …
|
.Oo % Ns Ar snap Ns Oo , Ns Ar snap Ns Oo % Ns Ar snap Oc Oc Oc Ns …
|
||||||
.Xc
|
.Xc
|
||||||
|
@ -155,6 +181,28 @@ Print machine-parsable verbose information about the deleted data.
|
||||||
Destroy
|
Destroy
|
||||||
.Pq or mark for deferred deletion
|
.Pq or mark for deferred deletion
|
||||||
all snapshots with this name in descendent file systems.
|
all snapshots with this name in descendent file systems.
|
||||||
|
.It Fl t Ar type
|
||||||
|
The specified type to destroy, where
|
||||||
|
.Ar type
|
||||||
|
is one of
|
||||||
|
.Sy filesystem ,
|
||||||
|
.Sy snapshot ,
|
||||||
|
.Sy volume ,
|
||||||
|
or
|
||||||
|
.Sy bookmark .
|
||||||
|
For example, specifying
|
||||||
|
.Fl t Sy snapshot
|
||||||
|
will not destroy data if the supplied argument is not a
|
||||||
|
.Sy snapshot .
|
||||||
|
.Sy fs ,
|
||||||
|
.Sy snap ,
|
||||||
|
or
|
||||||
|
.Sy vol
|
||||||
|
can be used as aliases for
|
||||||
|
.Sy filesystem ,
|
||||||
|
.Sy snapshot ,
|
||||||
|
or
|
||||||
|
.Sy volume .
|
||||||
.It Fl v
|
.It Fl v
|
||||||
Print verbose information about the deleted data.
|
Print verbose information about the deleted data.
|
||||||
.Pp
|
.Pp
|
||||||
|
|
|
@ -210,8 +210,9 @@ tests = ['zfs_clone_livelist_condense_and_disable',
|
||||||
'zfs_destroy_007_neg', 'zfs_destroy_008_pos', 'zfs_destroy_009_pos',
|
'zfs_destroy_007_neg', 'zfs_destroy_008_pos', 'zfs_destroy_009_pos',
|
||||||
'zfs_destroy_010_pos', 'zfs_destroy_011_pos', 'zfs_destroy_012_pos',
|
'zfs_destroy_010_pos', 'zfs_destroy_011_pos', 'zfs_destroy_012_pos',
|
||||||
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
|
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
|
||||||
'zfs_destroy_016_pos', 'zfs_destroy_clone_livelist',
|
'zfs_destroy_016_pos', 'zfs_destroy_017_pos',
|
||||||
'zfs_destroy_dev_removal', 'zfs_destroy_dev_removal_condense']
|
'zfs_destroy_clone_livelist', 'zfs_destroy_dev_removal',
|
||||||
|
'zfs_destroy_dev_removal_condense']
|
||||||
tags = ['functional', 'cli_root', 'zfs_destroy']
|
tags = ['functional', 'cli_root', 'zfs_destroy']
|
||||||
|
|
||||||
[tests/functional/cli_root/zfs_diff]
|
[tests/functional/cli_root/zfs_diff]
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
#!/bin/ksh -p
|
||||||
|
|
||||||
|
#
|
||||||
|
# CDDL HEADER START
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# CDDL HEADER END
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 by Chris Simons <chris@simons.network>
|
||||||
|
#
|
||||||
|
|
||||||
|
. $STF_SUITE/include/libtest.shlib
|
||||||
|
. $STF_SUITE/tests/functional/cli_root/zfs_destroy/zfs_destroy.cfg
|
||||||
|
|
||||||
|
#
|
||||||
|
# DESCRIPTION:
|
||||||
|
# Verify that 'zfs destroy' with a type filter only destroys the datasets
|
||||||
|
# of the specified type.
|
||||||
|
#
|
||||||
|
# STRATEGY:
|
||||||
|
# 1. Create various types
|
||||||
|
# 2. Attempt to destroy with correct and incorrect types
|
||||||
|
# 3. Verify only expected items are destroyed
|
||||||
|
# 4. Ensure recursive destruction still occurs
|
||||||
|
#
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
{
|
||||||
|
for ds in $TESTPOOL/$TESTFS1 $TESTPOOL/$TESTVOL; do
|
||||||
|
datasetexists $ds && log_must zfs destroy -r $ds
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
log_assert "Verify 'zfs destroy' with a type filter only affects specified type"
|
||||||
|
log_onexit cleanup
|
||||||
|
|
||||||
|
# Create a filesystem, dataset and a volume
|
||||||
|
log_must zfs create $TESTPOOL/$TESTFS1
|
||||||
|
log_must zfs create $TESTPOOL/$TESTFS1/testdataset
|
||||||
|
log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL
|
||||||
|
|
||||||
|
# Take a snapshots
|
||||||
|
log_must zfs snapshot $TESTPOOL/$TESTFS1@snap
|
||||||
|
log_must zfs snapshot $TESTPOOL/$TESTVOL@snap
|
||||||
|
log_must zfs snapshot $TESTPOOL/$TESTFS1@snap_del
|
||||||
|
log_must zfs snapshot $TESTPOOL/$TESTVOL@snap_del
|
||||||
|
log_must zfs snapshot -r $TESTPOOL/$TESTFS1@snap_recursive
|
||||||
|
|
||||||
|
# Destroy only snapshots with the type filter
|
||||||
|
log_mustnot zfs destroy -t snapshot $TESTPOOL/$TESTFS1
|
||||||
|
log_mustnot zfs destroy -t snapshot -r $TESTPOOL/$TESTFS1
|
||||||
|
log_mustnot zfs destroy -t snapshot $TESTPOOL/$TESTVOL
|
||||||
|
log_mustnot zfs destroy -t snapshot -r $TESTPOOL/$TESTVOL
|
||||||
|
log_must zfs destroy -t snapshot $TESTPOOL/$TESTFS1@snap_del
|
||||||
|
log_must zfs destroy -t snapshot $TESTPOOL/$TESTVOL@snap_del
|
||||||
|
log_must zfs destroy -t snapshot -r $TESTPOOL/$TESTFS1@snap_recursive
|
||||||
|
|
||||||
|
# Verify the filesystem snapshot is destroyed and the volume snapshot remains
|
||||||
|
log_must datasetexists $TESTPOOL/$TESTFS1
|
||||||
|
log_must datasetexists $TESTPOOL/$TESTVOL
|
||||||
|
log_mustnot datasetexists $TESTPOOL/$TESTFS1@snap_del
|
||||||
|
log_mustnot datasetexists $TESTPOOL/$TESTVOL@snap_del
|
||||||
|
log_mustnot datasetexists $TESTPOOL/$TESTVOL@snap_recursive
|
||||||
|
log_mustnot datasetexists $TESTPOOL/$TESTVOL/testdataset@snap_recursive
|
||||||
|
|
||||||
|
log_pass "zfs destroy with a snapshot filter only affects specified type"
|
||||||
|
|
Loading…
Reference in New Issue