Print this page
FAR: generating send-streams in portable format
This commit adds a switch '-F' to  zfs send. This set, zfs send generates
a stream in FAR-format instead of the traditional zfs stream format. The
generated send stream is compatible with the stream generated from 'btrfs send'
and can in principle easily be received to any filesystem.

@@ -787,11 +787,11 @@
         const char *fromsnap;
         const char *tosnap;
         char prevsnap[ZFS_MAXNAMELEN];
         uint64_t prevsnap_obj;
         boolean_t seenfrom, seento, replicate, doall, fromorigin;
-        boolean_t verbose, dryrun, parsable, progress;
+        boolean_t verbose, dryrun, parsable, progress, far;
         int outfd;
         boolean_t err;
         nvlist_t *fss;
         avl_tree_t *fsavl;
         snapfilter_cb_t *filter_cb;

@@ -866,11 +866,11 @@
  * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
  * NULL) to the file descriptor specified by outfd.
  */
 static int
 dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
-    boolean_t fromorigin, int outfd, nvlist_t *debugnv)
+    boolean_t fromorigin, int outfd, int far, nvlist_t *debugnv)
 {
         zfs_cmd_t zc = { 0 };
         libzfs_handle_t *hdl = zhp->zfs_hdl;
         nvlist_t *thisdbg;
 

@@ -880,10 +880,11 @@
         (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
         zc.zc_cookie = outfd;
         zc.zc_obj = fromorigin;
         zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
         zc.zc_fromobj = fromsnap_obj;
+        zc.zc_guid = far ? 2 : 0;
 
         VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
         if (fromsnap && fromsnap[0] != '\0') {
                 VERIFY(0 == nvlist_add_string(thisdbg,
                     "fromsnap", fromsnap));

@@ -1167,11 +1168,11 @@
                                 return (err);
                         }
                 }
 
                 err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
-                    fromorigin, sdd->outfd, sdd->debugnv);
+                    fromorigin, sdd->outfd, sdd->far, sdd->debugnv);
 
                 if (sdd->progress) {
                         (void) pthread_cancel(tid);
                         (void) pthread_join(tid, NULL);
                 }

@@ -1459,11 +1460,11 @@
                                 nvlist_free(fss);
                                 goto stderr_out;
                         }
                 }
 
-                if (!flags->dryrun) {
+                if (!flags->dryrun && !flags->far) {
                         /* write first begin record */
                         drr.drr_type = DRR_BEGIN;
                         drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
                         DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
                             drr_versioninfo, DMU_COMPOUNDSTREAM);

@@ -1518,10 +1519,11 @@
         sdd.fsavl = fsavl;
         sdd.verbose = flags->verbose;
         sdd.parsable = flags->parsable;
         sdd.progress = flags->progress;
         sdd.dryrun = flags->dryrun;
+        sdd.far = flags->far;
         sdd.filter_cb = filter_func;
         sdd.filter_cb_arg = cb_arg;
         if (debugnvp)
                 sdd.debugnv = *debugnvp;
 

@@ -1579,12 +1581,12 @@
         if (sdd.cleanup_fd != -1) {
                 VERIFY(0 == close(sdd.cleanup_fd));
                 sdd.cleanup_fd = -1;
         }
 
-        if (!flags->dryrun && (flags->replicate || flags->doall ||
-            flags->props)) {
+        if (!flags->dryrun && !flags->far &&
+            (flags->replicate || flags->doall || flags->props)) {
                 /*
                  * write final end record.  NB: want to do this even if
                  * there was some error, because it might not be totally
                  * failed.
                  */