]>
Commit | Line | Data |
---|---|---|
d99a0153 CW |
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 | /* | |
3b9edd7b | 17 | * Copyright (c) 2016, 2018 by Delphix. All rights reserved. |
d99a0153 CW |
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> | |
3b9edd7b | 26 | #include <sys/dsl_bookmark.h> |
d99a0153 CW |
27 | #include <sys/dsl_dataset.h> |
28 | #include <sys/dsl_pool.h> | |
29 | #include <sys/dmu_tx.h> | |
30 | #include <sys/dmu_objset.h> | |
31 | #include <sys/zap.h> | |
32 | #include <sys/dsl_dir.h> | |
33 | #include <sys/zcp_prop.h> | |
34 | ||
35 | #include <sys/zcp.h> | |
36 | ||
2e5dc449 MA |
37 | #include "zfs_comutil.h" |
38 | ||
d99a0153 CW |
39 | typedef int (zcp_list_func_t)(lua_State *); |
40 | typedef struct zcp_list_info { | |
41 | const char *name; | |
42 | zcp_list_func_t *func; | |
43 | zcp_list_func_t *gc; | |
44 | const zcp_arg_t pargs[4]; | |
45 | const zcp_arg_t kwargs[2]; | |
46 | } zcp_list_info_t; | |
47 | ||
48 | static int | |
49 | zcp_clones_iter(lua_State *state) | |
50 | { | |
51 | int err; | |
52 | char clonename[ZFS_MAX_DATASET_NAME_LEN]; | |
53 | uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); | |
54 | uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); | |
55 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
56 | dsl_dataset_t *ds, *clone; | |
57 | zap_attribute_t za; | |
58 | zap_cursor_t zc; | |
59 | ||
60 | err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); | |
61 | if (err == ENOENT) { | |
62 | return (0); | |
63 | } else if (err != 0) { | |
64 | return (luaL_error(state, | |
65 | "unexpected error %d from dsl_dataset_hold_obj(dsobj)", | |
66 | err)); | |
67 | } | |
68 | ||
69 | if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) { | |
70 | dsl_dataset_rele(ds, FTAG); | |
71 | return (0); | |
72 | } | |
73 | ||
74 | zap_cursor_init_serialized(&zc, dp->dp_meta_objset, | |
75 | dsl_dataset_phys(ds)->ds_next_clones_obj, cursor); | |
76 | dsl_dataset_rele(ds, FTAG); | |
77 | ||
78 | err = zap_cursor_retrieve(&zc, &za); | |
79 | if (err != 0) { | |
80 | zap_cursor_fini(&zc); | |
81 | if (err != ENOENT) { | |
82 | return (luaL_error(state, | |
83 | "unexpected error %d from zap_cursor_retrieve()", | |
84 | err)); | |
85 | } | |
86 | return (0); | |
87 | } | |
88 | zap_cursor_advance(&zc); | |
89 | cursor = zap_cursor_serialize(&zc); | |
90 | zap_cursor_fini(&zc); | |
91 | ||
92 | err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone); | |
93 | if (err != 0) { | |
94 | return (luaL_error(state, | |
95 | "unexpected error %d from " | |
96 | "dsl_dataset_hold_obj(za_first_integer)", err)); | |
97 | } | |
98 | ||
99 | dsl_dir_name(clone->ds_dir, clonename); | |
100 | dsl_dataset_rele(clone, FTAG); | |
101 | ||
102 | lua_pushnumber(state, cursor); | |
103 | lua_replace(state, lua_upvalueindex(2)); | |
104 | ||
105 | (void) lua_pushstring(state, clonename); | |
106 | return (1); | |
107 | } | |
108 | ||
109 | static int zcp_clones_list(lua_State *); | |
18168da7 | 110 | static const zcp_list_info_t zcp_clones_list_info = { |
d99a0153 CW |
111 | .name = "clones", |
112 | .func = zcp_clones_list, | |
113 | .gc = NULL, | |
114 | .pargs = { | |
18168da7 | 115 | { .za_name = "snapshot", .za_lua_type = LUA_TSTRING }, |
d99a0153 CW |
116 | {NULL, 0} |
117 | }, | |
118 | .kwargs = { | |
119 | {NULL, 0} | |
120 | } | |
121 | }; | |
122 | ||
123 | static int | |
124 | zcp_clones_list(lua_State *state) | |
125 | { | |
126 | const char *snapname = lua_tostring(state, 1); | |
127 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
d99a0153 CW |
128 | |
129 | /* | |
130 | * zcp_dataset_hold will either successfully return the requested | |
131 | * dataset or throw a lua error and longjmp out of the zfs.list.clones | |
132 | * call without returning. | |
133 | */ | |
134 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG); | |
135 | if (ds == NULL) | |
136 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
3b9edd7b SD |
137 | boolean_t issnap = ds->ds_is_snapshot; |
138 | uint64_t cursor = 0; | |
139 | uint64_t dsobj = ds->ds_object; | |
d99a0153 CW |
140 | dsl_dataset_rele(ds, FTAG); |
141 | ||
142 | if (!issnap) { | |
143 | return (zcp_argerror(state, 1, "%s is not a snapshot", | |
144 | snapname)); | |
145 | } | |
146 | ||
147 | lua_pushnumber(state, dsobj); | |
148 | lua_pushnumber(state, cursor); | |
149 | lua_pushcclosure(state, &zcp_clones_iter, 2); | |
150 | return (1); | |
151 | } | |
152 | ||
153 | static int | |
154 | zcp_snapshots_iter(lua_State *state) | |
155 | { | |
156 | int err; | |
157 | char snapname[ZFS_MAX_DATASET_NAME_LEN]; | |
158 | uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); | |
159 | uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); | |
160 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
161 | dsl_dataset_t *ds; | |
162 | objset_t *os; | |
163 | char *p; | |
164 | ||
165 | err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); | |
166 | if (err != 0) { | |
167 | return (luaL_error(state, | |
168 | "unexpected error %d from dsl_dataset_hold_obj(dsobj)", | |
169 | err)); | |
170 | } | |
171 | ||
172 | dsl_dataset_name(ds, snapname); | |
173 | VERIFY3U(sizeof (snapname), >, | |
174 | strlcat(snapname, "@", sizeof (snapname))); | |
175 | ||
176 | p = strchr(snapname, '\0'); | |
177 | VERIFY0(dmu_objset_from_ds(ds, &os)); | |
178 | err = dmu_snapshot_list_next(os, | |
179 | sizeof (snapname) - (p - snapname), p, NULL, &cursor, NULL); | |
180 | dsl_dataset_rele(ds, FTAG); | |
181 | ||
182 | if (err == ENOENT) { | |
183 | return (0); | |
184 | } else if (err != 0) { | |
185 | return (luaL_error(state, | |
186 | "unexpected error %d from dmu_snapshot_list_next()", err)); | |
187 | } | |
188 | ||
189 | lua_pushnumber(state, cursor); | |
190 | lua_replace(state, lua_upvalueindex(2)); | |
191 | ||
192 | (void) lua_pushstring(state, snapname); | |
193 | return (1); | |
194 | } | |
195 | ||
196 | static int zcp_snapshots_list(lua_State *); | |
18168da7 | 197 | static const zcp_list_info_t zcp_snapshots_list_info = { |
d99a0153 CW |
198 | .name = "snapshots", |
199 | .func = zcp_snapshots_list, | |
200 | .gc = NULL, | |
201 | .pargs = { | |
18168da7 | 202 | { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING }, |
d99a0153 CW |
203 | {NULL, 0} |
204 | }, | |
205 | .kwargs = { | |
206 | {NULL, 0} | |
207 | } | |
208 | }; | |
209 | ||
210 | static int | |
211 | zcp_snapshots_list(lua_State *state) | |
212 | { | |
213 | const char *fsname = lua_tostring(state, 1); | |
214 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
215 | boolean_t issnap; | |
216 | uint64_t dsobj; | |
217 | ||
218 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG); | |
219 | if (ds == NULL) | |
220 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
221 | issnap = ds->ds_is_snapshot; | |
222 | dsobj = ds->ds_object; | |
223 | dsl_dataset_rele(ds, FTAG); | |
224 | ||
225 | if (issnap) { | |
226 | return (zcp_argerror(state, 1, | |
227 | "argument %s cannot be a snapshot", fsname)); | |
228 | } | |
229 | ||
230 | lua_pushnumber(state, dsobj); | |
231 | lua_pushnumber(state, 0); | |
232 | lua_pushcclosure(state, &zcp_snapshots_iter, 2); | |
233 | return (1); | |
234 | } | |
235 | ||
d99a0153 CW |
236 | static int |
237 | zcp_children_iter(lua_State *state) | |
238 | { | |
239 | int err; | |
240 | char childname[ZFS_MAX_DATASET_NAME_LEN]; | |
241 | uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); | |
242 | uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); | |
243 | zcp_run_info_t *ri = zcp_run_info(state); | |
244 | dsl_pool_t *dp = ri->zri_pool; | |
245 | dsl_dataset_t *ds; | |
246 | objset_t *os; | |
247 | char *p; | |
248 | ||
249 | err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); | |
250 | if (err != 0) { | |
251 | return (luaL_error(state, | |
252 | "unexpected error %d from dsl_dataset_hold_obj(dsobj)", | |
253 | err)); | |
254 | } | |
255 | ||
256 | dsl_dataset_name(ds, childname); | |
257 | VERIFY3U(sizeof (childname), >, | |
258 | strlcat(childname, "/", sizeof (childname))); | |
259 | p = strchr(childname, '\0'); | |
260 | ||
261 | VERIFY0(dmu_objset_from_ds(ds, &os)); | |
262 | do { | |
263 | err = dmu_dir_list_next(os, | |
264 | sizeof (childname) - (p - childname), p, NULL, &cursor); | |
2e5dc449 | 265 | } while (err == 0 && zfs_dataset_name_hidden(childname)); |
d99a0153 CW |
266 | dsl_dataset_rele(ds, FTAG); |
267 | ||
268 | if (err == ENOENT) { | |
269 | return (0); | |
270 | } else if (err != 0) { | |
271 | return (luaL_error(state, | |
272 | "unexpected error %d from dmu_dir_list_next()", | |
273 | err)); | |
274 | } | |
275 | ||
276 | lua_pushnumber(state, cursor); | |
277 | lua_replace(state, lua_upvalueindex(2)); | |
278 | ||
279 | (void) lua_pushstring(state, childname); | |
280 | return (1); | |
281 | } | |
282 | ||
283 | static int zcp_children_list(lua_State *); | |
18168da7 | 284 | static const zcp_list_info_t zcp_children_list_info = { |
d99a0153 CW |
285 | .name = "children", |
286 | .func = zcp_children_list, | |
287 | .gc = NULL, | |
288 | .pargs = { | |
18168da7 | 289 | { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING }, |
d99a0153 CW |
290 | {NULL, 0} |
291 | }, | |
292 | .kwargs = { | |
293 | {NULL, 0} | |
294 | } | |
295 | }; | |
296 | ||
297 | static int | |
298 | zcp_children_list(lua_State *state) | |
299 | { | |
300 | const char *fsname = lua_tostring(state, 1); | |
301 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
302 | boolean_t issnap; | |
303 | uint64_t dsobj; | |
304 | ||
305 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG); | |
306 | if (ds == NULL) | |
307 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
308 | ||
309 | issnap = ds->ds_is_snapshot; | |
310 | dsobj = ds->ds_object; | |
311 | dsl_dataset_rele(ds, FTAG); | |
312 | ||
313 | if (issnap) { | |
314 | return (zcp_argerror(state, 1, | |
315 | "argument %s cannot be a snapshot", fsname)); | |
316 | } | |
317 | ||
318 | lua_pushnumber(state, dsobj); | |
319 | lua_pushnumber(state, 0); | |
320 | lua_pushcclosure(state, &zcp_children_iter, 2); | |
321 | return (1); | |
322 | } | |
323 | ||
324 | static int | |
3b9edd7b | 325 | zcp_user_props_list_gc(lua_State *state) |
d99a0153 CW |
326 | { |
327 | nvlist_t **props = lua_touserdata(state, 1); | |
328 | if (*props != NULL) | |
329 | fnvlist_free(*props); | |
330 | return (0); | |
331 | } | |
332 | ||
333 | static int | |
3b9edd7b | 334 | zcp_user_props_iter(lua_State *state) |
d99a0153 | 335 | { |
d1807f16 | 336 | const char *source, *val; |
d99a0153 CW |
337 | nvlist_t *nvprop; |
338 | nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1)); | |
339 | nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2)); | |
340 | ||
341 | do { | |
342 | pair = nvlist_next_nvpair(*props, pair); | |
343 | if (pair == NULL) { | |
344 | fnvlist_free(*props); | |
345 | *props = NULL; | |
346 | return (0); | |
347 | } | |
348 | } while (!zfs_prop_user(nvpair_name(pair))); | |
349 | ||
350 | lua_pushlightuserdata(state, pair); | |
351 | lua_replace(state, lua_upvalueindex(2)); | |
352 | ||
353 | nvprop = fnvpair_value_nvlist(pair); | |
354 | val = fnvlist_lookup_string(nvprop, ZPROP_VALUE); | |
355 | source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE); | |
356 | ||
357 | (void) lua_pushstring(state, nvpair_name(pair)); | |
358 | (void) lua_pushstring(state, val); | |
359 | (void) lua_pushstring(state, source); | |
360 | return (3); | |
361 | } | |
362 | ||
3b9edd7b | 363 | static int zcp_user_props_list(lua_State *); |
18168da7 | 364 | static const zcp_list_info_t zcp_user_props_list_info = { |
3b9edd7b SD |
365 | .name = "user_properties", |
366 | .func = zcp_user_props_list, | |
367 | .gc = zcp_user_props_list_gc, | |
368 | .pargs = { | |
369 | { .za_name = "filesystem | snapshot | volume", | |
18168da7 | 370 | .za_lua_type = LUA_TSTRING }, |
3b9edd7b SD |
371 | {NULL, 0} |
372 | }, | |
373 | .kwargs = { | |
374 | {NULL, 0} | |
375 | } | |
376 | }; | |
377 | ||
378 | /* | |
379 | * 'properties' was the initial name for 'user_properties' seen | |
380 | * above. 'user_properties' is a better name as it distinguishes | |
381 | * these properties from 'system_properties' which are different. | |
382 | * In order to avoid breaking compatibility between different | |
383 | * versions of ZFS, we declare 'properties' as an alias for | |
384 | * 'user_properties'. | |
385 | */ | |
18168da7 | 386 | static const zcp_list_info_t zcp_props_list_info = { |
d99a0153 | 387 | .name = "properties", |
3b9edd7b SD |
388 | .func = zcp_user_props_list, |
389 | .gc = zcp_user_props_list_gc, | |
d99a0153 CW |
390 | .pargs = { |
391 | { .za_name = "filesystem | snapshot | volume", | |
18168da7 | 392 | .za_lua_type = LUA_TSTRING }, |
d99a0153 CW |
393 | {NULL, 0} |
394 | }, | |
395 | .kwargs = { | |
396 | {NULL, 0} | |
397 | } | |
398 | }; | |
399 | ||
400 | static int | |
3b9edd7b | 401 | zcp_user_props_list(lua_State *state) |
d99a0153 CW |
402 | { |
403 | const char *dsname = lua_tostring(state, 1); | |
404 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
405 | objset_t *os; | |
406 | nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *)); | |
407 | ||
408 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG); | |
409 | if (ds == NULL) | |
410 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
411 | VERIFY0(dmu_objset_from_ds(ds, &os)); | |
412 | VERIFY0(dsl_prop_get_all(os, props)); | |
413 | dsl_dataset_rele(ds, FTAG); | |
414 | ||
415 | /* | |
3b9edd7b SD |
416 | * Set the metatable for the properties list to free it on |
417 | * completion. | |
d99a0153 | 418 | */ |
3b9edd7b | 419 | luaL_getmetatable(state, zcp_user_props_list_info.name); |
d99a0153 CW |
420 | (void) lua_setmetatable(state, -2); |
421 | ||
422 | lua_pushlightuserdata(state, NULL); | |
3b9edd7b | 423 | lua_pushcclosure(state, &zcp_user_props_iter, 2); |
d99a0153 CW |
424 | return (1); |
425 | } | |
426 | ||
427 | ||
428 | /* | |
3b9edd7b | 429 | * Populate nv with all valid system properties and their values for the given |
d99a0153 CW |
430 | * dataset. |
431 | */ | |
432 | static void | |
3b9edd7b | 433 | zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv) |
d99a0153 CW |
434 | { |
435 | for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) { | |
436 | /* Do not display hidden props */ | |
437 | if (!zfs_prop_visible(prop)) | |
438 | continue; | |
439 | /* Do not display props not valid for this dataset */ | |
440 | if (!prop_valid_for_ds(ds, prop)) | |
441 | continue; | |
442 | fnvlist_add_boolean(nv, zfs_prop_to_name(prop)); | |
443 | } | |
444 | } | |
445 | ||
446 | static int zcp_system_props_list(lua_State *); | |
18168da7 | 447 | static const zcp_list_info_t zcp_system_props_list_info = { |
d99a0153 CW |
448 | .name = "system_properties", |
449 | .func = zcp_system_props_list, | |
450 | .pargs = { | |
18168da7 | 451 | { .za_name = "dataset", .za_lua_type = LUA_TSTRING }, |
d99a0153 CW |
452 | {NULL, 0} |
453 | }, | |
454 | .kwargs = { | |
455 | {NULL, 0} | |
456 | } | |
457 | }; | |
458 | ||
459 | /* | |
e1cfd73f | 460 | * Get a list of all visible system properties and their values for a given |
3b9edd7b | 461 | * dataset. Returned on the stack as a Lua table. |
d99a0153 CW |
462 | */ |
463 | static int | |
464 | zcp_system_props_list(lua_State *state) | |
465 | { | |
466 | int error; | |
467 | char errbuf[128]; | |
468 | const char *dataset_name; | |
469 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
18168da7 | 470 | const zcp_list_info_t *libinfo = &zcp_system_props_list_info; |
d99a0153 CW |
471 | zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); |
472 | dataset_name = lua_tostring(state, 1); | |
473 | nvlist_t *nv = fnvlist_alloc(); | |
474 | ||
475 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
476 | if (ds == NULL) | |
477 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
478 | ||
3b9edd7b SD |
479 | /* Get the names of all valid system properties for this dataset */ |
480 | zcp_dataset_system_props(ds, nv); | |
d99a0153 CW |
481 | dsl_dataset_rele(ds, FTAG); |
482 | ||
483 | /* push list as lua table */ | |
484 | error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf)); | |
485 | nvlist_free(nv); | |
486 | if (error != 0) { | |
487 | return (luaL_error(state, | |
488 | "Error returning nvlist: %s", errbuf)); | |
489 | } | |
490 | return (1); | |
491 | } | |
492 | ||
3b9edd7b SD |
493 | static int |
494 | zcp_bookmarks_iter(lua_State *state) | |
495 | { | |
496 | char ds_name[ZFS_MAX_DATASET_NAME_LEN]; | |
497 | char bookmark_name[ZFS_MAX_DATASET_NAME_LEN]; | |
498 | uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); | |
499 | uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); | |
500 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
501 | dsl_dataset_t *ds; | |
502 | zap_attribute_t za; | |
503 | zap_cursor_t zc; | |
504 | ||
505 | int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); | |
506 | if (err == ENOENT) { | |
507 | return (0); | |
508 | } else if (err != 0) { | |
509 | return (luaL_error(state, | |
510 | "unexpected error %d from dsl_dataset_hold_obj(dsobj)", | |
511 | err)); | |
512 | } | |
513 | ||
514 | if (!dsl_dataset_is_zapified(ds)) { | |
515 | dsl_dataset_rele(ds, FTAG); | |
516 | return (0); | |
517 | } | |
518 | ||
519 | err = zap_lookup(dp->dp_meta_objset, ds->ds_object, | |
520 | DS_FIELD_BOOKMARK_NAMES, sizeof (ds->ds_bookmarks_obj), 1, | |
521 | &ds->ds_bookmarks_obj); | |
522 | if (err != 0 && err != ENOENT) { | |
523 | dsl_dataset_rele(ds, FTAG); | |
524 | return (luaL_error(state, | |
525 | "unexpected error %d from zap_lookup()", err)); | |
526 | } | |
527 | if (ds->ds_bookmarks_obj == 0) { | |
528 | dsl_dataset_rele(ds, FTAG); | |
529 | return (0); | |
530 | } | |
531 | ||
532 | /* Store the dataset's name so we can append the bookmark's name */ | |
533 | dsl_dataset_name(ds, ds_name); | |
534 | ||
535 | zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset, | |
536 | ds->ds_bookmarks_obj, cursor); | |
537 | dsl_dataset_rele(ds, FTAG); | |
538 | ||
539 | err = zap_cursor_retrieve(&zc, &za); | |
540 | if (err != 0) { | |
541 | zap_cursor_fini(&zc); | |
542 | if (err != ENOENT) { | |
543 | return (luaL_error(state, | |
544 | "unexpected error %d from zap_cursor_retrieve()", | |
545 | err)); | |
546 | } | |
547 | return (0); | |
548 | } | |
549 | zap_cursor_advance(&zc); | |
550 | cursor = zap_cursor_serialize(&zc); | |
551 | zap_cursor_fini(&zc); | |
552 | ||
553 | /* Create the full "pool/fs#bookmark" string to return */ | |
554 | int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s", | |
555 | ds_name, za.za_name); | |
556 | if (n >= ZFS_MAX_DATASET_NAME_LEN) { | |
557 | return (luaL_error(state, | |
558 | "unexpected error %d from snprintf()", ENAMETOOLONG)); | |
559 | } | |
560 | ||
561 | lua_pushnumber(state, cursor); | |
562 | lua_replace(state, lua_upvalueindex(2)); | |
563 | ||
564 | (void) lua_pushstring(state, bookmark_name); | |
565 | return (1); | |
566 | } | |
567 | ||
568 | static int zcp_bookmarks_list(lua_State *); | |
18168da7 | 569 | static const zcp_list_info_t zcp_bookmarks_list_info = { |
3b9edd7b SD |
570 | .name = "bookmarks", |
571 | .func = zcp_bookmarks_list, | |
572 | .pargs = { | |
18168da7 | 573 | { .za_name = "dataset", .za_lua_type = LUA_TSTRING }, |
3b9edd7b SD |
574 | {NULL, 0} |
575 | }, | |
576 | .kwargs = { | |
577 | {NULL, 0} | |
578 | } | |
579 | }; | |
580 | ||
581 | static int | |
582 | zcp_bookmarks_list(lua_State *state) | |
583 | { | |
584 | const char *dsname = lua_tostring(state, 1); | |
585 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
586 | ||
587 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG); | |
588 | if (ds == NULL) | |
589 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
590 | ||
591 | boolean_t issnap = ds->ds_is_snapshot; | |
592 | uint64_t dsobj = ds->ds_object; | |
593 | uint64_t cursor = 0; | |
594 | dsl_dataset_rele(ds, FTAG); | |
595 | ||
596 | if (issnap) { | |
597 | return (zcp_argerror(state, 1, "%s is a snapshot", dsname)); | |
598 | } | |
599 | ||
600 | lua_pushnumber(state, dsobj); | |
601 | lua_pushnumber(state, cursor); | |
602 | lua_pushcclosure(state, &zcp_bookmarks_iter, 2); | |
603 | return (1); | |
604 | } | |
605 | ||
606 | static int | |
607 | zcp_holds_iter(lua_State *state) | |
608 | { | |
609 | uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1)); | |
610 | uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); | |
611 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
612 | dsl_dataset_t *ds; | |
613 | zap_attribute_t za; | |
614 | zap_cursor_t zc; | |
615 | ||
616 | int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); | |
617 | if (err == ENOENT) { | |
618 | return (0); | |
619 | } else if (err != 0) { | |
620 | return (luaL_error(state, | |
621 | "unexpected error %d from dsl_dataset_hold_obj(dsobj)", | |
622 | err)); | |
623 | } | |
624 | ||
625 | if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) { | |
626 | dsl_dataset_rele(ds, FTAG); | |
627 | return (0); | |
628 | } | |
629 | ||
630 | zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset, | |
631 | dsl_dataset_phys(ds)->ds_userrefs_obj, cursor); | |
632 | dsl_dataset_rele(ds, FTAG); | |
633 | ||
634 | err = zap_cursor_retrieve(&zc, &za); | |
635 | if (err != 0) { | |
636 | zap_cursor_fini(&zc); | |
637 | if (err != ENOENT) { | |
638 | return (luaL_error(state, | |
639 | "unexpected error %d from zap_cursor_retrieve()", | |
640 | err)); | |
641 | } | |
642 | return (0); | |
643 | } | |
644 | zap_cursor_advance(&zc); | |
645 | cursor = zap_cursor_serialize(&zc); | |
646 | zap_cursor_fini(&zc); | |
647 | ||
648 | lua_pushnumber(state, cursor); | |
649 | lua_replace(state, lua_upvalueindex(2)); | |
650 | ||
651 | (void) lua_pushstring(state, za.za_name); | |
652 | (void) lua_pushnumber(state, za.za_first_integer); | |
653 | return (2); | |
654 | } | |
655 | ||
656 | static int zcp_holds_list(lua_State *); | |
18168da7 | 657 | static const zcp_list_info_t zcp_holds_list_info = { |
3b9edd7b SD |
658 | .name = "holds", |
659 | .func = zcp_holds_list, | |
660 | .gc = NULL, | |
661 | .pargs = { | |
18168da7 | 662 | { .za_name = "snapshot", .za_lua_type = LUA_TSTRING }, |
3b9edd7b SD |
663 | {NULL, 0} |
664 | }, | |
665 | .kwargs = { | |
666 | {NULL, 0} | |
667 | } | |
668 | }; | |
669 | ||
670 | /* | |
671 | * Iterate over all the holds for a given dataset. Each iteration returns | |
672 | * a hold's tag and its timestamp as an integer. | |
673 | */ | |
674 | static int | |
675 | zcp_holds_list(lua_State *state) | |
676 | { | |
677 | const char *snapname = lua_tostring(state, 1); | |
678 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
679 | ||
680 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG); | |
681 | if (ds == NULL) | |
682 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
683 | ||
684 | boolean_t issnap = ds->ds_is_snapshot; | |
685 | uint64_t dsobj = ds->ds_object; | |
686 | uint64_t cursor = 0; | |
687 | dsl_dataset_rele(ds, FTAG); | |
688 | ||
689 | if (!issnap) { | |
690 | return (zcp_argerror(state, 1, "%s is not a snapshot", | |
691 | snapname)); | |
692 | } | |
693 | ||
694 | lua_pushnumber(state, dsobj); | |
695 | lua_pushnumber(state, cursor); | |
696 | lua_pushcclosure(state, &zcp_holds_iter, 2); | |
697 | return (1); | |
698 | } | |
699 | ||
d99a0153 CW |
700 | static int |
701 | zcp_list_func(lua_State *state) | |
702 | { | |
703 | zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1)); | |
704 | ||
705 | zcp_parse_args(state, info->name, info->pargs, info->kwargs); | |
706 | ||
707 | return (info->func(state)); | |
708 | } | |
709 | ||
710 | int | |
711 | zcp_load_list_lib(lua_State *state) | |
712 | { | |
18168da7 | 713 | const zcp_list_info_t *zcp_list_funcs[] = { |
d99a0153 CW |
714 | &zcp_children_list_info, |
715 | &zcp_snapshots_list_info, | |
3b9edd7b | 716 | &zcp_user_props_list_info, |
d99a0153 CW |
717 | &zcp_props_list_info, |
718 | &zcp_clones_list_info, | |
719 | &zcp_system_props_list_info, | |
3b9edd7b SD |
720 | &zcp_bookmarks_list_info, |
721 | &zcp_holds_list_info, | |
d99a0153 CW |
722 | NULL |
723 | }; | |
724 | ||
725 | lua_newtable(state); | |
726 | ||
18168da7 AZ |
727 | for (int i = 0; zcp_list_funcs[i] != NULL; i++) { |
728 | const zcp_list_info_t *info = zcp_list_funcs[i]; | |
d99a0153 CW |
729 | |
730 | if (info->gc != NULL) { | |
731 | /* | |
732 | * If the function requires garbage collection, create | |
733 | * a metatable with its name and register the __gc | |
734 | * function. | |
735 | */ | |
736 | (void) luaL_newmetatable(state, info->name); | |
737 | (void) lua_pushstring(state, "__gc"); | |
738 | lua_pushcfunction(state, info->gc); | |
739 | lua_settable(state, -3); | |
740 | lua_pop(state, 1); | |
741 | } | |
742 | ||
18168da7 | 743 | lua_pushlightuserdata(state, (void *)(uintptr_t)info); |
d99a0153 CW |
744 | lua_pushcclosure(state, &zcp_list_func, 1); |
745 | lua_setfield(state, -2, info->name); | |
d99a0153 CW |
746 | } |
747 | ||
748 | return (1); | |
749 | } |