]> granicus.if.org Git - zfs/commitdiff
Free objects when receiving full stream as clone
authorFabian Grünbichler <f.gruenbichler@proxmox.com>
Fri, 29 Sep 2017 10:00:29 +0000 (12:00 +0200)
committerTony Hutter <hutter2@llnl.gov>
Mon, 16 Oct 2017 17:57:55 +0000 (10:57 -0700)
All objects after the last written or freed object are not supposed to
exist after receiving the stream.  Free them accordingly, as if a
freeobjects record for them had been included in the stream.

Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Closes #5699
Closes #6507
Closes #6616

include/sys/dmu_send.h
module/zfs/dmu_send.c

index e9bef8bddb736e7bcdc9b0f0d9c26ffadb3c6570..5cf67a6ab580cbd1c841c3b3ca171024f1ac40ed 100644 (file)
@@ -61,6 +61,7 @@ typedef struct dmu_recv_cookie {
        boolean_t drc_byteswap;
        boolean_t drc_force;
        boolean_t drc_resumable;
+       boolean_t drc_clone;
        struct avl_tree *drc_guid_to_ds_map;
        zio_cksum_t drc_cksum;
        uint64_t drc_newsnapobj;
index 0c53ced0ff0ad4b397a9d8cf2a3bfaf37be6880a..0fd2d6a5e5f224c9d647c238039ed3a887beb814 100644 (file)
@@ -1807,6 +1807,7 @@ dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin,
        drc->drc_force = force;
        drc->drc_resumable = resumable;
        drc->drc_cred = CRED();
+       drc->drc_clone = (origin != NULL);
 
        if (drc->drc_drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
                drc->drc_byteswap = B_TRUE;
@@ -1867,7 +1868,9 @@ struct receive_writer_arg {
        /* A map from guid to dataset to help handle dedup'd streams. */
        avl_tree_t *guid_to_ds_map;
        boolean_t resumable;
-       uint64_t last_object, last_offset;
+       uint64_t last_object;
+       uint64_t last_offset;
+       uint64_t max_object; /* highest object ID referenced in stream */
        uint64_t bytes_read; /* bytes read when current record created */
 };
 
@@ -2141,6 +2144,9 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro,
                return (SET_ERROR(EINVAL));
        object = err == 0 ? drro->drr_object : DMU_NEW_OBJECT;
 
+       if (drro->drr_object > rwa->max_object)
+               rwa->max_object = drro->drr_object;
+
        /*
         * If we are losing blkptrs or changing the block size this must
         * be a new file instance.  We must clear out the previous file
@@ -2241,6 +2247,9 @@ receive_freeobjects(struct receive_writer_arg *rwa,
                err = dmu_free_long_object(rwa->os, obj);
                if (err != 0)
                        return (err);
+
+               if (obj > rwa->max_object)
+                       rwa->max_object = obj;
        }
        if (next_err != ESRCH)
                return (next_err);
@@ -2271,6 +2280,9 @@ receive_write(struct receive_writer_arg *rwa, struct drr_write *drrw,
        rwa->last_object = drrw->drr_object;
        rwa->last_offset = drrw->drr_offset;
 
+       if (rwa->last_object > rwa->max_object)
+               rwa->max_object = rwa->last_object;
+
        if (dmu_object_info(rwa->os, drrw->drr_object, NULL) != 0)
                return (SET_ERROR(EINVAL));
 
@@ -2346,6 +2358,9 @@ receive_write_byref(struct receive_writer_arg *rwa,
                ref_os = rwa->os;
        }
 
+       if (drrwbr->drr_object > rwa->max_object)
+               rwa->max_object = drrwbr->drr_object;
+
        err = dmu_buf_hold(ref_os, drrwbr->drr_refobject,
            drrwbr->drr_refoffset, FTAG, &dbp, DMU_READ_PREFETCH);
        if (err != 0)
@@ -2388,6 +2403,9 @@ receive_write_embedded(struct receive_writer_arg *rwa,
        if (drrwe->drr_compression >= ZIO_COMPRESS_FUNCTIONS)
                return (EINVAL);
 
+       if (drrwe->drr_object > rwa->max_object)
+               rwa->max_object = drrwe->drr_object;
+
        tx = dmu_tx_create(rwa->os);
 
        dmu_tx_hold_write(tx, drrwe->drr_object,
@@ -2424,6 +2442,9 @@ receive_spill(struct receive_writer_arg *rwa, struct drr_spill *drrs,
        if (dmu_object_info(rwa->os, drrs->drr_object, NULL) != 0)
                return (SET_ERROR(EINVAL));
 
+       if (drrs->drr_object > rwa->max_object)
+               rwa->max_object = drrs->drr_object;
+
        VERIFY0(dmu_bonus_hold(rwa->os, drrs->drr_object, FTAG, &db));
        if ((err = dmu_spill_hold_by_bonus(db, FTAG, &db_spill)) != 0) {
                dmu_buf_rele(db, FTAG);
@@ -2468,6 +2489,9 @@ receive_free(struct receive_writer_arg *rwa, struct drr_free *drrf)
        if (dmu_object_info(rwa->os, drrf->drr_object, NULL) != 0)
                return (SET_ERROR(EINVAL));
 
+       if (drrf->drr_object > rwa->max_object)
+               rwa->max_object = drrf->drr_object;
+
        err = dmu_free_long_range(rwa->os, drrf->drr_object,
            drrf->drr_offset, drrf->drr_length);
 
@@ -3191,6 +3215,32 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp,
        }
        mutex_exit(&rwa->mutex);
 
+       /*
+        * If we are receiving a full stream as a clone, all object IDs which
+        * are greater than the maximum ID referenced in the stream are
+        * by definition unused and must be freed.
+        */
+       if (drc->drc_clone && drc->drc_drrb->drr_fromguid == 0) {
+               uint64_t obj = rwa->max_object + 1;
+               int free_err = 0;
+               int next_err = 0;
+
+               while (next_err == 0) {
+                       free_err = dmu_free_long_object(rwa->os, obj);
+                       if (free_err != 0 && free_err != ENOENT)
+                               break;
+
+                       next_err = dmu_object_next(rwa->os, &obj, FALSE, 0);
+               }
+
+               if (err == 0) {
+                       if (free_err != 0 && free_err != ENOENT)
+                               err = free_err;
+                       else if (next_err != ESRCH)
+                               err = next_err;
+               }
+       }
+
        cv_destroy(&rwa->cv);
        mutex_destroy(&rwa->mutex);
        bqueue_destroy(&rwa->q);