Print this page
3819 zfs receive can fail due to processing order

@@ -721,11 +721,11 @@
 
         /* iterate over snaps, and set sd->parent_fromsnap_guid */
         sd->parent_fromsnap_guid = 0;
         VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
         VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
-        (void) zfs_iter_snapshots(zhp, send_iterate_snap, sd);
+        (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd);
         VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
         VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
         nvlist_free(sd->parent_snaps);
         nvlist_free(sd->snapprops);
 

@@ -1934,15 +1934,15 @@
 static int
 recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
     recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
     nvlist_t *renamed)
 {
-        nvlist_t *local_nv;
+        nvlist_t *local_nv, *deleted = NULL;
         avl_tree_t *local_avl;
         nvpair_t *fselem, *nextfselem;
         char *fromsnap;
-        char newname[ZFS_MAXNAMELEN];
+        char newname[ZFS_MAXNAMELEN], guidname[32];
         int error;
         boolean_t needagain, progress, recursive;
         char *s1, *s2;
 
         VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap));

@@ -1958,10 +1958,12 @@
 
         if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
             recursive, &local_nv, &local_avl)) != 0)
                 return (error);
 
+        deleted = fnvlist_alloc();
+
         /*
          * Process deletes and renames
          */
         for (fselem = nvlist_next_nvpair(local_nv, NULL);
             fselem; fselem = nextfselem) {

@@ -2029,10 +2031,11 @@
                         default:
                                 break;
                         case -1:
                                 fsavl_destroy(local_avl);
                                 nvlist_free(local_nv);
+                                nvlist_free(deleted);
                                 return (-1);
                         }
                         /*
                          * We had/have the wrong origin, therefore our
                          * list of snapshots is wrong.  Need to handle

@@ -2068,10 +2071,13 @@
                                     strlen(fsname)+1, newname, flags);
                                 if (error)
                                         needagain = B_TRUE;
                                 else
                                         progress = B_TRUE;
+                                (void) sprintf(guidname, "%llu",
+                                    (u_longlong_t)thisguid);
+                                fnvlist_add_boolean(deleted, guidname);
                                 continue;
                         }
 
                         stream_nvfs = found;
 

@@ -2123,10 +2129,13 @@
                             newname, flags);
                         if (error)
                                 needagain = B_TRUE;
                         else
                                 progress = B_TRUE;
+                        (void) sprintf(guidname, "%llu",
+                            (u_longlong_t)parent_fromsnap_guid);
+                        fnvlist_add_boolean(deleted, guidname);
                         continue;
                 }
 
                 if (fromguid == 0) {
                         if (flags->verbose) {

@@ -2145,10 +2154,28 @@
 
                 s1 = strrchr(fsname, '/');
                 s2 = strrchr(stream_fsname, '/');
 
                 /*
+                 * Check if we're going to rename based on parent guid change
+                 * and the current parent guid was also deleted. If it was then
+                 * the rename will fail so avoid this and force an early retry
+                 * to determine the new parent_fromsnap_guid.
+                 */
+                if (stream_parent_fromsnap_guid != 0 &&
+                    parent_fromsnap_guid != 0 &&
+                    stream_parent_fromsnap_guid != parent_fromsnap_guid) {
+                        (void) sprintf(guidname, "%llu",
+                            (u_longlong_t)parent_fromsnap_guid);
+                        if (nvlist_exists(deleted, guidname)) {
+                                progress = B_TRUE;
+                                needagain = B_TRUE;
+                                goto doagain;
+                        }
+                }
+
+                /*
                  * Check for rename. If the exact receive path is specified, it
                  * does not count as a rename, but we still need to check the
                  * datasets beneath it.
                  */
                 if ((stream_parent_fromsnap_guid != 0 &&

@@ -2198,12 +2225,14 @@
                         else
                                 progress = B_TRUE;
                 }
         }
 
+doagain:
         fsavl_destroy(local_avl);
         nvlist_free(local_nv);
+        nvlist_free(deleted);
 
         if (needagain && progress) {
                 /* do another pass to fix up temporary names */
                 if (flags->verbose)
                         (void) printf("another pass:\n");