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