Support setting user properties in a channel program

This adds support for setting user properties in a
zfs channel program by adding 'zfs.sync.set_prop'
and 'zfs.check.set_prop' to the ZFS LUA API.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Matt Ahrens <matt@delphix.com>
Co-authored-by: Sara Hartse <sara.hartse@delphix.com>
Contributions-by: Jason King <jason.king@joyent.com>
Signed-off-by: Sara Hartse <sara.hartse@delphix.com>
Signed-off-by: Jason King <jason.king@joyent.com>
Closes #9950
This commit is contained in:
Jason King 2020-02-14 15:41:42 -06:00 committed by GitHub
parent 4fe3a842bb
commit 13b5a4d5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 361 additions and 3 deletions

View File

@ -99,6 +99,7 @@ COMMON_H = \
$(top_srcdir)/include/sys/zcp_global.h \ $(top_srcdir)/include/sys/zcp_global.h \
$(top_srcdir)/include/sys/zcp_iter.h \ $(top_srcdir)/include/sys/zcp_iter.h \
$(top_srcdir)/include/sys/zcp_prop.h \ $(top_srcdir)/include/sys/zcp_prop.h \
$(top_srcdir)/include/sys/zcp_set.h \
$(top_srcdir)/include/sys/zfeature.h \ $(top_srcdir)/include/sys/zfeature.h \
$(top_srcdir)/include/sys/zfs_acl.h \ $(top_srcdir)/include/sys/zfs_acl.h \
$(top_srcdir)/include/sys/zfs_context.h \ $(top_srcdir)/include/sys/zfs_context.h \

44
include/sys/zcp_set.h Normal file
View File

@ -0,0 +1,44 @@
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
*/
/*
* Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_ZCP_SET_H
#define _SYS_ZCP_SET_H
#include <sys/dmu_tx.h>
#include <sys/dsl_pool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct zcp_set_prop_arg {
lua_State *state;
const char *dsname;
const char *prop;
const char *val;
} zcp_set_prop_arg_t;
int zcp_set_prop_check(void *arg, dmu_tx_t *tx);
void zcp_set_prop_sync(void *arg, dmu_tx_t *tx);
#ifdef __cplusplus
}
#endif
#endif /* _SYS_ZCP_SET_H */

View File

@ -141,6 +141,7 @@ KERNEL_C = \
zcp_get.c \ zcp_get.c \
zcp_global.c \ zcp_global.c \
zcp_iter.c \ zcp_iter.c \
zcp_set.c \
zcp_synctask.c \ zcp_synctask.c \
zfeature.c \ zfeature.c \
zfs_byteswap.c \ zfs_byteswap.c \

View File

@ -12,7 +12,7 @@
.\" Copyright (c) 2019, 2020 by Christian Schwarz. All Rights Reserved. .\" Copyright (c) 2019, 2020 by Christian Schwarz. All Rights Reserved.
.\" Copyright 2020 Joyent, Inc. .\" Copyright 2020 Joyent, Inc.
.\" .\"
.Dd January 15, 2020 .Dd February 3, 2020
.Dt ZFS-PROGRAM 8 .Dt ZFS-PROGRAM 8
.Os .Os
.Sh NAME .Sh NAME
@ -155,7 +155,7 @@ can guarantee that it will finish successfully against a similar size system.
If a channel program attempts to return too large a value, the program will If a channel program attempts to return too large a value, the program will
fully execute but exit with a nonzero status code and no return value. fully execute but exit with a nonzero status code and no return value.
.Pp .Pp
.Em Note: .Em Note :
ZFS API functions do not generate Fatal Errors when correctly invoked, they ZFS API functions do not generate Fatal Errors when correctly invoked, they
return an error code and the channel program continues executing. return an error code and the channel program continues executing.
See the See the
@ -408,6 +408,26 @@ filesystem (string)
.Bd -ragged -compact -offset "xxxx" .Bd -ragged -compact -offset "xxxx"
Filesystem to rollback. Filesystem to rollback.
.Ed .Ed
.It Em zfs.sync.set_prop(dataset, property, value)
Sets the given property on a dataset.
Currently only user properties are supported.
Returns 0 if the property was set, or a nonzero error code otherwise.
.Pp
dataset (string)
.Bd -ragged -compact -offset "xxxx"
The dataset where the property will be set.
.Ed
.Pp
property (string)
.Bd -ragged -compact -offset "xxxx"
The property to set.
Only user properties are supported.
.Ed
.Pp
value (string)
.Bd -ragged -compact -offset "xxxx"
The value of the property to be set.
.Ed
.It Em zfs.sync.snapshot(dataset) .It Em zfs.sync.snapshot(dataset)
Create a snapshot of a filesystem. Create a snapshot of a filesystem.
Returns 0 if the snapshot was successfully created, Returns 0 if the snapshot was successfully created,
@ -455,6 +475,7 @@ The available zfs.check functions are:
.It Em zfs.check.destroy(dataset, [defer=true|false]) .It Em zfs.check.destroy(dataset, [defer=true|false])
.It Em zfs.check.promote(dataset) .It Em zfs.check.promote(dataset)
.It Em zfs.check.rollback(filesystem) .It Em zfs.check.rollback(filesystem)
.It Em zfs.check.set_property(dataset, property, value)
.It Em zfs.check.snapshot(dataset) .It Em zfs.check.snapshot(dataset)
.El .El
.It Sy zfs.list submodule .It Sy zfs.list submodule

