1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2716fd348SMartin Matuska /*
3716fd348SMartin Matuska * CDDL HEADER START
4716fd348SMartin Matuska *
5716fd348SMartin Matuska * The contents of this file are subject to the terms of the
6716fd348SMartin Matuska * Common Development and Distribution License (the "License").
7716fd348SMartin Matuska * You may not use this file except in compliance with the License.
8716fd348SMartin Matuska *
9716fd348SMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0.
11716fd348SMartin Matuska * See the License for the specific language governing permissions
12716fd348SMartin Matuska * and limitations under the License.
13716fd348SMartin Matuska *
14716fd348SMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each
15716fd348SMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16716fd348SMartin Matuska * If applicable, add the following below this CDDL HEADER, with the
17716fd348SMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying
18716fd348SMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner]
19716fd348SMartin Matuska *
20716fd348SMartin Matuska * CDDL HEADER END
21716fd348SMartin Matuska */
22716fd348SMartin Matuska
23716fd348SMartin Matuska /*
24716fd348SMartin Matuska * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
25716fd348SMartin Matuska * Copyright (c) 2013 Steven Hartland. All rights reserved.
26716fd348SMartin Matuska */
27716fd348SMartin Matuska
28716fd348SMartin Matuska /*
29716fd348SMartin Matuska * zhack is a debugging tool that can write changes to ZFS pool using libzpool
30716fd348SMartin Matuska * for testing purposes. Altering pools with zhack is unsupported and may
31716fd348SMartin Matuska * result in corrupted pools.
32716fd348SMartin Matuska */
33716fd348SMartin Matuska
3415f0b8c3SMartin Matuska #include <zfs_prop.h>
35716fd348SMartin Matuska #include <stdio.h>
36716fd348SMartin Matuska #include <stdlib.h>
37716fd348SMartin Matuska #include <ctype.h>
38716fd348SMartin Matuska #include <sys/stat.h>
39716fd348SMartin Matuska #include <sys/zfs_context.h>
40716fd348SMartin Matuska #include <sys/spa.h>
41716fd348SMartin Matuska #include <sys/spa_impl.h>
42716fd348SMartin Matuska #include <sys/dmu.h>
43716fd348SMartin Matuska #include <sys/zap.h>
44716fd348SMartin Matuska #include <sys/zfs_znode.h>
45716fd348SMartin Matuska #include <sys/dsl_synctask.h>
46716fd348SMartin Matuska #include <sys/vdev.h>
47716fd348SMartin Matuska #include <sys/vdev_impl.h>
48716fd348SMartin Matuska #include <sys/fs/zfs.h>
49716fd348SMartin Matuska #include <sys/dmu_objset.h>
50716fd348SMartin Matuska #include <sys/dsl_pool.h>
51716fd348SMartin Matuska #include <sys/zio_checksum.h>
52716fd348SMartin Matuska #include <sys/zio_compress.h>
53716fd348SMartin Matuska #include <sys/zfeature.h>
54716fd348SMartin Matuska #include <sys/dmu_tx.h>
55716fd348SMartin Matuska #include <zfeature_common.h>
56716fd348SMartin Matuska #include <libzutil.h>
57716fd348SMartin Matuska
58716fd348SMartin Matuska static importargs_t g_importargs;
59716fd348SMartin Matuska static char *g_pool;
60716fd348SMartin Matuska static boolean_t g_readonly;
61716fd348SMartin Matuska
62e639e0d2SMartin Matuska typedef enum {
63e639e0d2SMartin Matuska ZHACK_REPAIR_OP_UNKNOWN = 0,
64e639e0d2SMartin Matuska ZHACK_REPAIR_OP_CKSUM = (1 << 0),
65e639e0d2SMartin Matuska ZHACK_REPAIR_OP_UNDETACH = (1 << 1)
66e639e0d2SMartin Matuska } zhack_repair_op_t;
67e639e0d2SMartin Matuska
68716fd348SMartin Matuska static __attribute__((noreturn)) void
usage(void)69716fd348SMartin Matuska usage(void)
70716fd348SMartin Matuska {
71716fd348SMartin Matuska (void) fprintf(stderr,
72716fd348SMartin Matuska "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n"
73716fd348SMartin Matuska "where <subcommand> <args> is one of the following:\n"
74716fd348SMartin Matuska "\n");
75716fd348SMartin Matuska
76716fd348SMartin Matuska (void) fprintf(stderr,
77716fd348SMartin Matuska " feature stat <pool>\n"
78716fd348SMartin Matuska " print information about enabled features\n"
79716fd348SMartin Matuska " feature enable [-r] [-d desc] <pool> <feature>\n"
80716fd348SMartin Matuska " add a new enabled feature to the pool\n"
81716fd348SMartin Matuska " -d <desc> sets the feature's description\n"
82716fd348SMartin Matuska " -r set read-only compatible flag for feature\n"
83716fd348SMartin Matuska " feature ref [-md] <pool> <feature>\n"
84716fd348SMartin Matuska " change the refcount on the given feature\n"
85716fd348SMartin Matuska " -d decrease instead of increase the refcount\n"
86716fd348SMartin Matuska " -m add the feature to the label if increasing refcount\n"
87716fd348SMartin Matuska "\n"
88716fd348SMartin Matuska " <feature> : should be a feature guid\n"
89716fd348SMartin Matuska "\n"
90716fd348SMartin Matuska " label repair <device>\n"
91e639e0d2SMartin Matuska " repair labels of a specified device according to options\n"
92e639e0d2SMartin Matuska " which may be combined to do their functions in one call\n"
93e639e0d2SMartin Matuska " -c repair corrupted label checksums\n"
94e639e0d2SMartin Matuska " -u restore the label on a detached device\n"
95716fd348SMartin Matuska "\n"
96716fd348SMartin Matuska " <device> : path to vdev\n");
97716fd348SMartin Matuska exit(1);
98716fd348SMartin Matuska }
99716fd348SMartin Matuska
100716fd348SMartin Matuska
101716fd348SMartin Matuska static __attribute__((format(printf, 3, 4))) __attribute__((noreturn)) void
fatal(spa_t * spa,const void * tag,const char * fmt,...)102a0b956f5SMartin Matuska fatal(spa_t *spa, const void *tag, const char *fmt, ...)
103716fd348SMartin Matuska {
104716fd348SMartin Matuska va_list ap;
105716fd348SMartin Matuska
106716fd348SMartin Matuska if (spa != NULL) {
107716fd348SMartin Matuska spa_close(spa, tag);
108716fd348SMartin Matuska (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
109716fd348SMartin Matuska }
110716fd348SMartin Matuska
111716fd348SMartin Matuska va_start(ap, fmt);
112716fd348SMartin Matuska (void) fputs("zhack: ", stderr);
113716fd348SMartin Matuska (void) vfprintf(stderr, fmt, ap);
114716fd348SMartin Matuska va_end(ap);
115716fd348SMartin Matuska (void) fputc('\n', stderr);
116716fd348SMartin Matuska
117716fd348SMartin Matuska exit(1);
118716fd348SMartin Matuska }
119716fd348SMartin Matuska
120716fd348SMartin Matuska static int
space_delta_cb(dmu_object_type_t bonustype,const void * data,zfs_file_info_t * zoi)121716fd348SMartin Matuska space_delta_cb(dmu_object_type_t bonustype, const void *data,
122716fd348SMartin Matuska zfs_file_info_t *zoi)
123716fd348SMartin Matuska {
124716fd348SMartin Matuska (void) data, (void) zoi;
125716fd348SMartin Matuska
126716fd348SMartin Matuska /*
127716fd348SMartin Matuska * Is it a valid type of object to track?
128716fd348SMartin Matuska */
129716fd348SMartin Matuska if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
130716fd348SMartin Matuska return (ENOENT);
131716fd348SMartin Matuska (void) fprintf(stderr, "modifying object that needs user accounting");
132716fd348SMartin Matuska abort();
133716fd348SMartin Matuska }
134716fd348SMartin Matuska
135716fd348SMartin Matuska /*
136716fd348SMartin Matuska * Target is the dataset whose pool we want to open.
137716fd348SMartin Matuska */
138716fd348SMartin Matuska static void
zhack_import(char * target,boolean_t readonly)139716fd348SMartin Matuska zhack_import(char *target, boolean_t readonly)
140716fd348SMartin Matuska {
141716fd348SMartin Matuska nvlist_t *config;
142716fd348SMartin Matuska nvlist_t *props;
143716fd348SMartin Matuska int error;
144716fd348SMartin Matuska
145716fd348SMartin Matuska kernel_init(readonly ? SPA_MODE_READ :
146716fd348SMartin Matuska (SPA_MODE_READ | SPA_MODE_WRITE));
147716fd348SMartin Matuska
148716fd348SMartin Matuska dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
149716fd348SMartin Matuska
150716fd348SMartin Matuska g_readonly = readonly;
151716fd348SMartin Matuska g_importargs.can_be_active = readonly;
152716fd348SMartin Matuska g_pool = strdup(target);
153716fd348SMartin Matuska
154dbd5678dSMartin Matuska libpc_handle_t lpch = {
155dbd5678dSMartin Matuska .lpc_lib_handle = NULL,
156dbd5678dSMartin Matuska .lpc_ops = &libzpool_config_ops,
157dbd5678dSMartin Matuska .lpc_printerr = B_TRUE
158dbd5678dSMartin Matuska };
159dbd5678dSMartin Matuska error = zpool_find_config(&lpch, target, &config, &g_importargs);
160716fd348SMartin Matuska if (error)
161716fd348SMartin Matuska fatal(NULL, FTAG, "cannot import '%s'", target);
162716fd348SMartin Matuska
163716fd348SMartin Matuska props = NULL;
164716fd348SMartin Matuska if (readonly) {
165716fd348SMartin Matuska VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
166716fd348SMartin Matuska VERIFY(nvlist_add_uint64(props,
167716fd348SMartin Matuska zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
168716fd348SMartin Matuska }
169716fd348SMartin Matuska
170716fd348SMartin Matuska zfeature_checks_disable = B_TRUE;
171716fd348SMartin Matuska error = spa_import(target, config, props,
172716fd348SMartin Matuska (readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
173716fd348SMartin Matuska fnvlist_free(config);
174716fd348SMartin Matuska zfeature_checks_disable = B_FALSE;
175716fd348SMartin Matuska if (error == EEXIST)
176716fd348SMartin Matuska error = 0;
177716fd348SMartin Matuska
178716fd348SMartin Matuska if (error)
179716fd348SMartin Matuska fatal(NULL, FTAG, "can't import '%s': %s", target,
180716fd348SMartin Matuska strerror(error));
181716fd348SMartin Matuska }
182716fd348SMartin Matuska
183716fd348SMartin Matuska static void
zhack_spa_open(char * target,boolean_t readonly,const void * tag,spa_t ** spa)184a0b956f5SMartin Matuska zhack_spa_open(char *target, boolean_t readonly, const void *tag, spa_t **spa)
185716fd348SMartin Matuska {
186716fd348SMartin Matuska int err;
187716fd348SMartin Matuska
188716fd348SMartin Matuska zhack_import(target, readonly);
189716fd348SMartin Matuska
190716fd348SMartin Matuska zfeature_checks_disable = B_TRUE;
191716fd348SMartin Matuska err = spa_open(target, spa, tag);
192716fd348SMartin Matuska zfeature_checks_disable = B_FALSE;
193716fd348SMartin Matuska
194716fd348SMartin Matuska if (err != 0)
195716fd348SMartin Matuska fatal(*spa, FTAG, "cannot open '%s': %s", target,
196716fd348SMartin Matuska strerror(err));
197716fd348SMartin Matuska if (spa_version(*spa) < SPA_VERSION_FEATURES) {
198716fd348SMartin Matuska fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
199716fd348SMartin Matuska target, (int)spa_version(*spa));
200716fd348SMartin Matuska }
201716fd348SMartin Matuska }
202716fd348SMartin Matuska
203716fd348SMartin Matuska static void
dump_obj(objset_t * os,uint64_t obj,const char * name)204716fd348SMartin Matuska dump_obj(objset_t *os, uint64_t obj, const char *name)
205716fd348SMartin Matuska {
206716fd348SMartin Matuska zap_cursor_t zc;
2077a7741afSMartin Matuska zap_attribute_t *za = zap_attribute_long_alloc();
208716fd348SMartin Matuska
209716fd348SMartin Matuska (void) printf("%s_obj:\n", name);
210716fd348SMartin Matuska
211716fd348SMartin Matuska for (zap_cursor_init(&zc, os, obj);
2127a7741afSMartin Matuska zap_cursor_retrieve(&zc, za) == 0;
213716fd348SMartin Matuska zap_cursor_advance(&zc)) {
2147a7741afSMartin Matuska if (za->za_integer_length == 8) {
2157a7741afSMartin Matuska ASSERT(za->za_num_integers == 1);
216716fd348SMartin Matuska (void) printf("\t%s = %llu\n",
2177a7741afSMartin Matuska za->za_name, (u_longlong_t)za->za_first_integer);
218716fd348SMartin Matuska } else {
2197a7741afSMartin Matuska ASSERT(za->za_integer_length == 1);
220716fd348SMartin Matuska char val[1024];
2217a7741afSMartin Matuska VERIFY(zap_lookup(os, obj, za->za_name,
222716fd348SMartin Matuska 1, sizeof (val), val) == 0);
2237a7741afSMartin Matuska (void) printf("\t%s = %s\n", za->za_name, val);
224716fd348SMartin Matuska }
225716fd348SMartin Matuska }
226716fd348SMartin Matuska zap_cursor_fini(&zc);
2277a7741afSMartin Matuska zap_attribute_free(za);
228716fd348SMartin Matuska }
229716fd348SMartin Matuska
230716fd348SMartin Matuska static void
dump_mos(spa_t * spa)231716fd348SMartin Matuska dump_mos(spa_t *spa)
232716fd348SMartin Matuska {
233716fd348SMartin Matuska nvlist_t *nv = spa->spa_label_features;
234716fd348SMartin Matuska nvpair_t *pair;
235716fd348SMartin Matuska
236716fd348SMartin Matuska (void) printf("label config:\n");
237716fd348SMartin Matuska for (pair = nvlist_next_nvpair(nv, NULL);
238716fd348SMartin Matuska pair != NULL;
239716fd348SMartin Matuska pair = nvlist_next_nvpair(nv, pair)) {
240716fd348SMartin Matuska (void) printf("\t%s\n", nvpair_name(pair));
241716fd348SMartin Matuska }
242716fd348SMartin Matuska }
243716fd348SMartin Matuska
244716fd348SMartin Matuska static void
zhack_do_feature_stat(int argc,char ** argv)245716fd348SMartin Matuska zhack_do_feature_stat(int argc, char **argv)
246716fd348SMartin Matuska {
247716fd348SMartin Matuska spa_t *spa;
248716fd348SMartin Matuska objset_t *os;
249716fd348SMartin Matuska char *target;
250716fd348SMartin Matuska
251716fd348SMartin Matuska argc--;
252716fd348SMartin Matuska argv++;
253716fd348SMartin Matuska
254716fd348SMartin Matuska if (argc < 1) {
255716fd348SMartin Matuska (void) fprintf(stderr, "error: missing pool name\n");
256716fd348SMartin Matuska usage();
257716fd348SMartin Matuska }
258716fd348SMartin Matuska target = argv[0];
259716fd348SMartin Matuska
260716fd348SMartin Matuska zhack_spa_open(target, B_TRUE, FTAG, &spa);
261716fd348SMartin Matuska os = spa->spa_meta_objset;
262716fd348SMartin Matuska
263716fd348SMartin Matuska dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
264716fd348SMartin Matuska dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
265716fd348SMartin Matuska dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
266716fd348SMartin Matuska if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
267716fd348SMartin Matuska dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
268716fd348SMartin Matuska }
269716fd348SMartin Matuska dump_mos(spa);
270716fd348SMartin Matuska
271716fd348SMartin Matuska spa_close(spa, FTAG);
272716fd348SMartin Matuska }
273716fd348SMartin Matuska
274716fd348SMartin Matuska static void
zhack_feature_enable_sync(void * arg,dmu_tx_t * tx)275716fd348SMartin Matuska zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
276716fd348SMartin Matuska {
277716fd348SMartin Matuska spa_t *spa = dmu_tx_pool(tx)->dp_spa;
278716fd348SMartin Matuska zfeature_info_t *feature = arg;
279716fd348SMartin Matuska
280716fd348SMartin Matuska feature_enable_sync(spa, feature, tx);
281716fd348SMartin Matuska
282716fd348SMartin Matuska spa_history_log_internal(spa, "zhack enable feature", tx,
283716fd348SMartin Matuska "name=%s flags=%u",
284716fd348SMartin Matuska feature->fi_guid, feature->fi_flags);
285716fd348SMartin Matuska }
286716fd348SMartin Matuska
287716fd348SMartin Matuska static void
zhack_do_feature_enable(int argc,char ** argv)288716fd348SMartin Matuska zhack_do_feature_enable(int argc, char **argv)
289716fd348SMartin Matuska {
290716fd348SMartin Matuska int c;
291716fd348SMartin Matuska char *desc, *target;
292716fd348SMartin Matuska spa_t *spa;
293716fd348SMartin Matuska objset_t *mos;
294716fd348SMartin Matuska zfeature_info_t feature;
295716fd348SMartin Matuska const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
296716fd348SMartin Matuska
297716fd348SMartin Matuska /*
298716fd348SMartin Matuska * Features are not added to the pool's label until their refcounts
299716fd348SMartin Matuska * are incremented, so fi_mos can just be left as false for now.
300716fd348SMartin Matuska */
301716fd348SMartin Matuska desc = NULL;
302716fd348SMartin Matuska feature.fi_uname = "zhack";
303716fd348SMartin Matuska feature.fi_flags = 0;
304716fd348SMartin Matuska feature.fi_depends = nodeps;
305716fd348SMartin Matuska feature.fi_feature = SPA_FEATURE_NONE;
306716fd348SMartin Matuska
307716fd348SMartin Matuska optind = 1;
308716fd348SMartin Matuska while ((c = getopt(argc, argv, "+rd:")) != -1) {
309716fd348SMartin Matuska switch (c) {
310716fd348SMartin Matuska case 'r':
311716fd348SMartin Matuska feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
312716fd348SMartin Matuska break;
313716fd348SMartin Matuska case 'd':
314be181ee2SMartin Matuska if (desc != NULL)
315be181ee2SMartin Matuska free(desc);
316716fd348SMartin Matuska desc = strdup(optarg);
317716fd348SMartin Matuska break;
318716fd348SMartin Matuska default:
319716fd348SMartin Matuska usage();
320716fd348SMartin Matuska break;
321716fd348SMartin Matuska }
322716fd348SMartin Matuska }
323716fd348SMartin Matuska
324716fd348SMartin Matuska if (desc == NULL)
325716fd348SMartin Matuska desc = strdup("zhack injected");
326716fd348SMartin Matuska feature.fi_desc = desc;
327716fd348SMartin Matuska
328716fd348SMartin Matuska argc -= optind;
329716fd348SMartin Matuska argv += optind;
330716fd348SMartin Matuska
331716fd348SMartin Matuska if (argc < 2) {
332716fd348SMartin Matuska (void) fprintf(stderr, "error: missing feature or pool name\n");
333716fd348SMartin Matuska usage();
334716fd348SMartin Matuska }
335716fd348SMartin Matuska target = argv[0];
336716fd348SMartin Matuska feature.fi_guid = argv[1];
337716fd348SMartin Matuska
338716fd348SMartin Matuska if (!zfeature_is_valid_guid(feature.fi_guid))
339716fd348SMartin Matuska fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
340716fd348SMartin Matuska
341716fd348SMartin Matuska zhack_spa_open(target, B_FALSE, FTAG, &spa);
342716fd348SMartin Matuska mos = spa->spa_meta_objset;
343716fd348SMartin Matuska
344716fd348SMartin Matuska if (zfeature_is_supported(feature.fi_guid))
345716fd348SMartin Matuska fatal(spa, FTAG, "'%s' is a real feature, will not enable",
346716fd348SMartin Matuska feature.fi_guid);
347716fd348SMartin Matuska if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
348716fd348SMartin Matuska fatal(spa, FTAG, "feature already enabled: %s",
349716fd348SMartin Matuska feature.fi_guid);
350716fd348SMartin Matuska
351716fd348SMartin Matuska VERIFY0(dsl_sync_task(spa_name(spa), NULL,
352716fd348SMartin Matuska zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
353716fd348SMartin Matuska
354716fd348SMartin Matuska spa_close(spa, FTAG);
355716fd348SMartin Matuska
356716fd348SMartin Matuska free(desc);
357716fd348SMartin Matuska }
358716fd348SMartin Matuska
359716fd348SMartin Matuska static void
feature_incr_sync(void * arg,dmu_tx_t * tx)360716fd348SMartin Matuska feature_incr_sync(void *arg, dmu_tx_t *tx)
361716fd348SMartin Matuska {
362716fd348SMartin Matuska spa_t *spa = dmu_tx_pool(tx)->dp_spa;
363716fd348SMartin Matuska zfeature_info_t *feature = arg;
364716fd348SMartin Matuska uint64_t refcount;
365716fd348SMartin Matuska
366716fd348SMartin Matuska VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
367716fd348SMartin Matuska feature_sync(spa, feature, refcount + 1, tx);
368716fd348SMartin Matuska spa_history_log_internal(spa, "zhack feature incr", tx,
369716fd348SMartin Matuska "name=%s", feature->fi_guid);
370716fd348SMartin Matuska }
371716fd348SMartin Matuska
372716fd348SMartin Matuska static void
feature_decr_sync(void * arg,dmu_tx_t * tx)373716fd348SMartin Matuska feature_decr_sync(void *arg, dmu_tx_t *tx)
374716fd348SMartin Matuska {
375716fd348SMartin Matuska spa_t *spa = dmu_tx_pool(tx)->dp_spa;
376716fd348SMartin Matuska zfeature_info_t *feature = arg;
377716fd348SMartin Matuska uint64_t refcount;
378716fd348SMartin Matuska
379716fd348SMartin Matuska VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
380716fd348SMartin Matuska feature_sync(spa, feature, refcount - 1, tx);
381716fd348SMartin Matuska spa_history_log_internal(spa, "zhack feature decr", tx,
382716fd348SMartin Matuska "name=%s", feature->fi_guid);
383716fd348SMartin Matuska }
384716fd348SMartin Matuska
385716fd348SMartin Matuska static void
zhack_do_feature_ref(int argc,char ** argv)386716fd348SMartin Matuska zhack_do_feature_ref(int argc, char **argv)
387716fd348SMartin Matuska {
388716fd348SMartin Matuska int c;
389716fd348SMartin Matuska char *target;
390716fd348SMartin Matuska boolean_t decr = B_FALSE;
391716fd348SMartin Matuska spa_t *spa;
392716fd348SMartin Matuska objset_t *mos;
393716fd348SMartin Matuska zfeature_info_t feature;
394716fd348SMartin Matuska const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
395716fd348SMartin Matuska
396716fd348SMartin Matuska /*
397716fd348SMartin Matuska * fi_desc does not matter here because it was written to disk
398716fd348SMartin Matuska * when the feature was enabled, but we need to properly set the
399716fd348SMartin Matuska * feature for read or write based on the information we read off
400716fd348SMartin Matuska * disk later.
401716fd348SMartin Matuska */
402716fd348SMartin Matuska feature.fi_uname = "zhack";
403716fd348SMartin Matuska feature.fi_flags = 0;
404716fd348SMartin Matuska feature.fi_desc = NULL;
405716fd348SMartin Matuska feature.fi_depends = nodeps;
406716fd348SMartin Matuska feature.fi_feature = SPA_FEATURE_NONE;
407716fd348SMartin Matuska
408716fd348SMartin Matuska optind = 1;
409716fd348SMartin Matuska while ((c = getopt(argc, argv, "+md")) != -1) {
410716fd348SMartin Matuska switch (c) {
411716fd348SMartin Matuska case 'm':
412716fd348SMartin Matuska feature.fi_flags |= ZFEATURE_FLAG_MOS;
413716fd348SMartin Matuska break;
414716fd348SMartin Matuska case 'd':
415716fd348SMartin Matuska decr = B_TRUE;
416716fd348SMartin Matuska break;
417716fd348SMartin Matuska default:
418716fd348SMartin Matuska usage();
419716fd348SMartin Matuska break;
420716fd348SMartin Matuska }
421716fd348SMartin Matuska }
422716fd348SMartin Matuska argc -= optind;
423716fd348SMartin Matuska argv += optind;
424716fd348SMartin Matuska
425716fd348SMartin Matuska if (argc < 2) {
426716fd348SMartin Matuska (void) fprintf(stderr, "error: missing feature or pool name\n");
427716fd348SMartin Matuska usage();
428716fd348SMartin Matuska }
429716fd348SMartin Matuska target = argv[0];
430716fd348SMartin Matuska feature.fi_guid = argv[1];
431716fd348SMartin Matuska
432716fd348SMartin Matuska if (!zfeature_is_valid_guid(feature.fi_guid))
433716fd348SMartin Matuska fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
434716fd348SMartin Matuska
435716fd348SMartin Matuska zhack_spa_open(target, B_FALSE, FTAG, &spa);
436716fd348SMartin Matuska mos = spa->spa_meta_objset;
437716fd348SMartin Matuska
438716fd348SMartin Matuska if (zfeature_is_supported(feature.fi_guid)) {
439716fd348SMartin Matuska fatal(spa, FTAG,
440716fd348SMartin Matuska "'%s' is a real feature, will not change refcount",
441716fd348SMartin Matuska feature.fi_guid);
442716fd348SMartin Matuska }
443716fd348SMartin Matuska
444716fd348SMartin Matuska if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
445716fd348SMartin Matuska feature.fi_guid)) {
446716fd348SMartin Matuska feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
447716fd348SMartin Matuska } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
448716fd348SMartin Matuska feature.fi_guid)) {
449716fd348SMartin Matuska feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
450716fd348SMartin Matuska } else {
451716fd348SMartin Matuska fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
452716fd348SMartin Matuska }
453716fd348SMartin Matuska
454716fd348SMartin Matuska if (decr) {
455716fd348SMartin Matuska uint64_t count;
456716fd348SMartin Matuska if (feature_get_refcount_from_disk(spa, &feature,
457716fd348SMartin Matuska &count) == 0 && count == 0) {
458716fd348SMartin Matuska fatal(spa, FTAG, "feature refcount already 0: %s",
459716fd348SMartin Matuska feature.fi_guid);
460716fd348SMartin Matuska }
461716fd348SMartin Matuska }
462716fd348SMartin Matuska
463716fd348SMartin Matuska VERIFY0(dsl_sync_task(spa_name(spa), NULL,
464716fd348SMartin Matuska decr ? feature_decr_sync : feature_incr_sync, &feature,
465716fd348SMartin Matuska 5, ZFS_SPACE_CHECK_NORMAL));
466716fd348SMartin Matuska
467716fd348SMartin Matuska spa_close(spa, FTAG);
468716fd348SMartin Matuska }
469716fd348SMartin Matuska
470716fd348SMartin Matuska static int
zhack_do_feature(int argc,char ** argv)471716fd348SMartin Matuska zhack_do_feature(int argc, char **argv)
472716fd348SMartin Matuska {
473716fd348SMartin Matuska char *subcommand;
474716fd348SMartin Matuska
475716fd348SMartin Matuska argc--;
476716fd348SMartin Matuska argv++;
477716fd348SMartin Matuska if (argc == 0) {
478716fd348SMartin Matuska (void) fprintf(stderr,
479716fd348SMartin Matuska "error: no feature operation specified\n");
480716fd348SMartin Matuska usage();
481716fd348SMartin Matuska }
482716fd348SMartin Matuska
483716fd348SMartin Matuska subcommand = argv[0];
484716fd348SMartin Matuska if (strcmp(subcommand, "stat") == 0) {
485716fd348SMartin Matuska zhack_do_feature_stat(argc, argv);
486716fd348SMartin Matuska } else if (strcmp(subcommand, "enable") == 0) {
487716fd348SMartin Matuska zhack_do_feature_enable(argc, argv);
488716fd348SMartin Matuska } else if (strcmp(subcommand, "ref") == 0) {
489716fd348SMartin Matuska zhack_do_feature_ref(argc, argv);
490716fd348SMartin Matuska } else {
491716fd348SMartin Matuska (void) fprintf(stderr, "error: unknown subcommand: %s\n",
492716fd348SMartin Matuska subcommand);
493716fd348SMartin Matuska usage();
494716fd348SMartin Matuska }
495716fd348SMartin Matuska
496716fd348SMartin Matuska return (0);
497716fd348SMartin Matuska }
498716fd348SMartin Matuska
499e639e0d2SMartin Matuska #define ASHIFT_UBERBLOCK_SHIFT(ashift) \
500e639e0d2SMartin Matuska MIN(MAX(ashift, UBERBLOCK_SHIFT), \
501e639e0d2SMartin Matuska MAX_UBERBLOCK_SHIFT)
502e639e0d2SMartin Matuska #define ASHIFT_UBERBLOCK_SIZE(ashift) \
503e639e0d2SMartin Matuska (1ULL << ASHIFT_UBERBLOCK_SHIFT(ashift))
504e639e0d2SMartin Matuska
505e639e0d2SMartin Matuska #define REPAIR_LABEL_STATUS_CKSUM (1 << 0)
506e639e0d2SMartin Matuska #define REPAIR_LABEL_STATUS_UB (1 << 1)
507e639e0d2SMartin Matuska
508716fd348SMartin Matuska static int
zhack_repair_read_label(const int fd,vdev_label_t * vl,const uint64_t label_offset,const int l)509e639e0d2SMartin Matuska zhack_repair_read_label(const int fd, vdev_label_t *vl,
510e639e0d2SMartin Matuska const uint64_t label_offset, const int l)
511716fd348SMartin Matuska {
512e639e0d2SMartin Matuska const int err = pread64(fd, vl, sizeof (vdev_label_t), label_offset);
513e639e0d2SMartin Matuska
514e639e0d2SMartin Matuska if (err == -1) {
515e639e0d2SMartin Matuska (void) fprintf(stderr,
516e639e0d2SMartin Matuska "error: cannot read label %d: %s\n",
517e639e0d2SMartin Matuska l, strerror(errno));
518e639e0d2SMartin Matuska return (err);
519e639e0d2SMartin Matuska } else if (err != sizeof (vdev_label_t)) {
520e639e0d2SMartin Matuska (void) fprintf(stderr,
521e639e0d2SMartin Matuska "error: bad label %d read size\n", l);
522e639e0d2SMartin Matuska return (err);
523e639e0d2SMartin Matuska }
524e639e0d2SMartin Matuska
525e639e0d2SMartin Matuska return (0);
526e639e0d2SMartin Matuska }
527e639e0d2SMartin Matuska
528e639e0d2SMartin Matuska static void
zhack_repair_calc_cksum(const int byteswap,void * data,const uint64_t offset,const uint64_t abdsize,zio_eck_t * eck,zio_cksum_t * cksum)529e639e0d2SMartin Matuska zhack_repair_calc_cksum(const int byteswap, void *data, const uint64_t offset,
530e639e0d2SMartin Matuska const uint64_t abdsize, zio_eck_t *eck, zio_cksum_t *cksum)
531e639e0d2SMartin Matuska {
532e639e0d2SMartin Matuska zio_cksum_t verifier;
533e639e0d2SMartin Matuska zio_cksum_t current_cksum;
534e639e0d2SMartin Matuska zio_checksum_info_t *ci;
535e639e0d2SMartin Matuska abd_t *abd;
536e639e0d2SMartin Matuska
537e639e0d2SMartin Matuska ZIO_SET_CHECKSUM(&verifier, offset, 0, 0, 0);
538e639e0d2SMartin Matuska
539e639e0d2SMartin Matuska if (byteswap)
540e639e0d2SMartin Matuska byteswap_uint64_array(&verifier, sizeof (zio_cksum_t));
541e639e0d2SMartin Matuska
542e639e0d2SMartin Matuska current_cksum = eck->zec_cksum;
543e639e0d2SMartin Matuska eck->zec_cksum = verifier;
544e639e0d2SMartin Matuska
545e639e0d2SMartin Matuska ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL];
546e639e0d2SMartin Matuska abd = abd_get_from_buf(data, abdsize);
547e639e0d2SMartin Matuska ci->ci_func[byteswap](abd, abdsize, NULL, cksum);
548e639e0d2SMartin Matuska abd_free(abd);
549e639e0d2SMartin Matuska
550e639e0d2SMartin Matuska eck->zec_cksum = current_cksum;
551e639e0d2SMartin Matuska }
552e639e0d2SMartin Matuska
553e639e0d2SMartin Matuska static int
zhack_repair_check_label(uberblock_t * ub,const int l,const char ** cfg_keys,const size_t cfg_keys_len,nvlist_t * cfg,nvlist_t * vdev_tree_cfg,uint64_t * ashift)554e639e0d2SMartin Matuska zhack_repair_check_label(uberblock_t *ub, const int l, const char **cfg_keys,
555e639e0d2SMartin Matuska const size_t cfg_keys_len, nvlist_t *cfg, nvlist_t *vdev_tree_cfg,
556e639e0d2SMartin Matuska uint64_t *ashift)
557e639e0d2SMartin Matuska {
558e639e0d2SMartin Matuska int err;
559e639e0d2SMartin Matuska
560e639e0d2SMartin Matuska if (ub->ub_txg != 0) {
561e639e0d2SMartin Matuska (void) fprintf(stderr,
562e639e0d2SMartin Matuska "error: label %d: UB TXG of 0 expected, but got %"
563e639e0d2SMartin Matuska PRIu64 "\n",
564e639e0d2SMartin Matuska l, ub->ub_txg);
565e639e0d2SMartin Matuska (void) fprintf(stderr, "It would appear the device was not "
566e639e0d2SMartin Matuska "properly removed.\n");
567e639e0d2SMartin Matuska return (1);
568e639e0d2SMartin Matuska }
569e639e0d2SMartin Matuska
570e639e0d2SMartin Matuska for (int i = 0; i < cfg_keys_len; i++) {
571e639e0d2SMartin Matuska uint64_t val;
572e639e0d2SMartin Matuska err = nvlist_lookup_uint64(cfg, cfg_keys[i], &val);
573e639e0d2SMartin Matuska if (err) {
574e639e0d2SMartin Matuska (void) fprintf(stderr,
575e639e0d2SMartin Matuska "error: label %d, %d: "
576e639e0d2SMartin Matuska "cannot find nvlist key %s\n",
577e639e0d2SMartin Matuska l, i, cfg_keys[i]);
578e639e0d2SMartin Matuska return (err);
579e639e0d2SMartin Matuska }
580e639e0d2SMartin Matuska }
581e639e0d2SMartin Matuska
582e639e0d2SMartin Matuska err = nvlist_lookup_nvlist(cfg,
583e639e0d2SMartin Matuska ZPOOL_CONFIG_VDEV_TREE, &vdev_tree_cfg);
584e639e0d2SMartin Matuska if (err) {
585e639e0d2SMartin Matuska (void) fprintf(stderr,
586e639e0d2SMartin Matuska "error: label %d: cannot find nvlist key %s\n",
587e639e0d2SMartin Matuska l, ZPOOL_CONFIG_VDEV_TREE);
588e639e0d2SMartin Matuska return (err);
589e639e0d2SMartin Matuska }
590e639e0d2SMartin Matuska
591e639e0d2SMartin Matuska err = nvlist_lookup_uint64(vdev_tree_cfg,
592e639e0d2SMartin Matuska ZPOOL_CONFIG_ASHIFT, ashift);
593e639e0d2SMartin Matuska if (err) {
594e639e0d2SMartin Matuska (void) fprintf(stderr,
595e639e0d2SMartin Matuska "error: label %d: cannot find nvlist key %s\n",
596e639e0d2SMartin Matuska l, ZPOOL_CONFIG_ASHIFT);
597e639e0d2SMartin Matuska return (err);
598e639e0d2SMartin Matuska }
599e639e0d2SMartin Matuska
600e639e0d2SMartin Matuska if (*ashift == 0) {
601e639e0d2SMartin Matuska (void) fprintf(stderr,
602e639e0d2SMartin Matuska "error: label %d: nvlist key %s is zero\n",
603e639e0d2SMartin Matuska l, ZPOOL_CONFIG_ASHIFT);
604e639e0d2SMartin Matuska return (err);
605e639e0d2SMartin Matuska }
606e639e0d2SMartin Matuska
607e639e0d2SMartin Matuska return (0);
608e639e0d2SMartin Matuska }
609e639e0d2SMartin Matuska
610e639e0d2SMartin Matuska static int
zhack_repair_undetach(uberblock_t * ub,nvlist_t * cfg,const int l)611e639e0d2SMartin Matuska zhack_repair_undetach(uberblock_t *ub, nvlist_t *cfg, const int l)
612e639e0d2SMartin Matuska {
613e639e0d2SMartin Matuska /*
614e639e0d2SMartin Matuska * Uberblock root block pointer has valid birth TXG.
615e639e0d2SMartin Matuska * Copying it to the label NVlist
616e639e0d2SMartin Matuska */
617783d3ff6SMartin Matuska if (BP_GET_LOGICAL_BIRTH(&ub->ub_rootbp) != 0) {
618783d3ff6SMartin Matuska const uint64_t txg = BP_GET_LOGICAL_BIRTH(&ub->ub_rootbp);
619e639e0d2SMartin Matuska ub->ub_txg = txg;
620e639e0d2SMartin Matuska
621e639e0d2SMartin Matuska if (nvlist_remove_all(cfg, ZPOOL_CONFIG_CREATE_TXG) != 0) {
622e639e0d2SMartin Matuska (void) fprintf(stderr,
623e639e0d2SMartin Matuska "error: label %d: "
624e639e0d2SMartin Matuska "Failed to remove pool creation TXG\n",
625e639e0d2SMartin Matuska l);
626e639e0d2SMartin Matuska return (1);
627e639e0d2SMartin Matuska }
628e639e0d2SMartin Matuska
629e639e0d2SMartin Matuska if (nvlist_remove_all(cfg, ZPOOL_CONFIG_POOL_TXG) != 0) {
630e639e0d2SMartin Matuska (void) fprintf(stderr,
631e639e0d2SMartin Matuska "error: label %d: Failed to remove pool TXG to "
632e639e0d2SMartin Matuska "be replaced.\n",
633e639e0d2SMartin Matuska l);
634e639e0d2SMartin Matuska return (1);
635e639e0d2SMartin Matuska }
636e639e0d2SMartin Matuska
637e639e0d2SMartin Matuska if (nvlist_add_uint64(cfg, ZPOOL_CONFIG_POOL_TXG, txg) != 0) {
638e639e0d2SMartin Matuska (void) fprintf(stderr,
639e639e0d2SMartin Matuska "error: label %d: "
640e639e0d2SMartin Matuska "Failed to add pool TXG of %" PRIu64 "\n",
641e639e0d2SMartin Matuska l, txg);
642e639e0d2SMartin Matuska return (1);
643e639e0d2SMartin Matuska }
644e639e0d2SMartin Matuska }
645e639e0d2SMartin Matuska
646e639e0d2SMartin Matuska return (0);
647e639e0d2SMartin Matuska }
648e639e0d2SMartin Matuska
649e639e0d2SMartin Matuska static boolean_t
zhack_repair_write_label(const int l,const int fd,const int byteswap,void * data,zio_eck_t * eck,const uint64_t offset,const uint64_t abdsize)650e639e0d2SMartin Matuska zhack_repair_write_label(const int l, const int fd, const int byteswap,
651e639e0d2SMartin Matuska void *data, zio_eck_t *eck, const uint64_t offset, const uint64_t abdsize)
652e639e0d2SMartin Matuska {
653e639e0d2SMartin Matuska zio_cksum_t actual_cksum;
654e639e0d2SMartin Matuska zhack_repair_calc_cksum(byteswap, data, offset, abdsize, eck,
655e639e0d2SMartin Matuska &actual_cksum);
656e639e0d2SMartin Matuska zio_cksum_t expected_cksum = eck->zec_cksum;
657e639e0d2SMartin Matuska ssize_t err;
658e639e0d2SMartin Matuska
659e639e0d2SMartin Matuska if (ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum))
660e639e0d2SMartin Matuska return (B_FALSE);
661e639e0d2SMartin Matuska
662e639e0d2SMartin Matuska eck->zec_cksum = actual_cksum;
663e639e0d2SMartin Matuska
664e639e0d2SMartin Matuska err = pwrite64(fd, data, abdsize, offset);
665e639e0d2SMartin Matuska if (err == -1) {
666e639e0d2SMartin Matuska (void) fprintf(stderr, "error: cannot write label %d: %s\n",
667e639e0d2SMartin Matuska l, strerror(errno));
668e639e0d2SMartin Matuska return (B_FALSE);
669e639e0d2SMartin Matuska } else if (err != abdsize) {
670e639e0d2SMartin Matuska (void) fprintf(stderr, "error: bad write size label %d\n", l);
671e639e0d2SMartin Matuska return (B_FALSE);
672e639e0d2SMartin Matuska } else {
673e639e0d2SMartin Matuska (void) fprintf(stderr,
674e639e0d2SMartin Matuska "label %d: wrote %" PRIu64 " bytes at offset %" PRIu64 "\n",
675e639e0d2SMartin Matuska l, abdsize, offset);
676e639e0d2SMartin Matuska }
677e639e0d2SMartin Matuska
678e639e0d2SMartin Matuska return (B_TRUE);
679e639e0d2SMartin Matuska }
680e639e0d2SMartin Matuska
681e639e0d2SMartin Matuska static void
zhack_repair_write_uberblock(vdev_label_t * vl,const int l,const uint64_t ashift,const int fd,const int byteswap,const uint64_t label_offset,uint32_t * labels_repaired)682e639e0d2SMartin Matuska zhack_repair_write_uberblock(vdev_label_t *vl, const int l,
683e639e0d2SMartin Matuska const uint64_t ashift, const int fd, const int byteswap,
684e639e0d2SMartin Matuska const uint64_t label_offset, uint32_t *labels_repaired)
685e639e0d2SMartin Matuska {
686e639e0d2SMartin Matuska void *ub_data =
687e639e0d2SMartin Matuska (char *)vl + offsetof(vdev_label_t, vl_uberblock);
688e639e0d2SMartin Matuska zio_eck_t *ub_eck =
689e639e0d2SMartin Matuska (zio_eck_t *)
690e639e0d2SMartin Matuska ((char *)(ub_data) + (ASHIFT_UBERBLOCK_SIZE(ashift))) - 1;
691e639e0d2SMartin Matuska
692e639e0d2SMartin Matuska if (ub_eck->zec_magic != 0) {
693e639e0d2SMartin Matuska (void) fprintf(stderr,
694e639e0d2SMartin Matuska "error: label %d: "
695e639e0d2SMartin Matuska "Expected Uberblock checksum magic number to "
696e639e0d2SMartin Matuska "be 0, but got %" PRIu64 "\n",
697e639e0d2SMartin Matuska l, ub_eck->zec_magic);
698e639e0d2SMartin Matuska (void) fprintf(stderr, "It would appear there's already "
699e639e0d2SMartin Matuska "a checksum for the uberblock.\n");
700e639e0d2SMartin Matuska return;
701e639e0d2SMartin Matuska }
702e639e0d2SMartin Matuska
703e639e0d2SMartin Matuska
704e639e0d2SMartin Matuska ub_eck->zec_magic = byteswap ? BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
705e639e0d2SMartin Matuska
706e639e0d2SMartin Matuska if (zhack_repair_write_label(l, fd, byteswap,
707e639e0d2SMartin Matuska ub_data, ub_eck,
708e639e0d2SMartin Matuska label_offset + offsetof(vdev_label_t, vl_uberblock),
709e639e0d2SMartin Matuska ASHIFT_UBERBLOCK_SIZE(ashift)))
710e639e0d2SMartin Matuska labels_repaired[l] |= REPAIR_LABEL_STATUS_UB;
711e639e0d2SMartin Matuska }
712e639e0d2SMartin Matuska
713e639e0d2SMartin Matuska static void
zhack_repair_print_cksum(FILE * stream,const zio_cksum_t * cksum)714e639e0d2SMartin Matuska zhack_repair_print_cksum(FILE *stream, const zio_cksum_t *cksum)
715e639e0d2SMartin Matuska {
716e639e0d2SMartin Matuska (void) fprintf(stream,
717e639e0d2SMartin Matuska "%016llx:%016llx:%016llx:%016llx",
718e639e0d2SMartin Matuska (u_longlong_t)cksum->zc_word[0],
719e639e0d2SMartin Matuska (u_longlong_t)cksum->zc_word[1],
720e639e0d2SMartin Matuska (u_longlong_t)cksum->zc_word[2],
721e639e0d2SMartin Matuska (u_longlong_t)cksum->zc_word[3]);
722e639e0d2SMartin Matuska }
723e639e0d2SMartin Matuska
724e639e0d2SMartin Matuska static int
zhack_repair_test_cksum(const int byteswap,void * vdev_data,zio_eck_t * vdev_eck,const uint64_t vdev_phys_offset,const int l)725e639e0d2SMartin Matuska zhack_repair_test_cksum(const int byteswap, void *vdev_data,
726e639e0d2SMartin Matuska zio_eck_t *vdev_eck, const uint64_t vdev_phys_offset, const int l)
727e639e0d2SMartin Matuska {
728e639e0d2SMartin Matuska const zio_cksum_t expected_cksum = vdev_eck->zec_cksum;
729e639e0d2SMartin Matuska zio_cksum_t actual_cksum;
730e639e0d2SMartin Matuska zhack_repair_calc_cksum(byteswap, vdev_data, vdev_phys_offset,
731e639e0d2SMartin Matuska VDEV_PHYS_SIZE, vdev_eck, &actual_cksum);
732e639e0d2SMartin Matuska const uint64_t expected_magic = byteswap ?
733e639e0d2SMartin Matuska BSWAP_64(ZEC_MAGIC) : ZEC_MAGIC;
734e639e0d2SMartin Matuska const uint64_t actual_magic = vdev_eck->zec_magic;
735e639e0d2SMartin Matuska int err = 0;
736e639e0d2SMartin Matuska if (actual_magic != expected_magic) {
737e639e0d2SMartin Matuska (void) fprintf(stderr, "error: label %d: "
738e639e0d2SMartin Matuska "Expected "
739e639e0d2SMartin Matuska "the nvlist checksum magic number to not be %"
740e639e0d2SMartin Matuska PRIu64 " not %" PRIu64 "\n",
741e639e0d2SMartin Matuska l, expected_magic, actual_magic);
742e639e0d2SMartin Matuska err = ECKSUM;
743e639e0d2SMartin Matuska }
744e639e0d2SMartin Matuska if (!ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) {
745e639e0d2SMartin Matuska (void) fprintf(stderr, "error: label %d: "
746e639e0d2SMartin Matuska "Expected the nvlist checksum to be ", l);
747e639e0d2SMartin Matuska (void) zhack_repair_print_cksum(stderr,
748e639e0d2SMartin Matuska &expected_cksum);
749e639e0d2SMartin Matuska (void) fprintf(stderr, " not ");
750e639e0d2SMartin Matuska zhack_repair_print_cksum(stderr, &actual_cksum);
751e639e0d2SMartin Matuska (void) fprintf(stderr, "\n");
752e639e0d2SMartin Matuska err = ECKSUM;
753e639e0d2SMartin Matuska }
754e639e0d2SMartin Matuska return (err);
755e639e0d2SMartin Matuska }
756e639e0d2SMartin Matuska
757e639e0d2SMartin Matuska static void
zhack_repair_one_label(const zhack_repair_op_t op,const int fd,vdev_label_t * vl,const uint64_t label_offset,const int l,uint32_t * labels_repaired)758e639e0d2SMartin Matuska zhack_repair_one_label(const zhack_repair_op_t op, const int fd,
759e639e0d2SMartin Matuska vdev_label_t *vl, const uint64_t label_offset, const int l,
760e639e0d2SMartin Matuska uint32_t *labels_repaired)
761e639e0d2SMartin Matuska {
762e639e0d2SMartin Matuska ssize_t err;
763e639e0d2SMartin Matuska uberblock_t *ub = (uberblock_t *)vl->vl_uberblock;
764e639e0d2SMartin Matuska void *vdev_data =
765e639e0d2SMartin Matuska (char *)vl + offsetof(vdev_label_t, vl_vdev_phys);
766e639e0d2SMartin Matuska zio_eck_t *vdev_eck =
767e639e0d2SMartin Matuska (zio_eck_t *)((char *)(vdev_data) + VDEV_PHYS_SIZE) - 1;
768e639e0d2SMartin Matuska const uint64_t vdev_phys_offset =
769e639e0d2SMartin Matuska label_offset + offsetof(vdev_label_t, vl_vdev_phys);
770716fd348SMartin Matuska const char *cfg_keys[] = { ZPOOL_CONFIG_VERSION,
771716fd348SMartin Matuska ZPOOL_CONFIG_POOL_STATE, ZPOOL_CONFIG_GUID };
772e639e0d2SMartin Matuska nvlist_t *cfg;
773e639e0d2SMartin Matuska nvlist_t *vdev_tree_cfg = NULL;
774e639e0d2SMartin Matuska uint64_t ashift;
775e639e0d2SMartin Matuska int byteswap;
776e639e0d2SMartin Matuska
777e639e0d2SMartin Matuska err = zhack_repair_read_label(fd, vl, label_offset, l);
778e639e0d2SMartin Matuska if (err)
779e639e0d2SMartin Matuska return;
780e639e0d2SMartin Matuska
781e639e0d2SMartin Matuska if (vdev_eck->zec_magic == 0) {
782e639e0d2SMartin Matuska (void) fprintf(stderr, "error: label %d: "
783e639e0d2SMartin Matuska "Expected the nvlist checksum magic number to not be zero"
784e639e0d2SMartin Matuska "\n",
785e639e0d2SMartin Matuska l);
786e639e0d2SMartin Matuska (void) fprintf(stderr, "There should already be a checksum "
787e639e0d2SMartin Matuska "for the label.\n");
788e639e0d2SMartin Matuska return;
789e639e0d2SMartin Matuska }
790e639e0d2SMartin Matuska
791e639e0d2SMartin Matuska byteswap =
792e639e0d2SMartin Matuska (vdev_eck->zec_magic == BSWAP_64((uint64_t)ZEC_MAGIC));
793e639e0d2SMartin Matuska
794e639e0d2SMartin Matuska if (byteswap) {
795e639e0d2SMartin Matuska byteswap_uint64_array(&vdev_eck->zec_cksum,
796e639e0d2SMartin Matuska sizeof (zio_cksum_t));
797e639e0d2SMartin Matuska vdev_eck->zec_magic = BSWAP_64(vdev_eck->zec_magic);
798e639e0d2SMartin Matuska }
799e639e0d2SMartin Matuska
800e639e0d2SMartin Matuska if ((op & ZHACK_REPAIR_OP_CKSUM) == 0 &&
801e639e0d2SMartin Matuska zhack_repair_test_cksum(byteswap, vdev_data, vdev_eck,
802e639e0d2SMartin Matuska vdev_phys_offset, l) != 0) {
803e639e0d2SMartin Matuska (void) fprintf(stderr, "It would appear checksums are "
804e639e0d2SMartin Matuska "corrupted. Try zhack repair label -c <device>\n");
805e639e0d2SMartin Matuska return;
806e639e0d2SMartin Matuska }
807e639e0d2SMartin Matuska
808e639e0d2SMartin Matuska err = nvlist_unpack(vl->vl_vdev_phys.vp_nvlist,
809e639e0d2SMartin Matuska VDEV_PHYS_SIZE - sizeof (zio_eck_t), &cfg, 0);
810e639e0d2SMartin Matuska if (err) {
811e639e0d2SMartin Matuska (void) fprintf(stderr,
812e639e0d2SMartin Matuska "error: cannot unpack nvlist label %d\n", l);
813e639e0d2SMartin Matuska return;
814e639e0d2SMartin Matuska }
815e639e0d2SMartin Matuska
816e639e0d2SMartin Matuska err = zhack_repair_check_label(ub,
817e639e0d2SMartin Matuska l, cfg_keys, ARRAY_SIZE(cfg_keys), cfg, vdev_tree_cfg, &ashift);
818e639e0d2SMartin Matuska if (err)
819e639e0d2SMartin Matuska return;
820e639e0d2SMartin Matuska
821e639e0d2SMartin Matuska if ((op & ZHACK_REPAIR_OP_UNDETACH) != 0) {
822e639e0d2SMartin Matuska char *buf;
823e639e0d2SMartin Matuska size_t buflen;
824e639e0d2SMartin Matuska
825e639e0d2SMartin Matuska err = zhack_repair_undetach(ub, cfg, l);
826e639e0d2SMartin Matuska if (err)
827e639e0d2SMartin Matuska return;
828e639e0d2SMartin Matuska
829e639e0d2SMartin Matuska buf = vl->vl_vdev_phys.vp_nvlist;
830e639e0d2SMartin Matuska buflen = VDEV_PHYS_SIZE - sizeof (zio_eck_t);
831e639e0d2SMartin Matuska if (nvlist_pack(cfg, &buf, &buflen, NV_ENCODE_XDR, 0) != 0) {
832e639e0d2SMartin Matuska (void) fprintf(stderr,
833e639e0d2SMartin Matuska "error: label %d: Failed to pack nvlist\n", l);
834e639e0d2SMartin Matuska return;
835e639e0d2SMartin Matuska }
836e639e0d2SMartin Matuska
837e639e0d2SMartin Matuska zhack_repair_write_uberblock(vl,
838e639e0d2SMartin Matuska l, ashift, fd, byteswap, label_offset, labels_repaired);
839e639e0d2SMartin Matuska }
840e639e0d2SMartin Matuska
841e639e0d2SMartin Matuska if (zhack_repair_write_label(l, fd, byteswap, vdev_data, vdev_eck,
842e639e0d2SMartin Matuska vdev_phys_offset, VDEV_PHYS_SIZE))
843e639e0d2SMartin Matuska labels_repaired[l] |= REPAIR_LABEL_STATUS_CKSUM;
844e639e0d2SMartin Matuska
845e639e0d2SMartin Matuska fsync(fd);
846e639e0d2SMartin Matuska }
847e639e0d2SMartin Matuska
848e639e0d2SMartin Matuska static const char *
zhack_repair_label_status(const uint32_t label_status,const uint32_t to_check)849e639e0d2SMartin Matuska zhack_repair_label_status(const uint32_t label_status,
850e639e0d2SMartin Matuska const uint32_t to_check)
851e639e0d2SMartin Matuska {
852e639e0d2SMartin Matuska return ((label_status & to_check) != 0 ? "repaired" : "skipped");
853e639e0d2SMartin Matuska }
854e639e0d2SMartin Matuska
855e639e0d2SMartin Matuska static int
zhack_label_repair(const zhack_repair_op_t op,const int argc,char ** argv)856e639e0d2SMartin Matuska zhack_label_repair(const zhack_repair_op_t op, const int argc, char **argv)
857e639e0d2SMartin Matuska {
858e639e0d2SMartin Matuska uint32_t labels_repaired[VDEV_LABELS] = {0};
859716fd348SMartin Matuska vdev_label_t labels[VDEV_LABELS] = {{{0}}};
860e639e0d2SMartin Matuska struct stat64 st;
861716fd348SMartin Matuska int fd;
862e639e0d2SMartin Matuska off_t filesize;
863e639e0d2SMartin Matuska uint32_t repaired = 0;
864716fd348SMartin Matuska
865716fd348SMartin Matuska abd_init();
866716fd348SMartin Matuska
867716fd348SMartin Matuska if (argc < 1) {
868716fd348SMartin Matuska (void) fprintf(stderr, "error: missing device\n");
869716fd348SMartin Matuska usage();
870716fd348SMartin Matuska }
871716fd348SMartin Matuska
872716fd348SMartin Matuska if ((fd = open(argv[0], O_RDWR)) == -1)
873716fd348SMartin Matuska fatal(NULL, FTAG, "cannot open '%s': %s", argv[0],
874716fd348SMartin Matuska strerror(errno));
875716fd348SMartin Matuska
876e639e0d2SMartin Matuska if (fstat64_blk(fd, &st) != 0)
877716fd348SMartin Matuska fatal(NULL, FTAG, "cannot stat '%s': %s", argv[0],
878716fd348SMartin Matuska strerror(errno));
879716fd348SMartin Matuska
880e639e0d2SMartin Matuska filesize = st.st_size;
881e639e0d2SMartin Matuska (void) fprintf(stderr, "Calculated filesize to be %jd\n",
882e639e0d2SMartin Matuska (intmax_t)filesize);
883e639e0d2SMartin Matuska
884e639e0d2SMartin Matuska if (filesize % sizeof (vdev_label_t) != 0)
885e639e0d2SMartin Matuska filesize =
886e639e0d2SMartin Matuska (filesize / sizeof (vdev_label_t)) * sizeof (vdev_label_t);
887e639e0d2SMartin Matuska
888716fd348SMartin Matuska for (int l = 0; l < VDEV_LABELS; l++) {
889e639e0d2SMartin Matuska zhack_repair_one_label(op, fd, &labels[l],
890e639e0d2SMartin Matuska vdev_label_offset(filesize, l, 0), l, labels_repaired);
891716fd348SMartin Matuska }
892716fd348SMartin Matuska
893716fd348SMartin Matuska close(fd);
894716fd348SMartin Matuska
895716fd348SMartin Matuska abd_fini();
896716fd348SMartin Matuska
897716fd348SMartin Matuska for (int l = 0; l < VDEV_LABELS; l++) {
898e639e0d2SMartin Matuska const uint32_t lr = labels_repaired[l];
899e639e0d2SMartin Matuska (void) printf("label %d: ", l);
900e639e0d2SMartin Matuska (void) printf("uberblock: %s ",
901e639e0d2SMartin Matuska zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_UB));
902e639e0d2SMartin Matuska (void) printf("checksum: %s\n",
903e639e0d2SMartin Matuska zhack_repair_label_status(lr, REPAIR_LABEL_STATUS_CKSUM));
904e639e0d2SMartin Matuska repaired |= lr;
905716fd348SMartin Matuska }
906716fd348SMartin Matuska
907e639e0d2SMartin Matuska if (repaired > 0)
908716fd348SMartin Matuska return (0);
909716fd348SMartin Matuska
910716fd348SMartin Matuska return (1);
911716fd348SMartin Matuska }
912716fd348SMartin Matuska
913716fd348SMartin Matuska static int
zhack_do_label_repair(int argc,char ** argv)914e639e0d2SMartin Matuska zhack_do_label_repair(int argc, char **argv)
915e639e0d2SMartin Matuska {
916e639e0d2SMartin Matuska zhack_repair_op_t op = ZHACK_REPAIR_OP_UNKNOWN;
917e639e0d2SMartin Matuska int c;
918e639e0d2SMartin Matuska
919e639e0d2SMartin Matuska optind = 1;
920e639e0d2SMartin Matuska while ((c = getopt(argc, argv, "+cu")) != -1) {
921e639e0d2SMartin Matuska switch (c) {
922e639e0d2SMartin Matuska case 'c':
923e639e0d2SMartin Matuska op |= ZHACK_REPAIR_OP_CKSUM;
924e639e0d2SMartin Matuska break;
925e639e0d2SMartin Matuska case 'u':
926e639e0d2SMartin Matuska op |= ZHACK_REPAIR_OP_UNDETACH;
927e639e0d2SMartin Matuska break;
928e639e0d2SMartin Matuska default:
929e639e0d2SMartin Matuska usage();
930e639e0d2SMartin Matuska break;
931e639e0d2SMartin Matuska }
932e639e0d2SMartin Matuska }
933e639e0d2SMartin Matuska
934e639e0d2SMartin Matuska argc -= optind;
935e639e0d2SMartin Matuska argv += optind;
936e639e0d2SMartin Matuska
937e639e0d2SMartin Matuska if (op == ZHACK_REPAIR_OP_UNKNOWN)
938e639e0d2SMartin Matuska op = ZHACK_REPAIR_OP_CKSUM;
939e639e0d2SMartin Matuska
940e639e0d2SMartin Matuska return (zhack_label_repair(op, argc, argv));
941e639e0d2SMartin Matuska }
942e639e0d2SMartin Matuska
943e639e0d2SMartin Matuska static int
zhack_do_label(int argc,char ** argv)944716fd348SMartin Matuska zhack_do_label(int argc, char **argv)
945716fd348SMartin Matuska {
946716fd348SMartin Matuska char *subcommand;
947716fd348SMartin Matuska int err;
948716fd348SMartin Matuska
949716fd348SMartin Matuska argc--;
950716fd348SMartin Matuska argv++;
951716fd348SMartin Matuska if (argc == 0) {
952716fd348SMartin Matuska (void) fprintf(stderr,
953716fd348SMartin Matuska "error: no label operation specified\n");
954716fd348SMartin Matuska usage();
955716fd348SMartin Matuska }
956716fd348SMartin Matuska
957716fd348SMartin Matuska subcommand = argv[0];
958716fd348SMartin Matuska if (strcmp(subcommand, "repair") == 0) {
959e639e0d2SMartin Matuska err = zhack_do_label_repair(argc, argv);
960716fd348SMartin Matuska } else {
961716fd348SMartin Matuska (void) fprintf(stderr, "error: unknown subcommand: %s\n",
962716fd348SMartin Matuska subcommand);
963716fd348SMartin Matuska usage();
964716fd348SMartin Matuska }
965716fd348SMartin Matuska
966716fd348SMartin Matuska return (err);
967716fd348SMartin Matuska }
968716fd348SMartin Matuska
969716fd348SMartin Matuska #define MAX_NUM_PATHS 1024
970716fd348SMartin Matuska
971716fd348SMartin Matuska int
main(int argc,char ** argv)972716fd348SMartin Matuska main(int argc, char **argv)
973716fd348SMartin Matuska {
974716fd348SMartin Matuska char *path[MAX_NUM_PATHS];
975716fd348SMartin Matuska const char *subcommand;
976716fd348SMartin Matuska int rv = 0;
977716fd348SMartin Matuska int c;
978716fd348SMartin Matuska
979716fd348SMartin Matuska g_importargs.path = path;
980716fd348SMartin Matuska
981716fd348SMartin Matuska dprintf_setup(&argc, argv);
982716fd348SMartin Matuska zfs_prop_init();
983716fd348SMartin Matuska
984716fd348SMartin Matuska while ((c = getopt(argc, argv, "+c:d:")) != -1) {
985716fd348SMartin Matuska switch (c) {
986716fd348SMartin Matuska case 'c':
987716fd348SMartin Matuska g_importargs.cachefile = optarg;
988716fd348SMartin Matuska break;
989716fd348SMartin Matuska case 'd':
990716fd348SMartin Matuska assert(g_importargs.paths < MAX_NUM_PATHS);
991716fd348SMartin Matuska g_importargs.path[g_importargs.paths++] = optarg;
992716fd348SMartin Matuska break;
993716fd348SMartin Matuska default:
994716fd348SMartin Matuska usage();
995716fd348SMartin Matuska break;
996716fd348SMartin Matuska }
997716fd348SMartin Matuska }
998716fd348SMartin Matuska
999716fd348SMartin Matuska argc -= optind;
1000716fd348SMartin Matuska argv += optind;
1001716fd348SMartin Matuska optind = 1;
1002716fd348SMartin Matuska
1003716fd348SMartin Matuska if (argc == 0) {
1004716fd348SMartin Matuska (void) fprintf(stderr, "error: no command specified\n");
1005716fd348SMartin Matuska usage();
1006716fd348SMartin Matuska }
1007716fd348SMartin Matuska
1008716fd348SMartin Matuska subcommand = argv[0];
1009716fd348SMartin Matuska
1010716fd348SMartin Matuska if (strcmp(subcommand, "feature") == 0) {
1011716fd348SMartin Matuska rv = zhack_do_feature(argc, argv);
1012716fd348SMartin Matuska } else if (strcmp(subcommand, "label") == 0) {
1013716fd348SMartin Matuska return (zhack_do_label(argc, argv));
1014716fd348SMartin Matuska } else {
1015716fd348SMartin Matuska (void) fprintf(stderr, "error: unknown subcommand: %s\n",
1016716fd348SMartin Matuska subcommand);
1017716fd348SMartin Matuska usage();
1018716fd348SMartin Matuska }
1019716fd348SMartin Matuska
1020716fd348SMartin Matuska if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
1021716fd348SMartin Matuska fatal(NULL, FTAG, "pool export failed; "
1022716fd348SMartin Matuska "changes may not be committed to disk\n");
1023716fd348SMartin Matuska }
1024716fd348SMartin Matuska
1025716fd348SMartin Matuska kernel_fini();
1026716fd348SMartin Matuska
1027716fd348SMartin Matuska return (rv);
1028716fd348SMartin Matuska }
1029