1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2005 Ivan Voras <ivoras@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sbin/geom/class/virstor/geom_virstor.c 330726 2018-03-10 02:15:45Z asomers $"); 30 31#include <sys/param.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 <fcntl.h> 40#include <unistd.h> 41#include <libgeom.h> 42#include <err.h> 43#include <assert.h> 44 45#include <core/geom.h> 46#include <misc/subr.h> 47 48#include <geom/virstor/g_virstor_md.h> 49#include <geom/virstor/g_virstor.h> 50 51uint32_t lib_version = G_LIB_VERSION; 52uint32_t version = G_VIRSTOR_VERSION; 53 54#define GVIRSTOR_CHUNK_SIZE "4M" 55#define GVIRSTOR_VIR_SIZE "2T" 56 57#if G_LIB_VERSION == 1 58/* Support RELENG_6 */ 59#define G_TYPE_BOOL G_TYPE_NONE 60#endif 61 62/* 63 * virstor_main gets called by the geom(8) utility 64 */ 65static void virstor_main(struct gctl_req *req, unsigned flags); 66 67struct g_command class_commands[] = { 68 { "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS, 69 "[-v] prov ..." 70 }, 71 { "dump", 0, virstor_main, G_NULL_OPTS, 72 "prov ..." 73 }, 74 { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main, 75 { 76 { 'h', "hardcode", NULL, G_TYPE_BOOL}, 77 { 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER}, 78 { 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER}, 79 G_OPT_SENTINEL 80 }, 81 "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]" 82 }, 83 { "destroy", G_FLAG_VERBOSE, NULL, 84 { 85 { 'f', "force", NULL, G_TYPE_BOOL}, 86 G_OPT_SENTINEL 87 }, 88 "[-fv] name ..." 89 }, 90 { "stop", G_FLAG_VERBOSE, NULL, 91 { 92 { 'f', "force", NULL, G_TYPE_BOOL}, 93 G_OPT_SENTINEL 94 }, 95 "[-fv] name ... (alias for \"destroy\")" 96 }, 97 { "add", G_FLAG_VERBOSE, NULL, 98 { 99 { 'h', "hardcode", NULL, G_TYPE_BOOL}, 100 G_OPT_SENTINEL 101 }, 102 "[-vh] name prov [prov ...]" 103 }, 104 { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, 105 "[-v] name ..." 106 }, 107 G_CMD_SENTINEL 108}; 109 110static int verbose = 0; 111 112/* Helper functions' declarations */ 113static void virstor_clear(struct gctl_req *req); 114static void virstor_dump(struct gctl_req *req); 115static void virstor_label(struct gctl_req *req); 116 117/* Dispatcher function (no real work done here, only verbose flag recorder) */ 118static void 119virstor_main(struct gctl_req *req, unsigned flags) 120{ 121 const char *name; 122 123 if ((flags & G_FLAG_VERBOSE) != 0) 124 verbose = 1; 125 126 name = gctl_get_ascii(req, "verb"); 127 if (name == NULL) { 128 gctl_error(req, "No '%s' argument.", "verb"); 129 return; 130 } 131 if (strcmp(name, "label") == 0) 132 virstor_label(req); 133 else if (strcmp(name, "clear") == 0) 134 virstor_clear(req); 135 else if (strcmp(name, "dump") == 0) 136 virstor_dump(req); 137 else 138 gctl_error(req, "%s: Unknown command: %s.", __func__, name); 139 140 /* No CTASSERT in userland 141 CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS); 142 */ 143} 144 145static void 146pathgen(const char *name, char *path, size_t size) 147{ 148 149 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0) 150 snprintf(path, size, "%s%s", _PATH_DEV, name); 151 else 152 strlcpy(path, name, size); 153} 154 155static int 156my_g_metadata_store(const char *name, u_char *md, size_t size) 157{ 158 char path[MAXPATHLEN]; 159 unsigned sectorsize; 160 off_t mediasize; 161 u_char *sector; 162 int error, fd; 163 164 pathgen(name, path, sizeof(path)); 165 sector = NULL; 166 error = 0; 167 168 fd = open(path, O_RDWR); 169 if (fd == -1) 170 return (errno); 171 mediasize = g_get_mediasize(name); 172 if (mediasize == 0) { 173 error = errno; 174 goto out; 175 } 176 sectorsize = g_get_sectorsize(name); 177 if (sectorsize == 0) { 178 error = errno; 179 goto out; 180 } 181 assert(sectorsize >= size); 182 sector = malloc(sectorsize); 183 if (sector == NULL) { 184 error = ENOMEM; 185 goto out; 186 } 187 bcopy(md, sector, size); 188 bzero(sector + size, sectorsize - size); 189 if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != 190 (ssize_t)sectorsize) { 191 error = errno; 192 goto out; 193 } 194out: 195 if (sector != NULL) 196 free(sector); 197 close(fd); 198 return (error); 199} 200 201/* 202 * Labels a new geom Meaning: parses and checks the parameters, calculates & 203 * writes metadata to the relevant providers so when the next round of 204 * "tasting" comes (which will be just after the provider(s) are closed) geom 205 * can be instantiated with the tasted metadata. 206 */ 207static void 208virstor_label(struct gctl_req *req) 209{ 210 struct g_virstor_metadata md; 211 off_t msize; 212 unsigned char *sect; 213 unsigned int i; 214 size_t ssize, secsize; 215 const char *name; 216 char param[32]; 217 int hardcode, nargs, error; 218 struct virstor_map_entry *map; 219 size_t total_chunks; /* We'll run out of memory if 220 this needs to be bigger. */ 221 unsigned int map_chunks; /* Chunks needed by the map (map size). */ 222 size_t map_size; /* In bytes. */ 223 ssize_t written; 224 int fd; 225 226 nargs = gctl_get_int(req, "nargs"); 227 if (nargs < 2) { 228 gctl_error(req, "Too few arguments (%d): expecting: name " 229 "provider0 [provider1 ...]", nargs); 230 return; 231 } 232 233 hardcode = gctl_get_int(req, "hardcode"); 234 235 /* 236 * Initialize constant parts of metadata: magic signature, version, 237 * name. 238 */ 239 bzero(&md, sizeof(md)); 240 strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic)); 241 md.md_version = G_VIRSTOR_VERSION; 242 name = gctl_get_ascii(req, "arg0"); 243 if (name == NULL) { 244 gctl_error(req, "No 'arg%u' argument.", 0); 245 return; 246 } 247 strlcpy(md.md_name, name, sizeof(md.md_name)); 248 249 md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size"); 250 md.md_chunk_size = gctl_get_intmax(req, "chunk_size"); 251 md.md_count = nargs - 1; 252 253 if (md.md_virsize == 0 || md.md_chunk_size == 0) { 254 gctl_error(req, "Virtual size and chunk size must be non-zero"); 255 return; 256 } 257 258 if (md.md_chunk_size % MAXPHYS != 0) { 259 /* XXX: This is not strictly needed, but it's convenient to 260 * impose some limitations on it, so why not MAXPHYS. */ 261 size_t new_size = rounddown(md.md_chunk_size, MAXPHYS); 262 if (new_size < md.md_chunk_size) 263 new_size += MAXPHYS; 264 fprintf(stderr, "Resizing chunk size to be a multiple of " 265 "MAXPHYS (%d kB).\n", MAXPHYS / 1024); 266 fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024); 267 md.md_chunk_size = new_size; 268 } 269 270 if (md.md_virsize % md.md_chunk_size != 0) { 271 off_t chunk_count = md.md_virsize / md.md_chunk_size; 272 md.md_virsize = chunk_count * md.md_chunk_size; 273 fprintf(stderr, "Resizing virtual size to be a multiple of " 274 "chunk size.\n"); 275 fprintf(stderr, "New virtual size: %zu MB\n", 276 (size_t)(md.md_virsize/(1024 * 1024))); 277 } 278 279 msize = secsize = 0; 280 for (i = 1; i < (unsigned)nargs; i++) { 281 snprintf(param, sizeof(param), "arg%u", i); 282 name = gctl_get_ascii(req, "%s", param); 283 ssize = g_get_sectorsize(name); 284 if (ssize == 0) 285 fprintf(stderr, "%s for %s\n", strerror(errno), name); 286 msize += g_get_mediasize(name); 287 if (secsize == 0) 288 secsize = ssize; 289 else if (secsize != ssize) { 290 gctl_error(req, "Devices need to have same sector size " 291 "(%u on %s needs to be %u).", 292 (u_int)ssize, name, (u_int)secsize); 293 return; 294 } 295 } 296 297 if (secsize == 0) { 298 gctl_error(req, "Device not specified"); 299 return; 300 } 301 302 if (md.md_chunk_size % secsize != 0) { 303 fprintf(stderr, "Error: chunk size is not a multiple of sector " 304 "size."); 305 gctl_error(req, "Chunk size (in bytes) must be multiple of %u.", 306 (unsigned int)secsize); 307 return; 308 } 309 310 total_chunks = md.md_virsize / md.md_chunk_size; 311 map_size = total_chunks * sizeof(*map); 312 assert(md.md_virsize % md.md_chunk_size == 0); 313 314 ssize = map_size % secsize; 315 if (ssize != 0) { 316 size_t add_chunks = (secsize - ssize) / sizeof(*map); 317 total_chunks += add_chunks; 318 md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size; 319 map_size = total_chunks * sizeof(*map); 320 fprintf(stderr, "Resizing virtual size to fit virstor " 321 "structures.\n"); 322 fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n", 323 (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks); 324 } 325 326 if (verbose) 327 printf("Total virtual chunks: %zu (%zu MB each), %ju MB total " 328 "virtual size.\n", 329 total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)), 330 md.md_virsize/(1024 * 1024)); 331 332 if ((off_t)md.md_virsize < msize) 333 fprintf(stderr, "WARNING: Virtual storage size < Physical " 334 "available storage (%ju < %ju)\n", md.md_virsize, msize); 335 336 /* Clear last sector first to spoil all components if device exists. */ 337 if (verbose) 338 printf("Clearing metadata on"); 339 340 for (i = 1; i < (unsigned)nargs; i++) { 341 snprintf(param, sizeof(param), "arg%u", i); 342 name = gctl_get_ascii(req, "%s", param); 343 344 if (verbose) 345 printf(" %s", name); 346 347 msize = g_get_mediasize(name); 348 ssize = g_get_sectorsize(name); 349 if (msize == 0 || ssize == 0) { 350 gctl_error(req, "Can't retrieve information about " 351 "%s: %s.", name, strerror(errno)); 352 return; 353 } 354 if (msize < (off_t) MAX(md.md_chunk_size*4, map_size)) 355 gctl_error(req, "Device %s is too small", name); 356 error = g_metadata_clear(name, NULL); 357 if (error != 0) { 358 gctl_error(req, "Can't clear metadata on %s: %s.", name, 359 strerror(error)); 360 return; 361 } 362 } 363 364 365 /* Write allocation table to the first provider - this needs to be done 366 * before metadata is written because when kernel tastes it it's too 367 * late */ 368 name = gctl_get_ascii(req, "arg1"); /* device with metadata */ 369 if (verbose) 370 printf(".\nWriting allocation table to %s...", name); 371 372 /* How many chunks does the map occupy? */ 373 map_chunks = map_size/md.md_chunk_size; 374 if (map_size % md.md_chunk_size != 0) 375 map_chunks++; 376 if (verbose) { 377 printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks); 378 fflush(stdout); 379 } 380 381 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 382 fd = open(name, O_RDWR); 383 else { 384 sprintf(param, "%s%s", _PATH_DEV, name); 385 fd = open(param, O_RDWR); 386 } 387 if (fd < 0) 388 gctl_error(req, "Cannot open provider %s to write map", name); 389 390 /* Do it with calloc because there might be a need to set up chunk flags 391 * in the future */ 392 map = calloc(total_chunks, sizeof(*map)); 393 if (map == NULL) { 394 gctl_error(req, 395 "Out of memory (need %zu bytes for allocation map)", 396 map_size); 397 } 398 399 written = pwrite(fd, map, map_size, 0); 400 free(map); 401 if ((size_t)written != map_size) { 402 if (verbose) { 403 fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n", 404 map_size, written, strerror(errno)); 405 } 406 gctl_error(req, "Error writing out allocation map!"); 407 return; 408 } 409 close (fd); 410 411 if (verbose) 412 printf("\nStoring metadata on "); 413 414 /* 415 * ID is randomly generated, unique for a geom. This is used to 416 * recognize all providers belonging to one geom. 417 */ 418 md.md_id = arc4random(); 419 420 /* Ok, store metadata. */ 421 for (i = 1; i < (unsigned)nargs; i++) { 422 snprintf(param, sizeof(param), "arg%u", i); 423 name = gctl_get_ascii(req, "%s", param); 424 425 msize = g_get_mediasize(name); 426 ssize = g_get_sectorsize(name); 427 428 if (verbose) 429 printf("%s ", name); 430 431 /* this provider's position/type in geom */ 432 md.no = i - 1; 433 /* this provider's size */ 434 md.provsize = msize; 435 /* chunk allocation info */ 436 md.chunk_count = md.provsize / md.md_chunk_size; 437 if (verbose) 438 printf("(%u chunks) ", md.chunk_count); 439 /* Check to make sure last sector is unused */ 440 if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize)) 441 md.chunk_count--; 442 md.chunk_next = 0; 443 if (i != 1) { 444 md.chunk_reserved = 0; 445 md.flags = 0; 446 } else { 447 md.chunk_reserved = map_chunks * 2; 448 md.flags = VIRSTOR_PROVIDER_ALLOCATED | 449 VIRSTOR_PROVIDER_CURRENT; 450 md.chunk_next = md.chunk_reserved; 451 if (verbose) 452 printf("(%u reserved) ", md.chunk_reserved); 453 } 454 455 if (!hardcode) 456 bzero(md.provider, sizeof(md.provider)); 457 else { 458 /* convert "/dev/something" to "something" */ 459 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) { 460 strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1, 461 sizeof(md.provider)); 462 } else 463 strlcpy(md.provider, name, sizeof(md.provider)); 464 } 465 sect = malloc(ssize); 466 if (sect == NULL) 467 err(1, "Cannot allocate sector of %zu bytes", ssize); 468 bzero(sect, ssize); 469 virstor_metadata_encode(&md, sect); 470 error = my_g_metadata_store(name, sect, ssize); 471 free(sect); 472 if (error != 0) { 473 if (verbose) 474 printf("\n"); 475 fprintf(stderr, "Can't store metadata on %s: %s.\n", 476 name, strerror(error)); 477 gctl_error(req, 478 "Not fully done (error storing metadata)."); 479 return; 480 } 481 } 482#if 0 483 if (verbose) 484 printf("\n"); 485#endif 486} 487 488/* Clears metadata on given provider(s) IF it's owned by us */ 489static void 490virstor_clear(struct gctl_req *req) 491{ 492 const char *name; 493 char param[32]; 494 unsigned i; 495 int nargs, error; 496 int fd; 497 498 nargs = gctl_get_int(req, "nargs"); 499 if (nargs < 1) { 500 gctl_error(req, "Too few arguments."); 501 return; 502 } 503 for (i = 0; i < (unsigned)nargs; i++) { 504 snprintf(param, sizeof(param), "arg%u", i); 505 name = gctl_get_ascii(req, "%s", param); 506 507 error = g_metadata_clear(name, G_VIRSTOR_MAGIC); 508 if (error != 0) { 509 fprintf(stderr, "Can't clear metadata on %s: %s " 510 "(do I own it?)\n", name, strerror(error)); 511 gctl_error(req, 512 "Not fully done (can't clear metadata)."); 513 continue; 514 } 515 if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) 516 fd = open(name, O_RDWR); 517 else { 518 sprintf(param, "%s%s", _PATH_DEV, name); 519 fd = open(param, O_RDWR); 520 } 521 if (fd < 0) { 522 gctl_error(req, "Cannot clear header sector for %s", 523 name); 524 continue; 525 } 526 if (verbose) 527 printf("Metadata cleared on %s.\n", name); 528 } 529} 530 531/* Print some metadata information */ 532static void 533virstor_metadata_dump(const struct g_virstor_metadata *md) 534{ 535 printf(" Magic string: %s\n", md->md_magic); 536 printf(" Metadata version: %u\n", (u_int) md->md_version); 537 printf(" Device name: %s\n", md->md_name); 538 printf(" Device ID: %u\n", (u_int) md->md_id); 539 printf(" Provider index: %u\n", (u_int) md->no); 540 printf(" Active providers: %u\n", (u_int) md->md_count); 541 printf(" Hardcoded provider: %s\n", 542 md->provider[0] != '\0' ? md->provider : "(not hardcoded)"); 543 printf(" Virtual size: %u MB\n", 544 (unsigned int)(md->md_virsize/(1024 * 1024))); 545 printf(" Chunk size: %u kB\n", md->md_chunk_size / 1024); 546 printf(" Chunks on provider: %u\n", md->chunk_count); 547 printf(" Chunks free: %u\n", md->chunk_count - md->chunk_next); 548 printf(" Reserved chunks: %u\n", md->chunk_reserved); 549} 550 551/* Called by geom(8) via gvirstor_main() to dump metadata information */ 552static void 553virstor_dump(struct gctl_req *req) 554{ 555 struct g_virstor_metadata md; 556 u_char tmpmd[512]; /* temporary buffer */ 557 const char *name; 558 char param[16]; 559 int nargs, error, i; 560 561 assert(sizeof(tmpmd) >= sizeof(md)); 562 563 nargs = gctl_get_int(req, "nargs"); 564 if (nargs < 1) { 565 gctl_error(req, "Too few arguments."); 566 return; 567 } 568 for (i = 0; i < nargs; i++) { 569 snprintf(param, sizeof(param), "arg%u", i); 570 name = gctl_get_ascii(req, "%s", param); 571 572 error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd), 573 G_VIRSTOR_MAGIC); 574 if (error != 0) { 575 fprintf(stderr, "Can't read metadata from %s: %s.\n", 576 name, strerror(error)); 577 gctl_error(req, 578 "Not fully done (error reading metadata)."); 579 continue; 580 } 581 virstor_metadata_decode((u_char *) & tmpmd, &md); 582 printf("Metadata on %s:\n", name); 583 virstor_metadata_dump(&md); 584 printf("\n"); 585 } 586} 587