Changeset View
Changeset View
Standalone View
Standalone View
head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
Show All 21 Lines | |||||
/* | /* | ||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | ||||
* Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. | * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. | ||||
* Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved. | * Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved. | ||||
* Copyright 2014 Xin Li <delphij@FreeBSD.org>. All rights reserved. | * Copyright 2014 Xin Li <delphij@FreeBSD.org>. All rights reserved. | ||||
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. | * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. | ||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved. | * Copyright 2015 Nexenta Systems, Inc. All rights reserved. | ||||
* Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. | * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. | ||||
* Copyright (c) 2011, 2017 by Delphix. All rights reserved. | * Copyright (c) 2011, 2018 by Delphix. All rights reserved. | ||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved. | * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. | ||||
* Copyright (c) 2013 Steven Hartland. All rights reserved. | * Copyright (c) 2013 Steven Hartland. All rights reserved. | ||||
* Copyright (c) 2014 Integros [integros.com] | * Copyright (c) 2014 Integros [integros.com] | ||||
* Copyright 2016 Toomas Soome <tsoome@me.com> | * Copyright 2016 Toomas Soome <tsoome@me.com> | ||||
* Copyright 2017 RackTop Systems. | * Copyright 2017 RackTop Systems. | ||||
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. | * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. | ||||
* Copyright (c) 2019 Datto Inc. | * Copyright (c) 2019 Datto Inc. | ||||
*/ | */ | ||||
Show All 17 Lines | |||||
* const char *name | * const char *name | ||||
* The name of the ioctl. This is used for history logging. If the | * The name of the ioctl. This is used for history logging. If the | ||||
* ioctl returns successfully (the callback returns 0), and allow_log | * ioctl returns successfully (the callback returns 0), and allow_log | ||||
* is true, then a history log entry will be recorded with the input & | * is true, then a history log entry will be recorded with the input & | ||||
* output nvlists. The log entry can be printed with "zpool history -i". | * output nvlists. The log entry can be printed with "zpool history -i". | ||||
* | * | ||||
* zfs_ioc_t ioc | * zfs_ioc_t ioc | ||||
* The ioctl request number, which userland will pass to ioctl(2). | * The ioctl request number, which userland will pass to ioctl(2). | ||||
* The ioctl numbers can change from release to release, because | * We want newer versions of libzfs and libzfs_core to run against | ||||
* the caller (libzfs) must be matched to the kernel. | * existing zfs kernel modules (i.e. a deferred reboot after an update). | ||||
* Therefore the ioctl numbers cannot change from release to release. | |||||
* | * | ||||
* zfs_secpolicy_func_t *secpolicy | * zfs_secpolicy_func_t *secpolicy | ||||
* This function will be called before the zfs_ioc_func_t, to | * This function will be called before the zfs_ioc_func_t, to | ||||
* determine if this operation is permitted. It should return EPERM | * determine if this operation is permitted. It should return EPERM | ||||
* on failure, and 0 on success. Checks include determining if the | * on failure, and 0 on success. Checks include determining if the | ||||
* dataset is visible in this zone, and if the user has either all | * dataset is visible in this zone, and if the user has either all | ||||
* zfs privileges in the zone (SYS_MOUNT), or has been granted permission | * zfs privileges in the zone (SYS_MOUNT), or has been granted permission | ||||
* to do this operation on this dataset with "zfs allow". | * to do this operation on this dataset with "zfs allow". | ||||
Show All 9 Lines | |||||
* zfs_ioc_poolcheck_t pool_check | * zfs_ioc_poolcheck_t pool_check | ||||
* This specifies requirements on the pool state. If the pool does | * This specifies requirements on the pool state. If the pool does | ||||
* not meet them (is suspended or is readonly), the ioctl will fail | * not meet them (is suspended or is readonly), the ioctl will fail | ||||
* and the callback will not be called. If any checks are specified | * and the callback will not be called. If any checks are specified | ||||
* (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. | * (i.e. it is not POOL_CHECK_NONE), namecheck must not be NO_NAME. | ||||
* Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | | * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | | ||||
* POOL_CHECK_READONLY). | * POOL_CHECK_READONLY). | ||||
* | * | ||||
* zfs_ioc_key_t *nvl_keys | |||||
* The list of expected/allowable innvl input keys. This list is used | |||||
* to validate the nvlist input to the ioctl. | |||||
* | |||||
* boolean_t smush_outnvlist | * boolean_t smush_outnvlist | ||||
* If smush_outnvlist is true, then the output is presumed to be a | * If smush_outnvlist is true, then the output is presumed to be a | ||||
* list of errors, and it will be "smushed" down to fit into the | * list of errors, and it will be "smushed" down to fit into the | ||||
* caller's buffer, by removing some entries and replacing them with a | * caller's buffer, by removing some entries and replacing them with a | ||||
* single "N_MORE_ERRORS" entry indicating how many were removed. See | * single "N_MORE_ERRORS" entry indicating how many were removed. See | ||||
* nvlist_smush() for details. If smush_outnvlist is false, and the | * nvlist_smush() for details. If smush_outnvlist is false, and the | ||||
* outnvlist does not fit into the userland-provided buffer, then the | * outnvlist does not fit into the userland-provided buffer, then the | ||||
* ioctl will fail with ENOMEM. | * ioctl will fail with ENOMEM. | ||||
Show All 32 Lines | |||||
* the ioctl again. | * the ioctl again. | ||||
* | * | ||||
* - To return multiple errors from an ioctl which makes on-disk | * - To return multiple errors from an ioctl which makes on-disk | ||||
* changes. In this case, smush_outnvlist should be true. | * changes. In this case, smush_outnvlist should be true. | ||||
* Ioctls which make on-disk modifications should generally not | * Ioctls which make on-disk modifications should generally not | ||||
* use the outnvl if they succeed, because the caller can not | * use the outnvl if they succeed, because the caller can not | ||||
* distinguish between the operation failing, and | * distinguish between the operation failing, and | ||||
* deserialization failing. | * deserialization failing. | ||||
* | |||||
* | |||||
* IOCTL Interface Errors | |||||
* | |||||
* The following ioctl input errors can be returned: | |||||
* ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel | |||||
* ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel | |||||
* ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing | |||||
* ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type | |||||
*/ | */ | ||||
#ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
#include "opt_kstack_pages.h" | #include "opt_kstack_pages.h" | ||||
#endif | #endif | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
#include "zfs_prop.h" | #include "zfs_prop.h" | ||||
#include "zfs_deleg.h" | #include "zfs_deleg.h" | ||||
#include "zfs_comutil.h" | #include "zfs_comutil.h" | ||||
#include "zfs_ioctl_compat.h" | #include "zfs_ioctl_compat.h" | ||||
#include "lua.h" | #include "lua.h" | ||||
#include "lauxlib.h" | #include "lauxlib.h" | ||||
#ifndef ARRAY_SIZE | |||||
#define ARRAY_SIZE(x) nitems(x) | |||||
#endif | |||||
static struct cdev *zfsdev; | static struct cdev *zfsdev; | ||||
extern void zfs_init(void); | extern void zfs_init(void); | ||||
extern void zfs_fini(void); | extern void zfs_fini(void); | ||||
uint_t zfs_fsyncer_key; | uint_t zfs_fsyncer_key; | ||||
extern uint_t rrw_tsd_key; | extern uint_t rrw_tsd_key; | ||||
static uint_t zfs_allow_log_key; | static uint_t zfs_allow_log_key; | ||||
extern uint_t zfs_geom_probe_vdev_key; | extern uint_t zfs_geom_probe_vdev_key; | ||||
typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); | typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); | ||||
typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); | typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); | ||||
typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); | typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); | ||||
/* | |||||
* IOC Keys are used to document and validate user->kernel interface inputs. | |||||
* See zfs_keys_recv_new for an example declaration. Any key name that is not | |||||
* listed will be rejected as input. | |||||
* | |||||
* The keyname 'optional' is always allowed, and must be an nvlist if present. | |||||
* Arguments which older kernels can safely ignore can be placed under the | |||||
* "optional" key. | |||||
* | |||||
* When adding new keys to an existing ioc for new functionality, consider: | |||||
* - adding an entry into zfs_sysfs.c zfs_features[] list | |||||
* - updating the libzfs_input_check.c test utility | |||||
* | |||||
* Note: in the ZK_WILDCARDLIST case, the name serves as documentation | |||||
* for the expected name (bookmark, snapshot, property, etc) but there | |||||
* is no validation in the preflight zfs_check_input_nvpairs() check. | |||||
*/ | |||||
typedef enum { | typedef enum { | ||||
ZK_OPTIONAL = 1 << 0, /* pair is optional */ | |||||
ZK_WILDCARDLIST = 1 << 1, /* one or more unspecified key names */ | |||||
} ioc_key_flag_t; | |||||
/* DATA_TYPE_ANY is used when zkey_type can vary. */ | |||||
#define DATA_TYPE_ANY DATA_TYPE_UNKNOWN | |||||
typedef struct zfs_ioc_key { | |||||
const char *zkey_name; | |||||
data_type_t zkey_type; | |||||
ioc_key_flag_t zkey_flags; | |||||
} zfs_ioc_key_t; | |||||
typedef enum { | |||||
NO_NAME, | NO_NAME, | ||||
POOL_NAME, | POOL_NAME, | ||||
DATASET_NAME, | DATASET_NAME, | ||||
ENTITY_NAME | ENTITY_NAME | ||||
} zfs_ioc_namecheck_t; | } zfs_ioc_namecheck_t; | ||||
typedef enum { | typedef enum { | ||||
POOL_CHECK_NONE = 1 << 0, | POOL_CHECK_NONE = 1 << 0, | ||||
POOL_CHECK_SUSPENDED = 1 << 1, | POOL_CHECK_SUSPENDED = 1 << 1, | ||||
POOL_CHECK_READONLY = 1 << 2, | POOL_CHECK_READONLY = 1 << 2, | ||||
} zfs_ioc_poolcheck_t; | } zfs_ioc_poolcheck_t; | ||||
typedef struct zfs_ioc_vec { | typedef struct zfs_ioc_vec { | ||||
zfs_ioc_legacy_func_t *zvec_legacy_func; | zfs_ioc_legacy_func_t *zvec_legacy_func; | ||||
zfs_ioc_func_t *zvec_func; | zfs_ioc_func_t *zvec_func; | ||||
zfs_secpolicy_func_t *zvec_secpolicy; | zfs_secpolicy_func_t *zvec_secpolicy; | ||||
zfs_ioc_namecheck_t zvec_namecheck; | zfs_ioc_namecheck_t zvec_namecheck; | ||||
boolean_t zvec_allow_log; | boolean_t zvec_allow_log; | ||||
zfs_ioc_poolcheck_t zvec_pool_check; | zfs_ioc_poolcheck_t zvec_pool_check; | ||||
boolean_t zvec_smush_outnvlist; | boolean_t zvec_smush_outnvlist; | ||||
const char *zvec_name; | const char *zvec_name; | ||||
const zfs_ioc_key_t *zvec_nvl_keys; | |||||
size_t zvec_nvl_key_count; | |||||
} zfs_ioc_vec_t; | } zfs_ioc_vec_t; | ||||
/* This array is indexed by zfs_userquota_prop_t */ | /* This array is indexed by zfs_userquota_prop_t */ | ||||
static const char *userquota_perms[] = { | static const char *userquota_perms[] = { | ||||
ZFS_DELEG_PERM_USERUSED, | ZFS_DELEG_PERM_USERUSED, | ||||
ZFS_DELEG_PERM_USERQUOTA, | ZFS_DELEG_PERM_USERQUOTA, | ||||
ZFS_DELEG_PERM_GROUPUSED, | ZFS_DELEG_PERM_GROUPUSED, | ||||
ZFS_DELEG_PERM_GROUPQUOTA, | ZFS_DELEG_PERM_GROUPQUOTA, | ||||
▲ Show 20 Lines • Show All 605 Lines • ▼ Show 20 Lines | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | ||||
{ | { | ||||
nvlist_t *snaps; | nvlist_t *snaps; | ||||
nvpair_t *pair, *nextpair; | nvpair_t *pair, *nextpair; | ||||
int error = 0; | int error = 0; | ||||
if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) | snaps = fnvlist_lookup_nvlist(innvl, "snaps"); | ||||
return (SET_ERROR(EINVAL)); | |||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | ||||
pair = nextpair) { | pair = nextpair) { | ||||
nextpair = nvlist_next_nvpair(snaps, pair); | nextpair = nvlist_next_nvpair(snaps, pair); | ||||
error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); | error = zfs_secpolicy_destroy_perms(nvpair_name(pair), cr); | ||||
if (error == ENOENT) { | if (error == ENOENT) { | ||||
/* | /* | ||||
* Ignore any snapshots that don't exist (we consider | * Ignore any snapshots that don't exist (we consider | ||||
* them "already destroyed"). Remove the name from the | * them "already destroyed"). Remove the name from the | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | ||||
{ | { | ||||
nvlist_t *snaps; | nvlist_t *snaps; | ||||
int error; | int error; | ||||
nvpair_t *pair; | nvpair_t *pair; | ||||
if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) | snaps = fnvlist_lookup_nvlist(innvl, "snaps"); | ||||
return (SET_ERROR(EINVAL)); | |||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | ||||
pair = nvlist_next_nvpair(snaps, pair)) { | pair = nvlist_next_nvpair(snaps, pair)) { | ||||
char *name = nvpair_name(pair); | char *name = nvpair_name(pair); | ||||
char *atp = strchr(name, '@'); | char *atp = strchr(name, '@'); | ||||
if (atp == NULL) { | if (atp == NULL) { | ||||
error = SET_ERROR(EINVAL); | error = SET_ERROR(EINVAL); | ||||
break; | break; | ||||
} | } | ||||
*atp = '\0'; | *atp = '\0'; | ||||
error = zfs_secpolicy_snapshot_perms(name, cr); | error = zfs_secpolicy_snapshot_perms(name, cr); | ||||
*atp = '@'; | *atp = '@'; | ||||
if (error != 0) | if (error != 0) | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Check for permission to create each snapshot in the nvlist. | * Check for permission to create each bookmark in the nvlist. | ||||
*/ | */ | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | zfs_secpolicy_bookmark(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | ||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | ||||
▲ Show 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | ||||
{ | { | ||||
nvpair_t *pair; | nvpair_t *pair; | ||||
nvlist_t *holds; | nvlist_t *holds; | ||||
int error; | int error; | ||||
error = nvlist_lookup_nvlist(innvl, "holds", &holds); | holds = fnvlist_lookup_nvlist(innvl, "holds"); | ||||
if (error != 0) | |||||
return (SET_ERROR(EINVAL)); | |||||
for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | ||||
pair = nvlist_next_nvpair(holds, pair)) { | pair = nvlist_next_nvpair(holds, pair)) { | ||||
char fsname[ZFS_MAX_DATASET_NAME_LEN]; | char fsname[ZFS_MAX_DATASET_NAME_LEN]; | ||||
error = dmu_fsname(nvpair_name(pair), fsname); | error = dmu_fsname(nvpair_name(pair), fsname); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = zfs_secpolicy_write_perms(fsname, | error = zfs_secpolicy_write_perms(fsname, | ||||
Show All 38 Lines | zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) | ||||
*/ | */ | ||||
int error; | int error; | ||||
if ((error = zfs_secpolicy_write_perms(zc->zc_name, | if ((error = zfs_secpolicy_write_perms(zc->zc_name, | ||||
ZFS_DELEG_PERM_DIFF, cr)) == 0) | ZFS_DELEG_PERM_DIFF, cr)) == 0) | ||||
return (0); | return (0); | ||||
error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); | error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); | ||||
if (innvl != NULL) { | |||||
if (error == 0) | if (error == 0) | ||||
error = zfs_secpolicy_hold(zc, innvl, cr); | error = zfs_secpolicy_hold(zc, innvl, cr); | ||||
if (error == 0) | if (error == 0) | ||||
error = zfs_secpolicy_release(zc, innvl, cr); | error = zfs_secpolicy_release(zc, innvl, cr); | ||||
if (error == 0) | if (error == 0) | ||||
error = zfs_secpolicy_destroy(zc, innvl, cr); | error = zfs_secpolicy_destroy(zc, innvl, cr); | ||||
} | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Returns the nvlist as specified by the user in the zfs_cmd_t. | * Returns the nvlist as specified by the user in the zfs_cmd_t. | ||||
*/ | */ | ||||
static int | static int | ||||
get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) | get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp) | ||||
▲ Show 20 Lines • Show All 1,938 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* innvl: { | * innvl: { | ||||
* "type" -> dmu_objset_type_t (int32) | * "type" -> dmu_objset_type_t (int32) | ||||
* (optional) "props" -> { prop -> value } | * (optional) "props" -> { prop -> value } | ||||
* } | * } | ||||
* | * | ||||
* outnvl: propname -> error code (int32) | * outnvl: propname -> error code (int32) | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_create[] = { | |||||
{"type", DATA_TYPE_INT32, 0}, | |||||
{"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, | |||||
{"hidden_args", DATA_TYPE_NVLIST, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
zfs_creat_t zct = { 0 }; | zfs_creat_t zct = { 0 }; | ||||
nvlist_t *nvprops = NULL; | nvlist_t *nvprops = NULL; | ||||
void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); | void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); | ||||
int32_t type32; | |||||
dmu_objset_type_t type; | dmu_objset_type_t type; | ||||
boolean_t is_insensitive = B_FALSE; | boolean_t is_insensitive = B_FALSE; | ||||
if (nvlist_lookup_int32(innvl, "type", &type32) != 0) | type = (dmu_objset_type_t)fnvlist_lookup_int32(innvl, "type"); | ||||
return (SET_ERROR(EINVAL)); | |||||
type = type32; | |||||
(void) nvlist_lookup_nvlist(innvl, "props", &nvprops); | (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); | ||||
switch (type) { | switch (type) { | ||||
case DMU_OST_ZFS: | case DMU_OST_ZFS: | ||||
cbfunc = zfs_create_cb; | cbfunc = zfs_create_cb; | ||||
break; | break; | ||||
case DMU_OST_ZVOL: | case DMU_OST_ZVOL: | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* innvl: { | * innvl: { | ||||
* "origin" -> name of origin snapshot | * "origin" -> name of origin snapshot | ||||
* (optional) "props" -> { prop -> value } | * (optional) "props" -> { prop -> value } | ||||
* } | * } | ||||
* | * | ||||
* outnvl: propname -> error code (int32) | * outnvl: propname -> error code (int32) | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_clone[] = { | |||||
{"origin", DATA_TYPE_STRING, 0}, | |||||
{"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, | |||||
{"hidden_args", DATA_TYPE_NVLIST, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
nvlist_t *nvprops = NULL; | nvlist_t *nvprops = NULL; | ||||
char *origin_name; | char *origin_name; | ||||
if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) | origin_name = fnvlist_lookup_string(innvl, "origin"); | ||||
return (SET_ERROR(EINVAL)); | |||||
(void) nvlist_lookup_nvlist(innvl, "props", &nvprops); | (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); | ||||
if (strchr(fsname, '@') || | if (strchr(fsname, '@') || | ||||
strchr(fsname, '%')) | strchr(fsname, '%')) | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
if (dataset_namecheck(origin_name, NULL, NULL) != 0) | if (dataset_namecheck(origin_name, NULL, NULL) != 0) | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
error = dmu_objset_clone(fsname, origin_name); | error = dmu_objset_clone(fsname, origin_name); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* It would be nice to do this atomically. | * It would be nice to do this atomically. | ||||
*/ | */ | ||||
if (error == 0) { | if (error == 0) { | ||||
error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, | error = zfs_set_prop_nvlist(fsname, ZPROP_SRC_LOCAL, | ||||
nvprops, outnvl); | nvprops, outnvl); | ||||
if (error != 0) | if (error != 0) | ||||
(void) dsl_destroy_head(fsname); | (void) dsl_destroy_head(fsname); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static const zfs_ioc_key_t zfs_keys_remap[] = { | |||||
/* no nvl keys */ | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
if (strchr(fsname, '@') || | if (strchr(fsname, '@') || | ||||
strchr(fsname, '%')) | strchr(fsname, '%')) | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
return (dmu_objset_remap_indirects(fsname)); | return (dmu_objset_remap_indirects(fsname)); | ||||
} | } | ||||
/* | /* | ||||
* innvl: { | * innvl: { | ||||
* "snaps" -> { snapshot1, snapshot2 } | * "snaps" -> { snapshot1, snapshot2 } | ||||
* (optional) "props" -> { prop -> value (string) } | * (optional) "props" -> { prop -> value (string) } | ||||
* } | * } | ||||
* | * | ||||
* outnvl: snapshot -> error code (int32) | * outnvl: snapshot -> error code (int32) | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_snapshot[] = { | |||||
{"snaps", DATA_TYPE_NVLIST, 0}, | |||||
{"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
nvlist_t *snaps; | nvlist_t *snaps; | ||||
nvlist_t *props = NULL; | nvlist_t *props = NULL; | ||||
int error, poollen; | int error, poollen; | ||||
nvpair_t *pair; | nvpair_t *pair; | ||||
(void) nvlist_lookup_nvlist(innvl, "props", &props); | (void) nvlist_lookup_nvlist(innvl, "props", &props); | ||||
if (!nvlist_empty(props) && | if (!nvlist_empty(props) && | ||||
zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) | zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS)) | ||||
return (SET_ERROR(ENOTSUP)); | return (SET_ERROR(ENOTSUP)); | ||||
if ((error = zfs_check_userprops(props)) != 0) | if ((error = zfs_check_userprops(props)) != 0) | ||||
return (error); | return (error); | ||||
if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) | snaps = fnvlist_lookup_nvlist(innvl, "snaps"); | ||||
return (SET_ERROR(EINVAL)); | |||||
poollen = strlen(poolname); | poollen = strlen(poolname); | ||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | ||||
pair = nvlist_next_nvpair(snaps, pair)) { | pair = nvlist_next_nvpair(snaps, pair)) { | ||||
const char *name = nvpair_name(pair); | const char *name = nvpair_name(pair); | ||||
char *cp = strchr(name, '@'); | char *cp = strchr(name, '@'); | ||||
/* | /* | ||||
* The snap name must contain an @, and the part after it must | * The snap name must contain an @, and the part after it must | ||||
Show All 34 Lines | zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
error = dsl_dataset_snapshot(snaps, props, outnvl); | error = dsl_dataset_snapshot(snaps, props, outnvl); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* innvl: "message" -> string | * innvl: "message" -> string | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_log_history[] = { | |||||
{"message", DATA_TYPE_STRING, 0}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
char *message; | char *message; | ||||
spa_t *spa; | spa_t *spa; | ||||
int error; | int error; | ||||
char *poolname; | char *poolname; | ||||
/* | /* | ||||
* The poolname in the ioctl is not set, we get it from the TSD, | * The poolname in the ioctl is not set, we get it from the TSD, | ||||
* which was set at the end of the last successful ioctl that allows | * which was set at the end of the last successful ioctl that allows | ||||
* logging. The secpolicy func already checked that it is set. | * logging. The secpolicy func already checked that it is set. | ||||
* Only one log ioctl is allowed after each successful ioctl, so | * Only one log ioctl is allowed after each successful ioctl, so | ||||
* we clear the TSD here. | * we clear the TSD here. | ||||
*/ | */ | ||||
poolname = tsd_get(zfs_allow_log_key); | poolname = tsd_get(zfs_allow_log_key); | ||||
(void) tsd_set(zfs_allow_log_key, NULL); | (void) tsd_set(zfs_allow_log_key, NULL); | ||||
error = spa_open(poolname, &spa, FTAG); | error = spa_open(poolname, &spa, FTAG); | ||||
strfree(poolname); | strfree(poolname); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
if (nvlist_lookup_string(innvl, "message", &message) != 0) { | message = fnvlist_lookup_string(innvl, "message"); | ||||
spa_close(spa, FTAG); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { | if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { | ||||
spa_close(spa, FTAG); | spa_close(spa, FTAG); | ||||
return (SET_ERROR(ENOTSUP)); | return (SET_ERROR(ENOTSUP)); | ||||
} | } | ||||
error = spa_history_log(spa, message); | error = spa_history_log(spa, message); | ||||
spa_close(spa, FTAG); | spa_close(spa, FTAG); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
static const zfs_ioc_key_t zfs_keys_nextboot[] = { | |||||
{"command", DATA_TYPE_STRING, 0}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
char name[MAXNAMELEN]; | char name[MAXNAMELEN]; | ||||
spa_t *spa; | spa_t *spa; | ||||
vdev_t *vd; | vdev_t *vd; | ||||
char *command; | char *command; | ||||
uint64_t pool_guid; | uint64_t pool_guid; | ||||
uint64_t vdev_guid; | uint64_t vdev_guid; | ||||
int error; | int error; | ||||
if (nvlist_lookup_uint64(innvl, | if (nvlist_lookup_uint64(innvl, | ||||
ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0) | ZPOOL_CONFIG_POOL_GUID, &pool_guid) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (nvlist_lookup_uint64(innvl, | if (nvlist_lookup_uint64(innvl, | ||||
ZPOOL_CONFIG_GUID, &vdev_guid) != 0) | ZPOOL_CONFIG_GUID, &vdev_guid) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (nvlist_lookup_string(innvl, | command = fnvlist_lookup_string(innvl, "command"); | ||||
"command", &command) != 0) | |||||
return (EINVAL); | |||||
mutex_enter(&spa_namespace_lock); | mutex_enter(&spa_namespace_lock); | ||||
spa = spa_by_guid(pool_guid, vdev_guid); | spa = spa_by_guid(pool_guid, vdev_guid); | ||||
if (spa != NULL) | if (spa != NULL) | ||||
strcpy(name, spa_name(spa)); | strcpy(name, spa_name(spa)); | ||||
mutex_exit(&spa_namespace_lock); | mutex_exit(&spa_namespace_lock); | ||||
if (spa == NULL) | if (spa == NULL) | ||||
return (ENOENT); | return (ENOENT); | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
* innvl: { | * innvl: { | ||||
* "snaps" -> { snapshot1, snapshot2 } | * "snaps" -> { snapshot1, snapshot2 } | ||||
* (optional boolean) "defer" | * (optional boolean) "defer" | ||||
* } | * } | ||||
* | * | ||||
* outnvl: snapshot -> error code (int32) | * outnvl: snapshot -> error code (int32) | ||||
* | * | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_destroy_snaps[] = { | |||||
{"snaps", DATA_TYPE_NVLIST, 0}, | |||||
{"defer", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
int error, poollen; | int error, poollen; | ||||
nvlist_t *snaps; | nvlist_t *snaps; | ||||
nvpair_t *pair; | nvpair_t *pair; | ||||
boolean_t defer; | boolean_t defer; | ||||
if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) | snaps = fnvlist_lookup_nvlist(innvl, "snaps"); | ||||
return (SET_ERROR(EINVAL)); | |||||
defer = nvlist_exists(innvl, "defer"); | defer = nvlist_exists(innvl, "defer"); | ||||
poollen = strlen(poolname); | poollen = strlen(poolname); | ||||
for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; | ||||
pair = nvlist_next_nvpair(snaps, pair)) { | pair = nvlist_next_nvpair(snaps, pair)) { | ||||
const char *name = nvpair_name(pair); | const char *name = nvpair_name(pair); | ||||
/* | /* | ||||
Show All 16 Lines | |||||
* | * | ||||
* innvl: { | * innvl: { | ||||
* bookmark1 -> snapshot1, bookmark2 -> snapshot2 | * bookmark1 -> snapshot1, bookmark2 -> snapshot2 | ||||
* } | * } | ||||
* | * | ||||
* outnvl: bookmark -> error code (int32) | * outnvl: bookmark -> error code (int32) | ||||
* | * | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_bookmark[] = { | |||||
{"<bookmark>...", DATA_TYPE_STRING, ZK_WILDCARDLIST}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | ||||
pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { | pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { | ||||
char *snap_name; | char *snap_name; | ||||
Show All 21 Lines | |||||
* } | * } | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* bookmark name 1 -> { property 1, property 2, ... }, | * bookmark name 1 -> { property 1, property 2, ... }, | ||||
* bookmark name 2 -> { property 1, property 2, ... } | * bookmark name 2 -> { property 1, property 2, ... } | ||||
* } | * } | ||||
* | * | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_get_bookmarks[] = { | |||||
{"<property>...", DATA_TYPE_BOOLEAN, ZK_WILDCARDLIST | ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
return (dsl_get_bookmarks(fsname, innvl, outnvl)); | return (dsl_get_bookmarks(fsname, innvl, outnvl)); | ||||
} | } | ||||
/* | /* | ||||
* innvl: { | * innvl: { | ||||
* bookmark name 1, bookmark name 2 | * bookmark name 1, bookmark name 2 | ||||
* } | * } | ||||
* | * | ||||
* outnvl: bookmark -> error code (int32) | * outnvl: bookmark -> error code (int32) | ||||
* | * | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_destroy_bookmarks[] = { | |||||
{"<bookmark>...", DATA_TYPE_BOOLEAN, ZK_WILDCARDLIST}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, | zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, | ||||
nvlist_t *outnvl) | nvlist_t *outnvl) | ||||
{ | { | ||||
int error, poollen; | int error, poollen; | ||||
poollen = strlen(poolname); | poollen = strlen(poolname); | ||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | ||||
Show All 16 Lines | if (strncmp(name, poolname, poollen) != 0 || | ||||
(name[poollen] != '/' && name[poollen] != '#')) | (name[poollen] != '/' && name[poollen] != '#')) | ||||
return (SET_ERROR(EXDEV)); | return (SET_ERROR(EXDEV)); | ||||
} | } | ||||
error = dsl_bookmark_destroy(innvl, outnvl); | error = dsl_bookmark_destroy(innvl, outnvl); | ||||
return (error); | return (error); | ||||
} | } | ||||
static const zfs_ioc_key_t zfs_keys_channel_program[] = { | |||||
{"program", DATA_TYPE_STRING, 0}, | |||||
{"arg", DATA_TYPE_ANY, 0}, | |||||
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL}, | |||||
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL}, | |||||
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, | zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, | ||||
nvlist_t *outnvl) | nvlist_t *outnvl) | ||||
{ | { | ||||
char *program; | char *program; | ||||
uint64_t instrlimit, memlimit; | uint64_t instrlimit, memlimit; | ||||
boolean_t sync_flag; | boolean_t sync_flag; | ||||
nvpair_t *nvarg = NULL; | nvpair_t *nvarg = NULL; | ||||
if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { | program = fnvlist_lookup_string(innvl, ZCP_ARG_PROGRAM); | ||||
return (EINVAL); | |||||
} | |||||
if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) { | if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) { | ||||
sync_flag = B_TRUE; | sync_flag = B_TRUE; | ||||
} | } | ||||
if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { | if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { | ||||
instrlimit = ZCP_DEFAULT_INSTRLIMIT; | instrlimit = ZCP_DEFAULT_INSTRLIMIT; | ||||
} | } | ||||
if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { | if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { | ||||
memlimit = ZCP_DEFAULT_MEMLIMIT; | memlimit = ZCP_DEFAULT_MEMLIMIT; | ||||
} | } | ||||
if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { | nvarg = fnvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST); | ||||
return (EINVAL); | |||||
} | |||||
if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) | if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (memlimit == 0 || memlimit > zfs_lua_max_memlimit) | if (memlimit == 0 || memlimit > zfs_lua_max_memlimit) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (zcp_eval(poolname, program, sync_flag, instrlimit, memlimit, | return (zcp_eval(poolname, program, sync_flag, instrlimit, memlimit, | ||||
nvarg, outnvl)); | nvarg, outnvl)); | ||||
} | } | ||||
/* | /* | ||||
* innvl: unused | * innvl: unused | ||||
* outnvl: empty | * outnvl: empty | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_pool_checkpoint[] = { | |||||
/* no nvl keys */ | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
return (spa_checkpoint(poolname)); | return (spa_checkpoint(poolname)); | ||||
} | } | ||||
/* | /* | ||||
* innvl: unused | * innvl: unused | ||||
* outnvl: empty | * outnvl: empty | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_pool_discard_checkpoint[] = { | |||||
/* no nvl keys */ | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, | zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, | ||||
nvlist_t *outnvl) | nvlist_t *outnvl) | ||||
{ | { | ||||
return (spa_checkpoint_discard(poolname)); | return (spa_checkpoint_discard(poolname)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
* [func: EINVAL (if provided command type didn't make sense)], | * [func: EINVAL (if provided command type didn't make sense)], | ||||
* [vdevs: { | * [vdevs: { | ||||
* guid1: errno, (see function body for possible errnos) | * guid1: errno, (see function body for possible errnos) | ||||
* ... | * ... | ||||
* }] | * }] | ||||
* } | * } | ||||
* | * | ||||
*/ | */ | ||||
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 | static int | ||||
zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
spa_t *spa; | spa_t *spa; | ||||
int error; | int error; | ||||
error = spa_open(poolname, &spa, FTAG); | error = spa_open(poolname, &spa, FTAG); | ||||
if (error != 0) | if (error != 0) | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* fsname is name of dataset to rollback (to most recent snapshot) | * fsname is name of dataset to rollback (to most recent snapshot) | ||||
* | * | ||||
* innvl may contain name of expected target snapshot | * innvl may contain name of expected target snapshot | ||||
* | * | ||||
* outnvl: "target" -> name of most recent snapshot | * outnvl: "target" -> name of most recent snapshot | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_rollback[] = { | |||||
{"target", DATA_TYPE_STRING, ZK_OPTIONAL}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
zfsvfs_t *zfsvfs; | zfsvfs_t *zfsvfs; | ||||
char *target = NULL; | char *target = NULL; | ||||
int error; | int error; | ||||
▲ Show 20 Lines • Show All 1,131 Lines • ▼ Show 20 Lines | zfs_ioc_clear(zfs_cmd_t *zc) | ||||
if (zio_resume(spa) != 0) | if (zio_resume(spa) != 0) | ||||
error = SET_ERROR(EIO); | error = SET_ERROR(EIO); | ||||
spa_close(spa, FTAG); | spa_close(spa, FTAG); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | |||||
* Reopen all the vdevs associated with the pool. | |||||
* | |||||
* innvl: { | |||||
* "scrub_restart" -> when true and scrub is running, allow to restart | |||||
* scrub as the side effect of the reopen (boolean). | |||||
* } | |||||
* | |||||
* outnvl is unused | |||||
*/ | |||||
static const zfs_ioc_key_t zfs_keys_pool_reopen[] = { | |||||
{"scrub_restart", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_pool_reopen(zfs_cmd_t *zc) | zfs_ioc_pool_reopen(const char *pool, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
spa_t *spa; | spa_t *spa; | ||||
int error; | int error; | ||||
boolean_t scrub_restart = B_TRUE; | |||||
error = spa_open(zc->zc_name, &spa, FTAG); | if (innvl) { | ||||
scrub_restart = fnvlist_lookup_boolean_value(innvl, | |||||
"scrub_restart"); | |||||
} | |||||
error = spa_open(pool, &spa, FTAG); | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
spa_vdev_state_enter(spa, SCL_NONE); | spa_vdev_state_enter(spa, SCL_NONE); | ||||
/* | /* | ||||
* If a resilver is already in progress then set the | * If a resilver is already in progress then set the | ||||
* spa_scrub_reopen flag to B_TRUE so that we don't restart | * spa_scrub_reopen flag to B_TRUE so that we don't restart | ||||
* the scan as a side effect of the reopen. Otherwise, let | * the scan as a side effect of the reopen. Otherwise, let | ||||
* vdev_open() decided if a resilver is required. | * vdev_open() decided if a resilver is required. | ||||
*/ | */ | ||||
spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); | spa->spa_scrub_reopen = (!scrub_restart && | ||||
dsl_scan_resilvering(spa->spa_dsl_pool)); | |||||
vdev_reopen(spa->spa_root_vdev); | vdev_reopen(spa->spa_root_vdev); | ||||
spa->spa_scrub_reopen = B_FALSE; | spa->spa_scrub_reopen = B_FALSE; | ||||
(void) spa_vdev_state_exit(spa, NULL, 0); | (void) spa_vdev_state_exit(spa, NULL, 0); | ||||
spa_close(spa, FTAG); | spa_close(spa, FTAG); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* inputs: | * inputs: | ||||
* zc_name name of filesystem | * zc_name name of filesystem | ||||
* | * | ||||
* outputs: | * outputs: | ||||
* zc_string name of conflicting snapshot, if there is one | * zc_string name of conflicting snapshot, if there is one | ||||
*/ | */ | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 580 Lines • ▼ Show 20 Lines | |||||
* (optional) "cleanup_fd" -> fd (int32) | * (optional) "cleanup_fd" -> fd (int32) | ||||
* } | * } | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* snapname -> error value (int32) | * snapname -> error value (int32) | ||||
* ... | * ... | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_hold[] = { | |||||
{"holds", DATA_TYPE_NVLIST, 0}, | |||||
{"cleanup_fd", DATA_TYPE_INT32, ZK_OPTIONAL}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) | zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) | ||||
{ | { | ||||
nvpair_t *pair; | nvpair_t *pair; | ||||
nvlist_t *holds; | nvlist_t *holds; | ||||
int cleanup_fd = -1; | int cleanup_fd = -1; | ||||
int error; | int error; | ||||
minor_t minor = 0; | minor_t minor = 0; | ||||
error = nvlist_lookup_nvlist(args, "holds", &holds); | holds = fnvlist_lookup_nvlist(args, "holds"); | ||||
if (error != 0) | |||||
return (SET_ERROR(EINVAL)); | |||||
/* make sure the user didn't pass us any invalid (empty) tags */ | /* make sure the user didn't pass us any invalid (empty) tags */ | ||||
for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; | ||||
pair = nvlist_next_nvpair(holds, pair)) { | pair = nvlist_next_nvpair(holds, pair)) { | ||||
char *htag; | char *htag; | ||||
error = nvpair_value_string(pair, &htag); | error = nvpair_value_string(pair, &htag); | ||||
if (error != 0) | if (error != 0) | ||||
Show All 18 Lines | |||||
/* | /* | ||||
* innvl is not used. | * innvl is not used. | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* holdname -> time added (uint64 seconds since epoch) | * holdname -> time added (uint64 seconds since epoch) | ||||
* ... | * ... | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_get_holds[] = { | |||||
/* no nvl keys */ | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) | zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) | ||||
{ | { | ||||
ASSERT3P(args, ==, NULL); | |||||
return (dsl_dataset_get_holds(snapname, outnvl)); | return (dsl_dataset_get_holds(snapname, outnvl)); | ||||
} | } | ||||
/* | /* | ||||
* innvl: { | * innvl: { | ||||
* snapname -> { holdname, ... } | * snapname -> { holdname, ... } | ||||
* ... | * ... | ||||
* } | * } | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* snapname -> error value (int32) | * snapname -> error value (int32) | ||||
* ... | * ... | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_release[] = { | |||||
{"<snapname>...", DATA_TYPE_NVLIST, ZK_WILDCARDLIST}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) | zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) | ||||
{ | { | ||||
return (dsl_dataset_user_release(holds, errlist)); | return (dsl_dataset_user_release(holds, errlist)); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
* } | * } | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* "used" -> space in bytes | * "used" -> space in bytes | ||||
* "compressed" -> compressed space in bytes | * "compressed" -> compressed space in bytes | ||||
* "uncompressed" -> uncompressed space in bytes | * "uncompressed" -> uncompressed space in bytes | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_space_snaps[] = { | |||||
{"firstsnap", DATA_TYPE_STRING, 0}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
int error; | int error; | ||||
dsl_pool_t *dp; | dsl_pool_t *dp; | ||||
dsl_dataset_t *new, *old; | dsl_dataset_t *new, *old; | ||||
char *firstsnap; | char *firstsnap; | ||||
uint64_t used, comp, uncomp; | uint64_t used, comp, uncomp; | ||||
if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) | firstsnap = fnvlist_lookup_string(innvl, "firstsnap"); | ||||
return (SET_ERROR(EINVAL)); | |||||
error = dsl_pool_hold(lastsnap, FTAG, &dp); | error = dsl_pool_hold(lastsnap, FTAG, &dp); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); | error = dsl_dataset_hold(dp, lastsnap, FTAG, &new); | ||||
if (error == 0 && !new->ds_is_snapshot) { | if (error == 0 && !new->ds_is_snapshot) { | ||||
dsl_dataset_rele(new, FTAG); | dsl_dataset_rele(new, FTAG); | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
* (optional) "compressok" -> (value ignored) | * (optional) "compressok" -> (value ignored) | ||||
* presence indicates compressed DRR_WRITE records are permitted | * presence indicates compressed DRR_WRITE records are permitted | ||||
* (optional) "resume_object" and "resume_offset" -> (uint64) | * (optional) "resume_object" and "resume_offset" -> (uint64) | ||||
* if present, resume send stream from specified object and offset. | * if present, resume send stream from specified object and offset. | ||||
* } | * } | ||||
* | * | ||||
* outnvl is unused | * outnvl is unused | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_send_new[] = { | |||||
{"fd", DATA_TYPE_INT32, 0}, | |||||
{"fromsnap", DATA_TYPE_STRING, ZK_OPTIONAL}, | |||||
{"largeblockok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"embedok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"compressok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"resume_object", DATA_TYPE_UINT64, ZK_OPTIONAL}, | |||||
{"resume_offset", DATA_TYPE_UINT64, ZK_OPTIONAL}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
file_t *fp; | file_t *fp; | ||||
int error; | int error; | ||||
offset_t off; | offset_t off; | ||||
char *fromname = NULL; | char *fromname = NULL; | ||||
int fd; | int fd; | ||||
boolean_t largeblockok; | boolean_t largeblockok; | ||||
boolean_t embedok; | boolean_t embedok; | ||||
boolean_t compressok; | boolean_t compressok; | ||||
uint64_t resumeobj = 0; | uint64_t resumeobj = 0; | ||||
uint64_t resumeoff = 0; | uint64_t resumeoff = 0; | ||||
error = nvlist_lookup_int32(innvl, "fd", &fd); | fd = fnvlist_lookup_int32(innvl, "fd"); | ||||
if (error != 0) | |||||
return (SET_ERROR(EINVAL)); | |||||
(void) nvlist_lookup_string(innvl, "fromsnap", &fromname); | (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); | ||||
largeblockok = nvlist_exists(innvl, "largeblockok"); | largeblockok = nvlist_exists(innvl, "largeblockok"); | ||||
embedok = nvlist_exists(innvl, "embedok"); | embedok = nvlist_exists(innvl, "embedok"); | ||||
compressok = nvlist_exists(innvl, "compressok"); | compressok = nvlist_exists(innvl, "compressok"); | ||||
(void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); | (void) nvlist_lookup_uint64(innvl, "resume_object", &resumeobj); | ||||
Show All 40 Lines | |||||
* (optional) "compressok" -> (value ignored) | * (optional) "compressok" -> (value ignored) | ||||
* presence indicates compressed DRR_WRITE records are permitted | * presence indicates compressed DRR_WRITE records are permitted | ||||
* } | * } | ||||
* | * | ||||
* outnvl: { | * outnvl: { | ||||
* "space" -> bytes of space (uint64) | * "space" -> bytes of space (uint64) | ||||
* } | * } | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_send_space[] = { | |||||
{"from", DATA_TYPE_STRING, ZK_OPTIONAL}, | |||||
{"fromsnap", DATA_TYPE_STRING, ZK_OPTIONAL}, | |||||
{"largeblockok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"embedok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"compressok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
{"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, | |||||
}; | |||||
static int | static int | ||||
zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) | zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) | ||||
{ | { | ||||
dsl_pool_t *dp; | dsl_pool_t *dp; | ||||
dsl_dataset_t *tosnap; | dsl_dataset_t *tosnap; | ||||
int error; | int error; | ||||
char *fromname; | char *fromname; | ||||
boolean_t compressok; | boolean_t compressok; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
* innvl: { | * innvl: { | ||||
* "force" -> when true, force uberblock update even if there is no dirty data. | * "force" -> when true, force uberblock update even if there is no dirty data. | ||||
* In addition this will cause the vdev configuration to be written | * In addition this will cause the vdev configuration to be written | ||||
* out including updating the zpool cache file. (boolean_t) | * out including updating the zpool cache file. (boolean_t) | ||||
* } | * } | ||||
* | * | ||||
* onvl is unused | * onvl is unused | ||||
*/ | */ | ||||
static const zfs_ioc_key_t zfs_keys_pool_sync[] = { | |||||
{"force", DATA_TYPE_BOOLEAN_VALUE, 0}, | |||||
}; | |||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl) | zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl) | ||||
{ | { | ||||
int err; | int err; | ||||
boolean_t force; | boolean_t force; | ||||
spa_t *spa; | spa_t *spa; | ||||
Show All 37 Lines | |||||
/* | /* | ||||
* See the block comment at the beginning of this file for details on | * See the block comment at the beginning of this file for details on | ||||
* each argument to this function. | * each argument to this function. | ||||
*/ | */ | ||||
static void | static void | ||||
zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, | zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, | ||||
zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, | zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, | ||||
zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, | zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, | ||||
boolean_t allow_log) | boolean_t allow_log, const zfs_ioc_key_t *nvl_keys, size_t num_keys) | ||||
{ | { | ||||
zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; | zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; | ||||
ASSERT3U(ioc, >=, ZFS_IOC_FIRST); | ASSERT3U(ioc, >=, ZFS_IOC_FIRST); | ||||
ASSERT3U(ioc, <, ZFS_IOC_LAST); | ASSERT3U(ioc, <, ZFS_IOC_LAST); | ||||
ASSERT3P(vec->zvec_legacy_func, ==, NULL); | ASSERT3P(vec->zvec_legacy_func, ==, NULL); | ||||
ASSERT3P(vec->zvec_func, ==, NULL); | ASSERT3P(vec->zvec_func, ==, NULL); | ||||
/* if we are logging, the name must be valid */ | /* if we are logging, the name must be valid */ | ||||
ASSERT(!allow_log || namecheck != NO_NAME); | ASSERT(!allow_log || namecheck != NO_NAME); | ||||
vec->zvec_name = name; | vec->zvec_name = name; | ||||
vec->zvec_func = func; | vec->zvec_func = func; | ||||
vec->zvec_secpolicy = secpolicy; | vec->zvec_secpolicy = secpolicy; | ||||
vec->zvec_namecheck = namecheck; | vec->zvec_namecheck = namecheck; | ||||
vec->zvec_pool_check = pool_check; | vec->zvec_pool_check = pool_check; | ||||
vec->zvec_smush_outnvlist = smush_outnvlist; | vec->zvec_smush_outnvlist = smush_outnvlist; | ||||
vec->zvec_allow_log = allow_log; | vec->zvec_allow_log = allow_log; | ||||
vec->zvec_nvl_keys = nvl_keys; | |||||
vec->zvec_nvl_key_count = num_keys; | |||||
} | } | ||||
static void | static void | ||||
zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, | zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, | ||||
zfs_secpolicy_func_t *secpolicy, boolean_t log_history, | zfs_secpolicy_func_t *secpolicy, boolean_t log_history, | ||||
zfs_ioc_poolcheck_t pool_check) | zfs_ioc_poolcheck_t pool_check) | ||||
{ | { | ||||
zfs_ioctl_register_legacy(ioc, func, secpolicy, | zfs_ioctl_register_legacy(ioc, func, secpolicy, | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | zfs_ioctl_register_legacy(ioc, func, secpolicy, | ||||
DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); | DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); | ||||
} | } | ||||
static void | static void | ||||
zfs_ioctl_init(void) | zfs_ioctl_init(void) | ||||
{ | { | ||||
zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, | zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, | ||||
zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, | zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_snapshot, ARRAY_SIZE(zfs_keys_snapshot)); | |||||
zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, | zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, | ||||
zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, | zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE, | ||||
zfs_keys_log_history, ARRAY_SIZE(zfs_keys_log_history)); | |||||
zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, | zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, | ||||
zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, | zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, | ||||
zfs_keys_space_snaps, ARRAY_SIZE(zfs_keys_space_snaps)); | |||||
zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, | zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, | ||||
zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, | zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, | ||||
zfs_keys_send_new, ARRAY_SIZE(zfs_keys_send_new)); | |||||
zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, | zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, | ||||
zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, | zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, | ||||
zfs_keys_send_space, ARRAY_SIZE(zfs_keys_send_space)); | |||||
zfs_ioctl_register("create", ZFS_IOC_CREATE, | zfs_ioctl_register("create", ZFS_IOC_CREATE, | ||||
zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, | zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_create, ARRAY_SIZE(zfs_keys_create)); | |||||
zfs_ioctl_register("clone", ZFS_IOC_CLONE, | zfs_ioctl_register("clone", ZFS_IOC_CLONE, | ||||
zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, | zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_clone, ARRAY_SIZE(zfs_keys_clone)); | |||||
zfs_ioctl_register("remap", ZFS_IOC_REMAP, | zfs_ioctl_register("remap", ZFS_IOC_REMAP, | ||||
zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME, | zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE, | ||||
zfs_keys_remap, ARRAY_SIZE(zfs_keys_remap)); | |||||
zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, | zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, | ||||
zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, | zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_destroy_snaps, ARRAY_SIZE(zfs_keys_destroy_snaps)); | |||||
zfs_ioctl_register("hold", ZFS_IOC_HOLD, | zfs_ioctl_register("hold", ZFS_IOC_HOLD, | ||||
zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, | zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_hold, ARRAY_SIZE(zfs_keys_hold)); | |||||
zfs_ioctl_register("release", ZFS_IOC_RELEASE, | zfs_ioctl_register("release", ZFS_IOC_RELEASE, | ||||
zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, | zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_release, ARRAY_SIZE(zfs_keys_release)); | |||||
zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, | zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, | ||||
zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, | zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, | ||||
zfs_keys_get_holds, ARRAY_SIZE(zfs_keys_get_holds)); | |||||
zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, | zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, | ||||
zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, | zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE, | ||||
zfs_keys_rollback, ARRAY_SIZE(zfs_keys_rollback)); | |||||
zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, | zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, | ||||
zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, | zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_bookmark, ARRAY_SIZE(zfs_keys_bookmark)); | |||||
zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, | zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, | ||||
zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, | zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, | ||||
POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, | ||||
zfs_keys_get_bookmarks, ARRAY_SIZE(zfs_keys_get_bookmarks)); | |||||
zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, | zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, | ||||
zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, | zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, | ||||
POOL_NAME, | POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_destroy_bookmarks, | |||||
ARRAY_SIZE(zfs_keys_destroy_bookmarks)); | |||||
zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, | zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, | ||||
zfs_ioc_channel_program, zfs_secpolicy_config, | zfs_ioc_channel_program, zfs_secpolicy_config, | ||||
POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, | POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, | ||||
B_TRUE); | B_TRUE, zfs_keys_channel_program, | ||||
ARRAY_SIZE(zfs_keys_channel_program)); | |||||
zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT, | zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT, | ||||
zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME, | zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_pool_checkpoint, ARRAY_SIZE(zfs_keys_pool_checkpoint)); | |||||
zfs_ioctl_register("zpool_discard_checkpoint", | zfs_ioctl_register("zpool_discard_checkpoint", | ||||
ZFS_IOC_POOL_DISCARD_CHECKPOINT, zfs_ioc_pool_discard_checkpoint, | ZFS_IOC_POOL_DISCARD_CHECKPOINT, zfs_ioc_pool_discard_checkpoint, | ||||
zfs_secpolicy_config, POOL_NAME, | zfs_secpolicy_config, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_pool_discard_checkpoint, | |||||
ARRAY_SIZE(zfs_keys_pool_discard_checkpoint)); | |||||
zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE, | zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE, | ||||
zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME, | zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, | ||||
zfs_keys_pool_initialize, ARRAY_SIZE(zfs_keys_pool_initialize)); | |||||
zfs_ioctl_register("sync", ZFS_IOC_POOL_SYNC, | zfs_ioctl_register("sync", ZFS_IOC_POOL_SYNC, | ||||
zfs_ioc_pool_sync, zfs_secpolicy_none, POOL_NAME, | zfs_ioc_pool_sync, zfs_secpolicy_none, POOL_NAME, | ||||
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); | POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE, | ||||
zfs_keys_pool_sync, ARRAY_SIZE(zfs_keys_pool_sync)); | |||||
zfs_ioctl_register("reopen", ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, | |||||
zfs_secpolicy_config, POOL_NAME, POOL_CHECK_SUSPENDED, B_TRUE, | |||||
B_TRUE, zfs_keys_pool_reopen, ARRAY_SIZE(zfs_keys_pool_reopen)); | |||||
/* IOCTLS that use the legacy function signature */ | /* IOCTLS that use the legacy function signature */ | ||||
zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, | zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, | ||||
zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); | zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY); | ||||
zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, | zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, | ||||
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); | zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_HISTORY, | ||||
zfs_ioc_pool_get_history, | zfs_ioc_pool_get_history, | ||||
zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); | zfs_secpolicy_config, B_FALSE, POOL_CHECK_SUSPENDED); | ||||
zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, | zfs_ioctl_register_pool(ZFS_IOC_POOL_IMPORT, zfs_ioc_pool_import, | ||||
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); | zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); | ||||
zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, | 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_READONLY); | ||||
zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, | |||||
zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); | |||||
zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, | zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, | ||||
zfs_ioc_space_written); | zfs_ioc_space_written); | ||||
zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, | zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, | ||||
zfs_ioc_objset_recvd_props); | zfs_ioc_objset_recvd_props); | ||||
zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, | zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, | ||||
zfs_ioc_next_obj); | zfs_ioc_next_obj); | ||||
zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, | zfs_ioctl_register_dataset_read(ZFS_IOC_GET_FSACL, | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
#ifdef __FreeBSD__ | #ifdef __FreeBSD__ | ||||
zfs_ioctl_register_dataset_nolog(ZFS_IOC_JAIL, zfs_ioc_jail, | zfs_ioctl_register_dataset_nolog(ZFS_IOC_JAIL, zfs_ioc_jail, | ||||
zfs_secpolicy_config, POOL_CHECK_NONE); | zfs_secpolicy_config, POOL_CHECK_NONE); | ||||
zfs_ioctl_register_dataset_nolog(ZFS_IOC_UNJAIL, zfs_ioc_unjail, | zfs_ioctl_register_dataset_nolog(ZFS_IOC_UNJAIL, zfs_ioc_unjail, | ||||
zfs_secpolicy_config, POOL_CHECK_NONE); | zfs_secpolicy_config, POOL_CHECK_NONE); | ||||
zfs_ioctl_register("fbsd_nextboot", ZFS_IOC_NEXTBOOT, | zfs_ioctl_register("fbsd_nextboot", ZFS_IOC_NEXTBOOT, | ||||
zfs_ioc_nextboot, zfs_secpolicy_config, NO_NAME, | zfs_ioc_nextboot, zfs_secpolicy_config, NO_NAME, | ||||
POOL_CHECK_NONE, B_FALSE, B_FALSE); | POOL_CHECK_NONE, B_FALSE, B_FALSE, | ||||
zfs_keys_nextboot, ARRAY_SIZE(zfs_keys_nextboot)); | |||||
#endif | #endif | ||||
} | } | ||||
/* | |||||
* Verify that for non-legacy ioctls the input nvlist | |||||
* pairs match against the expected input. | |||||
* | |||||
* Possible errors are: | |||||
* ZFS_ERR_IOC_ARG_UNAVAIL An unrecognized nvpair was encountered | |||||
* ZFS_ERR_IOC_ARG_REQUIRED A required nvpair is missing | |||||
* ZFS_ERR_IOC_ARG_BADTYPE Invalid type for nvpair | |||||
*/ | |||||
static int | |||||
zfs_check_input_nvpairs(nvlist_t *innvl, const zfs_ioc_vec_t *vec) | |||||
{ | |||||
const zfs_ioc_key_t *nvl_keys = vec->zvec_nvl_keys; | |||||
boolean_t required_keys_found = B_FALSE; | |||||
/* | |||||
* examine each input pair | |||||
*/ | |||||
for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); | |||||
pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { | |||||
char *name = nvpair_name(pair); | |||||
data_type_t type = nvpair_type(pair); | |||||
boolean_t identified = B_FALSE; | |||||
/* | |||||
* check pair against the documented names and type | |||||
*/ | |||||
for (int k = 0; k < vec->zvec_nvl_key_count; k++) { | |||||
/* if not a wild card name, check for an exact match */ | |||||
if ((nvl_keys[k].zkey_flags & ZK_WILDCARDLIST) == 0 && | |||||
strcmp(nvl_keys[k].zkey_name, name) != 0) | |||||
continue; | |||||
identified = B_TRUE; | |||||
if (nvl_keys[k].zkey_type != DATA_TYPE_ANY && | |||||
nvl_keys[k].zkey_type != type) { | |||||
return (SET_ERROR(ZFS_ERR_IOC_ARG_BADTYPE)); | |||||
} | |||||
if (nvl_keys[k].zkey_flags & ZK_OPTIONAL) | |||||
continue; | |||||
required_keys_found = B_TRUE; | |||||
break; | |||||
} | |||||
/* allow an 'optional' key, everything else is invalid */ | |||||
if (!identified && | |||||
(strcmp(name, "optional") != 0 || | |||||
type != DATA_TYPE_NVLIST)) { | |||||
return (SET_ERROR(ZFS_ERR_IOC_ARG_UNAVAIL)); | |||||
} | |||||
} | |||||
/* verify that all required keys were found */ | |||||
for (int k = 0; k < vec->zvec_nvl_key_count; k++) { | |||||
if (nvl_keys[k].zkey_flags & ZK_OPTIONAL) | |||||
continue; | |||||
if (nvl_keys[k].zkey_flags & ZK_WILDCARDLIST) { | |||||
/* at least one non-optionial key is expected here */ | |||||
if (!required_keys_found) | |||||
return (SET_ERROR(ZFS_ERR_IOC_ARG_REQUIRED)); | |||||
continue; | |||||
} | |||||
if (!nvlist_exists(innvl, nvl_keys[k].zkey_name)) | |||||
return (SET_ERROR(ZFS_ERR_IOC_ARG_REQUIRED)); | |||||
} | |||||
return (0); | |||||
} | |||||
int | int | ||||
pool_status_check(const char *name, zfs_ioc_namecheck_t type, | pool_status_check(const char *name, zfs_ioc_namecheck_t type, | ||||
zfs_ioc_poolcheck_t check) | zfs_ioc_poolcheck_t check) | ||||
{ | { | ||||
spa_t *spa; | spa_t *spa; | ||||
int error; | int error; | ||||
ASSERT(type == POOL_NAME || type == DATASET_NAME || | ASSERT(type == POOL_NAME || type == DATASET_NAME || | ||||
▲ Show 20 Lines • Show All 190 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
#ifdef illumos | #ifdef illumos | ||||
vecnum = cmd - ZFS_IOC_FIRST; | vecnum = cmd - ZFS_IOC_FIRST; | ||||
ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); | ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip)); | ||||
#endif | #endif | ||||
if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) | if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL)); | ||||
vec = &zfs_ioc_vec[vecnum]; | vec = &zfs_ioc_vec[vecnum]; | ||||
zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP); | zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP); | ||||
#ifdef illumos | #ifdef illumos | ||||
error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); | error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag); | ||||
if (error != 0) { | if (error != 0) { | ||||
error = SET_ERROR(EFAULT); | error = SET_ERROR(EFAULT); | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | case ENTITY_NAME: | ||||
} else { | } else { | ||||
error = pool_status_check(zc->zc_name, | error = pool_status_check(zc->zc_name, | ||||
vec->zvec_namecheck, vec->zvec_pool_check); | vec->zvec_namecheck, vec->zvec_pool_check); | ||||
} | } | ||||
break; | break; | ||||
case NO_NAME: | case NO_NAME: | ||||
break; | break; | ||||
} | |||||
/* | |||||
* Ensure that all input pairs are valid before we pass them down | |||||
* to the lower layers. | |||||
* | |||||
* The vectored functions can use fnvlist_lookup_{type} for any | |||||
* required pairs since zfs_check_input_nvpairs() confirmed that | |||||
* they exist and are of the correct type. | |||||
*/ | |||||
if (error == 0 && vec->zvec_func != NULL) { | |||||
error = zfs_check_input_nvpairs(innvl, vec); | |||||
if (error != 0) | |||||
goto out; | |||||
} | } | ||||
if (error == 0) | if (error == 0) | ||||
error = vec->zvec_secpolicy(zc, innvl, cr); | error = vec->zvec_secpolicy(zc, innvl, cr); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
▲ Show 20 Lines • Show All 464 Lines • Show Last 20 Lines |