* Copyright (c) 2014 Integros [integros.com]
* Copyright 2016 Toomas Soome <tsoome@me.com>
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
- * Copyright (c) 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
- * Copyright (c) 2017 Datto Inc. All rights reserved.
+ * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
* Copyright 2017 RackTop Systems.
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
+ * Copyright (c) 2019 Datto Inc.
*/
/*
#include <sys/fm/util.h>
#include <sys/dsl_crypt.h>
+#include <sys/dmu_recv.h>
#include <sys/dmu_send.h>
#include <sys/dsl_destroy.h>
#include <sys/dsl_bookmark.h>
#include <sys/zio_checksum.h>
#include <sys/vdev_removal.h>
#include <sys/zfs_sysfs.h>
+#include <sys/vdev_impl.h>
+#include <sys/vdev_initialize.h>
+#include <sys/vdev_trim.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
ZFS_DELEG_PERM_SEND, cr));
}
-#ifdef HAVE_SMB_SHARE
-/* ARGSUSED */
-static int
-zfs_secpolicy_deleg_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
-{
- vnode_t *vp;
- int error;
-
- if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
- NO_FOLLOW, NULL, &vp)) != 0)
- return (error);
-
- /* Now make sure mntpnt and dataset are ZFS */
-
- if (vp->v_vfsp->vfs_fstype != zfsfstype ||
- (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
- zc->zc_name) != 0)) {
- VN_RELE(vp);
- return (SET_ERROR(EPERM));
- }
-
- VN_RELE(vp);
- return (dsl_deleg_access(zc->zc_name,
- ZFS_DELEG_PERM_SHARE, cr));
-}
-#endif /* HAVE_SMB_SHARE */
-
int
zfs_secpolicy_share(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
-#ifdef HAVE_SMB_SHARE
- if (!INGLOBALZONE(curproc))
- return (SET_ERROR(EPERM));
-
- if (secpolicy_nfs(cr) == 0) {
- return (0);
- } else {
- return (zfs_secpolicy_deleg_share(zc, innvl, cr));
- }
-#else
return (SET_ERROR(ENOTSUP));
-#endif /* HAVE_SMB_SHARE */
}
int
zfs_secpolicy_smb_acl(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
-#ifdef HAVE_SMB_SHARE
- if (!INGLOBALZONE(curproc))
- return (SET_ERROR(EPERM));
-
- if (secpolicy_smb(cr) == 0) {
- return (0);
- } else {
- return (zfs_secpolicy_deleg_share(zc, innvl, cr));
- }
-#else
return (SET_ERROR(ENOTSUP));
-#endif /* HAVE_SMB_SHARE */
}
static int
* inputs:
* zc_name name of filesystem
* zc_cookie zap cursor
- * zc_nvlist_dst_size size of buffer for property nvlist
+ * zc_nvlist_src iteration range nvlist
+ * zc_nvlist_src_size size of iteration range nvlist
*
* outputs:
* zc_name name of next snapshot
static int
zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
{
- objset_t *os;
int error;
+ objset_t *os, *ossnap;
+ dsl_dataset_t *ds;
+ uint64_t min_txg = 0, max_txg = 0;
+
+ if (zc->zc_nvlist_src_size != 0) {
+ nvlist_t *props = NULL;
+ error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &props);
+ if (error != 0)
+ return (error);
+ (void) nvlist_lookup_uint64(props, SNAP_ITER_MIN_TXG,
+ &min_txg);
+ (void) nvlist_lookup_uint64(props, SNAP_ITER_MAX_TXG,
+ &max_txg);
+ nvlist_free(props);
+ }
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error != 0) {
return (SET_ERROR(ESRCH));
}
- error = dmu_snapshot_list_next(os,
- sizeof (zc->zc_name) - strlen(zc->zc_name),
- zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
- NULL);
+ while (error == 0) {
+ if (issig(JUSTLOOKING) && issig(FORREAL)) {
+ error = SET_ERROR(EINTR);
+ break;
+ }
- if (error == 0 && !zc->zc_simple) {
- dsl_dataset_t *ds;
- dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
+ error = dmu_snapshot_list_next(os,
+ sizeof (zc->zc_name) - strlen(zc->zc_name),
+ zc->zc_name + strlen(zc->zc_name), &zc->zc_obj,
+ &zc->zc_cookie, NULL);
+ if (error == ENOENT) {
+ error = SET_ERROR(ESRCH);
+ break;
+ } else if (error != 0) {
+ break;
+ }
- error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
- if (error == 0) {
- objset_t *ossnap;
+ error = dsl_dataset_hold_obj(dmu_objset_pool(os), zc->zc_obj,
+ FTAG, &ds);
+ if (error != 0)
+ break;
- error = dmu_objset_from_ds(ds, &ossnap);
- if (error == 0)
- error = zfs_ioc_objset_stats_impl(zc, ossnap);
+ if ((min_txg != 0 && dsl_get_creationtxg(ds) < min_txg) ||
+ (max_txg != 0 && dsl_get_creationtxg(ds) > max_txg)) {
dsl_dataset_rele(ds, FTAG);
+ /* undo snapshot name append */
+ *(strchr(zc->zc_name, '@') + 1) = '\0';
+ /* skip snapshot */
+ continue;
}
- } else if (error == ENOENT) {
- error = SET_ERROR(ESRCH);
+
+ if (zc->zc_simple) {
+ dsl_dataset_rele(ds, FTAG);
+ break;
+ }
+
+ if ((error = dmu_objset_from_ds(ds, &ossnap)) != 0) {
+ dsl_dataset_rele(ds, FTAG);
+ break;
+ }
+ if ((error = zfs_ioc_objset_stats_impl(zc, ossnap)) != 0) {
+ dsl_dataset_rele(ds, FTAG);
+ break;
+ }
+ dsl_dataset_rele(ds, FTAG);
+ break;
}
dmu_objset_rele(os, FTAG);
ASSERT(zplprops != NULL);
+ /* parent dataset must be a filesystem */
if (os != NULL && os->os_phys->os_type != DMU_OST_ZFS)
- return (SET_ERROR(EINVAL));
+ return (SET_ERROR(ZFS_ERR_WRONG_PARENT));
/*
* Pull out creator prop choices, if any.
uint64_t zplver = ZPL_VERSION;
objset_t *os = NULL;
char parentname[ZFS_MAX_DATASET_NAME_LEN];
- char *cp;
spa_t *spa;
uint64_t spa_vers;
int error;
- (void) strlcpy(parentname, dataset, sizeof (parentname));
- cp = strrchr(parentname, '/');
- ASSERT(cp != NULL);
- cp[0] = '\0';
+ zfs_get_parent(dataset, parentname, sizeof (parentname));
if ((error = spa_open(dataset, &spa, FTAG)) != 0)
return (error);
/*
* inputs:
* zc_name name of dataset to destroy
- * zc_objset_type type of objset
* zc_defer_destroy mark for deferred destroy
*
* outputs: none
static int
zfs_ioc_destroy(zfs_cmd_t *zc)
{
+ objset_t *os;
+ dmu_objset_type_t ost;
int err;
- if (zc->zc_objset_type == DMU_OST_ZFS)
+ err = dmu_objset_hold(zc->zc_name, FTAG, &os);
+ if (err != 0)
+ return (err);
+ ost = dmu_objset_type(os);
+ dmu_objset_rele(os, FTAG);
+
+ if (ost == DMU_OST_ZFS)
zfs_unmount_snap(zc->zc_name);
if (strchr(zc->zc_name, '@')) {
return (err);
}
+/*
+ * innvl: {
+ * "initialize_command" -> POOL_INITIALIZE_{CANCEL|START|SUSPEND} (uint64)
+ * "initialize_vdevs": { -> guids to initialize (nvlist)
+ * "vdev_path_1": vdev_guid_1, (uint64),
+ * "vdev_path_2": vdev_guid_2, (uint64),
+ * ...
+ * },
+ * }
+ *
+ * outnvl: {
+ * "initialize_vdevs": { -> initialization errors (nvlist)
+ * "vdev_path_1": errno, see function body for possible errnos (uint64)
+ * "vdev_path_2": errno, ... (uint64)
+ * ...
+ * }
+ * }
+ *
+ * EINVAL is returned for an unknown commands or if any of the provided vdev
+ * guids have be specified with a type other than uint64.
+ */
+static const zfs_ioc_key_t zfs_keys_pool_initialize[] = {
+ {ZPOOL_INITIALIZE_COMMAND, DATA_TYPE_UINT64, 0},
+ {ZPOOL_INITIALIZE_VDEVS, DATA_TYPE_NVLIST, 0}
+};
+
+static int
+zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ uint64_t cmd_type;
+ if (nvlist_lookup_uint64(innvl, ZPOOL_INITIALIZE_COMMAND,
+ &cmd_type) != 0) {
+ return (SET_ERROR(EINVAL));
+ }
+
+ if (!(cmd_type == POOL_INITIALIZE_CANCEL ||
+ cmd_type == POOL_INITIALIZE_START ||
+ cmd_type == POOL_INITIALIZE_SUSPEND)) {
+ return (SET_ERROR(EINVAL));
+ }
+
+ nvlist_t *vdev_guids;
+ if (nvlist_lookup_nvlist(innvl, ZPOOL_INITIALIZE_VDEVS,
+ &vdev_guids) != 0) {
+ return (SET_ERROR(EINVAL));
+ }
+
+ for (nvpair_t *pair = nvlist_next_nvpair(vdev_guids, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(vdev_guids, pair)) {
+ uint64_t vdev_guid;
+ if (nvpair_value_uint64(pair, &vdev_guid) != 0) {
+ return (SET_ERROR(EINVAL));
+ }
+ }
+
+ spa_t *spa;
+ int error = spa_open(poolname, &spa, FTAG);
+ if (error != 0)
+ return (error);
+
+ nvlist_t *vdev_errlist = fnvlist_alloc();
+ int total_errors = spa_vdev_initialize(spa, vdev_guids, cmd_type,
+ vdev_errlist);
+
+ if (fnvlist_size(vdev_errlist) > 0) {
+ fnvlist_add_nvlist(outnvl, ZPOOL_INITIALIZE_VDEVS,
+ vdev_errlist);
+ }
+ fnvlist_free(vdev_errlist);
+
+ spa_close(spa, FTAG);
+ return (total_errors > 0 ? EINVAL : 0);
+}
+
+/*
+ * innvl: {
+ * "trim_command" -> POOL_TRIM_{CANCEL|START|SUSPEND} (uint64)
+ * "trim_vdevs": { -> guids to TRIM (nvlist)
+ * "vdev_path_1": vdev_guid_1, (uint64),
+ * "vdev_path_2": vdev_guid_2, (uint64),
+ * ...
+ * },
+ * "trim_rate" -> Target TRIM rate in bytes/sec.
+ * "trim_secure" -> Set to request a secure TRIM.
+ * }
+ *
+ * outnvl: {
+ * "trim_vdevs": { -> TRIM errors (nvlist)
+ * "vdev_path_1": errno, see function body for possible errnos (uint64)
+ * "vdev_path_2": errno, ... (uint64)
+ * ...
+ * }
+ * }
+ *
+ * EINVAL is returned for an unknown commands or if any of the provided vdev
+ * guids have be specified with a type other than uint64.
+ */
+static const zfs_ioc_key_t zfs_keys_pool_trim[] = {
+ {ZPOOL_TRIM_COMMAND, DATA_TYPE_UINT64, 0},
+ {ZPOOL_TRIM_VDEVS, DATA_TYPE_NVLIST, 0},
+ {ZPOOL_TRIM_RATE, DATA_TYPE_UINT64, ZK_OPTIONAL},
+ {ZPOOL_TRIM_SECURE, DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
+};
+
+static int
+zfs_ioc_pool_trim(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
+{
+ uint64_t cmd_type;
+ if (nvlist_lookup_uint64(innvl, ZPOOL_TRIM_COMMAND, &cmd_type) != 0)
+ return (SET_ERROR(EINVAL));
+
+ if (!(cmd_type == POOL_TRIM_CANCEL ||
+ cmd_type == POOL_TRIM_START ||
+ cmd_type == POOL_TRIM_SUSPEND)) {
+ return (SET_ERROR(EINVAL));
+ }
+
+ nvlist_t *vdev_guids;
+ if (nvlist_lookup_nvlist(innvl, ZPOOL_TRIM_VDEVS, &vdev_guids) != 0)
+ return (SET_ERROR(EINVAL));
+
+ for (nvpair_t *pair = nvlist_next_nvpair(vdev_guids, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(vdev_guids, pair)) {
+ uint64_t vdev_guid;
+ if (nvpair_value_uint64(pair, &vdev_guid) != 0) {
+ return (SET_ERROR(EINVAL));
+ }
+ }
+
+ /* Optional, defaults to maximum rate when not provided */
+ uint64_t rate;
+ if (nvlist_lookup_uint64(innvl, ZPOOL_TRIM_RATE, &rate) != 0)
+ rate = 0;
+
+ /* Optional, defaults to standard TRIM when not provided */
+ boolean_t secure;
+ if (nvlist_lookup_boolean_value(innvl, ZPOOL_TRIM_SECURE,
+ &secure) != 0) {
+ secure = B_FALSE;
+ }
+
+ spa_t *spa;
+ int error = spa_open(poolname, &spa, FTAG);
+ if (error != 0)
+ return (error);
+
+ nvlist_t *vdev_errlist = fnvlist_alloc();
+ int total_errors = spa_vdev_trim(spa, vdev_guids, cmd_type,
+ rate, !!zfs_trim_metaslab_skip, secure, vdev_errlist);
+
+ if (fnvlist_size(vdev_errlist) > 0)
+ fnvlist_add_nvlist(outnvl, ZPOOL_TRIM_VDEVS, vdev_errlist);
+
+ fnvlist_free(vdev_errlist);
+
+ spa_close(spa, FTAG);
+ return (total_errors > 0 ? EINVAL : 0);
+}
+
/*
* fsname is name of dataset to rollback (to most recent snapshot)
*
static int
zfs_ioc_rename(zfs_cmd_t *zc)
{
+ objset_t *os;
+ dmu_objset_type_t ost;
boolean_t recursive = zc->zc_cookie & 1;
char *at;
+ int err;
/* "zfs rename" from and to ...%recv datasets should both fail */
zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%'))
return (SET_ERROR(EINVAL));
+ err = dmu_objset_hold(zc->zc_name, FTAG, &os);
+ if (err != 0)
+ return (err);
+ ost = dmu_objset_type(os);
+ dmu_objset_rele(os, FTAG);
+
at = strchr(zc->zc_name, '@');
if (at != NULL) {
/* snaps must be in same fs */
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) {
+ if (ost == DMU_OST_ZFS) {
error = dmu_objset_find(zc->zc_name,
recursive_unmount, at + 1,
recursive ? DS_FIND_CHILDREN : 0);
if (error != 0)
return (error);
+ /*
+ * If multihost is enabled, resuming I/O is unsafe as another
+ * host may have imported the pool.
+ */
+ if (spa_multihost(spa) && spa_suspended(spa))
+ return (SET_ERROR(EINVAL));
+
spa_vdev_state_enter(spa, SCL_NONE);
if (zc->zc_guid == 0) {
return (error);
}
-/*
- * Remove all ACL files in shares dir
- */
-#ifdef HAVE_SMB_SHARE
-static int
-zfs_smb_acl_purge(znode_t *dzp)
-{
- zap_cursor_t zc;
- zap_attribute_t zap;
- zfsvfs_t *zfsvfs = ZTOZSB(dzp);
- int error;
-
- for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
- (error = zap_cursor_retrieve(&zc, &zap)) == 0;
- zap_cursor_advance(&zc)) {
- if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
- NULL, 0)) != 0)
- break;
- }
- zap_cursor_fini(&zc);
- return (error);
-}
-#endif /* HAVE_SMB_SHARE */
-
static int
zfs_ioc_smb_acl(zfs_cmd_t *zc)
{
-#ifdef HAVE_SMB_SHARE
- vnode_t *vp;
- znode_t *dzp;
- vnode_t *resourcevp = NULL;
- znode_t *sharedir;
- zfsvfs_t *zfsvfs;
- nvlist_t *nvlist;
- char *src, *target;
- vattr_t vattr;
- vsecattr_t vsec;
- int error = 0;
-
- if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
- NO_FOLLOW, NULL, &vp)) != 0)
- return (error);
-
- /* Now make sure mntpnt and dataset are ZFS */
-
- if (vp->v_vfsp->vfs_fstype != zfsfstype ||
- (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
- zc->zc_name) != 0)) {
- VN_RELE(vp);
- return (SET_ERROR(EINVAL));
- }
-
- dzp = VTOZ(vp);
- zfsvfs = ZTOZSB(dzp);
- ZFS_ENTER(zfsvfs);
-
- /*
- * Create share dir if its missing.
- */
- mutex_enter(&zfsvfs->z_lock);
- if (zfsvfs->z_shares_dir == 0) {
- dmu_tx_t *tx;
-
- tx = dmu_tx_create(zfsvfs->z_os);
- dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
- ZFS_SHARES_DIR);
- dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
- error = dmu_tx_assign(tx, TXG_WAIT);
- if (error != 0) {
- dmu_tx_abort(tx);
- } else {
- error = zfs_create_share_dir(zfsvfs, tx);
- dmu_tx_commit(tx);
- }
- if (error != 0) {
- mutex_exit(&zfsvfs->z_lock);
- VN_RELE(vp);
- ZFS_EXIT(zfsvfs);
- return (error);
- }
- }
- mutex_exit(&zfsvfs->z_lock);
-
- ASSERT(zfsvfs->z_shares_dir);
- if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
- VN_RELE(vp);
- ZFS_EXIT(zfsvfs);
- return (error);
- }
-
- switch (zc->zc_cookie) {
- case ZFS_SMB_ACL_ADD:
- vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
- vattr.va_mode = S_IFREG|0777;
- vattr.va_uid = 0;
- vattr.va_gid = 0;
-
- vsec.vsa_mask = VSA_ACE;
- vsec.vsa_aclentp = &full_access;
- vsec.vsa_aclentsz = sizeof (full_access);
- vsec.vsa_aclcnt = 1;
-
- error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
- &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
- if (resourcevp)
- VN_RELE(resourcevp);
- break;
-
- case ZFS_SMB_ACL_REMOVE:
- error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
- NULL, 0);
- break;
-
- case ZFS_SMB_ACL_RENAME:
- if ((error = get_nvlist(zc->zc_nvlist_src,
- zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
- VN_RELE(vp);
- VN_RELE(ZTOV(sharedir));
- ZFS_EXIT(zfsvfs);
- return (error);
- }
- if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
- nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
- &target)) {
- VN_RELE(vp);
- VN_RELE(ZTOV(sharedir));
- ZFS_EXIT(zfsvfs);
- nvlist_free(nvlist);
- return (error);
- }
- error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
- kcred, NULL, 0);
- nvlist_free(nvlist);
- break;
-
- case ZFS_SMB_ACL_PURGE:
- error = zfs_smb_acl_purge(sharedir);
- break;
-
- default:
- error = SET_ERROR(EINVAL);
- break;
- }
-
- VN_RELE(vp);
- VN_RELE(ZTOV(sharedir));
-
- ZFS_EXIT(zfsvfs);
-
- return (error);
-#else
return (SET_ERROR(ENOTSUP));
-#endif /* HAVE_SMB_SHARE */
}
/*
zfs_keys_pool_discard_checkpoint,
ARRAY_SIZE(zfs_keys_pool_discard_checkpoint));
+ zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE,
+ zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME,
+ POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE,
+ zfs_keys_pool_initialize, ARRAY_SIZE(zfs_keys_pool_initialize));
+
+ zfs_ioctl_register("trim", ZFS_IOC_POOL_TRIM,
+ zfs_ioc_pool_trim, zfs_secpolicy_config, POOL_NAME,
+ POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE,
+ zfs_keys_pool_trim, ARRAY_SIZE(zfs_keys_pool_trim));
+
/* IOCTLS that use the legacy function signature */
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
};
static struct miscdevice zfs_misc = {
- .minor = ZFS_MINOR,
+ .minor = ZFS_DEVICE_MINOR,
.name = ZFS_DRIVER,
.fops = &zfsdev_fops,
};
-MODULE_ALIAS_MISCDEV(ZFS_MINOR);
+MODULE_ALIAS_MISCDEV(ZFS_DEVICE_MINOR);
MODULE_ALIAS("devname:zfs");
static int
*/
printk(KERN_INFO "ZFS: misc_register() with static minor %d "
"failed %d, retrying with MISC_DYNAMIC_MINOR\n",
- ZFS_MINOR, error);
+ ZFS_DEVICE_MINOR, error);
zfs_misc.minor = MISC_DYNAMIC_MINOR;
error = misc_register(&zfs_misc);