Darren Reed
2010-Jun-30 02:40 UTC
[zfs-code] Receiving just a single volume from an entire pool
If I''ve used zfs send to create a dump of an entire pool, is it possible to use zfs receive to extract just a single filesystem? For example, if I did "zfs send tank at send | ..." and tank had tank/foo, tank/bar, tank/fubar, and now I just want to extract tank/bar, is that possible? What I want to do, in effect, is "zfs receive tank/bar at send" and only receive tank/bar, but to me the man page doesn''t read like it is meant to work like that. If it isn''t currently possible, what obstacles, if any, would there be in patching libzfs to support this? Looking at the code, it would seem like one way to do this would be to pass a matching string through to zfs_receive() and abort the receive in a similar fashion to -n if the name of the incoming data set does not match that desired. Thoughts? Darren
Darren Reed
2010-Jul-07 00:37 UTC
[zfs-code] Receiving just a single volume from an entire pool
On 29/06/10 10:38 PM, Richard Elling wrote:> On Jun 29, 2010, at 7:40 PM, Darren Reed wrote: > >> If I''ve used zfs send to create a dump of an entire pool, >> is it possible to use zfs receive to extract just a single >> filesystem? > > zfs send only works on data sets. There is no way to > capture a pool in an object stream. > >> For example, if I did "zfs send tank at send | ..." and tank >> had tank/foo, tank/bar, tank/fubar, and now I just want to >> extract tank/bar, is that possible? What I want to do, in >> effect, is "zfs receive tank/bar at send" and only receive >> tank/bar, but to me the man page doesn''t read like it >> is meant to work like that. > > Plan ahead and don''t send a recursive stream.As they sight, hindsight is 20/20.>> If it isn''t currently possible, what obstacles, if any, would >> there be in patching libzfs to support this? >> >> Looking at the code, it would seem like one way to do this >> would be to pass a matching string through to zfs_receive() >> and abort the receive in a similar fashion to -n if the name >> of the incoming data set does not match that desired. >> >> Thoughts? > > Seems reasonable to me. > -- richardI had some spare time over the long weekend to have a play with this. I''ve filed the bug as CR#6967089. The patch (or suggested fix) is below. I''ll probably work on it a bit more to test it and see if it needs any other changes. Darren remote: Not trusting file /export/onnv-clone/.hg/hgrc from untrusted user onhg, group gk remote: Not trusting file /export/onnv-clone/.hg/hgrc from untrusted user onhg, group gk diff -r 6ab6e1b7e92f usr/src/cmd/zfs/zfs_main.c --- a/usr/src/cmd/zfs/zfs_main.c Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/cmd/zfs/zfs_main.c Tue Jul 06 17:21:48 2010 -0700 @@ -2758,10 +2758,10 @@ zfs_do_receive(int argc, char **argv) { int c, err; - recvflags_t flags = { 0 }; - - /* check options */ - while ((c = getopt(argc, argv, ":denuvF")) != -1) { + recvflags_t flags = { 0, NULL }; + + /* check options */ + while ((c = getopt(argc, argv, ":denm:uvF")) != -1) { switch (c) { case ''d'': flags.isprefix = B_TRUE; @@ -2770,6 +2770,9 @@ flags.isprefix = B_TRUE; flags.istail = B_TRUE; break; + case ''m'': + flags.matching = optarg; + break; case ''n'': flags.dryrun = B_TRUE; break; @@ -2815,7 +2818,7 @@ return (1); } - err = zfs_receive(g_zfs, argv[0], flags, STDIN_FILENO, NULL); + err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL); return (err != 0); } diff -r 6ab6e1b7e92f usr/src/lib/libzfs/common/libzfs.h --- a/usr/src/lib/libzfs/common/libzfs.h Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/lib/libzfs/common/libzfs.h Tue Jul 06 17:21:48 2010 -0700 @@ -574,9 +574,12 @@ /* do not mount file systems as they are extracted (private) */ int nomount : 1; + + /* only receive the matching volume */ + char *matching; } recvflags_t; -extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t, +extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *, int, avl_tree_t *); /* diff -r 6ab6e1b7e92f usr/src/lib/libzfs/common/libzfs_sendrecv.c --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c Tue Jul 06 17:21:48 2010 -0700 @@ -50,7 +50,7 @@ /* 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, +static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *); static const zio_cksum_t zero_cksum = { 0 }; @@ -1470,7 +1470,7 @@ static int recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, - int baselen, char *newname, recvflags_t flags) + int baselen, char *newname, recvflags_t *flags) { static int seq; zfs_cmd_t zc = { 0 }; @@ -1482,7 +1482,7 @@ if (zhp == NULL) return (-1); clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, - flags.force ? MS_FORCE : 0); + flags->force ? MS_FORCE : 0); zfs_close(zhp); if (clp == NULL) return (-1); @@ -1498,7 +1498,7 @@ (void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value)); - if (flags.verbose) { + if (flags->verbose) { (void) printf("attempting rename %s to %s\n", zc.zc_name, zc.zc_value); } @@ -1517,19 +1517,19 @@ "recv-%u-%u", getpid(), seq); (void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value)); - if (flags.verbose) { + if (flags->verbose) { (void) printf("failed - trying rename %s to %s\n", zc.zc_name, zc.zc_value); } err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); if (err == 0) changelist_rename(clp, name, newname); - if (err && flags.verbose) { + if (err && flags->verbose) { (void) printf("failed (%u) - " "will try again on next pass\n", errno); } err = EAGAIN; - } else if (flags.verbose) { + } else if (flags->verbose) { if (err == 0) (void) printf("success\n"); else @@ -1544,7 +1544,7 @@ static int recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, - char *newname, recvflags_t flags) + char *newname, recvflags_t *flags) { zfs_cmd_t zc = { 0 }; int err = 0; @@ -1557,7 +1557,7 @@ if (zhp == NULL) return (-1); clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, - flags.force ? MS_FORCE : 0); + flags->force ? MS_FORCE : 0); if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && zfs_spa_version(zhp, &spa_version) == 0 && spa_version >= SPA_VERSION_USERREFS) @@ -1573,11 +1573,11 @@ zc.zc_defer_destroy = defer; (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); - if (flags.verbose) + if (flags->verbose) (void) printf("attempting destroy %s\n", zc.zc_name); err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); if (err == 0) { - if (flags.verbose) + if (flags->verbose) (void) printf("success\n"); changelist_remove(clp, zc.zc_name); } @@ -1701,7 +1701,7 @@ static int recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, - recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl, + recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl, nvlist_t *renamed) { nvlist_t *local_nv; @@ -1718,7 +1718,7 @@ recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") = ENOENT); - if (flags.dryrun) + if (flags->dryrun) return (0); again: @@ -1778,7 +1778,7 @@ nvlist_t *origin_nvfs; char *origin_fsname; - if (flags.verbose) + if (flags->verbose) (void) printf("promoting %s\n", fsname); origin_nvfs = fsavl_find(local_avl, originguid, @@ -1826,7 +1826,7 @@ if (found == NULL) { char name[ZFS_MAXNAMELEN]; - if (!flags.force) + if (!flags->force) continue; (void) snprintf(name, sizeof (name), "%s@%s", @@ -1884,7 +1884,7 @@ /* check for delete */ if (stream_nvfs == NULL) { - if (!flags.force) + if (!flags->force) continue; error = recv_destroy(hdl, fsname, strlen(tofs)+1, @@ -1897,7 +1897,7 @@ } if (fromguid == 0) { - if (flags.verbose) { + if (flags->verbose) { (void) printf("local fs %s does not have " "fromsnap (%s in stream); must have " "been deleted locally; ignoring\n", @@ -1922,7 +1922,7 @@ if ((stream_parent_fromsnap_guid != 0 && parent_fromsnap_guid != 0 && stream_parent_fromsnap_guid != parent_fromsnap_guid) || - ((flags.isprefix || strcmp(tofs, fsname) != 0) && + ((flags->isprefix || strcmp(tofs, fsname) != 0) && (s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) { nvlist_t *parent; char tryname[ZFS_MAXNAMELEN]; @@ -1945,7 +1945,7 @@ "%s%s", pname, strrchr(stream_fsname, ''/'')); } else { tryname[0] = ''\0''; - if (flags.verbose) { + if (flags->verbose) { (void) printf("local fs %s new parent " "not found\n", fsname); } @@ -1973,7 +1973,7 @@ if (needagain && progress) { /* do another pass to fix up temporary names */ - if (flags.verbose) + if (flags->verbose) (void) printf("another pass:\n"); goto again; } @@ -1983,7 +1983,7 @@ 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, + recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc, char **top_zfs, int cleanup_fd, uint64_t *action_handlep) { nvlist_t *stream_nv = NULL; @@ -2012,7 +2012,7 @@ */ if (drr->drr_payloadlen != 0) { error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen, - &stream_nv, flags.byteswap, zc); + &stream_nv, flags->byteswap, zc); if (error) { error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); goto out; @@ -2033,9 +2033,9 @@ * Read in the end record and verify checksum. */ if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre), - flags.byteswap, NULL))) + flags->byteswap, NULL))) goto out; - if (flags.byteswap) { + if (flags->byteswap) { drre.drr_type = BSWAP_32(drre.drr_type); drre.drr_u.drr_end.drr_checksum.zc_word[0] BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]); @@ -2076,11 +2076,11 @@ nvpair_t *pair = NULL; (void) strlcpy(tofs, destname, ZFS_MAXNAMELEN); - if (flags.isprefix) { + if (flags->isprefix) { struct drr_begin *drrb = &drr->drr_u.drr_begin; int i; - if (flags.istail) { + if (flags->istail) { cp = strrchr(drrb->drr_toname, ''/''); if (cp == NULL) { (void) strlcat(tofs, "/", @@ -2098,7 +2098,7 @@ *strchr(tofs, ''@'') = ''\0''; } - if (recursive && !flags.dryrun && !flags.nomount) { + if (recursive && !flags->dryrun && !flags->nomount) { VERIFY(0 == nvlist_alloc(&renamed, NV_UNIQUE_NAME, 0)); } @@ -2272,7 +2272,7 @@ */ static int zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, - recvflags_t flags, dmu_replay_record_t *drr, + 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, int cleanup_fd, uint64_t *action_handlep) @@ -2292,6 +2292,7 @@ nvlist_t *snapprops_nvlist = NULL; zprop_errflags_t prop_errflags; boolean_t recursive; + boolean_t skipstream; begin_time = time(NULL); @@ -2314,7 +2315,7 @@ if (err) VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0)); - if (flags.canmountoff) { + if (flags->canmountoff) { VERIFY(0 == nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0)); } @@ -2341,7 +2342,7 @@ * If they specified a snapshot, chop the entire name stored in * the stream. */ - if (flags.istail) { + if (flags->istail) { /* * A filesystem was specified with -e. We want to tack on only * the tail of the sent snapshot path. @@ -2367,7 +2368,7 @@ } else { chopprefix = drrb->drr_toname + (chopprefix - sendfs); } - } else if (flags.isprefix) { + } else if (flags->isprefix) { /* * A filesystem was specified with -d. We want to tack on * everything but the first element of the sent snapshot path @@ -2429,7 +2430,7 @@ zc.zc_value); return (zfs_error(hdl, EZFS_NOENT, errbuf)); } - if (flags.verbose) + if (flags->verbose) (void) printf("found clone origin %s\n", zc.zc_string); } @@ -2474,7 +2475,7 @@ * topmost path in the stream, then if the fs does not exist we * should look no further. */ - if ((flags.isprefix || (*(chopprefix = drrb->drr_toname + + if ((flags->isprefix || (*(chopprefix = drrb->drr_toname + strlen(sendfs)) != ''\0'' && *chopprefix != ''@'')) && !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { char snap[ZFS_MAXNAMELEN]; @@ -2501,7 +2502,7 @@ * snapshots). */ if (stream_wantsnewfs) { - if (!flags.force) { + if (!flags->force) { zcmd_free_nvlists(&zc); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "destination ''%s'' exists\n" @@ -2537,7 +2538,7 @@ return (zfs_error(hdl, EZFS_EXISTS, errbuf)); } - if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM && + if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM && stream_wantsnewfs) { /* We can''t do online recv in this case */ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0); @@ -2576,7 +2577,7 @@ */ *cp = ''\0''; - if (flags.isprefix && !flags.istail && !flags.dryrun && + if (flags->isprefix && !flags->istail && !flags->dryrun && create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) { zcmd_free_nvlists(&zc); return (zfs_error(hdl, EZFS_BADRESTORE, errbuf)); @@ -2587,18 +2588,27 @@ zc.zc_begin_record = drr_noswap->drr_u.drr_begin; zc.zc_cookie = infd; - zc.zc_guid = flags.force; - if (flags.verbose) { - (void) printf("%s %s stream of %s into %s\n", - flags.dryrun ? "would receive" : "receiving", - drrb->drr_fromguid ? "incremental" : "full", - drrb->drr_toname, zc.zc_value); - (void) fflush(stdout); + zc.zc_guid = flags->force; + skipstream = flags->dryrun; + if (flags->verbose) { + if (flags->matching != NULL && + strcmp(drrb->drr_toname, flags->matching)) { + (void) printf("skipping stream %s due to mismatch\n", + drrb->drr_toname); + (void) fflush(stdout); + skipstream = B_TRUE; + } else { + (void) printf("%s %s stream of %s into %s\n", + flags->dryrun ? "would receive" : "receiving", + drrb->drr_fromguid ? "incremental" : "full", + drrb->drr_toname, zc.zc_value); + (void) fflush(stdout); + } } - if (flags.dryrun) { + if (skipstream) { zcmd_free_nvlists(&zc); - return (recv_skip(hdl, infd, flags.byteswap)); + return (recv_skip(hdl, infd, flags->byteswap)); } zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf; @@ -2679,12 +2689,12 @@ nvlist_free(local_nv); if (fs != NULL) { - if (flags.verbose) { + if (flags->verbose) { (void) printf("snap %s already exists; " "ignoring\n", zc.zc_value); } err = ioctl_err = recv_skip(hdl, infd, - flags.byteswap); + flags->byteswap); } } *cp = ''@''; @@ -2794,7 +2804,7 @@ *action_handlep = zc.zc_action_handle; - if (flags.verbose) { + if (flags->verbose) { char buf1[64]; char buf2[64]; uint64_t bytes = zc.zc_cookie; @@ -2812,7 +2822,7 @@ } static int -zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags, +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, int cleanup_fd, uint64_t *action_handlep) { @@ -2827,7 +2837,7 @@ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot receive")); - if (flags.isprefix && + if (flags->isprefix && !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs " "(%s) does not exist"), tosnap); @@ -2847,7 +2857,7 @@ /* the kernel needs the non-byteswapped begin record */ drr_noswap = drr; - flags.byteswap = B_FALSE; + flags->byteswap = B_FALSE; if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { /* * We computed the checksum in the wrong byteorder in @@ -2855,7 +2865,7 @@ */ bzero(&zcksum, sizeof (zio_cksum_t)); fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum); - flags.byteswap = B_TRUE; + flags->byteswap = B_TRUE; drr.drr_type = BSWAP_32(drr.drr_type); drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen); @@ -2923,7 +2933,7 @@ * (-1 will override -2). */ int -zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags, +zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, int infd, avl_tree_t *stream_avl) { char *top_zfs = NULL; @@ -2939,7 +2949,7 @@ VERIFY(0 == close(cleanup_fd)); - if (err == 0 && !flags.nomount && top_zfs) { + if (err == 0 && !flags->nomount && top_zfs) { zfs_handle_t *zhp; prop_changelist_t *clp;
Pawel Jakub Dawidek
2010-Jul-07 07:06 UTC
[zfs-code] Receiving just a single volume from an entire pool
On Tue, Jul 06, 2010 at 05:37:41PM -0700, Darren Reed wrote:> I had some spare time over the long weekend to have a play with this. > > I''ve filed the bug as CR#6967089. The patch (or suggested fix) is below. > > I''ll probably work on it a bit more to test it and see if it needs any > other changes.[...]> zc.zc_begin_record = drr_noswap->drr_u.drr_begin; > zc.zc_cookie = infd; > - zc.zc_guid = flags.force; > - if (flags.verbose) { > - (void) printf("%s %s stream of %s into %s\n", > - flags.dryrun ? "would receive" : "receiving", > - drrb->drr_fromguid ? "incremental" : "full", > - drrb->drr_toname, zc.zc_value); > - (void) fflush(stdout); > + zc.zc_guid = flags->force; > + skipstream = flags->dryrun; > + if (flags->verbose) { > + if (flags->matching != NULL && > + strcmp(drrb->drr_toname, flags->matching)) { > + (void) printf("skipping stream %s due to mismatch\n", > + drrb->drr_toname); > + (void) fflush(stdout); > + skipstream = B_TRUE;Am I correct in reading the patch that unmatching streams will be skipped only in verbose mode?> + } else { > + (void) printf("%s %s stream of %s into %s\n", > + flags->dryrun ? "would receive" : "receiving", > + drrb->drr_fromguid ? "incremental" : "full", > + drrb->drr_toname, zc.zc_value); > + (void) fflush(stdout); > + } > } > > - if (flags.dryrun) { > + if (skipstream) { > zcmd_free_nvlists(&zc); > - return (recv_skip(hdl, infd, flags.byteswap)); > + return (recv_skip(hdl, infd, flags->byteswap)); > }-- Pawel Jakub Dawidek http://www.wheelsystems.com pjd at FreeBSD.org http://www.FreeBSD.org FreeBSD committer Am I Evil? Yes, I Am! -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 196 bytes Desc: not available URL: <http://mail.opensolaris.org/pipermail/zfs-code/attachments/20100707/9435ad0c/attachment.bin>
Darren Reed
2010-Jul-07 22:50 UTC
[zfs-code] Receiving just a single volume from an entire pool
On 7/07/10 12:06 AM, Pawel Jakub Dawidek wrote:> On Tue, Jul 06, 2010 at 05:37:41PM -0700, Darren Reed wrote: >> I had some spare time over the long weekend to have a play with this. >> >> I''ve filed the bug as CR#6967089. The patch (or suggested fix) is below. >> >> I''ll probably work on it a bit more to test it and see if it needs any >> other changes. > [...] >> zc.zc_begin_record = drr_noswap->drr_u.drr_begin; >> zc.zc_cookie = infd; >> - zc.zc_guid = flags.force; >> - if (flags.verbose) { >> - (void) printf("%s %s stream of %s into %s\n", >> - flags.dryrun ? "would receive" : "receiving", >> - drrb->drr_fromguid ? "incremental" : "full", >> - drrb->drr_toname, zc.zc_value); >> - (void) fflush(stdout); >> + zc.zc_guid = flags->force; >> + skipstream = flags->dryrun; >> + if (flags->verbose) { >> + if (flags->matching != NULL&& >> + strcmp(drrb->drr_toname, flags->matching)) { >> + (void) printf("skipping stream %s due to mismatch\n", >> + drrb->drr_toname); >> + (void) fflush(stdout); >> + skipstream = B_TRUE; > > Am I correct in reading the patch that unmatching streams will be > skipped only in verbose mode?Like I said, it needed further testing O:-) See below. Someone asked me why I thought I needed this because "zfs send tank at send" should send only "tank at send" and not all of its descendants. In reviewing the man page for zfs(1m), I note that I interpret the documentation to mean that unless I do "-R", then not all of the properties are sent across. From the man page: zfs send [-vR] [-[iI] snapshot] snapshot Creates a stream representation of the second snapshot, which is written to standard output. The output can be redirected to a file or to a different system (for exam- ple, using ssh(1). By default, a full stream is gen- erated. ... no mention here of properties ... -R Generate a replication stream package, which will replicate the specified filesystem, and all descen- dent file systems, up to the named snapshot. When received, all properties, snapshots, descendent file systems, and clones are preserved. ... here properties are mentioned, implying that without "-R" the data stream will not have all of the properties associated with the filesystem included. If "full stream" is meant to cover this then I''d suggest that the man page needs to explicitly state what a "full stream" is. At present, "full stream" does not appear to be defined by that man page. Thus having read the man page, I''m given the impression that I always need to use "-R" even if I just want to send a single snapshot of a pool. It might also be worth mentioning the word "recursive" with "-R" (I know that this is a keyword I look for when reading man pages to do with filesystems and directories if I''m concerned about that type of behaviour.) Having said all of that, I can conceive of times when it would be useful to just receive a particular dataset from inside a zfs data stream (like you''ve dump''d an entire pool using ''zfs send tank at send'' but then after restoring, you have an accident and destroy one filesystem on your recipient and you''re not real keen on sending that whole data set back again, making the entire pool unusable for the same of one small filesystem.) Darren remote: Not trusting file /export/onnv-clone/.hg/hgrc from untrusted user onhg, group gk remote: Not trusting file /export/onnv-clone/.hg/hgrc from untrusted user onhg, group gk diff -r 6ab6e1b7e92f usr/src/cmd/zfs/zfs_main.c --- a/usr/src/cmd/zfs/zfs_main.c Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/cmd/zfs/zfs_main.c Wed Jul 07 15:44:58 2010 -0700 @@ -2758,10 +2758,10 @@ zfs_do_receive(int argc, char **argv) { int c, err; - recvflags_t flags = { 0 }; - - /* check options */ - while ((c = getopt(argc, argv, ":denuvF")) != -1) { + recvflags_t flags = { 0, NULL }; + + /* check options */ + while ((c = getopt(argc, argv, ":denm:uvF")) != -1) { switch (c) { case ''d'': flags.isprefix = B_TRUE; @@ -2770,6 +2770,9 @@ flags.isprefix = B_TRUE; flags.istail = B_TRUE; break; + case ''m'': + flags.matching = optarg; + break; case ''n'': flags.dryrun = B_TRUE; break; @@ -2815,7 +2818,7 @@ return (1); } - err = zfs_receive(g_zfs, argv[0], flags, STDIN_FILENO, NULL); + err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL); return (err != 0); } diff -r 6ab6e1b7e92f usr/src/lib/libzfs/common/libzfs.h --- a/usr/src/lib/libzfs/common/libzfs.h Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/lib/libzfs/common/libzfs.h Wed Jul 07 15:44:58 2010 -0700 @@ -574,9 +574,12 @@ /* do not mount file systems as they are extracted (private) */ int nomount : 1; + + /* only receive the matching volume */ + char *matching; } recvflags_t; -extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t, +extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *, int, avl_tree_t *); /* diff -r 6ab6e1b7e92f usr/src/lib/libzfs/common/libzfs_sendrecv.c --- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c Wed Jun 30 19:40:00 2010 -0400 +++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c Wed Jul 07 15:44:58 2010 -0700 @@ -50,7 +50,7 @@ /* 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, +static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *); static const zio_cksum_t zero_cksum = { 0 }; @@ -1470,7 +1470,7 @@ static int recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, - int baselen, char *newname, recvflags_t flags) + int baselen, char *newname, recvflags_t *flags) { static int seq; zfs_cmd_t zc = { 0 }; @@ -1482,7 +1482,7 @@ if (zhp == NULL) return (-1); clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, - flags.force ? MS_FORCE : 0); + flags->force ? MS_FORCE : 0); zfs_close(zhp); if (clp == NULL) return (-1); @@ -1498,7 +1498,7 @@ (void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value)); - if (flags.verbose) { + if (flags->verbose) { (void) printf("attempting rename %s to %s\n", zc.zc_name, zc.zc_value); } @@ -1517,19 +1517,19 @@ "recv-%u-%u", getpid(), seq); (void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value)); - if (flags.verbose) { + if (flags->verbose) { (void) printf("failed - trying rename %s to %s\n", zc.zc_name, zc.zc_value); } err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); if (err == 0) changelist_rename(clp, name, newname); - if (err && flags.verbose) { + if (err && flags->verbose) { (void) printf("failed (%u) - " "will try again on next pass\n", errno); } err = EAGAIN; - } else if (flags.verbose) { + } else if (flags->verbose) { if (err == 0) (void) printf("success\n"); else @@ -1544,7 +1544,7 @@ static int recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, - char *newname, recvflags_t flags) + char *newname, recvflags_t *flags) { zfs_cmd_t zc = { 0 }; int err = 0; @@ -1557,7 +1557,7 @@ if (zhp == NULL) return (-1); clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, - flags.force ? MS_FORCE : 0); + flags->force ? MS_FORCE : 0); if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && zfs_spa_version(zhp, &spa_version) == 0 && spa_version >= SPA_VERSION_USERREFS) @@ -1573,11 +1573,11 @@ zc.zc_defer_destroy = defer; (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); - if (flags.verbose) + if (flags->verbose) (void) printf("attempting destroy %s\n", zc.zc_name); err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); if (err == 0) { - if (flags.verbose) + if (flags->verbose) (void) printf("success\n"); changelist_remove(clp, zc.zc_name); } @@ -1701,7 +1701,7 @@ static int recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, - recvflags_t flags, nvlist_t *stream_nv, avl_tree_t *stream_avl, + recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl, nvlist_t *renamed) { nvlist_t *local_nv; @@ -1718,7 +1718,7 @@ recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") = ENOENT); - if (flags.dryrun) + if (flags->dryrun) return (0); again: @@ -1778,7 +1778,7 @@ nvlist_t *origin_nvfs; char *origin_fsname; - if (flags.verbose) + if (flags->verbose) (void) printf("promoting %s\n", fsname); origin_nvfs = fsavl_find(local_avl, originguid, @@ -1826,7 +1826,7 @@ if (found == NULL) { char name[ZFS_MAXNAMELEN]; - if (!flags.force) + if (!flags->force) continue; (void) snprintf(name, sizeof (name), "%s@%s", @@ -1884,7 +1884,7 @@ /* check for delete */ if (stream_nvfs == NULL) { - if (!flags.force) + if (!flags->force) continue; error = recv_destroy(hdl, fsname, strlen(tofs)+1, @@ -1897,7 +1897,7 @@ } if (fromguid == 0) { - if (flags.verbose) { + if (flags->verbose) { (void) printf("local fs %s does not have " "fromsnap (%s in stream); must have " "been deleted locally; ignoring\n", @@ -1922,7 +1922,7 @@ if ((stream_parent_fromsnap_guid != 0 && parent_fromsnap_guid != 0 && stream_parent_fromsnap_guid != parent_fromsnap_guid) || - ((flags.isprefix || strcmp(tofs, fsname) != 0) && + ((flags->isprefix || strcmp(tofs, fsname) != 0) && (s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) { nvlist_t *parent; char tryname[ZFS_MAXNAMELEN]; @@ -1945,7 +1945,7 @@ "%s%s", pname, strrchr(stream_fsname, ''/'')); } else { tryname[0] = ''\0''; - if (flags.verbose) { + if (flags->verbose) { (void) printf("local fs %s new parent " "not found\n", fsname); } @@ -1973,7 +1973,7 @@ if (needagain && progress) { /* do another pass to fix up temporary names */ - if (flags.verbose) + if (flags->verbose) (void) printf("another pass:\n"); goto again; } @@ -1983,7 +1983,7 @@ 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, + recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc, char **top_zfs, int cleanup_fd, uint64_t *action_handlep) { nvlist_t *stream_nv = NULL; @@ -2012,7 +2012,7 @@ */ if (drr->drr_payloadlen != 0) { error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen, - &stream_nv, flags.byteswap, zc); + &stream_nv, flags->byteswap, zc); if (error) { error = zfs_error(hdl, EZFS_BADSTREAM, errbuf); goto out; @@ -2033,9 +2033,9 @@ * Read in the end record and verify checksum. */ if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre), - flags.byteswap, NULL))) + flags->byteswap, NULL))) goto out; - if (flags.byteswap) { + if (flags->byteswap) { drre.drr_type = BSWAP_32(drre.drr_type); drre.drr_u.drr_end.drr_checksum.zc_word[0] BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]); @@ -2076,11 +2076,11 @@ nvpair_t *pair = NULL; (void) strlcpy(tofs, destname, ZFS_MAXNAMELEN); - if (flags.isprefix) { + if (flags->isprefix) { struct drr_begin *drrb = &drr->drr_u.drr_begin; int i; - if (flags.istail) { + if (flags->istail) { cp = strrchr(drrb->drr_toname, ''/''); if (cp == NULL) { (void) strlcat(tofs, "/", @@ -2098,7 +2098,7 @@ *strchr(tofs, ''@'') = ''\0''; } - if (recursive && !flags.dryrun && !flags.nomount) { + if (recursive && !flags->dryrun && !flags->nomount) { VERIFY(0 == nvlist_alloc(&renamed, NV_UNIQUE_NAME, 0)); } @@ -2272,7 +2272,7 @@ */ static int zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, - recvflags_t flags, dmu_replay_record_t *drr, + 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, int cleanup_fd, uint64_t *action_handlep) @@ -2292,6 +2292,7 @@ nvlist_t *snapprops_nvlist = NULL; zprop_errflags_t prop_errflags; boolean_t recursive; + boolean_t skipstream; begin_time = time(NULL); @@ -2314,7 +2315,7 @@ if (err) VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0)); - if (flags.canmountoff) { + if (flags->canmountoff) { VERIFY(0 == nvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0)); } @@ -2341,7 +2342,7 @@ * If they specified a snapshot, chop the entire name stored in * the stream. */ - if (flags.istail) { + if (flags->istail) { /* * A filesystem was specified with -e. We want to tack on only * the tail of the sent snapshot path. @@ -2367,7 +2368,7 @@ } else { chopprefix = drrb->drr_toname + (chopprefix - sendfs); } - } else if (flags.isprefix) { + } else if (flags->isprefix) { /* * A filesystem was specified with -d. We want to tack on * everything but the first element of the sent snapshot path @@ -2429,7 +2430,7 @@ zc.zc_value); return (zfs_error(hdl, EZFS_NOENT, errbuf)); } - if (flags.verbose) + if (flags->verbose) (void) printf("found clone origin %s\n", zc.zc_string); } @@ -2474,7 +2475,7 @@ * topmost path in the stream, then if the fs does not exist we * should look no further. */ - if ((flags.isprefix || (*(chopprefix = drrb->drr_toname + + if ((flags->isprefix || (*(chopprefix = drrb->drr_toname + strlen(sendfs)) != ''\0'' && *chopprefix != ''@'')) && !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) { char snap[ZFS_MAXNAMELEN]; @@ -2501,7 +2502,7 @@ * snapshots). */ if (stream_wantsnewfs) { - if (!flags.force) { + if (!flags->force) { zcmd_free_nvlists(&zc); zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "destination ''%s'' exists\n" @@ -2537,7 +2538,7 @@ return (zfs_error(hdl, EZFS_EXISTS, errbuf)); } - if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM && + if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM && stream_wantsnewfs) { /* We can''t do online recv in this case */ clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0); @@ -2576,7 +2577,7 @@ */ *cp = ''\0''; - if (flags.isprefix && !flags.istail && !flags.dryrun && + if (flags->isprefix && !flags->istail && !flags->dryrun && create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) { zcmd_free_nvlists(&zc); return (zfs_error(hdl, EZFS_BADRESTORE, errbuf)); @@ -2587,18 +2588,31 @@ zc.zc_begin_record = drr_noswap->drr_u.drr_begin; zc.zc_cookie = infd; - zc.zc_guid = flags.force; - if (flags.verbose) { - (void) printf("%s %s stream of %s into %s\n", - flags.dryrun ? "would receive" : "receiving", - drrb->drr_fromguid ? "incremental" : "full", - drrb->drr_toname, zc.zc_value); - (void) fflush(stdout); + zc.zc_guid = flags->force; + skipstream = flags->dryrun; + + if (flags->verbose) { + if (flags->matching != NULL && + strcmp(drrb->drr_toname, flags->matching)) { + (void) printf("skipping stream %s due to mismatch\n", + drrb->drr_toname); + (void) fflush(stdout); + skipstream = B_TRUE; + } else { + (void) printf("%s %s stream of %s into %s\n", + flags->dryrun ? "would receive" : "receiving", + drrb->drr_fromguid ? "incremental" : "full", + drrb->drr_toname, zc.zc_value); + (void) fflush(stdout); + } + } else if (!skipstream && flags->matching != NULL && + strcmp(drrb->drr_toname, flags->matching) != 0) { + skipstream = B_TRUE; } - if (flags.dryrun) { + if (skipstream) { zcmd_free_nvlists(&zc); - return (recv_skip(hdl, infd, flags.byteswap)); + return (recv_skip(hdl, infd, flags->byteswap)); } zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf; @@ -2679,12 +2693,12 @@ nvlist_free(local_nv); if (fs != NULL) { - if (flags.verbose) { + if (flags->verbose) { (void) printf("snap %s already exists; " "ignoring\n", zc.zc_value); } err = ioctl_err = recv_skip(hdl, infd, - flags.byteswap); + flags->byteswap); } } *cp = ''@''; @@ -2794,7 +2808,7 @@ *action_handlep = zc.zc_action_handle; - if (flags.verbose) { + if (flags->verbose) { char buf1[64]; char buf2[64]; uint64_t bytes = zc.zc_cookie; @@ -2812,7 +2826,7 @@ } static int -zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags, +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, int cleanup_fd, uint64_t *action_handlep) { @@ -2827,7 +2841,7 @@ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot receive")); - if (flags.isprefix && + if (flags->isprefix && !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs " "(%s) does not exist"), tosnap); @@ -2847,7 +2861,7 @@ /* the kernel needs the non-byteswapped begin record */ drr_noswap = drr; - flags.byteswap = B_FALSE; + flags->byteswap = B_FALSE; if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { /* * We computed the checksum in the wrong byteorder in @@ -2855,7 +2869,7 @@ */ bzero(&zcksum, sizeof (zio_cksum_t)); fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum); - flags.byteswap = B_TRUE; + flags->byteswap = B_TRUE; drr.drr_type = BSWAP_32(drr.drr_type); drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen); @@ -2923,7 +2937,7 @@ * (-1 will override -2). */ int -zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t flags, +zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, int infd, avl_tree_t *stream_avl) { char *top_zfs = NULL; @@ -2939,7 +2953,7 @@ VERIFY(0 == close(cleanup_fd)); - if (err == 0 && !flags.nomount && top_zfs) { + if (err == 0 && !flags->nomount && top_zfs) { zfs_handle_t *zhp; prop_changelist_t *clp;
Matthew Ahrens
2010-Jul-08 03:13 UTC
[zfs-code] Receiving just a single volume from an entire pool
Darren Reed wrote:> Someone asked me why I thought I needed this because "zfs send > tank at send" should send only "tank at send" and not all of its > descendants. In reviewing the man page for zfs(1m), I note that > I interpret the documentation to mean that unless I do "-R", > then not all of the properties are sent across. From the man > page: > > zfs send [-vR] [-[iI] snapshot] snapshotYou have an outdated manpage / software. There is now a -p option: zfs send [-DvRp] [-[iI] snapshot] snapshot ... -p Send properties. --matt