View File

@ -105,6 +105,7 @@ $(MODULE)-objs += zcp.o
$(MODULE)-objs += zcp_get.o $(MODULE)-objs += zcp_get.o
$(MODULE)-objs += zcp_global.o $(MODULE)-objs += zcp_global.o
$(MODULE)-objs += zcp_iter.o $(MODULE)-objs += zcp_iter.o
$(MODULE)-objs += zcp_set.o
$(MODULE)-objs += zcp_synctask.o $(MODULE)-objs += zcp_synctask.o
$(MODULE)-objs += zfeature.o $(MODULE)-objs += zfeature.o
$(MODULE)-objs += zfs_byteswap.o $(MODULE)-objs += zfs_byteswap.o

100
module/zfs/zcp_set.c Normal file
View File

@ -0,0 +1,100 @@
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2016 by Delphix. All rights reserved.
* Copyrigh 2020 Joyent, Inc.
*/
#include <sys/lua/lua.h>
#include <sys/lua/lualib.h>
#include <sys/lua/lauxlib.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_dir.h>
#include <sys/dsl_synctask.h>
#include <sys/dsl_dataset.h>
#include <sys/zcp.h>
#include <sys/zcp_set.h>
#include <sys/zcp_iter.h>
#include <sys/zcp_global.h>
#include <sys/zvol.h>
#include <zfs_prop.h>
static void
zcp_set_user_prop(lua_State *state, dsl_pool_t *dp, const char *dsname,
const char *prop_name, const char *prop_val, dmu_tx_t *tx)
{
dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
if (ds == NULL)
return; /* not reached; zcp_dataset_hold() longjmp'd */
nvlist_t *nvl = fnvlist_alloc();
fnvlist_add_string(nvl, prop_name, prop_val);
dsl_props_set_sync_impl(ds, ZPROP_SRC_LOCAL, nvl, tx);
fnvlist_free(nvl);
dsl_dataset_rele(ds, FTAG);
}
int
zcp_set_prop_check(void *arg, dmu_tx_t *tx)
{
zcp_set_prop_arg_t *args = arg;
const char *prop_name = args->prop;
dsl_props_set_arg_t dpsa = {
.dpsa_dsname = args->dsname,
.dpsa_source = ZPROP_SRC_LOCAL,
};
nvlist_t *nvl = NULL;
int ret = 0;
/*
* Only user properties are currently supported. When non-user
* properties are supported, we will want to use
* zfs_valid_proplist() to verify the properties.
*/
if (!zfs_prop_user(prop_name)) {
return (EINVAL);
}
nvl = fnvlist_alloc();
fnvlist_add_string(nvl, args->prop, args->val);
dpsa.dpsa_props = nvl;
ret = dsl_props_set_check(&dpsa, tx);
nvlist_free(nvl);
return (ret);
}
void
zcp_set_prop_sync(void *arg, dmu_tx_t *tx)
{
zcp_set_prop_arg_t *args = arg;
zcp_run_info_t *ri = zcp_run_info(args->state);
dsl_pool_t *dp = ri->zri_pool;
const char *dsname = args->dsname;
const char *prop_name = args->prop;
const char *prop_val = args->val;
if (zfs_prop_user(prop_name)) {
zcp_set_user_prop(args->state, dp, dsname, prop_name,
prop_val, tx);
}
}

