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