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