]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_iter.c
Upgrade Unbound to 1.6.2. More to follow.
[FreeBSD/FreeBSD.git] / sys / cddl / contrib / opensolaris / uts / common / fs / 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 "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, 0}
114         },
115         .kwargs = {
116             {NULL, 0}
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, 0}
203         },
204         .kwargs = {
205             {NULL, 0}
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, 0}
304         },
305         .kwargs = {
306             {NULL, 0}
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, 0}
385         },
386         .kwargs = {
387             {NULL, 0}
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, 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
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 }