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
)
727 struct g_consumer
*cp
;
730 if (g_ventoy_disk_size
== 0)
732 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
734 G_DEBUG("ventoy map data is valid. [OK]\n");
736 for (i
= 0; i
< 16; i
++)
738 sprintf(uuid
+ i
* 2, "%02x", g_ventoy_map_data
.diskuuid
[i
]);
740 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", uuid
);
741 g_ventoy_disk_size
= g_ventoy_map_data
.disksize
;
743 G_DEBUG("ventoy.disksize: %llu\n", (unsigned long long)g_ventoy_disk_size
);
744 G_DEBUG("ventoy.diskuuid: <%s>\n", g_ventoy_disk_uuid
);
748 G_DEBUG("ventoy map data is invalid, get from resource\n");
750 if (resource_string_value("ventoy", 0, "disksize", &value
) == 0)
752 G_DEBUG("ventoy.disksize: %s\n", value
);
753 g_ventoy_disk_size
= strtouq(value
, NULL
, 0);
756 if (resource_string_value("ventoy", 0, "diskuuid", &value
) == 0)
758 snprintf(g_ventoy_disk_uuid
, sizeof(g_ventoy_disk_uuid
), "%s", value
);
759 G_DEBUG("ventoy.diskuuid: <%s>\n", value
);
764 if (g_ventoy_disk_size
!= pp
->mediasize
)
769 if (strncmp(pp
->name
, "cd", 2) == 0 || strchr(pp
->name
, '/'))
774 /* read UUID from disk */
775 gp
= g_new_geomf(mp
, "ventoy:taste");
777 gp
->access
= g_ventoy_access
;
778 gp
->orphan
= g_ventoy_orphan
;
779 cp
= g_new_consumer(gp
);
782 g_access(cp
, 1, 0, 0);
784 buf
= g_read_data(cp
, 0, pp
->sectorsize
, NULL
);
786 g_access(cp
, -1, 0, 0);
789 g_destroy_consumer(cp
);
798 for (i
= 0; i
< 16; i
++)
800 sprintf(uuid
+ i
* 2, "%02x", buf
[0x180 + i
]);
804 if (strncmp(g_ventoy_disk_uuid
, uuid
, 32) == 0)
812 static struct g_geom
*
813 g_ventoy_taste(struct g_class
*mp
, struct g_provider
*pp
, int flags __unused
)
821 struct g_ventoy_metadata md
;
822 struct g_ventoy_softc
*sc
;
829 G_DEBUG("%s(%s, %s)\n", __func__
, mp
->name
, pp
->name
);
832 /* Skip providers that are already open for writing. */
836 if (!g_vtoy_check_disk(mp
, pp
))
841 g_ventoy_tasted
= true;
843 G_DEBUG("######### ventoy disk <%s> #############\n", pp
->name
);
845 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
847 disknum
= (int)g_ventoy_map_data
.segnum
;
848 G_DEBUG("segnum from map data is:<%d>\n", disknum
);
852 resource_int_value("ventoy", 0, "segnum", &disknum
);
853 G_DEBUG("segnum from resource is:<%d>\n", disknum
);
856 strlcpy(md
.md_magic
, G_VENTOY_MAGIC
, sizeof(md
.md_magic
));
857 md
.md_version
= G_VENTOY_VERSION
;
858 strlcpy(md
.md_name
, "IMAGE", sizeof(md
.md_name
));
859 md
.md_id
= arc4random();
861 md
.md_all
= (uint16_t)disknum
;
862 bzero(md
.md_provider
, sizeof(md
.md_provider
));
863 /* This field is not important here. */
866 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
868 G_VENTOY_DEBUG(0, "Cannot create device %s.",
874 for (i
= 0; i
< disknum
; i
++)
876 if (VENTOY_MAP_VALID(g_ventoy_map_data
.magic2
))
878 G_DEBUG("[map] ventoy segment%d: 0x%llx@0x%llx\n", i
,
879 (long long)g_ventoy_map_data
.seglist
[i
].seg_start_bytes
,
880 (long long)g_ventoy_map_data
.seglist
[i
].seg_end_bytes
);
882 g_disk_map_start
= (off_t
)g_ventoy_map_data
.seglist
[i
].seg_start_bytes
;
883 g_disk_map_end
= (off_t
)g_ventoy_map_data
.seglist
[i
].seg_end_bytes
;
887 if (resource_string_value("ventoy", i
, "seg", &value
) == 0)
889 g_disk_map_start
= strtouq(value
, &endpos
, 0);
890 g_disk_map_end
= strtouq(endpos
+ 1, NULL
, 0);
894 printf("Failed to parse ventoy seg %d\n", i
);
897 G_DEBUG("[resource] ventoy segment%d: %s\n", i
, value
);
900 G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp
->name
, gp
->name
);
901 error
= g_ventoy_add_disk(sc
, pp
, i
);
904 "Cannot add disk %s to %s (error=%d).", pp
->name
,
906 g_ventoy_destroy(sc
, 1);
910 g_disk_map_start
= 0;
918 g_ventoy_ctl_create(struct gctl_req
*req
, struct g_class
*mp
)
921 struct g_ventoy_metadata md
;
922 struct g_provider
*pp
;
923 struct g_ventoy_softc
*sc
;
931 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
933 gctl_error(req
, "No '%s' argument.", "nargs");
937 gctl_error(req
, "Too few arguments.");
941 strlcpy(md
.md_magic
, G_VENTOY_MAGIC
, sizeof(md
.md_magic
));
942 md
.md_version
= G_VENTOY_VERSION
;
943 name
= gctl_get_asciiparam(req
, "arg0");
945 gctl_error(req
, "No 'arg%u' argument.", 0);
948 strlcpy(md
.md_name
, name
, sizeof(md
.md_name
));
949 md
.md_id
= arc4random();
951 md
.md_all
= *nargs
- 1;
952 bzero(md
.md_provider
, sizeof(md
.md_provider
));
953 /* This field is not important here. */
956 /* Check all providers are valid */
957 for (no
= 1; no
< *nargs
; no
++) {
958 snprintf(param
, sizeof(param
), "arg%u", no
);
959 pp
= gctl_get_provider(req
, param
);
964 gp
= g_ventoy_create(mp
, &md
, G_VENTOY_TYPE_MANUAL
);
966 gctl_error(req
, "Can't configure %s.", md
.md_name
);
971 sb
= sbuf_new_auto();
972 sbuf_printf(sb
, "Can't attach disk(s) to %s:", gp
->name
);
973 for (attached
= 0, no
= 1; no
< *nargs
; no
++) {
974 snprintf(param
, sizeof(param
), "arg%u", no
);
975 pp
= gctl_get_provider(req
, param
);
977 name
= gctl_get_asciiparam(req
, param
);
979 sbuf_printf(sb
, " %s", name
);
982 if (g_ventoy_add_disk(sc
, pp
, no
- 1) != 0) {
983 G_VENTOY_DEBUG(1, "Disk %u (%s) not attached to %s.",
984 no
, pp
->name
, gp
->name
);
985 sbuf_printf(sb
, " %s", pp
->name
);
991 if (md
.md_all
!= attached
) {
992 g_ventoy_destroy(gp
->softc
, 1);
993 gctl_error(req
, "%s", sbuf_data(sb
));
998 static struct g_ventoy_softc
*
999 g_ventoy_find_device(struct g_class
*mp
, const char *name
)
1001 struct g_ventoy_softc
*sc
;
1004 if (strncmp(name
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0)
1005 name
+= strlen(_PATH_DEV
);
1007 LIST_FOREACH(gp
, &mp
->geom
, geom
) {
1011 if (strcmp(sc
->sc_name
, name
) == 0)
1018 g_ventoy_ctl_destroy(struct gctl_req
*req
, struct g_class
*mp
)
1020 struct g_ventoy_softc
*sc
;
1021 int *force
, *nargs
, error
;
1026 g_topology_assert();
1028 nargs
= gctl_get_paraml(req
, "nargs", sizeof(*nargs
));
1029 if (nargs
== NULL
) {
1030 gctl_error(req
, "No '%s' argument.", "nargs");
1034 gctl_error(req
, "Missing device(s).");
1037 force
= gctl_get_paraml(req
, "force", sizeof(*force
));
1038 if (force
== NULL
) {
1039 gctl_error(req
, "No '%s' argument.", "force");
1043 for (i
= 0; i
< (u_int
)*nargs
; i
++) {
1044 snprintf(param
, sizeof(param
), "arg%u", i
);
1045 name
= gctl_get_asciiparam(req
, param
);
1047 gctl_error(req
, "No 'arg%u' argument.", i
);
1050 sc
= g_ventoy_find_device(mp
, name
);
1052 gctl_error(req
, "No such device: %s.", name
);
1055 error
= g_ventoy_destroy(sc
, *force
);
1057 gctl_error(req
, "Cannot destroy device %s (error=%d).",
1058 sc
->sc_name
, error
);
1065 g_ventoy_config(struct gctl_req
*req
, struct g_class
*mp
, const char *verb
)
1071 g_topology_assert();
1073 version
= gctl_get_paraml(req
, "version", sizeof(*version
));
1074 if (version
== NULL
) {
1075 gctl_error(req
, "No '%s' argument.", "version");
1078 if (*version
!= G_VENTOY_VERSION
) {
1079 gctl_error(req
, "Userland and kernel parts are out of sync.");
1083 if (strcmp(verb
, "create") == 0) {
1084 g_ventoy_ctl_create(req
, mp
);
1086 } else if (strcmp(verb
, "destroy") == 0 ||
1087 strcmp(verb
, "stop") == 0) {
1088 g_ventoy_ctl_destroy(req
, mp
);
1091 gctl_error(req
, "Unknown verb.");
1095 g_ventoy_dumpconf(struct sbuf
*sb
, const char *indent
, struct g_geom
*gp
,
1096 struct g_consumer
*cp
, struct g_provider
*pp
)
1098 struct g_ventoy_softc
*sc
;
1100 g_topology_assert();
1106 } else if (cp
!= NULL
) {
1107 struct g_ventoy_disk
*disk
;
1112 sbuf_printf(sb
, "%s<End>%jd</End>\n", indent
,
1113 (intmax_t)disk
->d_end
);
1114 sbuf_printf(sb
, "%s<Start>%jd</Start>\n", indent
,
1115 (intmax_t)disk
->d_start
);
1117 sbuf_printf(sb
, "%s<ID>%u</ID>\n", indent
, (u_int
)sc
->sc_id
);
1118 sbuf_printf(sb
, "%s<Type>", indent
);
1119 switch (sc
->sc_type
) {
1120 case G_VENTOY_TYPE_AUTOMATIC
:
1121 sbuf_cat(sb
, "AUTOMATIC");
1123 case G_VENTOY_TYPE_MANUAL
:
1124 sbuf_cat(sb
, "MANUAL");
1127 sbuf_cat(sb
, "UNKNOWN");
1130 sbuf_cat(sb
, "</Type>\n");
1131 sbuf_printf(sb
, "%s<Status>Total=%u, Online=%u</Status>\n",
1132 indent
, sc
->sc_ndisks
, g_ventoy_nvalid(sc
));
1133 sbuf_printf(sb
, "%s<State>", indent
);
1134 if (sc
->sc_provider
!= NULL
&& sc
->sc_provider
->error
== 0)
1137 sbuf_cat(sb
, "DOWN");
1138 sbuf_cat(sb
, "</State>\n");
1142 DECLARE_GEOM_CLASS(g_ventoy_class
, g_ventoy
);
1143 //MODULE_VERSION(geom_ventoy, 0);