Print this page
4986 receiving replication stream fails if any snapshot exceeds refquota

@@ -22,10 +22,11 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
+ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
  */
 
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>

@@ -56,11 +57,11 @@
 /* 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 *, const char *,
     recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
-    uint64_t *);
+    uint64_t *, const char *);
 static int guid_to_name(libzfs_handle_t *, const char *,
     uint64_t, boolean_t, char *);
 
 static const zio_cksum_t zero_cksum = { 0 };
 

@@ -2559,10 +2560,11 @@
     char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
 {
         nvlist_t *stream_nv = NULL;
         avl_tree_t *stream_avl = NULL;
         char *fromsnap = NULL;
+        char *sendsnap = NULL;
         char *cp;
         char tofs[ZFS_MAXNAMELEN];
         char sendfs[ZFS_MAXNAMELEN];
         char errbuf[1024];
         dmu_replay_record_t drre;

@@ -2707,12 +2709,19 @@
          * specified by 'zfs send') and pass it to each invocation of
          * zfs_receive_one().
          */
         (void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
             ZFS_MAXNAMELEN);
-        if ((cp = strchr(sendfs, '@')) != NULL)
+        if ((cp = strchr(sendfs, '@')) != NULL) {
                 *cp = '\0';
+                /*
+                 * Find the "sendsnap", the final snapshot in a replication
+                 * stream, so zfs_receive_one() can set filesystem properties
+                 * ONLY when receiving that final snapshot.
+                 */
+                sendsnap = (cp + 1);
+        }
 
         /* Finally, receive each contained stream */
         do {
                 /*
                  * we should figure out if it has a recoverable

@@ -2721,11 +2730,11 @@
                  * zfs_receive_one() will take care of it (ie,
                  * recv_skip() and return 0).
                  */
                 error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
                     sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
-                    action_handlep);
+                    action_handlep, sendsnap);
                 if (error == ENODATA) {
                         error = 0;
                         break;
                 }
                 anyerr |= error;

@@ -2885,13 +2894,13 @@
  * Restores a backup of tosnap from the file descriptor specified by infd.
  */
 static int
 zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
     const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
-    dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
+    dmu_replay_record_t *drr_noswap, const char *sendfs,
     avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
-    uint64_t *action_handlep)
+    uint64_t *action_handlep, const char *finalsnap)
 {
         zfs_cmd_t zc = { 0 };
         time_t begin_time;
         int ioctl_err, ioctl_errno, err;
         char *cp;

@@ -2903,39 +2912,44 @@
         boolean_t stream_wantsnewfs;
         uint64_t parent_snapguid = 0;
         prop_changelist_t *clp = NULL;
         nvlist_t *snapprops_nvlist = NULL;
         zprop_errflags_t prop_errflags;
-        boolean_t recursive;
 
         begin_time = time(NULL);
 
         (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
             "cannot receive"));
 
-        recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
-            ENOENT);
-
         if (stream_avl != NULL) {
                 char *snapname;
                 nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
                     &snapname);
                 nvlist_t *props;
                 int ret;
+                boolean_t is_finalsnap;
 
+                VERIFY(fs != NULL);
                 (void) nvlist_lookup_uint64(fs, "parentfromsnap",
                     &parent_snapguid);
+                /*
+                 * Can safely use strcmp because at least "snapname" has been
+                 * verified.
+                 */
+                is_finalsnap = (strcmp(snapname, finalsnap) == 0);
+
+                if (is_finalsnap)
                 err = nvlist_lookup_nvlist(fs, "props", &props);
-                if (err)
+                if (!is_finalsnap || err)
                         VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
 
                 if (flags->canmountoff) {
                         VERIFY(0 == nvlist_add_uint64(props,
                             zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
                 }
                 ret = zcmd_write_src_nvlist(hdl, &zc, props);
-                if (err)
+                if (!is_finalsnap || err)
                         nvlist_free(props);
 
                 if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
                         VERIFY(0 == nvlist_lookup_nvlist(props,
                             snapname, &snapprops_nvlist));

@@ -3002,11 +3016,11 @@
                  * tack on everything after the fs specified by 'zfs send'.
                  */
                 chopprefix = drrb->drr_toname + strlen(sendfs);
         } else {
                 /* A snapshot was specified as an exact path (no -d or -e). */
-                if (recursive) {
+                if (flags->recursive) {
                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                             "cannot specify snapshot name for multi-snapshot "
                             "stream"));
                         return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
                 }

@@ -3446,11 +3460,11 @@
 
 static int
 zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
     const char *originsnap, 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)
+    uint64_t *action_handlep, const char *finalsnap)
 {
         int err;
         dmu_replay_record_t drr, drr_noswap;
         struct drr_begin *drrb = &drr.drr_u.drr_begin;
         char errbuf[1024];

@@ -3542,14 +3556,21 @@
                         (void) strlcpy(nonpackage_sendfs,
                             drr.drr_u.drr_begin.drr_toname, ZFS_MAXNAMELEN);
                         if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
                                 *cp = '\0';
                         sendfs = nonpackage_sendfs;
+                        VERIFY(finalsnap == NULL);
                 }
-                return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
-                    &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
-                    cleanup_fd, action_handlep));
+                flags->recursive =
+                    (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
+                    ENOENT);
+                err = zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
+                    &drr, &drr_noswap, sendfs, stream_avl, top_zfs,
+                    cleanup_fd, action_handlep, finalsnap);
+                /* Clear out internal-only flags. */
+                flags->recursive = B_FALSE;
+                return (err);
         } else {
                 assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
                     DMU_COMPOUNDSTREAM);
                 return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
                     &zcksum, top_zfs, cleanup_fd, action_handlep));

@@ -3580,11 +3601,11 @@
 
         cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
         VERIFY(cleanup_fd >= 0);
 
         err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
-            stream_avl, &top_zfs, cleanup_fd, &action_handle);
+            stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL);
 
         VERIFY(0 == close(cleanup_fd));
 
         if (err == 0 && !flags->nomount && top_zfs) {
                 zfs_handle_t *zhp;