Print this page
FITS: generating send-streams in portable format
This commit adds the command 'zfs fits-send', analogous to zfs send. The
generated send stream is compatible with the stream generated with that
from 'btrfs send' and can in principle easily be received to any filesystem.

@@ -3195,5 +3195,104 @@
         if (top_zfs)
                 free(top_zfs);
 
         return (err);
 }
+
+/*
+ * Generate a fits stream for the dataset identified by the argument zhp.
+ *
+ * The content of the send stream is the snapshot identified by
+ * 'tosnap'.  Incremental streams are requested from the snapshot identified
+ * by "fromsnap" (if non-null)
+ * Currently no recursive send is implemented
+ */
+int
+zfs_fits_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
+    sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
+    void *cb_arg)
+{
+        char errbuf[1024];
+        char name[MAXPATHLEN];
+        zfs_cmd_t zc = { 0 };
+        libzfs_handle_t *hdl = zhp->zfs_hdl;
+        zfs_handle_t *thdl;
+
+        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+            "cannot send '%s'"), zhp->zfs_name);
+
+        if (fromsnap && fromsnap[0] == '\0') {
+                zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                    "zero-length incremental source"));
+                return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
+        }
+
+        (void) snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name, tosnap);
+        if ((thdl = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT)) == NULL) {
+                (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+                    "WARNING: could not send %s@%s: does not exist\n"),
+                    zhp->zfs_name, tosnap);
+                return (B_TRUE);
+        }
+        zc.zc_sendobj = zfs_prop_get_int(thdl, ZFS_PROP_OBJSETID);
+        zfs_close(thdl);
+
+        if (fromsnap) {
+                (void) snprintf(name, sizeof (name), "%s@%s",
+                    zhp->zfs_name, fromsnap);
+                if ((thdl = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT)) == NULL) {
+                        (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+                            "WARNING: could not send %s@%s:\n"
+                            "incremental source (%s@%s) does not exist\n"),
+                            zhp->zfs_name, tosnap,
+                            zhp->zfs_name, fromsnap);
+                        return (B_TRUE);
+                }
+                zc.zc_fromobj = zfs_prop_get_int(thdl, ZFS_PROP_OBJSETID);
+                zfs_close(thdl);
+        }
+
+        assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+        (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
+            zhp->zfs_name, tosnap);
+        zc.zc_cookie = outfd;
+        zc.zc_obj = 0;
+
+        if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_FITS_SEND, &zc) != 0) {
+                char errbuf[1024];
+                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+                    "warning: cannot send '%s'"), zhp->zfs_name);
+
+                switch (errno) {
+                case EXDEV:
+                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                            "not an earlier snapshot from the same fs"));
+                        return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
+                case ENOENT:
+                        if (zfs_dataset_exists(hdl, zc.zc_name,
+                            ZFS_TYPE_SNAPSHOT)) {
+                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                    "incremental source (@%s) does not exist"),
+                                    zc.zc_value);
+                        }
+                        return (zfs_error(hdl, EZFS_NOENT, errbuf));
+                case EDQUOT:
+                case EFBIG:
+                case EIO:
+                case ENOLINK:
+                case ENOSPC:
+                case ENOSTR:
+                case ENXIO:
+                case EPIPE:
+                case ERANGE:
+                case EFAULT:
+                case EROFS:
+                        zfs_error_aux(hdl, strerror(errno));
+                        return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
+                default:
+                        return (zfs_standard_error(hdl, errno, errbuf));
+                }
+        }
+
+        return (0);
+}