From d80a008c04872ca89d51c3d414e1834d317d8519 Mon Sep 17 00:00:00 2001 From: longpanda Date: Fri, 31 Jul 2020 23:08:17 +0800 Subject: [PATCH] add support to freebsd --- .../grub-2.04/grub-core/Makefile.core.def | 1 + .../grub-2.04/grub-core/ventoy/ventoy.c | 68 +- .../grub-2.04/grub-core/ventoy/ventoy_def.h | 34 + .../grub-2.04/grub-core/ventoy/ventoy_linux.c | 3 +- INSTALL/Ventoy2Disk.exe | Bin 319488 -> 319488 bytes INSTALL/Ventoy2Disk.sh | 9 +- INSTALL/grub/grub.cfg | 107 ++ INSTALL/tool/ventoy_lib.sh | 14 +- INSTALL/ventoy/ventoy_unix.cpio | Bin 0 -> 27648 bytes LANGUAGES/languages.ini | Bin 110638 -> 110514 bytes Unix/pack_unix.sh | 13 + Unix/ventoy_unix.cpio | Bin 0 -> 27648 bytes .../geom_ventoy_ko/11.x/32/geom_ventoy.ko.xz | Bin 0 -> 6596 bytes .../geom_ventoy_ko/11.x/64/geom_ventoy.ko.xz | Bin 0 -> 6200 bytes .../geom_ventoy_ko/12.x/32/geom_ventoy.ko.xz | Bin 0 -> 6508 bytes .../geom_ventoy_ko/12.x/64/geom_ventoy.ko.xz | Bin 0 -> 6184 bytes .../geom_ventoy_src/10.x/same_with_11.x.txt | 1 + .../11.x/sys/geom/ventoy/g_ventoy.c | 1081 +++++++++++++++++ .../11.x/sys/geom/ventoy/g_ventoy.h | 123 ++ .../sys/modules/geom/geom_ventoy/Makefile | 8 + .../12.x/sys/geom/ventoy/g_ventoy.c | 1081 +++++++++++++++++ .../12.x/sys/geom/ventoy/g_ventoy.h | 123 ++ .../sys/modules/geom/geom_ventoy/Makefile | 8 + Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c | 3 +- 24 files changed, 2662 insertions(+), 15 deletions(-) create mode 100644 INSTALL/ventoy/ventoy_unix.cpio create mode 100644 Unix/pack_unix.sh create mode 100644 Unix/ventoy_unix.cpio create mode 100644 Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/11.x/32/geom_ventoy.ko.xz create mode 100644 Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/11.x/64/geom_ventoy.ko.xz create mode 100644 Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/12.x/32/geom_ventoy.ko.xz create mode 100644 Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/12.x/64/geom_ventoy.ko.xz create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/10.x/same_with_11.x.txt create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.c create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.h create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/modules/geom/geom_ventoy/Makefile create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.c create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.h create mode 100644 Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/modules/geom/geom_ventoy/Makefile diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def b/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def index c8867f37..e8d4a7f4 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/Makefile.core.def @@ -1578,6 +1578,7 @@ module = { name = ventoy; common = ventoy/ventoy.c; common = ventoy/ventoy_linux.c; + common = ventoy/ventoy_unix.c; common = ventoy/ventoy_windows.c; common = ventoy/ventoy_plugin.c; common = ventoy/ventoy_json.c; diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c index 43bdff5d..0cc78d25 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy.c @@ -305,6 +305,18 @@ static grub_err_t ventoy_cmd_break(grub_extcmd_context_t ctxt, int argc, char ** VENTOY_CMD_RETURN(GRUB_ERR_NONE); } +static grub_err_t ventoy_cmd_strstr(grub_extcmd_context_t ctxt, int argc, char **args) +{ + (void)ctxt; + + if (argc != 2) + { + return 1; + } + + return (grub_strstr(args[0], args[1])) ? 0 : 1; +} + static grub_err_t ventoy_cmd_incr(grub_extcmd_context_t ctxt, int argc, char **args) { long value_long = 0; @@ -1389,7 +1401,7 @@ static grub_err_t ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt, int arg VENTOY_CMD_RETURN(GRUB_ERR_NONE); } -static int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid) +int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid) { grub_disk_t disk; char *device_name; @@ -1484,7 +1496,7 @@ int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector) return 1; } - if (buf[i] == 0x91 && buf[i + 1] == 0x00 && x86count == 1) + if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0x00 && x86count == 1) { debug("0x9100 assume %s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]); return 1; @@ -2231,6 +2243,50 @@ static grub_err_t ventoy_cmd_find_bootable_hdd(grub_extcmd_context_t ctxt, int a return 0; } +static grub_err_t ventoy_cmd_parse_volume(grub_extcmd_context_t ctxt, int argc, char **args) +{ + int len; + grub_file_t file; + char buf[64]; + ventoy_iso9660_vd pvd; + + (void)ctxt; + (void)argc; + + if (argc != 3) + { + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s sysid volid \n", cmd_raw_name); + } + + file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]); + if (!file) + { + debug("failed to open file %s\n", args[0]); + return 0; + } + + grub_file_seek(file, 16 * 2048); + len = (int)grub_file_read(file, &pvd, sizeof(pvd)); + if (len != sizeof(pvd)) + { + debug("failed to read pvd %d\n", len); + goto end; + } + + grub_memset(buf, 0, sizeof(buf)); + grub_memcpy(buf, pvd.sys, sizeof(pvd.sys)); + ventoy_set_env(args[1], buf); + + grub_memset(buf, 0, sizeof(buf)); + grub_memcpy(buf, pvd.vol, sizeof(pvd.vol)); + ventoy_set_env(args[2], buf); + +end: + grub_file_close(file); + + return 0; +} + grub_uint64_t ventoy_grub_get_file_size(const char *fmt, ...) { grub_uint64_t size = 0; @@ -2353,6 +2409,7 @@ static int ventoy_env_init(void) static cmd_para ventoy_cmds[] = { { "vt_incr", ventoy_cmd_incr, 0, NULL, "{Var} {INT}", "Increase integer variable", NULL }, + { "vt_strstr", ventoy_cmd_strstr, 0, NULL, "", "", NULL }, { "vt_debug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, { "vtdebug", ventoy_cmd_debug, 0, NULL, "{on|off}", "turn debug on/off", NULL }, { "vtbreak", ventoy_cmd_break, 0, NULL, "{level}", "set debug break", NULL }, @@ -2410,6 +2467,13 @@ static cmd_para ventoy_cmds[] = { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL }, { "vt_check_plugin_json", ventoy_cmd_plugin_check_json, 0, NULL, "", "", NULL }, + + { "vt_parse_iso_volume", ventoy_cmd_parse_volume, 0, NULL, "", "", NULL }, + { "vt_unix_parse_freebsd_ver", ventoy_cmd_unix_freebsd_ver, 0, NULL, "", "", NULL }, + { "vt_unix_reset", ventoy_cmd_unix_reset, 0, NULL, "", "", NULL }, + { "vt_unix_replace_conf", ventoy_cmd_unix_replace_conf, 0, NULL, "", "", NULL }, + { "vt_unix_replace_ko", ventoy_cmd_unix_replace_ko, 0, NULL, "", "", NULL }, + { "vt_unix_chain_data", ventoy_cmd_unix_chain_data, 0, NULL, "", "", NULL }, }; diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h index 2bf4fec4..952bd9dc 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_def.h @@ -123,6 +123,16 @@ typedef struct ventoy_udf_override grub_uint32_t position; }ventoy_udf_override; +typedef struct ventoy_iso9660_vd +{ + grub_uint8_t type; + grub_uint8_t id[5]; + grub_uint8_t ver; + grub_uint8_t res; + char sys[32]; + char vol[32]; +}ventoy_iso9660_vd; + #pragma pack() #define img_type_iso 0 @@ -667,6 +677,24 @@ extern int g_ventoy_iso_uefi_drv; extern int g_ventoy_case_insensitive; extern grub_uint8_t g_ventoy_chain_type; + +#define ventoy_unix_fill_virt(new_data, new_len) \ +{ \ + data_secs = (new_len + 2047) / 2048; \ + cur->mem_sector_start = sector; \ + cur->mem_sector_end = cur->mem_sector_start + data_secs; \ + cur->mem_sector_offset = offset; \ + cur->remap_sector_start = 0; \ + cur->remap_sector_end = 0; \ + cur->org_sector_start = 0; \ + grub_memcpy(override + offset, new_data, new_len); \ + cur++; \ + sector += data_secs; \ + offset += new_len; \ + chain->virt_img_size_in_bytes += data_secs * 2048; \ +} + +char * ventoy_get_line(char *start); int ventoy_cmp_img(img_info *img1, img_info *img2); void ventoy_swap_img(img_info *img1, img_info *img2); char * ventoy_plugin_get_cur_install_template(const char *isopath); @@ -687,6 +715,12 @@ grub_err_t ventoy_cmd_linux_get_main_initrd_index(grub_extcmd_context_t ctxt, in grub_err_t ventoy_cmd_collect_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args); grub_err_t ventoy_cmd_wim_patch_count(grub_extcmd_context_t ctxt, int argc, char **args); grub_err_t ventoy_cmd_locate_wim_patch(grub_extcmd_context_t ctxt, int argc, char **args); +grub_err_t ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt, int argc, char **args); +int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid); +grub_err_t ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt, int argc, char **args); +grub_err_t ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt, int argc, char **args); +grub_err_t ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt, int argc, char **args); +grub_err_t ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt, int argc, char **args); #endif /* __VENTOY_DEF_H__ */ diff --git a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c index 8e1ffdd6..ad871a10 100644 --- a/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c +++ b/GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_linux.c @@ -38,8 +38,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); - -static char * ventoy_get_line(char *start) +char * ventoy_get_line(char *start) { if (start == NULL) { diff --git a/INSTALL/Ventoy2Disk.exe b/INSTALL/Ventoy2Disk.exe index 20dee82a94e323b76d6d56122ba30d01d59f056b..c530a6b544b64ef9806ece1cbe655fbc4dc5681f 100644 GIT binary patch delta 384 zcmZoTAlz_3cmoF`vz?RTW^Tsa*BO^gK7PYSFM*kXp>c1)-~ay^m>3xNTLXanuHK^m z|NnQj76ZwKpP~G%YCu7oRs5}TlMQdaV)U3Sb89K1%;ZD2mMc!R1FGq6^#YQOhZr~* z7`l7?fNbkS{H;?NfF}Q(JoC1m@RgVU|2H4uF+LC<7v1gq!}#orrpdQ%n=_3}7Sr=0S@o@DT_>Mx@tQU3sDbt7|0aw;`6#QHu++Sg{L1N9<}zus p9s&v-ny4t=E-;S?h?%zw%wsXo<=kxk+uYb9+9GSa=V_MhA^`2PnezYu delta 402 zcmZoTAlz_3cmoF`^Zq|lo4FZxUuRrD`S=YRy=-O%hQ_@GfB*kyU}9k4Z;b}>yLyZM z|Nr0BS_~u`eunb58Uh7vR`IuLPBy&xiZOVy%&n!2+LI66T5iy52U0oL4M;W~WZ+<6 z=$`5aWOuiEf%N`<&)+(ifq}vLAb;yrAl>+Da@TD=;iE7A|8G9RV|*Y!F1p+Ihw<4L z#gk9pHf5}x%y>t{L&FJZ%G21x84L`J3_*bb8SjBQ`L`Vo$T+duiQ$DOShEb0=I@RW z4gA{<1n_S=7?8oT3aCzTa^f8cjuTM1=AJv-d+soD_!$EI9}hGT2( zczAe(gv1z_8yQbOIhRR$`scY!g6#tHn1GmhyTCja16|HT=3Ewb7HJlx+dWUSY!?9l D0cWM3 diff --git a/INSTALL/Ventoy2Disk.sh b/INSTALL/Ventoy2Disk.sh index 02a69060..06521af4 100644 --- a/INSTALL/Ventoy2Disk.sh +++ b/INSTALL/Ventoy2Disk.sh @@ -39,12 +39,17 @@ if ! [ -f ./tool/ash ]; then if ! [ -f ./tool/ash ]; then echo 'Failed to decompress tools ...' - cd $OLDDIR + if [ -n "$OLDDIR" ]; then + cd $OLDDIR + fi exit 1 fi fi ./tool/ash ./tool/VentoyWorker.sh $* -cd $OLDDIR +if [ -n "$OLDDIR" ]; then + cd $OLDDIR +fi + diff --git a/INSTALL/grub/grub.cfg b/INSTALL/grub/grub.cfg index 865c9237..64fe729b 100644 --- a/INSTALL/grub/grub.cfg +++ b/INSTALL/grub/grub.cfg @@ -89,6 +89,14 @@ function get_os_type { fi done + if [ "$vtoy_os" = "Linux" ]; then + if vt_strstr "$vt_system_id" "FreeBSD"; then + set vtoy_os=Unix + elif [ -e (loop)/bin/freebsd-version ]; then + set vtoy_os=Unix + fi + fi + if [ -n "${vtdebug_flag}" ]; then echo ISO is $vtoy_os fi @@ -211,6 +219,70 @@ function distro_specify_initrd_file_phase2 { fi } +function ventoy_freebsd_proc { + if regexp "^12_[0-9]" $vt_volume_id; then + set vt_freebsd_ver=12.x + elif regexp "^11_[0-9]" $vt_volume_id; then + set vt_freebsd_ver=11.x + elif regexp "^10_[0-9]" $vt_volume_id; then + set vt_freebsd_ver=10.x + elif [ -e (loop)/bin/freebsd-version ]; then + vt_unix_parse_freebsd_ver (loop)/bin/freebsd-version vt_userland_ver + if regexp "\"12\.[0-9]-" $vt_userland_ver; then + set vt_freebsd_ver=12.x + elif regexp "\"11\.[0-9]-" $vt_userland_ver; then + set vt_freebsd_ver=11.x + elif regexp "\"10\.[0-9]-" $vt_userland_ver; then + set vt_freebsd_ver=10.x + fi + else + set vt_freebsd_ver=12.x + fi + + if file --is-i386-kfreebsd (loop)/boot/kernel/kernel; then + set vt_freebsd_bit=32 + else + set vt_freebsd_bit=64 + fi + + if [ -n "${vtdebug_flag}" ]; then + echo "This is FreeBSD $vt_freebsd_ver ${vt_freebsd_bit}bit" + fi + + for file in "geom_nop" "ipmi"; do + if [ -e (loop)/boot/kernel/${file}.ko ]; then + set vt_unix_ko=$file + break + fi + done + + vt_unix_replace_ko $vt_unix_ko (vtunix)/ventoy_unix/FreeBSD/geom_ventoy_ko/$vt_freebsd_ver/$vt_freebsd_bit/geom_ventoy.ko.xz + vt_unix_replace_conf FreeBSD ${1}${chosen_path} +} + +function ventoy_unix_comm_proc { + vt_unix_reset + + if [ "$ventoy_compatible" = "NO" ]; then + loopback vtunix $vtoy_efi_part/ventoy/ventoy_unix.cpio + + set vt_unix_type=unknown + if vt_strstr "$vt_system_id" "FreeBSD"; then + ventoy_freebsd_proc $1 ${chosen_path} + elif [ -e (loop)/bin/freebsd-version ]; then + ventoy_freebsd_proc $1 ${chosen_path} + else + if [ -n "${vtdebug_flag}" ]; then + echo "Unknown unix type" + fi + fi + fi + + vt_unix_chain_data ${1}${chosen_path} + ventoy_debug_pause +} + + function uefi_windows_menu_func { vt_windows_reset @@ -330,6 +402,18 @@ function uefi_linux_menu_func { fi } +function uefi_unix_menu_func { + ventoy_unix_comm_proc $1 ${chosen_path} + + if [ -n "$vtoy_chain_mem_addr" ]; then + ventoy_cli_console + chainloader ${vtoy_path}/ventoy_x64.efi env_param=${env_param} isoefi=${LoadIsoEfiDriver} FirstTry=${FirstTryBootFile} ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size} + boot + else + echo "chain empty failed" + ventoy_pause + fi +} function uefi_iso_menu_func { @@ -358,6 +442,7 @@ function uefi_iso_menu_func { fi loopback loop ${1}${chosen_path} + vt_parse_iso_volume ${1}${chosen_path} vt_system_id vt_volume_id get_os_type (loop) if [ -d (loop)/EFI ]; then @@ -382,6 +467,8 @@ function uefi_iso_menu_func { if [ "$vtoy_os" = "Windows" ]; then vt_check_compatible_pe (loop) uefi_windows_menu_func $1 ${chosen_path} + elif [ "$vtoy_os" = "Unix" ]; then + uefi_unix_menu_func $1 ${chosen_path} else uefi_linux_menu_func $1 ${chosen_path} fi @@ -501,6 +588,20 @@ function legacy_linux_menu_func { fi } + +function legacy_unix_menu_func { + ventoy_unix_comm_proc $1 ${chosen_path} + + if [ -n "$vtoy_chain_mem_addr" ]; then + linux16 $vtoy_path/ipxe.krn ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size} + boot + else + echo "chain empty failed" + ventoy_pause + fi +} + + function legacy_iso_menu_func { if [ -d (loop)/ ]; then @@ -519,6 +620,7 @@ function legacy_iso_menu_func { fi loopback loop ${1}${chosen_path} + vt_parse_iso_volume ${1}${chosen_path} vt_system_id vt_volume_id get_os_type (loop) if [ -n "$vtcompat" ]; then @@ -535,6 +637,8 @@ function legacy_iso_menu_func { if [ "$vtoy_os" = "Windows" ]; then vt_check_compatible_pe (loop) legacy_windows_menu_func $1 ${chosen_path} + elif [ "$vtoy_os" = "Unix" ]; then + legacy_unix_menu_func $1 ${chosen_path} else legacy_linux_menu_func $1 ${chosen_path} fi @@ -550,6 +654,9 @@ function legacy_iso_memdisk { } function iso_common_menuentry { + unset vt_system_id + unset vt_volume_id + if [ "$grub_platform" = "pc" ]; then if vt_check_mode 0; then legacy_iso_memdisk $vtoy_iso_part diff --git a/INSTALL/tool/ventoy_lib.sh b/INSTALL/tool/ventoy_lib.sh index 915deaa6..78d93e9c 100644 --- a/INSTALL/tool/ventoy_lib.sh +++ b/INSTALL/tool/ventoy_lib.sh @@ -129,13 +129,13 @@ is_disk_contains_ventoy() { PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') PART2_TYPE=$(dd if=$DISK bs=1 count=1 skip=466 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') - if [ "$PART1_TYPE" != "EE" ]; then - if [ "$PART2_TYPE" != "EF" ]; then - vtdebug "part2 type is $PART2_TYPE not EF" - ventoy_false - return - fi - fi + # if [ "$PART1_TYPE" != "EE" ]; then + # if [ "$PART2_TYPE" != "EF" ]; then + # vtdebug "part2 type is $PART2_TYPE not EF" + # ventoy_false + # return + # fi + # fi # PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') # if [ "$PART1_TYPE" != "07" ]; then diff --git a/INSTALL/ventoy/ventoy_unix.cpio b/INSTALL/ventoy/ventoy_unix.cpio new file mode 100644 index 0000000000000000000000000000000000000000..e6a324e611be670c5c86ed26e1a28af2ee9f4f1f GIT binary patch literal 27648 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 literal 0 HcmV?d00001 diff --git a/LANGUAGES/languages.ini b/LANGUAGES/languages.ini index 86bc100a117a2f105ae0da3338b2771f663bbd48..e2e57eb7b23c8b24fcad050a14272999f77b01d4 100644 GIT binary patch delta 22 ecmZ4YfNj%#whc{Mlf{ventoy_unix.cpio + +echo '======== SUCCESS =============' + +rm -f $VENTOY_PATH/INSTALL/ventoy/ventoy_unix.cpio +cp -a ventoy_unix.cpio $VENTOY_PATH/INSTALL/ventoy/ + diff --git a/Unix/ventoy_unix.cpio b/Unix/ventoy_unix.cpio new file mode 100644 index 0000000000000000000000000000000000000000..e6a324e611be670c5c86ed26e1a28af2ee9f4f1f GIT binary patch literal 27648 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 literal 0 HcmV?d00001 diff --git a/Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/11.x/32/geom_ventoy.ko.xz b/Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/11.x/32/geom_ventoy.ko.xz new file mode 100644 index 0000000000000000000000000000000000000000..de98acbd50c1d62fa05249adcb0c293262046b06 GIT binary patch literal 6596 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 S`y5uW#Ao{g000001X)@o2BQA} literal 0 HcmV?d00001 diff --git a/Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/12.x/64/geom_ventoy.ko.xz b/Unix/ventoy_unix/FreeBSD/geom_ventoy_ko/12.x/64/geom_ventoy.ko.xz new file mode 100644 index 0000000000000000000000000000000000000000..b24d3d6292a020a9124d24b7bc1c9d2c9e5d6f27 GIT binary patch literal 6184 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 G1X)@hjPaBJ literal 0 HcmV?d00001 diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/10.x/same_with_11.x.txt b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/10.x/same_with_11.x.txt new file mode 100644 index 00000000..061dba92 --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/10.x/same_with_11.x.txt @@ -0,0 +1 @@ +same with 11.x \ No newline at end of file diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.c b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.c new file mode 100644 index 00000000..c99fadd5 --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.c @@ -0,0 +1,1081 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 longpanda + * Copyright (c) 2004-2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/* + * This file is just copied from g_concat.h and replace strings + * "concat" ==> "ventoy" + * "CONCAT" ==> "VENTOY" + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE(geom_ventoy, "GEOM ventoy support"); + +static MALLOC_DEFINE(M_VENTOY, "ventoy_data", "GEOM_VENTOY Data"); + +SYSCTL_DECL(_kern_geom); +static SYSCTL_NODE(_kern_geom, OID_AUTO, ventoy, CTLFLAG_RW, 0, + "GEOM_VENTOY stuff"); +static u_int g_ventoy_debug = 0; +SYSCTL_UINT(_kern_geom_ventoy, OID_AUTO, debug, CTLFLAG_RWTUN, &g_ventoy_debug, 0, + "Debug level"); + +extern int resource_string_value(const char *name, int unit, const char *resname, const char **result); +extern int resource_int_value(const char *name, int unit, const char *resname, int *result); + +static int g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force); +static int g_ventoy_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp); + +static g_taste_t g_ventoy_taste; +static g_ctl_req_t g_ventoy_config; +static g_dumpconf_t g_ventoy_dumpconf; + +static const char *g_ventoy_disk_uuid = NULL; +static bool g_ventoy_tasted = false; +static off_t g_ventoy_disk_size = 0; +static off_t g_disk_map_start = 0; +static off_t g_disk_map_end = 0; + +struct g_class g_ventoy_class = { + .name = G_VENTOY_CLASS_NAME, + .version = G_VERSION, + .ctlreq = g_ventoy_config, + .taste = g_ventoy_taste, + .destroy_geom = g_ventoy_destroy_geom +}; + + +/* + * Greatest Common Divisor. + */ +static u_int +gcd(u_int a, u_int b) +{ + u_int c; + + while (b != 0) { + c = a; + a = b; + b = (c % b); + } + return (a); +} + +/* + * Least Common Multiple. + */ +static u_int +lcm(u_int a, u_int b) +{ + + return ((a * b) / gcd(a, b)); +} + +/* + * Return the number of valid disks. + */ +static u_int +g_ventoy_nvalid(struct g_ventoy_softc *sc) +{ + u_int i, no; + + no = 0; + for (i = 0; i < sc->sc_ndisks; i++) { + if (sc->sc_disks[i].d_consumer != NULL) + no++; + } + + return (no); +} + +static void +g_ventoy_remove_disk(struct g_ventoy_disk *disk) +{ + struct g_consumer *cp; + struct g_ventoy_softc *sc; + + g_topology_assert(); + KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); + sc = disk->d_softc; + cp = disk->d_consumer; + + if (!disk->d_removed) { + G_VENTOY_DEBUG(0, "Disk %s removed from %s.", + cp->provider->name, sc->sc_name); + disk->d_removed = 1; + } + + if (sc->sc_provider != NULL) { + sc->sc_provider->flags |= G_PF_WITHER; + G_VENTOY_DEBUG(0, "Device %s deactivated.", + sc->sc_provider->name); + g_orphan_provider(sc->sc_provider, ENXIO); + sc->sc_provider = NULL; + } + + if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) + return; + disk->d_consumer = NULL; + g_detach(cp); + g_destroy_consumer(cp); + /* If there are no valid disks anymore, remove device. */ + if (LIST_EMPTY(&sc->sc_geom->consumer)) + g_ventoy_destroy(sc, 1); +} + +static void +g_ventoy_orphan(struct g_consumer *cp) +{ + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct g_geom *gp; + + g_topology_assert(); + gp = cp->geom; + sc = gp->softc; + if (sc == NULL) + return; + + disk = cp->private; + if (disk == NULL) /* Possible? */ + return; + g_ventoy_remove_disk(disk); +} + +static int +g_ventoy_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_consumer *cp1, *cp2, *tmp; + struct g_ventoy_disk *disk; + struct g_geom *gp; + int error; + + if (dw > 0) /* readonly */ + return (EPERM); + + g_topology_assert(); + gp = pp->geom; + + /* On first open, grab an extra "exclusive" bit */ + if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) + de++; + /* ... and let go of it on last close */ + if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) + de--; + + LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { + error = g_access(cp1, dr, dw, de); + if (error != 0) + goto fail; + disk = cp1->private; + if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && + disk->d_removed) { + g_ventoy_remove_disk(disk); /* May destroy geom. */ + } + } + return (0); + +fail: + LIST_FOREACH(cp2, &gp->consumer, consumer) { + if (cp1 == cp2) + break; + g_access(cp2, -dr, -dw, -de); + } + return (error); +} + +static void +g_ventoy_kernel_dump(struct bio *bp) +{ + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct bio *cbp; + struct g_kerneldump *gkd; + u_int i; + + sc = bp->bio_to->geom->softc; + gkd = (struct g_kerneldump *)bp->bio_data; + for (i = 0; i < sc->sc_ndisks; i++) { + if (sc->sc_disks[i].d_start <= gkd->offset && + sc->sc_disks[i].d_end > gkd->offset) + break; + } + if (i == sc->sc_ndisks) + g_io_deliver(bp, EOPNOTSUPP); + disk = &sc->sc_disks[i]; + gkd->offset -= disk->d_start; + if (gkd->length > disk->d_end - disk->d_start - gkd->offset) + gkd->length = disk->d_end - disk->d_start - gkd->offset; + cbp = g_clone_bio(bp); + if (cbp == NULL) { + g_io_deliver(bp, ENOMEM); + return; + } + cbp->bio_done = g_std_done; + g_io_request(cbp, disk->d_consumer); + G_VENTOY_DEBUG(1, "Kernel dump will go to %s.", + disk->d_consumer->provider->name); +} + +static void +g_ventoy_done(struct bio *bp) +{ + struct g_ventoy_softc *sc; + struct bio *pbp; + + pbp = bp->bio_parent; + sc = pbp->bio_to->geom->softc; + mtx_lock(&sc->sc_lock); + if (pbp->bio_error == 0) + pbp->bio_error = bp->bio_error; + pbp->bio_completed += bp->bio_completed; + pbp->bio_inbed++; + if (pbp->bio_children == pbp->bio_inbed) { + mtx_unlock(&sc->sc_lock); + g_io_deliver(pbp, pbp->bio_error); + } else + mtx_unlock(&sc->sc_lock); + g_destroy_bio(bp); +} + +static void +g_ventoy_flush(struct g_ventoy_softc *sc, struct bio *bp) +{ + struct bio_queue_head queue; + struct g_consumer *cp; + struct bio *cbp; + u_int no; + + bioq_init(&queue); + for (no = 0; no < sc->sc_ndisks; no++) { + cbp = g_clone_bio(bp); + if (cbp == NULL) { + while ((cbp = bioq_takefirst(&queue)) != NULL) + g_destroy_bio(cbp); + if (bp->bio_error == 0) + bp->bio_error = ENOMEM; + g_io_deliver(bp, bp->bio_error); + return; + } + bioq_insert_tail(&queue, cbp); + cbp->bio_done = g_ventoy_done; + cbp->bio_caller1 = sc->sc_disks[no].d_consumer; + cbp->bio_to = sc->sc_disks[no].d_consumer->provider; + } + while ((cbp = bioq_takefirst(&queue)) != NULL) { + G_VENTOY_LOGREQ(cbp, "Sending request."); + cp = cbp->bio_caller1; + cbp->bio_caller1 = NULL; + g_io_request(cbp, cp); + } +} + +static void +g_ventoy_start(struct bio *bp) +{ + struct bio_queue_head queue; + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct g_provider *pp; + off_t offset, end, length, off, len; + struct bio *cbp; + char *addr; + u_int no; + + pp = bp->bio_to; + sc = pp->geom->softc; + /* + * If sc == NULL, provider's error should be set and g_ventoy_start() + * should not be called at all. + */ + KASSERT(sc != NULL, + ("Provider's error should be set (error=%d)(device=%s).", + bp->bio_to->error, bp->bio_to->name)); + + G_VENTOY_LOGREQ(bp, "Request received."); + + switch (bp->bio_cmd) { + case BIO_READ: + case BIO_WRITE: + case BIO_DELETE: + break; + case BIO_FLUSH: + g_ventoy_flush(sc, bp); + return; + case BIO_GETATTR: + if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { + g_ventoy_kernel_dump(bp); + return; + } + /* To which provider it should be delivered? */ + /* FALLTHROUGH */ + default: + g_io_deliver(bp, EOPNOTSUPP); + return; + } + + offset = bp->bio_offset; + length = bp->bio_length; + if ((bp->bio_flags & BIO_UNMAPPED) != 0) + addr = NULL; + else + addr = bp->bio_data; + end = offset + length; + + bioq_init(&queue); + for (no = 0; no < sc->sc_ndisks; no++) { + disk = &sc->sc_disks[no]; + if (disk->d_end <= offset) + continue; + if (disk->d_start >= end) + break; + + off = offset - disk->d_start; + len = MIN(length, disk->d_end - offset); + length -= len; + offset += len; + + cbp = g_clone_bio(bp); + if (cbp == NULL) { + while ((cbp = bioq_takefirst(&queue)) != NULL) + g_destroy_bio(cbp); + if (bp->bio_error == 0) + bp->bio_error = ENOMEM; + g_io_deliver(bp, bp->bio_error); + return; + } + bioq_insert_tail(&queue, cbp); + /* + * Fill in the component buf structure. + */ + if (len == bp->bio_length) + cbp->bio_done = g_std_done; + else + cbp->bio_done = g_ventoy_done; + cbp->bio_offset = off + disk->d_map_start; + cbp->bio_length = len; + if ((bp->bio_flags & BIO_UNMAPPED) != 0) { + cbp->bio_ma_offset += (uintptr_t)addr; + cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; + cbp->bio_ma_offset %= PAGE_SIZE; + cbp->bio_ma_n = round_page(cbp->bio_ma_offset + + cbp->bio_length) / PAGE_SIZE; + } else + cbp->bio_data = addr; + addr += len; + cbp->bio_to = disk->d_consumer->provider; + cbp->bio_caller1 = disk; + + if (length == 0) + break; + } + KASSERT(length == 0, + ("Length is still greater than 0 (class=%s, name=%s).", + bp->bio_to->geom->class->name, bp->bio_to->geom->name)); + while ((cbp = bioq_takefirst(&queue)) != NULL) { + G_VENTOY_LOGREQ(cbp, "Sending request."); + disk = cbp->bio_caller1; + cbp->bio_caller1 = NULL; + g_io_request(cbp, disk->d_consumer); + } +} + +static void +g_ventoy_check_and_run(struct g_ventoy_softc *sc) +{ + struct g_ventoy_disk *disk; + struct g_provider *dp, *pp; + u_int no, sectorsize = 0; + off_t start; + + g_topology_assert(); + if (g_ventoy_nvalid(sc) != sc->sc_ndisks) + return; + + pp = g_new_providerf(sc->sc_geom, "ventoy/%s", sc->sc_name); + pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | + G_PF_ACCEPT_UNMAPPED; + start = 0; + for (no = 0; no < sc->sc_ndisks; no++) { + disk = &sc->sc_disks[no]; + dp = disk->d_consumer->provider; + disk->d_start = start; + disk->d_end = disk->d_start + (disk->d_map_end - disk->d_map_start); + if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) + disk->d_end -= dp->sectorsize; + start = disk->d_end; + if (no == 0) + sectorsize = dp->sectorsize; + else + sectorsize = lcm(sectorsize, dp->sectorsize); + + /* A provider underneath us doesn't support unmapped */ + if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { + G_VENTOY_DEBUG(1, "Cancelling unmapped " + "because of %s.", dp->name); + pp->flags &= ~G_PF_ACCEPT_UNMAPPED; + } + } + pp->sectorsize = sectorsize; + /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ + pp->mediasize = start; + pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; + pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; + sc->sc_provider = pp; + g_error_provider(pp, 0); + + G_VENTOY_DEBUG(0, "Device %s activated.", sc->sc_provider->name); +} + +static int +g_ventoy_read_metadata(struct g_consumer *cp, struct g_ventoy_metadata *md) +{ + struct g_provider *pp; + u_char *buf; + int error; + + g_topology_assert(); + + error = g_access(cp, 1, 0, 0); + if (error != 0) + return (error); + pp = cp->provider; + g_topology_unlock(); + buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, + &error); + g_topology_lock(); + g_access(cp, -1, 0, 0); + if (buf == NULL) + return (error); + + /* Decode metadata. */ + ventoy_metadata_decode(buf, md); + g_free(buf); + + return (0); +} + +/* + * Add disk to given device. + */ +static int +g_ventoy_add_disk(struct g_ventoy_softc *sc, struct g_provider *pp, u_int no) +{ + struct g_ventoy_disk *disk; + struct g_consumer *cp, *fcp; + struct g_geom *gp; + int error; + + g_topology_assert(); + /* Metadata corrupted? */ + if (no >= sc->sc_ndisks) + return (EINVAL); + + disk = &sc->sc_disks[no]; + /* Check if disk is not already attached. */ + if (disk->d_consumer != NULL) + return (EEXIST); + + gp = sc->sc_geom; + fcp = LIST_FIRST(&gp->consumer); + + cp = g_new_consumer(gp); + cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; + error = g_attach(cp, pp); + if (error != 0) { + g_destroy_consumer(cp); + return (error); + } + + if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { + error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); + if (error != 0) { + g_detach(cp); + g_destroy_consumer(cp); + return (error); + } + } + if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) { + struct g_ventoy_metadata md; + + /* Re-read metadata. */ + error = g_ventoy_read_metadata(cp, &md); + if (error != 0) + goto fail; + + if (strcmp(md.md_magic, G_VENTOY_MAGIC) != 0 || + strcmp(md.md_name, sc->sc_name) != 0 || + md.md_id != sc->sc_id) { + G_VENTOY_DEBUG(0, "Metadata on %s changed.", pp->name); + goto fail; + } + } + + cp->private = disk; + disk->d_consumer = cp; + disk->d_softc = sc; + disk->d_start = 0; /* not yet */ + disk->d_end = 0; /* not yet */ + disk->d_removed = 0; + + disk->d_map_start = g_disk_map_start; + disk->d_map_end = g_disk_map_end; + + G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); + + g_ventoy_check_and_run(sc); + + return (0); +fail: + if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) + g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); + g_detach(cp); + g_destroy_consumer(cp); + return (error); +} + +static struct g_geom * +g_ventoy_create(struct g_class *mp, const struct g_ventoy_metadata *md, + u_int type) +{ + struct g_ventoy_softc *sc; + struct g_geom *gp; + u_int no; + + G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md->md_name, + md->md_id); + + /* One disks is minimum. */ + if (md->md_all < 1) + return (NULL); + + /* Check for duplicate unit */ + LIST_FOREACH(gp, &mp->geom, geom) { + sc = gp->softc; + if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { + G_VENTOY_DEBUG(0, "Device %s already configured.", + gp->name); + return (NULL); + } + } + gp = g_new_geomf(mp, "%s", md->md_name); + sc = malloc(sizeof(*sc), M_VENTOY, M_WAITOK | M_ZERO); + gp->start = g_ventoy_start; + gp->spoiled = g_ventoy_orphan; + gp->orphan = g_ventoy_orphan; + gp->access = g_ventoy_access; + gp->dumpconf = g_ventoy_dumpconf; + + sc->sc_id = md->md_id; + sc->sc_ndisks = md->md_all; + sc->sc_disks = malloc(sizeof(struct g_ventoy_disk) * sc->sc_ndisks, + M_VENTOY, M_WAITOK | M_ZERO); + for (no = 0; no < sc->sc_ndisks; no++) + sc->sc_disks[no].d_consumer = NULL; + sc->sc_type = type; + mtx_init(&sc->sc_lock, "gventoy lock", NULL, MTX_DEF); + + gp->softc = sc; + sc->sc_geom = gp; + sc->sc_provider = NULL; + + G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); + + return (gp); +} + +static int +g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force) +{ + struct g_provider *pp; + struct g_consumer *cp, *cp1; + struct g_geom *gp; + + g_topology_assert(); + + if (sc == NULL) + return (ENXIO); + + pp = sc->sc_provider; + if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { + if (force) { + G_VENTOY_DEBUG(0, "Device %s is still open, so it " + "can't be definitely removed.", pp->name); + } else { + G_VENTOY_DEBUG(1, + "Device %s is still open (r%dw%de%d).", pp->name, + pp->acr, pp->acw, pp->ace); + return (EBUSY); + } + } + + gp = sc->sc_geom; + LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { + g_ventoy_remove_disk(cp->private); + if (cp1 == NULL) + return (0); /* Recursion happened. */ + } + if (!LIST_EMPTY(&gp->consumer)) + return (EINPROGRESS); + + gp->softc = NULL; + KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", + gp->name)); + free(sc->sc_disks, M_VENTOY); + mtx_destroy(&sc->sc_lock); + free(sc, M_VENTOY); + + G_VENTOY_DEBUG(0, "Device %s destroyed.", gp->name); + g_wither_geom(gp, ENXIO); + return (0); +} + +static int +g_ventoy_destroy_geom(struct gctl_req *req __unused, + struct g_class *mp __unused, struct g_geom *gp) +{ + struct g_ventoy_softc *sc; + + sc = gp->softc; + return (g_ventoy_destroy(sc, 0)); +} + +static bool g_vtoy_check_disk(struct g_class *mp, struct g_provider *pp) +{ + int i; + uint8_t *buf; + char uuid[64]; + const char *value; + struct g_consumer *cp; + struct g_geom *gp; + + if (g_ventoy_disk_size == 0) + { + if (resource_string_value("ventoy", 0, "disksize", &value) == 0) + { + G_DEBUG("ventoy.disksize: %s\n", value); + g_ventoy_disk_size = strtouq(value, NULL, 0); + } + + if (resource_string_value("ventoy", 0, "diskuuid", &g_ventoy_disk_uuid) == 0) + { + G_DEBUG("ventoy.diskuuid: <%s>\n", g_ventoy_disk_uuid); + } + } + + if (g_ventoy_disk_size != pp->mediasize) + { + return false; + } + + if (strncmp(pp->name, "cd", 2) == 0 || strchr(pp->name, '/')) + { + return false; + } + + /* read UUID from disk */ + gp = g_new_geomf(mp, "ventoy:taste"); + gp->start = NULL; + gp->access = g_ventoy_access; + gp->orphan = g_ventoy_orphan; + cp = g_new_consumer(gp); + g_attach(cp, pp); + + g_access(cp, 1, 0, 0); + g_topology_unlock(); + buf = g_read_data(cp, 0, pp->sectorsize, NULL); + g_topology_lock(); + g_access(cp, -1, 0, 0); + + g_detach(cp); + g_destroy_consumer(cp); + g_destroy_geom(gp); + gp = NULL; + + if (!buf) + { + return false; + } + + for (i = 0; i < 16; i++) + { + sprintf(uuid + i * 2, "%02x", buf[0x180 + i]); + } + g_free(buf); + + if (strncmp(g_ventoy_disk_uuid, uuid, 32) == 0) + { + return true; + } + + return false; +} + +static struct g_geom * +g_ventoy_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) +{ + int i; + int error; + int disknum; + char *endpos; + const char *value; + struct g_geom *gp; + struct g_ventoy_metadata md; + struct g_ventoy_softc *sc; + + if (g_ventoy_tasted) + { + return NULL; + } + + G_DEBUG("%s(%s, %s)\n", __func__, mp->name, pp->name); + g_topology_assert(); + + /* Skip providers that are already open for writing. */ + if (pp->acw > 0) + return (NULL); + + if (!g_vtoy_check_disk(mp, pp)) + { + return NULL; + } + + if (strcmp(pp->name, "ada1")) + { + return NULL; + } + + g_ventoy_tasted = true; + + G_DEBUG("######### ventoy disk <%s> #############\n", pp->name); + + resource_int_value("ventoy", 0, "segnum", &disknum); + + strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic)); + md.md_version = G_VENTOY_VERSION; + strlcpy(md.md_name, "IMAGE", sizeof(md.md_name)); + md.md_id = arc4random(); + md.md_no = 0; + md.md_all = (uint16_t)disknum; + bzero(md.md_provider, sizeof(md.md_provider)); + /* This field is not important here. */ + md.md_provsize = 0; + + gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL); + if (gp == NULL) { + G_VENTOY_DEBUG(0, "Cannot create device %s.", + md.md_name); + return (NULL); + } + sc = gp->softc; + + for (i = 0; i < disknum; i ++) + { + if (resource_string_value("ventoy", i, "seg", &value) == 0) + { + g_disk_map_start = strtouq(value, &endpos, 0); + g_disk_map_end = strtouq(endpos + 1, NULL, 0); + } + else + { + printf("Failed to parse ventoy seg %d\n", i); + continue; + } + + G_DEBUG("ventoy segment%d: %s\n", i, value); + + G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); + error = g_ventoy_add_disk(sc, pp, i); + if (error != 0) { + G_VENTOY_DEBUG(0, + "Cannot add disk %s to %s (error=%d).", pp->name, + gp->name, error); + g_ventoy_destroy(sc, 1); + return (NULL); + } + + g_disk_map_start = 0; + g_disk_map_end = 0; + } + + return (gp); +} + +static void +g_ventoy_ctl_create(struct gctl_req *req, struct g_class *mp) +{ + u_int attached, no; + struct g_ventoy_metadata md; + struct g_provider *pp; + struct g_ventoy_softc *sc; + struct g_geom *gp; + struct sbuf *sb; + const char *name; + char param[16]; + int *nargs; + + g_topology_assert(); + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs < 2) { + gctl_error(req, "Too few arguments."); + return; + } + + strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic)); + md.md_version = G_VENTOY_VERSION; + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + strlcpy(md.md_name, name, sizeof(md.md_name)); + md.md_id = arc4random(); + md.md_no = 0; + md.md_all = *nargs - 1; + bzero(md.md_provider, sizeof(md.md_provider)); + /* This field is not important here. */ + md.md_provsize = 0; + + /* Check all providers are valid */ + for (no = 1; no < *nargs; no++) { + snprintf(param, sizeof(param), "arg%u", no); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", no); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + G_VENTOY_DEBUG(1, "Disk %s is invalid.", name); + gctl_error(req, "Disk %s is invalid.", name); + return; + } + } + + gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL); + if (gp == NULL) { + gctl_error(req, "Can't configure %s.", md.md_name); + return; + } + + sc = gp->softc; + sb = sbuf_new_auto(); + sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); + for (attached = 0, no = 1; no < *nargs; no++) { + snprintf(param, sizeof(param), "arg%u", no); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument.", no); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + KASSERT(pp != NULL, ("Provider %s disappear?!", name)); + if (g_ventoy_add_disk(sc, pp, no - 1) != 0) { + G_VENTOY_DEBUG(1, "Disk %u (%s) not attached to %s.", + no, pp->name, gp->name); + sbuf_printf(sb, " %s", pp->name); + continue; + } + attached++; + } + sbuf_finish(sb); + if (md.md_all != attached) { + g_ventoy_destroy(gp->softc, 1); + gctl_error(req, "%s", sbuf_data(sb)); + } + sbuf_delete(sb); +} + +static struct g_ventoy_softc * +g_ventoy_find_device(struct g_class *mp, const char *name) +{ + struct g_ventoy_softc *sc; + struct g_geom *gp; + + LIST_FOREACH(gp, &mp->geom, geom) { + sc = gp->softc; + if (sc == NULL) + continue; + if (strcmp(sc->sc_name, name) == 0) + return (sc); + } + return (NULL); +} + +static void +g_ventoy_ctl_destroy(struct gctl_req *req, struct g_class *mp) +{ + struct g_ventoy_softc *sc; + int *force, *nargs, error; + const char *name; + char param[16]; + u_int i; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No '%s' argument.", "force"); + return; + } + + for (i = 0; i < (u_int)*nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", i); + return; + } + sc = g_ventoy_find_device(mp, name); + if (sc == NULL) { + gctl_error(req, "No such device: %s.", name); + return; + } + error = g_ventoy_destroy(sc, *force); + if (error != 0) { + gctl_error(req, "Cannot destroy device %s (error=%d).", + sc->sc_name, error); + return; + } + } +} + +static void +g_ventoy_config(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + uint32_t *version; + + return; + + g_topology_assert(); + + version = gctl_get_paraml(req, "version", sizeof(*version)); + if (version == NULL) { + gctl_error(req, "No '%s' argument.", "version"); + return; + } + if (*version != G_VENTOY_VERSION) { + gctl_error(req, "Userland and kernel parts are out of sync."); + return; + } + + if (strcmp(verb, "create") == 0) { + g_ventoy_ctl_create(req, mp); + return; + } else if (strcmp(verb, "destroy") == 0 || + strcmp(verb, "stop") == 0) { + g_ventoy_ctl_destroy(req, mp); + return; + } + gctl_error(req, "Unknown verb."); +} + +static void +g_ventoy_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, + struct g_consumer *cp, struct g_provider *pp) +{ + struct g_ventoy_softc *sc; + + g_topology_assert(); + sc = gp->softc; + if (sc == NULL) + return; + if (pp != NULL) { + /* Nothing here. */ + } else if (cp != NULL) { + struct g_ventoy_disk *disk; + + disk = cp->private; + if (disk == NULL) + return; + sbuf_printf(sb, "%s%jd\n", indent, + (intmax_t)disk->d_end); + sbuf_printf(sb, "%s%jd\n", indent, + (intmax_t)disk->d_start); + } else { + sbuf_printf(sb, "%s%u\n", indent, (u_int)sc->sc_id); + sbuf_printf(sb, "%s", indent); + switch (sc->sc_type) { + case G_VENTOY_TYPE_AUTOMATIC: + sbuf_cat(sb, "AUTOMATIC"); + break; + case G_VENTOY_TYPE_MANUAL: + sbuf_cat(sb, "MANUAL"); + break; + default: + sbuf_cat(sb, "UNKNOWN"); + break; + } + sbuf_cat(sb, "\n"); + sbuf_printf(sb, "%sTotal=%u, Online=%u\n", + indent, sc->sc_ndisks, g_ventoy_nvalid(sc)); + sbuf_printf(sb, "%s", indent); + if (sc->sc_provider != NULL && sc->sc_provider->error == 0) + sbuf_cat(sb, "UP"); + else + sbuf_cat(sb, "DOWN"); + sbuf_cat(sb, "\n"); + } +} + +DECLARE_GEOM_CLASS(g_ventoy_class, g_ventoy); diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.h b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.h new file mode 100644 index 00000000..81801bee --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/geom/ventoy/g_ventoy.h @@ -0,0 +1,123 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 longpanda + * Copyright (c) 2004-2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file is just copied from g_concat.h and replace strings + * "concat" ==> "ventoy" + * "CONCAT" ==> "VENTOY" + */ + +#ifndef _G_VENTOY_H_ +#define _G_VENTOY_H_ + +#include + +#define G_VENTOY_CLASS_NAME "VENTOY" + +#define G_VENTOY_MAGIC "GEOM::VENTOY" +/* + * Version history: + * 1 - Initial version number. + * 2 - Added 'stop' command to gconcat(8). + * 3 - Added md_provider field to metadata and '-h' option to gconcat(8). + * 4 - Added md_provsize field to metadata. + */ +#define G_VENTOY_VERSION 4 + +#ifdef _KERNEL +#define G_VENTOY_TYPE_MANUAL 0 +#define G_VENTOY_TYPE_AUTOMATIC 1 + +#define G_DEBUG(...) if (bootverbose) printf(__VA_ARGS__) +#define G_VENTOY_DEBUG(lvl, ...) if (g_ventoy_debug) printf(__VA_ARGS__) +#define G_VENTOY_LOGREQ(bp, ...) if (g_ventoy_debug) printf(__VA_ARGS__) + +struct g_ventoy_disk { + struct g_consumer *d_consumer; + struct g_ventoy_softc *d_softc; + off_t d_start; + off_t d_end; + off_t d_map_start; + off_t d_map_end; + int d_candelete; + int d_removed; +}; + +struct g_ventoy_softc { + u_int sc_type; /* provider type */ + struct g_geom *sc_geom; + struct g_provider *sc_provider; + uint32_t sc_id; /* concat unique ID */ + + struct g_ventoy_disk *sc_disks; + uint16_t sc_ndisks; + struct mtx sc_lock; +}; +#define sc_name sc_geom->name +#endif /* _KERNEL */ + +struct g_ventoy_metadata { + char md_magic[16]; /* Magic value. */ + uint32_t md_version; /* Version number. */ + char md_name[16]; /* Concat name. */ + uint32_t md_id; /* Unique ID. */ + uint16_t md_no; /* Disk number. */ + uint16_t md_all; /* Number of all disks. */ + char md_provider[16]; /* Hardcoded provider. */ + uint64_t md_provsize; /* Provider's size. */ +}; +static __inline void +ventoy_metadata_encode(const struct g_ventoy_metadata *md, u_char *data) +{ + + bcopy(md->md_magic, data, sizeof(md->md_magic)); + le32enc(data + 16, md->md_version); + bcopy(md->md_name, data + 20, sizeof(md->md_name)); + le32enc(data + 36, md->md_id); + le16enc(data + 40, md->md_no); + le16enc(data + 42, md->md_all); + bcopy(md->md_provider, data + 44, sizeof(md->md_provider)); + le64enc(data + 60, md->md_provsize); +} +static __inline void +ventoy_metadata_decode(const u_char *data, struct g_ventoy_metadata *md) +{ + + bcopy(data, md->md_magic, sizeof(md->md_magic)); + md->md_version = le32dec(data + 16); + bcopy(data + 20, md->md_name, sizeof(md->md_name)); + md->md_id = le32dec(data + 36); + md->md_no = le16dec(data + 40); + md->md_all = le16dec(data + 42); + bcopy(data + 44, md->md_provider, sizeof(md->md_provider)); + md->md_provsize = le64dec(data + 60); +} +#endif /* _G_VENTOY_H_ */ diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/modules/geom/geom_ventoy/Makefile b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/modules/geom/geom_ventoy/Makefile new file mode 100644 index 00000000..165f1bdf --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/11.x/sys/modules/geom/geom_ventoy/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/geom/ventoy + +KMOD= geom_ventoy +SRCS= g_ventoy.c + +.include diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.c b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.c new file mode 100644 index 00000000..571e3526 --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.c @@ -0,0 +1,1081 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 longpanda + * Copyright (c) 2004-2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/* + * This file is just copied from g_concat.h and replace strings + * "concat" ==> "ventoy" + * "CONCAT" ==> "VENTOY" + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE(geom_ventoy, "GEOM ventoy support"); + +static MALLOC_DEFINE(M_VENTOY, "ventoy_data", "GEOM_VENTOY Data"); + +SYSCTL_DECL(_kern_geom); +static SYSCTL_NODE(_kern_geom, OID_AUTO, ventoy, CTLFLAG_RW, 0, + "GEOM_VENTOY stuff"); +static u_int g_ventoy_debug = 0; +SYSCTL_UINT(_kern_geom_ventoy, OID_AUTO, debug, CTLFLAG_RWTUN, &g_ventoy_debug, 0, + "Debug level"); + +extern int resource_string_value(const char *name, int unit, const char *resname, const char **result); +extern int resource_int_value(const char *name, int unit, const char *resname, int *result); + +static int g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force); +static int g_ventoy_destroy_geom(struct gctl_req *req, struct g_class *mp, + struct g_geom *gp); + +static g_taste_t g_ventoy_taste; +static g_ctl_req_t g_ventoy_config; +static g_dumpconf_t g_ventoy_dumpconf; + +static const char *g_ventoy_disk_uuid = NULL; +static bool g_ventoy_tasted = false; +static off_t g_ventoy_disk_size = 0; +static off_t g_disk_map_start = 0; +static off_t g_disk_map_end = 0; + +struct g_class g_ventoy_class = { + .name = G_VENTOY_CLASS_NAME, + .version = G_VERSION, + .ctlreq = g_ventoy_config, + .taste = g_ventoy_taste, + .destroy_geom = g_ventoy_destroy_geom +}; + + +/* + * Greatest Common Divisor. + */ +static u_int +gcd(u_int a, u_int b) +{ + u_int c; + + while (b != 0) { + c = a; + a = b; + b = (c % b); + } + return (a); +} + +/* + * Least Common Multiple. + */ +static u_int +lcm(u_int a, u_int b) +{ + + return ((a * b) / gcd(a, b)); +} + +/* + * Return the number of valid disks. + */ +static u_int +g_ventoy_nvalid(struct g_ventoy_softc *sc) +{ + u_int i, no; + + no = 0; + for (i = 0; i < sc->sc_ndisks; i++) { + if (sc->sc_disks[i].d_consumer != NULL) + no++; + } + + return (no); +} + +static void +g_ventoy_remove_disk(struct g_ventoy_disk *disk) +{ + struct g_consumer *cp; + struct g_ventoy_softc *sc; + + g_topology_assert(); + KASSERT(disk->d_consumer != NULL, ("Non-valid disk in %s.", __func__)); + sc = disk->d_softc; + cp = disk->d_consumer; + + if (!disk->d_removed) { + G_VENTOY_DEBUG(0, "Disk %s removed from %s.", + cp->provider->name, sc->sc_name); + disk->d_removed = 1; + } + + if (sc->sc_provider != NULL) { + G_VENTOY_DEBUG(0, "Device %s deactivated.", + sc->sc_provider->name); + g_wither_provider(sc->sc_provider, ENXIO); + sc->sc_provider = NULL; + } + + if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0) + return; + disk->d_consumer = NULL; + g_detach(cp); + g_destroy_consumer(cp); + /* If there are no valid disks anymore, remove device. */ + if (LIST_EMPTY(&sc->sc_geom->consumer)) + g_ventoy_destroy(sc, 1); +} + +static void +g_ventoy_orphan(struct g_consumer *cp) +{ + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct g_geom *gp; + + g_topology_assert(); + gp = cp->geom; + sc = gp->softc; + if (sc == NULL) + return; + + disk = cp->private; + if (disk == NULL) /* Possible? */ + return; + g_ventoy_remove_disk(disk); +} + +static int +g_ventoy_access(struct g_provider *pp, int dr, int dw, int de) +{ + struct g_consumer *cp1, *cp2, *tmp; + struct g_ventoy_disk *disk; + struct g_geom *gp; + int error; + + if (dw > 0) /* readonly */ + return (EPERM); + + g_topology_assert(); + gp = pp->geom; + + /* On first open, grab an extra "exclusive" bit */ + if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) + de++; + /* ... and let go of it on last close */ + if ((pp->acr + dr) == 0 && (pp->acw + dw) == 0 && (pp->ace + de) == 0) + de--; + + LIST_FOREACH_SAFE(cp1, &gp->consumer, consumer, tmp) { + error = g_access(cp1, dr, dw, de); + if (error != 0) + goto fail; + disk = cp1->private; + if (cp1->acr == 0 && cp1->acw == 0 && cp1->ace == 0 && + disk->d_removed) { + g_ventoy_remove_disk(disk); /* May destroy geom. */ + } + } + return (0); + +fail: + LIST_FOREACH(cp2, &gp->consumer, consumer) { + if (cp1 == cp2) + break; + g_access(cp2, -dr, -dw, -de); + } + return (error); +} + +static void +g_ventoy_kernel_dump(struct bio *bp) +{ + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct bio *cbp; + struct g_kerneldump *gkd; + u_int i; + + sc = bp->bio_to->geom->softc; + gkd = (struct g_kerneldump *)bp->bio_data; + for (i = 0; i < sc->sc_ndisks; i++) { + if (sc->sc_disks[i].d_start <= gkd->offset && + sc->sc_disks[i].d_end > gkd->offset) + break; + } + if (i == sc->sc_ndisks) + g_io_deliver(bp, EOPNOTSUPP); + disk = &sc->sc_disks[i]; + gkd->offset -= disk->d_start; + if (gkd->length > disk->d_end - disk->d_start - gkd->offset) + gkd->length = disk->d_end - disk->d_start - gkd->offset; + cbp = g_clone_bio(bp); + if (cbp == NULL) { + g_io_deliver(bp, ENOMEM); + return; + } + cbp->bio_done = g_std_done; + g_io_request(cbp, disk->d_consumer); + G_VENTOY_DEBUG(1, "Kernel dump will go to %s.", + disk->d_consumer->provider->name); +} + +static void +g_ventoy_done(struct bio *bp) +{ + struct g_ventoy_softc *sc; + struct bio *pbp; + + pbp = bp->bio_parent; + sc = pbp->bio_to->geom->softc; + mtx_lock(&sc->sc_lock); + if (pbp->bio_error == 0) + pbp->bio_error = bp->bio_error; + pbp->bio_completed += bp->bio_completed; + pbp->bio_inbed++; + if (pbp->bio_children == pbp->bio_inbed) { + mtx_unlock(&sc->sc_lock); + g_io_deliver(pbp, pbp->bio_error); + } else + mtx_unlock(&sc->sc_lock); + g_destroy_bio(bp); +} + +static void +g_ventoy_flush(struct g_ventoy_softc *sc, struct bio *bp) +{ + struct bio_queue_head queue; + struct g_consumer *cp; + struct bio *cbp; + u_int no; + + bioq_init(&queue); + for (no = 0; no < sc->sc_ndisks; no++) { + cbp = g_clone_bio(bp); + if (cbp == NULL) { + while ((cbp = bioq_takefirst(&queue)) != NULL) + g_destroy_bio(cbp); + if (bp->bio_error == 0) + bp->bio_error = ENOMEM; + g_io_deliver(bp, bp->bio_error); + return; + } + bioq_insert_tail(&queue, cbp); + cbp->bio_done = g_ventoy_done; + cbp->bio_caller1 = sc->sc_disks[no].d_consumer; + cbp->bio_to = sc->sc_disks[no].d_consumer->provider; + } + while ((cbp = bioq_takefirst(&queue)) != NULL) { + G_VENTOY_LOGREQ(cbp, "Sending request."); + cp = cbp->bio_caller1; + cbp->bio_caller1 = NULL; + g_io_request(cbp, cp); + } +} + +static void +g_ventoy_start(struct bio *bp) +{ + struct bio_queue_head queue; + struct g_ventoy_softc *sc; + struct g_ventoy_disk *disk; + struct g_provider *pp; + off_t offset, end, length, off, len; + struct bio *cbp; + char *addr; + u_int no; + + pp = bp->bio_to; + sc = pp->geom->softc; + /* + * If sc == NULL, provider's error should be set and g_ventoy_start() + * should not be called at all. + */ + KASSERT(sc != NULL, + ("Provider's error should be set (error=%d)(device=%s).", + bp->bio_to->error, bp->bio_to->name)); + + G_VENTOY_LOGREQ(bp, "Request received."); + + switch (bp->bio_cmd) { + case BIO_READ: + case BIO_WRITE: + case BIO_DELETE: + break; + case BIO_FLUSH: + g_ventoy_flush(sc, bp); + return; + case BIO_GETATTR: + if (strcmp("GEOM::kerneldump", bp->bio_attribute) == 0) { + g_ventoy_kernel_dump(bp); + return; + } + /* To which provider it should be delivered? */ + /* FALLTHROUGH */ + default: + g_io_deliver(bp, EOPNOTSUPP); + return; + } + + offset = bp->bio_offset; + length = bp->bio_length; + if ((bp->bio_flags & BIO_UNMAPPED) != 0) + addr = NULL; + else + addr = bp->bio_data; + end = offset + length; + + bioq_init(&queue); + for (no = 0; no < sc->sc_ndisks; no++) { + disk = &sc->sc_disks[no]; + if (disk->d_end <= offset) + continue; + if (disk->d_start >= end) + break; + + off = offset - disk->d_start; + len = MIN(length, disk->d_end - offset); + length -= len; + offset += len; + + cbp = g_clone_bio(bp); + if (cbp == NULL) { + while ((cbp = bioq_takefirst(&queue)) != NULL) + g_destroy_bio(cbp); + if (bp->bio_error == 0) + bp->bio_error = ENOMEM; + g_io_deliver(bp, bp->bio_error); + return; + } + bioq_insert_tail(&queue, cbp); + /* + * Fill in the component buf structure. + */ + if (len == bp->bio_length) + cbp->bio_done = g_std_done; + else + cbp->bio_done = g_ventoy_done; + cbp->bio_offset = off + disk->d_map_start; + cbp->bio_length = len; + if ((bp->bio_flags & BIO_UNMAPPED) != 0) { + cbp->bio_ma_offset += (uintptr_t)addr; + cbp->bio_ma += cbp->bio_ma_offset / PAGE_SIZE; + cbp->bio_ma_offset %= PAGE_SIZE; + cbp->bio_ma_n = round_page(cbp->bio_ma_offset + + cbp->bio_length) / PAGE_SIZE; + } else + cbp->bio_data = addr; + addr += len; + cbp->bio_to = disk->d_consumer->provider; + cbp->bio_caller1 = disk; + + if (length == 0) + break; + } + KASSERT(length == 0, + ("Length is still greater than 0 (class=%s, name=%s).", + bp->bio_to->geom->class->name, bp->bio_to->geom->name)); + while ((cbp = bioq_takefirst(&queue)) != NULL) { + G_VENTOY_LOGREQ(cbp, "Sending request."); + disk = cbp->bio_caller1; + cbp->bio_caller1 = NULL; + g_io_request(cbp, disk->d_consumer); + } +} + +static void +g_ventoy_check_and_run(struct g_ventoy_softc *sc) +{ + struct g_ventoy_disk *disk; + struct g_provider *dp, *pp; + u_int no, sectorsize = 0; + off_t start; + + g_topology_assert(); + if (g_ventoy_nvalid(sc) != sc->sc_ndisks) + return; + + pp = g_new_providerf(sc->sc_geom, "ventoy/%s", sc->sc_name); + pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | + G_PF_ACCEPT_UNMAPPED; + start = 0; + for (no = 0; no < sc->sc_ndisks; no++) { + disk = &sc->sc_disks[no]; + dp = disk->d_consumer->provider; + disk->d_start = start; + disk->d_end = disk->d_start + (disk->d_map_end - disk->d_map_start); + if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) + disk->d_end -= dp->sectorsize; + start = disk->d_end; + if (no == 0) + sectorsize = dp->sectorsize; + else + sectorsize = lcm(sectorsize, dp->sectorsize); + + /* A provider underneath us doesn't support unmapped */ + if ((dp->flags & G_PF_ACCEPT_UNMAPPED) == 0) { + G_VENTOY_DEBUG(1, "Cancelling unmapped " + "because of %s.", dp->name); + pp->flags &= ~G_PF_ACCEPT_UNMAPPED; + } + } + pp->sectorsize = sectorsize; + /* We have sc->sc_disks[sc->sc_ndisks - 1].d_end in 'start'. */ + pp->mediasize = start; + pp->stripesize = sc->sc_disks[0].d_consumer->provider->stripesize; + pp->stripeoffset = sc->sc_disks[0].d_consumer->provider->stripeoffset; + sc->sc_provider = pp; + g_error_provider(pp, 0); + + G_VENTOY_DEBUG(0, "Device %s activated.", sc->sc_provider->name); +} + +static int +g_ventoy_read_metadata(struct g_consumer *cp, struct g_ventoy_metadata *md) +{ + struct g_provider *pp; + u_char *buf; + int error; + + g_topology_assert(); + + error = g_access(cp, 1, 0, 0); + if (error != 0) + return (error); + pp = cp->provider; + g_topology_unlock(); + buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, + &error); + g_topology_lock(); + g_access(cp, -1, 0, 0); + if (buf == NULL) + return (error); + + /* Decode metadata. */ + ventoy_metadata_decode(buf, md); + g_free(buf); + + return (0); +} + +/* + * Add disk to given device. + */ +static int +g_ventoy_add_disk(struct g_ventoy_softc *sc, struct g_provider *pp, u_int no) +{ + struct g_ventoy_disk *disk; + struct g_consumer *cp, *fcp; + struct g_geom *gp; + int error; + + g_topology_assert(); + /* Metadata corrupted? */ + if (no >= sc->sc_ndisks) + return (EINVAL); + + disk = &sc->sc_disks[no]; + /* Check if disk is not already attached. */ + if (disk->d_consumer != NULL) + return (EEXIST); + + gp = sc->sc_geom; + fcp = LIST_FIRST(&gp->consumer); + + cp = g_new_consumer(gp); + cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE; + error = g_attach(cp, pp); + if (error != 0) { + g_destroy_consumer(cp); + return (error); + } + + if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) { + error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); + if (error != 0) { + g_detach(cp); + g_destroy_consumer(cp); + return (error); + } + } + if (sc->sc_type == G_VENTOY_TYPE_AUTOMATIC) { + struct g_ventoy_metadata md; + + /* Re-read metadata. */ + error = g_ventoy_read_metadata(cp, &md); + if (error != 0) + goto fail; + + if (strcmp(md.md_magic, G_VENTOY_MAGIC) != 0 || + strcmp(md.md_name, sc->sc_name) != 0 || + md.md_id != sc->sc_id) { + G_VENTOY_DEBUG(0, "Metadata on %s changed.", pp->name); + goto fail; + } + } + + cp->private = disk; + disk->d_consumer = cp; + disk->d_softc = sc; + disk->d_start = 0; /* not yet */ + disk->d_end = 0; /* not yet */ + disk->d_removed = 0; + + disk->d_map_start = g_disk_map_start; + disk->d_map_end = g_disk_map_end; + + G_VENTOY_DEBUG(0, "Disk %s attached to %s.", pp->name, sc->sc_name); + + g_ventoy_check_and_run(sc); + + return (0); +fail: + if (fcp != NULL && (fcp->acr > 0 || fcp->acw > 0 || fcp->ace > 0)) + g_access(cp, -fcp->acr, -fcp->acw, -fcp->ace); + g_detach(cp); + g_destroy_consumer(cp); + return (error); +} + +static struct g_geom * +g_ventoy_create(struct g_class *mp, const struct g_ventoy_metadata *md, + u_int type) +{ + struct g_ventoy_softc *sc; + struct g_geom *gp; + u_int no; + + G_VENTOY_DEBUG(1, "Creating device %s (id=%u).", md->md_name, + md->md_id); + + /* One disks is minimum. */ + if (md->md_all < 1) + return (NULL); + + /* Check for duplicate unit */ + LIST_FOREACH(gp, &mp->geom, geom) { + sc = gp->softc; + if (sc != NULL && strcmp(sc->sc_name, md->md_name) == 0) { + G_VENTOY_DEBUG(0, "Device %s already configured.", + gp->name); + return (NULL); + } + } + gp = g_new_geomf(mp, "%s", md->md_name); + sc = malloc(sizeof(*sc), M_VENTOY, M_WAITOK | M_ZERO); + gp->start = g_ventoy_start; + gp->spoiled = g_ventoy_orphan; + gp->orphan = g_ventoy_orphan; + gp->access = g_ventoy_access; + gp->dumpconf = g_ventoy_dumpconf; + + sc->sc_id = md->md_id; + sc->sc_ndisks = md->md_all; + sc->sc_disks = malloc(sizeof(struct g_ventoy_disk) * sc->sc_ndisks, + M_VENTOY, M_WAITOK | M_ZERO); + for (no = 0; no < sc->sc_ndisks; no++) + sc->sc_disks[no].d_consumer = NULL; + sc->sc_type = type; + mtx_init(&sc->sc_lock, "gventoy lock", NULL, MTX_DEF); + + gp->softc = sc; + sc->sc_geom = gp; + sc->sc_provider = NULL; + + G_VENTOY_DEBUG(0, "Device %s created (id=%u).", sc->sc_name, sc->sc_id); + + return (gp); +} + +static int +g_ventoy_destroy(struct g_ventoy_softc *sc, boolean_t force) +{ + struct g_provider *pp; + struct g_consumer *cp, *cp1; + struct g_geom *gp; + + g_topology_assert(); + + if (sc == NULL) + return (ENXIO); + + pp = sc->sc_provider; + if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { + if (force) { + G_VENTOY_DEBUG(0, "Device %s is still open, so it " + "can't be definitely removed.", pp->name); + } else { + G_VENTOY_DEBUG(1, + "Device %s is still open (r%dw%de%d).", pp->name, + pp->acr, pp->acw, pp->ace); + return (EBUSY); + } + } + + gp = sc->sc_geom; + LIST_FOREACH_SAFE(cp, &gp->consumer, consumer, cp1) { + g_ventoy_remove_disk(cp->private); + if (cp1 == NULL) + return (0); /* Recursion happened. */ + } + if (!LIST_EMPTY(&gp->consumer)) + return (EINPROGRESS); + + gp->softc = NULL; + KASSERT(sc->sc_provider == NULL, ("Provider still exists? (device=%s)", + gp->name)); + free(sc->sc_disks, M_VENTOY); + mtx_destroy(&sc->sc_lock); + free(sc, M_VENTOY); + + G_VENTOY_DEBUG(0, "Device %s destroyed.", gp->name); + g_wither_geom(gp, ENXIO); + return (0); +} + +static int +g_ventoy_destroy_geom(struct gctl_req *req __unused, + struct g_class *mp __unused, struct g_geom *gp) +{ + struct g_ventoy_softc *sc; + + sc = gp->softc; + return (g_ventoy_destroy(sc, 0)); +} + +static bool g_vtoy_check_disk(struct g_class *mp, struct g_provider *pp) +{ + int i; + uint8_t *buf; + char uuid[64]; + const char *value; + struct g_consumer *cp; + struct g_geom *gp; + + if (g_ventoy_disk_size == 0) + { + if (resource_string_value("ventoy", 0, "disksize", &value) == 0) + { + G_DEBUG("ventoy.disksize: %s\n", value); + g_ventoy_disk_size = strtouq(value, NULL, 0); + } + + if (resource_string_value("ventoy", 0, "diskuuid", &g_ventoy_disk_uuid) == 0) + { + G_DEBUG("ventoy.diskuuid: <%s>\n", g_ventoy_disk_uuid); + } + } + + if (g_ventoy_disk_size != pp->mediasize) + { + return false; + } + + if (strncmp(pp->name, "cd", 2) == 0 || strchr(pp->name, '/')) + { + return false; + } + + /* read UUID from disk */ + gp = g_new_geomf(mp, "ventoy:taste"); + gp->start = NULL; + gp->access = g_ventoy_access; + gp->orphan = g_ventoy_orphan; + cp = g_new_consumer(gp); + g_attach(cp, pp); + + g_access(cp, 1, 0, 0); + g_topology_unlock(); + buf = g_read_data(cp, 0, pp->sectorsize, NULL); + g_topology_lock(); + g_access(cp, -1, 0, 0); + + g_detach(cp); + g_destroy_consumer(cp); + g_destroy_geom(gp); + gp = NULL; + + if (!buf) + { + return false; + } + + for (i = 0; i < 16; i++) + { + sprintf(uuid + i * 2, "%02x", buf[0x180 + i]); + } + g_free(buf); + + if (strncmp(g_ventoy_disk_uuid, uuid, 32) == 0) + { + return true; + } + + return false; +} + +static struct g_geom * +g_ventoy_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) +{ + int i; + int error; + int disknum; + char *endpos; + const char *value; + struct g_geom *gp; + struct g_ventoy_metadata md; + struct g_ventoy_softc *sc; + + if (g_ventoy_tasted) + { + return NULL; + } + + G_DEBUG("%s(%s, %s)\n", __func__, mp->name, pp->name); + g_topology_assert(); + + /* Skip providers that are already open for writing. */ + if (pp->acw > 0) + return (NULL); + + if (!g_vtoy_check_disk(mp, pp)) + { + return NULL; + } + + if (strcmp(pp->name, "ada1")) + { + return NULL; + } + + g_ventoy_tasted = true; + + G_DEBUG("######### ventoy disk <%s> #############\n", pp->name); + + resource_int_value("ventoy", 0, "segnum", &disknum); + + strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic)); + md.md_version = G_VENTOY_VERSION; + strlcpy(md.md_name, "IMAGE", sizeof(md.md_name)); + md.md_id = arc4random(); + md.md_no = 0; + md.md_all = (uint16_t)disknum; + bzero(md.md_provider, sizeof(md.md_provider)); + /* This field is not important here. */ + md.md_provsize = 0; + + gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL); + if (gp == NULL) { + G_VENTOY_DEBUG(0, "Cannot create device %s.", + md.md_name); + return (NULL); + } + sc = gp->softc; + + for (i = 0; i < disknum; i ++) + { + if (resource_string_value("ventoy", i, "seg", &value) == 0) + { + g_disk_map_start = strtouq(value, &endpos, 0); + g_disk_map_end = strtouq(endpos + 1, NULL, 0); + } + else + { + printf("Failed to parse ventoy seg %d\n", i); + continue; + } + + G_DEBUG("ventoy segment%d: %s\n", i, value); + + G_VENTOY_DEBUG(1, "Adding disk %s to %s.", pp->name, gp->name); + error = g_ventoy_add_disk(sc, pp, i); + if (error != 0) { + G_VENTOY_DEBUG(0, + "Cannot add disk %s to %s (error=%d).", pp->name, + gp->name, error); + g_ventoy_destroy(sc, 1); + return (NULL); + } + + g_disk_map_start = 0; + g_disk_map_end = 0; + } + + return (gp); +} + +static void +g_ventoy_ctl_create(struct gctl_req *req, struct g_class *mp) +{ + u_int attached, no; + struct g_ventoy_metadata md; + struct g_provider *pp; + struct g_ventoy_softc *sc; + struct g_geom *gp; + struct sbuf *sb; + const char *name; + char param[16]; + int *nargs; + + g_topology_assert(); + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs < 2) { + gctl_error(req, "Too few arguments."); + return; + } + + strlcpy(md.md_magic, G_VENTOY_MAGIC, sizeof(md.md_magic)); + md.md_version = G_VENTOY_VERSION; + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + strlcpy(md.md_name, name, sizeof(md.md_name)); + md.md_id = arc4random(); + md.md_no = 0; + md.md_all = *nargs - 1; + bzero(md.md_provider, sizeof(md.md_provider)); + /* This field is not important here. */ + md.md_provsize = 0; + + /* Check all providers are valid */ + for (no = 1; no < *nargs; no++) { + snprintf(param, sizeof(param), "arg%u", no); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", no); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + G_VENTOY_DEBUG(1, "Disk %s is invalid.", name); + gctl_error(req, "Disk %s is invalid.", name); + return; + } + } + + gp = g_ventoy_create(mp, &md, G_VENTOY_TYPE_MANUAL); + if (gp == NULL) { + gctl_error(req, "Can't configure %s.", md.md_name); + return; + } + + sc = gp->softc; + sb = sbuf_new_auto(); + sbuf_printf(sb, "Can't attach disk(s) to %s:", gp->name); + for (attached = 0, no = 1; no < *nargs; no++) { + snprintf(param, sizeof(param), "arg%u", no); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument.", no); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + KASSERT(pp != NULL, ("Provider %s disappear?!", name)); + if (g_ventoy_add_disk(sc, pp, no - 1) != 0) { + G_VENTOY_DEBUG(1, "Disk %u (%s) not attached to %s.", + no, pp->name, gp->name); + sbuf_printf(sb, " %s", pp->name); + continue; + } + attached++; + } + sbuf_finish(sb); + if (md.md_all != attached) { + g_ventoy_destroy(gp->softc, 1); + gctl_error(req, "%s", sbuf_data(sb)); + } + sbuf_delete(sb); +} + +static struct g_ventoy_softc * +g_ventoy_find_device(struct g_class *mp, const char *name) +{ + struct g_ventoy_softc *sc; + struct g_geom *gp; + + LIST_FOREACH(gp, &mp->geom, geom) { + sc = gp->softc; + if (sc == NULL) + continue; + if (strcmp(sc->sc_name, name) == 0) + return (sc); + } + return (NULL); +} + +static void +g_ventoy_ctl_destroy(struct gctl_req *req, struct g_class *mp) +{ + struct g_ventoy_softc *sc; + int *force, *nargs, error; + const char *name; + char param[16]; + u_int i; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No '%s' argument.", "force"); + return; + } + + for (i = 0; i < (u_int)*nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", i); + return; + } + sc = g_ventoy_find_device(mp, name); + if (sc == NULL) { + gctl_error(req, "No such device: %s.", name); + return; + } + error = g_ventoy_destroy(sc, *force); + if (error != 0) { + gctl_error(req, "Cannot destroy device %s (error=%d).", + sc->sc_name, error); + return; + } + } +} + +static void +g_ventoy_config(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + uint32_t *version; + + return; + + g_topology_assert(); + + version = gctl_get_paraml(req, "version", sizeof(*version)); + if (version == NULL) { + gctl_error(req, "No '%s' argument.", "version"); + return; + } + if (*version != G_VENTOY_VERSION) { + gctl_error(req, "Userland and kernel parts are out of sync."); + return; + } + + if (strcmp(verb, "create") == 0) { + g_ventoy_ctl_create(req, mp); + return; + } else if (strcmp(verb, "destroy") == 0 || + strcmp(verb, "stop") == 0) { + g_ventoy_ctl_destroy(req, mp); + return; + } + gctl_error(req, "Unknown verb."); +} + +static void +g_ventoy_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, + struct g_consumer *cp, struct g_provider *pp) +{ + struct g_ventoy_softc *sc; + + g_topology_assert(); + sc = gp->softc; + if (sc == NULL) + return; + if (pp != NULL) { + /* Nothing here. */ + } else if (cp != NULL) { + struct g_ventoy_disk *disk; + + disk = cp->private; + if (disk == NULL) + return; + sbuf_printf(sb, "%s%jd\n", indent, + (intmax_t)disk->d_end); + sbuf_printf(sb, "%s%jd\n", indent, + (intmax_t)disk->d_start); + } else { + sbuf_printf(sb, "%s%u\n", indent, (u_int)sc->sc_id); + sbuf_printf(sb, "%s", indent); + switch (sc->sc_type) { + case G_VENTOY_TYPE_AUTOMATIC: + sbuf_cat(sb, "AUTOMATIC"); + break; + case G_VENTOY_TYPE_MANUAL: + sbuf_cat(sb, "MANUAL"); + break; + default: + sbuf_cat(sb, "UNKNOWN"); + break; + } + sbuf_cat(sb, "\n"); + sbuf_printf(sb, "%sTotal=%u, Online=%u\n", + indent, sc->sc_ndisks, g_ventoy_nvalid(sc)); + sbuf_printf(sb, "%s", indent); + if (sc->sc_provider != NULL && sc->sc_provider->error == 0) + sbuf_cat(sb, "UP"); + else + sbuf_cat(sb, "DOWN"); + sbuf_cat(sb, "\n"); + } +} + +DECLARE_GEOM_CLASS(g_ventoy_class, g_ventoy); +//MODULE_VERSION(geom_ventoy, 0); diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.h b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.h new file mode 100644 index 00000000..81801bee --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/geom/ventoy/g_ventoy.h @@ -0,0 +1,123 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 longpanda + * Copyright (c) 2004-2005 Pawel Jakub Dawidek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file is just copied from g_concat.h and replace strings + * "concat" ==> "ventoy" + * "CONCAT" ==> "VENTOY" + */ + +#ifndef _G_VENTOY_H_ +#define _G_VENTOY_H_ + +#include + +#define G_VENTOY_CLASS_NAME "VENTOY" + +#define G_VENTOY_MAGIC "GEOM::VENTOY" +/* + * Version history: + * 1 - Initial version number. + * 2 - Added 'stop' command to gconcat(8). + * 3 - Added md_provider field to metadata and '-h' option to gconcat(8). + * 4 - Added md_provsize field to metadata. + */ +#define G_VENTOY_VERSION 4 + +#ifdef _KERNEL +#define G_VENTOY_TYPE_MANUAL 0 +#define G_VENTOY_TYPE_AUTOMATIC 1 + +#define G_DEBUG(...) if (bootverbose) printf(__VA_ARGS__) +#define G_VENTOY_DEBUG(lvl, ...) if (g_ventoy_debug) printf(__VA_ARGS__) +#define G_VENTOY_LOGREQ(bp, ...) if (g_ventoy_debug) printf(__VA_ARGS__) + +struct g_ventoy_disk { + struct g_consumer *d_consumer; + struct g_ventoy_softc *d_softc; + off_t d_start; + off_t d_end; + off_t d_map_start; + off_t d_map_end; + int d_candelete; + int d_removed; +}; + +struct g_ventoy_softc { + u_int sc_type; /* provider type */ + struct g_geom *sc_geom; + struct g_provider *sc_provider; + uint32_t sc_id; /* concat unique ID */ + + struct g_ventoy_disk *sc_disks; + uint16_t sc_ndisks; + struct mtx sc_lock; +}; +#define sc_name sc_geom->name +#endif /* _KERNEL */ + +struct g_ventoy_metadata { + char md_magic[16]; /* Magic value. */ + uint32_t md_version; /* Version number. */ + char md_name[16]; /* Concat name. */ + uint32_t md_id; /* Unique ID. */ + uint16_t md_no; /* Disk number. */ + uint16_t md_all; /* Number of all disks. */ + char md_provider[16]; /* Hardcoded provider. */ + uint64_t md_provsize; /* Provider's size. */ +}; +static __inline void +ventoy_metadata_encode(const struct g_ventoy_metadata *md, u_char *data) +{ + + bcopy(md->md_magic, data, sizeof(md->md_magic)); + le32enc(data + 16, md->md_version); + bcopy(md->md_name, data + 20, sizeof(md->md_name)); + le32enc(data + 36, md->md_id); + le16enc(data + 40, md->md_no); + le16enc(data + 42, md->md_all); + bcopy(md->md_provider, data + 44, sizeof(md->md_provider)); + le64enc(data + 60, md->md_provsize); +} +static __inline void +ventoy_metadata_decode(const u_char *data, struct g_ventoy_metadata *md) +{ + + bcopy(data, md->md_magic, sizeof(md->md_magic)); + md->md_version = le32dec(data + 16); + bcopy(data + 20, md->md_name, sizeof(md->md_name)); + md->md_id = le32dec(data + 36); + md->md_no = le16dec(data + 40); + md->md_all = le16dec(data + 42); + bcopy(data + 44, md->md_provider, sizeof(md->md_provider)); + md->md_provsize = le64dec(data + 60); +} +#endif /* _G_VENTOY_H_ */ diff --git a/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/modules/geom/geom_ventoy/Makefile b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/modules/geom/geom_ventoy/Makefile new file mode 100644 index 00000000..165f1bdf --- /dev/null +++ b/Unix/ventoy_unix_src/FreeBSD/geom_ventoy_src/12.x/sys/modules/geom/geom_ventoy/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/geom/ventoy + +KMOD= geom_ventoy +SRCS= g_ventoy.c + +.include diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c index 2820cb0f..557bdfc1 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c @@ -167,8 +167,7 @@ static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes, MBR_HEAD *pMBR, UIN PartStartSector = MBR.PartTbl[0].StartSectorId + MBR.PartTbl[0].SectorCount; PartSectorCount = VENTOY_EFI_PART_SIZE / 512; - if (MBR.PartTbl[1].FsFlag != 0xEF || - MBR.PartTbl[1].StartSectorId != PartStartSector || + if (MBR.PartTbl[1].StartSectorId != PartStartSector || MBR.PartTbl[1].SectorCount != PartSectorCount) { Log("Part2 not match [0x%x 0x%x] [%u %u] [%u %u]",