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
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.
17 * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
20 #include <sys/lua/lua.h>
21 #include <sys/lua/lauxlib.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>
32 #include <sys/dsl_dir.h>
33 #include <sys/zcp_prop.h>
37 #include "zfs_comutil.h"
39 typedef int (zcp_list_func_t)(lua_State *);
40 typedef struct zcp_list_info {
42 zcp_list_func_t *func;
44 const zcp_arg_t pargs[4];
45 const zcp_arg_t kwargs[2];
49 zcp_clones_iter(lua_State *state)
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;
60 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
63 } else if (err != 0) {
64 return (luaL_error(state,
65 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
69 if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
70 dsl_dataset_rele(ds, FTAG);
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);
78 err = zap_cursor_retrieve(&zc, &za);
82 return (luaL_error(state,
83 "unexpected error %d from zap_cursor_retrieve()",
88 zap_cursor_advance(&zc);
89 cursor = zap_cursor_serialize(&zc);
92 err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone);
94 return (luaL_error(state,
95 "unexpected error %d from "
96 "dsl_dataset_hold_obj(za_first_integer)", err));
99 dsl_dir_name(clone->ds_dir, clonename);
100 dsl_dataset_rele(clone, FTAG);
102 lua_pushnumber(state, cursor);
103 lua_replace(state, lua_upvalueindex(2));
105 (void) lua_pushstring(state, clonename);
109 static int zcp_clones_list(lua_State *);
110 static zcp_list_info_t zcp_clones_list_info = {
112 .func = zcp_clones_list,
115 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING},
124 zcp_clones_list(lua_State *state)
126 const char *snapname = lua_tostring(state, 1);
127 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
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.
134 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
136 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
137 boolean_t issnap = ds->ds_is_snapshot;
139 uint64_t dsobj = ds->ds_object;
140 dsl_dataset_rele(ds, FTAG);
143 return (zcp_argerror(state, 1, "%s is not a snapshot",
147 lua_pushnumber(state, dsobj);
148 lua_pushnumber(state, cursor);
149 lua_pushcclosure(state, &zcp_clones_iter, 2);
154 zcp_snapshots_iter(lua_State *state)
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;
165 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
167 return (luaL_error(state,
168 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
172 dsl_dataset_name(ds, snapname);
173 VERIFY3U(sizeof (snapname), >,
174 strlcat(snapname, "@", sizeof (snapname)));
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);
184 } else if (err != 0) {
185 return (luaL_error(state,
186 "unexpected error %d from dmu_snapshot_list_next()", err));
189 lua_pushnumber(state, cursor);
190 lua_replace(state, lua_upvalueindex(2));
192 (void) lua_pushstring(state, snapname);
196 static int zcp_snapshots_list(lua_State *);
197 static zcp_list_info_t zcp_snapshots_list_info = {
199 .func = zcp_snapshots_list,
202 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING},
211 zcp_snapshots_list(lua_State *state)
213 const char *fsname = lua_tostring(state, 1);
214 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
218 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
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);
226 return (zcp_argerror(state, 1,
227 "argument %s cannot be a snapshot", fsname));
230 lua_pushnumber(state, dsobj);
231 lua_pushnumber(state, 0);
232 lua_pushcclosure(state, &zcp_snapshots_iter, 2);
237 zcp_children_iter(lua_State *state)
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;
249 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
251 return (luaL_error(state,
252 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
256 dsl_dataset_name(ds, childname);
257 VERIFY3U(sizeof (childname), >,
258 strlcat(childname, "/", sizeof (childname)));
259 p = strchr(childname, '\0');
261 VERIFY0(dmu_objset_from_ds(ds, &os));
263 err = dmu_dir_list_next(os,
264 sizeof (childname) - (p - childname), p, NULL, &cursor);
265 } while (err == 0 && zfs_dataset_name_hidden(childname));
266 dsl_dataset_rele(ds, FTAG);
270 } else if (err != 0) {
271 return (luaL_error(state,
272 "unexpected error %d from dmu_dir_list_next()",
276 lua_pushnumber(state, cursor);
277 lua_replace(state, lua_upvalueindex(2));
279 (void) lua_pushstring(state, childname);
283 static int zcp_children_list(lua_State *);
284 static zcp_list_info_t zcp_children_list_info = {
286 .func = zcp_children_list,
289 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING},
298 zcp_children_list(lua_State *state)
300 const char *fsname = lua_tostring(state, 1);
301 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
305 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
307 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
309 issnap = ds->ds_is_snapshot;
310 dsobj = ds->ds_object;
311 dsl_dataset_rele(ds, FTAG);
314 return (zcp_argerror(state, 1,
315 "argument %s cannot be a snapshot", fsname));
318 lua_pushnumber(state, dsobj);
319 lua_pushnumber(state, 0);
320 lua_pushcclosure(state, &zcp_children_iter, 2);
325 zcp_user_props_list_gc(lua_State *state)
327 nvlist_t **props = lua_touserdata(state, 1);
329 fnvlist_free(*props);
334 zcp_user_props_iter(lua_State *state)
338 nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1));
339 nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2));
342 pair = nvlist_next_nvpair(*props, pair);
344 fnvlist_free(*props);
348 } while (!zfs_prop_user(nvpair_name(pair)));
350 lua_pushlightuserdata(state, pair);
351 lua_replace(state, lua_upvalueindex(2));
353 nvprop = fnvpair_value_nvlist(pair);
354 val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
355 source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
357 (void) lua_pushstring(state, nvpair_name(pair));
358 (void) lua_pushstring(state, val);
359 (void) lua_pushstring(state, source);
363 static int zcp_user_props_list(lua_State *);
364 static zcp_list_info_t zcp_user_props_list_info = {
365 .name = "user_properties",
366 .func = zcp_user_props_list,
367 .gc = zcp_user_props_list_gc,
369 { .za_name = "filesystem | snapshot | volume",
370 .za_lua_type = LUA_TSTRING},
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
386 static zcp_list_info_t zcp_props_list_info = {
387 .name = "properties",
388 .func = zcp_user_props_list,
389 .gc = zcp_user_props_list_gc,
391 { .za_name = "filesystem | snapshot | volume",
392 .za_lua_type = LUA_TSTRING},
401 zcp_user_props_list(lua_State *state)
403 const char *dsname = lua_tostring(state, 1);
404 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
406 nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *));
408 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
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);
416 * Set the metatable for the properties list to free it on
419 luaL_getmetatable(state, zcp_user_props_list_info.name);
420 (void) lua_setmetatable(state, -2);
422 lua_pushlightuserdata(state, NULL);
423 lua_pushcclosure(state, &zcp_user_props_iter, 2);
429 * Populate nv with all valid system properties and their values for the given
433 zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
435 for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) {
436 /* Do not display hidden props */
437 if (!zfs_prop_visible(prop))
439 /* Do not display props not valid for this dataset */
440 if (!prop_valid_for_ds(ds, prop))
442 fnvlist_add_boolean(nv, zfs_prop_to_name(prop));
446 static int zcp_system_props_list(lua_State *);
447 static zcp_list_info_t zcp_system_props_list_info = {
448 .name = "system_properties",
449 .func = zcp_system_props_list,
451 { .za_name = "dataset", .za_lua_type = LUA_TSTRING},
460 * Get a list of all visible system properties and their values for a given
461 * dataset. Returned on the stack as a Lua table.
464 zcp_system_props_list(lua_State *state)
468 const char *dataset_name;
469 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
470 zcp_list_info_t *libinfo = &zcp_system_props_list_info;
471 zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs);
472 dataset_name = lua_tostring(state, 1);
473 nvlist_t *nv = fnvlist_alloc();
475 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
477 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
479 /* Get the names of all valid system properties for this dataset */
480 zcp_dataset_system_props(ds, nv);
481 dsl_dataset_rele(ds, FTAG);
483 /* push list as lua table */
484 error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf));
487 return (luaL_error(state,
488 "Error returning nvlist: %s", errbuf));
494 zcp_bookmarks_iter(lua_State *state)
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;
505 int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
508 } else if (err != 0) {
509 return (luaL_error(state,
510 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
514 if (!dsl_dataset_is_zapified(ds)) {
515 dsl_dataset_rele(ds, FTAG);
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));
527 if (ds->ds_bookmarks_obj == 0) {
528 dsl_dataset_rele(ds, FTAG);
532 /* Store the dataset's name so we can append the bookmark's name */
533 dsl_dataset_name(ds, ds_name);
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);
539 err = zap_cursor_retrieve(&zc, &za);
541 zap_cursor_fini(&zc);
543 return (luaL_error(state,
544 "unexpected error %d from zap_cursor_retrieve()",
549 zap_cursor_advance(&zc);
550 cursor = zap_cursor_serialize(&zc);
551 zap_cursor_fini(&zc);
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));
561 lua_pushnumber(state, cursor);
562 lua_replace(state, lua_upvalueindex(2));
564 (void) lua_pushstring(state, bookmark_name);
568 static int zcp_bookmarks_list(lua_State *);
569 static zcp_list_info_t zcp_bookmarks_list_info = {
571 .func = zcp_bookmarks_list,
573 { .za_name = "dataset", .za_lua_type = LUA_TSTRING},
582 zcp_bookmarks_list(lua_State *state)
584 const char *dsname = lua_tostring(state, 1);
585 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
587 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
589 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
591 boolean_t issnap = ds->ds_is_snapshot;
592 uint64_t dsobj = ds->ds_object;
594 dsl_dataset_rele(ds, FTAG);
597 return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
600 lua_pushnumber(state, dsobj);
601 lua_pushnumber(state, cursor);
602 lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
607 zcp_holds_iter(lua_State *state)
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;
616 int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
619 } else if (err != 0) {
620 return (luaL_error(state,
621 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
625 if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
626 dsl_dataset_rele(ds, FTAG);
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);
634 err = zap_cursor_retrieve(&zc, &za);
636 zap_cursor_fini(&zc);
638 return (luaL_error(state,
639 "unexpected error %d from zap_cursor_retrieve()",
644 zap_cursor_advance(&zc);
645 cursor = zap_cursor_serialize(&zc);
646 zap_cursor_fini(&zc);
648 lua_pushnumber(state, cursor);
649 lua_replace(state, lua_upvalueindex(2));
651 (void) lua_pushstring(state, za.za_name);
652 (void) lua_pushnumber(state, za.za_first_integer);
656 static int zcp_holds_list(lua_State *);
657 static zcp_list_info_t zcp_holds_list_info = {
659 .func = zcp_holds_list,
662 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING},
671 * Iterate over all the holds for a given dataset. Each iteration returns
672 * a hold's tag and its timestamp as an integer.
675 zcp_holds_list(lua_State *state)
677 const char *snapname = lua_tostring(state, 1);
678 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
680 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
682 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
684 boolean_t issnap = ds->ds_is_snapshot;
685 uint64_t dsobj = ds->ds_object;
687 dsl_dataset_rele(ds, FTAG);
690 return (zcp_argerror(state, 1, "%s is not a snapshot",
694 lua_pushnumber(state, dsobj);
695 lua_pushnumber(state, cursor);
696 lua_pushcclosure(state, &zcp_holds_iter, 2);
701 zcp_list_func(lua_State *state)
703 zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
705 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
707 return (info->func(state));
711 zcp_load_list_lib(lua_State *state)
714 zcp_list_info_t *zcp_list_funcs[] = {
715 &zcp_children_list_info,
716 &zcp_snapshots_list_info,
717 &zcp_user_props_list_info,
718 &zcp_props_list_info,
719 &zcp_clones_list_info,
720 &zcp_system_props_list_info,
721 &zcp_bookmarks_list_info,
722 &zcp_holds_list_info,
728 for (i = 0; zcp_list_funcs[i] != NULL; i++) {
729 zcp_list_info_t *info = zcp_list_funcs[i];
731 if (info->gc != NULL) {
733 * If the function requires garbage collection, create
734 * a metatable with its name and register the __gc
737 (void) luaL_newmetatable(state, info->name);
738 (void) lua_pushstring(state, "__gc");
739 lua_pushcfunction(state, info->gc);
740 lua_settable(state, -3);
744 lua_pushlightuserdata(state, info);
745 lua_pushcclosure(state, &zcp_list_func, 1);
746 lua_setfield(state, -2, info->name);