aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/libzfs/Makefile.am1
-rw-r--r--lib/libzfs/libzfs_config.c4
-rw-r--r--lib/libzfs/libzfs_dataset.c372
-rw-r--r--lib/libzfs/libzfs_diff.c8
-rw-r--r--lib/libzfs/libzfs_fru.c2
-rw-r--r--lib/libzfs/libzfs_graph.c4
-rw-r--r--lib/libzfs/libzfs_import.c2
-rw-r--r--lib/libzfs/libzfs_iter.c12
-rw-r--r--lib/libzfs/libzfs_pool.c114
-rw-r--r--lib/libzfs/libzfs_sendrecv.c20
-rw-r--r--lib/libzfs/libzfs_util.c24
-rw-r--r--lib/libzfs_core/Makefile.am15
-rw-r--r--lib/libzfs_core/libzfs_core.c477
-rw-r--r--lib/libzpool/kernel.c6
15 files changed, 790 insertions, 273 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 09139d58c..8e7caf2a1 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,4 +4,4 @@ SUBDIRS = libspl libavl libefi libshare libunicode
# These four libraries, which are installed as the final build product,
# incorporate the five convenience libraries given above.
-SUBDIRS += libuutil libnvpair libzpool libzfs
+SUBDIRS += libuutil libnvpair libzpool libzfs_core libzfs
diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am
index 524efaa60..14abd7ab3 100644
--- a/lib/libzfs/Makefile.am
+++ b/lib/libzfs/Makefile.am
@@ -22,6 +22,7 @@ libzfs_la_SOURCES = \
$(top_srcdir)/lib/libzfs/libzfs_util.c
libzfs_la_LIBADD = \
+ $(top_builddir)/lib/libzfs_core/libzfs_core.la \
$(top_builddir)/lib/libshare/libshare.la \
$(top_builddir)/lib/libnvpair/libnvpair.la \
$(top_builddir)/lib/libzpool/libzpool.la
diff --git a/lib/libzfs/libzfs_config.c b/lib/libzfs/libzfs_config.c
index ee94fe106..99b6e67c3 100644
--- a/lib/libzfs/libzfs_config.c
+++ b/lib/libzfs/libzfs_config.c
@@ -106,7 +106,7 @@ namespace_reload(libzfs_handle_t *hdl)
nvlist_t *config;
config_node_t *cn;
nvpair_t *elem;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
void *cookie;
if (hdl->libzfs_ns_gen == 0) {
@@ -261,7 +261,7 @@ zpool_get_features(zpool_handle_t *zhp)
int
zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int error;
nvlist_t *config;
libzfs_handle_t *hdl = zhp->zpool_hdl;
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index 244b687ed..041750bca 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -313,7 +313,7 @@ get_recvd_props_ioctl(zfs_handle_t *zhp)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *recvdprops;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int err;
if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
@@ -376,7 +376,7 @@ static int
get_stats(zfs_handle_t *zhp)
{
int rc = 0;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
@@ -439,7 +439,7 @@ make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
zfs_handle_t *
make_dataset_handle(libzfs_handle_t *hdl, const char *path)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
@@ -1427,15 +1427,14 @@ zfs_is_namespace_prop(zfs_prop_t prop)
int
zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int ret = -1;
prop_changelist_t *cl = NULL;
char errbuf[1024];
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *nvl = NULL, *realprops;
zfs_prop_t prop;
- boolean_t do_prefix;
- uint64_t idx;
+ boolean_t do_prefix = B_TRUE;
int added_resv = 0;
(void) snprintf(errbuf, sizeof (errbuf),
@@ -1474,12 +1473,17 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
}
/*
- * If the dataset's canmount property is being set to noauto,
- * then we want to prevent unmounting & remounting it.
+ * We don't want to unmount & remount the dataset when changing
+ * its canmount property to 'on' or 'noauto'. We only use
+ * the changelist logic to unmount when setting canmount=off.
*/
- do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
- (zprop_string_to_index(prop, propval, &idx,
- ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
+ if (prop == ZFS_PROP_CANMOUNT) {
+ uint64_t idx;
+ int err = zprop_string_to_index(prop, propval, &idx,
+ ZFS_TYPE_DATASET);
+ if (err == 0 && idx != ZFS_CANMOUNT_OFF)
+ do_prefix = B_FALSE;
+ }
if (do_prefix && (ret = changelist_prefix(cl)) != 0)
goto error;
@@ -1549,7 +1553,7 @@ error:
int
zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int ret;
prop_changelist_t *cl;
libzfs_handle_t *hdl = zhp->zfs_hdl;
@@ -1724,7 +1728,7 @@ static int
get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
char **source, uint64_t *val)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
nvlist_t *zplprops = NULL;
struct mnttab mnt;
char *mntopt_on = NULL;
@@ -2576,7 +2580,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
uint64_t *propvalue, zfs_userquota_prop_t *typep)
{
int err;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
@@ -2636,7 +2640,7 @@ 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 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
const char *snapname;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
@@ -2686,25 +2690,6 @@ zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
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.
*/
@@ -2775,7 +2760,7 @@ static int
check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
boolean_t accept_ancestor, int *prefixlen)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char parent[ZFS_MAXNAMELEN];
char *slash;
zfs_handle_t *zhp;
@@ -2905,7 +2890,6 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
*/
for (cp = target + prefixlen + 1;
(cp = strchr(cp, '/')); *cp = '/', cp++) {
- char *logstr;
*cp = '\0';
@@ -2916,16 +2900,12 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
continue;
}
- logstr = hdl->libzfs_log_str;
- hdl->libzfs_log_str = NULL;
if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
NULL) != 0) {
- hdl->libzfs_log_str = logstr;
opname = dgettext(TEXT_DOMAIN, "create");
goto ancestorerr;
}
- hdl->libzfs_log_str = logstr;
h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
if (h == NULL) {
opname = dgettext(TEXT_DOMAIN, "open");
@@ -2983,12 +2963,12 @@ int
zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
nvlist_t *props)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
uint64_t size = 0;
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
char errbuf[1024];
uint64_t zoned;
+ dmu_objset_type_t ost;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create '%s'"), path);
@@ -3008,17 +2988,16 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
* will return ENOENT, not EEXIST. To prevent this from happening, we
* first try to see if the dataset exists.
*/
- (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
- if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
+ if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"dataset already exists"));
return (zfs_error(hdl, EZFS_EXISTS, errbuf));
}
if (type == ZFS_TYPE_VOLUME)
- zc.zc_objset_type = DMU_OST_ZVOL;
+ ost = DMU_OST_ZVOL;
else
- zc.zc_objset_type = DMU_OST_ZFS;
+ ost = DMU_OST_ZFS;
if (props && (props = zfs_valid_proplist(hdl, type, props,
zoned, NULL, errbuf)) == 0)
@@ -3070,12 +3049,9 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
}
}
- if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
- return (-1);
- nvlist_free(props);
-
/* create the dataset */
- ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
+ ret = lzc_create(path, ost, props);
+ nvlist_free(props);
if (ret == 0 && type == ZFS_TYPE_VOLUME) {
ret = zvol_create_link(hdl, path);
@@ -3084,13 +3060,10 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
dgettext(TEXT_DOMAIN,
"Volume successfully created, but device links "
"were not created"));
- zcmd_free_nvlists(&zc);
return (-1);
}
}
- zcmd_free_nvlists(&zc);
-
/* check for failure */
if (ret != 0) {
char parent[ZFS_MAXNAMELEN];
@@ -3147,7 +3120,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
int
zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
@@ -3241,33 +3214,36 @@ 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 };
+ nvlist_t *errlist;
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
- return (-1);
- zc.zc_defer_destroy = defer;
+ ret = lzc_destroy_snaps(snaps, defer, &errlist);
- 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 snapshots in %s"), zc.zc_name);
-
- switch (errno) {
- case EEXIST:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "snapshot is cloned"));
- return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
+ nvpair_t *pair;
+ for (pair = nvlist_next_nvpair(errlist, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
+ nvpair_name(pair));
- default:
- return (zfs_standard_error(zhp->zfs_hdl, errno,
- errbuf));
+ switch (fnvpair_value_int32(pair)) {
+ case EEXIST:
+ zfs_error_aux(zhp->zfs_hdl,
+ dgettext(TEXT_DOMAIN,
+ "snapshot is cloned"));
+ ret = zfs_error(zhp->zfs_hdl, EZFS_EXISTS,
+ errbuf);
+ break;
+ default:
+ ret = zfs_standard_error(zhp->zfs_hdl, errno,
+ errbuf);
+ break;
+ }
}
}
- return (0);
+ return (ret);
}
/*
@@ -3276,12 +3252,10 @@ zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
int
zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
char parent[ZFS_MAXNAMELEN];
int ret;
char errbuf[1024];
libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_type_t type;
uint64_t zoned;
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
@@ -3300,32 +3274,21 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
(void) parent_name(target, parent, sizeof (parent));
/* do the clone */
- if (ZFS_IS_VOLUME(zhp)) {
- zc.zc_objset_type = DMU_OST_ZVOL;
- type = ZFS_TYPE_VOLUME;
- } else {
- zc.zc_objset_type = DMU_OST_ZFS;
- type = ZFS_TYPE_FILESYSTEM;
- }
if (props) {
+ zfs_type_t type;
+ if (ZFS_IS_VOLUME(zhp)) {
+ type = ZFS_TYPE_VOLUME;
+ } else {
+ type = ZFS_TYPE_FILESYSTEM;
+ }
if ((props = zfs_valid_proplist(hdl, type, props, zoned,
zhp, errbuf)) == NULL)
return (-1);
-
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
- }
-
- nvlist_free(props);
}
- (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
-
- zcmd_free_nvlists(&zc);
+ ret = lzc_clone(target, zhp->zfs_name, props);
+ nvlist_free(props);
if (ret != 0) {
switch (errno) {
@@ -3425,7 +3388,7 @@ int
zfs_promote(zfs_handle_t *zhp)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char parent[MAXPATHLEN];
char *cp;
int ret;
@@ -3540,94 +3503,159 @@ zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
return (ret);
}
+typedef struct snapdata {
+ nvlist_t *sd_nvl;
+ const char *sd_snapname;
+} snapdata_t;
+
+static int
+zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
+{
+ snapdata_t *sd = arg;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
+
+ fnvlist_add_boolean(sd->sd_nvl, name);
+
+ rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
+ zfs_close(zhp);
+ return (rv);
+}
+
/*
- * Takes a snapshot of the given dataset.
+ * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
+ * created.
*/
int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
- nvlist_t *props)
+zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
{
- const char *delim;
- char parent[ZFS_MAXNAMELEN];
- zfs_handle_t *zhp;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int ret;
char errbuf[1024];
+ nvpair_t *elem;
+ nvlist_t *errors;
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot snapshot '%s'"), path);
-
- /* validate the target name */
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+ "cannot create snapshots "));
- if (props) {
- if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
- props, B_FALSE, NULL, errbuf)) == NULL)
- return (-1);
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
+ const char *snapname = nvpair_name(elem);
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
+ /* validate the target name */
+ if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
+ B_TRUE)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s'"), snapname);
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
}
-
- nvlist_free(props);
}
- /* make sure the parent exists and is of the appropriate type */
- delim = strchr(path, '@');
- (void) strncpy(parent, path, delim - path);
- parent[delim - path] = '\0';
-
- if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME)) == NULL) {
- zcmd_free_nvlists(&zc);
+ if (props != NULL &&
+ (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
+ props, B_FALSE, NULL, errbuf)) == NULL) {
return (-1);
}
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
- zc.zc_cookie = recursive;
- ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
+ ret = lzc_snapshot(snaps, props, &errors);
- zcmd_free_nvlists(&zc);
+ if (ret != 0) {
+ boolean_t printed = B_FALSE;
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot create snapshot '%s'"), nvpair_name(elem));
+ (void) zfs_standard_error(hdl,
+ fnvpair_value_int32(elem), errbuf);
+ printed = B_TRUE;
+ }
+ if (!printed) {
+ switch (ret) {
+ case EXDEV:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "multiple snapshots of same "
+ "fs not allowed"));
+ (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
- if (ret != 0)
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
+ break;
+ default:
+ (void) zfs_standard_error(hdl, ret, errbuf);
+ }
+ }
+ } else {
+ zfs_handle_t *zhp;
+ int linktries = 0, linkok = 0, linkfail = 0;
+ nvpair_t *snap;
- if (ret == 0 && recursive) {
- struct createdata cd;
+ for (snap = nvlist_next_nvpair(snaps, NULL); snap != NULL;
+ snap = nvlist_next_nvpair(snaps, snap)) {
+ char *cp, *snapname;
- cd.cd_snapname = delim + 1;
- cd.cd_ifexists = B_FALSE;
- (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd);
- }
- if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
- ret = zvol_create_link(zhp->zfs_hdl, path);
- if (ret != 0) {
- (void) zfs_standard_error(hdl, errno,
- dgettext(TEXT_DOMAIN,
- "Volume successfully snapshotted, but device links "
- "were not created"));
- zfs_close(zhp);
- return (-1);
+ snapname = nvpair_name(snap);
+ cp = strchr(snapname, '@');
+ *cp = '\0';
+
+ if ((zhp = zfs_open(hdl, snapname, ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME)) != NULL) {
+ if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
+ ++linktries;
+ *cp = '@';
+ if (zvol_create_link(zhp->zfs_hdl, nvpair_name(snap)))
+ ++linkfail;
+ else
+ ++linkok;
+ }
+ }
}
}
- if (ret != 0)
- (void) zfs_standard_error(hdl, errno, errbuf);
+ nvlist_free(props);
+ nvlist_free(errors);
+ return (ret);
+}
- zfs_close(zhp);
+int
+zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
+ nvlist_t *props)
+{
+ int ret;
+ snapdata_t sd = { 0 };
+ char fsname[ZFS_MAXNAMELEN];
+ char *cp;
+ zfs_handle_t *zhp;
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot snapshot %s"), path);
+
+ if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
+ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+
+ (void) strlcpy(fsname, path, sizeof (fsname));
+ cp = strchr(fsname, '@');
+ *cp = '\0';
+ sd.sd_snapname = cp + 1;
+ if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME)) == NULL) {
+ return (-1);
+ }
+
+ verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
+ if (recursive) {
+ (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
+ } else {
+ fnvlist_add_boolean(sd.sd_nvl, path);
+ }
+
+ ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
+ nvlist_free(sd.sd_nvl);
+ zfs_close(zhp);
return (ret);
}
@@ -3655,17 +3683,13 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
cbp->cb_create) {
- char *logstr;
cbp->cb_dependent = B_TRUE;
cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
rollback_destroy, cbp);
cbp->cb_dependent = B_FALSE;
- logstr = zhp->zfs_hdl->libzfs_log_str;
- zhp->zfs_hdl->libzfs_log_str = NULL;
cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
- zhp->zfs_hdl->libzfs_log_str = logstr;
}
} else {
/* We must destroy this clone; first unmount it */
@@ -3702,7 +3726,7 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
{
rollback_data_t cb = { 0 };
int err;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
boolean_t restore_resv = 0;
uint64_t old_volsize = 0, new_volsize;
zfs_prop_t resv_prop = { 0 };
@@ -3789,7 +3813,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
boolean_t force_unmount)
{
int ret;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char *delim;
prop_changelist_t *cl = NULL;
zfs_handle_t *zhrp = NULL;
@@ -4008,7 +4032,7 @@ zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
static int
zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char path[MAXPATHLEN];
int error;
@@ -4072,7 +4096,7 @@ zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
int
zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int timeout = 3000; /* in milliseconds */
int error = 0;
int i;
@@ -4265,7 +4289,7 @@ static int
zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
nvlist_t *nvlist = NULL;
int error;
@@ -4347,7 +4371,7 @@ int
zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
zfs_userspace_cb_t func, void *arg)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_useracct_t buf[100];
libzfs_handle_t *hdl = zhp->zfs_hdl;
int ret;
@@ -4389,7 +4413,7 @@ zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
ASSERT(!recursive || dsobj == 0);
@@ -4447,7 +4471,7 @@ int
zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
boolean_t recursive)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
@@ -4487,7 +4511,7 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
int
zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
int nvsz = 2048;
void *nvbuf;
@@ -4554,7 +4578,7 @@ out:
int
zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
char *nvbuf;
char errbuf[ZFS_MAXNAMELEN+32];
@@ -4608,7 +4632,7 @@ zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
int
zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
int nvsz = 2048;
void *nvbuf;
diff --git a/lib/libzfs/libzfs_diff.c b/lib/libzfs/libzfs_diff.c
index 77d5a09ec..d8ef6ff02 100644
--- a/lib/libzfs/libzfs_diff.c
+++ b/lib/libzfs/libzfs_diff.c
@@ -90,7 +90,7 @@ static int
get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
char *pn, int maxlen, zfs_stat_t *sb)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int error;
(void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
@@ -379,7 +379,7 @@ describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf,
static int
write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *lhdl = di->zhp->zfs_hdl;
char fobjname[MAXPATHLEN];
@@ -507,7 +507,7 @@ static int
make_temp_snapshot(differ_info_t *di)
{
libzfs_handle_t *hdl = di->zhp->zfs_hdl;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) snprintf(zc.zc_value, sizeof (zc.zc_value),
ZDIFF_PREFIX, getpid());
@@ -749,7 +749,7 @@ int
zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap,
const char *tosnap, int flags)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char errbuf[1024];
differ_info_t di = { 0 };
pthread_t tid;
diff --git a/lib/libzfs/libzfs_fru.c b/lib/libzfs/libzfs_fru.c
index 78f2f9c37..aa84aa30d 100644
--- a/lib/libzfs/libzfs_fru.c
+++ b/lib/libzfs/libzfs_fru.c
@@ -361,7 +361,7 @@ libzfs_fru_devpath(libzfs_handle_t *hdl, const char *fru)
int
zpool_fru_set(zpool_handle_t *zhp, uint64_t vdev_guid, const char *fru)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
(void) strncpy(zc.zc_value, fru, sizeof (zc.zc_value));
diff --git a/lib/libzfs/libzfs_graph.c b/lib/libzfs/libzfs_graph.c
index 0e538e3de..3c5bdcc67 100644
--- a/lib/libzfs/libzfs_graph.c
+++ b/lib/libzfs/libzfs_graph.c
@@ -379,7 +379,7 @@ zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
static int
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_vertex_t *zvp;
/*
@@ -473,7 +473,7 @@ iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
static boolean_t
external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
/*
* Check whether this dataset is a clone or has clones since
diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c
index 9e79bd934..af6a43d83 100644
--- a/lib/libzfs/libzfs_import.c
+++ b/lib/libzfs/libzfs_import.c
@@ -365,7 +365,7 @@ static nvlist_t *
refresh_config(libzfs_handle_t *hdl, nvlist_t *config)
{
nvlist_t *nvl;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int err;
if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0)
diff --git a/lib/libzfs/libzfs_iter.c b/lib/libzfs/libzfs_iter.c
index 8215d3cb1..ff76f9f36 100644
--- a/lib/libzfs/libzfs_iter.c
+++ b/lib/libzfs/libzfs_iter.c
@@ -22,7 +22,7 @@
/*
* 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.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
#include <stdio.h>
@@ -103,7 +103,7 @@ top:
int
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_handle_t *nzhp;
int ret;
@@ -140,7 +140,7 @@ 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_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_handle_t *nzhp;
int ret;
@@ -306,12 +306,11 @@ 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;
+ char *buf, *comma_separated, *cp;
int err = 0;
int ret = 0;
- (void) strlcpy(buf, spec_orig, sizeof (buf));
+ buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
cp = buf;
while ((comma_separated = strsep(&cp, ",")) != NULL) {
@@ -369,6 +368,7 @@ zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
}
}
+ free(buf);
return (ret);
}
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c
index a6cacd370..45c39cc0f 100644
--- a/lib/libzfs/libzfs_pool.c
+++ b/lib/libzfs/libzfs_pool.c
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
+#include <libgen.h>
#include <zone.h>
#include <sys/stat.h>
#include <sys/efi_partition.h>
@@ -63,7 +64,7 @@ typedef struct prop_flags {
static int
zpool_get_all_props(zpool_handle_t *zhp)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
@@ -691,7 +692,7 @@ error:
int
zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int ret = -1;
char errbuf[1024];
nvlist_t *nvl = NULL;
@@ -1140,7 +1141,7 @@ int
zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
nvlist_t *props, nvlist_t *fsprops)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
nvlist_t *zc_fsprops = NULL;
nvlist_t *zc_props = NULL;
char msg[1024];
@@ -1272,9 +1273,9 @@ create_failed:
* datasets left in the pool.
*/
int
-zpool_destroy(zpool_handle_t *zhp)
+zpool_destroy(zpool_handle_t *zhp, const char *log_str)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_handle_t *zfp = NULL;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
@@ -1284,6 +1285,7 @@ zpool_destroy(zpool_handle_t *zhp)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_history = (uint64_t)(uintptr_t)log_str;
if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -1317,7 +1319,7 @@ zpool_destroy(zpool_handle_t *zhp)
int
zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int ret;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
@@ -1440,10 +1442,11 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
* Exports the pool from the system. The caller must ensure that there are no
* mounted datasets in the pool.
*/
-int
-zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
+static int
+zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce,
+ const char *log_str)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -1452,6 +1455,7 @@ zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
zc.zc_cookie = force;
zc.zc_guid = hardforce;
+ zc.zc_history = (uint64_t)(uintptr_t)log_str;
if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
switch (errno) {
@@ -1473,15 +1477,15 @@ zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce)
}
int
-zpool_export(zpool_handle_t *zhp, boolean_t force)
+zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str)
{
- return (zpool_export_common(zhp, force, B_FALSE));
+ return (zpool_export_common(zhp, force, B_FALSE, log_str));
}
int
-zpool_export_force(zpool_handle_t *zhp)
+zpool_export_force(zpool_handle_t *zhp, const char *log_str)
{
- return (zpool_export_common(zhp, B_TRUE, B_TRUE));
+ return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str));
}
static void
@@ -1717,7 +1721,7 @@ int
zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
nvlist_t *props, int flags)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zpool_rewind_policy_t policy;
nvlist_t *nv = NULL;
nvlist_t *nvinfo = NULL;
@@ -1909,7 +1913,7 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
int
zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -2385,7 +2389,7 @@ int
zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
vdev_state_t *newstate)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
@@ -2469,7 +2473,7 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
int
zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache;
@@ -2519,7 +2523,7 @@ zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
int
zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -2554,7 +2558,7 @@ zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
int
zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -2608,7 +2612,7 @@ int
zpool_vdev_attach(zpool_handle_t *zhp,
const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
int ret;
nvlist_t *tgt;
@@ -2784,7 +2788,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
int
zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache;
@@ -2882,7 +2886,7 @@ int
zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
nvlist_t *props, splitflags_t flags)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tree, *config, **child, **newchild, *newconfig = NULL;
nvlist_t **varray = NULL, *zc_props = NULL;
@@ -3093,7 +3097,7 @@ out:
int
zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
@@ -3138,7 +3142,7 @@ zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
int
zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
nvlist_t *tgt;
zpool_rewind_policy_t policy;
@@ -3214,7 +3218,7 @@ zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
int
zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -3240,7 +3244,7 @@ zpool_reguid(zpool_handle_t *zhp)
{
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
@@ -3258,7 +3262,7 @@ zpool_reguid(zpool_handle_t *zhp)
int
zpool_reopen(zpool_handle_t *zhp)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
libzfs_handle_t *hdl = zhp->zpool_hdl;
@@ -3338,7 +3342,7 @@ path_to_devid(const char *path)
static void
set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
(void) strncpy(zc.zc_value, path, sizeof (zc.zc_value));
@@ -3513,7 +3517,7 @@ zbookmark_compare(const void *a, const void *b)
int
zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
uint64_t count;
zbookmark_t *zb = NULL;
int i;
@@ -3609,7 +3613,7 @@ nomem:
int
zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strcpy(zc.zc_name, zhp->zpool_name);
@@ -3623,40 +3627,32 @@ zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
}
void
-zpool_set_history_str(const char *subcommand, int argc, char **argv,
- char *history_str)
+zfs_save_arguments(int argc, char **argv, char *string, int len)
{
int i;
- (void) strlcpy(history_str, subcommand, HIS_MAX_RECORD_LEN);
+ (void) strlcpy(string, basename(argv[0]), len);
for (i = 1; i < argc; i++) {
- if (strlen(history_str) + 1 + strlen(argv[i]) >
- HIS_MAX_RECORD_LEN)
- break;
- (void) strlcat(history_str, " ", HIS_MAX_RECORD_LEN);
- (void) strlcat(history_str, argv[i], HIS_MAX_RECORD_LEN);
+ (void) strlcat(string, " ", len);
+ (void) strlcat(string, argv[i], len);
}
}
-/*
- * Stage command history for logging.
- */
int
-zpool_stage_history(libzfs_handle_t *hdl, const char *history_str)
+zpool_log_history(libzfs_handle_t *hdl, const char *message)
{
- if (history_str == NULL)
- return (EINVAL);
-
- if (strlen(history_str) > HIS_MAX_RECORD_LEN)
- return (EINVAL);
-
- if (hdl->libzfs_log_str != NULL)
- free(hdl->libzfs_log_str);
-
- if ((hdl->libzfs_log_str = strdup(history_str)) == NULL)
- return (no_memory(hdl));
-
- return (0);
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
+ nvlist_t *args;
+ int err;
+
+ args = fnvlist_alloc();
+ fnvlist_add_string(args, "message", message);
+ err = zcmd_write_src_nvlist(hdl, &zc, args);
+ if (err == 0)
+ err = ioctl(hdl->libzfs_fd, ZFS_IOC_LOG_HISTORY, &zc);
+ nvlist_free(args);
+ zcmd_free_nvlists(&zc);
+ return (err);
}
/*
@@ -3671,7 +3667,7 @@ zpool_stage_history(libzfs_handle_t *hdl, const char *history_str)
static int
get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
@@ -3808,7 +3804,7 @@ int
zpool_events_next(libzfs_handle_t *hdl, nvlist_t **nvp,
int *dropped, int block, int cleanup_fd)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int error = 0;
*nvp = NULL;
@@ -3867,7 +3863,7 @@ out:
int
zpool_events_clear(libzfs_handle_t *hdl, int *count)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
char msg[1024];
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
@@ -3886,7 +3882,7 @@ void
zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
char *pathname, size_t len)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
boolean_t mounted = B_FALSE;
char *mntpnt = NULL;
char dsname[MAXNAMELEN];
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 9dbfb1641..5d0ab0eb4 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -812,7 +812,7 @@ static int
estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
boolean_t fromorigin, uint64_t *sizep)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
@@ -876,7 +876,7 @@ static int
dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
boolean_t fromorigin, int outfd, nvlist_t *debugnv)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *thisdbg;
@@ -992,7 +992,7 @@ send_progress_thread(void *arg)
{
progress_arg_t *pa = arg;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zfs_handle_t *zhp = pa->pa_zhp;
libzfs_handle_t *hdl = zhp->zfs_hdl;
unsigned long long bytes;
@@ -1195,7 +1195,7 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
int rv = 0;
send_dump_data_t *sdd = arg;
boolean_t missingfrom = B_FALSE;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
@@ -1683,7 +1683,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
int baselen, char *newname, recvflags_t *flags)
{
static int seq;
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int err;
prop_changelist_t *clp;
zfs_handle_t *zhp;
@@ -1756,7 +1756,7 @@ static int
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
char *newname, recvflags_t *flags)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
int err = 0;
prop_changelist_t *clp;
zfs_handle_t *zhp;
@@ -2015,7 +2015,7 @@ again:
stream_originguid, originguid)) {
case 1: {
/* promote it! */
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
nvlist_t *origin_nvfs;
char *origin_fsname;
@@ -2087,7 +2087,7 @@ again:
if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
&props) && 0 == nvlist_lookup_nvlist(props,
stream_snapname, &props)) {
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
zc.zc_cookie = B_TRUE; /* received */
(void) snprintf(zc.zc_name, sizeof (zc.zc_name),
@@ -2518,7 +2518,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
uint64_t *action_handlep)
{
- zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
time_t begin_time;
int ioctl_err, ioctl_errno, err;
char *cp;
@@ -2892,7 +2892,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
zcmd_free_nvlists(&zc);
if (err == 0 && snapprops_nvlist) {
- zfs_cmd_t zc2 = { "\0", "\0", "\0", "\0", 0 };
+ zfs_cmd_t zc2 = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
(void) strcpy(zc2.zc_name, zc.zc_value);
zc2.zc_cookie = B_TRUE; /* received */
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index 5bb88e946..54dc2afc5 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -45,6 +45,7 @@
#include <wait.h>
#include <libzfs.h>
+#include <libzfs_core.h>
#include "libzfs_impl.h"
#include "zfs_prop.h"
@@ -712,6 +713,14 @@ libzfs_init(void)
hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");
+ if (libzfs_core_init() != 0) {
+ (void) close(hdl->libzfs_fd);
+ (void) fclose(hdl->libzfs_mnttab);
+ (void) fclose(hdl->libzfs_sharetab);
+ free(hdl);
+ return (NULL);
+ }
+
zfs_prop_init();
zpool_prop_init();
zpool_feature_init();
@@ -733,12 +742,11 @@ libzfs_fini(libzfs_handle_t *hdl)
if (hdl->libzfs_sharetab)
(void) fclose(hdl->libzfs_sharetab);
zfs_uninit_libshare(hdl);
- if (hdl->libzfs_log_str)
- (void) free(hdl->libzfs_log_str);
zpool_free_handles(hdl);
libzfs_fru_clear(hdl, B_TRUE);
namespace_clear(hdl);
libzfs_mnttab_fini(hdl);
+ libzfs_core_fini();
free(hdl);
}
@@ -1061,17 +1069,7 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
int
zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
{
- int error;
-
- zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
- error = ioctl(hdl->libzfs_fd, request, zc);
- if (hdl->libzfs_log_str) {
- free(hdl->libzfs_log_str);
- hdl->libzfs_log_str = NULL;
- }
- zc->zc_history = 0;
-
- return (error);
+ return (ioctl(hdl->libzfs_fd, request, zc));
}
/*
diff --git a/lib/libzfs_core/Makefile.am b/lib/libzfs_core/Makefile.am
new file mode 100644
index 000000000..180292de1
--- /dev/null
+++ b/lib/libzfs_core/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/config/Rules.am
+
+DEFAULT_INCLUDES += \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib/libspl/include
+
+lib_LTLIBRARIES = libzfs_core.la
+
+libzfs_core_la_SOURCES = \
+ $(top_srcdir)/lib/libzfs_core/libzfs_core.c
+
+libzfs_core_la_LIBADD = \
+ $(top_builddir)/lib/libnvpair/libnvpair.la
+
+libzfs_core_la_LDFLAGS = -pthread -version-info 1:1:0
diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c
new file mode 100644
index 000000000..bff6902ca
--- /dev/null
+++ b/lib/libzfs_core/libzfs_core.c
@@ -0,0 +1,477 @@
+/*
+ * 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) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * LibZFS_Core (lzc) is intended to replace most functionality in libzfs.
+ * It has the following characteristics:
+ *
+ * - Thread Safe. libzfs_core is accessible concurrently from multiple
+ * threads. This is accomplished primarily by avoiding global data
+ * (e.g. caching). Since it's thread-safe, there is no reason for a
+ * process to have multiple libzfs "instances". Therefore, we store
+ * our few pieces of data (e.g. the file descriptor) in global
+ * variables. The fd is reference-counted so that the libzfs_core
+ * library can be "initialized" multiple times (e.g. by different
+ * consumers within the same process).
+ *
+ * - Committed Interface. The libzfs_core interface will be committed,
+ * therefore consumers can compile against it and be confident that
+ * their code will continue to work on future releases of this code.
+ * Currently, the interface is Evolving (not Committed), but we intend
+ * to commit to it once it is more complete and we determine that it
+ * meets the needs of all consumers.
+ *
+ * - Programatic Error Handling. libzfs_core communicates errors with
+ * defined error numbers, and doesn't print anything to stdout/stderr.
+ *
+ * - Thin Layer. libzfs_core is a thin layer, marshaling arguments
+ * to/from the kernel ioctls. There is generally a 1:1 correspondence
+ * between libzfs_core functions and ioctls to /dev/zfs.
+ *
+ * - Clear Atomicity. Because libzfs_core functions are generally 1:1
+ * with kernel ioctls, and kernel ioctls are general atomic, each
+ * libzfs_core function is atomic. For example, creating multiple
+ * snapshots with a single call to lzc_snapshot() is atomic -- it
+ * can't fail with only some of the requested snapshots created, even
+ * in the event of power loss or system crash.
+ *
+ * - Continued libzfs Support. Some higher-level operations (e.g.
+ * support for "zfs send -R") are too complicated to fit the scope of
+ * libzfs_core. This functionality will continue to live in libzfs.
+ * Where appropriate, libzfs will use the underlying atomic operations
+ * of libzfs_core. For example, libzfs may implement "zfs send -R |
+ * zfs receive" by using individual "send one snapshot", rename,
+ * destroy, and "receive one snapshot" operations in libzfs_core.
+ * /sbin/zfs and /zbin/zpool will link with both libzfs and
+ * libzfs_core. Other consumers should aim to use only libzfs_core,
+ * since that will be the supported, stable interface going forwards.
+ */
+
+#include <libzfs_core.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/nvpair.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/zfs_ioctl.h>
+
+static int g_fd;
+static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
+static int g_refcount;
+
+int
+libzfs_core_init(void)
+{
+ (void) pthread_mutex_lock(&g_lock);
+ if (g_refcount == 0) {
+ g_fd = open("/dev/zfs", O_RDWR);
+ if (g_fd < 0) {
+ (void) pthread_mutex_unlock(&g_lock);
+ return (errno);
+ }
+ }
+ g_refcount++;
+ (void) pthread_mutex_unlock(&g_lock);
+ return (0);
+}
+
+void
+libzfs_core_fini(void)
+{
+ (void) pthread_mutex_lock(&g_lock);
+ ASSERT3S(g_refcount, >, 0);
+ g_refcount--;
+ if (g_refcount == 0)
+ (void) close(g_fd);
+ (void) pthread_mutex_unlock(&g_lock);
+}
+
+static int
+lzc_ioctl(zfs_ioc_t ioc, const char *name,
+ nvlist_t *source, nvlist_t **resultp)
+{
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
+ int error = 0;
+ char *packed;
+ size_t size;
+
+ ASSERT3S(g_refcount, >, 0);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+
+ packed = fnvlist_pack(source, &size);
+ zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_nvlist_src_size = size;
+
+ if (resultp != NULL) {
+ zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
+ zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
+ malloc(zc.zc_nvlist_dst_size);
+ if (zc.zc_nvlist_dst == (uint64_t)0) {
+ error = ENOMEM;
+ goto out;
+ }
+ }
+
+ while (ioctl(g_fd, ioc, &zc) != 0) {
+ if (errno == ENOMEM && resultp != NULL) {
+ free((void *)(uintptr_t)zc.zc_nvlist_dst);
+ zc.zc_nvlist_dst_size *= 2;
+ zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
+ malloc(zc.zc_nvlist_dst_size);
+ if (zc.zc_nvlist_dst == (uint64_t)0) {
+ error = ENOMEM;
+ goto out;
+ }
+ } else {
+ error = errno;
+ break;
+ }
+ }
+ if (zc.zc_nvlist_dst_filled) {
+ *resultp = fnvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
+ zc.zc_nvlist_dst_size);
+ } else if (resultp != NULL) {
+ *resultp = NULL;
+ }
+
+out:
+ fnvlist_pack_free(packed, size);
+ free((void *)(uintptr_t)zc.zc_nvlist_dst);
+ return (error);
+}
+
+int
+lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props)
+{
+ int error;
+ nvlist_t *args = fnvlist_alloc();
+ fnvlist_add_int32(args, "type", type);
+ if (props != NULL)
+ fnvlist_add_nvlist(args, "props", props);
+ error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL);
+ nvlist_free(args);
+ return (error);
+}
+
+int
+lzc_clone(const char *fsname, const char *origin,
+ nvlist_t *props)
+{
+ int error;
+ nvlist_t *args = fnvlist_alloc();
+ fnvlist_add_string(args, "origin", origin);
+ if (props != NULL)
+ fnvlist_add_nvlist(args, "props", props);
+ error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL);
+ nvlist_free(args);
+ return (error);
+}
+
+/*
+ * Creates snapshots.
+ *
+ * The keys in the snaps nvlist are the snapshots to be created.
+ * They must all be in the same pool.
+ *
+ * The props nvlist is properties to set. Currently only user properties
+ * are supported. { user:prop_name -> string value }
+ *
+ * The returned results nvlist will have an entry for each snapshot that failed.
+ * The value will be the (int32) error code.
+ *
+ * The return value will be 0 if all snapshots were created, otherwise it will
+ * be the errno of a (undetermined) snapshot that failed.
+ */
+int
+lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist)
+{
+ nvpair_t *elem;
+ nvlist_t *args;
+ int error;
+ char pool[MAXNAMELEN];
+
+ *errlist = NULL;
+
+ /* determine the pool name */
+ elem = nvlist_next_nvpair(snaps, NULL);
+ if (elem == NULL)
+ return (0);
+ (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
+ pool[strcspn(pool, "/@")] = '\0';
+
+ args = fnvlist_alloc();
+ fnvlist_add_nvlist(args, "snaps", snaps);
+ if (props != NULL)
+ fnvlist_add_nvlist(args, "props", props);
+
+ error = lzc_ioctl(ZFS_IOC_SNAPSHOT, pool, args, errlist);
+ nvlist_free(args);
+
+ return (error);
+}
+
+/*
+ * Destroys snapshots.
+ *
+ * The keys in the snaps nvlist are the snapshots to be destroyed.
+ * They must all be in the same pool.
+ *
+ * Snapshots that do not exist will be silently ignored.
+ *
+ * If 'defer' is not set, and a snapshot has user holds or clones, the
+ * destroy operation will fail and none of the snapshots will be
+ * destroyed.
+ *
+ * If 'defer' is set, and a snapshot has user holds or clones, it will be
+ * marked for deferred destruction, and will be destroyed when the last hold
+ * or clone is removed/destroyed.
+ *
+ * The return value will be 0 if all snapshots were destroyed (or marked for
+ * later destruction if 'defer' is set) or didn't exist to begin with.
+ *
+ * Otherwise the return value will be the errno of a (undetermined) snapshot
+ * that failed, no snapshots will be destroyed, and the errlist will have an
+ * entry for each snapshot that failed. The value in the errlist will be
+ * the (int32) error code.
+ */
+int
+lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist)
+{
+ nvpair_t *elem;
+ nvlist_t *args;
+ int error;
+ char pool[MAXNAMELEN];
+
+ /* determine the pool name */
+ elem = nvlist_next_nvpair(snaps, NULL);
+ if (elem == NULL)
+ return (0);
+ (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
+ pool[strcspn(pool, "/@")] = '\0';
+
+ args = fnvlist_alloc();
+ fnvlist_add_nvlist(args, "snaps", snaps);
+ if (defer)
+ fnvlist_add_boolean(args, "defer");
+
+ error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist);
+ nvlist_free(args);
+
+ return (error);
+
+}
+
+int
+lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
+ uint64_t *usedp)
+{
+ nvlist_t *args;
+ nvlist_t *result;
+ int err;
+ char fs[MAXNAMELEN];
+ char *atp;
+
+ /* determine the fs name */
+ (void) strlcpy(fs, firstsnap, sizeof (fs));
+ atp = strchr(fs, '@');
+ if (atp == NULL)
+ return (EINVAL);
+ *atp = '\0';
+
+ args = fnvlist_alloc();
+ fnvlist_add_string(args, "firstsnap", firstsnap);
+
+ err = lzc_ioctl(ZFS_IOC_SPACE_SNAPS, lastsnap, args, &result);
+ nvlist_free(args);
+ if (err == 0)
+ *usedp = fnvlist_lookup_uint64(result, "used");
+ fnvlist_free(result);
+
+ return (err);
+}
+
+boolean_t
+lzc_exists(const char *dataset)
+{
+ /*
+ * The objset_stats ioctl is still legacy, so we need to construct our
+ * own zfs_cmd_t rather than using zfsc_ioctl().
+ */
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ return (ioctl(g_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0);
+}
+
+/*
+ * If fromsnap is NULL, a full (non-incremental) stream will be sent.
+ */
+int
+lzc_send(const char *snapname, const char *fromsnap, int fd)
+{
+ nvlist_t *args;
+ int err;
+
+ args = fnvlist_alloc();
+ fnvlist_add_int32(args, "fd", fd);
+ if (fromsnap != NULL)
+ fnvlist_add_string(args, "fromsnap", fromsnap);
+ err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
+ nvlist_free(args);
+ return (err);
+}
+
+/*
+ * If fromsnap is NULL, a full (non-incremental) stream will be estimated.
+ */
+int
+lzc_send_space(const char *snapname, const char *fromsnap, uint64_t *spacep)
+{
+ nvlist_t *args;
+ nvlist_t *result;
+ int err;
+
+ args = fnvlist_alloc();
+ if (fromsnap != NULL)
+ fnvlist_add_string(args, "fromsnap", fromsnap);
+ err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result);
+ nvlist_free(args);
+ if (err == 0)
+ *spacep = fnvlist_lookup_uint64(result, "space");
+ nvlist_free(result);
+ return (err);
+}
+
+static int
+recv_read(int fd, void *buf, int ilen)
+{
+ char *cp = buf;
+ int rv;
+ int len = ilen;
+
+ do {
+ rv = read(fd, cp, len);
+ cp += rv;
+ len -= rv;
+ } while (rv > 0);
+
+ if (rv < 0 || len != 0)
+ return (EIO);
+
+ return (0);
+}
+
+/*
+ * The simplest receive case: receive from the specified fd, creating the
+ * specified snapshot. Apply the specified properties a "received" properties
+ * (which can be overridden by locally-set properties). If the stream is a
+ * clone, its origin snapshot must be specified by 'origin'. The 'force'
+ * flag will cause the target filesystem to be rolled back or destroyed if
+ * necessary to receive.
+ *
+ * Return 0 on success or an errno on failure.
+ *
+ * Note: this interface does not work on dedup'd streams
+ * (those with DMU_BACKUP_FEATURE_DEDUP).
+ */
+int
+lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
+ boolean_t force, int fd)
+{
+ /*
+ * The receive ioctl is still legacy, so we need to construct our own
+ * zfs_cmd_t rather than using zfsc_ioctl().
+ */
+ zfs_cmd_t zc = {"\0", 0, 0, 0, 0, 0, 0, 0, "\0", "\0", "\0"};
+ char *atp;
+ char *packed = NULL;
+ size_t size;
+ dmu_replay_record_t drr;
+ int error;
+
+ ASSERT3S(g_refcount, >, 0);
+
+ /* zc_name is name of containing filesystem */
+ (void) strlcpy(zc.zc_name, snapname, sizeof (zc.zc_name));
+ atp = strchr(zc.zc_name, '@');
+ if (atp == NULL)
+ return (EINVAL);
+ *atp = '\0';
+
+ /* if the fs does not exist, try its parent. */
+ if (!lzc_exists(zc.zc_name)) {
+ char *slashp = strrchr(zc.zc_name, '/');
+ if (slashp == NULL)
+ return (ENOENT);
+ *slashp = '\0';
+
+ }
+
+ /* zc_value is full name of the snapshot to create */
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+
+ if (props != NULL) {
+ /* zc_nvlist_src is props to set */
+ packed = fnvlist_pack(props, &size);
+ zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_nvlist_src_size = size;
+ }
+
+ /* zc_string is name of clone origin (if DRR_FLAG_CLONE) */
+ if (origin != NULL)
+ (void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
+
+ /* zc_begin_record is non-byteswapped BEGIN record */
+ error = recv_read(fd, &drr, sizeof (drr));
+ if (error != 0)
+ goto out;
+ zc.zc_begin_record = drr.drr_u.drr_begin;
+
+ /* zc_cookie is fd to read from */
+ zc.zc_cookie = fd;
+
+ /* zc guid is force flag */
+ zc.zc_guid = force;
+
+ /* zc_cleanup_fd is unused */
+ zc.zc_cleanup_fd = -1;
+
+ error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
+ if (error != 0)
+ error = errno;
+
+out:
+ if (packed != NULL)
+ fnvlist_pack_free(packed, size);
+ free((void*)(uintptr_t)zc.zc_nvlist_dst);
+ return (error);
+}
diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c
index f7aeeb440..0293b5eb5 100644
--- a/lib/libzpool/kernel.c
+++ b/lib/libzpool/kernel.c
@@ -1082,6 +1082,12 @@ crgetuid(cred_t *cr)
return (0);
}
+uid_t
+crgetruid(cred_t *cr)
+{
+ return (0);
+}
+
gid_t
crgetgid(cred_t *cr)
{