From 4fda20d8442d5e947a349673d68ec387abc43ad4 Mon Sep 17 00:00:00 2001 From: longpanda Date: Tue, 7 Jul 2020 23:37:00 +0800 Subject: [PATCH] update --- GRUB2/MOD_SRC/grub-2.04/install.sh | 2 +- INSTALL/Ventoy2Disk.exe | Bin 318976 -> 318976 bytes INSTALL/all_in_one.sh | 2 + INSTALL/grub/grub.cfg | 41 +- INSTALL/tool/VentoyWorker.sh | 30 +- INSTALL/tool/ventoy_lib.sh | 12 +- INSTALL/tool/vtoygpt_32 | Bin 0 -> 22144 bytes INSTALL/tool/vtoygpt_64 | Bin 0 -> 19304 bytes Ventoy2Disk/Ventoy2Disk/PartDialog.c | Bin 10260 -> 10974 bytes Ventoy2Disk/Ventoy2Disk/PhyDrive.c | 3894 ++++++++++++------------ Ventoy2Disk/Ventoy2Disk/Utility.c | 1608 +++++----- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc | Bin 11510 -> 11464 bytes Ventoy2Disk/Ventoy2Disk/WinDialog.c | Bin 57956 -> 57986 bytes vtoygpt/build.sh | 19 + vtoygpt/crc32.c | 315 ++ vtoygpt/vtoygpt.c | 320 ++ 16 files changed, 3474 insertions(+), 2769 deletions(-) create mode 100644 INSTALL/tool/vtoygpt_32 create mode 100644 INSTALL/tool/vtoygpt_64 create mode 100644 vtoygpt/build.sh create mode 100644 vtoygpt/crc32.c create mode 100644 vtoygpt/vtoygpt.c diff --git a/GRUB2/MOD_SRC/grub-2.04/install.sh b/GRUB2/MOD_SRC/grub-2.04/install.sh index 8d9ba4cb..19cffe56 100644 --- a/GRUB2/MOD_SRC/grub-2.04/install.sh +++ b/GRUB2/MOD_SRC/grub-2.04/install.sh @@ -12,7 +12,7 @@ make install PATH=$PATH:$VT_DIR/GRUB2/INSTALL/bin/:$VT_DIR/GRUB2/INSTALL/sbin/ net_modules_legacy="net tftp http" -all_modules_legacy="date drivemap blocklist ntldr search at_keyboard usb_keyboard gcry_md5 hashsum gzio xzio lzopio lspci pci ext2 xfs ventoy chain read halt iso9660 linux16 test true sleep reboot echo videotest videoinfo videotest_checksum video_colors video_cirrus video_bochs vga vbe video_fb font video gettext extcmd terminal linux minicmd help configfile tr trig boot biosdisk disk ls tar squash4 password_pbkdf2 all_video png jpeg part_gpt part_msdos fat exfat ntfs loopback gzio normal udf gfxmenu gfxterm gfxterm_background gfxterm_menu" +all_modules_legacy="date drivemap blocklist vga_text ntldr search at_keyboard usb_keyboard gcry_md5 hashsum gzio xzio lzopio lspci pci ext2 xfs ventoy chain read halt iso9660 linux16 test true sleep reboot echo videotest videoinfo videotest_checksum video_colors video_cirrus video_bochs vga vbe video_fb font video gettext extcmd terminal linux minicmd help configfile tr trig boot biosdisk disk ls tar squash4 password_pbkdf2 all_video png jpeg part_gpt part_msdos fat exfat ntfs loopback gzio normal udf gfxmenu gfxterm gfxterm_background gfxterm_menu" net_modules_uefi="efinet net tftp http" all_modules_uefi="blocklist ventoy test search at_keyboard usb_keyboard gcry_md5 hashsum gzio xzio lzopio ext2 xfs read halt sleep serial terminfo png password_pbkdf2 gcry_sha512 pbkdf2 part_gpt part_msdos ls tar squash4 loopback part_apple minicmd diskfilter linux relocator jpeg iso9660 udf hfsplus halt acpi mmap gfxmenu video_colors trig bitmap_scale gfxterm bitmap font fat exfat ntfs fshelp efifwsetup reboot echo configfile normal terminal gettext chain priority_queue bufio datetime cat extcmd crypto gzio boot all_video efi_gop efi_uga video_bochs video_cirrus video video_fb gfxterm_background gfxterm_menu" diff --git a/INSTALL/Ventoy2Disk.exe b/INSTALL/Ventoy2Disk.exe index 0951124453c43783d1e6d9be93757728f160ebbb..5c4f5ae99e000d89d16655dacfdb0a4e6f9438e8 100644 GIT binary patch delta 43715 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 z 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 b$)_f1%a?sbpNH&o*3IlM(t&j5BO3fK*)N&8 diff --git a/INSTALL/all_in_one.sh b/INSTALL/all_in_one.sh index 89484e7c..660b63be 100644 --- a/INSTALL/all_in_one.sh +++ b/INSTALL/all_in_one.sh @@ -23,6 +23,8 @@ sh buildlib.sh cd $VTOY_PATH/vtoyfat sh build.sh || exit 1 +cd $VTOY_PATH/vtoygpt +sh build.sh || exit 1 cd $VTOY_PATH/ExFAT sh buidlibfuse.sh || exit 1 diff --git a/INSTALL/grub/grub.cfg b/INSTALL/grub/grub.cfg index 52e8a6b7..6b94d74a 100644 --- a/INSTALL/grub/grub.cfg +++ b/INSTALL/grub/grub.cfg @@ -30,6 +30,29 @@ function ventoy_debug_pause { fi } +function ventoy_cli_console { + if [ "$grub_platform" = "pc" ]; then + terminal_output vga_text + else + if [ "$vtoy_display_mode" != "CLI" ]; then + terminal_output console + fi + fi +} + +function ventoy_gui_console { + if [ "$grub_platform" = "pc" ]; then + if [ "$vtoy_display_mode" = "CLI" ]; then + terminal_output console + else + terminal_output gfxterm + fi + else + if [ "$vtoy_display_mode" != "CLI" ]; then + terminal_output gfxterm + fi + fi +} function ventoy_power { configfile $prefix/power.cfg @@ -186,7 +209,7 @@ function uefi_windows_menu_func { ventoy_debug_pause if [ -n "$vtoy_chain_mem_addr" ]; then - terminal_output console + ventoy_cli_console chainloader ${vtoy_path}/ventoy_x64.efi env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size} boot else @@ -262,7 +285,7 @@ function uefi_linux_menu_func { vt_linux_chain_data ${1}${chosen_path} if [ -n "$vtoy_chain_mem_addr" ]; then - terminal_output console + 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 @@ -327,7 +350,7 @@ function uefi_iso_menu_func { uefi_linux_menu_func $1 ${chosen_path} fi - terminal_output gfxterm + ventoy_gui_console } function uefi_iso_memdisk { @@ -336,11 +359,11 @@ function uefi_iso_memdisk { echo 'Loading ISO file to memory ...' vt_load_iso_to_mem ${1}${chosen_path} vtoy_iso_buf - terminal_output console + ventoy_cli_console chainloader ${vtoy_path}/ventoy_x64.efi memdisk env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_iso_buf_addr}:size:${vtoy_iso_buf_size} boot - terminal_output gfxterm + ventoy_gui_console } @@ -527,9 +550,9 @@ function wim_common_menuentry { if [ "$grub_platform" = "pc" ]; then linux16 $vtoy_path/ipxe.krn ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size} else - terminal_output console + ventoy_cli_console chainloader ${vtoy_path}/ventoy_x64.efi env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size} - terminal_output gfxterm + ventoy_gui_console fi boot else @@ -545,10 +568,10 @@ function wim_unsupport_menuentry { function efi_common_menuentry { vt_chosen_img_path chosen_path - terminal_output console + ventoy_cli_console chainloader ${iso_path}${chosen_path} boot - terminal_output gfxterm + ventoy_gui_console } function efi_unsupport_menuentry { diff --git a/INSTALL/tool/VentoyWorker.sh b/INSTALL/tool/VentoyWorker.sh index 099b2d55..2fe07e6a 100644 --- a/INSTALL/tool/VentoyWorker.sh +++ b/INSTALL/tool/VentoyWorker.sh @@ -327,22 +327,28 @@ else SHORT_PART2=${PART2#/dev/} part2_start=$(cat /sys/class/block/$SHORT_PART2/start) - dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=440 + PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') - PART1_ACTIVE=$(dd if=$DISK bs=1 count=1 skip=446 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') - PART2_ACTIVE=$(dd if=$DISK bs=1 count=1 skip=462 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') + if [ "$PART1_TYPE" = "EE" ]; then + vtdebug "This is GPT partition style ..." + ./tool/xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2014 seek=34 + echo -en '\x23' | dd of=$DISK conv=fsync bs=1 count=1 seek=17908 status=none + else + vtdebug "This is MBR partition style ..." + dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=440 - vtdebug "PART1_ACTIVE=$PART1_ACTIVE PART2_ACTIVE=$PART2_ACTIVE" - if [ "$PART1_ACTIVE" = "00" ] && [ "$PART2_ACTIVE" = "80" ]; then - vtdebug "change 1st partition active, 2nd partition inactive ..." - echo -en '\x80' | dd of=$DISK conv=fsync bs=1 count=1 seek=446 status=none - echo -en '\x00' | dd of=$DISK conv=fsync bs=1 count=1 seek=462 status=none + PART1_ACTIVE=$(dd if=$DISK bs=1 count=1 skip=446 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') + PART2_ACTIVE=$(dd if=$DISK bs=1 count=1 skip=462 status=none | ./tool/hexdump -n1 -e '1/1 "%02X"') + + vtdebug "PART1_ACTIVE=$PART1_ACTIVE PART2_ACTIVE=$PART2_ACTIVE" + if [ "$PART1_ACTIVE" = "00" ] && [ "$PART2_ACTIVE" = "80" ]; then + vtdebug "change 1st partition active, 2nd partition inactive ..." + echo -en '\x80' | dd of=$DISK conv=fsync bs=1 count=1 seek=446 status=none + echo -en '\x00' | dd of=$DISK conv=fsync bs=1 count=1 seek=462 status=none + fi + ./tool/xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2047 seek=1 fi - - ./tool/xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2047 seek=1 - - ./tool/xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start sync diff --git a/INSTALL/tool/ventoy_lib.sh b/INSTALL/tool/ventoy_lib.sh index 9e4d45d8..915deaa6 100644 --- a/INSTALL/tool/ventoy_lib.sh +++ b/INSTALL/tool/ventoy_lib.sh @@ -345,11 +345,19 @@ format_ventoy_disk_gpt() { unit s \ mkpart Ventoy ntfs $part1_start_sector $part1_end_sector \ mkpart VTOYEFI fat16 $part2_start_sector $part2_end_sector \ - set 2 boot on \ - set 2 esp on \ + set 2 msftdata on \ set 2 hidden on \ quit + + sync + + if ventoy_is_linux64; then + vtoygpt=./tool/vtoygpt_64 + else + vtoygpt=./tool/vtoygpt_32 + fi + $vtoygpt -f $DISK sync udevadm trigger >/dev/null 2>&1 diff --git a/INSTALL/tool/vtoygpt_32 b/INSTALL/tool/vtoygpt_32 new file mode 100644 index 0000000000000000000000000000000000000000..127f080c5bd351fd85d6e4a93dcee4f93b800bf9 GIT binary patch literal 22144 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 SeB8?EGC8h*QPeLl-~R#qMfQdO literal 0 HcmV?d00001 diff --git a/INSTALL/tool/vtoygpt_64 b/INSTALL/tool/vtoygpt_64 new file mode 100644 index 0000000000000000000000000000000000000000..068aaf6e62c3dc15b49fd3e07f95bf0b77c1b174 GIT binary patch literal 19304 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 UM&0YF{gw=cqS#NUrh)DM4{bNwJ^%m! literal 0 HcmV?d00001 diff --git a/Ventoy2Disk/Ventoy2Disk/PartDialog.c b/Ventoy2Disk/Ventoy2Disk/PartDialog.c index b5f737c01a14ff94ed254a913faf8df2b372d21b..2b30c7464faa4ad9b1a16dd6575c4834559a98fe 100644 GIT binary patch delta 291 zcmbOda4&R&4ky0?0|aI=qybqPlN;G}88s)*6j7e6$Eh<}LP22i1v$CN8`StFpWu|6 zd`Mko@;y0@$ug=kNa9A5PXNXH6gVbN643&yJtrcznTaca57T&F^U1SBlz@iIOfHa+ zfLXu}vz}2xbn*rXFQB;G$xWW8$Og1j Z08T?4g<_U~lnu#d-BTCZY#{xF2LNv@Riyv` delta 251 zcmcZ?IwfF(4ksh$m(S>ef(REtF1W$YCgENQLr{)jBeA zZr0`w=K~ugJNbbakGL&^0)qjACWc~fsdRpz1qiEvmc5r+&I%IaoV-w0SR2HGSqY - * Copyright (c) 2011-2020, Pete Batard - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include -#include -#include -#include "resource.h" -#include "Language.h" -#include "Ventoy2Disk.h" -#include "fat_filelib.h" -#include "ff.h" - -/* - * Some code and functions in the file are copied from rufus. - * https://github.com/pbatard/rufus - */ -#define VDS_SET_ERROR SetLastError -#define IVdsServiceLoader_LoadService(This, pwszMachineName, ppService) (This)->lpVtbl->LoadService(This, pwszMachineName, ppService) -#define IVdsServiceLoader_Release(This) (This)->lpVtbl->Release(This) -#define IVdsService_QueryProviders(This, masks, ppEnum) (This)->lpVtbl->QueryProviders(This, masks, ppEnum) -#define IVdsService_WaitForServiceReady(This) ((This)->lpVtbl->WaitForServiceReady(This)) -#define IVdsService_CleanupObsoleteMountPoints(This) ((This)->lpVtbl->CleanupObsoleteMountPoints(This)) -#define IVdsService_Refresh(This) ((This)->lpVtbl->Refresh(This)) -#define IVdsService_Reenumerate(This) ((This)->lpVtbl->Reenumerate(This)) -#define IVdsSwProvider_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) -#define IVdsProvider_Release(This) (This)->lpVtbl->Release(This) -#define IVdsSwProvider_QueryPacks(This, ppEnum) (This)->lpVtbl->QueryPacks(This, ppEnum) -#define IVdsSwProvider_Release(This) (This)->lpVtbl->Release(This) -#define IVdsPack_QueryDisks(This, ppEnum) (This)->lpVtbl->QueryDisks(This, ppEnum) -#define IVdsDisk_GetProperties(This, pDiskProperties) (This)->lpVtbl->GetProperties(This, pDiskProperties) -#define IVdsDisk_Release(This) (This)->lpVtbl->Release(This) -#define IVdsDisk_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) -#define IVdsAdvancedDisk_QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) (This)->lpVtbl->QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) -#define IVdsAdvancedDisk_DeletePartition(This, ullOffset, bForce, bForceProtected) (This)->lpVtbl->DeletePartition(This, ullOffset, bForce, bForceProtected) -#define IVdsAdvancedDisk_Clean(This, bForce, bForceOEM, bFullClean, ppAsync) (This)->lpVtbl->Clean(This, bForce, bForceOEM, bFullClean, ppAsync) -#define IVdsAdvancedDisk_Release(This) (This)->lpVtbl->Release(This) -#define IEnumVdsObject_Next(This, celt, ppObjectArray, pcFetched) (This)->lpVtbl->Next(This, celt, ppObjectArray, pcFetched) -#define IVdsPack_QueryVolumes(This, ppEnum) (This)->lpVtbl->QueryVolumes(This, ppEnum) -#define IVdsVolume_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) -#define IVdsVolume_Release(This) (This)->lpVtbl->Release(This) -#define IVdsVolumeMF3_QueryVolumeGuidPathnames(This, pwszPathArray, pulNumberOfPaths) (This)->lpVtbl->QueryVolumeGuidPathnames(This,pwszPathArray,pulNumberOfPaths) -#define IVdsVolumeMF3_FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) (This)->lpVtbl->FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) -#define IVdsVolumeMF3_Release(This) (This)->lpVtbl->Release(This) -#define IVdsVolume_GetProperties(This, pVolumeProperties) (This)->lpVtbl->GetProperties(This,pVolumeProperties) -#define IVdsAsync_Cancel(This) (This)->lpVtbl->Cancel(This) -#define IVdsAsync_QueryStatus(This,pHrResult,pulPercentCompleted) (This)->lpVtbl->QueryStatus(This,pHrResult,pulPercentCompleted) -#define IVdsAsync_Wait(This,pHrResult,pAsyncOut) (This)->lpVtbl->Wait(This,pHrResult,pAsyncOut) -#define IVdsAsync_Release(This) (This)->lpVtbl->Release(This) - -#define IUnknown_QueryInterface(This, a, b) (This)->lpVtbl->QueryInterface(This,a,b) -#define IUnknown_Release(This) (This)->lpVtbl->Release(This) - -/* -* Delete all the partitions from a disk, using VDS -* Mostly copied from https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom?forum=vcgeneral -*/ -BOOL DeletePartitions(DWORD DriveIndex, BOOL OnlyPart2) -{ - BOOL r = FALSE; - HRESULT hr; - ULONG ulFetched; - wchar_t wPhysicalName[48]; - IVdsServiceLoader *pLoader; - IVdsService *pService; - IEnumVdsObject *pEnum; - IUnknown *pUnk; - - swprintf_s(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex); - - // Initialize COM - CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); - CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, - RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); - - // Create a VDS Loader Instance - hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, - &IID_IVdsServiceLoader, (void **)&pLoader); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not create VDS Loader Instance: %u", LASTERR); - goto out; - } - - // Load the VDS Service - hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); - IVdsServiceLoader_Release(pLoader); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not load VDS Service: %u", LASTERR); - goto out; - } - - // Wait for the Service to become ready if needed - hr = IVdsService_WaitForServiceReady(pService); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("VDS Service is not ready: %u", LASTERR); - goto out; - } - - // Query the VDS Service Providers - hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not query VDS Service Providers: %u", LASTERR); - goto out; - } - - while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { - IVdsProvider *pProvider; - IVdsSwProvider *pSwProvider; - IEnumVdsObject *pEnumPack; - IUnknown *pPackUnk; - - // Get VDS Provider - hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider); - IUnknown_Release(pUnk); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not get VDS Provider: %u", LASTERR); - goto out; - } - - // Get VDS Software Provider - hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider); - IVdsProvider_Release(pProvider); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not get VDS Software Provider: %u", LASTERR); - goto out; - } - - // Get VDS Software Provider Packs - hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); - IVdsSwProvider_Release(pSwProvider); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not get VDS Software Provider Packs: %u", LASTERR); - goto out; - } - - // Enumerate Provider Packs - while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) { - IVdsPack *pPack; - IEnumVdsObject *pEnumDisk; - IUnknown *pDiskUnk; - - hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack); - IUnknown_Release(pPackUnk); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not query VDS Software Provider Pack: %u", LASTERR); - goto out; - } - - // Use the pack interface to access the disks - hr = IVdsPack_QueryDisks(pPack, &pEnumDisk); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not query VDS disks: %u", LASTERR); - goto out; - } - - // List disks - while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFetched) == S_OK) { - VDS_DISK_PROP diskprop; - VDS_PARTITION_PROP* prop_array; - LONG i, prop_array_size; - IVdsDisk *pDisk; - IVdsAdvancedDisk *pAdvancedDisk; - - // Get the disk interface. - hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void **)&pDisk); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not query VDS Disk Interface: %u", LASTERR); - goto out; - } - - // Get the disk properties - hr = IVdsDisk_GetProperties(pDisk, &diskprop); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not query VDS Disk Properties: %u", LASTERR); - goto out; - } - - // Isolate the disk we want - if (_wcsicmp(wPhysicalName, diskprop.pwszName) != 0) { - IVdsDisk_Release(pDisk); - continue; - } - - // Instantiate the AdvanceDisk interface for our disk. - hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk, (void **)&pAdvancedDisk); - IVdsDisk_Release(pDisk); - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not access VDS Advanced Disk interface: %u", LASTERR); - goto out; - } - - // Query the partition data, so we can get the start offset, which we need for deletion - hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size); - if (hr == S_OK) { - Log("Deleting ALL partition(s) from disk '%S':", diskprop.pwszName); - // Now go through each partition - for (i = 0; i < prop_array_size; i++) { - - Log("* Partition %d (offset: %lld, size: %llu)", prop_array[i].ulPartitionNumber, - prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize); - - if (OnlyPart2 && prop_array[i].ullOffset == 2048*512) - { - Log("Skip this partition..."); - continue; - } - - - hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE); - if (hr != S_OK) { - r = FALSE; - VDS_SET_ERROR(hr); - Log("Could not delete partitions: %u", LASTERR); - } - } - r = TRUE; - } - else { - Log("No partition to delete on disk '%S'", diskprop.pwszName); - r = TRUE; - } - CoTaskMemFree(prop_array); - -#if 0 - // Issue a Clean while we're at it - HRESULT hr2 = E_FAIL; - ULONG completed; - IVdsAsync* pAsync; - hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, FALSE, FALSE, &pAsync); - while (SUCCEEDED(hr)) { - if (IS_ERROR(FormatStatus)) { - IVdsAsync_Cancel(pAsync); - break; - } - hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed); - if (SUCCEEDED(hr)) { - hr = hr2; - if (hr == S_OK) - break; - if (hr == VDS_E_OPERATION_PENDING) - hr = S_OK; - } - Sleep(500); - } - if (hr != S_OK) { - VDS_SET_ERROR(hr); - Log("Could not clean disk: %s", LASTERR); - } -#endif - IVdsAdvancedDisk_Release(pAdvancedDisk); - goto out; - } - } - } - -out: - return r; -} - - -static DWORD GetVentoyVolumeName(int PhyDrive, UINT32 StartSectorId, CHAR *NameBuf, UINT32 BufLen, BOOL DelSlash) -{ - size_t len; - BOOL bRet; - DWORD dwSize; - HANDLE hDrive; - HANDLE hVolume; - UINT64 PartOffset; - DWORD Status = ERROR_NOT_FOUND; - DISK_EXTENT *pExtents = NULL; - CHAR VolumeName[MAX_PATH] = { 0 }; - VOLUME_DISK_EXTENTS DiskExtents; - - PartOffset = 512ULL * StartSectorId; - - Log("GetVentoyVolumeName PhyDrive %d SectorStart:%u PartOffset:%llu", PhyDrive, StartSectorId, (ULONGLONG)PartOffset); - - hVolume = FindFirstVolumeA(VolumeName, sizeof(VolumeName)); - if (hVolume == INVALID_HANDLE_VALUE) - { - return 1; - } - - do { - - len = strlen(VolumeName); - Log("Find volume:%s", VolumeName); - - VolumeName[len - 1] = 0; - - hDrive = CreateFileA(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hDrive == INVALID_HANDLE_VALUE) - { - continue; - } - - bRet = DeviceIoControl(hDrive, - IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, - NULL, - 0, - &DiskExtents, - (DWORD)(sizeof(DiskExtents)), - (LPDWORD)&dwSize, - NULL); - - Log("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS bRet:%u code:%u", bRet, LASTERR); - Log("NumberOfDiskExtents:%u DiskNumber:%u", DiskExtents.NumberOfDiskExtents, DiskExtents.Extents[0].DiskNumber); - - if (bRet && DiskExtents.NumberOfDiskExtents == 1) - { - pExtents = DiskExtents.Extents; - - Log("This volume DiskNumber:%u offset:%llu", pExtents->DiskNumber, (ULONGLONG)pExtents->StartingOffset.QuadPart); - if ((int)pExtents->DiskNumber == PhyDrive && pExtents->StartingOffset.QuadPart == PartOffset) - { - Log("This volume match"); - - if (!DelSlash) - { - VolumeName[len - 1] = '\\'; - } - - sprintf_s(NameBuf, BufLen, "%s", VolumeName); - Status = ERROR_SUCCESS; - CloseHandle(hDrive); - break; - } - } - - CloseHandle(hDrive); - } while (FindNextVolumeA(hVolume, VolumeName, sizeof(VolumeName))); - - FindVolumeClose(hVolume); - - Log("GetVentoyVolumeName return %u", Status); - return Status; -} - -static int GetLettersBelongPhyDrive(int PhyDrive, char *DriveLetters, size_t Length) -{ - int n = 0; - DWORD DataSize = 0; - CHAR *Pos = NULL; - CHAR *StringBuf = NULL; - - DataSize = GetLogicalDriveStringsA(0, NULL); - StringBuf = (CHAR *)malloc(DataSize + 1); - if (StringBuf == NULL) - { - return 1; - } - - GetLogicalDriveStringsA(DataSize, StringBuf); - - for (Pos = StringBuf; *Pos; Pos += strlen(Pos) + 1) - { - if (n < (int)Length && PhyDrive == GetPhyDriveByLogicalDrive(Pos[0])) - { - Log("%C: is belong to phydrive%d", Pos[0], PhyDrive); - DriveLetters[n++] = Pos[0]; - } - } - - free(StringBuf); - return 0; -} - -static HANDLE GetPhysicalHandle(int Drive, BOOLEAN bLockDrive, BOOLEAN bWriteAccess, BOOLEAN bWriteShare) -{ - int i; - DWORD dwSize; - DWORD LastError; - UINT64 EndTime; - HANDLE hDrive = INVALID_HANDLE_VALUE; - CHAR PhyDrive[128]; - CHAR DevPath[MAX_PATH] = { 0 }; - - safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", Drive); - - if (0 == QueryDosDeviceA(PhyDrive + 4, DevPath, sizeof(DevPath))) - { - Log("QueryDosDeviceA failed error:%u", GetLastError()); - strcpy_s(DevPath, sizeof(DevPath), "???"); - } - else - { - Log("QueryDosDeviceA success %s", DevPath); - } - - for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) - { - // Try without FILE_SHARE_WRITE (unless specifically requested) so that - // we won't be bothered by the OS or other apps when we set up our data. - // However this means we might have to wait for an access gap... - // We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is - // required for enumeration. - hDrive = CreateFileA(PhyDrive, - GENERIC_READ | (bWriteAccess ? GENERIC_WRITE : 0), - FILE_SHARE_READ | (bWriteShare ? FILE_SHARE_WRITE : 0), - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - NULL); - - LastError = GetLastError(); - Log("[%d] CreateFileA %s code:%u %p", i, PhyDrive, LastError, hDrive); - - if (hDrive != INVALID_HANDLE_VALUE) - { - break; - } - - if ((LastError != ERROR_SHARING_VIOLATION) && (LastError != ERROR_ACCESS_DENIED)) - { - break; - } - - if (i == 0) - { - Log("Waiting for access on %s [%s]...", PhyDrive, DevPath); - } - else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES / 3)) - { - // If we can't seem to get a hold of the drive for some time, try to enable FILE_SHARE_WRITE... - Log("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled..."); - bWriteShare = TRUE; - - // Try to report the process that is locking the drive - // We also use bit 6 as a flag to indicate that SearchProcess was called. - //access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40; - - } - Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); - } - - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Could not open %s %u", PhyDrive, LASTERR); - goto End; - } - - if (bWriteAccess) - { - Log("Opened %s for %s write access", PhyDrive, bWriteShare ? "shared" : "exclusive"); - } - - if (bLockDrive) - { - if (DeviceIoControl(hDrive, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwSize, NULL)) - { - Log("I/O boundary checks disabled"); - } - - EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT; - - do { - if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL)) - { - Log("FSCTL_LOCK_VOLUME success"); - goto End; - } - Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); - } while (GetTickCount64() < EndTime); - - // If we reached this section, either we didn't manage to get a lock or the user cancelled - Log("Could not lock access to %s %u", PhyDrive, LASTERR); - - // See if we can report the processes are accessing the drive - //if (!IS_ERROR(FormatStatus) && (access_mask == 0)) - // access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE); - // Try to continue if the only access rights we saw were for read-only - //if ((access_mask & 0x07) != 0x01) - // safe_closehandle(hDrive); - - CHECK_CLOSE_HANDLE(hDrive); - } - -End: - - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Can get handle of %s, maybe some process control it.", DevPath); - } - - return hDrive; -} - -int GetPhyDriveByLogicalDrive(int DriveLetter) -{ - BOOL Ret; - DWORD dwSize; - HANDLE Handle; - VOLUME_DISK_EXTENTS DiskExtents; - CHAR PhyPath[128]; - - safe_sprintf(PhyPath, "\\\\.\\%C:", (CHAR)DriveLetter); - - Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if (Handle == INVALID_HANDLE_VALUE) - { - Log("Could not open the disk<%s>, error:%u", PhyPath, LASTERR); - return -1; - } - - Ret = DeviceIoControl(Handle, - IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, - NULL, - 0, - &DiskExtents, - (DWORD)(sizeof(DiskExtents)), - (LPDWORD)&dwSize, - NULL); - - if (!Ret || DiskExtents.NumberOfDiskExtents == 0) - { - Log("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed %s, error:%u", PhyPath, LASTERR); - CHECK_CLOSE_HANDLE(Handle); - return -1; - } - CHECK_CLOSE_HANDLE(Handle); - - Log("LogicalDrive:%s PhyDrive:%d Offset:%llu ExtentLength:%llu", - PhyPath, - DiskExtents.Extents[0].DiskNumber, - DiskExtents.Extents[0].StartingOffset.QuadPart, - DiskExtents.Extents[0].ExtentLength.QuadPart - ); - - return (int)DiskExtents.Extents[0].DiskNumber; -} - -int GetAllPhysicalDriveInfo(PHY_DRIVE_INFO *pDriveList, DWORD *pDriveCount) -{ - int i; - int Count; - int id; - int Letter = 'A'; - BOOL bRet; - DWORD dwBytes; - DWORD DriveCount = 0; - HANDLE Handle = INVALID_HANDLE_VALUE; - CHAR PhyDrive[128]; - PHY_DRIVE_INFO *CurDrive = pDriveList; - GET_LENGTH_INFORMATION LengthInfo; - STORAGE_PROPERTY_QUERY Query; - STORAGE_DESCRIPTOR_HEADER DevDescHeader; - STORAGE_DEVICE_DESCRIPTOR *pDevDesc; - int PhyDriveId[VENTOY_MAX_PHY_DRIVE]; - - Count = GetPhysicalDriveCount(); - - for (i = 0; i < Count && i < VENTOY_MAX_PHY_DRIVE; i++) - { - PhyDriveId[i] = i; - } - - dwBytes = GetLogicalDrives(); - Log("Logical Drives: 0x%x", dwBytes); - while (dwBytes) - { - if (dwBytes & 0x01) - { - id = GetPhyDriveByLogicalDrive(Letter); - Log("%C --> %d", Letter, id); - if (id >= 0) - { - for (i = 0; i < Count; i++) - { - if (PhyDriveId[i] == id) - { - break; - } - } - - if (i >= Count) - { - Log("Add phy%d to list", i); - PhyDriveId[Count] = id; - Count++; - } - } - } - - Letter++; - dwBytes >>= 1; - } - - for (i = 0; i < Count && DriveCount < VENTOY_MAX_PHY_DRIVE; i++) - { - CHECK_CLOSE_HANDLE(Handle); - - safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", PhyDriveId[i]); - Handle = CreateFileA(PhyDrive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - Log("Create file Handle:%p %s status:%u", Handle, PhyDrive, LASTERR); - - if (Handle == INVALID_HANDLE_VALUE) - { - continue; - } - - bRet = DeviceIoControl(Handle, - IOCTL_DISK_GET_LENGTH_INFO, NULL, - 0, - &LengthInfo, - sizeof(LengthInfo), - &dwBytes, - NULL); - if (!bRet) - { - Log("DeviceIoControl IOCTL_DISK_GET_LENGTH_INFO failed error:%u", LASTERR); - continue; - } - - Log("PHYSICALDRIVE%d size %llu bytes", i, (ULONGLONG)LengthInfo.Length.QuadPart); - - Query.PropertyId = StorageDeviceProperty; - Query.QueryType = PropertyStandardQuery; - - bRet = DeviceIoControl(Handle, - IOCTL_STORAGE_QUERY_PROPERTY, - &Query, - sizeof(Query), - &DevDescHeader, - sizeof(STORAGE_DESCRIPTOR_HEADER), - &dwBytes, - NULL); - if (!bRet) - { - Log("DeviceIoControl1 error:%u dwBytes:%u", LASTERR, dwBytes); - continue; - } - - if (DevDescHeader.Size < sizeof(STORAGE_DEVICE_DESCRIPTOR)) - { - Log("Invalid DevDescHeader.Size:%u", DevDescHeader.Size); - continue; - } - - pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(DevDescHeader.Size); - if (!pDevDesc) - { - Log("failed to malloc error:%u len:%u", LASTERR, DevDescHeader.Size); - continue; - } - - bRet = DeviceIoControl(Handle, - IOCTL_STORAGE_QUERY_PROPERTY, - &Query, - sizeof(Query), - pDevDesc, - DevDescHeader.Size, - &dwBytes, - NULL); - if (!bRet) - { - Log("DeviceIoControl2 error:%u dwBytes:%u", LASTERR, dwBytes); - free(pDevDesc); - continue; - } - - CurDrive->PhyDrive = i; - CurDrive->SizeInBytes = LengthInfo.Length.QuadPart; - CurDrive->DeviceType = pDevDesc->DeviceType; - CurDrive->RemovableMedia = pDevDesc->RemovableMedia; - CurDrive->BusType = pDevDesc->BusType; - - if (pDevDesc->VendorIdOffset) - { - safe_strcpy(CurDrive->VendorId, (char *)pDevDesc + pDevDesc->VendorIdOffset); - TrimString(CurDrive->VendorId); - } - - if (pDevDesc->ProductIdOffset) - { - safe_strcpy(CurDrive->ProductId, (char *)pDevDesc + pDevDesc->ProductIdOffset); - TrimString(CurDrive->ProductId); - } - - if (pDevDesc->ProductRevisionOffset) - { - safe_strcpy(CurDrive->ProductRev, (char *)pDevDesc + pDevDesc->ProductRevisionOffset); - TrimString(CurDrive->ProductRev); - } - - if (pDevDesc->SerialNumberOffset) - { - safe_strcpy(CurDrive->SerialNumber, (char *)pDevDesc + pDevDesc->SerialNumberOffset); - TrimString(CurDrive->SerialNumber); - } - - CurDrive++; - DriveCount++; - - free(pDevDesc); - - CHECK_CLOSE_HANDLE(Handle); - } - - for (i = 0, CurDrive = pDriveList; i < (int)DriveCount; i++, CurDrive++) - { - Log("PhyDrv:%d BusType:%-4s Removable:%u Size:%dGB(%llu) Name:%s %s", - CurDrive->PhyDrive, GetBusTypeString(CurDrive->BusType), CurDrive->RemovableMedia, - GetHumanReadableGBSize(CurDrive->SizeInBytes), CurDrive->SizeInBytes, - CurDrive->VendorId, CurDrive->ProductId); - } - - *pDriveCount = DriveCount; - - return 0; -} - - -static HANDLE g_FatPhyDrive; -static UINT64 g_Part2StartSec; -static int GetVentoyVersionFromFatFile(CHAR *VerBuf, size_t BufLen) -{ - int rc = 1; - int size = 0; - char *buf = NULL; - void *flfile = NULL; - - flfile = fl_fopen("/grub/grub.cfg", "rb"); - if (flfile) - { - fl_fseek(flfile, 0, SEEK_END); - size = (int)fl_ftell(flfile); - - fl_fseek(flfile, 0, SEEK_SET); - - buf = (char *)malloc(size + 1); - if (buf) - { - fl_fread(buf, 1, size, flfile); - buf[size] = 0; - - rc = 0; - sprintf_s(VerBuf, BufLen, "%s", ParseVentoyVersionFromString(buf)); - free(buf); - } - - fl_fclose(flfile); - } - - return rc; -} - -static int VentoyFatDiskRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount) -{ - DWORD dwSize; - BOOL bRet; - DWORD ReadSize; - LARGE_INTEGER liCurrentPosition; - - liCurrentPosition.QuadPart = Sector + g_Part2StartSec; - liCurrentPosition.QuadPart *= 512; - SetFilePointerEx(g_FatPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN); - - ReadSize = (DWORD)(SectorCount * 512); - - bRet = ReadFile(g_FatPhyDrive, Buffer, ReadSize, &dwSize, NULL); - if (bRet == FALSE || dwSize != ReadSize) - { - Log("ReadFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u\n", bRet, ReadSize, dwSize, LASTERR); - } - - return 1; -} - - -int GetVentoyVerInPhyDrive(const PHY_DRIVE_INFO *pDriveInfo, UINT64 Part2StartSector, CHAR *VerBuf, size_t BufLen) -{ - int rc = 0; - HANDLE hDrive; - - hDrive = GetPhysicalHandle(pDriveInfo->PhyDrive, FALSE, FALSE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - return 1; - } - - g_FatPhyDrive = hDrive; - g_Part2StartSec = Part2StartSector; - - Log("Parse FAT fs..."); - - fl_init(); - - if (0 == fl_attach_media(VentoyFatDiskRead, NULL)) - { - rc = GetVentoyVersionFromFatFile(VerBuf, BufLen); - } - else - { - rc = 1; - } - - fl_shutdown(); - - CHECK_CLOSE_HANDLE(hDrive); - - return rc; -} - - - - - -static unsigned int g_disk_unxz_len = 0; -static BYTE *g_part_img_pos = NULL; -static BYTE *g_part_img_buf[VENTOY_EFI_PART_SIZE / SIZE_1MB]; - - -static int VentoyFatMemRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount) -{ - uint32 i; - uint32 offset; - BYTE *MbBuf = NULL; - - for (i = 0; i < SectorCount; i++) - { - offset = (Sector + i) * 512; - - if (g_part_img_buf[1] == NULL) - { - MbBuf = g_part_img_buf[0] + offset; - memcpy(Buffer + i * 512, MbBuf, 512); - } - else - { - MbBuf = g_part_img_buf[offset / SIZE_1MB]; - memcpy(Buffer + i * 512, MbBuf + (offset % SIZE_1MB), 512); - } - } - - return 1; -} - - -static int VentoyFatMemWrite(uint32 Sector, uint8 *Buffer, uint32 SectorCount) -{ - uint32 i; - uint32 offset; - BYTE *MbBuf = NULL; - - for (i = 0; i < SectorCount; i++) - { - offset = (Sector + i) * 512; - - if (g_part_img_buf[1] == NULL) - { - MbBuf = g_part_img_buf[0] + offset; - memcpy(MbBuf, Buffer + i * 512, 512); - } - else - { - MbBuf = g_part_img_buf[offset / SIZE_1MB]; - memcpy(MbBuf + (offset % SIZE_1MB), Buffer + i * 512, 512); - } - } - - return 1; -} - -int VentoyProcSecureBoot(BOOL SecureBoot) -{ - int rc = 0; - int size; - char *filebuf = NULL; - void *file = NULL; - - Log("VentoyProcSecureBoot %d ...", SecureBoot); - - if (SecureBoot) - { - Log("Secure boot is enabled ..."); - return 0; - } - - fl_init(); - - if (0 == fl_attach_media(VentoyFatMemRead, VentoyFatMemWrite)) - { - file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb"); - Log("Open ventoy efi file %p ", file); - if (file) - { - fl_fseek(file, 0, SEEK_END); - size = (int)fl_ftell(file); - fl_fseek(file, 0, SEEK_SET); - - Log("ventoy efi file size %d ...", size); - - filebuf = (char *)malloc(size); - if (filebuf) - { - fl_fread(filebuf, 1, size, file); - } - - fl_fclose(file); - - Log("Now delete all efi files ..."); - fl_remove("/EFI/BOOT/BOOTX64.EFI"); - fl_remove("/EFI/BOOT/grubx64.efi"); - fl_remove("/EFI/BOOT/grubx64_real.efi"); - fl_remove("/EFI/BOOT/MokManager.efi"); - - file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb"); - Log("Open bootx64 efi file %p ", file); - if (file) - { - if (filebuf) - { - fl_fwrite(filebuf, 1, size, file); - } - - fl_fflush(file); - fl_fclose(file); - } - - if (filebuf) - { - free(filebuf); - } - } - } - else - { - rc = 1; - } - - fl_shutdown(); - - return rc; -} - - - -static int disk_xz_flush(void *src, unsigned int size) -{ - unsigned int i; - BYTE *buf = (BYTE *)src; - - for (i = 0; i < size; i++) - { - *g_part_img_pos = *buf++; - - g_disk_unxz_len++; - if ((g_disk_unxz_len % SIZE_1MB) == 0) - { - g_part_img_pos = g_part_img_buf[g_disk_unxz_len / SIZE_1MB]; - } - else - { - g_part_img_pos++; - } - } - - return (int)size; -} - -static void unxz_error(char *x) -{ - Log("%s", x); -} - -static BOOL TryWritePart2(HANDLE hDrive, UINT64 StartSectorId) -{ - BOOL bRet; - DWORD TrySize = 16 * 1024; - DWORD dwSize; - BYTE *Buffer = NULL; - unsigned char *data = NULL; - LARGE_INTEGER liCurrentPosition; - - liCurrentPosition.QuadPart = StartSectorId * 512; - SetFilePointerEx(hDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN); - - Buffer = malloc(TrySize); - - bRet = WriteFile(hDrive, Buffer, TrySize, &dwSize, NULL); - - free(Buffer); - - Log("Try write part2 bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); - - if (bRet && dwSize == TrySize) - { - return TRUE; - } - - return FALSE; -} - -static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) -{ - int i; - int rc = 0; - int len = 0; - int writelen = 0; - int partwrite = 0; - DWORD dwSize = 0; - BOOL bRet; - unsigned char *data = NULL; - LARGE_INTEGER liCurrentPosition; - LARGE_INTEGER liNewPosition; - - Log("FormatPart2Fat %llu...", StartSectorId); - - rc = ReadWholeFileToBuf(VENTOY_FILE_DISK_IMG, 0, (void **)&data, &len); - if (rc) - { - Log("Failed to read img file %p %u", data, len); - return 1; - } - - liCurrentPosition.QuadPart = StartSectorId * 512; - SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); - - Log("Set file pointer: %llu New pointer:%llu", liCurrentPosition.QuadPart, liNewPosition.QuadPart); - - memset(g_part_img_buf, 0, sizeof(g_part_img_buf)); - - g_part_img_buf[0] = (BYTE *)malloc(VENTOY_EFI_PART_SIZE); - if (g_part_img_buf[0]) - { - Log("Malloc whole img buffer success, now decompress ..."); - unxz(data, len, NULL, NULL, g_part_img_buf[0], &writelen, unxz_error); - - if (len == writelen) - { - Log("decompress finished success"); - - VentoyProcSecureBoot(g_SecureBoot); - - for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) - { - dwSize = 0; - bRet = WriteFile(hDrive, g_part_img_buf[0] + i * SIZE_1MB, SIZE_1MB, &dwSize, NULL); - Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); - - if (!bRet) - { - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); - } - } - else - { - rc = 1; - Log("decompress finished failed"); - goto End; - } - } - else - { - Log("Failed to malloc whole img size %u, now split it", VENTOY_EFI_PART_SIZE); - - partwrite = 1; - for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) - { - g_part_img_buf[i] = (BYTE *)malloc(SIZE_1MB); - if (g_part_img_buf[i] == NULL) - { - rc = 1; - goto End; - } - } - - Log("Malloc part img buffer success, now decompress ..."); - - g_part_img_pos = g_part_img_buf[0]; - - unxz(data, len, NULL, disk_xz_flush, NULL, NULL, unxz_error); - - if (g_disk_unxz_len == VENTOY_EFI_PART_SIZE) - { - Log("decompress finished success"); - - VentoyProcSecureBoot(g_SecureBoot); - - for (int i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) - { - dwSize = 0; - bRet = WriteFile(hDrive, g_part_img_buf[i], SIZE_1MB, &dwSize, NULL); - Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); - - if (!bRet) - { - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); - } - } - else - { - rc = 1; - Log("decompress finished failed"); - goto End; - } - } - -End: - - if (data) free(data); - - if (partwrite) - { - for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) - { - if (g_part_img_buf[i]) free(g_part_img_buf[i]); - } - } - else - { - if (g_part_img_buf[0]) free(g_part_img_buf[0]); - } - - return rc; -} - -static int WriteGrubStage1ToPhyDrive(HANDLE hDrive, int PartStyle) -{ - int Len = 0; - int readLen = 0; - BOOL bRet; - DWORD dwSize; - BYTE *ImgBuf = NULL; - BYTE *RawBuf = NULL; - - Log("WriteGrubStage1ToPhyDrive ..."); - - RawBuf = (BYTE *)malloc(SIZE_1MB); - if (!RawBuf) - { - return 1; - } - - if (ReadWholeFileToBuf(VENTOY_FILE_STG1_IMG, 0, (void **)&ImgBuf, &Len)) - { - Log("Failed to read stage1 img"); - free(RawBuf); - return 1; - } - - unxz(ImgBuf, Len, NULL, NULL, RawBuf, &readLen, unxz_error); - - if (PartStyle) - { - Log("Write GPT stage1 ..."); - RawBuf[500] = 35;//update blocklist - SetFilePointer(hDrive, 512 * 34, NULL, FILE_BEGIN); - bRet = WriteFile(hDrive, RawBuf, SIZE_1MB - 512 * 34, &dwSize, NULL); - } - else - { - Log("Write MBR stage1 ..."); - SetFilePointer(hDrive, 512, NULL, FILE_BEGIN); - bRet = WriteFile(hDrive, RawBuf, SIZE_1MB - 512, &dwSize, NULL); - } - - Log("WriteFile Ret:%u dwSize:%u ErrCode:%u", bRet, dwSize, GetLastError()); - - free(RawBuf); - free(ImgBuf); - return 0; -} - - - -static int FormatPart1exFAT(UINT64 DiskSizeBytes) -{ - MKFS_PARM Option; - FRESULT Ret; - FATFS fs; - - Option.fmt = FM_EXFAT; - Option.n_fat = 1; - Option.align = 8; - Option.n_root = 1; - - // < 32GB select 32KB as cluster size - // > 32GB select 128KB as cluster size - if (DiskSizeBytes / 1024 / 1024 / 1024 <= 32) - { - Option.au_size = 32768; - } - else - { - Option.au_size = 131072; - } - - Log("Formatting Part1 exFAT ..."); - - Ret = f_mkfs(TEXT("0:"), &Option, 0, 8 * 1024 * 1024); - - if (FR_OK == Ret) - { - Log("Formatting Part1 exFAT success"); - - Ret = f_mount(&fs, TEXT("0:"), 1); - Log("mount part %d", Ret); - - if (FR_OK == Ret) - { - Ret = f_setlabel(TEXT("Ventoy")); - Log("f_setlabel %d", Ret); - - Ret = f_mount(0, TEXT("0:"), 1); - Log("umount part %d", Ret); - return 0; - } - else - { - Log("mount exfat failed %d", Ret); - return 1; - } - } - else - { - Log("Formatting Part1 exFAT failed"); - return 1; - } -} - - - -int ClearVentoyFromPhyDrive(HWND hWnd, PHY_DRIVE_INFO *pPhyDrive, char *pDrvLetter) -{ - int i; - int rc = 0; - int state = 0; - HANDLE hDrive; - DWORD dwSize; - BOOL bRet; - CHAR MountDrive; - CHAR DriveName[] = "?:\\"; - CHAR DriveLetters[MAX_PATH] = { 0 }; - LARGE_INTEGER liCurrentPosition; - char *pTmpBuf = NULL; - MBR_HEAD MBR; - - *pDrvLetter = 0; - - Log("ClearVentoyFromPhyDrive PhyDrive%d <<%s %s %dGB>>", - pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, - GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); - - Log("Lock disk for clean ............................. "); - - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to open physical disk"); - return 1; - } - - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); - - if (DriveLetters[0] == 0) - { - Log("No drive letter was assigned..."); - DriveName[0] = GetFirstUnusedDriveLetter(); - Log("GetFirstUnusedDriveLetter %C: ...", DriveName[0]); - } - else - { - // Unmount all mounted volumes that belong to this drive - // Do it in reverse so that we always end on the first volume letter - for (i = (int)strlen(DriveLetters); i > 0; i--) - { - DriveName[0] = DriveLetters[i - 1]; - bRet = DeleteVolumeMountPointA(DriveName); - Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, GetLastError()); - } - } - - MountDrive = DriveName[0]; - Log("Will use '%C:' as volume mountpoint", DriveName[0]); - - // It kind of blows, but we have to relinquish access to the physical drive - // for VDS to be able to delete the partitions that reside on it... - DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - CHECK_CLOSE_HANDLE(hDrive); - - PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); - - if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) - { - Log("Notice: Could not delete partitions: %u", GetLastError()); - } - - Log("Deleting all partitions ......................... OK"); - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); - - Log("Lock disk for write ............................. "); - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to GetPhysicalHandle for write."); - rc = 1; - goto End; - } - - // clear first and last 1MB space - pTmpBuf = malloc(SIZE_1MB); - if (!pTmpBuf) - { - Log("Failed to alloc memory."); - rc = 1; - goto End; - } - memset(pTmpBuf, 0, SIZE_1MB); - - SET_FILE_POS(512); - bRet = WriteFile(hDrive, pTmpBuf, SIZE_1MB - 512, &dwSize, NULL); - Log("Write fisrt 1MB ret:%d size:%u err:%d", bRet, dwSize, LASTERR); - if (!bRet) - { - rc = 1; - goto End; - } - - SET_FILE_POS(SIZE_1MB); - bRet = WriteFile(hDrive, pTmpBuf, SIZE_1MB, &dwSize, NULL); - Log("Write 2nd 1MB ret:%d size:%u err:%d", bRet, dwSize, LASTERR); - if (!bRet) - { - rc = 1; - goto End; - } - - SET_FILE_POS(0); - bRet = ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL); - Log("Read MBR ret:%d size:%u err:%d", bRet, dwSize, LASTERR); - if (!bRet) - { - rc = 1; - goto End; - } - - //clear boot code and partition table (reserved disk signature) - memset(MBR.BootCode, 0, 440); - memset(MBR.PartTbl, 0, sizeof(MBR.PartTbl)); - - VentoyFillLocation(pPhyDrive->SizeInBytes, 2048, (UINT32)(pPhyDrive->SizeInBytes / 512 - 2048), MBR.PartTbl); - - MBR.PartTbl[0].Active = 0x00; // bootable - MBR.PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS - - SET_FILE_POS(0); - bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); - Log("Write MBR ret:%d size:%u err:%d", bRet, dwSize, LASTERR); - if (!bRet) - { - rc = 1; - goto End; - } - - Log("Clear Ventoy successfully finished"); - - //Refresh Drive Layout - DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); - -End: - - PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME); - - if (pTmpBuf) - { - free(pTmpBuf); - } - - if (rc == 0) - { - Log("Mounting Ventoy Partition ....................... "); - Sleep(1000); - - state = 0; - memset(DriveLetters, 0, sizeof(DriveLetters)); - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); - Log("Logical drive letter after write ventoy: <%s>", DriveLetters); - - for (i = 0; i < sizeof(DriveLetters) && DriveLetters[i]; i++) - { - DriveName[0] = DriveLetters[i]; - Log("%s is ventoy part1, already mounted", DriveName); - state = 1; - } - - if (state != 1) - { - Log("need to mount ventoy part1..."); - if (0 == GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[0].StartSectorId, DriveLetters, sizeof(DriveLetters), FALSE)) - { - DriveName[0] = MountDrive; - bRet = SetVolumeMountPointA(DriveName, DriveLetters); - Log("SetVolumeMountPoint <%s> <%s> bRet:%u code:%u", DriveName, DriveLetters, bRet, GetLastError()); - - *pDrvLetter = MountDrive; - } - else - { - Log("Failed to find ventoy volume"); - } - } - - Log("OK\n"); - } - else - { - FindProcessOccupyDisk(hDrive, pPhyDrive); - } - - CHECK_CLOSE_HANDLE(hDrive); - return rc; -} - -int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle) -{ - int i; - int rc = 0; - int state = 0; - HANDLE hDrive; - DWORD dwSize; - BOOL bRet; - CHAR MountDrive; - CHAR DriveName[] = "?:\\"; - CHAR DriveLetters[MAX_PATH] = { 0 }; - MBR_HEAD MBR; - VTOY_GPT_INFO *pGptInfo = NULL; - - Log("InstallVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", - PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, - GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); - - if (PartStyle) - { - pGptInfo = malloc(sizeof(VTOY_GPT_INFO)); - memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO)); - } - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); - - VentoyFillMBR(pPhyDrive->SizeInBytes, &MBR, PartStyle);//also used to format 1st partition in GPT mode - if (PartStyle) - { - VentoyFillGpt(pPhyDrive->SizeInBytes, pGptInfo); - } - - Log("Lock disk for clean ............................. "); - - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to open physical disk"); - free(pGptInfo); - return 1; - } - - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); - - if (DriveLetters[0] == 0) - { - Log("No drive letter was assigned..."); - DriveName[0] = GetFirstUnusedDriveLetter(); - Log("GetFirstUnusedDriveLetter %C: ...", DriveName[0]); - } - else - { - // Unmount all mounted volumes that belong to this drive - // Do it in reverse so that we always end on the first volume letter - for (i = (int)strlen(DriveLetters); i > 0; i--) - { - DriveName[0] = DriveLetters[i - 1]; - bRet = DeleteVolumeMountPointA(DriveName); - Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, GetLastError()); - } - } - - MountDrive = DriveName[0]; - Log("Will use '%C:' as volume mountpoint", DriveName[0]); - - // It kind of blows, but we have to relinquish access to the physical drive - // for VDS to be able to delete the partitions that reside on it... - DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - CHECK_CLOSE_HANDLE(hDrive); - - PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); - - if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) - { - Log("Notice: Could not delete partitions: %u", GetLastError()); - } - - Log("Deleting all partitions ......................... OK"); - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); - - Log("Lock disk for write ............................. "); - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to GetPhysicalHandle for write."); - rc = 1; - goto End; - } - - //Refresh Drive Layout - DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); - - disk_io_set_param(hDrive, MBR.PartTbl[0].StartSectorId + MBR.PartTbl[0].SectorCount); - - PROGRESS_BAR_SET_POS(PT_FORMAT_PART1); - - Log("Formatting part1 exFAT ..."); - if (0 != FormatPart1exFAT(pPhyDrive->SizeInBytes)) - { - Log("FormatPart1exFAT failed."); - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_FORMAT_PART2); - Log("Writing part2 FAT img ..."); - if (0 != FormatPart2Fat(hDrive, MBR.PartTbl[1].StartSectorId)) - { - Log("FormatPart2Fat failed."); - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_WRITE_STG1_IMG); - Log("Writting Boot Image ............................. "); - if (WriteGrubStage1ToPhyDrive(hDrive, PartStyle) != 0) - { - Log("WriteGrubStage1ToPhyDrive failed."); - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_WRITE_PART_TABLE); - Log("Writting Partition Table ........................ "); - SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); - - if (PartStyle) - { - VTOY_GPT_HDR BackupHead; - LARGE_INTEGER liCurrentPosition; - - SET_FILE_POS(pPhyDrive->SizeInBytes - 512); - VentoyFillBackupGptHead(pGptInfo, &BackupHead); - if (!WriteFile(hDrive, &BackupHead, sizeof(VTOY_GPT_HDR), &dwSize, NULL)) - { - rc = 1; - Log("Write GPT Backup Head Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); - goto End; - } - - SET_FILE_POS(pPhyDrive->SizeInBytes - 512 * 33); - if (!WriteFile(hDrive, pGptInfo->PartTbl, sizeof(pGptInfo->PartTbl), &dwSize, NULL)) - { - rc = 1; - Log("Write GPT Backup Part Table Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); - goto End; - } - - SET_FILE_POS(0); - if (!WriteFile(hDrive, pGptInfo, sizeof(VTOY_GPT_INFO), &dwSize, NULL)) - { - rc = 1; - Log("Write GPT Info Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); - goto End; - } - - Log("Write GPT Info OK ..."); - } - else - { - if (!WriteFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL)) - { - rc = 1; - Log("Write MBR Failed, dwSize:%u ErrCode:%u", dwSize, GetLastError()); - goto End; - } - Log("Write MBR OK ..."); - } - - - //Refresh Drive Layout - DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); - -End: - - PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME); - - if (rc == 0) - { - Log("Mounting Ventoy Partition ....................... "); - Sleep(1000); - - state = 0; - memset(DriveLetters, 0, sizeof(DriveLetters)); - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); - Log("Logical drive letter after write ventoy: <%s>", DriveLetters); - - for (i = 0; i < sizeof(DriveLetters) && DriveLetters[i]; i++) - { - DriveName[0] = DriveLetters[i]; - if (IsVentoyLogicalDrive(DriveName[0])) - { - Log("%s is ventoy part2, delete mountpoint", DriveName); - DeleteVolumeMountPointA(DriveName); - } - else - { - Log("%s is ventoy part1, already mounted", DriveName); - state = 1; - } - } - - if (state != 1) - { - Log("need to mount ventoy part1..."); - if (0 == GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[0].StartSectorId, DriveLetters, sizeof(DriveLetters), FALSE)) - { - DriveName[0] = MountDrive; - bRet = SetVolumeMountPointA(DriveName, DriveLetters); - Log("SetVolumeMountPoint <%s> <%s> bRet:%u code:%u", DriveName, DriveLetters, bRet, GetLastError()); - } - else - { - Log("Failed to find ventoy volume"); - } - } - Log("OK\n"); - } - else - { - FindProcessOccupyDisk(hDrive, pPhyDrive); - } - - if (pGptInfo) - { - free(pGptInfo); - } - - CHECK_CLOSE_HANDLE(hDrive); - return rc; -} - -int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) -{ - int i; - int rc = 0; - BOOL ForceMBR = FALSE; - HANDLE hVolume; - HANDLE hDrive; - DWORD Status; - DWORD dwSize; - BOOL bRet; - CHAR DriveName[] = "?:\\"; - CHAR DriveLetters[MAX_PATH] = { 0 }; - UINT64 StartSector; - UINT64 ReservedMB = 0; - MBR_HEAD BootImg; - MBR_HEAD MBR; - VTOY_GPT_INFO *pGptInfo = NULL; - - Log("UpdateVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", - pPhyDrive->PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, - GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); - - Log("Lock disk for umount ............................ "); - - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to open physical disk"); - return 1; - } - - if (pPhyDrive->PartStyle) - { - pGptInfo = malloc(sizeof(VTOY_GPT_INFO)); - if (!pGptInfo) - { - return 1; - } - - memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO)); - - // Read GPT Info - SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); - ReadFile(hDrive, pGptInfo, sizeof(VTOY_GPT_INFO), &dwSize, NULL); - - StartSector = pGptInfo->PartTbl[1].StartLBA; - Log("GPT StartSector in PartTbl:%llu", (ULONGLONG)StartSector); - - ReservedMB = (pPhyDrive->SizeInBytes / 512 - (StartSector + VENTOY_EFI_PART_SIZE / 512) - 33) / 2048; - Log("GPT Reserved Disk Space:%llu MB", (ULONGLONG)ReservedMB); - } - else - { - // Read MBR - SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); - ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL); - - StartSector = MBR.PartTbl[1].StartSectorId; - Log("MBR StartSector in PartTbl:%llu", (ULONGLONG)StartSector); - - ReservedMB = (pPhyDrive->SizeInBytes / 512 - (StartSector + VENTOY_EFI_PART_SIZE / 512)) / 2048; - Log("MBR Reserved Disk Space:%llu MB", (ULONGLONG)ReservedMB); - } - - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); - - if (DriveLetters[0] == 0) - { - Log("No drive letter was assigned..."); - } - else - { - // Unmount all mounted volumes that belong to this drive - // Do it in reverse so that we always end on the first volume letter - for (i = (int)strlen(DriveLetters); i > 0; i--) - { - DriveName[0] = DriveLetters[i - 1]; - if (IsVentoyLogicalDrive(DriveName[0])) - { - Log("%s is ventoy logical drive", DriveName); - bRet = DeleteVolumeMountPointA(DriveName); - Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, LASTERR); - break; - } - } - } - - // It kind of blows, but we have to relinquish access to the physical drive - // for VDS to be able to delete the partitions that reside on it... - DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - CHECK_CLOSE_HANDLE(hDrive); - - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); - - Log("Lock disk for update ............................ "); - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to GetPhysicalHandle for write."); - rc = 1; - goto End; - } - - PROGRESS_BAR_SET_POS(PT_LOCK_VOLUME); - - Log("Lock volume for update .......................... "); - hVolume = INVALID_HANDLE_VALUE; - Status = GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[1].StartSectorId, DriveLetters, sizeof(DriveLetters), TRUE); - if (ERROR_SUCCESS == Status) - { - Log("Now lock and dismount volume <%s>", DriveLetters); - hVolume = CreateFileA(DriveLetters, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - NULL); - - if (hVolume == INVALID_HANDLE_VALUE) - { - Log("Failed to create file volume, errcode:%u", LASTERR); - rc = 1; - goto End; - } - - bRet = DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - Log("FSCTL_LOCK_VOLUME bRet:%u code:%u", bRet, LASTERR); - - bRet = DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - Log("FSCTL_DISMOUNT_VOLUME bRet:%u code:%u", bRet, LASTERR); - } - else if (ERROR_NOT_FOUND == Status) - { - Log("Volume not found, maybe not supported"); - } - else - { - rc = 1; - goto End; - } - - - if (!TryWritePart2(hDrive, StartSector)) - { - ForceMBR = TRUE; - Log("Try write failed, now delete partition 2..."); - - CHECK_CLOSE_HANDLE(hDrive); - - Log("Now delete partition 2..."); - DeletePartitions(pPhyDrive->PhyDrive, TRUE); - - hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); - if (hDrive == INVALID_HANDLE_VALUE) - { - Log("Failed to GetPhysicalHandle for write."); - rc = 1; - goto End; - } - } - - - PROGRESS_BAR_SET_POS(PT_FORMAT_PART2); - - Log("Write Ventoy to disk ............................ "); - if (0 != FormatPart2Fat(hDrive, StartSector)) - { - rc = 1; - goto End; - } - - if (hVolume != INVALID_HANDLE_VALUE) - { - bRet = DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); - Log("FSCTL_UNLOCK_VOLUME bRet:%u code:%u", bRet, LASTERR); - CHECK_CLOSE_HANDLE(hVolume); - } - - Log("Updating Boot Image ............................. "); - if (WriteGrubStage1ToPhyDrive(hDrive, pPhyDrive->PartStyle) != 0) - { - rc = 1; - goto End; - } - - // Boot Image - VentoyGetLocalBootImg(&BootImg); - - // Use Old UUID - memcpy(BootImg.BootCode + 0x180, MBR.BootCode + 0x180, 16); - if (pPhyDrive->PartStyle) - { - BootImg.BootCode[92] = 0x22; - } - - if (ForceMBR == FALSE && memcmp(BootImg.BootCode, MBR.BootCode, 440) == 0) - { - Log("Boot image has no difference, no need to write."); - } - else - { - Log("Boot image need to write %u.", ForceMBR); - - SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); - - memcpy(MBR.BootCode, BootImg.BootCode, 440); - bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); - Log("Write Boot Image ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR); - } - - if (pPhyDrive->PartStyle == 0) - { - if (0x00 == MBR.PartTbl[0].Active && 0x80 == MBR.PartTbl[1].Active) - { - Log("Need to chage 1st partition active and 2nd partition inactive."); - - MBR.PartTbl[0].Active = 0x80; - MBR.PartTbl[1].Active = 0x00; - - SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); - bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); - Log("Write NEW MBR ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR); - } - } - - //Refresh Drive Layout - DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); - -End: - - if (rc == 0) - { - Log("OK"); - } - else - { - FindProcessOccupyDisk(hDrive, pPhyDrive); - } - - CHECK_CLOSE_HANDLE(hDrive); - - if (pGptInfo) - { - free(pGptInfo); - } - - return rc; -} - - +/****************************************************************************** + * PhyDrive.c + * + * Copyright (c) 2020, longpanda + * Copyright (c) 2011-2020, Pete Batard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include +#include +#include +#include "resource.h" +#include "Language.h" +#include "Ventoy2Disk.h" +#include "fat_filelib.h" +#include "ff.h" + +/* + * Some code and functions in the file are copied from rufus. + * https://github.com/pbatard/rufus + */ +#define VDS_SET_ERROR SetLastError +#define IVdsServiceLoader_LoadService(This, pwszMachineName, ppService) (This)->lpVtbl->LoadService(This, pwszMachineName, ppService) +#define IVdsServiceLoader_Release(This) (This)->lpVtbl->Release(This) +#define IVdsService_QueryProviders(This, masks, ppEnum) (This)->lpVtbl->QueryProviders(This, masks, ppEnum) +#define IVdsService_WaitForServiceReady(This) ((This)->lpVtbl->WaitForServiceReady(This)) +#define IVdsService_CleanupObsoleteMountPoints(This) ((This)->lpVtbl->CleanupObsoleteMountPoints(This)) +#define IVdsService_Refresh(This) ((This)->lpVtbl->Refresh(This)) +#define IVdsService_Reenumerate(This) ((This)->lpVtbl->Reenumerate(This)) +#define IVdsSwProvider_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IVdsProvider_Release(This) (This)->lpVtbl->Release(This) +#define IVdsSwProvider_QueryPacks(This, ppEnum) (This)->lpVtbl->QueryPacks(This, ppEnum) +#define IVdsSwProvider_Release(This) (This)->lpVtbl->Release(This) +#define IVdsPack_QueryDisks(This, ppEnum) (This)->lpVtbl->QueryDisks(This, ppEnum) +#define IVdsDisk_GetProperties(This, pDiskProperties) (This)->lpVtbl->GetProperties(This, pDiskProperties) +#define IVdsDisk_Release(This) (This)->lpVtbl->Release(This) +#define IVdsDisk_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IVdsAdvancedDisk_QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) (This)->lpVtbl->QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) +#define IVdsAdvancedDisk_DeletePartition(This, ullOffset, bForce, bForceProtected) (This)->lpVtbl->DeletePartition(This, ullOffset, bForce, bForceProtected) +#define IVdsAdvancedDisk_Clean(This, bForce, bForceOEM, bFullClean, ppAsync) (This)->lpVtbl->Clean(This, bForce, bForceOEM, bFullClean, ppAsync) +#define IVdsAdvancedDisk_Release(This) (This)->lpVtbl->Release(This) +#define IEnumVdsObject_Next(This, celt, ppObjectArray, pcFetched) (This)->lpVtbl->Next(This, celt, ppObjectArray, pcFetched) +#define IVdsPack_QueryVolumes(This, ppEnum) (This)->lpVtbl->QueryVolumes(This, ppEnum) +#define IVdsVolume_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject) +#define IVdsVolume_Release(This) (This)->lpVtbl->Release(This) +#define IVdsVolumeMF3_QueryVolumeGuidPathnames(This, pwszPathArray, pulNumberOfPaths) (This)->lpVtbl->QueryVolumeGuidPathnames(This,pwszPathArray,pulNumberOfPaths) +#define IVdsVolumeMF3_FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) (This)->lpVtbl->FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) +#define IVdsVolumeMF3_Release(This) (This)->lpVtbl->Release(This) +#define IVdsVolume_GetProperties(This, pVolumeProperties) (This)->lpVtbl->GetProperties(This,pVolumeProperties) +#define IVdsAsync_Cancel(This) (This)->lpVtbl->Cancel(This) +#define IVdsAsync_QueryStatus(This,pHrResult,pulPercentCompleted) (This)->lpVtbl->QueryStatus(This,pHrResult,pulPercentCompleted) +#define IVdsAsync_Wait(This,pHrResult,pAsyncOut) (This)->lpVtbl->Wait(This,pHrResult,pAsyncOut) +#define IVdsAsync_Release(This) (This)->lpVtbl->Release(This) + +#define IUnknown_QueryInterface(This, a, b) (This)->lpVtbl->QueryInterface(This,a,b) +#define IUnknown_Release(This) (This)->lpVtbl->Release(This) + +/* +* Delete all the partitions from a disk, using VDS +* Mostly copied from https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom?forum=vcgeneral +*/ +BOOL DeletePartitions(DWORD DriveIndex, BOOL OnlyPart2) +{ + BOOL r = FALSE; + HRESULT hr; + ULONG ulFetched; + wchar_t wPhysicalName[48]; + IVdsServiceLoader *pLoader; + IVdsService *pService; + IEnumVdsObject *pEnum; + IUnknown *pUnk; + + swprintf_s(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex); + + // Initialize COM + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); + + // Create a VDS Loader Instance + hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, + &IID_IVdsServiceLoader, (void **)&pLoader); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not create VDS Loader Instance: %u", LASTERR); + goto out; + } + + // Load the VDS Service + hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService); + IVdsServiceLoader_Release(pLoader); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not load VDS Service: %u", LASTERR); + goto out; + } + + // Wait for the Service to become ready if needed + hr = IVdsService_WaitForServiceReady(pService); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("VDS Service is not ready: %u", LASTERR); + goto out; + } + + // Query the VDS Service Providers + hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not query VDS Service Providers: %u", LASTERR); + goto out; + } + + while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) { + IVdsProvider *pProvider; + IVdsSwProvider *pSwProvider; + IEnumVdsObject *pEnumPack; + IUnknown *pPackUnk; + + // Get VDS Provider + hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider); + IUnknown_Release(pUnk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not get VDS Provider: %u", LASTERR); + goto out; + } + + // Get VDS Software Provider + hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider); + IVdsProvider_Release(pProvider); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not get VDS Software Provider: %u", LASTERR); + goto out; + } + + // Get VDS Software Provider Packs + hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack); + IVdsSwProvider_Release(pSwProvider); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not get VDS Software Provider Packs: %u", LASTERR); + goto out; + } + + // Enumerate Provider Packs + while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) { + IVdsPack *pPack; + IEnumVdsObject *pEnumDisk; + IUnknown *pDiskUnk; + + hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack); + IUnknown_Release(pPackUnk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not query VDS Software Provider Pack: %u", LASTERR); + goto out; + } + + // Use the pack interface to access the disks + hr = IVdsPack_QueryDisks(pPack, &pEnumDisk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not query VDS disks: %u", LASTERR); + goto out; + } + + // List disks + while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFetched) == S_OK) { + VDS_DISK_PROP diskprop; + VDS_PARTITION_PROP* prop_array; + LONG i, prop_array_size; + IVdsDisk *pDisk; + IVdsAdvancedDisk *pAdvancedDisk; + + // Get the disk interface. + hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void **)&pDisk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not query VDS Disk Interface: %u", LASTERR); + goto out; + } + + // Get the disk properties + hr = IVdsDisk_GetProperties(pDisk, &diskprop); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not query VDS Disk Properties: %u", LASTERR); + goto out; + } + + // Isolate the disk we want + if (_wcsicmp(wPhysicalName, diskprop.pwszName) != 0) { + IVdsDisk_Release(pDisk); + continue; + } + + // Instantiate the AdvanceDisk interface for our disk. + hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk, (void **)&pAdvancedDisk); + IVdsDisk_Release(pDisk); + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not access VDS Advanced Disk interface: %u", LASTERR); + goto out; + } + + // Query the partition data, so we can get the start offset, which we need for deletion + hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size); + if (hr == S_OK) { + Log("Deleting ALL partition(s) from disk '%S':", diskprop.pwszName); + // Now go through each partition + for (i = 0; i < prop_array_size; i++) { + + Log("* Partition %d (offset: %lld, size: %llu)", prop_array[i].ulPartitionNumber, + prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize); + + if (OnlyPart2 && prop_array[i].ullOffset == 2048*512) + { + Log("Skip this partition..."); + continue; + } + + + hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE); + if (hr != S_OK) { + r = FALSE; + VDS_SET_ERROR(hr); + Log("Could not delete partitions: %u", LASTERR); + } + } + r = TRUE; + } + else { + Log("No partition to delete on disk '%S'", diskprop.pwszName); + r = TRUE; + } + CoTaskMemFree(prop_array); + +#if 0 + // Issue a Clean while we're at it + HRESULT hr2 = E_FAIL; + ULONG completed; + IVdsAsync* pAsync; + hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, FALSE, FALSE, &pAsync); + while (SUCCEEDED(hr)) { + if (IS_ERROR(FormatStatus)) { + IVdsAsync_Cancel(pAsync); + break; + } + hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed); + if (SUCCEEDED(hr)) { + hr = hr2; + if (hr == S_OK) + break; + if (hr == VDS_E_OPERATION_PENDING) + hr = S_OK; + } + Sleep(500); + } + if (hr != S_OK) { + VDS_SET_ERROR(hr); + Log("Could not clean disk: %s", LASTERR); + } +#endif + IVdsAdvancedDisk_Release(pAdvancedDisk); + goto out; + } + } + } + +out: + return r; +} + + +static DWORD GetVentoyVolumeName(int PhyDrive, UINT32 StartSectorId, CHAR *NameBuf, UINT32 BufLen, BOOL DelSlash) +{ + size_t len; + BOOL bRet; + DWORD dwSize; + HANDLE hDrive; + HANDLE hVolume; + UINT64 PartOffset; + DWORD Status = ERROR_NOT_FOUND; + DISK_EXTENT *pExtents = NULL; + CHAR VolumeName[MAX_PATH] = { 0 }; + VOLUME_DISK_EXTENTS DiskExtents; + + PartOffset = 512ULL * StartSectorId; + + Log("GetVentoyVolumeName PhyDrive %d SectorStart:%u PartOffset:%llu", PhyDrive, StartSectorId, (ULONGLONG)PartOffset); + + hVolume = FindFirstVolumeA(VolumeName, sizeof(VolumeName)); + if (hVolume == INVALID_HANDLE_VALUE) + { + return 1; + } + + do { + + len = strlen(VolumeName); + Log("Find volume:%s", VolumeName); + + VolumeName[len - 1] = 0; + + hDrive = CreateFileA(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hDrive == INVALID_HANDLE_VALUE) + { + continue; + } + + bRet = DeviceIoControl(hDrive, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, + 0, + &DiskExtents, + (DWORD)(sizeof(DiskExtents)), + (LPDWORD)&dwSize, + NULL); + + Log("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS bRet:%u code:%u", bRet, LASTERR); + Log("NumberOfDiskExtents:%u DiskNumber:%u", DiskExtents.NumberOfDiskExtents, DiskExtents.Extents[0].DiskNumber); + + if (bRet && DiskExtents.NumberOfDiskExtents == 1) + { + pExtents = DiskExtents.Extents; + + Log("This volume DiskNumber:%u offset:%llu", pExtents->DiskNumber, (ULONGLONG)pExtents->StartingOffset.QuadPart); + if ((int)pExtents->DiskNumber == PhyDrive && pExtents->StartingOffset.QuadPart == PartOffset) + { + Log("This volume match"); + + if (!DelSlash) + { + VolumeName[len - 1] = '\\'; + } + + sprintf_s(NameBuf, BufLen, "%s", VolumeName); + Status = ERROR_SUCCESS; + CloseHandle(hDrive); + break; + } + } + + CloseHandle(hDrive); + } while (FindNextVolumeA(hVolume, VolumeName, sizeof(VolumeName))); + + FindVolumeClose(hVolume); + + Log("GetVentoyVolumeName return %u", Status); + return Status; +} + +static int GetLettersBelongPhyDrive(int PhyDrive, char *DriveLetters, size_t Length) +{ + int n = 0; + DWORD DataSize = 0; + CHAR *Pos = NULL; + CHAR *StringBuf = NULL; + + DataSize = GetLogicalDriveStringsA(0, NULL); + StringBuf = (CHAR *)malloc(DataSize + 1); + if (StringBuf == NULL) + { + return 1; + } + + GetLogicalDriveStringsA(DataSize, StringBuf); + + for (Pos = StringBuf; *Pos; Pos += strlen(Pos) + 1) + { + if (n < (int)Length && PhyDrive == GetPhyDriveByLogicalDrive(Pos[0])) + { + Log("%C: is belong to phydrive%d", Pos[0], PhyDrive); + DriveLetters[n++] = Pos[0]; + } + } + + free(StringBuf); + return 0; +} + +static HANDLE GetPhysicalHandle(int Drive, BOOLEAN bLockDrive, BOOLEAN bWriteAccess, BOOLEAN bWriteShare) +{ + int i; + DWORD dwSize; + DWORD LastError; + UINT64 EndTime; + HANDLE hDrive = INVALID_HANDLE_VALUE; + CHAR PhyDrive[128]; + CHAR DevPath[MAX_PATH] = { 0 }; + + safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", Drive); + + if (0 == QueryDosDeviceA(PhyDrive + 4, DevPath, sizeof(DevPath))) + { + Log("QueryDosDeviceA failed error:%u", GetLastError()); + strcpy_s(DevPath, sizeof(DevPath), "???"); + } + else + { + Log("QueryDosDeviceA success %s", DevPath); + } + + for (i = 0; i < DRIVE_ACCESS_RETRIES; i++) + { + // Try without FILE_SHARE_WRITE (unless specifically requested) so that + // we won't be bothered by the OS or other apps when we set up our data. + // However this means we might have to wait for an access gap... + // We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is + // required for enumeration. + hDrive = CreateFileA(PhyDrive, + GENERIC_READ | (bWriteAccess ? GENERIC_WRITE : 0), + FILE_SHARE_READ | (bWriteShare ? FILE_SHARE_WRITE : 0), + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, + NULL); + + LastError = GetLastError(); + Log("[%d] CreateFileA %s code:%u %p", i, PhyDrive, LastError, hDrive); + + if (hDrive != INVALID_HANDLE_VALUE) + { + break; + } + + if ((LastError != ERROR_SHARING_VIOLATION) && (LastError != ERROR_ACCESS_DENIED)) + { + break; + } + + if (i == 0) + { + Log("Waiting for access on %s [%s]...", PhyDrive, DevPath); + } + else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES / 3)) + { + // If we can't seem to get a hold of the drive for some time, try to enable FILE_SHARE_WRITE... + Log("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled..."); + bWriteShare = TRUE; + + // Try to report the process that is locking the drive + // We also use bit 6 as a flag to indicate that SearchProcess was called. + //access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40; + + } + Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); + } + + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Could not open %s %u", PhyDrive, LASTERR); + goto End; + } + + if (bWriteAccess) + { + Log("Opened %s for %s write access", PhyDrive, bWriteShare ? "shared" : "exclusive"); + } + + if (bLockDrive) + { + if (DeviceIoControl(hDrive, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwSize, NULL)) + { + Log("I/O boundary checks disabled"); + } + + EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT; + + do { + if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL)) + { + Log("FSCTL_LOCK_VOLUME success"); + goto End; + } + Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES); + } while (GetTickCount64() < EndTime); + + // If we reached this section, either we didn't manage to get a lock or the user cancelled + Log("Could not lock access to %s %u", PhyDrive, LASTERR); + + // See if we can report the processes are accessing the drive + //if (!IS_ERROR(FormatStatus) && (access_mask == 0)) + // access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE); + // Try to continue if the only access rights we saw were for read-only + //if ((access_mask & 0x07) != 0x01) + // safe_closehandle(hDrive); + + CHECK_CLOSE_HANDLE(hDrive); + } + +End: + + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Can get handle of %s, maybe some process control it.", DevPath); + } + + return hDrive; +} + +int GetPhyDriveByLogicalDrive(int DriveLetter) +{ + BOOL Ret; + DWORD dwSize; + HANDLE Handle; + VOLUME_DISK_EXTENTS DiskExtents; + CHAR PhyPath[128]; + + safe_sprintf(PhyPath, "\\\\.\\%C:", (CHAR)DriveLetter); + + Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (Handle == INVALID_HANDLE_VALUE) + { + Log("Could not open the disk<%s>, error:%u", PhyPath, LASTERR); + return -1; + } + + Ret = DeviceIoControl(Handle, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + NULL, + 0, + &DiskExtents, + (DWORD)(sizeof(DiskExtents)), + (LPDWORD)&dwSize, + NULL); + + if (!Ret || DiskExtents.NumberOfDiskExtents == 0) + { + Log("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed %s, error:%u", PhyPath, LASTERR); + CHECK_CLOSE_HANDLE(Handle); + return -1; + } + CHECK_CLOSE_HANDLE(Handle); + + Log("LogicalDrive:%s PhyDrive:%d Offset:%llu ExtentLength:%llu", + PhyPath, + DiskExtents.Extents[0].DiskNumber, + DiskExtents.Extents[0].StartingOffset.QuadPart, + DiskExtents.Extents[0].ExtentLength.QuadPart + ); + + return (int)DiskExtents.Extents[0].DiskNumber; +} + +int GetAllPhysicalDriveInfo(PHY_DRIVE_INFO *pDriveList, DWORD *pDriveCount) +{ + int i; + int Count; + int id; + int Letter = 'A'; + BOOL bRet; + DWORD dwBytes; + DWORD DriveCount = 0; + HANDLE Handle = INVALID_HANDLE_VALUE; + CHAR PhyDrive[128]; + PHY_DRIVE_INFO *CurDrive = pDriveList; + GET_LENGTH_INFORMATION LengthInfo; + STORAGE_PROPERTY_QUERY Query; + STORAGE_DESCRIPTOR_HEADER DevDescHeader; + STORAGE_DEVICE_DESCRIPTOR *pDevDesc; + int PhyDriveId[VENTOY_MAX_PHY_DRIVE]; + + Count = GetPhysicalDriveCount(); + + for (i = 0; i < Count && i < VENTOY_MAX_PHY_DRIVE; i++) + { + PhyDriveId[i] = i; + } + + dwBytes = GetLogicalDrives(); + Log("Logical Drives: 0x%x", dwBytes); + while (dwBytes) + { + if (dwBytes & 0x01) + { + id = GetPhyDriveByLogicalDrive(Letter); + Log("%C --> %d", Letter, id); + if (id >= 0) + { + for (i = 0; i < Count; i++) + { + if (PhyDriveId[i] == id) + { + break; + } + } + + if (i >= Count) + { + Log("Add phy%d to list", i); + PhyDriveId[Count] = id; + Count++; + } + } + } + + Letter++; + dwBytes >>= 1; + } + + for (i = 0; i < Count && DriveCount < VENTOY_MAX_PHY_DRIVE; i++) + { + CHECK_CLOSE_HANDLE(Handle); + + safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", PhyDriveId[i]); + Handle = CreateFileA(PhyDrive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + Log("Create file Handle:%p %s status:%u", Handle, PhyDrive, LASTERR); + + if (Handle == INVALID_HANDLE_VALUE) + { + continue; + } + + bRet = DeviceIoControl(Handle, + IOCTL_DISK_GET_LENGTH_INFO, NULL, + 0, + &LengthInfo, + sizeof(LengthInfo), + &dwBytes, + NULL); + if (!bRet) + { + Log("DeviceIoControl IOCTL_DISK_GET_LENGTH_INFO failed error:%u", LASTERR); + continue; + } + + Log("PHYSICALDRIVE%d size %llu bytes", i, (ULONGLONG)LengthInfo.Length.QuadPart); + + Query.PropertyId = StorageDeviceProperty; + Query.QueryType = PropertyStandardQuery; + + bRet = DeviceIoControl(Handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &Query, + sizeof(Query), + &DevDescHeader, + sizeof(STORAGE_DESCRIPTOR_HEADER), + &dwBytes, + NULL); + if (!bRet) + { + Log("DeviceIoControl1 error:%u dwBytes:%u", LASTERR, dwBytes); + continue; + } + + if (DevDescHeader.Size < sizeof(STORAGE_DEVICE_DESCRIPTOR)) + { + Log("Invalid DevDescHeader.Size:%u", DevDescHeader.Size); + continue; + } + + pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(DevDescHeader.Size); + if (!pDevDesc) + { + Log("failed to malloc error:%u len:%u", LASTERR, DevDescHeader.Size); + continue; + } + + bRet = DeviceIoControl(Handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &Query, + sizeof(Query), + pDevDesc, + DevDescHeader.Size, + &dwBytes, + NULL); + if (!bRet) + { + Log("DeviceIoControl2 error:%u dwBytes:%u", LASTERR, dwBytes); + free(pDevDesc); + continue; + } + + CurDrive->PhyDrive = i; + CurDrive->SizeInBytes = LengthInfo.Length.QuadPart; + CurDrive->DeviceType = pDevDesc->DeviceType; + CurDrive->RemovableMedia = pDevDesc->RemovableMedia; + CurDrive->BusType = pDevDesc->BusType; + + if (pDevDesc->VendorIdOffset) + { + safe_strcpy(CurDrive->VendorId, (char *)pDevDesc + pDevDesc->VendorIdOffset); + TrimString(CurDrive->VendorId); + } + + if (pDevDesc->ProductIdOffset) + { + safe_strcpy(CurDrive->ProductId, (char *)pDevDesc + pDevDesc->ProductIdOffset); + TrimString(CurDrive->ProductId); + } + + if (pDevDesc->ProductRevisionOffset) + { + safe_strcpy(CurDrive->ProductRev, (char *)pDevDesc + pDevDesc->ProductRevisionOffset); + TrimString(CurDrive->ProductRev); + } + + if (pDevDesc->SerialNumberOffset) + { + safe_strcpy(CurDrive->SerialNumber, (char *)pDevDesc + pDevDesc->SerialNumberOffset); + TrimString(CurDrive->SerialNumber); + } + + CurDrive++; + DriveCount++; + + free(pDevDesc); + + CHECK_CLOSE_HANDLE(Handle); + } + + for (i = 0, CurDrive = pDriveList; i < (int)DriveCount; i++, CurDrive++) + { + Log("PhyDrv:%d BusType:%-4s Removable:%u Size:%dGB(%llu) Name:%s %s", + CurDrive->PhyDrive, GetBusTypeString(CurDrive->BusType), CurDrive->RemovableMedia, + GetHumanReadableGBSize(CurDrive->SizeInBytes), CurDrive->SizeInBytes, + CurDrive->VendorId, CurDrive->ProductId); + } + + *pDriveCount = DriveCount; + + return 0; +} + + +static HANDLE g_FatPhyDrive; +static UINT64 g_Part2StartSec; +static int GetVentoyVersionFromFatFile(CHAR *VerBuf, size_t BufLen) +{ + int rc = 1; + int size = 0; + char *buf = NULL; + void *flfile = NULL; + + flfile = fl_fopen("/grub/grub.cfg", "rb"); + if (flfile) + { + fl_fseek(flfile, 0, SEEK_END); + size = (int)fl_ftell(flfile); + + fl_fseek(flfile, 0, SEEK_SET); + + buf = (char *)malloc(size + 1); + if (buf) + { + fl_fread(buf, 1, size, flfile); + buf[size] = 0; + + rc = 0; + sprintf_s(VerBuf, BufLen, "%s", ParseVentoyVersionFromString(buf)); + free(buf); + } + + fl_fclose(flfile); + } + + return rc; +} + +static int VentoyFatDiskRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount) +{ + DWORD dwSize; + BOOL bRet; + DWORD ReadSize; + LARGE_INTEGER liCurrentPosition; + + liCurrentPosition.QuadPart = Sector + g_Part2StartSec; + liCurrentPosition.QuadPart *= 512; + SetFilePointerEx(g_FatPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN); + + ReadSize = (DWORD)(SectorCount * 512); + + bRet = ReadFile(g_FatPhyDrive, Buffer, ReadSize, &dwSize, NULL); + if (bRet == FALSE || dwSize != ReadSize) + { + Log("ReadFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u\n", bRet, ReadSize, dwSize, LASTERR); + } + + return 1; +} + + +int GetVentoyVerInPhyDrive(const PHY_DRIVE_INFO *pDriveInfo, UINT64 Part2StartSector, CHAR *VerBuf, size_t BufLen) +{ + int rc = 0; + HANDLE hDrive; + + hDrive = GetPhysicalHandle(pDriveInfo->PhyDrive, FALSE, FALSE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + return 1; + } + + g_FatPhyDrive = hDrive; + g_Part2StartSec = Part2StartSector; + + Log("Parse FAT fs..."); + + fl_init(); + + if (0 == fl_attach_media(VentoyFatDiskRead, NULL)) + { + rc = GetVentoyVersionFromFatFile(VerBuf, BufLen); + } + else + { + rc = 1; + } + + fl_shutdown(); + + CHECK_CLOSE_HANDLE(hDrive); + + return rc; +} + + + + + +static unsigned int g_disk_unxz_len = 0; +static BYTE *g_part_img_pos = NULL; +static BYTE *g_part_img_buf[VENTOY_EFI_PART_SIZE / SIZE_1MB]; + + +static int VentoyFatMemRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount) +{ + uint32 i; + uint32 offset; + BYTE *MbBuf = NULL; + + for (i = 0; i < SectorCount; i++) + { + offset = (Sector + i) * 512; + + if (g_part_img_buf[1] == NULL) + { + MbBuf = g_part_img_buf[0] + offset; + memcpy(Buffer + i * 512, MbBuf, 512); + } + else + { + MbBuf = g_part_img_buf[offset / SIZE_1MB]; + memcpy(Buffer + i * 512, MbBuf + (offset % SIZE_1MB), 512); + } + } + + return 1; +} + + +static int VentoyFatMemWrite(uint32 Sector, uint8 *Buffer, uint32 SectorCount) +{ + uint32 i; + uint32 offset; + BYTE *MbBuf = NULL; + + for (i = 0; i < SectorCount; i++) + { + offset = (Sector + i) * 512; + + if (g_part_img_buf[1] == NULL) + { + MbBuf = g_part_img_buf[0] + offset; + memcpy(MbBuf, Buffer + i * 512, 512); + } + else + { + MbBuf = g_part_img_buf[offset / SIZE_1MB]; + memcpy(MbBuf + (offset % SIZE_1MB), Buffer + i * 512, 512); + } + } + + return 1; +} + +int VentoyProcSecureBoot(BOOL SecureBoot) +{ + int rc = 0; + int size; + char *filebuf = NULL; + void *file = NULL; + + Log("VentoyProcSecureBoot %d ...", SecureBoot); + + if (SecureBoot) + { + Log("Secure boot is enabled ..."); + return 0; + } + + fl_init(); + + if (0 == fl_attach_media(VentoyFatMemRead, VentoyFatMemWrite)) + { + file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb"); + Log("Open ventoy efi file %p ", file); + if (file) + { + fl_fseek(file, 0, SEEK_END); + size = (int)fl_ftell(file); + fl_fseek(file, 0, SEEK_SET); + + Log("ventoy efi file size %d ...", size); + + filebuf = (char *)malloc(size); + if (filebuf) + { + fl_fread(filebuf, 1, size, file); + } + + fl_fclose(file); + + Log("Now delete all efi files ..."); + fl_remove("/EFI/BOOT/BOOTX64.EFI"); + fl_remove("/EFI/BOOT/grubx64.efi"); + fl_remove("/EFI/BOOT/grubx64_real.efi"); + fl_remove("/EFI/BOOT/MokManager.efi"); + fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer"); + + file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb"); + Log("Open bootx64 efi file %p ", file); + if (file) + { + if (filebuf) + { + fl_fwrite(filebuf, 1, size, file); + } + + fl_fflush(file); + fl_fclose(file); + } + + if (filebuf) + { + free(filebuf); + } + } + } + else + { + rc = 1; + } + + fl_shutdown(); + + return rc; +} + + + +static int disk_xz_flush(void *src, unsigned int size) +{ + unsigned int i; + BYTE *buf = (BYTE *)src; + + for (i = 0; i < size; i++) + { + *g_part_img_pos = *buf++; + + g_disk_unxz_len++; + if ((g_disk_unxz_len % SIZE_1MB) == 0) + { + g_part_img_pos = g_part_img_buf[g_disk_unxz_len / SIZE_1MB]; + } + else + { + g_part_img_pos++; + } + } + + return (int)size; +} + +static void unxz_error(char *x) +{ + Log("%s", x); +} + +static BOOL TryWritePart2(HANDLE hDrive, UINT64 StartSectorId) +{ + BOOL bRet; + DWORD TrySize = 16 * 1024; + DWORD dwSize; + BYTE *Buffer = NULL; + unsigned char *data = NULL; + LARGE_INTEGER liCurrentPosition; + + liCurrentPosition.QuadPart = StartSectorId * 512; + SetFilePointerEx(hDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN); + + Buffer = malloc(TrySize); + + bRet = WriteFile(hDrive, Buffer, TrySize, &dwSize, NULL); + + free(Buffer); + + Log("Try write part2 bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); + + if (bRet && dwSize == TrySize) + { + return TRUE; + } + + return FALSE; +} + +static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) +{ + int i; + int rc = 0; + int len = 0; + int writelen = 0; + int partwrite = 0; + DWORD dwSize = 0; + BOOL bRet; + unsigned char *data = NULL; + LARGE_INTEGER liCurrentPosition; + LARGE_INTEGER liNewPosition; + + Log("FormatPart2Fat %llu...", StartSectorId); + + rc = ReadWholeFileToBuf(VENTOY_FILE_DISK_IMG, 0, (void **)&data, &len); + if (rc) + { + Log("Failed to read img file %p %u", data, len); + return 1; + } + + liCurrentPosition.QuadPart = StartSectorId * 512; + SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); + + Log("Set file pointer: %llu New pointer:%llu", liCurrentPosition.QuadPart, liNewPosition.QuadPart); + + memset(g_part_img_buf, 0, sizeof(g_part_img_buf)); + + g_part_img_buf[0] = (BYTE *)malloc(VENTOY_EFI_PART_SIZE); + if (g_part_img_buf[0]) + { + Log("Malloc whole img buffer success, now decompress ..."); + unxz(data, len, NULL, NULL, g_part_img_buf[0], &writelen, unxz_error); + + if (len == writelen) + { + Log("decompress finished success"); + + VentoyProcSecureBoot(g_SecureBoot); + + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + dwSize = 0; + bRet = WriteFile(hDrive, g_part_img_buf[0] + i * SIZE_1MB, SIZE_1MB, &dwSize, NULL); + Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); + + if (!bRet) + { + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); + } + } + else + { + rc = 1; + Log("decompress finished failed"); + goto End; + } + } + else + { + Log("Failed to malloc whole img size %u, now split it", VENTOY_EFI_PART_SIZE); + + partwrite = 1; + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + g_part_img_buf[i] = (BYTE *)malloc(SIZE_1MB); + if (g_part_img_buf[i] == NULL) + { + rc = 1; + goto End; + } + } + + Log("Malloc part img buffer success, now decompress ..."); + + g_part_img_pos = g_part_img_buf[0]; + + unxz(data, len, NULL, disk_xz_flush, NULL, NULL, unxz_error); + + if (g_disk_unxz_len == VENTOY_EFI_PART_SIZE) + { + Log("decompress finished success"); + + VentoyProcSecureBoot(g_SecureBoot); + + for (int i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + dwSize = 0; + bRet = WriteFile(hDrive, g_part_img_buf[i], SIZE_1MB, &dwSize, NULL); + Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); + + if (!bRet) + { + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); + } + } + else + { + rc = 1; + Log("decompress finished failed"); + goto End; + } + } + +End: + + if (data) free(data); + + if (partwrite) + { + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + if (g_part_img_buf[i]) free(g_part_img_buf[i]); + } + } + else + { + if (g_part_img_buf[0]) free(g_part_img_buf[0]); + } + + return rc; +} + +static int WriteGrubStage1ToPhyDrive(HANDLE hDrive, int PartStyle) +{ + int Len = 0; + int readLen = 0; + BOOL bRet; + DWORD dwSize; + BYTE *ImgBuf = NULL; + BYTE *RawBuf = NULL; + + Log("WriteGrubStage1ToPhyDrive ..."); + + RawBuf = (BYTE *)malloc(SIZE_1MB); + if (!RawBuf) + { + return 1; + } + + if (ReadWholeFileToBuf(VENTOY_FILE_STG1_IMG, 0, (void **)&ImgBuf, &Len)) + { + Log("Failed to read stage1 img"); + free(RawBuf); + return 1; + } + + unxz(ImgBuf, Len, NULL, NULL, RawBuf, &readLen, unxz_error); + + if (PartStyle) + { + Log("Write GPT stage1 ..."); + RawBuf[500] = 35;//update blocklist + SetFilePointer(hDrive, 512 * 34, NULL, FILE_BEGIN); + bRet = WriteFile(hDrive, RawBuf, SIZE_1MB - 512 * 34, &dwSize, NULL); + } + else + { + Log("Write MBR stage1 ..."); + SetFilePointer(hDrive, 512, NULL, FILE_BEGIN); + bRet = WriteFile(hDrive, RawBuf, SIZE_1MB - 512, &dwSize, NULL); + } + + Log("WriteFile Ret:%u dwSize:%u ErrCode:%u", bRet, dwSize, GetLastError()); + + free(RawBuf); + free(ImgBuf); + return 0; +} + + + +static int FormatPart1exFAT(UINT64 DiskSizeBytes) +{ + MKFS_PARM Option; + FRESULT Ret; + FATFS fs; + + Option.fmt = FM_EXFAT; + Option.n_fat = 1; + Option.align = 8; + Option.n_root = 1; + + // < 32GB select 32KB as cluster size + // > 32GB select 128KB as cluster size + if (DiskSizeBytes / 1024 / 1024 / 1024 <= 32) + { + Option.au_size = 32768; + } + else + { + Option.au_size = 131072; + } + + Log("Formatting Part1 exFAT ..."); + + Ret = f_mkfs(TEXT("0:"), &Option, 0, 8 * 1024 * 1024); + + if (FR_OK == Ret) + { + Log("Formatting Part1 exFAT success"); + + Ret = f_mount(&fs, TEXT("0:"), 1); + Log("mount part %d", Ret); + + if (FR_OK == Ret) + { + Ret = f_setlabel(TEXT("Ventoy")); + Log("f_setlabel %d", Ret); + + Ret = f_mount(0, TEXT("0:"), 1); + Log("umount part %d", Ret); + return 0; + } + else + { + Log("mount exfat failed %d", Ret); + return 1; + } + } + else + { + Log("Formatting Part1 exFAT failed"); + return 1; + } +} + + + +int ClearVentoyFromPhyDrive(HWND hWnd, PHY_DRIVE_INFO *pPhyDrive, char *pDrvLetter) +{ + int i; + int rc = 0; + int state = 0; + HANDLE hDrive; + DWORD dwSize; + BOOL bRet; + CHAR MountDrive; + CHAR DriveName[] = "?:\\"; + CHAR DriveLetters[MAX_PATH] = { 0 }; + LARGE_INTEGER liCurrentPosition; + char *pTmpBuf = NULL; + MBR_HEAD MBR; + + *pDrvLetter = 0; + + Log("ClearVentoyFromPhyDrive PhyDrive%d <<%s %s %dGB>>", + pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, + GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); + + Log("Lock disk for clean ............................. "); + + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to open physical disk"); + return 1; + } + + GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + + if (DriveLetters[0] == 0) + { + Log("No drive letter was assigned..."); + DriveName[0] = GetFirstUnusedDriveLetter(); + Log("GetFirstUnusedDriveLetter %C: ...", DriveName[0]); + } + else + { + // Unmount all mounted volumes that belong to this drive + // Do it in reverse so that we always end on the first volume letter + for (i = (int)strlen(DriveLetters); i > 0; i--) + { + DriveName[0] = DriveLetters[i - 1]; + bRet = DeleteVolumeMountPointA(DriveName); + Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, GetLastError()); + } + } + + MountDrive = DriveName[0]; + Log("Will use '%C:' as volume mountpoint", DriveName[0]); + + // It kind of blows, but we have to relinquish access to the physical drive + // for VDS to be able to delete the partitions that reside on it... + DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + CHECK_CLOSE_HANDLE(hDrive); + + PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); + + if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) + { + Log("Notice: Could not delete partitions: %u", GetLastError()); + } + + Log("Deleting all partitions ......................... OK"); + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); + + Log("Lock disk for write ............................. "); + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to GetPhysicalHandle for write."); + rc = 1; + goto End; + } + + // clear first and last 1MB space + pTmpBuf = malloc(SIZE_1MB); + if (!pTmpBuf) + { + Log("Failed to alloc memory."); + rc = 1; + goto End; + } + memset(pTmpBuf, 0, SIZE_1MB); + + SET_FILE_POS(512); + bRet = WriteFile(hDrive, pTmpBuf, SIZE_1MB - 512, &dwSize, NULL); + Log("Write fisrt 1MB ret:%d size:%u err:%d", bRet, dwSize, LASTERR); + if (!bRet) + { + rc = 1; + goto End; + } + + SET_FILE_POS(SIZE_1MB); + bRet = WriteFile(hDrive, pTmpBuf, SIZE_1MB, &dwSize, NULL); + Log("Write 2nd 1MB ret:%d size:%u err:%d", bRet, dwSize, LASTERR); + if (!bRet) + { + rc = 1; + goto End; + } + + SET_FILE_POS(0); + bRet = ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL); + Log("Read MBR ret:%d size:%u err:%d", bRet, dwSize, LASTERR); + if (!bRet) + { + rc = 1; + goto End; + } + + //clear boot code and partition table (reserved disk signature) + memset(MBR.BootCode, 0, 440); + memset(MBR.PartTbl, 0, sizeof(MBR.PartTbl)); + + VentoyFillLocation(pPhyDrive->SizeInBytes, 2048, (UINT32)(pPhyDrive->SizeInBytes / 512 - 2048), MBR.PartTbl); + + MBR.PartTbl[0].Active = 0x00; // bootable + MBR.PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS + + SET_FILE_POS(0); + bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); + Log("Write MBR ret:%d size:%u err:%d", bRet, dwSize, LASTERR); + if (!bRet) + { + rc = 1; + goto End; + } + + Log("Clear Ventoy successfully finished"); + + //Refresh Drive Layout + DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); + +End: + + PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME); + + if (pTmpBuf) + { + free(pTmpBuf); + } + + if (rc == 0) + { + Log("Mounting Ventoy Partition ....................... "); + Sleep(1000); + + state = 0; + memset(DriveLetters, 0, sizeof(DriveLetters)); + GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + Log("Logical drive letter after write ventoy: <%s>", DriveLetters); + + for (i = 0; i < sizeof(DriveLetters) && DriveLetters[i]; i++) + { + DriveName[0] = DriveLetters[i]; + Log("%s is ventoy part1, already mounted", DriveName); + state = 1; + } + + if (state != 1) + { + Log("need to mount ventoy part1..."); + if (0 == GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[0].StartSectorId, DriveLetters, sizeof(DriveLetters), FALSE)) + { + DriveName[0] = MountDrive; + bRet = SetVolumeMountPointA(DriveName, DriveLetters); + Log("SetVolumeMountPoint <%s> <%s> bRet:%u code:%u", DriveName, DriveLetters, bRet, GetLastError()); + + *pDrvLetter = MountDrive; + } + else + { + Log("Failed to find ventoy volume"); + } + } + + Log("OK\n"); + } + else + { + FindProcessOccupyDisk(hDrive, pPhyDrive); + } + + CHECK_CLOSE_HANDLE(hDrive); + return rc; +} + +int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle) +{ + int i; + int rc = 0; + int state = 0; + HANDLE hDrive; + DWORD dwSize; + BOOL bRet; + CHAR MountDrive; + CHAR DriveName[] = "?:\\"; + CHAR DriveLetters[MAX_PATH] = { 0 }; + MBR_HEAD MBR; + VTOY_GPT_INFO *pGptInfo = NULL; + + Log("InstallVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", + PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, + GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); + + if (PartStyle) + { + pGptInfo = malloc(sizeof(VTOY_GPT_INFO)); + memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO)); + } + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); + + VentoyFillMBR(pPhyDrive->SizeInBytes, &MBR, PartStyle);//also used to format 1st partition in GPT mode + if (PartStyle) + { + VentoyFillGpt(pPhyDrive->SizeInBytes, pGptInfo); + } + + Log("Lock disk for clean ............................. "); + + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to open physical disk"); + free(pGptInfo); + return 1; + } + + GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + + if (DriveLetters[0] == 0) + { + Log("No drive letter was assigned..."); + DriveName[0] = GetFirstUnusedDriveLetter(); + Log("GetFirstUnusedDriveLetter %C: ...", DriveName[0]); + } + else + { + // Unmount all mounted volumes that belong to this drive + // Do it in reverse so that we always end on the first volume letter + for (i = (int)strlen(DriveLetters); i > 0; i--) + { + DriveName[0] = DriveLetters[i - 1]; + bRet = DeleteVolumeMountPointA(DriveName); + Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, GetLastError()); + } + } + + MountDrive = DriveName[0]; + Log("Will use '%C:' as volume mountpoint", DriveName[0]); + + // It kind of blows, but we have to relinquish access to the physical drive + // for VDS to be able to delete the partitions that reside on it... + DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + CHECK_CLOSE_HANDLE(hDrive); + + PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); + + if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) + { + Log("Notice: Could not delete partitions: %u", GetLastError()); + } + + Log("Deleting all partitions ......................... OK"); + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); + + Log("Lock disk for write ............................. "); + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to GetPhysicalHandle for write."); + rc = 1; + goto End; + } + + //Refresh Drive Layout + DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); + + disk_io_set_param(hDrive, MBR.PartTbl[0].StartSectorId + MBR.PartTbl[0].SectorCount); + + PROGRESS_BAR_SET_POS(PT_FORMAT_PART1); + + if (PartStyle == 1 && pPhyDrive->PartStyle == 0) + { + Log("Wait for format part1 ..."); + Sleep(1000 * 5); + } + + Log("Formatting part1 exFAT ..."); + if (0 != FormatPart1exFAT(pPhyDrive->SizeInBytes)) + { + Log("FormatPart1exFAT failed."); + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_FORMAT_PART2); + Log("Writing part2 FAT img ..."); + if (0 != FormatPart2Fat(hDrive, MBR.PartTbl[1].StartSectorId)) + { + Log("FormatPart2Fat failed."); + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_WRITE_STG1_IMG); + Log("Writting Boot Image ............................. "); + if (WriteGrubStage1ToPhyDrive(hDrive, PartStyle) != 0) + { + Log("WriteGrubStage1ToPhyDrive failed."); + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_WRITE_PART_TABLE); + Log("Writting Partition Table ........................ "); + SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); + + if (PartStyle) + { + VTOY_GPT_HDR BackupHead; + LARGE_INTEGER liCurrentPosition; + + SET_FILE_POS(pPhyDrive->SizeInBytes - 512); + VentoyFillBackupGptHead(pGptInfo, &BackupHead); + if (!WriteFile(hDrive, &BackupHead, sizeof(VTOY_GPT_HDR), &dwSize, NULL)) + { + rc = 1; + Log("Write GPT Backup Head Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); + goto End; + } + + SET_FILE_POS(pPhyDrive->SizeInBytes - 512 * 33); + if (!WriteFile(hDrive, pGptInfo->PartTbl, sizeof(pGptInfo->PartTbl), &dwSize, NULL)) + { + rc = 1; + Log("Write GPT Backup Part Table Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); + goto End; + } + + SET_FILE_POS(0); + if (!WriteFile(hDrive, pGptInfo, sizeof(VTOY_GPT_INFO), &dwSize, NULL)) + { + rc = 1; + Log("Write GPT Info Failed, dwSize:%u (%u) ErrCode:%u", dwSize, sizeof(VTOY_GPT_INFO), GetLastError()); + goto End; + } + + Log("Write GPT Info OK ..."); + } + else + { + if (!WriteFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL)) + { + rc = 1; + Log("Write MBR Failed, dwSize:%u ErrCode:%u", dwSize, GetLastError()); + goto End; + } + Log("Write MBR OK ..."); + } + + + //Refresh Drive Layout + DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); + +End: + + PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME); + + if (rc == 0) + { + Log("Mounting Ventoy Partition ....................... "); + Sleep(1000); + + state = 0; + memset(DriveLetters, 0, sizeof(DriveLetters)); + GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + Log("Logical drive letter after write ventoy: <%s>", DriveLetters); + + for (i = 0; i < sizeof(DriveLetters) && DriveLetters[i]; i++) + { + DriveName[0] = DriveLetters[i]; + if (IsVentoyLogicalDrive(DriveName[0])) + { + Log("%s is ventoy part2, delete mountpoint", DriveName); + DeleteVolumeMountPointA(DriveName); + } + else + { + Log("%s is ventoy part1, already mounted", DriveName); + state = 1; + } + } + + if (state != 1) + { + Log("need to mount ventoy part1..."); + if (0 == GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[0].StartSectorId, DriveLetters, sizeof(DriveLetters), FALSE)) + { + DriveName[0] = MountDrive; + bRet = SetVolumeMountPointA(DriveName, DriveLetters); + Log("SetVolumeMountPoint <%s> <%s> bRet:%u code:%u", DriveName, DriveLetters, bRet, GetLastError()); + } + else + { + Log("Failed to find ventoy volume"); + } + } + Log("OK\n"); + } + else + { + FindProcessOccupyDisk(hDrive, pPhyDrive); + } + + if (pGptInfo) + { + free(pGptInfo); + } + + CHECK_CLOSE_HANDLE(hDrive); + return rc; +} + +int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) +{ + int i; + int rc = 0; + BOOL ForceMBR = FALSE; + HANDLE hVolume; + HANDLE hDrive; + DWORD Status; + DWORD dwSize; + BOOL bRet; + CHAR DriveName[] = "?:\\"; + CHAR DriveLetters[MAX_PATH] = { 0 }; + UINT64 StartSector; + UINT64 ReservedMB = 0; + MBR_HEAD BootImg; + MBR_HEAD MBR; + VTOY_GPT_INFO *pGptInfo = NULL; + + Log("UpdateVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", + pPhyDrive->PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, + GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); + + Log("Lock disk for umount ............................ "); + + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to open physical disk"); + return 1; + } + + if (pPhyDrive->PartStyle) + { + pGptInfo = malloc(sizeof(VTOY_GPT_INFO)); + if (!pGptInfo) + { + return 1; + } + + memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO)); + + // Read GPT Info + SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); + ReadFile(hDrive, pGptInfo, sizeof(VTOY_GPT_INFO), &dwSize, NULL); + + //MBR will be used to compare with local boot image + memcpy(&MBR, &pGptInfo->MBR, sizeof(MBR_HEAD)); + + StartSector = pGptInfo->PartTbl[1].StartLBA; + Log("GPT StartSector in PartTbl:%llu", (ULONGLONG)StartSector); + + ReservedMB = (pPhyDrive->SizeInBytes / 512 - (StartSector + VENTOY_EFI_PART_SIZE / 512) - 33) / 2048; + Log("GPT Reserved Disk Space:%llu MB", (ULONGLONG)ReservedMB); + } + else + { + // Read MBR + SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); + ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL); + + StartSector = MBR.PartTbl[1].StartSectorId; + Log("MBR StartSector in PartTbl:%llu", (ULONGLONG)StartSector); + + ReservedMB = (pPhyDrive->SizeInBytes / 512 - (StartSector + VENTOY_EFI_PART_SIZE / 512)) / 2048; + Log("MBR Reserved Disk Space:%llu MB", (ULONGLONG)ReservedMB); + } + + GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + + if (DriveLetters[0] == 0) + { + Log("No drive letter was assigned..."); + } + else + { + // Unmount all mounted volumes that belong to this drive + // Do it in reverse so that we always end on the first volume letter + for (i = (int)strlen(DriveLetters); i > 0; i--) + { + DriveName[0] = DriveLetters[i - 1]; + if (IsVentoyLogicalDrive(DriveName[0])) + { + Log("%s is ventoy logical drive", DriveName); + bRet = DeleteVolumeMountPointA(DriveName); + Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, LASTERR); + break; + } + } + } + + // It kind of blows, but we have to relinquish access to the physical drive + // for VDS to be able to delete the partitions that reside on it... + DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + CHECK_CLOSE_HANDLE(hDrive); + + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); + + Log("Lock disk for update ............................ "); + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to GetPhysicalHandle for write."); + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(PT_LOCK_VOLUME); + + Log("Lock volume for update .......................... "); + hVolume = INVALID_HANDLE_VALUE; + Status = GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[1].StartSectorId, DriveLetters, sizeof(DriveLetters), TRUE); + if (ERROR_SUCCESS == Status) + { + Log("Now lock and dismount volume <%s>", DriveLetters); + hVolume = CreateFileA(DriveLetters, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, + NULL); + + if (hVolume == INVALID_HANDLE_VALUE) + { + Log("Failed to create file volume, errcode:%u", LASTERR); + rc = 1; + goto End; + } + + bRet = DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + Log("FSCTL_LOCK_VOLUME bRet:%u code:%u", bRet, LASTERR); + + bRet = DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + Log("FSCTL_DISMOUNT_VOLUME bRet:%u code:%u", bRet, LASTERR); + } + else if (ERROR_NOT_FOUND == Status) + { + Log("Volume not found, maybe not supported"); + } + else + { + rc = 1; + goto End; + } + + + if (!TryWritePart2(hDrive, StartSector)) + { + ForceMBR = TRUE; + Log("Try write failed, now delete partition 2..."); + + CHECK_CLOSE_HANDLE(hDrive); + + Log("Now delete partition 2..."); + DeletePartitions(pPhyDrive->PhyDrive, TRUE); + + hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); + if (hDrive == INVALID_HANDLE_VALUE) + { + Log("Failed to GetPhysicalHandle for write."); + rc = 1; + goto End; + } + } + + + PROGRESS_BAR_SET_POS(PT_FORMAT_PART2); + + Log("Write Ventoy to disk ............................ "); + if (0 != FormatPart2Fat(hDrive, StartSector)) + { + rc = 1; + goto End; + } + + if (hVolume != INVALID_HANDLE_VALUE) + { + bRet = DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL); + Log("FSCTL_UNLOCK_VOLUME bRet:%u code:%u", bRet, LASTERR); + CHECK_CLOSE_HANDLE(hVolume); + } + + Log("Updating Boot Image ............................. "); + if (WriteGrubStage1ToPhyDrive(hDrive, pPhyDrive->PartStyle) != 0) + { + rc = 1; + goto End; + } + + // Boot Image + VentoyGetLocalBootImg(&BootImg); + + // Use Old UUID + memcpy(BootImg.BootCode + 0x180, MBR.BootCode + 0x180, 16); + if (pPhyDrive->PartStyle) + { + BootImg.BootCode[92] = 0x22; + } + + if (ForceMBR == FALSE && memcmp(BootImg.BootCode, MBR.BootCode, 440) == 0) + { + Log("Boot image has no difference, no need to write."); + } + else + { + Log("Boot image need to write %u.", ForceMBR); + + SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); + + memcpy(MBR.BootCode, BootImg.BootCode, 440); + bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); + Log("Write Boot Image ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR); + } + + if (pPhyDrive->PartStyle == 0) + { + if (0x00 == MBR.PartTbl[0].Active && 0x80 == MBR.PartTbl[1].Active) + { + Log("Need to chage 1st partition active and 2nd partition inactive."); + + MBR.PartTbl[0].Active = 0x80; + MBR.PartTbl[1].Active = 0x00; + + SetFilePointer(hDrive, 0, NULL, FILE_BEGIN); + bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL); + Log("Write NEW MBR ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR); + } + } + + //Refresh Drive Layout + DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL); + +End: + + if (rc == 0) + { + Log("OK"); + } + else + { + FindProcessOccupyDisk(hDrive, pPhyDrive); + } + + CHECK_CLOSE_HANDLE(hDrive); + + if (pGptInfo) + { + free(pGptInfo); + } + + return rc; +} + + diff --git a/Ventoy2Disk/Ventoy2Disk/Utility.c b/Ventoy2Disk/Ventoy2Disk/Utility.c index 586d2368..2b9327f1 100644 --- a/Ventoy2Disk/Ventoy2Disk/Utility.c +++ b/Ventoy2Disk/Ventoy2Disk/Utility.c @@ -1,803 +1,805 @@ -/****************************************************************************** - * Utility.c - * - * Copyright (c) 2020, longpanda - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ -#include -#include "Ventoy2Disk.h" - -void Log(const char *Fmt, ...) -{ - va_list Arg; - int Len = 0; - FILE *File = NULL; - SYSTEMTIME Sys; - char szBuf[1024]; - - GetLocalTime(&Sys); - Len += safe_sprintf(szBuf, - "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ", - Sys.wYear, Sys.wMonth, Sys.wDay, - Sys.wHour, Sys.wMinute, Sys.wSecond, - Sys.wMilliseconds); - - va_start(Arg, Fmt); - Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg); - va_end(Arg); - - //printf("%s\n", szBuf); - -#if 1 - fopen_s(&File, VENTOY_FILE_LOG, "a+"); - if (File) - { - fwrite(szBuf, 1, Len, File); - fwrite("\n", 1, 1, File); - fclose(File); - } -#endif - -} - -BOOL IsPathExist(BOOL Dir, const char *Fmt, ...) -{ - va_list Arg; - HANDLE hFile; - DWORD Attr; - CHAR FilePath[MAX_PATH]; - - va_start(Arg, Fmt); - vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg); - va_end(Arg); - - hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if (INVALID_HANDLE_VALUE == hFile) - { - return FALSE; - } - - CloseHandle(hFile); - - Attr = GetFileAttributesA(FilePath); - - if (Dir) - { - if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0) - { - return FALSE; - } - } - else - { - if (Attr & FILE_ATTRIBUTE_DIRECTORY) - { - return FALSE; - } - } - - return TRUE; -} - - -int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen) -{ - int FileSize; - FILE *File = NULL; - void *Data = NULL; - - fopen_s(&File, FileName, "rb"); - if (File == NULL) - { - Log("Failed to open file %s", FileName); - return 1; - } - - fseek(File, 0, SEEK_END); - FileSize = (int)ftell(File); - - Data = malloc(FileSize + ExtLen); - if (!Data) - { - fclose(File); - return 1; - } - - fseek(File, 0, SEEK_SET); - fread(Data, 1, FileSize, File); - - fclose(File); - - *Bufer = Data; - *BufLen = FileSize; - - return 0; -} - -const CHAR* GetLocalVentoyVersion(void) -{ - int rc; - int FileSize; - CHAR *Pos = NULL; - CHAR *Buf = NULL; - static CHAR LocalVersion[64] = { 0 }; - - if (LocalVersion[0] == 0) - { - rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize); - if (rc) - { - return ""; - } - Buf[FileSize] = 0; - - for (Pos = Buf; *Pos; Pos++) - { - if (*Pos == '\r' || *Pos == '\n') - { - *Pos = 0; - break; - } - } - - safe_sprintf(LocalVersion, "%s", Buf); - free(Buf); - } - - return LocalVersion; -} - -const CHAR* ParseVentoyVersionFromString(CHAR *Buf) -{ - CHAR *Pos = NULL; - CHAR *End = NULL; - static CHAR LocalVersion[64] = { 0 }; - - Pos = strstr(Buf, "VENTOY_VERSION="); - if (Pos) - { - Pos += strlen("VENTOY_VERSION="); - if (*Pos == '"') - { - Pos++; - } - - End = Pos; - while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n') - { - End++; - } - - *End = 0; - - safe_sprintf(LocalVersion, "%s", Pos); - return LocalVersion; - } - - return ""; -} - -BOOL IsWow64(void) -{ - typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); - LPFN_ISWOW64PROCESS fnIsWow64Process; - BOOL bIsWow64 = FALSE; - - fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); - if (NULL != fnIsWow64Process) - { - fnIsWow64Process(GetCurrentProcess(), &bIsWow64); - } - - return bIsWow64; -} - -void DumpWindowsVersion(void) -{ - int Bit; - BOOL WsVer; - DWORD Major, Minor; - ULONGLONG MajorEqual, MinorEqual; - OSVERSIONINFOEXA Ver1, Ver2; - const CHAR *Ver = NULL; - CHAR WinVer[256] = { 0 }; - - memset(&Ver1, 0, sizeof(Ver1)); - memset(&Ver2, 0, sizeof(Ver2)); - - Ver1.dwOSVersionInfoSize = sizeof(Ver1); - - // suppress the C4996 warning for GetVersionExA - #pragma warning(push) - #pragma warning(disable:4996) - if (!GetVersionExA((OSVERSIONINFOA *)&Ver1)) - { - memset(&Ver1, 0, sizeof(Ver1)); - Ver1.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - if (!GetVersionExA((OSVERSIONINFOA *)&Ver1)) - { - return; - } - } - #pragma warning(pop) - - if (Ver1.dwPlatformId == VER_PLATFORM_WIN32_NT) - { - if (Ver1.dwMajorVersion > 6 || (Ver1.dwMajorVersion == 6 && Ver1.dwMinorVersion >= 2)) - { - // GetVersionEx() has problem on some Windows version - - MajorEqual = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); - for (Major = Ver1.dwMajorVersion; Major <= 9; Major++) - { - memset(&Ver2, 0, sizeof(Ver2)); - Ver2.dwOSVersionInfoSize = sizeof(Ver2); - Ver2.dwMajorVersion = Major; - - if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual)) - { - continue; - } - - if (Ver1.dwMajorVersion < Major) - { - Ver1.dwMajorVersion = Major; - Ver1.dwMinorVersion = 0; - } - - MinorEqual = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); - for (Minor = Ver1.dwMinorVersion; Minor <= 9; Minor++) - { - memset(&Ver2, 0, sizeof(Ver2)); - - Ver2.dwOSVersionInfoSize = sizeof(Ver2); - Ver2.dwMinorVersion = Minor; - - if (!VerifyVersionInfoA(&Ver2, VER_MINORVERSION, MinorEqual)) - { - continue; - } - - Ver1.dwMinorVersion = Minor; - break; - } - - break; - } - } - - if (Ver1.dwMajorVersion <= 0xF && Ver1.dwMinorVersion <= 0xF) - { - WsVer = (Ver1.wProductType <= VER_NT_WORKSTATION); - switch ((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) - { - case 0x51: - { - Ver = "XP"; - break; - } - case 0x52: - { - Ver = GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003"; - break; - } - case 0x60: - { - Ver = WsVer ? "Vista" : "Server 2008"; - break; - } - case 0x61: - { - Ver = WsVer ? "7" : "Server 2008 R2"; - break; - } - case 0x62: - { - Ver = WsVer ? "8" : "Server 2012"; - break; - } - case 0x63: - { - Ver = WsVer ? "8.1" : "Server 2012 R2"; - break; - } - case 0x64: - { - Ver = WsVer ? "10 (Preview 1)" : "Server 10 (Preview 1)"; - break; - } - case 0xA0: - { - Ver = WsVer ? "10" : ((Ver1.dwBuildNumber > 15000) ? "Server 2019" : "Server 2016"); - break; - } - default: - { - Ver = "10 or later"; - break; - } - } - } - } - - Bit = IsWow64() ? 64 : 32; - - if (Ver1.wServicePackMinor) - { - safe_sprintf(WinVer, "Windows %s SP%u.%u %d-bit", Ver, Ver1.wServicePackMajor, Ver1.wServicePackMinor, Bit); - } - else if (Ver1.wServicePackMajor) - { - safe_sprintf(WinVer, "Windows %s SP%u %d-bit", Ver, Ver1.wServicePackMajor, Bit); - } - else - { - safe_sprintf(WinVer, "Windows %s %d-bit", Ver, Bit); - } - - if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62) - { - Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber); - } - else - { - Log("Windows Version : %s", WinVer); - } - - return; -} - -BOOL IsVentoyLogicalDrive(CHAR DriveLetter) -{ - int i; - CONST CHAR *Files[] = - { - "EFI\\BOOT\\BOOTX64.EFI", - "grub\\themes\\ventoy\\theme.txt", - "ventoy\\ventoy.cpio", - }; - - for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++) - { - if (!IsFileExist("%C:\\%s", DriveLetter, Files[i])) - { - return FALSE; - } - } - - return TRUE; -} - - -int VentoyFillLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table) -{ - BYTE Head; - BYTE Sector; - BYTE nSector = 63; - BYTE nHead = 8; - UINT32 Cylinder; - UINT32 EndSectorId; - - while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024) - { - nHead = (BYTE)nHead * 2; - } - - if (nHead == 0) - { - nHead = 255; - } - - Cylinder = StartSectorId / nSector / nHead; - Head = StartSectorId / nSector % nHead; - Sector = StartSectorId % nSector + 1; - - Table->StartHead = Head; - Table->StartSector = Sector; - Table->StartCylinder = Cylinder; - - EndSectorId = StartSectorId + SectorCount - 1; - Cylinder = EndSectorId / nSector / nHead; - Head = EndSectorId / nSector % nHead; - Sector = EndSectorId % nSector + 1; - - Table->EndHead = Head; - Table->EndSector = Sector; - Table->EndCylinder = Cylinder; - - Table->StartSectorId = StartSectorId; - Table->SectorCount = SectorCount; - - return 0; -} - -int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR, int PartStyle) -{ - GUID Guid; - int ReservedValue; - UINT32 DiskSignature; - UINT32 DiskSectorCount; - UINT32 PartSectorCount; - UINT32 PartStartSector; - UINT32 ReservedSector; - - VentoyGetLocalBootImg(pMBR); - - CoCreateGuid(&Guid); - - memcpy(&DiskSignature, &Guid, sizeof(UINT32)); - - Log("Disk signature: 0x%08x", DiskSignature); - - *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature; - - if (DiskSizeBytes / 512 > 0xFFFFFFFF) - { - DiskSectorCount = 0xFFFFFFFF; - } - else - { - DiskSectorCount = (UINT32)(DiskSizeBytes / 512); - } - - ReservedValue = GetReservedSpaceInMB(); - if (ReservedValue <= 0) - { - ReservedSector = 0; - } - else - { - ReservedSector = (UINT32)(ReservedValue * 2048); - } - - if (PartStyle) - { - ReservedSector += 33; // backup GPT part table - } - - Log("ReservedSector: %u", ReservedSector); - - //Part1 - PartStartSector = VENTOY_PART1_START_SECTOR; - PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector; - VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl); - - pMBR->PartTbl[0].Active = 0x80; // bootable - pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS - - //Part2 - PartStartSector += PartSectorCount; - PartSectorCount = VENTOY_EFI_PART_SIZE / 512; - VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1); - - pMBR->PartTbl[1].Active = 0x00; - pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition - - pMBR->Byte55 = 0x55; - pMBR->ByteAA = 0xAA; - - return 0; -} - - -static int VentoyFillProtectMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR) -{ - GUID Guid; - UINT32 DiskSignature; - UINT64 DiskSectorCount; - - VentoyGetLocalBootImg(pMBR); - - CoCreateGuid(&Guid); - - memcpy(&DiskSignature, &Guid, sizeof(UINT32)); - - Log("Disk signature: 0x%08x", DiskSignature); - - *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature; - - DiskSectorCount = DiskSizeBytes / 512 - 1; - if (DiskSectorCount > 0xFFFFFFFF) - { - DiskSectorCount = 0xFFFFFFFF; - } - - memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl)); - - pMBR->PartTbl[0].Active = 0x00; - pMBR->PartTbl[0].FsFlag = 0xee; // EE - - pMBR->PartTbl[0].StartHead = 0; - pMBR->PartTbl[0].StartSector = 1; - pMBR->PartTbl[0].StartCylinder = 0; - pMBR->PartTbl[0].EndHead = 254; - pMBR->PartTbl[0].EndSector = 63; - pMBR->PartTbl[0].EndCylinder = 1023; - - pMBR->PartTbl[0].StartSectorId = 1; - pMBR->PartTbl[0].SectorCount = (UINT32)DiskSectorCount; - - pMBR->Byte55 = 0x55; - pMBR->ByteAA = 0xAA; - - pMBR->BootCode[92] = 0x22; - - return 0; -} - - -int VentoyFillGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo) -{ - INT64 ReservedValue = 0; - UINT64 ReservedSector = 33; - UINT64 Part1SectorCount = 0; - UINT64 DiskSectorCount = DiskSizeBytes / 512; - VTOY_GPT_HDR *Head = &pInfo->Head; - VTOY_GPT_PART_TBL *Table = pInfo->PartTbl; - static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } }; - static GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; - //static GUID BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } }; - - VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR); - - ReservedValue = GetReservedSpaceInMB(); - if (ReservedValue > 0) - { - ReservedSector += ReservedValue * 2048; - } - - Part1SectorCount = DiskSectorCount - ReservedSector - (VENTOY_EFI_PART_SIZE / 512) - 2048; - - memcpy(Head->Signature, "EFI PART", 8); - Head->Version[2] = 0x01; - Head->Length = 92; - Head->Crc = 0; - Head->EfiStartLBA = 1; - Head->EfiBackupLBA = DiskSectorCount - 1; - Head->PartAreaStartLBA = 34; - Head->PartAreaEndLBA = DiskSectorCount - 34; - CoCreateGuid(&Head->DiskGuid); - Head->PartTblStartLBA = 2; - Head->PartTblTotNum = 128; - Head->PartTblEntryLen = 128; - - - memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID)); - CoCreateGuid(&(Table[0].PartGuid)); - Table[0].StartLBA = 2048; - Table[0].LastLBA = 2048 + Part1SectorCount - 1; - Table[0].Attr = 0; - memcpy(Table[0].Name, L"Ventoy", 6 * 2); - - memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID)); - CoCreateGuid(&(Table[1].PartGuid)); - Table[1].StartLBA = Table[0].LastLBA + 1; - Table[1].LastLBA = Table[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1; - Table[1].Attr = 1; - memcpy(Table[1].Name, L"VTOYEFI", 7 * 2); - -#if 0 - memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(GUID)); - CoCreateGuid(&(Table[2].PartGuid)); - Table[2].StartLBA = 34; - Table[2].LastLBA = 2047; - Table[2].Attr = 0; -#endif - - //Update CRC - Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl)); - Head->Crc = VentoyCrc32(Head, Head->Length); - - return 0; -} - -int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead) -{ - UINT64 LBA; - UINT64 BackupLBA; - - memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR)); - - LBA = pHead->EfiStartLBA; - BackupLBA = pHead->EfiBackupLBA; - - pHead->EfiStartLBA = BackupLBA; - pHead->EfiBackupLBA = LBA; - pHead->PartTblStartLBA = BackupLBA + 1 - 33; - - pHead->Crc = 0; - pHead->Crc = VentoyCrc32(pHead, pHead->Length); - - return 0; -} - -CHAR GetFirstUnusedDriveLetter(void) -{ - CHAR Letter = 'D'; - DWORD Drives = GetLogicalDrives(); - - Drives >>= 3; - while (Drives & 0x1) - { - Letter++; - Drives >>= 1; - } - - return Letter; -} - -const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type) -{ - switch (Type) - { - case BusTypeUnknown: return "unknown"; - case BusTypeScsi: return "SCSI"; - case BusTypeAtapi: return "Atapi"; - case BusTypeAta: return "ATA"; - case BusType1394: return "1394"; - case BusTypeSsa: return "SSA"; - case BusTypeFibre: return "Fibre"; - case BusTypeUsb: return "USB"; - case BusTypeRAID: return "RAID"; - case BusTypeiScsi: return "iSCSI"; - case BusTypeSas: return "SAS"; - case BusTypeSata: return "SATA"; - case BusTypeSd: return "SD"; - case BusTypeMmc: return "MMC"; - case BusTypeVirtual: return "Virtual"; - case BusTypeFileBackedVirtual: return "FileBackedVirtual"; - case BusTypeSpaces: return "Spaces"; - case BusTypeNvme: return "Nvme"; - } - return "unknown"; -} - -int VentoyGetLocalBootImg(MBR_HEAD *pMBR) -{ - int Len = 0; - BYTE *ImgBuf = NULL; - static int Loaded = 0; - static MBR_HEAD MBR; - - if (Loaded) - { - memcpy(pMBR, &MBR, 512); - return 0; - } - - if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len)) - { - Log("Copy boot img success"); - memcpy(pMBR, ImgBuf, 512); - free(ImgBuf); - - CoCreateGuid((GUID *)(pMBR->BootCode + 0x180)); - - memcpy(&MBR, pMBR, 512); - Loaded = 1; - - return 0; - } - else - { - Log("Copy boot img failed"); - return 1; - } -} - -int GetHumanReadableGBSize(UINT64 SizeBytes) -{ - int i; - int Pow2 = 1; - double Delta; - double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000; - - for (i = 0; i < 12; i++) - { - if (Pow2 > GB) - { - Delta = (Pow2 - GB) / Pow2; - } - else - { - Delta = (GB - Pow2) / Pow2; - } - - if (Delta < 0.05) - { - return Pow2; - } - - Pow2 <<= 1; - } - - return (int)GB; -} - -void TrimString(CHAR *String) -{ - CHAR *Pos1 = String; - CHAR *Pos2 = String; - size_t Len = strlen(String); - - while (Len > 0) - { - if (String[Len - 1] != ' ' && String[Len - 1] != '\t') - { - break; - } - String[Len - 1] = 0; - Len--; - } - - while (*Pos1 == ' ' || *Pos1 == '\t') - { - Pos1++; - } - - while (*Pos1) - { - *Pos2++ = *Pos1++; - } - *Pos2++ = 0; - - return; -} - -int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue) -{ - HKEY hKey; - DWORD Type; - DWORD Size; - LSTATUS lRet; - DWORD Value; - - lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey); - Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet); - - if (ERROR_SUCCESS == lRet) - { - Size = sizeof(Value); - lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size); - Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value); - - *pValue = Value; - RegCloseKey(hKey); - - return 0; - } - else - { - return 1; - } -} - -int GetPhysicalDriveCount(void) -{ - DWORD Value; - int Count = 0; - - if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0) - { - Count = (int)Value; - } - - Log("GetPhysicalDriveCount: %d", Count); - return Count; -} - - - +/****************************************************************************** + * Utility.c + * + * Copyright (c) 2020, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ +#include +#include "Ventoy2Disk.h" + +void Log(const char *Fmt, ...) +{ + va_list Arg; + int Len = 0; + FILE *File = NULL; + SYSTEMTIME Sys; + char szBuf[1024]; + + GetLocalTime(&Sys); + Len += safe_sprintf(szBuf, + "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ", + Sys.wYear, Sys.wMonth, Sys.wDay, + Sys.wHour, Sys.wMinute, Sys.wSecond, + Sys.wMilliseconds); + + va_start(Arg, Fmt); + Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg); + va_end(Arg); + + //printf("%s\n", szBuf); + +#if 1 + fopen_s(&File, VENTOY_FILE_LOG, "a+"); + if (File) + { + fwrite(szBuf, 1, Len, File); + fwrite("\n", 1, 1, File); + fclose(File); + } +#endif + +} + +BOOL IsPathExist(BOOL Dir, const char *Fmt, ...) +{ + va_list Arg; + HANDLE hFile; + DWORD Attr; + CHAR FilePath[MAX_PATH]; + + va_start(Arg, Fmt); + vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg); + va_end(Arg); + + hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (INVALID_HANDLE_VALUE == hFile) + { + return FALSE; + } + + CloseHandle(hFile); + + Attr = GetFileAttributesA(FilePath); + + if (Dir) + { + if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + return FALSE; + } + } + else + { + if (Attr & FILE_ATTRIBUTE_DIRECTORY) + { + return FALSE; + } + } + + return TRUE; +} + + +int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen) +{ + int FileSize; + FILE *File = NULL; + void *Data = NULL; + + fopen_s(&File, FileName, "rb"); + if (File == NULL) + { + Log("Failed to open file %s", FileName); + return 1; + } + + fseek(File, 0, SEEK_END); + FileSize = (int)ftell(File); + + Data = malloc(FileSize + ExtLen); + if (!Data) + { + fclose(File); + return 1; + } + + fseek(File, 0, SEEK_SET); + fread(Data, 1, FileSize, File); + + fclose(File); + + *Bufer = Data; + *BufLen = FileSize; + + return 0; +} + +const CHAR* GetLocalVentoyVersion(void) +{ + int rc; + int FileSize; + CHAR *Pos = NULL; + CHAR *Buf = NULL; + static CHAR LocalVersion[64] = { 0 }; + + if (LocalVersion[0] == 0) + { + rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize); + if (rc) + { + return ""; + } + Buf[FileSize] = 0; + + for (Pos = Buf; *Pos; Pos++) + { + if (*Pos == '\r' || *Pos == '\n') + { + *Pos = 0; + break; + } + } + + safe_sprintf(LocalVersion, "%s", Buf); + free(Buf); + } + + return LocalVersion; +} + +const CHAR* ParseVentoyVersionFromString(CHAR *Buf) +{ + CHAR *Pos = NULL; + CHAR *End = NULL; + static CHAR LocalVersion[64] = { 0 }; + + Pos = strstr(Buf, "VENTOY_VERSION="); + if (Pos) + { + Pos += strlen("VENTOY_VERSION="); + if (*Pos == '"') + { + Pos++; + } + + End = Pos; + while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n') + { + End++; + } + + *End = 0; + + safe_sprintf(LocalVersion, "%s", Pos); + return LocalVersion; + } + + return ""; +} + +BOOL IsWow64(void) +{ + typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process; + BOOL bIsWow64 = FALSE; + + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); + if (NULL != fnIsWow64Process) + { + fnIsWow64Process(GetCurrentProcess(), &bIsWow64); + } + + return bIsWow64; +} + +void DumpWindowsVersion(void) +{ + int Bit; + BOOL WsVer; + DWORD Major, Minor; + ULONGLONG MajorEqual, MinorEqual; + OSVERSIONINFOEXA Ver1, Ver2; + const CHAR *Ver = NULL; + CHAR WinVer[256] = { 0 }; + + memset(&Ver1, 0, sizeof(Ver1)); + memset(&Ver2, 0, sizeof(Ver2)); + + Ver1.dwOSVersionInfoSize = sizeof(Ver1); + + // suppress the C4996 warning for GetVersionExA + #pragma warning(push) + #pragma warning(disable:4996) + if (!GetVersionExA((OSVERSIONINFOA *)&Ver1)) + { + memset(&Ver1, 0, sizeof(Ver1)); + Ver1.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&Ver1)) + { + return; + } + } + #pragma warning(pop) + + if (Ver1.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if (Ver1.dwMajorVersion > 6 || (Ver1.dwMajorVersion == 6 && Ver1.dwMinorVersion >= 2)) + { + // GetVersionEx() has problem on some Windows version + + MajorEqual = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (Major = Ver1.dwMajorVersion; Major <= 9; Major++) + { + memset(&Ver2, 0, sizeof(Ver2)); + Ver2.dwOSVersionInfoSize = sizeof(Ver2); + Ver2.dwMajorVersion = Major; + + if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual)) + { + continue; + } + + if (Ver1.dwMajorVersion < Major) + { + Ver1.dwMajorVersion = Major; + Ver1.dwMinorVersion = 0; + } + + MinorEqual = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (Minor = Ver1.dwMinorVersion; Minor <= 9; Minor++) + { + memset(&Ver2, 0, sizeof(Ver2)); + + Ver2.dwOSVersionInfoSize = sizeof(Ver2); + Ver2.dwMinorVersion = Minor; + + if (!VerifyVersionInfoA(&Ver2, VER_MINORVERSION, MinorEqual)) + { + continue; + } + + Ver1.dwMinorVersion = Minor; + break; + } + + break; + } + } + + if (Ver1.dwMajorVersion <= 0xF && Ver1.dwMinorVersion <= 0xF) + { + WsVer = (Ver1.wProductType <= VER_NT_WORKSTATION); + switch ((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) + { + case 0x51: + { + Ver = "XP"; + break; + } + case 0x52: + { + Ver = GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003"; + break; + } + case 0x60: + { + Ver = WsVer ? "Vista" : "Server 2008"; + break; + } + case 0x61: + { + Ver = WsVer ? "7" : "Server 2008 R2"; + break; + } + case 0x62: + { + Ver = WsVer ? "8" : "Server 2012"; + break; + } + case 0x63: + { + Ver = WsVer ? "8.1" : "Server 2012 R2"; + break; + } + case 0x64: + { + Ver = WsVer ? "10 (Preview 1)" : "Server 10 (Preview 1)"; + break; + } + case 0xA0: + { + Ver = WsVer ? "10" : ((Ver1.dwBuildNumber > 15000) ? "Server 2019" : "Server 2016"); + break; + } + default: + { + Ver = "10 or later"; + break; + } + } + } + } + + Bit = IsWow64() ? 64 : 32; + + if (Ver1.wServicePackMinor) + { + safe_sprintf(WinVer, "Windows %s SP%u.%u %d-bit", Ver, Ver1.wServicePackMajor, Ver1.wServicePackMinor, Bit); + } + else if (Ver1.wServicePackMajor) + { + safe_sprintf(WinVer, "Windows %s SP%u %d-bit", Ver, Ver1.wServicePackMajor, Bit); + } + else + { + safe_sprintf(WinVer, "Windows %s %d-bit", Ver, Bit); + } + + if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62) + { + Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber); + } + else + { + Log("Windows Version : %s", WinVer); + } + + return; +} + +BOOL IsVentoyLogicalDrive(CHAR DriveLetter) +{ + int i; + CONST CHAR *Files[] = + { + "EFI\\BOOT\\BOOTX64.EFI", + "grub\\themes\\ventoy\\theme.txt", + "ventoy\\ventoy.cpio", + }; + + for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++) + { + if (!IsFileExist("%C:\\%s", DriveLetter, Files[i])) + { + return FALSE; + } + } + + return TRUE; +} + + +int VentoyFillLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table) +{ + BYTE Head; + BYTE Sector; + BYTE nSector = 63; + BYTE nHead = 8; + UINT32 Cylinder; + UINT32 EndSectorId; + + while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024) + { + nHead = (BYTE)nHead * 2; + } + + if (nHead == 0) + { + nHead = 255; + } + + Cylinder = StartSectorId / nSector / nHead; + Head = StartSectorId / nSector % nHead; + Sector = StartSectorId % nSector + 1; + + Table->StartHead = Head; + Table->StartSector = Sector; + Table->StartCylinder = Cylinder; + + EndSectorId = StartSectorId + SectorCount - 1; + Cylinder = EndSectorId / nSector / nHead; + Head = EndSectorId / nSector % nHead; + Sector = EndSectorId % nSector + 1; + + Table->EndHead = Head; + Table->EndSector = Sector; + Table->EndCylinder = Cylinder; + + Table->StartSectorId = StartSectorId; + Table->SectorCount = SectorCount; + + return 0; +} + +int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR, int PartStyle) +{ + GUID Guid; + int ReservedValue; + UINT32 DiskSignature; + UINT32 DiskSectorCount; + UINT32 PartSectorCount; + UINT32 PartStartSector; + UINT32 ReservedSector; + + VentoyGetLocalBootImg(pMBR); + + CoCreateGuid(&Guid); + + memcpy(&DiskSignature, &Guid, sizeof(UINT32)); + + Log("Disk signature: 0x%08x", DiskSignature); + + *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature; + + if (DiskSizeBytes / 512 > 0xFFFFFFFF) + { + DiskSectorCount = 0xFFFFFFFF; + } + else + { + DiskSectorCount = (UINT32)(DiskSizeBytes / 512); + } + + ReservedValue = GetReservedSpaceInMB(); + if (ReservedValue <= 0) + { + ReservedSector = 0; + } + else + { + ReservedSector = (UINT32)(ReservedValue * 2048); + } + + if (PartStyle) + { + ReservedSector += 33; // backup GPT part table + } + + Log("ReservedSector: %u", ReservedSector); + + //Part1 + PartStartSector = VENTOY_PART1_START_SECTOR; + PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector; + VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl); + + pMBR->PartTbl[0].Active = 0x80; // bootable + pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS + + //Part2 + PartStartSector += PartSectorCount; + PartSectorCount = VENTOY_EFI_PART_SIZE / 512; + VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1); + + pMBR->PartTbl[1].Active = 0x00; + pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition + + pMBR->Byte55 = 0x55; + pMBR->ByteAA = 0xAA; + + return 0; +} + + +static int VentoyFillProtectMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR) +{ + GUID Guid; + UINT32 DiskSignature; + UINT64 DiskSectorCount; + + VentoyGetLocalBootImg(pMBR); + + CoCreateGuid(&Guid); + + memcpy(&DiskSignature, &Guid, sizeof(UINT32)); + + Log("Disk signature: 0x%08x", DiskSignature); + + *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature; + + DiskSectorCount = DiskSizeBytes / 512 - 1; + if (DiskSectorCount > 0xFFFFFFFF) + { + DiskSectorCount = 0xFFFFFFFF; + } + + memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl)); + + pMBR->PartTbl[0].Active = 0x00; + pMBR->PartTbl[0].FsFlag = 0xee; // EE + + pMBR->PartTbl[0].StartHead = 0; + pMBR->PartTbl[0].StartSector = 1; + pMBR->PartTbl[0].StartCylinder = 0; + pMBR->PartTbl[0].EndHead = 254; + pMBR->PartTbl[0].EndSector = 63; + pMBR->PartTbl[0].EndCylinder = 1023; + + pMBR->PartTbl[0].StartSectorId = 1; + pMBR->PartTbl[0].SectorCount = (UINT32)DiskSectorCount; + + pMBR->Byte55 = 0x55; + pMBR->ByteAA = 0xAA; + + pMBR->BootCode[92] = 0x22; + + return 0; +} + + +int VentoyFillGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo) +{ + INT64 ReservedValue = 0; + UINT64 ReservedSector = 33; + UINT64 Part1SectorCount = 0; + UINT64 DiskSectorCount = DiskSizeBytes / 512; + VTOY_GPT_HDR *Head = &pInfo->Head; + VTOY_GPT_PART_TBL *Table = pInfo->PartTbl; + static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } }; + static GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; + static GUID BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } }; + + VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR); + + ReservedValue = GetReservedSpaceInMB(); + if (ReservedValue > 0) + { + ReservedSector += ReservedValue * 2048; + } + + Part1SectorCount = DiskSectorCount - ReservedSector - (VENTOY_EFI_PART_SIZE / 512) - 2048; + + memcpy(Head->Signature, "EFI PART", 8); + Head->Version[2] = 0x01; + Head->Length = 92; + Head->Crc = 0; + Head->EfiStartLBA = 1; + Head->EfiBackupLBA = DiskSectorCount - 1; + Head->PartAreaStartLBA = 34; + Head->PartAreaEndLBA = DiskSectorCount - 34; + CoCreateGuid(&Head->DiskGuid); + Head->PartTblStartLBA = 2; + Head->PartTblTotNum = 128; + Head->PartTblEntryLen = 128; + + + memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID)); + CoCreateGuid(&(Table[0].PartGuid)); + Table[0].StartLBA = 2048; + Table[0].LastLBA = 2048 + Part1SectorCount - 1; + Table[0].Attr = 0; + memcpy(Table[0].Name, L"Ventoy", 6 * 2); + + // to fix windows issue + //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID)); + memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(GUID)); + CoCreateGuid(&(Table[1].PartGuid)); + Table[1].StartLBA = Table[0].LastLBA + 1; + Table[1].LastLBA = Table[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1; + Table[1].Attr = 0xC000000000000001ULL; + memcpy(Table[1].Name, L"VTOYEFI", 7 * 2); + +#if 0 + memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(GUID)); + CoCreateGuid(&(Table[2].PartGuid)); + Table[2].StartLBA = 34; + Table[2].LastLBA = 2047; + Table[2].Attr = 0; +#endif + + //Update CRC + Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl)); + Head->Crc = VentoyCrc32(Head, Head->Length); + + return 0; +} + +int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead) +{ + UINT64 LBA; + UINT64 BackupLBA; + + memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR)); + + LBA = pHead->EfiStartLBA; + BackupLBA = pHead->EfiBackupLBA; + + pHead->EfiStartLBA = BackupLBA; + pHead->EfiBackupLBA = LBA; + pHead->PartTblStartLBA = BackupLBA + 1 - 33; + + pHead->Crc = 0; + pHead->Crc = VentoyCrc32(pHead, pHead->Length); + + return 0; +} + +CHAR GetFirstUnusedDriveLetter(void) +{ + CHAR Letter = 'D'; + DWORD Drives = GetLogicalDrives(); + + Drives >>= 3; + while (Drives & 0x1) + { + Letter++; + Drives >>= 1; + } + + return Letter; +} + +const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type) +{ + switch (Type) + { + case BusTypeUnknown: return "unknown"; + case BusTypeScsi: return "SCSI"; + case BusTypeAtapi: return "Atapi"; + case BusTypeAta: return "ATA"; + case BusType1394: return "1394"; + case BusTypeSsa: return "SSA"; + case BusTypeFibre: return "Fibre"; + case BusTypeUsb: return "USB"; + case BusTypeRAID: return "RAID"; + case BusTypeiScsi: return "iSCSI"; + case BusTypeSas: return "SAS"; + case BusTypeSata: return "SATA"; + case BusTypeSd: return "SD"; + case BusTypeMmc: return "MMC"; + case BusTypeVirtual: return "Virtual"; + case BusTypeFileBackedVirtual: return "FileBackedVirtual"; + case BusTypeSpaces: return "Spaces"; + case BusTypeNvme: return "Nvme"; + } + return "unknown"; +} + +int VentoyGetLocalBootImg(MBR_HEAD *pMBR) +{ + int Len = 0; + BYTE *ImgBuf = NULL; + static int Loaded = 0; + static MBR_HEAD MBR; + + if (Loaded) + { + memcpy(pMBR, &MBR, 512); + return 0; + } + + if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len)) + { + Log("Copy boot img success"); + memcpy(pMBR, ImgBuf, 512); + free(ImgBuf); + + CoCreateGuid((GUID *)(pMBR->BootCode + 0x180)); + + memcpy(&MBR, pMBR, 512); + Loaded = 1; + + return 0; + } + else + { + Log("Copy boot img failed"); + return 1; + } +} + +int GetHumanReadableGBSize(UINT64 SizeBytes) +{ + int i; + int Pow2 = 1; + double Delta; + double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000; + + for (i = 0; i < 12; i++) + { + if (Pow2 > GB) + { + Delta = (Pow2 - GB) / Pow2; + } + else + { + Delta = (GB - Pow2) / Pow2; + } + + if (Delta < 0.05) + { + return Pow2; + } + + Pow2 <<= 1; + } + + return (int)GB; +} + +void TrimString(CHAR *String) +{ + CHAR *Pos1 = String; + CHAR *Pos2 = String; + size_t Len = strlen(String); + + while (Len > 0) + { + if (String[Len - 1] != ' ' && String[Len - 1] != '\t') + { + break; + } + String[Len - 1] = 0; + Len--; + } + + while (*Pos1 == ' ' || *Pos1 == '\t') + { + Pos1++; + } + + while (*Pos1) + { + *Pos2++ = *Pos1++; + } + *Pos2++ = 0; + + return; +} + +int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue) +{ + HKEY hKey; + DWORD Type; + DWORD Size; + LSTATUS lRet; + DWORD Value; + + lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey); + Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet); + + if (ERROR_SUCCESS == lRet) + { + Size = sizeof(Value); + lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size); + Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value); + + *pValue = Value; + RegCloseKey(hKey); + + return 0; + } + else + { + return 1; + } +} + +int GetPhysicalDriveCount(void) +{ + DWORD Value; + int Count = 0; + + if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0) + { + Count = (int)Value; + } + + Log("GetPhysicalDriveCount: %d", Count); + return Count; +} + + + diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc index 03a4a003ff6786f74be1fe58bfdbfdc5ff5ef70b..b8d9b67f83e7c8c1c07d7a331cf34cd117441804 100644 GIT binary patch delta 39 vcmewsc_MOyfH1QOgYo3A(%zHfgy&4|kP+H!CGw7+(QNWvLGjHFD%&^!DBKPw delta 77 zcmX>R`7LsTfH1Q$gXQGo(%zHfgy(3wGDI*0GlVciGWamKGAJ;F19|a4Rs@g?Ven@N bVDJT!Koy(63bP6@8cklWA--8iWg71z( diff --git a/Ventoy2Disk/Ventoy2Disk/WinDialog.c b/Ventoy2Disk/Ventoy2Disk/WinDialog.c index 814ba2777615c631feaaf281d273399fbc8f896e..b72d22f4ef7396e6b7eb8699526d10e417cc8634 100644 GIT binary patch delta 45 zcmaEIgt_S`^M(gkIF%VP8PXUO7z{SwzLLzut;~?hkjIb$5}e$4R&{gSbvAAQe(VpQ delta 43 zcmV+`0M!42#RKHT1F+!Kv+mSh0+XIE5R>2=2$Rq(N|S)mAd`^J9+RNaD6=HkUJCCz B6=VPa diff --git a/vtoygpt/build.sh b/vtoygpt/build.sh new file mode 100644 index 00000000..a8c3f7ed --- /dev/null +++ b/vtoygpt/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +rm -f vtoytool/00/* + +/opt/diet64/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64 vtoygpt.c crc32.c -o vtoygpt_64 +/opt/diet32/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64 -m32 vtoygpt.c crc32.c -o vtoygpt_32 + +#gcc -D_FILE_OFFSET_BITS=64 -static -Wall vtoygpt.c -o vtoytool_64 +#gcc -D_FILE_OFFSET_BITS=64 -Wall -m32 vtoygpt.c -o vtoytool_32 + +if [ -e vtoygpt_64 ] && [ -e vtoygpt_32 ]; then + echo -e '\n############### SUCCESS ###############\n' + mv vtoygpt_64 ../INSTALL/tool/ + mv vtoygpt_32 ../INSTALL/tool/ +else + echo -e '\n############### FAILED ################\n' + exit 1 +fi + diff --git a/vtoygpt/crc32.c b/vtoygpt/crc32.c new file mode 100644 index 00000000..354372b8 --- /dev/null +++ b/vtoygpt/crc32.c @@ -0,0 +1,315 @@ +/****************************************************************************** + * vtoygpt.c ---- ventoy gpt util + * + * Copyright (c) 2020, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VOID void +#define CHAR char +#define UINT64 unsigned long long +#define UINT32 unsigned int +#define UINT16 unsigned short +#define CHAR16 unsigned short +#define UINT8 unsigned char + +UINT32 g_crc_table[256] = { + 0x00000000, + 0x77073096, + 0xEE0E612C, + 0x990951BA, + 0x076DC419, + 0x706AF48F, + 0xE963A535, + 0x9E6495A3, + 0x0EDB8832, + 0x79DCB8A4, + 0xE0D5E91E, + 0x97D2D988, + 0x09B64C2B, + 0x7EB17CBD, + 0xE7B82D07, + 0x90BF1D91, + 0x1DB71064, + 0x6AB020F2, + 0xF3B97148, + 0x84BE41DE, + 0x1ADAD47D, + 0x6DDDE4EB, + 0xF4D4B551, + 0x83D385C7, + 0x136C9856, + 0x646BA8C0, + 0xFD62F97A, + 0x8A65C9EC, + 0x14015C4F, + 0x63066CD9, + 0xFA0F3D63, + 0x8D080DF5, + 0x3B6E20C8, + 0x4C69105E, + 0xD56041E4, + 0xA2677172, + 0x3C03E4D1, + 0x4B04D447, + 0xD20D85FD, + 0xA50AB56B, + 0x35B5A8FA, + 0x42B2986C, + 0xDBBBC9D6, + 0xACBCF940, + 0x32D86CE3, + 0x45DF5C75, + 0xDCD60DCF, + 0xABD13D59, + 0x26D930AC, + 0x51DE003A, + 0xC8D75180, + 0xBFD06116, + 0x21B4F4B5, + 0x56B3C423, + 0xCFBA9599, + 0xB8BDA50F, + 0x2802B89E, + 0x5F058808, + 0xC60CD9B2, + 0xB10BE924, + 0x2F6F7C87, + 0x58684C11, + 0xC1611DAB, + 0xB6662D3D, + 0x76DC4190, + 0x01DB7106, + 0x98D220BC, + 0xEFD5102A, + 0x71B18589, + 0x06B6B51F, + 0x9FBFE4A5, + 0xE8B8D433, + 0x7807C9A2, + 0x0F00F934, + 0x9609A88E, + 0xE10E9818, + 0x7F6A0DBB, + 0x086D3D2D, + 0x91646C97, + 0xE6635C01, + 0x6B6B51F4, + 0x1C6C6162, + 0x856530D8, + 0xF262004E, + 0x6C0695ED, + 0x1B01A57B, + 0x8208F4C1, + 0xF50FC457, + 0x65B0D9C6, + 0x12B7E950, + 0x8BBEB8EA, + 0xFCB9887C, + 0x62DD1DDF, + 0x15DA2D49, + 0x8CD37CF3, + 0xFBD44C65, + 0x4DB26158, + 0x3AB551CE, + 0xA3BC0074, + 0xD4BB30E2, + 0x4ADFA541, + 0x3DD895D7, + 0xA4D1C46D, + 0xD3D6F4FB, + 0x4369E96A, + 0x346ED9FC, + 0xAD678846, + 0xDA60B8D0, + 0x44042D73, + 0x33031DE5, + 0xAA0A4C5F, + 0xDD0D7CC9, + 0x5005713C, + 0x270241AA, + 0xBE0B1010, + 0xC90C2086, + 0x5768B525, + 0x206F85B3, + 0xB966D409, + 0xCE61E49F, + 0x5EDEF90E, + 0x29D9C998, + 0xB0D09822, + 0xC7D7A8B4, + 0x59B33D17, + 0x2EB40D81, + 0xB7BD5C3B, + 0xC0BA6CAD, + 0xEDB88320, + 0x9ABFB3B6, + 0x03B6E20C, + 0x74B1D29A, + 0xEAD54739, + 0x9DD277AF, + 0x04DB2615, + 0x73DC1683, + 0xE3630B12, + 0x94643B84, + 0x0D6D6A3E, + 0x7A6A5AA8, + 0xE40ECF0B, + 0x9309FF9D, + 0x0A00AE27, + 0x7D079EB1, + 0xF00F9344, + 0x8708A3D2, + 0x1E01F268, + 0x6906C2FE, + 0xF762575D, + 0x806567CB, + 0x196C3671, + 0x6E6B06E7, + 0xFED41B76, + 0x89D32BE0, + 0x10DA7A5A, + 0x67DD4ACC, + 0xF9B9DF6F, + 0x8EBEEFF9, + 0x17B7BE43, + 0x60B08ED5, + 0xD6D6A3E8, + 0xA1D1937E, + 0x38D8C2C4, + 0x4FDFF252, + 0xD1BB67F1, + 0xA6BC5767, + 0x3FB506DD, + 0x48B2364B, + 0xD80D2BDA, + 0xAF0A1B4C, + 0x36034AF6, + 0x41047A60, + 0xDF60EFC3, + 0xA867DF55, + 0x316E8EEF, + 0x4669BE79, + 0xCB61B38C, + 0xBC66831A, + 0x256FD2A0, + 0x5268E236, + 0xCC0C7795, + 0xBB0B4703, + 0x220216B9, + 0x5505262F, + 0xC5BA3BBE, + 0xB2BD0B28, + 0x2BB45A92, + 0x5CB36A04, + 0xC2D7FFA7, + 0xB5D0CF31, + 0x2CD99E8B, + 0x5BDEAE1D, + 0x9B64C2B0, + 0xEC63F226, + 0x756AA39C, + 0x026D930A, + 0x9C0906A9, + 0xEB0E363F, + 0x72076785, + 0x05005713, + 0x95BF4A82, + 0xE2B87A14, + 0x7BB12BAE, + 0x0CB61B38, + 0x92D28E9B, + 0xE5D5BE0D, + 0x7CDCEFB7, + 0x0BDBDF21, + 0x86D3D2D4, + 0xF1D4E242, + 0x68DDB3F8, + 0x1FDA836E, + 0x81BE16CD, + 0xF6B9265B, + 0x6FB077E1, + 0x18B74777, + 0x88085AE6, + 0xFF0F6A70, + 0x66063BCA, + 0x11010B5C, + 0x8F659EFF, + 0xF862AE69, + 0x616BFFD3, + 0x166CCF45, + 0xA00AE278, + 0xD70DD2EE, + 0x4E048354, + 0x3903B3C2, + 0xA7672661, + 0xD06016F7, + 0x4969474D, + 0x3E6E77DB, + 0xAED16A4A, + 0xD9D65ADC, + 0x40DF0B66, + 0x37D83BF0, + 0xA9BCAE53, + 0xDEBB9EC5, + 0x47B2CF7F, + 0x30B5FFE9, + 0xBDBDF21C, + 0xCABAC28A, + 0x53B39330, + 0x24B4A3A6, + 0xBAD03605, + 0xCDD70693, + 0x54DE5729, + 0x23D967BF, + 0xB3667A2E, + 0xC4614AB8, + 0x5D681B02, + 0x2A6F2B94, + 0xB40BBE37, + 0xC30C8EA1, + 0x5A05DF1B, + 0x2D02EF8D +}; + +UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length) +{ + UINT32 i; + UINT8 *Ptr = Buffer; + UINT32 Crc = 0xFFFFFFFF; + + for (i = 0; i < Length; i++, Ptr++) + { + Crc = (Crc >> 8) ^ g_crc_table[(UINT8) Crc ^ *Ptr]; + } + + return Crc ^ 0xffffffff; +} + diff --git a/vtoygpt/vtoygpt.c b/vtoygpt/vtoygpt.c new file mode 100644 index 00000000..705f750d --- /dev/null +++ b/vtoygpt/vtoygpt.c @@ -0,0 +1,320 @@ +/****************************************************************************** + * vtoygpt.c ---- ventoy gpt util + * + * Copyright (c) 2020, longpanda + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VOID void +#define CHAR char +#define UINT64 unsigned long long +#define UINT32 unsigned int +#define UINT16 unsigned short +#define CHAR16 unsigned short +#define UINT8 unsigned char + +UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length); + +#define COMPILE_ASSERT(expr) extern char __compile_assert[(expr) ? 1 : -1] + +#pragma pack(1) + +typedef struct PART_TABLE +{ + UINT8 Active; + + UINT8 StartHead; + UINT16 StartSector : 6; + UINT16 StartCylinder : 10; + + UINT8 FsFlag; + + UINT8 EndHead; + UINT16 EndSector : 6; + UINT16 EndCylinder : 10; + + UINT32 StartSectorId; + UINT32 SectorCount; +}PART_TABLE; + +typedef struct MBR_HEAD +{ + UINT8 BootCode[446]; + PART_TABLE PartTbl[4]; + UINT8 Byte55; + UINT8 ByteAA; +}MBR_HEAD; + +typedef struct GUID +{ + UINT32 data1; + UINT16 data2; + UINT16 data3; + UINT8 data4[8]; +}GUID; + +typedef struct VTOY_GPT_HDR +{ + CHAR Signature[8]; /* EFI PART */ + UINT8 Version[4]; + UINT32 Length; + UINT32 Crc; + UINT8 Reserved1[4]; + UINT64 EfiStartLBA; + UINT64 EfiBackupLBA; + UINT64 PartAreaStartLBA; + UINT64 PartAreaEndLBA; + GUID DiskGuid; + UINT64 PartTblStartLBA; + UINT32 PartTblTotNum; + UINT32 PartTblEntryLen; + UINT32 PartTblCrc; + UINT8 Reserved2[420]; +}VTOY_GPT_HDR; + +COMPILE_ASSERT(sizeof(VTOY_GPT_HDR) == 512); + +typedef struct VTOY_GPT_PART_TBL +{ + GUID PartType; + GUID PartGuid; + UINT64 StartLBA; + UINT64 LastLBA; + UINT64 Attr; + CHAR16 Name[36]; +}VTOY_GPT_PART_TBL; +COMPILE_ASSERT(sizeof(VTOY_GPT_PART_TBL) == 128); + +typedef struct VTOY_GPT_INFO +{ + MBR_HEAD MBR; + VTOY_GPT_HDR Head; + VTOY_GPT_PART_TBL PartTbl[128]; +}VTOY_GPT_INFO; + +typedef struct VTOY_BK_GPT_INFO +{ + VTOY_GPT_PART_TBL PartTbl[128]; + VTOY_GPT_HDR Head; +}VTOY_BK_GPT_INFO; + +COMPILE_ASSERT(sizeof(VTOY_GPT_INFO) == 512 * 34); +COMPILE_ASSERT(sizeof(VTOY_BK_GPT_INFO) == 512 * 33); + +#pragma pack() + +void DumpGuid(const char *prefix, GUID *guid) +{ + printf("%s: %08x-%04x-%04x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", + prefix, + guid->data1, guid->data2, guid->data3, + guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], + guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7] + ); +} + +void DumpHead(VTOY_GPT_HDR *pHead) +{ + UINT32 CrcRead; + UINT32 CrcCalc; + + printf("Signature:<%s>\n", pHead->Signature); + printf("Version:<%02x %02x %02x %02x>\n", pHead->Version[0], pHead->Version[1], pHead->Version[2], pHead->Version[3]); + printf("Length:%u\n", pHead->Length); + printf("Crc:0x%08x\n", pHead->Crc); + printf("EfiStartLBA:%lu\n", pHead->EfiStartLBA); + printf("EfiBackupLBA:%lu\n", pHead->EfiBackupLBA); + printf("PartAreaStartLBA:%lu\n", pHead->PartAreaStartLBA); + printf("PartAreaEndLBA:%lu\n", pHead->PartAreaEndLBA); + DumpGuid("DiskGuid", &pHead->DiskGuid); + + printf("PartTblStartLBA:%lu\n", pHead->PartTblStartLBA); + printf("PartTblTotNum:%u\n", pHead->PartTblTotNum); + printf("PartTblEntryLen:%u\n", pHead->PartTblEntryLen); + printf("PartTblCrc:0x%08x\n", pHead->PartTblCrc); + + CrcRead = pHead->Crc; + pHead->Crc = 0; + CrcCalc = VtoyCrc32(pHead, pHead->Length); + + if (CrcCalc != CrcRead) + { + printf("Head CRC Check Failed\n"); + } + else + { + printf("Head CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead); + } + + CrcRead = pHead->PartTblCrc; + CrcCalc = VtoyCrc32(pHead + 1, pHead->PartTblEntryLen * pHead->PartTblTotNum); + if (CrcCalc != CrcRead) + { + printf("Part Table CRC Check Failed\n"); + } + else + { + printf("Part Table CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead); + } +} + +void DumpPartTable(VTOY_GPT_PART_TBL *Tbl) +{ + int i; + + DumpGuid("PartType", &Tbl->PartType); + DumpGuid("PartGuid", &Tbl->PartGuid); + printf("StartLBA:%lu\n", Tbl->StartLBA); + printf("LastLBA:%lu\n", Tbl->LastLBA); + printf("Attr:0x%lx\n", Tbl->Attr); + printf("Name:"); + + for (i = 0; i < 36 && Tbl->Name[i]; i++) + { + printf("%c", (CHAR)(Tbl->Name[i])); + } + printf("\n"); +} + +void DumpMBR(MBR_HEAD *pMBR) +{ + int i; + + for (i = 0; i < 4; i++) + { + printf("=========== Partition Table %d ============\n", i + 1); + printf("PartTbl.Active = 0x%x\n", pMBR->PartTbl[i].Active); + printf("PartTbl.FsFlag = 0x%x\n", pMBR->PartTbl[i].FsFlag); + printf("PartTbl.StartSectorId = %u\n", pMBR->PartTbl[i].StartSectorId); + printf("PartTbl.SectorCount = %u\n", pMBR->PartTbl[i].SectorCount); + printf("PartTbl.StartHead = %u\n", pMBR->PartTbl[i].StartHead); + printf("PartTbl.StartSector = %u\n", pMBR->PartTbl[i].StartSector); + printf("PartTbl.StartCylinder = %u\n", pMBR->PartTbl[i].StartCylinder); + printf("PartTbl.EndHead = %u\n", pMBR->PartTbl[i].EndHead); + printf("PartTbl.EndSector = %u\n", pMBR->PartTbl[i].EndSector); + printf("PartTbl.EndCylinder = %u\n", pMBR->PartTbl[i].EndCylinder); + } +} + +int DumpGptInfo(VTOY_GPT_INFO *pGptInfo) +{ + int i; + + DumpMBR(&pGptInfo->MBR); + DumpHead(&pGptInfo->Head); + + for (i = 0; i < 128; i++) + { + if (pGptInfo->PartTbl[i].StartLBA == 0) + { + break; + } + + printf("=====Part %d=====\n", i); + DumpPartTable(pGptInfo->PartTbl + i); + } + + return 0; +} + +#define VENTOY_EFI_PART_ATTR 0xC000000000000001ULL + +int main(int argc, const char **argv) +{ + int i; + int fd; + UINT64 DiskSize; + CHAR16 *Name = NULL; + VTOY_GPT_INFO *pMainGptInfo = NULL; + VTOY_BK_GPT_INFO *pBackGptInfo = NULL; + + if (argc != 3) + { + printf("usage: vtoygpt -f /dev/sdb\n"); + return 1; + } + + fd = open(argv[2], O_RDWR); + if (fd < 0) + { + printf("Failed to open %s\n", argv[2]); + return 1; + } + + pMainGptInfo = malloc(sizeof(VTOY_GPT_INFO)); + pBackGptInfo = malloc(sizeof(VTOY_BK_GPT_INFO)); + if (NULL == pMainGptInfo || NULL == pBackGptInfo) + { + close(fd); + return 1; + } + + read(fd, pMainGptInfo, sizeof(VTOY_GPT_INFO)); + + if (argv[1][0] == '-' && argv[1][1] == 'd') + { + DumpGptInfo(pMainGptInfo); + } + else + { + DiskSize = lseek(fd, 0, SEEK_END); + lseek(fd, DiskSize - 33 * 512, SEEK_SET); + read(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO)); + + Name = pMainGptInfo->PartTbl[1].Name; + if (Name[0] == 'V' && Name[1] == 'T' && Name[2] == 'O' && Name[3] == 'Y') + { + pMainGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR; + pMainGptInfo->Head.PartTblCrc = VtoyCrc32(pMainGptInfo->PartTbl, sizeof(pMainGptInfo->PartTbl)); + pMainGptInfo->Head.Crc = 0; + pMainGptInfo->Head.Crc = VtoyCrc32(&pMainGptInfo->Head, pMainGptInfo->Head.Length); + + pBackGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR; + pBackGptInfo->Head.PartTblCrc = VtoyCrc32(pBackGptInfo->PartTbl, sizeof(pBackGptInfo->PartTbl)); + pBackGptInfo->Head.Crc = 0; + pBackGptInfo->Head.Crc = VtoyCrc32(&pBackGptInfo->Head, pBackGptInfo->Head.Length); + + lseek(fd, 512, SEEK_SET); + write(fd, (UINT8 *)pMainGptInfo + 512, sizeof(VTOY_GPT_INFO) - 512); + + lseek(fd, DiskSize - 33 * 512, SEEK_SET); + write(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO)); + + fsync(fd); + } + } + + free(pMainGptInfo); + free(pBackGptInfo); + close(fd); + + return 0; +} +