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,3199 **** --- 3195,3298 ---- 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); + }