mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
Implement Redacted Send/Receive
Redacted send/receive allows users to send subsets of their data to a target system. One possible use case for this feature is to not transmit sensitive information to a data warehousing, test/dev, or analytics environment. Another is to save space by not replicating unimportant data within a given dataset, for example in backup tools like zrepl. Redacted send/receive is a three-stage process. First, a clone (or clones) is made of the snapshot to be sent to the target. In this clone (or clones), all unnecessary or unwanted data is removed or modified. This clone is then snapshotted to create the "redaction snapshot" (or snapshots). Second, the new zfs redact command is used to create a redaction bookmark. The redaction bookmark stores the list of blocks in a snapshot that were modified by the redaction snapshot(s). Finally, the redaction bookmark is passed as a parameter to zfs send. When sending to the snapshot that was redacted, the redaction bookmark is used to filter out blocks that contain sensitive or unwanted information, and those blocks are not included in the send stream. When sending from the redaction bookmark, the blocks it contains are considered as candidate blocks in addition to those blocks in the destination snapshot that were modified since the creation_txg of the redaction bookmark. This step is necessary to allow the target to rehydrate data in the case where some blocks are accidentally or unnecessarily modified in the redaction snapshot. The changes to bookmarks to enable fast space estimation involve adding deadlists to bookmarks. There is also logic to manage the life cycles of these deadlists. The new size estimation process operates in cases where previously an accurate estimate could not be provided. In those cases, a send is performed where no data blocks are read, reducing the runtime significantly and providing a byte-accurate size estimate. Reviewed-by: Dan Kimmel <dan.kimmel@delphix.com> Reviewed-by: Matt Ahrens <mahrens@delphix.com> Reviewed-by: Prashanth Sreenivasa <pks@delphix.com> Reviewed-by: John Kennedy <john.kennedy@delphix.com> Reviewed-by: George Wilson <george.wilson@delphix.com> Reviewed-by: Chris Williamson <chris.williamson@delphix.com> Reviewed-by: Pavel Zhakarov <pavel.zakharov@delphix.com> Reviewed-by: Sebastien Roy <sebastien.roy@delphix.com> Reviewed-by: Prakash Surya <prakash.surya@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Paul Dagnelie <pcd@delphix.com> Closes #7958
This commit is contained in:
committed by
Brian Behlendorf
parent
c1b5801bb5
commit
30af21b025
@@ -408,6 +408,47 @@ zpool_feature_init(void)
|
||||
edonr_deps);
|
||||
}
|
||||
|
||||
{
|
||||
static const spa_feature_t redact_books_deps[] = {
|
||||
SPA_FEATURE_BOOKMARK_V2,
|
||||
SPA_FEATURE_EXTENSIBLE_DATASET,
|
||||
SPA_FEATURE_BOOKMARKS,
|
||||
SPA_FEATURE_NONE
|
||||
};
|
||||
zfeature_register(SPA_FEATURE_REDACTION_BOOKMARKS,
|
||||
"com.delphix:redaction_bookmarks", "redaction_bookmarks",
|
||||
"Support for bookmarks which store redaction lists for zfs "
|
||||
"redacted send/recv.", 0, ZFEATURE_TYPE_BOOLEAN,
|
||||
redact_books_deps);
|
||||
}
|
||||
|
||||
{
|
||||
static const spa_feature_t redact_datasets_deps[] = {
|
||||
SPA_FEATURE_EXTENSIBLE_DATASET,
|
||||
SPA_FEATURE_NONE
|
||||
};
|
||||
zfeature_register(SPA_FEATURE_REDACTED_DATASETS,
|
||||
"com.delphix:redacted_datasets", "redacted_datasets", "Support for "
|
||||
"redacted datasets, produced by receiving a redacted zfs send "
|
||||
"stream.", ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_UINT64_ARRAY,
|
||||
redact_datasets_deps);
|
||||
}
|
||||
|
||||
{
|
||||
static const spa_feature_t bookmark_written_deps[] = {
|
||||
SPA_FEATURE_BOOKMARK_V2,
|
||||
SPA_FEATURE_EXTENSIBLE_DATASET,
|
||||
SPA_FEATURE_BOOKMARKS,
|
||||
SPA_FEATURE_NONE
|
||||
};
|
||||
zfeature_register(SPA_FEATURE_BOOKMARK_WRITTEN,
|
||||
"com.delphix:bookmark_written", "bookmark_written",
|
||||
"Additional accounting, enabling the written#<bookmark> property"
|
||||
"(space written since a bookmark), and estimates of send stream "
|
||||
"sizes for incrementals from bookmarks.",
|
||||
0, ZFEATURE_TYPE_BOOLEAN, bookmark_written_deps);
|
||||
}
|
||||
|
||||
zfeature_register(SPA_FEATURE_DEVICE_REMOVAL,
|
||||
"com.delphix:device_removal", "device_removal",
|
||||
"Top-level vdevs can be removed, reducing logical pool size.",
|
||||
|
||||
@@ -418,6 +418,7 @@ pool_namecheck(const char *pool, namecheck_err_t *why, char *what)
|
||||
}
|
||||
|
||||
#if defined(_KERNEL)
|
||||
EXPORT_SYMBOL(entity_namecheck);
|
||||
EXPORT_SYMBOL(pool_namecheck);
|
||||
EXPORT_SYMBOL(dataset_namecheck);
|
||||
EXPORT_SYMBOL(zfs_component_namecheck);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||
* Copyright 2016, Joyent, Inc.
|
||||
*/
|
||||
@@ -458,6 +458,10 @@ zfs_prop_init(void)
|
||||
zprop_register_string(ZFS_PROP_KEYLOCATION, "keylocation",
|
||||
"none", PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
|
||||
"prompt | <file URI>", "KEYLOCATION");
|
||||
zprop_register_string(ZFS_PROP_REDACT_SNAPS,
|
||||
"redact_snaps", NULL, PROP_READONLY,
|
||||
ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "<snapshot>[,...]",
|
||||
"RSNAPS");
|
||||
|
||||
/* readonly number properties */
|
||||
zprop_register_number(ZFS_PROP_USED, "used", 0, PROP_READONLY,
|
||||
@@ -465,9 +469,10 @@ zfs_prop_init(void)
|
||||
zprop_register_number(ZFS_PROP_AVAILABLE, "available", 0, PROP_READONLY,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>", "AVAIL");
|
||||
zprop_register_number(ZFS_PROP_REFERENCED, "referenced", 0,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "<size>", "REFER");
|
||||
PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "<size>",
|
||||
"REFER");
|
||||
zprop_register_number(ZFS_PROP_COMPRESSRATIO, "compressratio", 0,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
|
||||
"<1.00x or higher if compressed>", "RATIO");
|
||||
zprop_register_number(ZFS_PROP_REFRATIO, "refcompressratio", 0,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET,
|
||||
@@ -495,7 +500,8 @@ zfs_prop_init(void)
|
||||
PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "<size>",
|
||||
"LUSED");
|
||||
zprop_register_number(ZFS_PROP_LOGICALREFERENCED, "logicalreferenced",
|
||||
0, PROP_READONLY, ZFS_TYPE_DATASET, "<size>", "LREFER");
|
||||
0, PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "<size>",
|
||||
"LREFER");
|
||||
zprop_register_number(ZFS_PROP_FILESYSTEM_COUNT, "filesystem_count",
|
||||
UINT64_MAX, PROP_READONLY, ZFS_TYPE_FILESYSTEM,
|
||||
"<count>", "FSCOUNT");
|
||||
@@ -569,6 +575,8 @@ zfs_prop_init(void)
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "PBKDF2SALT");
|
||||
zprop_register_hidden(ZFS_PROP_KEY_GUID, "keyguid", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "KEYGUID");
|
||||
zprop_register_hidden(ZFS_PROP_REDACTED, "redacted", PROP_TYPE_NUMBER,
|
||||
PROP_READONLY, ZFS_TYPE_DATASET, "REDACTED");
|
||||
|
||||
/*
|
||||
* Property to be removed once libbe is integrated
|
||||
@@ -668,8 +676,10 @@ zfs_prop_userquota(const char *name)
|
||||
boolean_t
|
||||
zfs_prop_written(const char *name)
|
||||
{
|
||||
static const char *prefix = "written@";
|
||||
return (strncmp(name, prefix, strlen(prefix)) == 0);
|
||||
static const char *prop_prefix = "written@";
|
||||
static const char *book_prefix = "written#";
|
||||
return (strncmp(name, prop_prefix, strlen(prop_prefix)) == 0 ||
|
||||
strncmp(name, book_prefix, strlen(book_prefix)) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -35,6 +35,7 @@ $(MODULE)-objs += dmu_diff.o
|
||||
$(MODULE)-objs += dmu_object.o
|
||||
$(MODULE)-objs += dmu_objset.o
|
||||
$(MODULE)-objs += dmu_recv.o
|
||||
$(MODULE)-objs += dmu_redact.o
|
||||
$(MODULE)-objs += dmu_send.o
|
||||
$(MODULE)-objs += dmu_traverse.o
|
||||
$(MODULE)-objs += dmu_tx.o
|
||||
@@ -60,6 +61,7 @@ $(MODULE)-objs += lz4.o
|
||||
$(MODULE)-objs += metaslab.o
|
||||
$(MODULE)-objs += mmp.o
|
||||
$(MODULE)-objs += multilist.o
|
||||
$(MODULE)-objs += objlist.o
|
||||
$(MODULE)-objs += pathname.o
|
||||
$(MODULE)-objs += policy.o
|
||||
$(MODULE)-objs += range_tree.o
|
||||
|
||||
@@ -6170,6 +6170,8 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp,
|
||||
|
||||
ASSERT(!embedded_bp ||
|
||||
BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA);
|
||||
ASSERT(!BP_IS_HOLE(bp));
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
|
||||
top:
|
||||
if (!embedded_bp) {
|
||||
|
||||
+3
-2
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/arc.h>
|
||||
@@ -156,7 +156,8 @@ bptree_visit_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
int err;
|
||||
struct bptree_args *ba = arg;
|
||||
|
||||
if (bp == NULL || BP_IS_HOLE(bp))
|
||||
if (zb->zb_level == ZB_DNODE_LEVEL || BP_IS_HOLE(bp) ||
|
||||
BP_IS_REDACTED(bp))
|
||||
return (0);
|
||||
|
||||
err = ba->ba_func(ba->ba_arg, bp, ba->ba_tx);
|
||||
|
||||
+58
-15
@@ -13,7 +13,7 @@
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2014 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2014, 2018 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/bqueue.h>
|
||||
@@ -27,13 +27,27 @@ obj2node(bqueue_t *q, void *data)
|
||||
|
||||
/*
|
||||
* Initialize a blocking queue The maximum capacity of the queue is set to
|
||||
* size. Types that want to be stored in a bqueue must contain a bqueue_node_t,
|
||||
* and offset should give its offset from the start of the struct. Return 0 on
|
||||
* success, or -1 on failure.
|
||||
* size. Types that are stored in a bqueue must contain a bqueue_node_t,
|
||||
* and node_offset must be its offset from the start of the struct.
|
||||
* fill_fraction is a performance tuning value; when the queue is full, any
|
||||
* threads attempting to enqueue records will block. They will block until
|
||||
* they're signaled, which will occur when the queue is at least 1/fill_fraction
|
||||
* empty. Similar behavior occurs on dequeue; if the queue is empty, threads
|
||||
* block. They will be signalled when the queue has 1/fill_fraction full, or
|
||||
* when bqueue_flush is called. As a result, you must call bqueue_flush when
|
||||
* you enqueue your final record on a thread, in case the dequeueing threads are
|
||||
* currently blocked and that enqueue does not cause them to be awoken.
|
||||
* Alternatively, this behavior can be disabled (causing signaling to happen
|
||||
* immediately) by setting fill_fraction to any value larger than size.
|
||||
* Return 0 on success, or -1 on failure.
|
||||
*/
|
||||
int
|
||||
bqueue_init(bqueue_t *q, uint64_t size, size_t node_offset)
|
||||
bqueue_init(bqueue_t *q, uint64_t fill_fraction, uint64_t size,
|
||||
size_t node_offset)
|
||||
{
|
||||
if (fill_fraction == 0) {
|
||||
return (-1);
|
||||
}
|
||||
list_create(&q->bq_list, node_offset + sizeof (bqueue_node_t),
|
||||
node_offset + offsetof(bqueue_node_t, bqn_node));
|
||||
cv_init(&q->bq_add_cv, NULL, CV_DEFAULT, NULL);
|
||||
@@ -42,6 +56,7 @@ bqueue_init(bqueue_t *q, uint64_t size, size_t node_offset)
|
||||
q->bq_node_offset = node_offset;
|
||||
q->bq_size = 0;
|
||||
q->bq_maxsize = size;
|
||||
q->bq_fill_fraction = fill_fraction;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -53,20 +68,18 @@ bqueue_init(bqueue_t *q, uint64_t size, size_t node_offset)
|
||||
void
|
||||
bqueue_destroy(bqueue_t *q)
|
||||
{
|
||||
mutex_enter(&q->bq_lock);
|
||||
ASSERT0(q->bq_size);
|
||||
cv_destroy(&q->bq_add_cv);
|
||||
cv_destroy(&q->bq_pop_cv);
|
||||
mutex_destroy(&q->bq_lock);
|
||||
list_destroy(&q->bq_list);
|
||||
mutex_exit(&q->bq_lock);
|
||||
mutex_destroy(&q->bq_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add data to q, consuming size units of capacity. If there is insufficient
|
||||
* capacity to consume size units, block until capacity exists. Asserts size is
|
||||
* > 0.
|
||||
*/
|
||||
void
|
||||
bqueue_enqueue(bqueue_t *q, void *data, uint64_t item_size)
|
||||
static void
|
||||
bqueue_enqueue_impl(bqueue_t *q, void *data, uint64_t item_size,
|
||||
boolean_t flush)
|
||||
{
|
||||
ASSERT3U(item_size, >, 0);
|
||||
ASSERT3U(item_size, <=, q->bq_maxsize);
|
||||
@@ -77,9 +90,38 @@ bqueue_enqueue(bqueue_t *q, void *data, uint64_t item_size)
|
||||
}
|
||||
q->bq_size += item_size;
|
||||
list_insert_tail(&q->bq_list, data);
|
||||
cv_signal(&q->bq_pop_cv);
|
||||
if (q->bq_size >= q->bq_maxsize / q->bq_fill_fraction)
|
||||
cv_signal(&q->bq_pop_cv);
|
||||
if (flush)
|
||||
cv_broadcast(&q->bq_pop_cv);
|
||||
mutex_exit(&q->bq_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add data to q, consuming size units of capacity. If there is insufficient
|
||||
* capacity to consume size units, block until capacity exists. Asserts size is
|
||||
* > 0.
|
||||
*/
|
||||
void
|
||||
bqueue_enqueue(bqueue_t *q, void *data, uint64_t item_size)
|
||||
{
|
||||
bqueue_enqueue_impl(q, data, item_size, B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue an entry, and then flush the queue. This forces the popping threads
|
||||
* to wake up, even if we're below the fill fraction. We have this in a single
|
||||
* function, rather than having a separate call, because it prevents race
|
||||
* conditions between the enqueuing thread and the dequeueing thread, where the
|
||||
* enqueueing thread will wake up the dequeueing thread, that thread will
|
||||
* destroy the condvar before the enqueuing thread is done.
|
||||
*/
|
||||
void
|
||||
bqueue_enqueue_flush(bqueue_t *q, void *data, uint64_t item_size)
|
||||
{
|
||||
bqueue_enqueue_impl(q, data, item_size, B_TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take the first element off of q. If there are no elements on the queue, wait
|
||||
* until one is put there. Return the removed element.
|
||||
@@ -97,7 +139,8 @@ bqueue_dequeue(bqueue_t *q)
|
||||
ASSERT3P(ret, !=, NULL);
|
||||
item_size = obj2node(q, ret)->bqn_size;
|
||||
q->bq_size -= item_size;
|
||||
cv_signal(&q->bq_add_cv);
|
||||
if (q->bq_size <= q->bq_maxsize - (q->bq_maxsize / q->bq_fill_fraction))
|
||||
cv_signal(&q->bq_add_cv);
|
||||
mutex_exit(&q->bq_lock);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
+98
-5
@@ -1359,6 +1359,20 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Any attempt to read a redacted block should result in an error. This
|
||||
* will never happen under normal conditions, but can be useful for
|
||||
* debugging purposes.
|
||||
*/
|
||||
if (BP_IS_REDACTED(db->db_blkptr)) {
|
||||
ASSERT(dsl_dataset_feature_is_active(
|
||||
db->db_objset->os_dsl_dataset,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
DB_DNODE_EXIT(db);
|
||||
mutex_exit(&db->db_mtx);
|
||||
return (SET_ERROR(EIO));
|
||||
}
|
||||
|
||||
|
||||
SET_BOOKMARK(&zb, dmu_objset_id(db->db_objset),
|
||||
db->db.db_object, db->db_level, db->db_blkid);
|
||||
@@ -2395,11 +2409,23 @@ dmu_buf_set_crypt_params(dmu_buf_t *db_fake, boolean_t byteorder,
|
||||
bcopy(mac, dr->dt.dl.dr_mac, ZIO_DATA_MAC_LEN);
|
||||
}
|
||||
|
||||
#pragma weak dmu_buf_fill_done = dbuf_fill_done
|
||||
static void
|
||||
dbuf_override_impl(dmu_buf_impl_t *db, const blkptr_t *bp, dmu_tx_t *tx)
|
||||
{
|
||||
struct dirty_leaf *dl;
|
||||
|
||||
ASSERT3U(db->db_last_dirty->dr_txg, ==, tx->tx_txg);
|
||||
dl = &db->db_last_dirty->dt.dl;
|
||||
dl->dr_overridden_by = *bp;
|
||||
dl->dr_override_state = DR_OVERRIDDEN;
|
||||
dl->dr_overridden_by.blk_birth = db->db_last_dirty->dr_txg;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
void
|
||||
dbuf_fill_done(dmu_buf_impl_t *db, dmu_tx_t *tx)
|
||||
dmu_buf_fill_done(dmu_buf_t *dbuf, dmu_tx_t *tx)
|
||||
{
|
||||
dmu_buf_impl_t *db = (dmu_buf_impl_t *)dbuf;
|
||||
mutex_enter(&db->db_mtx);
|
||||
DBUF_VERIFY(db);
|
||||
|
||||
@@ -2454,6 +2480,31 @@ dmu_buf_write_embedded(dmu_buf_t *dbuf, void *data,
|
||||
dl->dr_overridden_by.blk_birth = db->db_last_dirty->dr_txg;
|
||||
}
|
||||
|
||||
void
|
||||
dmu_buf_redact(dmu_buf_t *dbuf, dmu_tx_t *tx)
|
||||
{
|
||||
dmu_buf_impl_t *db = (dmu_buf_impl_t *)dbuf;
|
||||
dmu_object_type_t type;
|
||||
ASSERT(dsl_dataset_feature_is_active(db->db_objset->os_dsl_dataset,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
|
||||
DB_DNODE_ENTER(db);
|
||||
type = DB_DNODE(db)->dn_type;
|
||||
DB_DNODE_EXIT(db);
|
||||
|
||||
ASSERT0(db->db_level);
|
||||
dmu_buf_will_not_fill(dbuf, tx);
|
||||
|
||||
blkptr_t bp = { { { {0} } } };
|
||||
BP_SET_TYPE(&bp, type);
|
||||
BP_SET_LEVEL(&bp, 0);
|
||||
BP_SET_BIRTH(&bp, tx->tx_txg, 0);
|
||||
BP_SET_REDACTED(&bp);
|
||||
BPE_SET_LSIZE(&bp, dbuf->db_size);
|
||||
|
||||
dbuf_override_impl(db, &bp, tx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Directly assign a provided arc buf to a given dbuf if it's not referenced
|
||||
* by anybody except our caller. Otherwise copy arcbuf's contents to dbuf.
|
||||
@@ -2820,6 +2871,36 @@ dbuf_create(dnode_t *dn, uint8_t level, uint64_t blkid,
|
||||
return (db);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns a block pointer and information about the object,
|
||||
* given a dnode and a block. This is a publicly accessible version of
|
||||
* dbuf_findbp that only returns some information, rather than the
|
||||
* dbuf. Note that the dnode passed in must be held, and the dn_struct_rwlock
|
||||
* should be locked as (at least) a reader.
|
||||
*/
|
||||
int
|
||||
dbuf_dnode_findbp(dnode_t *dn, uint64_t level, uint64_t blkid,
|
||||
blkptr_t *bp, uint16_t *datablkszsec, uint8_t *indblkshift)
|
||||
{
|
||||
dmu_buf_impl_t *dbp = NULL;
|
||||
blkptr_t *bp2;
|
||||
int err = 0;
|
||||
ASSERT(RW_LOCK_HELD(&dn->dn_struct_rwlock));
|
||||
|
||||
err = dbuf_findbp(dn, level, blkid, B_FALSE, &dbp, &bp2);
|
||||
if (err == 0) {
|
||||
*bp = *bp2;
|
||||
if (dbp != NULL)
|
||||
dbuf_rele(dbp, NULL);
|
||||
if (datablkszsec != NULL)
|
||||
*datablkszsec = dn->dn_phys->dn_datablkszsec;
|
||||
if (indblkshift != NULL)
|
||||
*indblkshift = dn->dn_phys->dn_indblkshift;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
typedef struct dbuf_prefetch_arg {
|
||||
spa_t *dpa_spa; /* The spa to issue the prefetch in. */
|
||||
zbookmark_phys_t dpa_zb; /* The target block to prefetch. */
|
||||
@@ -2837,7 +2918,12 @@ typedef struct dbuf_prefetch_arg {
|
||||
static void
|
||||
dbuf_issue_final_prefetch(dbuf_prefetch_arg_t *dpa, blkptr_t *bp)
|
||||
{
|
||||
if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
|
||||
ASSERT(!BP_IS_REDACTED(bp) ||
|
||||
dsl_dataset_feature_is_active(
|
||||
dpa->dpa_dnode->dn_objset->os_dsl_dataset,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
|
||||
if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp) || BP_IS_REDACTED(bp))
|
||||
return;
|
||||
|
||||
int zio_flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE;
|
||||
@@ -2921,7 +3007,11 @@ dbuf_prefetch_indirect_done(zio_t *zio, const zbookmark_phys_t *zb,
|
||||
blkptr_t *bp = ((blkptr_t *)abuf->b_data) +
|
||||
P2PHASE(nextblkid, 1ULL << dpa->dpa_epbs);
|
||||
|
||||
if (BP_IS_HOLE(bp)) {
|
||||
ASSERT(!BP_IS_REDACTED(bp) ||
|
||||
dsl_dataset_feature_is_active(
|
||||
dpa->dpa_dnode->dn_objset->os_dsl_dataset,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
if (BP_IS_HOLE(bp) || BP_IS_REDACTED(bp)) {
|
||||
kmem_free(dpa, sizeof (*dpa));
|
||||
} else if (dpa->dpa_curlevel == dpa->dpa_zb.zb_level) {
|
||||
ASSERT3U(nextblkid, ==, dpa->dpa_zb.zb_blkid);
|
||||
@@ -3025,7 +3115,10 @@ dbuf_prefetch(dnode_t *dn, int64_t level, uint64_t blkid, zio_priority_t prio,
|
||||
ASSERT3U(curblkid, <, dn->dn_phys->dn_nblkptr);
|
||||
bp = dn->dn_phys->dn_blkptr[curblkid];
|
||||
}
|
||||
if (BP_IS_HOLE(&bp))
|
||||
ASSERT(!BP_IS_REDACTED(&bp) ||
|
||||
dsl_dataset_feature_is_active(dn->dn_objset->os_dsl_dataset,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
if (BP_IS_HOLE(&bp) || BP_IS_REDACTED(&bp))
|
||||
return;
|
||||
|
||||
ASSERT3U(curlevel, ==, BP_GET_LEVEL(&bp));
|
||||
|
||||
+15
-1
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
* Copyright (c) 2016, Nexenta Systems, Inc. All rights reserved.
|
||||
@@ -1285,6 +1285,20 @@ dmu_write_embedded(objset_t *os, uint64_t object, uint64_t offset,
|
||||
dmu_buf_rele(db, FTAG);
|
||||
}
|
||||
|
||||
void
|
||||
dmu_redact(objset_t *os, uint64_t object, uint64_t offset, uint64_t size,
|
||||
dmu_tx_t *tx)
|
||||
{
|
||||
int numbufs, i;
|
||||
dmu_buf_t **dbp;
|
||||
|
||||
VERIFY0(dmu_buf_hold_array(os, object, offset, size, FALSE, FTAG,
|
||||
&numbufs, &dbp));
|
||||
for (i = 0; i < numbufs; i++)
|
||||
dmu_buf_redact(dbp[i], tx);
|
||||
dmu_buf_rele_array(dbp, numbufs, FTAG);
|
||||
}
|
||||
|
||||
/*
|
||||
* DMU support for xuio
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/dmu.h>
|
||||
@@ -115,7 +115,8 @@ diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
if (issig(JUSTLOOKING) && issig(FORREAL))
|
||||
return (SET_ERROR(EINTR));
|
||||
|
||||
if (bp == NULL || zb->zb_object != DMU_META_DNODE_OBJECT)
|
||||
if (zb->zb_level == ZB_DNODE_LEVEL ||
|
||||
zb->zb_object != DMU_META_DNODE_OBJECT)
|
||||
return (0);
|
||||
|
||||
if (BP_IS_HOLE(bp)) {
|
||||
|
||||
@@ -412,6 +412,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
|
||||
int i, err;
|
||||
|
||||
ASSERT(ds == NULL || MUTEX_HELD(&ds->ds_opening_lock));
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
|
||||
/*
|
||||
* The $ORIGIN dataset (if it exists) doesn't have an associated
|
||||
|
||||
+1238
-1110
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2110
-764
File diff suppressed because it is too large
Load Diff
+15
-13
@@ -67,8 +67,8 @@ typedef struct traverse_data {
|
||||
boolean_t td_realloc_possible;
|
||||
} traverse_data_t;
|
||||
|
||||
static int traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
uint64_t objset, uint64_t object);
|
||||
static int traverse_dnode(traverse_data_t *td, const blkptr_t *bp,
|
||||
const dnode_phys_t *dnp, uint64_t objset, uint64_t object);
|
||||
static void prefetch_dnode_metadata(traverse_data_t *td, const dnode_phys_t *,
|
||||
uint64_t objset, uint64_t object);
|
||||
|
||||
@@ -194,6 +194,7 @@ traverse_prefetch_metadata(traverse_data_t *td,
|
||||
return;
|
||||
if (BP_GET_LEVEL(bp) == 0 && BP_GET_TYPE(bp) != DMU_OT_DNODE)
|
||||
return;
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
|
||||
if ((td->td_flags & TRAVERSE_NO_DECRYPT) && BP_IS_PROTECTED(bp))
|
||||
zio_flags |= ZIO_FLAG_RAW;
|
||||
@@ -207,7 +208,7 @@ prefetch_needed(prefetch_data_t *pfd, const blkptr_t *bp)
|
||||
{
|
||||
ASSERT(pfd->pd_flags & TRAVERSE_PREFETCH_DATA);
|
||||
if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp) ||
|
||||
BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG)
|
||||
BP_GET_TYPE(bp) == DMU_OT_INTENT_LOG || BP_IS_REDACTED(bp))
|
||||
return (B_FALSE);
|
||||
return (B_TRUE);
|
||||
}
|
||||
@@ -274,7 +275,7 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
mutex_exit(&pd->pd_mtx);
|
||||
}
|
||||
|
||||
if (BP_IS_HOLE(bp)) {
|
||||
if (BP_IS_HOLE(bp) || BP_IS_REDACTED(bp)) {
|
||||
err = td->td_func(td->td_spa, NULL, bp, zb, dnp, td->td_arg);
|
||||
if (err != 0)
|
||||
goto post;
|
||||
@@ -354,7 +355,7 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
|
||||
/* recursively visitbp() blocks below this */
|
||||
for (i = 0; i < epb; i += child_dnp[i].dn_extra_slots + 1) {
|
||||
err = traverse_dnode(td, &child_dnp[i],
|
||||
err = traverse_dnode(td, bp, &child_dnp[i],
|
||||
zb->zb_objset, zb->zb_blkid * epb + i);
|
||||
if (err != 0)
|
||||
break;
|
||||
@@ -395,19 +396,19 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
zb->zb_objset, DMU_USERUSED_OBJECT);
|
||||
}
|
||||
|
||||
err = traverse_dnode(td, &osp->os_meta_dnode, zb->zb_objset,
|
||||
err = traverse_dnode(td, bp, &osp->os_meta_dnode, zb->zb_objset,
|
||||
DMU_META_DNODE_OBJECT);
|
||||
if (err == 0 && OBJSET_BUF_HAS_USERUSED(buf)) {
|
||||
if (OBJSET_BUF_HAS_PROJECTUSED(buf))
|
||||
err = traverse_dnode(td,
|
||||
err = traverse_dnode(td, bp,
|
||||
&osp->os_projectused_dnode, zb->zb_objset,
|
||||
DMU_PROJECTUSED_OBJECT);
|
||||
if (err == 0)
|
||||
err = traverse_dnode(td,
|
||||
err = traverse_dnode(td, bp,
|
||||
&osp->os_groupused_dnode, zb->zb_objset,
|
||||
DMU_GROUPUSED_OBJECT);
|
||||
if (err == 0)
|
||||
err = traverse_dnode(td,
|
||||
err = traverse_dnode(td, bp,
|
||||
&osp->os_userused_dnode, zb->zb_objset,
|
||||
DMU_USERUSED_OBJECT);
|
||||
}
|
||||
@@ -475,7 +476,7 @@ prefetch_dnode_metadata(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
}
|
||||
|
||||
static int
|
||||
traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
traverse_dnode(traverse_data_t *td, const blkptr_t *bp, const dnode_phys_t *dnp,
|
||||
uint64_t objset, uint64_t object)
|
||||
{
|
||||
int j, err = 0;
|
||||
@@ -488,7 +489,7 @@ traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
if (td->td_flags & TRAVERSE_PRE) {
|
||||
SET_BOOKMARK(&czb, objset, object, ZB_DNODE_LEVEL,
|
||||
ZB_DNODE_BLKID);
|
||||
err = td->td_func(td->td_spa, NULL, NULL, &czb, dnp,
|
||||
err = td->td_func(td->td_spa, NULL, bp, &czb, dnp,
|
||||
td->td_arg);
|
||||
if (err == TRAVERSE_VISIT_NO_CHILDREN)
|
||||
return (0);
|
||||
@@ -511,7 +512,7 @@ traverse_dnode(traverse_data_t *td, const dnode_phys_t *dnp,
|
||||
if (err == 0 && (td->td_flags & TRAVERSE_POST)) {
|
||||
SET_BOOKMARK(&czb, objset, object, ZB_DNODE_LEVEL,
|
||||
ZB_DNODE_BLKID);
|
||||
err = td->td_func(td->td_spa, NULL, NULL, &czb, dnp,
|
||||
err = td->td_func(td->td_spa, NULL, bp, &czb, dnp,
|
||||
td->td_arg);
|
||||
if (err == TRAVERSE_VISIT_NO_CHILDREN)
|
||||
return (0);
|
||||
@@ -532,7 +533,7 @@ traverse_prefetcher(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
ARC_FLAG_PRESCIENT_PREFETCH;
|
||||
|
||||
ASSERT(pfd->pd_bytes_fetched >= 0);
|
||||
if (bp == NULL)
|
||||
if (zb->zb_level == ZB_DNODE_LEVEL)
|
||||
return (0);
|
||||
if (pfd->pd_cancel)
|
||||
return (SET_ERROR(EINTR));
|
||||
@@ -635,6 +636,7 @@ traverse_impl(spa_t *spa, dsl_dataset_t *ds, uint64_t objset, blkptr_t *rootbp,
|
||||
uint32_t flags = ARC_FLAG_WAIT;
|
||||
objset_phys_t *osp;
|
||||
arc_buf_t *buf;
|
||||
ASSERT(!BP_IS_REDACTED(rootbp));
|
||||
|
||||
if ((td->td_flags & TRAVERSE_NO_DECRYPT) &&
|
||||
BP_IS_PROTECTED(rootbp))
|
||||
|
||||
+1145
-122
File diff suppressed because it is too large
Load Diff
+333
-119
@@ -57,6 +57,7 @@
|
||||
#include <sys/dsl_userhold.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
#include <sys/policy.h>
|
||||
#include <sys/dmu_send.h>
|
||||
#include <sys/dmu_recv.h>
|
||||
#include <sys/zio_compress.h>
|
||||
#include <zfs_fletcher.h>
|
||||
@@ -72,6 +73,7 @@
|
||||
* of this setting.
|
||||
*/
|
||||
int zfs_max_recordsize = 1 * 1024 * 1024;
|
||||
int zfs_allow_redacted_dataset_mount = 0;
|
||||
|
||||
#define SWITCH64(x, y) \
|
||||
{ \
|
||||
@@ -131,7 +133,7 @@ dsl_dataset_block_born(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx)
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
/* It could have been compressed away to nothing */
|
||||
if (BP_IS_HOLE(bp))
|
||||
if (BP_IS_HOLE(bp) || BP_IS_REDACTED(bp))
|
||||
return;
|
||||
ASSERT(BP_GET_TYPE(bp) != DMU_OT_NONE);
|
||||
ASSERT(DMU_OT_IS_VALID(BP_GET_TYPE(bp)));
|
||||
@@ -220,7 +222,7 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx,
|
||||
int compressed = BP_GET_PSIZE(bp);
|
||||
int uncompressed = BP_GET_UCSIZE(bp);
|
||||
|
||||
if (BP_IS_HOLE(bp))
|
||||
if (BP_IS_HOLE(bp) || BP_IS_REDACTED(bp))
|
||||
return (0);
|
||||
|
||||
ASSERT(dmu_tx_is_syncing(tx));
|
||||
@@ -284,6 +286,9 @@ dsl_dataset_block_kill(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx,
|
||||
DD_USED_HEAD, DD_USED_SNAP, tx);
|
||||
}
|
||||
}
|
||||
|
||||
dsl_bookmark_block_killed(ds, bp, tx);
|
||||
|
||||
mutex_enter(&ds->ds_lock);
|
||||
ASSERT3U(dsl_dataset_phys(ds)->ds_referenced_bytes, >=, used);
|
||||
dsl_dataset_phys(ds)->ds_referenced_bytes -= used;
|
||||
@@ -395,6 +400,8 @@ dsl_dataset_evict_async(void *dbu)
|
||||
ds->ds_prev = NULL;
|
||||
}
|
||||
|
||||
dsl_bookmark_fini_ds(ds);
|
||||
|
||||
bplist_destroy(&ds->ds_pending_deadlist);
|
||||
if (dsl_deadlist_is_open(&ds->ds_deadlist))
|
||||
dsl_deadlist_close(&ds->ds_deadlist);
|
||||
@@ -564,8 +571,8 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||
|
||||
bplist_create(&ds->ds_pending_deadlist);
|
||||
|
||||
list_create(&ds->ds_sendstreams, sizeof (dmu_sendarg_t),
|
||||
offsetof(dmu_sendarg_t, dsa_link));
|
||||
list_create(&ds->ds_sendstreams, sizeof (dmu_sendstatus_t),
|
||||
offsetof(dmu_sendstatus_t, dss_link));
|
||||
|
||||
list_create(&ds->ds_prop_cbs, sizeof (dsl_prop_cb_record_t),
|
||||
offsetof(dsl_prop_cb_record_t, cbr_ds_node));
|
||||
@@ -588,14 +595,7 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||
dsl_dataset_phys(ds)->ds_prev_snap_obj,
|
||||
ds, &ds->ds_prev);
|
||||
}
|
||||
if (doi.doi_type == DMU_OTN_ZAP_METADATA) {
|
||||
int zaperr = zap_lookup(mos, ds->ds_object,
|
||||
DS_FIELD_BOOKMARK_NAMES,
|
||||
sizeof (ds->ds_bookmarks), 1,
|
||||
&ds->ds_bookmarks);
|
||||
if (zaperr != ENOENT)
|
||||
VERIFY0(zaperr);
|
||||
}
|
||||
err = dsl_bookmark_init_ds(ds);
|
||||
} else {
|
||||
if (zfs_flags & ZFS_DEBUG_SNAPNAMES)
|
||||
err = dsl_dataset_get_snapname(ds);
|
||||
@@ -647,9 +647,15 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
|
||||
dsl_deadlist_close(&ds->ds_deadlist);
|
||||
if (dsl_deadlist_is_open(&ds->ds_remap_deadlist))
|
||||
dsl_deadlist_close(&ds->ds_remap_deadlist);
|
||||
dsl_bookmark_fini_ds(ds);
|
||||
if (ds->ds_prev)
|
||||
dsl_dataset_rele(ds->ds_prev, ds);
|
||||
dsl_dir_rele(ds->ds_dir, ds);
|
||||
for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
|
||||
if (dsl_dataset_feature_is_active(ds, f))
|
||||
unload_zfeature(ds, f);
|
||||
}
|
||||
|
||||
list_destroy(&ds->ds_prop_cbs);
|
||||
list_destroy(&ds->ds_sendstreams);
|
||||
mutex_destroy(&ds->ds_lock);
|
||||
@@ -784,14 +790,14 @@ dsl_dataset_hold(dsl_pool_t *dp, const char *name, void *tag,
|
||||
return (dsl_dataset_hold_flags(dp, name, 0, tag, dsp));
|
||||
}
|
||||
|
||||
int
|
||||
dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, ds_hold_flags_t flags,
|
||||
void *tag, dsl_dataset_t **dsp)
|
||||
static int
|
||||
dsl_dataset_own_obj_impl(dsl_pool_t *dp, uint64_t dsobj, ds_hold_flags_t flags,
|
||||
void *tag, boolean_t override, dsl_dataset_t **dsp)
|
||||
{
|
||||
int err = dsl_dataset_hold_obj_flags(dp, dsobj, flags, tag, dsp);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
if (!dsl_dataset_tryown(*dsp, tag)) {
|
||||
if (!dsl_dataset_tryown(*dsp, tag, override)) {
|
||||
dsl_dataset_rele_flags(*dsp, flags, tag);
|
||||
*dsp = NULL;
|
||||
return (SET_ERROR(EBUSY));
|
||||
@@ -799,20 +805,49 @@ dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, ds_hold_flags_t flags,
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dsl_dataset_own(dsl_pool_t *dp, const char *name, ds_hold_flags_t flags,
|
||||
dsl_dataset_own_obj(dsl_pool_t *dp, uint64_t dsobj, ds_hold_flags_t flags,
|
||||
void *tag, dsl_dataset_t **dsp)
|
||||
{
|
||||
return (dsl_dataset_own_obj_impl(dp, dsobj, flags, tag, B_FALSE, dsp));
|
||||
}
|
||||
|
||||
int
|
||||
dsl_dataset_own_obj_force(dsl_pool_t *dp, uint64_t dsobj,
|
||||
ds_hold_flags_t flags, void *tag, dsl_dataset_t **dsp)
|
||||
{
|
||||
return (dsl_dataset_own_obj_impl(dp, dsobj, flags, tag, B_TRUE, dsp));
|
||||
}
|
||||
|
||||
static int
|
||||
dsl_dataset_own_impl(dsl_pool_t *dp, const char *name, ds_hold_flags_t flags,
|
||||
void *tag, boolean_t override, dsl_dataset_t **dsp)
|
||||
{
|
||||
int err = dsl_dataset_hold_flags(dp, name, flags, tag, dsp);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
if (!dsl_dataset_tryown(*dsp, tag)) {
|
||||
if (!dsl_dataset_tryown(*dsp, tag, override)) {
|
||||
dsl_dataset_rele_flags(*dsp, flags, tag);
|
||||
return (SET_ERROR(EBUSY));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dsl_dataset_own_force(dsl_pool_t *dp, const char *name, ds_hold_flags_t flags,
|
||||
void *tag, dsl_dataset_t **dsp)
|
||||
{
|
||||
return (dsl_dataset_own_impl(dp, name, flags, tag, B_TRUE, dsp));
|
||||
}
|
||||
|
||||
int
|
||||
dsl_dataset_own(dsl_pool_t *dp, const char *name, ds_hold_flags_t flags,
|
||||
void *tag, dsl_dataset_t **dsp)
|
||||
{
|
||||
return (dsl_dataset_own_impl(dp, name, flags, tag, B_FALSE, dsp));
|
||||
}
|
||||
|
||||
/*
|
||||
* See the comment above dsl_pool_hold() for details. In summary, a long
|
||||
* hold is used to prevent destruction of a dataset while the pool hold
|
||||
@@ -927,13 +962,16 @@ dsl_dataset_disown(dsl_dataset_t *ds, ds_hold_flags_t flags, void *tag)
|
||||
}
|
||||
|
||||
boolean_t
|
||||
dsl_dataset_tryown(dsl_dataset_t *ds, void *tag)
|
||||
dsl_dataset_tryown(dsl_dataset_t *ds, void *tag, boolean_t override)
|
||||
{
|
||||
boolean_t gotit = FALSE;
|
||||
|
||||
ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool));
|
||||
mutex_enter(&ds->ds_lock);
|
||||
if (ds->ds_owner == NULL && !DS_IS_INCONSISTENT(ds)) {
|
||||
if (ds->ds_owner == NULL && (override || !(DS_IS_INCONSISTENT(ds) ||
|
||||
(dsl_dataset_feature_is_active(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS) &&
|
||||
!zfs_allow_redacted_dataset_mount)))) {
|
||||
ds->ds_owner = tag;
|
||||
dsl_dataset_long_hold(ds, tag);
|
||||
gotit = TRUE;
|
||||
@@ -1696,6 +1734,7 @@ dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname,
|
||||
dsl_dataset_phys(ds)->ds_deadlist_obj);
|
||||
dsl_deadlist_add_key(&ds->ds_deadlist,
|
||||
dsl_dataset_phys(ds)->ds_prev_snap_txg, tx);
|
||||
dsl_bookmark_snapshotted(ds, tx);
|
||||
|
||||
if (dsl_dataset_remap_deadlist_exists(ds)) {
|
||||
uint64_t remap_deadlist_obj =
|
||||
@@ -2013,6 +2052,8 @@ dsl_dataset_sync_done(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||
bplist_iterate(&ds->ds_pending_deadlist,
|
||||
deadlist_enqueue_cb, &ds->ds_deadlist, tx);
|
||||
|
||||
dsl_bookmark_sync_done(ds, tx);
|
||||
|
||||
if (os->os_synced_dnodes != NULL) {
|
||||
multilist_destroy(os->os_synced_dnodes);
|
||||
os->os_synced_dnodes = NULL;
|
||||
@@ -2151,6 +2192,34 @@ get_receive_resume_stats_impl(dsl_dataset_t *ds)
|
||||
DS_FIELD_RESUME_RAWOK) == 0) {
|
||||
fnvlist_add_boolean(token_nv, "rawok");
|
||||
}
|
||||
if (dsl_dataset_feature_is_active(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS)) {
|
||||
uint64_t num_redact_snaps;
|
||||
uint64_t *redact_snaps;
|
||||
VERIFY(dsl_dataset_get_uint64_array_feature(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS, &num_redact_snaps,
|
||||
&redact_snaps));
|
||||
fnvlist_add_uint64_array(token_nv, "redact_snaps",
|
||||
redact_snaps, num_redact_snaps);
|
||||
}
|
||||
if (zap_contains(dp->dp_meta_objset, ds->ds_object,
|
||||
DS_FIELD_RESUME_REDACT_BOOKMARK_SNAPS) == 0) {
|
||||
uint64_t num_redact_snaps, int_size;
|
||||
uint64_t *redact_snaps;
|
||||
VERIFY0(zap_length(dp->dp_meta_objset, ds->ds_object,
|
||||
DS_FIELD_RESUME_REDACT_BOOKMARK_SNAPS, &int_size,
|
||||
&num_redact_snaps));
|
||||
ASSERT3U(int_size, ==, sizeof (uint64_t));
|
||||
|
||||
redact_snaps = kmem_alloc(int_size * num_redact_snaps,
|
||||
KM_SLEEP);
|
||||
VERIFY0(zap_lookup(dp->dp_meta_objset, ds->ds_object,
|
||||
DS_FIELD_RESUME_REDACT_BOOKMARK_SNAPS, int_size,
|
||||
num_redact_snaps, redact_snaps));
|
||||
fnvlist_add_uint64_array(token_nv, "book_redact_snaps",
|
||||
redact_snaps, num_redact_snaps);
|
||||
kmem_free(redact_snaps, int_size * num_redact_snaps);
|
||||
}
|
||||
packed = fnvlist_pack(token_nv, &packed_size);
|
||||
fnvlist_free(token_nv);
|
||||
compressed = kmem_alloc(packed_size, KM_SLEEP);
|
||||
@@ -2336,6 +2405,13 @@ dsl_get_inconsistent(dsl_dataset_t *ds)
|
||||
1 : 0);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
dsl_get_redacted(dsl_dataset_t *ds)
|
||||
{
|
||||
return (dsl_dataset_feature_is_active(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
}
|
||||
|
||||
uint64_t
|
||||
dsl_get_available(dsl_dataset_t *ds)
|
||||
{
|
||||
@@ -2391,6 +2467,18 @@ dsl_get_prev_snap(dsl_dataset_t *ds, char *snap)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dsl_get_redact_snaps(dsl_dataset_t *ds, nvlist_t *propval)
|
||||
{
|
||||
uint64_t nsnaps;
|
||||
uint64_t *snaps;
|
||||
if (dsl_dataset_get_uint64_array_feature(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS, &nsnaps, &snaps)) {
|
||||
fnvlist_add_uint64_array(propval, ZPROP_VALUE, snaps,
|
||||
nsnaps);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the mountpoint property and source for the given dataset in the value
|
||||
* and source buffers. The value buffer must be at least as large as MAXPATHLEN
|
||||
@@ -2496,6 +2584,12 @@ dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
|
||||
dsl_dir_stats(ds->ds_dir, nv);
|
||||
}
|
||||
|
||||
nvlist_t *propval = fnvlist_alloc();
|
||||
dsl_get_redact_snaps(ds, propval);
|
||||
fnvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_REDACT_SNAPS),
|
||||
propval);
|
||||
nvlist_free(propval);
|
||||
|
||||
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE,
|
||||
dsl_get_available(ds));
|
||||
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED,
|
||||
@@ -2564,6 +2658,7 @@ dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat)
|
||||
stat->dds_creation_txg = dsl_get_creationtxg(ds);
|
||||
stat->dds_inconsistent = dsl_get_inconsistent(ds);
|
||||
stat->dds_guid = dsl_get_guid(ds);
|
||||
stat->dds_redacted = dsl_get_redacted(ds);
|
||||
stat->dds_origin[0] = '\0';
|
||||
if (ds->ds_is_snapshot) {
|
||||
stat->dds_is_snapshot = B_TRUE;
|
||||
@@ -2891,28 +2986,11 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
|
||||
}
|
||||
|
||||
/* must not have any bookmarks after the most recent snapshot */
|
||||
nvlist_t *proprequest = fnvlist_alloc();
|
||||
fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
nvlist_t *bookmarks = fnvlist_alloc();
|
||||
error = dsl_get_bookmarks_impl(ds, proprequest, bookmarks);
|
||||
fnvlist_free(proprequest);
|
||||
if (error != 0) {
|
||||
if (dsl_bookmark_latest_txg(ds) >
|
||||
dsl_dataset_phys(ds)->ds_prev_snap_txg) {
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (error);
|
||||
return (SET_ERROR(EEXIST));
|
||||
}
|
||||
for (nvpair_t *pair = nvlist_next_nvpair(bookmarks, NULL);
|
||||
pair != NULL; pair = nvlist_next_nvpair(bookmarks, pair)) {
|
||||
nvlist_t *valuenv =
|
||||
fnvlist_lookup_nvlist(fnvpair_value_nvlist(pair),
|
||||
zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||
uint64_t createtxg = fnvlist_lookup_uint64(valuenv, "value");
|
||||
if (createtxg > dsl_dataset_phys(ds)->ds_prev_snap_txg) {
|
||||
fnvlist_free(bookmarks);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
return (SET_ERROR(EEXIST));
|
||||
}
|
||||
}
|
||||
fnvlist_free(bookmarks);
|
||||
|
||||
error = dsl_dataset_handoff_check(ds, ddra->ddra_owner, tx);
|
||||
if (error != 0) {
|
||||
@@ -3025,7 +3103,7 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx)
|
||||
dsl_pool_t *dp = dmu_tx_pool(tx);
|
||||
dsl_dataset_t *hds;
|
||||
struct promotenode *snap;
|
||||
dsl_dataset_t *origin_ds;
|
||||
dsl_dataset_t *origin_ds, *origin_head;
|
||||
int err;
|
||||
uint64_t unused;
|
||||
uint64_t ss_mv_cnt;
|
||||
@@ -3045,6 +3123,7 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx)
|
||||
}
|
||||
|
||||
snap = list_head(&ddpa->shared_snaps);
|
||||
origin_head = snap->ds;
|
||||
if (snap == NULL) {
|
||||
err = SET_ERROR(ENOENT);
|
||||
goto out;
|
||||
@@ -3141,6 +3220,32 @@ dsl_dataset_promote_check(void *arg, dmu_tx_t *tx)
|
||||
ddpa->uncomp += dluncomp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that bookmarks that are being transferred don't have
|
||||
* name conflicts.
|
||||
*/
|
||||
for (dsl_bookmark_node_t *dbn = avl_first(&origin_head->ds_bookmarks);
|
||||
dbn != NULL && dbn->dbn_phys.zbm_creation_txg <=
|
||||
dsl_dataset_phys(origin_ds)->ds_creation_txg;
|
||||
dbn = AVL_NEXT(&origin_head->ds_bookmarks, dbn)) {
|
||||
if (strlen(dbn->dbn_name) >= max_snap_len) {
|
||||
err = SET_ERROR(ENAMETOOLONG);
|
||||
goto out;
|
||||
}
|
||||
zfs_bookmark_phys_t bm;
|
||||
err = dsl_bookmark_lookup_impl(ddpa->ddpa_clone,
|
||||
dbn->dbn_name, &bm);
|
||||
|
||||
if (err == 0) {
|
||||
fnvlist_add_boolean(ddpa->err_ds, dbn->dbn_name);
|
||||
conflicting_snaps = B_TRUE;
|
||||
} else if (err == ESRCH) {
|
||||
err = 0;
|
||||
} else if (err != 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In order to return the full list of conflicting snapshots, we check
|
||||
* whether there was a conflict after traversing all of them.
|
||||
@@ -3298,6 +3403,25 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx)
|
||||
dsl_dir_phys(dd)->dd_clones, origin_head->ds_object, tx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Move bookmarks to this dir.
|
||||
*/
|
||||
dsl_bookmark_node_t *dbn_next;
|
||||
for (dsl_bookmark_node_t *dbn = avl_first(&origin_head->ds_bookmarks);
|
||||
dbn != NULL && dbn->dbn_phys.zbm_creation_txg <=
|
||||
dsl_dataset_phys(origin_ds)->ds_creation_txg;
|
||||
dbn = dbn_next) {
|
||||
dbn_next = AVL_NEXT(&origin_head->ds_bookmarks, dbn);
|
||||
|
||||
avl_remove(&origin_head->ds_bookmarks, dbn);
|
||||
VERIFY0(zap_remove(dp->dp_meta_objset,
|
||||
origin_head->ds_bookmarks_obj, dbn->dbn_name, tx));
|
||||
|
||||
dsl_bookmark_node_add(hds, dbn, tx);
|
||||
}
|
||||
|
||||
dsl_bookmark_next_changed(hds, origin_ds, tx);
|
||||
|
||||
/* move snapshots to this dir */
|
||||
for (snap = list_head(&ddpa->shared_snaps); snap;
|
||||
snap = list_next(&ddpa->shared_snaps, snap)) {
|
||||
@@ -3758,9 +3882,9 @@ dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone,
|
||||
dsl_dataset_phys(clone)->ds_unique_bytes);
|
||||
|
||||
/*
|
||||
* Reset origin's unique bytes, if it exists.
|
||||
* Reset origin's unique bytes.
|
||||
*/
|
||||
if (clone->ds_prev) {
|
||||
{
|
||||
dsl_dataset_t *origin = clone->ds_prev;
|
||||
uint64_t comp, uncomp;
|
||||
|
||||
@@ -3858,6 +3982,12 @@ dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone,
|
||||
dsl_dataset_phys(origin_head)->ds_deadlist_obj);
|
||||
dsl_dataset_swap_remap_deadlists(clone, origin_head, tx);
|
||||
|
||||
/*
|
||||
* If there is a bookmark at the origin, its "next dataset" is
|
||||
* changing, so we need to reset its FBN.
|
||||
*/
|
||||
dsl_bookmark_next_changed(origin_head, origin_head->ds_prev, tx);
|
||||
|
||||
dsl_scan_ds_clone_swapped(origin_head, clone, tx);
|
||||
|
||||
spa_history_log_internal_ds(clone, "clone swap", tx,
|
||||
@@ -4148,93 +4278,143 @@ dsl_dataset_set_refreservation(const char *dsname, zprop_source_t source,
|
||||
}
|
||||
|
||||
/*
|
||||
* Return (in *usedp) the amount of space written in new that is not
|
||||
* present in oldsnap. New may be a snapshot or the head. Old must be
|
||||
* a snapshot before new, in new's filesystem (or its origin). If not then
|
||||
* fail and return EINVAL.
|
||||
* Return (in *usedp) the amount of space referenced by "new" that was not
|
||||
* referenced at the time the bookmark corresponds to. "New" may be a
|
||||
* snapshot or a head. The bookmark must be before new, in
|
||||
* new's filesystem (or its origin) -- caller verifies this.
|
||||
*
|
||||
* The written space is calculated by considering two components: First, we
|
||||
* ignore any freed space, and calculate the written as new's used space
|
||||
* minus old's used space. Next, we add in the amount of space that was freed
|
||||
* between the two snapshots, thus reducing new's used space relative to old's.
|
||||
* Specifically, this is the space that was born before old->ds_creation_txg,
|
||||
* and freed before new (ie. on new's deadlist or a previous deadlist).
|
||||
* between the two time points, thus reducing new's used space relative to
|
||||
* old's. Specifically, this is the space that was born before
|
||||
* zbm_creation_txg, and freed before new (ie. on new's deadlist or a
|
||||
* previous deadlist).
|
||||
*
|
||||
* space freed [---------------------]
|
||||
* snapshots ---O-------O--------O-------O------
|
||||
* oldsnap new
|
||||
* bookmark new
|
||||
*
|
||||
* Note, the bookmark's zbm_*_bytes_refd must be valid, but if the HAS_FBN
|
||||
* flag is not set, we will calculate the freed_before_next based on the
|
||||
* next snapshot's deadlist, rather than using zbm_*_freed_before_next_snap.
|
||||
*/
|
||||
static int
|
||||
dsl_dataset_space_written_impl(zfs_bookmark_phys_t *bmp,
|
||||
dsl_dataset_t *new, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
|
||||
{
|
||||
int err = 0;
|
||||
dsl_pool_t *dp = new->ds_dir->dd_pool;
|
||||
|
||||
ASSERT(dsl_pool_config_held(dp));
|
||||
if (dsl_dataset_is_snapshot(new)) {
|
||||
ASSERT3U(bmp->zbm_creation_txg, <,
|
||||
dsl_dataset_phys(new)->ds_creation_txg);
|
||||
}
|
||||
|
||||
*usedp = 0;
|
||||
*usedp += dsl_dataset_phys(new)->ds_referenced_bytes;
|
||||
*usedp -= bmp->zbm_referenced_bytes_refd;
|
||||
|
||||
*compp = 0;
|
||||
*compp += dsl_dataset_phys(new)->ds_compressed_bytes;
|
||||
*compp -= bmp->zbm_compressed_bytes_refd;
|
||||
|
||||
*uncompp = 0;
|
||||
*uncompp += dsl_dataset_phys(new)->ds_uncompressed_bytes;
|
||||
*uncompp -= bmp->zbm_uncompressed_bytes_refd;
|
||||
|
||||
dsl_dataset_t *snap = new;
|
||||
|
||||
while (dsl_dataset_phys(snap)->ds_prev_snap_txg >
|
||||
bmp->zbm_creation_txg) {
|
||||
uint64_t used, comp, uncomp;
|
||||
|
||||
dsl_deadlist_space_range(&snap->ds_deadlist,
|
||||
0, bmp->zbm_creation_txg,
|
||||
&used, &comp, &uncomp);
|
||||
*usedp += used;
|
||||
*compp += comp;
|
||||
*uncompp += uncomp;
|
||||
|
||||
uint64_t snapobj = dsl_dataset_phys(snap)->ds_prev_snap_obj;
|
||||
if (snap != new)
|
||||
dsl_dataset_rele(snap, FTAG);
|
||||
err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &snap);
|
||||
if (err != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We might not have the FBN if we are calculating written from
|
||||
* a snapshot (because we didn't know the correct "next" snapshot
|
||||
* until now).
|
||||
*/
|
||||
if (bmp->zbm_flags & ZBM_FLAG_HAS_FBN) {
|
||||
*usedp += bmp->zbm_referenced_freed_before_next_snap;
|
||||
*compp += bmp->zbm_compressed_freed_before_next_snap;
|
||||
*uncompp += bmp->zbm_uncompressed_freed_before_next_snap;
|
||||
} else {
|
||||
ASSERT3U(dsl_dataset_phys(snap)->ds_prev_snap_txg, ==,
|
||||
bmp->zbm_creation_txg);
|
||||
uint64_t used, comp, uncomp;
|
||||
dsl_deadlist_space(&snap->ds_deadlist, &used, &comp, &uncomp);
|
||||
*usedp += used;
|
||||
*compp += comp;
|
||||
*uncompp += uncomp;
|
||||
}
|
||||
if (snap != new)
|
||||
dsl_dataset_rele(snap, FTAG);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return (in *usedp) the amount of space written in new that was not
|
||||
* present at the time the bookmark corresponds to. New may be a
|
||||
* snapshot or the head. Old must be a bookmark before new, in
|
||||
* new's filesystem (or its origin) -- caller verifies this.
|
||||
*/
|
||||
int
|
||||
dsl_dataset_space_written_bookmark(zfs_bookmark_phys_t *bmp,
|
||||
dsl_dataset_t *new, uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
|
||||
{
|
||||
if (!(bmp->zbm_flags & ZBM_FLAG_HAS_FBN))
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
return (dsl_dataset_space_written_impl(bmp, new,
|
||||
usedp, compp, uncompp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return (in *usedp) the amount of space written in new that is not
|
||||
* present in oldsnap. New may be a snapshot or the head. Old must be
|
||||
* a snapshot before new, in new's filesystem (or its origin). If not then
|
||||
* fail and return EINVAL.
|
||||
*/
|
||||
int
|
||||
dsl_dataset_space_written(dsl_dataset_t *oldsnap, dsl_dataset_t *new,
|
||||
uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
|
||||
{
|
||||
int err = 0;
|
||||
uint64_t snapobj;
|
||||
dsl_pool_t *dp = new->ds_dir->dd_pool;
|
||||
if (!dsl_dataset_is_before(new, oldsnap, 0))
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
ASSERT(dsl_pool_config_held(dp));
|
||||
zfs_bookmark_phys_t zbm = { 0 };
|
||||
dsl_dataset_phys_t *dsp = dsl_dataset_phys(oldsnap);
|
||||
zbm.zbm_guid = dsp->ds_guid;
|
||||
zbm.zbm_creation_txg = dsp->ds_creation_txg;
|
||||
zbm.zbm_creation_time = dsp->ds_creation_time;
|
||||
zbm.zbm_referenced_bytes_refd = dsp->ds_referenced_bytes;
|
||||
zbm.zbm_compressed_bytes_refd = dsp->ds_compressed_bytes;
|
||||
zbm.zbm_uncompressed_bytes_refd = dsp->ds_uncompressed_bytes;
|
||||
|
||||
*usedp = 0;
|
||||
*usedp += dsl_dataset_phys(new)->ds_referenced_bytes;
|
||||
*usedp -= dsl_dataset_phys(oldsnap)->ds_referenced_bytes;
|
||||
|
||||
*compp = 0;
|
||||
*compp += dsl_dataset_phys(new)->ds_compressed_bytes;
|
||||
*compp -= dsl_dataset_phys(oldsnap)->ds_compressed_bytes;
|
||||
|
||||
*uncompp = 0;
|
||||
*uncompp += dsl_dataset_phys(new)->ds_uncompressed_bytes;
|
||||
*uncompp -= dsl_dataset_phys(oldsnap)->ds_uncompressed_bytes;
|
||||
|
||||
snapobj = new->ds_object;
|
||||
while (snapobj != oldsnap->ds_object) {
|
||||
dsl_dataset_t *snap;
|
||||
uint64_t used, comp, uncomp;
|
||||
|
||||
if (snapobj == new->ds_object) {
|
||||
snap = new;
|
||||
} else {
|
||||
err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &snap);
|
||||
if (err != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dsl_dataset_phys(snap)->ds_prev_snap_txg ==
|
||||
dsl_dataset_phys(oldsnap)->ds_creation_txg) {
|
||||
/*
|
||||
* The blocks in the deadlist can not be born after
|
||||
* ds_prev_snap_txg, so get the whole deadlist space,
|
||||
* which is more efficient (especially for old-format
|
||||
* deadlists). Unfortunately the deadlist code
|
||||
* doesn't have enough information to make this
|
||||
* optimization itself.
|
||||
*/
|
||||
dsl_deadlist_space(&snap->ds_deadlist,
|
||||
&used, &comp, &uncomp);
|
||||
} else {
|
||||
dsl_deadlist_space_range(&snap->ds_deadlist,
|
||||
0, dsl_dataset_phys(oldsnap)->ds_creation_txg,
|
||||
&used, &comp, &uncomp);
|
||||
}
|
||||
*usedp += used;
|
||||
*compp += comp;
|
||||
*uncompp += uncomp;
|
||||
|
||||
/*
|
||||
* If we get to the beginning of the chain of snapshots
|
||||
* (ds_prev_snap_obj == 0) before oldsnap, then oldsnap
|
||||
* was not a snapshot of/before new.
|
||||
*/
|
||||
snapobj = dsl_dataset_phys(snap)->ds_prev_snap_obj;
|
||||
if (snap != new)
|
||||
dsl_dataset_rele(snap, FTAG);
|
||||
if (snapobj == 0) {
|
||||
err = SET_ERROR(EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return (err);
|
||||
/*
|
||||
* If oldsnap is the origin (or origin's origin, ...) of new,
|
||||
* we can't easily calculate the effective FBN. Therefore,
|
||||
* we do not set ZBM_FLAG_HAS_FBN, so that the _impl will calculate
|
||||
* it relative to the correct "next": the next snapshot towards "new",
|
||||
* rather than the next snapshot in oldsnap's dsl_dir.
|
||||
*/
|
||||
return (dsl_dataset_space_written_impl(&zbm, new,
|
||||
usedp, compp, uncompp));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4327,16 +4507,26 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier,
|
||||
|
||||
if (later->ds_dir == earlier->ds_dir)
|
||||
return (B_TRUE);
|
||||
if (!dsl_dir_is_clone(later->ds_dir))
|
||||
|
||||
/*
|
||||
* We check dd_origin_obj explicitly here rather than using
|
||||
* dsl_dir_is_clone() so that we will return TRUE if "earlier"
|
||||
* is $ORIGIN@$ORIGIN. dsl_dataset_space_written() depends on
|
||||
* this behavior.
|
||||
*/
|
||||
if (dsl_dir_phys(later->ds_dir)->dd_origin_obj == 0)
|
||||
return (B_FALSE);
|
||||
|
||||
if (dsl_dir_phys(later->ds_dir)->dd_origin_obj == earlier->ds_object)
|
||||
return (B_TRUE);
|
||||
dsl_dataset_t *origin;
|
||||
error = dsl_dataset_hold_obj(dp,
|
||||
dsl_dir_phys(later->ds_dir)->dd_origin_obj, FTAG, &origin);
|
||||
if (error != 0)
|
||||
return (B_FALSE);
|
||||
if (dsl_dataset_phys(origin)->ds_creation_txg == earlier_txg &&
|
||||
origin->ds_dir == earlier->ds_dir) {
|
||||
dsl_dataset_rele(origin, FTAG);
|
||||
return (B_TRUE);
|
||||
}
|
||||
ret = dsl_dataset_is_before(origin, earlier, earlier_txg);
|
||||
dsl_dataset_rele(origin, FTAG);
|
||||
return (ret);
|
||||
@@ -4453,6 +4643,26 @@ dsl_dataset_create_remap_deadlist(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||
spa_feature_incr(spa, SPA_FEATURE_OBSOLETE_COUNTS, tx);
|
||||
}
|
||||
|
||||
void
|
||||
dsl_dataset_activate_redaction(dsl_dataset_t *ds, uint64_t *redact_snaps,
|
||||
uint64_t num_redact_snaps, dmu_tx_t *tx)
|
||||
{
|
||||
uint64_t dsobj = ds->ds_object;
|
||||
struct feature_type_uint64_array_arg *ftuaa =
|
||||
kmem_zalloc(sizeof (*ftuaa), KM_SLEEP);
|
||||
ftuaa->length = (int64_t)num_redact_snaps;
|
||||
if (num_redact_snaps > 0) {
|
||||
ftuaa->array = kmem_alloc(num_redact_snaps * sizeof (uint64_t),
|
||||
KM_SLEEP);
|
||||
bcopy(redact_snaps, ftuaa->array, num_redact_snaps *
|
||||
sizeof (uint64_t));
|
||||
}
|
||||
dsl_dataset_activate_feature(dsobj, SPA_FEATURE_REDACTED_DATASETS,
|
||||
ftuaa, tx);
|
||||
ds->ds_feature[SPA_FEATURE_REDACTED_DATASETS] = ftuaa;
|
||||
}
|
||||
|
||||
|
||||
#if defined(_KERNEL)
|
||||
#if defined(_LP64)
|
||||
module_param(zfs_max_recordsize, int, 0644);
|
||||
@@ -4463,6 +4673,10 @@ module_param(zfs_max_recordsize, int, 0444);
|
||||
MODULE_PARM_DESC(zfs_max_recordsize, "Max allowed record size");
|
||||
#endif
|
||||
|
||||
module_param(zfs_allow_redacted_dataset_mount, int, 0644);
|
||||
MODULE_PARM_DESC(zfs_allow_redacted_dataset_mount,
|
||||
"Allow mounting of redacted datasets");
|
||||
|
||||
EXPORT_SYMBOL(dsl_dataset_hold);
|
||||
EXPORT_SYMBOL(dsl_dataset_hold_flags);
|
||||
EXPORT_SYMBOL(dsl_dataset_hold_obj);
|
||||
|
||||
+37
-31
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||
*/
|
||||
|
||||
@@ -80,7 +80,7 @@ dsl_deadlist_load_tree(dsl_deadlist_t *dl)
|
||||
zap_cursor_advance(&zc)) {
|
||||
dsl_deadlist_entry_t *dle = kmem_alloc(sizeof (*dle), KM_SLEEP);
|
||||
dle->dle_mintxg = zfs_strtonum(za.za_name, NULL);
|
||||
VERIFY3U(0, ==, bpobj_open(&dle->dle_bpobj, dl->dl_os,
|
||||
VERIFY0(bpobj_open(&dle->dle_bpobj, dl->dl_os,
|
||||
za.za_first_integer));
|
||||
avl_add(&dl->dl_tree, dle);
|
||||
}
|
||||
@@ -98,13 +98,13 @@ dsl_deadlist_open(dsl_deadlist_t *dl, objset_t *os, uint64_t object)
|
||||
mutex_init(&dl->dl_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
dl->dl_os = os;
|
||||
dl->dl_object = object;
|
||||
VERIFY3U(0, ==, dmu_bonus_hold(os, object, dl, &dl->dl_dbuf));
|
||||
VERIFY0(dmu_bonus_hold(os, object, dl, &dl->dl_dbuf));
|
||||
dmu_object_info_from_db(dl->dl_dbuf, &doi);
|
||||
if (doi.doi_type == DMU_OT_BPOBJ) {
|
||||
dmu_buf_rele(dl->dl_dbuf, dl);
|
||||
dl->dl_dbuf = NULL;
|
||||
dl->dl_oldfmt = B_TRUE;
|
||||
VERIFY3U(0, ==, bpobj_open(&dl->dl_bpobj, os, object));
|
||||
VERIFY0(bpobj_open(&dl->dl_bpobj, os, object));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ dsl_deadlist_free(objset_t *os, uint64_t dlobj, dmu_tx_t *tx)
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
|
||||
VERIFY3U(0, ==, dmu_object_info(os, dlobj, &doi));
|
||||
VERIFY0(dmu_object_info(os, dlobj, &doi));
|
||||
if (doi.doi_type == DMU_OT_BPOBJ) {
|
||||
bpobj_free(os, dlobj, tx);
|
||||
return;
|
||||
@@ -183,7 +183,7 @@ dsl_deadlist_free(objset_t *os, uint64_t dlobj, dmu_tx_t *tx)
|
||||
bpobj_free(os, obj, tx);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
VERIFY3U(0, ==, dmu_object_free(os, dlobj, tx));
|
||||
VERIFY0(dmu_object_free(os, dlobj, tx));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -196,8 +196,8 @@ dle_enqueue(dsl_deadlist_t *dl, dsl_deadlist_entry_t *dle,
|
||||
uint64_t obj = bpobj_alloc(dl->dl_os, SPA_OLD_MAXBLOCKSIZE, tx);
|
||||
bpobj_close(&dle->dle_bpobj);
|
||||
bpobj_decr_empty(dl->dl_os, tx);
|
||||
VERIFY3U(0, ==, bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
VERIFY3U(0, ==, zap_update_int_key(dl->dl_os, dl->dl_object,
|
||||
VERIFY0(bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
VERIFY0(zap_update_int_key(dl->dl_os, dl->dl_object,
|
||||
dle->dle_mintxg, obj, tx));
|
||||
}
|
||||
bpobj_enqueue(&dle->dle_bpobj, bp, tx);
|
||||
@@ -214,8 +214,8 @@ dle_enqueue_subobj(dsl_deadlist_t *dl, dsl_deadlist_entry_t *dle,
|
||||
} else {
|
||||
bpobj_close(&dle->dle_bpobj);
|
||||
bpobj_decr_empty(dl->dl_os, tx);
|
||||
VERIFY3U(0, ==, bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
VERIFY3U(0, ==, zap_update_int_key(dl->dl_os, dl->dl_object,
|
||||
VERIFY0(bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
VERIFY0(zap_update_int_key(dl->dl_os, dl->dl_object,
|
||||
dle->dle_mintxg, obj, tx));
|
||||
}
|
||||
}
|
||||
@@ -279,10 +279,10 @@ dsl_deadlist_add_key(dsl_deadlist_t *dl, uint64_t mintxg, dmu_tx_t *tx)
|
||||
dsl_deadlist_load_tree(dl);
|
||||
|
||||
obj = bpobj_alloc_empty(dl->dl_os, SPA_OLD_MAXBLOCKSIZE, tx);
|
||||
VERIFY3U(0, ==, bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
VERIFY0(bpobj_open(&dle->dle_bpobj, dl->dl_os, obj));
|
||||
avl_add(&dl->dl_tree, dle);
|
||||
|
||||
VERIFY3U(0, ==, zap_add_int_key(dl->dl_os, dl->dl_object,
|
||||
VERIFY0(zap_add_int_key(dl->dl_os, dl->dl_object,
|
||||
mintxg, obj, tx));
|
||||
mutex_exit(&dl->dl_lock);
|
||||
}
|
||||
@@ -298,12 +298,12 @@ dsl_deadlist_remove_key(dsl_deadlist_t *dl, uint64_t mintxg, dmu_tx_t *tx)
|
||||
|
||||
if (dl->dl_oldfmt)
|
||||
return;
|
||||
|
||||
mutex_enter(&dl->dl_lock);
|
||||
dsl_deadlist_load_tree(dl);
|
||||
|
||||
dle_tofind.dle_mintxg = mintxg;
|
||||
dle = avl_find(&dl->dl_tree, &dle_tofind, NULL);
|
||||
ASSERT3P(dle, !=, NULL);
|
||||
dle_prev = AVL_PREV(&dl->dl_tree, dle);
|
||||
|
||||
dle_enqueue_subobj(dl, dle_prev, dle->dle_bpobj.bpo_object, tx);
|
||||
@@ -312,7 +312,7 @@ dsl_deadlist_remove_key(dsl_deadlist_t *dl, uint64_t mintxg, dmu_tx_t *tx)
|
||||
bpobj_close(&dle->dle_bpobj);
|
||||
kmem_free(dle, sizeof (*dle));
|
||||
|
||||
VERIFY3U(0, ==, zap_remove_int(dl->dl_os, dl->dl_object, mintxg, tx));
|
||||
VERIFY0(zap_remove_int(dl->dl_os, dl->dl_object, mintxg, tx));
|
||||
mutex_exit(&dl->dl_lock);
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ dsl_deadlist_regenerate(objset_t *os, uint64_t dlobj,
|
||||
|
||||
while (mrs_obj != 0) {
|
||||
dsl_dataset_t *ds;
|
||||
VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, mrs_obj, FTAG, &ds));
|
||||
VERIFY0(dsl_dataset_hold_obj(dp, mrs_obj, FTAG, &ds));
|
||||
dsl_deadlist_add_key(&dl,
|
||||
dsl_dataset_phys(ds)->ds_prev_snap_txg, tx);
|
||||
mrs_obj = dsl_dataset_phys(ds)->ds_prev_snap_obj;
|
||||
@@ -368,7 +368,7 @@ dsl_deadlist_clone(dsl_deadlist_t *dl, uint64_t maxtxg,
|
||||
break;
|
||||
|
||||
obj = bpobj_alloc_empty(dl->dl_os, SPA_OLD_MAXBLOCKSIZE, tx);
|
||||
VERIFY3U(0, ==, zap_add_int_key(dl->dl_os, newobj,
|
||||
VERIFY0(zap_add_int_key(dl->dl_os, newobj,
|
||||
dle->dle_mintxg, obj, tx));
|
||||
}
|
||||
mutex_exit(&dl->dl_lock);
|
||||
@@ -381,7 +381,7 @@ dsl_deadlist_space(dsl_deadlist_t *dl,
|
||||
{
|
||||
ASSERT(dsl_deadlist_is_open(dl));
|
||||
if (dl->dl_oldfmt) {
|
||||
VERIFY3U(0, ==, bpobj_space(&dl->dl_bpobj,
|
||||
VERIFY0(bpobj_space(&dl->dl_bpobj,
|
||||
usedp, compp, uncompp));
|
||||
return;
|
||||
}
|
||||
@@ -397,7 +397,7 @@ dsl_deadlist_space(dsl_deadlist_t *dl,
|
||||
* return space used in the range (mintxg, maxtxg].
|
||||
* Includes maxtxg, does not include mintxg.
|
||||
* mintxg and maxtxg must both be keys in the deadlist (unless maxtxg is
|
||||
* larger than any bp in the deadlist (eg. UINT64_MAX)).
|
||||
* UINT64_MAX).
|
||||
*/
|
||||
void
|
||||
dsl_deadlist_space_range(dsl_deadlist_t *dl, uint64_t mintxg, uint64_t maxtxg,
|
||||
@@ -408,7 +408,7 @@ dsl_deadlist_space_range(dsl_deadlist_t *dl, uint64_t mintxg, uint64_t maxtxg,
|
||||
avl_index_t where;
|
||||
|
||||
if (dl->dl_oldfmt) {
|
||||
VERIFY3U(0, ==, bpobj_space_range(&dl->dl_bpobj,
|
||||
VERIFY0(bpobj_space_range(&dl->dl_bpobj,
|
||||
mintxg, maxtxg, usedp, compp, uncompp));
|
||||
return;
|
||||
}
|
||||
@@ -430,13 +430,20 @@ dsl_deadlist_space_range(dsl_deadlist_t *dl, uint64_t mintxg, uint64_t maxtxg,
|
||||
dle = AVL_NEXT(&dl->dl_tree, dle)) {
|
||||
uint64_t used, comp, uncomp;
|
||||
|
||||
VERIFY3U(0, ==, bpobj_space(&dle->dle_bpobj,
|
||||
VERIFY0(bpobj_space(&dle->dle_bpobj,
|
||||
&used, &comp, &uncomp));
|
||||
|
||||
*usedp += used;
|
||||
*compp += comp;
|
||||
*uncompp += uncomp;
|
||||
}
|
||||
|
||||
/*
|
||||
* This assertion ensures that the maxtxg is a key in the deadlist
|
||||
* (unless it's UINT64_MAX).
|
||||
*/
|
||||
ASSERT(maxtxg == UINT64_MAX ||
|
||||
(dle != NULL && dle->dle_mintxg == maxtxg));
|
||||
mutex_exit(&dl->dl_lock);
|
||||
}
|
||||
|
||||
@@ -452,8 +459,8 @@ dsl_deadlist_insert_bpobj(dsl_deadlist_t *dl, uint64_t obj, uint64_t birth,
|
||||
|
||||
ASSERT(MUTEX_HELD(&dl->dl_lock));
|
||||
|
||||
VERIFY3U(0, ==, bpobj_open(&bpo, dl->dl_os, obj));
|
||||
VERIFY3U(0, ==, bpobj_space(&bpo, &used, &comp, &uncomp));
|
||||
VERIFY0(bpobj_open(&bpo, dl->dl_os, obj));
|
||||
VERIFY0(bpobj_space(&bpo, &used, &comp, &uncomp));
|
||||
bpobj_close(&bpo);
|
||||
|
||||
dsl_deadlist_load_tree(dl);
|
||||
@@ -491,12 +498,11 @@ dsl_deadlist_merge(dsl_deadlist_t *dl, uint64_t obj, dmu_tx_t *tx)
|
||||
dsl_deadlist_phys_t *dlp;
|
||||
dmu_object_info_t doi;
|
||||
|
||||
VERIFY3U(0, ==, dmu_object_info(dl->dl_os, obj, &doi));
|
||||
VERIFY0(dmu_object_info(dl->dl_os, obj, &doi));
|
||||
if (doi.doi_type == DMU_OT_BPOBJ) {
|
||||
bpobj_t bpo;
|
||||
VERIFY3U(0, ==, bpobj_open(&bpo, dl->dl_os, obj));
|
||||
VERIFY3U(0, ==, bpobj_iterate(&bpo,
|
||||
dsl_deadlist_insert_cb, dl, tx));
|
||||
VERIFY0(bpobj_open(&bpo, dl->dl_os, obj));
|
||||
VERIFY0(bpobj_iterate(&bpo, dsl_deadlist_insert_cb, dl, tx));
|
||||
bpobj_close(&bpo);
|
||||
return;
|
||||
}
|
||||
@@ -507,11 +513,11 @@ dsl_deadlist_merge(dsl_deadlist_t *dl, uint64_t obj, dmu_tx_t *tx)
|
||||
zap_cursor_advance(&zc)) {
|
||||
uint64_t mintxg = zfs_strtonum(za.za_name, NULL);
|
||||
dsl_deadlist_insert_bpobj(dl, za.za_first_integer, mintxg, tx);
|
||||
VERIFY3U(0, ==, zap_remove_int(dl->dl_os, obj, mintxg, tx));
|
||||
VERIFY0(zap_remove_int(dl->dl_os, obj, mintxg, tx));
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
|
||||
VERIFY3U(0, ==, dmu_bonus_hold(dl->dl_os, obj, FTAG, &bonus));
|
||||
VERIFY0(dmu_bonus_hold(dl->dl_os, obj, FTAG, &bonus));
|
||||
dlp = bonus->db_data;
|
||||
dmu_buf_will_dirty(bonus, tx);
|
||||
bzero(dlp, sizeof (*dlp));
|
||||
@@ -520,7 +526,7 @@ dsl_deadlist_merge(dsl_deadlist_t *dl, uint64_t obj, dmu_tx_t *tx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove entries on dl that are >= mintxg, and put them on the bpobj.
|
||||
* Remove entries on dl that are born > mintxg, and put them on the bpobj.
|
||||
*/
|
||||
void
|
||||
dsl_deadlist_move_bpobj(dsl_deadlist_t *dl, bpobj_t *bpo, uint64_t mintxg,
|
||||
@@ -546,7 +552,7 @@ dsl_deadlist_move_bpobj(dsl_deadlist_t *dl, bpobj_t *bpo, uint64_t mintxg,
|
||||
|
||||
bpobj_enqueue_subobj(bpo, dle->dle_bpobj.bpo_object, tx);
|
||||
|
||||
VERIFY3U(0, ==, bpobj_space(&dle->dle_bpobj,
|
||||
VERIFY0(bpobj_space(&dle->dle_bpobj,
|
||||
&used, &comp, &uncomp));
|
||||
ASSERT3U(dl->dl_phys->dl_used, >=, used);
|
||||
ASSERT3U(dl->dl_phys->dl_comp, >=, comp);
|
||||
@@ -555,7 +561,7 @@ dsl_deadlist_move_bpobj(dsl_deadlist_t *dl, bpobj_t *bpo, uint64_t mintxg,
|
||||
dl->dl_phys->dl_comp -= comp;
|
||||
dl->dl_phys->dl_uncomp -= uncomp;
|
||||
|
||||
VERIFY3U(0, ==, zap_remove_int(dl->dl_os, dl->dl_object,
|
||||
VERIFY0(zap_remove_int(dl->dl_os, dl->dl_object,
|
||||
dle->dle_mintxg, tx));
|
||||
|
||||
dle_next = AVL_NEXT(&dl->dl_tree, dle);
|
||||
|
||||
+107
-61
@@ -31,6 +31,7 @@
|
||||
#include <sys/dsl_dataset.h>
|
||||
#include <sys/dsl_synctask.h>
|
||||
#include <sys/dsl_destroy.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/dsl_pool.h>
|
||||
#include <sys/dsl_dir.h>
|
||||
@@ -181,70 +182,86 @@ process_old_deadlist(dsl_dataset_t *ds, dsl_dataset_t *ds_prev,
|
||||
dsl_dataset_phys(ds_next)->ds_deadlist_obj);
|
||||
}
|
||||
|
||||
struct removeclonesnode {
|
||||
list_node_t link;
|
||||
dsl_dataset_t *ds;
|
||||
};
|
||||
typedef struct remaining_clones_key {
|
||||
dsl_dataset_t *rck_clone;
|
||||
list_node_t rck_node;
|
||||
} remaining_clones_key_t;
|
||||
|
||||
static remaining_clones_key_t *
|
||||
rck_alloc(dsl_dataset_t *clone)
|
||||
{
|
||||
remaining_clones_key_t *rck = kmem_alloc(sizeof (*rck), KM_SLEEP);
|
||||
rck->rck_clone = clone;
|
||||
return (rck);
|
||||
}
|
||||
|
||||
static void
|
||||
dsl_dataset_remove_clones_key(dsl_dataset_t *ds, uint64_t mintxg, dmu_tx_t *tx)
|
||||
dsl_dir_remove_clones_key_impl(dsl_dir_t *dd, uint64_t mintxg, dmu_tx_t *tx,
|
||||
list_t *stack, void *tag)
|
||||
{
|
||||
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
|
||||
list_t clones;
|
||||
struct removeclonesnode *rcn;
|
||||
objset_t *mos = dd->dd_pool->dp_meta_objset;
|
||||
|
||||
list_create(&clones, sizeof (struct removeclonesnode),
|
||||
offsetof(struct removeclonesnode, link));
|
||||
/*
|
||||
* If it is the old version, dd_clones doesn't exist so we can't
|
||||
* find the clones, but dsl_deadlist_remove_key() is a no-op so it
|
||||
* doesn't matter.
|
||||
*/
|
||||
if (dsl_dir_phys(dd)->dd_clones == 0)
|
||||
return;
|
||||
|
||||
rcn = kmem_zalloc(sizeof (struct removeclonesnode), KM_SLEEP);
|
||||
rcn->ds = ds;
|
||||
list_insert_head(&clones, rcn);
|
||||
zap_cursor_t *zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP);
|
||||
zap_attribute_t *za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
|
||||
|
||||
for (; rcn != NULL; rcn = list_next(&clones, rcn)) {
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
/*
|
||||
* If it is the old version, dd_clones doesn't exist so we can't
|
||||
* find the clones, but dsl_deadlist_remove_key() is a no-op so
|
||||
* it doesn't matter.
|
||||
*/
|
||||
if (dsl_dir_phys(rcn->ds->ds_dir)->dd_clones == 0)
|
||||
continue;
|
||||
for (zap_cursor_init(zc, mos, dsl_dir_phys(dd)->dd_clones);
|
||||
zap_cursor_retrieve(zc, za) == 0;
|
||||
zap_cursor_advance(zc)) {
|
||||
dsl_dataset_t *clone;
|
||||
|
||||
for (zap_cursor_init(&zc, mos,
|
||||
dsl_dir_phys(rcn->ds->ds_dir)->dd_clones);
|
||||
zap_cursor_retrieve(&zc, &za) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
dsl_dataset_t *clone;
|
||||
VERIFY0(dsl_dataset_hold_obj(dd->dd_pool,
|
||||
za->za_first_integer, tag, &clone));
|
||||
|
||||
VERIFY0(dsl_dataset_hold_obj(rcn->ds->ds_dir->dd_pool,
|
||||
za.za_first_integer, FTAG, &clone));
|
||||
if (clone->ds_dir->dd_origin_txg > mintxg) {
|
||||
dsl_deadlist_remove_key(&clone->ds_deadlist,
|
||||
mintxg, tx);
|
||||
if (dsl_dataset_remap_deadlist_exists(clone)) {
|
||||
dsl_deadlist_remove_key(
|
||||
&clone->ds_remap_deadlist, mintxg,
|
||||
tx);
|
||||
}
|
||||
rcn = kmem_zalloc(
|
||||
sizeof (struct removeclonesnode), KM_SLEEP);
|
||||
rcn->ds = clone;
|
||||
list_insert_tail(&clones, rcn);
|
||||
} else {
|
||||
dsl_dataset_rele(clone, FTAG);
|
||||
if (clone->ds_dir->dd_origin_txg > mintxg) {
|
||||
dsl_deadlist_remove_key(&clone->ds_deadlist,
|
||||
mintxg, tx);
|
||||
|
||||
if (dsl_dataset_remap_deadlist_exists(clone)) {
|
||||
dsl_deadlist_remove_key(
|
||||
&clone->ds_remap_deadlist, mintxg, tx);
|
||||
}
|
||||
|
||||
list_insert_head(stack, rck_alloc(clone));
|
||||
} else {
|
||||
dsl_dataset_rele(clone, tag);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
}
|
||||
zap_cursor_fini(zc);
|
||||
|
||||
kmem_free(za, sizeof (zap_attribute_t));
|
||||
kmem_free(zc, sizeof (zap_cursor_t));
|
||||
}
|
||||
|
||||
void
|
||||
dsl_dir_remove_clones_key(dsl_dir_t *top_dd, uint64_t mintxg, dmu_tx_t *tx)
|
||||
{
|
||||
list_t stack;
|
||||
|
||||
list_create(&stack, sizeof (remaining_clones_key_t),
|
||||
offsetof(remaining_clones_key_t, rck_node));
|
||||
|
||||
dsl_dir_remove_clones_key_impl(top_dd, mintxg, tx, &stack, FTAG);
|
||||
for (remaining_clones_key_t *rck = list_remove_head(&stack);
|
||||
rck != NULL; rck = list_remove_head(&stack)) {
|
||||
dsl_dataset_t *clone = rck->rck_clone;
|
||||
dsl_dir_t *clone_dir = clone->ds_dir;
|
||||
|
||||
kmem_free(rck, sizeof (*rck));
|
||||
|
||||
dsl_dir_remove_clones_key_impl(clone_dir, mintxg, tx,
|
||||
&stack, FTAG);
|
||||
dsl_dataset_rele(clone, FTAG);
|
||||
}
|
||||
|
||||
rcn = list_remove_head(&clones);
|
||||
kmem_free(rcn, sizeof (struct removeclonesnode));
|
||||
while ((rcn = list_remove_head(&clones)) != NULL) {
|
||||
dsl_dataset_rele(rcn->ds, FTAG);
|
||||
kmem_free(rcn, sizeof (struct removeclonesnode));
|
||||
}
|
||||
list_destroy(&clones);
|
||||
list_destroy(&stack);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -314,6 +331,8 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
|
||||
|
||||
obj = ds->ds_object;
|
||||
|
||||
boolean_t book_exists = dsl_bookmark_ds_destroyed(ds, tx);
|
||||
|
||||
for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
|
||||
if (dsl_dataset_feature_is_active(ds, f))
|
||||
dsl_dataset_deactivate_feature(ds, f, tx);
|
||||
@@ -400,9 +419,11 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
|
||||
|
||||
dsl_destroy_snapshot_handle_remaps(ds, ds_next, tx);
|
||||
|
||||
/* Collapse range in clone heads */
|
||||
dsl_dataset_remove_clones_key(ds,
|
||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||
if (!book_exists) {
|
||||
/* Collapse range in clone heads */
|
||||
dsl_dir_remove_clones_key(ds->ds_dir,
|
||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||
}
|
||||
|
||||
if (ds_next->ds_is_snapshot) {
|
||||
dsl_dataset_t *ds_nextnext;
|
||||
@@ -430,9 +451,13 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
|
||||
/* Collapse range in this head. */
|
||||
dsl_dataset_t *hds;
|
||||
VERIFY0(dsl_dataset_hold_obj(dp,
|
||||
dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj, FTAG, &hds));
|
||||
dsl_deadlist_remove_key(&hds->ds_deadlist,
|
||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||
dsl_dir_phys(ds->ds_dir)->dd_head_dataset_obj,
|
||||
FTAG, &hds));
|
||||
if (!book_exists) {
|
||||
/* Collapse range in this head. */
|
||||
dsl_deadlist_remove_key(&hds->ds_deadlist,
|
||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||
}
|
||||
if (dsl_dataset_remap_deadlist_exists(hds)) {
|
||||
dsl_deadlist_remove_key(&hds->ds_remap_deadlist,
|
||||
dsl_dataset_phys(ds)->ds_creation_txg, tx);
|
||||
@@ -675,7 +700,8 @@ kill_blkptr(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
struct killarg *ka = arg;
|
||||
dmu_tx_t *tx = ka->tx;
|
||||
|
||||
if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
|
||||
if (zb->zb_level == ZB_DNODE_LEVEL || BP_IS_HOLE(bp) ||
|
||||
BP_IS_EMBEDDED(bp))
|
||||
return (0);
|
||||
|
||||
if (zb->zb_level == ZB_ZIL_LEVEL) {
|
||||
@@ -973,8 +999,28 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
|
||||
VERIFY0(zap_destroy(mos,
|
||||
dsl_dataset_phys(ds)->ds_snapnames_zapobj, tx));
|
||||
|
||||
if (ds->ds_bookmarks != 0) {
|
||||
VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
|
||||
if (ds->ds_bookmarks_obj != 0) {
|
||||
void *cookie = NULL;
|
||||
dsl_bookmark_node_t *dbn;
|
||||
|
||||
while ((dbn = avl_destroy_nodes(&ds->ds_bookmarks, &cookie)) !=
|
||||
NULL) {
|
||||
if (dbn->dbn_phys.zbm_redaction_obj != 0) {
|
||||
VERIFY0(dmu_object_free(mos,
|
||||
dbn->dbn_phys.zbm_redaction_obj, tx));
|
||||
spa_feature_decr(dmu_objset_spa(mos),
|
||||
SPA_FEATURE_REDACTION_BOOKMARKS, tx);
|
||||
}
|
||||
if (dbn->dbn_phys.zbm_flags & ZBM_FLAG_HAS_FBN) {
|
||||
spa_feature_decr(dmu_objset_spa(mos),
|
||||
SPA_FEATURE_BOOKMARK_WRITTEN, tx);
|
||||
}
|
||||
spa_strfree(dbn->dbn_name);
|
||||
mutex_destroy(&dbn->dbn_lock);
|
||||
kmem_free(dbn, sizeof (*dbn));
|
||||
}
|
||||
avl_destroy(&ds->ds_bookmarks);
|
||||
VERIFY0(zap_destroy(mos, ds->ds_bookmarks_obj, tx));
|
||||
spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
|
||||
@@ -42,7 +42,6 @@
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/zfs_znode.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/dsl_deadlist.h>
|
||||
#include <sys/vdev_impl.h>
|
||||
#include <sys/metaslab_impl.h>
|
||||
#include <sys/bptree.h>
|
||||
|
||||
+12
-2
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
|
||||
* Copyright 2016 Gary Mills
|
||||
* Copyright (c) 2017 Datto Inc.
|
||||
* Copyright 2017 Joyent, Inc.
|
||||
@@ -1343,6 +1343,7 @@ dsl_scan_zil_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
|
||||
zil_header_t *zh = zsa->zsa_zh;
|
||||
zbookmark_phys_t zb;
|
||||
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
if (BP_IS_HOLE(bp) || bp->blk_birth <= scn->scn_phys.scn_cur_min_txg)
|
||||
return (0);
|
||||
|
||||
@@ -1375,6 +1376,7 @@ dsl_scan_zil_record(zilog_t *zilog, lr_t *lrc, void *arg, uint64_t claim_txg)
|
||||
blkptr_t *bp = &lr->lr_blkptr;
|
||||
zbookmark_phys_t zb;
|
||||
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
if (BP_IS_HOLE(bp) ||
|
||||
bp->blk_birth <= scn->scn_phys.scn_cur_min_txg)
|
||||
return (0);
|
||||
@@ -1519,7 +1521,7 @@ dsl_scan_prefetch(scan_prefetch_ctx_t *spc, blkptr_t *bp, zbookmark_phys_t *zb)
|
||||
spa_t *spa = scn->scn_dp->dp_spa;
|
||||
scan_prefetch_issue_ctx_t *spic;
|
||||
|
||||
if (zfs_no_scrub_prefetch)
|
||||
if (zfs_no_scrub_prefetch || BP_IS_REDACTED(bp))
|
||||
return;
|
||||
|
||||
if (BP_IS_HOLE(bp) || bp->blk_birth <= scn->scn_phys.scn_cur_min_txg ||
|
||||
@@ -1771,6 +1773,8 @@ dsl_scan_recurse(dsl_scan_t *scn, dsl_dataset_t *ds, dmu_objset_type_t ostype,
|
||||
int zio_flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SCAN_THREAD;
|
||||
int err;
|
||||
|
||||
ASSERT(!BP_IS_REDACTED(bp));
|
||||
|
||||
if (BP_GET_LEVEL(bp) > 0) {
|
||||
arc_flags_t flags = ARC_FLAG_WAIT;
|
||||
int i;
|
||||
@@ -1924,6 +1928,12 @@ dsl_scan_visitbp(blkptr_t *bp, const zbookmark_phys_t *zb,
|
||||
return;
|
||||
}
|
||||
|
||||
if (BP_IS_REDACTED(bp)) {
|
||||
ASSERT(dsl_dataset_feature_is_active(ds,
|
||||
SPA_FEATURE_REDACTED_DATASETS));
|
||||
return;
|
||||
}
|
||||
|
||||
if (bp->blk_birth <= scn->scn_phys.scn_cur_min_txg) {
|
||||
scn->scn_lt_min_this_txg++;
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* This file and its contents are supplied under the terms of the
|
||||
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||
* You may only use this file in accordance with the terms of version
|
||||
* 1.0 of the CDDL.
|
||||
*
|
||||
* A full copy of the text of the CDDL should have accompanied this
|
||||
* source. A copy of the CDDL is also available via the Internet at
|
||||
* http://www.illumos.org/license/CDDL.
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2018 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/objlist.h>
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
objlist_t *
|
||||
objlist_create(void)
|
||||
{
|
||||
objlist_t *list = kmem_alloc(sizeof (*list), KM_SLEEP);
|
||||
list_create(&list->ol_list, sizeof (objlist_node_t),
|
||||
offsetof(objlist_node_t, on_node));
|
||||
list->ol_last_lookup = 0;
|
||||
return (list);
|
||||
}
|
||||
|
||||
void
|
||||
objlist_destroy(objlist_t *list)
|
||||
{
|
||||
for (objlist_node_t *n = list_remove_head(&list->ol_list);
|
||||
n != NULL; n = list_remove_head(&list->ol_list)) {
|
||||
kmem_free(n, sizeof (*n));
|
||||
}
|
||||
list_destroy(&list->ol_list);
|
||||
kmem_free(list, sizeof (*list));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function looks through the objlist to see if the specified object number
|
||||
* is contained in the objlist. In the process, it will remove all object
|
||||
* numbers in the list that are smaller than the specified object number. Thus,
|
||||
* any lookup of an object number smaller than a previously looked up object
|
||||
* number will always return false; therefore, all lookups should be done in
|
||||
* ascending order.
|
||||
*/
|
||||
boolean_t
|
||||
objlist_exists(objlist_t *list, uint64_t object)
|
||||
{
|
||||
objlist_node_t *node = list_head(&list->ol_list);
|
||||
ASSERT3U(object, >=, list->ol_last_lookup);
|
||||
list->ol_last_lookup = object;
|
||||
while (node != NULL && node->on_object < object) {
|
||||
VERIFY3P(node, ==, list_remove_head(&list->ol_list));
|
||||
kmem_free(node, sizeof (*node));
|
||||
node = list_head(&list->ol_list);
|
||||
}
|
||||
return (node != NULL && node->on_object == object);
|
||||
}
|
||||
|
||||
/*
|
||||
* The objlist is a list of object numbers stored in ascending order. However,
|
||||
* the insertion of new object numbers does not seek out the correct location to
|
||||
* store a new object number; instead, it appends it to the list for simplicity.
|
||||
* Thus, any users must take care to only insert new object numbers in ascending
|
||||
* order.
|
||||
*/
|
||||
void
|
||||
objlist_insert(objlist_t *list, uint64_t object)
|
||||
{
|
||||
objlist_node_t *node = kmem_zalloc(sizeof (*node), KM_SLEEP);
|
||||
node->on_object = object;
|
||||
#ifdef ZFS_DEBUG
|
||||
objlist_node_t *last_object = list_tail(&list->ol_list);
|
||||
uint64_t last_objnum = (last_object != NULL ? last_object->on_object :
|
||||
0);
|
||||
ASSERT3U(node->on_object, >, last_objnum);
|
||||
#endif
|
||||
list_insert_tail(&list->ol_list, node);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ zfs_refcount_destroy_many(zfs_refcount_t *rc, uint64_t number)
|
||||
{
|
||||
reference_t *ref;
|
||||
|
||||
ASSERT(rc->rc_count == number);
|
||||
ASSERT3U(rc->rc_count, ==, number);
|
||||
while ((ref = list_head(&rc->rc_list))) {
|
||||
list_remove(&rc->rc_list, ref);
|
||||
kmem_cache_free(reference_cache, ref);
|
||||
@@ -132,7 +132,7 @@ zfs_refcount_add_many(zfs_refcount_t *rc, uint64_t number, void *holder)
|
||||
ref->ref_number = number;
|
||||
}
|
||||
mutex_enter(&rc->rc_mtx);
|
||||
ASSERT(rc->rc_count >= 0);
|
||||
ASSERT3U(rc->rc_count, >=, 0);
|
||||
if (rc->rc_tracked)
|
||||
list_insert_head(&rc->rc_list, ref);
|
||||
rc->rc_count += number;
|
||||
@@ -155,7 +155,7 @@ zfs_refcount_remove_many(zfs_refcount_t *rc, uint64_t number, void *holder)
|
||||
int64_t count;
|
||||
|
||||
mutex_enter(&rc->rc_mtx);
|
||||
ASSERT(rc->rc_count >= number);
|
||||
ASSERT3U(rc->rc_count, >=, number);
|
||||
|
||||
if (!rc->rc_tracked) {
|
||||
rc->rc_count -= number;
|
||||
|
||||
+2
-1
@@ -2119,7 +2119,8 @@ static int
|
||||
spa_load_verify_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)
|
||||
{
|
||||
if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
|
||||
if (zb->zb_level == ZB_DNODE_LEVEL || BP_IS_HOLE(bp) ||
|
||||
BP_IS_EMBEDDED(bp) || BP_IS_REDACTED(bp))
|
||||
return (0);
|
||||
/*
|
||||
* Note: normally this routine will not be called if
|
||||
|
||||
+1
-1
@@ -692,7 +692,7 @@ txg_wait_synced(dsl_pool_t *dp, uint64_t txg)
|
||||
txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
|
||||
while (tx->tx_synced_txg < txg) {
|
||||
dprintf("broadcasting sync more "
|
||||
"tx_synced=%llu waiting=%llu dp=%p\n",
|
||||
"tx_synced=%llu waiting=%llu dp=%px\n",
|
||||
tx->tx_synced_txg, tx->tx_sync_txg_waiting, dp);
|
||||
cv_broadcast(&tx->tx_sync_more_cv);
|
||||
cv_wait_io(&tx->tx_sync_done_cv, &tx->tx_sync_lock);
|
||||
|
||||
+310
-70
@@ -176,6 +176,7 @@
|
||||
#include <sys/dsl_deleg.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/dmu_impl.h>
|
||||
#include <sys/dmu_redact.h>
|
||||
#include <sys/dmu_tx.h>
|
||||
#include <sys/sunddi.h>
|
||||
#include <sys/policy.h>
|
||||
@@ -194,6 +195,7 @@
|
||||
|
||||
#include <sys/dmu_recv.h>
|
||||
#include <sys/dmu_send.h>
|
||||
#include <sys/dmu_recv.h>
|
||||
#include <sys/dsl_destroy.h>
|
||||
#include <sys/dsl_bookmark.h>
|
||||
#include <sys/dsl_userhold.h>
|
||||
@@ -271,7 +273,8 @@ typedef struct zfs_ioc_key {
|
||||
typedef enum {
|
||||
NO_NAME,
|
||||
POOL_NAME,
|
||||
DATASET_NAME
|
||||
DATASET_NAME,
|
||||
ENTITY_NAME
|
||||
} zfs_ioc_namecheck_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -3708,6 +3711,37 @@ zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
return (dsl_get_bookmarks(fsname, innvl, outnvl));
|
||||
}
|
||||
|
||||
/*
|
||||
* innvl is not used.
|
||||
*
|
||||
* outnvl: {
|
||||
* property 1, property 2, ...
|
||||
* }
|
||||
*
|
||||
*/
|
||||
static const zfs_ioc_key_t zfs_keys_get_bookmark_props[] = {
|
||||
/* no nvl keys */
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zfs_ioc_get_bookmark_props(const char *bookmark, nvlist_t *innvl,
|
||||
nvlist_t *outnvl)
|
||||
{
|
||||
char fsname[ZFS_MAX_DATASET_NAME_LEN];
|
||||
char *bmname;
|
||||
|
||||
bmname = strchr(bookmark, '#');
|
||||
if (bmname == NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
bmname++;
|
||||
|
||||
(void) strlcpy(fsname, bookmark, sizeof (fsname));
|
||||
*(strchr(fsname, '#')) = '\0';
|
||||
|
||||
return (dsl_get_bookmark_props(fsname, bmname, outnvl));
|
||||
}
|
||||
|
||||
/*
|
||||
* innvl: {
|
||||
* bookmark name 1, bookmark name 2
|
||||
@@ -4111,6 +4145,40 @@ recursive_unmount(const char *fsname, void *arg)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* snapname is the snapshot to redact.
|
||||
* innvl: {
|
||||
* "bookname" -> (string)
|
||||
* name of the redaction bookmark to generate
|
||||
* "snapnv" -> (nvlist, values ignored)
|
||||
* snapshots to redact snapname with respect to
|
||||
* }
|
||||
*
|
||||
* outnvl is unused
|
||||
*/
|
||||
|
||||
/* ARGSUSED */
|
||||
static const zfs_ioc_key_t zfs_keys_redact[] = {
|
||||
{"bookname", DATA_TYPE_STRING, 0},
|
||||
{"snapnv", DATA_TYPE_NVLIST, 0},
|
||||
};
|
||||
static int
|
||||
zfs_ioc_redact(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
nvlist_t *redactnvl = NULL;
|
||||
char *redactbook = NULL;
|
||||
|
||||
if (nvlist_lookup_nvlist(innvl, "snapnv", &redactnvl) != 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
if (fnvlist_num_pairs(redactnvl) == 0)
|
||||
return (SET_ERROR(ENXIO));
|
||||
if (nvlist_lookup_string(innvl, "bookname", &redactbook) != 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
return (dmu_redact_snap(snapname, redactnvl, redactbook));
|
||||
}
|
||||
|
||||
/*
|
||||
* inputs:
|
||||
* zc_name old name of dataset
|
||||
@@ -4626,6 +4694,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
nvlist_t *origprops = NULL; /* existing properties */
|
||||
nvlist_t *origrecvd = NULL; /* existing received properties */
|
||||
boolean_t first_recvd_props = B_FALSE;
|
||||
boolean_t tofs_was_redacted;
|
||||
file_t *input_fp;
|
||||
|
||||
*read_bytes = 0;
|
||||
@@ -4636,10 +4705,13 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
if (input_fp == NULL)
|
||||
return (SET_ERROR(EBADF));
|
||||
|
||||
off = input_fp->f_offset;
|
||||
error = dmu_recv_begin(tofs, tosnap, begin_record, force,
|
||||
resumable, localprops, hidden_args, origin, &drc);
|
||||
resumable, localprops, hidden_args, origin, &drc, input_fp->f_vnode,
|
||||
&off);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
tofs_was_redacted = dsl_get_redacted(drc.drc_ds);
|
||||
|
||||
/*
|
||||
* Set properties before we receive the stream so that they are applied
|
||||
@@ -4740,9 +4812,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
nvlist_free(xprops);
|
||||
}
|
||||
|
||||
off = input_fp->f_offset;
|
||||
error = dmu_recv_stream(&drc, input_fp->f_vnode, &off, cleanup_fd,
|
||||
action_handle);
|
||||
error = dmu_recv_stream(&drc, cleanup_fd, action_handle, &off);
|
||||
|
||||
if (error == 0) {
|
||||
zfsvfs_t *zfsvfs = NULL;
|
||||
@@ -4752,6 +4822,9 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
/* online recv */
|
||||
dsl_dataset_t *ds;
|
||||
int end_err;
|
||||
boolean_t stream_is_redacted = DMU_GET_FEATUREFLAGS(
|
||||
begin_record->drr_u.drr_begin.
|
||||
drr_versioninfo) & DMU_BACKUP_FEATURE_REDACTED;
|
||||
|
||||
ds = dmu_objset_ds(zfsvfs->z_os);
|
||||
error = zfs_suspend_fs(zfsvfs);
|
||||
@@ -4760,8 +4833,17 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
* likely also fail, and clean up after itself.
|
||||
*/
|
||||
end_err = dmu_recv_end(&drc, zfsvfs);
|
||||
if (error == 0)
|
||||
/*
|
||||
* If the dataset was not redacted, but we received a
|
||||
* redacted stream onto it, we need to unmount the
|
||||
* dataset. Otherwise, resume the filesystem.
|
||||
*/
|
||||
if (error == 0 && !drc.drc_newfs &&
|
||||
stream_is_redacted && !tofs_was_redacted) {
|
||||
error = zfs_end_fs(zfsvfs, ds);
|
||||
} else if (error == 0) {
|
||||
error = zfs_resume_fs(zfsvfs, ds);
|
||||
}
|
||||
error = error ? error : end_err;
|
||||
deactivate_super(zfsvfs->z_sb);
|
||||
} else if ((zv = zvol_suspend(tofs)) != NULL) {
|
||||
@@ -5118,6 +5200,49 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
return (error);
|
||||
}
|
||||
|
||||
typedef struct dump_bytes_io {
|
||||
vnode_t *dbi_vp;
|
||||
void *dbi_buf;
|
||||
int dbi_len;
|
||||
int dbi_err;
|
||||
} dump_bytes_io_t;
|
||||
|
||||
static void
|
||||
dump_bytes_cb(void *arg)
|
||||
{
|
||||
dump_bytes_io_t *dbi = (dump_bytes_io_t *)arg;
|
||||
ssize_t resid; /* have to get resid to get detailed errno */
|
||||
|
||||
dbi->dbi_err = vn_rdwr(UIO_WRITE, dbi->dbi_vp,
|
||||
(caddr_t)dbi->dbi_buf, dbi->dbi_len,
|
||||
0, UIO_SYSSPACE, FAPPEND, RLIM64_INFINITY, CRED(), &resid);
|
||||
}
|
||||
|
||||
static int
|
||||
dump_bytes(objset_t *os, void *buf, int len, void *arg)
|
||||
{
|
||||
dump_bytes_io_t dbi;
|
||||
|
||||
dbi.dbi_vp = arg;
|
||||
dbi.dbi_buf = buf;
|
||||
dbi.dbi_len = len;
|
||||
|
||||
#if defined(HAVE_LARGE_STACKS)
|
||||
dump_bytes_cb(&dbi);
|
||||
#else
|
||||
/*
|
||||
* The vn_rdwr() call is performed in a taskq to ensure that there is
|
||||
* always enough stack space to write safely to the target filesystem.
|
||||
* The ZIO_TYPE_FREE threads are used because there can be a lot of
|
||||
* them and they are used in vdev_file.c for a similar purpose.
|
||||
*/
|
||||
spa_taskq_dispatch_sync(dmu_objset_spa(os), ZIO_TYPE_FREE,
|
||||
ZIO_TASKQ_ISSUE, dump_bytes_cb, &dbi, TQ_SLEEP);
|
||||
#endif /* HAVE_LARGE_STACKS */
|
||||
|
||||
return (dbi.dbi_err);
|
||||
}
|
||||
|
||||
/*
|
||||
* inputs:
|
||||
* zc_name name of snapshot to send
|
||||
@@ -5193,8 +5318,8 @@ zfs_ioc_send(zfs_cmd_t *zc)
|
||||
}
|
||||
}
|
||||
|
||||
error = dmu_send_estimate(tosnap, fromsnap, compressok || rawok,
|
||||
&zc->zc_objset_type);
|
||||
error = dmu_send_estimate_fast(tosnap, fromsnap, NULL,
|
||||
compressok || rawok, &zc->zc_objset_type);
|
||||
|
||||
if (fromsnap != NULL)
|
||||
dsl_dataset_rele(fromsnap, FTAG);
|
||||
@@ -5206,9 +5331,13 @@ zfs_ioc_send(zfs_cmd_t *zc)
|
||||
return (SET_ERROR(EBADF));
|
||||
|
||||
off = fp->f_offset;
|
||||
dmu_send_outparams_t out = {0};
|
||||
out.dso_outfunc = dump_bytes;
|
||||
out.dso_arg = fp->f_vnode;
|
||||
out.dso_dryrun = B_FALSE;
|
||||
error = dmu_send_obj(zc->zc_name, zc->zc_sendobj,
|
||||
zc->zc_fromobj, embedok, large_block_ok, compressok, rawok,
|
||||
zc->zc_cookie, fp->f_vnode, &off);
|
||||
zc->zc_cookie, &off, &out);
|
||||
|
||||
if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
|
||||
fp->f_offset = off;
|
||||
@@ -5219,18 +5348,19 @@ zfs_ioc_send(zfs_cmd_t *zc)
|
||||
|
||||
/*
|
||||
* inputs:
|
||||
* zc_name name of snapshot on which to report progress
|
||||
* zc_cookie file descriptor of send stream
|
||||
* zc_name name of snapshot on which to report progress
|
||||
* zc_cookie file descriptor of send stream
|
||||
*
|
||||
* outputs:
|
||||
* zc_cookie number of bytes written in send stream thus far
|
||||
* zc_cookie number of bytes written in send stream thus far
|
||||
* zc_objset_type logical size of data traversed by send thus far
|
||||
*/
|
||||
static int
|
||||
zfs_ioc_send_progress(zfs_cmd_t *zc)
|
||||
{
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *ds;
|
||||
dmu_sendarg_t *dsp = NULL;
|
||||
dmu_sendstatus_t *dsp = NULL;
|
||||
int error;
|
||||
|
||||
error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
|
||||
@@ -5254,15 +5384,19 @@ zfs_ioc_send_progress(zfs_cmd_t *zc)
|
||||
|
||||
for (dsp = list_head(&ds->ds_sendstreams); dsp != NULL;
|
||||
dsp = list_next(&ds->ds_sendstreams, dsp)) {
|
||||
if (dsp->dsa_outfd == zc->zc_cookie &&
|
||||
dsp->dsa_proc->group_leader == curproc->group_leader)
|
||||
if (dsp->dss_outfd == zc->zc_cookie &&
|
||||
dsp->dss_proc == curproc)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dsp != NULL)
|
||||
zc->zc_cookie = *(dsp->dsa_off);
|
||||
else
|
||||
if (dsp != NULL) {
|
||||
zc->zc_cookie = atomic_cas_64((volatile uint64_t *)dsp->dss_off,
|
||||
0, 0);
|
||||
/* This is the closest thing we have to atomic_read_64. */
|
||||
zc->zc_objset_type = atomic_cas_64(&dsp->dss_blocks, 0, 0);
|
||||
} else {
|
||||
error = SET_ERROR(ENOENT);
|
||||
}
|
||||
|
||||
mutex_exit(&ds->ds_sendstream_lock);
|
||||
dsl_dataset_rele(ds, FTAG);
|
||||
@@ -5973,8 +6107,8 @@ zfs_ioc_events_seek(zfs_cmd_t *zc)
|
||||
|
||||
/*
|
||||
* inputs:
|
||||
* zc_name name of new filesystem or snapshot
|
||||
* zc_value full name of old snapshot
|
||||
* zc_name name of later filesystem or snapshot
|
||||
* zc_value full name of old snapshot or bookmark
|
||||
*
|
||||
* outputs:
|
||||
* zc_cookie space in bytes
|
||||
@@ -5986,7 +6120,7 @@ zfs_ioc_space_written(zfs_cmd_t *zc)
|
||||
{
|
||||
int error;
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *new, *old;
|
||||
dsl_dataset_t *new;
|
||||
|
||||
error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
|
||||
if (error != 0)
|
||||
@@ -5996,16 +6130,26 @@ zfs_ioc_space_written(zfs_cmd_t *zc)
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old);
|
||||
if (error != 0) {
|
||||
dsl_dataset_rele(new, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
if (strchr(zc->zc_value, '#') != NULL) {
|
||||
zfs_bookmark_phys_t bmp;
|
||||
error = dsl_bookmark_lookup(dp, zc->zc_value,
|
||||
new, &bmp);
|
||||
if (error == 0) {
|
||||
error = dsl_dataset_space_written_bookmark(&bmp, new,
|
||||
&zc->zc_cookie,
|
||||
&zc->zc_objset_type, &zc->zc_perm_action);
|
||||
}
|
||||
} else {
|
||||
dsl_dataset_t *old;
|
||||
error = dsl_dataset_hold(dp, zc->zc_value, FTAG, &old);
|
||||
|
||||
error = dsl_dataset_space_written(old, new, &zc->zc_cookie,
|
||||
&zc->zc_objset_type, &zc->zc_perm_action);
|
||||
dsl_dataset_rele(old, FTAG);
|
||||
if (error == 0) {
|
||||
error = dsl_dataset_space_written(old, new,
|
||||
&zc->zc_cookie,
|
||||
&zc->zc_objset_type, &zc->zc_perm_action);
|
||||
dsl_dataset_rele(old, FTAG);
|
||||
}
|
||||
}
|
||||
dsl_dataset_rele(new, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
@@ -6085,6 +6229,9 @@ zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
* presence indicates raw encrypted records should be used.
|
||||
* (optional) "resume_object" and "resume_offset" -> (uint64)
|
||||
* if present, resume send stream from specified object and offset.
|
||||
* (optional) "redactbook" -> (string)
|
||||
* if present, use this bookmark's redaction list to generate a redacted
|
||||
* send stream
|
||||
* }
|
||||
*
|
||||
* outnvl is unused
|
||||
@@ -6098,6 +6245,7 @@ static const zfs_ioc_key_t zfs_keys_send_new[] = {
|
||||
{"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL},
|
||||
{"resume_object", DATA_TYPE_UINT64, ZK_OPTIONAL},
|
||||
{"resume_offset", DATA_TYPE_UINT64, ZK_OPTIONAL},
|
||||
{"redactbook", DATA_TYPE_STRING, ZK_OPTIONAL},
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
@@ -6115,6 +6263,7 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
boolean_t rawok;
|
||||
uint64_t resumeobj = 0;
|
||||
uint64_t resumeoff = 0;
|
||||
char *redactbook = NULL;
|
||||
|
||||
fd = fnvlist_lookup_int32(innvl, "fd");
|
||||
|
||||
@@ -6128,12 +6277,18 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
(void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj);
|
||||
(void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff);
|
||||
|
||||
(void) nvlist_lookup_string(innvl, "redactbook", &redactbook);
|
||||
|
||||
if ((fp = getf(fd)) == NULL)
|
||||
return (SET_ERROR(EBADF));
|
||||
|
||||
off = fp->f_offset;
|
||||
dmu_send_outparams_t out = {0};
|
||||
out.dso_outfunc = dump_bytes;
|
||||
out.dso_arg = fp->f_vnode;
|
||||
out.dso_dryrun = B_FALSE;
|
||||
error = dmu_send(snapname, fromname, embedok, largeblockok, compressok,
|
||||
rawok, fd, resumeobj, resumeoff, fp->f_vnode, &off);
|
||||
rawok, resumeobj, resumeoff, redactbook, fd, &off, &out);
|
||||
|
||||
if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
|
||||
fp->f_offset = off;
|
||||
@@ -6142,6 +6297,15 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
send_space_sum(objset_t *os, void *buf, int len, void *arg)
|
||||
{
|
||||
uint64_t *size = arg;
|
||||
*size += len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine approximately how large a zfs send stream will be -- the number
|
||||
* of bytes that will be written to the fd supplied to zfs_ioc_send_new().
|
||||
@@ -6157,6 +6321,8 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
* presence indicates compressed DRR_WRITE records are permitted
|
||||
* (optional) "rawok" -> (value ignored)
|
||||
* presence indicates raw encrypted records should be used.
|
||||
* (optional) "fd" -> file descriptor to use as a cookie for progress
|
||||
* tracking (int32)
|
||||
* }
|
||||
*
|
||||
* outnvl: {
|
||||
@@ -6170,6 +6336,11 @@ static const zfs_ioc_key_t zfs_keys_send_space[] = {
|
||||
{"embedok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL},
|
||||
{"compressok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL},
|
||||
{"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL},
|
||||
{"fd", DATA_TYPE_INT32, ZK_OPTIONAL},
|
||||
{"redactbook", DATA_TYPE_STRING, ZK_OPTIONAL},
|
||||
{"resumeobj", DATA_TYPE_UINT64, ZK_OPTIONAL},
|
||||
{"resumeoff", DATA_TYPE_UINT64, ZK_OPTIONAL},
|
||||
{"bytes", DATA_TYPE_UINT64, ZK_OPTIONAL},
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -6177,11 +6348,21 @@ zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
{
|
||||
dsl_pool_t *dp;
|
||||
dsl_dataset_t *tosnap;
|
||||
dsl_dataset_t *fromsnap = NULL;
|
||||
int error;
|
||||
char *fromname;
|
||||
char *fromname = NULL;
|
||||
char *redactlist_book = NULL;
|
||||
boolean_t largeblockok;
|
||||
boolean_t embedok;
|
||||
boolean_t compressok;
|
||||
boolean_t rawok;
|
||||
uint64_t space;
|
||||
uint64_t space = 0;
|
||||
boolean_t full_estimate = B_FALSE;
|
||||
uint64_t resumeobj = 0;
|
||||
uint64_t resumeoff = 0;
|
||||
uint64_t resume_bytes = 0;
|
||||
int32_t fd = -1;
|
||||
zfs_bookmark_phys_t zbm = {0};
|
||||
|
||||
error = dsl_pool_hold(snapname, FTAG, &dp);
|
||||
if (error != 0)
|
||||
@@ -6192,61 +6373,101 @@ zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl)
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
(void) nvlist_lookup_int32(innvl, "fd", &fd);
|
||||
|
||||
largeblockok = nvlist_exists(innvl, "largeblockok");
|
||||
embedok = nvlist_exists(innvl, "embedok");
|
||||
compressok = nvlist_exists(innvl, "compressok");
|
||||
rawok = nvlist_exists(innvl, "rawok");
|
||||
boolean_t from = (nvlist_lookup_string(innvl, "from", &fromname) == 0);
|
||||
boolean_t altbook = (nvlist_lookup_string(innvl, "redactbook",
|
||||
&redactlist_book) == 0);
|
||||
|
||||
(void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj);
|
||||
(void) nvlist_lookup_uint64(innvl, "resume_offset", &resumeoff);
|
||||
(void) nvlist_lookup_uint64(innvl, "bytes", &resume_bytes);
|
||||
|
||||
if (altbook) {
|
||||
full_estimate = B_TRUE;
|
||||
} else if (from) {
|
||||
if (strchr(fromname, '#')) {
|
||||
error = dsl_bookmark_lookup(dp, fromname, tosnap, &zbm);
|
||||
|
||||
error = nvlist_lookup_string(innvl, "from", &fromname);
|
||||
if (error == 0) {
|
||||
if (strchr(fromname, '@') != NULL) {
|
||||
/*
|
||||
* If from is a snapshot, hold it and use the more
|
||||
* efficient dmu_send_estimate to estimate send space
|
||||
* size using deadlists.
|
||||
* dsl_bookmark_lookup() will fail with EXDEV if
|
||||
* the from-bookmark and tosnap are at the same txg.
|
||||
* However, it's valid to do a send (and therefore,
|
||||
* a send estimate) from and to the same time point,
|
||||
* if the bookmark is redacted (the incremental send
|
||||
* can change what's redacted on the target). In
|
||||
* this case, dsl_bookmark_lookup() fills in zbm
|
||||
* but returns EXDEV. Ignore this error.
|
||||
*/
|
||||
dsl_dataset_t *fromsnap;
|
||||
if (error == EXDEV && zbm.zbm_redaction_obj != 0 &&
|
||||
zbm.zbm_guid ==
|
||||
dsl_dataset_phys(tosnap)->ds_guid)
|
||||
error = 0;
|
||||
|
||||
if (error != 0) {
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
if (zbm.zbm_redaction_obj != 0 || !(zbm.zbm_flags &
|
||||
ZBM_FLAG_HAS_FBN)) {
|
||||
full_estimate = B_TRUE;
|
||||
}
|
||||
} else if (strchr(fromname, '@')) {
|
||||
error = dsl_dataset_hold(dp, fromname, FTAG, &fromsnap);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
error = dmu_send_estimate(tosnap, fromsnap,
|
||||
compressok || rawok, &space);
|
||||
dsl_dataset_rele(fromsnap, FTAG);
|
||||
} else if (strchr(fromname, '#') != NULL) {
|
||||
/*
|
||||
* If from is a bookmark, fetch the creation TXG of the
|
||||
* snapshot it was created from and use that to find
|
||||
* blocks that were born after it.
|
||||
*/
|
||||
zfs_bookmark_phys_t frombm;
|
||||
if (error != 0) {
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = dsl_bookmark_lookup(dp, fromname, tosnap,
|
||||
&frombm);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
error = dmu_send_estimate_from_txg(tosnap,
|
||||
frombm.zbm_creation_txg, compressok || rawok,
|
||||
&space);
|
||||
if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) {
|
||||
full_estimate = B_TRUE;
|
||||
dsl_dataset_rele(fromsnap, FTAG);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* from is not properly formatted as a snapshot or
|
||||
* bookmark
|
||||
*/
|
||||
error = SET_ERROR(EINVAL);
|
||||
goto out;
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (full_estimate) {
|
||||
dmu_send_outparams_t out = {0};
|
||||
offset_t off = 0;
|
||||
out.dso_outfunc = send_space_sum;
|
||||
out.dso_arg = &space;
|
||||
out.dso_dryrun = B_TRUE;
|
||||
/*
|
||||
* If estimating the size of a full send, use dmu_send_estimate.
|
||||
* We have to release these holds so dmu_send can take them. It
|
||||
* will do all the error checking we need.
|
||||
*/
|
||||
error = dmu_send_estimate(tosnap, NULL, compressok || rawok,
|
||||
&space);
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
error = dmu_send(snapname, fromname, embedok, largeblockok,
|
||||
compressok, rawok, resumeobj, resumeoff, redactlist_book,
|
||||
fd, &off, &out);
|
||||
} else {
|
||||
error = dmu_send_estimate_fast(tosnap, fromsnap,
|
||||
(from && strchr(fromname, '#') != NULL ? &zbm : NULL),
|
||||
compressok || rawok, &space);
|
||||
space -= resume_bytes;
|
||||
if (fromsnap != NULL)
|
||||
dsl_dataset_rele(fromsnap, FTAG);
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
}
|
||||
|
||||
fnvlist_add_uint64(outnvl, "space", space);
|
||||
|
||||
out:
|
||||
dsl_dataset_rele(tosnap, FTAG);
|
||||
dsl_pool_rele(dp, FTAG);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -6607,6 +6828,11 @@ zfs_ioctl_init(void)
|
||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE,
|
||||
zfs_keys_get_bookmarks, ARRAY_SIZE(zfs_keys_get_bookmarks));
|
||||
|
||||
zfs_ioctl_register("get_bookmark_props", ZFS_IOC_GET_BOOKMARK_PROPS,
|
||||
zfs_ioc_get_bookmark_props, zfs_secpolicy_read, ENTITY_NAME,
|
||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, zfs_keys_get_bookmark_props,
|
||||
ARRAY_SIZE(zfs_keys_get_bookmark_props));
|
||||
|
||||
zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS,
|
||||
zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks,
|
||||
POOL_NAME,
|
||||
@@ -6646,6 +6872,11 @@ zfs_ioctl_init(void)
|
||||
B_TRUE, zfs_keys_channel_program,
|
||||
ARRAY_SIZE(zfs_keys_channel_program));
|
||||
|
||||
zfs_ioctl_register("redact", ZFS_IOC_REDACT,
|
||||
zfs_ioc_redact, zfs_secpolicy_config, DATASET_NAME,
|
||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE,
|
||||
zfs_keys_redact, ARRAY_SIZE(zfs_keys_redact));
|
||||
|
||||
zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT,
|
||||
zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME,
|
||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE,
|
||||
@@ -6891,7 +7122,8 @@ pool_status_check(const char *name, zfs_ioc_namecheck_t type,
|
||||
spa_t *spa;
|
||||
int error;
|
||||
|
||||
ASSERT(type == POOL_NAME || type == DATASET_NAME);
|
||||
ASSERT(type == POOL_NAME || type == DATASET_NAME ||
|
||||
type == ENTITY_NAME);
|
||||
|
||||
if (check & POOL_CHECK_NONE)
|
||||
return (0);
|
||||
@@ -7162,10 +7394,18 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
|
||||
vec->zvec_namecheck, vec->zvec_pool_check);
|
||||
break;
|
||||
|
||||
case ENTITY_NAME:
|
||||
if (entity_namecheck(zc->zc_name, NULL, NULL) != 0) {
|
||||
error = SET_ERROR(EINVAL);
|
||||
} else {
|
||||
error = pool_status_check(zc->zc_name,
|
||||
vec->zvec_namecheck, vec->zvec_pool_check);
|
||||
}
|
||||
break;
|
||||
|
||||
case NO_NAME:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that all input pairs are valid before we pass them down
|
||||
* to the lower layers.
|
||||
|
||||
+101
-1
@@ -55,6 +55,7 @@
|
||||
#include <sys/sunddi.h>
|
||||
#include <sys/dmu_objset.h>
|
||||
#include <sys/spa_boot.h>
|
||||
#include <sys/objlist.h>
|
||||
#include <sys/zpl.h>
|
||||
#include <linux/vfs_compat.h>
|
||||
#include "zfs_comutil.h"
|
||||
@@ -2205,11 +2206,14 @@ zfs_resume_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds)
|
||||
}
|
||||
|
||||
bail:
|
||||
if (err != 0)
|
||||
zfsvfs->z_unmounted = B_TRUE;
|
||||
|
||||
/* release the VFS ops */
|
||||
rw_exit(&zfsvfs->z_teardown_inactive_lock);
|
||||
rrm_exit(&zfsvfs->z_teardown_lock, FTAG);
|
||||
|
||||
if (err) {
|
||||
if (err != 0) {
|
||||
/*
|
||||
* Since we couldn't setup the sa framework, try to force
|
||||
* unmount this file system.
|
||||
@@ -2220,6 +2224,37 @@ bail:
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release VOPs and unmount a suspended filesystem.
|
||||
*/
|
||||
int
|
||||
zfs_end_fs(zfsvfs_t *zfsvfs, dsl_dataset_t *ds)
|
||||
{
|
||||
ASSERT(RRM_WRITE_HELD(&zfsvfs->z_teardown_lock));
|
||||
ASSERT(RW_WRITE_HELD(&zfsvfs->z_teardown_inactive_lock));
|
||||
|
||||
/*
|
||||
* We already own this, so just hold and rele it to update the
|
||||
* objset_t, as the one we had before may have been evicted.
|
||||
*/
|
||||
objset_t *os;
|
||||
VERIFY3P(ds->ds_owner, ==, zfsvfs);
|
||||
VERIFY(dsl_dataset_long_held(ds));
|
||||
VERIFY0(dmu_objset_from_ds(ds, &os));
|
||||
zfsvfs->z_os = os;
|
||||
|
||||
/* release the VOPs */
|
||||
rw_exit(&zfsvfs->z_teardown_inactive_lock);
|
||||
rrm_exit(&zfsvfs->z_teardown_lock, FTAG);
|
||||
|
||||
/*
|
||||
* Try to force unmount this file system.
|
||||
*/
|
||||
(void) zfs_umount(zfsvfs->z_sb);
|
||||
zfsvfs->z_unmounted = B_TRUE;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers)
|
||||
{
|
||||
@@ -2394,6 +2429,71 @@ zfs_get_vfs_flag_unmounted(objset_t *os)
|
||||
return (unmounted);
|
||||
}
|
||||
|
||||
struct objnode {
|
||||
avl_node_t node;
|
||||
uint64_t obj;
|
||||
};
|
||||
|
||||
static int
|
||||
objnode_compare(const void *o1, const void *o2)
|
||||
{
|
||||
const struct objnode *obj1 = o1;
|
||||
const struct objnode *obj2 = o2;
|
||||
if (obj1->obj < obj2->obj)
|
||||
return (-1);
|
||||
if (obj1->obj > obj2->obj)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
objlist_t *
|
||||
zfs_get_deleteq(objset_t *os)
|
||||
{
|
||||
objlist_t *deleteq_objlist = objlist_create();
|
||||
uint64_t deleteq_obj;
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t za;
|
||||
dmu_object_info_t doi;
|
||||
|
||||
ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
|
||||
VERIFY0(dmu_object_info(os, MASTER_NODE_OBJ, &doi));
|
||||
ASSERT3U(doi.doi_type, ==, DMU_OT_MASTER_NODE);
|
||||
|
||||
VERIFY0(zap_lookup(os, MASTER_NODE_OBJ,
|
||||
ZFS_UNLINKED_SET, sizeof (uint64_t), 1, &deleteq_obj));
|
||||
|
||||
/*
|
||||
* In order to insert objects into the objlist, they must be in sorted
|
||||
* order. We don't know what order we'll get them out of the ZAP in, so
|
||||
* we insert them into and remove them from an avl_tree_t to sort them.
|
||||
*/
|
||||
avl_tree_t at;
|
||||
avl_create(&at, objnode_compare, sizeof (struct objnode),
|
||||
offsetof(struct objnode, node));
|
||||
|
||||
for (zap_cursor_init(&zc, os, deleteq_obj);
|
||||
zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) {
|
||||
struct objnode *obj = kmem_zalloc(sizeof (*obj), KM_SLEEP);
|
||||
obj->obj = za.za_first_integer;
|
||||
avl_add(&at, obj);
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
|
||||
struct objnode *next, *found = avl_first(&at);
|
||||
while (found != NULL) {
|
||||
next = AVL_NEXT(&at, found);
|
||||
objlist_insert(deleteq_objlist, found->obj);
|
||||
found = next;
|
||||
}
|
||||
|
||||
void *cookie = NULL;
|
||||
while ((found = avl_destroy_nodes(&at, &cookie)) != NULL)
|
||||
kmem_free(found, sizeof (*found));
|
||||
avl_destroy(&at);
|
||||
return (deleteq_objlist);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
zfs_init(void)
|
||||
{
|
||||
|
||||
@@ -4799,6 +4799,9 @@ zbookmark_compare(uint16_t dbss1, uint8_t ibs1, uint16_t dbss2, uint8_t ibs2,
|
||||
zb1->zb_blkid == zb2->zb_blkid)
|
||||
return (0);
|
||||
|
||||
IMPLY(zb1->zb_level > 0, ibs1 >= SPA_MINBLOCKSHIFT);
|
||||
IMPLY(zb2->zb_level > 0, ibs2 >= SPA_MINBLOCKSHIFT);
|
||||
|
||||
/*
|
||||
* BP_SPANB calculates the span in blocks.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user