Exemple #1
0
int
lzc_send_resume(const char *snapname, const char *from, int fd,
    enum lzc_send_flags flags, uint64_t resumeobj, uint64_t resumeoff)
{
	nvlist_t *args;
	int err;

	args = fnvlist_alloc();
	fnvlist_add_int32(args, "fd", fd);
	if (from != NULL)
		fnvlist_add_string(args, "fromsnap", from);
	if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
		fnvlist_add_boolean(args, "largeblockok");
	if (flags & LZC_SEND_FLAG_EMBED_DATA)
		fnvlist_add_boolean(args, "embedok");
	if (flags & LZC_SEND_FLAG_COMPRESS)
		fnvlist_add_boolean(args, "compressok");
	if (flags & LZC_SEND_FLAG_RAW)
		fnvlist_add_boolean(args, "rawok");
	if (resumeobj != 0 || resumeoff != 0) {
		fnvlist_add_uint64(args, "resume_object", resumeobj);
		fnvlist_add_uint64(args, "resume_offset", resumeoff);
	}
	err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
	nvlist_free(args);
	return (err);
}
Exemple #2
0
int
spa_history_log_nvl(spa_t *spa, nvlist_t *nvl)
{
    int err = 0;
    dmu_tx_t *tx;
    nvlist_t *nvarg;

    if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY || !spa_writeable(spa))
        return (SET_ERROR(EINVAL));

    tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
    err = dmu_tx_assign(tx, TXG_WAIT);
    if (err) {
        dmu_tx_abort(tx);
        return (err);
    }

    VERIFY0(nvlist_dup(nvl, &nvarg, KM_SLEEP));
    if (spa_history_zone() != NULL) {
        fnvlist_add_string(nvarg, ZPOOL_HIST_ZONE,
                           spa_history_zone());
    }
    fnvlist_add_uint64(nvarg, ZPOOL_HIST_WHO, crgetruid(CRED()));

    /* Kick this off asynchronously; errors are ignored. */
    dsl_sync_task_nowait(spa_get_dsl(spa), spa_history_log_sync,
                         nvarg, 0, ZFS_SPACE_CHECK_NONE, tx);
    dmu_tx_commit(tx);

    /* spa_history_log_sync will free nvl */
    return (err);

}
Exemple #3
0
/*
 * "from" can be NULL, a snapshot, or a bookmark.
 *
 * If from is NULL, a full (non-incremental) stream will be estimated.  This
 * is calculated very efficiently.
 *
 * If from is a snapshot, lzc_send_space uses the deadlists attached to
 * each snapshot to efficiently estimate the stream size.
 *
 * If from is a bookmark, the indirect blocks in the destination snapshot
 * are traversed, looking for blocks with a birth time since the creation TXG of
 * the snapshot this bookmark was created from.  This will result in
 * significantly more I/O and be less efficient than a send space estimation on
 * an equivalent snapshot.
 */
int
lzc_send_space(const char *snapname, const char *from,
    enum lzc_send_flags flags, uint64_t *spacep)
{
	nvlist_t *args;
	nvlist_t *result;
	int err;

