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 * https://opensource.org/license/CDDL-1.0.
11 */
12
13 /*
14 * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
15 */
16
17 #include <sys/lua/lua.h>
18 #include <sys/lua/lauxlib.h>
19
20 #include <sys/dmu.h>
21 #include <sys/dsl_prop.h>
22 #include <sys/dsl_synctask.h>
23 #include <sys/dsl_bookmark.h>
24 #include <sys/dsl_dataset.h>
25 #include <sys/dsl_pool.h>
26 #include <sys/dmu_tx.h>
27 #include <sys/dmu_objset.h>
28 #include <sys/zap.h>
29 #include <sys/dsl_dir.h>
30 #include <sys/zcp_prop.h>
31
32 #include <sys/zcp.h>
33
34 #include "zfs_comutil.h"
35
36 typedef int (zcp_list_func_t)(lua_State *);
37 typedef struct zcp_list_info {
38 const char *name;
39 zcp_list_func_t *func;
40 zcp_list_func_t *gc;
41 const zcp_arg_t pargs[4];
42 const zcp_arg_t kwargs[2];
43 } zcp_list_info_t;
44
45 static int
zcp_clones_iter(lua_State * state)46 zcp_clones_iter(lua_State *state)
47 {
48 int err;
49 char clonename[ZFS_MAX_DATASET_NAME_LEN];
50 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
51 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
52 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
53 dsl_dataset_t *ds, *clone;
54 zap_attribute_t *za;
55 zap_cursor_t zc;
56
57 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
58 if (err == ENOENT) {
59 return (0);
60 } else if (err != 0) {
61 return (luaL_error(state,
62 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
63 err));
64 }
65
66 if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
67 dsl_dataset_rele(ds, FTAG);
68 return (0);
69 }
70
71 zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
72 dsl_dataset_phys(ds)->ds_next_clones_obj, cursor);
73 dsl_dataset_rele(ds, FTAG);
74
75 za = zap_attribute_alloc();
76 err = zap_cursor_retrieve(&zc, za);
77 if (err != 0) {
78 zap_cursor_fini(&zc);
79 zap_attribute_free(za);
80 if (err != ENOENT) {
81 return (luaL_error(state,
82 "unexpected error %d from zap_cursor_retrieve()",
83 err));
84 }
85 return (0);
86 }
87 zap_cursor_advance(&zc);
88 cursor = zap_cursor_serialize(&zc);
89 zap_cursor_fini(&zc);
90
91 err = dsl_dataset_hold_obj(dp, za->za_first_integer, FTAG, &clone);
92 zap_attribute_free(za);
93 if (err != 0) {
94 return (luaL_error(state,
95 "unexpected error %d from "
96 "dsl_dataset_hold_obj(za_first_integer)", err));
97 }
98
99 dsl_dir_name(clone->ds_dir, clonename);
100 dsl_dataset_rele(clone, FTAG);
101
102 lua_pushnumber(state, cursor);
103 lua_replace(state, lua_upvalueindex(2));
104
105 (void) lua_pushstring(state, clonename);
106 return (1);
107 }
108
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}
117 },
118 .kwargs = {
119 {NULL, 0}
120 }
121 };
122
123 static int
zcp_clones_list(lua_State * state)124 zcp_clones_list(lua_State *state)
125 {
126 const char *snapname = lua_tostring(state, 1);
127 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
128
129 /*
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.
133 */
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);
141
142 if (!issnap) {
143 return (zcp_argerror(state, 1, "%s is not a snapshot",
144 snapname));
145 }
146
147 lua_pushnumber(state, dsobj);
148 lua_pushnumber(state, cursor);
149 lua_pushcclosure(state, &zcp_clones_iter, 2);
150 return (1);
151 }
152
153 static int
zcp_snapshots_iter(lua_State * state)154 zcp_snapshots_iter(lua_State *state)
155 {
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;
164
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));
170 }
171
172 dsl_dataset_name(ds, snapname);
173 VERIFY3U(sizeof (snapname), >,
174 strlcat(snapname, "@", sizeof (snapname)));
175
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);
181
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));
187 }
188
189 lua_pushnumber(state, cursor);
190 lua_replace(state, lua_upvalueindex(2));
191
192 (void) lua_pushstring(state, snapname);
193 return (1);
194 }
195
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}
204 },
205 .kwargs = {
206 {NULL, 0}
207 }
208 };
209
210 static int
zcp_snapshots_list(lua_State * state)211 zcp_snapshots_list(lua_State *state)
212 {
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;
217
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);
224
225 if (issnap) {
226 return (zcp_argerror(state, 1,
227 "argument %s cannot be a snapshot", fsname));
228 }
229
230 lua_pushnumber(state, dsobj);
231 lua_pushnumber(state, 0);
232 lua_pushcclosure(state, &zcp_snapshots_iter, 2);
233 return (1);
234 }
235
236 static int
zcp_children_iter(lua_State * state)237 zcp_children_iter(lua_State *state)
238 {
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;
248
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));
254 }
255
256 dsl_dataset_name(ds, childname);
257 VERIFY3U(sizeof (childname), >,
258 strlcat(childname, "/", sizeof (childname)));
259 p = strchr(childname, '\0');
260
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);
267
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));
274 }
275
276 lua_pushnumber(state, cursor);
277 lua_replace(state, lua_upvalueindex(2));
278
279 (void) lua_pushstring(state, childname);
280 return (1);
281 }
282
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}
291 },
292 .kwargs = {
293 {NULL, 0}
294 }
295 };
296
297 static int
zcp_children_list(lua_State * state)298 zcp_children_list(lua_State *state)
299 {
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;
304
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 */
308
309 issnap = ds->ds_is_snapshot;
310 dsobj = ds->ds_object;
311 dsl_dataset_rele(ds, FTAG);
312
313 if (issnap) {
314 return (zcp_argerror(state, 1,
315 "argument %s cannot be a snapshot", fsname));
316 }
317
318 lua_pushnumber(state, dsobj);
319 lua_pushnumber(state, 0);
320 lua_pushcclosure(state, &zcp_children_iter, 2);
321 return (1);
322 }
323
324 static int
zcp_user_props_list_gc(lua_State * state)325 zcp_user_props_list_gc(lua_State *state)
326 {
327 nvlist_t **props = lua_touserdata(state, 1);
328 if (*props != NULL)
329 fnvlist_free(*props);
330 return (0);
331 }
332
333 static int
zcp_user_props_iter(lua_State * state)334 zcp_user_props_iter(lua_State *state)
335 {
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));
340
341 do {
342 pair = nvlist_next_nvpair(*props, pair);
343 if (pair == NULL) {
344 fnvlist_free(*props);
345 *props = NULL;
346 return (0);
347 }
348 } while (!zfs_prop_user(nvpair_name(pair)));
349
350 lua_pushlightuserdata(state, pair);
351 lua_replace(state, lua_upvalueindex(2));
352
353 nvprop = fnvpair_value_nvlist(pair);
354 val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
355 source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
356
357 (void) lua_pushstring(state, nvpair_name(pair));
358 (void) lua_pushstring(state, val);
359 (void) lua_pushstring(state, source);
360 return (3);
361 }
362
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}
372 },
373 .kwargs = {
374 {NULL, 0}
375 }
376 };
377
378 /*
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'.
385 */
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}
394 },
395 .kwargs = {
396 {NULL, 0}
397 }
398 };
399
400 static int
zcp_user_props_list(lua_State * state)401 zcp_user_props_list(lua_State *state)
402 {
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 *));
407
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);
414
415 /*
416 * Set the metatable for the properties list to free it on
417 * completion.
418 */
419 luaL_getmetatable(state, zcp_user_props_list_info.name);
420 (void) lua_setmetatable(state, -2);
421
422 lua_pushlightuserdata(state, NULL);
423 lua_pushcclosure(state, &zcp_user_props_iter, 2);
424 return (1);
425 }
426
427
428 /*
429 * Populate nv with all valid system properties and their values for the given
430 * dataset.
431 */
432 static void
zcp_dataset_system_props(dsl_dataset_t * ds,nvlist_t * nv)433 zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
434 {
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));
443 }
444 }
445
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}
453 },
454 .kwargs = {
455 {NULL, 0}
456 }
457 };
458
459 /*
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.
462 */
463 static int
zcp_system_props_list(lua_State * state)464 zcp_system_props_list(lua_State *state)
465 {
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();
474
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 */
478
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);
482
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));
489 }
490 return (1);
491 }
492
493 static int
zcp_bookmarks_iter(lua_State * state)494 zcp_bookmarks_iter(lua_State *state)
495 {
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;
504
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));
512 }
513
514 if (!dsl_dataset_is_zapified(ds)) {
515 dsl_dataset_rele(ds, FTAG);
516 return (0);
517 }
518
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));
526 }
527 if (ds->ds_bookmarks_obj == 0) {
528 dsl_dataset_rele(ds, FTAG);
529 return (0);
530 }
531
532 /* Store the dataset's name so we can append the bookmark's name */
533 dsl_dataset_name(ds, ds_name);
534
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);
538
539 za = zap_attribute_alloc();
540 err = zap_cursor_retrieve(&zc, za);
541 if (err != 0) {
542 zap_cursor_fini(&zc);
543 zap_attribute_free(za);
544 if (err != ENOENT) {
545 return (luaL_error(state,
546 "unexpected error %d from zap_cursor_retrieve()",
547 err));
548 }
549 return (0);
550 }
551 zap_cursor_advance(&zc);
552 cursor = zap_cursor_serialize(&zc);
553 zap_cursor_fini(&zc);
554
555 /* Create the full "pool/fs#bookmark" string to return */
556 int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s",
557 ds_name, za->za_name);
558 zap_attribute_free(za);
559 if (n >= ZFS_MAX_DATASET_NAME_LEN) {
560 return (luaL_error(state,
561 "unexpected error %d from snprintf()", ENAMETOOLONG));
562 }
563
564 lua_pushnumber(state, cursor);
565 lua_replace(state, lua_upvalueindex(2));
566
567 (void) lua_pushstring(state, bookmark_name);
568 return (1);
569 }
570
571 static int zcp_bookmarks_list(lua_State *);
572 static const zcp_list_info_t zcp_bookmarks_list_info = {
573 .name = "bookmarks",
574 .func = zcp_bookmarks_list,
575 .pargs = {
576 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
577 {NULL, 0}
578 },
579 .kwargs = {
580 {NULL, 0}
581 }
582 };
583
584 static int
zcp_bookmarks_list(lua_State * state)585 zcp_bookmarks_list(lua_State *state)
586 {
587 const char *dsname = lua_tostring(state, 1);
588 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
589
590 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
591 if (ds == NULL)
592 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
593
594 boolean_t issnap = ds->ds_is_snapshot;
595 uint64_t dsobj = ds->ds_object;
596 uint64_t cursor = 0;
597 dsl_dataset_rele(ds, FTAG);
598
599 if (issnap) {
600 return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
601 }
602
603 lua_pushnumber(state, dsobj);
604 lua_pushnumber(state, cursor);
605 lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
606 return (1);
607 }
608
609 static int
zcp_holds_iter(lua_State * state)610 zcp_holds_iter(lua_State *state)
611 {
612 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
613 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
614 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
615 dsl_dataset_t *ds;
616 zap_attribute_t *za;
617 zap_cursor_t zc;
618
619 int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
620 if (err == ENOENT) {
621 return (0);
622 } else if (err != 0) {
623 return (luaL_error(state,
624 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
625 err));
626 }
627
628 if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
629 dsl_dataset_rele(ds, FTAG);
630 return (0);
631 }
632
633 zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
634 dsl_dataset_phys(ds)->ds_userrefs_obj, cursor);
635 dsl_dataset_rele(ds, FTAG);
636
637 za = zap_attribute_alloc();
638 err = zap_cursor_retrieve(&zc, za);
639 if (err != 0) {
640 zap_cursor_fini(&zc);
641 zap_attribute_free(za);
642 if (err != ENOENT) {
643 return (luaL_error(state,
644 "unexpected error %d from zap_cursor_retrieve()",
645 err));
646 }
647 return (0);
648 }
649 zap_cursor_advance(&zc);
650 cursor = zap_cursor_serialize(&zc);
651 zap_cursor_fini(&zc);
652
653 lua_pushnumber(state, cursor);
654 lua_replace(state, lua_upvalueindex(2));
655
656 (void) lua_pushstring(state, za->za_name);
657 (void) lua_pushnumber(state, za->za_first_integer);
658 zap_attribute_free(za);
659 return (2);
660 }
661
662 static int zcp_holds_list(lua_State *);
663 static const zcp_list_info_t zcp_holds_list_info = {
664 .name = "holds",
665 .func = zcp_holds_list,
666 .gc = NULL,
667 .pargs = {
668 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
669 {NULL, 0}
670 },
671 .kwargs = {
672 {NULL, 0}
673 }
674 };
675
676 /*
677 * Iterate over all the holds for a given dataset. Each iteration returns
678 * a hold's tag and its timestamp as an integer.
679 */
680 static int
zcp_holds_list(lua_State * state)681 zcp_holds_list(lua_State *state)
682 {
683 const char *snapname = lua_tostring(state, 1);
684 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
685
686 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
687 if (ds == NULL)
688 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
689
690 boolean_t issnap = ds->ds_is_snapshot;
691 uint64_t dsobj = ds->ds_object;
692 uint64_t cursor = 0;
693 dsl_dataset_rele(ds, FTAG);
694
695 if (!issnap) {
696 return (zcp_argerror(state, 1, "%s is not a snapshot",
697 snapname));
698 }
699
700 lua_pushnumber(state, dsobj);
701 lua_pushnumber(state, cursor);
702 lua_pushcclosure(state, &zcp_holds_iter, 2);
703 return (1);
704 }
705
706 static int
zcp_list_func(lua_State * state)707 zcp_list_func(lua_State *state)
708 {
709 zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
710
711 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
712
713 return (info->func(state));
714 }
715
716 int
zcp_load_list_lib(lua_State * state)717 zcp_load_list_lib(lua_State *state)
718 {
719 const zcp_list_info_t *zcp_list_funcs[] = {
720 &zcp_children_list_info,
721 &zcp_snapshots_list_info,
722 &zcp_user_props_list_info,
723 &zcp_props_list_info,
724 &zcp_clones_list_info,
725 &zcp_system_props_list_info,
726 &zcp_bookmarks_list_info,
727 &zcp_holds_list_info,
728 NULL
729 };
730
731 lua_newtable(state);
732
733 for (int i = 0; zcp_list_funcs[i] != NULL; i++) {
734 const zcp_list_info_t *info = zcp_list_funcs[i];
735
736 if (info->gc != NULL) {
737 /*
738 * If the function requires garbage collection, create
739 * a metatable with its name and register the __gc
740 * function.
741 */
742 (void) luaL_newmetatable(state, info->name);
743 (void) lua_pushstring(state, "__gc");
744 lua_pushcfunction(state, info->gc);
745 lua_settable(state, -3);
746 lua_pop(state, 1);
747 }
748
749 lua_pushlightuserdata(state, (void *)(uintptr_t)info);
750 lua_pushcclosure(state, &zcp_list_func, 1);
751 lua_setfield(state, -2, info->name);
752 }
753
754 return (1);
755 }
756