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