	args = fnvlist_alloc();
	if (from != NULL)
		fnvlist_add_string(args, "from", from);
	if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
		fnvlist_add_boolean(args, "largeblockok");
	if (flags & LZC_SEND_FLAG_EMBED_DATA)
		fnvlist_add_boolean(args, "embedok");
	if (flags & LZC_SEND_FLAG_COMPRESS)
		fnvlist_add_boolean(args, "compressok");
	if (flags & LZC_SEND_FLAG_RAW)
		fnvlist_add_boolean(args, "rawok");
	err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result);
	nvlist_free(args);
	if (err == 0)
		*spacep = fnvlist_lookup_uint64(result, "space");
	nvlist_free(result);
	return (err);
}
Exemple #4
0
static void
test_send_new(const char *snapshot, int fd)
{
	nvlist_t *required = fnvlist_alloc();
	nvlist_t *optional = fnvlist_alloc();

	fnvlist_add_int32(required, "fd", fd);

	fnvlist_add_boolean(optional, "largeblockok");
	fnvlist_add_boolean(optional, "embedok");
	fnvlist_add_boolean(optional, "compressok");
	fnvlist_add_boolean(optional, "rawok");

	/*
	 * TODO - Resumable send is harder to set up. So we currently
	 * ignore testing for that variant.
	 */
#if 0
	fnvlist_add_string(optional, "fromsnap", from);
	fnvlist_add_uint64(optional, "resume_object", resumeobj);
	fnvlist_add_uint64(optional, "resume_offset", offset);
#endif
	IOC_INPUT_TEST(ZFS_IOC_SEND_NEW, snapshot, required, optional, 0);

	nvlist_free(optional);
	nvlist_free(required);
}
Exemple #5
0
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);
}
Exemple #6
0
int
lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
    uint64_t *usedp)
{
	nvlist_t *args;
	nvlist_t *result;
	int err;
	char fs[ZFS_MAX_DATASET_NAME_LEN];
	char *atp;

	/* determine the fs name */
	(void) strlcpy(fs, firstsnap, sizeof (fs));
	atp = strchr(fs, '@');
	if (atp == NULL)
		return (EINVAL);
	*atp = '\0';

	args = fnvlist_alloc();
	fnvlist_add_string(args, "firstsnap", firstsnap);

	err = lzc_ioctl(ZFS_IOC_SPACE_SNAPS, lastsnap, args, &result);
	nvlist_free(args);
	if (err == 0)
		*usedp = fnvlist_lookup_uint64(result, "used");
	fnvlist_free(result);

	return (err);
}
Exemple #7
0
static void
test_space_snaps(const char *snapshot)
{
	nvlist_t *required = fnvlist_alloc();
	fnvlist_add_string(required, "firstsnap", snapshot);

	IOC_INPUT_TEST(ZFS_IOC_SPACE_SNAPS, snapshot, required, NULL, 0);

	nvlist_free(required);
}
Exemple #8
0
/*
 * 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);
}
Exemple #9
0
/*
 * Write out a history event.
 */
int
spa_history_log(spa_t *spa, const char *msg)
{
	int err;
	nvlist_t *nvl = fnvlist_alloc();

	fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg);
	err = spa_history_log_nvl(spa, nvl);
	fnvlist_free(nvl);
	return (err);
}
Exemple #10
0
static void
test_rollback(const char *dataset, const char *snapshot)
{
	nvlist_t *optional = fnvlist_alloc();

	fnvlist_add_string(optional, "target", snapshot);

	IOC_INPUT_TEST(ZFS_IOC_ROLLBACK, dataset, NULL, optional, B_FALSE);

	nvlist_free(optional);
}
Exemple #11
0
static void
test_bookmark(const char *pool, const char *snapshot, const char *bookmark)
{
	nvlist_t *required = fnvlist_alloc();

	fnvlist_add_string(required, bookmark, snapshot);

	IOC_INPUT_TEST_WILD(ZFS_IOC_BOOKMARK, pool, required, NULL, 0);

	nvlist_free(required);
}
Exemple #12
0
static void
test_log_history(const char *pool)
{
	nvlist_t *required = fnvlist_alloc();

	fnvlist_add_string(required, "message", "input check");

	IOC_INPUT_TEST(ZFS_IOC_LOG_HISTORY, pool, required, NULL, 0);

	nvlist_free(required);
}
Exemple #13
0
/*
 * Post a history sysevent.
 *
 * The nvlist_t* passed into this function will be transformed into a new
 * nvlist where:
 *
 * 1. Nested nvlists will be flattened to a single level
 * 2. Keys will have their names normalized (to remove any problematic
 * characters, such as whitespace)
 *
 * The nvlist_t passed into this function will duplicated and should be freed
 * by caller.
 *
 */
static void
spa_history_log_notify(spa_t *spa, nvlist_t *nvl)
{
	nvlist_t *hist_nvl = fnvlist_alloc();
	uint64_t uint64;
	char *string;

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_CMD, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_CMD, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_NAME, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_ZONE, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_ZONE, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_HOST, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_HOST, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_DSNAME, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_DSNAME, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_STR, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_STR, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_IOCTL, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_IOCTL, string);

	if (nvlist_lookup_string(nvl, ZPOOL_HIST_INT_NAME, &string) == 0)
		fnvlist_add_string(hist_nvl, ZFS_EV_HIST_INT_NAME, string);

	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_DSID, &uint64) == 0)
		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_DSID, uint64);

	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_TXG, &uint64) == 0)
		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_TXG, uint64);

	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_TIME, &uint64) == 0)
		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_TIME, uint64);

	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_WHO, &uint64) == 0)
		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_WHO, uint64);

	if (nvlist_lookup_uint64(nvl, ZPOOL_HIST_INT_EVENT, &uint64) == 0)
		fnvlist_add_uint64(hist_nvl, ZFS_EV_HIST_INT_EVENT, uint64);

	spa_event_notify(spa, NULL, hist_nvl, ESC_ZFS_HISTORY_EVENT);

	nvlist_free(hist_nvl);
}
Exemple #14
0
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);
}
Exemple #15
0
/*
 * Write out a history event.
 */
