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 t...@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/b...@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.
  -- richard

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.

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",
@@ -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;


_______________________________________________
zfs-code mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/zfs-code

Reply via email to