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