View File

@ -23,6 +23,7 @@
#include <sys/lua/lauxlib.h> #include <sys/lua/lauxlib.h>
#include <sys/zcp.h> #include <sys/zcp.h>
#include <sys/zcp_set.h>
#include <sys/dsl_dir.h> #include <sys/dsl_dir.h>
#include <sys/dsl_pool.h> #include <sys/dsl_pool.h>
#include <sys/dsl_prop.h> #include <sys/dsl_prop.h>
@ -414,6 +415,44 @@ zcp_synctask_bookmark(lua_State *state, boolean_t sync, nvlist_t *err_details)
return (err); return (err);
} }
static int zcp_synctask_set_prop(lua_State *, boolean_t, nvlist_t *err_details);
static zcp_synctask_info_t zcp_synctask_set_prop_info = {
.name = "set_prop",
.func = zcp_synctask_set_prop,
.space_check = ZFS_SPACE_CHECK_RESERVED,
.blocks_modified = 2,
.pargs = {
{ .za_name = "dataset", .za_lua_type = LUA_TSTRING},
{ .za_name = "property", .za_lua_type = LUA_TSTRING},
{ .za_name = "value", .za_lua_type = LUA_TSTRING},
{ NULL, 0 }
},
.kwargs = {
{ NULL, 0 }
}
};
static int
zcp_synctask_set_prop(lua_State *state, boolean_t sync, nvlist_t *err_details)
{
int err;
zcp_set_prop_arg_t args = { 0 };
const char *dsname = lua_tostring(state, 1);
const char *prop = lua_tostring(state, 2);
const char *val = lua_tostring(state, 3);
args.state = state;
args.dsname = dsname;
args.prop = prop;
args.val = val;
err = zcp_sync_task(state, zcp_set_prop_check, zcp_set_prop_sync,
&args, sync, dsname);
return (err);
}
static int static int
zcp_synctask_wrapper(lua_State *state) zcp_synctask_wrapper(lua_State *state)
{ {
@ -484,6 +523,7 @@ zcp_load_synctask_lib(lua_State *state, boolean_t sync)
&zcp_synctask_snapshot_info, &zcp_synctask_snapshot_info,
&zcp_synctask_inherit_prop_info, &zcp_synctask_inherit_prop_info,
&zcp_synctask_bookmark_info, &zcp_synctask_bookmark_info,
&zcp_synctask_set_prop_info,
NULL NULL
}; };

View File

@ -85,7 +85,7 @@ tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit',
'tst.list_snapshots', 'tst.list_system_props', 'tst.list_snapshots', 'tst.list_system_props',
'tst.list_user_props', 'tst.parse_args_neg','tst.promote_conflict', 'tst.list_user_props', 'tst.parse_args_neg','tst.promote_conflict',
'tst.promote_multiple', 'tst.promote_simple', 'tst.rollback_mult', 'tst.promote_multiple', 'tst.promote_simple', 'tst.rollback_mult',
'tst.rollback_one', 'tst.snapshot_destroy', 'tst.snapshot_neg', 'tst.rollback_one', 'tst.set_props', 'tst.snapshot_destroy', 'tst.snapshot_neg',
'tst.snapshot_recursive', 'tst.snapshot_simple', 'tst.snapshot_recursive', 'tst.snapshot_simple',
'tst.bookmark.create', 'tst.bookmark.copy', 'tst.bookmark.create', 'tst.bookmark.copy',
'tst.terminate_by_signal' 'tst.terminate_by_signal'

View File

@ -27,6 +27,7 @@ dist_pkgdata_SCRIPTS = \
tst.promote_simple.ksh \ tst.promote_simple.ksh \
tst.rollback_mult.ksh \ tst.rollback_mult.ksh \
tst.rollback_one.ksh \ tst.rollback_one.ksh \
tst.set_props.ksh \
tst.snapshot_destroy.ksh \ tst.snapshot_destroy.ksh \
tst.snapshot_neg.ksh \ tst.snapshot_neg.ksh \
tst.snapshot_recursive.ksh \ tst.snapshot_recursive.ksh \
@ -43,6 +44,7 @@ dist_pkgdata_DATA = \
tst.get_string_props.out \ tst.get_string_props.out \
tst.get_string_props.zcp \ tst.get_string_props.zcp \
tst.promote_conflict.zcp \ tst.promote_conflict.zcp \
tst.set_props.zcp \
tst.snapshot_destroy.zcp \ tst.snapshot_destroy.zcp \
tst.snapshot_neg.zcp \ tst.snapshot_neg.zcp \
tst.snapshot_recursive.zcp \ tst.snapshot_recursive.zcp \

