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