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