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 za
= zap_attribute_alloc();
79 err
= zap_cursor_retrieve(&zc
, za
);
82 zap_attribute_free(za
);
84 return (luaL_error(state
,
85 "unexpected error %d from zap_cursor_retrieve()",
90 zap_cursor_advance(&zc
);
91 cursor
= zap_cursor_serialize(&zc
);
94 err
= dsl_dataset_hold_obj(dp
, za
->za_first_integer
, FTAG
, &clone
);
95 zap_attribute_free(za
);
97 return (luaL_error(state
,
98 "unexpected error %d from "
99 "dsl_dataset_hold_obj(za_first_integer)", err
));
102 dsl_dir_name(clone
->ds_dir
, clonename
);
103 dsl_dataset_rele(clone
, FTAG
);
105 lua_pushnumber(state
, cursor
);
106 lua_replace(state
, lua_upvalueindex(2));
108 (void) lua_pushstring(state
, clonename
);
112 static int zcp_clones_list(lua_State
*);
113 static const zcp_list_info_t zcp_clones_list_info
= {
115 .func
= zcp_clones_list
,
118 { .za_name
= "snapshot", .za_lua_type
= LUA_TSTRING
},
127 zcp_clones_list(lua_State
*state
)
129 const char *snapname
= lua_tostring(state
, 1);
130 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
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.
137 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, snapname
, FTAG
);
139 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
140 boolean_t issnap
= ds
->ds_is_snapshot
;
142 uint64_t dsobj
= ds
->ds_object
;
143 dsl_dataset_rele(ds
, FTAG
);
146 return (zcp_argerror(state
, 1, "%s is not a snapshot",
150 lua_pushnumber(state
, dsobj
);
151 lua_pushnumber(state
, cursor
);
152 lua_pushcclosure(state
, &zcp_clones_iter
, 2);
157 zcp_snapshots_iter(lua_State
*state
)
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
;
168 err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
170 return (luaL_error(state
,
171 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
175 dsl_dataset_name(ds
, snapname
);
176 VERIFY3U(sizeof (snapname
), >,
177 strlcat(snapname
, "@", sizeof (snapname
)));
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
);
187 } else if (err
!= 0) {
188 return (luaL_error(state
,
189 "unexpected error %d from dmu_snapshot_list_next()", err
));
192 lua_pushnumber(state
, cursor
);
193 lua_replace(state
, lua_upvalueindex(2));
195 (void) lua_pushstring(state
, snapname
);
199 static int zcp_snapshots_list(lua_State
*);
200 static const zcp_list_info_t zcp_snapshots_list_info
= {
202 .func
= zcp_snapshots_list
,
205 { .za_name
= "filesystem | volume", .za_lua_type
= LUA_TSTRING
},
214 zcp_snapshots_list(lua_State
*state
)
216 const char *fsname
= lua_tostring(state
, 1);
217 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
221 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, fsname
, FTAG
);
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
);
229 return (zcp_argerror(state
, 1,
230 "argument %s cannot be a snapshot", fsname
));
233 lua_pushnumber(state
, dsobj
);
234 lua_pushnumber(state
, 0);
235 lua_pushcclosure(state
, &zcp_snapshots_iter
, 2);
240 zcp_children_iter(lua_State
*state
)
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
;
252 err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
254 return (luaL_error(state
,
255 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
259 dsl_dataset_name(ds
, childname
);
260 VERIFY3U(sizeof (childname
), >,
261 strlcat(childname
, "/", sizeof (childname
)));
262 p
= strchr(childname
, '\0');
264 VERIFY0(dmu_objset_from_ds(ds
, &os
));
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
);
273 } else if (err
!= 0) {
274 return (luaL_error(state
,
275 "unexpected error %d from dmu_dir_list_next()",
279 lua_pushnumber(state
, cursor
);
280 lua_replace(state
, lua_upvalueindex(2));
282 (void) lua_pushstring(state
, childname
);
286 static int zcp_children_list(lua_State
*);
287 static const zcp_list_info_t zcp_children_list_info
= {
289 .func
= zcp_children_list
,
292 { .za_name
= "filesystem | volume", .za_lua_type
= LUA_TSTRING
},
301 zcp_children_list(lua_State
*state
)
303 const char *fsname
= lua_tostring(state
, 1);
304 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
308 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, fsname
, FTAG
);
310 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
312 issnap
= ds
->ds_is_snapshot
;
313 dsobj
= ds
->ds_object
;
314 dsl_dataset_rele(ds
, FTAG
);
317 return (zcp_argerror(state
, 1,
318 "argument %s cannot be a snapshot", fsname
));
321 lua_pushnumber(state
, dsobj
);
322 lua_pushnumber(state
, 0);
323 lua_pushcclosure(state
, &zcp_children_iter
, 2);
328 zcp_user_props_list_gc(lua_State
*state
)
330 nvlist_t
**props
= lua_touserdata(state
, 1);
332 fnvlist_free(*props
);
337 zcp_user_props_iter(lua_State
*state
)
339 const char *source
, *val
;
341 nvlist_t
**props
= lua_touserdata(state
, lua_upvalueindex(1));
342 nvpair_t
*pair
= lua_touserdata(state
, lua_upvalueindex(2));
345 pair
= nvlist_next_nvpair(*props
, pair
);
347 fnvlist_free(*props
);
351 } while (!zfs_prop_user(nvpair_name(pair
)));
353 lua_pushlightuserdata(state
, pair
);
354 lua_replace(state
, lua_upvalueindex(2));
356 nvprop
= fnvpair_value_nvlist(pair
);
357 val
= fnvlist_lookup_string(nvprop
, ZPROP_VALUE
);
358 source
= fnvlist_lookup_string(nvprop
, ZPROP_SOURCE
);
360 (void) lua_pushstring(state
, nvpair_name(pair
));
361 (void) lua_pushstring(state
, val
);
362 (void) lua_pushstring(state
, source
);
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
,
372 { .za_name
= "filesystem | snapshot | volume",
373 .za_lua_type
= LUA_TSTRING
},
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
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
,
394 { .za_name
= "filesystem | snapshot | volume",
395 .za_lua_type
= LUA_TSTRING
},
404 zcp_user_props_list(lua_State
*state
)
406 const char *dsname
= lua_tostring(state
, 1);
407 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
409 nvlist_t
**props
= lua_newuserdata(state
, sizeof (nvlist_t
*));
411 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dsname
, FTAG
);
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
);
419 * Set the metatable for the properties list to free it on
422 luaL_getmetatable(state
, zcp_user_props_list_info
.name
);
423 (void) lua_setmetatable(state
, -2);
425 lua_pushlightuserdata(state
, NULL
);
426 lua_pushcclosure(state
, &zcp_user_props_iter
, 2);
432 * Populate nv with all valid system properties and their values for the given
436 zcp_dataset_system_props(dsl_dataset_t
*ds
, nvlist_t
*nv
)
438 for (int prop
= ZFS_PROP_TYPE
; prop
< ZFS_NUM_PROPS
; prop
++) {
439 /* Do not display hidden props */
440 if (!zfs_prop_visible(prop
))
442 /* Do not display props not valid for this dataset */
443 if (!prop_valid_for_ds(ds
, prop
))
445 fnvlist_add_boolean(nv
, zfs_prop_to_name(prop
));
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
,
454 { .za_name
= "dataset", .za_lua_type
= LUA_TSTRING
},
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.
467 zcp_system_props_list(lua_State
*state
)
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();
478 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
480 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
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
);
486 /* push list as lua table */
487 error
= zcp_nvlist_to_lua(state
, nv
, errbuf
, sizeof (errbuf
));
490 return (luaL_error(state
,
491 "Error returning nvlist: %s", errbuf
));
497 zcp_bookmarks_iter(lua_State
*state
)
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
;
508 int err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
511 } else if (err
!= 0) {
512 return (luaL_error(state
,
513 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
517 if (!dsl_dataset_is_zapified(ds
)) {
518 dsl_dataset_rele(ds
, FTAG
);
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
));
530 if (ds
->ds_bookmarks_obj
== 0) {
531 dsl_dataset_rele(ds
, FTAG
);
535 /* Store the dataset's name so we can append the bookmark's name */
536 dsl_dataset_name(ds
, ds_name
);
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
);
542 za
= zap_attribute_alloc();
543 err
= zap_cursor_retrieve(&zc
, za
);
545 zap_cursor_fini(&zc
);
546 zap_attribute_free(za
);
548 return (luaL_error(state
,
549 "unexpected error %d from zap_cursor_retrieve()",
554 zap_cursor_advance(&zc
);
555 cursor
= zap_cursor_serialize(&zc
);
556 zap_cursor_fini(&zc
);
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
));
567 lua_pushnumber(state
, cursor
);
568 lua_replace(state
, lua_upvalueindex(2));
570 (void) lua_pushstring(state
, bookmark_name
);
574 static int zcp_bookmarks_list(lua_State
*);
575 static const zcp_list_info_t zcp_bookmarks_list_info
= {
577 .func
= zcp_bookmarks_list
,
579 { .za_name
= "dataset", .za_lua_type
= LUA_TSTRING
},
588 zcp_bookmarks_list(lua_State
*state
)
590 const char *dsname
= lua_tostring(state
, 1);
591 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
593 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dsname
, FTAG
);
595 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
597 boolean_t issnap
= ds
->ds_is_snapshot
;
598 uint64_t dsobj
= ds
->ds_object
;
600 dsl_dataset_rele(ds
, FTAG
);
603 return (zcp_argerror(state
, 1, "%s is a snapshot", dsname
));
606 lua_pushnumber(state
, dsobj
);
607 lua_pushnumber(state
, cursor
);
608 lua_pushcclosure(state
, &zcp_bookmarks_iter
, 2);
613 zcp_holds_iter(lua_State
*state
)
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
;
622 int err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
625 } else if (err
!= 0) {
626 return (luaL_error(state
,
627 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
631 if (dsl_dataset_phys(ds
)->ds_userrefs_obj
== 0) {
632 dsl_dataset_rele(ds
, FTAG
);
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
);
640 za
= zap_attribute_alloc();
641 err
= zap_cursor_retrieve(&zc
, za
);
643 zap_cursor_fini(&zc
);
644 zap_attribute_free(za
);
646 return (luaL_error(state
,
647 "unexpected error %d from zap_cursor_retrieve()",
652 zap_cursor_advance(&zc
);
653 cursor
= zap_cursor_serialize(&zc
);
654 zap_cursor_fini(&zc
);
656 lua_pushnumber(state
, cursor
);
657 lua_replace(state
, lua_upvalueindex(2));
659 (void) lua_pushstring(state
, za
->za_name
);
660 (void) lua_pushnumber(state
, za
->za_first_integer
);
661 zap_attribute_free(za
);
665 static int zcp_holds_list(lua_State
*);
666 static const zcp_list_info_t zcp_holds_list_info
= {
668 .func
= zcp_holds_list
,
671 { .za_name
= "snapshot", .za_lua_type
= LUA_TSTRING
},
680 * Iterate over all the holds for a given dataset. Each iteration returns
681 * a hold's tag and its timestamp as an integer.
684 zcp_holds_list(lua_State
*state
)
686 const char *snapname
= lua_tostring(state
, 1);
687 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
689 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, snapname
, FTAG
);
691 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
693 boolean_t issnap
= ds
->ds_is_snapshot
;
694 uint64_t dsobj
= ds
->ds_object
;
696 dsl_dataset_rele(ds
, FTAG
);
699 return (zcp_argerror(state
, 1, "%s is not a snapshot",
703 lua_pushnumber(state
, dsobj
);
704 lua_pushnumber(state
, cursor
);
705 lua_pushcclosure(state
, &zcp_holds_iter
, 2);
710 zcp_list_func(lua_State
*state
)
712 zcp_list_info_t
*info
= lua_touserdata(state
, lua_upvalueindex(1));
714 zcp_parse_args(state
, info
->name
, info
->pargs
, info
->kwargs
);
716 return (info
->func(state
));
720 zcp_load_list_lib(lua_State
*state
)
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
,
736 for (int i
= 0; zcp_list_funcs
[i
] != NULL
; i
++) {
737 const zcp_list_info_t
*info
= zcp_list_funcs
[i
];
739 if (info
->gc
!= NULL
) {
741 * If the function requires garbage collection, create
742 * a metatable with its name and register the __gc
745 (void) luaL_newmetatable(state
, info
->name
);
746 (void) lua_pushstring(state
, "__gc");
747 lua_pushcfunction(state
, info
->gc
);
748 lua_settable(state
, -3);
752 lua_pushlightuserdata(state
, (void *)(uintptr_t)info
);
753 lua_pushcclosure(state
, &zcp_list_func
, 1);
754 lua_setfield(state
, -2, info
->name
);