int
spa_history_log(spa_t *spa, const char *msg)
{
	int err;
	nvlist_t *nvl;

	VERIFY0(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_PUSHPAGE));

	fnvlist_add_string(nvl, ZPOOL_HIST_CMD, msg);
	err = spa_history_log_nvl(spa, nvl);
	fnvlist_free(nvl);
	return (err);
}
Exemple #16
0
/*
 * The nvlist will be consumed by this call.
 */
static void
log_internal(nvlist_t *nvl, const char *operation, spa_t *spa,
    dmu_tx_t *tx, const char *fmt, va_list adx)
{
	char *msg;
	va_list adx1;
	int size;

	/*
	 * If this is part of creating a pool, not everything is
	 * initialized yet, so don't bother logging the internal events.
	 * Likewise if the pool is not writeable.
	 */
	if (tx->tx_txg == TXG_INITIAL || !spa_writeable(spa)) {
		fnvlist_free(nvl);
		return;
	}

	va_copy(adx1, adx);
	size = vsnprintf(NULL, 0, fmt, adx1) + 1;
	msg = kmem_alloc(size, KM_PUSHPAGE);
	va_end(adx1);
	va_copy(adx1, adx);
	(void) vsprintf(msg, fmt, adx1);
	va_end(adx1);
	fnvlist_add_string(nvl, ZPOOL_HIST_INT_STR, msg);
	kmem_free(msg, size);

	fnvlist_add_string(nvl, ZPOOL_HIST_INT_NAME, operation);
	fnvlist_add_uint64(nvl, ZPOOL_HIST_TXG, tx->tx_txg);

	if (dmu_tx_is_syncing(tx)) {
		spa_history_log_sync(nvl, tx);
	} else {
		dsl_sync_task_nowait(spa_get_dsl(spa),
		    spa_history_log_sync, nvl, 0, tx);
	}
	/* spa_history_log_sync() will free nvl */
}
Exemple #17
0
static int
dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
{
	dsl_dataset_user_hold_arg_t *dduha = arg;
	dsl_pool_t *dp = dmu_tx_pool(tx);
	nvpair_t *pair;

	if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
		return (SET_ERROR(ENOTSUP));

	if (!dmu_tx_is_syncing(tx))
		return (0);

	for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
	    pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
		dsl_dataset_t *ds;
		int error = 0;
		char *htag, *name;

		/* must be a snapshot */
		name = nvpair_name(pair);
		if (strchr(name, '@') == NULL)
			error = SET_ERROR(EINVAL);

		if (error == 0)
			error = nvpair_value_string(pair, &htag);

		if (error == 0)
			error = dsl_dataset_hold(dp, name, FTAG, &ds);

		if (error == 0) {
			error = dsl_dataset_user_hold_check_one(ds, htag,
			    dduha->dduha_minor != 0, tx);
			dsl_dataset_rele(ds, FTAG);
		}

		if (error == 0) {
			fnvlist_add_string(dduha->dduha_chkholds, name, htag);
		} else {
			/*
			 * We register ENOENT errors so they can be correctly
			 * reported if needed, such as when all holds fail.
			 */
			fnvlist_add_int32(dduha->dduha_errlist, name, error);
			if (error != ENOENT)
				return (error);
		}
	}

	return (0);
}
Exemple #18
0
/*
 * If fromsnap is NULL, a full (non-incremental) stream will be sent.
 */
int
lzc_send(const char *snapname, const char *fromsnap, int fd)
{
	nvlist_t *args;
	int err;

	args = fnvlist_alloc();
	fnvlist_add_int32(args, "fd", fd);
	if (fromsnap != NULL)
		fnvlist_add_string(args, "fromsnap", fromsnap);
	err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
	nvlist_free(args);
	return (err);
}
Exemple #19
0
/*
 * Roll back this filesystem or volume to the specified snapshot,
 * if possible.
 *
 * Return 0 on success or an errno on failure.
 */
