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 const 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 const 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 const 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
)
336 const char *source
, *val
;
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 const 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 const 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 const 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 const 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 const 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 const 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
)
713 const zcp_list_info_t
*zcp_list_funcs
[] = {
714 &zcp_children_list_info
,
715 &zcp_snapshots_list_info
,
716 &zcp_user_props_list_info
,
717 &zcp_props_list_info
,
718 &zcp_clones_list_info
,
719 &zcp_system_props_list_info
,
720 &zcp_bookmarks_list_info
,
721 &zcp_holds_list_info
,
727 for (int i
= 0; zcp_list_funcs
[i
] != NULL
; i
++) {
728 const zcp_list_info_t
*info
= zcp_list_funcs
[i
];
730 if (info
->gc
!= NULL
) {
732 * If the function requires garbage collection, create
733 * a metatable with its name and register the __gc
736 (void) luaL_newmetatable(state
, info
->name
);
737 (void) lua_pushstring(state
, "__gc");
738 lua_pushcfunction(state
, info
->gc
);
739 lua_settable(state
, -3);
743 lua_pushlightuserdata(state
, (void *)(uintptr_t)info
);
744 lua_pushcclosure(state
, &zcp_list_func
, 1);
745 lua_setfield(state
, -2, info
->name
);