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