#include <stddef.h>
#include <fcntl.h>
#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/avl.h>
+#include <sys/debug.h>
+#include <stddef.h>
#include <pthread.h>
#include <umem.h>
#include "zfs_prop.h"
#include "zfs_fletcher.h"
#include "libzfs_impl.h"
-#include <sha2.h>
#include <sys/zio_checksum.h>
#include <sys/ddt.h>
+#include <sys/socket.h>
/* in libzfs_dataset.c */
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t,
- int, const char *, nvlist_t *, avl_tree_t *, char **);
+ int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *);
-static const zio_cksum_t zero_cksum = { 0 };
+static const zio_cksum_t zero_cksum = { { 0 } };
typedef struct dedup_arg {
int inputfd;
if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
zero_cksum) ||
!DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) {
- SHA256_CTX ctx;
- zio_cksum_t tmpsha256;
+ zio_cksum_t tmpsha256;
+
+ zio_checksum_SHA256(buf,
+ drrw->drr_length, &tmpsha256);
- SHA256Init(&ctx);
- SHA256Update(&ctx, buf, drrw->drr_length);
- SHA256Final(&tmpsha256, &ctx);
drrw->drr_key.ddk_cksum.zc_word[0] =
BE_64(tmpsha256.zc_word[0]);
drrw->drr_key.ddk_cksum.zc_word[1] =
zfs_sort_snaps(zfs_handle_t *zhp, void *data)
{
avl_tree_t *avl = data;
- zfs_node_t *node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+ zfs_node_t *node;
+ zfs_node_t search;
+ search.zn_handle = zhp;
+ node = avl_find(avl, &search, NULL);
+ if (node) {
+ /*
+ * If this snapshot was renamed while we were creating the
+ * AVL tree, it's possible that we already inserted it under
+ * its old name. Remove the old handle before adding the new
+ * one.
+ */
+ zfs_close(node->zn_handle);
+ avl_remove(avl, node);
+ free(node);
+ }
+
+ node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
node->zn_handle = zhp;
avl_add(avl, node);
+
return (0);
}
-/* ARGSUSED */
static int
zfs_snapshot_compare(const void *larg, const void *rarg)
{
const char *fromsnap;
const char *tosnap;
char prevsnap[ZFS_MAXNAMELEN];
+ uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
boolean_t verbose;
int outfd;
snapfilter_cb_t *filter_cb;
void *filter_cb_arg;
nvlist_t *debugnv;
+ char holdtag[ZFS_MAXNAMELEN];
+ int cleanup_fd;
} send_dump_data_t;
/*
* NULL) to the file descriptor specified by outfd.
*/
static int
-dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, boolean_t fromorigin,
- int outfd, boolean_t enoent_ok, boolean_t *got_enoent, nvlist_t *debugnv)
+dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
+ boolean_t fromorigin, int outfd, nvlist_t *debugnv)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
libzfs_handle_t *hdl = zhp->zfs_hdl;
nvlist_t *thisdbg;
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- assert(fromsnap == NULL || fromsnap[0] == '\0' || !fromorigin);
+ assert(fromsnap_obj == 0 || !fromorigin);
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (fromsnap)
- (void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_value));
zc.zc_cookie = outfd;
zc.zc_obj = fromorigin;
-
- *got_enoent = B_FALSE;
+ zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
+ zc.zc_fromobj = fromsnap_obj;
VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
if (fromsnap && fromsnap[0] != '\0') {
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
case ENOENT:
- if (enoent_ok) {
- *got_enoent = B_TRUE;
- return (0);
- }
if (zfs_dataset_exists(hdl, zc.zc_name,
ZFS_TYPE_SNAPSHOT)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
}
static int
+hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
+{
+ zfs_handle_t *pzhp;
+ int error = 0;
+ char *thissnap;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+
+ /*
+ * zfs_send() only opens a cleanup_fd for sends that need it,
+ * e.g. replication and doall.
+ */
+ if (sdd->cleanup_fd == -1)
+ return (0);
+
+ thissnap = strchr(zhp->zfs_name, '@') + 1;
+ *(thissnap - 1) = '\0';
+ pzhp = zfs_open(zhp->zfs_hdl, zhp->zfs_name, ZFS_TYPE_DATASET);
+ *(thissnap - 1) = '@';
+
+ /*
+ * It's OK if the parent no longer exists. The send code will
+ * handle that error.
+ */
+ if (pzhp) {
+ error = zfs_hold(pzhp, thissnap, sdd->holdtag,
+ B_FALSE, B_TRUE, B_TRUE, sdd->cleanup_fd,
+ zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID),
+ zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG));
+ zfs_close(pzhp);
+ }
+
+ return (error);
+}
+
+static int
dump_snapshot(zfs_handle_t *zhp, void *arg)
{
send_dump_data_t *sdd = arg;
- const char *thissnap;
+ char *thissnap;
int err;
- boolean_t got_enoent;
boolean_t isfromsnap, istosnap;
boolean_t exclude = B_FALSE;
strcmp(sdd->fromsnap, thissnap) == 0);
if (!sdd->seenfrom && isfromsnap) {
- sdd->seenfrom = B_TRUE;
- (void) strcpy(sdd->prevsnap, thissnap);
+ err = hold_for_send(zhp, sdd);
+ if (err == 0) {
+ sdd->seenfrom = B_TRUE;
+ (void) strcpy(sdd->prevsnap, thissnap);
+ sdd->prevsnap_obj = zfs_prop_get_int(zhp,
+ ZFS_PROP_OBJSETID);
+ } else if (err == ENOENT) {
+ err = 0;
+ }
zfs_close(zhp);
- return (0);
+ return (err);
}
if (sdd->seento || !sdd->seenfrom) {
sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) {
/*
* This snapshot is filtered out. Don't send it, and don't
- * set prevsnap, so it will be as if this snapshot didn't
+ * set prevsnap_obj, so it will be as if this snapshot didn't
* exist, and the next accepted snapshot will be sent as
* an incremental from the last accepted one, or as the
* first (and full) snapshot in the case of a replication,
return (0);
}
+ err = hold_for_send(zhp, sdd);
+ if (err) {
+ if (err == ENOENT)
+ err = 0;
+ zfs_close(zhp);
+ return (err);
+ }
+
/* send it */
if (sdd->verbose) {
(void) fprintf(stderr, "sending from @%s to %s\n",
sdd->prevsnap, zhp->zfs_name);
}
- err = dump_ioctl(zhp, sdd->prevsnap,
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
- sdd->outfd, B_TRUE, &got_enoent, sdd->debugnv);
+ sdd->outfd, sdd->debugnv);
- if (got_enoent)
- err = 0;
- else
- (void) strcpy(sdd->prevsnap, thissnap);
+ (void) strcpy(sdd->prevsnap, thissnap);
+ sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
zfs_close(zhp);
return (err);
}
int rv = 0;
send_dump_data_t *sdd = arg;
boolean_t missingfrom = B_FALSE;
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
}
sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0;
+ sdd->prevsnap_obj = 0;
if (sdd->fromsnap == NULL || missingfrom)
sdd->seenfrom = B_TRUE;
int err;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
- char holdtag[128];
static uint64_t holdseq;
int spa_version;
boolean_t holdsnaps = B_FALSE;
dedup_arg_t dda = { 0 };
int featureflags = 0;
- if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
- uint64_t version;
- version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- if (version >= ZPL_VERSION_SA) {
- featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
- }
- }
-
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot send '%s'"), zhp->zfs_name);
return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
}
+ if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
+ uint64_t version;
+ version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
+ if (version >= ZPL_VERSION_SA) {
+ featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
+ }
+ }
+
if (zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS)
+ spa_version >= SPA_VERSION_USERREFS &&
+ (flags.doall || flags.replicate))
holdsnaps = B_TRUE;
if (flags.dedup) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
- if (err = pipe(pipefd)) {
+ if ((err = socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd))) {
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED,
errbuf));
dda.outputfd = outfd;
dda.inputfd = pipefd[1];
dda.dedup_hdl = zhp->zfs_hdl;
- if (err = pthread_create(&tid, NULL, cksummer, &dda)) {
+ if ((err = pthread_create(&tid, NULL, cksummer, &dda))) {
(void) close(pipefd[0]);
(void) close(pipefd[1]);
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
dmu_replay_record_t drr = { 0 };
char *packbuf = NULL;
size_t buflen = 0;
- zio_cksum_t zc = { 0 };
-
- if (holdsnaps) {
- (void) snprintf(holdtag, sizeof (holdtag),
- ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
- ++holdseq;
- err = zfs_hold_range(zhp, fromsnap, tosnap,
- holdtag, flags.replicate, B_TRUE, filter_func,
- cb_arg);
- if (err)
- goto err_out;
- }
+ zio_cksum_t zc = { { 0 } };
if (flags.replicate || flags.props) {
nvlist_t *hdrnv;
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
fromsnap, tosnap, flags.replicate, &fss, &fsavl);
- if (err) {
- if (holdsnaps) {
- (void) zfs_release_range(zhp, fromsnap,
- tosnap, holdtag, flags.replicate);
- }
+ if (err)
goto err_out;
- }
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
err = nvlist_pack(hdrnv, &packbuf, &buflen,
NV_ENCODE_XDR, 0);
if (err) {
fsavl_destroy(fsavl);
nvlist_free(fss);
- if (holdsnaps) {
- (void) zfs_release_range(zhp, fromsnap,
- tosnap, holdtag, flags.replicate);
- }
goto stderr_out;
}
}
if (err == -1) {
fsavl_destroy(fsavl);
nvlist_free(fss);
- if (holdsnaps) {
- (void) zfs_release_range(zhp, fromsnap, tosnap,
- holdtag, flags.replicate);
- }
err = errno;
goto stderr_out;
}
fsavl_destroy(fsavl);
nvlist_free(fss);
err = errno;
- if (holdsnaps) {
- (void) zfs_release_range(zhp, fromsnap,
- tosnap, holdtag, flags.replicate);
- }
goto stderr_out;
}
}
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
sdd.debugnv = *debugnvp;
+ if (holdsnaps) {
+ ++holdseq;
+ (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
+ ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
+ sdd.cleanup_fd = open(ZFS_DEV, O_RDWR);
+ if (sdd.cleanup_fd < 0) {
+ err = errno;
+ goto stderr_out;
+ }
+ } else {
+ sdd.cleanup_fd = -1;
+ }
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
(void) pthread_join(tid, NULL);
}
+ if (sdd.cleanup_fd != -1) {
+ VERIFY(0 == close(sdd.cleanup_fd));
+ sdd.cleanup_fd = -1;
+ }
+
if (flags.replicate || flags.doall || flags.props) {
/*
* write final end record. NB: want to do this even if
*/
dmu_replay_record_t drr = { 0 };
drr.drr_type = DRR_END;
- if (holdsnaps) {
- (void) zfs_release_range(zhp, fromsnap, tosnap,
- holdtag, flags.replicate);
- }
if (write(outfd, &drr, sizeof (drr)) == -1) {
return (zfs_standard_error(zhp->zfs_hdl,
errno, errbuf));
stderr_out:
err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
err_out:
+ if (sdd.cleanup_fd != -1)
+ VERIFY(0 == close(sdd.cleanup_fd));
if (flags.dedup) {
(void) pthread_cancel(tid);
(void) pthread_join(tid, NULL);
int baselen, char *newname, recvflags_t flags)
{
static int seq;
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int err;
prop_changelist_t *clp;
zfs_handle_t *zhp;
(void) strncpy(newname, name, baselen);
(void) snprintf(newname+baselen, ZFS_MAXNAMELEN-baselen,
- "recv-%u-%u", getpid(), seq);
+ "recv-%ld-%u", (long) getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
if (flags.verbose) {
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
char *newname, recvflags_t flags)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
int err = 0;
prop_changelist_t *clp;
zfs_handle_t *zhp;
stream_originguid, originguid)) {
case 1: {
/* promote it! */
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
nvlist_t *origin_nvfs;
char *origin_fsname;
if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
&props) && 0 == nvlist_lookup_nvlist(props,
stream_snapname, &props)) {
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
zc.zc_cookie = B_TRUE; /* received */
(void) snprintf(zc.zc_name, sizeof (zc.zc_name),
static int
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
recvflags_t flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
- char **top_zfs)
+ char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
nvlist_t *stream_nv = NULL;
avl_tree_t *stream_avl = NULL;
* recv_skip() and return 0).
*/
error = zfs_receive_impl(hdl, destname, flags, fd,
- sendfs, stream_nv, stream_avl, top_zfs);
+ sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
+ action_handlep);
if (error == ENODATA) {
error = 0;
break;
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
recvflags_t flags, dmu_replay_record_t *drr,
dmu_replay_record_t *drr_noswap, const char *sendfs,
- nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs)
+ nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
+ uint64_t *action_handlep)
{
- zfs_cmd_t zc = { 0 };
+ zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
time_t begin_time;
int ioctl_err, ioctl_errno, err;
char *cp;
*/
(void) strcpy(zc.zc_top_ds, tosnap);
(void) strcpy(zc.zc_value, tosnap);
- (void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
+ (void) strlcat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
free(cp);
if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
zcmd_free_nvlists(&zc);
(void) printf("found clone origin %s\n", zc.zc_string);
}
- stream_wantsnewfs = (drrb->drr_fromguid == NULL ||
+ stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
(drrb->drr_flags & DRR_FLAG_CLONE));
if (stream_wantsnewfs) {
return (-1);
}
}
+ if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_VOLUME &&
+ zvol_remove_link(hdl, zhp->zfs_name) != 0) {
+ zfs_close(zhp);
+ zcmd_free_nvlists(&zc);
+ return (-1);
+ }
zfs_close(zhp);
} else {
/*
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
+ zc.zc_cleanup_fd = cleanup_fd;
+ zc.zc_action_handle = *action_handlep;
err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
ioctl_errno = errno;
zcmd_free_nvlists(&zc);
if (err == 0 && snapprops_nvlist) {
- zfs_cmd_t zc2 = { 0 };
+ zfs_cmd_t zc2 = { "\0", "\0", "\0", "\0", 0 };
(void) strcpy(zc2.zc_name, zc.zc_value);
zc2.zc_cookie = B_TRUE; /* received */
if (h != NULL) {
if (h->zfs_type == ZFS_TYPE_VOLUME) {
*cp = '@';
+ err = zvol_create_link(hdl, h->zfs_name);
+ if (err == 0 && ioctl_err == 0)
+ err = zvol_create_link(hdl,
+ zc.zc_value);
} else if (newfs || stream_avl) {
/*
* Track the first/top of hierarchy fs,
if (err || ioctl_err)
return (-1);
+ *action_handlep = zc.zc_action_handle;
+
if (flags.verbose) {
char buf1[64];
char buf2[64];
static int
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags,
int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl,
- char **top_zfs)
+ char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
{
int err;
dmu_replay_record_t drr, drr_noswap;
struct drr_begin *drrb = &drr.drr_u.drr_begin;
char errbuf[1024];
- zio_cksum_t zcksum = { 0 };
+ zio_cksum_t zcksum = { { 0 } };
uint64_t featureflags;
int hdrtype;
}
return (zfs_receive_one(hdl, infd, tosnap, flags,
&drr, &drr_noswap, sendfs, stream_nv, stream_avl,
- top_zfs));
+ top_zfs, cleanup_fd, action_handlep));
} else {
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
DMU_COMPOUNDSTREAM);
return (zfs_receive_package(hdl, infd, tosnap, flags,
- &drr, &zcksum, top_zfs));
+ &drr, &zcksum, top_zfs, cleanup_fd, action_handlep));
}
}
{
char *top_zfs = NULL;
int err;
+ int cleanup_fd;
+ uint64_t action_handle = 0;
+
+ cleanup_fd = open(ZFS_DEV, O_RDWR);
+ VERIFY(cleanup_fd >= 0);
err = zfs_receive_impl(hdl, tosnap, flags, infd, NULL, NULL,
- stream_avl, &top_zfs);
+ stream_avl, &top_zfs, cleanup_fd, &action_handle);
+
+ VERIFY(0 == close(cleanup_fd));
if (err == 0 && !flags.nomount && top_zfs) {
zfs_handle_t *zhp;