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