int
lzc_rollback_to(const char *fsname, const char *snapname)
{
	nvlist_t *args;
	nvlist_t *result;
	int err;

	args = fnvlist_alloc();
	fnvlist_add_string(args, "target", snapname);
	err = lzc_ioctl(ZFS_IOC_ROLLBACK, fsname, args, &result);
	nvlist_free(args);
	nvlist_free(result);
	return (err);
}
Exemple #20
0
static void
test_send_space(const char *snapshot1, const char *snapshot2)
{
	nvlist_t *optional = fnvlist_alloc();

	fnvlist_add_string(optional, "from", snapshot1);
	fnvlist_add_boolean(optional, "largeblockok");
	fnvlist_add_boolean(optional, "embedok");
	fnvlist_add_boolean(optional, "compressok");
	fnvlist_add_boolean(optional, "rawok");

	IOC_INPUT_TEST(ZFS_IOC_SEND_SPACE, snapshot2, NULL, optional, 0);

	nvlist_free(optional);
}
Exemple #21
0
static void
test_clone(const char *snapshot, const char *clone)
{
	nvlist_t *required = fnvlist_alloc();
	nvlist_t *optional = fnvlist_alloc();
	nvlist_t *props = fnvlist_alloc();

	fnvlist_add_string(required, "origin", snapshot);

	IOC_INPUT_TEST(ZFS_IOC_CLONE, clone, required, NULL, 0);

	nvlist_free(props);
	nvlist_free(optional);
	nvlist_free(required);
}
Exemple #22
0
/*
 * 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);
}
Exemple #23
0
/*
 * "from" can be NULL, a snapshot, or a bookmark.
 *
 * If from is NULL, a full (non-incremental) stream will be estimated.  This
 * is calculated very efficiently.
 *
 * If from is a snapshot, lzc_send_space uses the deadlists attached to
 * each snapshot to efficiently estimate the stream size.
 *
 * If from is a bookmark, the indirect blocks in the destination snapshot
 * are traversed, looking for blocks with a birth time since the creation TXG of
 * the snapshot this bookmark was created from.  This will result in
 * significantly more I/O and be less efficient than a send space estimation on
 * an equivalent snapshot.
 */
int
lzc_send_space(const char *snapname, const char *from, uint64_t *spacep)
{
	nvlist_t *args;
	nvlist_t *result;
	int err;

	args = fnvlist_alloc();
	if (from != NULL)
		fnvlist_add_string(args, "from", from);
	err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result);
	nvlist_free(args);
	if (err == 0)
		*spacep = fnvlist_lookup_uint64(result, "space");
	nvlist_free(result);
	return (err);
}
Exemple #24
0
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);
}
Exemple #25
0
void
spa_history_log_internal_ds(dsl_dataset_t *ds, const char *operation,
    dmu_tx_t *tx, const char *fmt, ...)
{
	va_list adx;
	char namebuf[MAXNAMELEN];
	nvlist_t *nvl = fnvlist_alloc();

	ASSERT(tx != NULL);

	dsl_dataset_name(ds, namebuf);
	fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
	fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID, ds->ds_object);

	va_start(adx, fmt);
	log_internal(nvl, operation, dsl_dataset_get_spa(ds), tx, fmt, adx);
	va_end(adx);
}
Exemple #26
0
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);
}
Exemple #27
0
/*
 * Generate a zfs send stream for the specified snapshot and write it to
 * the specified file descriptor.
 *
 * "snapname" is the full name of the snapshot to send (e.g. "pool/fs@snap")
 *
 * If "from" is NULL, a full (non-incremental) stream will be sent.
 * If "from" is non-NULL, it must be the full name of a snapshot or
 * bookmark to send an incremental from (e.g. "pool/fs@earlier_snap" or
 * "pool/fs#earlier_bmark").  If non-NULL, the specified snapshot or
 * bookmark must represent an earlier point in the history of "snapname").
 * It can be an earlier snapshot in the same filesystem or zvol as "snapname",
 * or it can be the origin of "snapname"'s filesystem, or an earlier
 * snapshot in the origin, etc.
 *
 * "fd" is the file descriptor to write the send stream to.
 *
 * If "flags" contains LZC_SEND_FLAG_LARGE_BLOCK, the stream is permitted
 * to contain DRR_WRITE records with drr_length > 128K, and DRR_OBJECT
 * records with drr_blksz > 128K.
 *
 * If "flags" contains LZC_SEND_FLAG_EMBED_DATA, the stream is permitted
 * to contain DRR_WRITE_EMBEDDED records with drr_etype==BP_EMBEDDED_TYPE_DATA,
 * which the receiving system must support (as indicated by support
 * for the "embedded_data" feature).
 */
