aboutsummaryrefslogtreecommitdiffstats
path: root/lib/libzfs_core
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2016-06-09 17:04:12 -0700
committerBrian Behlendorf <[email protected]>2016-06-28 13:47:03 -0700
commit43e52eddb13d8accbd052fac9a242ce979531aa4 (patch)
tree35a4f1d573f6255e13ad1e36ed1c72827c376849 /lib/libzfs_core
parent8c62a0d0f39c450d1fdb001b8073193f89611f56 (diff)
Implement zfs_ioc_recv_new() for OpenZFS 2605
Adds ZFS_IOC_RECV_NEW for resumable streams and preserves the legacy ZFS_IOC_RECV user/kernel interface. The new interface supports all stream options but is currently only used for resumable streams. This way updated user space utilities will interoperate with older kernel modules. ZFS_IOC_RECV_NEW is modeled after the existing ZFS_IOC_SEND_NEW handler. Non-Linux OpenZFS platforms have opted to change the legacy interface in an incompatible fashion instead of adding a new ioctl. Signed-off-by: Brian Behlendorf <[email protected]>
Diffstat (limited to 'lib/libzfs_core')
-rw-r--r--lib/libzfs_core/libzfs_core.c225
1 files changed, 172 insertions, 53 deletions
diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c
index ada46969d..e20e58dba 100644
--- a/lib/libzfs_core/libzfs_core.c
+++ b/lib/libzfs_core/libzfs_core.c
@@ -547,82 +547,168 @@ recv_read(int fd, void *buf, int ilen)
return (0);
}
+/*
+ * Linux adds ZFS_IOC_RECV_NEW for resumable streams and preserves the legacy
+ * ZFS_IOC_RECV user/kernel interface. The new interface supports all stream
+ * options but is currently only used for resumable streams. This way updated
+ * user space utilities will interoperate with older kernel modules.
+ *
+ * Non-Linux OpenZFS platforms have opted to modify the legacy interface.
+ */
static int
recv_impl(const char *snapname, nvlist_t *props, const char *origin,
- boolean_t force, boolean_t resumable, int fd,
- const dmu_replay_record_t *begin_record)
+ boolean_t force, boolean_t resumable, int input_fd,
+ const dmu_replay_record_t *begin_record, int cleanup_fd,
+ uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
+ nvlist_t **errors)
{
- /*
- * 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"};
+ dmu_replay_record_t drr;
+ char fsname[MAXPATHLEN];
char *atp;
- char *packed = NULL;
- size_t size;
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, '@');
+ /* Set 'fsname' to the name of containing filesystem */
+ (void) strlcpy(fsname, snapname, sizeof (fsname));
+ atp = strchr(fsname, '@');
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 the fs does not exist, try its parent. */
+ if (!lzc_exists(fsname)) {
+ char *slashp = strrchr(fsname, '/');
if (slashp == NULL)
return (ENOENT);
*slashp = '\0';
+ }
+ /*
+ * The begin_record is normally a non-byteswapped BEGIN record.
+ * For resumable streams it may be set to any non-byteswapped
+ * dmu_replay_record_t.
+ */
+ if (begin_record == NULL) {
+ error = recv_read(input_fd, &drr, sizeof (drr));
+ if (error != 0)
+ return (error);
+ } else {
+ drr = *begin_record;
}
- /* zc_value is full name of the snapshot to create */
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
+ if (resumable) {
+ nvlist_t *outnvl = NULL;
+ nvlist_t *innvl = fnvlist_alloc();
- 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;
- }
+ fnvlist_add_string(innvl, "snapname", snapname);
- /* zc_string is name of clone origin (if DRR_FLAG_CLONE) */
- if (origin != NULL)
- (void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
+ if (props != NULL)
+ fnvlist_add_nvlist(innvl, "props", props);
- /* zc_begin_record is non-byteswapped BEGIN record */
- if (begin_record == NULL) {
- error = recv_read(fd, &zc.zc_begin_record,
- sizeof (zc.zc_begin_record));
- if (error != 0)
- goto out;
+ if (origin != NULL && strlen(origin))
+ fnvlist_add_string(innvl, "origin", origin);
+
+ fnvlist_add_byte_array(innvl, "begin_record",
+ (uchar_t *) &drr, sizeof (drr));
+
+ fnvlist_add_int32(innvl, "input_fd", input_fd);
+
+ if (force)
+ fnvlist_add_boolean(innvl, "force");
+
+ if (resumable)
+ fnvlist_add_boolean(innvl, "resumable");
+
+ if (cleanup_fd >= 0)
+ fnvlist_add_int32(innvl, "cleanup_fd", cleanup_fd);
+
+ if (action_handle != NULL)
+ fnvlist_add_uint64(innvl, "action_handle",
+ *action_handle);
+
+ error = lzc_ioctl(ZFS_IOC_RECV_NEW, fsname, innvl, &outnvl);
+
+ if (error == 0 && read_bytes != NULL)
+ error = nvlist_lookup_uint64(outnvl, "read_bytes",
+ read_bytes);
+
+ if (error == 0 && errflags != NULL)
+ error = nvlist_lookup_uint64(outnvl, "error_flags",
+ errflags);
+
+ if (error == 0 && action_handle != NULL)
+ error = nvlist_lookup_uint64(outnvl, "action_handle",
+ action_handle);
+
+ if (error == 0 && errors != NULL) {
+ nvlist_t *nvl;
+ error = nvlist_lookup_nvlist(outnvl, "errors", &nvl);
+ if (error == 0)
+ *errors = fnvlist_dup(nvl);
+ }
+
+ fnvlist_free(innvl);
+ fnvlist_free(outnvl);
} else {
- zc.zc_begin_record = *begin_record;
- }
+ zfs_cmd_t zc = {"\0"};
+ char *packed = NULL;
+ size_t size;
- /* zc_cookie is fd to read from */
- zc.zc_cookie = fd;
+ ASSERT3S(g_refcount, >, 0);
- /* zc guid is force flag */
- zc.zc_guid = force;
+ (void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_value));
+ (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- zc.zc_resumable = resumable;
+ if (props != NULL) {
+ packed = fnvlist_pack(props, &size);
+ zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_nvlist_src_size = size;
+ }
- /* zc_cleanup_fd is unused */
- zc.zc_cleanup_fd = -1;
+ if (origin != NULL)
+ (void) strlcpy(zc.zc_string, origin,
+ sizeof (zc.zc_string));
- error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
- if (error != 0)
- error = errno;
+ ASSERT3S(drr.drr_type, ==, DRR_BEGIN);
+ zc.zc_begin_record = drr.drr_u.drr_begin;
+ zc.zc_guid = force;
+ zc.zc_cookie = input_fd;
+ zc.zc_cleanup_fd = -1;
+ zc.zc_action_handle = 0;
+
+ if (cleanup_fd >= 0)
+ zc.zc_cleanup_fd = cleanup_fd;
+
+ if (action_handle != NULL)
+ zc.zc_action_handle = *action_handle;
+
+ zc.zc_nvlist_dst_size = 128 * 1024;
+ zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
+ malloc(zc.zc_nvlist_dst_size);
+
+ error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
+ if (error != 0) {
+ error = errno;
+ } else {
+ if (read_bytes != NULL)
+ *read_bytes = zc.zc_cookie;
+
+ if (errflags != NULL)
+ *errflags = zc.zc_obj;
+
+ if (action_handle != NULL)
+ *action_handle = zc.zc_action_handle;
+
+ if (errors != NULL)
+ VERIFY0(nvlist_unpack(
+ (void *)(uintptr_t)zc.zc_nvlist_dst,
+ zc.zc_nvlist_dst_size, errors, KM_SLEEP));
+ }
+
+ if (packed != NULL)
+ fnvlist_pack_free(packed, size);
+ free((void *)(uintptr_t)zc.zc_nvlist_dst);
+ }
-out:
- if (packed != NULL)
- fnvlist_pack_free(packed, size);
- free((void*)(uintptr_t)zc.zc_nvlist_dst);
return (error);
}
@@ -643,7 +729,8 @@ int
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
boolean_t force, int fd)
{
- return (recv_impl(snapname, props, origin, force, B_FALSE, fd, NULL));
+ return (recv_impl(snapname, props, origin, force, B_FALSE, fd,
+ NULL, -1, NULL, NULL, NULL, NULL));
}
/*
@@ -656,7 +743,8 @@ int
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
boolean_t force, int fd)
{
- return (recv_impl(snapname, props, origin, force, B_TRUE, fd, NULL));
+ return (recv_impl(snapname, props, origin, force, B_TRUE, fd,
+ NULL, -1, NULL, NULL, NULL, NULL));
}
/*
@@ -678,7 +766,38 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
if (begin_record == NULL)
return (EINVAL);
return (recv_impl(snapname, props, origin, force, resumable, fd,
- begin_record));
+ begin_record, -1, NULL, NULL, NULL, NULL));
+}
+
+/*
+ * Like lzc_receive, but allows the caller to pass all supported arguments
+ * and retrieve all values returned. The only additional input parameter
+ * is 'cleanup_fd' which is used to set a cleanup-on-exit file descriptor.
+ *
+ * The following parameters all provide return values. Several may be set
+ * in the failure case and will contain additional information.
+ *
+ * The 'read_bytes' value will be set to the total number of bytes read.
+ *
+ * The 'errflags' value will contain zprop_errflags_t flags which are
+ * used to describe any failures.
+ *
+ * The 'action_handle' is used to pass the handle for this guid/ds mapping.
+ * It should be set to zero on first call and will contain an updated handle
+ * on success, it should be passed in subsequent calls.
+ *
+ * The 'errors' nvlist contains an entry for each unapplied received
+ * property. Callers are responsible for freeing this nvlist.
+ */
+int lzc_receive_one(const char *snapname, nvlist_t *props,
+ const char *origin, boolean_t force, boolean_t resumable, int input_fd,
+ const dmu_replay_record_t *begin_record, int cleanup_fd,
+ uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
+ nvlist_t **errors)
+{
+ return (recv_impl(snapname, props, origin, force, resumable,
+ input_fd, begin_record, cleanup_fd, read_bytes, errflags,
+ action_handle, errors));
}
/*