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
| CTLFLAG_MPSAFE
, 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 */
204 /* On first open, grab an extra "exclusive" bit */
205 if (pp
->acr
== 0 && pp
->acw
== 0 && pp
->ace
== 0)
207 /* ... and let go of it on last close */
208 if ((pp
->acr
+ dr
) == 0 && (pp
->acw
+ dw
) == 0 && (pp
->ace
+ de
) == 0)
211 G_DEBUG("g_ventoy_access fake de (%d)-->(0)\n", de
);
215 LIST_FOREACH_SAFE(cp1
, &gp
->consumer
, consumer
, tmp
) {
216 error
= g_access(cp1
, dr
, dw
, de
);
220 if (cp1
->acr
== 0 && cp1
->acw
== 0 && cp1
->ace
== 0 &&
222 g_ventoy_remove_disk(disk
); /* May destroy geom. */
228 LIST_FOREACH(cp2
, &gp
->consumer
, consumer
) {
231 g_access(cp2
, -dr
, -dw
, -de
);
237 g_ventoy_candelete(struct bio
*bp
)
239 struct g_ventoy_softc
*sc
;
240 struct g_ventoy_disk
*disk
;
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
)
249 val
= i
< sc
->sc_ndisks
;
250 g_handleattr(bp
, "GEOM::candelete", &val
, sizeof(val
));
254 g_ventoy_kernel_dump(struct bio
*bp
)
256 struct g_ventoy_softc
*sc
;
257 struct g_ventoy_disk
*disk
;
259 struct g_kerneldump
*gkd
;
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
)
269 if (i
== sc
->sc_ndisks
) {
270 g_io_deliver(bp
, EOPNOTSUPP
);
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
);
279 g_io_deliver(bp
, ENOMEM
);
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
);
289 g_ventoy_done(struct bio
*bp
)
291 struct g_ventoy_softc
*sc
;
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
;
301 if (pbp
->bio_children
== pbp
->bio_inbed
) {
302 mtx_unlock(&sc
->sc_lock
);
303 g_io_deliver(pbp
, pbp
->bio_error
);
305 mtx_unlock(&sc
->sc_lock
);
310 g_ventoy_flush(struct g_ventoy_softc
*sc
, struct bio
*bp
)
312 struct bio_queue_head queue
;
313 struct g_consumer
*cp
;
318 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
319 cbp
= g_clone_bio(bp
);
321 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
323 if (bp
->bio_error
== 0)
324 bp
->bio_error
= ENOMEM
;
325 g_io_deliver(bp
, bp
->bio_error
);
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
;
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
);
342 g_ventoy_start(struct bio
*bp
)
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
;
354 sc
= pp
->geom
->softc
;
356 * If sc == NULL, provider's error should be set and g_ventoy_start()
357 * should not be called at all.
360 ("Provider's error should be set (error=%d)(device=%s).",
361 bp
->bio_to
->error
, bp
->bio_to
->name
));
363 G_VENTOY_LOGREQ(bp
, "Request received.");
365 switch (bp
->bio_cmd
) {
372 g_ventoy_flush(sc
, bp
);
375 if (strcmp("GEOM::kerneldump", bp
->bio_attribute
) == 0) {
376 g_ventoy_kernel_dump(bp
);
378 } else if (strcmp("GEOM::candelete", bp
->bio_attribute
) == 0) {
379 g_ventoy_candelete(bp
);
382 /* To which provider it should be delivered? */
385 g_io_deliver(bp
, EOPNOTSUPP
);
389 offset
= bp
->bio_offset
;
390 length
= bp
->bio_length
;
391 if ((bp
->bio_flags
& BIO_UNMAPPED
) != 0)
395 end
= offset
+ length
;
398 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
399 disk
= &sc
->sc_disks
[no
];
400 if (disk
->d_end
<= offset
)
402 if (disk
->d_start
>= end
)
405 off
= offset
- disk
->d_start
;
406 len
= MIN(length
, disk
->d_end
- offset
);
410 cbp
= g_clone_bio(bp
);
412 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
414 if (bp
->bio_error
== 0)
415 bp
->bio_error
= ENOMEM
;
416 g_io_deliver(bp
, bp
->bio_error
);
419 bioq_insert_tail(&queue
, cbp
);
421 * Fill in the component buf structure.
423 if (len
== bp
->bio_length
)
424 cbp
->bio_done
= g_std_done
;
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
;
436 cbp
->bio_data
= addr
;
438 cbp
->bio_to
= disk
->d_consumer
->provider
;
439 cbp
->bio_caller1
= disk
;
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
);
456 g_ventoy_check_and_run(struct g_ventoy_softc
*sc
)
458 struct g_ventoy_disk
*disk
;
459 struct g_provider
*dp
, *pp
;
460 u_int no
, sectorsize
= 0;
465 if (g_ventoy_nvalid(sc
) != sc
->sc_ndisks
)
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
;
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
;
480 error
= g_access(disk
->d_consumer
, 1, 0, 0);
482 error
= g_getattr("GEOM::candelete", disk
->d_consumer
,
485 disk
->d_candelete
= 0;
486 (void)g_access(disk
->d_consumer
, -1, 0, 0);
488 G_VENTOY_DEBUG(1, "Failed to access disk %s, error %d.",
491 sectorsize
= dp
->sectorsize
;
493 sectorsize
= lcm(sectorsize
, dp
->sectorsize
);
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
;
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);
510 G_VENTOY_DEBUG(0, "Device %s activated.", sc
->sc_provider
->name
);
514 g_ventoy_read_metadata(struct g_consumer
*cp
, struct g_ventoy_metadata
*md
)
516 struct g_provider
*pp
;
522 error
= g_access(cp
, 1, 0, 0);
527 buf
= g_read_data(cp
, pp
->mediasize
- pp
->sectorsize
, pp
->sectorsize
,
530 g_access(cp
, -1, 0, 0);
534 /* Decode metadata. */
535 ventoy_metadata_decode(buf
, md
);
542 * Add disk to given device.
545 g_ventoy_add_disk(struct g_ventoy_softc
*sc
, struct g_provider
*pp
, u_int no
)
547 struct g_ventoy_disk
*disk
;
548 struct g_consumer
*cp
, *fcp
;
553 /* Metadata corrupted? */
554 if (no
>= sc
->sc_ndisks
)
557 disk
= &sc
->sc_disks
[no
];
558 /* Check if disk is not already attached. */
559 if (disk
->d_consumer
!= NULL
)
563 fcp
= LIST_FIRST(&gp
->consumer
);
565 cp
= g_new_consumer(gp
);
566 cp
->flags
|= G_CF_DIRECT_SEND
| G_CF_DIRECT_RECEIVE
;
567 error
= g_attach(cp
, pp
);
569 g_destroy_consumer(cp
);
573 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0)) {
574 error
= g_access(cp
, fcp
->acr
, fcp
->acw
, fcp
->ace
);
577 g_destroy_consumer(cp
);
581 if (sc
->sc_type
== G_VENTOY_TYPE_AUTOMATIC
) {
582 struct g_ventoy_metadata md
;
584 /* Re-read metadata. */
585 error
= g_ventoy_read_metadata(cp
, &md
);
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
);
598 disk
->d_consumer
= cp
;
600 disk
->d_start
= 0; /* not yet */
601 disk
->d_end
= 0; /* not yet */
604 disk
->d_map_start
= g_disk_map_start
;
605 disk
->d_map_end
= g_disk_map_end
;
607 G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp
->name
, sc
->sc_name
);
609 g_ventoy_check_and_run(sc
);
613 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0))
614 g_access(cp
, -fcp
->acr
, -fcp
->acw
, -fcp
->ace
);
616 g_destroy_consumer(cp
);
620 static struct g_geom
*
621 g_ventoy_create(struct g_class
*mp
, const struct g_ventoy_metadata
*md
,
624 struct g_ventoy_softc
*sc
;
628 G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md
->md_name
,
631 /* One disks is minimum. */
635 /* Check for duplicate unit */
636 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
638 if (sc
!= NULL
&& strcmp(sc
->sc_name
, md
->md_name
) == 0) {
639 G_VENTOY_DEBUG(0, "Device %s already configured.",
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
;
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
;
659 mtx_init(&sc
->sc_lock
, "gventoy lock", NULL
, MTX_DEF
);
663 sc
->sc_provider
= NULL
;
665 G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc
->sc_name
, sc
->sc_id
);
671 g_ventoy_destroy(struct g_ventoy_softc
*sc
, boolean_t force
)
673 struct g_provider
*pp
;
674 struct g_consumer
*cp
, *cp1
;
682 pp
= sc
->sc_provider
;
683 if (pp
!= NULL
&& (pp
->acr
!= 0 || pp
->acw
!= 0 || pp
->ace
!= 0)) {
685 G_VENTOY_DEBUG(0, "Device %s is still open, so it "
686 "can't be definitely removed.", pp
->name
);
689 "Device %s is still open (r%dw%de%d).", pp
->name
,
690 pp
->acr
, pp
->acw
, pp
->ace
);
696 LIST_FOREACH_SAFE(cp
, &gp
->consumer
, consumer
, cp1
) {
697 g_ventoy_remove_disk(cp
->private);
699 return (0); /* Recursion happened. */
701 if (!LIST_EMPTY(&gp
->consumer
))
702 return (EINPROGRESS
);
705 KASSERT(sc
->sc_provider
== NULL
, ("Provider still exists? (device=%s)",
707 free(sc
->sc_disks
, M_VENTOY
);
708 mtx_destroy(&sc
->sc_lock
);
711 G_VENTOY_DEBUG(0, "Device %s destroyed.", gp
->name
);
712 g_wither_geom(gp
, ENXIO
);
717 g_ventoy_destroy_geom(struct gctl_req
*req __unused
,
718 struct g_class
*mp __unused
, struct g_geom
*gp
)
720 struct g_ventoy_softc
*sc
;
723 return (g_ventoy_destroy(sc
, 0));
726 static bool g_vtoy_check_disk(struct g_class
*mp
, struct g_provider
*pp
)
733 struct g_consumer
*cp
;
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,
742 if (g_ventoy_disk_size
== 0)
744 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
746 G_DEBUG("ventoy map data is valid. [OK]\n");
748 for (i
= 0; i
< 16; i
++)
750 sprintf(uuid
+ i
* 2, "%02x", g_ventoy_map_data
.diskuuid
[i
]);
752 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", uuid
);
753 g_ventoy_disk_size
= g_ventoy_map_data
.disksize
;
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
);
760 G_DEBUG("ventoy map data is invalid, get from resource\n");
762 if (resource_string_value("ventoy", 0, "disksize", &value
) == 0)
764 G_DEBUG("ventoy.disksize: %s\n", value
);
765 g_ventoy_disk_size
= strtouq(value
, NULL
, 0);
768 if (resource_string_value("ventoy", 0, "diskuuid", &value
) == 0)
770 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", value
);
771 G_DEBUG("ventoy.diskuuid: <%s>\n", value
);
776 if (g_ventoy_disk_size
!= pp
->mediasize
)
781 if (strncmp(pp
->name
, "cd", 2) == 0 || strchr(pp
->name
, '/'))
786 /* read UUID from disk */
787 gp
= g_new_geomf(mp
, "ventoy:taste");
789 gp
->access
= g_ventoy_access
;
790 gp
->orphan
= g_ventoy_orphan
;
791 cp
= g_new_consumer(gp
);
794 g_access(cp
, 1, 0, 0);
796 buf
= g_read_data(cp
, 0, pp
->sectorsize
, NULL
);
798 g_access(cp
, -1, 0, 0);
801 g_destroy_consumer(cp
);
810 for (i
= 0; i
< 16; i
++)
812 sprintf(uuid
+ i
* 2, "%02x", buf
[0x180 + i
]);
815 if (strncmp(g_ventoy_disk_uuid
, uuid
, 32))
820 if (memcmp(mbrdata
, buf
, 0x30) || memcmp(mbrdata
+ 0x30, buf
+ 0x190, 16))
829 G_DEBUG("ventoy disk check OK\n");
835 static struct g_geom
*
836 g_ventoy_taste(struct g_class
*mp
, struct g_provider
*pp
, int flags __unused
)
843 const char *alias
= NULL
;
845 struct g_ventoy_metadata md
;
846 struct g_ventoy_softc
*sc
;
853 G_DEBUG("%s(%s, %s)\n", __func__
, mp
->name
, pp
->name
);
856 /* Skip providers that are already open for writing. */
860 if (!g_vtoy_check_disk(mp
, pp
))
865 g_ventoy_tasted
= true;
867 G_DEBUG("###### ventoy disk <%s> ######\n", pp
->name
);
869 /* hint.ventoy.0.alias=xxx */
870 if (resource_string_value("ventoy", 0, "alias", &alias
) == 0)
872 G_DEBUG("###### ventoy alias <%s> ######\n", alias
);
879 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
881 disknum
= (int)g_ventoy_map_data
.segnum
;
882 G_DEBUG("segnum from map data is:<%d>\n", disknum
);
886 resource_int_value("ventoy", 0, "segnum", &disknum
);
887 G_DEBUG("segnum from resource is:<%d>\n", disknum
);
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();
895 md
.md_all
= (uint16_t)disknum
;
896 bzero(md
.md_provider
, sizeof(md
.md_provider
));
897 /* This field is not important here. */
900 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
902 G_VENTOY_DEBUG(0, "Cannot create device %s.",
908 for (i
= 0; i
< disknum
; i
++)
910 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
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
);
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
;
921 if (resource_string_value("ventoy", i
, "seg", &value
) == 0)
923 g_disk_map_start
= strtouq(value
, &endpos
, 0);
924 g_disk_map_end
= strtouq(endpos
+ 1, NULL
, 0);
928 printf("Failed to parse ventoy seg %d\n", i
);
931 G_DEBUG("[resource] ventoy segment%d: %s\n", i
, value
);
934 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp
->name
, gp
->name
);
935 error
= g_ventoy_add_disk(sc
, pp
, i
);
938 "Cannot add disk %s to %s (error=%d).", pp
->name
,
940 g_ventoy_destroy(sc
, 1);
944 g_disk_map_start
= 0;
948 if (alias
&& sc
&& sc
->sc_provider
)
950 g_provider_add_alias(sc
->sc_provider
, "%s", alias
);
957 g_ventoy_ctl_create(struct gctl_req
*req
, struct g_class
*mp
)
960 struct g_ventoy_metadata md
;
961 struct g_provider
*pp
;
962 struct g_ventoy_softc
*sc
;
970 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
972 gctl_error(req
, "No '%s' argument.", "nargs");
976 gctl_error(req
, "Too few arguments.");
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");
984 gctl_error(req
, "No 'arg%u' argument.", 0);
987 strlcpy(md
.md_name
, name
, sizeof(md
.md_name
));
988 md
.md_id
= arc4random();
990 md
.md_all
= *nargs
- 1;
991 bzero(md
.md_provider
, sizeof(md
.md_provider
));
992 /* This field is not important here. */
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
);
1003 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
1005 gctl_error(req
, "Can't configure %s.", md
.md_name
);
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
);
1016 name
= gctl_get_asciiparam(req
, param
);
1017 MPASS(name
!= NULL
);
1018 sbuf_printf(sb
, " %s", name
);
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
);
1030 if (md
.md_all
!= attached
) {
1031 g_ventoy_destroy(gp
->softc
, 1);
1032 gctl_error(req
, "%s", sbuf_data(sb
));
1037 static struct g_ventoy_softc
*
1038 g_ventoy_find_device(struct g_class
*mp
, const char *name
)
1040 struct g_ventoy_softc
*sc
;
1043 if (strncmp(name
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
1044 name
+= strlen(_PATH_DEV
);
1046 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
1050 if (strcmp(sc
->sc_name
, name
) == 0)
1057 g_ventoy_ctl_destroy(struct gctl_req
*req
, struct g_class
*mp
)
1059 struct g_ventoy_softc
*sc
;
1060 int *force
, *nargs
, error
;
1065 g_topology_assert();
1067 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
1068 if (nargs
== NULL
) {
1069 gctl_error(req
, "No '%s' argument.", "nargs");
1073 gctl_error(req
, "Missing device(s).");
1076 force
= gctl_get_paraml(req
, "force", sizeof(*force
));
1077 if (force
== NULL
) {
1078 gctl_error(req
, "No '%s' argument.", "force");
1082 for (i
= 0; i
< (u_int
)*nargs
; i
++) {
1083 snprintf(param
, sizeof(param
), "arg%u", i
);
1084 name
= gctl_get_asciiparam(req
, param
);
1086 gctl_error(req
, "No 'arg%u' argument.", i
);
1089 sc
= g_ventoy_find_device(mp
, name
);
1091 gctl_error(req
, "No such device: %s.", name
);
1094 error
= g_ventoy_destroy(sc
, *force
);
1096 gctl_error(req
, "Cannot destroy device %s (error=%d).",
1097 sc
->sc_name
, error
);
1104 g_ventoy_config(struct gctl_req
*req
, struct g_class
*mp
, const char *verb
)
1110 g_topology_assert();
1112 version
= gctl_get_paraml(req
, "version", sizeof(*version
));
1113 if (version
== NULL
) {
1114 gctl_error(req
, "No '%s' argument.", "version");
1117 if (*version
!= G_VENTOY_VERSION
) {
1118 gctl_error(req
, "Userland and kernel parts are out of sync.");
1122 if (strcmp(verb
, "create") == 0) {
1123 g_ventoy_ctl_create(req
, mp
);
1125 } else if (strcmp(verb
, "destroy") == 0 ||
1126 strcmp(verb
, "stop") == 0) {
1127 g_ventoy_ctl_destroy(req
, mp
);
1130 gctl_error(req
, "Unknown verb.");
1134 g_ventoy_dumpconf(struct sbuf
*sb
, const char *indent
, struct g_geom
*gp
,
1135 struct g_consumer
*cp
, struct g_provider
*pp
)
1137 struct g_ventoy_softc
*sc
;
1139 g_topology_assert();
1145 } else if (cp
!= NULL
) {
1146 struct g_ventoy_disk
*disk
;
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
);
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");
1162 case G_VENTOY_TYPE_MANUAL
:
1163 sbuf_cat(sb
, "MANUAL");
1166 sbuf_cat(sb
, "UNKNOWN");
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)
1176 sbuf_cat(sb
, "DOWN");
1177 sbuf_cat(sb
, "</State>\n");
1181 DECLARE_GEOM_CLASS(g_ventoy_class
, g_ventoy
);
1182 //MODULE_VERSION(geom_ventoy, 0);