int
lzc_send(const char *snapname, const char *from, int fd,
    enum lzc_send_flags flags)
{
	nvlist_t *args;
	int err;

	args = fnvlist_alloc();
	fnvlist_add_int32(args, "fd", fd);
	if (from != NULL)
		fnvlist_add_string(args, "fromsnap", from);
	if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
		fnvlist_add_boolean(args, "largeblockok");
	if (flags & LZC_SEND_FLAG_EMBED_DATA)
		fnvlist_add_boolean(args, "embedok");
	err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
	nvlist_free(args);
	return (err);
}
Exemple #28
0
void
spa_history_log_internal_dd(dsl_dir_t *dd, const char *operation,
    dmu_tx_t *tx, const char *fmt, ...)
{
	va_list adx;
	char namebuf[MAXNAMELEN];
	nvlist_t *nvl = fnvlist_alloc();

	ASSERT(tx != NULL);

	dsl_dir_name(dd, namebuf);
	fnvlist_add_string(nvl, ZPOOL_HIST_DSNAME, namebuf);
	fnvlist_add_uint64(nvl, ZPOOL_HIST_DSID,
	    dd->dd_phys->dd_head_dataset_obj);

	va_start(adx, fmt);
	log_internal(nvl, operation, dd->dd_pool->dp_spa, tx, fmt, adx);
	va_end(adx);
}
Exemple #29
0
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);
}
Exemple #30
0
/*
 * Convert our list of pools into the definitive set of configurations.  We
 * start by picking the best config for each toplevel vdev.  Once that's done,
 * we assemble the toplevel vdevs into a full config for the pool.  We make a
 * pass to fix up any incorrect paths, and then add it to the main list to
 * return to the user.
 */
