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