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.

@@ -87,10 +87,11 @@
 static int zfs_do_snapshot(int argc, char **argv);
 static int zfs_do_unmount(int argc, char **argv);
 static int zfs_do_share(int argc, char **argv);
 static int zfs_do_unshare(int argc, char **argv);
 static int zfs_do_send(int argc, char **argv);
+static int zfs_do_fits_send(int argc, char **argv);
 static int zfs_do_receive(int argc, char **argv);
 static int zfs_do_promote(int argc, char **argv);
 static int zfs_do_userspace(int argc, char **argv);
 static int zfs_do_allow(int argc, char **argv);
 static int zfs_do_unallow(int argc, char **argv);

@@ -129,10 +130,11 @@
         HELP_PROMOTE,
         HELP_RECEIVE,
         HELP_RENAME,
         HELP_ROLLBACK,
         HELP_SEND,
+        HELP_FITS_SEND,
         HELP_SET,
         HELP_SHARE,
         HELP_SNAPSHOT,
         HELP_UNMOUNT,
         HELP_UNSHARE,

@@ -186,10 +188,12 @@
         { "unshare",    zfs_do_unshare,         HELP_UNSHARE            },
         { NULL },
         { "send",       zfs_do_send,            HELP_SEND               },
         { "receive",    zfs_do_receive,         HELP_RECEIVE            },
         { NULL },
+        { "fits-send",  zfs_do_fits_send,       HELP_FITS_SEND          },
+        { NULL },
         { "allow",      zfs_do_allow,           HELP_ALLOW              },
         { NULL },
         { "unallow",    zfs_do_unallow,         HELP_UNALLOW            },
         { NULL },
         { "hold",       zfs_do_hold,            HELP_HOLD               },

@@ -252,10 +256,13 @@
         case HELP_ROLLBACK:
                 return (gettext("\trollback [-rRf] <snapshot>\n"));
         case HELP_SEND:
                 return (gettext("\tsend [-DnPpRv] [-[iI] snapshot] "
                     "<snapshot>\n"));
+        case HELP_FITS_SEND:
+                return (gettext("\tfits-send [-v] [-i snapshot] "
+                    "<snapshot>\n"));
         case HELP_SET:
                 return (gettext("\tset <property=value> "
                     "<filesystem|volume|snapshot> ...\n"));
         case HELP_SHARE:
                 return (gettext("\tshare <-a | filesystem>\n"));

@@ -3723,10 +3730,118 @@
 
         return (err != 0);
 }
 
 /*
+ * Send a backup stream to stdout in fits format.
+ */
+static int
+zfs_do_fits_send(int argc, char **argv)
+{
+        char *fromname = NULL;
+        char *toname = NULL;
+        char *cp;
+        zfs_handle_t *zhp;
+        sendflags_t flags = { 0 };
+        int c, err;
+
+        /* check options */
+        while ((c = getopt(argc, argv, ":i:v")) != -1) {
+                switch (c) {
+                case 'i':
+                        if (fromname)
+                                usage(B_FALSE);
+                        fromname = optarg;
+                        break;
+                case 'v':
+                        flags.verbose = B_TRUE;
+                        break;
+                case ':':
+                        (void) fprintf(stderr, gettext("missing argument for "
+                            "'%c' option\n"), optopt);
+                        usage(B_FALSE);
+                        break;
+                case '?':
+                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                            optopt);
+                        usage(B_FALSE);
+                }
+        }
+
+        argc -= optind;
+        argv += optind;
+
+        /* check number of arguments */
+        if (argc < 1) {
+                (void) fprintf(stderr, gettext("missing snapshot argument\n"));
+                usage(B_FALSE);
+        }
+        if (argc > 1) {
+                (void) fprintf(stderr, gettext("too many arguments\n"));
+                usage(B_FALSE);
+        }
+
+        if (isatty(STDOUT_FILENO)) {
+                (void) fprintf(stderr,
+                    gettext("Error: Stream can not be written to a terminal.\n"
+                    "You must redirect standard output.\n"));
+                return (1);
+        }
+
+        cp = strchr(argv[0], '@');
+        if (cp == NULL) {
+                (void) fprintf(stderr,
+                    gettext("argument must be a snapshot\n"));
+                usage(B_FALSE);
+        }
+        *cp = '\0';
+        toname = cp + 1;
+        zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);
+        if (zhp == NULL)
+                return (1);
+
+        /*
+         * If they specified the full path to the snapshot, chop off
+         * everything except the short name of the snapshot, but special
+         * case if they specify the origin.
+         */
+        if (fromname && (cp = strchr(fromname, '@')) != NULL) {
+                char origin[ZFS_MAXNAMELEN];
+                zprop_source_t src;
+
+                (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
+                    origin, sizeof (origin), &src, NULL, 0, B_FALSE);
+
+                if (strcmp(origin, fromname) == 0) {
+                        fromname = NULL;
+                        flags.fromorigin = B_TRUE;
+                } else {
+                        *cp = '\0';
+                        if (cp != fromname && strcmp(argv[0], fromname)) {
+                                (void) fprintf(stderr,
+                                    gettext("incremental source must be "
+                                    "in same filesystem\n"));
+                                usage(B_FALSE);
+                        }
+                        fromname = cp + 1;
+                        if (strchr(fromname, '@') || strchr(fromname, '/')) {
+                                (void) fprintf(stderr,
+                                    gettext("invalid incremental source\n"));
+                                usage(B_FALSE);
+                        }
+                }
+        }
+
+        err = zfs_fits_send(zhp, fromname, toname, &flags, STDOUT_FILENO,
+            NULL, 0);
+
+        zfs_close(zhp);
+
+        return (err != 0);
+}
+
+/*
  * allow/unallow stuff
  */
 /* copied from zfs/sys/dsl_deleg.h */
 #define ZFS_DELEG_PERM_CREATE           "create"
 #define ZFS_DELEG_PERM_DESTROY          "destroy"