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] ... "
|
||||
"-V <size> <volume>\n"));
|
||||
case HELP_DESTROY:
|
||||
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
||||
return (gettext("\tdestroy [-fnpRrv] [-t type] "
|
||||
"<filesystem|volume>\n"
|
||||
"\tdestroy [-dnpRrv] "
|
||||
"<filesystem|volume>@<snap>[%<snap>][,...]\n"
|
||||
"\tdestroy <filesystem|volume>#<bookmark>\n"));
|
||||
|
@ -1642,9 +1643,10 @@ zfs_do_destroy(int argc, char **argv)
|
|||
zfs_handle_t *zhp = NULL;
|
||||
char *at, *pound;
|
||||
zfs_type_t type = ZFS_TYPE_DATASET;
|
||||
char *type_filter = NULL;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
|
||||
while ((c = getopt(argc, argv, "vpndfrRt:")) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
cb.cb_verbose = B_TRUE;
|
||||
|
@ -1670,6 +1672,9 @@ zfs_do_destroy(int argc, char **argv)
|
|||
cb.cb_recurse = B_TRUE;
|
||||
cb.cb_doclones = B_TRUE;
|
||||
break;
|
||||
case 't':
|
||||
type_filter = optarg;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
||||
|
@ -1691,6 +1696,54 @@ zfs_do_destroy(int argc, char **argv)
|
|||
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], '@');
|
||||
pound = strchr(argv[0], '#');
|
||||
if (at != NULL) {
|
||||
|
|
|
@ -40,10 +40,12 @@
|
|||
.Nm zfs
|
||||
.Cm destroy
|
||||
.Op Fl Rfnprv
|
||||
.Oo Fl t Ar type Ns Oc
|
||||
.Ar filesystem Ns | Ns Ar volume
|
||||
.Nm zfs
|
||||
.Cm destroy
|
||||
.Op Fl Rdnprv
|
||||
.Oo Fl t Ar type Ns Oc
|
||||
.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 …
|
||||
.Nm zfs
|
||||
|
@ -56,6 +58,7 @@
|
|||
.Nm zfs
|
||||
.Cm destroy
|
||||
.Op Fl Rfnprv
|
||||
.Oo Fl t Ar type Ns Oc
|
||||
.Ar filesystem Ns | Ns Ar volume
|
||||
.Xc
|
||||
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.
|
||||
.It Fl r
|
||||
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
|
||||
Print verbose information about the deleted data.
|
||||
.El
|
||||
|
@ -98,6 +123,7 @@ behavior for mounted file systems in use.
|
|||
.Nm zfs
|
||||
.Cm destroy
|
||||
.Op Fl Rdnprv
|
||||
.Oo Fl t Ar type Ns Oc
|
||||
.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 …
|
||||
.Xc
|
||||
|
@ -155,6 +181,28 @@ Print machine-parsable verbose information about the deleted data.
|
|||
Destroy
|
||||
.Pq or mark for deferred deletion
|
||||
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
|
||||
Print verbose information about the deleted data.
|
||||
.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_010_pos', 'zfs_destroy_011_pos', 'zfs_destroy_012_pos',
|
||||
'zfs_destroy_013_neg', 'zfs_destroy_014_pos', 'zfs_destroy_015_pos',
|
||||
'zfs_destroy_016_pos', 'zfs_destroy_clone_livelist',
|
||||
'zfs_destroy_dev_removal', 'zfs_destroy_dev_removal_condense']
|
||||
'zfs_destroy_016_pos', 'zfs_destroy_017_pos',
|
||||
'zfs_destroy_clone_livelist', 'zfs_destroy_dev_removal',
|
||||
'zfs_destroy_dev_removal_condense']
|
||||
tags = ['functional', 'cli_root', 'zfs_destroy']
|
||||
|
||||
[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