/* * Creates snapshots. * * The keys in the snaps nvlist are the snapshots to be created. * They must all be in the same pool. * * The props nvlist is properties to set. Currently only user properties * are supported. { user:prop_name -> string value } * * The returned results nvlist will have an entry for each snapshot that failed. * The value will be the (int32) error code. * * The return value will be 0 if all snapshots were created, otherwise it will * be the errno of a (unspecified) snapshot that failed. */ int lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist) { nvpair_t *elem; nvlist_t *args; int error; char pool[MAXNAMELEN]; *errlist = NULL; /* determine the pool name */ elem = nvlist_next_nvpair(snaps, NULL); if (elem == NULL) return (0); (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); pool[strcspn(pool, "/@")] = '\0'; args = fnvlist_alloc(); fnvlist_add_nvlist(args, "snaps", snaps); if (props != NULL) fnvlist_add_nvlist(args, "props", props); error = lzc_ioctl(ZFS_IOC_SNAPSHOT, pool, args, errlist); nvlist_free(args); return (error); }
int lzc_change_key(const char *fsname, uint64_t crypt_cmd, nvlist_t *props, uint8_t *wkeydata, uint_t wkeylen) { int error; nvlist_t *ioc_args = fnvlist_alloc(); nvlist_t *hidden_args = NULL; fnvlist_add_uint64(ioc_args, "crypt_cmd", crypt_cmd); if (wkeydata != NULL) { hidden_args = fnvlist_alloc(); fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata, wkeylen); fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); } if (props != NULL) fnvlist_add_nvlist(ioc_args, "props", props); error = lzc_ioctl(ZFS_IOC_CHANGE_KEY, fsname, ioc_args, NULL); nvlist_free(hidden_args); nvlist_free(ioc_args); return (error); }
static void test_recv_new(const char *dataset, int fd) { dmu_replay_record_t drr = { 0 }; nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *props = fnvlist_alloc(); char snapshot[MAXNAMELEN + 32]; ssize_t count; int cleanup_fd = open(ZFS_DEV, O_RDWR); (void) snprintf(snapshot, sizeof (snapshot), "%s@replicant", dataset); count = pread(fd, &drr, sizeof (drr), 0); if (count != sizeof (drr)) { (void) fprintf(stderr, "could not read stream: %s\n", strerror(errno)); } fnvlist_add_string(required, "snapname", snapshot); fnvlist_add_byte_array(required, "begin_record", (uchar_t *)&drr, sizeof (drr)); fnvlist_add_int32(required, "input_fd", fd); fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013"); fnvlist_add_nvlist(optional, "localprops", props); fnvlist_add_boolean(optional, "force"); fnvlist_add_int32(optional, "cleanup_fd", cleanup_fd); /* * TODO - Resumable receive is harder to set up. So we currently * ignore testing for one. */ #if 0 fnvlist_add_nvlist(optional, "props", recvdprops); fnvlist_add_string(optional, "origin", origin); fnvlist_add_boolean(optional, "resumable"); fnvlist_add_uint64(optional, "action_handle", *action_handle); #endif IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional, EBADE); nvlist_free(props); nvlist_free(optional); nvlist_free(required); (void) close(cleanup_fd); }
static void test_channel_program(const char *pool) { const char *program = "arg = ...\n" "argv = arg[\"argv\"]\n" "return argv[1]"; char *const argv[1] = { "Hello World!" }; nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *args = fnvlist_alloc(); fnvlist_add_string(required, "program", program); fnvlist_add_string_array(args, "argv", argv, 1); fnvlist_add_nvlist(required, "arg", args); fnvlist_add_boolean_value(optional, "sync", B_TRUE); fnvlist_add_uint64(optional, "instrlimit", 1000 * 1000); fnvlist_add_uint64(optional, "memlimit", 8192 * 1024); IOC_INPUT_TEST(ZFS_IOC_CHANNEL_PROGRAM, pool, required, optional, 0); nvlist_free(args); nvlist_free(optional); nvlist_free(required); }
/* * Sigh. Inside a local zone, we don't have access to /etc/zfs/zpool.cache, * and we don't want to allow the local zone to see all the pools anyway. * So we have to invent the ZFS_IOC_CONFIG ioctl to grab the configuration * information for all pool visible within the zone. */ nvlist_t * spa_all_configs(uint64_t *generation) { nvlist_t *pools; spa_t *spa = NULL; if (*generation == spa_config_generation) return (NULL); pools = fnvlist_alloc(); mutex_enter(&spa_namespace_lock); while ((spa = spa_next(spa)) != NULL) { if (INGLOBALZONE(curproc) || zone_dataset_visible(spa_name(spa), NULL)) { mutex_enter(&spa->spa_props_lock); fnvlist_add_nvlist(pools, spa_name(spa), spa->spa_config); mutex_exit(&spa->spa_props_lock); } } *generation = spa_config_generation; mutex_exit(&spa_namespace_lock); return (pools); }
/* * Destroys snapshots. * * The keys in the snaps nvlist are the snapshots to be destroyed. * They must all be in the same pool. * * Snapshots that do not exist will be silently ignored. * * If 'defer' is not set, and a snapshot has user holds or clones, the * destroy operation will fail and none of the snapshots will be * destroyed. * * If 'defer' is set, and a snapshot has user holds or clones, it will be * marked for deferred destruction, and will be destroyed when the last hold * or clone is removed/destroyed. * * The return value will be 0 if all snapshots were destroyed (or marked for * later destruction if 'defer' is set) or didn't exist to begin with. * * Otherwise the return value will be the errno of a (unspecified) snapshot * that failed, no snapshots will be destroyed, and the errlist will have an * entry for each snapshot that failed. The value in the errlist will be * the (int32) error code. */ int lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist) { nvpair_t *elem; nvlist_t *args; int error; char pool[MAXNAMELEN]; /* determine the pool name */ elem = nvlist_next_nvpair(snaps, NULL); if (elem == NULL) return (0); (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); pool[strcspn(pool, "/@")] = '\0'; args = fnvlist_alloc(); fnvlist_add_nvlist(args, "snaps", snaps); if (defer) fnvlist_add_boolean(args, "defer"); error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist); nvlist_free(args); return (error); }
/* * Create a redaction bookmark named bookname by redacting snapshot with respect * to all the snapshots in snapnv. */ int lzc_redact(const char *snapshot, const char *bookname, nvlist_t *snapnv) { nvlist_t *args = fnvlist_alloc(); fnvlist_add_string(args, "bookname", bookname); fnvlist_add_nvlist(args, "snapnv", snapnv); int error = lzc_ioctl(ZFS_IOC_REDACT, snapshot, args, NULL); fnvlist_free(args); return (error); }
static void test_snapshot(const char *pool, const char *snapshot) { nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *snaps = fnvlist_alloc(); nvlist_t *props = fnvlist_alloc(); fnvlist_add_boolean(snaps, snapshot); fnvlist_add_nvlist(required, "snaps", snaps); fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013"); fnvlist_add_nvlist(optional, "props", props); IOC_INPUT_TEST(ZFS_IOC_SNAPSHOT, pool, required, optional, 0); nvlist_free(props); nvlist_free(snaps); nvlist_free(optional); nvlist_free(required); }
int lzc_create(const char *fsname, dmu_objset_type_t type, nvlist_t *props) { int error; nvlist_t *args = fnvlist_alloc(); fnvlist_add_int32(args, "type", type); if (props != NULL) fnvlist_add_nvlist(args, "props", props); error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL); nvlist_free(args); return (error); }
int dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) { int err = 0; zap_cursor_t zc; zap_attribute_t attr; dsl_pool_t *dp = ds->ds_dir->dd_pool; uint64_t bmark_zapobj = ds->ds_bookmarks; if (bmark_zapobj == 0) return (0); for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); zap_cursor_retrieve(&zc, &attr) == 0; zap_cursor_advance(&zc)) { nvlist_t *out_props; char *bmark_name = attr.za_name; zfs_bookmark_phys_t bmark_phys = { 0 }; err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); ASSERT3U(err, !=, ENOENT); if (err != 0) break; out_props = fnvlist_alloc(); if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_GUID))) { dsl_prop_nvlist_add_uint64(out_props, ZFS_PROP_GUID, bmark_phys.zbm_guid); } if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_CREATETXG))) { dsl_prop_nvlist_add_uint64(out_props, ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); } if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_CREATION))) { dsl_prop_nvlist_add_uint64(out_props, ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); } if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_IVSET_GUID))) { dsl_prop_nvlist_add_uint64(out_props, ZFS_PROP_IVSET_GUID, bmark_phys.zbm_ivset_guid); } fnvlist_add_nvlist(outnvl, bmark_name, out_props); fnvlist_free(out_props); } zap_cursor_fini(&zc); return (err); }
int lzc_clone(const char *fsname, const char *origin, nvlist_t *props) { int error; nvlist_t *args = fnvlist_alloc(); fnvlist_add_string(args, "origin", origin); if (props != NULL) fnvlist_add_nvlist(args, "props", props); error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL); nvlist_free(args); return (error); }
static void test_destroy_snaps(const char *pool, const char *snapshot) { nvlist_t *required = fnvlist_alloc(); nvlist_t *snaps = fnvlist_alloc(); fnvlist_add_boolean(snaps, snapshot); fnvlist_add_nvlist(required, "snaps", snaps); IOC_INPUT_TEST(ZFS_IOC_DESTROY_SNAPS, pool, required, NULL, 0); nvlist_free(snaps); nvlist_free(required); }
static void test_release(const char *pool, const char *snapshot) { nvlist_t *required = fnvlist_alloc(); nvlist_t *release = fnvlist_alloc(); fnvlist_add_boolean(release, "libzfs_check_hold"); fnvlist_add_nvlist(required, snapshot, release); IOC_INPUT_TEST_WILD(ZFS_IOC_RELEASE, pool, required, NULL, 0); nvlist_free(release); nvlist_free(required); }
static void dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds, const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx) { dsl_pool_t *dp = ds->ds_dir->dd_pool; objset_t *mos = dp->dp_meta_objset; uint64_t zapobj; ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock)); if (ds->ds_phys->ds_userrefs_obj == 0) { /* * This is the first user hold for this dataset. Create * the userrefs zap object. */ dmu_buf_will_dirty(ds->ds_dbuf, tx); zapobj = ds->ds_phys->ds_userrefs_obj = zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); } else { zapobj = ds->ds_phys->ds_userrefs_obj; } ds->ds_userrefs++; VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx)); if (minor != 0) { char name[MAXNAMELEN]; nvlist_t *tags; VERIFY0(dsl_pool_user_hold(dp, ds->ds_object, htag, now, tx)); (void) snprintf(name, sizeof (name), "%llx", (u_longlong_t)ds->ds_object); if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) { VERIFY0(nvlist_alloc(&tags, NV_UNIQUE_NAME, KM_PUSHPAGE)); fnvlist_add_boolean(tags, htag); fnvlist_add_nvlist(tmpholds, name, tags); fnvlist_free(tags); } else { fnvlist_add_boolean(tags, htag); } } spa_history_log_internal_ds(ds, "hold", tx, "tag=%s temp=%d refs=%llu", htag, minor != 0, ds->ds_userrefs); }
int lzc_create(const char *fsname, enum lzc_dataset_type type, nvlist_t *props, uint8_t *wkeydata, uint_t wkeylen) { int error; nvlist_t *hidden_args = NULL; nvlist_t *args = fnvlist_alloc(); fnvlist_add_int32(args, "type", (dmu_objset_type_t)type); if (props != NULL) fnvlist_add_nvlist(args, "props", props); if (wkeydata != NULL) { hidden_args = fnvlist_alloc(); fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata, wkeylen); fnvlist_add_nvlist(args, ZPOOL_HIDDEN_ARGS, hidden_args); } error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL); nvlist_free(hidden_args); nvlist_free(args); return (error); }
static void test_vdev_initialize(const char *pool) { nvlist_t *required = fnvlist_alloc(); nvlist_t *vdev_guids = fnvlist_alloc(); fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef); fnvlist_add_uint64(required, ZPOOL_INITIALIZE_COMMAND, POOL_INITIALIZE_DO); fnvlist_add_nvlist(required, ZPOOL_INITIALIZE_VDEVS, vdev_guids); IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, NULL, EINVAL); nvlist_free(vdev_guids); nvlist_free(required); }
/* * Convert a value from the given index into the lua stack to an nvpair, adding * it to an nvlist with the given key. * * Values are converted as follows: * * string -> string * number -> int64 * boolean -> boolean * nil -> boolean (no value) * * Lua tables are converted to nvlists and then inserted. The table's keys * are converted to strings then used as keys in the nvlist to store each table * element. Keys are converted as follows: * * string -> no change * number -> "%lld" * boolean -> "true" | "false" * nil -> error * * In the case of a key collision, an error is thrown. * * If an error is encountered, a nonzero error code is returned, and an error * string will be pushed onto the Lua stack. */ static int zcp_lua_to_nvlist_impl(lua_State *state, int index, nvlist_t *nvl, const char *key, int depth) { /* * Verify that we have enough remaining space in the lua stack to parse * a key-value pair and push an error. */ if (!lua_checkstack(state, 3)) { (void) lua_pushstring(state, "Lua stack overflow"); return (1); } index = lua_absindex(state, index); switch (lua_type(state, index)) { case LUA_TNIL: fnvlist_add_boolean(nvl, key); break; case LUA_TBOOLEAN: fnvlist_add_boolean_value(nvl, key, lua_toboolean(state, index)); break; case LUA_TNUMBER: fnvlist_add_int64(nvl, key, lua_tonumber(state, index)); break; case LUA_TSTRING: fnvlist_add_string(nvl, key, lua_tostring(state, index)); break; case LUA_TTABLE: { nvlist_t *value_nvl = zcp_table_to_nvlist(state, index, depth); if (value_nvl == NULL) return (EINVAL); fnvlist_add_nvlist(nvl, key, value_nvl); fnvlist_free(value_nvl); break; } default: (void) lua_pushfstring(state, "Invalid value type '%s' for key '%s'", lua_typename(state, lua_type(state, index)), key); return (EINVAL); } return (0); }
static void test_load_key(const char *dataset) { nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *hidden = fnvlist_alloc(); uint8_t keydata[WRAPPING_KEY_LEN] = {0}; fnvlist_add_uint8_array(hidden, "wkeydata", keydata, sizeof (keydata)); fnvlist_add_nvlist(required, "hidden_args", hidden); fnvlist_add_boolean(optional, "noop"); IOC_INPUT_TEST(ZFS_IOC_LOAD_KEY, dataset, required, optional, EINVAL); nvlist_free(hidden); nvlist_free(optional); nvlist_free(required); }
static void test_hold(const char *pool, const char *snapshot) { nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *holds = fnvlist_alloc(); fnvlist_add_string(holds, snapshot, "libzfs_check_hold"); fnvlist_add_nvlist(required, "holds", holds); fnvlist_add_int32(optional, "cleanup_fd", zfs_fd); IOC_INPUT_TEST(ZFS_IOC_HOLD, pool, required, optional, 0); nvlist_free(holds); nvlist_free(optional); nvlist_free(required); }
static int lzc_channel_program_impl(const char *pool, const char *program, boolean_t sync, uint64_t instrlimit, uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl) { int error; nvlist_t *args; args = fnvlist_alloc(); fnvlist_add_string(args, ZCP_ARG_PROGRAM, program); fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl); fnvlist_add_boolean_value(args, ZCP_ARG_SYNC, sync); fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit); fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit); error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl); fnvlist_free(args); return (error); }
static void test_create(const char *pool) { char dataset[MAXNAMELEN + 32]; (void) snprintf(dataset, sizeof (dataset), "%s/create-fs", pool); nvlist_t *required = fnvlist_alloc(); nvlist_t *optional = fnvlist_alloc(); nvlist_t *props = fnvlist_alloc(); fnvlist_add_int32(required, "type", DMU_OST_ZFS); fnvlist_add_uint64(props, "recordsize", 8192); fnvlist_add_nvlist(optional, "props", props); IOC_INPUT_TEST(ZFS_IOC_CREATE, dataset, required, optional, 0); nvlist_free(required); nvlist_free(optional); }
/* * Performs key management functions * * crypto_cmd should be a value from zfs_ioc_crypto_cmd_t. If the command * specifies to load or change a wrapping key, the key should be specified in * the hidden_args nvlist so that it is not logged */ int lzc_load_key(const char *fsname, boolean_t noop, uint8_t *wkeydata, uint_t wkeylen) { int error; nvlist_t *ioc_args; nvlist_t *hidden_args; if (wkeydata == NULL) return (EINVAL); ioc_args = fnvlist_alloc(); hidden_args = fnvlist_alloc(); fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata, wkeylen); fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args); if (noop) fnvlist_add_boolean(ioc_args, "noop"); error = lzc_ioctl(ZFS_IOC_LOAD_KEY, fsname, ioc_args, NULL); nvlist_free(hidden_args); nvlist_free(ioc_args); return (error); }
/* * Create "user holds" on snapshots. If there is a hold on a snapshot, * the snapshot can not be destroyed. (However, it can be marked for deletion * by lzc_destroy_snaps(defer=B_TRUE).) * * The keys in the nvlist are snapshot names. * The snapshots must all be in the same pool. * The value is the name of the hold (string type). * * If cleanup_fd is not -1, it must be the result of open("/dev/zfs", O_EXCL). * In this case, when the cleanup_fd is closed (including on process * termination), the holds will be released. If the system is shut down * uncleanly, the holds will be released when the pool is next opened * or imported. * * Holds for snapshots which don't exist will be skipped and have an entry * added to errlist, but will not cause an overall failure. * * The return value will be 0 if all holds, for snapshots that existed, * were succesfully created. * * Otherwise the return value will be the errno of a (unspecified) hold that * failed and no holds will be created. * * In all cases the errlist will have an entry for each hold that failed * (name = snapshot), with its value being the error code (int32). */ int lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist) { char pool[MAXNAMELEN]; nvlist_t *args; nvpair_t *elem; int error; /* determine the pool name */ elem = nvlist_next_nvpair(holds, NULL); if (elem == NULL) return (0); (void) strlcpy(pool, nvpair_name(elem), sizeof (pool)); pool[strcspn(pool, "/@")] = '\0'; args = fnvlist_alloc(); fnvlist_add_nvlist(args, "holds", holds); if (cleanup_fd != -1) fnvlist_add_int32(args, "cleanup_fd", cleanup_fd); error = lzc_ioctl(ZFS_IOC_HOLD, pool, args, errlist); nvlist_free(args); return (error); }
/* * Generate the pool's configuration based on the current in-core state. * * We infer whether to generate a complete config or just one top-level config * based on whether vd is the root vdev. */ nvlist_t * spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) { nvlist_t *config, *nvroot; vdev_t *rvd = spa->spa_root_vdev; unsigned long hostid = 0; boolean_t locked = B_FALSE; uint64_t split_guid; char *pool_name; int config_gen_flags = 0; if (vd == NULL) { vd = rvd; locked = B_TRUE; spa_config_enter(spa, SCL_CONFIG | SCL_STATE, FTAG, RW_READER); } ASSERT(spa_config_held(spa, SCL_CONFIG | SCL_STATE, RW_READER) == (SCL_CONFIG | SCL_STATE)); /* * If txg is -1, report the current value of spa->spa_config_txg. */ if (txg == -1ULL) txg = spa->spa_config_txg; /* * Originally, users had to handle spa namespace collisions by either * exporting the already imported pool or by specifying a new name for * the pool with a conflicting name. In the case of root pools from * virtual guests, neither approach to collision resolution is * reasonable. This is addressed by extending the new name syntax with * an option to specify that the new name is temporary. When specified, * ZFS_IMPORT_TEMP_NAME will be set in spa->spa_import_flags to tell us * to use the previous name, which we do below. */ if (spa->spa_import_flags & ZFS_IMPORT_TEMP_NAME) { VERIFY0(nvlist_lookup_string(spa->spa_config, ZPOOL_CONFIG_POOL_NAME, &pool_name)); } else pool_name = spa_name(spa); config = fnvlist_alloc(); fnvlist_add_uint64(config, ZPOOL_CONFIG_VERSION, spa_version(spa)); fnvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME, pool_name); fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE, spa_state(spa)); fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, txg); fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa)); fnvlist_add_uint64(config, ZPOOL_CONFIG_ERRATA, spa->spa_errata); if (spa->spa_comment != NULL) fnvlist_add_string(config, ZPOOL_CONFIG_COMMENT, spa->spa_comment); #ifdef _KERNEL hostid = zone_get_hostid(NULL); #else /* _KERNEL */ /* * We're emulating the system's hostid in userland, so we can't use * zone_get_hostid(). */ (void) ddi_strtoul(hw_serial, NULL, 10, &hostid); #endif /* _KERNEL */ if (hostid != 0) fnvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID, hostid); fnvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME, utsname()->nodename); if (vd != rvd) { fnvlist_add_uint64(config, ZPOOL_CONFIG_TOP_GUID, vd->vdev_top->vdev_guid); fnvlist_add_uint64(config, ZPOOL_CONFIG_GUID, vd->vdev_guid); if (vd->vdev_isspare) fnvlist_add_uint64(config, ZPOOL_CONFIG_IS_SPARE, 1ULL); if (vd->vdev_islog) fnvlist_add_uint64(config, ZPOOL_CONFIG_IS_LOG, 1ULL); vd = vd->vdev_top; /* label contains top config */ } else { /* * Only add the (potentially large) split information * in the mos config, and not in the vdev labels */ if (spa->spa_config_splitting != NULL) fnvlist_add_nvlist(config, ZPOOL_CONFIG_SPLIT, spa->spa_config_splitting); fnvlist_add_boolean(config, ZPOOL_CONFIG_HAS_PER_VDEV_ZAPS); config_gen_flags |= VDEV_CONFIG_MOS; } /* * Add the top-level config. We even add this on pools which * don't support holes in the namespace. */ vdev_top_config_generate(spa, config); /* * If we're splitting, record the original pool's guid. */ if (spa->spa_config_splitting != NULL && nvlist_lookup_uint64(spa->spa_config_splitting, ZPOOL_CONFIG_SPLIT_GUID, &split_guid) == 0) { fnvlist_add_uint64(config, ZPOOL_CONFIG_SPLIT_GUID, split_guid); } nvroot = vdev_config_generate(spa, vd, getstats, config_gen_flags); fnvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot); nvlist_free(nvroot); /* * Store what's necessary for reading the MOS in the label. */ fnvlist_add_nvlist(config, ZPOOL_CONFIG_FEATURES_FOR_READ, spa->spa_label_features); if (getstats && spa_load_state(spa) == SPA_LOAD_NONE) { ddt_histogram_t *ddh; ddt_stat_t *dds; ddt_object_t *ddo; ddh = kmem_zalloc(sizeof (ddt_histogram_t), KM_SLEEP); ddt_get_dedup_histogram(spa, ddh); fnvlist_add_uint64_array(config, ZPOOL_CONFIG_DDT_HISTOGRAM, (uint64_t *)ddh, sizeof (*ddh) / sizeof (uint64_t)); kmem_free(ddh, sizeof (ddt_histogram_t)); ddo = kmem_zalloc(sizeof (ddt_object_t), KM_SLEEP); ddt_get_dedup_object_stats(spa, ddo); fnvlist_add_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS, (uint64_t *)ddo, sizeof (*ddo) / sizeof (uint64_t)); kmem_free(ddo, sizeof (ddt_object_t)); dds = kmem_zalloc(sizeof (ddt_stat_t), KM_SLEEP); ddt_get_dedup_stats(spa, dds); fnvlist_add_uint64_array(config, ZPOOL_CONFIG_DDT_STATS, (uint64_t *)dds, sizeof (*dds) / sizeof (uint64_t)); kmem_free(dds, sizeof (ddt_stat_t)); } if (locked) spa_config_exit(spa, SCL_CONFIG | SCL_STATE, FTAG); return (config); }
static int lzc_ioctl(zfs_ioc_t ioc, const char *name, nvlist_t *source, nvlist_t **resultp) { zfs_cmd_t zc = { 0 }; int error = 0; char *packed; #ifdef __FreeBSD__ nvlist_t *oldsource; #endif size_t size; ASSERT3S(g_refcount, >, 0); (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); #ifdef __FreeBSD__ if (zfs_ioctl_version == ZFS_IOCVER_UNDEF) zfs_ioctl_version = get_zfs_ioctl_version(); if (zfs_ioctl_version < ZFS_IOCVER_LZC) { oldsource = source; error = lzc_compat_pre(&zc, &ioc, &source); if (error) return (error); } #endif packed = fnvlist_pack(source, &size); zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed; zc.zc_nvlist_src_size = size; if (resultp != NULL) { *resultp = NULL; zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024); zc.zc_nvlist_dst = (uint64_t)(uintptr_t) malloc(zc.zc_nvlist_dst_size); #ifdef illumos if (zc.zc_nvlist_dst == NULL) { #else if (zc.zc_nvlist_dst == 0) { #endif error = ENOMEM; goto out; } } while (ioctl(g_fd, ioc, &zc) != 0) { if (errno == ENOMEM && resultp != NULL) { free((void *)(uintptr_t)zc.zc_nvlist_dst); zc.zc_nvlist_dst_size *= 2; zc.zc_nvlist_dst = (uint64_t)(uintptr_t) malloc(zc.zc_nvlist_dst_size); #ifdef illumos if (zc.zc_nvlist_dst == NULL) { #else if (zc.zc_nvlist_dst == 0) { #endif error = ENOMEM; goto out; } } else { error = errno; break; } } #ifdef __FreeBSD__ if (zfs_ioctl_version < ZFS_IOCVER_LZC) lzc_compat_post(&zc, ioc); #endif if (zc.zc_nvlist_dst_filled) { *resultp = fnvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst, zc.zc_nvlist_dst_size); } #ifdef __FreeBSD__ if (zfs_ioctl_version < ZFS_IOCVER_LZC) lzc_compat_outnvl(&zc, ioc, resultp); #endif out: #ifdef __FreeBSD__ if (zfs_ioctl_version < ZFS_IOCVER_LZC) { if (source != oldsource) nvlist_free(source); source = oldsource; } #endif fnvlist_pack_free(packed, size); free((void *)(uintptr_t)zc.zc_nvlist_dst); return (error); } int lzc_create(const char *fsname, enum lzc_dataset_type type, nvlist_t *props) { int error; nvlist_t *args = fnvlist_alloc(); fnvlist_add_int32(args, "type", (dmu_objset_type_t)type); if (props != NULL) fnvlist_add_nvlist(args, "props", props); error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL); nvlist_free(args); return (error); }
/* * Linux adds ZFS_IOC_RECV_NEW for resumable and raw streams and preserves the * legacy ZFS_IOC_RECV user/kernel interface. The new interface supports all * stream options but is currently only used for resumable streams. This way * updated user space utilities will interoperate with older kernel modules. * * Non-Linux OpenZFS platforms have opted to modify the legacy interface. */ static int recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops, const char *origin, boolean_t force, boolean_t resumable, boolean_t raw, int input_fd, const dmu_replay_record_t *begin_record, int cleanup_fd, uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle, nvlist_t **errors) { dmu_replay_record_t drr; char fsname[MAXPATHLEN]; char *atp; int error; ASSERT3S(g_refcount, >, 0); VERIFY3S(g_fd, !=, -1); /* Set 'fsname' to the name of containing filesystem */ (void) strlcpy(fsname, snapname, sizeof (fsname)); atp = strchr(fsname, '@'); if (atp == NULL) return (EINVAL); *atp = '\0'; /* If the fs does not exist, try its parent. */ if (!lzc_exists(fsname)) { char *slashp = strrchr(fsname, '/'); if (slashp == NULL) return (ENOENT); *slashp = '\0'; } /* * The begin_record is normally a non-byteswapped BEGIN record. * For resumable streams it may be set to any non-byteswapped * dmu_replay_record_t. */ if (begin_record == NULL) { error = recv_read(input_fd, &drr, sizeof (drr)); if (error != 0) return (error); } else { drr = *begin_record; } if (resumable || raw) { nvlist_t *outnvl = NULL; nvlist_t *innvl = fnvlist_alloc(); fnvlist_add_string(innvl, "snapname", snapname); if (recvdprops != NULL) fnvlist_add_nvlist(innvl, "props", recvdprops); if (localprops != NULL) fnvlist_add_nvlist(innvl, "localprops", localprops); if (origin != NULL && strlen(origin)) fnvlist_add_string(innvl, "origin", origin); fnvlist_add_byte_array(innvl, "begin_record", (uchar_t *)&drr, sizeof (drr)); fnvlist_add_int32(innvl, "input_fd", input_fd); if (force) fnvlist_add_boolean(innvl, "force"); if (resumable) fnvlist_add_boolean(innvl, "resumable"); if (cleanup_fd >= 0) fnvlist_add_int32(innvl, "cleanup_fd", cleanup_fd); if (action_handle != NULL) fnvlist_add_uint64(innvl, "action_handle", *action_handle); error = lzc_ioctl(ZFS_IOC_RECV_NEW, fsname, innvl, &outnvl); if (error == 0 && read_bytes != NULL) error = nvlist_lookup_uint64(outnvl, "read_bytes", read_bytes); if (error == 0 && errflags != NULL) error = nvlist_lookup_uint64(outnvl, "error_flags", errflags); if (error == 0 && action_handle != NULL) error = nvlist_lookup_uint64(outnvl, "action_handle", action_handle); if (error == 0 && errors != NULL) { nvlist_t *nvl; error = nvlist_lookup_nvlist(outnvl, "errors", &nvl); if (error == 0) *errors = fnvlist_dup(nvl); } fnvlist_free(innvl); fnvlist_free(outnvl); } else { zfs_cmd_t zc = {"\0"}; char *packed = NULL; size_t size; ASSERT3S(g_refcount, >, 0); (void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_value)); (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value)); if (recvdprops != NULL) { packed = fnvlist_pack(recvdprops, &size); zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed; zc.zc_nvlist_src_size = size; } if (localprops != NULL) { packed = fnvlist_pack(localprops, &size); zc.zc_nvlist_conf = (uint64_t)(uintptr_t)packed; zc.zc_nvlist_conf_size = size; } if (origin != NULL) (void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string)); ASSERT3S(drr.drr_type, ==, DRR_BEGIN); zc.zc_begin_record = drr.drr_u.drr_begin; zc.zc_guid = force; zc.zc_cookie = input_fd; zc.zc_cleanup_fd = -1; zc.zc_action_handle = 0; if (cleanup_fd >= 0) zc.zc_cleanup_fd = cleanup_fd; if (action_handle != NULL) zc.zc_action_handle = *action_handle; zc.zc_nvlist_dst_size = 128 * 1024; zc.zc_nvlist_dst = (uint64_t)(uintptr_t) malloc(zc.zc_nvlist_dst_size); error = ioctl(g_fd, ZFS_IOC_RECV, &zc); if (error != 0) { error = errno; } else { if (read_bytes != NULL) *read_bytes = zc.zc_cookie; if (errflags != NULL) *errflags = zc.zc_obj; if (action_handle != NULL) *action_handle = zc.zc_action_handle; if (errors != NULL) VERIFY0(nvlist_unpack( (void *)(uintptr_t)zc.zc_nvlist_dst, zc.zc_nvlist_dst_size, errors, KM_SLEEP)); } if (packed != NULL) fnvlist_pack_free(packed, size); free((void *)(uintptr_t)zc.zc_nvlist_dst); } return (error); }
static void run_tests(void) { const char *key = "key"; /* Note: maximum nvlist key length is 32KB */ int len = 1024 * 31; char *bigstring = malloc(len); for (int i = 0; i < len; i++) bigstring[i] = 'a' + i % 26; bigstring[len - 1] = '\0'; nvl = fnvlist_alloc(); fnvlist_add_boolean(nvl, key); test("boolean", B_TRUE, B_FALSE); fnvlist_add_boolean_value(nvl, key, B_TRUE); test("boolean_value", B_FALSE, B_FALSE); fnvlist_add_byte(nvl, key, 1); test("byte", B_FALSE, B_FALSE); fnvlist_add_int8(nvl, key, 1); test("int8", B_FALSE, B_FALSE); fnvlist_add_uint8(nvl, key, 1); test("uint8", B_FALSE, B_FALSE); fnvlist_add_int16(nvl, key, 1); test("int16", B_FALSE, B_FALSE); fnvlist_add_uint16(nvl, key, 1); test("uint16", B_FALSE, B_FALSE); fnvlist_add_int32(nvl, key, 1); test("int32", B_FALSE, B_FALSE); fnvlist_add_uint32(nvl, key, 1); test("uint32", B_FALSE, B_FALSE); fnvlist_add_int64(nvl, key, 1); test("int64", B_TRUE, B_TRUE); fnvlist_add_uint64(nvl, key, 1); test("uint64", B_FALSE, B_FALSE); fnvlist_add_string(nvl, key, "1"); test("string", B_TRUE, B_TRUE); { nvlist_t *val = fnvlist_alloc(); fnvlist_add_string(val, "subkey", "subvalue"); fnvlist_add_nvlist(nvl, key, val); fnvlist_free(val); test("nvlist", B_TRUE, B_TRUE); } { boolean_t val[2] = { B_FALSE, B_TRUE }; fnvlist_add_boolean_array(nvl, key, val, 2); test("boolean_array", B_FALSE, B_FALSE); } { uchar_t val[2] = { 0, 1 }; fnvlist_add_byte_array(nvl, key, val, 2); test("byte_array", B_FALSE, B_FALSE); } { int8_t val[2] = { 0, 1 }; fnvlist_add_int8_array(nvl, key, val, 2); test("int8_array", B_FALSE, B_FALSE); } { uint8_t val[2] = { 0, 1 }; fnvlist_add_uint8_array(nvl, key, val, 2); test("uint8_array", B_FALSE, B_FALSE); } { int16_t val[2] = { 0, 1 }; fnvlist_add_int16_array(nvl, key, val, 2); test("int16_array", B_FALSE, B_FALSE); } { uint16_t val[2] = { 0, 1 }; fnvlist_add_uint16_array(nvl, key, val, 2); test("uint16_array", B_FALSE, B_FALSE); } { int32_t val[2] = { 0, 1 }; fnvlist_add_int32_array(nvl, key, val, 2); test("int32_array", B_FALSE, B_FALSE); } { uint32_t val[2] = { 0, 1 }; fnvlist_add_uint32_array(nvl, key, val, 2); test("uint32_array", B_FALSE, B_FALSE); } { int64_t val[2] = { 0, 1 }; fnvlist_add_int64_array(nvl, key, val, 2); test("int64_array", B_TRUE, B_FALSE); } { uint64_t val[2] = { 0, 1 }; fnvlist_add_uint64_array(nvl, key, val, 2); test("uint64_array", B_FALSE, B_FALSE); } { char *const val[2] = { "0", "1" }; fnvlist_add_string_array(nvl, key, val, 2); test("string_array", B_TRUE, B_FALSE); } { nvlist_t *val[2]; val[0] = fnvlist_alloc(); fnvlist_add_string(val[0], "subkey", "subvalue"); val[1] = fnvlist_alloc(); fnvlist_add_string(val[1], "subkey2", "subvalue2"); fnvlist_add_nvlist_array(nvl, key, val, 2); fnvlist_free(val[0]); fnvlist_free(val[1]); test("nvlist_array", B_FALSE, B_FALSE); } { fnvlist_add_string(nvl, bigstring, "1"); test("large_key", B_TRUE, B_TRUE); } { fnvlist_add_string(nvl, key, bigstring); test("large_value", B_TRUE, B_TRUE); } { for (int i = 0; i < 1024; i++) { char buf[32]; (void) snprintf(buf, sizeof (buf), "key-%u", i); fnvlist_add_int64(nvl, buf, i); } test("many_keys", B_TRUE, B_TRUE); } #ifndef __sparc__ { for (int i = 0; i < 10; i++) { nvlist_t *newval = fnvlist_alloc(); fnvlist_add_nvlist(newval, "key", nvl); fnvlist_free(nvl); nvl = newval; } test("deeply_nested_pos", B_TRUE, B_TRUE); } { for (int i = 0; i < 90; i++) { nvlist_t *newval = fnvlist_alloc(); fnvlist_add_nvlist(newval, "key", nvl); fnvlist_free(nvl); nvl = newval; } test("deeply_nested_neg", B_FALSE, B_FALSE); } #endif free(bigstring); fnvlist_free(nvl); }
/* * Synchronize pool configuration to disk. This must be called with the * namespace lock held. Synchronizing the pool cache is typically done after * the configuration has been synced to the MOS. This exposes a window where * the MOS config will have been updated but the cache file has not. If * the system were to crash at that instant then the cached config may not * contain the correct information to open the pool and an explicit import * would be required. */ void spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) { spa_config_dirent_t *dp, *tdp; nvlist_t *nvl; char *pool_name; boolean_t ccw_failure; int error = 0; ASSERT(MUTEX_HELD(&spa_namespace_lock)); if (rootdir == NULL || !(spa_mode_global & FWRITE)) return; /* * Iterate over all cachefiles for the pool, past or present. When the * cachefile is changed, the new one is pushed onto this list, allowing * us to update previous cachefiles that no longer contain this pool. */ ccw_failure = B_FALSE; for (dp = list_head(&target->spa_config_list); dp != NULL; dp = list_next(&target->spa_config_list, dp)) { spa_t *spa = NULL; if (dp->scd_path == NULL) continue; /* * Iterate over all pools, adding any matching pools to 'nvl'. */ nvl = NULL; while ((spa = spa_next(spa)) != NULL) { /* * Skip over our own pool if we're about to remove * ourselves from the spa namespace or any pool that * is readonly. Since we cannot guarantee that a * readonly pool would successfully import upon reboot, * we don't allow them to be written to the cache file. */ if ((spa == target && removing) || !spa_writeable(spa)) continue; mutex_enter(&spa->spa_props_lock); tdp = list_head(&spa->spa_config_list); if (spa->spa_config == NULL || tdp == NULL || tdp->scd_path == NULL || strcmp(tdp->scd_path, dp->scd_path) != 0) { mutex_exit(&spa->spa_props_lock); continue; } if (nvl == NULL) nvl = fnvlist_alloc(); if (spa->spa_import_flags & ZFS_IMPORT_TEMP_NAME) pool_name = fnvlist_lookup_string( spa->spa_config, ZPOOL_CONFIG_POOL_NAME); else pool_name = spa_name(spa); fnvlist_add_nvlist(nvl, pool_name, spa->spa_config); mutex_exit(&spa->spa_props_lock); } error = spa_config_write(dp, nvl); if (error != 0) ccw_failure = B_TRUE; nvlist_free(nvl); } if (ccw_failure) { /* * Keep trying so that configuration data is * written if/when any temporary filesystem * resource issues are resolved. */ if (target->spa_ccw_fail_time == 0) { zfs_ereport_post(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE, target, NULL, NULL, 0, 0); } target->spa_ccw_fail_time = gethrtime(); spa_async_request(target, SPA_ASYNC_CONFIG_UPDATE); } else { /* * Do not rate limit future attempts to update * the config cache. */ target->spa_ccw_fail_time = 0; } /* * Remove any config entries older than the current one. */ dp = list_head(&target->spa_config_list); while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) { list_remove(&target->spa_config_list, tdp); if (tdp->scd_path != NULL) spa_strfree(tdp->scd_path); kmem_free(tdp, sizeof (spa_config_dirent_t)); } spa_config_generation++; if (postsysevent) spa_event_notify(target, NULL, ESC_ZFS_CONFIG_SYNC); }
static int dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura, dsl_dataset_t *ds, nvlist_t *holds, const char *snapname) { uint64_t zapobj; nvlist_t *holds_found; objset_t *mos; int numholds; if (!dsl_dataset_is_snapshot(ds)) return (SET_ERROR(EINVAL)); if (nvlist_empty(holds)) return (0); numholds = 0; mos = ds->ds_dir->dd_pool->dp_meta_objset; zapobj = ds->ds_phys->ds_userrefs_obj; holds_found = fnvlist_alloc(); for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL; pair = nvlist_next_nvpair(holds, pair)) { uint64_t tmp; int error; const char *holdname = nvpair_name(pair); if (zapobj != 0) error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp); else error = SET_ERROR(ENOENT); /* * Non-existent holds are put on the errlist, but don't * cause an overall failure. */ if (error == ENOENT) { if (ddura->ddura_errlist != NULL) { char *errtag = kmem_asprintf("%s#%s", snapname, holdname); fnvlist_add_int32(ddura->ddura_errlist, errtag, ENOENT); strfree(errtag); } continue; } if (error != 0) { fnvlist_free(holds_found); return (error); } fnvlist_add_boolean(holds_found, holdname); numholds++; } if (DS_IS_DEFER_DESTROY(ds) && ds->ds_phys->ds_num_children == 1 && ds->ds_userrefs == numholds) { /* we need to destroy the snapshot as well */ if (dsl_dataset_long_held(ds)) { fnvlist_free(holds_found); return (SET_ERROR(EBUSY)); } fnvlist_add_boolean(ddura->ddura_todelete, snapname); } if (numholds != 0) { fnvlist_add_nvlist(ddura->ddura_chkholds, snapname, holds_found); } fnvlist_free(holds_found); return (0); }
/* * Test each ioc for the folowing ioctl input errors: * ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel * ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing * ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type */ static int lzc_ioctl_test(zfs_ioc_t ioc, const char *name, nvlist_t *required, nvlist_t *optional, int expected_error, boolean_t wildcard) { nvlist_t *input = fnvlist_alloc(); nvlist_t *future = fnvlist_alloc(); int error = 0; if (required != NULL) { for (nvpair_t *pair = nvlist_next_nvpair(required, NULL); pair != NULL; pair = nvlist_next_nvpair(required, pair)) { fnvlist_add_nvpair(input, pair); } } if (optional != NULL) { for (nvpair_t *pair = nvlist_next_nvpair(optional, NULL); pair != NULL; pair = nvlist_next_nvpair(optional, pair)) { fnvlist_add_nvpair(input, pair); } } /* * Generic input run with 'optional' nvlist pair */ if (!wildcard) fnvlist_add_nvlist(input, "optional", future); lzc_ioctl_run(ioc, name, input, expected_error); if (!wildcard) fnvlist_remove(input, "optional"); /* * Bogus input value */ if (!wildcard) { fnvlist_add_string(input, "bogus_input", "bogus"); lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_UNAVAIL); fnvlist_remove(input, "bogus_input"); } /* * Missing required inputs */ if (required != NULL) { nvlist_t *empty = fnvlist_alloc(); lzc_ioctl_run(ioc, name, empty, ZFS_ERR_IOC_ARG_REQUIRED); nvlist_free(empty); } /* * Wrong nvpair type */ if (required != NULL || optional != NULL) { /* * switch the type of one of the input pairs */ for (nvpair_t *pair = nvlist_next_nvpair(input, NULL); pair != NULL; pair = nvlist_next_nvpair(input, pair)) { char pname[MAXNAMELEN]; data_type_t ptype; strlcpy(pname, nvpair_name(pair), sizeof (pname)); pname[sizeof (pname) - 1] = '\0'; ptype = nvpair_type(pair); fnvlist_remove_nvpair(input, pair); switch (ptype) { case DATA_TYPE_STRING: fnvlist_add_uint64(input, pname, 42); break; default: fnvlist_add_string(input, pname, "bogus"); break; } } lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_BADTYPE); } nvlist_free(future); nvlist_free(input); return (error); }