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
virstor_main(struct gctl_req * req,unsigned flags)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
virstor_label(struct gctl_req * req)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
virstor_clear(struct gctl_req * req)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
virstor_metadata_dump(const struct g_virstor_metadata * md)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
virstor_dump(struct gctl_req * req)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