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