summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzfs/common/libzfs_dataset.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libzfs/common/libzfs_dataset.c')
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c133
1 files changed, 123 insertions, 10 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 2ca09e51d4..117e414a9a 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -59,6 +59,7 @@
#include <sys/dnode.h>
#include <sys/spa.h>
#include <sys/zap.h>
+#include <sys/dsl_crypt.h>
#include <libzfs.h>
#include "zfs_namecheck.h"
@@ -951,7 +952,7 @@ zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
nvlist_t *
zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
- const char *errbuf)
+ boolean_t key_params_ok, const char *errbuf)
{
nvpair_t *elem;
uint64_t intval;
@@ -1108,7 +1109,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
}
if (zfs_prop_readonly(prop) &&
- (!zfs_prop_setonce(prop) || zhp != NULL)) {
+ !(zfs_prop_setonce(prop) && zhp == NULL) &&
+ !(zfs_prop_encryption_key_param(prop) && key_params_ok)) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
propname);
@@ -1403,6 +1405,48 @@ badlabel:
break;
+ case ZFS_PROP_KEYLOCATION:
+ if (!zfs_prop_valid_keylocation(strval, B_FALSE)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "invalid keylocation"));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+
+ if (zhp != NULL) {
+ uint64_t crypt =
+ zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
+
+ if (crypt == ZIO_CRYPT_OFF &&
+ strcmp(strval, "none") != 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "keylocation must be 'none' "
+ "for unencrypted datasets"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ } else if (crypt != ZIO_CRYPT_OFF &&
+ strcmp(strval, "none") == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "keylocation must not be 'none' "
+ "for encrypted datasets"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ }
+ break;
+
+ case ZFS_PROP_PBKDF2_ITERS:
+ if (intval < MIN_PBKDF2_ITERATIONS) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "minimum pbkdf2 iterations is %u"),
+ MIN_PBKDF2_ITERATIONS);
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ goto error;
+ }
+ break;
+
case ZFS_PROP_UTF8ONLY:
chosen_utf = (int)intval;
break;
@@ -1476,6 +1520,27 @@ badlabel:
break;
}
}
+
+ /* check encryption properties */
+ if (zhp != NULL) {
+ int64_t crypt = zfs_prop_get_int(zhp,
+ ZFS_PROP_ENCRYPTION);
+
+ switch (prop) {
+ case ZFS_PROP_COPIES:
+ if (crypt != ZIO_CRYPT_OFF && intval > 2) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "encrypted datasets cannot have "
+ "3 copies"));
+ (void) zfs_error(hdl, EZFS_BADPROP,
+ errbuf);
+ goto error;
+ }
+ break;
+ default:
+ break;
+ }
+ }
}
/*
@@ -1688,6 +1753,16 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
}
break;
+ case EACCES:
+ if (prop == ZFS_PROP_KEYLOCATION) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "keylocation may only be set on encryption roots"));
+ (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+ } else {
+ (void) zfs_standard_error(hdl, err, errbuf);
+ }
+ break;
+
case EOVERFLOW:
/*
* This platform can't address a volume this big.
@@ -1757,7 +1832,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
- errbuf)) == NULL)
+ B_FALSE, errbuf)) == NULL)
goto error;
/*
@@ -3254,6 +3329,12 @@ parent_name(const char *path, char *buf, size_t buflen)
return (0);
}
+int
+zfs_parent_name(zfs_handle_t *zhp, char *buf, size_t buflen)
+{
+ return (parent_name(zfs_get_name(zhp), buf, buflen));
+}
+
/*
* If accept_ancestor is false, then check to make sure that the given path has
* a parent, and that it exists. If accept_ancestor is true, then find the
@@ -3486,7 +3567,10 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
int ret;
uint64_t size = 0;
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
+ uint8_t *wkeydata = NULL;
+ uint_t wkeylen = 0;
char errbuf[1024];
+ char parent[MAXNAMELEN];
uint64_t zoned;
enum lzc_dataset_type ost;
zpool_handle_t *zpool_handle;
@@ -3539,7 +3623,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
return (-1);
if (props && (props = zfs_valid_proplist(hdl, type, props,
- zoned, NULL, zpool_handle, errbuf)) == 0) {
+ zoned, NULL, zpool_handle, B_TRUE, errbuf)) == 0) {
zpool_close(zpool_handle);
return (-1);
}
@@ -3591,15 +3675,21 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
}
}
+ (void) parent_name(path, parent, sizeof (parent));
+ if (zfs_crypto_create(hdl, parent, props, NULL, &wkeydata,
+ &wkeylen) != 0) {
+ nvlist_free(props);
+ return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
+ }
+
/* create the dataset */
- ret = lzc_create(path, ost, props);
+ ret = lzc_create(path, ost, props, wkeydata, wkeylen);
nvlist_free(props);
+ if (wkeydata != NULL)
+ free(wkeydata);
/* check for failure */
if (ret != 0) {
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- (void) parent_name(path, parent, sizeof (parent));
-
switch (errno) {
case ENOENT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -3620,6 +3710,12 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property value(s) specified"));
return (zfs_error(hdl, EZFS_BADPROP, errbuf));
+ case EACCES:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "encryption root's key is not loaded "
+ "or provided"));
+ return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
+
#ifdef _ILP32
case EOVERFLOW:
/*
@@ -3815,7 +3911,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
type = ZFS_TYPE_FILESYSTEM;
}
if ((props = zfs_valid_proplist(hdl, type, props, zoned,
- zhp, zhp->zpool_hdl, errbuf)) == NULL)
+ zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL)
return (-1);
if (zfs_fix_auto_resv(zhp, props) == -1) {
nvlist_free(props);
@@ -3823,6 +3919,11 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
}
}
+ if (zfs_crypto_clone_check(hdl, zhp, parent, props) != 0) {
+ nvlist_free(props);
+ return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
+ }
+
ret = lzc_clone(target, zhp->zfs_name, props);
nvlist_free(props);
@@ -4001,7 +4102,7 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
if (props != NULL &&
(props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
- props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
+ props, B_FALSE, NULL, zpool_hdl, B_FALSE, errbuf)) == NULL) {
zpool_close(zpool_hdl);
return (-1);
}
@@ -4392,6 +4493,18 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
"a child dataset already has a snapshot "
"with the new name"));
(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
+ } else if (errno == EACCES) {
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) ==
+ ZIO_CRYPT_OFF) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cannot rename an unencrypted dataset to "
+ "be a decendent of an encrypted one"));
+ } else {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cannot move encryption child outside of "
+ "its encryption root"));
+ }
+ (void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
} else {
(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
}