diff options
author | Chris Kirby <Chris.Kirby@oracle.com> | 2010-07-07 15:04:13 -0600 |
---|---|---|
committer | Chris Kirby <Chris.Kirby@oracle.com> | 2010-07-07 15:04:13 -0600 |
commit | a7f53a5629374ca27c5696ace9a1946c2ca050f4 (patch) | |
tree | d5bf31a797c0b2bc1eef43d9b6da990795dc0691 /usr/src/lib/libzfs/common/libzfs_sendrecv.c | |
parent | d5493db7e14e61d7910d92d1316d079110a327ef (diff) | |
download | illumos-gate-a7f53a5629374ca27c5696ace9a1946c2ca050f4.tar.gz |
6938335 zfs send -R can still miss renamed snapshots
6955879 panic in dmu_objset_stats while running nfs IOs.
6928104 zfs send/rename race can leak snapshot holds
6953835 mem leak in dsl_dataset_user_release_tmp()
6915117 zfs_iter_snapshots() should deal with midstream snapshot renames
Diffstat (limited to 'usr/src/lib/libzfs/common/libzfs_sendrecv.c')
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_sendrecv.c | 171 |
1 files changed, 114 insertions, 57 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c index 0762451554..3093ab974d 100644 --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c @@ -782,14 +782,30 @@ static int 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) { @@ -844,6 +860,7 @@ typedef struct send_dump_data { 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; @@ -853,6 +870,8 @@ typedef struct send_dump_data { snapfilter_cb_t *filter_cb; void *filter_cb_arg; nvlist_t *debugnv; + char holdtag[ZFS_MAXNAMELEN]; + int cleanup_fd; } send_dump_data_t; /* @@ -860,23 +879,21 @@ typedef struct send_dump_data { * 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 }; 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') { @@ -904,10 +921,6 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, boolean_t fromorigin, 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, @@ -943,12 +956,47 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, boolean_t fromorigin, } 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; @@ -957,10 +1005,17 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) 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) { @@ -1001,7 +1056,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) 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, @@ -1011,20 +1066,26 @@ dump_snapshot(zfs_handle_t *zhp, void *arg) 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); } @@ -1064,6 +1125,7 @@ dump_filesystem(zfs_handle_t *zhp, void *arg) } sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0; + sdd->prevsnap_obj = 0; if (sdd->fromsnap == NULL || missingfrom) sdd->seenfrom = B_TRUE; @@ -1202,7 +1264,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 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; @@ -1210,15 +1271,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, int pipefd[2]; dedup_arg_t dda = { 0 }; int featureflags = 0; - int cleanup_fd = -1; - - 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); @@ -1229,8 +1281,17 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 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) { @@ -1259,22 +1320,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, size_t buflen = 0; zio_cksum_t zc = { 0 }; - if (holdsnaps) { - ++holdseq; - (void) snprintf(holdtag, sizeof (holdtag), - ".send-%d-%llu", getpid(), (u_longlong_t)holdseq); - cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL); - if (cleanup_fd < 0) { - err = errno; - goto stderr_out; - } - err = zfs_hold_range(zhp, fromsnap, tosnap, - holdtag, flags.replicate, B_TRUE, filter_func, - cb_arg, cleanup_fd); - if (err) - goto err_out; - } - if (flags.replicate || flags.props) { nvlist_t *hdrnv; @@ -1364,6 +1409,18 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, 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|O_EXCL); + 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); @@ -1373,9 +1430,9 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, (void) pthread_join(tid, NULL); } - if (cleanup_fd != -1) { - VERIFY(0 == close(cleanup_fd)); - cleanup_fd = -1; + if (sdd.cleanup_fd != -1) { + VERIFY(0 == close(sdd.cleanup_fd)); + sdd.cleanup_fd = -1; } if (flags.replicate || flags.doall || flags.props) { @@ -1397,8 +1454,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, stderr_out: err = zfs_standard_error(zhp->zfs_hdl, err, errbuf); err_out: - if (cleanup_fd != -1) - VERIFY(0 == close(cleanup_fd)); + if (sdd.cleanup_fd != -1) + VERIFY(0 == close(sdd.cleanup_fd)); if (flags.dedup) { (void) pthread_cancel(tid); (void) pthread_join(tid, NULL); |