2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2020 longpanda <admin@ventoy.net>
5 * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
38 #include <sys/mutex.h>
41 #include <sys/sysctl.h>
42 #include <sys/malloc.h>
43 #include <geom/geom.h>
44 #include <geom/ventoy/g_ventoy.h>
46 FEATURE(geom_ventoy
, "GEOM ventoy support");
48 static MALLOC_DEFINE(M_VENTOY
, "ventoy_data", "GEOM_VENTOY Data");
50 SYSCTL_DECL(_kern_geom
);
51 static SYSCTL_NODE(_kern_geom
, OID_AUTO
, ventoy
, CTLFLAG_RW
, 0,
53 static u_int g_ventoy_debug
= 0;
54 SYSCTL_UINT(_kern_geom_ventoy
, OID_AUTO
, debug
, CTLFLAG_RWTUN
, &g_ventoy_debug
, 0,
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
);
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
,
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
;
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;
74 struct g_ventoy_map g_ventoy_map_data
__attribute__((aligned (65536))) =
76 { VENTOY_UNIX_SEG_MAGIC0
, VENTOY_UNIX_SEG_MAGIC1
, VENTOY_UNIX_SEG_MAGIC2
, VENTOY_UNIX_SEG_MAGIC3
},
79 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
81 { VENTOY_UNIX_SEG_MAGIC0
, VENTOY_UNIX_SEG_MAGIC1
, VENTOY_UNIX_SEG_MAGIC2
, VENTOY_UNIX_SEG_MAGIC3
}
84 struct g_class g_ventoy_class
= {
85 .name
= G_VENTOY_CLASS_NAME
,
87 .ctlreq
= g_ventoy_config
,
88 .taste
= g_ventoy_taste
,
89 .destroy_geom
= g_ventoy_destroy_geom
94 * Greatest Common Divisor.
110 * Least Common Multiple.
113 lcm(u_int a
, u_int b
)
116 return ((a
* b
) / gcd(a
, b
));
120 * Return the number of valid disks.
123 g_ventoy_nvalid(struct g_ventoy_softc
*sc
)
128 for (i
= 0; i
< sc
->sc_ndisks
; i
++) {
129 if (sc
->sc_disks
[i
].d_consumer
!= NULL
)
137 g_ventoy_remove_disk(struct g_ventoy_disk
*disk
)
139 struct g_consumer
*cp
;
140 struct g_ventoy_softc
*sc
;
143 KASSERT(disk
->d_consumer
!= NULL
, ("Non-valid disk in %s.", __func__
));
145 cp
= disk
->d_consumer
;
147 if (!disk
->d_removed
) {
148 G_VENTOY_DEBUG(0, "Disk %s removed from %s.",
149 cp
->provider
->name
, sc
->sc_name
);
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
;
160 if (cp
->acr
> 0 || cp
->acw
> 0 || cp
->ace
> 0)
162 disk
->d_consumer
= NULL
;
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);
171 g_ventoy_orphan(struct g_consumer
*cp
)
173 struct g_ventoy_softc
*sc
;
174 struct g_ventoy_disk
*disk
;
184 if (disk
== NULL
) /* Possible? */
186 g_ventoy_remove_disk(disk
);
190 g_ventoy_access(struct g_provider
*pp
, int dr
, int dw
, int de
)
192 struct g_consumer
*cp1
, *cp2
, *tmp
;
193 struct g_ventoy_disk
*disk
;
197 if (dw
> 0) /* readonly */
203 /* On first open, grab an extra "exclusive" bit */
204 if (pp
->acr
== 0 && pp
->acw
== 0 && pp
->ace
== 0)
206 /* ... and let go of it on last close */
207 if ((pp
->acr
+ dr
) == 0 && (pp
->acw
+ dw
) == 0 && (pp
->ace
+ de
) == 0)
210 LIST_FOREACH_SAFE(cp1
, &gp
->consumer
, consumer
, tmp
) {
211 error
= g_access(cp1
, dr
, dw
, de
);
215 if (cp1
->acr
== 0 && cp1
->acw
== 0 && cp1
->ace
== 0 &&
217 g_ventoy_remove_disk(disk
); /* May destroy geom. */
223 LIST_FOREACH(cp2
, &gp
->consumer
, consumer
) {
226 g_access(cp2
, -dr
, -dw
, -de
);
232 g_ventoy_kernel_dump(struct bio
*bp
)
234 struct g_ventoy_softc
*sc
;
235 struct g_ventoy_disk
*disk
;
237 struct g_kerneldump
*gkd
;
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
)
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
);
255 g_io_deliver(bp
, ENOMEM
);
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
);
265 g_ventoy_done(struct bio
*bp
)
267 struct g_ventoy_softc
*sc
;
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
;
277 if (pbp
->bio_children
== pbp
->bio_inbed
) {
278 mtx_unlock(&sc
->sc_lock
);
279 g_io_deliver(pbp
, pbp
->bio_error
);
281 mtx_unlock(&sc
->sc_lock
);
286 g_ventoy_flush(struct g_ventoy_softc
*sc
, struct bio
*bp
)
288 struct bio_queue_head queue
;
289 struct g_consumer
*cp
;
294 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
295 cbp
= g_clone_bio(bp
);
297 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
299 if (bp
->bio_error
== 0)
300 bp
->bio_error
= ENOMEM
;
301 g_io_deliver(bp
, bp
->bio_error
);
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
;
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
);
318 g_ventoy_start(struct bio
*bp
)
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
;
330 sc
= pp
->geom
->softc
;
332 * If sc == NULL, provider's error should be set and g_ventoy_start()
333 * should not be called at all.
336 ("Provider's error should be set (error=%d)(device=%s).",
337 bp
->bio_to
->error
, bp
->bio_to
->name
));
339 G_VENTOY_LOGREQ(bp
, "Request received.");
341 switch (bp
->bio_cmd
) {
347 g_ventoy_flush(sc
, bp
);
350 if (strcmp("GEOM::kerneldump", bp
->bio_attribute
) == 0) {
351 g_ventoy_kernel_dump(bp
);
354 /* To which provider it should be delivered? */
357 g_io_deliver(bp
, EOPNOTSUPP
);
361 offset
= bp
->bio_offset
;
362 length
= bp
->bio_length
;
363 if ((bp
->bio_flags
& BIO_UNMAPPED
) != 0)
367 end
= offset
+ length
;
370 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
371 disk
= &sc
->sc_disks
[no
];
372 if (disk
->d_end
<= offset
)
374 if (disk
->d_start
>= end
)
377 off
= offset
- disk
->d_start
;
378 len
= MIN(length
, disk
->d_end
- offset
);
382 cbp
= g_clone_bio(bp
);
384 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
386 if (bp
->bio_error
== 0)
387 bp
->bio_error
= ENOMEM
;
388 g_io_deliver(bp
, bp
->bio_error
);
391 bioq_insert_tail(&queue
, cbp
);
393 * Fill in the component buf structure.
395 if (len
== bp
->bio_length
)
396 cbp
->bio_done
= g_std_done
;
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
;
408 cbp
->bio_data
= addr
;
410 cbp
->bio_to
= disk
->d_consumer
->provider
;
411 cbp
->bio_caller1
= disk
;
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
);
428 g_ventoy_check_and_run(struct g_ventoy_softc
*sc
)
430 struct g_ventoy_disk
*disk
;
431 struct g_provider
*dp
, *pp
;
432 u_int no
, sectorsize
= 0;
436 if (g_ventoy_nvalid(sc
) != sc
->sc_ndisks
)
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
;
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
;
452 sectorsize
= dp
->sectorsize
;
454 sectorsize
= lcm(sectorsize
, dp
->sectorsize
);
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
;
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);
471 G_VENTOY_DEBUG(0, "Device %s activated.", sc
->sc_provider
->name
);
475 g_ventoy_read_metadata(struct g_consumer
*cp
, struct g_ventoy_metadata
*md
)
477 struct g_provider
*pp
;
483 error
= g_access(cp
, 1, 0, 0);
488 buf
= g_read_data(cp
, pp
->mediasize
- pp
->sectorsize
, pp
->sectorsize
,
491 g_access(cp
, -1, 0, 0);
495 /* Decode metadata. */
496 ventoy_metadata_decode(buf
, md
);
503 * Add disk to given device.
506 g_ventoy_add_disk(struct g_ventoy_softc
*sc
, struct g_provider
*pp
, u_int no
)
508 struct g_ventoy_disk
*disk
;
509 struct g_consumer
*cp
, *fcp
;
514 /* Metadata corrupted? */
515 if (no
>= sc
->sc_ndisks
)
518 disk
= &sc
->sc_disks
[no
];
519 /* Check if disk is not already attached. */
520 if (disk
->d_consumer
!= NULL
)
524 fcp
= LIST_FIRST(&gp
->consumer
);
526 cp
= g_new_consumer(gp
);
527 cp
->flags
|= G_CF_DIRECT_SEND
| G_CF_DIRECT_RECEIVE
;
528 error
= g_attach(cp
, pp
);
530 g_destroy_consumer(cp
);
534 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0)) {
535 error
= g_access(cp
, fcp
->acr
, fcp
->acw
, fcp
->ace
);
538 g_destroy_consumer(cp
);
542 if (sc
->sc_type
== G_VENTOY_TYPE_AUTOMATIC
) {
543 struct g_ventoy_metadata md
;
545 /* Re-read metadata. */
546 error
= g_ventoy_read_metadata(cp
, &md
);
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
);
559 disk
->d_consumer
= cp
;
561 disk
->d_start
= 0; /* not yet */
562 disk
->d_end
= 0; /* not yet */
565 disk
->d_map_start
= g_disk_map_start
;
566 disk
->d_map_end
= g_disk_map_end
;
568 G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp
->name
, sc
->sc_name
);
570 g_ventoy_check_and_run(sc
);
574 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0))
575 g_access(cp
, -fcp
->acr
, -fcp
->acw
, -fcp
->ace
);
577 g_destroy_consumer(cp
);
581 static struct g_geom
*
582 g_ventoy_create(struct g_class
*mp
, const struct g_ventoy_metadata
*md
,
585 struct g_ventoy_softc
*sc
;
589 G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md
->md_name
,
592 /* One disks is minimum. */
596 /* Check for duplicate unit */
597 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
599 if (sc
!= NULL
&& strcmp(sc
->sc_name
, md
->md_name
) == 0) {
600 G_VENTOY_DEBUG(0, "Device %s already configured.",
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
;
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
;
620 mtx_init(&sc
->sc_lock
, "gventoy lock", NULL
, MTX_DEF
);
624 sc
->sc_provider
= NULL
;
626 G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc
->sc_name
, sc
->sc_id
);
632 g_ventoy_destroy(struct g_ventoy_softc
*sc
, boolean_t force
)
634 struct g_provider
*pp
;
635 struct g_consumer
*cp
, *cp1
;
643 pp
= sc
->sc_provider
;
644 if (pp
!= NULL
&& (pp
->acr
!= 0 || pp
->acw
!= 0 || pp
->ace
!= 0)) {
646 G_VENTOY_DEBUG(0, "Device %s is still open, so it "
647 "can't be definitely removed.", pp
->name
);
650 "Device %s is still open (r%dw%de%d).", pp
->name
,
651 pp
->acr
, pp
->acw
, pp
->ace
);
657 LIST_FOREACH_SAFE(cp
, &gp
->consumer
, consumer
, cp1
) {
658 g_ventoy_remove_disk(cp
->private);
660 return (0); /* Recursion happened. */
662 if (!LIST_EMPTY(&gp
->consumer
))
663 return (EINPROGRESS
);
666 KASSERT(sc
->sc_provider
== NULL
, ("Provider still exists? (device=%s)",
668 free(sc
->sc_disks
, M_VENTOY
);
669 mtx_destroy(&sc
->sc_lock
);
672 G_VENTOY_DEBUG(0, "Device %s destroyed.", gp
->name
);
673 g_wither_geom(gp
, ENXIO
);
678 g_ventoy_destroy_geom(struct gctl_req
*req __unused
,
679 struct g_class
*mp __unused
, struct g_geom
*gp
)
681 struct g_ventoy_softc
*sc
;
684 return (g_ventoy_destroy(sc
, 0));
687 static bool g_vtoy_check_disk(struct g_class
*mp
, struct g_provider
*pp
)
694 struct g_consumer
*cp
;
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,
703 if (g_ventoy_disk_size
== 0)
705 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
707 G_DEBUG("ventoy map data is valid. [OK]\n");
709 for (i
= 0; i
< 16; i
++)
711 sprintf(uuid
+ i
* 2, "%02x", g_ventoy_map_data
.diskuuid
[i
]);
713 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", uuid
);
714 g_ventoy_disk_size
= g_ventoy_map_data
.disksize
;
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
);
721 G_DEBUG("ventoy map data is invalid, get from resource\n");
723 if (resource_string_value("ventoy", 0, "disksize", &value
) == 0)
725 G_DEBUG("ventoy.disksize: %s\n", value
);
726 g_ventoy_disk_size
= strtouq(value
, NULL
, 0);
729 if (resource_string_value("ventoy", 0, "diskuuid", &value
) == 0)
731 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", value
);
732 G_DEBUG("ventoy.diskuuid: <%s>\n", value
);
737 if (g_ventoy_disk_size
!= pp
->mediasize
)
742 if (strncmp(pp
->name
, "cd", 2) == 0 || strchr(pp
->name
, '/'))
747 /* read UUID from disk */
748 gp
= g_new_geomf(mp
, "ventoy:taste");
750 gp
->access
= g_ventoy_access
;
751 gp
->orphan
= g_ventoy_orphan
;
752 cp
= g_new_consumer(gp
);
755 g_access(cp
, 1, 0, 0);
757 buf
= g_read_data(cp
, 0, pp
->sectorsize
, NULL
);
759 g_access(cp
, -1, 0, 0);
762 g_destroy_consumer(cp
);
771 for (i
= 0; i
< 16; i
++)
773 sprintf(uuid
+ i
* 2, "%02x", buf
[0x180 + i
]);
776 if (strncmp(g_ventoy_disk_uuid
, uuid
, 32))
781 if (memcmp(mbrdata
, buf
, 0x30) || memcmp(mbrdata
+ 0x30, buf
+ 0x190, 16))
790 G_DEBUG("ventoy disk check OK\n");
796 static struct g_geom
*
797 g_ventoy_taste(struct g_class
*mp
, struct g_provider
*pp
, int flags __unused
)
805 struct g_ventoy_metadata md
;
806 struct g_ventoy_softc
*sc
;
813 G_DEBUG("%s(%s, %s)\n", __func__
, mp
->name
, pp
->name
);
816 /* Skip providers that are already open for writing. */
820 if (!g_vtoy_check_disk(mp
, pp
))
825 g_ventoy_tasted
= true;
827 G_DEBUG("######### ventoy disk <%s> #############\n", pp
->name
);
829 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
831 disknum
= (int)g_ventoy_map_data
.segnum
;
832 G_DEBUG("segnum from map data is:<%d>\n", disknum
);
836 resource_int_value("ventoy", 0, "segnum", &disknum
);
837 G_DEBUG("segnum from resource is:<%d>\n", disknum
);
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();
845 md
.md_all
= (uint16_t)disknum
;
846 bzero(md
.md_provider
, sizeof(md
.md_provider
));
847 /* This field is not important here. */
850 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
852 G_VENTOY_DEBUG(0, "Cannot create device %s.",
858 for (i
= 0; i
< disknum
; i
++)
860 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
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
);
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
;
871 if (resource_string_value("ventoy", i
, "seg", &value
) == 0)
873 g_disk_map_start
= strtouq(value
, &endpos
, 0);
874 g_disk_map_end
= strtouq(endpos
+ 1, NULL
, 0);
878 printf("Failed to parse ventoy seg %d\n", i
);
881 G_DEBUG("[resource] ventoy segment%d: %s\n", i
, value
);
884 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp
->name
, gp
->name
);
885 error
= g_ventoy_add_disk(sc
, pp
, i
);
888 "Cannot add disk %s to %s (error=%d).", pp
->name
,
890 g_ventoy_destroy(sc
, 1);
894 g_disk_map_start
= 0;
902 g_ventoy_ctl_create(struct gctl_req
*req
, struct g_class
*mp
)
905 struct g_ventoy_metadata md
;
906 struct g_provider
*pp
;
907 struct g_ventoy_softc
*sc
;
915 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
917 gctl_error(req
, "No '%s' argument.", "nargs");
921 gctl_error(req
, "Too few arguments.");
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");
929 gctl_error(req
, "No 'arg%u' argument.", 0);
932 strlcpy(md
.md_name
, name
, sizeof(md
.md_name
));
933 md
.md_id
= arc4random();
935 md
.md_all
= *nargs
- 1;
936 bzero(md
.md_provider
, sizeof(md
.md_provider
));
937 /* This field is not important here. */
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
);
945 gctl_error(req
, "No 'arg%u' argument.", no
);
948 if (strncmp(name
, "/dev/", strlen("/dev/")) == 0)
949 name
+= strlen("/dev/");
950 pp
= g_provider_by_name(name
);
952 G_VENTOY_DEBUG(1, "Disk %s is invalid.", name
);
953 gctl_error(req
, "Disk %s is invalid.", name
);
958 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
960 gctl_error(req
, "Can't configure %s.", md
.md_name
);
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
);
971 gctl_error(req
, "No 'arg%d' argument.", no
);
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
);
987 if (md
.md_all
!= attached
) {
988 g_ventoy_destroy(gp
->softc
, 1);
989 gctl_error(req
, "%s", sbuf_data(sb
));
994 static struct g_ventoy_softc
*
995 g_ventoy_find_device(struct g_class
*mp
, const char *name
)
997 struct g_ventoy_softc
*sc
;
1000 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
1004 if (strcmp(sc
->sc_name
, name
) == 0)
1011 g_ventoy_ctl_destroy(struct gctl_req
*req
, struct g_class
*mp
)
1013 struct g_ventoy_softc
*sc
;
1014 int *force
, *nargs
, error
;
1019 g_topology_assert();
1021 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
1022 if (nargs
== NULL
) {
1023 gctl_error(req
, "No '%s' argument.", "nargs");
1027 gctl_error(req
, "Missing device(s).");
1030 force
= gctl_get_paraml(req
, "force", sizeof(*force
));
1031 if (force
== NULL
) {
1032 gctl_error(req
, "No '%s' argument.", "force");
1036 for (i
= 0; i
< (u_int
)*nargs
; i
++) {
1037 snprintf(param
, sizeof(param
), "arg%u", i
);
1038 name
= gctl_get_asciiparam(req
, param
);
1040 gctl_error(req
, "No 'arg%u' argument.", i
);
1043 sc
= g_ventoy_find_device(mp
, name
);
1045 gctl_error(req
, "No such device: %s.", name
);
1048 error
= g_ventoy_destroy(sc
, *force
);
1050 gctl_error(req
, "Cannot destroy device %s (error=%d).",
1051 sc
->sc_name
, error
);
1058 g_ventoy_config(struct gctl_req
*req
, struct g_class
*mp
, const char *verb
)
1064 g_topology_assert();
1066 version
= gctl_get_paraml(req
, "version", sizeof(*version
));
1067 if (version
== NULL
) {
1068 gctl_error(req
, "No '%s' argument.", "version");
1071 if (*version
!= G_VENTOY_VERSION
) {
1072 gctl_error(req
, "Userland and kernel parts are out of sync.");
1076 if (strcmp(verb
, "create") == 0) {
1077 g_ventoy_ctl_create(req
, mp
);
1079 } else if (strcmp(verb
, "destroy") == 0 ||
1080 strcmp(verb
, "stop") == 0) {
1081 g_ventoy_ctl_destroy(req
, mp
);
1084 gctl_error(req
, "Unknown verb.");
1088 g_ventoy_dumpconf(struct sbuf
*sb
, const char *indent
, struct g_geom
*gp
,
1089 struct g_consumer
*cp
, struct g_provider
*pp
)
1091 struct g_ventoy_softc
*sc
;
1093 g_topology_assert();
1099 } else if (cp
!= NULL
) {
1100 struct g_ventoy_disk
*disk
;
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
);
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");
1116 case G_VENTOY_TYPE_MANUAL
:
1117 sbuf_cat(sb
, "MANUAL");
1120 sbuf_cat(sb
, "UNKNOWN");
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)
1130 sbuf_cat(sb
, "DOWN");
1131 sbuf_cat(sb
, "</State>\n");
1135 DECLARE_GEOM_CLASS(g_ventoy_class
, g_ventoy
);
1136 //MODULE_VERSION(geom_ventoy, 0);