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