xref: /illumos-gate/usr/src/uts/common/fs/zfs/zcp_iter.c (revision 3299f39fdcbdab4be7a9c70daa3873f2b78a398d)
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
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, NULL}
114 	},
115 	.kwargs = {
116 	    {NULL, NULL}
117 	}
118 };
119 
120 static int
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
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, NULL}
203 	},
204 	.kwargs = {
205 	    {NULL, NULL}
206 	}
207 };
208 
209 static int
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
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
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, NULL}
304 	},
305 	.kwargs = {
306 	    {NULL, NULL}
307 	}
308 };
309 
310 static int
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
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
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, NULL}
385 	},
386 	.kwargs = {
387 	    {NULL, NULL}
388 	}
389 };
390 
391 static int
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
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, NULL}
443 	},
444 	.kwargs = {
445 	    {NULL, NULL}
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
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
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
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