summaryrefslogtreecommitdiffstats
path: root/cmd/zfs
diff options
context:
space:
mode:
authorChris Williamson <[email protected]>2018-02-08 09:16:23 -0700
committerBrian Behlendorf <[email protected]>2018-02-08 15:28:18 -0800
commitd99a015343425a1c856c900aa8223016400ac2dc (patch)
treef6ab517b27b650c32127953b74567baa99951d08 /cmd/zfs
parent8824a7f133e4402f7176115cf8efd535c8cbdab2 (diff)
OpenZFS 7431 - ZFS Channel Programs
Authored by: Chris Williamson <[email protected]> Reviewed by: Matthew Ahrens <[email protected]> Reviewed by: George Wilson <[email protected]> Reviewed by: John Kennedy <[email protected]> Reviewed by: Dan Kimmel <[email protected]> Approved by: Garrett D'Amore <[email protected]> Ported-by: Don Brady <[email protected]> Ported-by: John Kennedy <[email protected]> OpenZFS-issue: https://www.illumos.org/issues/7431 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/dfc11533 Porting Notes: * The CLI long option arguments for '-t' and '-m' don't parse on linux * Switched from kmem_alloc to vmem_alloc in zcp_lua_alloc * Lua implementation is built as its own module (zlua.ko) * Lua headers consumed directly by zfs code moved to 'include/sys/lua/' * There is no native setjmp/longjump available in stock Linux kernel. Brought over implementations from illumos and FreeBSD * The get_temporary_prop() was adapted due to VFS platform differences * Use of inline functions in lua parser to reduce stack usage per C call * Skip some ZFS Test Suite ZCP tests on sparc64 to avoid stack overflow
Diffstat (limited to 'cmd/zfs')
-rw-r--r--cmd/zfs/zfs_main.c213
1 files changed, 212 insertions, 1 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 22735ae38..cf31624f0 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
@@ -48,6 +48,7 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
+#include <sys/debug.h>
#include <sys/list.h>
#include <sys/mkdev.h>
#include <sys/mntent.h>
@@ -106,6 +107,7 @@ static int zfs_do_holds(int argc, char **argv);
static int zfs_do_release(int argc, char **argv);
static int zfs_do_diff(int argc, char **argv);
static int zfs_do_bookmark(int argc, char **argv);
+static int zfs_do_channel_program(int argc, char **argv);
static int zfs_do_load_key(int argc, char **argv);
static int zfs_do_unload_key(int argc, char **argv);
static int zfs_do_change_key(int argc, char **argv);
@@ -156,6 +158,7 @@ typedef enum {
HELP_RELEASE,
HELP_DIFF,
HELP_BOOKMARK,
+ HELP_CHANNEL_PROGRAM,
HELP_LOAD_KEY,
HELP_UNLOAD_KEY,
HELP_CHANGE_KEY,
@@ -186,6 +189,7 @@ static zfs_command_t command_table[] = {
{ "promote", zfs_do_promote, HELP_PROMOTE },
{ "rename", zfs_do_rename, HELP_RENAME },
{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
+ { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },
{ NULL },
{ "list", zfs_do_list, HELP_LIST },
{ NULL },
@@ -335,6 +339,10 @@ get_usage(zfs_help_t idx)
"[snapshot|filesystem]\n"));
case HELP_BOOKMARK:
return (gettext("\tbookmark <snapshot> <bookmark>\n"));
+ case HELP_CHANNEL_PROGRAM:
+ return (gettext("\tprogram [-t <instruction limit>] "
+ "[-m <memory limit (b)>] <pool> <program file> "
+ "[lua args...]\n"));
case HELP_LOAD_KEY:
return (gettext("\tload-key [-rn] [-L <keylocation>] "
"<-a | filesystem|volume>\n"));
@@ -374,6 +382,18 @@ safe_malloc(size_t size)
return (data);
}
+void *
+safe_realloc(void *data, size_t size)
+{
+ void *newp;
+ if ((newp = realloc(data, size)) == NULL) {
+ free(data);
+ nomem();
+ }
+
+ return (newp);
+}
+
static char *
safe_strdup(char *str)
{
@@ -7069,6 +7089,197 @@ usage:
return (-1);
}
+static int
+zfs_do_channel_program(int argc, char **argv)
+{
+ int ret, fd, c;
+ char *progbuf, *filename, *poolname;
+ size_t progsize, progread;
+ nvlist_t *outnvl;
+ uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
+ uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
+ zpool_handle_t *zhp;
+
+ /* check options */
+ while ((c = getopt(argc, argv, "t:m:")) != -1) {
+ switch (c) {
+ case 't':
+ case 'm': {
+ uint64_t arg;
+ char *endp;
+
+ errno = 0;
+ arg = strtoull(optarg, &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ (void) fprintf(stderr, gettext(
+ "invalid argument "
+ "'%s': expected integer\n"), optarg);
+ goto usage;
+ }
+
+ if (c == 't') {
+ if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) {
+ (void) fprintf(stderr, gettext(
+ "Invalid instruction limit: "
+ "%s\n"), optarg);
+ return (1);
+ } else {
+ instrlimit = arg;
+ }
+ } else {
+ ASSERT3U(c, ==, 'm');
+ if (arg > ZCP_MAX_MEMLIMIT || arg == 0) {
+ (void) fprintf(stderr, gettext(
+ "Invalid memory limit: "
+ "%s\n"), optarg);
+ return (1);
+ } else {
+ memlimit = arg;
+ }
+ }
+ break;
+ }
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ goto usage;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ gettext("invalid number of arguments\n"));
+ goto usage;
+ }
+
+ poolname = argv[0];
+ filename = argv[1];
+ if (strcmp(filename, "-") == 0) {
+ fd = 0;
+ filename = "standard input";
+ } else if ((fd = open(filename, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
+ filename, strerror(errno));
+ return (1);
+ }
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
+ (void) fprintf(stderr, gettext("cannot open pool '%s'"),
+ poolname);
+ return (1);
+ }
+ zpool_close(zhp);
+
+ /*
+ * Read in the channel program, expanding the program buffer as
+ * necessary.
+ */
+ progread = 0;
+ progsize = 1024;
+ progbuf = safe_malloc(progsize);
+ do {
+ ret = read(fd, progbuf + progread, progsize - progread);
+ progread += ret;
+ if (progread == progsize && ret > 0) {
+ progsize *= 2;
+ progbuf = safe_realloc(progbuf, progsize);
+ }
+ } while (ret > 0);
+
+ if (fd != 0)
+ (void) close(fd);
+ if (ret < 0) {
+ free(progbuf);
+ (void) fprintf(stderr,
+ gettext("cannot read '%s': %s\n"),
+ filename, strerror(errno));
+ return (1);
+ }
+ progbuf[progread] = '\0';
+
+ /*
+ * Any remaining arguments are passed as arguments to the lua script as
+ * a string array:
+ * {
+ * "argv" -> [ "arg 1", ... "arg n" ],
+ * }
+ */
+ nvlist_t *argnvl = fnvlist_alloc();
+ fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
+
+ ret = lzc_channel_program(poolname, progbuf, instrlimit, memlimit,
+ argnvl, &outnvl);
+
+ if (ret != 0) {
+ /*
+ * On error, report the error message handed back by lua if one
+ * exists. Otherwise, generate an appropriate error message,
+ * falling back on strerror() for an unexpected return code.
+ */
+ char *errstring = NULL;
+ uint64_t instructions = 0;
+ if (nvlist_exists(outnvl, ZCP_RET_ERROR)) {
+ (void) nvlist_lookup_string(outnvl,
+ ZCP_RET_ERROR, &errstring);
+ if (errstring == NULL)
+ errstring = strerror(ret);
+ if (ret == ETIME) {
+ (void) nvlist_lookup_uint64(outnvl,
+ ZCP_ARG_INSTRLIMIT, &instructions);
+ }
+ } else {
+ switch (ret) {
+ case EINVAL:
+ errstring =
+ "Invalid instruction or memory limit.";
+ break;
+ case ENOMEM:
+ errstring = "Return value too large.";
+ break;
+ case ENOSPC:
+ errstring = "Memory limit exhausted.";
+ break;
+ case ETIME:
+ errstring = "Timed out.";
+ break;
+ case EPERM:
+ errstring = "Permission denied. Channel "
+ "programs must be run as root.";
+ break;
+ default:
+ errstring = strerror(ret);
+ }
+ }
+ (void) fprintf(stderr,
+ gettext("Channel program execution failed:\n%s\n"),
+ errstring);
+ if (ret == ETIME && instructions != 0)
+ (void) fprintf(stderr, "%llu Lua instructions\n",
+ (u_longlong_t)instructions);
+ } else {
+ (void) printf("Channel program fully executed ");
+ if (nvlist_empty(outnvl)) {
+ (void) printf("with no return value.\n");
+ } else {
+ (void) printf("with return value:\n");
+ dump_nvlist(outnvl, 4);
+ }
+ }
+
+ free(progbuf);
+ fnvlist_free(outnvl);
+ fnvlist_free(argnvl);
+ return (ret != 0);
+
+usage:
+ usage(B_FALSE);
+ return (-1);
+}
+
+
typedef struct loadkey_cbdata {
boolean_t cb_loadkey;
boolean_t cb_recursive;