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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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