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 by Delphix. All rights reserved.
24 #include <sys/dsl_prop.h>
25 #include <sys/dsl_synctask.h>
26 #include <sys/dsl_dataset.h>
27 #include <sys/dsl_pool.h>
28 #include <sys/dmu_tx.h>
29 #include <sys/dmu_objset.h>
31 #include <sys/dsl_dir.h>
32 #include <sys/zcp_prop.h>
36 typedef int (zcp_list_func_t
)(lua_State
*);
37 typedef struct zcp_list_info
{
39 zcp_list_func_t
*func
;
41 const zcp_arg_t pargs
[4];
42 const zcp_arg_t kwargs
[2];
46 zcp_clones_iter(lua_State
*state
)
49 char clonename
[ZFS_MAX_DATASET_NAME_LEN
];
50 uint64_t dsobj
= lua_tonumber(state
, lua_upvalueindex(1));
51 uint64_t cursor
= lua_tonumber(state
, lua_upvalueindex(2));
52 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
53 dsl_dataset_t
*ds
, *clone
;
57 err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
60 } else if (err
!= 0) {
61 return (luaL_error(state
,
62 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
66 if (dsl_dataset_phys(ds
)->ds_next_clones_obj
== 0) {
67 dsl_dataset_rele(ds
, FTAG
);
71 zap_cursor_init_serialized(&zc
, dp
->dp_meta_objset
,
72 dsl_dataset_phys(ds
)->ds_next_clones_obj
, cursor
);
73 dsl_dataset_rele(ds
, FTAG
);
75 err
= zap_cursor_retrieve(&zc
, &za
);
79 return (luaL_error(state
,
80 "unexpected error %d from zap_cursor_retrieve()",
85 zap_cursor_advance(&zc
);
86 cursor
= zap_cursor_serialize(&zc
);
89 err
= dsl_dataset_hold_obj(dp
, za
.za_first_integer
, FTAG
, &clone
);
91 return (luaL_error(state
,
92 "unexpected error %d from "
93 "dsl_dataset_hold_obj(za_first_integer)", err
));
96 dsl_dir_name(clone
->ds_dir
, clonename
);
97 dsl_dataset_rele(clone
, FTAG
);
99 lua_pushnumber(state
, cursor
);
100 lua_replace(state
, lua_upvalueindex(2));
102 (void) lua_pushstring(state
, clonename
);
106 static int zcp_clones_list(lua_State
*);
107 static zcp_list_info_t zcp_clones_list_info
= {
109 .func
= zcp_clones_list
,
112 { .za_name
= "snapshot", .za_lua_type
= LUA_TSTRING
},
121 zcp_clones_list(lua_State
*state
)
123 const char *snapname
= lua_tostring(state
, 1);
124 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
126 uint64_t dsobj
, cursor
;
129 * zcp_dataset_hold will either successfully return the requested
130 * dataset or throw a lua error and longjmp out of the zfs.list.clones
131 * call without returning.
133 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, snapname
, FTAG
);
135 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
137 issnap
= ds
->ds_is_snapshot
;
138 dsobj
= ds
->ds_object
;
139 dsl_dataset_rele(ds
, FTAG
);
142 return (zcp_argerror(state
, 1, "%s is not a snapshot",
146 lua_pushnumber(state
, dsobj
);
147 lua_pushnumber(state
, cursor
);
148 lua_pushcclosure(state
, &zcp_clones_iter
, 2);
153 zcp_snapshots_iter(lua_State
*state
)
156 char snapname
[ZFS_MAX_DATASET_NAME_LEN
];
157 uint64_t dsobj
= lua_tonumber(state
, lua_upvalueindex(1));
158 uint64_t cursor
= lua_tonumber(state
, lua_upvalueindex(2));
159 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
164 err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
166 return (luaL_error(state
,
167 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
171 dsl_dataset_name(ds
, snapname
);
172 VERIFY3U(sizeof (snapname
), >,
173 strlcat(snapname
, "@", sizeof (snapname
)));
175 p
= strchr(snapname
, '\0');
176 VERIFY0(dmu_objset_from_ds(ds
, &os
));
177 err
= dmu_snapshot_list_next(os
,
178 sizeof (snapname
) - (p
- snapname
), p
, NULL
, &cursor
, NULL
);
179 dsl_dataset_rele(ds
, FTAG
);
183 } else if (err
!= 0) {
184 return (luaL_error(state
,
185 "unexpected error %d from dmu_snapshot_list_next()", err
));
188 lua_pushnumber(state
, cursor
);
189 lua_replace(state
, lua_upvalueindex(2));
191 (void) lua_pushstring(state
, snapname
);
195 static int zcp_snapshots_list(lua_State
*);
196 static zcp_list_info_t zcp_snapshots_list_info
= {
198 .func
= zcp_snapshots_list
,
201 { .za_name
= "filesystem | volume", .za_lua_type
= LUA_TSTRING
},
210 zcp_snapshots_list(lua_State
*state
)
212 const char *fsname
= lua_tostring(state
, 1);
213 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
217 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, fsname
, FTAG
);
219 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
220 issnap
= ds
->ds_is_snapshot
;
221 dsobj
= ds
->ds_object
;
222 dsl_dataset_rele(ds
, FTAG
);
225 return (zcp_argerror(state
, 1,
226 "argument %s cannot be a snapshot", fsname
));
229 lua_pushnumber(state
, dsobj
);
230 lua_pushnumber(state
, 0);
231 lua_pushcclosure(state
, &zcp_snapshots_iter
, 2);
236 * Note: channel programs only run in the global zone, so all datasets
237 * are visible to this zone.
240 dataset_name_hidden(const char *name
)
242 if (strchr(name
, '$') != NULL
)
244 if (strchr(name
, '%') != NULL
)
250 zcp_children_iter(lua_State
*state
)
253 char childname
[ZFS_MAX_DATASET_NAME_LEN
];
254 uint64_t dsobj
= lua_tonumber(state
, lua_upvalueindex(1));
255 uint64_t cursor
= lua_tonumber(state
, lua_upvalueindex(2));
256 zcp_run_info_t
*ri
= zcp_run_info(state
);
257 dsl_pool_t
*dp
= ri
->zri_pool
;
262 err
= dsl_dataset_hold_obj(dp
, dsobj
, FTAG
, &ds
);
264 return (luaL_error(state
,
265 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
269 dsl_dataset_name(ds
, childname
);
270 VERIFY3U(sizeof (childname
), >,
271 strlcat(childname
, "/", sizeof (childname
)));
272 p
= strchr(childname
, '\0');
274 VERIFY0(dmu_objset_from_ds(ds
, &os
));
276 err
= dmu_dir_list_next(os
,
277 sizeof (childname
) - (p
- childname
), p
, NULL
, &cursor
);
278 } while (err
== 0 && dataset_name_hidden(childname
));
279 dsl_dataset_rele(ds
, FTAG
);
283 } else if (err
!= 0) {
284 return (luaL_error(state
,
285 "unexpected error %d from dmu_dir_list_next()",
289 lua_pushnumber(state
, cursor
);
290 lua_replace(state
, lua_upvalueindex(2));
292 (void) lua_pushstring(state
, childname
);
296 static int zcp_children_list(lua_State
*);
297 static zcp_list_info_t zcp_children_list_info
= {
299 .func
= zcp_children_list
,
302 { .za_name
= "filesystem | volume", .za_lua_type
= LUA_TSTRING
},
311 zcp_children_list(lua_State
*state
)
313 const char *fsname
= lua_tostring(state
, 1);
314 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
318 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, fsname
, FTAG
);
320 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
322 issnap
= ds
->ds_is_snapshot
;
323 dsobj
= ds
->ds_object
;
324 dsl_dataset_rele(ds
, FTAG
);
327 return (zcp_argerror(state
, 1,
328 "argument %s cannot be a snapshot", fsname
));
331 lua_pushnumber(state
, dsobj
);
332 lua_pushnumber(state
, 0);
333 lua_pushcclosure(state
, &zcp_children_iter
, 2);
338 zcp_props_list_gc(lua_State
*state
)
340 nvlist_t
**props
= lua_touserdata(state
, 1);
342 fnvlist_free(*props
);
347 zcp_props_iter(lua_State
*state
)
351 nvlist_t
**props
= lua_touserdata(state
, lua_upvalueindex(1));
352 nvpair_t
*pair
= lua_touserdata(state
, lua_upvalueindex(2));
355 pair
= nvlist_next_nvpair(*props
, pair
);
357 fnvlist_free(*props
);
361 } while (!zfs_prop_user(nvpair_name(pair
)));
363 lua_pushlightuserdata(state
, pair
);
364 lua_replace(state
, lua_upvalueindex(2));
366 nvprop
= fnvpair_value_nvlist(pair
);
367 val
= fnvlist_lookup_string(nvprop
, ZPROP_VALUE
);
368 source
= fnvlist_lookup_string(nvprop
, ZPROP_SOURCE
);
370 (void) lua_pushstring(state
, nvpair_name(pair
));
371 (void) lua_pushstring(state
, val
);
372 (void) lua_pushstring(state
, source
);
376 static int zcp_props_list(lua_State
*);
377 static zcp_list_info_t zcp_props_list_info
= {
378 .name
= "properties",
379 .func
= zcp_props_list
,
380 .gc
= zcp_props_list_gc
,
382 { .za_name
= "filesystem | snapshot | volume",
383 .za_lua_type
= LUA_TSTRING
},
392 zcp_props_list(lua_State
*state
)
394 const char *dsname
= lua_tostring(state
, 1);
395 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
397 nvlist_t
**props
= lua_newuserdata(state
, sizeof (nvlist_t
*));
399 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dsname
, FTAG
);
401 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
402 VERIFY0(dmu_objset_from_ds(ds
, &os
));
403 VERIFY0(dsl_prop_get_all(os
, props
));
404 dsl_dataset_rele(ds
, FTAG
);
407 * Set the metatable for the properties list to free it on completion.
409 luaL_getmetatable(state
, zcp_props_list_info
.name
);
410 (void) lua_setmetatable(state
, -2);
412 lua_pushlightuserdata(state
, NULL
);
413 lua_pushcclosure(state
, &zcp_props_iter
, 2);
419 * Populate nv with all valid properties and their values for the given
423 zcp_dataset_props(dsl_dataset_t
*ds
, nvlist_t
*nv
)
425 for (int prop
= ZFS_PROP_TYPE
; prop
< ZFS_NUM_PROPS
; prop
++) {
426 /* Do not display hidden props */
427 if (!zfs_prop_visible(prop
))
429 /* Do not display props not valid for this dataset */
430 if (!prop_valid_for_ds(ds
, prop
))
432 fnvlist_add_boolean(nv
, zfs_prop_to_name(prop
));
436 static int zcp_system_props_list(lua_State
*);
437 static zcp_list_info_t zcp_system_props_list_info
= {
438 .name
= "system_properties",
439 .func
= zcp_system_props_list
,
441 { .za_name
= "dataset", .za_lua_type
= LUA_TSTRING
},
450 * Get a list of all visble properties and their values for a given dataset.
451 * Returned on the stack as a Lua table.
454 zcp_system_props_list(lua_State
*state
)
458 const char *dataset_name
;
459 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
460 zcp_list_info_t
*libinfo
= &zcp_system_props_list_info
;
461 zcp_parse_args(state
, libinfo
->name
, libinfo
->pargs
, libinfo
->kwargs
);
462 dataset_name
= lua_tostring(state
, 1);
463 nvlist_t
*nv
= fnvlist_alloc();
465 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
467 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
469 /* Get the names of all valid properties for this dataset */
470 zcp_dataset_props(ds
, nv
);
471 dsl_dataset_rele(ds
, FTAG
);
473 /* push list as lua table */
474 error
= zcp_nvlist_to_lua(state
, nv
, errbuf
, sizeof (errbuf
));
477 return (luaL_error(state
,
478 "Error returning nvlist: %s", errbuf
));
484 zcp_list_func(lua_State
*state
)
486 zcp_list_info_t
*info
= lua_touserdata(state
, lua_upvalueindex(1));
488 zcp_parse_args(state
, info
->name
, info
->pargs
, info
->kwargs
);
490 return (info
->func(state
));
494 zcp_load_list_lib(lua_State
*state
)
497 zcp_list_info_t
*zcp_list_funcs
[] = {
498 &zcp_children_list_info
,
499 &zcp_snapshots_list_info
,
500 &zcp_props_list_info
,
501 &zcp_clones_list_info
,
502 &zcp_system_props_list_info
,
508 for (i
= 0; zcp_list_funcs
[i
] != NULL
; i
++) {
509 zcp_list_info_t
*info
= zcp_list_funcs
[i
];
511 if (info
->gc
!= NULL
) {
513 * If the function requires garbage collection, create
514 * a metatable with its name and register the __gc
517 (void) luaL_newmetatable(state
, info
->name
);
518 (void) lua_pushstring(state
, "__gc");
519 lua_pushcfunction(state
, info
->gc
);
520 lua_settable(state
, -3);
524 lua_pushlightuserdata(state
, info
);
525 lua_pushcclosure(state
, &zcp_list_func
, 1);
526 lua_setfield(state
, -2, info
->name
);