Index: cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 =================================================================== --- /dev/null +++ cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 @@ -0,0 +1,499 @@ +.\" 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. +.\" +.Dd September 28, 2017 +.Dt ZFS-PROGRAM 1M +.Os +.Sh NAME +.Nm zfs program +.Nd executes ZFS channel programs +.Sh SYNOPSIS +.Cm zfs program +.Op Fl t Ar instruction-limit +.Op Fl m Ar memory-limit +.Ar pool +.Ar script +.\".Op Ar optional arguments to channel program +.Sh DESCRIPTION +The ZFS channel program interface allows ZFS administrative operations to be +run programmatically as a Lua script. +The entire script is executed atomically, with no other administrative +operations taking effect concurrently. +A library of ZFS calls is made available to channel program scripts. +Channel programs may only be run with root privileges. +.Pp +A modified version of the Lua 5.2 interpreter is used to run channel program +scripts. +The Lua 5.2 manual can be found at: +.Bd -centered -offset indent +.Lk http://www.lua.org/manual/5.2/ +.Ed +.Pp +The channel program given by +.Ar script +will be run on +.Ar pool , +and any attempts to access or modify other pools will cause an error. +.Sh OPTIONS +.Bl -tag -width "-t" +.It Fl t Ar instruction-limit +Execution time limit, in number of Lua instructions to execute. +If a channel program executes more than the specified number of instructions, +it will be stopped and an error will be returned. +The default limit is 10 million instructions, and it can be set to a maximum of +100 million instructions. +.It Fl m Ar memory-limit +Memory limit, in bytes. +If a channel program attempts to allocate more memory than the given limit, it +will be stopped and an error returned. +The default memory limit is 10 MB, and can be set to a maximum of 100 MB. +.El +.Pp +All remaining argument strings will be passed directly to the Lua script as +described in the +.Sx LUA INTERFACE +section below. +.Sh LUA INTERFACE +A channel program can be invoked either from the command line, or via a library +call to +.Fn lzc_channel_program . +.Ss Arguments +Arguments passed to the channel program are converted to a Lua table. +If invoked from the command line, extra arguments to the Lua script will be +accessible as an array stored in the argument table with the key 'argv': +.Bd -literal -offset indent +args = ... +argv = args["argv"] +-- argv == {1="arg1", 2="arg2", ...} +.Ed +.Pp +If invoked from the libZFS interface, an arbitrary argument list can be +passed to the channel program, which is accessible via the same +"..." syntax in Lua: +.Bd -literal -offset indent +args = ... +-- args == {"foo"="bar", "baz"={...}, ...} +.Ed +.Pp +Note that because Lua arrays are 1-indexed, arrays passed to Lua from the +libZFS interface will have their indices incremented by 1. +That is, the element +in +.Va arr[0] +in a C array passed to a channel program will be stored in +.Va arr[1] +when accessed from Lua. +.Ss Return Values +Lua return statements take the form: +.Bd -literal -offset indent +return ret0, ret1, ret2, ... +.Ed +.Pp +Return statements returning multiple values are permitted internally in a +channel program script, but attempting to return more than one value from the +top level of the channel program is not permitted and will throw an error. +However, tables containing multiple values can still be returned. +If invoked from the command line, a return statement: +.Bd -literal -offset indent +a = {foo="bar", baz=2} +return a +.Ed +.Pp +Will be output formatted as: +.Bd -literal -offset indent +Channel program fully executed with return value: + return: + baz: 2 + foo: 'bar' +.Ed +.Ss Fatal Errors +If the channel program encounters a fatal error while running, a non-zero exit +status will be returned. +If more information about the error is available, a singleton list will be +returned detailing the error: +.Bd -literal -offset indent +error: "error string, including Lua stack trace" +.Ed +.Pp +If a fatal error is returned, the channel program may have not executed at all, +may have partially executed, or may have fully executed but failed to pass a +return value back to userland. +.Pp +If the channel program exhausts an instruction or memory limit, a fatal error +will be generated and the program will be stopped, leaving the program partially +executed. +No attempt is made to reverse or undo any operations already performed. +Note that because both the instruction count and amount of memory used by a +channel program are deterministic when run against the same inputs and +filesystem state, as long as a channel program has run successfully once, you +can guarantee that it will finish successfully against a similar size system. +.Pp +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. +.Pp +.Em Note: +ZFS API functions do not generate Fatal Errors when correctly invoked, they +return an error code and the channel program continues executing. +See the +.Sx ZFS API +section below for function-specific details on error return codes. +.Ss Lua to C Value Conversion +When invoking a channel program via the libZFS interface, it is necessary to +translate arguments and return values from Lua values to their C equivalents, +and vice-versa. +.Pp +There is a correspondence between nvlist values in C and Lua tables. +A Lua table which is returned from the channel program will be recursively +converted to an nvlist, with table values converted to their natural +equivalents: +.Bd -literal -offset indent +string -> string +number -> int64 +boolean -> boolean_value +nil -> boolean (no value) +table -> nvlist +.Ed +.Pp +Likewise, table keys are replaced by string equivalents as follows: +.Bd -literal -offset indent +string -> no change +number -> signed decimal string ("%lld") +boolean -> "true" | "false" +.Ed +.Pp +Any collision of table key strings (for example, the string "true" and a +true boolean value) will cause a fatal error. +.Pp +Lua numbers are represented internally as signed 64-bit integers. +.Sh LUA STANDARD LIBRARY +The following Lua built-in base library functions are available: +.Bd -literal -offset indent +assert rawlen +collectgarbage rawget +error rawset +getmetatable select +ipairs setmetatable +next tonumber +pairs tostring +rawequal type +.Ed +.Pp +All functions in the +.Em coroutine , +.Em string , +and +.Em table +built-in submodules are also available. +A complete list and documentation of these modules is available in the Lua +manual. +.Pp +The following functions base library functions have been disabled and are +not available for use in channel programs: +.Bd -literal -offset indent +dofile +loadfile +load +pcall +print +xpcall +.Ed +.Sh ZFS API +.Ss Function Arguments +Each API function takes a fixed set of required positional arguments and +optional keyword arguments. +For example, the destroy function takes a single positional string argument +(the name of the dataset to destroy) and an optional "defer" keyword boolean +argument. +When using parentheses to specify the arguments to a Lua function, only +positional arguments can be used: +.Bd -literal -offset indent +zfs.sync.destroy("rpool@snap") +.Ed +.Pp +To use keyword arguments, functions must be called with a single argument that +is a Lua table containing entries mapping integers to positional arguments and +strings to keyword arguments: +.Bd -literal -offset indent +zfs.sync.destroy({1="rpool@snap", defer=true}) +.Ed +.Pp +The Lua language allows curly braces to be used in place of parenthesis as +syntactic sugar for this calling convention: +.Bd -literal -offset indent +zfs.sync.snapshot{"rpool@snap", defer=true} +.Ed +.Ss Function Return Values +If an API function succeeds, it returns 0. +If it fails, it returns an error code and the channel program continues +executing. +API functions do not generate Fatal Errors except in the case of an +unrecoverable internal file system error. +.Pp +In addition to returning an error code, some functions also return extra +details describing what caused the error. +This extra description is given as a second return value, and will always be a +Lua table, or Nil if no error details were returned. +Different keys will exist in the error details table depending on the function +and error case. +Any such function may be called expecting a single return value: +.Bd -literal -offset indent +errno = zfs.sync.promote(dataset) +.Ed +.Pp +Or, the error details can be retrieved: +.Bd -literal -offset indent +errno, details = zfs.sync.promote(dataset) +if (errno == EEXIST) then + assert(details ~= Nil) + list_of_conflicting_snapshots = details +end +.Ed +.Pp +The following global aliases for API function error return codes are defined +for use in channel programs: +.Bd -literal -offset indent +EPERM ECHILD ENODEV ENOSPC +ENOENT EAGAIN ENOTDIR ESPIPE +ESRCH ENOMEM EISDIR EROFS +EINTR EACCES EINVAL EMLINK +EIO EFAULT ENFILE EPIPE +ENXIO ENOTBLK EMFILE EDOM +E2BIG EBUSY ENOTTY ERANGE +ENOEXEC EEXIST ETXTBSY EDQUOT +EBADF EXDEV EFBIG +.Ed +.Ss API Functions +For detailed descriptions of the exact behavior of any zfs administrative +operations, see the main +.Xr zfs 1 +manual page. +.Bl -tag -width "xx" +.It Em zfs.debug(msg) +Record a debug message in the zfs_dbgmsg log. +A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or +can be monitored live by running: +.Bd -literal -offset indent + dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}' +.Ed +.Pp +msg (string) +.Bd -ragged -compact -offset "xxxx" +Debug message to be printed. +.Ed +.It Em zfs.get_prop(dataset, property) +Returns two values. +First, a string, number or table containing the property value for the given +dataset. +Second, a string containing the source of the property (i.e. the name of the +dataset in which it was set or nil if it is readonly). +Throws a Lua error if the dataset is invalid or the property doesn't exist. +Note that Lua only supports int64 number types whereas ZFS number properties +are uint64. +This means very large values (like guid) may wrap around and appear negative. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Filesystem or snapshot path to retrieve properties from. +.Ed +.Pp +property (string) +.Bd -ragged -compact -offset "xxxx" +Name of property to retrieve. +All filesystem, snapshot and volume properties are supported except +for 'mounted' and 'iscsioptions.' +Also supports the 'written@snap' and 'written#bookmark' properties and +the '@id' properties, though the id must be in numeric +form. +.Ed +.El +.Bl -tag -width "xx" +.It Sy zfs.sync submodule +The sync submodule contains functions that modify the on-disk state. +They are executed in "syncing context". +.Pp +The available sync submodule functions are as follows: +.Bl -tag -width "xx" +.It Em zfs.sync.destroy(dataset, [defer=true|false]) +Destroy the given dataset. +Returns 0 on successful destroy, or a nonzero error code if the dataset could +not be destroyed (for example, if the dataset has any active children or +clones). +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Filesystem or snapshot to be destroyed. +.Ed +.Pp +[optional] defer (boolean) +.Bd -ragged -compact -offset "xxxx" +Valid only for destroying snapshots. +If set to true, and the snapshot has holds or clones, allows the snapshot to be +marked for deferred deletion rather than failing. +.Ed +.It Em zfs.sync.promote(dataset) +Promote the given clone to a filesystem. +Returns 0 on successful promotion, or a nonzero error code otherwise. +If EEXIST is returned, the second return value will be an array of the clone's +snapshots whose names collide with snapshots of the parent filesystem. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Clone to be promoted. +.Ed +.El +.It Sy zfs.check submodule +For each function in the zfs.sync submodule, there is a corresponding zfs.check +function which performs a "dry run" of the same operation. +Each takes the same arguments as its zfs.sync counterpart and returns 0 if the +operation would succeed, or a non-zero error code if it would fail, along with +any other error details. +That is, each has the same behavior as the corresponding sync function except +for actually executing the requested change. +For example, +.Em zfs.check.destroy("fs") +returns 0 if +.Em zfs.sync.destroy("fs") +would successfully destroy the dataset. +.Pp +The available zfs.check functions are: +.Bl -tag -width "xx" +.It Em zfs.check.destroy(dataset, [defer=true|false]) +.It Em zfs.check.promote(dataset) +.El +.It Sy zfs.list submodule +The zfs.list submodule provides functions for iterating over datasets and +properties. +Rather than returning tables, these functions act as Lua iterators, and are +generally used as follows: +.Bd -literal -offset indent +for child in zfs.list.children("rpool") do + ... +end +.Ed +.Pp +The available zfs.list functions are: +.Bl -tag -width "xx" +.It Em zfs.list.clones(snapshot) +Iterate through all clones of the given snapshot. +.Pp +snapshot (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid snapshot path in the current pool. +.Ed +.It Em zfs.list.snapshots(dataset) +Iterate through all snapshots of the given dataset. +Each snapshot is returned as a string containing the full dataset name, e.g. +"pool/fs@snap". +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem or volume. +.Ed +.It Em zfs.list.children(dataset) +Iterate through all direct children of the given dataset. +Each child is returned as a string containing the full dataset name, e.g. +"pool/fs/child". +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem or volume. +.Ed +.It Em zfs.list.properties(dataset) +Iterate through all user properties for the given dataset. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem, snapshot, or volume. +.Ed +.It Em zfs.list.system_properties(dataset) +Returns an array of strings, the names of the valid system (non-user defined) +properties for the given dataset. +Throws a Lua error if the dataset is invalid. +.Pp +dataset (string) +.Bd -ragged -compact -offset "xxxx" +Must be a valid filesystem, snapshot or volume. +.Ed +.El +.El +.Sh EXAMPLES +.Ss Example 1 +The following channel program recursively destroys a filesystem and all its +snapshots and children in a naive manner. +Note that this does not involve any error handling or reporting. +.Bd -literal -offset indent +function destroy_recursive(root) + for child in zfs.list.children(root) do + destroy_recursive(child) + end + for snap in zfs.list.snapshots(root) do + zfs.sync.destroy(snap) + end + zfs.sync.destroy(root) +end +destroy_recursive("pool/somefs") +.Ed +.Ss Example 2 +A more verbose and robust version of the same channel program, which +properly detects and reports errors, and also takes the dataset to destroy +as a command line argument, would be as follows: +.Bd -literal -offset indent +succeeded = {} +failed = {} + +function destroy_recursive(root) + for child in zfs.list.children(root) do + destroy_recursive(child) + end + for snap in zfs.list.snapshots(root) do + err = zfs.sync.destroy(snap) + if (err ~= 0) then + failed[snap] = err + else + succeeded[snap] = err + end + end + err = zfs.sync.destroy(root) + if (err ~= 0) then + failed[root] = err + else + succeeded[root] = err + end +end + +args = ... +argv = args["argv"] + +destroy_recursive(argv[1]) + +results = {} +results["succeeded"] = succeeded +results["failed"] = failed +return results +.Ed +.Ss Example 3 +The following function performs a forced promote operation by attempting to +promote the given clone and destroying any conflicting snapshots. +.Bd -literal -offset indent +function force_promote(ds) + errno, details = zfs.check.promote(ds) + if (errno == EEXIST) then + assert(details ~= Nil) + for i, snap in ipairs(details) do + zfs.sync.destroy(ds .. "@" .. snap) + end + elseif (errno ~= 0) then + return errno + end + return zfs.sync.promote(ds) +end +.Ed Index: cddl/contrib/opensolaris/cmd/zfs/zfs.8 =================================================================== --- cddl/contrib/opensolaris/cmd/zfs/zfs.8 +++ cddl/contrib/opensolaris/cmd/zfs/zfs.8 @@ -286,6 +286,12 @@ .Ar snapshot .Op Ar snapshot Ns | Ns Ar filesystem .Nm +.Cm program +.Op Fl t Ar timeout +.Op Fl m Ar memory_limit +.Ar pool script +.Op Ar arg1 No ... +.Nm .Cm jail .Ar jailid Ns | Ns Ar jailname filesystem .Nm @@ -3284,6 +3290,48 @@ arrows. .It Fl t Display the path's inode change time as the first column of output. +.El +.It Xo +.Nm +.Cm program +.Op Fl t Ar timeout +.Op Fl m Ar memory_limit +.Ar pool script +.Op Ar arg1 No ... +.Xc +.Pp +Executes +.Ar script +as a ZFS channel program on +.Ar pool . +The ZFS channel +program interface allows ZFS administrative operations to be run +programmatically via a Lua script. +The entire script is executed atomically, with no other administrative +operations taking effect concurrently. +A library of ZFS calls is made available to channel program scripts. +Channel programs may only be run with root privileges. +.Pp +For full documentation of the ZFS channel program interface, see the manual +page for +.Xr zfs-program 8 . +.Bl -tag -width indent +.It Fl t Ar timeout +Execution time limit, in milliseconds. +If a channel program executes for longer than the provided timeout, it will +be stopped and an error will be returned. +The default timeout is 1000 ms, and can be set to a maximum of 10000 ms. +.It Fl m Ar memory-limit +Memory limit, in bytes. +If a channel program attempts to allocate more memory than the given limit, +it will be stopped and an error returned. +The default memory limit is 10 MB, and can be set to a maximum of 100 MB. +.Pp +All remaining argument strings are passed directly to the channel program as +arguments. +See +.Xr zfs-program 8 +for more information. .El .It Xo .Nm Index: cddl/contrib/opensolaris/cmd/zfs/zfs_main.c =================================================================== --- cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ cddl/contrib/opensolaris/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) 2011-2012 Pawel Jakub Dawidek. All rights reserved. @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ static int zfs_do_jail(int argc, char **argv); static int zfs_do_unjail(int argc, char **argv); static int zfs_do_bookmark(int argc, char **argv); +static int zfs_do_channel_program(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -160,6 +162,7 @@ HELP_RELEASE, HELP_DIFF, HELP_BOOKMARK, + HELP_CHANNEL_PROGRAM, } zfs_help_t; typedef struct zfs_command { @@ -187,6 +190,7 @@ { "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 }, @@ -340,6 +344,10 @@ "[snapshot|filesystem]\n")); case HELP_BOOKMARK: return (gettext("\tbookmark \n")); + case HELP_CHANNEL_PROGRAM: + return (gettext("\tprogram [-t ] " + "[-m ] " + "[lua args...]\n")); } abort(); @@ -368,6 +376,18 @@ 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) { @@ -7094,6 +7114,194 @@ dgettext(TEXT_DOMAIN, err_msg)); } + return (ret != 0); + +usage: + usage(B_FALSE); + return (-1); +} + +static int +zfs_do_channel_program(int argc, char **argv) +{ + int ret, fd; + char 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 (-1 != + (c = getopt(argc, argv, "t:(instr-limit)m:(memory-limit)"))) { + 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; + if (nvlist_exists(outnvl, ZCP_RET_ERROR)) { + (void) nvlist_lookup_string(outnvl, + ZCP_RET_ERROR, &errstring); + if (errstring == NULL) + errstring = strerror(ret); + } 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; +#ifdef illumos + case ETIME: +#else + case ETIMEDOUT: +#endif + 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); + } 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: Index: cddl/contrib/opensolaris/cmd/zpool/zpool_main.c =================================================================== --- cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ cddl/contrib/opensolaris/cmd/zpool/zpool_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 (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. @@ -5253,6 +5253,11 @@ (void) printf(" output:\n"); dump_nvlist(fnvlist_lookup_nvlist(rec, ZPOOL_HIST_OUTPUT_NVL), 8); + } + if (nvlist_exists(rec, ZPOOL_HIST_ERRNO)) { + (void) printf(" errno: %lld\n", + fnvlist_lookup_int64(rec, + ZPOOL_HIST_ERRNO)); } } else { if (!cb->internal) Index: cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c =================================================================== --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c @@ -2355,6 +2355,74 @@ } /* + * Accepts a property and value and checks that the value + * matches the one found by the channel program. If they are + * not equal, print both of them. + */ +void +zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval, + const char *strval) +{ + if (!zhp->zfs_hdl->libzfs_prop_debug) + return; + int error; + char *poolname = zhp->zpool_hdl->zpool_name; + const char *program = + "args = ...\n" + "ds = args['dataset']\n" + "prop = args['property']\n" + "value, setpoint = zfs.get_prop(ds, prop)\n" + "return {value=value, setpoint=setpoint}\n"; + nvlist_t *outnvl; + nvlist_t *retnvl; + nvlist_t *argnvl = fnvlist_alloc(); + + fnvlist_add_string(argnvl, "dataset", zhp->zfs_name); + fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop)); + + error = lzc_channel_program(poolname, program, + 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl); + + if (error == 0) { + retnvl = fnvlist_lookup_nvlist(outnvl, "return"); + if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) { + int64_t ans; + error = nvlist_lookup_int64(retnvl, "value", &ans); + if (error != 0) { + (void) fprintf(stderr, "zcp check error: %u\n", + error); + return; + } + if (ans != intval) { + (void) fprintf(stderr, + "%s: zfs found %lld, but zcp found %lld\n", + zfs_prop_to_name(prop), + (longlong_t)intval, (longlong_t)ans); + } + } else { + char *str_ans; + error = nvlist_lookup_string(retnvl, "value", &str_ans); + if (error != 0) { + (void) fprintf(stderr, "zcp check error: %u\n", + error); + return; + } + if (strcmp(strval, str_ans) != 0) { + (void) fprintf(stderr, + "%s: zfs found %s, but zcp found %s\n", + zfs_prop_to_name(prop), + strval, str_ans); + } + } + } else { + (void) fprintf(stderr, + "zcp check failed, channel program error: %u\n", error); + } + nvlist_free(argnvl); + nvlist_free(outnvl); +} + +/* * Retrieve a property from the given object. If 'literal' is specified, then * numbers are left as exact values. Otherwise, numbers are converted to a * human-readable form. @@ -2400,6 +2468,7 @@ &t) == 0) (void) snprintf(propbuf, proplen, "%llu", val); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_MOUNTPOINT: @@ -2468,7 +2537,7 @@ /* 'legacy' or 'none' */ (void) strlcpy(propbuf, str, proplen); } - + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_ORIGIN: @@ -2476,6 +2545,7 @@ if (str == NULL) return (-1); (void) strlcpy(propbuf, str, proplen); + zcp_check(zhp, prop, NULL, str); break; case ZFS_PROP_CLONES: @@ -2490,7 +2560,6 @@ if (get_numeric_property(zhp, prop, src, &source, &val) != 0) return (-1); - /* * If quota or reservation is 0, we translate this into 'none' * (unless literal is set), and indicate that it's the default @@ -2509,6 +2578,7 @@ else zfs_nicenum(val, propbuf, proplen); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_FILESYSTEM_LIMIT: @@ -2533,6 +2603,8 @@ } else { zfs_nicenum(val, propbuf, proplen); } + + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_REFRATIO: @@ -2542,6 +2614,7 @@ (void) snprintf(propbuf, proplen, "%llu.%02llux", (u_longlong_t)(val / 100), (u_longlong_t)(val % 100)); + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_TYPE: @@ -2562,6 +2635,7 @@ abort(); } (void) snprintf(propbuf, proplen, "%s", str); + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_MOUNTED: @@ -2587,6 +2661,7 @@ * consumers. */ (void) strlcpy(propbuf, zhp->zfs_name, proplen); + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_MLSLABEL: @@ -2640,26 +2715,33 @@ if (get_numeric_property(zhp, prop, src, &source, &val) != 0) return (-1); (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); + zcp_check(zhp, prop, val, NULL); break; default: switch (zfs_prop_get_type(prop)) { case PROP_TYPE_NUMBER: if (get_numeric_property(zhp, prop, src, - &source, &val) != 0) + &source, &val) != 0) { return (-1); - if (literal) + } + + if (literal) { (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val); - else + } else { zfs_nicenum(val, propbuf, proplen); + } + zcp_check(zhp, prop, val, NULL); break; case PROP_TYPE_STRING: str = getprop_string(zhp, prop, &source); if (str == NULL) return (-1); + (void) strlcpy(propbuf, str, proplen); + zcp_check(zhp, prop, NULL, str); break; case PROP_TYPE_INDEX: @@ -2668,7 +2750,9 @@ return (-1); if (zfs_prop_index_to_string(prop, val, &strval) != 0) return (-1); + (void) strlcpy(propbuf, strval, proplen); + zcp_check(zhp, prop, NULL, strval); break; default: Index: cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h =================================================================== --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h @@ -79,6 +79,7 @@ libzfs_fru_t **libzfs_fru_hash; libzfs_fru_t *libzfs_fru_list; char libzfs_chassis_id[256]; + boolean_t libzfs_prop_debug; }; #define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */ Index: cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c =================================================================== --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c @@ -678,6 +678,10 @@ zpool_feature_init(); libzfs_mnttab_init(hdl); + if (getenv("ZFS_PROP_DEBUG") != NULL) { + hdl->libzfs_prop_debug = B_TRUE; + } + return (hdl); } Index: cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h =================================================================== --- cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h +++ cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Martin Matuska . All rights reserved. * Copyright 2017 RackTop Systems. */ @@ -85,6 +85,9 @@ int lzc_rollback(const char *, char *, int); int lzc_rollback_to(const char *, const char *); + +int lzc_channel_program(const char *, const char *, uint64_t, uint64_t, + nvlist_t *, nvlist_t **); #ifdef __cplusplus } Index: cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c =================================================================== --- cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c +++ cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2014 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. @@ -174,7 +174,15 @@ } while (ioctl(g_fd, ioc, &zc) != 0) { - if (errno == ENOMEM && resultp != NULL) { + /* + * If ioctl exited with ENOMEM, we retry the ioctl after + * increasing the size of the destination nvlist. + * + * Channel programs that exit with ENOMEM probably ran over the + * lua memory sandbox; they should not be retried. + */ + if (errno == ENOMEM && resultp != NULL && + ioc != ZFS_IOC_CHANNEL_PROGRAM) { free((void *)(uintptr_t)zc.zc_nvlist_dst); zc.zc_nvlist_dst_size *= 2; zc.zc_nvlist_dst = (uint64_t)(uintptr_t) @@ -901,6 +909,57 @@ pool[strcspn(pool, "/#")] = '\0'; error = lzc_ioctl(ZFS_IOC_DESTROY_BOOKMARKS, pool, bmarks, errlist); + + return (error); +} + +/* + * Executes a channel program. + * + * If this function returns 0 the channel program was successfully loaded and + * ran without failing. Note that individual commands the channel program ran + * may have failed and the channel program is responsible for reporting such + * errors through outnvl if they are important. + * + * This method may also return: + * + * EINVAL The program contains syntax errors, or an invalid memory or time + * limit was given. No part of the channel program was executed. + * If caused by syntax errors, 'outnvl' contains information about the + * errors. + * + * EDOM The program was executed, but encountered a runtime error, such as + * calling a function with incorrect arguments, invoking the error() + * function directly, failing an assert() command, etc. Some portion + * of the channel program may have executed and committed changes. + * Information about the failure can be found in 'outnvl'. + * + * ENOMEM The program fully executed, but the output buffer was not large + * enough to store the returned value. No output is returned through + * 'outnvl'. + * + * ENOSPC The program was terminated because it exceeded its memory usage + * limit. Some portion of the channel program may have executed and + * committed changes to disk. No output is returned through 'outnvl'. + * + * ETIMEDOUT The program was terminated because it exceeded its Lua instruction + * limit. Some portion of the channel program may have executed and + * committed changes to disk. No output is returned through 'outnvl'. + */ +int +lzc_channel_program(const char *pool, const char *program, uint64_t instrlimit, + uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl) +{ + int error; + nvlist_t *args; + + args = fnvlist_alloc(); + fnvlist_add_string(args, ZCP_ARG_PROGRAM, program); + fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl); + fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit); + fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit); + error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl); + fnvlist_free(args); return (error); } Index: cddl/contrib/opensolaris/lib/libzpool/common/kernel.c =================================================================== --- cddl/contrib/opensolaris/lib/libzpool/common/kernel.c +++ cddl/contrib/opensolaris/lib/libzpool/common/kernel.c @@ -738,6 +738,7 @@ char buf[512]; (void) vsnprintf(buf, 512, fmt, adx); assfail(buf, NULL, 0); + abort(); /* necessary to make vpanic meet noreturn requirements */ } void Index: cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h =================================================================== --- cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h +++ cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -126,8 +127,8 @@ extern void cmn_err(int, const char *, ...); extern void vcmn_err(int, const char *, __va_list); -extern void panic(const char *, ...); -extern void vpanic(const char *, __va_list); +extern void panic(const char *, ...) __NORETURN; +extern void vpanic(const char *, __va_list) __NORETURN; #define fm_panic panic @@ -349,6 +350,7 @@ #define KM_SLEEP UMEM_NOFAIL #define KM_PUSHPAGE KM_SLEEP #define KM_NOSLEEP UMEM_DEFAULT +#define KM_NORMALPRI 0 /* not needed with UMEM_DEFAULT */ #define KMC_NODEBUG UMC_NODEBUG #define KMC_NOTOUCH 0 /* not needed for userland caches */ #define KM_NODEBUG 0 Index: cddl/lib/libzpool/Makefile =================================================================== --- cddl/lib/libzpool/Makefile +++ cddl/lib/libzpool/Makefile @@ -4,6 +4,8 @@ # ZFS_COMMON_SRCS .PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +# LUA_SRCS +.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua # ZFS_SHARED_SRCS .PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs # KERNEL_SRCS @@ -28,11 +30,12 @@ ZFS_COMMON_SRCS= ${ZFS_COMMON_OBJS:C/.o$/.c/} trim_map.c ZFS_SHARED_SRCS= ${ZFS_SHARED_OBJS:C/.o$/.c/} +LUA_SRCS= ${LUA_OBJS:C/.o$/.c/} KERNEL_SRCS= kernel.c taskq.c util.c LIST_SRCS= list.c UNICODE_SRCS= u8_textprep.c -SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} \ +SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} ${LUA_SRCS} \ ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS} \ ${UNICODE_SRCS} @@ -43,6 +46,7 @@ CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/sys CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head Index: cddl/sbin/zfs/Makefile =================================================================== --- cddl/sbin/zfs/Makefile +++ cddl/sbin/zfs/Makefile @@ -3,7 +3,7 @@ .PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zfs PROG= zfs -MAN= zfs.8 +MAN= zfs.8 zfs-program.8 SRCS= zfs_main.c zfs_iter.c WARNS?= 0 Index: sys/cddl/compat/opensolaris/kern/opensolaris_sunddi.c =================================================================== --- sys/cddl/compat/opensolaris/kern/opensolaris_sunddi.c +++ sys/cddl/compat/opensolaris/kern/opensolaris_sunddi.c @@ -77,6 +77,14 @@ return (0); } +int +ddi_strtoll(const char *str, char **nptr, int base, long long *result) +{ + + *result = (long long)strtoq(str, nptr, base); + return (0); +} + struct ddi_soft_state_item { int ssi_item; void *ssi_data; Index: sys/cddl/compat/opensolaris/sys/sunddi.h =================================================================== --- sys/cddl/compat/opensolaris/sys/sunddi.h +++ sys/cddl/compat/opensolaris/sys/sunddi.h @@ -42,6 +42,7 @@ (copyout((from), (to), (size)), 0) int ddi_strtol(const char *str, char **nptr, int base, long *result); int ddi_strtoul(const char *str, char **nptr, int base, unsigned long *result); +int ddi_strtoll(const char *str, char **nptr, int base, long long *result); int ddi_strtoull(const char *str, char **nptr, int base, unsigned long long *result); Index: sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c =================================================================== --- sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c +++ sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -397,7 +397,8 @@ zprop_register_number(ZFS_PROP_WRITTEN, "written", 0, PROP_READONLY, ZFS_TYPE_DATASET, "", "WRITTEN"); zprop_register_number(ZFS_PROP_LOGICALUSED, "logicalused", 0, - PROP_READONLY, ZFS_TYPE_DATASET, "", "LUSED"); + PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "", + "LUSED"); zprop_register_number(ZFS_PROP_LOGICALREFERENCED, "logicalreferenced", 0, PROP_READONLY, ZFS_TYPE_DATASET, "", "LREFER"); @@ -598,6 +599,15 @@ { return (zfs_prop_table[prop].pd_attr == PROP_READONLY || zfs_prop_table[prop].pd_attr == PROP_ONETIME); +} + +/* + * Returns TRUE if the property is visible (not hidden). + */ +boolean_t +zfs_prop_visible(zfs_prop_t prop) +{ + return (zfs_prop_table[prop].pd_visible); } /* Index: sys/cddl/contrib/opensolaris/uts/common/Makefile.files =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/Makefile.files +++ sys/cddl/contrib/opensolaris/uts/common/Makefile.files @@ -32,6 +32,35 @@ # and its children. These are the source files which may be considered # common to all SunOS systems. +LUA_OBJS += \ + ldo.o \ + lvm.o \ + lbitlib.o \ + lopcodes.o \ + lstring.o \ + ltable.o \ + ltm.o \ + lcorolib.o \ + lauxlib.o \ + ldebug.o \ + lstate.o \ + lgc.o \ + lmem.o \ + lctype.o \ + lfunc.o \ + ldump.o \ + lundump.o \ + lstrlib.o \ + ltablib.o \ + lapi.o \ + lobject.o \ + lbaselib.o \ + lcompat.o \ + lzio.o \ + lcode.o \ + llex.o \ + lparser.o + ZFS_COMMON_OBJS += \ abd.o \ arc.o \ @@ -98,6 +127,11 @@ zap.o \ zap_leaf.o \ zap_micro.o \ + zcp.o \ + zcp_get.o \ + zcp_global.o \ + zcp_iter.o \ + zcp_synctask.o \ zfs_byteswap.o \ zfs_debug.o \ zfs_fm.o \ Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c @@ -1682,7 +1682,6 @@ return (error); } - void dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx) { @@ -1750,30 +1749,17 @@ dmu_buf_rele(ds->ds_dbuf, ds); } -static void -get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) +int +get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val) { uint64_t count = 0; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; zap_cursor_t zc; zap_attribute_t za; - nvlist_t *propval = fnvlist_alloc(); - nvlist_t *val; ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); /* - * We use nvlist_alloc() instead of fnvlist_alloc() because the - * latter would allocate the list with NV_UNIQUE_NAME flag. - * As a result, every time a clone name is appended to the list - * it would be (linearly) searched for for a duplicate name. - * We already know that all clone names must be unique and we - * want avoid the quadratic complexity of double-checking that - * because we can have a large number of clones. - */ - VERIFY0(nvlist_alloc(&val, 0, KM_SLEEP)); - - /* * There may be missing entries in ds_next_clones_obj * due to a bug in a previous version of the code. * Only trust it if it has the right number of entries. @@ -1782,8 +1768,9 @@ VERIFY0(zap_count(mos, dsl_dataset_phys(ds)->ds_next_clones_obj, &count)); } - if (count != dsl_dataset_phys(ds)->ds_num_children - 1) - goto fail; + if (count != dsl_dataset_phys(ds)->ds_num_children - 1) { + return (ENOENT); + } for (zap_cursor_init(&zc, mos, dsl_dataset_phys(ds)->ds_next_clones_obj); zap_cursor_retrieve(&zc, &za) == 0; @@ -1797,16 +1784,43 @@ dsl_dataset_rele(clone, FTAG); } zap_cursor_fini(&zc); - fnvlist_add_nvlist(propval, ZPROP_VALUE, val); - fnvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES), propval); -fail: - nvlist_free(val); - nvlist_free(propval); + return (0); } -static void -get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) +void +get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv) { + nvlist_t *propval = fnvlist_alloc(); + nvlist_t *val; + + /* + * We use nvlist_alloc() instead of fnvlist_alloc() because the + * latter would allocate the list with NV_UNIQUE_NAME flag. + * As a result, every time a clone name is appended to the list + * it would be (linearly) searched for for a duplicate name. + * We already know that all clone names must be unique and we + * want avoid the quadratic complexity of double-checking that + * because we can have a large number of clones. + */ + VERIFY0(nvlist_alloc(&val, 0, KM_SLEEP)); + + if (get_clones_stat_impl(ds, val) == 0) { + fnvlist_add_nvlist(propval, ZPROP_VALUE, val); + fnvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES), + propval); + } else { + nvlist_free(val); + nvlist_free(propval); + } +} + +/* + * Returns a string that represents the receive resume stats token. It should + * be freed with strfree(). + */ +char * +get_receive_resume_stats_impl(dsl_dataset_t *ds) +{ dsl_pool_t *dp = ds->ds_dir->dd_pool; if (dsl_dataset_has_resume_receive_state(ds)) { @@ -1873,84 +1887,359 @@ ZFS_SEND_RESUME_TOKEN_VERSION, (longlong_t)cksum.zc_word[0], (longlong_t)packed_size, str); - dsl_prop_nvlist_add_string(nv, - ZFS_PROP_RECEIVE_RESUME_TOKEN, propval); kmem_free(packed, packed_size); kmem_free(str, compressed_size * 2 + 1); kmem_free(compressed, packed_size); - strfree(propval); + return (propval); } + return (spa_strdup("")); } +/* + * Returns a string that represents the receive resume stats token of the + * dataset's child. It should be freed with strfree(). + */ +char * +get_child_receive_stats(dsl_dataset_t *ds) +{ + char recvname[ZFS_MAX_DATASET_NAME_LEN + 6]; + dsl_dataset_t *recv_ds; + dsl_dataset_name(ds, recvname); + if (strlcat(recvname, "/", sizeof (recvname)) < + sizeof (recvname) && + strlcat(recvname, recv_clone_name, sizeof (recvname)) < + sizeof (recvname) && + dsl_dataset_hold(ds->ds_dir->dd_pool, recvname, FTAG, + &recv_ds) == 0) { + char *propval = get_receive_resume_stats_impl(recv_ds); + dsl_dataset_rele(recv_ds, FTAG); + return (propval); + } + return (spa_strdup("")); +} + +static void +get_receive_resume_stats(dsl_dataset_t *ds, nvlist_t *nv) +{ + char *propval = get_receive_resume_stats_impl(ds); + if (strcmp(propval, "") != 0) { + dsl_prop_nvlist_add_string(nv, + ZFS_PROP_RECEIVE_RESUME_TOKEN, propval); + } else { + char *childval = get_child_receive_stats(ds); + if (strcmp(childval, "") != 0) { + dsl_prop_nvlist_add_string(nv, + ZFS_PROP_RECEIVE_RESUME_TOKEN, childval); + } + strfree(childval); + } + strfree(propval); +} + +uint64_t +dsl_get_refratio(dsl_dataset_t *ds) +{ + uint64_t ratio = dsl_dataset_phys(ds)->ds_compressed_bytes == 0 ? 100 : + (dsl_dataset_phys(ds)->ds_uncompressed_bytes * 100 / + dsl_dataset_phys(ds)->ds_compressed_bytes); + return (ratio); +} + +uint64_t +dsl_get_logicalreferenced(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_uncompressed_bytes); +} + +uint64_t +dsl_get_compressratio(dsl_dataset_t *ds) +{ + if (ds->ds_is_snapshot) { + return (dsl_get_refratio(ds)); + } else { + dsl_dir_t *dd = ds->ds_dir; + mutex_enter(&dd->dd_lock); + uint64_t val = dsl_dir_get_compressratio(dd); + mutex_exit(&dd->dd_lock); + return (val); + } +} + +uint64_t +dsl_get_used(dsl_dataset_t *ds) +{ + if (ds->ds_is_snapshot) { + return (dsl_dataset_phys(ds)->ds_unique_bytes); + } else { + dsl_dir_t *dd = ds->ds_dir; + mutex_enter(&dd->dd_lock); + uint64_t val = dsl_dir_get_used(dd); + mutex_exit(&dd->dd_lock); + return (val); + } +} + +uint64_t +dsl_get_creation(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_creation_time); +} + +uint64_t +dsl_get_creationtxg(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_creation_txg); +} + +uint64_t +dsl_get_refquota(dsl_dataset_t *ds) +{ + return (ds->ds_quota); +} + +uint64_t +dsl_get_refreservation(dsl_dataset_t *ds) +{ + return (ds->ds_reserved); +} + +uint64_t +dsl_get_guid(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_guid); +} + +uint64_t +dsl_get_unique(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_unique_bytes); +} + +uint64_t +dsl_get_objsetid(dsl_dataset_t *ds) +{ + return (ds->ds_object); +} + +uint64_t +dsl_get_userrefs(dsl_dataset_t *ds) +{ + return (ds->ds_userrefs); +} + +uint64_t +dsl_get_defer_destroy(dsl_dataset_t *ds) +{ + return (DS_IS_DEFER_DESTROY(ds) ? 1 : 0); +} + +uint64_t +dsl_get_referenced(dsl_dataset_t *ds) +{ + return (dsl_dataset_phys(ds)->ds_referenced_bytes); +} + +uint64_t +dsl_get_numclones(dsl_dataset_t *ds) +{ + ASSERT(ds->ds_is_snapshot); + return (dsl_dataset_phys(ds)->ds_num_children - 1); +} + +uint64_t +dsl_get_inconsistent(dsl_dataset_t *ds) +{ + return ((dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT) ? + 1 : 0); +} + +uint64_t +dsl_get_available(dsl_dataset_t *ds) +{ + uint64_t refdbytes = dsl_get_referenced(ds); + uint64_t availbytes = dsl_dir_space_available(ds->ds_dir, + NULL, 0, TRUE); + if (ds->ds_reserved > dsl_dataset_phys(ds)->ds_unique_bytes) { + availbytes += + ds->ds_reserved - dsl_dataset_phys(ds)->ds_unique_bytes; + } + if (ds->ds_quota != 0) { + /* + * Adjust available bytes according to refquota + */ + if (refdbytes < ds->ds_quota) { + availbytes = MIN(availbytes, + ds->ds_quota - refdbytes); + } else { + availbytes = 0; + } + } + return (availbytes); +} + +int +dsl_get_written(dsl_dataset_t *ds, uint64_t *written) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + dsl_dataset_t *prev; + int err = dsl_dataset_hold_obj(dp, + dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &prev); + if (err == 0) { + uint64_t comp, uncomp; + err = dsl_dataset_space_written(prev, ds, written, + &comp, &uncomp); + dsl_dataset_rele(prev, FTAG); + } + return (err); +} + +/* + * 'snap' should be a buffer of size ZFS_MAX_DATASET_NAME_LEN. + */ +int +dsl_get_prev_snap(dsl_dataset_t *ds, char *snap) +{ + dsl_pool_t *dp = ds->ds_dir->dd_pool; + if (ds->ds_prev != NULL && ds->ds_prev != dp->dp_origin_snap) { + dsl_dataset_name(ds->ds_prev, snap); + return (0); + } else { + return (ENOENT); + } +} + +/* + * Returns the mountpoint property and source for the given dataset in the value + * and source buffers. The value buffer must be at least as large as MAXPATHLEN + * and the source buffer as least as large a ZFS_MAX_DATASET_NAME_LEN. + * Returns 0 on success and an error on failure. + */ +int +dsl_get_mountpoint(dsl_dataset_t *ds, const char *dsname, char *value, + char *source) +{ + int error; + dsl_pool_t *dp = ds->ds_dir->dd_pool; + + /* Retrieve the mountpoint value stored in the zap opbject */ + error = dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1, + ZAP_MAXVALUELEN, value, source); + if (error != 0) { + return (error); + } + + /* Process the dsname and source to find the full mountpoint string */ + if (value[0] == '/') { + char *buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); + char *root = buf; + const char *relpath; + + /* + * If we inherit the mountpoint, even from a dataset + * with a received value, the source will be the path of + * the dataset we inherit from. If source is + * ZPROP_SOURCE_VAL_RECVD, the received value is not + * inherited. + */ + if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) { + relpath = ""; + } else { + ASSERT0(strncmp(dsname, source, strlen(source))); + relpath = dsname + strlen(source); + if (relpath[0] == '/') + relpath++; + } + + spa_altroot(dp->dp_spa, root, ZAP_MAXVALUELEN); + + /* + * Special case an alternate root of '/'. This will + * avoid having multiple leading slashes in the + * mountpoint path. + */ + if (strcmp(root, "/") == 0) + root++; + + /* + * If the mountpoint is '/' then skip over this + * if we are obtaining either an alternate root or + * an inherited mountpoint. + */ + char *mnt = value; + if (value[1] == '\0' && (root[0] != '\0' || + relpath[0] != '\0')) + mnt = value + 1; + + if (relpath[0] == '\0') { + (void) snprintf(value, ZAP_MAXVALUELEN, "%s%s", + root, mnt); + } else { + (void) snprintf(value, ZAP_MAXVALUELEN, "%s%s%s%s", + root, mnt, relpath[0] == '@' ? "" : "/", + relpath); + } + kmem_free(buf, ZAP_MAXVALUELEN); + } else { + /* 'legacy' or 'none' */ + (void) snprintf(value, ZAP_MAXVALUELEN, "%s", value); + } + return (0); +} + void dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv) { dsl_pool_t *dp = ds->ds_dir->dd_pool; - uint64_t refd, avail, uobjs, aobjs, ratio; ASSERT(dsl_pool_config_held(dp)); - ratio = dsl_dataset_phys(ds)->ds_compressed_bytes == 0 ? 100 : - (dsl_dataset_phys(ds)->ds_uncompressed_bytes * 100 / - dsl_dataset_phys(ds)->ds_compressed_bytes); - - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, ratio); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, + dsl_get_refratio(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALREFERENCED, - dsl_dataset_phys(ds)->ds_uncompressed_bytes); + dsl_get_logicalreferenced(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, + dsl_get_compressratio(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, + dsl_get_used(ds)); if (ds->ds_is_snapshot) { - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, - dsl_dataset_phys(ds)->ds_unique_bytes); get_clones_stat(ds, nv); } else { - if (ds->ds_prev != NULL && ds->ds_prev != dp->dp_origin_snap) { - char buf[ZFS_MAX_DATASET_NAME_LEN]; - dsl_dataset_name(ds->ds_prev, buf); - dsl_prop_nvlist_add_string(nv, ZFS_PROP_PREV_SNAP, buf); - } - + char buf[ZFS_MAX_DATASET_NAME_LEN]; + if (dsl_get_prev_snap(ds, buf) == 0) + dsl_prop_nvlist_add_string(nv, ZFS_PROP_PREV_SNAP, + buf); dsl_dir_stats(ds->ds_dir, nv); } - dsl_dataset_space(ds, &refd, &avail, &uobjs, &aobjs); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, avail); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, refd); - + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_AVAILABLE, + dsl_get_available(ds)); + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFERENCED, + dsl_get_referenced(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATION, - dsl_dataset_phys(ds)->ds_creation_time); + dsl_get_creation(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_CREATETXG, - dsl_dataset_phys(ds)->ds_creation_txg); + dsl_get_creationtxg(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFQUOTA, - ds->ds_quota); + dsl_get_refquota(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRESERVATION, - ds->ds_reserved); + dsl_get_refreservation(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_GUID, - dsl_dataset_phys(ds)->ds_guid); + dsl_get_guid(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_UNIQUE, - dsl_dataset_phys(ds)->ds_unique_bytes); + dsl_get_unique(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_OBJSETID, - ds->ds_object); + dsl_get_objsetid(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USERREFS, - ds->ds_userrefs); + dsl_get_userrefs(ds)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY, - DS_IS_DEFER_DESTROY(ds) ? 1 : 0); + dsl_get_defer_destroy(ds)); if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { - uint64_t written, comp, uncomp; - dsl_pool_t *dp = ds->ds_dir->dd_pool; - dsl_dataset_t *prev; - - int err = dsl_dataset_hold_obj(dp, - dsl_dataset_phys(ds)->ds_prev_snap_obj, FTAG, &prev); - if (err == 0) { - err = dsl_dataset_space_written(prev, ds, &written, - &comp, &uncomp); - dsl_dataset_rele(prev, FTAG); - if (err == 0) { - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN, - written); - } + uint64_t written; + if (dsl_get_written(ds, &written) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN, + written); } } @@ -1987,27 +2276,19 @@ dsl_pool_t *dp = ds->ds_dir->dd_pool; ASSERT(dsl_pool_config_held(dp)); - stat->dds_creation_txg = dsl_dataset_phys(ds)->ds_creation_txg; - stat->dds_inconsistent = - dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT; - stat->dds_guid = dsl_dataset_phys(ds)->ds_guid; + stat->dds_creation_txg = dsl_get_creationtxg(ds); + stat->dds_inconsistent = dsl_get_inconsistent(ds); + stat->dds_guid = dsl_get_guid(ds); stat->dds_origin[0] = '\0'; if (ds->ds_is_snapshot) { stat->dds_is_snapshot = B_TRUE; - stat->dds_num_clones = - dsl_dataset_phys(ds)->ds_num_children - 1; + stat->dds_num_clones = dsl_get_numclones(ds); } else { stat->dds_is_snapshot = B_FALSE; stat->dds_num_clones = 0; if (dsl_dir_is_clone(ds->ds_dir)) { - dsl_dataset_t *ods; - - VERIFY0(dsl_dataset_hold_obj(dp, - dsl_dir_phys(ds->ds_dir)->dd_origin_obj, - FTAG, &ods)); - dsl_dataset_name(ods, stat->dds_origin); - dsl_dataset_rele(ods, FTAG); + dsl_dir_get_origin(ds->ds_dir, stat->dds_origin); } } } @@ -2434,22 +2715,12 @@ dsl_dataset_t *ds; }; -typedef struct dsl_dataset_promote_arg { - const char *ddpa_clonename; - dsl_dataset_t *ddpa_clone; - list_t shared_snaps, origin_snaps, clone_snaps; - dsl_dataset_t *origin_origin; /* origin of the origin */ - uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap; - char *err_ds; - cred_t *cr; -} dsl_dataset_promote_arg_t; - static int snaplist_space(list_t *l, uint64_t mintxg, uint64_t *spacep); static int promote_hold(dsl_dataset_promote_arg_t *ddpa, dsl_pool_t *dp, void *tag); static void promote_rele(dsl_dataset_promote_arg_t *ddpa, void *tag); -static int +int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx) { dsl_dataset_promote_arg_t *ddpa = arg; @@ -2461,14 +2732,19 @@ uint64_t unused; uint64_t ss_mv_cnt; size_t max_snap_len; + boolean_t conflicting_snaps; err = promote_hold(ddpa, dp, FTAG); if (err != 0) return (err); hds = ddpa->ddpa_clone; + snap = list_head(&ddpa->shared_snaps); + origin_ds = snap->ds; max_snap_len = MAXNAMELEN - strlen(ddpa->ddpa_clonename) - 1; + snap = list_head(&ddpa->origin_snaps); + if (dsl_dataset_phys(hds)->ds_flags & DS_FLAG_NOPROMOTE) { promote_rele(ddpa, FTAG); return (SET_ERROR(EXDEV)); @@ -2483,9 +2759,6 @@ return (0); } - snap = list_head(&ddpa->shared_snaps); - origin_ds = snap->ds; - /* compute origin's new unique space */ snap = list_tail(&ddpa->clone_snaps); ASSERT3U(dsl_dataset_phys(snap->ds)->ds_prev_snap_obj, ==, @@ -2509,6 +2782,7 @@ * Note however, if we stop before we reach the ORIGIN we get: * uN + kN + kN-1 + ... + kM - uM-1 */ + conflicting_snaps = B_FALSE; ss_mv_cnt = 0; ddpa->used = dsl_dataset_phys(origin_ds)->ds_referenced_bytes; ddpa->comp = dsl_dataset_phys(origin_ds)->ds_compressed_bytes; @@ -2537,12 +2811,12 @@ } err = dsl_dataset_snap_lookup(hds, ds->ds_snapname, &val); if (err == 0) { - (void) strcpy(ddpa->err_ds, snap->ds->ds_snapname); - err = SET_ERROR(EEXIST); + fnvlist_add_boolean(ddpa->err_ds, + snap->ds->ds_snapname); + conflicting_snaps = B_TRUE; + } else if (err != ENOENT) { goto out; } - if (err != ENOENT) - goto out; /* The very first snapshot does not have a deadlist */ if (dsl_dataset_phys(ds)->ds_prev_snap_obj == 0) @@ -2556,6 +2830,15 @@ } /* + * In order to return the full list of conflicting snapshots, we check + * whether there was a conflict after traversing all of them. + */ + if (conflicting_snaps) { + err = SET_ERROR(EEXIST); + goto out; + } + + /* * If we are a clone of a clone then we never reached ORIGIN, * so we need to subtract out the clone origin's used space. */ @@ -2617,7 +2900,7 @@ return (err); } -static void +void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) { dsl_dataset_promote_arg_t *ddpa = arg; @@ -2965,6 +3248,7 @@ dsl_dataset_promote_arg_t ddpa = { 0 }; uint64_t numsnaps; int error; + nvpair_t *snap_pair; objset_t *os; /* @@ -2982,12 +3266,22 @@ return (error); ddpa.ddpa_clonename = name; - ddpa.err_ds = conflsnap; + ddpa.err_ds = fnvlist_alloc(); ddpa.cr = CRED(); - return (dsl_sync_task(name, dsl_dataset_promote_check, + error = dsl_sync_task(name, dsl_dataset_promote_check, dsl_dataset_promote_sync, &ddpa, - 2 + numsnaps, ZFS_SPACE_CHECK_RESERVED)); + 2 + numsnaps, ZFS_SPACE_CHECK_RESERVED); + + /* + * Return the first conflicting snapshot found. + */ + snap_pair = nvlist_next_nvpair(ddpa.err_ds, NULL); + if (snap_pair != NULL && conflsnap != NULL) + (void) strcpy(conflsnap, nvpair_name(snap_pair)); + + fnvlist_free(ddpa.err_ds); + return (error); } int Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 by Joyent, Inc. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -41,14 +42,8 @@ #include #include #include +#include -typedef struct dmu_snapshots_destroy_arg { - nvlist_t *dsda_snaps; - nvlist_t *dsda_successful_snaps; - boolean_t dsda_defer; - nvlist_t *dsda_errlist; -} dmu_snapshots_destroy_arg_t; - int dsl_destroy_snapshot_check_impl(dsl_dataset_t *ds, boolean_t defer) { @@ -85,51 +80,33 @@ return (0); } -static int +int dsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx) { - dmu_snapshots_destroy_arg_t *dsda = arg; + dsl_destroy_snapshot_arg_t *ddsa = arg; + const char *dsname = ddsa->ddsa_name; + boolean_t defer = ddsa->ddsa_defer; + dsl_pool_t *dp = dmu_tx_pool(tx); - nvpair_t *pair; int error = 0; + dsl_dataset_t *ds; - if (!dmu_tx_is_syncing(tx)) + error = dsl_dataset_hold(dp, dsname, FTAG, &ds); + + /* + * If the snapshot does not exist, silently ignore it, and + * dsl_destroy_snapshot_sync() will be a no-op + * (it's "already destroyed"). + */ + if (error == ENOENT) return (0); - for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL); - pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) { - dsl_dataset_t *ds; - - error = dsl_dataset_hold(dp, nvpair_name(pair), - FTAG, &ds); - - /* - * If the snapshot does not exist, silently ignore it - * (it's "already destroyed"). - */ - if (error == ENOENT) - continue; - - if (error == 0) { - error = dsl_destroy_snapshot_check_impl(ds, - dsda->dsda_defer); - dsl_dataset_rele(ds, FTAG); - } - - if (error == 0) { - fnvlist_add_boolean(dsda->dsda_successful_snaps, - nvpair_name(pair)); - } else { - fnvlist_add_int32(dsda->dsda_errlist, - nvpair_name(pair), error); - } + if (error == 0) { + error = dsl_destroy_snapshot_check_impl(ds, defer); + dsl_dataset_rele(ds, FTAG); } - pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL); - if (pair != NULL) - return (fnvpair_value_int32(pair)); - - return (0); + return (error); } struct process_old_arg { @@ -473,23 +450,22 @@ dmu_object_free_zapified(mos, obj, tx); } -static void +void dsl_destroy_snapshot_sync(void *arg, dmu_tx_t *tx) { - dmu_snapshots_destroy_arg_t *dsda = arg; + dsl_destroy_snapshot_arg_t *ddsa = arg; + const char *dsname = ddsa->ddsa_name; + boolean_t defer = ddsa->ddsa_defer; + dsl_pool_t *dp = dmu_tx_pool(tx); - nvpair_t *pair; + dsl_dataset_t *ds; - for (pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, NULL); - pair != NULL; - pair = nvlist_next_nvpair(dsda->dsda_successful_snaps, pair)) { - dsl_dataset_t *ds; - - VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); - - dsl_destroy_snapshot_sync_impl(ds, dsda->dsda_defer, tx); - dsl_dataset_rele(ds, FTAG); - } + int error = dsl_dataset_hold(dp, dsname, FTAG, &ds); + if (error == ENOENT) + return; + ASSERT0(error); + dsl_destroy_snapshot_sync_impl(ds, defer, tx); + dsl_dataset_rele(ds, FTAG); } /* @@ -509,25 +485,86 @@ dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer, nvlist_t *errlist) { - dmu_snapshots_destroy_arg_t dsda; - int error; - nvpair_t *pair; - - pair = nvlist_next_nvpair(snaps, NULL); - if (pair == NULL) + if (nvlist_next_nvpair(snaps, NULL) == NULL) return (0); - dsda.dsda_snaps = snaps; - dsda.dsda_successful_snaps = fnvlist_alloc(); - dsda.dsda_defer = defer; - dsda.dsda_errlist = errlist; + nvlist_t *arg = fnvlist_alloc(); + nvlist_t *snaps_normalized = fnvlist_alloc(); + /* + * lzc_destroy_snaps() is documented to take an nvlist whose + * values "don't matter". We need to convert that nvlist to one + * that we know can be converted to LUA. + */ + for (nvpair_t *pair = nvlist_next_nvpair(snaps, NULL); + pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) { + fnvlist_add_boolean_value(snaps_normalized, + nvpair_name(pair), B_TRUE); + } + fnvlist_add_nvlist(arg, "snaps", snaps_normalized); + fnvlist_free(snaps_normalized); + fnvlist_add_boolean_value(arg, "defer", defer); - error = dsl_sync_task(nvpair_name(pair), - dsl_destroy_snapshot_check, dsl_destroy_snapshot_sync, - &dsda, 0, ZFS_SPACE_CHECK_NONE); - fnvlist_free(dsda.dsda_successful_snaps); + nvlist_t *wrapper = fnvlist_alloc(); + fnvlist_add_nvlist(wrapper, ZCP_ARG_ARGLIST, arg); + fnvlist_free(arg); - return (error); + const char *program = + "arg = ...\n" + "snaps = arg['snaps']\n" + "defer = arg['defer']\n" + "errors = { }\n" + "has_errors = false\n" + "for snap, v in pairs(snaps) do\n" + " errno = zfs.check.destroy{snap, defer=defer}\n" + " zfs.debug('snap: ' .. snap .. ' errno: ' .. errno)\n" + " if errno == ENOENT then\n" + " snaps[snap] = nil\n" + " elseif errno ~= 0 then\n" + " errors[snap] = errno\n" + " has_errors = true\n" + " end\n" + "end\n" + "if has_errors then\n" + " return errors\n" + "end\n" + "for snap, v in pairs(snaps) do\n" + " errno = zfs.sync.destroy{snap, defer=defer}\n" + " assert(errno == 0)\n" + "end\n" + "return { }\n"; + + nvlist_t *result = fnvlist_alloc(); + int error = zcp_eval(nvpair_name(nvlist_next_nvpair(snaps, NULL)), + program, + 0, + zfs_lua_max_memlimit, + fnvlist_lookup_nvpair(wrapper, ZCP_ARG_ARGLIST), result); + if (error != 0) { + char *errorstr = NULL; + (void) nvlist_lookup_string(result, ZCP_RET_ERROR, &errorstr); + if (errorstr != NULL) { + zfs_dbgmsg(errorstr); + } + return (error); + } + fnvlist_free(wrapper); + + /* + * lzc_destroy_snaps() is documented to fill the errlist with + * int32 values, so we need to covert the int64 values that are + * returned from LUA. + */ + int rv = 0; + nvlist_t *errlist_raw = fnvlist_lookup_nvlist(result, ZCP_RET_RETURN); + for (nvpair_t *pair = nvlist_next_nvpair(errlist_raw, NULL); + pair != NULL; pair = nvlist_next_nvpair(errlist_raw, pair)) { + int32_t val = (int32_t)fnvpair_value_int64(pair); + if (rv == 0) + rv = val; + fnvlist_add_int32(errlist, nvpair_name(pair), val); + } + fnvlist_free(result); + return (rv); } int @@ -598,10 +635,6 @@ dsl_dataset_phys(ds)->ds_unique_bytes == 0); } -typedef struct dsl_destroy_head_arg { - const char *ddha_name; -} dsl_destroy_head_arg_t; - int dsl_destroy_head_check_impl(dsl_dataset_t *ds, int expected_holds) { @@ -647,7 +680,7 @@ return (0); } -static int +int dsl_destroy_head_check(void *arg, dmu_tx_t *tx) { dsl_destroy_head_arg_t *ddha = arg; @@ -880,7 +913,7 @@ } } -static void +void dsl_destroy_head_sync(void *arg, dmu_tx_t *tx) { dsl_destroy_head_arg_t *ddha = arg; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c @@ -934,61 +934,139 @@ dd->dd_pool->dp_origin_snap->ds_object)); } + +uint64_t +dsl_dir_get_used(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_bytes); +} + +uint64_t +dsl_dir_get_quota(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_quota); +} + +uint64_t +dsl_dir_get_reservation(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_reserved); +} + +uint64_t +dsl_dir_get_compressratio(dsl_dir_t *dd) +{ + /* a fixed point number, 100x the ratio */ + return (dsl_dir_phys(dd)->dd_compressed_bytes == 0 ? 100 : + (dsl_dir_phys(dd)->dd_uncompressed_bytes * 100 / + dsl_dir_phys(dd)->dd_compressed_bytes)); +} + +uint64_t +dsl_dir_get_logicalused(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_uncompressed_bytes); +} + +uint64_t +dsl_dir_get_usedsnap(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_SNAP]); +} + +uint64_t +dsl_dir_get_usedds(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_HEAD]); +} + +uint64_t +dsl_dir_get_usedrefreserv(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_REFRSRV]); +} + +uint64_t +dsl_dir_get_usedchild(dsl_dir_t *dd) +{ + return (dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD] + + dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD_RSRV]); +} + void +dsl_dir_get_origin(dsl_dir_t *dd, char *buf) +{ + dsl_dataset_t *ds; + VERIFY0(dsl_dataset_hold_obj(dd->dd_pool, + dsl_dir_phys(dd)->dd_origin_obj, FTAG, &ds)); + + dsl_dataset_name(ds, buf); + + dsl_dataset_rele(ds, FTAG); +} + +int +dsl_dir_get_filesystem_count(dsl_dir_t *dd, uint64_t *count) +{ + if (dsl_dir_is_zapified(dd)) { + objset_t *os = dd->dd_pool->dp_meta_objset; + return (zap_lookup(os, dd->dd_object, DD_FIELD_FILESYSTEM_COUNT, + sizeof (*count), 1, count)); + } else { + return (ENOENT); + } +} + +int +dsl_dir_get_snapshot_count(dsl_dir_t *dd, uint64_t *count) +{ + if (dsl_dir_is_zapified(dd)) { + objset_t *os = dd->dd_pool->dp_meta_objset; + return (zap_lookup(os, dd->dd_object, DD_FIELD_SNAPSHOT_COUNT, + sizeof (*count), 1, count)); + } else { + return (ENOENT); + } +} + +void dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv) { mutex_enter(&dd->dd_lock); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED, - dsl_dir_phys(dd)->dd_used_bytes); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_QUOTA, - dsl_dir_phys(dd)->dd_quota); + dsl_dir_get_quota(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_RESERVATION, - dsl_dir_phys(dd)->dd_reserved); - dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, - dsl_dir_phys(dd)->dd_compressed_bytes == 0 ? 100 : - (dsl_dir_phys(dd)->dd_uncompressed_bytes * 100 / - dsl_dir_phys(dd)->dd_compressed_bytes)); + dsl_dir_get_reservation(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_LOGICALUSED, - dsl_dir_phys(dd)->dd_uncompressed_bytes); + dsl_dir_get_logicalused(dd)); if (dsl_dir_phys(dd)->dd_flags & DD_FLAG_USED_BREAKDOWN) { dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDSNAP, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_SNAP]); + dsl_dir_get_usedsnap(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDDS, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_HEAD]); + dsl_dir_get_usedds(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDREFRESERV, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_REFRSRV]); + dsl_dir_get_usedrefreserv(dd)); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USEDCHILD, - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD] + - dsl_dir_phys(dd)->dd_used_breakdown[DD_USED_CHILD_RSRV]); + dsl_dir_get_usedchild(dd)); } mutex_exit(&dd->dd_lock); - if (dsl_dir_is_zapified(dd)) { - uint64_t count; - objset_t *os = dd->dd_pool->dp_meta_objset; - - if (zap_lookup(os, dd->dd_object, DD_FIELD_FILESYSTEM_COUNT, - sizeof (count), 1, &count) == 0) { - dsl_prop_nvlist_add_uint64(nv, - ZFS_PROP_FILESYSTEM_COUNT, count); - } - if (zap_lookup(os, dd->dd_object, DD_FIELD_SNAPSHOT_COUNT, - sizeof (count), 1, &count) == 0) { - dsl_prop_nvlist_add_uint64(nv, - ZFS_PROP_SNAPSHOT_COUNT, count); - } + uint64_t count; + if (dsl_dir_get_filesystem_count(dd, &count) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_FILESYSTEM_COUNT, + count); } + if (dsl_dir_get_snapshot_count(dd, &count) == 0) { + dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_SNAPSHOT_COUNT, + count); + } if (dsl_dir_is_clone(dd)) { - dsl_dataset_t *ds; char buf[ZFS_MAX_DATASET_NAME_LEN]; - - VERIFY0(dsl_dataset_hold_obj(dd->dd_pool, - dsl_dir_phys(dd)->dd_origin_obj, FTAG, &ds)); - dsl_dataset_name(ds, buf); - dsl_dataset_rele(ds, FTAG); + dsl_dir_get_origin(dd, buf); dsl_prop_nvlist_add_string(nv, ZFS_PROP_ORIGIN, buf); } + } void Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lbaselib.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lbaselib.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lbaselib.c @@ -17,7 +17,11 @@ #include #include +#ifdef illumos #define toupper(C) (((C) >= 'a' && (C) <= 'z')? (C) - 'a' + 'A': (C)) +#else +#define isalnum(C) (isalpha(C) || isdigit(C)) +#endif #define lbaselib_c #define LUA_LIB Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldo.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldo.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldo.c @@ -47,9 +47,15 @@ #if !defined(LUAI_THROW) #ifdef _KERNEL +#ifdef illumos #define LUAI_THROW(L,c) longjmp(&(c)->b) #define LUAI_TRY(L,c,a) if (setjmp(&(c)->b) == 0) { a } #define luai_jmpbuf label_t +#else +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf +#endif #else #if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* C++ exceptions */ Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstrlib.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstrlib.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstrlib.c @@ -32,8 +32,12 @@ /* * PATCHED: add missing character macros. */ +#ifdef illumos #define tolower(C) (((C) >= 'A' && (C) <= 'Z') ? (C) - 'A' + 'a' : (C)) #define toupper(C) (((C) >= 'a' && (C) <= 'z') ? (C) - 'a' + 'A': (C)) +#else +#define isalnum(C) (isalpha(C) || isdigit(C)) +#endif #define iscntrl(C) ((((C) >= 0) && ((C) <= 0x1f)) || ((C) == 0x7f)) #define isgraph(C) ((C) >= 0x21 && (C) <= 0x7E) #define ispunct(C) (((C) >= 0x21 && (C) <= 0x2F) || \ @@ -958,6 +962,7 @@ nb = str_sprintf(buff, form, ni); break; } +#if defined(LUA_USE_FLOAT_FORMATS) case 'e': case 'E': case 'f': #if defined(LUA_USE_AFORMAT) case 'a': case 'A': @@ -967,6 +972,7 @@ nb = str_sprintf(buff, form, (LUA_FLTFRM_T)luaL_checknumber(L, arg)); break; } +#endif case 'q': { addquoted(L, &b, arg); break; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/luaconf.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/luaconf.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/luaconf.h @@ -9,8 +9,11 @@ #define lconfig_h #include - +#ifdef illumos #include +#else +#include +#endif extern ssize_t lcompat_sprintf(char *, const char *, ...); extern int64_t lcompat_strtoll(const char *, char **); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h @@ -227,6 +227,16 @@ return (ds->ds_dbuf->db_data); } +typedef struct dsl_dataset_promote_arg { + const char *ddpa_clonename; + dsl_dataset_t *ddpa_clone; + list_t shared_snaps, origin_snaps, clone_snaps; + dsl_dataset_t *origin_origin; /* origin of the origin */ + uint64_t used, comp, uncomp, unique, cloneusedsnap, originusedsnap; + nvlist_t *err_ds; + cred_t *cr; +} dsl_dataset_promote_arg_t; + /* * The max length of a temporary tag prefix is the number of hex digits * required to express UINT64_MAX plus one for the hyphen. @@ -260,6 +270,8 @@ uint64_t dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, uint64_t flags, dmu_tx_t *tx); int dsl_dataset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors); +void dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx); +int dsl_dataset_promote_check(void *arg, dmu_tx_t *tx); int dsl_dataset_promote(const char *name, char *conflsnap); int dsl_dataset_clone_swap(dsl_dataset_t *clone, dsl_dataset_t *origin_head, boolean_t force); @@ -284,7 +296,36 @@ dmu_tx_t *tx, boolean_t async); void dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx); + +int get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val); +char *get_receive_resume_stats_impl(dsl_dataset_t *ds); +char *get_child_receive_stats(dsl_dataset_t *ds); +uint64_t dsl_get_refratio(dsl_dataset_t *ds); +uint64_t dsl_get_logicalreferenced(dsl_dataset_t *ds); +uint64_t dsl_get_compressratio(dsl_dataset_t *ds); +uint64_t dsl_get_used(dsl_dataset_t *ds); +uint64_t dsl_get_creation(dsl_dataset_t *ds); +uint64_t dsl_get_creationtxg(dsl_dataset_t *ds); +uint64_t dsl_get_refquota(dsl_dataset_t *ds); +uint64_t dsl_get_refreservation(dsl_dataset_t *ds); +uint64_t dsl_get_guid(dsl_dataset_t *ds); +uint64_t dsl_get_unique(dsl_dataset_t *ds); +uint64_t dsl_get_objsetid(dsl_dataset_t *ds); +uint64_t dsl_get_userrefs(dsl_dataset_t *ds); +uint64_t dsl_get_defer_destroy(dsl_dataset_t *ds); +uint64_t dsl_get_referenced(dsl_dataset_t *ds); +uint64_t dsl_get_numclones(dsl_dataset_t *ds); +uint64_t dsl_get_inconsistent(dsl_dataset_t *ds); +uint64_t dsl_get_available(dsl_dataset_t *ds); +int dsl_get_written(dsl_dataset_t *ds, uint64_t *written); +int dsl_get_prev_snap(dsl_dataset_t *ds, char *snap); +int dsl_get_mountpoint(dsl_dataset_t *ds, const char *dsname, char *value, + char *source); + +void get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv); + void dsl_dataset_stats(dsl_dataset_t *os, nvlist_t *nv); + void dsl_dataset_fast_stat(dsl_dataset_t *ds, dmu_objset_stats_t *stat); void dsl_dataset_space(dsl_dataset_t *ds, uint64_t *refdbytesp, uint64_t *availbytesp, Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_destroy.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_destroy.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_destroy.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ @@ -45,6 +45,21 @@ int dsl_destroy_snapshot_check_impl(struct dsl_dataset *, boolean_t); void dsl_destroy_snapshot_sync_impl(struct dsl_dataset *, boolean_t, struct dmu_tx *); + +typedef struct dsl_destroy_snapshot_arg { + const char *ddsa_name; + boolean_t ddsa_defer; +} dsl_destroy_snapshot_arg_t; + +int dsl_destroy_snapshot_check(void *, dmu_tx_t *); +void dsl_destroy_snapshot_sync(void *, dmu_tx_t *); + +typedef struct dsl_destroy_head_arg { + const char *ddha_name; +} dsl_destroy_head_arg_t; + +int dsl_destroy_head_check(void *, dmu_tx_t *); +void dsl_destroy_head_sync(void *, dmu_tx_t *); #ifdef __cplusplus } Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h @@ -131,6 +131,20 @@ int dsl_dir_namelen(dsl_dir_t *dd); uint64_t dsl_dir_create_sync(dsl_pool_t *dp, dsl_dir_t *pds, const char *name, dmu_tx_t *tx); + +uint64_t dsl_dir_get_used(dsl_dir_t *dd); +uint64_t dsl_dir_get_quota(dsl_dir_t *dd); +uint64_t dsl_dir_get_reservation(dsl_dir_t *dd); +uint64_t dsl_dir_get_compressratio(dsl_dir_t *dd); +uint64_t dsl_dir_get_logicalused(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedsnap(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedds(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedrefreserv(dsl_dir_t *dd); +uint64_t dsl_dir_get_usedchild(dsl_dir_t *dd); +void dsl_dir_get_origin(dsl_dir_t *dd, char *buf); +int dsl_dir_get_filesystem_count(dsl_dir_t *dd, uint64_t *count); +int dsl_dir_get_snapshot_count(dsl_dir_t *dd, uint64_t *count); + void dsl_dir_stats(dsl_dir_t *dd, nvlist_t *nv); uint64_t dsl_dir_space_available(dsl_dir_t *dd, dsl_dir_t *ancestor, int64_t delta, int ondiskonly); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. - * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright 2016 RackTop Systems. * Copyright (c) 2014 Integros [integros.com] */ @@ -412,19 +412,21 @@ #define ZPOOL_EXPORT_AFTER_SPLIT 0x1 #ifdef _KERNEL +struct objset; +struct zfsvfs; typedef struct zfs_creat { nvlist_t *zct_zplprops; nvlist_t *zct_props; } zfs_creat_t; -extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr); -extern int zfs_secpolicy_rename_perms(const char *from, - const char *to, cred_t *cr); -extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr); +extern int zfs_secpolicy_snapshot_perms(const char *, cred_t *); +extern int zfs_secpolicy_rename_perms(const char *, const char *, cred_t *); +extern int zfs_secpolicy_destroy_perms(const char *, cred_t *); extern int zfs_busy(void); extern int zfs_unmount_snap(const char *); extern void zfs_destroy_unmount_origin(const char *); +extern int getzfsvfs_impl(struct objset *, struct zfsvfs **); /* * ZFS minor numbers can refer to either a control device instance or Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h @@ -155,6 +155,7 @@ uint64_t fuid); extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers); extern int zfsvfs_create(const char *name, zfsvfs_t **zfvp); +extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os); extern void zfsvfs_free(zfsvfs_t *zfsvfs); extern int zfs_check_global_label(const char *dsname, const char *hexsl); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp.c @@ -100,8 +100,15 @@ #include #include #include +#ifdef illumos #include +#endif +#ifdef __FreeBSD__ +#define ECHRNG EDOM +#define ETIME ETIMEDOUT +#endif + uint64_t zfs_lua_check_instrlimit_interval = 100; uint64_t zfs_lua_max_instrlimit = ZCP_MAX_INSTRLIMIT; uint64_t zfs_lua_max_memlimit = ZCP_MAX_MEMLIMIT; @@ -607,23 +614,23 @@ { if (error == ENOENT) { (void) zcp_argerror(state, 1, "no such dataset '%s'", dsname); - return (NULL); /* not reached; zcp_argerror will longjmp */ + return (0); /* not reached; zcp_argerror will longjmp */ } else if (error == EXDEV) { (void) zcp_argerror(state, 1, "dataset '%s' is not in the target pool '%s'", dsname, spa_name(dp->dp_spa)); - return (NULL); /* not reached; zcp_argerror will longjmp */ + return (0); /* not reached; zcp_argerror will longjmp */ } else if (error == EIO) { (void) luaL_error(state, "I/O error while accessing dataset '%s'", dsname); - return (NULL); /* not reached; luaL_error will longjmp */ + return (0); /* not reached; luaL_error will longjmp */ } else if (error != 0) { (void) luaL_error(state, "unexpected error %d while accessing dataset '%s'", error, dsname); - return (NULL); /* not reached; luaL_error will longjmp */ + return (0); /* not reached; luaL_error will longjmp */ } - return (NULL); + return (0); } /* @@ -646,10 +653,10 @@ .func = zcp_debug, .pargs = { { .za_name = "debug string", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -675,10 +682,10 @@ .func = zcp_exists, .pargs = { { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -849,7 +856,7 @@ /* * Tell the Lua interpreter to call our handler every count * instructions. Channel programs that execute too many instructions - * should die with ETIME. + * should die with ETIMEDOUT. */ (void) lua_sethook(state, zcp_lua_counthook, LUA_MASKCOUNT, zfs_lua_check_instrlimit_interval); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c @@ -285,11 +285,19 @@ tmp = 1; break; default: +#ifdef illumos VFS_RELE(vfsp); +#else + vfs_unbusy(vfsp); +#endif return (ENOENT); } +#ifdef illumos VFS_RELE(vfsp); +#else + vfs_unbusy(vfsp); +#endif if (tmp != *val) { (void) strcpy(setpoint, "temporary"); *val = tmp; @@ -330,8 +338,7 @@ error = get_clones_stat_impl(ds, clones); if (error == 0) { /* push list to lua stack */ - VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, - NULL)); + VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, 0)); /* source */ (void) lua_pushnil(state); } @@ -784,10 +791,10 @@ .pargs = { { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, { .za_name = "property", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_global.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_global.c @@ -63,7 +63,7 @@ {"EDOM", EDOM}, {"ERANGE", ERANGE}, {"EDQUOT", EDQUOT}, - {NULL, NULL} + {NULL, 0} }; static void Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_iter.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_iter.c @@ -110,10 +110,10 @@ .gc = NULL, .pargs = { { .za_name = "snapshot", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -199,10 +199,10 @@ .gc = NULL, .pargs = { { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -300,10 +300,10 @@ .gc = NULL, .pargs = { { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -381,10 +381,10 @@ .pargs = { { .za_name = "filesystem | snapshot | volume", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; @@ -439,10 +439,10 @@ .func = zcp_system_props_list, .pargs = { { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_synctask.c =================================================================== --- /dev/null +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_synctask.c @@ -95,11 +95,11 @@ .blocks_modified = 0, .pargs = { {.za_name = "filesystem | snapshot", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { {.za_name = "defer", .za_lua_type = LUA_TBOOLEAN}, - {NULL, NULL} + {NULL, 0} } }; @@ -148,10 +148,10 @@ .blocks_modified = 3, .pargs = { {.za_name = "clone", .za_lua_type = LUA_TSTRING}, - {NULL, NULL} + {NULL, 0} }, .kwargs = { - {NULL, NULL} + {NULL, 0} } }; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -27,7 +27,7 @@ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. - * Copyright (c) 2011, 2015 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -192,6 +192,7 @@ #include #include #include +#include #include #include "zfs_namecheck.h" @@ -200,8 +201,10 @@ #include "zfs_comutil.h" #include "zfs_ioctl_compat.h" -CTASSERT(sizeof(zfs_cmd_t) < IOCPARM_MAX); +#include "lua.h" +#include "lauxlib.h" +CTASSERT(sizeof(zfs_cmd_t) < IOCPARM_MAX); static struct cdev *zfsdev; extern void zfs_init(void); @@ -1436,34 +1439,42 @@ return (error); } -static int -getzfsvfs(const char *dsname, zfsvfs_t **zfvp) +int +getzfsvfs_impl(objset_t *os, zfsvfs_t **zfvp) { - objset_t *os; vfs_t *vfsp; - int error; + int error = 0; - error = dmu_objset_hold(dsname, FTAG, &os); - if (error != 0) - return (error); if (dmu_objset_type(os) != DMU_OST_ZFS) { - dmu_objset_rele(os, FTAG); return (SET_ERROR(EINVAL)); } mutex_enter(&os->os_user_ptr_lock); *zfvp = dmu_objset_get_user(os); if (*zfvp) { - vfsp = (*zfvp)->z_vfs; - vfs_ref(vfsp); + vfs_ref((*zfvp)->z_vfs); } else { error = SET_ERROR(ESRCH); } mutex_exit(&os->os_user_ptr_lock); + return (error); +} + +static int +getzfsvfs(const char *dsname, zfsvfs_t **zfvp) +{ + objset_t *os; + int error; + + error = dmu_objset_hold(dsname, FTAG, &os); + if (error != 0) + return (error); + + error = getzfsvfs_impl(os, zfvp); dmu_objset_rele(os, FTAG); if (error == 0) { - error = vfs_busy(vfsp, 0); - vfs_rel(vfsp); + error = vfs_busy((*zfvp)->z_vfs, 0); + vfs_rel((*zfvp)->z_vfs); if (error != 0) { *zfvp = NULL; error = SET_ERROR(ESRCH); @@ -3756,6 +3767,36 @@ return (error); } +static int +zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, + nvlist_t *outnvl) +{ + char *program; + uint64_t instrlimit, memlimit; + nvpair_t *nvarg = NULL; + + if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { + return (EINVAL); + } + if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) { + instrlimit = ZCP_DEFAULT_INSTRLIMIT; + } + if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { + memlimit = ZCP_DEFAULT_MEMLIMIT; + } + if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { + return (EINVAL); + } + + if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) + return (EINVAL); + if (memlimit == 0 || memlimit > ZCP_MAX_MEMLIMIT) + return (EINVAL); + + return (zcp_eval(poolname, program, instrlimit, memlimit, + nvarg, outnvl)); +} + /* * inputs: * zc_name name of dataset to destroy @@ -6021,6 +6062,11 @@ POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, + zfs_ioc_channel_program, zfs_secpolicy_config, + POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, + B_TRUE); + /* IOCTLS that use the legacy function signature */ zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, @@ -6541,11 +6587,22 @@ outnvl = fnvlist_alloc(); error = vec->zvec_func(zc->zc_name, innvl, outnvl); - if (error == 0 && vec->zvec_allow_log && + /* + * Some commands can partially execute, modfiy state, and still + * return an error. In these cases, attempt to record what + * was modified. + */ + if ((error == 0 || + (cmd == ZFS_IOC_CHANNEL_PROGRAM && error != EINVAL)) && + vec->zvec_allow_log && spa_open(zc->zc_name, &spa, FTAG) == 0) { if (!nvlist_empty(outnvl)) { fnvlist_add_nvlist(lognv, ZPOOL_HIST_OUTPUT_NVL, outnvl); + } + if (error != 0) { + fnvlist_add_int64(lognv, ZPOOL_HIST_ERRNO, + error); } (void) spa_history_log_nvl(spa, lognv); spa_close(spa, FTAG); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c @@ -995,12 +995,26 @@ * We claim to always be readonly so we can open snapshots; * other ZPL code will prevent us from writing to snapshots. */ + error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, zfsvfs, &os); - if (error) { + if (error != 0) { kmem_free(zfsvfs, sizeof (zfsvfs_t)); return (error); } + error = zfsvfs_create_impl(zfvp, zfsvfs, os); + if (error != 0) { + dmu_objset_disown(os, zfsvfs); + } + return (error); +} + + +int +zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os) +{ + int error; + zfsvfs->z_vfs = NULL; zfsvfs->z_parent = zfsvfs; @@ -1020,7 +1034,6 @@ error = zfsvfs_init(zfsvfs, os); if (error != 0) { - dmu_objset_disown(os, zfsvfs); *zfvp = NULL; kmem_free(zfsvfs, sizeof (zfsvfs_t)); return (error); Index: sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012, Martin Matuska . All rights reserved. @@ -257,11 +257,17 @@ #define ZPOOL_ROOTFS_PROPS "root-props-nvl" /* + * Length of 'written@' and 'written#' + */ +#define ZFS_WRITTEN_PROP_PREFIX_LEN 8 + +/* * Dataset property functions shared between libzfs and kernel. */ const char *zfs_prop_default_string(zfs_prop_t); uint64_t zfs_prop_default_numeric(zfs_prop_t); boolean_t zfs_prop_readonly(zfs_prop_t); +boolean_t zfs_prop_visible(zfs_prop_t prop); boolean_t zfs_prop_inheritable(zfs_prop_t); boolean_t zfs_prop_setonce(zfs_prop_t); const char *zfs_prop_to_name(zfs_prop_t); @@ -905,7 +911,10 @@ ZFS_IOC_BOOKMARK, ZFS_IOC_GET_BOOKMARKS, ZFS_IOC_DESTROY_BOOKMARKS, +#ifdef __FreeBSD__ ZFS_IOC_NEXTBOOT, +#endif + ZFS_IOC_CHANNEL_PROGRAM, ZFS_IOC_LAST } zfs_ioc_t; @@ -950,6 +959,7 @@ #define ZPOOL_HIST_OUTPUT_NVL "out_nvl" #define ZPOOL_HIST_DSNAME "dsname" #define ZPOOL_HIST_DSID "dsid" +#define ZPOOL_HIST_ERRNO "errno" /* * Flags for ZFS_IOC_VDEV_SET_STATE @@ -968,6 +978,24 @@ #define ZFS_IMPORT_ANY_HOST 0x2 #define ZFS_IMPORT_MISSING_LOG 0x4 #define ZFS_IMPORT_ONLY 0x8 + +/* + * Channel program argument/return nvlist keys and defaults. + */ +#define ZCP_ARG_PROGRAM "program" +#define ZCP_ARG_ARGLIST "arg" +#define ZCP_ARG_INSTRLIMIT "instrlimit" +#define ZCP_ARG_MEMLIMIT "memlimit" + +#define ZCP_ARG_CLIARGV "argv" + +#define ZCP_RET_ERROR "error" +#define ZCP_RET_RETURN "return" + +#define ZCP_DEFAULT_INSTRLIMIT (10 * 1000 * 1000) +#define ZCP_MAX_INSTRLIMIT (10 * ZCP_DEFAULT_INSTRLIMIT) +#define ZCP_DEFAULT_MEMLIMIT (10 * 1024 * 1024) +#define ZCP_MAX_MEMLIMIT (10 * ZCP_DEFAULT_MEMLIMIT) /* * Sysevent payload members. ZFS will generate the following sysevents with the Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -267,6 +267,34 @@ cddl/contrib/opensolaris/uts/common/zmod/zmod.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/uts/common/zmod/zmod_subr.c optional zfs compile-with "${ZFS_C}" cddl/contrib/opensolaris/uts/common/zmod/zutil.c optional zfs compile-with "${ZFS_C}" +# zfs lua support +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lapi.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lauxlib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lbaselib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lbitlib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lcode.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lcompat.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lcorolib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lctype.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldebug.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldo.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ldump.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lfunc.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lgc.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/llex.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lmem.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lobject.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lopcodes.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lparser.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstate.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstring.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstrlib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ltable.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ltablib.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ltm.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lundump.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lvm.c optional zfs compile-with "${ZFS_C}" +cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lzio.c optional zfs compile-with "${ZFS_C}" # dtrace specific cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c optional dtrace compile-with "${DTRACE_C}" \ warning "kernel contains CDDL licensed DTRACE" Index: sys/conf/kern.pre.mk =================================================================== --- sys/conf/kern.pre.mk +++ sys/conf/kern.pre.mk @@ -139,7 +139,11 @@ CDDL_C= ${CC} -c ${CDDL_CFLAGS} ${WERROR} ${PROF} ${.IMPSRC} # Special flags for managing the compat compiles for ZFS -ZFS_CFLAGS= -DBUILDING_ZFS -I$S/cddl/contrib/opensolaris/uts/common/fs/zfs -I$S/cddl/contrib/opensolaris/uts/common/zmod -I$S/cddl/contrib/opensolaris/common/zfs ${CDDL_CFLAGS} +ZFS_CFLAGS= -DBUILDING_ZFS -I$S/cddl/contrib/opensolaris/uts/common/fs/zfs +ZFS_CFLAGS+= -I$S/cddl/contrib/opensolaris/uts/common/fs/zfs/lua +ZFS_CFLAGS+= -I$S/cddl/contrib/opensolaris/uts/common/zmod +ZFS_CFLAGS+= -I$S/cddl/contrib/opensolaris/common/zfs +ZFS_CFLAGS+= ${CDDL_CFLAGS} ZFS_ASM_CFLAGS= -x assembler-with-cpp -DLOCORE ${ZFS_CFLAGS} ZFS_C= ${CC} -c ${ZFS_CFLAGS} ${WERROR} ${PROF} ${.IMPSRC} ZFS_S= ${CC} -c ${ZFS_ASM_CFLAGS} ${WERROR} ${.IMPSRC} Index: sys/modules/zfs/Makefile =================================================================== --- sys/modules/zfs/Makefile +++ sys/modules/zfs/Makefile @@ -79,12 +79,16 @@ SRCS+= ${ZFS_SRCS} SRCS+= vdev_geom.c SRCS+= trim_map.c +.PATH: ${SUNW}/uts/common/fs/zfs/lua +LUA_SRCS= ${LUA_OBJS:C/.o$/.c/} +SRCS+= ${LUA_SRCS} # Use FreeBSD's namecache. CFLAGS+=-DFREEBSD_NAMECACHE CFLAGS+=-I${SYSDIR}/cddl/compat/opensolaris CFLAGS+=-I${SUNW}/uts/common/fs/zfs +CFLAGS+=-I${SUNW}/uts/common/fs/zfs/lua CFLAGS+=-I${SUNW}/uts/common/zmod CFLAGS+=-I${SUNW}/uts/common CFLAGS+=-I${SYSDIR}