1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2012 by Delphix. All rights reserved.
24 * Copyright (c) 2013 Steven Hartland. All rights reserved.
25 */
26
27/*
28 * zhack is a debugging tool that can write changes to ZFS pool using libzpool
29 * for testing purposes. Altering pools with zhack is unsupported and may
30 * result in corrupted pools.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <ctype.h>
36#include <sys/zfs_context.h>
37#include <sys/spa.h>
38#include <sys/spa_impl.h>
39#include <sys/dmu.h>
40#include <sys/zap.h>
41#include <sys/zfs_znode.h>
42#include <sys/dsl_synctask.h>
43#include <sys/vdev.h>
44#include <sys/fs/zfs.h>
45#include <sys/dmu_objset.h>
46#include <sys/dsl_pool.h>
47#include <sys/zio_checksum.h>
48#include <sys/zio_compress.h>
49#include <sys/zfeature.h>
50#include <sys/dmu_tx.h>
51#undef ZFS_MAXNAMELEN
52#undef verify
53#include <libzfs.h>
54
55extern boolean_t zfeature_checks_disable;
56
57const char cmdname[] = "zhack";
58libzfs_handle_t *g_zfs;
59static importargs_t g_importargs;
60static char *g_pool;
61static boolean_t g_readonly;
62
63static void
64usage(void)
65{
66	(void) fprintf(stderr,
67	    "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
68	    "where <subcommand> <args> is one of the following:\n"
69	    "\n", cmdname);
70
71	(void) fprintf(stderr,
72	    "    feature stat <pool>\n"
73	    "        print information about enabled features\n"
74	    "    feature enable [-d desc] <pool> <feature>\n"
75	    "        add a new enabled feature to the pool\n"
76	    "        -d <desc> sets the feature's description\n"
77	    "    feature ref [-md] <pool> <feature>\n"
78	    "        change the refcount on the given feature\n"
79	    "        -d decrease instead of increase the refcount\n"
80	    "        -m add the feature to the label if increasing refcount\n"
81	    "\n"
82	    "    <feature> : should be a feature guid\n");
83	exit(1);
84}
85
86
87static void
88fatal(const char *fmt, ...)
89{
90	va_list ap;
91
92	va_start(ap, fmt);
93	(void) fprintf(stderr, "%s: ", cmdname);
94	(void) vfprintf(stderr, fmt, ap);
95	va_end(ap);
96	(void) fprintf(stderr, "\n");
97
98	exit(1);
99}
100
101/* ARGSUSED */
102static int
103space_delta_cb(dmu_object_type_t bonustype, void *data,
104    uint64_t *userp, uint64_t *groupp)
105{
106	/*
107	 * Is it a valid type of object to track?
108	 */
109	if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
110		return (ENOENT);
111	(void) fprintf(stderr, "modifying object that needs user accounting");
112	abort();
113	/* NOTREACHED */
114}
115
116/*
117 * Target is the dataset whose pool we want to open.
118 */
119static void
120import_pool(const char *target, boolean_t readonly)
121{
122	nvlist_t *config;
123	nvlist_t *pools;
124	int error;
125	char *sepp;
126	spa_t *spa;
127	nvpair_t *elem;
128	nvlist_t *props;
129	const char *name;
130
131	kernel_init(readonly ? FREAD : (FREAD | FWRITE));
132	g_zfs = libzfs_init();
133	ASSERT(g_zfs != NULL);
134
135	dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
136
137	g_readonly = readonly;
138
139	/*
140	 * If we only want readonly access, it's OK if we find
141	 * a potentially-active (ie, imported into the kernel) pool from the
142	 * default cachefile.
143	 */
144	if (readonly && spa_open(target, &spa, FTAG) == 0) {
145		spa_close(spa, FTAG);
146		return;
147	}
148
149	g_importargs.unique = B_TRUE;
150	g_importargs.can_be_active = readonly;
151	g_pool = strdup(target);
152	if ((sepp = strpbrk(g_pool, "/@")) != NULL)
153		*sepp = '\0';
154	g_importargs.poolname = g_pool;
155	pools = zpool_search_import(g_zfs, &g_importargs);
156
157	if (nvlist_empty(pools)) {
158		if (!g_importargs.can_be_active) {
159			g_importargs.can_be_active = B_TRUE;
160			if (zpool_search_import(g_zfs, &g_importargs) != NULL ||
161			    spa_open(target, &spa, FTAG) == 0) {
162				fatal("cannot import '%s': pool is active; run "
163				    "\"zpool export %s\" first\n",
164				    g_pool, g_pool);
165			}
166		}
167
168		fatal("cannot import '%s': no such pool available\n", g_pool);
169	}
170
171	elem = nvlist_next_nvpair(pools, NULL);
172	name = nvpair_name(elem);
173	verify(nvpair_value_nvlist(elem, &config) == 0);
174
175	props = NULL;
176	if (readonly) {
177		verify(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
178		verify(nvlist_add_uint64(props,
179		    zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
180	}
181
182	zfeature_checks_disable = B_TRUE;
183	error = spa_import(name, config, props, ZFS_IMPORT_NORMAL);
184	zfeature_checks_disable = B_FALSE;
185	if (error == EEXIST)
186		error = 0;
187
188	if (error)
189		fatal("can't import '%s': %s", name, strerror(error));
190}
191
192static void
193zhack_spa_open(const char *target, boolean_t readonly, void *tag, spa_t **spa)
194{
195	int err;
196
197	import_pool(target, readonly);
198
199	zfeature_checks_disable = B_TRUE;
200	err = spa_open(target, spa, tag);
201	zfeature_checks_disable = B_FALSE;
202
203	if (err != 0)
204		fatal("cannot open '%s': %s", target, strerror(err));
205	if (spa_version(*spa) < SPA_VERSION_FEATURES) {
206		fatal("'%s' has version %d, features not enabled", target,
207		    (int)spa_version(*spa));
208	}
209}
210
211static void
212dump_obj(objset_t *os, uint64_t obj, const char *name)
213{
214	zap_cursor_t zc;
215	zap_attribute_t za;
216
217	(void) printf("%s_obj:\n", name);
218
219	for (zap_cursor_init(&zc, os, obj);
220	    zap_cursor_retrieve(&zc, &za) == 0;
221	    zap_cursor_advance(&zc)) {
222		if (za.za_integer_length == 8) {
223			ASSERT(za.za_num_integers == 1);
224			(void) printf("\t%s = %llu\n",
225			    za.za_name, (u_longlong_t)za.za_first_integer);
226		} else {
227			ASSERT(za.za_integer_length == 1);
228			char val[1024];
229			VERIFY(zap_lookup(os, obj, za.za_name,
230			    1, sizeof (val), val) == 0);
231			(void) printf("\t%s = %s\n", za.za_name, val);
232		}
233	}
234	zap_cursor_fini(&zc);
235}
236
237static void
238dump_mos(spa_t *spa)
239{
240	nvlist_t *nv = spa->spa_label_features;
241
242	(void) printf("label config:\n");
243	for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
244	    pair != NULL;
245	    pair = nvlist_next_nvpair(nv, pair)) {
246		(void) printf("\t%s\n", nvpair_name(pair));
247	}
248}
249
250static void
251zhack_do_feature_stat(int argc, char **argv)
252{
253	spa_t *spa;
254	objset_t *os;
255	char *target;
256
257	argc--;
258	argv++;
259
260	if (argc < 1) {
261		(void) fprintf(stderr, "error: missing pool name\n");
262		usage();
263	}
264	target = argv[0];
265
266	zhack_spa_open(target, B_TRUE, FTAG, &spa);
267	os = spa->spa_meta_objset;
268
269	dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
270	dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
271	dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
272	dump_mos(spa);
273
274	spa_close(spa, FTAG);
275}
276
277static void
278feature_enable_sync(void *arg, dmu_tx_t *tx)
279{
280	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
281	zfeature_info_t *feature = arg;
282
283	spa_feature_enable(spa, feature, tx);
284	spa_history_log_internal(spa, "zhack enable feature", tx,
285	    "name=%s can_readonly=%u",
286	    feature->fi_guid, feature->fi_can_readonly);
287}
288
289static void
290zhack_do_feature_enable(int argc, char **argv)
291{
292	char c;
293	char *desc, *target;
294	spa_t *spa;
295	objset_t *mos;
296	zfeature_info_t feature;
297	zfeature_info_t *nodeps[] = { NULL };
298
299	/*
300	 * Features are not added to the pool's label until their refcounts
301	 * are incremented, so fi_mos can just be left as false for now.
302	 */
303	desc = NULL;
304	feature.fi_uname = "zhack";
305	feature.fi_mos = B_FALSE;
306	feature.fi_can_readonly = B_FALSE;
307	feature.fi_depends = nodeps;
308
309	optind = 1;
310	while ((c = getopt(argc, argv, "rmd:")) != -1) {
311		switch (c) {
312		case 'r':
313			feature.fi_can_readonly = B_TRUE;
314			break;
315		case 'd':
316			desc = strdup(optarg);
317			break;
318		default:
319			usage();
320			break;
321		}
322	}
323
324	if (desc == NULL)
325		desc = strdup("zhack injected");
326	feature.fi_desc = desc;
327
328	argc -= optind;
329	argv += optind;
330
331	if (argc < 2) {
332		(void) fprintf(stderr, "error: missing feature or pool name\n");
333		usage();
334	}
335	target = argv[0];
336	feature.fi_guid = argv[1];
337
338	if (!zfeature_is_valid_guid(feature.fi_guid))
339		fatal("invalid feature guid: %s", feature.fi_guid);
340
341	zhack_spa_open(target, B_FALSE, FTAG, &spa);
342	mos = spa->spa_meta_objset;
343
344	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
345		fatal("'%s' is a real feature, will not enable");
346	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
347		fatal("feature already enabled: %s", feature.fi_guid);
348
349	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
350	    feature_enable_sync, &feature, 5));
351
352	spa_close(spa, FTAG);
353
354	free(desc);
355}
356
357static void
358feature_incr_sync(void *arg, dmu_tx_t *tx)
359{
360	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
361	zfeature_info_t *feature = arg;
362
363	spa_feature_incr(spa, feature, tx);
364	spa_history_log_internal(spa, "zhack feature incr", tx,
365	    "name=%s", feature->fi_guid);
366}
367
368static void
369feature_decr_sync(void *arg, dmu_tx_t *tx)
370{
371	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
372	zfeature_info_t *feature = arg;
373
374	spa_feature_decr(spa, feature, tx);
375	spa_history_log_internal(spa, "zhack feature decr", tx,
376	    "name=%s", feature->fi_guid);
377}
378
379static void
380zhack_do_feature_ref(int argc, char **argv)
381{
382	char c;
383	char *target;
384	boolean_t decr = B_FALSE;
385	spa_t *spa;
386	objset_t *mos;
387	zfeature_info_t feature;
388	zfeature_info_t *nodeps[] = { NULL };
389
390	/*
391	 * fi_desc does not matter here because it was written to disk
392	 * when the feature was enabled, but we need to properly set the
393	 * feature for read or write based on the information we read off
394	 * disk later.
395	 */
396	feature.fi_uname = "zhack";
397	feature.fi_mos = B_FALSE;
398	feature.fi_desc = NULL;
399	feature.fi_depends = nodeps;
400
401	optind = 1;
402	while ((c = getopt(argc, argv, "md")) != -1) {
403		switch (c) {
404		case 'm':
405			feature.fi_mos = B_TRUE;
406			break;
407		case 'd':
408			decr = B_TRUE;
409			break;
410		default:
411			usage();
412			break;
413		}
414	}
415	argc -= optind;
416	argv += optind;
417
418	if (argc < 2) {
419		(void) fprintf(stderr, "error: missing feature or pool name\n");
420		usage();
421	}
422	target = argv[0];
423	feature.fi_guid = argv[1];
424
425	if (!zfeature_is_valid_guid(feature.fi_guid))
426		fatal("invalid feature guid: %s", feature.fi_guid);
427
428	zhack_spa_open(target, B_FALSE, FTAG, &spa);
429	mos = spa->spa_meta_objset;
430
431	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
432		fatal("'%s' is a real feature, will not change refcount");
433
434	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
435	    feature.fi_guid)) {
436		feature.fi_can_readonly = B_FALSE;
437	} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
438	    feature.fi_guid)) {
439		feature.fi_can_readonly = B_TRUE;
440	} else {
441		fatal("feature is not enabled: %s", feature.fi_guid);
442	}
443
444	if (decr && !spa_feature_is_active(spa, &feature))
445		fatal("feature refcount already 0: %s", feature.fi_guid);
446
447	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
448	    decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
449
450	spa_close(spa, FTAG);
451}
452
453static int
454zhack_do_feature(int argc, char **argv)
455{
456	char *subcommand;
457
458	argc--;
459	argv++;
460	if (argc == 0) {
461		(void) fprintf(stderr,
462		    "error: no feature operation specified\n");
463		usage();
464	}
465
466	subcommand = argv[0];
467	if (strcmp(subcommand, "stat") == 0) {
468		zhack_do_feature_stat(argc, argv);
469	} else if (strcmp(subcommand, "enable") == 0) {
470		zhack_do_feature_enable(argc, argv);
471	} else if (strcmp(subcommand, "ref") == 0) {
472		zhack_do_feature_ref(argc, argv);
473	} else {
474		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
475		    subcommand);
476		usage();
477	}
478
479	return (0);
480}
481
482#define	MAX_NUM_PATHS 1024
483
484int
485main(int argc, char **argv)
486{
487	extern void zfs_prop_init(void);
488
489	char *path[MAX_NUM_PATHS];
490	const char *subcommand;
491	int rv = 0;
492	char c;
493
494	g_importargs.path = path;
495
496	dprintf_setup(&argc, argv);
497	zfs_prop_init();
498
499	while ((c = getopt(argc, argv, "c:d:")) != -1) {
500		switch (c) {
501		case 'c':
502			g_importargs.cachefile = optarg;
503			break;
504		case 'd':
505			assert(g_importargs.paths < MAX_NUM_PATHS);
506			g_importargs.path[g_importargs.paths++] = optarg;
507			break;
508		default:
509			usage();
510			break;
511		}
512	}
513
514	argc -= optind;
515	argv += optind;
516	optind = 1;
517
518	if (argc == 0) {
519		(void) fprintf(stderr, "error: no command specified\n");
520		usage();
521	}
522
523	subcommand = argv[0];
524
525	if (strcmp(subcommand, "feature") == 0) {
526		rv = zhack_do_feature(argc, argv);
527	} else {
528		(void) fprintf(stderr, "error: unknown subcommand: %s\n",
529		    subcommand);
530		usage();
531	}
532
533	if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_TRUE) != 0) {
534		fatal("pool export failed; "
535		    "changes may not be committed to disk\n");
536	}
537
538	libzfs_fini(g_zfs);
539	kernel_fini();
540
541	return (rv);
542}
543