View File

@ -0,0 +1,39 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2016 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
#
# DESCRIPTION:
# Setting user props should work correctly on datasets.
#
verify_runnable "global"
fs=$TESTPOOL/$TESTFS/testchild
function cleanup
{
destroy_dataset $fs "-R"
}
log_onexit cleanup
log_must zfs create $fs
log_must_program_sync $TESTPOOL $ZCP_ROOT/synctask_core/tst.set_props.zcp $fs
log_pass "Setting props from channel program works correctly."

View File

@ -0,0 +1,109 @@
--
-- This file and its contents are supplied under the terms of the
-- Common Development and Distribution License ("CDDL"), version 1.0.
-- You may only use this file in accordance with the terms of version
-- 1.0 of the CDDL.
--
-- A full copy of the text of the CDDL should have accompanied this
-- source. A copy of the CDDL is also available via the Internet at
-- http://www.illumos.org/license/CDDL.
--
--
-- Copyright (c) 2017 by Delphix. All rights reserved.
-- Copyright 2020 Joyent, Inc.
--
arg = ...
fs = arg["argv"][1]
-- values from zfs.h
maxname = 256 -- ZAP_MAXNAMELEN
maxvalue = 8192 -- ZAP_MAXVALUELEN
pos_props = {}
neg_props = {}
-- In lua, strings are immutable, so to avoid a bunch of copies, we
-- build the value in a table and use concat (which appears to be the
-- recommend method for such things).
largeprop = {}
for i = 0,maxvalue,8
do
table.insert(largeprop, "aaaaaaaa")
end
-- add an extra character so we spill over the limit
table.insert(largeprop, "b")
largepropv = table.concat(largeprop)
largepropname = { "b:" }
for i = 0,maxname,8
do
table.insert(largepropname, "aaaaaaaa")
end
largepropnamev = table.concat(largepropname)
pos_props["a:prop"] = {"hello"}
-- For neg_props, an optional expected error value can be added after the
-- property value as seen below.
neg_props["notaproperty"] = {"hello", EINVAL}
neg_props["a:very.long.property.value"] = { largepropv, E2BIG }
neg_props[largepropnamev] = {"greetings", ENAMETOOLONG }
-- non-user properties aren't currently supported
-- Even if they were, the argument must be a string due to requirements of
-- the ZCP api.
neg_props["mountpoint"] = {"/foo/bar"}
neg_props["copies"] = { "2" }
-- read-only properties should never succeed
neg_props["guid"] = { "12345" }
set_fail = {}
val_fail = {}
-- Test properies that should work
for prop, values in pairs(pos_props) do
for i, val in ipairs(values) do
old_val, src = zfs.get_prop(fs, prop)
-- Attempt to set the property to the specified value
err = zfs.sync.set_prop(fs, prop, val)
if (err ~= 0) then
set_fail[prop] = err -- tuple of prop, val that resulted in error
else
-- use get_prop to check that the set took affect
new_val, src = zfs.get_prop(fs, prop)
if (tostring(new_val) ~= tostring(val)) then
val_fail[prop] = new_val
end
-- We modified the prop, restore old value (if one existed)
if (old_val ~= nil) then
err = zfs.sync.set_prop(fs, prop, old_val)
if (err ~= 0) then return err end
else
-- Didn't have an old value, delete (inherit) instead
err = zfs.sync.inherit(fs, prop)
if (err ~= 0) then return err end
end
end
end
end
-- Test properies that should fail
for prop, expected in pairs(neg_props) do
exp_val = expected[1]
exp_err = expected[2]
-- Attempt to set the property to the specified value
err = zfs.sync.set_prop(fs, prop, exp_val)
if (err == 0 or (exp_err ~= nil and err ~= exp_err)) then
set_fail[prop] = err -- tuple of prop, val that resulted in error
end
end
return {set_fail, val_fail}