From 30af21b02569ac192f52ce6e6511015f8a8d5729 Mon Sep 17 00:00:00 2001 From: Paul Dagnelie Date: Wed, 19 Jun 2019 09:48:13 -0700 Subject: 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 Reviewed-by: Matt Ahrens Reviewed-by: Prashanth Sreenivasa Reviewed-by: John Kennedy Reviewed-by: George Wilson Reviewed-by: Chris Williamson Reviewed-by: Pavel Zhakarov Reviewed-by: Sebastien Roy Reviewed-by: Prakash Surya Reviewed-by: Brian Behlendorf Signed-off-by: Paul Dagnelie Closes #7958 --- module/zfs/zfs_ioctl.c | 380 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 310 insertions(+), 70 deletions(-) (limited to 'module/zfs/zfs_ioctl.c') diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index c6b55d24f..2b67761fd 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -176,6 +176,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,7 @@ #include #include +#include #include #include #include @@ -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. -- cgit v1.2.3