]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/13.x/sys/geom/ventoy/g_ventoy.c
1924a06eac03882ce72acb4add448c4ebde03055
[Ventoy.git] / Unix / ventoy_unix_src / FreeBSD / geom_ventoy_src / 13.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 | CTLFLAG_MPSAFE, 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_candelete(struct bio *bp)
233 {
234 struct g_ventoy_softc *sc;
235 struct g_ventoy_disk *disk;
236 int i, val;
237
238 sc = bp->bio_to->geom->softc;
239 for (i = 0; i < sc->sc_ndisks; i++) {
240 disk = &sc->sc_disks[i];
241 if (!disk->d_removed && disk->d_candelete)
242 break;
243 }
244 val = i < sc->sc_ndisks;
245 g_handleattr(bp, "GEOM::candelete", &val, sizeof(val));
246 }
247
248 static void
249 g_ventoy_kernel_dump(struct bio *bp)
250 {
251 struct g_ventoy_softc *sc;
252 struct g_ventoy_disk *disk;
253 struct bio *cbp;
254 struct g_kerneldump *gkd;
255 u_int i;
256
257 sc = bp->bio_to->geom->softc;
258 gkd = (struct g_kerneldump *)bp->bio_data;
259 for (i = 0; i < sc->sc_ndisks; i++) {
260 if (sc->sc_disks[i].d_start <= gkd->offset &&
261 sc->sc_disks[i].d_end > gkd->offset)
262 break;
263 }
264 if (i == sc->sc_ndisks) {
265 g_io_deliver(bp, EOPNOTSUPP);
266 return;
267 }
268 disk = &sc->sc_disks[i];
269 gkd->offset -= disk->d_start;
270 if (gkd->length > disk->d_end - disk->d_start - gkd->offset)
271 gkd->length = disk->d_end - disk->d_start - gkd->offset;
272 cbp = g_clone_bio(bp);
273 if (cbp == NULL) {
274 g_io_deliver(bp, ENOMEM);
275 return;
276 }
277 cbp->bio_done = g_std_done;
278 g_io_request(cbp, disk->d_consumer);
279 G_VENTOY_DEBUG(1, "Kernel dump will go to %s.",
280 disk->d_consumer->provider->name);
281 }
282
283 static void
284 g_ventoy_done(struct bio *bp)
285 {
286 struct g_ventoy_softc *sc;
287 struct bio *pbp;
288
289 pbp = bp->bio_parent;
290 sc = pbp->bio_to->geom->softc;
291 mtx_lock(&sc->sc_lock);
292 if (pbp->bio_error == 0)
293 pbp->bio_error = bp->bio_error;
294 pbp->bio_completed += bp->bio_completed;
295 pbp->bio_inbed++;
296 if (pbp->bio_children == pbp->bio_inbed) {
297 mtx_unlock(&sc->sc_lock);
298 g_io_deliver(pbp, pbp->bio_error);
299 } else
300 mtx_unlock(&sc->sc_lock);
301 g_destroy_bio(bp);
302 }
303
304 static void
305 g_ventoy_flush(struct g_ventoy_softc *sc, struct bio *bp)
306 {
307 struct bio_queue_head queue;
308 struct g_consumer *cp;
309 struct bio *cbp;
310 u_int no;
311
312 bioq_init(&queue);
313 for (no = 0; no < sc->sc_ndisks; no++) {
314 cbp = g_clone_bio(bp);
315 if (cbp == NULL) {
316 while ((cbp = bioq_takefirst(&queue)) != NULL)
317 g_destroy_bio(cbp);
318 if (bp->bio_error == 0)
319 bp->bio_error = ENOMEM;
320 g_io_deliver(bp, bp->bio_error);
321 return;
322 }
323 bioq_insert_tail(&queue, cbp);
324 cbp->bio_done = g_ventoy_done;
325 cbp->bio_caller1 = sc->sc_disks[no].d_consumer;
326 cbp->bio_to = sc->sc_disks[no].d_consumer->provider;
327 }
328 while ((cbp = bioq_takefirst(&queue)) != NULL) {
329 G_VENTOY_LOGREQ(cbp, "Sending request.");
330 cp = cbp->bio_caller1;
331 cbp->bio_caller1 = NULL;
332 g_io_request(cbp, cp);
333 }
334 }
335
336 static void
337 g_ventoy_start(struct bio *bp)
338 {
339 struct bio_queue_head queue;
340 struct g_ventoy_softc *sc;
341 struct g_ventoy_disk *disk;
342 struct g_provider *pp;
343 off_t offset, end, length, off, len;
344 struct bio *cbp;
345 char *addr;
346 u_int no;
347
348 pp = bp->bio_to;
349 sc = pp->geom->softc;
350 /*
351 * If sc == NULL, provider's error should be set and g_ventoy_start()
352 * should not be called at all.
353 */
354 KASSERT(sc != NULL,
355 ("Provider's error should be set (error=%d)(device=%s).",
356 bp->bio_to->error, bp->bio_to->name));
357
358 G_VENTOY_LOGREQ(bp, "Request received.");
359
360 switch (bp->bio_cmd) {
361 case BIO_READ:
362 case BIO_WRITE:
363 case BIO_DELETE:
364 break;
365 case BIO_SPEEDUP:
366 case BIO_FLUSH:
367 g_ventoy_flush(sc, bp);
368 return;
369 case BIO_GETATTR:
370 if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) {
371 g_ventoy_kernel_dump(bp);
372 return;
373 } else if (strcmp("GEOM::candelete", bp->bio_attribute) == 0) {
374 g_ventoy_candelete(bp);
375 return;
376 }
377 /* To which provider it should be delivered? */
378 /* FALLTHROUGH */
379 default:
380 g_io_deliver(bp, EOPNOTSUPP);
381 return;
382 }
383
384 offset = bp->bio_offset;
385 length = bp->bio_length;
386 if ((bp->bio_flags & BIO_UNMAPPED) != 0)
387 addr = NULL;
388 else
389 addr = bp->bio_data;
390 end = offset + length;
391
392 bioq_init(&queue);
393 for (no = 0; no < sc->sc_ndisks; no++) {
394 disk = &sc->sc_disks[no];
395 if (disk->d_end <= offset)
396 continue;
397 if (disk->d_start >= end)
398 break;
399
400 off = offset - disk->d_start;
401 len = MIN(length, disk->d_end - offset);
402 length -= len;
403 offset += len;
404
405 cbp = g_clone_bio(bp);
406 if (cbp == NULL) {
407 while ((cbp = bioq_takefirst(&queue)) != NULL)
408 g_destroy_bio(cbp);
409 if (bp->bio_error == 0)
410 bp->bio_error = ENOMEM;
411 g_io_deliver(bp, bp->bio_error);
412 return;
413 }
414 bioq_insert_tail(&queue, cbp);
415 /*
416 * Fill in the component buf structure.
417 */
418 if (len == bp->bio_length)
419 cbp->bio_done = g_std_done;
420 else
421 cbp->bio_done = g_ventoy_done;
422 cbp->bio_offset = off + disk->d_map_start;
423 cbp->bio_length = len;
424 if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
425 cbp->bio_ma_offset += (uintptr_t)addr;
426 cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE;
427 cbp->bio_ma_offset %= PAGE_SIZE;
428 cbp->bio_ma_n = round_page(cbp->bio_ma_offset +
429 cbp->bio_length) / PAGE_SIZE;
430 } else
431 cbp->bio_data = addr;
432 addr += len;
433 cbp->bio_to = disk->d_consumer->provider;
434 cbp->bio_caller1 = disk;
435
436 if (length == 0)
437 break;
438 }
439 KASSERT(length == 0,
440 ("Length is still greater than 0 (class=%s, name=%s).",
441 bp->bio_to->geom->class->name, bp->bio_to->geom->name));
442 while ((cbp = bioq_takefirst(&queue)) != NULL) {
443 G_VENTOY_LOGREQ(cbp, "Sending request.");
444 disk = cbp->bio_caller1;
445 cbp->bio_caller1 = NULL;
446 g_io_request(cbp, disk->d_consumer);
447 }
448 }
449
450 static void
451 g_ventoy_check_and_run(struct g_ventoy_softc *sc)
452 {
453 struct g_ventoy_disk *disk;
454 struct g_provider *dp, *pp;
455 u_int no, sectorsize = 0;
456 off_t start;
457 int error;
458
459 g_topology_assert();
460 if (g_ventoy_nvalid(sc) != sc->sc_ndisks)
461 return;
462
463 pp = g_new_providerf(sc->sc_geom, "ventoy/%s", sc->sc_name);
464 pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE |
465 G_PF_ACCEPT_UNMAPPED;
466 start = 0;
467 for (no = 0; no < sc->sc_ndisks; no++) {
468 disk = &sc->sc_disks[no];
469 dp = disk->d_consumer->provider;
470 disk->d_start = start;
471 disk->d_end = disk->d_start + (disk->d_map_end - disk->d_map_start);
472 if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC)
473 disk->d_end -= dp->sectorsize;
474 start = disk->d_end;
475 error = g_access(disk->d_consumer, 1, 0, 0);
476 if (error == 0) {
477 error = g_getattr("GEOM::candelete", disk->d_consumer,
478 &disk->d_candelete);
479 if (error != 0)
480 disk->d_candelete = 0;
481 (void)g_access(disk->d_consumer, -1, 0, 0);
482 } else
483 G_VENTOY_DEBUG(1, "Failed to access disk %s, error %d.",
484 dp->name, error);
485 if (no == 0)
486 sectorsize = dp->sectorsize;
487 else
488 sectorsize = lcm(sectorsize, dp->sectorsize);
489
490 /* A provider underneath us doesn't support unmapped */
491 if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) {
492 G_VENTOY_DEBUG(1, "Cancelling unmapped "
493 "because of %s.", dp->name);
494 pp->flags &= ~G_PF_ACCEPT_UNMAPPED;
495 }
496 }
497 pp->sectorsize = sectorsize;
498 /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */
499 pp->mediasize = start;
500 pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize;
501 pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset;
502 sc->sc_provider = pp;
503 g_error_provider(pp, 0);
504
505 G_VENTOY_DEBUG(0, "Device %s activated.", sc->sc_provider->name);
506 }
507
508 static int
509 g_ventoy_read_metadata(struct g_consumer *cp, struct g_ventoy_metadata *md)
510 {
511 struct g_provider *pp;
512 u_char *buf;
513 int error;
514
515 g_topology_assert();
516
517 error = g_access(cp, 1, 0, 0);
518 if (error != 0)
519 return (error);
520 pp = cp->provider;
521 g_topology_unlock();
522 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
523 &error);
524 g_topology_lock();
525 g_access(cp, -1, 0, 0);
526 if (buf == NULL)
527 return (error);
528
529 /* Decode metadata. */
530 ventoy_metadata_decode(buf, md);
531 g_free(buf);
532
533 return (0);
534 }
535
536 /*
537 * Add disk to given device.
538 */
539 static int
540 g_ventoy_add_disk(struct g_ventoy_softc *sc, struct g_provider *pp, u_int no)
541 {
542 struct g_ventoy_disk *disk;
543 struct g_consumer *cp, *fcp;
544 struct g_geom *gp;
545 int error;
546
547 g_topology_assert();
548 /* Metadata corrupted? */
549 if (no >= sc->sc_ndisks)
550 return (EINVAL);
551
552 disk = &sc->sc_disks[no];
553 /* Check if disk is not already attached. */
554 if (disk->d_consumer != NULL)
555 return (EEXIST);
556
557 gp = sc->sc_geom;
558 fcp = LIST_FIRST(&gp->consumer);
559
560 cp = g_new_consumer(gp);
561 cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
562 error = g_attach(cp, pp);
563 if (error != 0) {
564 g_destroy_consumer(cp);
565 return (error);
566 }
567
568 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) {
569 error = g_access(cp, fcp->acr, fcp->acw, fcp->ace);
570 if (error != 0) {
571 g_detach(cp);
572 g_destroy_consumer(cp);
573 return (error);
574 }
575 }
576 if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) {
577 struct g_ventoy_metadata md;
578
579 /* Re-read metadata. */
580 error = g_ventoy_read_metadata(cp, &md);
581 if (error != 0)
582 goto fail;
583
584 if (strcmp(md.md_magic, G_VENTOY_MAGIC) != 0 ||
585 strcmp(md.md_name, sc->sc_name) != 0 ||
586 md.md_id != sc->sc_id) {
587 G_VENTOY_DEBUG(0, "Metadata on %s changed.", pp->name);
588 goto fail;
589 }
590 }
591
592 cp->private = disk;
593 disk->d_consumer = cp;
594 disk->d_softc = sc;
595 disk->d_start = 0; /* not yet */
596 disk->d_end = 0; /* not yet */
597 disk->d_removed = 0;
598
599 disk->d_map_start = g_disk_map_start;
600 disk->d_map_end = g_disk_map_end;
601
602 G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name);
603
604 g_ventoy_check_and_run(sc);
605
606 return (0);
607 fail:
608 if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0))
609 g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace);
610 g_detach(cp);
611 g_destroy_consumer(cp);
612 return (error);
613 }
614
615 static struct g_geom *
616 g_ventoy_create(struct g_class *mp, const struct g_ventoy_metadata *md,
617 u_int type)
618 {
619 struct g_ventoy_softc *sc;
620 struct g_geom *gp;
621 u_int no;
622
623 G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md->md_name,
624 md->md_id);
625
626 /* One disks is minimum. */
627 if (md->md_all < 1)
628 return (NULL);
629
630 /* Check for duplicate unit */
631 LIST_FOREACH(gp, &mp->geom, geom) {
632 sc = gp->softc;
633 if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) {
634 G_VENTOY_DEBUG(0, "Device %s already configured.",
635 gp->name);
636 return (NULL);
637 }
638 }
639 gp = g_new_geomf(mp, "%s", md->md_name);
640 sc = malloc(sizeof(*sc), M_VENTOY, M_WAITOK | M_ZERO);
641 gp->start = g_ventoy_start;
642 gp->spoiled = g_ventoy_orphan;
643 gp->orphan = g_ventoy_orphan;
644 gp->access = g_ventoy_access;
645 gp->dumpconf = g_ventoy_dumpconf;
646
647 sc->sc_id = md->md_id;
648 sc->sc_ndisks = md->md_all;
649 sc->sc_disks = malloc(sizeof(struct g_ventoy_disk) * sc->sc_ndisks,
650 M_VENTOY, M_WAITOK | M_ZERO);
651 for (no = 0; no < sc->sc_ndisks; no++)
652 sc->sc_disks[no].d_consumer = NULL;
653 sc->sc_type = type;
654 mtx_init(&sc->sc_lock, "gventoy lock", NULL, MTX_DEF);
655
656 gp->softc = sc;
657 sc->sc_geom = gp;
658 sc->sc_provider = NULL;
659
660 G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id);
661
662 return (gp);
663 }
664
665 static int
666 g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force)
667 {
668 struct g_provider *pp;
669 struct g_consumer *cp, *cp1;
670 struct g_geom *gp;
671
672 g_topology_assert();
673
674 if (sc == NULL)
675 return (ENXIO);
676
677 pp = sc->sc_provider;
678 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) {
679 if (force) {
680 G_VENTOY_DEBUG(0, "Device %s is still open, so it "
681 "can't be definitely removed.", pp->name);
682 } else {
683 G_VENTOY_DEBUG(1,
684 "Device %s is still open (r%dw%de%d).", pp->name,
685 pp->acr, pp->acw, pp->ace);
686 return (EBUSY);
687 }
688 }
689
690 gp = sc->sc_geom;
691 LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) {
692 g_ventoy_remove_disk(cp->private);
693 if (cp1 == NULL)
694 return (0); /* Recursion happened. */
695 }
696 if (!LIST_EMPTY(&gp->consumer))
697 return (EINPROGRESS);
698
699 gp->softc = NULL;
700 KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)",
701 gp->name));
702 free(sc->sc_disks, M_VENTOY);
703 mtx_destroy(&sc->sc_lock);
704 free(sc, M_VENTOY);
705
706 G_VENTOY_DEBUG(0, "Device %s destroyed.", gp->name);
707 g_wither_geom(gp, ENXIO);
708 return (0);
709 }
710
711 static int
712 g_ventoy_destroy_geom(struct gctl_req *req __unused,
713 struct g_class *mp __unused, struct g_geom *gp)
714 {
715 struct g_ventoy_softc *sc;
716
717 sc = gp->softc;
718 return (g_ventoy_destroy(sc, 0));
719 }
720
721 static bool g_vtoy_check_disk(struct g_class *mp, struct g_provider *pp)
722 {
723 int i;
724 bool ret = true;
725 uint8_t *buf;
726 char uuid[64];
727 const char *value;
728 struct g_consumer *cp;
729 struct g_geom *gp;
730 uint8_t mbrdata[] = {
731 0xEB, 0x63, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
732 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
733 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
734 0x56, 0x54, 0x00, 0x47, 0x65, 0x00, 0x48, 0x44, 0x00, 0x52, 0x64, 0x00, 0x20, 0x45, 0x72, 0x0D,
735 };
736
737 if (g_ventoy_disk_size == 0)
738 {
739 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
740 {
741 G_DEBUG("ventoy map data is valid. [OK]\n");
742
743 for (i = 0; i < 16; i++)
744 {
745 sprintf(uuid + i * 2, "%02x", g_ventoy_map_data.diskuuid[i]);
746 }
747 snprintf(g_ventoy_disk_uuid, sizeof(g_ventoy_disk_uuid), "%s", uuid);
748 g_ventoy_disk_size = g_ventoy_map_data.disksize;
749
750 G_DEBUG("ventoy.disksize: %llu\n", (unsigned long long)g_ventoy_disk_size);
751 G_DEBUG("ventoy.diskuuid: <%s>\n", g_ventoy_disk_uuid);
752 }
753 else
754 {
755 G_DEBUG("ventoy map data is invalid, get from resource\n");
756
757 if (resource_string_value("ventoy", 0, "disksize", &value) == 0)
758 {
759 G_DEBUG("ventoy.disksize: %s\n", value);
760 g_ventoy_disk_size = strtouq(value, NULL, 0);
761 }
762
763 if (resource_string_value("ventoy", 0, "diskuuid", &value) == 0)
764 {
765 snprintf(g_ventoy_disk_uuid, sizeof(g_ventoy_disk_uuid), "%s", value);
766 G_DEBUG("ventoy.diskuuid: <%s>\n", value);
767 }
768 }
769 }
770
771 if (g_ventoy_disk_size != pp->mediasize)
772 {
773 return false;
774 }
775
776 if (strncmp(pp->name, "cd", 2) == 0 || strchr(pp->name, '/'))
777 {
778 return false;
779 }
780
781 /* read UUID from disk */
782 gp = g_new_geomf(mp, "ventoy:taste");
783 gp->start = NULL;
784 gp->access = g_ventoy_access;
785 gp->orphan = g_ventoy_orphan;
786 cp = g_new_consumer(gp);
787 g_attach(cp, pp);
788
789 g_access(cp, 1, 0, 0);
790 g_topology_unlock();
791 buf = g_read_data(cp, 0, pp->sectorsize, NULL);
792 g_topology_lock();
793 g_access(cp, -1, 0, 0);
794
795 g_detach(cp);
796 g_destroy_consumer(cp);
797 g_destroy_geom(gp);
798 gp = NULL;
799
800 if (!buf)
801 {
802 return false;
803 }
804
805 for (i = 0; i < 16; i++)
806 {
807 sprintf(uuid + i * 2, "%02x", buf[0x180 + i]);
808 }
809
810 if (strncmp(g_ventoy_disk_uuid, uuid, 32))
811 {
812 ret = false;
813 }
814
815 if (memcmp(mbrdata, buf, 0x30) || memcmp(mbrdata + 0x30, buf + 0x190, 16))
816 {
817 ret = false;
818 }
819
820 g_free(buf);
821
822 if (ret)
823 {
824 G_DEBUG("ventoy disk check OK\n");
825 }
826
827 return ret;
828 }
829
830 static struct g_geom *
831 g_ventoy_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
832 {
833 int i;
834 int error;
835 int disknum;
836 char *endpos;
837 const char *value;
838 struct g_geom *gp;
839 struct g_ventoy_metadata md;
840 struct g_ventoy_softc *sc;
841
842 if (g_ventoy_tasted)
843 {
844 return NULL;
845 }
846
847 G_DEBUG("%s(%s, %s)\n", __func__, mp->name, pp->name);
848 g_topology_assert();
849
850 /* Skip providers that are already open for writing. */
851 if (pp->acw > 0)
852 return (NULL);
853
854 if (!g_vtoy_check_disk(mp, pp))
855 {
856 return NULL;
857 }
858
859 g_ventoy_tasted = true;
860
861 G_DEBUG("######### ventoy disk <%s> #############\n", pp->name);
862
863 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
864 {
865 disknum = (int)g_ventoy_map_data.segnum;
866 G_DEBUG("segnum from map data is:<%d>\n", disknum);
867 }
868 else
869 {
870 resource_int_value("ventoy", 0, "segnum", &disknum);
871 G_DEBUG("segnum from resource is:<%d>\n", disknum);
872 }
873
874 strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic));
875 md.md_version = G_VENTOY_VERSION;
876 strlcpy(md.md_name, "IMAGE", sizeof(md.md_name));
877 md.md_id = arc4random();
878 md.md_no = 0;
879 md.md_all = (uint16_t)disknum;
880 bzero(md.md_provider, sizeof(md.md_provider));
881 /* This field is not important here. */
882 md.md_provsize = 0;
883
884 gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL);
885 if (gp == NULL) {
886 G_VENTOY_DEBUG(0, "Cannot create device %s.",
887 md.md_name);
888 return (NULL);
889 }
890 sc = gp->softc;
891
892 for (i = 0; i < disknum; i ++)
893 {
894 if (VENTOY_MAP_VALID(g_ventoy_map_data.magic2))
895 {
896 G_DEBUG("[map] ventoy segment%d: 0x%llx@0x%llx\n", i,
897 (long long)g_ventoy_map_data.seglist[i].seg_start_bytes,
898 (long long)g_ventoy_map_data.seglist[i].seg_end_bytes);
899
900 g_disk_map_start = (off_t)g_ventoy_map_data.seglist[i].seg_start_bytes;
901 g_disk_map_end = (off_t)g_ventoy_map_data.seglist[i].seg_end_bytes;
902 }
903 else
904 {
905 if (resource_string_value("ventoy", i, "seg", &value) == 0)
906 {
907 g_disk_map_start = strtouq(value, &endpos, 0);
908 g_disk_map_end = strtouq(endpos + 1, NULL, 0);
909 }
910 else
911 {
912 printf("Failed to parse ventoy seg %d\n", i);
913 continue;
914 }
915 G_DEBUG("[resource] ventoy segment%d: %s\n", i, value);
916 }
917
918 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name);
919 error = g_ventoy_add_disk(sc, pp, i);
920 if (error != 0) {
921 G_VENTOY_DEBUG(0,
922 "Cannot add disk %s to %s (error=%d).", pp->name,
923 gp->name, error);
924 g_ventoy_destroy(sc, 1);
925 return (NULL);
926 }
927
928 g_disk_map_start = 0;
929 g_disk_map_end = 0;
930 }
931
932 return (gp);
933 }
934
935 static void
936 g_ventoy_ctl_create(struct gctl_req *req, struct g_class *mp)
937 {
938 u_int attached, no;
939 struct g_ventoy_metadata md;
940 struct g_provider *pp;
941 struct g_ventoy_softc *sc;
942 struct g_geom *gp;
943 struct sbuf *sb;
944 const char *name;
945 char param[16];
946 int *nargs;
947
948 g_topology_assert();
949 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
950 if (nargs == NULL) {
951 gctl_error(req, "No '%s' argument.", "nargs");
952 return;
953 }
954 if (*nargs < 2) {
955 gctl_error(req, "Too few arguments.");
956 return;
957 }
958
959 strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic));
960 md.md_version = G_VENTOY_VERSION;
961 name = gctl_get_asciiparam(req, "arg0");
962 if (name == NULL) {
963 gctl_error(req, "No 'arg%u' argument.", 0);
964 return;
965 }
966 strlcpy(md.md_name, name, sizeof(md.md_name));
967 md.md_id = arc4random();
968 md.md_no = 0;
969 md.md_all = *nargs - 1;
970 bzero(md.md_provider, sizeof(md.md_provider));
971 /* This field is not important here. */
972 md.md_provsize = 0;
973
974 /* Check all providers are valid */
975 for (no = 1; no < *nargs; no++) {
976 snprintf(param, sizeof(param), "arg%u", no);
977 pp = gctl_get_provider(req, param);
978 if (pp == NULL)
979 return;
980 }
981
982 gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL);
983 if (gp == NULL) {
984 gctl_error(req, "Can't configure %s.", md.md_name);
985 return;
986 }
987
988 sc = gp->softc;
989 sb = sbuf_new_auto();
990 sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name);
991 for (attached = 0, no = 1; no < *nargs; no++) {
992 snprintf(param, sizeof(param), "arg%u", no);
993 pp = gctl_get_provider(req, param);
994 if (pp == NULL) {
995 name = gctl_get_asciiparam(req, param);
996 MPASS(name != NULL);
997 sbuf_printf(sb, " %s", name);
998 continue;
999 }
1000 if (g_ventoy_add_disk(sc, pp, no - 1) != 0) {
1001 G_VENTOY_DEBUG(1, "Disk %u (%s) not attached to %s.",
1002 no, pp->name, gp->name);
1003 sbuf_printf(sb, " %s", pp->name);
1004 continue;
1005 }
1006 attached++;
1007 }
1008 sbuf_finish(sb);
1009 if (md.md_all != attached) {
1010 g_ventoy_destroy(gp->softc, 1);
1011 gctl_error(req, "%s", sbuf_data(sb));
1012 }
1013 sbuf_delete(sb);
1014 }
1015
1016 static struct g_ventoy_softc *
1017 g_ventoy_find_device(struct g_class *mp, const char *name)
1018 {
1019 struct g_ventoy_softc *sc;
1020 struct g_geom *gp;
1021
1022 if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
1023 name += strlen(_PATH_DEV);
1024
1025 LIST_FOREACH(gp, &mp->geom, geom) {
1026 sc = gp->softc;
1027 if (sc == NULL)
1028 continue;
1029 if (strcmp(sc->sc_name, name) == 0)
1030 return (sc);
1031 }
1032 return (NULL);
1033 }
1034
1035 static void
1036 g_ventoy_ctl_destroy(struct gctl_req *req, struct g_class *mp)
1037 {
1038 struct g_ventoy_softc *sc;
1039 int *force, *nargs, error;
1040 const char *name;
1041 char param[16];
1042 u_int i;
1043
1044 g_topology_assert();
1045
1046 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
1047 if (nargs == NULL) {
1048 gctl_error(req, "No '%s' argument.", "nargs");
1049 return;
1050 }
1051 if (*nargs <= 0) {
1052 gctl_error(req, "Missing device(s).");
1053 return;
1054 }
1055 force = gctl_get_paraml(req, "force", sizeof(*force));
1056 if (force == NULL) {
1057 gctl_error(req, "No '%s' argument.", "force");
1058 return;
1059 }
1060
1061 for (i = 0; i < (u_int)*nargs; i++) {
1062 snprintf(param, sizeof(param), "arg%u", i);
1063 name = gctl_get_asciiparam(req, param);
1064 if (name == NULL) {
1065 gctl_error(req, "No 'arg%u' argument.", i);
1066 return;
1067 }
1068 sc = g_ventoy_find_device(mp, name);
1069 if (sc == NULL) {
1070 gctl_error(req, "No such device: %s.", name);
1071 return;
1072 }
1073 error = g_ventoy_destroy(sc, *force);
1074 if (error != 0) {
1075 gctl_error(req, "Cannot destroy device %s (error=%d).",
1076 sc->sc_name, error);
1077 return;
1078 }
1079 }
1080 }
1081
1082 static void
1083 g_ventoy_config(struct gctl_req *req, struct g_class *mp, const char *verb)
1084 {
1085 uint32_t *version;
1086
1087 return;
1088
1089 g_topology_assert();
1090
1091 version = gctl_get_paraml(req, "version", sizeof(*version));
1092 if (version == NULL) {
1093 gctl_error(req, "No '%s' argument.", "version");
1094 return;
1095 }
1096 if (*version != G_VENTOY_VERSION) {
1097 gctl_error(req, "Userland and kernel parts are out of sync.");
1098 return;
1099 }
1100
1101 if (strcmp(verb, "create") == 0) {
1102 g_ventoy_ctl_create(req, mp);
1103 return;
1104 } else if (strcmp(verb, "destroy") == 0 ||
1105 strcmp(verb, "stop") == 0) {
1106 g_ventoy_ctl_destroy(req, mp);
1107 return;
1108 }
1109 gctl_error(req, "Unknown verb.");
1110 }
1111
1112 static void
1113 g_ventoy_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1114 struct g_consumer *cp, struct g_provider *pp)
1115 {
1116 struct g_ventoy_softc *sc;
1117
1118 g_topology_assert();
1119 sc = gp->softc;
1120 if (sc == NULL)
1121 return;
1122 if (pp != NULL) {
1123 /* Nothing here. */
1124 } else if (cp != NULL) {
1125 struct g_ventoy_disk *disk;
1126
1127 disk = cp->private;
1128 if (disk == NULL)
1129 return;
1130 sbuf_printf(sb, "%s<End>%jd</End>\n", indent,
1131 (intmax_t)disk->d_end);
1132 sbuf_printf(sb, "%s<Start>%jd</Start>\n", indent,
1133 (intmax_t)disk->d_start);
1134 } else {
1135 sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id);
1136 sbuf_printf(sb, "%s<Type>", indent);
1137 switch (sc->sc_type) {
1138 case G_VENTOY_TYPE_AUTOMATIC:
1139 sbuf_cat(sb, "AUTOMATIC");
1140 break;
1141 case G_VENTOY_TYPE_MANUAL:
1142 sbuf_cat(sb, "MANUAL");
1143 break;
1144 default:
1145 sbuf_cat(sb, "UNKNOWN");
1146 break;
1147 }
1148 sbuf_cat(sb, "</Type>\n");
1149 sbuf_printf(sb, "%s<Status>Total=%u, Online=%u</Status>\n",
1150 indent, sc->sc_ndisks, g_ventoy_nvalid(sc));
1151 sbuf_printf(sb, "%s<State>", indent);
1152 if (sc->sc_provider != NULL && sc->sc_provider->error == 0)
1153 sbuf_cat(sb, "UP");
1154 else
1155 sbuf_cat(sb, "DOWN");
1156 sbuf_cat(sb, "</State>\n");
1157 }
1158 }
1159
1160 DECLARE_GEOM_CLASS(g_ventoy_class, g_ventoy);
1161 //MODULE_VERSION(geom_ventoy, 0);