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