xref: /freebsd/sys/contrib/openzfs/man/man8/zfs-program.8 (revision 3a8960711f4319f9b894ea2453c89065ee1b3a10)
1.\" SPDX-License-Identifier: CDDL-1.0
2.\"
3.\" This file and its contents are supplied under the terms of the
4.\" Common Development and Distribution License ("CDDL"), version 1.0.
5.\" You may only use this file in accordance with the terms of version
6.\" 1.0 of the CDDL.
7.\"
8.\" A full copy of the text of the CDDL should have accompanied this
9.\" source.  A copy of the CDDL is also available via the Internet at
10.\" http://www.illumos.org/license/CDDL.
11.\"
12.\" Copyright (c) 2016, 2019 by Delphix. All Rights Reserved.
13.\" Copyright (c) 2019, 2020 by Christian Schwarz. All Rights Reserved.
14.\" Copyright 2020 Joyent, Inc.
15.\" Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
16.\"
17.Dd June 5, 2025
18.Dt ZFS-PROGRAM 8
19.Os
20.
21.Sh NAME
22.Nm zfs-program
23.Nd execute ZFS channel programs
24.Sh SYNOPSIS
25.Nm zfs
26.Cm program
27.Op Fl jn
28.Op Fl t Ar instruction-limit
29.Op Fl m Ar memory-limit
30.Ar pool
31.Ar script
32.Op Ar script arguments
33.
34.Sh DESCRIPTION
35The ZFS channel program interface allows ZFS administrative operations to be
36run programmatically as a Lua script.
37The entire script is executed atomically, with no other administrative
38operations taking effect concurrently.
39A library of ZFS calls is made available to channel program scripts.
40Channel programs may only be run with root privileges.
41.Pp
42A modified version of the Lua 5.2 interpreter is used to run channel program
43scripts.
44The Lua 5.2 manual can be found at
45.Lk http://www.lua.org/manual/5.2/
46.Pp
47The channel program given by
48.Ar script
49will be run on
50.Ar pool ,
51and any attempts to access or modify other pools will cause an error.
52.
53.Sh OPTIONS
54.Bl -tag -width "-t"
55.It Fl j , -json
56Display channel program output in JSON format.
57When this flag is specified and standard output is empty -
58channel program encountered an error.
59The details of such an error will be printed to standard error in plain text.
60.It Fl n
61Executes a read-only channel program, which runs faster.
62The program cannot change on-disk state by calling functions from the
63zfs.sync submodule.
64The program can be used to gather information such as properties and
65determining if changes would succeed (zfs.check.*).
66Without this flag, all pending changes must be synced to disk before a
67channel program can complete.
68.It Fl t Ar instruction-limit
69Limit the number of Lua instructions to execute.
70If a channel program executes more than the specified number of instructions,
71it will be stopped and an error will be returned.
72The default limit is 10 million instructions, and it can be set to a maximum of
73100 million instructions.
74.It Fl m Ar memory-limit
75Memory limit, in bytes.
76If a channel program attempts to allocate more memory than the given limit, it
77will be stopped and an error returned.
78The default memory limit is 10 MiB, and can be set to a maximum of 100 MiB.
79.El
80.Pp
81All remaining argument strings will be passed directly to the Lua script as
82described in the
83.Sx LUA INTERFACE
84section below.
85.
86.Sh LUA INTERFACE
87A channel program can be invoked either from the command line, or via a library
88call to
89.Fn lzc_channel_program .
90.
91.Ss Arguments
92Arguments passed to the channel program are converted to a Lua table.
93If invoked from the command line, extra arguments to the Lua script will be
94accessible as an array stored in the argument table with the key 'argv':
95.Bd -literal -compact -offset indent
96args = ...
97argv = args["argv"]
98-- argv == {1="arg1", 2="arg2", ...}
99.Ed
100.Pp
101If invoked from the libzfs interface, an arbitrary argument list can be
102passed to the channel program, which is accessible via the same
103.Qq Li ...
104syntax in Lua:
105.Bd -literal -compact -offset indent
106args = ...
107-- args == {"foo"="bar", "baz"={...}, ...}
108.Ed
109.Pp
110Note that because Lua arrays are 1-indexed, arrays passed to Lua from the
111libzfs interface will have their indices incremented by 1.
112That is, the element
113in
114.Va arr[0]
115in a C array passed to a channel program will be stored in
116.Va arr[1]
117when accessed from Lua.
118.
119.Ss Return Values
120Lua return statements take the form:
121.Dl return ret0, ret1, ret2, ...
122.Pp
123Return statements returning multiple values are permitted internally in a
124channel program script, but attempting to return more than one value from the
125top level of the channel program is not permitted and will throw an error.
126However, tables containing multiple values can still be returned.
127If invoked from the command line, a return statement:
128.Bd -literal -compact -offset indent
129a = {foo="bar", baz=2}
130return a
131.Ed
132.Pp
133Will be output formatted as:
134.Bd -literal -compact -offset indent
135Channel program fully executed with return value:
136    return:
137        baz: 2
138        foo: 'bar'
139.Ed
140.
141.Ss Fatal Errors
142If the channel program encounters a fatal error while running, a non-zero exit
143status will be returned.
144If more information about the error is available, a singleton list will be
145returned detailing the error:
146.Dl error: \&"error string, including Lua stack trace"
147.Pp
148If a fatal error is returned, the channel program may have not executed at all,
149may have partially executed, or may have fully executed but failed to pass a
150return value back to userland.
151.Pp
152If the channel program exhausts an instruction or memory limit, a fatal error
153will be generated and the program will be stopped, leaving the program partially
154executed.
155No attempt is made to reverse or undo any operations already performed.
156Note that because both the instruction count and amount of memory used by a
157channel program are deterministic when run against the same inputs and
158filesystem state, as long as a channel program has run successfully once, you
159can guarantee that it will finish successfully against a similar size system.
160.Pp
161If a channel program attempts to return too large a value, the program will
162fully execute but exit with a nonzero status code and no return value.
163.Pp
164.Em Note :
165ZFS API functions do not generate Fatal Errors when correctly invoked, they
166return an error code and the channel program continues executing.
167See the
168.Sx ZFS API
169section below for function-specific details on error return codes.
170.
171.Ss Lua to C Value Conversion
172When invoking a channel program via the libzfs interface, it is necessary to
173translate arguments and return values from Lua values to their C equivalents,
174and vice-versa.
175.Pp
176There is a correspondence between nvlist values in C and Lua tables.
177A Lua table which is returned from the channel program will be recursively
178converted to an nvlist, with table values converted to their natural
179equivalents:
180.TS
181cw3 l c l .
182	string	->	string
183	number	->	int64
184	boolean	->	boolean_value
185	nil	->	boolean (no value)
186	table	->	nvlist
187.TE
188.Pp
189Likewise, table keys are replaced by string equivalents as follows:
190.TS
191cw3 l c l .
192	string	->	no change
193	number	->	signed decimal string ("%lld")
194	boolean	->	"true" | "false"
195.TE
196.Pp
197Any collision of table key strings (for example, the string "true" and a
198true boolean value) will cause a fatal error.
199.Pp
200Lua numbers are represented internally as signed 64-bit integers.
201.
202.Sh LUA STANDARD LIBRARY
203The following Lua built-in base library functions are available:
204.TS
205cw3 l l l l .
206	assert	rawlen	collectgarbage	rawget
207	error	rawset	getmetatable	select
208	ipairs	setmetatable	next	tonumber
209	pairs	tostring	rawequal	type
210.TE
211.Pp
212All functions in the
213.Em coroutine ,
214.Em string ,
215and
216.Em table
217built-in submodules are also available.
218A complete list and documentation of these modules is available in the Lua
219manual.
220.Pp
221The following functions base library functions have been disabled and are
222not available for use in channel programs:
223.TS
224cw3 l l l l l l .
225	dofile	loadfile	load	pcall	print	xpcall
226.TE
227.
228.Sh ZFS API
229.
230.Ss Function Arguments
231Each API function takes a fixed set of required positional arguments and
232optional keyword arguments.
233For example, the destroy function takes a single positional string argument
234(the name of the dataset to destroy) and an optional "defer" keyword boolean
235argument.
236When using parentheses to specify the arguments to a Lua function, only
237positional arguments can be used:
238.Dl Sy zfs.sync.destroy Ns Pq \&"rpool@snap"
239.Pp
240To use keyword arguments, functions must be called with a single argument that
241is a Lua table containing entries mapping integers to positional arguments and
242strings to keyword arguments:
243.Dl Sy zfs.sync.destroy Ns Pq {1="rpool@snap", defer=true}
244.Pp
245The Lua language allows curly braces to be used in place of parenthesis as
246syntactic sugar for this calling convention:
247.Dl Sy zfs.sync.snapshot Ns {"rpool@snap", defer=true}
248.
249.Ss Function Return Values
250If an API function succeeds, it returns 0.
251If it fails, it returns an error code and the channel program continues
252executing.
253API functions do not generate Fatal Errors except in the case of an
254unrecoverable internal file system error.
255.Pp
256In addition to returning an error code, some functions also return extra
257details describing what caused the error.
258This extra description is given as a second return value, and will always be a
259Lua table, or Nil if no error details were returned.
260Different keys will exist in the error details table depending on the function
261and error case.
262Any such function may be called expecting a single return value:
263.Dl errno = Sy zfs.sync.promote Ns Pq dataset
264.Pp
265Or, the error details can be retrieved:
266.Bd -literal -compact -offset indent
267.No errno, details = Sy zfs.sync.promote Ns Pq dataset
268if (errno == EEXIST) then
269    assert(details ~= Nil)
270    list_of_conflicting_snapshots = details
271end
272.Ed
273.Pp
274The following global aliases for API function error return codes are defined
275for use in channel programs:
276.TS
277cw3 l l l l l l l .
278	EPERM	ECHILD	ENODEV	ENOSPC	ENOENT	EAGAIN	ENOTDIR
279	ESPIPE	ESRCH	ENOMEM	EISDIR	EROFS	EINTR	EACCES
280	EINVAL	EMLINK	EIO	EFAULT	ENFILE	EPIPE	ENXIO
281	ENOTBLK	EMFILE	EDOM	E2BIG	EBUSY	ENOTTY	ERANGE
282	ENOEXEC	EEXIST	ETXTBSY	EDQUOT	EBADF	EXDEV	EFBIG
283.TE
284.
285.Ss API Functions
286For detailed descriptions of the exact behavior of any ZFS administrative
287operations, see the main
288.Xr zfs 8
289manual page.
290.Bl -tag -width "xx"
291.It Fn zfs.debug msg
292Record a debug message in the zfs_dbgmsg log.
293A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or
294can be monitored live by running
295.Dl dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}'
296.Pp
297.Bl -tag -compact -width "property (string)"
298.It Ar msg Pq string
299Debug message to be printed.
300.El
301.It Fn zfs.exists dataset
302Returns true if the given dataset exists, or false if it doesn't.
303A fatal error will be thrown if the dataset is not in the target pool.
304That is, in a channel program running on rpool,
305.Sy zfs.exists Ns Pq \&"rpool/nonexistent_fs"
306returns false, but
307.Sy zfs.exists Ns Pq \&"somepool/fs_that_may_exist"
308will error.
309.Pp
310.Bl -tag -compact -width "property (string)"
311.It Ar dataset Pq string
312Dataset to check for existence.
313Must be in the target pool.
314.El
315.It Fn zfs.get_prop dataset property
316Returns two values.
317First, a string, number or table containing the property value for the given
318dataset.
319Second, a string containing the source of the property (i.e. the name of the
320dataset in which it was set or nil if it is readonly).
321Throws a Lua error if the dataset is invalid or the property doesn't exist.
322Note that Lua only supports int64 number types whereas ZFS number properties
323are uint64.
324This means very large values (like GUIDs) may wrap around and appear negative.
325.Pp
326.Bl -tag -compact -width "property (string)"
327.It Ar dataset Pq string
328Filesystem or snapshot path to retrieve properties from.
329.It Ar property Pq string
330Name of property to retrieve.
331All filesystem, snapshot and volume properties are supported except for
332.Sy mounted
333and
334.Sy iscsioptions .
335Also supports the
336.Sy written@ Ns Ar snap
337and
338.Sy written# Ns Ar bookmark
339properties and the
340.Ao Sy user Ns | Ns Sy group Ac Ns Ao Sy quota Ns | Ns Sy used Ac Ns Sy @ Ns Ar id
341properties, though the id must be in numeric form.
342.El
343.El
344.Bl -tag -width "xx"
345.It Sy zfs.sync submodule
346The sync submodule contains functions that modify the on-disk state.
347They are executed in "syncing context".
348.Pp
349The available sync submodule functions are as follows:
350.Bl -tag -width "xx"
351.It Fn zfs.sync.clone snapshot newdataset
352Create a new filesystem from a snapshot.
353Returns 0 if the filesystem was successfully created,
354and a nonzero error code otherwise.
355.Pp
356Note: Due to general limitations in channel programs, a filesystem created
357this way will not be mounted, regardless of the value of the
358.Sy mountpoint
359and
360.Sy canmount
361properties.
362This limitation may be removed in the future,
363so it is recommended that you set
364.Sy mountpoint Ns = Ns Sy none
365or
366.Sy canmount Ns = Ns Sy off
367or
368.Sy noauto
369to avoid surprises.
370.Pp
371.Bl -tag -compact -width "newbookmark (string)"
372.It Ar snapshot Pq string
373Name of the source snapshot to clone.
374.It Ar newdataset Pq string
375Name of the target dataset to create.
376.El
377.It Sy zfs.sync.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false
378Destroy the given dataset.
379Returns 0 on successful destroy, or a nonzero error code if the dataset could
380not be destroyed (for example, if the dataset has any active children or
381clones).
382.Pp
383.Bl -tag -compact -width "newbookmark (string)"
384.It Ar dataset Pq string
385Filesystem or snapshot to be destroyed.
386.It Op Ar defer Pq boolean
387Valid only for destroying snapshots.
388If set to true, and the snapshot has holds or clones, allows the snapshot to be
389marked for deferred deletion rather than failing.
390.El
391.It Fn zfs.sync.inherit dataset property
392Clears the specified property in the given dataset, causing it to be inherited
393from an ancestor, or restored to the default if no ancestor property is set.
394The
395.Nm zfs Cm inherit Fl S
396option has not been implemented.
397Returns 0 on success, or a nonzero error code if the property could not be
398cleared.
399.Pp
400.Bl -tag -compact -width "newbookmark (string)"
401.It Ar dataset Pq string
402Filesystem or snapshot containing the property to clear.
403.It Ar property Pq string
404The property to clear.
405Allowed properties are the same as those for the
406.Nm zfs Cm inherit
407command.
408.El
409.It Fn zfs.sync.promote dataset
410Promote the given clone to a filesystem.
411Returns 0 on successful promotion, or a nonzero error code otherwise.
412If EEXIST is returned, the second return value will be an array of the clone's
413snapshots whose names collide with snapshots of the parent filesystem.
414.Pp
415.Bl -tag -compact -width "newbookmark (string)"
416.It Ar dataset Pq string
417Clone to be promoted.
418.El
419.It Fn zfs.sync.rollback filesystem
420Rollback to the previous snapshot for a dataset.
421Returns 0 on successful rollback, or a nonzero error code otherwise.
422Rollbacks can be performed on filesystems or zvols, but not on snapshots
423or mounted datasets.
424EBUSY is returned in the case where the filesystem is mounted.
425.Pp
426.Bl -tag -compact -width "newbookmark (string)"
427.It Ar filesystem Pq string
428Filesystem to rollback.
429.El
430.It Fn zfs.sync.set_prop dataset property value
431Sets the given property on a dataset.
432Currently only user properties are supported.
433Returns 0 if the property was set, or a nonzero error code otherwise.
434.Pp
435.Bl -tag -compact -width "newbookmark (string)"
436.It Ar dataset Pq string
437The dataset where the property will be set.
438.It Ar property Pq string
439The property to set.
440.It Ar value Pq string
441The value of the property to be set.
442.El
443.It Fn zfs.sync.snapshot dataset
444Create a snapshot of a filesystem.
445Returns 0 if the snapshot was successfully created,
446and a nonzero error code otherwise.
447.Pp
448Note: Taking a snapshot will fail on any pool older than legacy version 27.
449To enable taking snapshots from ZCP scripts, the pool must be upgraded.
450.Pp
451.Bl -tag -compact -width "newbookmark (string)"
452.It Ar dataset Pq string
453Name of snapshot to create.
454.El
455.It Fn zfs.sync.rename_snapshot dataset oldsnapname newsnapname
456Rename a snapshot of a filesystem or a volume.
457Returns 0 if the snapshot was successfully renamed,
458and a nonzero error code otherwise.
459.Pp
460.Bl -tag -compact -width "newbookmark (string)"
461.It Ar dataset Pq string
462Name of the snapshot's parent dataset.
463.It Ar oldsnapname Pq string
464Original name of the snapshot.
465.It Ar newsnapname Pq string
466New name of the snapshot.
467.El
468.It Fn zfs.sync.bookmark source newbookmark
469Create a bookmark of an existing source snapshot or bookmark.
470Returns 0 if the new bookmark was successfully created,
471and a nonzero error code otherwise.
472.Pp
473Note: Bookmarking requires the corresponding pool feature to be enabled.
474.Pp
475.Bl -tag -compact -width "newbookmark (string)"
476.It Ar source Pq string
477Full name of the existing snapshot or bookmark.
478.It Ar newbookmark Pq string
479Full name of the new bookmark.
480.El
481.El
482.It Sy zfs.check submodule
483For each function in the
484.Sy zfs.sync
485submodule, there is a corresponding
486.Sy zfs.check
487function which performs a "dry run" of the same operation.
488Each takes the same arguments as its
489.Sy zfs.sync
490counterpart and returns 0 if the operation would succeed,
491or a non-zero error code if it would fail, along with any other error details.
492That is, each has the same behavior as the corresponding sync function except
493for actually executing the requested change.
494For example,
495.Fn zfs.check.destroy \&"fs"
496returns 0 if
497.Fn zfs.sync.destroy \&"fs"
498would successfully destroy the dataset.
499.Pp
500The available
501.Sy zfs.check
502functions are:
503.Bl -tag -compact -width "xx"
504.It Fn zfs.check.clone snapshot newdataset
505.It Sy zfs.check.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false
506.It Fn zfs.check.promote dataset
507.It Fn zfs.check.rollback filesystem
508.It Fn zfs.check.set_property dataset property value
509.It Fn zfs.check.snapshot dataset
510.El
511.It Sy zfs.list submodule
512The zfs.list submodule provides functions for iterating over datasets and
513properties.
514Rather than returning tables, these functions act as Lua iterators, and are
515generally used as follows:
516.Bd -literal -compact -offset indent
517.No for child in Fn zfs.list.children \&"rpool" No do
518    ...
519end
520.Ed
521.Pp
522The available
523.Sy zfs.list
524functions are:
525.Bl -tag -width "xx"
526.It Fn zfs.list.clones snapshot
527Iterate through all clones of the given snapshot.
528.Pp
529.Bl -tag -compact -width "snapshot (string)"
530.It Ar snapshot Pq string
531Must be a valid snapshot path in the current pool.
532.El
533.It Fn zfs.list.snapshots dataset
534Iterate through all snapshots of the given dataset.
535Each snapshot is returned as a string containing the full dataset name,
536e.g. "pool/fs@snap".
537.Pp
538.Bl -tag -compact -width "snapshot (string)"
539.It Ar dataset Pq string
540Must be a valid filesystem or volume.
541.El
542.It Fn zfs.list.children dataset
543Iterate through all direct children of the given dataset.
544Each child is returned as a string containing the full dataset name,
545e.g. "pool/fs/child".
546.Pp
547.Bl -tag -compact -width "snapshot (string)"
548.It Ar dataset Pq string
549Must be a valid filesystem or volume.
550.El
551.It Fn zfs.list.bookmarks dataset
552Iterate through all bookmarks of the given dataset.
553Each bookmark is returned as a string containing the full dataset name,
554e.g. "pool/fs#bookmark".
555.Pp
556.Bl -tag -compact -width "snapshot (string)"
557.It Ar dataset Pq string
558Must be a valid filesystem or volume.
559.El
560.It Fn zfs.list.holds snapshot
561Iterate through all user holds on the given snapshot.
562Each hold is returned
563as a pair of the hold's tag and the timestamp (in seconds since the epoch) at
564which it was created.
565.Pp
566.Bl -tag -compact -width "snapshot (string)"
567.It Ar snapshot Pq string
568Must be a valid snapshot.
569.El
570.It Fn zfs.list.properties dataset
571An alias for zfs.list.user_properties (see relevant entry).
572.Pp
573.Bl -tag -compact -width "snapshot (string)"
574.It Ar dataset Pq string
575Must be a valid filesystem, snapshot, or volume.
576.El
577.It Fn zfs.list.user_properties dataset
578Iterate through all user properties for the given dataset.
579For each step of the iteration, output the property name, its value,
580and its source.
581Throws a Lua error if the dataset is invalid.
582.Pp
583.Bl -tag -compact -width "snapshot (string)"
584.It Ar dataset Pq string
585Must be a valid filesystem, snapshot, or volume.
586.El
587.It Fn zfs.list.system_properties dataset
588Returns an array of strings, the names of the valid system (non-user defined)
589properties for the given dataset.
590Throws a Lua error if the dataset is invalid.
591.Pp
592.Bl -tag -compact -width "snapshot (string)"
593.It Ar dataset Pq string
594Must be a valid filesystem, snapshot or volume.
595.El
596.El
597.El
598.
599.Sh EXAMPLES
600.
601.Ss Example 1
602The following channel program recursively destroys a filesystem and all its
603snapshots and children in a naive manner.
604Note that this does not involve any error handling or reporting.
605.Bd -literal -offset indent
606function destroy_recursive(root)
607    for child in zfs.list.children(root) do
608        destroy_recursive(child)
609    end
610    for snap in zfs.list.snapshots(root) do
611        zfs.sync.destroy(snap)
612    end
613    zfs.sync.destroy(root)
614end
615destroy_recursive("pool/somefs")
616.Ed
617.
618.Ss Example 2
619A more verbose and robust version of the same channel program, which
620properly detects and reports errors, and also takes the dataset to destroy
621as a command line argument, would be as follows:
622.Bd -literal -offset indent
623succeeded = {}
624failed = {}
625
626function destroy_recursive(root)
627    for child in zfs.list.children(root) do
628        destroy_recursive(child)
629    end
630    for snap in zfs.list.snapshots(root) do
631        err = zfs.sync.destroy(snap)
632        if (err ~= 0) then
633            failed[snap] = err
634        else
635            succeeded[snap] = err
636        end
637    end
638    err = zfs.sync.destroy(root)
639    if (err ~= 0) then
640        failed[root] = err
641    else
642        succeeded[root] = err
643    end
644end
645
646args = ...
647argv = args["argv"]
648
649destroy_recursive(argv[1])
650
651results = {}
652results["succeeded"] = succeeded
653results["failed"] = failed
654return results
655.Ed
656.
657.Ss Example 3
658The following function performs a forced promote operation by attempting to
659promote the given clone and destroying any conflicting snapshots.
660.Bd -literal -offset indent
661function force_promote(ds)
662   errno, details = zfs.check.promote(ds)
663   if (errno == EEXIST) then
664       assert(details ~= Nil)
665       for i, snap in ipairs(details) do
666           zfs.sync.destroy(ds .. "@" .. snap)
667       end
668   elseif (errno ~= 0) then
669       return errno
670   end
671   return zfs.sync.promote(ds)
672end
673.Ed
674