mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
1644 add ZFS "clones" property
1645 add ZFS "written" and "written@..." properties
1646 "zfs send" should estimate size of stream
1647 "zfs destroy" should determine space reclaimed by
destroying multiple snapshots
1708 adjust size of zpool history data
References:
https://www.illumos.org/issues/1644
https://www.illumos.org/issues/1645
https://www.illumos.org/issues/1646
https://www.illumos.org/issues/1647
https://www.illumos.org/issues/1708
This commit modifies the user to kernel space ioctl ABI. Extra
care should be taken when updating to ensure both the kernel
modules and utilities are updated. This change has reordered
all of the new ioctl()s to the end of the list. This should
help minimize this issue in the future.
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: George Wilson <gwilson@zfsmail.com>
Reviewed by: Albert Lee <trisk@opensolaris.org>
Approved by: Garrett D'Amore <garret@nexenta.com>
Ported by: Martin Matuska <martin@matuska.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #826
Closes #664
This commit is contained in:
committed by
Brian Behlendorf
parent
7eebaff409
commit
330d06f90d
@@ -14,6 +14,7 @@ libzfs_la_SOURCES = \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_fru.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_graph.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_import.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_iter.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_mount.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_pool.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_sendrecv.c \
|
||||
|
||||
+11
-1
@@ -137,7 +137,7 @@ libzfs_la_DEPENDENCIES = $(top_builddir)/lib/libshare/libshare.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la
|
||||
am_libzfs_la_OBJECTS = libzfs_changelist.lo libzfs_config.lo \
|
||||
libzfs_dataset.lo libzfs_diff.lo libzfs_fru.lo libzfs_graph.lo \
|
||||
libzfs_import.lo libzfs_mount.lo libzfs_pool.lo \
|
||||
libzfs_import.lo libzfs_iter.lo libzfs_mount.lo libzfs_pool.lo \
|
||||
libzfs_sendrecv.lo libzfs_status.lo libzfs_util.lo
|
||||
libzfs_la_OBJECTS = $(am_libzfs_la_OBJECTS)
|
||||
AM_V_lt = $(am__v_lt_$(V))
|
||||
@@ -371,6 +371,7 @@ libzfs_la_SOURCES = \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_fru.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_graph.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_import.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_iter.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_mount.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_pool.c \
|
||||
$(top_srcdir)/lib/libzfs/libzfs_sendrecv.c \
|
||||
@@ -464,6 +465,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_fru.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_graph.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_import.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_iter.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_mount.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_pool.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzfs_sendrecv.Plo@am__quote@
|
||||
@@ -550,6 +552,14 @@ libzfs_import.lo: $(top_srcdir)/lib/libzfs/libzfs_import.c
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzfs_import.lo `test -f '$(top_srcdir)/lib/libzfs/libzfs_import.c' || echo '$(srcdir)/'`$(top_srcdir)/lib/libzfs/libzfs_import.c
|
||||
|
||||
libzfs_iter.lo: $(top_srcdir)/lib/libzfs/libzfs_iter.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzfs_iter.lo -MD -MP -MF $(DEPDIR)/libzfs_iter.Tpo -c -o libzfs_iter.lo `test -f '$(top_srcdir)/lib/libzfs/libzfs_iter.c' || echo '$(srcdir)/'`$(top_srcdir)/lib/libzfs/libzfs_iter.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libzfs_iter.Tpo $(DEPDIR)/libzfs_iter.Plo
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$(top_srcdir)/lib/libzfs/libzfs_iter.c' object='libzfs_iter.lo' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzfs_iter.lo `test -f '$(top_srcdir)/lib/libzfs/libzfs_iter.c' || echo '$(srcdir)/'`$(top_srcdir)/lib/libzfs/libzfs_iter.c
|
||||
|
||||
libzfs_mount.lo: $(top_srcdir)/lib/libzfs/libzfs_mount.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzfs_mount.lo -MD -MP -MF $(DEPDIR)/libzfs_mount.Tpo -c -o libzfs_mount.lo `test -f '$(top_srcdir)/lib/libzfs/libzfs_mount.c' || echo '$(srcdir)/'`$(top_srcdir)/lib/libzfs/libzfs_mount.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libzfs_mount.Tpo $(DEPDIR)/libzfs_mount.Plo
|
||||
|
||||
+289
-198
@@ -464,7 +464,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
static zfs_handle_t *
|
||||
zfs_handle_t *
|
||||
make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
@@ -481,7 +481,7 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
static zfs_handle_t *
|
||||
zfs_handle_t *
|
||||
make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
@@ -498,6 +498,53 @@ make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
zfs_handle_t *
|
||||
zfs_handle_dup(zfs_handle_t *zhp_orig)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
|
||||
if (zhp == NULL)
|
||||
return (NULL);
|
||||
|
||||
zhp->zfs_hdl = zhp_orig->zfs_hdl;
|
||||
zhp->zpool_hdl = zhp_orig->zpool_hdl;
|
||||
(void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
|
||||
sizeof (zhp->zfs_name));
|
||||
zhp->zfs_type = zhp_orig->zfs_type;
|
||||
zhp->zfs_head_type = zhp_orig->zfs_head_type;
|
||||
zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
|
||||
if (zhp_orig->zfs_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (zhp_orig->zfs_user_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_user_props,
|
||||
&zhp->zfs_user_props, 0) != 0) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (zhp_orig->zfs_recvd_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_recvd_props,
|
||||
&zhp->zfs_recvd_props, 0)) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
|
||||
if (zhp_orig->zfs_mntopts != NULL) {
|
||||
zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
|
||||
zhp_orig->zfs_mntopts);
|
||||
}
|
||||
zhp->zfs_props_table = zhp_orig->zfs_props_table;
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens the given snapshot, filesystem, or volume. The 'types'
|
||||
* argument is a mask of acceptable types. The function will print an
|
||||
@@ -861,6 +908,12 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
|
||||
goto error;
|
||||
}
|
||||
continue;
|
||||
} else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"'%s' is readonly"),
|
||||
propname);
|
||||
(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (prop == ZPROP_INVAL) {
|
||||
@@ -1877,8 +1930,6 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
|
||||
err = zfs_prop_get(zhp, prop, propbuf, proplen,
|
||||
NULL, NULL, 0, literal);
|
||||
zfs_unset_recvd_props_mode(zhp, &cookie);
|
||||
} else if (zfs_prop_userquota(propname)) {
|
||||
return (-1);
|
||||
} else {
|
||||
nvlist_t *propval;
|
||||
char *recvdval;
|
||||
@@ -1893,6 +1944,120 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
|
||||
return (err == 0 ? 0 : -1);
|
||||
}
|
||||
|
||||
static int
|
||||
get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
|
||||
{
|
||||
nvlist_t *value;
|
||||
nvpair_t *pair;
|
||||
|
||||
value = zfs_get_clones_nvl(zhp);
|
||||
if (value == NULL)
|
||||
return (-1);
|
||||
|
||||
propbuf[0] = '\0';
|
||||
for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(value, pair)) {
|
||||
if (propbuf[0] != '\0')
|
||||
(void) strlcat(propbuf, ",", proplen);
|
||||
(void) strlcat(propbuf, nvpair_name(pair), proplen);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct get_clones_arg {
|
||||
uint64_t numclones;
|
||||
nvlist_t *value;
|
||||
const char *origin;
|
||||
char buf[ZFS_MAXNAMELEN];
|
||||
};
|
||||
|
||||
int
|
||||
get_clones_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
struct get_clones_arg *gca = arg;
|
||||
|
||||
if (gca->numclones == 0) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
|
||||
NULL, NULL, 0, B_TRUE) != 0)
|
||||
goto out;
|
||||
if (strcmp(gca->buf, gca->origin) == 0) {
|
||||
if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (no_memory(zhp->zfs_hdl));
|
||||
}
|
||||
gca->numclones--;
|
||||
}
|
||||
|
||||
out:
|
||||
(void) zfs_iter_children(zhp, get_clones_cb, gca);
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
nvlist_t *
|
||||
zfs_get_clones_nvl(zfs_handle_t *zhp)
|
||||
{
|
||||
nvlist_t *nv, *value;
|
||||
|
||||
if (nvlist_lookup_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
|
||||
struct get_clones_arg gca;
|
||||
|
||||
/*
|
||||
* if this is a snapshot, then the kernel wasn't able
|
||||
* to get the clones. Do it by slowly iterating.
|
||||
*/
|
||||
if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
|
||||
return (NULL);
|
||||
if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
|
||||
return (NULL);
|
||||
if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
|
||||
nvlist_free(nv);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
|
||||
gca.value = value;
|
||||
gca.origin = zhp->zfs_name;
|
||||
|
||||
if (gca.numclones != 0) {
|
||||
zfs_handle_t *root;
|
||||
char pool[ZFS_MAXNAMELEN];
|
||||
char *cp = pool;
|
||||
|
||||
/* get the pool name */
|
||||
(void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
|
||||
(void) strsep(&cp, "/@");
|
||||
root = zfs_open(zhp->zfs_hdl, pool,
|
||||
ZFS_TYPE_FILESYSTEM);
|
||||
|
||||
(void) get_clones_cb(root, &gca);
|
||||
}
|
||||
|
||||
if (gca.numclones != 0 ||
|
||||
nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
|
||||
nvlist_add_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
|
||||
nvlist_free(nv);
|
||||
nvlist_free(value);
|
||||
return (NULL);
|
||||
}
|
||||
nvlist_free(nv);
|
||||
nvlist_free(value);
|
||||
verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
|
||||
}
|
||||
|
||||
verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve a property from the given object. If 'literal' is specified, then
|
||||
* numbers are left as exact values. Otherwise, numbers are converted to a
|
||||
@@ -2021,6 +2186,11 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_PROP_CLONES:
|
||||
if (get_clones_string(zhp, propbuf, proplen) != 0)
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_PROP_QUOTA:
|
||||
case ZFS_PROP_REFQUOTA:
|
||||
case ZFS_PROP_RESERVATION:
|
||||
@@ -2385,7 +2555,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
|
||||
int err;
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
|
||||
(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
err = userquota_propname_decode(propname,
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
|
||||
@@ -2438,6 +2608,80 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
|
||||
uint64_t *propvalue)
|
||||
{
|
||||
int err;
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
const char *snapname;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
snapname = strchr(propname, '@') + 1;
|
||||
if (strchr(snapname, '@')) {
|
||||
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
} else {
|
||||
/* snapname is the short name, append it to zhp's fsname */
|
||||
char *cp;
|
||||
|
||||
(void) strlcpy(zc.zc_value, zhp->zfs_name,
|
||||
sizeof (zc.zc_value));
|
||||
cp = strchr(zc.zc_value, '@');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
(void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
|
||||
(void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
}
|
||||
|
||||
err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
*propvalue = zc.zc_cookie;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
|
||||
char *propbuf, int proplen, boolean_t literal)
|
||||
{
|
||||
int err;
|
||||
uint64_t propvalue;
|
||||
|
||||
err = zfs_prop_get_written_int(zhp, propname, &propvalue);
|
||||
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
if (literal) {
|
||||
(void) snprintf(propbuf, proplen, "%llu", (long long unsigned int)propvalue);
|
||||
} else {
|
||||
zfs_nicenum(propvalue, propbuf, proplen);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
|
||||
uint64_t *usedp)
|
||||
{
|
||||
int err;
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
|
||||
(void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
|
||||
|
||||
err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
*usedp = zc.zc_cookie;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of the given zfs handle.
|
||||
*/
|
||||
@@ -2456,133 +2700,6 @@ zfs_get_type(const zfs_handle_t *zhp)
|
||||
return (zhp->zfs_type);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
|
||||
{
|
||||
int rc;
|
||||
uint64_t orig_cookie;
|
||||
|
||||
orig_cookie = zc->zc_cookie;
|
||||
top:
|
||||
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
|
||||
rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
/* expand nvlist memory and try again */
|
||||
if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
|
||||
zcmd_free_nvlists(zc);
|
||||
return (-1);
|
||||
}
|
||||
zc->zc_cookie = orig_cookie;
|
||||
goto top;
|
||||
/*
|
||||
* An errno value of ESRCH indicates normal completion.
|
||||
* If ENOENT is returned, then the underlying dataset
|
||||
* has been removed since we obtained the handle.
|
||||
*/
|
||||
case ESRCH:
|
||||
case ENOENT:
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = zfs_standard_error(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot iterate filesystems"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all child filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
/*
|
||||
* Silently ignore errors, as the only plausible explanation is
|
||||
* that the pool has since been removed.
|
||||
*/
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all snapshots
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
|
||||
void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
||||
return (0);
|
||||
|
||||
zc.zc_simple = simple;
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
|
||||
if (simple)
|
||||
nzhp = make_dataset_simple_handle_zc(zhp, &zc);
|
||||
else
|
||||
nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
|
||||
if (nzhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children, snapshots and filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
|
||||
}
|
||||
|
||||
/*
|
||||
* Is one dataset name a child dataset of another?
|
||||
*
|
||||
@@ -2606,18 +2723,19 @@ is_descendant(const char *ds1, const char *ds2)
|
||||
|
||||
/*
|
||||
* Given a complete name, return just the portion that refers to the parent.
|
||||
* Can return NULL if this is a pool.
|
||||
* Will return -1 if there is no parent (path is just the name of the
|
||||
* pool).
|
||||
*/
|
||||
static int
|
||||
parent_name(const char *path, char *buf, size_t buflen)
|
||||
{
|
||||
char *loc;
|
||||
char *slashp;
|
||||
|
||||
if ((loc = strrchr(path, '/')) == NULL)
|
||||
(void) strlcpy(buf, path, buflen);
|
||||
|
||||
if ((slashp = strrchr(buf, '/')) == NULL)
|
||||
return (-1);
|
||||
|
||||
(void) strncpy(buf, path, MIN(buflen, loc - path));
|
||||
buf[loc - path] = '\0';
|
||||
*slashp = '\0';
|
||||
|
||||
return (0);
|
||||
}
|
||||
@@ -3031,9 +3149,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
|
||||
}
|
||||
|
||||
struct destroydata {
|
||||
char *snapname;
|
||||
boolean_t gotone;
|
||||
boolean_t closezhp;
|
||||
nvlist_t *nvl;
|
||||
const char *snapname;
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -3042,16 +3159,14 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
||||
struct destroydata *dd = arg;
|
||||
zfs_handle_t *szhp;
|
||||
char name[ZFS_MAXNAMELEN];
|
||||
boolean_t closezhp = dd->closezhp;
|
||||
int rv = 0;
|
||||
|
||||
(void) strlcpy(name, zhp->zfs_name, sizeof (name));
|
||||
(void) strlcat(name, "@", sizeof (name));
|
||||
(void) strlcat(name, dd->snapname, sizeof (name));
|
||||
(void) snprintf(name, sizeof (name),
|
||||
"%s@%s", zhp->zfs_name, dd->snapname);
|
||||
|
||||
szhp = make_dataset_handle(zhp->zfs_hdl, name);
|
||||
if (szhp) {
|
||||
dd->gotone = B_TRUE;
|
||||
verify(nvlist_add_boolean(dd->nvl, name) == 0);
|
||||
zfs_close(szhp);
|
||||
}
|
||||
|
||||
@@ -3064,10 +3179,8 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
||||
*/
|
||||
}
|
||||
|
||||
dd->closezhp = B_TRUE;
|
||||
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
|
||||
if (closezhp)
|
||||
zfs_close(zhp);
|
||||
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
|
||||
zfs_close(zhp);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
@@ -3077,29 +3190,45 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
||||
int
|
||||
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
|
||||
{
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
int ret;
|
||||
struct destroydata dd = { 0 };
|
||||
|
||||
dd.snapname = snapname;
|
||||
(void) zfs_check_snap_cb(zhp, &dd);
|
||||
verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
|
||||
(void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
|
||||
|
||||
if (!dd.gotone) {
|
||||
return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
|
||||
if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
|
||||
ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
|
||||
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
|
||||
zhp->zfs_name, snapname));
|
||||
zhp->zfs_name, snapname);
|
||||
} else {
|
||||
ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
|
||||
}
|
||||
nvlist_free(dd.nvl);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys all the snapshots named in the nvlist. They must be underneath
|
||||
* the zhp (either snapshots of it, or snapshots of its descendants).
|
||||
*/
|
||||
int
|
||||
zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
|
||||
{
|
||||
int ret;
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
|
||||
return (-1);
|
||||
zc.zc_defer_destroy = defer;
|
||||
|
||||
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
|
||||
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
|
||||
if (ret != 0) {
|
||||
char errbuf[1024];
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot destroy '%s@%s'"), zc.zc_name, snapname);
|
||||
"cannot destroy snapshots in %s"), zc.zc_name);
|
||||
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
@@ -3135,7 +3264,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot create '%s'"), target);
|
||||
|
||||
/* validate the target name */
|
||||
/* validate the target/clone name */
|
||||
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
|
||||
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
|
||||
|
||||
@@ -3627,42 +3756,6 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all dependents for a given dataset. This includes both
|
||||
* hierarchical dependents (children) and data dependents (snapshots and
|
||||
* clones). The bulk of the processing occurs in get_dependents() in
|
||||
* libzfs_graph.c.
|
||||
*/
|
||||
int
|
||||
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
|
||||
zfs_iter_f func, void *data)
|
||||
{
|
||||
char **dependents;
|
||||
size_t count;
|
||||
int i;
|
||||
zfs_handle_t *child;
|
||||
int ret = 0;
|
||||
|
||||
if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
|
||||
&dependents, &count) != 0)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((child = make_dataset_handle(zhp->zfs_hdl,
|
||||
dependents[i])) == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(child, data)) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
free(dependents[i]);
|
||||
free(dependents);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Renames the given dataset.
|
||||
*/
|
||||
@@ -3780,8 +3873,6 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
||||
}
|
||||
|
||||
dd.snapname = delim + 1;
|
||||
dd.gotone = B_FALSE;
|
||||
dd.closezhp = B_TRUE;
|
||||
|
||||
/* We remove any zvol links prior to renaming them */
|
||||
ret = zfs_iter_filesystems(zhrp, zfs_check_snap_cb, &dd);
|
||||
@@ -4223,7 +4314,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
|
||||
int error;
|
||||
zfs_useracct_t buf[100];
|
||||
|
||||
(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
zc.zc_objset_type = type;
|
||||
zc.zc_nvlist_dst = (uintptr_t)buf;
|
||||
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <libintl.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
int
|
||||
zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
nvlist_t *nvl = zfs_get_clones_nvl(zhp);
|
||||
nvpair_t *pair;
|
||||
|
||||
if (nvl == NULL)
|
||||
return (0);
|
||||
|
||||
for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(nvl, pair)) {
|
||||
zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
||||
if (clone != NULL) {
|
||||
int err = func(clone, data);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
|
||||
{
|
||||
int rc;
|
||||
uint64_t orig_cookie;
|
||||
|
||||
orig_cookie = zc->zc_cookie;
|
||||
top:
|
||||
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
|
||||
rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
/* expand nvlist memory and try again */
|
||||
if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
|
||||
zcmd_free_nvlists(zc);
|
||||
return (-1);
|
||||
}
|
||||
zc->zc_cookie = orig_cookie;
|
||||
goto top;
|
||||
/*
|
||||
* An errno value of ESRCH indicates normal completion.
|
||||
* If ENOENT is returned, then the underlying dataset
|
||||
* has been removed since we obtained the handle.
|
||||
*/
|
||||
case ESRCH:
|
||||
case ENOENT:
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = zfs_standard_error(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot iterate filesystems"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all child filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
/*
|
||||
* Silently ignore errors, as the only plausible explanation is
|
||||
* that the pool has since been removed.
|
||||
*/
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all snapshots
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
|
||||
void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
||||
return (0);
|
||||
|
||||
zc.zc_simple = simple;
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
|
||||
if (simple)
|
||||
nzhp = make_dataset_simple_handle_zc(zhp, &zc);
|
||||
else
|
||||
nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
|
||||
if (nzhp == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for dealing with the sorted snapshot functionality
|
||||
*/
|
||||
typedef struct zfs_node {
|
||||
zfs_handle_t *zn_handle;
|
||||
avl_node_t zn_avlnode;
|
||||
} zfs_node_t;
|
||||
|
||||
static int
|
||||
zfs_sort_snaps(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
avl_tree_t *avl = data;
|
||||
zfs_node_t *node;
|
||||
zfs_node_t search;
|
||||
|
||||
search.zn_handle = zhp;
|
||||
node = avl_find(avl, &search, NULL);
|
||||
if (node) {
|
||||
/*
|
||||
* If this snapshot was renamed while we were creating the
|
||||
* AVL tree, it's possible that we already inserted it under
|
||||
* its old name. Remove the old handle before adding the new
|
||||
* one.
|
||||
*/
|
||||
zfs_close(node->zn_handle);
|
||||
avl_remove(avl, node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
|
||||
node->zn_handle = zhp;
|
||||
avl_add(avl, node);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_snapshot_compare(const void *larg, const void *rarg)
|
||||
{
|
||||
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
|
||||
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
|
||||
uint64_t lcreate, rcreate;
|
||||
|
||||
/*
|
||||
* Sort them according to creation time. We use the hidden
|
||||
* CREATETXG property to get an absolute ordering of snapshots.
|
||||
*/
|
||||
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
|
||||
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
|
||||
|
||||
if (lcreate < rcreate)
|
||||
return (-1);
|
||||
else if (lcreate > rcreate)
|
||||
return (+1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
zfs_node_t *node;
|
||||
avl_tree_t avl;
|
||||
void *cookie = NULL;
|
||||
|
||||
avl_create(&avl, zfs_snapshot_compare,
|
||||
sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
|
||||
|
||||
ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl);
|
||||
|
||||
for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
|
||||
ret |= callback(node->zn_handle, data);
|
||||
|
||||
while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
|
||||
free(node);
|
||||
|
||||
avl_destroy(&avl);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *ssa_first;
|
||||
char *ssa_last;
|
||||
boolean_t ssa_seenfirst;
|
||||
boolean_t ssa_seenlast;
|
||||
zfs_iter_f ssa_func;
|
||||
void *ssa_arg;
|
||||
} snapspec_arg_t;
|
||||
|
||||
static int
|
||||
snapspec_cb(zfs_handle_t *zhp, void *arg) {
|
||||
snapspec_arg_t *ssa = arg;
|
||||
char *shortsnapname;
|
||||
int err = 0;
|
||||
|
||||
if (ssa->ssa_seenlast)
|
||||
return (0);
|
||||
shortsnapname = zfs_strdup(zhp->zfs_hdl,
|
||||
strchr(zfs_get_name(zhp), '@') + 1);
|
||||
|
||||
if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
|
||||
ssa->ssa_seenfirst = B_TRUE;
|
||||
|
||||
if (ssa->ssa_seenfirst) {
|
||||
err = ssa->ssa_func(zhp, ssa->ssa_arg);
|
||||
} else {
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
if (strcmp(shortsnapname, ssa->ssa_last) == 0)
|
||||
ssa->ssa_seenlast = B_TRUE;
|
||||
free(shortsnapname);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* spec is a string like "A,B%C,D"
|
||||
*
|
||||
* <snaps>, where <snaps> can be:
|
||||
* <snap> (single snapshot)
|
||||
* <snap>%<snap> (range of snapshots, inclusive)
|
||||
* %<snap> (range of snapshots, starting with earliest)
|
||||
* <snap>% (range of snapshots, ending with last)
|
||||
* % (all snapshots)
|
||||
* <snaps>[,...] (comma separated list of the above)
|
||||
*
|
||||
* If a snapshot can not be opened, continue trying to open the others, but
|
||||
* return ENOENT at the end.
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
|
||||
zfs_iter_f func, void *arg)
|
||||
{
|
||||
char buf[ZFS_MAXNAMELEN];
|
||||
char *comma_separated, *cp;
|
||||
int err = 0;
|
||||
int ret = 0;
|
||||
|
||||
(void) strlcpy(buf, spec_orig, sizeof (buf));
|
||||
cp = buf;
|
||||
|
||||
while ((comma_separated = strsep(&cp, ",")) != NULL) {
|
||||
char *pct = strchr(comma_separated, '%');
|
||||
if (pct != NULL) {
|
||||
snapspec_arg_t ssa = { 0 };
|
||||
ssa.ssa_func = func;
|
||||
ssa.ssa_arg = arg;
|
||||
|
||||
if (pct == comma_separated)
|
||||
ssa.ssa_seenfirst = B_TRUE;
|
||||
else
|
||||
ssa.ssa_first = comma_separated;
|
||||
*pct = '\0';
|
||||
ssa.ssa_last = pct + 1;
|
||||
|
||||
/*
|
||||
* If there is a lastname specified, make sure it
|
||||
* exists.
|
||||
*/
|
||||
if (ssa.ssa_last[0] != '\0') {
|
||||
char snapname[ZFS_MAXNAMELEN];
|
||||
(void) snprintf(snapname, sizeof (snapname),
|
||||
"%s@%s", zfs_get_name(fs_zhp),
|
||||
ssa.ssa_last);
|
||||
if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
|
||||
snapname, ZFS_TYPE_SNAPSHOT)) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
err = zfs_iter_snapshots_sorted(fs_zhp,
|
||||
snapspec_cb, &ssa);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
if (ret == 0 && (!ssa.ssa_seenfirst ||
|
||||
(ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
|
||||
ret = ENOENT;
|
||||
}
|
||||
} else {
|
||||
char snapname[ZFS_MAXNAMELEN];
|
||||
zfs_handle_t *snap_zhp;
|
||||
(void) snprintf(snapname, sizeof (snapname), "%s@%s",
|
||||
zfs_get_name(fs_zhp), comma_separated);
|
||||
snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
|
||||
snapname);
|
||||
if (snap_zhp == NULL) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
err = func(snap_zhp, arg);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children, snapshots and filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
|
||||
}
|
||||
|
||||
|
||||
typedef struct iter_stack_frame {
|
||||
struct iter_stack_frame *next;
|
||||
zfs_handle_t *zhp;
|
||||
} iter_stack_frame_t;
|
||||
|
||||
typedef struct iter_dependents_arg {
|
||||
boolean_t first;
|
||||
boolean_t allowrecursion;
|
||||
iter_stack_frame_t *stack;
|
||||
zfs_iter_f func;
|
||||
void *data;
|
||||
} iter_dependents_arg_t;
|
||||
|
||||
static int
|
||||
iter_dependents_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
iter_dependents_arg_t *ida = arg;
|
||||
int err;
|
||||
boolean_t first = ida->first;
|
||||
ida->first = B_FALSE;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
|
||||
err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
|
||||
} else {
|
||||
iter_stack_frame_t isf;
|
||||
iter_stack_frame_t *f;
|
||||
|
||||
/*
|
||||
* check if there is a cycle by seeing if this fs is already
|
||||
* on the stack.
|
||||
*/
|
||||
for (f = ida->stack; f != NULL; f = f->next) {
|
||||
if (f->zhp->zfs_dmustats.dds_guid ==
|
||||
zhp->zfs_dmustats.dds_guid) {
|
||||
if (ida->allowrecursion) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
} else {
|
||||
zfs_error_aux(zhp->zfs_hdl,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"recursive dependency at '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
err = zfs_error(zhp->zfs_hdl,
|
||||
EZFS_RECURSIVE,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot determine dependent "
|
||||
"datasets"));
|
||||
zfs_close(zhp);
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isf.zhp = zhp;
|
||||
isf.next = ida->stack;
|
||||
ida->stack = &isf;
|
||||
err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
|
||||
if (err == 0)
|
||||
err = zfs_iter_snapshots(zhp, B_FALSE,
|
||||
iter_dependents_cb, ida);
|
||||
ida->stack = isf.next;
|
||||
}
|
||||
if (!first && err == 0)
|
||||
err = ida->func(zhp, ida->data);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
|
||||
zfs_iter_f func, void *data)
|
||||
{
|
||||
iter_dependents_arg_t ida;
|
||||
ida.allowrecursion = allowrecursion;
|
||||
ida.stack = NULL;
|
||||
ida.func = func;
|
||||
ida.data = data;
|
||||
ida.first = B_TRUE;
|
||||
return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
|
||||
}
|
||||
+320
-227
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -346,6 +347,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
|
||||
switch (error) {
|
||||
case ENXIO:
|
||||
case ENODEV:
|
||||
case EPIPE:
|
||||
zfs_verror(hdl, EZFS_IO, fmt, ap);
|
||||
break;
|
||||
|
||||
@@ -1420,7 +1422,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
|
||||
* dataset property,
|
||||
*/
|
||||
if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
|
||||
(!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
|
||||
(!zfs_prop_user(propname) && !zfs_prop_userquota(propname) &&
|
||||
!zfs_prop_written(propname)))) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid property '%s'"), propname);
|
||||
return (zfs_error(hdl, EZFS_BADPROP,
|
||||
|
||||
Reference in New Issue
Block a user