From owner-svn-src-stable@freebsd.org Wed Nov 8 08:53:46 2017 Return-Path: Delivered-To: svn-src-stable@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 6541EE71961; Wed, 8 Nov 2017 08:53:46 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id F04CC70E72; Wed, 8 Nov 2017 08:53:45 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id vA88rjSt063401; Wed, 8 Nov 2017 08:53:45 GMT (envelope-from avg@FreeBSD.org) Received: (from avg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id vA88ribj063395; Wed, 8 Nov 2017 08:53:44 GMT (envelope-from avg@FreeBSD.org) Message-Id: <201711080853.vA88ribj063395@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: avg set sender to avg@FreeBSD.org using -f From: Andriy Gapon Date: Wed, 8 Nov 2017 08:53:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r325534 - in stable/11: cddl/contrib/opensolaris/cmd/zfs cddl/contrib/opensolaris/cmd/zpool cddl/contrib/opensolaris/lib/libzfs/common cddl/contrib/opensolaris/lib/libzfs_core/common cd... X-SVN-Group: stable-11 X-SVN-Commit-Author: avg X-SVN-Commit-Paths: in stable/11: cddl/contrib/opensolaris/cmd/zfs cddl/contrib/opensolaris/cmd/zpool cddl/contrib/opensolaris/lib/libzfs/common cddl/contrib/opensolaris/lib/libzfs_core/common cddl/contrib/opensolaris/li... X-SVN-Commit-Revision: 325534 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Nov 2017 08:53:46 -0000 Author: avg Date: Wed Nov 8 08:53:44 2017 New Revision: 325534 URL: https://svnweb.freebsd.org/changeset/base/325534 Log: MFC r324163: MFV r323530,r323533,r323534: 7431 ZFS Channel Programs, and followups Also MFC-ed are the following fixes: - r324164 Add several new files to the files enabled by ZFS kernel option - r324178 unbreak kernel builds on sparc64 and powerpc - r324194 fix incorrect use of getzfsvfs_impl in r324163 - r324292 really unbreak kernel builds on sparc64 and powerpc64 Added: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 - copied unchanged from r324163, head/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ - copied from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/ stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp.h - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_global.h - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_global.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_iter.h - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_iter.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_prop.h - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zcp_prop.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp.c - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c - copied, changed from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_global.c - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_global.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_iter.c - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_iter.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_synctask.c - copied unchanged from r324163, head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_synctask.c Modified: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8 stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c stable/11/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c stable/11/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h stable/11/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c stable/11/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h stable/11/cddl/lib/libzpool/Makefile stable/11/cddl/sbin/zfs/Makefile stable/11/sys/cddl/compat/opensolaris/kern/opensolaris_sunddi.c stable/11/sys/cddl/compat/opensolaris/sys/sunddi.h stable/11/sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c stable/11/sys/cddl/contrib/opensolaris/uts/common/Makefile.files stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_destroy.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua/lstrlib.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dataset.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_destroy.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_dir.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c stable/11/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h stable/11/sys/conf/files stable/11/sys/conf/kern.pre.mk stable/11/sys/modules/zfs/Makefile Directory Properties: stable/11/ (props changed) Copied: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 (from r324163, head/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 Wed Nov 8 08:53:44 2017 (r325534, copy of r324163, head/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 Modified: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8 ============================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Wed Nov 8 08:37:05 2017 (r325533) +++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Wed Nov 8 08:53:44 2017 (r325534) @@ -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 @@ Give more parsable tab-separated output, without heade 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 Modified: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c ============================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c Wed Nov 8 08:37:05 2017 (r325533) +++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c Wed Nov 8 08:53:44 2017 (r325534) @@ -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_diff(int argc, char **argv); 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 @@ typedef enum { HELP_RELEASE, HELP_DIFF, HELP_BOOKMARK, + HELP_CHANNEL_PROGRAM, } zfs_help_t; typedef struct zfs_command { @@ -187,6 +190,7 @@ static zfs_command_t command_table[] = { { "promote", zfs_do_promote, HELP_PROMOTE }, { "rename", zfs_do_rename, HELP_RENAME }, { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, + { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM }, { NULL }, { "list", zfs_do_list, HELP_LIST }, { NULL }, @@ -340,6 +344,10 @@ get_usage(zfs_help_t idx) "[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 @@ safe_malloc(size_t size) return (data); } +void * +safe_realloc(void *data, size_t size) +{ + void *newp; + if ((newp = realloc(data, size)) == NULL) { + free(data); + nomem(); + } + + return (newp); +} + static char * safe_strdup(char *str) { @@ -7094,6 +7114,194 @@ zfs_do_bookmark(int argc, char **argv) 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: Modified: stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c ============================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c Wed Nov 8 08:37:05 2017 (r325533) +++ stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c Wed Nov 8 08:53:44 2017 (r325534) @@ -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 @@ get_history_one(zpool_handle_t *zhp, void *data) (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) Modified: stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c ============================================================================== --- stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c Wed Nov 8 08:37:05 2017 (r325533) +++ stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c Wed Nov 8 08:53:44 2017 (r325534) @@ -2356,6 +2356,74 @@ zfs_get_clones_nvl(zfs_handle_t *zhp) } /* + * 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. @@ -2401,6 +2469,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char &t) == 0) (void) snprintf(propbuf, proplen, "%llu", val); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_MOUNTPOINT: @@ -2469,7 +2538,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char /* 'legacy' or 'none' */ (void) strlcpy(propbuf, str, proplen); } - + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_ORIGIN: @@ -2477,6 +2546,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char if (str == NULL) return (-1); (void) strlcpy(propbuf, str, proplen); + zcp_check(zhp, prop, NULL, str); break; case ZFS_PROP_CLONES: @@ -2491,7 +2561,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char 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 @@ -2510,6 +2579,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char else zfs_nicenum(val, propbuf, proplen); } + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_FILESYSTEM_LIMIT: @@ -2534,6 +2604,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char } else { zfs_nicenum(val, propbuf, proplen); } + + zcp_check(zhp, prop, val, NULL); break; case ZFS_PROP_REFRATIO: @@ -2543,6 +2615,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char (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: @@ -2563,6 +2636,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char abort(); } (void) snprintf(propbuf, proplen, "%s", str); + zcp_check(zhp, prop, NULL, propbuf); break; case ZFS_PROP_MOUNTED: *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***