static nvlist_t *
get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok,
    nvlist_t *policy)
{
	pool_entry_t *pe;
	vdev_entry_t *ve;
	config_entry_t *ce;
	nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot;
	nvlist_t **spares, **l2cache;
	uint_t i, nspares, nl2cache;
	boolean_t config_seen;
	uint64_t best_txg;
	char *name, *hostname = NULL;
	uint64_t guid;
	uint_t children = 0;
	nvlist_t **child = NULL;
	uint_t holes;
	uint64_t *hole_array, max_id;
	uint_t c;
	boolean_t isactive;
	uint64_t hostid;
	nvlist_t *nvl;
	boolean_t valid_top_config = B_FALSE;

	if (nvlist_alloc(&ret, 0, 0) != 0)
		goto nomem;

	for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
		uint64_t id, max_txg = 0;

		if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0)
			goto nomem;
		config_seen = B_FALSE;

		/*
		 * Iterate over all toplevel vdevs.  Grab the pool configuration
		 * from the first one we find, and then go through the rest and
		 * add them as necessary to the 'vdevs' member of the config.
		 */
		for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {

			/*
			 * Determine the best configuration for this vdev by
			 * selecting the config with the latest transaction
			 * group.
			 */
			best_txg = 0;
			for (ce = ve->ve_configs; ce != NULL;
			    ce = ce->ce_next) {

				if (ce->ce_txg > best_txg) {
					tmp = ce->ce_config;
					best_txg = ce->ce_txg;
				}
			}

			/*
			 * We rely on the fact that the max txg for the
			 * pool will contain the most up-to-date information
			 * about the valid top-levels in the vdev namespace.
			 */
			if (best_txg > max_txg) {
				(void) nvlist_remove(config,
				    ZPOOL_CONFIG_VDEV_CHILDREN,
				    DATA_TYPE_UINT64);
				(void) nvlist_remove(config,
				    ZPOOL_CONFIG_HOLE_ARRAY,
				    DATA_TYPE_UINT64_ARRAY);

				max_txg = best_txg;
				hole_array = NULL;
				holes = 0;
				max_id = 0;
				valid_top_config = B_FALSE;

				if (nvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) {
					verify(nvlist_add_uint64(config,
					    ZPOOL_CONFIG_VDEV_CHILDREN,
					    max_id) == 0);
					valid_top_config = B_TRUE;
				}

				if (nvlist_lookup_uint64_array(tmp,
				    ZPOOL_CONFIG_HOLE_ARRAY, &hole_array,
				    &holes) == 0) {
					verify(nvlist_add_uint64_array(config,
					    ZPOOL_CONFIG_HOLE_ARRAY,
					    hole_array, holes) == 0);
				}
			}

			if (!config_seen) {
				/*
				 * Copy the relevant pieces of data to the pool
				 * configuration:
				 *
				 *	version
				 *	pool guid
				 *	name
				 *	pool txg (if available)
				 *	comment (if available)
				 *	pool state
				 *	hostid (if available)
				 *	hostname (if available)
				 */
				uint64_t state, version, pool_txg;
				char *comment = NULL;

				version = fnvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_VERSION);
				fnvlist_add_uint64(config,
				    ZPOOL_CONFIG_VERSION, version);
				guid = fnvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_POOL_GUID);
				fnvlist_add_uint64(config,
				    ZPOOL_CONFIG_POOL_GUID, guid);
				name = fnvlist_lookup_string(tmp,
				    ZPOOL_CONFIG_POOL_NAME);
				fnvlist_add_string(config,
				    ZPOOL_CONFIG_POOL_NAME, name);
				if (nvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_POOL_TXG, &pool_txg) == 0)
					fnvlist_add_uint64(config,
					    ZPOOL_CONFIG_POOL_TXG, pool_txg);

				if (nvlist_lookup_string(tmp,
				    ZPOOL_CONFIG_COMMENT, &comment) == 0)
					fnvlist_add_string(config,
					    ZPOOL_CONFIG_COMMENT, comment);

				state = fnvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_POOL_STATE);
				fnvlist_add_uint64(config,
				    ZPOOL_CONFIG_POOL_STATE, state);

				hostid = 0;
				if (nvlist_lookup_uint64(tmp,
				    ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
					fnvlist_add_uint64(config,
					    ZPOOL_CONFIG_HOSTID, hostid);
					hostname = fnvlist_lookup_string(tmp,
					    ZPOOL_CONFIG_HOSTNAME);
					fnvlist_add_string(config,
					    ZPOOL_CONFIG_HOSTNAME, hostname);
				}

				config_seen = B_TRUE;
			}

			/*
			 * Add this top-level vdev to the child array.
			 */
			verify(nvlist_lookup_nvlist(tmp,
			    ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
			verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
			    &id) == 0);

			if (id >= children) {
				nvlist_t **newchild;

				newchild = zfs_alloc(hdl, (id + 1) *
				    sizeof (nvlist_t *));
				if (newchild == NULL)
					goto nomem;

				for (c = 0; c < children; c++)
					newchild[c] = child[c];

				free(child);
				child = newchild;
				children = id + 1;
			}
			if (nvlist_dup(nvtop, &child[id], 0) != 0)
				goto nomem;

		}

		/*
		 * If we have information about all the top-levels then
		 * clean up the nvlist which we've constructed. This
		 * means removing any extraneous devices that are
		 * beyond the valid range or adding devices to the end
		 * of our array which appear to be missing.
		 */
		if (valid_top_config) {
			if (max_id < children) {
				for (c = max_id; c < children; c++)
					nvlist_free(child[c]);
				children = max_id;
			} else if (max_id > children) {
				nvlist_t **newchild;

				newchild = zfs_alloc(hdl, (max_id) *
				    sizeof (nvlist_t *));
				if (newchild == NULL)
					goto nomem;

				for (c = 0; c < children; c++)
					newchild[c] = child[c];

				free(child);
				child = newchild;
				children = max_id;
			}
		}

		verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
		    &guid) == 0);

		/*
		 * The vdev namespace may contain holes as a result of
		 * device removal. We must add them back into the vdev
		 * tree before we process any missing devices.
		 */
		if (holes > 0) {
			ASSERT(valid_top_config);

			for (c = 0; c < children; c++) {
				nvlist_t *holey;

				if (child[c] != NULL ||
				    !vdev_is_hole(hole_array, holes, c))
					continue;

				if (nvlist_alloc(&holey, NV_UNIQUE_NAME,
				    0) != 0)
					goto nomem;

				/*
				 * Holes in the namespace are treated as
				 * "hole" top-level vdevs and have a
				 * special flag set on them.
				 */
				if (nvlist_add_string(holey,
				    ZPOOL_CONFIG_TYPE,
				    VDEV_TYPE_HOLE) != 0 ||
				    nvlist_add_uint64(holey,
				    ZPOOL_CONFIG_ID, c) != 0 ||
				    nvlist_add_uint64(holey,
				    ZPOOL_CONFIG_GUID, 0ULL) != 0) {
					nvlist_free(holey);
					goto nomem;
				}
				child[c] = holey;
			}
		}

		/*
		 * Look for any missing top-level vdevs.  If this is the case,
		 * create a faked up 'missing' vdev as a placeholder.  We cannot
		 * simply compress the child array, because the kernel performs
		 * certain checks to make sure the vdev IDs match their location
		 * in the configuration.
		 */
		for (c = 0; c < children; c++) {
			if (child[c] == NULL) {
				nvlist_t *missing;
				if (nvlist_alloc(&missing, NV_UNIQUE_NAME,
				    0) != 0)
					goto nomem;
				if (nvlist_add_string(missing,
				    ZPOOL_CONFIG_TYPE,
				    VDEV_TYPE_MISSING) != 0 ||
				    nvlist_add_uint64(missing,
				    ZPOOL_CONFIG_ID, c) != 0 ||
				    nvlist_add_uint64(missing,
				    ZPOOL_CONFIG_GUID, 0ULL) != 0) {
					nvlist_free(missing);
					goto nomem;
				}
				child[c] = missing;
			}
		}

		/*
		 * Put all of this pool's top-level vdevs into a root vdev.
		 */
		if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0)
			goto nomem;
		if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
		    VDEV_TYPE_ROOT) != 0 ||
		    nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 ||
		    nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 ||
		    nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
		    child, children) != 0) {
			nvlist_free(nvroot);
			goto nomem;
		}

		for (c = 0; c < children; c++)
			nvlist_free(child[c]);
		free(child);
		children = 0;
		child = NULL;

		/*
		 * Go through and fix up any paths and/or devids based on our
		 * known list of vdev GUID -> path mappings.
		 */
		if (fix_paths(nvroot, pl->names) != 0) {
			nvlist_free(nvroot);
			goto nomem;
		}

		/*
		 * Add the root vdev to this pool's configuration.
		 */
		if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
		    nvroot) != 0) {
			nvlist_free(nvroot);
			goto nomem;
		}
		nvlist_free(nvroot);

		/*
		 * zdb uses this path to report on active pools that were
		 * imported or created using -R.
		 */
		if (active_ok)
			goto add_pool;

		/*
		 * Determine if this pool is currently active, in which case we
		 * can't actually import it.
		 */
		verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
		    &name) == 0);
		verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
		    &guid) == 0);

		if (pool_active(hdl, name, guid, &isactive) != 0)
			goto error;

		if (isactive) {
			nvlist_free(config);
			config = NULL;
			continue;
		}

		if (policy != NULL) {
			if (nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY,
			    policy) != 0)
				goto nomem;
		}

		if ((nvl = refresh_config(hdl, config)) == NULL) {
			nvlist_free(config);
			config = NULL;
			continue;
		}

		nvlist_free(config);
		config = nvl;

		/*
		 * Go through and update the paths for spares, now that we have
		 * them.
		 */
		verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
		    &nvroot) == 0);
		if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
		    &spares, &nspares) == 0) {
			for (i = 0; i < nspares; i++) {
				if (fix_paths(spares[i], pl->names) != 0)
					goto nomem;
			}
		}

		/*
		 * Update the paths for l2cache devices.
		 */
		if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
		    &l2cache, &nl2cache) == 0) {
			for (i = 0; i < nl2cache; i++) {
				if (fix_paths(l2cache[i], pl->names) != 0)
					goto nomem;
			}
		}

		/*
		 * Restore the original information read from the actual label.
		 */
		(void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID,
		    DATA_TYPE_UINT64);
		(void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME,
		    DATA_TYPE_STRING);
		if (hostid != 0) {
			verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
			    hostid) == 0);
			verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME,
			    hostname) == 0);
		}

add_pool:
		/*
		 * Add this pool to the list of configs.
		 */
		verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
		    &name) == 0);
		if (nvlist_add_nvlist(ret, name, config) != 0)
			goto nomem;

		nvlist_free(config);
		config = NULL;
	}

	return (ret);

nomem:
	(void) no_memory(hdl);
error:
	nvlist_free(config);
	nvlist_free(ret);
	for (c = 0; c < children; c++)
		nvlist_free(child[c]);
	free(child);

	return (NULL);
}