]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.c
ab5071921d04bc91e410ef4357eb2fcda52d6836
[Ventoy.git] / Unix / ventoy_unix_src / FreeBSD / geom_ventoy_src / 12.x / sys / geom / ventoy / g_ventoy.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2020 longpanda <admin@ventoy.net>
5 * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 #include <sys/bio.h>
40 #include <sys/sbuf.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
43 #include <geom/geom.h>
44 #include <geom/ventoy/g_ventoy.h>
45
46 FEATURE(geom_ventoy, "GEOM ventoy support");
47
48 static MALLOC_DEFINE(M_VENTOY, "ventoy_data", "GEOM_VENTOY Data");
49
50 SYSCTL_DECL(_kern_geom);
51 static SYSCTL_NODE(_kern_geom, OID_AUTO, ventoy, CTLFLAG_RW, 0,
52 "GEOM_VENTOY stuff");
53 static u_int g_ventoy_debug = 0;
54 SYSCTL_UINT(_kern_geom_ventoy, OID_AUTO, debug, CTLFLAG_RWTUN, &g_ventoy_debug, 0,
55 "Debug level");
56
57 extern int resource_string_value(const char *name, int unit, const char *resname, const char **result);
58 extern int resource_int_value(const char *name, int unit, const char *resname, int *result);
59
60 static int g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force);
61 static int g_ventoy_destroy_geom(struct gctl_req *req, struct g_class *mp,
62 struct g_geom *gp);
63
64 static g_taste_t g_ventoy_taste;
65 static g_ctl_req_t g_ventoy_config;
66 static g_dumpconf_t g_ventoy_dumpconf;
67
68 static char g_ventoy_disk_uuid[64];
69 static bool g_ventoy_tasted = false;
70 static off_t g_ventoy_disk_size = 0;
71 static off_t g_disk_map_start = 0;
72 static off_t g_disk_map_end = 0;
73
74 struct g_ventoy_map g_ventoy_map_data __attribute__((aligned (65536))) =
75 {
76 { VENTOY_UNIX_SEG_MAGIC0, VENTOY_UNIX_SEG_MAGIC1, VENTOY_UNIX_SEG_MAGIC2, VENTOY_UNIX_SEG_MAGIC3 },
77 { 0, 0, 0, 0 },
78 0, 0,
79 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
80 { { 0, 0 } },
81 { VENTOY_UNIX_SEG_MAGIC0, VENTOY_UNIX_SEG_MAGIC1, VENTOY_UNIX_SEG_MAGIC2, VENTOY_UNIX_SEG_MAGIC3 }
82 };
83
84 struct g_class g_ventoy_class = {
85 .name = G_VENTOY_CLASS_NAME,
86 .version = G_VERSION,
87 .ctlreq = g_ventoy_config,
88 .taste = g_ventoy_taste,
89 .destroy_geom = g_ventoy_destroy_geom
90 };
91
92
93 /*
94 * Greatest Common Divisor.
95 */
96 static u_int
97 gcd(u_int a, u_int b)
98 {
99 u_int c;
100
101 while (b != 0) {
102 c = a;
103 a = b;
104 b = (c % b);
105 }
106 return (a);
107 }
108
109 /*
110 * Least Common Multiple.
111 */
112 static u_int
113 lcm(u_int a, u_int b)
114 {
115
116 return ((a * b) / gcd(a, b));
117 }
118
119 /*
120 * Return the number of valid disks.
121 */
122 static u_int
123 g_ventoy_nvalid(struct g_ventoy_softc *sc)
124 {
125 u_int i, no;
126
127 no = 0;
128 for (i = 0; i < sc->sc_ndisks; i++) {
129 if (sc->sc_disks[i].d_consumer != NULL)
130 no++;
131 }
132
133 return (no);
134 }
135
136 static void
137 g_ventoy_remove_disk(struct g_ventoy_disk *disk)
138 {
139 struct g_consumer *cp;
140 struct g_ventoy_softc *sc;
141
142 g_topology_assert();
143 KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__));
144 sc = disk->d_softc;
145 cp = disk->d_consumer;
146
147 if (!disk->d_removed) {
148 G_VENTOY_DEBUG(0, "Disk %s removed from %s.",
149 cp->provider->name, sc->sc_name);
150 disk->d_removed = 1;
151 }
152
153 if (sc->sc_provider != NULL) {
154 G_VENTOY_DEBUG(0, "Device %s deactivated.",
155 sc->sc_provider->name);
156 g_wither_provider(sc->sc_provider, ENXIO);
157 sc->sc_provider = NULL;
158 }
159
160 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
161 return;
162 disk->d_consumer = NULL;
163 g_detach(cp);
164 g_destroy_consumer(cp);
165 /* If there are no valid disks anymore, remove device. */
166 if (LIST_EMPTY(&sc->sc_geom->consumer))
167 g_ventoy_destroy(sc, 1);
168 }
169
170 static void
171 g_ventoy_orphan(struct g_consumer *cp)
172 {
173 struct g_ventoy_softc *sc;
174 struct g_ventoy_disk *disk;
175 struct g_geom *gp;
176
177 g_topology_assert();
178 gp = cp->geom;
179 sc = gp->softc;
180 if (sc == NULL)
181 return;
182
183 disk = cp->private;
184 if (disk == NULL) /* Possible? */
185 return;
186 g_ventoy_remove_disk(disk);
187 }
188
189 static int
190 g_ventoy_access(struct g_provider *pp, int dr, int dw, int de)
191 {
192 struct g_consumer *cp1, *cp2, *tmp;
193 struct g_ventoy_disk *disk;
194 struct g_geom *gp;
195 int error;
196
197 if (dw > 0) /* readonly */
198 return (EPERM);
199
200 g_topology_assert();
201 gp = pp->geom;
202
203 /* On first open, grab an extra "exclusive" bit */
204 if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
205 de++;
206 /* ... and let go of it on last close */
207 if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0)
208 de--;
209
210 LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) {
211 error = g_access(cp1, dr, dw, de);
212 if (error != 0)
213 goto fail;
214 disk = cp1->private;
215 if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 &&
216 disk->d_removed) {
217 g_ventoy_remove_disk(disk); /* May destroy geom. */
218 }
219 }
220 return (0);
221
222 fail:
223 LIST_FOREACH(cp2, &gp->consumer, consumer) {
224 if (cp1 == cp2)
225 break;
226 g_access(cp2, -dr, -dw, -de);
227 }
228 return (error);
229 }
230
231 static void
232 g_ventoy_kernel_dump(struct bio *bp)
233 {
234 struct g_ventoy_softc *sc;
235 struct g_ventoy_disk *disk;
236 struct bio *cbp;
237 struct g_kerneldump *gkd;
238 u_int i;
239
240 sc = bp->bio_to->geom->softc;
241 gkd = (struct g_kerneldump *)bp->bio_data;
242 for (i = 0; i < sc->sc_ndisks; i++) {
243 if (sc->sc_disks[i].d_start <= gkd->offset &&
244 sc->sc_disks[i].d_end > gkd->offset)
245 break;
246 }
247 if (i == sc->sc_ndisks)
248 g_io_deliver(bp, EOPNOTSUPP);
249 disk = &sc->sc_disks[i];
250 gkd->offset -= disk->d_start;
251 if (gkd->length > disk->d_end - disk->d_start - gkd->offset)
252 gkd->length = disk->d_end - disk->d_start - gkd->offset;
253 cbp = g_clone_bio(bp);
254 if (cbp == NULL) {
255 g_io_deliver(bp, ENOMEM);
256 return;
257 }
258 cbp->bio_done = g_std_done;
259 g_io_request(cbp, disk->d_consumer);
260 G_VENTOY_DEBUG(1, "Kernel dump will go to %s.",
261 disk->d_consumer->provider->name);
262 }
263
264 static void
265 g_ventoy_done(struct bio *bp)
266 {
267 struct g_ventoy_softc *sc;
268 struct bio *pbp;
269
270 pbp = bp->bio_parent;
271 sc = pbp->bio_to->geom->softc;
272 mtx_lock(&sc->sc_lock);
273 if (pbp->bio_error == 0)
274 pbp->bio_error = bp->bio_error;
275 pbp->bio_completed += bp->bio_completed;
276 pbp->bio_inbed++;
277 if (pbp->bio_children == pbp->bio_inbed) {
278 mtx_unlock(&sc->sc_lock);
279 g_io_deliver(pbp, pbp->bio_error);
280 } else
281 mtx_unlock(&sc->sc_lock);
282 g_destroy_bio(bp);
283 }
284
285 static void
286 g_ventoy_flush(struct g_ventoy_softc *sc, struct bio *bp)
287 {
288 struct bio_queue_head queue;
289 struct g_consumer *cp;
290 struct bio *cbp;
291 u_int no;
292
293 bioq_init(&queue);
294 for (no = 0; no < sc->sc_ndisks; no++) {
295 cbp = g_clone_bio(bp);
296 if (cbp == NULL) {
297 while ((cbp = bioq_takefirst(&queue)) != NULL)
298 g_destroy_bio(cbp);
299 if (bp->bio_error == 0)
300 bp->bio_error = ENOMEM;
301 g_io_deliver(bp, bp->bio_error);
302 return;
303 }
304 bioq_insert_tail(&queue, cbp);
305 cbp->bio_done = g_ventoy_done;
306 cbp->bio_caller1 = sc->sc_disks[no].d_consumer;
307 cbp->bio_to = sc->sc_disks[no].d_consumer->provider;
308 }
309 while ((cbp = bioq_takefirst(&queue)) != NULL) {
310 G_VENTOY_LOGREQ(cbp, "Sending request.");
311 cp = cbp->bio_caller1;
312 cbp->bio_caller1 = NULL;
313 g_io_request(cbp, cp);
314 }
315 }
316
317 static void
318 g_ventoy_start(struct bio *bp)
319 {
320 struct bio_queue_head queue;
321 struct g_ventoy_softc *sc;
322 struct g_ventoy_disk *disk;
323 struct g_provider *pp;
324 off_t offset, end, length, off, len;
325 struct bio *cbp;
326 char *addr;
327 u_int no;
328
329 pp = bp->bio_to;
330 sc = pp->geom->softc;
331 /*
332 * If sc == NULL, provider's error should be set and g_ventoy_start()
333 * should not be called at all.
334 */
335 KASSERT(sc != NULL,
336 ("Provider's error should be set (error=%d)(device=%s).",
337 bp->bio_to->error, bp->bio_to->name));
338
339 G_VENTOY_LOGREQ(bp, "Request received.");
340
341 switch (bp->bio_cmd) {
342 case BIO_READ:
343 case BIO_WRITE:
344 case BIO_DELETE:
345 break;
346 case BIO_FLUSH:
347 g_ventoy_flush(sc, bp);
348 return;
349 case BIO_GETATTR:
350 if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) {
351 g_ventoy_kernel_dump(bp);
352 return;
353 }
354 /* To which provider it should be delivered? */
355 /* FALLTHROUGH */
356 default:
357 g_io_deliver(bp, EOPNOTSUPP);
358 return;
359 }
360
361 offset = bp->bio_offset;
362 length = bp->bio_length;
363 if ((bp->bio_flags & BIO_UNMAPPED) != 0)
364 addr = NULL;
365 else
366 addr = bp->bio_data;
367 end = offset + length;
368
369 bioq_init(&queue);
370 for (no = 0; no < sc->sc_ndisks; no++) {
371 disk = &sc->sc_disks[no];
372 if (disk->d_end <= offset)
373 continue;
374 if (disk->d_start >= end)
375 break;
376
377 off = offset - disk->d_start;
378 len = MIN(length, disk->d_end - offset);
379 length -= len;
380 offset += len;
381
382 cbp = g_clone_bio(bp);
383 if (cbp == NULL) {
384 while ((cbp = bioq_takefirst(&queue)) != NULL)
385 g_destroy_bio(cbp);
386 if (bp->bio_error == 0)
387 bp->bio_error = ENOMEM;
388 g_io_deliver(bp, bp->bio_error);
389 return;
390 }
391 bioq_insert_tail(&queue, cbp);
392 /*
393 * Fill in the component buf structure.
394 */
395 if (len == bp->bio_length)
396 cbp->bio_done = g_std_done;
397 else
398 cbp->bio_done = g_ventoy_done;
399 cbp->bio_offset = off + disk->d_map_start;
400 cbp->bio_length = len;
401 if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
402 cbp->bio_ma_offset += (uintptr_t)addr;
403 cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE;
404 cbp->bio_ma_offset %= PAGE_SIZE;
405 cbp->bio_ma_n = round_page(cbp->bio_ma_offset +
406 cbp->bio_length) / PAGE_SIZE;
407 } else
408 cbp->bio_data = addr;
409 addr += len;
410 cbp->bio_to = disk->d_consumer->provider;
411 cbp->bio_caller1 = disk;
412
413 if (length == 0)
414 break;
415 }
416 KASSERT(length == 0,
417 ("Length is still greater than 0 (class=%s, name=%s).",
418 bp->bio_to->geom->class->name, bp->bio_to->geom->name));
419 while ((cbp = bioq_takefirst(&queue)) != NULL) {
420 G_VENTOY_LOGREQ(cbp, "Sending request.");
421 disk = cbp->bio_caller1;
422 cbp->bio_caller1 = NULL;
423 g_io_request(cbp, disk->d_consumer);
424 }
425 }
426
427 static void
428 g_ventoy_check_and_run(struct g_ventoy_softc *sc)
429 {
430 struct g_ventoy_disk *disk;
431 struct g_provider *dp, *pp;
432 u_int no, sectorsize = 0;
433 off_t start;
434
435 g_topology_assert();
436 if (g_ventoy_nvalid(sc) != sc->sc_ndisks)
437 return;
438
439 pp = g_new_providerf(sc->sc_geom, "ventoy/%s", sc->sc_name);
440 pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE |
441 G_PF_ACCEPT_UNMAPPED;
442 start = 0;
443 for (no = 0; no < sc->sc_ndisks; no++) {
444 disk = &sc->sc_disks[no];
445 dp = disk->d_consumer->provider;
446 disk->d_start = start;
447 disk->d_end = disk->d_start + (disk->d_map_end - disk->d_map_start);
448 if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC)
449 disk->d_end -= dp->sectorsize;
450 start = disk->d_end;
451 if (no == 0)
452 sectorsize = dp->sectorsize;
453 else
454 sectorsize = lcm(sectorsize, dp->sectorsize);
455
456 /* A provider underneath us doesn't support unmapped */
457 if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) {
458 G_VENTOY_DEBUG(1, "Cancelling unmapped "
459 "because of %s.", dp->name);
460 pp->flags &= ~G_PF_ACCEPT_UNMAPPED;
461 }
462 }
463 pp->sectorsize = sectorsize;
464 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */
465 pp->mediasize = start;
466 pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize;
467 pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset;
468 sc->sc_provider = pp;
469 g_error_provider(pp, 0);
470
471 G_VENTOY_DEBUG(0, "Device %s activated.", sc->sc_provider->name);
472 }
473
474 static int
475 g_ventoy_read_metadata(struct g_consumer *cp, struct g_ventoy_metadata *md)
476 {
477 struct g_provider *pp;
478 u_char *buf;
479 int error;
480
481 g_topology_assert();
482
483 error = g_access(cp, 1, 0, 0);
484 if (error != 0)
485 return (error);
486 pp = cp->provider;
487 g_topology_unlock();
488 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
489 &error);
490 g_topology_lock();
491 g_access(cp, -1, 0, 0);
492 if (buf == NULL)
493 return (error);
494
495 /* Decode metadata. */
496 ventoy_metadata_decode(buf, md);
497 g_free(buf);
498
499 return (0);
500 }
501
502 /*
503 * Add disk to given device.
504 */
505 static int
506 g_ventoy_add_disk(struct g_ventoy_softc *sc, struct g_provider *pp, u_int no)
507 {
508 struct g_ventoy_disk *disk;
509 struct g_consumer *cp, *fcp;
510 struct g_geom *gp;
511 int error;
512
513 g_topology_assert();
514 /* Metadata corrupted? */
515 if (no >= sc->sc_ndisks)
516 return (EINVAL);
517
518 disk = &sc->sc_disks[no];
519 /* Check if disk is not already attached. */
520 if (disk->d_consumer != NULL)
521 return (EEXIST);
522
523 gp = sc->sc_geom;
524 fcp = LIST_FIRST(&gp->consumer);
525
526 cp = g_new_consumer(gp);
527 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
528 error = g_attach(cp, pp);
529 if (error != 0) {
530 g_destroy_consumer(cp);
531 return (error);
532 }
533
534 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) {
535 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
536 if (error != 0) {
537 g_detach(cp);
538 g_destroy_consumer(cp);
539 return (error);
540 }
541 }
542 if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) {
543 struct g_ventoy_metadata md;
544
545 /* Re-read metadata. */
546 error = g_ventoy_read_metadata(cp, &md);
547 if (error != 0)
548 goto fail;
549
550 if (strcmp(md.md_magic, G_VENTOY_MAGIC) != 0 ||
551 strcmp(md.md_name, sc->sc_name) != 0 ||
552 md.md_id != sc->sc_id) {
553 G_VENTOY_DEBUG(0, "Metadata on %s changed.", pp->name);
554 goto fail;
555 }
556 }
557
558 cp->private = disk;
559 disk->d_consumer = cp;
560 disk->d_softc = sc;
561 disk->d_start = 0; /* not yet */
562 disk->d_end = 0; /* not yet */
563 disk->d_removed = 0;
564
565 disk->d_map_start = g_disk_map_start;
566 disk->d_map_end = g_disk_map_end;
567
568 G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name);
569
570 g_ventoy_check_and_run(sc);
571
572 return (0);
573 fail:
574 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0))
575 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace);
576 g_detach(cp);
577 g_destroy_consumer(cp);
578 return (error);
579 }
580
581 static struct g_geom *
582 g_ventoy_create(struct g_class *mp, const struct g_ventoy_metadata *md,
583 u_int type)
584 {
585 struct g_ventoy_softc *sc;
586 struct g_geom *gp;
587 u_int no;
588
589 G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md->md_name,
590 md->md_id);
591
592 /* One disks is minimum. */
593 if (md->md_all < 1)
594 return (NULL);
595
596 /* Check for duplicate unit */
597 LIST_FOREACH(gp, &mp->geom, geom) {
598 sc = gp->softc;
599 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) {
600 G_VENTOY_DEBUG(0, "Device %s already configured.",
601 gp->name);
602 return (NULL);
603 }
604 }
605 gp = g_new_geomf(mp, "%s", md->md_name);
606 sc = malloc(sizeof(*sc), M_VENTOY, M_WAITOK | M_ZERO);
607 gp->start = g_ventoy_start;
608 gp->spoiled = g_ventoy_orphan;
609 gp->orphan = g_ventoy_orphan;
610 gp->access = g_ventoy_access;
611 gp->dumpconf = g_ventoy_dumpconf;
612
613 sc->sc_id = md->md_id;
614 sc->sc_ndisks = md->md_all;
615 sc->sc_disks = malloc(sizeof(struct g_ventoy_disk) * sc->sc_ndisks,
616 M_VENTOY, M_WAITOK | M_ZERO);
617 for (no = 0; no < sc->sc_ndisks; no++)
618 sc->sc_disks[no].d_consumer = NULL;
619 sc->sc_type = type;
620 mtx_init(&sc->sc_lock, "gventoy lock", NULL, MTX_DEF);
621
622 gp->softc = sc;
623 sc->sc_geom = gp;
624 sc->sc_provider = NULL;
625
626 G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id);
627
628 return (gp);
629 }
630
631 static int
632 g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force)
633 {
634 struct g_provider *pp;
635 struct g_consumer *cp, *cp1;
636 struct g_geom *gp;
637
638 g_topology_assert();
639
640 if (sc == NULL)
641 return (ENXIO);
642
643 pp = sc->sc_provider;
644 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
645 if (force) {
646 G_VENTOY_DEBUG(0, "Device %s is still open, so it "
647 "can't be definitely removed.", pp->name);
648 } else {
649 G_VENTOY_DEBUG(1,
650 "Device %s is still open (r%dw%de%d).", pp->name,
651 pp->acr, pp->acw, pp->ace);
652 return (EBUSY);
653 }
654 }
655
656 gp = sc->sc_geom;
657 LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
658 g_ventoy_remove_disk(cp->private);
659 if (cp1 == NULL)
660 return (0); /* Recursion happened. */
661 }
662 if (!LIST_EMPTY(&gp->consumer))
663 return (EINPROGRESS);
664
665 gp->softc = NULL;
666 KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)",
667 gp->name));
668 free(sc->sc_disks, M_VENTOY);
669 mtx_destroy(&sc->sc_lock);
670 free(sc, M_VENTOY);
671
672 G_VENTOY_DEBUG(0, "Device %s destroyed.", gp->name);
673 g_wither_geom(gp, ENXIO);
674 return (0);
675 }
676
677 static int
678 g_ventoy_destroy_geom(struct gctl_req *req __unused,
679 struct g_class *mp __unused, struct g_geom *gp)
680 {
681 struct g_ventoy_softc *sc;
682
683 sc = gp->softc;
684 return (g_ventoy_destroy(sc, 0));
685 }
686
687 static bool g_vtoy_check_disk(struct g_class *mp, struct g_provider *pp)
688 {
689 int i;
690 bool ret = true;
691 uint8_t *buf;
692 char uuid[64];
693 const char *value;
694 struct g_consumer *cp;
695 struct g_geom *gp;
696 uint8_t mbrdata[] = {
697 0xEB, 0x63, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
698 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
699 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
700 0x56, 0x54, 0x00, 0x47, 0x65, 0x00, 0x48, 0x44, 0x00, 0x52, 0x64, 0x00, 0x20, 0x45, 0x72, 0x0D,
701 };
702
703 if (g_ventoy_disk_size == 0)
704 {
705 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
706 {
707 G_DEBUG("ventoy map data is valid. [OK]\n");
708
709 for (i = 0; i < 16; i++)
710 {
711 sprintf(uuid + i * 2, "%02x", g_ventoy_map_data.diskuuid[i]);
712 }
713 snprintf(g_ventoy_disk_uuid, sizeof(g_ventoy_disk_uuid), "%s", uuid);
714 g_ventoy_disk_size = g_ventoy_map_data.disksize;
715
716 G_DEBUG("ventoy.disksize: %llu\n", (unsigned long long)g_ventoy_disk_size);
717 G_DEBUG("ventoy.diskuuid: <%s>\n", g_ventoy_disk_uuid);
718 }
719 else
720 {
721 G_DEBUG("ventoy map data is invalid, get from resource\n");
722
723 if (resource_string_value("ventoy", 0, "disksize", &value) == 0)
724 {
725 G_DEBUG("ventoy.disksize: %s\n", value);
726 g_ventoy_disk_size = strtouq(value, NULL, 0);
727 }
728
729 if (resource_string_value("ventoy", 0, "diskuuid", &value) == 0)
730 {
731 snprintf(g_ventoy_disk_uuid, sizeof(g_ventoy_disk_uuid), "%s", value);
732 G_DEBUG("ventoy.diskuuid: <%s>\n", value);
733 }
734 }
735 }
736
737 if (g_ventoy_disk_size != pp->mediasize)
738 {
739 return false;
740 }
741
742 if (strncmp(pp->name, "cd", 2) == 0 || strchr(pp->name, '/'))
743 {
744 return false;
745 }
746
747 /* read UUID from disk */
748 gp = g_new_geomf(mp, "ventoy:taste");
749 gp->start = NULL;
750 gp->access = g_ventoy_access;
751 gp->orphan = g_ventoy_orphan;
752 cp = g_new_consumer(gp);
753 g_attach(cp, pp);
754
755 g_access(cp, 1, 0, 0);
756 g_topology_unlock();
757 buf = g_read_data(cp, 0, pp->sectorsize, NULL);
758 g_topology_lock();
759 g_access(cp, -1, 0, 0);
760
761 g_detach(cp);
762 g_destroy_consumer(cp);
763 g_destroy_geom(gp);
764 gp = NULL;
765
766 if (!buf)
767 {
768 return false;
769 }
770
771 for (i = 0; i < 16; i++)
772 {
773 sprintf(uuid + i * 2, "%02x", buf[0x180 + i]);
774 }
775
776 if (strncmp(g_ventoy_disk_uuid, uuid, 32))
777 {
778 ret = false;
779 }
780
781 if (memcmp(mbrdata, buf, 0x30) || memcmp(mbrdata + 0x30, buf + 0x190, 16))
782 {
783 ret = false;
784 }
785
786 g_free(buf);
787
788 if (ret)
789 {
790 G_DEBUG("ventoy disk check OK\n");
791 }
792
793 return ret;
794 }
795
796 static struct g_geom *
797 g_ventoy_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
798 {
799 int i;
800 int error;
801 int disknum;
802 char *endpos;
803 const char *value;
804 struct g_geom *gp;
805 struct g_ventoy_metadata md;
806 struct g_ventoy_softc *sc;
807
808 if (g_ventoy_tasted)
809 {
810 return NULL;
811 }
812
813 G_DEBUG("%s(%s, %s)\n", __func__, mp->name, pp->name);
814 g_topology_assert();
815
816 /* Skip providers that are already open for writing. */
817 if (pp->acw > 0)
818 return (NULL);
819
820 if (!g_vtoy_check_disk(mp, pp))
821 {
822 return NULL;
823 }
824
825 g_ventoy_tasted = true;
826
827 G_DEBUG("######### ventoy disk <%s> #############\n", pp->name);
828
829 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
830 {
831 disknum = (int)g_ventoy_map_data.segnum;
832 G_DEBUG("segnum from map data is:<%d>\n", disknum);
833 }
834 else
835 {
836 resource_int_value("ventoy", 0, "segnum", &disknum);
837 G_DEBUG("segnum from resource is:<%d>\n", disknum);
838 }
839
840 strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic));
841 md.md_version = G_VENTOY_VERSION;
842 strlcpy(md.md_name, "IMAGE", sizeof(md.md_name));
843 md.md_id = arc4random();
844 md.md_no = 0;
845 md.md_all = (uint16_t)disknum;
846 bzero(md.md_provider, sizeof(md.md_provider));
847 /* This field is not important here. */
848 md.md_provsize = 0;
849
850 gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL);
851 if (gp == NULL) {
852 G_VENTOY_DEBUG(0, "Cannot create device %s.",
853 md.md_name);
854 return (NULL);
855 }
856 sc = gp->softc;
857
858 for (i = 0; i < disknum; i ++)
859 {
860 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
861 {
862 G_DEBUG("[map] ventoy segment%d: 0x%llx@0x%llx\n", i,
863 (long long)g_ventoy_map_data.seglist[i].seg_start_bytes,
864 (long long)g_ventoy_map_data.seglist[i].seg_end_bytes);
865
866 g_disk_map_start = (off_t)g_ventoy_map_data.seglist[i].seg_start_bytes;
867 g_disk_map_end = (off_t)g_ventoy_map_data.seglist[i].seg_end_bytes;
868 }
869 else
870 {
871 if (resource_string_value("ventoy", i, "seg", &value) == 0)
872 {
873 g_disk_map_start = strtouq(value, &endpos, 0);
874 g_disk_map_end = strtouq(endpos + 1, NULL, 0);
875 }
876 else
877 {
878 printf("Failed to parse ventoy seg %d\n", i);
879 continue;
880 }
881 G_DEBUG("[resource] ventoy segment%d: %s\n", i, value);
882 }
883
884 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
885 error = g_ventoy_add_disk(sc, pp, i);
886 if (error != 0) {
887 G_VENTOY_DEBUG(0,
888 "Cannot add disk %s to %s (error=%d).", pp->name,
889 gp->name, error);
890 g_ventoy_destroy(sc, 1);
891 return (NULL);
892 }
893
894 g_disk_map_start = 0;
895 g_disk_map_end = 0;
896 }
897
898 return (gp);
899 }
900
901 static void
902 g_ventoy_ctl_create(struct gctl_req *req, struct g_class *mp)
903 {
904 u_int attached, no;
905 struct g_ventoy_metadata md;
906 struct g_provider *pp;
907 struct g_ventoy_softc *sc;
908 struct g_geom *gp;
909 struct sbuf *sb;
910 const char *name;
911 char param[16];
912 int *nargs;
913
914 g_topology_assert();
915 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
916 if (nargs == NULL) {
917 gctl_error(req, "No '%s' argument.", "nargs");
918 return;
919 }
920 if (*nargs < 2) {
921 gctl_error(req, "Too few arguments.");
922 return;
923 }
924
925 strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic));
926 md.md_version = G_VENTOY_VERSION;
927 name = gctl_get_asciiparam(req, "arg0");
928 if (name == NULL) {
929 gctl_error(req, "No 'arg%u' argument.", 0);
930 return;
931 }
932 strlcpy(md.md_name, name, sizeof(md.md_name));
933 md.md_id = arc4random();
934 md.md_no = 0;
935 md.md_all = *nargs - 1;
936 bzero(md.md_provider, sizeof(md.md_provider));
937 /* This field is not important here. */
938 md.md_provsize = 0;
939
940 /* Check all providers are valid */
941 for (no = 1; no < *nargs; no++) {
942 snprintf(param, sizeof(param), "arg%u", no);
943 name = gctl_get_asciiparam(req, param);
944 if (name == NULL) {
945 gctl_error(req, "No 'arg%u' argument.", no);
946 return;
947 }
948 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
949 name += strlen("/dev/");
950 pp = g_provider_by_name(name);
951 if (pp == NULL) {
952 G_VENTOY_DEBUG(1, "Disk %s is invalid.", name);
953 gctl_error(req, "Disk %s is invalid.", name);
954 return;
955 }
956 }
957
958 gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL);
959 if (gp == NULL) {
960 gctl_error(req, "Can't configure %s.", md.md_name);
961 return;
962 }
963
964 sc = gp->softc;
965 sb = sbuf_new_auto();
966 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
967 for (attached = 0, no = 1; no < *nargs; no++) {
968 snprintf(param, sizeof(param), "arg%u", no);
969 name = gctl_get_asciiparam(req, param);
970 if (name == NULL) {
971 gctl_error(req, "No 'arg%d' argument.", no);
972 return;
973 }
974 if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
975 name += strlen("/dev/");
976 pp = g_provider_by_name(name);
977 KASSERT(pp != NULL, ("Provider %s disappear?!", name));
978 if (g_ventoy_add_disk(sc, pp, no - 1) != 0) {
979 G_VENTOY_DEBUG(1, "Disk %u (%s) not attached to %s.",
980 no, pp->name, gp->name);
981 sbuf_printf(sb, " %s", pp->name);
982 continue;
983 }
984 attached++;
985 }
986 sbuf_finish(sb);
987 if (md.md_all != attached) {
988 g_ventoy_destroy(gp->softc, 1);
989 gctl_error(req, "%s", sbuf_data(sb));
990 }
991 sbuf_delete(sb);
992 }
993
994 static struct g_ventoy_softc *
995 g_ventoy_find_device(struct g_class *mp, const char *name)
996 {
997 struct g_ventoy_softc *sc;
998 struct g_geom *gp;
999
1000 LIST_FOREACH(gp, &mp->geom, geom) {
1001 sc = gp->softc;
1002 if (sc == NULL)
1003 continue;
1004 if (strcmp(sc->sc_name, name) == 0)
1005 return (sc);
1006 }
1007 return (NULL);
1008 }
1009
1010 static void
1011 g_ventoy_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1012 {
1013 struct g_ventoy_softc *sc;
1014 int *force, *nargs, error;
1015 const char *name;
1016 char param[16];
1017 u_int i;
1018
1019 g_topology_assert();
1020
1021 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1022 if (nargs == NULL) {
1023 gctl_error(req, "No '%s' argument.", "nargs");
1024 return;
1025 }
1026 if (*nargs <= 0) {
1027 gctl_error(req, "Missing device(s).");
1028 return;
1029 }
1030 force = gctl_get_paraml(req, "force", sizeof(*force));
1031 if (force == NULL) {
1032 gctl_error(req, "No '%s' argument.", "force");
1033 return;
1034 }
1035
1036 for (i = 0; i < (u_int)*nargs; i++) {
1037 snprintf(param, sizeof(param), "arg%u", i);
1038 name = gctl_get_asciiparam(req, param);
1039 if (name == NULL) {
1040 gctl_error(req, "No 'arg%u' argument.", i);
1041 return;
1042 }
1043 sc = g_ventoy_find_device(mp, name);
1044 if (sc == NULL) {
1045 gctl_error(req, "No such device: %s.", name);
1046 return;
1047 }
1048 error = g_ventoy_destroy(sc, *force);
1049 if (error != 0) {
1050 gctl_error(req, "Cannot destroy device %s (error=%d).",
1051 sc->sc_name, error);
1052 return;
1053 }
1054 }
1055 }
1056
1057 static void
1058 g_ventoy_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1059 {
1060 uint32_t *version;
1061
1062 return;
1063
1064 g_topology_assert();
1065
1066 version = gctl_get_paraml(req, "version", sizeof(*version));
1067 if (version == NULL) {
1068 gctl_error(req, "No '%s' argument.", "version");
1069 return;
1070 }
1071 if (*version != G_VENTOY_VERSION) {
1072 gctl_error(req, "Userland and kernel parts are out of sync.");
1073 return;
1074 }
1075
1076 if (strcmp(verb, "create") == 0) {
1077 g_ventoy_ctl_create(req, mp);
1078 return;
1079 } else if (strcmp(verb, "destroy") == 0 ||
1080 strcmp(verb, "stop") == 0) {
1081 g_ventoy_ctl_destroy(req, mp);
1082 return;
1083 }
1084 gctl_error(req, "Unknown verb.");
1085 }
1086
1087 static void
1088 g_ventoy_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1089 struct g_consumer *cp, struct g_provider *pp)
1090 {
1091 struct g_ventoy_softc *sc;
1092
1093 g_topology_assert();
1094 sc = gp->softc;
1095 if (sc == NULL)
1096 return;
1097 if (pp != NULL) {
1098 /* Nothing here. */
1099 } else if (cp != NULL) {
1100 struct g_ventoy_disk *disk;
1101
1102 disk = cp->private;
1103 if (disk == NULL)
1104 return;
1105 sbuf_printf(sb, "%s<End>%jd</End>\n", indent,
1106 (intmax_t)disk->d_end);
1107 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent,
1108 (intmax_t)disk->d_start);
1109 } else {
1110 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id);
1111 sbuf_printf(sb, "%s<Type>", indent);
1112 switch (sc->sc_type) {
1113 case G_VENTOY_TYPE_AUTOMATIC:
1114 sbuf_cat(sb, "AUTOMATIC");
1115 break;
1116 case G_VENTOY_TYPE_MANUAL:
1117 sbuf_cat(sb, "MANUAL");
1118 break;
1119 default:
1120 sbuf_cat(sb, "UNKNOWN");
1121 break;
1122 }
1123 sbuf_cat(sb, "</Type>\n");
1124 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n",
1125 indent, sc->sc_ndisks, g_ventoy_nvalid(sc));
1126 sbuf_printf(sb, "%s<State>", indent);
1127 if (sc->sc_provider != NULL && sc->sc_provider->error == 0)
1128 sbuf_cat(sb, "UP");
1129 else
1130 sbuf_cat(sb, "DOWN");
1131 sbuf_cat(sb, "</State>\n");
1132 }
1133 }
1134
1135 DECLARE_GEOM_CLASS(g_ventoy_class, g_ventoy);
1136 //MODULE_VERSION(geom_ventoy, 0);