1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * This file and its contents are supplied under the terms of the
6 * Common Development and Distribution License ("CDDL"), version 1.0.
7 * You may only use this file in accordance with the terms of version
8 * 1.0 of the CDDL.
9 *
10 * A full copy of the text of the CDDL should have accompanied this
11 * source. A copy of the CDDL is also available via the Internet at
12 * http://www.illumos.org/license/CDDL.
13 *
14 * CDDL HEADER END
15 */
16
17 /*
18 * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
19 * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
20 * Copyright 2020 Joyent, Inc.
21 * Copyright (c) 2025, Rob Norris <robn@despairlabs.com>
22 */
23
24 #include <sys/lua/lua.h>
25 #include <sys/lua/lauxlib.h>
26
27 #include <sys/zcp.h>
28 #include <sys/zcp_set.h>
29 #include <sys/dsl_dir.h>
30 #include <sys/dsl_pool.h>
31 #include <sys/dsl_prop.h>
32 #include <sys/dsl_synctask.h>
33 #include <sys/dsl_dataset.h>
34 #include <sys/dsl_bookmark.h>
35 #include <sys/dsl_destroy.h>
36 #include <sys/dmu_objset.h>
37 #include <sys/zfs_znode.h>
38 #include <sys/zfeature.h>
39 #include <sys/metaslab.h>
40
41 #define DST_AVG_BLKSHIFT 14
42
43 typedef struct zcp_inherit_prop_arg {
44 lua_State *zipa_state;
45 const char *zipa_prop;
46 dsl_props_set_arg_t zipa_dpsa;
47 } zcp_inherit_prop_arg_t;
48
49 typedef int (zcp_synctask_func_t)(lua_State *, boolean_t, nvlist_t *);
50 typedef struct zcp_synctask_info {
51 const char *name;
52 zcp_synctask_func_t *func;
53 const zcp_arg_t pargs[4];
54 const zcp_arg_t kwargs[2];
55 zfs_space_check_t space_check;
56 int blocks_modified;
57 } zcp_synctask_info_t;
58
59 static void
zcp_synctask_cleanup(void * arg)60 zcp_synctask_cleanup(void *arg)
61 {
62 fnvlist_free(arg);
63 }
64
65 /*
66 * Generic synctask interface for channel program syncfuncs.
67 *
68 * To perform some action in syncing context, we'd generally call
69 * dsl_sync_task(), but since the Lua script is already running inside a
70 * synctask we need to leave out some actions (such as acquiring the config
71 * rwlock and performing space checks).
72 *
73 * If 'sync' is false, executes a dry run and returns the error code.
74 *
75 * If we are not running in syncing context and we are not doing a dry run
76 * (meaning we are running a zfs.sync function in open-context) then we
77 * return a Lua error.
78 *
79 * This function also handles common fatal error cases for channel program
80 * library functions. If a fatal error occurs, err_dsname will be the dataset
81 * name reported in error messages, if supplied.
82 */
83 static int
zcp_sync_task(lua_State * state,dsl_checkfunc_t * checkfunc,dsl_syncfunc_t * syncfunc,void * arg,boolean_t sync,const char * err_dsname)84 zcp_sync_task(lua_State *state, dsl_checkfunc_t *checkfunc,
85 dsl_syncfunc_t *syncfunc, void *arg, boolean_t sync, const char *err_dsname)
86 {
87 int err;
88 zcp_run_info_t *ri = zcp_run_info(state);
89
90 err = checkfunc(arg, ri->zri_tx);
91 if (!sync)
92 return (err);
93
94 if (!ri->zri_sync) {
95 return (luaL_error(state, "running functions from the zfs.sync "
96 "submodule requires passing sync=TRUE to "
97 "lzc_channel_program() (i.e. do not specify the \"-n\" "
98 "command line argument)"));
99 }
100
101 if (err == 0) {
102 syncfunc(arg, ri->zri_tx);
103 } else if (err == EIO) {
104 if (err_dsname != NULL) {
105 return (luaL_error(state,
106 "I/O error while accessing dataset '%s'",
107 err_dsname));
108 } else {
109 return (luaL_error(state,
110 "I/O error while accessing dataset."));
111 }
112 }
113
114 return (err);
115 }
116
117 static int zcp_synctask_clone(lua_State *, boolean_t, nvlist_t *);
118 static const zcp_synctask_info_t zcp_synctask_clone_info = {
119 .name = "clone",
120 .func = zcp_synctask_clone,
121 .pargs = {
122 {.za_name = "snapshot", .za_lua_type = LUA_TSTRING },
123 {.za_name = "newdataset", .za_lua_type = LUA_TSTRING },
124 {NULL, 0}
125 },
126 .kwargs = {
127 {NULL, 0}
128 },
129 .space_check = ZFS_SPACE_CHECK_NORMAL,
130 .blocks_modified = 3
131 };
132 static int
zcp_synctask_clone(lua_State * state,boolean_t sync,nvlist_t * err_details)133 zcp_synctask_clone(lua_State *state, boolean_t sync, nvlist_t *err_details)
134 {
135 (void) err_details;
136 int err;
137
138 zcp_run_info_t *ri = zcp_run_info(state);
139 dsl_dataset_clone_arg_t ddca = {
140 .ddca_origin = lua_tostring(state, 1),
141 .ddca_clone = lua_tostring(state, 2),
142 .ddca_cred = ri->zri_cred,
143 };
144
145 err = zcp_sync_task(state, dsl_dataset_clone_check,
146 dsl_dataset_clone_sync, &ddca, sync, ddca.ddca_origin);
147
148 if (err == 0)
149 /*
150 * If the new dataset is a zvol, it will need a device
151 * node. Put it on the list to be considered in open context.
152 * We don't have to check the type here; if it's not a zvol,
153 * or has volmode=none, it will be ignored.
154 */
155 fnvlist_add_boolean(ri->zri_new_zvols, ddca.ddca_clone);
156
157 return (err);
158 }
159
160 static int zcp_synctask_destroy(lua_State *, boolean_t, nvlist_t *);
161 static const zcp_synctask_info_t zcp_synctask_destroy_info = {
162 .name = "destroy",
163 .func = zcp_synctask_destroy,
164 .pargs = {
165 {.za_name = "filesystem | snapshot", .za_lua_type = LUA_TSTRING },
166 {NULL, 0}
167 },
168 .kwargs = {
169 {.za_name = "defer", .za_lua_type = LUA_TBOOLEAN },
170 {NULL, 0}
171 },
172 .space_check = ZFS_SPACE_CHECK_DESTROY,
173 .blocks_modified = 0
174 };
175
176 static int
zcp_synctask_destroy(lua_State * state,boolean_t sync,nvlist_t * err_details)177 zcp_synctask_destroy(lua_State *state, boolean_t sync, nvlist_t *err_details)
178 {
179 (void) err_details;
180 int err;
181 const char *dsname = lua_tostring(state, 1);
182
183 boolean_t issnap = (strchr(dsname, '@') != NULL);
184
185 if (!issnap && !lua_isnil(state, 2)) {
186 return (luaL_error(state,
187 "'deferred' kwarg only supported for snapshots: %s",
188 dsname));
189 }
190
191 if (issnap) {
192 dsl_destroy_snapshot_arg_t ddsa = { 0 };
193 ddsa.ddsa_name = dsname;
194 if (!lua_isnil(state, 2)) {
195 ddsa.ddsa_defer = lua_toboolean(state, 2);
196 } else {
197 ddsa.ddsa_defer = B_FALSE;
198 }
199
200 err = zcp_sync_task(state, dsl_destroy_snapshot_check,
201 dsl_destroy_snapshot_sync, &ddsa, sync, dsname);
202 } else {
203 dsl_destroy_head_arg_t ddha = { 0 };
204 ddha.ddha_name = dsname;
205
206 err = zcp_sync_task(state, dsl_destroy_head_check,
207 dsl_destroy_head_sync, &ddha, sync, dsname);
208 }
209
210 return (err);
211 }
212
213 static int zcp_synctask_promote(lua_State *, boolean_t, nvlist_t *);
214 static const zcp_synctask_info_t zcp_synctask_promote_info = {
215 .name = "promote",
216 .func = zcp_synctask_promote,
217 .pargs = {
218 {.za_name = "clone", .za_lua_type = LUA_TSTRING },
219 {NULL, 0}
220 },
221 .kwargs = {
222 {NULL, 0}
223 },
224 .space_check = ZFS_SPACE_CHECK_RESERVED,
225 .blocks_modified = 3
226 };
227
228 static int
zcp_synctask_promote(lua_State * state,boolean_t sync,nvlist_t * err_details)229 zcp_synctask_promote(lua_State *state, boolean_t sync, nvlist_t *err_details)
230 {
231 int err;
232 dsl_dataset_promote_arg_t ddpa = { 0 };
233 const char *dsname = lua_tostring(state, 1);
234 zcp_run_info_t *ri = zcp_run_info(state);
235
236 ddpa.ddpa_clonename = dsname;
237 ddpa.err_ds = err_details;
238 ddpa.cr = ri->zri_cred;
239
240 /*
241 * If there was a snapshot name conflict, then err_ds will be filled
242 * with a list of conflicting snapshot names.
243 */
244 err = zcp_sync_task(state, dsl_dataset_promote_check,
245 dsl_dataset_promote_sync, &ddpa, sync, dsname);
246
247 return (err);
248 }
249
250 static int zcp_synctask_rollback(lua_State *, boolean_t, nvlist_t *err_details);
251 static const zcp_synctask_info_t zcp_synctask_rollback_info = {
252 .name = "rollback",
253 .func = zcp_synctask_rollback,
254 .space_check = ZFS_SPACE_CHECK_RESERVED,
255 .blocks_modified = 1,
256 .pargs = {
257 {.za_name = "filesystem", .za_lua_type = LUA_TSTRING },
258 {0, 0}
259 },
260 .kwargs = {
261 {0, 0}
262 }
263 };
264
265 static int
zcp_synctask_rollback(lua_State * state,boolean_t sync,nvlist_t * err_details)266 zcp_synctask_rollback(lua_State *state, boolean_t sync, nvlist_t *err_details)
267 {
268 int err;
269 const char *dsname = lua_tostring(state, 1);
270 dsl_dataset_rollback_arg_t ddra = { 0 };
271
272 ddra.ddra_fsname = dsname;
273 ddra.ddra_result = err_details;
274
275 err = zcp_sync_task(state, dsl_dataset_rollback_check,
276 dsl_dataset_rollback_sync, &ddra, sync, dsname);
277
278 return (err);
279 }
280
281 static int zcp_synctask_snapshot(lua_State *, boolean_t, nvlist_t *);
282 static const zcp_synctask_info_t zcp_synctask_snapshot_info = {
283 .name = "snapshot",
284 .func = zcp_synctask_snapshot,
285 .pargs = {
286 {.za_name = "filesystem@snapname | volume@snapname",
287 .za_lua_type = LUA_TSTRING },
288 {NULL, 0}
289 },
290 .kwargs = {
291 {NULL, 0}
292 },
293 .space_check = ZFS_SPACE_CHECK_NORMAL,
294 .blocks_modified = 3
295 };
296
297 static int
zcp_synctask_snapshot(lua_State * state,boolean_t sync,nvlist_t * err_details)298 zcp_synctask_snapshot(lua_State *state, boolean_t sync, nvlist_t *err_details)
299 {
300 (void) err_details;
301 int err;
302 dsl_dataset_snapshot_arg_t ddsa = { 0 };
303 const char *dsname = lua_tostring(state, 1);
304 zcp_run_info_t *ri = zcp_run_info(state);
305
306 /*
307 * On old pools, the ZIL must not be active when a snapshot is created,
308 * but we can't suspend the ZIL because we're already in syncing
309 * context.
310 */
311 if (spa_version(ri->zri_pool->dp_spa) < SPA_VERSION_FAST_SNAP) {
312 return (SET_ERROR(ENOTSUP));
313 }
314
315 /*
316 * We only allow for a single snapshot rather than a list, so the
317 * error list output is unnecessary.
318 */
319 ddsa.ddsa_errors = NULL;
320 ddsa.ddsa_props = NULL;
321 ddsa.ddsa_cr = ri->zri_cred;
322 ddsa.ddsa_snaps = fnvlist_alloc();
323 fnvlist_add_boolean(ddsa.ddsa_snaps, dsname);
324
325 zcp_cleanup_handler_t *zch = zcp_register_cleanup(state,
326 zcp_synctask_cleanup, ddsa.ddsa_snaps);
327
328 err = zcp_sync_task(state, dsl_dataset_snapshot_check,
329 dsl_dataset_snapshot_sync, &ddsa, sync, dsname);
330
331 if (err == 0) {
332 /*
333 * We may need to create a new device minor node for this
334 * dataset (if it is a zvol and the "snapdev" property is set).
335 * Save it in the nvlist so that it can be processed in open
336 * context.
337 */
338 fnvlist_add_boolean(ri->zri_new_zvols, dsname);
339 }
340
341 zcp_deregister_cleanup(state, zch);
342 fnvlist_free(ddsa.ddsa_snaps);
343
344 return (err);
345 }
346
347 static int zcp_synctask_rename_snapshot(lua_State *, boolean_t, nvlist_t *);
348 static const zcp_synctask_info_t zcp_synctask_rename_snapshot_info = {
349 .name = "rename_snapshot",
350 .func = zcp_synctask_rename_snapshot,
351 .pargs = {
352 {.za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
353 {.za_name = "oldsnapname", .za_lua_type = LUA_TSTRING },
354 {.za_name = "newsnapname", .za_lua_type = LUA_TSTRING },
355 {NULL, 0}
356 },
357 .space_check = ZFS_SPACE_CHECK_RESERVED,
358 .blocks_modified = 1
359 };
360
361 static int
zcp_synctask_rename_snapshot(lua_State * state,boolean_t sync,nvlist_t * err_details)362 zcp_synctask_rename_snapshot(lua_State *state, boolean_t sync,
363 nvlist_t *err_details)
364 {
365 (void) err_details;
366 int err;
367 const char *fsname = lua_tostring(state, 1);
368 const char *oldsnapname = lua_tostring(state, 2);
369 const char *newsnapname = lua_tostring(state, 3);
370
371 struct dsl_dataset_rename_snapshot_arg ddrsa = { 0 };
372 ddrsa.ddrsa_fsname = fsname;
373 ddrsa.ddrsa_oldsnapname = oldsnapname;
374 ddrsa.ddrsa_newsnapname = newsnapname;
375 ddrsa.ddrsa_recursive = B_FALSE;
376
377 err = zcp_sync_task(state, dsl_dataset_rename_snapshot_check,
378 dsl_dataset_rename_snapshot_sync, &ddrsa, sync, NULL);
379
380 return (err);
381 }
382
383 static int zcp_synctask_inherit_prop(lua_State *, boolean_t,
384 nvlist_t *err_details);
385 static const zcp_synctask_info_t zcp_synctask_inherit_prop_info = {
386 .name = "inherit",
387 .func = zcp_synctask_inherit_prop,
388 .space_check = ZFS_SPACE_CHECK_RESERVED,
389 .blocks_modified = 2, /* 2 * numprops */
390 .pargs = {
391 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
392 { .za_name = "property", .za_lua_type = LUA_TSTRING },
393 { NULL, 0 }
394 },
395 .kwargs = {
396 { NULL, 0 }
397 },
398 };
399
400 static int
zcp_synctask_inherit_prop_check(void * arg,dmu_tx_t * tx)401 zcp_synctask_inherit_prop_check(void *arg, dmu_tx_t *tx)
402 {
403 zcp_inherit_prop_arg_t *args = arg;
404 zfs_prop_t prop = zfs_name_to_prop(args->zipa_prop);
405
406 if (prop == ZPROP_USERPROP) {
407 if (zfs_prop_user(args->zipa_prop))
408 return (0);
409
410 return (EINVAL);
411 }
412
413 if (zfs_prop_readonly(prop))
414 return (EINVAL);
415
416 if (!zfs_prop_inheritable(prop))
417 return (EINVAL);
418
419 return (dsl_props_set_check(&args->zipa_dpsa, tx));
420 }
421
422 static void
zcp_synctask_inherit_prop_sync(void * arg,dmu_tx_t * tx)423 zcp_synctask_inherit_prop_sync(void *arg, dmu_tx_t *tx)
424 {
425 zcp_inherit_prop_arg_t *args = arg;
426 dsl_props_set_arg_t *dpsa = &args->zipa_dpsa;
427
428 dsl_props_set_sync(dpsa, tx);
429 }
430
431 static int
zcp_synctask_inherit_prop(lua_State * state,boolean_t sync,nvlist_t * err_details)432 zcp_synctask_inherit_prop(lua_State *state, boolean_t sync,
433 nvlist_t *err_details)
434 {
435 (void) err_details;
436 int err;
437 zcp_inherit_prop_arg_t zipa = { 0 };
438 dsl_props_set_arg_t *dpsa = &zipa.zipa_dpsa;
439
440 const char *dsname = lua_tostring(state, 1);
441 const char *prop = lua_tostring(state, 2);
442
443 zipa.zipa_state = state;
444 zipa.zipa_prop = prop;
445 dpsa->dpsa_dsname = dsname;
446 dpsa->dpsa_source = ZPROP_SRC_INHERITED;
447 dpsa->dpsa_props = fnvlist_alloc();
448 fnvlist_add_boolean(dpsa->dpsa_props, prop);
449
450 zcp_cleanup_handler_t *zch = zcp_register_cleanup(state,
451 zcp_synctask_cleanup, dpsa->dpsa_props);
452
453 err = zcp_sync_task(state, zcp_synctask_inherit_prop_check,
454 zcp_synctask_inherit_prop_sync, &zipa, sync, dsname);
455
456 zcp_deregister_cleanup(state, zch);
457 fnvlist_free(dpsa->dpsa_props);
458
459 return (err);
460 }
461
462 static int zcp_synctask_bookmark(lua_State *, boolean_t, nvlist_t *);
463 static const zcp_synctask_info_t zcp_synctask_bookmark_info = {
464 .name = "bookmark",
465 .func = zcp_synctask_bookmark,
466 .pargs = {
467 {.za_name = "snapshot | bookmark", .za_lua_type = LUA_TSTRING },
468 {.za_name = "bookmark", .za_lua_type = LUA_TSTRING },
469 {NULL, 0}
470 },
471 .kwargs = {
472 {NULL, 0}
473 },
474 .space_check = ZFS_SPACE_CHECK_NORMAL,
475 .blocks_modified = 1,
476 };
477
478 static int
zcp_synctask_bookmark(lua_State * state,boolean_t sync,nvlist_t * err_details)479 zcp_synctask_bookmark(lua_State *state, boolean_t sync, nvlist_t *err_details)
480 {
481 (void) err_details;
482 int err;
483 const char *source = lua_tostring(state, 1);
484 const char *new = lua_tostring(state, 2);
485
486 nvlist_t *bmarks = fnvlist_alloc();
487 fnvlist_add_string(bmarks, new, source);
488
489 zcp_cleanup_handler_t *zch = zcp_register_cleanup(state,
490 zcp_synctask_cleanup, bmarks);
491
492 dsl_bookmark_create_arg_t dbca = {
493 .dbca_bmarks = bmarks,
494 .dbca_errors = NULL,
495 };
496 err = zcp_sync_task(state, dsl_bookmark_create_check,
497 dsl_bookmark_create_sync, &dbca, sync, source);
498
499 zcp_deregister_cleanup(state, zch);
500 fnvlist_free(bmarks);
501
502 return (err);
503 }
504
505 static int zcp_synctask_set_prop(lua_State *, boolean_t, nvlist_t *err_details);
506 static const zcp_synctask_info_t zcp_synctask_set_prop_info = {
507 .name = "set_prop",
508 .func = zcp_synctask_set_prop,
509 .space_check = ZFS_SPACE_CHECK_RESERVED,
510 .blocks_modified = 2,
511 .pargs = {
512 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
513 { .za_name = "property", .za_lua_type = LUA_TSTRING },
514 { .za_name = "value", .za_lua_type = LUA_TSTRING },
515 { NULL, 0 }
516 },
517 .kwargs = {
518 { NULL, 0 }
519 }
520 };
521
522 static int
zcp_synctask_set_prop(lua_State * state,boolean_t sync,nvlist_t * err_details)523 zcp_synctask_set_prop(lua_State *state, boolean_t sync, nvlist_t *err_details)
524 {
525 (void) err_details;
526 int err;
527 zcp_set_prop_arg_t args = { 0 };
528
529 const char *dsname = lua_tostring(state, 1);
530 const char *prop = lua_tostring(state, 2);
531 const char *val = lua_tostring(state, 3);
532
533 args.state = state;
534 args.dsname = dsname;
535 args.prop = prop;
536 args.val = val;
537
538 err = zcp_sync_task(state, zcp_set_prop_check, zcp_set_prop_sync,
539 &args, sync, dsname);
540
541 return (err);
542 }
543
544 static int
zcp_synctask_wrapper(lua_State * state)545 zcp_synctask_wrapper(lua_State *state)
546 {
547 int err;
548 zcp_cleanup_handler_t *zch;
549 int num_ret = 1;
550 nvlist_t *err_details = fnvlist_alloc();
551
552 /*
553 * Make sure err_details is properly freed, even if a fatal error is
554 * thrown during the synctask.
555 */
556 zch = zcp_register_cleanup(state, zcp_synctask_cleanup, err_details);
557
558 zcp_synctask_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
559 boolean_t sync = lua_toboolean(state, lua_upvalueindex(2));
560
561 zcp_run_info_t *ri = zcp_run_info(state);
562 dsl_pool_t *dp = ri->zri_pool;
563
564 /* MOS space is triple-dittoed, so we multiply by 3. */
565 uint64_t funcspace =
566 ((uint64_t)info->blocks_modified << DST_AVG_BLKSHIFT) * 3;
567
568 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
569
570 err = 0;
571 if (info->space_check != ZFS_SPACE_CHECK_NONE) {
572 uint64_t quota = dsl_pool_unreserved_space(dp,
573 info->space_check);
574 uint64_t used = dsl_dir_phys(dp->dp_root_dir)->dd_used_bytes +
575 ri->zri_space_used;
576
577 if (used + funcspace > quota) {
578 err = SET_ERROR(ENOSPC);
579 }
580 }
581
582 if (err == 0) {
583 err = info->func(state, sync, err_details);
584 }
585
586 if (err == 0) {
587 ri->zri_space_used += funcspace;
588 }
589
590 lua_pushnumber(state, (lua_Number)err);
591 if (fnvlist_num_pairs(err_details) > 0) {
592 (void) zcp_nvlist_to_lua(state, err_details, NULL, 0);
593 num_ret++;
594 }
595
596 zcp_deregister_cleanup(state, zch);
597 fnvlist_free(err_details);
598
599 return (num_ret);
600 }
601
602 int
zcp_load_synctask_lib(lua_State * state,boolean_t sync)603 zcp_load_synctask_lib(lua_State *state, boolean_t sync)
604 {
605 const zcp_synctask_info_t *zcp_synctask_funcs[] = {
606 &zcp_synctask_destroy_info,
607 &zcp_synctask_clone_info,
608 &zcp_synctask_promote_info,
609 &zcp_synctask_rollback_info,
610 &zcp_synctask_snapshot_info,
611 &zcp_synctask_rename_snapshot_info,
612 &zcp_synctask_inherit_prop_info,
613 &zcp_synctask_bookmark_info,
614 &zcp_synctask_set_prop_info,
615 NULL
616 };
617
618 lua_newtable(state);
619
620 for (int i = 0; zcp_synctask_funcs[i] != NULL; i++) {
621 const zcp_synctask_info_t *info = zcp_synctask_funcs[i];
622 lua_pushlightuserdata(state, (void *)(uintptr_t)info);
623 lua_pushboolean(state, sync);
624 lua_pushcclosure(state, &zcp_synctask_wrapper, 2);
625 lua_setfield(state, -2, info->name);
626 }
627
628 return (1);
629 }
630