1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2005 Ivan Voras <[email protected]>
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
virstor_main(struct gctl_req * req,unsigned flags)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
virstor_label(struct gctl_req * req)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
virstor_clear(struct gctl_req * req)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
virstor_metadata_dump(const struct g_virstor_metadata * md)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
virstor_dump(struct gctl_req * req)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