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 */
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_candelete(struct bio
*bp
)
234 struct g_ventoy_softc
*sc
;
235 struct g_ventoy_disk
*disk
;
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
)
244 val
= i
< sc
->sc_ndisks
;
245 g_handleattr(bp
, "GEOM::candelete", &val
, sizeof(val
));
249 g_ventoy_kernel_dump(struct bio
*bp
)
251 struct g_ventoy_softc
*sc
;
252 struct g_ventoy_disk
*disk
;
254 struct g_kerneldump
*gkd
;
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
)
264 if (i
== sc
->sc_ndisks
) {
265 g_io_deliver(bp
, EOPNOTSUPP
);
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
);
274 g_io_deliver(bp
, ENOMEM
);
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
);
284 g_ventoy_done(struct bio
*bp
)
286 struct g_ventoy_softc
*sc
;
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
;
296 if (pbp
->bio_children
== pbp
->bio_inbed
) {
297 mtx_unlock(&sc
->sc_lock
);
298 g_io_deliver(pbp
, pbp
->bio_error
);
300 mtx_unlock(&sc
->sc_lock
);
305 g_ventoy_flush(struct g_ventoy_softc
*sc
, struct bio
*bp
)
307 struct bio_queue_head queue
;
308 struct g_consumer
*cp
;
313 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
314 cbp
= g_clone_bio(bp
);
316 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
318 if (bp
->bio_error
== 0)
319 bp
->bio_error
= ENOMEM
;
320 g_io_deliver(bp
, bp
->bio_error
);
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
;
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
);
337 g_ventoy_start(struct bio
*bp
)
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
;
349 sc
= pp
->geom
->softc
;
351 * If sc == NULL, provider's error should be set and g_ventoy_start()
352 * should not be called at all.
355 ("Provider's error should be set (error=%d)(device=%s).",
356 bp
->bio_to
->error
, bp
->bio_to
->name
));
358 G_VENTOY_LOGREQ(bp
, "Request received.");
360 switch (bp
->bio_cmd
) {
367 g_ventoy_flush(sc
, bp
);
370 if (strcmp("GEOM::kerneldump", bp
->bio_attribute
) == 0) {
371 g_ventoy_kernel_dump(bp
);
373 } else if (strcmp("GEOM::candelete", bp
->bio_attribute
) == 0) {
374 g_ventoy_candelete(bp
);
377 /* To which provider it should be delivered? */
380 g_io_deliver(bp
, EOPNOTSUPP
);
384 offset
= bp
->bio_offset
;
385 length
= bp
->bio_length
;
386 if ((bp
->bio_flags
& BIO_UNMAPPED
) != 0)
390 end
= offset
+ length
;
393 for (no
= 0; no
< sc
->sc_ndisks
; no
++) {
394 disk
= &sc
->sc_disks
[no
];
395 if (disk
->d_end
<= offset
)
397 if (disk
->d_start
>= end
)
400 off
= offset
- disk
->d_start
;
401 len
= MIN(length
, disk
->d_end
- offset
);
405 cbp
= g_clone_bio(bp
);
407 while ((cbp
= bioq_takefirst(&queue
)) != NULL
)
409 if (bp
->bio_error
== 0)
410 bp
->bio_error
= ENOMEM
;
411 g_io_deliver(bp
, bp
->bio_error
);
414 bioq_insert_tail(&queue
, cbp
);
416 * Fill in the component buf structure.
418 if (len
== bp
->bio_length
)
419 cbp
->bio_done
= g_std_done
;
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
;
431 cbp
->bio_data
= addr
;
433 cbp
->bio_to
= disk
->d_consumer
->provider
;
434 cbp
->bio_caller1
= disk
;
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
);
451 g_ventoy_check_and_run(struct g_ventoy_softc
*sc
)
453 struct g_ventoy_disk
*disk
;
454 struct g_provider
*dp
, *pp
;
455 u_int no
, sectorsize
= 0;
460 if (g_ventoy_nvalid(sc
) != sc
->sc_ndisks
)
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
;
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
;
475 error
= g_access(disk
->d_consumer
, 1, 0, 0);
477 error
= g_getattr("GEOM::candelete", disk
->d_consumer
,
480 disk
->d_candelete
= 0;
481 (void)g_access(disk
->d_consumer
, -1, 0, 0);
483 G_VENTOY_DEBUG(1, "Failed to access disk %s, error %d.",
486 sectorsize
= dp
->sectorsize
;
488 sectorsize
= lcm(sectorsize
, dp
->sectorsize
);
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
;
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);
505 G_VENTOY_DEBUG(0, "Device %s activated.", sc
->sc_provider
->name
);
509 g_ventoy_read_metadata(struct g_consumer
*cp
, struct g_ventoy_metadata
*md
)
511 struct g_provider
*pp
;
517 error
= g_access(cp
, 1, 0, 0);
522 buf
= g_read_data(cp
, pp
->mediasize
- pp
->sectorsize
, pp
->sectorsize
,
525 g_access(cp
, -1, 0, 0);
529 /* Decode metadata. */
530 ventoy_metadata_decode(buf
, md
);
537 * Add disk to given device.
540 g_ventoy_add_disk(struct g_ventoy_softc
*sc
, struct g_provider
*pp
, u_int no
)
542 struct g_ventoy_disk
*disk
;
543 struct g_consumer
*cp
, *fcp
;
548 /* Metadata corrupted? */
549 if (no
>= sc
->sc_ndisks
)
552 disk
= &sc
->sc_disks
[no
];
553 /* Check if disk is not already attached. */
554 if (disk
->d_consumer
!= NULL
)
558 fcp
= LIST_FIRST(&gp
->consumer
);
560 cp
= g_new_consumer(gp
);
561 cp
->flags
|= G_CF_DIRECT_SEND
| G_CF_DIRECT_RECEIVE
;
562 error
= g_attach(cp
, pp
);
564 g_destroy_consumer(cp
);
568 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0)) {
569 error
= g_access(cp
, fcp
->acr
, fcp
->acw
, fcp
->ace
);
572 g_destroy_consumer(cp
);
576 if (sc
->sc_type
== G_VENTOY_TYPE_AUTOMATIC
) {
577 struct g_ventoy_metadata md
;
579 /* Re-read metadata. */
580 error
= g_ventoy_read_metadata(cp
, &md
);
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
);
593 disk
->d_consumer
= cp
;
595 disk
->d_start
= 0; /* not yet */
596 disk
->d_end
= 0; /* not yet */
599 disk
->d_map_start
= g_disk_map_start
;
600 disk
->d_map_end
= g_disk_map_end
;
602 G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp
->name
, sc
->sc_name
);
604 g_ventoy_check_and_run(sc
);
608 if (fcp
!= NULL
&& (fcp
->acr
> 0 || fcp
->acw
> 0 || fcp
->ace
> 0))
609 g_access(cp
, -fcp
->acr
, -fcp
->acw
, -fcp
->ace
);
611 g_destroy_consumer(cp
);
615 static struct g_geom
*
616 g_ventoy_create(struct g_class
*mp
, const struct g_ventoy_metadata
*md
,
619 struct g_ventoy_softc
*sc
;
623 G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md
->md_name
,
626 /* One disks is minimum. */
630 /* Check for duplicate unit */
631 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
633 if (sc
!= NULL
&& strcmp(sc
->sc_name
, md
->md_name
) == 0) {
634 G_VENTOY_DEBUG(0, "Device %s already configured.",
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
;
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
;
654 mtx_init(&sc
->sc_lock
, "gventoy lock", NULL
, MTX_DEF
);
658 sc
->sc_provider
= NULL
;
660 G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc
->sc_name
, sc
->sc_id
);
666 g_ventoy_destroy(struct g_ventoy_softc
*sc
, boolean_t force
)
668 struct g_provider
*pp
;
669 struct g_consumer
*cp
, *cp1
;
677 pp
= sc
->sc_provider
;
678 if (pp
!= NULL
&& (pp
->acr
!= 0 || pp
->acw
!= 0 || pp
->ace
!= 0)) {
680 G_VENTOY_DEBUG(0, "Device %s is still open, so it "
681 "can't be definitely removed.", pp
->name
);
684 "Device %s is still open (r%dw%de%d).", pp
->name
,
685 pp
->acr
, pp
->acw
, pp
->ace
);
691 LIST_FOREACH_SAFE(cp
, &gp
->consumer
, consumer
, cp1
) {
692 g_ventoy_remove_disk(cp
->private);
694 return (0); /* Recursion happened. */
696 if (!LIST_EMPTY(&gp
->consumer
))
697 return (EINPROGRESS
);
700 KASSERT(sc
->sc_provider
== NULL
, ("Provider still exists? (device=%s)",
702 free(sc
->sc_disks
, M_VENTOY
);
703 mtx_destroy(&sc
->sc_lock
);
706 G_VENTOY_DEBUG(0, "Device %s destroyed.", gp
->name
);
707 g_wither_geom(gp
, ENXIO
);
712 g_ventoy_destroy_geom(struct gctl_req
*req __unused
,
713 struct g_class
*mp __unused
, struct g_geom
*gp
)
715 struct g_ventoy_softc
*sc
;
718 return (g_ventoy_destroy(sc
, 0));
721 static bool g_vtoy_check_disk(struct g_class
*mp
, struct g_provider
*pp
)
728 struct g_consumer
*cp
;
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,
737 if (g_ventoy_disk_size
== 0)
739 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
741 G_DEBUG("ventoy map data is valid. [OK]\n");
743 for (i
= 0; i
< 16; i
++)
745 sprintf(uuid
+ i
* 2, "%02x", g_ventoy_map_data
.diskuuid
[i
]);
747 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", uuid
);
748 g_ventoy_disk_size
= g_ventoy_map_data
.disksize
;
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
);
755 G_DEBUG("ventoy map data is invalid, get from resource\n");
757 if (resource_string_value("ventoy", 0, "disksize", &value
) == 0)
759 G_DEBUG("ventoy.disksize: %s\n", value
);
760 g_ventoy_disk_size
= strtouq(value
, NULL
, 0);
763 if (resource_string_value("ventoy", 0, "diskuuid", &value
) == 0)
765 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", value
);
766 G_DEBUG("ventoy.diskuuid: <%s>\n", value
);
771 if (g_ventoy_disk_size
!= pp
->mediasize
)
776 if (strncmp(pp
->name
, "cd", 2) == 0 || strchr(pp
->name
, '/'))
781 /* read UUID from disk */
782 gp
= g_new_geomf(mp
, "ventoy:taste");
784 gp
->access
= g_ventoy_access
;
785 gp
->orphan
= g_ventoy_orphan
;
786 cp
= g_new_consumer(gp
);
789 g_access(cp
, 1, 0, 0);
791 buf
= g_read_data(cp
, 0, pp
->sectorsize
, NULL
);
793 g_access(cp
, -1, 0, 0);
796 g_destroy_consumer(cp
);
805 for (i
= 0; i
< 16; i
++)
807 sprintf(uuid
+ i
* 2, "%02x", buf
[0x180 + i
]);
810 if (strncmp(g_ventoy_disk_uuid
, uuid
, 32))
815 if (memcmp(mbrdata
, buf
, 0x30) || memcmp(mbrdata
+ 0x30, buf
+ 0x190, 16))
824 G_DEBUG("ventoy disk check OK\n");
830 static struct g_geom
*
831 g_ventoy_taste(struct g_class
*mp
, struct g_provider
*pp
, int flags __unused
)
839 struct g_ventoy_metadata md
;
840 struct g_ventoy_softc
*sc
;
847 G_DEBUG("%s(%s, %s)\n", __func__
, mp
->name
, pp
->name
);
850 /* Skip providers that are already open for writing. */
854 if (!g_vtoy_check_disk(mp
, pp
))
859 g_ventoy_tasted
= true;
861 G_DEBUG("######### ventoy disk <%s> #############\n", pp
->name
);
863 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
865 disknum
= (int)g_ventoy_map_data
.segnum
;
866 G_DEBUG("segnum from map data is:<%d>\n", disknum
);
870 resource_int_value("ventoy", 0, "segnum", &disknum
);
871 G_DEBUG("segnum from resource is:<%d>\n", disknum
);
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();
879 md
.md_all
= (uint16_t)disknum
;
880 bzero(md
.md_provider
, sizeof(md
.md_provider
));
881 /* This field is not important here. */
884 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
886 G_VENTOY_DEBUG(0, "Cannot create device %s.",
892 for (i
= 0; i
< disknum
; i
++)
894 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
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
);
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
;
905 if (resource_string_value("ventoy", i
, "seg", &value
) == 0)
907 g_disk_map_start
= strtouq(value
, &endpos
, 0);
908 g_disk_map_end
= strtouq(endpos
+ 1, NULL
, 0);
912 printf("Failed to parse ventoy seg %d\n", i
);
915 G_DEBUG("[resource] ventoy segment%d: %s\n", i
, value
);
918 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp
->name
, gp
->name
);
919 error
= g_ventoy_add_disk(sc
, pp
, i
);
922 "Cannot add disk %s to %s (error=%d).", pp
->name
,
924 g_ventoy_destroy(sc
, 1);
928 g_disk_map_start
= 0;
936 g_ventoy_ctl_create(struct gctl_req
*req
, struct g_class
*mp
)
939 struct g_ventoy_metadata md
;
940 struct g_provider
*pp
;
941 struct g_ventoy_softc
*sc
;
949 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
951 gctl_error(req
, "No '%s' argument.", "nargs");
955 gctl_error(req
, "Too few arguments.");
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");
963 gctl_error(req
, "No 'arg%u' argument.", 0);
966 strlcpy(md
.md_name
, name
, sizeof(md
.md_name
));
967 md
.md_id
= arc4random();
969 md
.md_all
= *nargs
- 1;
970 bzero(md
.md_provider
, sizeof(md
.md_provider
));
971 /* This field is not important here. */
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
);
982 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
984 gctl_error(req
, "Can't configure %s.", md
.md_name
);
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
);
995 name
= gctl_get_asciiparam(req
, param
);
997 sbuf_printf(sb
, " %s", name
);
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
);
1009 if (md
.md_all
!= attached
) {
1010 g_ventoy_destroy(gp
->softc
, 1);
1011 gctl_error(req
, "%s", sbuf_data(sb
));
1016 static struct g_ventoy_softc
*
1017 g_ventoy_find_device(struct g_class
*mp
, const char *name
)
1019 struct g_ventoy_softc
*sc
;
1022 if (strncmp(name
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
1023 name
+= strlen(_PATH_DEV
);
1025 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
1029 if (strcmp(sc
->sc_name
, name
) == 0)
1036 g_ventoy_ctl_destroy(struct gctl_req
*req
, struct g_class
*mp
)
1038 struct g_ventoy_softc
*sc
;
1039 int *force
, *nargs
, error
;
1044 g_topology_assert();
1046 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
1047 if (nargs
== NULL
) {
1048 gctl_error(req
, "No '%s' argument.", "nargs");
1052 gctl_error(req
, "Missing device(s).");
1055 force
= gctl_get_paraml(req
, "force", sizeof(*force
));
1056 if (force
== NULL
) {
1057 gctl_error(req
, "No '%s' argument.", "force");
1061 for (i
= 0; i
< (u_int
)*nargs
; i
++) {
1062 snprintf(param
, sizeof(param
), "arg%u", i
);
1063 name
= gctl_get_asciiparam(req
, param
);
1065 gctl_error(req
, "No 'arg%u' argument.", i
);
1068 sc
= g_ventoy_find_device(mp
, name
);
1070 gctl_error(req
, "No such device: %s.", name
);
1073 error
= g_ventoy_destroy(sc
, *force
);
1075 gctl_error(req
, "Cannot destroy device %s (error=%d).",
1076 sc
->sc_name
, error
);
1083 g_ventoy_config(struct gctl_req
*req
, struct g_class
*mp
, const char *verb
)
1089 g_topology_assert();
1091 version
= gctl_get_paraml(req
, "version", sizeof(*version
));
1092 if (version
== NULL
) {
1093 gctl_error(req
, "No '%s' argument.", "version");
1096 if (*version
!= G_VENTOY_VERSION
) {
1097 gctl_error(req
, "Userland and kernel parts are out of sync.");
1101 if (strcmp(verb
, "create") == 0) {
1102 g_ventoy_ctl_create(req
, mp
);
1104 } else if (strcmp(verb
, "destroy") == 0 ||
1105 strcmp(verb
, "stop") == 0) {
1106 g_ventoy_ctl_destroy(req
, mp
);
1109 gctl_error(req
, "Unknown verb.");
1113 g_ventoy_dumpconf(struct sbuf
*sb
, const char *indent
, struct g_geom
*gp
,
1114 struct g_consumer
*cp
, struct g_provider
*pp
)
1116 struct g_ventoy_softc
*sc
;
1118 g_topology_assert();
1124 } else if (cp
!= NULL
) {
1125 struct g_ventoy_disk
*disk
;
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
);
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");
1141 case G_VENTOY_TYPE_MANUAL
:
1142 sbuf_cat(sb
, "MANUAL");
1145 sbuf_cat(sb
, "UNKNOWN");
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)
1155 sbuf_cat(sb
, "DOWN");
1156 sbuf_cat(sb
, "</State>\n");
1160 DECLARE_GEOM_CLASS(g_ventoy_class
, g_ventoy
);
1161 //MODULE_VERSION(geom_ventoy, 0);