Print this page
8520 lzc_rollback_to should support rolling back to origin
7198 libzfs should gracefully handle EINVAL from lzc_rollback
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Dan McDonald <danmcd@joyent.com>
NEX-16623 Ability to set properties for multiple datasets/snapshots during single sync-round
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-16502 libshare needs to support SMB in a zone
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-8600 Destroying a KRRP session takes too long
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5736 implement autoreplace matching based on FRU slot number
NEX-6200 hot spares are not reactivated after reinserting into enclosure
NEX-9403 need to update FRU for spare and l2cache devices
NEX-9404 remove lofi autoreplace support from syseventd
NEX-9409 hotsparing doesn't work for vdevs without FRU
NEX-9424 zfs`vdev_online() needs better notification about state changes
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Portions contributed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-7822 40Gb Intel XL710 NIC performance data
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-8827 AUTOSNAP: can't register recursive zone when there is a child under autosnappool/volumegrou
Reviewed by: Alex Deiter <alex.deiter@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-8106 async ZFS event notifications don't need to announce publish failure
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-6490 Unable to destroy filesystem with receive_resume_token
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-6213 KRRP: System panics if recv-stream does not have snap-properties
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-5553 ZFS auto-trim, manual-trim and scrub can race and deadlock
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5795 Rename 'wrc' as 'wbc' in the source and in the tech docs
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-5272 KRRP: replicate snapshot properties
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5268 WBC: add sysevent migration-done
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5060 WBC: Writecache and deduplication should not be used together
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4830 writecache=off leaks data on special vdev (the data will never migrate)
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4608 WRC: Possible deadlock during the disabling of WRC for a dataset
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
4986 receiving replication stream fails if any snapshot exceeds refquota
Reviewed by: John Kennedy <john.kennedy@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
6388 Failure of userland copy should return EFAULT
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Dan Kimmel <dan.kimmel@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
6328 Fix cstyle errors in zfs codebase (fix studio)
6328 Fix cstyle errors in zfs codebase
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Alex Reece <alex@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Jorgen Lundman <lundman@lundman.net>
Approved by: Robert Mustacchi <rm@joyent.com>
2605 want to resume interrupted zfs send
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Xin Li <delphij@freebsd.org>
Reviewed by: Arne Jansen <sensille@gmx.net>
Approved by: Dan McDonald <danmcd@omniti.com>
6286 ZFS internal error when set large block on bootfs
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Approved by: Robert Mustacchi <rm@joyent.com>
4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R (fix studio build)
4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Garrett D'Amore <garrett@damore.org>
6096 ZFS_SMB_ACL_RENAME needs to cleanup better
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: George Wilson <gwilson@zfsmail.com>
Approved by: Robert Mustacchi <rm@joyent.com>
5946 zfs_ioc_space_snaps must check that firstsnap and lastsnap refer to snapshots
5945 zfs_ioc_send_space must ensure that fromsnap refers to a snapshot
Reviewed by: Steven Hartland <killing@multiplay.co.uk>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gordon.ross@nexenta.com>
5515 dataset user hold doesn't reject empty tags
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Approved by: Matthew Ahrens <mahrens@delphix.com>
5765 add support for estimating send stream size with lzc_send_space when source is a bookmark
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Christopher Siden <christopher.siden@delphix.com>
Reviewed by: Steven Hartland <killing@multiplay.co.uk>
Reviewed by: Bayard Bell <buffer.g.overflow@gmail.com>
Approved by: Albert Lee <trisk@nexenta.com>
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Revert "NEX-4476 WRC: Allow to use write back cache per tree of datasets"
This reverts commit fe97b74444278a6f36fec93179133641296312da.
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-4194 WRC does not migrate data when idle
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-4206 Event 'recv' should contain information that received ds is new
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
NEX-4044 remove sha1crc32 in preparation with upstream merge of edon-r and skien
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Conflicts:
usr/src/uts/common/fs/zfs/sys/zio_checksum.h
NEX-3984 On-demand TRIM
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Conflicts:
usr/src/common/zfs/zpool_prop.c
usr/src/uts/common/sys/fs/zfs.h
NEX-3964 It should not be allowed to rename a snapshot that its new name is matched to the prefix of in-kernel autosnapshots
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3878 Bogus address kernel panic in nvlist_copy_pairs from snmpd whilst exporting a zpool
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3921 Memory leaks in ZFS_IOC_OBJSET_STATS_NVL implementation
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-3010 e1000g use after free on start failure
Reviewed by: Marcel Telka <marcel.telka@nexenta.com>
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3731 fixed incorrect behavior of zpool export/destroy for faulted pool that is caused by krrp-check (lint)
NEX-3731 fixed incorrect behavior of zpool export/destroy for faulted pool that is caused by krrp-check
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-3723 Want ZFS dataset bulk listing support
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
NEX-2195 zfs panic assertion failed: strcmp(zn->zn_key_orig, mze->mze_name) != 0
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-3726 CBC decrypt returns encrypted data
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3709 Fixed KRRP/WRC code style and KRRP related warnings that are showed by 'git nits'
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-3558 KRRP Integration
NEX-3165 need some dedup improvements
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-3070 Panic in zpool_create_features_002_pos test
NEX-2935 want async notification for certain ZFS changes (fix)
NEX-2935 want async notification for certain ZFS changes (lint)
NEX-2935 want async notification for certain ZFS changes
OS-103 handle CoS descriptor persistent references across vdev operations
OS-102 add man page info and tests for vdev/CoS properties and ZFS meta features
OS-80 support for vdev and CoS properties for the new I/O scheduler
OS-95 lint warning introduced by OS-61
Remaining fixes for the illumos merge
Moved closed ZFS files to open repo, changed Makefiles accordingly
Removed unneeded weak symbols
Issue #34: Add feature flag for the compount checksum - sha1crc32
Contributors: Boris Protopopov
Fixup merge results
OS-15 ensure that truss continues to work
OS-15 revert changes to put_nvlist
re #13850 Refactor ZFS config discovery IOCs to libzfs_core patterns
re 13748 added zpool export -c option
zpool export -c command exports specified pool while keeping its latest
configuration in the cache file for subsequent zpool import -c.
re #12619 rb4429 More dp->dp_config_rwlock holds
re #13204 rb4280 zfs receive/rollback deadlock
re #12585 rb4049 ZFS++ work port - refactoring to improve separation of open/closed code, bug fixes, performance improvements - open code
Bug 11205: add missing libzfs_closed_stubs.c to fix opensource-only build.
ZFS plus work: special vdevs, cos, cos/vdev properties
Bug 10481 - Dry run option in 'zfs send' isn't the same as in NexentaStor 3.1
re #7550 rb2134 lint-clean nza-kernel
re #6815 rb1758 need WORM in nza-kernel (4.0)
@@ -19,19 +19,22 @@
* CDDL HEADER END
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
* Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
* Portions Copyright 2011 Martin Matuska
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2018 Nexenta Systems, Inc.
* Copyright 2016 Toomas Soome <tsoome@me.com>
* Copyright 2017 RackTop Systems.
* Copyright (c) 2017 Datto Inc.
*/
@@ -154,15 +157,17 @@
#include <sys/zap.h>
#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/vdev.h>
#include <sys/priv_impl.h>
+#include <sys/autosnap.h>
#include <sys/dmu.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_deleg.h>
+#include <sys/dsl_synctask.h>
#include <sys/dmu_objset.h>
#include <sys/dmu_impl.h>
#include <sys/dmu_tx.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
@@ -184,18 +189,23 @@
#include <sys/dmu_send.h>
#include <sys/dsl_destroy.h>
#include <sys/dsl_bookmark.h>
#include <sys/dsl_userhold.h>
#include <sys/zfeature.h>
+#include <sys/cos.h>
+#include <sys/cos_impl.h>
+#include <sys/zfeature.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
#include <sys/zcp.h>
#include <sys/zio_checksum.h>
-#include <sys/vdev_removal.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "zfs_deleg.h"
#include "zfs_comutil.h"
+#include "zfs_errno.h"
#include "lua.h"
#include "lauxlib.h"
extern struct modlfs zfs_modlfs;
@@ -255,10 +265,43 @@
int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *);
static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp);
static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature);
+static int
+zfs_is_wormed_ds(dsl_dataset_t *ds)
+{
+ char worminfo[13] = {0};
+
+ if (dsl_prop_get_ds(ds, "nms:worm", 1, 12, &worminfo, NULL) == 0 &&
+ worminfo[0] && strcmp(worminfo, "0") != 0 &&
+ strcmp(worminfo, "off") != 0 && strcmp(worminfo, "-") != 0) {
+ return (1);
+ }
+ return (0);
+}
+
+static int
+zfs_is_wormed(const char *name)
+{
+ char worminfo[13] = {0};
+ char cname[MAXNAMELEN];
+ char *end;
+
+ (void) strlcpy(cname, name, MAXNAMELEN);
+ end = strchr(cname, '@');
+ if (end)
+ *end = 0;
+
+ if (dsl_prop_get(cname, "nms:worm", 1, 12, &worminfo, NULL) == 0 &&
+ worminfo[0] && strcmp(worminfo, "0") != 0 &&
+ strcmp(worminfo, "off") != 0 && strcmp(worminfo, "-") != 0) {
+ return (1);
+ }
+ return (0);
+}
+
/* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
void
__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
{
const char *newfile;
@@ -770,13 +813,10 @@
}
int
zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
- if (!INGLOBALZONE(curproc))
- return (SET_ERROR(EPERM));
-
if (secpolicy_nfs(cr) == 0) {
return (0);
} else {
return (zfs_secpolicy_deleg_share(zc, innvl, cr));
}
@@ -783,13 +823,10 @@
}
int
zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
- if (!INGLOBALZONE(curproc))
- return (SET_ERROR(EPERM));
-
if (secpolicy_smb(cr) == 0) {
return (0);
} else {
return (zfs_secpolicy_deleg_share(zc, innvl, cr));
}
@@ -1041,18 +1078,10 @@
return (error);
}
/* ARGSUSED */
static int
-zfs_secpolicy_remap(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
-{
- return (zfs_secpolicy_write_perms(zc->zc_name,
- ZFS_DELEG_PERM_REMAP, cr));
-}
-
-/* ARGSUSED */
-static int
zfs_secpolicy_destroy_bookmarks(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
nvpair_t *pair, *nextpair;
int error = 0;
@@ -1381,34 +1410,79 @@
}
return (0);
}
+/*
+ * Callers will know whether there's anything to unpack based on ret non-0/errno
+ * set to ENOMEM, but observers (e.g truss) need the message properly marked to
+ * know if it should be unpacked and displayed. Don't marked as filled unless
+ * completely successful. If there's a non-empty nvlist, set size to its nvl
+ * size as resize hint.
+ */
static int
put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
{
char *packed = NULL;
int error = 0;
size_t size;
size = fnvlist_size(nvl);
+ zc->zc_nvlist_dst_filled = B_FALSE;
if (size > zc->zc_nvlist_dst_size) {
error = SET_ERROR(ENOMEM);
} else {
packed = fnvlist_pack(nvl, &size);
if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
size, zc->zc_iflags) != 0)
error = SET_ERROR(EFAULT);
+ else
+ zc->zc_nvlist_dst_filled = B_TRUE;
fnvlist_pack_free(packed, size);
}
zc->zc_nvlist_dst_size = size;
- zc->zc_nvlist_dst_filled = B_TRUE;
return (error);
}
+static int
+getzfsvfs_from_ds(dsl_dataset_t *ds, zfsvfs_t **zfvp)
+{
+ objset_t *os;
+ int error;
+ dsl_pool_t *dp;
+
+ dp = ds->ds_dir->dd_pool;
+ dsl_pool_config_enter(dp, FTAG);
+
+ /*
+ * IU: we probably need to hold dataset here.
+ * For now let's assume we do.
+ * May need revision later.
+ */
+ dsl_dataset_long_hold(ds, FTAG);
+ error = dmu_objset_from_ds(ds, &os);
+ if (dmu_objset_type(os) != DMU_OST_ZFS) {
+ dsl_dataset_long_rele(ds, FTAG);
+ dsl_pool_config_exit(dp, FTAG);
+ return (EINVAL);
+ }
+
+ mutex_enter(&os->os_user_ptr_lock);
+ *zfvp = dmu_objset_get_user(os);
+ if (*zfvp) {
+ VFS_HOLD((*zfvp)->z_vfs);
+ } else {
+ error = ESRCH;
+ }
+ mutex_exit(&os->os_user_ptr_lock);
+ dsl_dataset_long_rele(ds, FTAG);
+ dsl_pool_config_exit(dp, FTAG);
+ return (error);
+}
+
int
getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp)
{
int error = 0;
if (dmu_objset_type(os) != DMU_OST_ZFS) {
@@ -1481,17 +1555,41 @@
dmu_objset_disown(zfsvfs->z_os, zfsvfs);
zfsvfs_free(zfsvfs);
}
}
+
+/*
+ * Publish events using GPEC subsystem
+ */
+
+static evchan_t *zfs_channel = NULL;
+
+void
+zfs_event_post(const char *subclass, const char *operation, nvlist_t *ev_data)
+{
+
+ if (zfs_channel == NULL)
+ goto out;
+
+ fnvlist_add_string(ev_data, "operation", operation);
+
+ (void) sysevent_evc_publish(zfs_channel, subclass, operation,
+ "com.nexenta", "zfs-kernel", ev_data, EVCH_NOSLEEP);
+
+out:
+ fnvlist_free(ev_data);
+}
+
static int
zfs_ioc_pool_create(zfs_cmd_t *zc)
{
int error;
nvlist_t *config, *props = NULL;
nvlist_t *rootprops = NULL;
nvlist_t *zplprops = NULL;
+ nvlist_t *event;
if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
zc->zc_iflags, &config))
return (error);
@@ -1536,10 +1634,19 @@
*/
if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
(void) spa_destroy(zc->zc_name);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "name", zc->zc_name);
+ fnvlist_add_nvlist(event, "config", config);
+ if (props != NULL)
+ fnvlist_add_nvlist(event, "props", props);
+ zfs_event_post(ZPOOL_EC_STATUS, "create", event);
+ }
+
pool_props_bad:
nvlist_free(rootprops);
nvlist_free(zplprops);
nvlist_free(config);
nvlist_free(props);
@@ -1549,23 +1656,29 @@
static int
zfs_ioc_pool_destroy(zfs_cmd_t *zc)
{
int error;
+ nvlist_t *event;
zfs_log_history(zc);
error = spa_destroy(zc->zc_name);
- if (error == 0)
+ if (error == 0) {
zvol_remove_minors(zc->zc_name);
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ zfs_event_post(ZPOOL_EC_STATUS, "destroy", event);
+ }
return (error);
}
static int
zfs_ioc_pool_import(zfs_cmd_t *zc)
{
nvlist_t *config, *props = NULL;
uint64_t guid;
int error;
+ nvlist_t *event;
if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
zc->zc_iflags, &config)) != 0)
return (error);
@@ -1580,10 +1693,20 @@
guid != zc->zc_guid)
error = SET_ERROR(EINVAL);
else
error = spa_import(zc->zc_name, config, props, zc->zc_cookie);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_uint64(event, "guid", zc->zc_guid);
+ fnvlist_add_nvlist(event, "config", config);
+ if (props != NULL)
+ fnvlist_add_nvlist(event, "props", props);
+ zfs_event_post(ZPOOL_EC_STATUS, "import", event);
+ }
+
if (zc->zc_nvlist_dst != 0) {
int err;
if ((err = put_nvlist(zc, config)) != 0)
error = err;
@@ -1600,15 +1723,21 @@
zfs_ioc_pool_export(zfs_cmd_t *zc)
{
int error;
boolean_t force = (boolean_t)zc->zc_cookie;
boolean_t hardforce = (boolean_t)zc->zc_guid;
+ boolean_t saveconfig = (boolean_t)zc->zc_obj;
+ nvlist_t *event;
zfs_log_history(zc);
- error = spa_export(zc->zc_name, NULL, force, hardforce);
- if (error == 0)
+ error = spa_export(zc->zc_name, NULL, force, hardforce, saveconfig);
+ if (error == 0) {
zvol_remove_minors(zc->zc_name);
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ zfs_event_post(ZPOOL_EC_STATUS, "export", event);
+ }
return (error);
}
static int
zfs_ioc_pool_configs(zfs_cmd_t *zc)
@@ -1717,11 +1846,41 @@
spa_close(spa, FTAG);
return (error);
}
+/*
+ * inputs:
+ * zc_name name of the pool
+ * zc_cookie trim_cmd_info_t
+ */
static int
+zfs_ioc_pool_trim(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ int error;
+ trim_cmd_info_t tci;
+
+ if (ddi_copyin((void *)(uintptr_t)zc->zc_cookie, &tci,
+ sizeof (tci), 0) == -1)
+ return (EFAULT);
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+ return (error);
+
+ if (tci.tci_start) {
+ spa_man_trim(spa, tci.tci_rate);
+ } else {
+ spa_man_trim_stop(spa);
+ }
+
+ spa_close(spa, FTAG);
+
+ return (error);
+}
+
+static int
zfs_ioc_pool_freeze(zfs_cmd_t *zc)
{
spa_t *spa;
int error;
@@ -1868,10 +2027,11 @@
{
spa_t *spa;
int error;
nvlist_t *config, **l2cache, **spares;
uint_t nl2cache = 0, nspares = 0;
+ nvlist_t *event;
error = spa_open(zc->zc_name, &spa, FTAG);
if (error != 0)
return (error);
@@ -1899,36 +2059,47 @@
return (SET_ERROR(EDOM));
}
if (error == 0) {
error = spa_vdev_add(spa, config);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_nvlist(event, "config", config);
+ zfs_event_post(ZPOOL_EC_STATUS, "add", event);
+
+ }
nvlist_free(config);
}
spa_close(spa, FTAG);
return (error);
}
/*
* inputs:
* zc_name name of the pool
- * zc_guid guid of vdev to remove
- * zc_cookie cancel removal
+ * zc_nvlist_conf nvlist of devices to remove
+ * zc_cookie to stop the remove?
*/
static int
zfs_ioc_vdev_remove(zfs_cmd_t *zc)
{
spa_t *spa;
int error;
+ nvlist_t *event;
error = spa_open(zc->zc_name, &spa, FTAG);
if (error != 0)
return (error);
- if (zc->zc_cookie != 0) {
- error = spa_vdev_remove_cancel(spa);
- } else {
error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_uint64(event, "guid", zc->zc_guid);
+ zfs_event_post(ZPOOL_EC_STATUS, "remove", event);
}
+
spa_close(spa, FTAG);
return (error);
}
static int
@@ -1949,11 +2120,12 @@
error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
break;
case VDEV_STATE_FAULTED:
if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
- zc->zc_obj != VDEV_AUX_EXTERNAL)
+ zc->zc_obj != VDEV_AUX_EXTERNAL &&
+ zc->zc_obj != VDEV_AUX_OPEN_FAILED)
zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
break;
@@ -1977,18 +2149,26 @@
zfs_ioc_vdev_attach(zfs_cmd_t *zc)
{
spa_t *spa;
int replacing = zc->zc_cookie;
nvlist_t *config;
+ nvlist_t *event;
int error;
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
zc->zc_iflags, &config)) == 0) {
error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_nvlist(event, "config", config);
+ fnvlist_add_int32(event, "replacing", replacing);
+ zfs_event_post(ZPOOL_EC_STATUS, "attach", event);
+ }
nvlist_free(config);
}
spa_close(spa, FTAG);
return (error);
@@ -1997,16 +2177,22 @@
static int
zfs_ioc_vdev_detach(zfs_cmd_t *zc)
{
spa_t *spa;
int error;
+ nvlist_t *event;
if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
return (error);
error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
-
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_uint64(event, "guid", zc->zc_guid);
+ zfs_event_post(ZPOOL_EC_STATUS, "detach", event);
+ }
spa_close(spa, FTAG);
return (error);
}
static int
@@ -2043,10 +2229,28 @@
return (error);
}
static int
+zfs_ioc_vdev_setl2adddt(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ int error;
+ uint64_t guid = zc->zc_guid;
+ char *l2ad_ddt = zc->zc_value;
+
+ error = spa_open(zc->zc_name, &spa, FTAG);
+ if (error != 0)
+ return (error);
+
+ error = spa_vdev_setl2adddt(spa, guid, l2ad_ddt);
+ spa_close(spa, FTAG);
+ return (error);
+}
+
+
+static int
zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
{
spa_t *spa;
char *path = zc->zc_value;
uint64_t guid = zc->zc_guid;
@@ -2121,11 +2325,11 @@
* zc_nvlist_dst_size size of property nvlist
*/
static int
zfs_ioc_objset_stats(zfs_cmd_t *zc)
{
- objset_t *os;
+ objset_t *os = NULL;
int error;
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error == 0) {
error = zfs_ioc_objset_stats_impl(zc, os);
@@ -2230,27 +2434,10 @@
}
dmu_objset_rele(os, FTAG);
return (err);
}
-static boolean_t
-dataset_name_hidden(const char *name)
-{
- /*
- * Skip over datasets that are not visible in this zone,
- * internal datasets (which have a $ in their name), and
- * temporary datasets (which have a % in their name).
- */
- if (strchr(name, '$') != NULL)
- return (B_TRUE);
- if (strchr(name, '%') != NULL)
- return (B_TRUE);
- if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
- return (B_TRUE);
- return (B_FALSE);
-}
-
/*
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
* zc_nvlist_dst_size size of buffer for property nvlist
@@ -2519,25 +2706,51 @@
*/
int
zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
nvlist_t *errlist)
{
+ spa_t *spa = NULL;
nvpair_t *pair;
nvpair_t *propval;
int rv = 0;
uint64_t intval;
char *strval;
nvlist_t *genericnvl = fnvlist_alloc();
nvlist_t *retrynvl = fnvlist_alloc();
+ zfsvfs_t *zfsvfs;
+ boolean_t set_worm = B_FALSE;
+ boolean_t set_wbc_mode = B_FALSE;
+ boolean_t wbc_walk_locked = B_FALSE;
+ boolean_t set_dedup = B_FALSE;
+ if ((rv = spa_open(dsname, &spa, FTAG)) != 0)
+ return (rv);
+
retry:
pair = NULL;
while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
const char *propname = nvpair_name(pair);
zfs_prop_t prop = zfs_name_to_prop(propname);
int err = 0;
+ if (!set_worm && (strcmp(propname, "nms:worm") == 0)) {
+ set_worm = B_TRUE;
+ }
+
+ /*
+ * If 'wbc_mode' is going to be changed, then we need to
+ * do some actions before 'set'
+ */
+ if (prop == ZFS_PROP_WBC_MODE)
+ set_wbc_mode = B_TRUE;
+
+ /*
+ *
+ */
+ if (prop == ZFS_PROP_DEDUP)
+ set_dedup = B_TRUE;
+
/* decode the property value */
propval = pair;
if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
nvlist_t *attrs;
attrs = fnvpair_value_nvlist(pair);
@@ -2619,10 +2832,35 @@
if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
nvl = retrynvl;
goto retry;
}
+ /*
+ * Deduplication and WBC cannot be used together
+ * This code returns error also for case when
+ * WBC is ON, DEDUP is off and a user tries
+ * to do DEDUP=off, because in this case the code
+ * will be more complex, but benefit is too small
+ */
+ if (set_wbc_mode && set_dedup) {
+ nvlist_free(genericnvl);
+ nvlist_free(retrynvl);
+ spa_close(spa, FTAG);
+
+ return (SET_ERROR(EKZFS_WBCCONFLICT));
+ }
+
+ /*
+ * Additional actions before set wbc_mode:
+ * - first need to try to lock WBC-walking, to stop migration and
+ * avoid the openning of new migration window
+ * - second step (from sync-context): if migration window
+ * is active it will be purged, to correctly add/remove WBC-instance
+ */
+ if (set_wbc_mode && wbc_walk_lock(spa) == 0)
+ wbc_walk_locked = B_TRUE;
+
if (!nvlist_empty(genericnvl) &&
dsl_props_set(dsname, source, genericnvl) != 0) {
/*
* If this fails, we still want to set as many properties as we
* can, so try setting them individually.
@@ -2660,10 +2898,27 @@
}
}
nvlist_free(genericnvl);
nvlist_free(retrynvl);
+ if (wbc_walk_locked)
+ wbc_walk_unlock(spa);
+
+ if (set_worm && getzfsvfs(dsname, &zfsvfs) == 0) {
+ if (zfs_is_wormed(dsname)) {
+ zfsvfs->z_isworm = B_TRUE;
+ } else {
+ zfsvfs->z_isworm = B_FALSE;
+ }
+ VFS_RELE(zfsvfs->z_vfs);
+ }
+
+ if (rv == 0)
+ autosnap_force_snap_by_name(dsname, NULL, B_FALSE);
+
+ spa_close(spa, FTAG);
+
return (rv);
}
/*
* Check that all the properties are valid user properties.
@@ -2687,11 +2942,11 @@
if (strlen(propname) >= ZAP_MAXNAMELEN)
return (SET_ERROR(ENAMETOOLONG));
if (strlen(fnvpair_value_string(pair)) >= ZAP_MAXVALUELEN)
- return (E2BIG);
+ return (SET_ERROR(E2BIG));
}
return (0);
}
static void
@@ -2728,12 +2983,134 @@
}
nvlist_free(cleared_props);
return (err);
}
+int
+zfs_ioc_set_prop_impl(char *name, nvlist_t *props,
+ boolean_t received, nvlist_t **out_errors)
+{
+ int error = 0;
+ nvlist_t *errors, *event;
+ zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
+ ZPROP_SRC_LOCAL);
+
+ ASSERT(props != NULL);
+
+ if (received) {
+ nvlist_t *origprops;
+
+ if (dsl_prop_get_received(name, &origprops) == 0) {
+ (void) clear_received_props(name, origprops, props);
+ nvlist_free(origprops);
+ }
+
+ error = dsl_prop_set_hasrecvd(name);
+ }
+
+ errors = fnvlist_alloc();
+ if (error == 0)
+ error = zfs_set_prop_nvlist(name, source, props, errors);
+
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "fsname", name);
+ fnvlist_add_nvlist(event, "properties", props);
+ fnvlist_add_nvlist(event, "errors", errors);
+ zfs_event_post(ZFS_EC_STATUS, "set", event);
+
+ if (out_errors != NULL)
+ *out_errors = fnvlist_dup(errors);
+
+ fnvlist_free(errors);
+
+ return (error);
+}
+
/*
+ * XXX This functionality will be removed after integration of
+ * functionality, that does the same via zfs-channel programm.
+ * The zfs-channel programm implementation is being developed
+ * by Delphix.
+ *
+ * This functions sets provided props for provided datasets
+ * in one sync-round. There are some requirements:
+ * - all datasets should belong to the same pool
+ * - only user-properties
+ *
+ * This function does all or nothing.
+ *
* inputs:
+ * zc_nvlist_src{_size} nvlist of datasets and properties to apply
+ *
+ * outputs:
+ * zc_nvlist_dst{_size} error for each unapplied property
+ */
+/* ARGSUSED */
+static int
+zfs_ioc_set_prop_mds(const char *pool_name, nvlist_t *dss_props,
+ nvlist_t *outnvl)
+{
+ int error = 0;
+ spa_t *spa = NULL;
+ nvpair_t *pair = NULL;
+ size_t pool_name_len;
+ size_t total_num_props = 0;
+
+ ASSERT(dss_props != NULL);
+
+ if (nvlist_empty(dss_props))
+ return (SET_ERROR(ENODATA));
+
+ pool_name_len = strlen(pool_name);
+ while ((pair = nvlist_next_nvpair(dss_props, pair)) != NULL) {
+ nvlist_t *props;
+ nvpair_t *prop_nvp = NULL;
+ const char *ds_name;
+
+ ds_name = nvpair_name(pair);
+ if (strncmp(pool_name, ds_name, pool_name_len) == 0) {
+ char c = ds_name[pool_name_len];
+ if (c != '\0' && c != '/' && c != '@')
+ return (SET_ERROR(EXDEV));
+ }
+
+ if (nvpair_type(pair) != DATA_TYPE_NVLIST)
+ return (SET_ERROR(EINVAL));
+
+ props = fnvpair_value_nvlist(pair);
+ while ((prop_nvp = nvlist_next_nvpair(props,
+ prop_nvp)) != NULL) {
+ const char *propname = nvpair_name(prop_nvp);
+ /* Only user-props */
+ if (!zfs_prop_user(propname) ||
+ nvpair_type(prop_nvp) != DATA_TYPE_STRING)
+ return (SET_ERROR(EINVAL));
+
+ /*
+ * We count the number to use it
+ * later to check for ENOSPC
+ */
+ total_num_props++;
+ }
+ }
+
+ if ((error = spa_open(pool_name, &spa, FTAG)) != 0)
+ return (error);
+
+ error = dsl_props_set_mds(pool_name, dss_props, total_num_props);
+ spa_close(spa, FTAG);
+ if (error == 0) {
+ nvlist_t *event = fnvlist_alloc();
+ fnvlist_add_nvlist(event, "properties", dss_props);
+ zfs_event_post(ZFS_EC_STATUS, "set-mds", event);
+ }
+
+ return (error);
+}
+
+/*
+ * inputs:
* zc_name name of filesystem
* zc_value name of property to set
* zc_nvlist_src{_size} nvlist of properties to apply
* zc_cookie received properties flag
*
@@ -2743,35 +3120,19 @@
static int
zfs_ioc_set_prop(zfs_cmd_t *zc)
{
nvlist_t *nvl;
boolean_t received = zc->zc_cookie;
- zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
- ZPROP_SRC_LOCAL);
- nvlist_t *errors;
+ nvlist_t *errors = NULL;
int error;
if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &nvl)) != 0)
return (error);
- if (received) {
- nvlist_t *origprops;
+ error = zfs_ioc_set_prop_impl(zc->zc_name, nvl, received, &errors);
- if (dsl_prop_get_received(zc->zc_name, &origprops) == 0) {
- (void) clear_received_props(zc->zc_name,
- origprops, nvl);
- nvlist_free(origprops);
- }
-
- error = dsl_prop_set_hasrecvd(zc->zc_name);
- }
-
- errors = fnvlist_alloc();
- if (error == 0)
- error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, errors);
-
if (zc->zc_nvlist_dst != NULL && errors != NULL) {
(void) put_nvlist(zc, errors);
}
nvlist_free(errors);
@@ -2859,11 +3220,11 @@
{
nvlist_t *props;
spa_t *spa;
int error;
nvpair_t *pair;
-
+ nvlist_t *event;
if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &props))
return (error);
/*
@@ -2875,11 +3236,11 @@
zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
nvlist_next_nvpair(props, pair) == NULL) {
mutex_enter(&spa_namespace_lock);
if ((spa = spa_lookup(zc->zc_name)) != NULL) {
spa_configfile_set(spa, props, B_FALSE);
- spa_write_cachefile(spa, B_FALSE, B_TRUE);
+ spa_config_sync(spa, B_FALSE, B_TRUE);
}
mutex_exit(&spa_namespace_lock);
if (spa != NULL) {
nvlist_free(props);
return (0);
@@ -2891,10 +3252,17 @@
return (error);
}
error = spa_prop_set(spa, props);
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "pool", zc->zc_name);
+ fnvlist_add_nvlist(event, "props", props);
+ zfs_event_post(ZPOOL_EC_STATUS, "set", event);
+ }
+
nvlist_free(props);
spa_close(spa, FTAG);
return (error);
}
@@ -3012,10 +3380,13 @@
#define ZFS_PROP_UNDEFINED ((uint64_t)-1)
/*
* inputs:
+ * createprops list of properties requested by creator
+ * default_zplver zpl version to use if unspecified in createprops
+ * fuids_ok fuids allowed in this version of the spa?
* os parent objset pointer (NULL if root fs)
* fuids_ok fuids allowed in this version of the spa?
* sa_ok SAs allowed in this version of the spa?
* createprops list of properties requested by creator
*
@@ -3089,10 +3460,15 @@
if (norm == ZFS_PROP_UNDEFINED)
VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
+ if (os) {
+ if (zfs_is_wormed_ds(dmu_objset_ds(os)))
+ return (SET_ERROR(EPERM));
+ }
+
/*
* If we're normalizing, names must always be valid UTF-8 strings.
*/
if (norm)
u8 = 1;
@@ -3186,10 +3562,12 @@
nvlist_t *nvprops = NULL;
void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
int32_t type32;
dmu_objset_type_t type;
boolean_t is_insensitive = B_FALSE;
+ char parent[MAXNAMELEN];
+ nvlist_t *event;
if (nvlist_lookup_int32(innvl, "type", &type32) != 0)
return (SET_ERROR(EINVAL));
type = type32;
(void) nvlist_lookup_nvlist(innvl, "props", &nvprops);
@@ -3214,10 +3592,15 @@
zct.zct_props = nvprops;
if (cbfunc == NULL)
return (SET_ERROR(EINVAL));
+ if (zfs_get_parent(fsname, parent, MAXNAMELEN) == 0 &&
+ zfs_is_wormed(parent)) {
+ return (SET_ERROR(EPERM));
+ }
+
if (type == DMU_OST_ZVOL) {
uint64_t volsize, volblocksize;
if (nvprops == NULL)
return (SET_ERROR(EINVAL));
@@ -3238,12 +3621,10 @@
volblocksize)) != 0 ||
(error = zvol_check_volsize(volsize,
volblocksize)) != 0)
return (error);
} else if (type == DMU_OST_ZFS) {
- int error;
-
/*
* We have to have normalization and
* case-folding flags correct when we do the
* file system creation, so go figure them out
* now.
@@ -3269,10 +3650,20 @@
error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
nvprops, outnvl);
if (error != 0)
(void) dsl_destroy_head(fsname);
}
+
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "fsname", fsname);
+ fnvlist_add_int32(event, "type", type);
+ if (nvprops != NULL)
+ fnvlist_add_nvlist(event, "properties", nvprops);
+ zfs_event_post(ZFS_EC_STATUS, "create", event);
+ }
+
return (error);
}
/*
* innvl: {
@@ -3285,22 +3676,32 @@
static int
zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
{
int error = 0;
nvlist_t *nvprops = NULL;
- char *origin_name;
+ char *origin_name, *origin_snap;
+ nvlist_t *event;
if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0)
return (SET_ERROR(EINVAL));
+
+ origin_snap = strchr(origin_name, '@');
+ if (!origin_snap)
+ return (SET_ERROR(EINVAL));
+
+ if (autosnap_check_name(origin_snap))
+ return (SET_ERROR(EPERM));
+
(void) nvlist_lookup_nvlist(innvl, "props", &nvprops);
if (strchr(fsname, '@') ||
strchr(fsname, '%'))
return (SET_ERROR(EINVAL));
if (dataset_namecheck(origin_name, NULL, NULL) != 0)
return (SET_ERROR(EINVAL));
+
error = dmu_objset_clone(fsname, origin_name);
if (error != 0)
return (error);
/*
@@ -3310,22 +3711,21 @@
error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL,
nvprops, outnvl);
if (error != 0)
(void) dsl_destroy_head(fsname);
}
- return (error);
-}
-/* ARGSUSED */
-static int
-zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
-{
- if (strchr(fsname, '@') ||
- strchr(fsname, '%'))
- return (SET_ERROR(EINVAL));
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "origin", origin_name);
+ fnvlist_add_string(event, "fsname", fsname);
+ if (nvprops != NULL)
+ fnvlist_add_nvlist(event, "properties", nvprops);
+ zfs_event_post(ZFS_EC_STATUS, "clone", event);
+ }
- return (dmu_objset_remap_indirects(fsname));
+ return (error);
}
/*
* innvl: {
* "snaps" -> { snapshot1, snapshot2 }
@@ -3339,10 +3739,11 @@
{
nvlist_t *snaps;
nvlist_t *props = NULL;
int error, poollen;
nvpair_t *pair;
+ nvlist_t *event;
(void) nvlist_lookup_nvlist(innvl, "props", &props);
if ((error = zfs_check_userprops(poolname, props)) != 0)
return (error);
@@ -3364,10 +3765,13 @@
*/
if (cp == NULL ||
zfs_component_namecheck(cp + 1, NULL, NULL) != 0)
return (SET_ERROR(EINVAL));
+ if (autosnap_check_name(cp))
+ return (EINVAL);
+
/*
* The snap must be in the specified pool.
*/
if (strncmp(name, poolname, poollen) != 0 ||
(name[poollen] != '/' && name[poollen] != '@'))
@@ -3382,10 +3786,17 @@
}
}
}
error = dsl_dataset_snapshot(snaps, props, outnvl);
+
+ event = fnvlist_alloc();
+ fnvlist_add_nvlist(event, "snaps", snaps);
+ fnvlist_add_nvlist(event, "errors", outnvl);
+ fnvlist_add_string(event, "pool", poolname);
+ zfs_event_post(ZFS_EC_STATUS, "snapshot", event);
+
return (error);
}
/*
* innvl: "message" -> string
@@ -3499,10 +3910,29 @@
} else {
dmu_objset_rele(os, FTAG);
}
}
+static int
+zfs_destroy_check_autosnap(spa_t *spa, const char *name)
+{
+ const char *snap = strchr(name, '@');
+
+ if (snap == NULL)
+ return (EINVAL);
+
+ if (autosnap_check_name(snap)) {
+ int err = autosnap_check_for_destroy(
+ spa_get_autosnap(spa), name);
+
+ if (err != 0)
+ return (EBUSY);
+ }
+
+ return (0);
+}
+
/*
* innvl: {
* "snaps" -> { snapshot1, snapshot2 }
* (optional boolean) "defer"
* }
@@ -3515,21 +3945,52 @@
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
{
nvlist_t *snaps;
nvpair_t *pair;
boolean_t defer;
+ int error = 0;
+ nvlist_t *event;
+ spa_t *spa;
+ if (zfs_is_wormed(poolname))
+ return (SET_ERROR(EPERM));
+
if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0)
return (SET_ERROR(EINVAL));
defer = nvlist_exists(innvl, "defer");
+ error = spa_open(poolname, &spa, FTAG);
+ if (spa == NULL)
+ return (error);
+
+ for (pair = nvlist_next_nvpair(snaps, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) {
+ error = zfs_destroy_check_autosnap(spa, nvpair_name(pair));
+ if (error)
+ fnvlist_add_int32(outnvl, nvpair_name(pair), error);
+ }
+
+ spa_close(spa, FTAG);
+
+ if (error)
+ return (error);
+
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
pair = nvlist_next_nvpair(snaps, pair)) {
zfs_unmount_snap(nvpair_name(pair));
}
- return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
+ error = dsl_destroy_snapshots_nvl(snaps, defer, outnvl);
+
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_nvlist(event, "snaps", snaps);
+ fnvlist_add_nvlist(event, "errors", outnvl);
+ zfs_event_post(ZFS_EC_STATUS, "destroy_snaps", event);
+ }
+
+ return (error);
}
/*
* Create bookmarks. Bookmark names are of the form <fs>#<bmark>.
* All bookmarks must be in the same pool.
@@ -3661,27 +4122,109 @@
/*
* inputs:
* zc_name name of dataset to destroy
* zc_objset_type type of objset
* zc_defer_destroy mark for deferred destroy
+ * zc_guid if set, do atomical recursive destroy
*
* outputs: none
*/
static int
zfs_ioc_destroy(zfs_cmd_t *zc)
{
int err;
+ nvlist_t *event;
+ if (zfs_is_wormed(zc->zc_name))
+ return (SET_ERROR(EPERM));
+
if (zc->zc_objset_type == DMU_OST_ZFS)
zfs_unmount_snap(zc->zc_name);
- if (strchr(zc->zc_name, '@'))
- err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy);
- else
+ if (zc->zc_guid) {
+ spa_t *spa;
+
+ if ((err = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+ return (err);
+
+ err = autosnap_lock(spa, RW_WRITER);
+ if (err == 0) {
+ err = wbc_walk_lock(spa);
+ if (err != 0)
+ autosnap_unlock(spa);
+ }
+
+ if (err == 0) {
+ err = dsl_destroy_atomically(zc->zc_name,
+ zc->zc_defer_destroy);
+ wbc_walk_unlock(spa);
+ autosnap_unlock(spa);
+ }
+
+ spa_close(spa, FTAG);
+ } else {
+ if (strchr(zc->zc_name, '@')) {
+ spa_t *spa = NULL;
+
+ err = spa_open(zc->zc_name, &spa, FTAG);
+ if (err != 0)
+ return (err);
+
+ err = zfs_destroy_check_autosnap(spa, zc->zc_name);
+ if (err == 0) {
+ err = dsl_destroy_snapshot(zc->zc_name,
+ zc->zc_defer_destroy);
+ }
+
+ spa_close(spa, FTAG);
+ } else {
err = dsl_destroy_head(zc->zc_name);
+ if (err == EEXIST) {
+ /*
+ * It is possible that the given DS may have
+ * hidden child (%recv) datasets - "leftovers"
+ * resulting from the previously interrupted
+ * 'zfs receive'.
+ */
+ char namebuf[ZFS_MAX_DATASET_NAME_LEN];
+
+ if (snprintf(namebuf, sizeof (namebuf),
+ "%s/%%recv", zc->zc_name) >=
+ sizeof (namebuf))
+ return (err);
+
+ /* Try to remove the hidden child (%recv) */
+ err = dsl_destroy_head(namebuf);
+ if (err == 0) {
+ /*
+ * Now the given DS should not have
+ * children, so we can try to remove
+ * it again
+ */
+ err = dsl_destroy_head(zc->zc_name);
+ } else if (err == ENOENT) {
+ /*
+ * The hidden child (%recv) does not
+ * exist, so need to restore original
+ * error
+ */
+ err = EEXIST;
+ }
+
+ }
+ }
+ }
if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
(void) zvol_remove_minor(zc->zc_name);
+
+ if (err == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "fsname", zc->zc_name);
+ fnvlist_add_int32(event, "type", zc->zc_objset_type);
+ zfs_event_post(ZFS_EC_STATUS, "destroy", event);
+ }
+
return (err);
}
/*
* fsname is name of dataset to rollback (to most recent snapshot)
@@ -3696,11 +4239,16 @@
zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
{
zfsvfs_t *zfsvfs;
char *target = NULL;
int error;
+ nvlist_t *event;
+ int resume_err = 0;
+ if (zfs_is_wormed(fsname))
+ return (SET_ERROR(EPERM));
+
(void) nvlist_lookup_string(innvl, "target", &target);
if (target != NULL) {
const char *cp = strchr(target, '@');
/*
@@ -3716,21 +4264,28 @@
dsl_dataset_t *ds;
ds = dmu_objset_ds(zfsvfs->z_os);
error = zfs_suspend_fs(zfsvfs);
if (error == 0) {
- int resume_err;
-
error = dsl_dataset_rollback(fsname, target, zfsvfs,
outnvl);
resume_err = zfs_resume_fs(zfsvfs, ds);
- error = error ? error : resume_err;
}
VFS_RELE(zfsvfs->z_vfs);
} else {
error = dsl_dataset_rollback(fsname, target, NULL, outnvl);
}
+
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "target", (target != NULL) ? target : "");
+ fnvlist_add_string(event, "fsname", fsname);
+ fnvlist_add_int32(event, "resume_err", resume_err);
+ zfs_event_post(ZFS_EC_STATUS, "rollback", event);
+ }
+
+ error = (error != 0) ? error : resume_err;
return (error);
}
static int
recursive_unmount(const char *fsname, void *arg)
@@ -3755,23 +4310,24 @@
static int
zfs_ioc_rename(zfs_cmd_t *zc)
{
boolean_t recursive = zc->zc_cookie & 1;
char *at;
+ nvlist_t *event;
+ int error;
- /* "zfs rename" from and to ...%recv datasets should both fail */
- zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
+ if (zfs_is_wormed(zc->zc_name))
+ return (SET_ERROR(EPERM));
+
zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
- if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
- dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
- strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%'))
+ if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
+ strchr(zc->zc_value, '%'))
return (SET_ERROR(EINVAL));
at = strchr(zc->zc_name, '@');
if (at != NULL) {
/* snaps must be in same fs */
- int error;
if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
return (SET_ERROR(EXDEV));
*at = '\0';
if (zc->zc_objset_type == DMU_OST_ZFS) {
@@ -3785,16 +4341,25 @@
}
error = dsl_dataset_rename_snapshot(zc->zc_name,
at + 1, strchr(zc->zc_value, '@') + 1, recursive);
*at = '@';
- return (error);
} else {
if (zc->zc_objset_type == DMU_OST_ZVOL)
(void) zvol_remove_minor(zc->zc_name);
- return (dsl_dir_rename(zc->zc_name, zc->zc_value));
+ error = dsl_dir_rename(zc->zc_name, zc->zc_value);
}
+
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "origin", zc->zc_name);
+ fnvlist_add_string(event, "fsname", zc->zc_value);
+ fnvlist_add_int32(event, "type", zc->zc_objset_type);
+ zfs_event_post(ZFS_EC_STATUS, "rename", event);
+ }
+
+ return (error);
}
static int
zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
{
@@ -3947,10 +4512,36 @@
SPA_VERSION_PASSTHROUGH_X))
return (SET_ERROR(ENOTSUP));
}
break;
+ case ZFS_PROP_WBC_MODE:
+ {
+ spa_t *spa;
+ boolean_t wbc_feature_enabled;
+
+ if ((err = spa_open(dsname, &spa, FTAG)) != 0)
+ return (err);
+
+ wbc_feature_enabled =
+ spa_feature_is_enabled(spa, SPA_FEATURE_WBC);
+ spa_close(spa, FTAG);
+
+ /* WBC cannot be used without special-vdev */
+ if (!wbc_feature_enabled || !spa_has_special(spa))
+ return (SET_ERROR(EKZFS_WBCNOTSUP));
+
+ /*
+ * We do not want to have races, because on
+ * import or after reboot WBC does registration
+ * asynchronously.
+ */
+ if (!spa->spa_wbc.wbc_ready_to_use)
+ return (SET_ERROR(EBUSY));
+ }
+ break;
+
case ZFS_PROP_CHECKSUM:
case ZFS_PROP_DEDUP:
{
spa_feature_t feature;
spa_t *spa;
@@ -4222,79 +4813,37 @@
#ifdef DEBUG
static boolean_t zfs_ioc_recv_inject_err;
#endif
-/*
- * inputs:
- * zc_name name of containing filesystem
- * zc_nvlist_src{_size} nvlist of properties to apply
- * zc_value name of snapshot to create
- * zc_string name of clone origin (if DRR_FLAG_CLONE)
- * zc_cookie file descriptor to recv from
- * zc_begin_record the BEGIN record of the stream (not byteswapped)
- * zc_guid force flag
- * zc_cleanup_fd cleanup-on-exit file descriptor
- * zc_action_handle handle for this guid/ds mapping (or zero on first call)
- * zc_resumable if data is incomplete assume sender will resume
- *
- * outputs:
- * zc_cookie number of bytes read
- * zc_nvlist_dst{_size} error for each unapplied received property
- * zc_obj zprop_errflags_t
- * zc_action_handle handle for this guid/ds mapping
- */
-static int
-zfs_ioc_recv(zfs_cmd_t *zc)
+int
+dmu_recv_impl(int fd, char *tofs, char *tosnap, char *origin,
+ dmu_replay_record_t *drr_begin, boolean_t is_resumable, nvlist_t *props,
+ nvlist_t *errors, uint64_t *errf, int cfd, uint64_t *ahdl, uint64_t *sz,
+ boolean_t force, dmu_krrp_task_t *krrp_task)
{
- file_t *fp;
+ file_t *fp = getf(fd);
dmu_recv_cookie_t drc;
- boolean_t force = (boolean_t)zc->zc_guid;
- int fd;
int error = 0;
int props_error = 0;
- nvlist_t *errors;
offset_t off;
- nvlist_t *props = NULL; /* sent properties */
nvlist_t *origprops = NULL; /* existing properties */
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
- char *origin = NULL;
- char *tosnap;
- char tofs[ZFS_MAX_DATASET_NAME_LEN];
boolean_t first_recvd_props = B_FALSE;
+ nvlist_t *event;
+ boolean_t force_cksum =
+ !krrp_task || krrp_task->buffer_args.force_cksum;
- if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
- strchr(zc->zc_value, '@') == NULL ||
- strchr(zc->zc_value, '%'))
- return (SET_ERROR(EINVAL));
+ ASSERT(fp || krrp_task);
- (void) strcpy(tofs, zc->zc_value);
- tosnap = strchr(tofs, '@');
- *tosnap++ = '\0';
-
- if (zc->zc_nvlist_src != NULL &&
- (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
- zc->zc_iflags, &props)) != 0)
- return (error);
-
- fd = zc->zc_cookie;
- fp = getf(fd);
- if (fp == NULL) {
- nvlist_free(props);
- return (SET_ERROR(EBADF));
- }
-
- errors = fnvlist_alloc();
-
- if (zc->zc_string[0])
- origin = zc->zc_string;
-
error = dmu_recv_begin(tofs, tosnap,
- &zc->zc_begin_record, force, zc->zc_resumable, origin, &drc);
+ drr_begin, force, is_resumable, force_cksum, origin, &drc);
+
if (error != 0)
goto out;
+ drc.drc_krrp_task = krrp_task;
/*
* Set properties before we receive the stream so that they are applied
* to the new data. Note that we must call dmu_recv_stream() if
* dmu_recv_begin() succeeds.
*/
@@ -4325,13 +4874,13 @@
(void) nvlist_merge(errors, errlist, 0);
nvlist_free(errlist);
if (clear_received_props(tofs, origprops,
first_recvd_props ? NULL : props) != 0)
- zc->zc_obj |= ZPROP_ERR_NOCLEAR;
+ *errf |= ZPROP_ERR_NOCLEAR;
} else {
- zc->zc_obj |= ZPROP_ERR_NOCLEAR;
+ *errf |= ZPROP_ERR_NOCLEAR;
}
}
if (props != NULL) {
props_error = dsl_prop_set_hasrecvd(tofs);
@@ -4341,18 +4890,23 @@
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
props, errors);
}
}
+ if (fp) {
off = fp->f_offset;
- error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
- &zc->zc_action_handle);
+ } else {
+ off = 0;
+ }
+ error = dmu_recv_stream(&drc, fp ? fp->f_vnode : NULL,
+ &off, cfd, ahdl, krrp_task);
if (error == 0) {
zfsvfs_t *zfsvfs = NULL;
- if (getzfsvfs(tofs, &zfsvfs) == 0) {
+ error = getzfsvfs(tofs, &zfsvfs);
+ if (error == 0) {
/* online recv */
dsl_dataset_t *ds;
int end_err;
ds = dmu_objset_ds(zfsvfs->z_os);
@@ -4389,28 +4943,34 @@
*/
ASSERT(nvlist_merge(props, delayprops, 0) == 0);
nvlist_free(delayprops);
}
- /*
- * Now that all props, initial and delayed, are set, report the prop
- * errors to the caller.
- */
- if (zc->zc_nvlist_dst_size != 0 &&
- (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
- put_nvlist(zc, errors) != 0)) {
- /*
- * Caller made zc->zc_nvlist_dst less than the minimum expected
- * size or supplied an invalid address.
- */
- props_error = SET_ERROR(EINVAL);
- }
-
- zc->zc_cookie = off - fp->f_offset;
+ if (fp) {
+ *sz = off - fp->f_offset;
if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
fp->f_offset = off;
+ } else {
+ *sz = off;
+ }
+ if (error == 0) {
+ char val[MAXNAMELEN];
+ (void) strcpy(val, tofs);
+ (void) strcat(val, "@");
+ (void) strcat(val, tosnap);
+
+ event = fnvlist_alloc();
+ if (props != NULL)
+ fnvlist_add_nvlist(event, "props", props);
+ fnvlist_add_string(event, "origin", tofs);
+ fnvlist_add_string(event, "tosnap", val);
+ fnvlist_add_uint64(event, "bytes", *sz);
+ fnvlist_add_boolean_value(event, "newds", drc.drc_newfs);
+ zfs_event_post(ZFS_EC_STATUS, "recv", event);
+ }
+
#ifdef DEBUG
if (zfs_ioc_recv_inject_err) {
zfs_ioc_recv_inject_err = B_FALSE;
error = 1;
}
@@ -4423,18 +4983,18 @@
/*
* We failed to clear the received properties.
* Since we may have left a $recvd value on the
* system, we can't clear the $hasrecvd flag.
*/
- zc->zc_obj |= ZPROP_ERR_NORESTORE;
+ *errf |= ZPROP_ERR_NORESTORE;
} else if (first_recvd_props) {
dsl_prop_unset_hasrecvd(tofs);
}
if (origprops == NULL && !drc.drc_newfs) {
/* We failed to stash the original properties. */
- zc->zc_obj |= ZPROP_ERR_NORESTORE;
+ *errf |= ZPROP_ERR_NORESTORE;
}
/*
* dsl_props_set() will not convert RECEIVED to LOCAL on or
* after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
@@ -4447,17 +5007,16 @@
origprops, NULL) != 0) {
/*
* We stashed the original properties but failed to
* restore them.
*/
- zc->zc_obj |= ZPROP_ERR_NORESTORE;
+ *errf |= ZPROP_ERR_NORESTORE;
}
}
out:
- nvlist_free(props);
nvlist_free(origprops);
- nvlist_free(errors);
+ if (fp)
releasef(fd);
if (error == 0)
error = props_error;
@@ -4464,10 +5023,85 @@
return (error);
}
/*
* inputs:
+ * zc_name name of containing filesystem
+ * zc_nvlist_src{_size} nvlist of properties to apply
+ * zc_value name of snapshot to create
+ * zc_string name of clone origin (if DRR_FLAG_CLONE)
+ * zc_cookie file descriptor to recv from
+ * zc_begin_record the BEGIN record of the stream (not byteswapped)
+ * zc_guid force flag
+ * zc_cleanup_fd cleanup-on-exit file descriptor
+ * zc_action_handle handle for this guid/ds mapping (or zero on first call)
+ * zc_resumable if data is incomplete assume sender will resume
+ *
+ * outputs:
+ * zc_cookie number of bytes read
+ * zc_nvlist_dst{_size} error for each unapplied received property
+ * zc_obj zprop_errflags_t
+ * zc_action_handle handle for this guid/ds mapping
+ */
+static int
+zfs_ioc_recv(zfs_cmd_t *zc)
+{
+ int fd = zc->zc_cookie;
+ char tofs[ZFS_MAX_DATASET_NAME_LEN];
+ char *tosnap;
+ char *origin = NULL;
+ nvlist_t *errors;
+ nvlist_t *props = NULL; /* sent properties */
+ boolean_t force = (boolean_t)zc->zc_guid;
+ int err;
+
+ if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
+ strchr(zc->zc_value, '@') == NULL ||
+ strchr(zc->zc_value, '%'))
+ return (SET_ERROR(EINVAL));
+
+ (void) strcpy(tofs, zc->zc_value);
+ tosnap = strchr(tofs, '@');
+ *tosnap++ = '\0';
+
+ if (zc->zc_string[0])
+ origin = zc->zc_string;
+
+ if (zc->zc_nvlist_src != NULL &&
+ (err = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props)) != 0)
+ return (err);
+
+ errors = fnvlist_alloc();
+
+ err = dmu_recv_impl(fd, tofs, tosnap, origin,
+ &zc->zc_begin_record, zc->zc_resumable, props, errors, &zc->zc_obj,
+ zc->zc_cleanup_fd, &zc->zc_action_handle, &zc->zc_cookie,
+ force, NULL);
+
+ /*
+ * Now that all props, initial and delayed, are set, report the prop
+ * errors to the caller.
+ */
+ if (zc->zc_nvlist_dst_size != 0 &&
+ (nvlist_smush(errors, zc->zc_nvlist_dst_size) != 0 ||
+ put_nvlist(zc, errors) != 0)) {
+ /*
+ * Caller made zc->zc_nvlist_dst less than the minimum expected
+ * size or supplied an invalid address.
+ */
+ err = SET_ERROR(EINVAL);
+ }
+
+ nvlist_free(errors);
+ nvlist_free(props);
+ return (err);
+
+}
+
+/*
+ * inputs:
* zc_name name of snapshot to send
* zc_cookie file descriptor to send stream to
* zc_obj fromorigin flag (mutually exclusive with zc_fromobj)
* zc_sendobj objsetid of snapshot to send
* zc_fromobj objsetid of incremental fromsnap (may be zero)
@@ -4540,19 +5174,21 @@
if (fromsnap != NULL)
dsl_dataset_rele(fromsnap, FTAG);
dsl_dataset_rele(tosnap, FTAG);
dsl_pool_rele(dp, FTAG);
} else {
+ offset_t off_starting;
file_t *fp = getf(zc->zc_cookie);
if (fp == NULL)
return (SET_ERROR(EBADF));
- off = fp->f_offset;
+ off_starting = off = fp->f_offset;
error = dmu_send_obj(zc->zc_name, zc->zc_sendobj,
zc->zc_fromobj, embedok, large_block_ok, compressok,
- zc->zc_cookie, fp->f_vnode, &off);
+ zc->zc_cookie, fp->f_vnode, &off, zc->zc_sendsize);
+ zc->zc_sendcounter = off - off_starting;
if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
fp->f_offset = off;
releasef(zc->zc_cookie);
}
return (error);
@@ -4783,16 +5419,12 @@
dsl_pool_t *dp;
dsl_dataset_t *ds, *ods;
char origin[ZFS_MAX_DATASET_NAME_LEN];
char *cp;
int error;
+ nvlist_t *event;
- zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
- if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
- strchr(zc->zc_name, '%'))
- return (SET_ERROR(EINVAL));
-
error = dsl_pool_hold(zc->zc_name, FTAG, &dp);
if (error != 0)
return (error);
error = dsl_dataset_hold(dp, zc->zc_name, FTAG, &ds);
@@ -4827,11 +5459,20 @@
cp = strchr(origin, '@');
if (cp)
*cp = '\0';
(void) dmu_objset_find(origin,
zfs_unmount_snap_cb, NULL, DS_FIND_SNAPSHOTS);
- return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
+ error = dsl_dataset_promote(zc->zc_name, zc->zc_string);
+
+ if (error == 0) {
+ event = fnvlist_alloc();
+ fnvlist_add_string(event, "fsname", zc->zc_name);
+ fnvlist_add_string(event, "origin", zc->zc_value);
+ zfs_event_post(ZFS_EC_STATUS, "promote", event);
+ }
+
+ return (error);
}
/*
* Retrieve a single {user|group}{used|quota}@... property.
*
@@ -5508,10 +6149,217 @@
fnvlist_add_uint64(outnvl, "compressed", comp);
fnvlist_add_uint64(outnvl, "uncompressed", uncomp);
return (error);
}
+static int
+zfs_ioc_vdev_set_props(zfs_cmd_t *zc)
+{
+ nvlist_t *props;
+ spa_t *spa;
+ int error;
+ uint64_t vdev_guid = zc->zc_guid;
+
+ if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props))
+ return (error);
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+ nvlist_free(props);
+ return (error);
+ }
+
+ error = spa_vdev_prop_set(spa, vdev_guid, props);
+
+ nvlist_free(props);
+ spa_close(spa, FTAG);
+
+ return (error);
+}
+
+static int
+zfs_ioc_vdev_get_props(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ uint64_t vdev_guid = zc->zc_guid;
+ nvlist_t *nvp = NULL;
+ int error;
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+ /*
+ * If the pool is faulted, there may be properties we can still
+ * get (such as altroot and cachefile), so attempt to get them
+ * anyway.
+ */
+ mutex_enter(&spa_namespace_lock);
+ if ((spa = spa_lookup(zc->zc_name)) != NULL)
+ error = spa_vdev_prop_get(spa, vdev_guid, &nvp);
+ mutex_exit(&spa_namespace_lock);
+ } else {
+ error = spa_vdev_prop_get(spa, vdev_guid, &nvp);
+ spa_close(spa, FTAG);
+ }
+
+ if (error == 0 && zc->zc_nvlist_dst != NULL)
+ error = put_nvlist(zc, nvp);
+ else
+ error = EFAULT;
+
+ nvlist_free(nvp);
+ return (error);
+}
+
+static int
+zfs_ioc_cos_alloc(zfs_cmd_t *zc)
+{
+ nvlist_t *props;
+ spa_t *spa;
+ int error;
+
+ if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props))
+ return (error);
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+ nvlist_free(props);
+ return (error);
+ }
+
+ error = spa_alloc_cos(spa, zc->zc_string, 0);
+ if (!error)
+ error = spa_cos_prop_set(spa, zc->zc_string, props);
+
+ spa_close(spa, FTAG);
+ nvlist_free(props);
+
+ return (error);
+}
+
+static int
+zfs_ioc_cos_free(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ int error = 0;
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+ return (error);
+
+ error = spa_free_cos(spa, zc->zc_string, zc->zc_cookie);
+
+ spa_close(spa, FTAG);
+
+ return (error);
+}
+
+static int
+zfs_ioc_cos_list(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ nvlist_t *nvl;
+ int error = 0;
+
+ VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+ nvlist_free(nvl);
+ return (error);
+ }
+
+ error = spa_list_cos(spa, nvl);
+
+ if (error == 0 && zc->zc_nvlist_dst != NULL)
+ error = put_nvlist(zc, nvl);
+
+ spa_close(spa, FTAG);
+ nvlist_free(nvl);
+
+ return (error);
+}
+
+static int
+zfs_ioc_cos_set_props(zfs_cmd_t *zc)
+{
+ nvlist_t *props;
+ spa_t *spa;
+ cos_t *cos;
+ const char *cosname = NULL;
+ int error = 0;
+
+ if ((zc->zc_string == NULL || zc->zc_string[0] == '\0') &&
+ zc->zc_guid == 0)
+ return (SET_ERROR(EINVAL));
+
+ if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props))
+ return (error);
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
+ nvlist_free(props);
+ return (error);
+ }
+
+ if (zc->zc_guid == 0) {
+ cosname = zc->zc_string;
+ } else {
+ spa_cos_enter(spa);
+ cos = spa_lookup_cos_by_guid(spa, zc->zc_guid);
+ if (cos != NULL)
+ cosname = cos->cos_name;
+ else
+ error = SET_ERROR(ENOENT);
+ spa_cos_exit(spa);
+ }
+
+ if (error == 0)
+ error = spa_cos_prop_set(spa, cosname, props);
+
+ spa_close(spa, FTAG);
+ nvlist_free(props);
+
+ return (error);
+}
+
+static int
+zfs_ioc_cos_get_props(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ cos_t *cos;
+ nvlist_t *nvp = NULL;
+ const char *cosname = NULL;
+ int error = 0;
+
+ if ((zc->zc_string == NULL || zc->zc_string[0] == '\0') &&
+ zc->zc_guid == 0)
+ return (SET_ERROR(EINVAL));
+
+ if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
+ return (error);
+
+ if (zc->zc_guid == 0) {
+ cosname = zc->zc_string;
+ } else {
+ spa_cos_enter(spa);
+ cos = spa_lookup_cos_by_guid(spa, zc->zc_guid);
+ if (cos != NULL)
+ cosname = cos->cos_name;
+ spa_cos_exit(spa);
+ }
+
+ if (error == 0)
+ error = spa_cos_prop_get(spa, cosname, &nvp);
+
+ spa_close(spa, FTAG);
+
+ if (error == 0 && zc->zc_nvlist_dst != NULL)
+ error = put_nvlist(zc, nvp);
+ else
+ error = EFAULT;
+
+ nvlist_free(nvp);
+ return (error);
+}
+
/*
* innvl: {
* "fd" -> file descriptor to write stream to (int32)
* (optional) "fromsnap" -> full snap name to send an incremental from
* (optional) "largeblockok" -> (value ignored)
@@ -5658,10 +6506,224 @@
dsl_dataset_rele(tosnap, FTAG);
dsl_pool_rele(dp, FTAG);
return (error);
}
+typedef struct dp_cursor_cb_arg {
+ nvlist_t *outnvl;
+ uint64_t offset;
+ uint32_t count;
+ uint32_t skip;
+ boolean_t verbose;
+ boolean_t snaps;
+} dp_cursor_cb_arg_t;
+
+/* ARGSUSED */
+int
+ds_cursor_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
+{
+ int error;
+ char dsname[MAXNAMELEN];
+ objset_t *osp;
+
+ dp_cursor_cb_arg_t *cb = (dp_cursor_cb_arg_t *)arg;
+
+ dsl_dataset_name(ds, dsname);
+ nvlist_t *nv = fnvlist_alloc();
+
+ fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_GUID),
+ dsl_dataset_phys(ds)->ds_guid);
+
+ if (cb->verbose) {
+ uint64_t refd, avail, uobjs, aobjs;
+ dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs);
+
+ fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_AVAILABLE),
+ avail);
+ fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_REFERENCED),
+ refd);
+ fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_USED),
+ dsl_dir_phys(ds->ds_dir)->dd_used_bytes);
+ }
+
+ error = dmu_objset_from_ds(ds, &osp);
+
+ if (error == 0)
+ fnvlist_add_uint64(nv, zfs_prop_to_name(ZFS_PROP_TYPE),
+ dmu_objset_type(osp));
+
+ fnvlist_add_nvlist(cb->outnvl, dsname, nv);
+ nvlist_free(nv);
+ return (error);
+}
+
+int
+dmu_objset_find_dp_cursor(dsl_pool_t *dp, uint64_t ddobj,
+ int func(dsl_pool_t *, dsl_dataset_t *, void *), void *arg)
+{
+ dsl_dir_t *dd;
+ dsl_dataset_t *ds;
+ zap_cursor_t zc;
+ zap_attribute_t *attr;
+ uint64_t thisobj;
+ int error, i;
+
+ dp_cursor_cb_arg_t *cb = (dp_cursor_cb_arg_t *)arg;
+
+ ASSERT(dsl_pool_config_held(dp));
+ error = dsl_dir_hold_obj(dp, ddobj, NULL, FTAG, &dd);
+ thisobj = dsl_dir_phys(dd)->dd_head_dataset_obj;
+
+ if (error != 0)
+ return (error);
+
+ attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
+
+ /* we are interrestd in filesytems and volumes */
+ if (!cb->snaps) {
+
+ /* init the cursor at given offset */
+ zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
+ dsl_dir_phys(dd)->dd_child_dir_zapobj, cb->offset);
+
+
+ for (i = 0; i < cb->skip; i++) {
+ zap_cursor_advance(&zc);
+ if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+ error = ENOENT;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < cb->count; i++) {
+ zap_cursor_advance(&zc);
+ if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+ error = ENOENT;
+ goto out;
+ }
+
+ ASSERT3U(attr->za_integer_length, ==,
+ sizeof (uint64_t));
+ ASSERT3U(attr->za_num_integers, ==, 1);
+ /* recursivly walk objects skipping $MOS and $ORIGIN */
+ error = dmu_objset_find_dp(dp, attr->za_first_integer,
+ func, arg, 0);
+ if (error != 0)
+ break;
+ }
+ } else {
+
+ dsl_dataset_t *ds;
+
+ error = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds);
+
+ if (error == 0) {
+
+ dsl_dataset_rele(ds, FTAG);
+ zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
+ dsl_dataset_phys(ds)->ds_snapnames_zapobj,
+ cb->offset);
+
+ for (i = 0; i < cb->skip; i++) {
+ zap_cursor_advance(&zc);
+ if ((zap_cursor_retrieve(&zc,
+ attr) != 0)) {
+ error = ENOENT;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < cb->count; i++) {
+ zap_cursor_advance(&zc);
+ if ((zap_cursor_retrieve(&zc, attr) != 0)) {
+ error = ENOENT;
+ goto out;
+
+ }
+
+ ASSERT3U(attr->za_integer_length, ==,
+ sizeof (uint64_t));
+ ASSERT3U(attr->za_num_integers, ==, 1);
+
+ error = dsl_dataset_hold_obj(dp,
+ attr->za_first_integer, FTAG, &ds);
+ if (error != 0)
+ break;
+ error = func(dp, ds, arg);
+ dsl_dataset_rele(ds, FTAG);
+ if (error != 0)
+ break;
+ }
+ }
+ }
+out:
+ cb->offset = zap_cursor_serialize(&zc);
+ zap_cursor_fini(&zc);
+ dsl_dir_rele(dd, FTAG);
+ kmem_free(attr, sizeof (zap_attribute_t));
+
+ /* return self as the last dataset */
+ if (error == ENOENT) {
+ if ((error = dsl_dataset_hold_obj(dp, thisobj, FTAG, &ds)) != 0)
+ return (error);
+ error = func(dp, ds, arg);
+ dsl_dataset_rele(ds, FTAG);
+ if (error)
+ return (error);
+ error = ENOENT;
+ }
+
+ return (error);
+}
+
+
+/*
+ * We want to list all dataset under the given name. Optionally, we advance the
+ * ZAP cursor "skip" times and retrieve "count" datasets. We return the offset
+ * so the user can start the next invocation where he left off.
+ */
+
+static int
+zfs_ioc_list_from_cursor(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
+{
+
+ dsl_pool_t *dp;
+ dsl_dataset_t *ds;
+
+ int error;
+
+ dp_cursor_cb_arg_t cb_args;
+
+ if ((strchr(name, '@') != NULL))
+ return (EINVAL);
+
+ if ((error = dsl_pool_hold(name, FTAG, &dp)) != 0)
+ return (error);
+
+ if ((error = dsl_dataset_hold(dp, name, FTAG, &ds)) != 0) {
+ dsl_pool_rele(dp, FTAG);
+ return (error);
+ }
+
+ (void) nvlist_lookup_uint32(innvl, "count", &cb_args.count);
+ (void) nvlist_lookup_uint32(innvl, "skip", &cb_args.skip);
+ (void) nvlist_lookup_uint64(innvl, "offset", &cb_args.offset);
+ (void) nvlist_lookup_boolean_value(innvl, "verbose", &cb_args.verbose);
+ (void) nvlist_lookup_boolean_value(innvl, "snaps", &cb_args.snaps);
+
+ cb_args.outnvl = outnvl;
+ error = dmu_objset_find_dp_cursor(dp, ds->ds_dir->dd_object,
+ &ds_cursor_cb, &cb_args);
+
+ fnvlist_add_uint64(outnvl, "offset", cb_args.offset);
+ dsl_dataset_rele(ds, FTAG);
+ dsl_pool_rele(dp, FTAG);
+
+ return (error);
+}
+
+
static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST];
static void
zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck,
@@ -5763,13 +6825,372 @@
{
zfs_ioctl_register_legacy(ioc, func, secpolicy,
DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
}
+/*
+ * Appearing to take poolname as a parameter is a concession to the ioctl
+ * handler. Leading underbar for generation idea nvpair exists only on output to
+ * avoid pool name conflict.
+ */
+/* ARGSUSED */
+static int
+zfs_ioc_pool_configs_nvl(const char *poolname, nvlist_t *innvl,
+ nvlist_t *outnvl)
+{
+ nvlist_t *configs;
+ uint64_t generation;
+
+ if (nvlist_lookup_uint64(innvl, "generation", &generation) != 0)
+ return (SET_ERROR(EINVAL));
+
+ if ((configs = spa_all_configs(&generation)) == NULL)
+ return (SET_ERROR(EEXIST));
+
+ fnvlist_merge(outnvl, configs);
+ nvlist_free(configs);
+ fnvlist_add_uint64(outnvl, "_generation", generation);
+
+ return (0);
+}
+
+/*
+ * Ask spa for pool statistics. If we get a non-NULL config but a non-zero
+ * return from spa, we return EAGAIN to hint to callers that we've retrieved
+ * a config for a faulted pool. We take no arguments but declare otherwise to
+ * suit the ioctl handler's pattern. Similar considerations apply to outnvl as a
+ * single pointer that has to be merged with config allocated or nulled by spa.
+ */
+static int
+zfs_ioc_pool_stats_nvl(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ nvlist_t *config;
+ int error;
+ int ret = 0;
+
+ ASSERT3P(innvl, ==, NULL);
+ error = spa_get_stats(poolname, &config, NULL, 0);
+ ASSERT3U(error, !=, EAGAIN);
+
+ if (config != NULL) {
+ fnvlist_merge(outnvl, config);
+ nvlist_free(config);
+ if (error)
+ ret = SET_ERROR(EAGAIN);
+ } else {
+ ret = error;
+ }
+
+ return (ret);
+}
+
+static nvlist_t *
+objset_stats2nv(dmu_objset_stats_t *stat)
+{
+ nvlist_t *statlist = fnvlist_alloc();
+
+ fnvlist_add_uint64(statlist, "dds_num_clones", stat->dds_num_clones);
+ fnvlist_add_uint64(statlist, "dds_creation_txg",
+ stat->dds_creation_txg);
+ fnvlist_add_uint64(statlist, "dds_guid", stat->dds_guid);
+ fnvlist_add_uint8(statlist, "dds_type", (uint8_t)stat->dds_type);
+ fnvlist_add_uint8(statlist, "dds_is_snapshot", stat->dds_is_snapshot);
+ fnvlist_add_uint8(statlist, "dds_inconsistent",
+ stat->dds_inconsistent);
+ fnvlist_add_string(statlist, "dds_origin", stat->dds_origin);
+
+ return (statlist);
+}
+
+/* Given an objset, retrieve stats and props by adding them to the output nvl */
+static int
+objset_render(objset_t *os, nvlist_t *outnvl)
+{
+ int error = 0;
+ nvlist_t *props = NULL, *statlist = NULL;
+ dmu_objset_stats_t stats;
+
+ dmu_objset_fast_stat(os, &stats);
+
+ if ((error = dsl_prop_get_all(os, &props)) == 0) {
+ dmu_objset_stats(os, props);
+ /*
+ * NB: zvol_get_stats() will read the objset contents,
+ * which we aren't supposed to do with a
+ * DS_MODE_USER hold, because it could be
+ * inconsistent. So this is a bit of a workaround...
+ * XXX reading with out owning
+ */
+ if (!stats.dds_inconsistent &&
+ dmu_objset_type(os) == DMU_OST_ZVOL) {
+ error = zvol_get_stats(os, props);
+ if (error == EIO)
+ goto out;
+ VERIFY0(error);
+ }
+ fnvlist_add_nvlist(outnvl, "props", props);
+ statlist = objset_stats2nv(&stats);
+ fnvlist_add_nvlist(outnvl, "stats", statlist);
+ nvlist_free(statlist);
+ }
+
+out:
+ nvlist_free(props);
+ return (error);
+}
+
+/*
+ * Note: this IOC can be called internally by other IOCs as an existence
+ * check against race conditions. Given a dataset name, return its stats
+ * and props. Optionally we can verify type, which simplifies things for
+ * callers that may not want to parse stats for themselves (and may discard
+ * the outnvl in handlers).
+ */
+static int
+zfs_ioc_objset_stats_nvl(const char *data, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ objset_t *os;
+ int error;
+ dmu_objset_type_t checktype = DMU_OST_ANY;
+ boolean_t gettype = B_FALSE;
+
+ if (innvl != NULL) {
+ if (nvlist_lookup_uint8(innvl, "type", (uint8_t *)&checktype)
+ == 0)
+ gettype = B_TRUE;
+ }
+ if ((error = dmu_objset_hold(data, FTAG, &os)) == 0) {
+ error = objset_render(os, outnvl);
+ dmu_objset_rele(os, FTAG);
+
+ if (error == 0) {
+ nvlist_t *statlist;
+ dmu_objset_type_t type;
+ statlist = fnvlist_lookup_nvlist(outnvl, "stats");
+ type = fnvlist_lookup_uint8_t(statlist, "dds_type");
+ if (checktype != DMU_OST_ANY && type != checktype) {
+ error = EEXIST;
+ fnvlist_remove(outnvl, "stats");
+ fnvlist_remove(outnvl, "props");
+ }
+ if (gettype)
+ fnvlist_add_uint8(outnvl, "type", type);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Given a dataset name and an innvl containing a DMU cursor offset, find the
+ * next child dataset, and return its name, stats, and props and an updated
+ * cursor.
+ */
+static int
+zfs_ioc_dataset_list_next_nvl(const char *data, nvlist_t *innvl,
+ nvlist_t *outnvl)
+{
+ objset_t *os;
+ int error;
+ uint64_t off;
+ char *p, *nextds;
+ char name[MAXNAMELEN];
+ size_t len;
+ size_t orig_len = strlen(data);
+
+ if (innvl == NULL ||
+ nvlist_lookup_uint64(innvl, "offset", &off) != 0)
+ return (SET_ERROR(EINVAL));
+
+ (void) strlcpy(name, data, sizeof (name));
+top:
+ if (error = dmu_objset_hold(name, FTAG, &os)) {
+ if (error == ENOENT)
+ error = SET_ERROR(ESRCH);
+ return (error);
+ }
+
+ p = strrchr(name, '/');
+ if (p == NULL || p[1] != '\0') {
+ if ((len = strlcat(name, "/", sizeof (name))) >= MAXNAMELEN) {
+ dmu_objset_rele(os, FTAG);
+ return (SET_ERROR(ESRCH));
+ }
+ } else {
+ len = orig_len;
+ }
+ p = name + len;
+
+ do {
+ error = dmu_dir_list_next(os, sizeof (name) - len, p, NULL,
+ &off);
+ if (error == ENOENT)
+ error = ESRCH;
+ } while (error == 0 && dataset_name_hidden(name));
+ dmu_objset_rele(os, FTAG);
+
+ /*
+ * If it's an internal dataset (ie. with a '$' in its name),
+ * don't try to get stats for it, otherwise we'll return ENOENT.
+ */
+ if (error == 0 && strchr(name, '$') == NULL) {
+ error = zfs_ioc_objset_stats_nvl(name, NULL, outnvl);
+ if (error == ENOENT) {
+ /* We lost a race with destroy, get the next one. */
+ name[orig_len] = '\0';
+ goto top;
+ }
+ len = strlen(name) + 1;
+ nextds = kmem_alloc(len, KM_SLEEP);
+ (void) strlcpy(nextds, name, len);
+ fnvlist_add_string(outnvl, "nextds", (const char *)nextds);
+ fnvlist_add_uint64(outnvl, "offset", off);
+ }
+
+ return (error);
+}
+
+/*
+ * Given a dataset name and a DMU cursor offset, find its next snapshot, and
+ * return its name, props, and stats and an updated cursor offset.
+ */
+static int
+zfs_ioc_snapshot_list_next_nvl(const char *data, nvlist_t *innvl,
+ nvlist_t *outnvl)
+{
+ objset_t *os;
+ int error;
+ uint64_t off, obj;
+ char name[MAXNAMELEN], *nextsnap;
+ size_t len;
+
+ if (innvl == NULL ||
+ nvlist_lookup_uint64(innvl, "offset", &off) != 0)
+ return (SET_ERROR(EINVAL));
+
+ error = dmu_objset_hold(data, FTAG, &os);
+ if (error != 0) {
+ return (error == ENOENT ? ESRCH : error);
+ }
+
+ /*
+ * A dataset name of maximum length cannot have any snapshots,
+ * so exit immediately.
+ */
+ (void) strlcpy(name, data, sizeof (name));
+ if ((len = strlcat(name, "@", sizeof (name))) >= MAXNAMELEN) {
+ dmu_objset_rele(os, FTAG);
+ return (SET_ERROR(ESRCH));
+ }
+
+ /* Rest of name buffer is passed so snap ID can be appended. */
+ error = dmu_snapshot_list_next(os, sizeof (name) - len, name + len,
+ &obj, &off, NULL);
+
+ if (error == 0) {
+ dsl_dataset_t *ds;
+ dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
+
+ error = dsl_dataset_hold_obj(dp, obj, FTAG, &ds);
+ if (error == 0) {
+ objset_t *ossnap;
+
+ error = dmu_objset_from_ds(ds, &ossnap);
+ if (error == 0)
+ error = objset_render(ossnap, outnvl);
+ dsl_dataset_rele(ds, FTAG);
+ }
+ } else if (error == ENOENT) {
+ error = ESRCH;
+ }
+
+ dmu_objset_rele(os, FTAG);
+
+ if (error == 0) {
+ len = strlen(name) + 1;
+ nextsnap = kmem_alloc(len, KM_SLEEP);
+ (void) strlcpy(nextsnap, name, len);
+ fnvlist_add_string(outnvl, "nextsnap", (const char *)nextsnap);
+ fnvlist_add_uint64(outnvl, "offset", off);
+ }
+ return (error);
+}
+
+static int
+zfs_ioc_pool_get_props_nvl(const char *poolname, nvlist_t *innvl,
+ nvlist_t *outnvl)
+{
+ spa_t *spa;
+ int error;
+ nvlist_t *props = NULL;
+
+ ASSERT3P(innvl, ==, NULL);
+ if ((error = spa_open(poolname, &spa, FTAG)) != 0) {
+ /*
+ * If the pool is faulted, there may be properties we can still
+ * get (such as altroot and cachefile), so attempt to get them
+ * anyway.
+ */
+ mutex_enter(&spa_namespace_lock);
+ if ((spa = spa_lookup(poolname)) != NULL)
+ error = spa_prop_get(spa, &props);
+ mutex_exit(&spa_namespace_lock);
+ } else {
+ error = spa_prop_get(spa, &props);
+ spa_close(spa, FTAG);
+ }
+
+ if (props != NULL) {
+ fnvlist_merge(outnvl, props);
+ nvlist_free(props);
+ } else {
+ ASSERT3S(error, !=, 0);
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+zfs_ioc_check_krrp(const char *dataset, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ spa_t *spa;
+ int err;
+
+ /*
+ * Here we use different way to open spa for the given pool,
+ * because the pool maybe faulted
+ */
+
+ mutex_enter(&spa_namespace_lock);
+ if ((spa = spa_lookup(dataset)) == NULL) {
+ mutex_exit(&spa_namespace_lock);
+ /* From KRRP side everything nice */
+ return (0);
+ }
+
+ spa_open_ref(spa, FTAG);
+ mutex_exit(&spa_namespace_lock);
+
+ err = autosnap_check_for_destroy(spa_get_autosnap(spa), dataset);
+ if (err == 0)
+ err = ENOTSUP;
+
+ mutex_enter(&spa_namespace_lock);
+ spa_close(spa, FTAG);
+ mutex_exit(&spa_namespace_lock);
+
+ return (err != 0 ? SET_ERROR(err) : 0);
+}
+
static void
zfs_ioctl_init(void)
{
+ zfs_ioctl_register("bulk_list", ZFS_IOC_BULK_LIST,
+ zfs_ioc_list_from_cursor, zfs_secpolicy_read,
+ DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE);
+
zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT,
zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY,
@@ -5794,18 +7215,44 @@
zfs_ioctl_register("clone", ZFS_IOC_CLONE,
zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
- zfs_ioctl_register("remap", ZFS_IOC_REMAP,
- zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME,
- POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE);
-
zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS,
zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
+ zfs_ioctl_register("check_krrp", ZFS_IOC_CHECK_KRRP,
+ zfs_ioc_check_krrp, zfs_secpolicy_read, DATASET_NAME,
+ POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+ zfs_ioctl_register("pool_stats_nvl", ZFS_IOC_POOL_STATS_NVL,
+ zfs_ioc_pool_stats_nvl, zfs_secpolicy_read, POOL_NAME,
+ POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+ zfs_ioctl_register("pool_configs_nvl", ZFS_IOC_POOL_CONFIGS_NVL,
+ zfs_ioc_pool_configs_nvl, zfs_secpolicy_none, NO_NAME,
+ POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+ zfs_ioctl_register("pool_get_props_nvl", ZFS_IOC_POOL_GET_PROPS_NVL,
+ zfs_ioc_pool_get_props_nvl, zfs_secpolicy_read, POOL_NAME,
+ POOL_CHECK_NONE, B_FALSE, B_FALSE);
+
+ zfs_ioctl_register("objset_stats_nvl", ZFS_IOC_OBJSET_STATS_NVL,
+ zfs_ioc_objset_stats_nvl, zfs_secpolicy_read, DATASET_NAME,
+ POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE);
+
+ zfs_ioctl_register("dataset_list_next_nvl",
+ ZFS_IOC_DATASET_LIST_NEXT_NVL, zfs_ioc_dataset_list_next_nvl,
+ zfs_secpolicy_read, DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE,
+ B_FALSE);
+
+ zfs_ioctl_register("snapshot_list_next_nvl",
+ ZFS_IOC_SNAPSHOT_LIST_NEXT_NVL, zfs_ioc_snapshot_list_next_nvl,
+ zfs_secpolicy_read, DATASET_NAME, POOL_CHECK_SUSPENDED, B_FALSE,
+ B_FALSE);
+
zfs_ioctl_register("hold", ZFS_IOC_HOLD,
zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
zfs_ioctl_register("release", ZFS_IOC_RELEASE,
zfs_ioc_release, zfs_secpolicy_release, POOL_NAME,
@@ -5835,19 +7282,26 @@
zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM,
zfs_ioc_channel_program, zfs_secpolicy_config,
POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE,
B_TRUE);
+ zfs_ioctl_register("set_props_mds", ZFS_IOC_SET_PROPS_MDS,
+ zfs_ioc_set_prop_mds, zfs_secpolicy_config,
+ POOL_NAME,
+ POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
+
/* IOCTLS that use the legacy function signature */
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY);
zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create,
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN,
zfs_ioc_pool_scan);
+ zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_TRIM,
+ zfs_ioc_pool_trim);
zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE,
zfs_ioc_pool_upgrade);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD,
zfs_ioc_vdev_add);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE,
@@ -5856,10 +7310,12 @@
zfs_ioc_vdev_set_state);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH,
zfs_ioc_vdev_attach);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH,
zfs_ioc_vdev_detach);
+ zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETL2ADDDT,
+ zfs_ioc_vdev_setl2adddt);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH,
zfs_ioc_vdev_setpath);
zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU,
zfs_ioc_vdev_setfru);
zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS,
@@ -5906,14 +7362,27 @@
zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import,
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear,
- zfs_secpolicy_config, B_TRUE, POOL_CHECK_READONLY);
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen,
zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
-
+ zfs_ioctl_register_pool(ZFS_IOC_VDEV_SET_PROPS, zfs_ioc_vdev_set_props,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_VDEV_GET_PROPS, zfs_ioc_vdev_get_props,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_COS_ALLOC, zfs_ioc_cos_alloc,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_COS_FREE, zfs_ioc_cos_free,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_COS_LIST, zfs_ioc_cos_list,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_COS_SET_PROPS, zfs_ioc_cos_set_props,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
+ zfs_ioctl_register_pool(ZFS_IOC_COS_GET_PROPS, zfs_ioc_cos_get_props,
+ zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED);
zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN,
zfs_ioc_space_written);
zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS,
zfs_ioc_objset_recvd_props);
zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ,
@@ -6401,10 +7870,14 @@
error = ldi_ident_from_mod(&modlinkage, &zfs_li);
ASSERT(error == 0);
mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
+ if (sysevent_evc_bind(ZFS_EVENT_CHANNEL, &zfs_channel,
+ EVCH_HOLD_PEND | EVCH_CREAT) != 0)
+ cmn_err(CE_NOTE, "Failed to bind to zfs event channel");
+
return (0);
}
int
_fini(void)
@@ -6430,10 +7903,13 @@
tsd_destroy(&zfs_fsyncer_key);
ldi_ident_release(zfs_li);
zfs_li = NULL;
mutex_destroy(&zfs_share_lock);
+ if (zfs_channel)
+ (void) sysevent_evc_unbind(zfs_channel);
+
return (error);
}
int
_info(struct modinfo *modinfop)