]> git.proxmox.com Git - mirror_zfs.git/blame - module/zfs/zcp_iter.c
linux spl: fix typo in top comment of spl-condvar.c
[mirror_zfs.git] / module / zfs / zcp_iter.c
CommitLineData
d99a0153
CW
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/*
3b9edd7b 17 * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
d99a0153
CW
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>
3b9edd7b 26#include <sys/dsl_bookmark.h>
d99a0153
CW
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
2e5dc449
MA
37#include "zfs_comutil.h"
38
d99a0153
CW
39typedef int (zcp_list_func_t)(lua_State *);
40typedef 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
48static int
49zcp_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 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));
85 }
86 return (0);
87 }
88 zap_cursor_advance(&zc);
89 cursor = zap_cursor_serialize(&zc);
90 zap_cursor_fini(&zc);
91
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));
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
109static int zcp_clones_list(lua_State *);
18168da7 110static const zcp_list_info_t zcp_clones_list_info = {
d99a0153
CW
111 .name = "clones",
112 .func = zcp_clones_list,
113 .gc = NULL,
114 .pargs = {
18168da7 115 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
d99a0153
CW
116 {NULL, 0}
117 },
118 .kwargs = {
119 {NULL, 0}
120 }
121};
122
123static int
124zcp_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;
d99a0153
CW
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 */
3b9edd7b
SD
137 boolean_t issnap = ds->ds_is_snapshot;
138 uint64_t cursor = 0;
139 uint64_t dsobj = ds->ds_object;
d99a0153
CW
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
153static int
154zcp_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
196static int zcp_snapshots_list(lua_State *);
18168da7 197static const zcp_list_info_t zcp_snapshots_list_info = {
d99a0153
CW
198 .name = "snapshots",
199 .func = zcp_snapshots_list,
200 .gc = NULL,
201 .pargs = {
18168da7 202 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
d99a0153
CW
203 {NULL, 0}
204 },
205 .kwargs = {
206 {NULL, 0}
207 }
208};
209
210static int
211zcp_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
d99a0153
CW
236static int
237zcp_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);
2e5dc449 265 } while (err == 0 && zfs_dataset_name_hidden(childname));
d99a0153
CW
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
283static int zcp_children_list(lua_State *);
18168da7 284static const zcp_list_info_t zcp_children_list_info = {
d99a0153
CW
285 .name = "children",
286 .func = zcp_children_list,
287 .gc = NULL,
288 .pargs = {
18168da7 289 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
d99a0153
CW
290 {NULL, 0}
291 },
292 .kwargs = {
293 {NULL, 0}
294 }
295};
296
297static int
298zcp_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
324static int
3b9edd7b 325zcp_user_props_list_gc(lua_State *state)
d99a0153
CW
326{
327 nvlist_t **props = lua_touserdata(state, 1);
328 if (*props != NULL)
329 fnvlist_free(*props);
330 return (0);
331}
332
333static int
3b9edd7b 334zcp_user_props_iter(lua_State *state)
d99a0153 335{
d1807f16 336 const char *source, *val;
d99a0153
CW
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
3b9edd7b 363static int zcp_user_props_list(lua_State *);
18168da7 364static const zcp_list_info_t zcp_user_props_list_info = {
3b9edd7b
SD
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",
18168da7 370 .za_lua_type = LUA_TSTRING },
3b9edd7b
SD
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 */
18168da7 386static const zcp_list_info_t zcp_props_list_info = {
d99a0153 387 .name = "properties",
3b9edd7b
SD
388 .func = zcp_user_props_list,
389 .gc = zcp_user_props_list_gc,
d99a0153
CW
390 .pargs = {
391 { .za_name = "filesystem | snapshot | volume",
18168da7 392 .za_lua_type = LUA_TSTRING },
d99a0153
CW
393 {NULL, 0}
394 },
395 .kwargs = {
396 {NULL, 0}
397 }
398};
399
400static int
3b9edd7b 401zcp_user_props_list(lua_State *state)
d99a0153
CW
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 /*
3b9edd7b
SD
416 * Set the metatable for the properties list to free it on
417 * completion.
d99a0153 418 */
3b9edd7b 419 luaL_getmetatable(state, zcp_user_props_list_info.name);
d99a0153
CW
420 (void) lua_setmetatable(state, -2);
421
422 lua_pushlightuserdata(state, NULL);
3b9edd7b 423 lua_pushcclosure(state, &zcp_user_props_iter, 2);
d99a0153
CW
424 return (1);
425}
426
427
428/*
3b9edd7b 429 * Populate nv with all valid system properties and their values for the given
d99a0153
CW
430 * dataset.
431 */
432static void
3b9edd7b 433zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
d99a0153
CW
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
446static int zcp_system_props_list(lua_State *);
18168da7 447static const zcp_list_info_t zcp_system_props_list_info = {
d99a0153
CW
448 .name = "system_properties",
449 .func = zcp_system_props_list,
450 .pargs = {
18168da7 451 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
d99a0153
CW
452 {NULL, 0}
453 },
454 .kwargs = {
455 {NULL, 0}
456 }
457};
458
459/*
e1cfd73f 460 * Get a list of all visible system properties and their values for a given
3b9edd7b 461 * dataset. Returned on the stack as a Lua table.
d99a0153
CW
462 */
463static int
464zcp_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;
18168da7 470 const zcp_list_info_t *libinfo = &zcp_system_props_list_info;
d99a0153
CW
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
3b9edd7b
SD
479 /* Get the names of all valid system properties for this dataset */
480 zcp_dataset_system_props(ds, nv);
d99a0153
CW
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
3b9edd7b
SD
493static int
494zcp_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 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));
546 }
547 return (0);
548 }
549 zap_cursor_advance(&zc);
550 cursor = zap_cursor_serialize(&zc);
551 zap_cursor_fini(&zc);
552
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));
559 }
560
561 lua_pushnumber(state, cursor);
562 lua_replace(state, lua_upvalueindex(2));
563
564 (void) lua_pushstring(state, bookmark_name);
565 return (1);
566}
567
568static int zcp_bookmarks_list(lua_State *);
18168da7 569static const zcp_list_info_t zcp_bookmarks_list_info = {
3b9edd7b
SD
570 .name = "bookmarks",
571 .func = zcp_bookmarks_list,
572 .pargs = {
18168da7 573 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
3b9edd7b
SD
574 {NULL, 0}
575 },
576 .kwargs = {
577 {NULL, 0}
578 }
579};
580
581static int
582zcp_bookmarks_list(lua_State *state)
583{
584 const char *dsname = lua_tostring(state, 1);
585 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
586
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 */
590
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);
595
596 if (issnap) {
597 return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
598 }
599
600 lua_pushnumber(state, dsobj);
601 lua_pushnumber(state, cursor);
602 lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
603 return (1);
604}
605
606static int
607zcp_holds_iter(lua_State *state)
608{
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;
615
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));
623 }
624
625 if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
626 dsl_dataset_rele(ds, FTAG);
627 return (0);
628 }
629
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);
633
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));
641 }
642 return (0);
643 }
644 zap_cursor_advance(&zc);
645 cursor = zap_cursor_serialize(&zc);
646 zap_cursor_fini(&zc);
647
648 lua_pushnumber(state, cursor);
649 lua_replace(state, lua_upvalueindex(2));
650
651 (void) lua_pushstring(state, za.za_name);
652 (void) lua_pushnumber(state, za.za_first_integer);
653 return (2);
654}
655
656static int zcp_holds_list(lua_State *);
18168da7 657static const zcp_list_info_t zcp_holds_list_info = {
3b9edd7b
SD
658 .name = "holds",
659 .func = zcp_holds_list,
660 .gc = NULL,
661 .pargs = {
18168da7 662 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
3b9edd7b
SD
663 {NULL, 0}
664 },
665 .kwargs = {
666 {NULL, 0}
667 }
668};
669
670/*
671 * Iterate over all the holds for a given dataset. Each iteration returns
672 * a hold's tag and its timestamp as an integer.
673 */
674static int
675zcp_holds_list(lua_State *state)
676{
677 const char *snapname = lua_tostring(state, 1);
678 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
679
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 */
683
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);
688
689 if (!issnap) {
690 return (zcp_argerror(state, 1, "%s is not a snapshot",
691 snapname));
692 }
693
694 lua_pushnumber(state, dsobj);
695 lua_pushnumber(state, cursor);
696 lua_pushcclosure(state, &zcp_holds_iter, 2);
697 return (1);
698}
699
d99a0153
CW
700static int
701zcp_list_func(lua_State *state)
702{
703 zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
704
705 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
706
707 return (info->func(state));
708}
709
710int
711zcp_load_list_lib(lua_State *state)
712{
18168da7 713 const zcp_list_info_t *zcp_list_funcs[] = {
d99a0153
CW
714 &zcp_children_list_info,
715 &zcp_snapshots_list_info,
3b9edd7b 716 &zcp_user_props_list_info,
d99a0153
CW
717 &zcp_props_list_info,
718 &zcp_clones_list_info,
719 &zcp_system_props_list_info,
3b9edd7b
SD
720 &zcp_bookmarks_list_info,
721 &zcp_holds_list_info,
d99a0153
CW
722 NULL
723 };
724
725 lua_newtable(state);
726
18168da7
AZ
727 for (int i = 0; zcp_list_funcs[i] != NULL; i++) {
728 const zcp_list_info_t *info = zcp_list_funcs[i];
d99a0153
CW
729
730 if (info->gc != NULL) {
731 /*
732 * If the function requires garbage collection, create
733 * a metatable with its name and register the __gc
734 * function.
735 */
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);
741 }
742
18168da7 743 lua_pushlightuserdata(state, (void *)(uintptr_t)info);
d99a0153
CW
744 lua_pushcclosure(state, &zcp_list_func, 1);
745 lua_setfield(state, -2, info->name);
d99a0153
CW
746 }
747
748 return (1);
749}