xref: /freebsd/sys/contrib/openzfs/module/zfs/zcp_iter.c (revision 2a58b312b62f908ec92311d1bd8536dbaeb8e55b)
1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * This file and its contents are supplied under the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License ("CDDL"), version 1.0.
6eda14cbcSMatt Macy  * You may only use this file in accordance with the terms of version
7eda14cbcSMatt Macy  * 1.0 of the CDDL.
8eda14cbcSMatt Macy  *
9eda14cbcSMatt Macy  * A full copy of the text of the CDDL should have accompanied this
10eda14cbcSMatt Macy  * source.  A copy of the CDDL is also available via the Internet at
11eda14cbcSMatt Macy  * http://www.illumos.org/license/CDDL.
12eda14cbcSMatt Macy  *
13eda14cbcSMatt Macy  * CDDL HEADER END
14eda14cbcSMatt Macy  */
15eda14cbcSMatt Macy 
16eda14cbcSMatt Macy /*
17eda14cbcSMatt Macy  * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
18eda14cbcSMatt Macy  */
19eda14cbcSMatt Macy 
20eda14cbcSMatt Macy #include <sys/lua/lua.h>
21eda14cbcSMatt Macy #include <sys/lua/lauxlib.h>
22eda14cbcSMatt Macy 
23eda14cbcSMatt Macy #include <sys/dmu.h>
24eda14cbcSMatt Macy #include <sys/dsl_prop.h>
25eda14cbcSMatt Macy #include <sys/dsl_synctask.h>
26eda14cbcSMatt Macy #include <sys/dsl_bookmark.h>
27eda14cbcSMatt Macy #include <sys/dsl_dataset.h>
28eda14cbcSMatt Macy #include <sys/dsl_pool.h>
29eda14cbcSMatt Macy #include <sys/dmu_tx.h>
30eda14cbcSMatt Macy #include <sys/dmu_objset.h>
31eda14cbcSMatt Macy #include <sys/zap.h>
32eda14cbcSMatt Macy #include <sys/dsl_dir.h>
33eda14cbcSMatt Macy #include <sys/zcp_prop.h>
34eda14cbcSMatt Macy 
35eda14cbcSMatt Macy #include <sys/zcp.h>
36eda14cbcSMatt Macy 
37eda14cbcSMatt Macy #include "zfs_comutil.h"
38eda14cbcSMatt Macy 
39eda14cbcSMatt Macy typedef int (zcp_list_func_t)(lua_State *);
40eda14cbcSMatt Macy typedef struct zcp_list_info {
41eda14cbcSMatt Macy 	const char *name;
42eda14cbcSMatt Macy 	zcp_list_func_t *func;
43eda14cbcSMatt Macy 	zcp_list_func_t *gc;
44eda14cbcSMatt Macy 	const zcp_arg_t pargs[4];
45eda14cbcSMatt Macy 	const zcp_arg_t kwargs[2];
46eda14cbcSMatt Macy } zcp_list_info_t;
47eda14cbcSMatt Macy 
48eda14cbcSMatt Macy static int
49eda14cbcSMatt Macy zcp_clones_iter(lua_State *state)
50eda14cbcSMatt Macy {
51eda14cbcSMatt Macy 	int err;
52eda14cbcSMatt Macy 	char clonename[ZFS_MAX_DATASET_NAME_LEN];
53eda14cbcSMatt Macy 	uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
54eda14cbcSMatt Macy 	uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
55eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
56eda14cbcSMatt Macy 	dsl_dataset_t *ds, *clone;
57eda14cbcSMatt Macy 	zap_attribute_t za;
58eda14cbcSMatt Macy 	zap_cursor_t zc;
59eda14cbcSMatt Macy 
60eda14cbcSMatt Macy 	err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
61eda14cbcSMatt Macy 	if (err == ENOENT) {
62eda14cbcSMatt Macy 		return (0);
63eda14cbcSMatt Macy 	} else if (err != 0) {
64eda14cbcSMatt Macy 		return (luaL_error(state,
65eda14cbcSMatt Macy 		    "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
66eda14cbcSMatt Macy 		    err));
67eda14cbcSMatt Macy 	}
68eda14cbcSMatt Macy 
69eda14cbcSMatt Macy 	if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
70eda14cbcSMatt Macy 		dsl_dataset_rele(ds, FTAG);
71eda14cbcSMatt Macy 		return (0);
72eda14cbcSMatt Macy 	}
73eda14cbcSMatt Macy 
74eda14cbcSMatt Macy 	zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
75eda14cbcSMatt Macy 	    dsl_dataset_phys(ds)->ds_next_clones_obj, cursor);
76eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
77eda14cbcSMatt Macy 
78eda14cbcSMatt Macy 	err = zap_cursor_retrieve(&zc, &za);
79eda14cbcSMatt Macy 	if (err != 0) {
80eda14cbcSMatt Macy 		zap_cursor_fini(&zc);
81eda14cbcSMatt Macy 		if (err != ENOENT) {
82eda14cbcSMatt Macy 			return (luaL_error(state,
83eda14cbcSMatt Macy 			    "unexpected error %d from zap_cursor_retrieve()",
84eda14cbcSMatt Macy 			    err));
85eda14cbcSMatt Macy 		}
86eda14cbcSMatt Macy 		return (0);
87eda14cbcSMatt Macy 	}
88eda14cbcSMatt Macy 	zap_cursor_advance(&zc);
89eda14cbcSMatt Macy 	cursor = zap_cursor_serialize(&zc);
90eda14cbcSMatt Macy 	zap_cursor_fini(&zc);
91eda14cbcSMatt Macy 
92eda14cbcSMatt Macy 	err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone);
93eda14cbcSMatt Macy 	if (err != 0) {
94eda14cbcSMatt Macy 		return (luaL_error(state,
95eda14cbcSMatt Macy 		    "unexpected error %d from "
96eda14cbcSMatt Macy 		    "dsl_dataset_hold_obj(za_first_integer)", err));
97eda14cbcSMatt Macy 	}
98eda14cbcSMatt Macy 
99eda14cbcSMatt Macy 	dsl_dir_name(clone->ds_dir, clonename);
100eda14cbcSMatt Macy 	dsl_dataset_rele(clone, FTAG);
101eda14cbcSMatt Macy 
102eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
103eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
104eda14cbcSMatt Macy 
105eda14cbcSMatt Macy 	(void) lua_pushstring(state, clonename);
106eda14cbcSMatt Macy 	return (1);
107eda14cbcSMatt Macy }
108eda14cbcSMatt Macy 
109eda14cbcSMatt Macy static int zcp_clones_list(lua_State *);
110e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_clones_list_info = {
111eda14cbcSMatt Macy 	.name = "clones",
112eda14cbcSMatt Macy 	.func = zcp_clones_list,
113eda14cbcSMatt Macy 	.gc = NULL,
114eda14cbcSMatt Macy 	.pargs = {
115eda14cbcSMatt Macy 	    { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
116eda14cbcSMatt Macy 	    {NULL, 0}
117eda14cbcSMatt Macy 	},
118eda14cbcSMatt Macy 	.kwargs = {
119eda14cbcSMatt Macy 	    {NULL, 0}
120eda14cbcSMatt Macy 	}
121eda14cbcSMatt Macy };
122eda14cbcSMatt Macy 
123eda14cbcSMatt Macy static int
124eda14cbcSMatt Macy zcp_clones_list(lua_State *state)
125eda14cbcSMatt Macy {
126eda14cbcSMatt Macy 	const char *snapname = lua_tostring(state, 1);
127eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
128eda14cbcSMatt Macy 
129eda14cbcSMatt Macy 	/*
130eda14cbcSMatt Macy 	 * zcp_dataset_hold will either successfully return the requested
131eda14cbcSMatt Macy 	 * dataset or throw a lua error and longjmp out of the zfs.list.clones
132eda14cbcSMatt Macy 	 * call without returning.
133eda14cbcSMatt Macy 	 */
134eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
135eda14cbcSMatt Macy 	if (ds == NULL)
136eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
137eda14cbcSMatt Macy 	boolean_t issnap = ds->ds_is_snapshot;
138eda14cbcSMatt Macy 	uint64_t cursor = 0;
139eda14cbcSMatt Macy 	uint64_t dsobj = ds->ds_object;
140eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
141eda14cbcSMatt Macy 
142eda14cbcSMatt Macy 	if (!issnap) {
143eda14cbcSMatt Macy 		return (zcp_argerror(state, 1, "%s is not a snapshot",
144eda14cbcSMatt Macy 		    snapname));
145eda14cbcSMatt Macy 	}
146eda14cbcSMatt Macy 
147eda14cbcSMatt Macy 	lua_pushnumber(state, dsobj);
148eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
149eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_clones_iter, 2);
150eda14cbcSMatt Macy 	return (1);
151eda14cbcSMatt Macy }
152eda14cbcSMatt Macy 
153eda14cbcSMatt Macy static int
154eda14cbcSMatt Macy zcp_snapshots_iter(lua_State *state)
155eda14cbcSMatt Macy {
156eda14cbcSMatt Macy 	int err;
157eda14cbcSMatt Macy 	char snapname[ZFS_MAX_DATASET_NAME_LEN];
158eda14cbcSMatt Macy 	uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
159eda14cbcSMatt Macy 	uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
160eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
161eda14cbcSMatt Macy 	dsl_dataset_t *ds;
162eda14cbcSMatt Macy 	objset_t *os;
163eda14cbcSMatt Macy 	char *p;
164eda14cbcSMatt Macy 
165eda14cbcSMatt Macy 	err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
166eda14cbcSMatt Macy 	if (err != 0) {
167eda14cbcSMatt Macy 		return (luaL_error(state,
168eda14cbcSMatt Macy 		    "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
169eda14cbcSMatt Macy 		    err));
170eda14cbcSMatt Macy 	}
171eda14cbcSMatt Macy 
172eda14cbcSMatt Macy 	dsl_dataset_name(ds, snapname);
173eda14cbcSMatt Macy 	VERIFY3U(sizeof (snapname), >,
174eda14cbcSMatt Macy 	    strlcat(snapname, "@", sizeof (snapname)));
175eda14cbcSMatt Macy 
176eda14cbcSMatt Macy 	p = strchr(snapname, '\0');
177eda14cbcSMatt Macy 	VERIFY0(dmu_objset_from_ds(ds, &os));
178eda14cbcSMatt Macy 	err = dmu_snapshot_list_next(os,
179eda14cbcSMatt Macy 	    sizeof (snapname) - (p - snapname), p, NULL, &cursor, NULL);
180eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
181eda14cbcSMatt Macy 
182eda14cbcSMatt Macy 	if (err == ENOENT) {
183eda14cbcSMatt Macy 		return (0);
184eda14cbcSMatt Macy 	} else if (err != 0) {
185eda14cbcSMatt Macy 		return (luaL_error(state,
186eda14cbcSMatt Macy 		    "unexpected error %d from dmu_snapshot_list_next()", err));
187eda14cbcSMatt Macy 	}
188eda14cbcSMatt Macy 
189eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
190eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
191eda14cbcSMatt Macy 
192eda14cbcSMatt Macy 	(void) lua_pushstring(state, snapname);
193eda14cbcSMatt Macy 	return (1);
194eda14cbcSMatt Macy }
195eda14cbcSMatt Macy 
196eda14cbcSMatt Macy static int zcp_snapshots_list(lua_State *);
197e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_snapshots_list_info = {
198eda14cbcSMatt Macy 	.name = "snapshots",
199eda14cbcSMatt Macy 	.func = zcp_snapshots_list,
200eda14cbcSMatt Macy 	.gc = NULL,
201eda14cbcSMatt Macy 	.pargs = {
202eda14cbcSMatt Macy 	    { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
203eda14cbcSMatt Macy 	    {NULL, 0}
204eda14cbcSMatt Macy 	},
205eda14cbcSMatt Macy 	.kwargs = {
206eda14cbcSMatt Macy 	    {NULL, 0}
207eda14cbcSMatt Macy 	}
208eda14cbcSMatt Macy };
209eda14cbcSMatt Macy 
210eda14cbcSMatt Macy static int
211eda14cbcSMatt Macy zcp_snapshots_list(lua_State *state)
212eda14cbcSMatt Macy {
213eda14cbcSMatt Macy 	const char *fsname = lua_tostring(state, 1);
214eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
215eda14cbcSMatt Macy 	boolean_t issnap;
216eda14cbcSMatt Macy 	uint64_t dsobj;
217eda14cbcSMatt Macy 
218eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
219eda14cbcSMatt Macy 	if (ds == NULL)
220eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
221eda14cbcSMatt Macy 	issnap = ds->ds_is_snapshot;
222eda14cbcSMatt Macy 	dsobj = ds->ds_object;
223eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
224eda14cbcSMatt Macy 
225eda14cbcSMatt Macy 	if (issnap) {
226eda14cbcSMatt Macy 		return (zcp_argerror(state, 1,
227eda14cbcSMatt Macy 		    "argument %s cannot be a snapshot", fsname));
228eda14cbcSMatt Macy 	}
229eda14cbcSMatt Macy 
230eda14cbcSMatt Macy 	lua_pushnumber(state, dsobj);
231eda14cbcSMatt Macy 	lua_pushnumber(state, 0);
232eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_snapshots_iter, 2);
233eda14cbcSMatt Macy 	return (1);
234eda14cbcSMatt Macy }
235eda14cbcSMatt Macy 
236eda14cbcSMatt Macy static int
237eda14cbcSMatt Macy zcp_children_iter(lua_State *state)
238eda14cbcSMatt Macy {
239eda14cbcSMatt Macy 	int err;
240eda14cbcSMatt Macy 	char childname[ZFS_MAX_DATASET_NAME_LEN];
241eda14cbcSMatt Macy 	uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
242eda14cbcSMatt Macy 	uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
243eda14cbcSMatt Macy 	zcp_run_info_t *ri = zcp_run_info(state);
244eda14cbcSMatt Macy 	dsl_pool_t *dp = ri->zri_pool;
245eda14cbcSMatt Macy 	dsl_dataset_t *ds;
246eda14cbcSMatt Macy 	objset_t *os;
247eda14cbcSMatt Macy 	char *p;
248eda14cbcSMatt Macy 
249eda14cbcSMatt Macy 	err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
250eda14cbcSMatt Macy 	if (err != 0) {
251eda14cbcSMatt Macy 		return (luaL_error(state,
252eda14cbcSMatt Macy 		    "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
253eda14cbcSMatt Macy 		    err));
254eda14cbcSMatt Macy 	}
255eda14cbcSMatt Macy 
256eda14cbcSMatt Macy 	dsl_dataset_name(ds, childname);
257eda14cbcSMatt Macy 	VERIFY3U(sizeof (childname), >,
258eda14cbcSMatt Macy 	    strlcat(childname, "/", sizeof (childname)));
259eda14cbcSMatt Macy 	p = strchr(childname, '\0');
260eda14cbcSMatt Macy 
261eda14cbcSMatt Macy 	VERIFY0(dmu_objset_from_ds(ds, &os));
262eda14cbcSMatt Macy 	do {
263eda14cbcSMatt Macy 		err = dmu_dir_list_next(os,
264eda14cbcSMatt Macy 		    sizeof (childname) - (p - childname), p, NULL, &cursor);
265eda14cbcSMatt Macy 	} while (err == 0 && zfs_dataset_name_hidden(childname));
266eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
267eda14cbcSMatt Macy 
268eda14cbcSMatt Macy 	if (err == ENOENT) {
269eda14cbcSMatt Macy 		return (0);
270eda14cbcSMatt Macy 	} else if (err != 0) {
271eda14cbcSMatt Macy 		return (luaL_error(state,
272eda14cbcSMatt Macy 		    "unexpected error %d from dmu_dir_list_next()",
273eda14cbcSMatt Macy 		    err));
274eda14cbcSMatt Macy 	}
275eda14cbcSMatt Macy 
276eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
277eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
278eda14cbcSMatt Macy 
279eda14cbcSMatt Macy 	(void) lua_pushstring(state, childname);
280eda14cbcSMatt Macy 	return (1);
281eda14cbcSMatt Macy }
282eda14cbcSMatt Macy 
283eda14cbcSMatt Macy static int zcp_children_list(lua_State *);
284e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_children_list_info = {
285eda14cbcSMatt Macy 	.name = "children",
286eda14cbcSMatt Macy 	.func = zcp_children_list,
287eda14cbcSMatt Macy 	.gc = NULL,
288eda14cbcSMatt Macy 	.pargs = {
289eda14cbcSMatt Macy 	    { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
290eda14cbcSMatt Macy 	    {NULL, 0}
291eda14cbcSMatt Macy 	},
292eda14cbcSMatt Macy 	.kwargs = {
293eda14cbcSMatt Macy 	    {NULL, 0}
294eda14cbcSMatt Macy 	}
295eda14cbcSMatt Macy };
296eda14cbcSMatt Macy 
297eda14cbcSMatt Macy static int
298eda14cbcSMatt Macy zcp_children_list(lua_State *state)
299eda14cbcSMatt Macy {
300eda14cbcSMatt Macy 	const char *fsname = lua_tostring(state, 1);
301eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
302eda14cbcSMatt Macy 	boolean_t issnap;
303eda14cbcSMatt Macy 	uint64_t dsobj;
304eda14cbcSMatt Macy 
305eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
306eda14cbcSMatt Macy 	if (ds == NULL)
307eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
308eda14cbcSMatt Macy 
309eda14cbcSMatt Macy 	issnap = ds->ds_is_snapshot;
310eda14cbcSMatt Macy 	dsobj = ds->ds_object;
311eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
312eda14cbcSMatt Macy 
313eda14cbcSMatt Macy 	if (issnap) {
314eda14cbcSMatt Macy 		return (zcp_argerror(state, 1,
315eda14cbcSMatt Macy 		    "argument %s cannot be a snapshot", fsname));
316eda14cbcSMatt Macy 	}
317eda14cbcSMatt Macy 
318eda14cbcSMatt Macy 	lua_pushnumber(state, dsobj);
319eda14cbcSMatt Macy 	lua_pushnumber(state, 0);
320eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_children_iter, 2);
321eda14cbcSMatt Macy 	return (1);
322eda14cbcSMatt Macy }
323eda14cbcSMatt Macy 
324eda14cbcSMatt Macy static int
325eda14cbcSMatt Macy zcp_user_props_list_gc(lua_State *state)
326eda14cbcSMatt Macy {
327eda14cbcSMatt Macy 	nvlist_t **props = lua_touserdata(state, 1);
328eda14cbcSMatt Macy 	if (*props != NULL)
329eda14cbcSMatt Macy 		fnvlist_free(*props);
330eda14cbcSMatt Macy 	return (0);
331eda14cbcSMatt Macy }
332eda14cbcSMatt Macy 
333eda14cbcSMatt Macy static int
334eda14cbcSMatt Macy zcp_user_props_iter(lua_State *state)
335eda14cbcSMatt Macy {
336*2a58b312SMartin Matuska 	const char *source, *val;
337eda14cbcSMatt Macy 	nvlist_t *nvprop;
338eda14cbcSMatt Macy 	nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1));
339eda14cbcSMatt Macy 	nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2));
340eda14cbcSMatt Macy 
341eda14cbcSMatt Macy 	do {
342eda14cbcSMatt Macy 		pair = nvlist_next_nvpair(*props, pair);
343eda14cbcSMatt Macy 		if (pair == NULL) {
344eda14cbcSMatt Macy 			fnvlist_free(*props);
345eda14cbcSMatt Macy 			*props = NULL;
346eda14cbcSMatt Macy 			return (0);
347eda14cbcSMatt Macy 		}
348eda14cbcSMatt Macy 	} while (!zfs_prop_user(nvpair_name(pair)));
349eda14cbcSMatt Macy 
350eda14cbcSMatt Macy 	lua_pushlightuserdata(state, pair);
351eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
352eda14cbcSMatt Macy 
353eda14cbcSMatt Macy 	nvprop = fnvpair_value_nvlist(pair);
354eda14cbcSMatt Macy 	val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
355eda14cbcSMatt Macy 	source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
356eda14cbcSMatt Macy 
357eda14cbcSMatt Macy 	(void) lua_pushstring(state, nvpair_name(pair));
358eda14cbcSMatt Macy 	(void) lua_pushstring(state, val);
359eda14cbcSMatt Macy 	(void) lua_pushstring(state, source);
360eda14cbcSMatt Macy 	return (3);
361eda14cbcSMatt Macy }
362eda14cbcSMatt Macy 
363eda14cbcSMatt Macy static int zcp_user_props_list(lua_State *);
364e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_user_props_list_info = {
365eda14cbcSMatt Macy 	.name = "user_properties",
366eda14cbcSMatt Macy 	.func = zcp_user_props_list,
367eda14cbcSMatt Macy 	.gc = zcp_user_props_list_gc,
368eda14cbcSMatt Macy 	.pargs = {
369eda14cbcSMatt Macy 	    { .za_name = "filesystem | snapshot | volume",
370eda14cbcSMatt Macy 	    .za_lua_type = LUA_TSTRING },
371eda14cbcSMatt Macy 	    {NULL, 0}
372eda14cbcSMatt Macy 	},
373eda14cbcSMatt Macy 	.kwargs = {
374eda14cbcSMatt Macy 	    {NULL, 0}
375eda14cbcSMatt Macy 	}
376eda14cbcSMatt Macy };
377eda14cbcSMatt Macy 
378eda14cbcSMatt Macy /*
379eda14cbcSMatt Macy  * 'properties' was the initial name for 'user_properties' seen
380eda14cbcSMatt Macy  * above. 'user_properties' is a better name as it distinguishes
381eda14cbcSMatt Macy  * these properties from 'system_properties' which are different.
382eda14cbcSMatt Macy  * In order to avoid breaking compatibility between different
383eda14cbcSMatt Macy  * versions of ZFS, we declare 'properties' as an alias for
384eda14cbcSMatt Macy  * 'user_properties'.
385eda14cbcSMatt Macy  */
386e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_props_list_info = {
387eda14cbcSMatt Macy 	.name = "properties",
388eda14cbcSMatt Macy 	.func = zcp_user_props_list,
389eda14cbcSMatt Macy 	.gc = zcp_user_props_list_gc,
390eda14cbcSMatt Macy 	.pargs = {
391eda14cbcSMatt Macy 	    { .za_name = "filesystem | snapshot | volume",
392eda14cbcSMatt Macy 	    .za_lua_type = LUA_TSTRING },
393eda14cbcSMatt Macy 	    {NULL, 0}
394eda14cbcSMatt Macy 	},
395eda14cbcSMatt Macy 	.kwargs = {
396eda14cbcSMatt Macy 	    {NULL, 0}
397eda14cbcSMatt Macy 	}
398eda14cbcSMatt Macy };
399eda14cbcSMatt Macy 
400eda14cbcSMatt Macy static int
401eda14cbcSMatt Macy zcp_user_props_list(lua_State *state)
402eda14cbcSMatt Macy {
403eda14cbcSMatt Macy 	const char *dsname = lua_tostring(state, 1);
404eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
405eda14cbcSMatt Macy 	objset_t *os;
406eda14cbcSMatt Macy 	nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *));
407eda14cbcSMatt Macy 
408eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
409eda14cbcSMatt Macy 	if (ds == NULL)
410eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
411eda14cbcSMatt Macy 	VERIFY0(dmu_objset_from_ds(ds, &os));
412eda14cbcSMatt Macy 	VERIFY0(dsl_prop_get_all(os, props));
413eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
414eda14cbcSMatt Macy 
415eda14cbcSMatt Macy 	/*
416eda14cbcSMatt Macy 	 * Set the metatable for the properties list to free it on
417eda14cbcSMatt Macy 	 * completion.
418eda14cbcSMatt Macy 	 */
419eda14cbcSMatt Macy 	luaL_getmetatable(state, zcp_user_props_list_info.name);
420eda14cbcSMatt Macy 	(void) lua_setmetatable(state, -2);
421eda14cbcSMatt Macy 
422eda14cbcSMatt Macy 	lua_pushlightuserdata(state, NULL);
423eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_user_props_iter, 2);
424eda14cbcSMatt Macy 	return (1);
425eda14cbcSMatt Macy }
426eda14cbcSMatt Macy 
427eda14cbcSMatt Macy 
428eda14cbcSMatt Macy /*
429eda14cbcSMatt Macy  * Populate nv with all valid system properties and their values for the given
430eda14cbcSMatt Macy  * dataset.
431eda14cbcSMatt Macy  */
432eda14cbcSMatt Macy static void
433eda14cbcSMatt Macy zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
434eda14cbcSMatt Macy {
435eda14cbcSMatt Macy 	for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) {
436eda14cbcSMatt Macy 		/* Do not display hidden props */
437eda14cbcSMatt Macy 		if (!zfs_prop_visible(prop))
438eda14cbcSMatt Macy 			continue;
439eda14cbcSMatt Macy 		/* Do not display props not valid for this dataset */
440eda14cbcSMatt Macy 		if (!prop_valid_for_ds(ds, prop))
441eda14cbcSMatt Macy 			continue;
442eda14cbcSMatt Macy 		fnvlist_add_boolean(nv, zfs_prop_to_name(prop));
443eda14cbcSMatt Macy 	}
444eda14cbcSMatt Macy }
445eda14cbcSMatt Macy 
446eda14cbcSMatt Macy static int zcp_system_props_list(lua_State *);
447e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_system_props_list_info = {
448eda14cbcSMatt Macy 	.name = "system_properties",
449eda14cbcSMatt Macy 	.func = zcp_system_props_list,
450eda14cbcSMatt Macy 	.pargs = {
451eda14cbcSMatt Macy 	    { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
452eda14cbcSMatt Macy 	    {NULL, 0}
453eda14cbcSMatt Macy 	},
454eda14cbcSMatt Macy 	.kwargs = {
455eda14cbcSMatt Macy 	    {NULL, 0}
456eda14cbcSMatt Macy 	}
457eda14cbcSMatt Macy };
458eda14cbcSMatt Macy 
459eda14cbcSMatt Macy /*
460eda14cbcSMatt Macy  * Get a list of all visible system properties and their values for a given
461eda14cbcSMatt Macy  * dataset. Returned on the stack as a Lua table.
462eda14cbcSMatt Macy  */
463eda14cbcSMatt Macy static int
464eda14cbcSMatt Macy zcp_system_props_list(lua_State *state)
465eda14cbcSMatt Macy {
466eda14cbcSMatt Macy 	int error;
467eda14cbcSMatt Macy 	char errbuf[128];
468eda14cbcSMatt Macy 	const char *dataset_name;
469eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
470e92ffd9bSMartin Matuska 	const zcp_list_info_t *libinfo = &zcp_system_props_list_info;
471eda14cbcSMatt Macy 	zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs);
472eda14cbcSMatt Macy 	dataset_name = lua_tostring(state, 1);
473eda14cbcSMatt Macy 	nvlist_t *nv = fnvlist_alloc();
474eda14cbcSMatt Macy 
475eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
476eda14cbcSMatt Macy 	if (ds == NULL)
477eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
478eda14cbcSMatt Macy 
479eda14cbcSMatt Macy 	/* Get the names of all valid system properties for this dataset */
480eda14cbcSMatt Macy 	zcp_dataset_system_props(ds, nv);
481eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
482eda14cbcSMatt Macy 
483eda14cbcSMatt Macy 	/* push list as lua table */
484eda14cbcSMatt Macy 	error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf));
485eda14cbcSMatt Macy 	nvlist_free(nv);
486eda14cbcSMatt Macy 	if (error != 0) {
487eda14cbcSMatt Macy 		return (luaL_error(state,
488eda14cbcSMatt Macy 		    "Error returning nvlist: %s", errbuf));
489eda14cbcSMatt Macy 	}
490eda14cbcSMatt Macy 	return (1);
491eda14cbcSMatt Macy }
492eda14cbcSMatt Macy 
493eda14cbcSMatt Macy static int
494eda14cbcSMatt Macy zcp_bookmarks_iter(lua_State *state)
495eda14cbcSMatt Macy {
496eda14cbcSMatt Macy 	char ds_name[ZFS_MAX_DATASET_NAME_LEN];
497eda14cbcSMatt Macy 	char bookmark_name[ZFS_MAX_DATASET_NAME_LEN];
498eda14cbcSMatt Macy 	uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
499eda14cbcSMatt Macy 	uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
500eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
501eda14cbcSMatt Macy 	dsl_dataset_t *ds;
502eda14cbcSMatt Macy 	zap_attribute_t za;
503eda14cbcSMatt Macy 	zap_cursor_t zc;
504eda14cbcSMatt Macy 
505eda14cbcSMatt Macy 	int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
506eda14cbcSMatt Macy 	if (err == ENOENT) {
507eda14cbcSMatt Macy 		return (0);
508eda14cbcSMatt Macy 	} else if (err != 0) {
509eda14cbcSMatt Macy 		return (luaL_error(state,
510eda14cbcSMatt Macy 		    "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
511eda14cbcSMatt Macy 		    err));
512eda14cbcSMatt Macy 	}
513eda14cbcSMatt Macy 
514eda14cbcSMatt Macy 	if (!dsl_dataset_is_zapified(ds)) {
515eda14cbcSMatt Macy 		dsl_dataset_rele(ds, FTAG);
516eda14cbcSMatt Macy 		return (0);
517eda14cbcSMatt Macy 	}
518eda14cbcSMatt Macy 
519eda14cbcSMatt Macy 	err = zap_lookup(dp->dp_meta_objset, ds->ds_object,
520eda14cbcSMatt Macy 	    DS_FIELD_BOOKMARK_NAMES, sizeof (ds->ds_bookmarks_obj), 1,
521eda14cbcSMatt Macy 	    &ds->ds_bookmarks_obj);
522eda14cbcSMatt Macy 	if (err != 0 && err != ENOENT) {
523eda14cbcSMatt Macy 		dsl_dataset_rele(ds, FTAG);
524eda14cbcSMatt Macy 		return (luaL_error(state,
525eda14cbcSMatt Macy 		    "unexpected error %d from zap_lookup()", err));
526eda14cbcSMatt Macy 	}
527eda14cbcSMatt Macy 	if (ds->ds_bookmarks_obj == 0) {
528eda14cbcSMatt Macy 		dsl_dataset_rele(ds, FTAG);
529eda14cbcSMatt Macy 		return (0);
530eda14cbcSMatt Macy 	}
531eda14cbcSMatt Macy 
532eda14cbcSMatt Macy 	/* Store the dataset's name so we can append the bookmark's name */
533eda14cbcSMatt Macy 	dsl_dataset_name(ds, ds_name);
534eda14cbcSMatt Macy 
535eda14cbcSMatt Macy 	zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
536eda14cbcSMatt Macy 	    ds->ds_bookmarks_obj, cursor);
537eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
538eda14cbcSMatt Macy 
539eda14cbcSMatt Macy 	err = zap_cursor_retrieve(&zc, &za);
540eda14cbcSMatt Macy 	if (err != 0) {
541eda14cbcSMatt Macy 		zap_cursor_fini(&zc);
542eda14cbcSMatt Macy 		if (err != ENOENT) {
543eda14cbcSMatt Macy 			return (luaL_error(state,
544eda14cbcSMatt Macy 			    "unexpected error %d from zap_cursor_retrieve()",
545eda14cbcSMatt Macy 			    err));
546eda14cbcSMatt Macy 		}
547eda14cbcSMatt Macy 		return (0);
548eda14cbcSMatt Macy 	}
549eda14cbcSMatt Macy 	zap_cursor_advance(&zc);
550eda14cbcSMatt Macy 	cursor = zap_cursor_serialize(&zc);
551eda14cbcSMatt Macy 	zap_cursor_fini(&zc);
552eda14cbcSMatt Macy 
553eda14cbcSMatt Macy 	/* Create the full "pool/fs#bookmark" string to return */
554eda14cbcSMatt Macy 	int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s",
555eda14cbcSMatt Macy 	    ds_name, za.za_name);
556eda14cbcSMatt Macy 	if (n >= ZFS_MAX_DATASET_NAME_LEN) {
557eda14cbcSMatt Macy 		return (luaL_error(state,
558eda14cbcSMatt Macy 		    "unexpected error %d from snprintf()", ENAMETOOLONG));
559eda14cbcSMatt Macy 	}
560eda14cbcSMatt Macy 
561eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
562eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
563eda14cbcSMatt Macy 
564eda14cbcSMatt Macy 	(void) lua_pushstring(state, bookmark_name);
565eda14cbcSMatt Macy 	return (1);
566eda14cbcSMatt Macy }
567eda14cbcSMatt Macy 
568eda14cbcSMatt Macy static int zcp_bookmarks_list(lua_State *);
569e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_bookmarks_list_info = {
570eda14cbcSMatt Macy 	.name = "bookmarks",
571eda14cbcSMatt Macy 	.func = zcp_bookmarks_list,
572eda14cbcSMatt Macy 	.pargs = {
573eda14cbcSMatt Macy 	    { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
574eda14cbcSMatt Macy 	    {NULL, 0}
575eda14cbcSMatt Macy 	},
576eda14cbcSMatt Macy 	.kwargs = {
577eda14cbcSMatt Macy 	    {NULL, 0}
578eda14cbcSMatt Macy 	}
579eda14cbcSMatt Macy };
580eda14cbcSMatt Macy 
581eda14cbcSMatt Macy static int
582eda14cbcSMatt Macy zcp_bookmarks_list(lua_State *state)
583eda14cbcSMatt Macy {
584eda14cbcSMatt Macy 	const char *dsname = lua_tostring(state, 1);
585eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
586eda14cbcSMatt Macy 
587eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
588eda14cbcSMatt Macy 	if (ds == NULL)
589eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
590eda14cbcSMatt Macy 
591eda14cbcSMatt Macy 	boolean_t issnap = ds->ds_is_snapshot;
592eda14cbcSMatt Macy 	uint64_t dsobj = ds->ds_object;
593eda14cbcSMatt Macy 	uint64_t cursor = 0;
594eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
595eda14cbcSMatt Macy 
596eda14cbcSMatt Macy 	if (issnap) {
597eda14cbcSMatt Macy 		return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
598eda14cbcSMatt Macy 	}
599eda14cbcSMatt Macy 
600eda14cbcSMatt Macy 	lua_pushnumber(state, dsobj);
601eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
602eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
603eda14cbcSMatt Macy 	return (1);
604eda14cbcSMatt Macy }
605eda14cbcSMatt Macy 
606eda14cbcSMatt Macy static int
607eda14cbcSMatt Macy zcp_holds_iter(lua_State *state)
608eda14cbcSMatt Macy {
609eda14cbcSMatt Macy 	uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
610eda14cbcSMatt Macy 	uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
611eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
612eda14cbcSMatt Macy 	dsl_dataset_t *ds;
613eda14cbcSMatt Macy 	zap_attribute_t za;
614eda14cbcSMatt Macy 	zap_cursor_t zc;
615eda14cbcSMatt Macy 
616eda14cbcSMatt Macy 	int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
617eda14cbcSMatt Macy 	if (err == ENOENT) {
618eda14cbcSMatt Macy 		return (0);
619eda14cbcSMatt Macy 	} else if (err != 0) {
620eda14cbcSMatt Macy 		return (luaL_error(state,
621eda14cbcSMatt Macy 		    "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
622eda14cbcSMatt Macy 		    err));
623eda14cbcSMatt Macy 	}
624eda14cbcSMatt Macy 
625eda14cbcSMatt Macy 	if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
626eda14cbcSMatt Macy 		dsl_dataset_rele(ds, FTAG);
627eda14cbcSMatt Macy 		return (0);
628eda14cbcSMatt Macy 	}
629eda14cbcSMatt Macy 
630eda14cbcSMatt Macy 	zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
631eda14cbcSMatt Macy 	    dsl_dataset_phys(ds)->ds_userrefs_obj, cursor);
632eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
633eda14cbcSMatt Macy 
634eda14cbcSMatt Macy 	err = zap_cursor_retrieve(&zc, &za);
635eda14cbcSMatt Macy 	if (err != 0) {
636eda14cbcSMatt Macy 		zap_cursor_fini(&zc);
637eda14cbcSMatt Macy 		if (err != ENOENT) {
638eda14cbcSMatt Macy 			return (luaL_error(state,
639eda14cbcSMatt Macy 			    "unexpected error %d from zap_cursor_retrieve()",
640eda14cbcSMatt Macy 			    err));
641eda14cbcSMatt Macy 		}
642eda14cbcSMatt Macy 		return (0);
643eda14cbcSMatt Macy 	}
644eda14cbcSMatt Macy 	zap_cursor_advance(&zc);
645eda14cbcSMatt Macy 	cursor = zap_cursor_serialize(&zc);
646eda14cbcSMatt Macy 	zap_cursor_fini(&zc);
647eda14cbcSMatt Macy 
648eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
649eda14cbcSMatt Macy 	lua_replace(state, lua_upvalueindex(2));
650eda14cbcSMatt Macy 
651eda14cbcSMatt Macy 	(void) lua_pushstring(state, za.za_name);
652eda14cbcSMatt Macy 	(void) lua_pushnumber(state, za.za_first_integer);
653eda14cbcSMatt Macy 	return (2);
654eda14cbcSMatt Macy }
655eda14cbcSMatt Macy 
656eda14cbcSMatt Macy static int zcp_holds_list(lua_State *);
657e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_holds_list_info = {
658eda14cbcSMatt Macy 	.name = "holds",
659eda14cbcSMatt Macy 	.func = zcp_holds_list,
660eda14cbcSMatt Macy 	.gc = NULL,
661eda14cbcSMatt Macy 	.pargs = {
662eda14cbcSMatt Macy 	    { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
663eda14cbcSMatt Macy 	    {NULL, 0}
664eda14cbcSMatt Macy 	},
665eda14cbcSMatt Macy 	.kwargs = {
666eda14cbcSMatt Macy 	    {NULL, 0}
667eda14cbcSMatt Macy 	}
668eda14cbcSMatt Macy };
669eda14cbcSMatt Macy 
670eda14cbcSMatt Macy /*
671eda14cbcSMatt Macy  * Iterate over all the holds for a given dataset. Each iteration returns
672eda14cbcSMatt Macy  * a hold's tag and its timestamp as an integer.
673eda14cbcSMatt Macy  */
674eda14cbcSMatt Macy static int
675eda14cbcSMatt Macy zcp_holds_list(lua_State *state)
676eda14cbcSMatt Macy {
677eda14cbcSMatt Macy 	const char *snapname = lua_tostring(state, 1);
678eda14cbcSMatt Macy 	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
679eda14cbcSMatt Macy 
680eda14cbcSMatt Macy 	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
681eda14cbcSMatt Macy 	if (ds == NULL)
682eda14cbcSMatt Macy 		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
683eda14cbcSMatt Macy 
684eda14cbcSMatt Macy 	boolean_t issnap = ds->ds_is_snapshot;
685eda14cbcSMatt Macy 	uint64_t dsobj = ds->ds_object;
686eda14cbcSMatt Macy 	uint64_t cursor = 0;
687eda14cbcSMatt Macy 	dsl_dataset_rele(ds, FTAG);
688eda14cbcSMatt Macy 
689eda14cbcSMatt Macy 	if (!issnap) {
690eda14cbcSMatt Macy 		return (zcp_argerror(state, 1, "%s is not a snapshot",
691eda14cbcSMatt Macy 		    snapname));
692eda14cbcSMatt Macy 	}
693eda14cbcSMatt Macy 
694eda14cbcSMatt Macy 	lua_pushnumber(state, dsobj);
695eda14cbcSMatt Macy 	lua_pushnumber(state, cursor);
696eda14cbcSMatt Macy 	lua_pushcclosure(state, &zcp_holds_iter, 2);
697eda14cbcSMatt Macy 	return (1);
698eda14cbcSMatt Macy }
699eda14cbcSMatt Macy 
700eda14cbcSMatt Macy static int
701eda14cbcSMatt Macy zcp_list_func(lua_State *state)
702eda14cbcSMatt Macy {
703eda14cbcSMatt Macy 	zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
704eda14cbcSMatt Macy 
705eda14cbcSMatt Macy 	zcp_parse_args(state, info->name, info->pargs, info->kwargs);
706eda14cbcSMatt Macy 
707eda14cbcSMatt Macy 	return (info->func(state));
708eda14cbcSMatt Macy }
709eda14cbcSMatt Macy 
710eda14cbcSMatt Macy int
711eda14cbcSMatt Macy zcp_load_list_lib(lua_State *state)
712eda14cbcSMatt Macy {
713e92ffd9bSMartin Matuska 	const zcp_list_info_t *zcp_list_funcs[] = {
714eda14cbcSMatt Macy 		&zcp_children_list_info,
715eda14cbcSMatt Macy 		&zcp_snapshots_list_info,
716eda14cbcSMatt Macy 		&zcp_user_props_list_info,
717eda14cbcSMatt Macy 		&zcp_props_list_info,
718eda14cbcSMatt Macy 		&zcp_clones_list_info,
719eda14cbcSMatt Macy 		&zcp_system_props_list_info,
720eda14cbcSMatt Macy 		&zcp_bookmarks_list_info,
721eda14cbcSMatt Macy 		&zcp_holds_list_info,
722eda14cbcSMatt Macy 		NULL
723eda14cbcSMatt Macy 	};
724eda14cbcSMatt Macy 
725eda14cbcSMatt Macy 	lua_newtable(state);
726eda14cbcSMatt Macy 
727e92ffd9bSMartin Matuska 	for (int i = 0; zcp_list_funcs[i] != NULL; i++) {
728e92ffd9bSMartin Matuska 		const zcp_list_info_t *info = zcp_list_funcs[i];
729eda14cbcSMatt Macy 
730eda14cbcSMatt Macy 		if (info->gc != NULL) {
731eda14cbcSMatt Macy 			/*
732eda14cbcSMatt Macy 			 * If the function requires garbage collection, create
733eda14cbcSMatt Macy 			 * a metatable with its name and register the __gc
734eda14cbcSMatt Macy 			 * function.
735eda14cbcSMatt Macy 			 */
736eda14cbcSMatt Macy 			(void) luaL_newmetatable(state, info->name);
737eda14cbcSMatt Macy 			(void) lua_pushstring(state, "__gc");
738eda14cbcSMatt Macy 			lua_pushcfunction(state, info->gc);
739eda14cbcSMatt Macy 			lua_settable(state, -3);
740eda14cbcSMatt Macy 			lua_pop(state, 1);
741eda14cbcSMatt Macy 		}
742eda14cbcSMatt Macy 
743e92ffd9bSMartin Matuska 		lua_pushlightuserdata(state, (void *)(uintptr_t)info);
744eda14cbcSMatt Macy 		lua_pushcclosure(state, &zcp_list_func, 1);
745eda14cbcSMatt Macy 		lua_setfield(state, -2, info->name);
746eda14cbcSMatt Macy 	}
747eda14cbcSMatt Macy 
748eda14cbcSMatt Macy 	return (1);
749eda14cbcSMatt Macy }
750