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 by Delphix. All rights reserved.
18 */
19
20 #include "lua.h"
21 #include "lauxlib.h"
22
23 #include <sys/dmu.h>
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>
30 #include <sys/zap.h>
31 #include <sys/dsl_dir.h>
32 #include <sys/zcp_prop.h>
33
34 #include <sys/zcp.h>
35
36 typedef int (zcp_list_func_t)(lua_State *);
37 typedef struct zcp_list_info {
38 const char *name;
39 zcp_list_func_t *func;
40 zcp_list_func_t *gc;
41 const zcp_arg_t pargs[4];
42 const zcp_arg_t kwargs[2];
43 } zcp_list_info_t;
44
45 static int
zcp_clones_iter(lua_State * state)46 zcp_clones_iter(lua_State *state)
47 {
48 int err;
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;
54 zap_attribute_t za;
55 zap_cursor_t zc;
56
57 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
58 if (err == ENOENT) {
59 return (0);
60 } else if (err != 0) {
61 return (luaL_error(state,
62 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
63 err));
64 }
65
66 if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
67 dsl_dataset_rele(ds, FTAG);
68 return (0);
69 }
70
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);
74
75 err = zap_cursor_retrieve(&zc, &za);
76 if (err != 0) {
77 zap_cursor_fini(&zc);
78 if (err != ENOENT) {
79 return (luaL_error(state,
80 "unexpected error %d from zap_cursor_retrieve()",
81 err));
82 }
83 return (0);
84 }
85 zap_cursor_advance(&zc);
86 cursor = zap_cursor_serialize(&zc);
87 zap_cursor_fini(&zc);
88
89 err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone);
90 if (err != 0) {
91 return (luaL_error(state,
92 "unexpected error %d from "
93 "dsl_dataset_hold_obj(za_first_integer)", err));
94 }
95
96 dsl_dir_name(clone->ds_dir, clonename);
97 dsl_dataset_rele(clone, FTAG);
98
99 lua_pushnumber(state, cursor);
100 lua_replace(state, lua_upvalueindex(2));
101
102 (void) lua_pushstring(state, clonename);
103 return (1);
104 }
105
106 static int zcp_clones_list(lua_State *);
107 static zcp_list_info_t zcp_clones_list_info = {
108 .name = "clones",
109 .func = zcp_clones_list,
110 .gc = NULL,
111 .pargs = {
112 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING},
113 {NULL, 0}
114 },
115 .kwargs = {
116 {NULL, 0}
117 }
118 };
119
120 static int
zcp_clones_list(lua_State * state)121 zcp_clones_list(lua_State *state)
122 {
123 const char *snapname = lua_tostring(state, 1);
124 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
125 boolean_t issnap;
126 uint64_t dsobj, cursor;
127
128 /*
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.
132 */
133 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
134 if (ds == NULL)
135 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
136 cursor = 0;
137 issnap = ds->ds_is_snapshot;
138 dsobj = ds->ds_object;
139 dsl_dataset_rele(ds, FTAG);
140
141 if (!issnap) {
142 return (zcp_argerror(state, 1, "%s is not a snapshot",
143 snapname));
144 }
145
146 lua_pushnumber(state, dsobj);
147 lua_pushnumber(state, cursor);
148 lua_pushcclosure(state, &zcp_clones_iter, 2);
149 return (1);
150 }
151
152 static int
zcp_snapshots_iter(lua_State * state)153 zcp_snapshots_iter(lua_State *state)
154 {
155 int err;
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;
160 dsl_dataset_t *ds;
161 objset_t *os;
162 char *p;
163
164 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
165 if (err != 0) {
166 return (luaL_error(state,
167 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
168 err));
169 }
170
171 dsl_dataset_name(ds, snapname);
172 VERIFY3U(sizeof (snapname), >,
173 strlcat(snapname, "@", sizeof (snapname)));
174
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);
180
181 if (err == ENOENT) {
182 return (0);
183 } else if (err != 0) {
184 return (luaL_error(state,
185 "unexpected error %d from dmu_snapshot_list_next()", err));
186 }
187
188 lua_pushnumber(state, cursor);
189 lua_replace(state, lua_upvalueindex(2));
190
191 (void) lua_pushstring(state, snapname);
192 return (1);
193 }
194
195 static int zcp_snapshots_list(lua_State *);
196 static zcp_list_info_t zcp_snapshots_list_info = {
197 .name = "snapshots",
198 .func = zcp_snapshots_list,
199 .gc = NULL,
200 .pargs = {
201 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING},
202 {NULL, 0}
203 },
204 .kwargs = {
205 {NULL, 0}
206 }
207 };
208
209 static int
zcp_snapshots_list(lua_State * state)210 zcp_snapshots_list(lua_State *state)
211 {
212 const char *fsname = lua_tostring(state, 1);
213 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
214 boolean_t issnap;
215 uint64_t dsobj;
216
217 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
218 if (ds == NULL)
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);
223
224 if (issnap) {
225 return (zcp_argerror(state, 1,
226 "argument %s cannot be a snapshot", fsname));
227 }
228
229 lua_pushnumber(state, dsobj);
230 lua_pushnumber(state, 0);
231 lua_pushcclosure(state, &zcp_snapshots_iter, 2);
232 return (1);
233 }
234
235 /*
236 * Note: channel programs only run in the global zone, so all datasets
237 * are visible to this zone.
238 */
239 static boolean_t
dataset_name_hidden(const char * name)240 dataset_name_hidden(const char *name)
241 {
242 if (strchr(name, '$') != NULL)
243 return (B_TRUE);
244 if (strchr(name, '%') != NULL)
245 return (B_TRUE);
246 return (B_FALSE);
247 }
248
249 static int
zcp_children_iter(lua_State * state)250 zcp_children_iter(lua_State *state)
251 {
252 int err;
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;
258 dsl_dataset_t *ds;
259 objset_t *os;
260 char *p;
261
262 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
263 if (err != 0) {
264 return (luaL_error(state,
265 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
266 err));
267 }
268
269 dsl_dataset_name(ds, childname);
270 VERIFY3U(sizeof (childname), >,
271 strlcat(childname, "/", sizeof (childname)));
272 p = strchr(childname, '\0');
273
274 VERIFY0(dmu_objset_from_ds(ds, &os));
275 do {
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);
280
281 if (err == ENOENT) {
282 return (0);
283 } else if (err != 0) {
284 return (luaL_error(state,
285 "unexpected error %d from dmu_dir_list_next()",
286 err));
287 }
288
289 lua_pushnumber(state, cursor);
290 lua_replace(state, lua_upvalueindex(2));
291
292 (void) lua_pushstring(state, childname);
293 return (1);
294 }
295
296 static int zcp_children_list(lua_State *);
297 static zcp_list_info_t zcp_children_list_info = {
298 .name = "children",
299 .func = zcp_children_list,
300 .gc = NULL,
301 .pargs = {
302 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING},
303 {NULL, 0}
304 },
305 .kwargs = {
306 {NULL, 0}
307 }
308 };
309
310 static int
zcp_children_list(lua_State * state)311 zcp_children_list(lua_State *state)
312 {
313 const char *fsname = lua_tostring(state, 1);
314 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
315 boolean_t issnap;
316 uint64_t dsobj;
317
318 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
319 if (ds == NULL)
320 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
321
322 issnap = ds->ds_is_snapshot;
323 dsobj = ds->ds_object;
324 dsl_dataset_rele(ds, FTAG);
325
326 if (issnap) {
327 return (zcp_argerror(state, 1,
328 "argument %s cannot be a snapshot", fsname));
329 }
330
331 lua_pushnumber(state, dsobj);
332 lua_pushnumber(state, 0);
333 lua_pushcclosure(state, &zcp_children_iter, 2);
334 return (1);
335 }
336
337 static int
zcp_props_list_gc(lua_State * state)338 zcp_props_list_gc(lua_State *state)
339 {
340 nvlist_t **props = lua_touserdata(state, 1);
341 if (*props != NULL)
342 fnvlist_free(*props);
343 return (0);
344 }
345
346 static int
zcp_props_iter(lua_State * state)347 zcp_props_iter(lua_State *state)
348 {
349 char *source, *val;
350 nvlist_t *nvprop;
351 nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1));
352 nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2));
353
354 do {
355 pair = nvlist_next_nvpair(*props, pair);
356 if (pair == NULL) {
357 fnvlist_free(*props);
358 *props = NULL;
359 return (0);
360 }
361 } while (!zfs_prop_user(nvpair_name(pair)));
362
363 lua_pushlightuserdata(state, pair);
364 lua_replace(state, lua_upvalueindex(2));
365
366 nvprop = fnvpair_value_nvlist(pair);
367 val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
368 source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
369
370 (void) lua_pushstring(state, nvpair_name(pair));
371 (void) lua_pushstring(state, val);
372 (void) lua_pushstring(state, source);
373 return (3);
374 }
375
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,
381 .pargs = {
382 { .za_name = "filesystem | snapshot | volume",
383 .za_lua_type = LUA_TSTRING},
384 {NULL, 0}
385 },
386 .kwargs = {
387 {NULL, 0}
388 }
389 };
390
391 static int
zcp_props_list(lua_State * state)392 zcp_props_list(lua_State *state)
393 {
394 const char *dsname = lua_tostring(state, 1);
395 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
396 objset_t *os;
397 nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *));
398
399 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
400 if (ds == NULL)
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);
405
406 /*
407 * Set the metatable for the properties list to free it on completion.
408 */
409 luaL_getmetatable(state, zcp_props_list_info.name);
410 (void) lua_setmetatable(state, -2);
411
412 lua_pushlightuserdata(state, NULL);
413 lua_pushcclosure(state, &zcp_props_iter, 2);
414 return (1);
415 }
416
417
418 /*
419 * Populate nv with all valid properties and their values for the given
420 * dataset.
421 */
422 static void
zcp_dataset_props(dsl_dataset_t * ds,nvlist_t * nv)423 zcp_dataset_props(dsl_dataset_t *ds, nvlist_t *nv)
424 {
425 for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) {
426 /* Do not display hidden props */
427 if (!zfs_prop_visible(prop))
428 continue;
429 /* Do not display props not valid for this dataset */
430 if (!prop_valid_for_ds(ds, prop))
431 continue;
432 fnvlist_add_boolean(nv, zfs_prop_to_name(prop));
433 }
434 }
435
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,
440 .pargs = {
441 { .za_name = "dataset", .za_lua_type = LUA_TSTRING},
442 {NULL, 0}
443 },
444 .kwargs = {
445 {NULL, 0}
446 }
447 };
448
449 /*
450 * Get a list of all visble properties and their values for a given dataset.
451 * Returned on the stack as a Lua table.
452 */
453 static int
zcp_system_props_list(lua_State * state)454 zcp_system_props_list(lua_State *state)
455 {
456 int error;
457 char errbuf[128];
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();
464
465 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
466 if (ds == NULL)
467 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
468
469 /* Get the names of all valid properties for this dataset */
470 zcp_dataset_props(ds, nv);
471 dsl_dataset_rele(ds, FTAG);
472
473 /* push list as lua table */
474 error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf));
475 nvlist_free(nv);
476 if (error != 0) {
477 return (luaL_error(state,
478 "Error returning nvlist: %s", errbuf));
479 }
480 return (1);
481 }
482
483 static int
zcp_list_func(lua_State * state)484 zcp_list_func(lua_State *state)
485 {
486 zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
487
488 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
489
490 return (info->func(state));
491 }
492
493 int
zcp_load_list_lib(lua_State * state)494 zcp_load_list_lib(lua_State *state)
495 {
496 int i;
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,
503 NULL
504 };
505
506 lua_newtable(state);
507
508 for (i = 0; zcp_list_funcs[i] != NULL; i++) {
509 zcp_list_info_t *info = zcp_list_funcs[i];
510
511 if (info->gc != NULL) {
512 /*
513 * If the function requires garbage collection, create
514 * a metatable with its name and register the __gc
515 * function.
516 */
517 (void) luaL_newmetatable(state, info->name);
518 (void) lua_pushstring(state, "__gc");
519 lua_pushcfunction(state, info->gc);
520 lua_settable(state, -3);
521 lua_pop(state, 1);
522 }
523
524 lua_pushlightuserdata(state, info);
525 lua_pushcclosure(state, &zcp_list_func, 1);
526 lua_setfield(state, -2, info->name);
527 info++;
528 }
529
530 return (1);
531 }
532