--- sys/contrib/openzfs/module/os/freebsd/zfs/zio_crypt.c.orig +++ sys/contrib/openzfs/module/os/freebsd/zfs/zio_crypt.c @@ -1364,6 +1364,19 @@ vec++; total_len += crypt_len; } + } else if (txtype == TX_CLONE_RANGE) { + const size_t o = offsetof(lr_clone_range_t, lr_nbps); + crypt_len = o - sizeof (lr_t); + dst_iovecs[vec].iov_base = (char *)dlrp + sizeof (lr_t); + dst_iovecs[vec].iov_len = crypt_len; + + /* copy the bps now since they will not be encrypted */ + memcpy(dlrp + o, slrp + o, lr_len - o); + memcpy(aadp, slrp + o, lr_len - o); + aadp += lr_len - o; + aad_len += lr_len - o; + vec++; + total_len += crypt_len; } else { crypt_len = lr_len - sizeof (lr_t); dst_iovecs[vec].iov_base = (char *)dlrp + --- sys/contrib/openzfs/module/os/linux/zfs/zio_crypt.c.orig +++ sys/contrib/openzfs/module/os/linux/zfs/zio_crypt.c @@ -1543,6 +1543,21 @@ nr_iovecs++; total_len += crypt_len; } + } else if (txtype == TX_CLONE_RANGE) { + const size_t o = offsetof(lr_clone_range_t, lr_nbps); + crypt_len = o - sizeof (lr_t); + src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); + src_iovecs[nr_iovecs].iov_len = crypt_len; + dst_iovecs[nr_iovecs].iov_base = dlrp + sizeof (lr_t); + dst_iovecs[nr_iovecs].iov_len = crypt_len; + + /* copy the bps now since they will not be encrypted */ + memcpy(dlrp + o, slrp + o, lr_len - o); + memcpy(aadp, slrp + o, lr_len - o); + aadp += lr_len - o; + aad_len += lr_len - o; + nr_iovecs++; + total_len += crypt_len; } else { crypt_len = lr_len - sizeof (lr_t); src_iovecs[nr_iovecs].iov_base = slrp + sizeof (lr_t); --- sys/contrib/openzfs/module/zfs/dbuf.c.orig +++ sys/contrib/openzfs/module/zfs/dbuf.c @@ -2700,15 +2700,23 @@ * writes and clones into this block. */ mutex_enter(&db->db_mtx); + DBUF_VERIFY(db); VERIFY(!dbuf_undirty(db, tx)); ASSERT3P(dbuf_find_dirty_eq(db, tx->tx_txg), ==, NULL); if (db->db_buf != NULL) { arc_buf_destroy(db->db_buf, db); db->db_buf = NULL; + dbuf_clear_data(db); } + + db->db_state = DB_NOFILL; + DTRACE_SET_STATE(db, "allocating NOFILL buffer for clone"); + + DBUF_VERIFY(db); mutex_exit(&db->db_mtx); - dmu_buf_will_not_fill(db_fake, tx); + dbuf_noread(db); + (void) dbuf_dirty(db, tx); } void --- sys/contrib/openzfs/module/zfs/dnode.c.orig +++ sys/contrib/openzfs/module/zfs/dnode.c @@ -1764,7 +1764,14 @@ } /* - * Checks if the dnode contains any uncommitted dirty records. + * Checks if the dnode itself is dirty, or is carrying any uncommitted records. + * It is important to check both conditions, as some operations (eg appending + * to a file) can dirty both as a single logical unit, but they are not synced + * out atomically, so checking one and not the other can result in an object + * appearing to be clean mid-way through a commit. + * + * Do not change this lightly! If you get it wrong, dmu_offset_next() can + * detect a hole where there is really data, leading to silent corruption. */ boolean_t dnode_is_dirty(dnode_t *dn) @@ -1772,7 +1779,8 @@ mutex_enter(&dn->dn_mtx); for (int i = 0; i < TXG_SIZE; i++) { - if (multilist_link_active(&dn->dn_dirty_link[i])) { + if (multilist_link_active(&dn->dn_dirty_link[i]) || + !list_is_empty(&dn->dn_dirty_records[i])) { mutex_exit(&dn->dn_mtx); return (B_TRUE); }