]>
Commit | Line | Data |
---|---|---|
9ae529ec CS |
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 | /* | |
241b5415 | 23 | * Copyright (c) 2011, 2015 by Delphix. All rights reserved. |
95fd54a1 | 24 | * Copyright (c) 2013 Steven Hartland. All rights reserved. |
9ae529ec CS |
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> | |
d04b5c9e | 36 | #include <sys/stat.h> |
9ae529ec CS |
37 | #include <sys/zfs_context.h> |
38 | #include <sys/spa.h> | |
39 | #include <sys/spa_impl.h> | |
40 | #include <sys/dmu.h> | |
41 | #include <sys/zap.h> | |
42 | #include <sys/zfs_znode.h> | |
43 | #include <sys/dsl_synctask.h> | |
44 | #include <sys/vdev.h> | |
d04b5c9e | 45 | #include <sys/vdev_impl.h> |
9ae529ec CS |
46 | #include <sys/fs/zfs.h> |
47 | #include <sys/dmu_objset.h> | |
48 | #include <sys/dsl_pool.h> | |
49 | #include <sys/zio_checksum.h> | |
50 | #include <sys/zio_compress.h> | |
51 | #include <sys/zfeature.h> | |
13fe0198 | 52 | #include <sys/dmu_tx.h> |
739cfb96 | 53 | #include <zfeature_common.h> |
e89f1295 | 54 | #include <libzutil.h> |
9ae529ec | 55 | |
9ae529ec CS |
56 | static importargs_t g_importargs; |
57 | static char *g_pool; | |
58 | static boolean_t g_readonly; | |
59 | ||
90f1c3c9 | 60 | static __attribute__((noreturn)) void |
9ae529ec CS |
61 | usage(void) |
62 | { | |
63 | (void) fprintf(stderr, | |
18168da7 | 64 | "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n" |
9ae529ec | 65 | "where <subcommand> <args> is one of the following:\n" |
18168da7 | 66 | "\n"); |
9ae529ec CS |
67 | |
68 | (void) fprintf(stderr, | |
69 | " feature stat <pool>\n" | |
70 | " print information about enabled features\n" | |
9d69e9b2 | 71 | " feature enable [-r] [-d desc] <pool> <feature>\n" |
9ae529ec CS |
72 | " add a new enabled feature to the pool\n" |
73 | " -d <desc> sets the feature's description\n" | |
9d69e9b2 | 74 | " -r set read-only compatible flag for feature\n" |
9ae529ec CS |
75 | " feature ref [-md] <pool> <feature>\n" |
76 | " change the refcount on the given feature\n" | |
77 | " -d decrease instead of increase the refcount\n" | |
78 | " -m add the feature to the label if increasing refcount\n" | |
79 | "\n" | |
d04b5c9e FU |
80 | " <feature> : should be a feature guid\n" |
81 | "\n" | |
82 | " label repair <device>\n" | |
83 | " repair corrupted label checksums\n" | |
84 | "\n" | |
85 | " <device> : path to vdev\n"); | |
9ae529ec CS |
86 | exit(1); |
87 | } | |
88 | ||
89 | ||
90f1c3c9 | 90 | static __attribute__((noreturn)) __attribute__((format(printf, 3, 4))) void |
2fbc542e | 91 | fatal(spa_t *spa, void *tag, const char *fmt, ...) |
9ae529ec CS |
92 | { |
93 | va_list ap; | |
94 | ||
2fbc542e GW |
95 | if (spa != NULL) { |
96 | spa_close(spa, tag); | |
97 | (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE); | |
98 | } | |
99 | ||
9ae529ec | 100 | va_start(ap, fmt); |
18168da7 | 101 | (void) fputs("zhack: ", stderr); |
9ae529ec CS |
102 | (void) vfprintf(stderr, fmt, ap); |
103 | va_end(ap); | |
18168da7 | 104 | (void) fputc('\n', stderr); |
9ae529ec CS |
105 | |
106 | exit(1); | |
107 | } | |
108 | ||
9ae529ec | 109 | static int |
7bcb7f08 MA |
110 | space_delta_cb(dmu_object_type_t bonustype, const void *data, |
111 | zfs_file_info_t *zoi) | |
9ae529ec | 112 | { |
63b6c3e1 AZ |
113 | (void) data, (void) zoi; |
114 | ||
9ae529ec CS |
115 | /* |
116 | * Is it a valid type of object to track? | |
117 | */ | |
118 | if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA) | |
119 | return (ENOENT); | |
120 | (void) fprintf(stderr, "modifying object that needs user accounting"); | |
121 | abort(); | |
9ae529ec CS |
122 | } |
123 | ||
124 | /* | |
125 | * Target is the dataset whose pool we want to open. | |
126 | */ | |
127 | static void | |
379ca9cf | 128 | zhack_import(char *target, boolean_t readonly) |
9ae529ec CS |
129 | { |
130 | nvlist_t *config; | |
9ae529ec | 131 | nvlist_t *props; |
379ca9cf | 132 | int error; |
9ae529ec | 133 | |
da92d5cb MM |
134 | kernel_init(readonly ? SPA_MODE_READ : |
135 | (SPA_MODE_READ | SPA_MODE_WRITE)); | |
9ae529ec CS |
136 | |
137 | dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb); | |
138 | ||
139 | g_readonly = readonly; | |
9ae529ec CS |
140 | g_importargs.can_be_active = readonly; |
141 | g_pool = strdup(target); | |
9ae529ec | 142 | |
e89f1295 DB |
143 | error = zpool_find_config(NULL, target, &config, &g_importargs, |
144 | &libzpool_config_ops); | |
379ca9cf | 145 | if (error) |
e89f1295 | 146 | fatal(NULL, FTAG, "cannot import '%s'", target); |
9ae529ec CS |
147 | |
148 | props = NULL; | |
149 | if (readonly) { | |
150 | VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0); | |
151 | VERIFY(nvlist_add_uint64(props, | |
152 | zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0); | |
153 | } | |
154 | ||
155 | zfeature_checks_disable = B_TRUE; | |
60f51034 OF |
156 | error = spa_import(target, config, props, |
157 | (readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL)); | |
b6722b87 | 158 | fnvlist_free(config); |
9ae529ec CS |
159 | zfeature_checks_disable = B_FALSE; |
160 | if (error == EEXIST) | |
161 | error = 0; | |
162 | ||
163 | if (error) | |
379ca9cf | 164 | fatal(NULL, FTAG, "can't import '%s': %s", target, |
2fbc542e | 165 | strerror(error)); |
9ae529ec CS |
166 | } |
167 | ||
168 | static void | |
379ca9cf | 169 | zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa) |
9ae529ec CS |
170 | { |
171 | int err; | |
172 | ||
379ca9cf | 173 | zhack_import(target, readonly); |
9ae529ec CS |
174 | |
175 | zfeature_checks_disable = B_TRUE; | |
176 | err = spa_open(target, spa, tag); | |
177 | zfeature_checks_disable = B_FALSE; | |
178 | ||
179 | if (err != 0) | |
2fbc542e GW |
180 | fatal(*spa, FTAG, "cannot open '%s': %s", target, |
181 | strerror(err)); | |
9ae529ec | 182 | if (spa_version(*spa) < SPA_VERSION_FEATURES) { |
2fbc542e GW |
183 | fatal(*spa, FTAG, "'%s' has version %d, features not enabled", |
184 | target, (int)spa_version(*spa)); | |
9ae529ec CS |
185 | } |
186 | } | |
187 | ||
188 | static void | |
189 | dump_obj(objset_t *os, uint64_t obj, const char *name) | |
190 | { | |
191 | zap_cursor_t zc; | |
192 | zap_attribute_t za; | |
193 | ||
194 | (void) printf("%s_obj:\n", name); | |
195 | ||
196 | for (zap_cursor_init(&zc, os, obj); | |
197 | zap_cursor_retrieve(&zc, &za) == 0; | |
198 | zap_cursor_advance(&zc)) { | |
199 | if (za.za_integer_length == 8) { | |
200 | ASSERT(za.za_num_integers == 1); | |
201 | (void) printf("\t%s = %llu\n", | |
202 | za.za_name, (u_longlong_t)za.za_first_integer); | |
203 | } else { | |
204 | ASSERT(za.za_integer_length == 1); | |
205 | char val[1024]; | |
206 | VERIFY(zap_lookup(os, obj, za.za_name, | |
207 | 1, sizeof (val), val) == 0); | |
208 | (void) printf("\t%s = %s\n", za.za_name, val); | |
209 | } | |
210 | } | |
211 | zap_cursor_fini(&zc); | |
212 | } | |
213 | ||
214 | static void | |
215 | dump_mos(spa_t *spa) | |
216 | { | |
217 | nvlist_t *nv = spa->spa_label_features; | |
218 | nvpair_t *pair; | |
219 | ||
220 | (void) printf("label config:\n"); | |
221 | for (pair = nvlist_next_nvpair(nv, NULL); | |
222 | pair != NULL; | |
223 | pair = nvlist_next_nvpair(nv, pair)) { | |
224 | (void) printf("\t%s\n", nvpair_name(pair)); | |
225 | } | |
226 | } | |
227 | ||
228 | static void | |
229 | zhack_do_feature_stat(int argc, char **argv) | |
230 | { | |
231 | spa_t *spa; | |
232 | objset_t *os; | |
233 | char *target; | |
234 | ||
235 | argc--; | |
236 | argv++; | |
237 | ||
238 | if (argc < 1) { | |
239 | (void) fprintf(stderr, "error: missing pool name\n"); | |
240 | usage(); | |
241 | } | |
242 | target = argv[0]; | |
243 | ||
244 | zhack_spa_open(target, B_TRUE, FTAG, &spa); | |
245 | os = spa->spa_meta_objset; | |
246 | ||
247 | dump_obj(os, spa->spa_feat_for_read_obj, "for_read"); | |
248 | dump_obj(os, spa->spa_feat_for_write_obj, "for_write"); | |
249 | dump_obj(os, spa->spa_feat_desc_obj, "descriptions"); | |
b0bc7a84 MG |
250 | if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) { |
251 | dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg"); | |
252 | } | |
9ae529ec CS |
253 | dump_mos(spa); |
254 | ||
255 | spa_close(spa, FTAG); | |
256 | } | |
257 | ||
258 | static void | |
fa86b5db | 259 | zhack_feature_enable_sync(void *arg, dmu_tx_t *tx) |
9ae529ec | 260 | { |
13fe0198 MA |
261 | spa_t *spa = dmu_tx_pool(tx)->dp_spa; |
262 | zfeature_info_t *feature = arg; | |
9ae529ec | 263 | |
fa86b5db MA |
264 | feature_enable_sync(spa, feature, tx); |
265 | ||
6f1ffb06 | 266 | spa_history_log_internal(spa, "zhack enable feature", tx, |
241b5415 MA |
267 | "name=%s flags=%u", |
268 | feature->fi_guid, feature->fi_flags); | |
9ae529ec CS |
269 | } |
270 | ||
271 | static void | |
272 | zhack_do_feature_enable(int argc, char **argv) | |
273 | { | |
8b20a9f9 | 274 | int c; |
9ae529ec CS |
275 | char *desc, *target; |
276 | spa_t *spa; | |
277 | objset_t *mos; | |
278 | zfeature_info_t feature; | |
18168da7 | 279 | const spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; |
9ae529ec CS |
280 | |
281 | /* | |
282 | * Features are not added to the pool's label until their refcounts | |
283 | * are incremented, so fi_mos can just be left as false for now. | |
284 | */ | |
285 | desc = NULL; | |
286 | feature.fi_uname = "zhack"; | |
241b5415 | 287 | feature.fi_flags = 0; |
9ae529ec | 288 | feature.fi_depends = nodeps; |
b0bc7a84 | 289 | feature.fi_feature = SPA_FEATURE_NONE; |
9ae529ec CS |
290 | |
291 | optind = 1; | |
9d69e9b2 | 292 | while ((c = getopt(argc, argv, "+rd:")) != -1) { |
9ae529ec CS |
293 | switch (c) { |
294 | case 'r': | |
241b5415 | 295 | feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; |
9ae529ec CS |
296 | break; |
297 | case 'd': | |
298 | desc = strdup(optarg); | |
299 | break; | |
300 | default: | |
301 | usage(); | |
302 | break; | |
303 | } | |
304 | } | |
305 | ||
306 | if (desc == NULL) | |
307 | desc = strdup("zhack injected"); | |
308 | feature.fi_desc = desc; | |
309 | ||
310 | argc -= optind; | |
311 | argv += optind; | |
312 | ||
313 | if (argc < 2) { | |
314 | (void) fprintf(stderr, "error: missing feature or pool name\n"); | |
315 | usage(); | |
316 | } | |
317 | target = argv[0]; | |
318 | feature.fi_guid = argv[1]; | |
319 | ||
320 | if (!zfeature_is_valid_guid(feature.fi_guid)) | |
2fbc542e | 321 | fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid); |
9ae529ec CS |
322 | |
323 | zhack_spa_open(target, B_FALSE, FTAG, &spa); | |
324 | mos = spa->spa_meta_objset; | |
325 | ||
ada82581 | 326 | if (zfeature_is_supported(feature.fi_guid)) |
90f1c3c9 AZ |
327 | fatal(spa, FTAG, "'%s' is a real feature, will not enable", |
328 | feature.fi_guid); | |
9ae529ec | 329 | if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid)) |
2fbc542e GW |
330 | fatal(spa, FTAG, "feature already enabled: %s", |
331 | feature.fi_guid); | |
9ae529ec | 332 | |
13fe0198 | 333 | VERIFY0(dsl_sync_task(spa_name(spa), NULL, |
3d45fdd6 | 334 | zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL)); |
9ae529ec CS |
335 | |
336 | spa_close(spa, FTAG); | |
337 | ||
338 | free(desc); | |
339 | } | |
340 | ||
341 | static void | |
13fe0198 | 342 | feature_incr_sync(void *arg, dmu_tx_t *tx) |
9ae529ec | 343 | { |
13fe0198 MA |
344 | spa_t *spa = dmu_tx_pool(tx)->dp_spa; |
345 | zfeature_info_t *feature = arg; | |
fa86b5db | 346 | uint64_t refcount; |
9ae529ec | 347 | |
b0bc7a84 | 348 | VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount)); |
fa86b5db | 349 | feature_sync(spa, feature, refcount + 1, tx); |
6f1ffb06 MA |
350 | spa_history_log_internal(spa, "zhack feature incr", tx, |
351 | "name=%s", feature->fi_guid); | |
9ae529ec CS |
352 | } |
353 | ||
354 | static void | |
13fe0198 | 355 | feature_decr_sync(void *arg, dmu_tx_t *tx) |
9ae529ec | 356 | { |
13fe0198 MA |
357 | spa_t *spa = dmu_tx_pool(tx)->dp_spa; |
358 | zfeature_info_t *feature = arg; | |
fa86b5db | 359 | uint64_t refcount; |
9ae529ec | 360 | |
b0bc7a84 | 361 | VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount)); |
fa86b5db | 362 | feature_sync(spa, feature, refcount - 1, tx); |
6f1ffb06 MA |
363 | spa_history_log_internal(spa, "zhack feature decr", tx, |
364 | "name=%s", feature->fi_guid); | |
9ae529ec CS |
365 | } |
366 | ||
367 | static void | |
368 | zhack_do_feature_ref(int argc, char **argv) | |
369 | { | |
8b20a9f9 | 370 | int c; |
9ae529ec CS |
371 | char *target; |
372 | boolean_t decr = B_FALSE; | |
373 | spa_t *spa; | |
374 | objset_t *mos; | |
375 | zfeature_info_t feature; | |
18168da7 | 376 | const spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; |
9ae529ec CS |
377 | |
378 | /* | |
379 | * fi_desc does not matter here because it was written to disk | |
380 | * when the feature was enabled, but we need to properly set the | |
381 | * feature for read or write based on the information we read off | |
382 | * disk later. | |
383 | */ | |
384 | feature.fi_uname = "zhack"; | |
241b5415 | 385 | feature.fi_flags = 0; |
9ae529ec CS |
386 | feature.fi_desc = NULL; |
387 | feature.fi_depends = nodeps; | |
b0bc7a84 | 388 | feature.fi_feature = SPA_FEATURE_NONE; |
9ae529ec CS |
389 | |
390 | optind = 1; | |
9d69e9b2 | 391 | while ((c = getopt(argc, argv, "+md")) != -1) { |
9ae529ec CS |
392 | switch (c) { |
393 | case 'm': | |
241b5415 | 394 | feature.fi_flags |= ZFEATURE_FLAG_MOS; |
9ae529ec CS |
395 | break; |
396 | case 'd': | |
397 | decr = B_TRUE; | |
398 | break; | |
399 | default: | |
400 | usage(); | |
401 | break; | |
402 | } | |
403 | } | |
404 | argc -= optind; | |
405 | argv += optind; | |
406 | ||
407 | if (argc < 2) { | |
408 | (void) fprintf(stderr, "error: missing feature or pool name\n"); | |
409 | usage(); | |
410 | } | |
411 | target = argv[0]; | |
412 | feature.fi_guid = argv[1]; | |
413 | ||
414 | if (!zfeature_is_valid_guid(feature.fi_guid)) | |
2fbc542e | 415 | fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid); |
9ae529ec CS |
416 | |
417 | zhack_spa_open(target, B_FALSE, FTAG, &spa); | |
418 | mos = spa->spa_meta_objset; | |
419 | ||
fa86b5db MA |
420 | if (zfeature_is_supported(feature.fi_guid)) { |
421 | fatal(spa, FTAG, | |
90f1c3c9 AZ |
422 | "'%s' is a real feature, will not change refcount", |
423 | feature.fi_guid); | |
fa86b5db | 424 | } |
9ae529ec CS |
425 | |
426 | if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, | |
427 | feature.fi_guid)) { | |
241b5415 | 428 | feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT; |
9ae529ec CS |
429 | } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, |
430 | feature.fi_guid)) { | |
241b5415 | 431 | feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; |
9ae529ec | 432 | } else { |
2fbc542e | 433 | fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid); |
9ae529ec CS |
434 | } |
435 | ||
fa86b5db MA |
436 | if (decr) { |
437 | uint64_t count; | |
b0bc7a84 | 438 | if (feature_get_refcount_from_disk(spa, &feature, |
72c407f8 | 439 | &count) == 0 && count == 0) { |
fa86b5db MA |
440 | fatal(spa, FTAG, "feature refcount already 0: %s", |
441 | feature.fi_guid); | |
442 | } | |
443 | } | |
9ae529ec | 444 | |
13fe0198 | 445 | VERIFY0(dsl_sync_task(spa_name(spa), NULL, |
3d45fdd6 MA |
446 | decr ? feature_decr_sync : feature_incr_sync, &feature, |
447 | 5, ZFS_SPACE_CHECK_NORMAL)); | |
9ae529ec CS |
448 | |
449 | spa_close(spa, FTAG); | |
450 | } | |
451 | ||
452 | static int | |
453 | zhack_do_feature(int argc, char **argv) | |
454 | { | |
455 | char *subcommand; | |
456 | ||
457 | argc--; | |
458 | argv++; | |
459 | if (argc == 0) { | |
460 | (void) fprintf(stderr, | |
461 | "error: no feature operation specified\n"); | |
462 | usage(); | |
463 | } | |
464 | ||
465 | subcommand = argv[0]; | |
466 | if (strcmp(subcommand, "stat") == 0) { | |
467 | zhack_do_feature_stat(argc, argv); | |
468 | } else if (strcmp(subcommand, "enable") == 0) { | |
469 | zhack_do_feature_enable(argc, argv); | |
470 | } else if (strcmp(subcommand, "ref") == 0) { | |
471 | zhack_do_feature_ref(argc, argv); | |
472 | } else { | |
473 | (void) fprintf(stderr, "error: unknown subcommand: %s\n", | |
474 | subcommand); | |
475 | usage(); | |
476 | } | |
477 | ||
478 | return (0); | |
479 | } | |
480 | ||
d04b5c9e FU |
481 | static int |
482 | zhack_repair_label_cksum(int argc, char **argv) | |
483 | { | |
484 | zio_checksum_info_t *ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL]; | |
485 | const char *cfg_keys[] = { ZPOOL_CONFIG_VERSION, | |
486 | ZPOOL_CONFIG_POOL_STATE, ZPOOL_CONFIG_GUID }; | |
487 | boolean_t labels_repaired[VDEV_LABELS]; | |
488 | boolean_t repaired = B_FALSE; | |
489 | vdev_label_t labels[VDEV_LABELS]; | |
490 | struct stat st; | |
491 | int fd; | |
492 | ||
493 | bzero(labels_repaired, sizeof (labels_repaired)); | |
494 | bzero(labels, sizeof (labels)); | |
495 | ||
496 | abd_init(); | |
497 | ||
498 | argc -= 1; | |
499 | argv += 1; | |
500 | ||
501 | if (argc < 1) { | |
502 | (void) fprintf(stderr, "error: missing device\n"); | |
503 | usage(); | |
504 | } | |
505 | ||
506 | if ((fd = open(argv[0], O_RDWR)) == -1) | |
507 | fatal(NULL, FTAG, "cannot open '%s': %s", argv[0], | |
508 | strerror(errno)); | |
509 | ||
510 | if (stat(argv[0], &st) != 0) | |
511 | fatal(NULL, FTAG, "cannot stat '%s': %s", argv[0], | |
512 | strerror(errno)); | |
513 | ||
514 | for (int l = 0; l < VDEV_LABELS; l++) { | |
515 | uint64_t label_offset, offset; | |
516 | zio_cksum_t expected_cksum; | |
517 | zio_cksum_t actual_cksum; | |
518 | zio_cksum_t verifier; | |
519 | zio_eck_t *eck; | |
520 | nvlist_t *cfg; | |
521 | int byteswap; | |
522 | uint64_t val; | |
523 | ssize_t err; | |
524 | ||
525 | vdev_label_t *vl = &labels[l]; | |
526 | ||
527 | label_offset = vdev_label_offset(st.st_size, l, 0); | |
528 | err = pread64(fd, vl, sizeof (vdev_label_t), label_offset); | |
529 | if (err == -1) { | |
530 | (void) fprintf(stderr, "error: cannot read " | |
531 | "label %d: %s\n", l, strerror(errno)); | |
532 | continue; | |
533 | } else if (err != sizeof (vdev_label_t)) { | |
534 | (void) fprintf(stderr, "error: bad label %d read size " | |
535 | "\n", l); | |
536 | continue; | |
537 | } | |
538 | ||
539 | err = nvlist_unpack(vl->vl_vdev_phys.vp_nvlist, | |
540 | VDEV_PHYS_SIZE - sizeof (zio_eck_t), &cfg, 0); | |
541 | if (err) { | |
542 | (void) fprintf(stderr, "error: cannot unpack nvlist " | |
543 | "label %d\n", l); | |
544 | continue; | |
545 | } | |
546 | ||
547 | for (int i = 0; i < ARRAY_SIZE(cfg_keys); i++) { | |
548 | err = nvlist_lookup_uint64(cfg, cfg_keys[i], &val); | |
549 | if (err) { | |
550 | (void) fprintf(stderr, "error: label %d: " | |
551 | "cannot find nvlist key %s\n", | |
552 | l, cfg_keys[i]); | |
553 | continue; | |
554 | } | |
555 | } | |
556 | ||
557 | void *data = (char *)vl + offsetof(vdev_label_t, vl_vdev_phys); | |
558 | eck = (zio_eck_t *)((char *)(data) + VDEV_PHYS_SIZE) - 1; | |
559 | ||
560 | offset = label_offset + offsetof(vdev_label_t, vl_vdev_phys); | |
561 | ZIO_SET_CHECKSUM(&verifier, offset, 0, 0, 0); | |
562 | ||
563 | byteswap = (eck->zec_magic == BSWAP_64(ZEC_MAGIC)); | |
564 | if (byteswap) | |
565 | byteswap_uint64_array(&verifier, sizeof (zio_cksum_t)); | |
566 | ||
567 | expected_cksum = eck->zec_cksum; | |
568 | eck->zec_cksum = verifier; | |
569 | ||
570 | abd_t *abd = abd_get_from_buf(data, VDEV_PHYS_SIZE); | |
571 | ci->ci_func[byteswap](abd, VDEV_PHYS_SIZE, NULL, &actual_cksum); | |
572 | abd_free(abd); | |
573 | ||
574 | if (byteswap) | |
575 | byteswap_uint64_array(&expected_cksum, | |
576 | sizeof (zio_cksum_t)); | |
577 | ||
578 | if (ZIO_CHECKSUM_EQUAL(actual_cksum, expected_cksum)) | |
579 | continue; | |
580 | ||
581 | eck->zec_cksum = actual_cksum; | |
582 | ||
583 | err = pwrite64(fd, data, VDEV_PHYS_SIZE, offset); | |
584 | if (err == -1) { | |
585 | (void) fprintf(stderr, "error: cannot write " | |
586 | "label %d: %s\n", l, strerror(errno)); | |
587 | continue; | |
588 | } else if (err != VDEV_PHYS_SIZE) { | |
589 | (void) fprintf(stderr, "error: bad write size " | |
590 | "label %d\n", l); | |
591 | continue; | |
592 | } | |
593 | ||
594 | fsync(fd); | |
595 | ||
596 | labels_repaired[l] = B_TRUE; | |
597 | } | |
598 | ||
599 | close(fd); | |
600 | ||
601 | abd_fini(); | |
602 | ||
603 | for (int l = 0; l < VDEV_LABELS; l++) { | |
604 | (void) printf("label %d: %s\n", l, | |
605 | labels_repaired[l] ? "repaired" : "skipped"); | |
606 | repaired |= labels_repaired[l]; | |
607 | } | |
608 | ||
609 | if (repaired) | |
610 | return (0); | |
611 | ||
612 | return (1); | |
613 | } | |
614 | ||
615 | static int | |
616 | zhack_do_label(int argc, char **argv) | |
617 | { | |
618 | char *subcommand; | |
619 | int err; | |
620 | ||
621 | argc--; | |
622 | argv++; | |
623 | if (argc == 0) { | |
624 | (void) fprintf(stderr, | |
625 | "error: no label operation specified\n"); | |
626 | usage(); | |
627 | } | |
628 | ||
629 | subcommand = argv[0]; | |
630 | if (strcmp(subcommand, "repair") == 0) { | |
631 | err = zhack_repair_label_cksum(argc, argv); | |
632 | } else { | |
633 | (void) fprintf(stderr, "error: unknown subcommand: %s\n", | |
634 | subcommand); | |
635 | usage(); | |
636 | } | |
637 | ||
638 | return (err); | |
639 | } | |
640 | ||
9ae529ec CS |
641 | #define MAX_NUM_PATHS 1024 |
642 | ||
643 | int | |
644 | main(int argc, char **argv) | |
645 | { | |
646 | extern void zfs_prop_init(void); | |
647 | ||
648 | char *path[MAX_NUM_PATHS]; | |
649 | const char *subcommand; | |
650 | int rv = 0; | |
8b20a9f9 | 651 | int c; |
9ae529ec CS |
652 | |
653 | g_importargs.path = path; | |
654 | ||
655 | dprintf_setup(&argc, argv); | |
656 | zfs_prop_init(); | |
657 | ||
9d69e9b2 | 658 | while ((c = getopt(argc, argv, "+c:d:")) != -1) { |
9ae529ec CS |
659 | switch (c) { |
660 | case 'c': | |
661 | g_importargs.cachefile = optarg; | |
662 | break; | |
663 | case 'd': | |
664 | assert(g_importargs.paths < MAX_NUM_PATHS); | |
665 | g_importargs.path[g_importargs.paths++] = optarg; | |
666 | break; | |
667 | default: | |
668 | usage(); | |
669 | break; | |
670 | } | |
671 | } | |
672 | ||
673 | argc -= optind; | |
674 | argv += optind; | |
675 | optind = 1; | |
676 | ||
677 | if (argc == 0) { | |
678 | (void) fprintf(stderr, "error: no command specified\n"); | |
679 | usage(); | |
680 | } | |
681 | ||
682 | subcommand = argv[0]; | |
683 | ||
684 | if (strcmp(subcommand, "feature") == 0) { | |
685 | rv = zhack_do_feature(argc, argv); | |
d04b5c9e FU |
686 | } else if (strcmp(subcommand, "label") == 0) { |
687 | return (zhack_do_label(argc, argv)); | |
9ae529ec CS |
688 | } else { |
689 | (void) fprintf(stderr, "error: unknown subcommand: %s\n", | |
690 | subcommand); | |
691 | usage(); | |
692 | } | |
693 | ||
2fbc542e GW |
694 | if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) { |
695 | fatal(NULL, FTAG, "pool export failed; " | |
9ae529ec CS |
696 | "changes may not be committed to disk\n"); |
697 | } | |
698 | ||
9ae529ec CS |
699 | kernel_fini(); |
700 | ||
701 | return (rv); | |
702 | } |