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