1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <paths.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <stdint.h> 39 #include <string.h> 40 #include <strings.h> 41 #include <assert.h> 42 #include <libgeom.h> 43 #include <geom/mirror/g_mirror.h> 44 #include <core/geom.h> 45 #include <misc/subr.h> 46 47 uint32_t lib_version = G_LIB_VERSION; 48 uint32_t version = G_MIRROR_VERSION; 49 50 #define GMIRROR_BALANCE "load" 51 #define GMIRROR_SLICE "4096" 52 #define GMIRROR_PRIORITY "0" 53 54 static void mirror_main(struct gctl_req *req, unsigned flags); 55 static void mirror_activate(struct gctl_req *req); 56 static void mirror_clear(struct gctl_req *req); 57 static void mirror_dump(struct gctl_req *req); 58 static void mirror_label(struct gctl_req *req); 59 static void mirror_resize(struct gctl_req *req, unsigned flags); 60 61 struct g_command class_commands[] = { 62 { "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS, 63 "[-v] name prov ..." 64 }, 65 { "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS, 66 "[-v] prov ..." 67 }, 68 { "configure", G_FLAG_VERBOSE, NULL, 69 { 70 { 'a', "autosync", NULL, G_TYPE_BOOL }, 71 { 'b', "balance", "", G_TYPE_STRING }, 72 { 'd', "dynamic", NULL, G_TYPE_BOOL }, 73 { 'f', "failsync", NULL, G_TYPE_BOOL }, 74 { 'F', "nofailsync", NULL, G_TYPE_BOOL }, 75 { 'h', "hardcode", NULL, G_TYPE_BOOL }, 76 { 'n', "noautosync", NULL, G_TYPE_BOOL }, 77 { 'p', "priority", "-1", G_TYPE_NUMBER }, 78 { 's', "slice", "-1", G_TYPE_NUMBER }, 79 G_OPT_SENTINEL 80 }, 81 "[-adfFhnv] [-b balance] [-s slice] name\n" 82 "[-v] -p priority name prov" 83 }, 84 { "create", G_FLAG_VERBOSE, NULL, 85 { 86 { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING }, 87 { 'F', "nofailsync", NULL, G_TYPE_BOOL }, 88 { 'n', "noautosync", NULL, G_TYPE_BOOL }, 89 { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER }, 90 G_OPT_SENTINEL 91 }, 92 "[-Fnv] [-b balance] [-s slice] name prov ..." 93 }, 94 { "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 95 "[-v] name prov ..." 96 }, 97 { "destroy", G_FLAG_VERBOSE, NULL, 98 { 99 { 'f', "force", NULL, G_TYPE_BOOL }, 100 G_OPT_SENTINEL 101 }, 102 "[-fv] name ..." 103 }, 104 { "dump", 0, mirror_main, G_NULL_OPTS, 105 "prov ..." 106 }, 107 { "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 108 "name ..." 109 }, 110 { "label", G_FLAG_VERBOSE, mirror_main, 111 { 112 { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING }, 113 { 'F', "nofailsync", NULL, G_TYPE_BOOL }, 114 { 'h', "hardcode", NULL, G_TYPE_BOOL }, 115 { 'n', "noautosync", NULL, G_TYPE_BOOL }, 116 { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER }, 117 G_OPT_SENTINEL 118 }, 119 "[-Fhnv] [-b balance] [-s slice] name prov ..." 120 }, 121 { "insert", G_FLAG_VERBOSE, NULL, 122 { 123 { 'h', "hardcode", NULL, G_TYPE_BOOL }, 124 { 'i', "inactive", NULL, G_TYPE_BOOL }, 125 { 'p', "priority", GMIRROR_PRIORITY, G_TYPE_NUMBER }, 126 G_OPT_SENTINEL 127 }, 128 "[-hiv] [-p priority] name prov ..." 129 }, 130 { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 131 "[-v] name prov ..." 132 }, 133 { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 134 "[-v] name prov ..." 135 }, 136 { "resize", G_FLAG_VERBOSE, mirror_resize, 137 { 138 { 's', "size", "*", G_TYPE_STRING }, 139 G_OPT_SENTINEL 140 }, 141 "[-s size] [-v] name" 142 }, 143 { "stop", G_FLAG_VERBOSE, NULL, 144 { 145 { 'f', "force", NULL, G_TYPE_BOOL }, 146 G_OPT_SENTINEL 147 }, 148 "[-fv] name ..." 149 }, 150 G_CMD_SENTINEL 151 }; 152 153 static int verbose = 0; 154 155 static void 156 mirror_main(struct gctl_req *req, unsigned flags) 157 { 158 const char *name; 159 160 if ((flags & G_FLAG_VERBOSE) != 0) 161 verbose = 1; 162 163 name = gctl_get_ascii(req, "verb"); 164 if (name == NULL) { 165 gctl_error(req, "No '%s' argument.", "verb"); 166 return; 167 } 168 if (strcmp(name, "label") == 0) 169 mirror_label(req); 170 else if (strcmp(name, "clear") == 0) 171 mirror_clear(req); 172 else if (strcmp(name, "dump") == 0) 173 mirror_dump(req); 174 else if (strcmp(name, "activate") == 0) 175 mirror_activate(req); 176 else 177 gctl_error(req, "Unknown command: %s.", name); 178 } 179 180 static void 181 mirror_label(struct gctl_req *req) 182 { 183 struct g_mirror_metadata md; 184 u_char sector[512]; 185 const char *str; 186 unsigned sectorsize; 187 off_t mediasize; 188 intmax_t val; 189 int error, i, nargs, bal, hardcode; 190 191 bzero(sector, sizeof(sector)); 192 nargs = gctl_get_int(req, "nargs"); 193 if (nargs < 2) { 194 gctl_error(req, "Too few arguments."); 195 return; 196 } 197 198 strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic)); 199 md.md_version = G_MIRROR_VERSION; 200 str = gctl_get_ascii(req, "arg0"); 201 strlcpy(md.md_name, str, sizeof(md.md_name)); 202 md.md_mid = arc4random(); 203 md.md_all = nargs - 1; 204 md.md_mflags = 0; 205 md.md_dflags = 0; 206 md.md_genid = 0; 207 md.md_syncid = 1; 208 md.md_sync_offset = 0; 209 val = gctl_get_intmax(req, "slice"); 210 md.md_slice = val; 211 str = gctl_get_ascii(req, "balance"); 212 bal = balance_id(str); 213 if (bal == -1) { 214 gctl_error(req, "Invalid balance algorithm."); 215 return; 216 } 217 md.md_balance = bal; 218 if (gctl_get_int(req, "noautosync")) 219 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; 220 if (gctl_get_int(req, "nofailsync")) 221 md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; 222 hardcode = gctl_get_int(req, "hardcode"); 223 224 /* 225 * Calculate sectorsize by finding least common multiple from 226 * sectorsizes of every disk and find the smallest mediasize. 227 */ 228 mediasize = 0; 229 sectorsize = 0; 230 for (i = 1; i < nargs; i++) { 231 unsigned ssize; 232 off_t msize; 233 234 str = gctl_get_ascii(req, "arg%d", i); 235 msize = g_get_mediasize(str); 236 ssize = g_get_sectorsize(str); 237 if (msize == 0 || ssize == 0) { 238 gctl_error(req, "Can't get informations about %s: %s.", 239 str, strerror(errno)); 240 return; 241 } 242 msize -= ssize; 243 if (mediasize == 0 || (mediasize > 0 && msize < mediasize)) 244 mediasize = msize; 245 if (sectorsize == 0) 246 sectorsize = ssize; 247 else 248 sectorsize = g_lcm(sectorsize, ssize); 249 } 250 md.md_mediasize = mediasize; 251 md.md_sectorsize = sectorsize; 252 md.md_mediasize -= (md.md_mediasize % md.md_sectorsize); 253 254 /* 255 * Clear last sector first, to spoil all components if device exists. 256 */ 257 for (i = 1; i < nargs; i++) { 258 str = gctl_get_ascii(req, "arg%d", i); 259 error = g_metadata_clear(str, NULL); 260 if (error != 0) { 261 gctl_error(req, "Can't store metadata on %s: %s.", str, 262 strerror(error)); 263 return; 264 } 265 } 266 267 /* 268 * Ok, store metadata (use disk number as priority). 269 */ 270 for (i = 1; i < nargs; i++) { 271 str = gctl_get_ascii(req, "arg%d", i); 272 md.md_did = arc4random(); 273 md.md_priority = i - 1; 274 md.md_provsize = g_get_mediasize(str); 275 assert(md.md_provsize != 0); 276 if (!hardcode) 277 bzero(md.md_provider, sizeof(md.md_provider)); 278 else { 279 if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 280 str += sizeof(_PATH_DEV) - 1; 281 strlcpy(md.md_provider, str, sizeof(md.md_provider)); 282 } 283 mirror_metadata_encode(&md, sector); 284 error = g_metadata_store(str, sector, sizeof(sector)); 285 if (error != 0) { 286 fprintf(stderr, "Can't store metadata on %s: %s.\n", 287 str, strerror(error)); 288 gctl_error(req, "Not fully done."); 289 continue; 290 } 291 if (verbose) 292 printf("Metadata value stored on %s.\n", str); 293 } 294 } 295 296 static void 297 mirror_clear(struct gctl_req *req) 298 { 299 const char *name; 300 int error, i, nargs; 301 302 nargs = gctl_get_int(req, "nargs"); 303 if (nargs < 1) { 304 gctl_error(req, "Too few arguments."); 305 return; 306 } 307 308 for (i = 0; i < nargs; i++) { 309 name = gctl_get_ascii(req, "arg%d", i); 310 error = g_metadata_clear(name, G_MIRROR_MAGIC); 311 if (error != 0) { 312 fprintf(stderr, "Can't clear metadata on %s: %s.\n", 313 name, strerror(error)); 314 gctl_error(req, "Not fully done."); 315 continue; 316 } 317 if (verbose) 318 printf("Metadata cleared on %s.\n", name); 319 } 320 } 321 322 static void 323 mirror_dump(struct gctl_req *req) 324 { 325 struct g_mirror_metadata md, tmpmd; 326 const char *name; 327 int error, i, nargs; 328 329 nargs = gctl_get_int(req, "nargs"); 330 if (nargs < 1) { 331 gctl_error(req, "Too few arguments."); 332 return; 333 } 334 335 for (i = 0; i < nargs; i++) { 336 name = gctl_get_ascii(req, "arg%d", i); 337 error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd), 338 G_MIRROR_MAGIC); 339 if (error != 0) { 340 fprintf(stderr, "Can't read metadata from %s: %s.\n", 341 name, strerror(error)); 342 gctl_error(req, "Not fully done."); 343 continue; 344 } 345 if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) { 346 fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n", 347 name); 348 gctl_error(req, "Not fully done."); 349 continue; 350 } 351 printf("Metadata on %s:\n", name); 352 mirror_metadata_dump(&md); 353 printf("\n"); 354 } 355 } 356 357 static void 358 mirror_activate(struct gctl_req *req) 359 { 360 struct g_mirror_metadata md, tmpmd; 361 const char *name, *path; 362 int error, i, nargs; 363 364 nargs = gctl_get_int(req, "nargs"); 365 if (nargs < 2) { 366 gctl_error(req, "Too few arguments."); 367 return; 368 } 369 name = gctl_get_ascii(req, "arg0"); 370 371 for (i = 1; i < nargs; i++) { 372 path = gctl_get_ascii(req, "arg%d", i); 373 error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd), 374 G_MIRROR_MAGIC); 375 if (error != 0) { 376 fprintf(stderr, "Cannot read metadata from %s: %s.\n", 377 path, strerror(error)); 378 gctl_error(req, "Not fully done."); 379 continue; 380 } 381 if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) { 382 fprintf(stderr, 383 "MD5 hash mismatch for provider %s, skipping.\n", 384 path); 385 gctl_error(req, "Not fully done."); 386 continue; 387 } 388 if (strcmp(md.md_name, name) != 0) { 389 fprintf(stderr, 390 "Provider %s is not the mirror %s component.\n", 391 path, name); 392 gctl_error(req, "Not fully done."); 393 continue; 394 } 395 md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE; 396 mirror_metadata_encode(&md, (u_char *)&tmpmd); 397 error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd)); 398 if (error != 0) { 399 fprintf(stderr, "Cannot write metadata from %s: %s.\n", 400 path, strerror(error)); 401 gctl_error(req, "Not fully done."); 402 continue; 403 } 404 if (verbose) 405 printf("Provider %s activated.\n", path); 406 } 407 } 408 409 static struct gclass * 410 find_class(struct gmesh *mesh, const char *name) 411 { 412 struct gclass *classp; 413 414 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 415 if (strcmp(classp->lg_name, name) == 0) 416 return (classp); 417 } 418 return (NULL); 419 } 420 421 static struct ggeom * 422 find_geom(struct gclass *classp, const char *name) 423 { 424 struct ggeom *gp; 425 426 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 427 if (strcmp(gp->lg_name, name) == 0) 428 return (gp); 429 } 430 return (NULL); 431 } 432 433 static void 434 mirror_resize(struct gctl_req *req, unsigned flags __unused) 435 { 436 struct gmesh mesh; 437 struct gclass *classp; 438 struct ggeom *gp; 439 struct gprovider *pp; 440 struct gconsumer *cp; 441 off_t size; 442 int error, nargs; 443 const char *name; 444 char ssize[30]; 445 446 nargs = gctl_get_int(req, "nargs"); 447 if (nargs < 1) { 448 gctl_error(req, "Too few arguments."); 449 return; 450 } 451 error = geom_gettree(&mesh); 452 if (error) 453 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 454 name = gctl_get_ascii(req, "class"); 455 if (name == NULL) 456 abort(); 457 classp = find_class(&mesh, name); 458 if (classp == NULL) 459 errx(EXIT_FAILURE, "Class %s not found.", name); 460 name = gctl_get_ascii(req, "arg0"); 461 if (name == NULL) 462 abort(); 463 gp = find_geom(classp, name); 464 if (gp == NULL) 465 errx(EXIT_FAILURE, "No such geom: %s.", name); 466 pp = LIST_FIRST(&gp->lg_provider); 467 if (pp == NULL) 468 errx(EXIT_FAILURE, "Provider of geom %s not found.", name); 469 size = pp->lg_mediasize; 470 name = gctl_get_ascii(req, "size"); 471 if (name == NULL) 472 errx(EXIT_FAILURE, "The size is not specified."); 473 if (*name == '*') { 474 #define CSZ(c) ((c)->lg_provider->lg_mediasize - \ 475 (c)->lg_provider->lg_sectorsize) 476 /* Find the maximum possible size */ 477 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { 478 if (CSZ(cp) > size) 479 size = CSZ(cp); 480 } 481 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { 482 if (CSZ(cp) < size) 483 size = CSZ(cp); 484 } 485 #undef CSZ 486 if (size == pp->lg_mediasize) 487 errx(EXIT_FAILURE, 488 "Cannot expand provider %s\n", 489 pp->lg_name); 490 } else { 491 error = g_parse_lba(name, pp->lg_sectorsize, &size); 492 if (error) 493 errc(EXIT_FAILURE, error, "Invalid size param"); 494 size *= pp->lg_sectorsize; 495 } 496 snprintf(ssize, sizeof(ssize), "%ju", (uintmax_t)size); 497 gctl_change_param(req, "size", -1, ssize); 498 geom_deletetree(&mesh); 499 gctl_issue(req); 500 } 501