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