From 1473be0e4cc3127463a0ee4796893b9813b40e16 Mon Sep 17 00:00:00 2001 From: longpanda Date: Sat, 16 Oct 2021 23:34:20 +0800 Subject: [PATCH] Improve the success rate of Ventoy2Disk.exe installation and upgrade --- INSTALL/Ventoy2Disk.exe | Bin 321536 -> 329216 bytes INSTALL/grub/grub.cfg | 4 + Ventoy2Disk/Ventoy2Disk/DiskService.h | 57 ++ .../Ventoy2Disk/DiskService_diskpart.c | 84 +++ Ventoy2Disk/Ventoy2Disk/DiskService_vds.c | 629 ++++++++++++++++++ Ventoy2Disk/Ventoy2Disk/DiskService_wmsa.c | 27 + Ventoy2Disk/Ventoy2Disk/PhyDrive.c | 618 +++++++---------- Ventoy2Disk/Ventoy2Disk/Utility.c | 441 ++++++++---- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c | 18 +- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h | 10 +- Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc | Bin 12238 -> 12426 bytes Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj | 4 + .../Ventoy2Disk/Ventoy2Disk.vcxproj.filters | 12 + Ventoy2Disk/Ventoy2Disk/WinDialog.c | Bin 73636 -> 76412 bytes Ventoy2Disk/Ventoy2Disk/resource.h | Bin 4662 -> 4754 bytes 15 files changed, 1401 insertions(+), 503 deletions(-) create mode 100644 Ventoy2Disk/Ventoy2Disk/DiskService.h create mode 100644 Ventoy2Disk/Ventoy2Disk/DiskService_diskpart.c create mode 100644 Ventoy2Disk/Ventoy2Disk/DiskService_vds.c create mode 100644 Ventoy2Disk/Ventoy2Disk/DiskService_wmsa.c diff --git a/INSTALL/Ventoy2Disk.exe b/INSTALL/Ventoy2Disk.exe index d207ffdb7a360cdda4358c653e0a8ad3bfa692b2..90b024faac71d0ca024deece66e130127409a3f6 100644 GIT binary patch delta 81134 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

C(<(BC5IFD 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 diff --git a/INSTALL/grub/grub.cfg b/INSTALL/grub/grub.cfg index f180475e..5c6797a9 100644 --- a/INSTALL/grub/grub.cfg +++ b/INSTALL/grub/grub.cfg @@ -133,6 +133,8 @@ function vt_check_compatible_pe { #set compatible if ISO file is less than 80MB if [ $vt_chosen_size -gt 33554432 -a $vt_chosen_size -le 83886080 ]; then set ventoy_compatible=YES + elif [ -e $1/WEPE/WEPE.INI ]; then + set ventoy_compatible=YES fi return @@ -328,6 +330,8 @@ function distro_specify_initrd_file_phase2 { vt_linux_specify_initrd_file /360Disk/initrd.gz elif [ -f (loop)/porteus/initrd.xz ]; then vt_linux_specify_initrd_file /porteus/initrd.xz + elif [ -f (loop)/pyabr/boot/initrfs.img ]; then + vt_linux_specify_initrd_file /pyabr/boot/initrfs.img fi } diff --git a/Ventoy2Disk/Ventoy2Disk/DiskService.h b/Ventoy2Disk/Ventoy2Disk/DiskService.h new file mode 100644 index 00000000..a8b68af7 --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/DiskService.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * DiskService.h + * + * Copyright (c) 2021, 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 . + * + */ + +#ifndef __DISKSERVICE_H__ +#define __DISKSERVICE_H__ + +typedef struct VDS_PARA +{ + UINT64 Attr; + GUID Type; + GUID Id; + WCHAR Name[36]; + ULONG NameLen; + ULONGLONG Offset; +}VDS_PARA; + + +//VDS com +int VDS_Init(void); +BOOL VDS_CleanDisk(int DriveIndex); +BOOL VDS_DeleteAllPartitions(int DriveIndex); +BOOL VDS_DeleteVtoyEFIPartition(int DriveIndex); +BOOL VDS_ChangeVtoyEFIAttr(int DriveIndex, UINT64 Attr); +BOOL VDS_CreateVtoyEFIPart(int DriveIndex, UINT64 Offset); +BOOL VDS_ChangeVtoyEFI2ESP(int DriveIndex, UINT64 Offset); +BOOL VDS_ChangeVtoyEFI2Basic(int DriveIndex, UINT64 Offset); + + +//diskpart.exe +BOOL DSPT_CleanDisk(int DriveIndex); + + +// +// Internel define +// + + + + +#endif diff --git a/Ventoy2Disk/Ventoy2Disk/DiskService_diskpart.c b/Ventoy2Disk/Ventoy2Disk/DiskService_diskpart.c new file mode 100644 index 00000000..7a55395e --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/DiskService_diskpart.c @@ -0,0 +1,84 @@ +/****************************************************************************** + * DiskService_diskpart.c + * + * Copyright (c) 2021, 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 "Ventoy2Disk.h" +#include "DiskService.h" + +STATIC BOOL IsDiskpartExist(void) +{ + BOOL ret; + + ret = IsFileExist("C:\\Windows\\system32\\diskpart.exe"); + if (!ret) + { + Log("diskpart.exe not exist"); + } + + return ret; +} + +STATIC BOOL DSPT_CommProc(const char *Cmd) +{ + CHAR CmdBuf[MAX_PATH]; + CHAR CmdFile[MAX_PATH]; + STARTUPINFOA Si; + PROCESS_INFORMATION Pi; + + GetCurrentDirectoryA(sizeof(CmdBuf), CmdBuf); + sprintf_s(CmdFile, sizeof(CmdFile), "%s\\ventoy\\diskpart_%u.txt", CmdBuf, GetCurrentProcessId()); + + SaveBufToFile(CmdFile, Cmd, strlen(Cmd)); + + GetStartupInfoA(&Si); + Si.dwFlags |= STARTF_USESHOWWINDOW; + Si.wShowWindow = SW_HIDE; + + sprintf_s(CmdBuf, sizeof(CmdBuf), "C:\\Windows\\system32\\diskpart.exe /s \"%s\"", CmdFile); + + Log("CreateProcess <%s>", CmdBuf); + CreateProcessA(NULL, CmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, &Si, &Pi); + + Log("Wair process ..."); + WaitForSingleObject(Pi.hProcess, INFINITE); + Log("Process finished..."); + + DeleteFileA(CmdFile); + return TRUE; +} + +BOOL DSPT_CleanDisk(int DriveIndex) +{ + CHAR CmdBuf[128]; + + Log("CleanDiskByDiskpart <%d>", DriveIndex); + + if (!IsDiskpartExist()) + { + return FALSE; + } + + sprintf_s(CmdBuf, sizeof(CmdBuf), "select disk %d\r\nclean\r\n", DriveIndex); + return DSPT_CommProc(CmdBuf); +} diff --git a/Ventoy2Disk/Ventoy2Disk/DiskService_vds.c b/Ventoy2Disk/Ventoy2Disk/DiskService_vds.c new file mode 100644 index 00000000..38800c69 --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/DiskService_vds.c @@ -0,0 +1,629 @@ +/****************************************************************************** + * DiskService_vds.c + * + * Copyright (c) 2021, 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 "Ventoy2Disk.h" +#include "DiskService.h" + + +#define INTF_ADVANCEDDISK 1 +#define INTF_ADVANCEDDISK2 2 +#define INTF_CREATEPARTITIONEX 3 + +/* + * 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_ChangeAttributes(This, ullOffset, para) (This)->lpVtbl->ChangeAttributes(This, ullOffset, para) +#define IVdsAdvancedDisk_CreatePartition(This, ullOffset, ullSize, para, ppAsync) (This)->lpVtbl->CreatePartition(This, ullOffset, ullSize, para, ppAsync) +#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 IVdsAdvancedDisk2_ChangePartitionType(This, ullOffset, bForce, para) (This)->lpVtbl->ChangePartitionType(This, ullOffset, bForce, para) +#define IVdsAdvancedDisk2_Release(This) (This)->lpVtbl->Release(This) + +#define IVdsCreatePartitionEx_CreatePartitionEx(This, ullOffset, ullSize, ulAlign, para, ppAsync) (This)->lpVtbl->CreatePartitionEx(This, ullOffset, ullSize, ulAlign, para, ppAsync) +#define IVdsCreatePartitionEx_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) + +typedef BOOL(*VDS_Callback_PF)(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data); + +STATIC IVdsService * VDS_InitService(void) +{ + HRESULT hr; + IVdsServiceLoader *pLoader; + IVdsService *pService; + + // 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); + return NULL; + } + + // 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); + return NULL; + } + + // 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); + return NULL; + } + + Log("VDS init OK, service %p", pService); + return pService; +} + + +STATIC BOOL VDS_DiskCommProc(int intf, int DriveIndex, VDS_Callback_PF callback, UINT64 data) +{ + BOOL r = FALSE; + HRESULT hr; + ULONG ulFetched; + IUnknown *pUnk = NULL; + IEnumVdsObject *pEnum = NULL; + IVdsService *pService = NULL; + wchar_t wPhysicalName[48]; + + swprintf_s(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%d", DriveIndex); + + pService = VDS_InitService(); + if (!pService) + { + Log("Could not query VDS Service"); + 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: 0x%lx %u", hr, 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; + IVdsDisk *pDisk; + IVdsAdvancedDisk *pAdvancedDisk; + IVdsAdvancedDisk2 *pAdvancedDisk2; + IVdsCreatePartitionEx *pCreatePartitionEx; + + // 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; + } + + if (intf == INTF_ADVANCEDDISK) + { + // 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; + } + else + { + Log("Callback %d process for disk <%S>", intf, diskprop.pwszName); + r = callback(pAdvancedDisk, &diskprop, data); + } + IVdsAdvancedDisk_Release(pAdvancedDisk); + } + else if (intf == INTF_ADVANCEDDISK2) + { + // Instantiate the AdvanceDisk interface for our disk. + hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk2, (void **)&pAdvancedDisk2); + IVdsDisk_Release(pDisk); + if (hr != S_OK) + { + VDS_SET_ERROR(hr); + Log("Could not access VDS Advanced Disk2 interface: %u", LASTERR); + goto out; + } + else + { + Log("Callback %d process for disk2 <%S>", intf, diskprop.pwszName); + r = callback(pAdvancedDisk2, &diskprop, data); + } + IVdsAdvancedDisk2_Release(pAdvancedDisk2); + } + else if (intf == INTF_CREATEPARTITIONEX) + { + // Instantiate the CreatePartitionEx interface for our disk. + hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsCreatePartitionEx, (void **)&pCreatePartitionEx); + IVdsDisk_Release(pDisk); + if (hr != S_OK) + { + VDS_SET_ERROR(hr); + Log("Could not access VDS CreatePartitionEx interface: %u", LASTERR); + goto out; + } + else + { + Log("Callback %d process for disk <%S>", intf, diskprop.pwszName); + r = callback(pCreatePartitionEx, &diskprop, data); + } + IVdsCreatePartitionEx_Release(pCreatePartitionEx); + } + + goto out; + } + } + } + +out: + return r; +} + +STATIC BOOL VDS_CallBack_CleanDisk(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data) +{ + HRESULT hr, hr2; + ULONG completed; + IVdsAsync* pAsync; + IVdsAdvancedDisk *pAdvancedDisk = (IVdsAdvancedDisk *)pInterface; + + (void)pDiskProp; + (void)data; + + hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, TRUE, FALSE, &pAsync); + while (SUCCEEDED(hr)) + { + hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed); + if (SUCCEEDED(hr)) + { + hr = hr2; + if (hr == S_OK) + { + Log("Disk clean QueryStatus OK"); + break; + } + else if (hr == VDS_E_OPERATION_PENDING) + { + hr = S_OK; + } + else + { + Log("QueryStatus invalid status:%u", hr); + } + } + Sleep(500); + } + + if (hr != S_OK) + { + VDS_SET_ERROR(hr); + Log("Could not clean disk 0x%lx err:%u", hr, LASTERR); + return FALSE; + } + + return TRUE; +} + +BOOL VDS_CleanDisk(int DriveIndex) +{ + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK, DriveIndex, VDS_CallBack_CleanDisk, 0); + Log("VDS_CleanDisk %d ret:%d", DriveIndex, ret); + return ret; +} + +STATIC BOOL VDS_CallBack_DeletePartition(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data) +{ + BOOL r = FALSE; + HRESULT hr; + VDS_PARTITION_PROP* prop_array = NULL; + LONG i, prop_array_size; + ULONG PartNumber = (ULONG)data; + IVdsAdvancedDisk *pAdvancedDisk = (IVdsAdvancedDisk *)pInterface; + + if (PartNumber == 0) + { + Log("Deleting ALL partitions from disk '%S':", pDiskProp->pwszName); + } + else + { + Log("Deleting partition(%ld) from disk '%S':", PartNumber, pDiskProp->pwszName); + } + + // 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) + { + for (i = 0; i < prop_array_size; i++) + { + if (PartNumber == 0 || PartNumber == prop_array[i].ulPartitionNumber) + { + Log("* Partition %d (offset: %lld, size: %llu) delete it.", + prop_array[i].ulPartitionNumber, prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize); + } + else + { + Log(" Partition %d (offset: %lld, size: %llu) skip it.", + prop_array[i].ulPartitionNumber, prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize); + 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); + } + else + { + Log("Delete this partitions success"); + } + } + r = TRUE; + } + else + { + Log("No partition to delete on disk '%S'", pDiskProp->pwszName); + r = TRUE; + } + CoTaskMemFree(prop_array); + + return r; +} + +BOOL VDS_DeleteAllPartitions(int DriveIndex) +{ + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK, DriveIndex, VDS_CallBack_DeletePartition, 0); + Log("VDS_DeleteAllPartitions %d ret:%d", DriveIndex, ret); + return ret; +} + +BOOL VDS_DeleteVtoyEFIPartition(int DriveIndex) +{ + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK, DriveIndex, VDS_CallBack_DeletePartition, 2); + Log("VDS_DeleteVtoyEFIPartition %d ret:%d", DriveIndex, ret); + return ret; +} + +STATIC BOOL VDS_CallBack_ChangeEFIAttr(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data) +{ + BOOL r = FALSE; + HRESULT hr; + VDS_PARTITION_PROP* prop_array = NULL; + LONG i, prop_array_size; + CHANGE_ATTRIBUTES_PARAMETERS AttrPara; + IVdsAdvancedDisk *pAdvancedDisk = (IVdsAdvancedDisk *)pInterface; + + // 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) + { + for (i = 0; i < prop_array_size; i++) + { + if (prop_array[i].ullSize == VENTOY_EFI_PART_SIZE && + prop_array[i].PartitionStyle == VDS_PST_GPT && + memcmp(prop_array[i].Gpt.name, L"VTOYEFI", 7 * 2) == 0) + { + Log("* Partition %d (offset: %lld, size: %llu, Attr:0x%llx)", prop_array[i].ulPartitionNumber, + prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize, prop_array[i].Gpt.attributes); + + if (prop_array[i].Gpt.attributes == data) + { + Log("Attribute match, No need to change."); + r = TRUE; + } + else + { + AttrPara.style = VDS_PST_GPT; + AttrPara.GptPartInfo.attributes = data; + hr = IVdsAdvancedDisk_ChangeAttributes(pAdvancedDisk, prop_array[i].ullOffset, &AttrPara); + if (hr == S_OK) + { + r = TRUE; + Log("Change this partitions attribute success"); + } + else + { + r = FALSE; + VDS_SET_ERROR(hr); + Log("Could not change partitions attr: %u", LASTERR); + } + } + break; + } + } + } + else + { + Log("No partition found on disk '%S'", pDiskProp->pwszName); + } + CoTaskMemFree(prop_array); + + return r; +} + +BOOL VDS_ChangeVtoyEFIAttr(int DriveIndex, UINT64 Attr) +{ + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK, DriveIndex, VDS_CallBack_ChangeEFIAttr, Attr); + Log("VDS_ChangeVtoyEFIAttr %d ret:%d", DriveIndex, ret); + return ret; +} + + + +STATIC BOOL VDS_CallBack_ChangeEFIType(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data) +{ + BOOL r = FALSE; + HRESULT hr; + IVdsAdvancedDisk2 *pAdvancedDisk2 = (IVdsAdvancedDisk2 *)pInterface; + VDS_PARA *VdsPara = (VDS_PARA *)(ULONG)data; + CHANGE_PARTITION_TYPE_PARAMETERS para; + + para.style = VDS_PST_GPT; + memcpy(&(para.GptPartInfo.partitionType), &VdsPara->Type, sizeof(GUID)); + + hr = IVdsAdvancedDisk2_ChangePartitionType(pAdvancedDisk2, VdsPara->Offset, TRUE, ¶); + if (hr == S_OK) + { + r = TRUE; + } + else + { + Log("Failed to change partition type 0x%lx", hr); + } + + return r; +} + + +BOOL VDS_ChangeVtoyEFI2ESP(int DriveIndex, UINT64 Offset) +{ + VDS_PARA Para; + GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; + + memcpy(&(Para.Type), &EspPartType, sizeof(GUID)); + Para.Offset = Offset; + + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK2, DriveIndex, VDS_CallBack_ChangeEFIType, (ULONG)&Para); + Log("VDS_ChangeVtoyEFI2ESP %d ret:%d", DriveIndex, ret); + return ret; +} + +BOOL VDS_ChangeVtoyEFI2Basic(int DriveIndex, UINT64 Offset) +{ + VDS_PARA Para; + GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } }; + + memcpy(&(Para.Type), &WindowsDataPartType, sizeof(GUID)); + Para.Offset = Offset; + + BOOL ret = VDS_DiskCommProc(INTF_ADVANCEDDISK2, DriveIndex, VDS_CallBack_ChangeEFIType, (ULONG)&Para); + Log("VDS_ChangeVtoyEFI2ESP %d ret:%d", DriveIndex, ret); + return ret; +} + + +STATIC BOOL VDS_CallBack_CreateVtoyEFI(void *pInterface, VDS_DISK_PROP *pDiskProp, UINT64 data) +{ + HRESULT hr, hr2; + ULONG completed; + IVdsAsync* pAsync; + CREATE_PARTITION_PARAMETERS para; + IVdsCreatePartitionEx *pCreatePartitionEx = (IVdsCreatePartitionEx *)pInterface; + VDS_PARA *VdsPara = (VDS_PARA *)(ULONG)data; + + (void)pDiskProp; + + memset(¶, 0, sizeof(para)); + para.style = VDS_PST_GPT; + memcpy(&(para.GptPartInfo.partitionType), &VdsPara->Type, sizeof(GUID)); + memcpy(&(para.GptPartInfo.partitionId), &VdsPara->Id, sizeof(GUID)); + para.GptPartInfo.attributes = VdsPara->Attr; + memcpy(para.GptPartInfo.name, VdsPara->Name, sizeof(WCHAR)* VdsPara->NameLen); + + hr = IVdsCreatePartitionEx_CreatePartitionEx(pCreatePartitionEx, VdsPara->Offset, VENTOY_EFI_PART_SIZE, 512, ¶, &pAsync); + while (SUCCEEDED(hr)) + { + hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed); + if (SUCCEEDED(hr)) + { + hr = hr2; + if (hr == S_OK) + { + Log("Disk create partition QueryStatus OK, %lu%%", completed); + break; + } + else if (hr == VDS_E_OPERATION_PENDING) + { + Log("Disk partition finish: %lu%%", completed); + hr = S_OK; + } + else + { + Log("QueryStatus invalid status:0x%lx", hr); + } + } + Sleep(1000); + } + + if (hr != S_OK) + { + VDS_SET_ERROR(hr); + Log("Could not create partition, err:0x%lx", LASTERR); + return FALSE; + } + + return TRUE; +} + +BOOL VDS_CreateVtoyEFIPart(int DriveIndex, UINT64 Offset) +{ + VDS_PARA Para; + GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } }; + GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; + + Log("VDS_CreateVtoyEFIPart %u Offset:%llu Sector:%llu", DriveIndex, Offset, Offset / 512); + + memset(&Para, 0, sizeof(Para)); + Para.Attr = 0x8000000000000000ULL; + Para.Offset = Offset; + memcpy(Para.Name, L"VTOYEFI", 7 * 2); + Para.NameLen = 7; + memcpy(&(Para.Type), &EspPartType, sizeof(GUID)); + CoCreateGuid(&(Para.Id)); + + BOOL ret = VDS_DiskCommProc(INTF_CREATEPARTITIONEX, DriveIndex, VDS_CallBack_CreateVtoyEFI, (ULONG)&Para); + Log("VDS_CreateVtoyEFIPart %d ret:%d", DriveIndex, ret); + return ret; +} + diff --git a/Ventoy2Disk/Ventoy2Disk/DiskService_wmsa.c b/Ventoy2Disk/Ventoy2Disk/DiskService_wmsa.c new file mode 100644 index 00000000..c5af4ccc --- /dev/null +++ b/Ventoy2Disk/Ventoy2Disk/DiskService_wmsa.c @@ -0,0 +1,27 @@ +/****************************************************************************** +* DiskService_wsma.c +* +* Copyright (c) 2021, 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 "Ventoy2Disk.h" +#include "DiskService.h" diff --git a/Ventoy2Disk/Ventoy2Disk/PhyDrive.c b/Ventoy2Disk/Ventoy2Disk/PhyDrive.c index 8d4e9bb4..a916d155 100644 --- a/Ventoy2Disk/Ventoy2Disk/PhyDrive.c +++ b/Ventoy2Disk/Ventoy2Disk/PhyDrive.c @@ -23,276 +23,12 @@ #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) - { - if (prop_array[i].ullOffset == 2048 * 512 || prop_array[i].ullSize != 32 * 1024 * 1024) - { - 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); - } - else { - Log("Delete this partitions success"); - } - } - 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; -} - +#include "DiskService.h" static DWORD GetVentoyVolumeName(int PhyDrive, UINT64 StartSectorId, CHAR *NameBuf, UINT32 BufLen, BOOL DelSlash) { @@ -1095,25 +831,33 @@ static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) int len = 0; int writelen = 0; int partwrite = 0; + int Pos = PT_WRITE_VENTOY_START; DWORD dwSize = 0; BOOL bRet; unsigned char *data = NULL; LARGE_INTEGER liCurrentPosition; LARGE_INTEGER liNewPosition; + BYTE *CheckBuf = NULL; Log("FormatPart2Fat %llu...", StartSectorId); + CheckBuf = malloc(SIZE_1MB); + if (!CheckBuf) + { + Log("Failed to malloc check buf"); + return 1; + } + rc = ReadWholeFileToBuf(VENTOY_FILE_DISK_IMG, 0, (void **)&data, &len); if (rc) { Log("Failed to read img file %p %u", data, len); + free(CheckBuf); return 1; } liCurrentPosition.QuadPart = StartSectorId * 512; - SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); - - Log("Set file pointer: %llu New pointer:%llu", liCurrentPosition.QuadPart, liNewPosition.QuadPart); + SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); memset(g_part_img_buf, 0, sizeof(g_part_img_buf)); @@ -1141,7 +885,34 @@ static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) goto End; } - PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); + PROGRESS_BAR_SET_POS(Pos); + if (i % 2 == 0) + { + Pos++; + } + } + + //Read and check the data + liCurrentPosition.QuadPart = StartSectorId * 512; + SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); + + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + bRet = ReadFile(hDrive, CheckBuf, SIZE_1MB, &dwSize, NULL); + Log("Read part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); + + if (!bRet || memcmp(CheckBuf, g_part_img_buf[0] + i * SIZE_1MB, SIZE_1MB)) + { + Log("### [Check Fail] The data write and read does not match"); + rc = 1; + goto End; + } + + PROGRESS_BAR_SET_POS(Pos); + if (i % 2 == 0) + { + Pos++; + } } } else @@ -1178,7 +949,7 @@ static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) VentoyProcSecureBoot(g_SecureBoot); - for (int i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) { dwSize = 0; bRet = WriteFile(hDrive, g_part_img_buf[i], SIZE_1MB, &dwSize, NULL); @@ -1189,8 +960,35 @@ static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) rc = 1; goto End; } + + PROGRESS_BAR_SET_POS(Pos); + if (i % 2 == 0) + { + Pos++; + } + } + + //Read and check the data + liCurrentPosition.QuadPart = StartSectorId * 512; + SetFilePointerEx(hDrive, liCurrentPosition, &liNewPosition, FILE_BEGIN); + + for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++) + { + bRet = ReadFile(hDrive, CheckBuf, SIZE_1MB, &dwSize, NULL); + Log("Read part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR); + + if (!bRet || memcmp(CheckBuf, g_part_img_buf[i], SIZE_1MB)) + { + Log("### [Check Fail] The data write and read does not match"); + rc = 1; + goto End; + } - PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i); + PROGRESS_BAR_SET_POS(Pos); + if (i % 2 == 0) + { + Pos++; + } } } else @@ -1204,6 +1002,7 @@ static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId) End: if (data) free(data); + if (CheckBuf)free(CheckBuf); if (partwrite) { @@ -1369,7 +1168,7 @@ int ClearVentoyFromPhyDrive(HWND hWnd, PHY_DRIVE_INFO *pPhyDrive, char *pDrvLett PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); - if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) + if (!VDS_DeleteAllPartitions(pPhyDrive->PhyDrive)) { Log("Notice: Could not delete partitions: %u", GetLastError()); } @@ -1772,7 +1571,7 @@ End: } -int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle) +int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle, int TryId) { int i; int rc = 0; @@ -1789,7 +1588,7 @@ int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle) UINT64 Part1SectorCount = 0; UINT64 Part2StartSector = 0; - Log("InstallVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", + Log("InstallVentoy2PhyDrive try%d %s PhyDrive%d <<%s %s %dGB>>", TryId, PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); @@ -1856,7 +1655,7 @@ int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle) PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); - if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE)) + if (!VDS_DeleteAllPartitions(pPhyDrive->PhyDrive)) { Log("Notice: Could not delete partitions: %u", GetLastError()); } @@ -2026,107 +1825,127 @@ End: return rc; } -int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) + +int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int TryId) { - 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; + int i; + int rc = 0; + int MaxRetry = 3; + BOOL ForceMBR = FALSE; + BOOL Esp2Basic = 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; - UINT8 ReservedData[4096]; + MBR_HEAD BootImg; + MBR_HEAD MBR; + VTOY_GPT_INFO *pGptInfo = NULL; + UINT8 ReservedData[4096]; - Log("UpdateVentoy2PhyDrive %s PhyDrive%d <<%s %s %dGB>>", - pPhyDrive->PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, - GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); + Log("UpdateVentoy2PhyDrive try%d %s PhyDrive%d <<%s %s %dGB>>", TryId, + pPhyDrive->PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, + GetHumanReadableGBSize(pPhyDrive->SizeInBytes)); - PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); + PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN); - Log("Lock disk for umount ............................ "); + 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; - } + 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; - } + if (pPhyDrive->PartStyle) + { + pGptInfo = malloc(sizeof(VTOY_GPT_INFO)); + if (!pGptInfo) + { + return 1; + } - memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO)); + 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); + // 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)); + //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); + 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); + 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); + 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); - } + ReservedMB = (pPhyDrive->SizeInBytes / 512 - (StartSector + VENTOY_EFI_PART_SIZE / 512)) / 2048; + Log("MBR Reserved Disk Space:%llu MB", (ULONGLONG)ReservedMB); + } - //Read Reserved Data - SetFilePointer(hDrive, 512 * 2040, NULL, FILE_BEGIN); - ReadFile(hDrive, ReservedData, sizeof(ReservedData), &dwSize, NULL); + //Read Reserved Data + SetFilePointer(hDrive, 512 * 2040, NULL, FILE_BEGIN); + ReadFile(hDrive, ReservedData, sizeof(ReservedData), &dwSize, NULL); - GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters)); + 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; - } - } - } + 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); + // 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); + + if (pPhyDrive->PartStyle == 1) + { + Log("TryId=%d EFI GPT partition type is 0x%llx", TryId, pPhyDrive->Part2GPTAttr); + + if ((TryId == 1 && (pPhyDrive->Part2GPTAttr >> 56) == 0xC0) || TryId == 2) + { + PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART); + Log("Change GPT partition type to ESP"); + + if (VDS_ChangeVtoyEFI2ESP(pPhyDrive->PhyDrive, StartSector * 512)) + { + Esp2Basic = TRUE; + Sleep(1000); + } + } + } PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE); @@ -2143,30 +1962,66 @@ int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) Log("Lock volume for update .......................... "); hVolume = INVALID_HANDLE_VALUE; - Status = GetVentoyVolumeName(pPhyDrive->PhyDrive, StartSector, DriveLetters, sizeof(DriveLetters), TRUE); + + //If we change VTOYEFI to ESP, it can not have s volume name, so don't try to get it. + if (Esp2Basic) + { + Status = ERROR_NOT_FOUND; + } + else + { + for (i = 0; i < MaxRetry; i++) + { + Status = GetVentoyVolumeName(pPhyDrive->PhyDrive, StartSector, DriveLetters, sizeof(DriveLetters), TRUE); + if (ERROR_SUCCESS == Status) + { + break; + } + else + { + Log("==== Volume not found, wait and retry %d... ====", i); + Sleep(2); + } + } + } + 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); + + for (i = 0; i < MaxRetry; i++) + { + 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, wait and retry ...", LASTERR); + Sleep(2000); + } + else + { + break; + } + } if (hVolume == INVALID_HANDLE_VALUE) { Log("Failed to create file volume, errcode:%u", LASTERR); - rc = 1; - goto End; } + else + { + 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_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); + 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) { @@ -2178,18 +2033,17 @@ int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) goto End; } - if (!TryWritePart2(hDrive, StartSector)) { if (pPhyDrive->PartStyle == 0) { ForceMBR = TRUE; - Log("Try write failed, now delete partition 2..."); + Log("Try write failed, now delete partition 2 for MBR..."); CHECK_CLOSE_HANDLE(hDrive); Log("Now delete partition 2..."); - DeletePartitions(pPhyDrive->PhyDrive, TRUE); + VDS_DeleteVtoyEFIPartition(pPhyDrive->PhyDrive); hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE); if (hDrive == INVALID_HANDLE_VALUE) @@ -2199,6 +2053,12 @@ int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive) goto End; } } + else + { + Log("TryWritePart2 failed ...."); + rc = 1; + goto End; + } } PROGRESS_BAR_SET_POS(PT_FORMAT_PART2); @@ -2285,6 +2145,12 @@ End: CHECK_CLOSE_HANDLE(hDrive); + if (Esp2Basic) + { + Log("Recover GPT partition type to basic"); + VDS_ChangeVtoyEFI2Basic(pPhyDrive->PhyDrive, StartSector * 512); + } + if (pGptInfo) { free(pGptInfo); diff --git a/Ventoy2Disk/Ventoy2Disk/Utility.c b/Ventoy2Disk/Ventoy2Disk/Utility.c index 6b441607..c9c32bd1 100644 --- a/Ventoy2Disk/Ventoy2Disk/Utility.c +++ b/Ventoy2Disk/Ventoy2Disk/Utility.c @@ -1,7 +1,8 @@ /****************************************************************************** * Utility.c * - * Copyright (c) 2020, longpanda + * Copyright (c) 2021, 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 @@ -20,6 +21,25 @@ #include #include "Ventoy2Disk.h" +void TraceOut(const char *Fmt, ...) +{ + va_list Arg; + int Len = 0; + FILE *File = NULL; + char szBuf[1024]; + + va_start(Arg, Fmt); + Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg); + va_end(Arg); + + fopen_s(&File, VENTOY_FILE_LOG, "a+"); + if (File) + { + fwrite(szBuf, 1, Len, File); + fclose(File); + } +} + void Log(const char *Fmt, ...) { va_list Arg; @@ -92,6 +112,22 @@ BOOL IsPathExist(BOOL Dir, const char *Fmt, ...) return TRUE; } +int SaveBufToFile(const CHAR *FileName, const void *Buffer, int BufLen) +{ + FILE *File = NULL; + void *Data = NULL; + + fopen_s(&File, FileName, "wb"); + if (File == NULL) + { + Log("Failed to open file %s", FileName); + return 1; + } + + fwrite(Buffer, 1, BufLen, File); + fclose(File); + return 0; +} int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen) { @@ -205,73 +241,239 @@ BOOL IsWow64(void) return bIsWow64; } -void DumpWindowsVersion(void) +/* +* Some code and functions in the file are copied from rufus. +* https://github.com/pbatard/rufus +*/ + +/* Windows versions */ +enum WindowsVersion { + WINDOWS_UNDEFINED = -1, + WINDOWS_UNSUPPORTED = 0, + WINDOWS_XP = 0x51, + WINDOWS_2003 = 0x52, // Also XP_64 + WINDOWS_VISTA = 0x60, // Also Server 2008 + WINDOWS_7 = 0x61, // Also Server 2008_R2 + WINDOWS_8 = 0x62, // Also Server 2012 + WINDOWS_8_1 = 0x63, // Also Server 2012_R2 + WINDOWS_10_PREVIEW1 = 0x64, + WINDOWS_10 = 0xA0, // Also Server 2016, also Server 2019 + WINDOWS_11 = 0xB0, // Also Server 2022 + WINDOWS_MAX +}; + +static const char* GetEdition(DWORD ProductType) { - 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); - + // From: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo + // These values can be found in the winnt.h header. + switch (ProductType) { + case 0x00000000: return ""; // Undefined + case 0x00000001: return "Ultimate"; + case 0x00000002: return "Home Basic"; + case 0x00000003: return "Home Premium"; + case 0x00000004: return "Enterprise"; + case 0x00000005: return "Home Basic N"; + case 0x00000006: return "Business"; + case 0x00000007: return "Standard Server"; + case 0x00000008: return "Datacenter Server"; + case 0x00000009: return "Smallbusiness Server"; + case 0x0000000A: return "Enterprise Server"; + case 0x0000000B: return "Starter"; + case 0x00000010: return "Business N"; + case 0x00000011: return "Web Server"; + case 0x00000012: return "Cluster Server"; + case 0x00000013: return "Home Server"; + case 0x0000001A: return "Home Premium N"; + case 0x0000001B: return "Enterprise N"; + case 0x0000001C: return "Ultimate N"; + case 0x00000022: return "Home Premium Server"; + case 0x0000002F: return "Starter N"; + case 0x00000030: return "Pro"; + case 0x00000031: return "Pro N"; + case 0x00000042: return "Starter E"; + case 0x00000043: return "Home Basic E"; + case 0x00000044: return "Premium E"; + case 0x00000045: return "Pro E"; + case 0x00000046: return "Enterprise E"; + case 0x00000047: return "Ultimate E"; + case 0x00000048: return "Enterprise Eval"; + case 0x00000054: return "Enterprise N Eval"; + case 0x00000057: return "Thin PC"; + case 0x0000006F: return "Core Connected"; + case 0x00000070: return "Pro Student"; + case 0x00000071: return "Core Connected N"; + case 0x00000072: return "Pro Student N"; + case 0x00000073: return "Core Connected Single Language"; + case 0x00000074: return "Core Connected China"; + case 0x00000079: return "Edu"; + case 0x0000007A: return "Edu N"; + case 0x0000007D: return "Enterprise S"; + case 0x0000007E: return "Enterprise S N"; + case 0x0000007F: return "Pro S"; + case 0x00000080: return "Pro S N"; + case 0x00000081: return "Enterprise S Eval"; + case 0x00000082: return "Enterprise S N Eval"; + case 0x0000008A: return "Pro Single Language"; + case 0x0000008B: return "Pro China"; + case 0x0000008C: return "Enterprise Subscription"; + case 0x0000008D: return "Enterprise Subscription N"; + case 0x00000095: return "Utility VM"; + case 0x000000A1: return "Pro Workstation"; + case 0x000000A2: return "Pro Workstation N"; + case 0x000000A4: return "Pro for Education"; + case 0x000000A5: return "Pro for Education N"; + case 0x000000AB: return "Enterprise G"; // I swear Microsoft are just making up editions... + case 0x000000AC: return "Enterprise G N"; + case 0x000000B6: return "Core OS"; + case 0x000000B7: return "Cloud E"; + case 0x000000B8: return "Cloud E N"; + case 0x000000BD: return "Lite"; + case 0xABCDABCD: return "(Unlicensed)"; + default: return "(Unknown Edition)"; + } +} + +#define is_x64 IsWow64 +#define static_strcpy safe_strcpy +#define REGKEY_HKCU HKEY_CURRENT_USER +#define REGKEY_HKLM HKEY_LOCAL_MACHINE +static int nWindowsVersion = WINDOWS_UNDEFINED; +static int nWindowsBuildNumber = -1; +static char WindowsVersionStr[128] = ""; + +/* Helpers for 32 bit registry operations */ + +/* +* Read a generic registry key value. If a short key_name is used, assume that +* it belongs to the application and create the app subkey if required +*/ +static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type, + LPBYTE dest, DWORD dest_size) +{ + const char software_prefix[] = "SOFTWARE\\"; + char long_key_name[MAX_PATH] = { 0 }; + BOOL r = FALSE; + size_t i; + LONG s; + HKEY hSoftware = NULL, hApp = NULL; + DWORD dwType = -1, dwSize = dest_size; + + memset(dest, 0, dest_size); + + if (key_name == NULL) + return FALSE; + + for (i = strlen(key_name); i>0; i--) { + if (key_name[i] == '\\') + break; + } + + if (i > 0) { + // Prefix with "SOFTWARE" if needed + if (_strnicmp(key_name, software_prefix, sizeof(software_prefix)-1) != 0) { + if (i + sizeof(software_prefix) >= sizeof(long_key_name)) + return FALSE; + strcpy_s(long_key_name, sizeof(long_key_name), software_prefix); + strcat_s(long_key_name, sizeof(long_key_name), key_name); + long_key_name[sizeof(software_prefix)+i - 1] = 0; + } + else { + if (i >= sizeof(long_key_name)) + return FALSE; + static_strcpy(long_key_name, key_name); + long_key_name[i] = 0; + } + i++; + if (RegOpenKeyExA(key_root, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) { + hApp = NULL; + goto out; + } + } + else { + if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ | KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) { + hSoftware = NULL; + goto out; + } + } + + s = RegQueryValueExA(hApp, &key_name[i], NULL, &dwType, (LPBYTE)dest, &dwSize); + // No key means default value of 0 or empty string + if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType == reg_type) && (dwSize > 0))) { + r = TRUE; + } +out: + if (hSoftware != NULL) + RegCloseKey(hSoftware); + if (hApp != NULL) + RegCloseKey(hApp); + return r; +} + +#define GetRegistryKey32(root, key, pval) _GetRegistryKey(root, key, REG_DWORD, (LPBYTE)pval, sizeof(DWORD)) +static __inline INT32 ReadRegistryKey32(HKEY root, const char* key) { + DWORD val; + GetRegistryKey32(root, key, &val); + return (INT32)val; +} + +/* +* Modified from smartmontools' os_win32.cpp +*/ +void GetWindowsVersion(void) +{ + OSVERSIONINFOEXA vi, vi2; + DWORD dwProductType; + const char* w = 0; + const char* w64 = "32 bit"; + char *vptr; + size_t vlen; + unsigned major, minor; + ULONGLONG major_equal, minor_equal; + BOOL ws; + + nWindowsVersion = WINDOWS_UNDEFINED; + static_strcpy(WindowsVersionStr, "Windows Undefined"); + // 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)) - { + + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(vi); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) { + memset(&vi, 0, sizeof(vi)); + vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA((OSVERSIONINFOA *)&vi)) 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 + if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) { - 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 (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) { + // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx + // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS + // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor... - if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual)) - { + major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL); + for (major = vi.dwMajorVersion; major <= 9; major++) { + memset(&vi2, 0, sizeof(vi2)); + vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major; + if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal)) continue; + if (vi.dwMajorVersion < major) { + vi.dwMajorVersion = major; vi.dwMinorVersion = 0; } - - 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)) - { + minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL); + for (minor = vi.dwMinorVersion; minor <= 9; minor++) { + memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2); + vi2.dwMinorVersion = minor; + if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal)) continue; - } - - Ver1.dwMinorVersion = Minor; + vi.dwMinorVersion = minor; break; } @@ -279,84 +481,81 @@ void DumpWindowsVersion(void) } } - 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"; + if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) { + ws = (vi.wProductType <= VER_NT_WORKSTATION); + nWindowsVersion = vi.dwMajorVersion << 4 | vi.dwMinorVersion; + switch (nWindowsVersion) { + case WINDOWS_XP: w = "XP"; + break; + case WINDOWS_2003: w = (ws ? "XP_64" : (!GetSystemMetrics(89) ? "Server 2003" : "Server 2003_R2")); + break; + case WINDOWS_VISTA: w = (ws ? "Vista" : "Server 2008"); + break; + case WINDOWS_7: w = (ws ? "7" : "Server 2008_R2"); + break; + case WINDOWS_8: w = (ws ? "8" : "Server 2012"); + break; + case WINDOWS_8_1: w = (ws ? "8.1" : "Server 2012_R2"); + break; + case WINDOWS_10_PREVIEW1: w = (ws ? "10 (Preview 1)" : "Server 10 (Preview 1)"); + break; + // Starting with Windows 10 Preview 2, the major is the same as the public-facing version + case WINDOWS_10: + if (vi.dwBuildNumber < 20000) { + w = (ws ? "10" : ((vi.dwBuildNumber < 17763) ? "Server 2016" : "Server 2019")); break; } + nWindowsVersion = WINDOWS_11; + // Fall through + case WINDOWS_11: w = (ws ? "11" : "Server 2022"); + break; + default: + if (nWindowsVersion < WINDOWS_XP) + nWindowsVersion = WINDOWS_UNSUPPORTED; + else + w = "12 or later"; + break; } } } - Bit = IsWow64() ? 64 : 32; + if (is_x64()) + w64 = "64-bit"; - 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); - } + GetProductInfo(vi.dwMajorVersion, vi.dwMinorVersion, vi.wServicePackMajor, vi.wServicePackMinor, &dwProductType); + vptr = WindowsVersionStr; + vlen = sizeof(WindowsVersionStr) - 1; - if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62) - { - Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber); - } + if (!w) + sprintf_s(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"), + (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64); + else if (vi.wServicePackMinor) + sprintf_s(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64); + else if (vi.wServicePackMajor) + sprintf_s(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64); else - { - Log("Windows Version : %s", WinVer); + sprintf_s(vptr, vlen, "%s%s%s, %s", + w, (dwProductType != PRODUCT_UNDEFINED) ? " " : "", GetEdition(dwProductType), w64); + + // Add the build number (including UBR if available) for Windows 8.0 and later + nWindowsBuildNumber = vi.dwBuildNumber; + if (nWindowsVersion >= 0x62) { + int nUbr = ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows NT\\CurrentVersion\\UBR"); + vptr = WindowsVersionStr + strlen(WindowsVersionStr); + vlen = sizeof(WindowsVersionStr) - strlen(WindowsVersionStr) - 1; + if (nUbr > 0) + sprintf_s(vptr, vlen, " (Build %d.%d)", nWindowsBuildNumber, nUbr); + else + sprintf_s(vptr, vlen, " (Build %d)", nWindowsBuildNumber); } +} + + +void DumpWindowsVersion(void) +{ + GetWindowsVersion(); + Log("Windows Version: <>", WindowsVersionStr); return; } diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c index 9eb25701..5ba9e71a 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c @@ -71,7 +71,7 @@ int ParseCmdLineOption(LPSTR lpCmdLine) return 0; } -static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes, MBR_HEAD *pMBR, UINT64 *Part2StartSector) +static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes, MBR_HEAD *pMBR, UINT64 *Part2StartSector, UINT64 *GptPart2Attr) { int i; BOOL bRet; @@ -149,7 +149,15 @@ static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes, MBR_HEAD *pMBR, UIN if (memcmp(pGpt->PartTbl[1].Name, L"VTOYEFI", 7 * 2)) { - Log("Invalid ventoy efi part name"); + if (pGpt->PartTbl[1].Name[0]) + { + Log("Invalid ventoy efi part name <%S>", pGpt->PartTbl[1].Name); + } + else + { + Log("Invalid ventoy efi part name "); + } + return FALSE; } @@ -170,6 +178,7 @@ static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes, MBR_HEAD *pMBR, UIN return FALSE; } + *GptPart2Attr = pGpt->PartTbl[1].Attr; *Part2StartSector = pGpt->PartTbl[1].StartLBA; memcpy(pMBR, &(pGpt->MBR), sizeof(MBR_HEAD)); @@ -225,6 +234,7 @@ static int FilterPhysicalDrive(PHY_DRIVE_INFO *pDriveList, DWORD DriveCount) int Letter = 'A'; int Id = 0; int LetterCount = 0; + UINT64 Part2GPTAttr = 0; UINT64 Part2StartSector = 0; PHY_DRIVE_INFO *CurDrive; MBR_HEAD MBR; @@ -247,6 +257,7 @@ static int FilterPhysicalDrive(PHY_DRIVE_INFO *pDriveList, DWORD DriveCount) for (i = 0; i < DriveCount; i++) { + Part2GPTAttr = 0; CurDrive = pDriveList + i; CurDrive->Id = -1; @@ -278,10 +289,11 @@ static int FilterPhysicalDrive(PHY_DRIVE_INFO *pDriveList, DWORD DriveCount) } } - if (IsVentoyPhyDrive(CurDrive->PhyDrive, CurDrive->SizeInBytes, &MBR, &Part2StartSector)) + if (IsVentoyPhyDrive(CurDrive->PhyDrive, CurDrive->SizeInBytes, &MBR, &Part2StartSector, &Part2GPTAttr)) { memcpy(&(CurDrive->MBR), &MBR, sizeof(MBR)); CurDrive->PartStyle = (MBR.PartTbl[0].FsFlag == 0xEE) ? 1 : 0; + CurDrive->Part2GPTAttr = Part2GPTAttr; GetVentoyVerInPhyDrive(CurDrive, Part2StartSector, CurDrive->VentoyVersion, sizeof(CurDrive->VentoyVersion), &(CurDrive->SecureBootSupport)); Log("PhyDrive %d is Ventoy Disk ver:%s SecureBoot:%u", CurDrive->PhyDrive, CurDrive->VentoyVersion, CurDrive->SecureBootSupport); diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h index 72c9a363..dca9eb9f 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h @@ -53,6 +53,7 @@ } #define LASTERR GetLastError() +#define RET_LASTERR (ret ? 0 : LASTERR) #pragma pack(1) typedef struct PART_TABLE @@ -153,12 +154,13 @@ typedef struct PHY_DRIVE_INFO BOOL SecureBootSupport; MBR_HEAD MBR; + UINT64 Part2GPTAttr; }PHY_DRIVE_INFO; typedef enum PROGRESS_POINT { PT_START = 0, - PT_LOCK_FOR_CLEAN, + PT_LOCK_FOR_CLEAN = 8, PT_DEL_ALL_PART, PT_LOCK_FOR_WRITE, PT_FORMAT_PART1, @@ -185,6 +187,7 @@ extern HFONT g_language_normal_font; extern HFONT g_language_bold_font; extern int g_FilterUSB; +void TraceOut(const char *Fmt, ...); void Log(const char *Fmt, ...); BOOL IsPathExist(BOOL Dir, const char *Fmt, ...); void DumpWindowsVersion(void); @@ -207,11 +210,12 @@ int Ventoy2DiskInit(void); int Ventoy2DiskDestroy(void); PHY_DRIVE_INFO * GetPhyDriveInfoById(int Id); int ParseCmdLineOption(LPSTR lpCmdLine); -int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle); -int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive); +int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle, int TryId); +int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int TryId); int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead); int VentoyFillWholeGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo); void SetProgressBarPos(int Pos); +int SaveBufToFile(const CHAR *FileName, const void *Buffer, int BufLen); int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen); int INIT unxz(unsigned char *in, int in_size, int(*fill)(void *dest, unsigned int size), diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc index 4cbe6228b7cac099f9e5a6415b2cc9d5330bc9f8..18c7d06bfafce5adaa85416cfc714e61434ab59e 100644 GIT binary patch delta 237 zcmX>X-<7!G05_xY;-iv3-HfjG~B$F|2-pE zKz#BBZI#JYynLIV32tCyHf1o{JX82IBcu7`esSr|4WfKZjOLsB#Y0&q7szl-?on1^ zwP8qPu$#=zC!1`;pbug*C^6_Tcrv&!I5WgE1T#beu@8{%$KcIi2$WF*@+%pNfnu2q zc?{WL)!{&~c!m%LM+PS#D}=$HApoe-kinF}5= + + + @@ -115,6 +118,7 @@ + diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters index 45f86ffe..2be3bb28 100644 --- a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters +++ b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters @@ -81,6 +81,15 @@ 源文件 + + 源文件 + + + 源文件 + + + 源文件 + @@ -143,6 +152,9 @@ 头文件 + + 头文件 + diff --git a/Ventoy2Disk/Ventoy2Disk/WinDialog.c b/Ventoy2Disk/Ventoy2Disk/WinDialog.c index a5402338960afb4b4f4d4b1e4da87c446c95111f..a2c41acdd1804812247eedd5afecd1128cd2923e 100644 GIT binary patch delta 1777 zcma)6U2KzO6h5y`+agpu*R5S^{nk3zQXSg`Ovjut8^}WGmX(QrWYE&dCtF+AB5c8| z-kN}fV|WuH1PqskpfzhWBsy-mSWuUEWlTuC@`6NmuhHjx?YFhDiaBZee((8vo^#H7 zu7BI~$4^Z+KXZ6S$fP_Srv#;Gj@~DepK(Mu%Qp`NWd$OwkM?9ia@gN2{^L@)Yh&D&Vw(^8E4g4$cLg{?};)nWt!Y z-K&a+zxc+_Kd$)r=VTllvnW81@l_92lC^{IdW9S7lufE8MNpgdsxBi{x{qcKQ*qE+?l zt8L2Q??O%+|lP#8(`525hrvr`XB_^wKWc zLYojn#I~BU>N5eo3EMrGnk>z3cxZF8!el*oT>sLnS{CagcA>Q~_{V`{FNR`*y3zD; zAe_T*7GE#W&^n6R?5+mc{zo)7!qvd!wPf3(tA>QdKLgoK zr*TTrNj#>sm1+`cVkD0&Mq$)Ehx-gp_RTR^7I2%V0oo2ZWl0YnyVw=rF;6`>rE#u! z#nDqcrgd(qCP-jZg!f*I%0d1;>3_mSNwi2_{BiwPKy_%hS2dTnI@CE2Kgc`1rUm1@ z>cAKP3E{IozdvPBN@4d*qsp>1-ljuBpI6w|t|KcE>&e@js)27uW8XQH(?muTTCjNSlF75EvZN1qE#lQnKJ^xl zANI91B5cwjzO4JT&#&4eVd_NGN(=tYcr4J*#TnyK>_HA(8k3c_7@=A*@boJ6G-%L@ zI0Qr)tCj|8oyF*6@IYlegtx5qdFxibvlxyHx{*akMN`??)9a5piwM%M6n;lwhs{2S zq9?6#$V3|S3VyYK{*#cbzioUz*J3S9S=NJRefo-Db@AziA^!AMtG@47vByp#L0Z4o uu6F6G?dlD^(5i~I)~AyJbzQD@Y9puyE_4UWhl9%f$i-3SSxr{h?KC-Rhs@-JZFyk%06Ce-2j=rk-f&E7GS`7~n*;Vv6NH-NHF?5$ zyUh>I$1!dWxOmKRvVpw7^Z-#tq3t4kjD3RBO%xepCMQU7O#i0HC^fmGmrv4-!IQy< z!IdGNAq+?cF$4qo{tSK$nv)Gr>rO7{m6*<=!l*IXV~W7!qyu8p8&nv5_){428Oj;* z7;=DgB16jbizhC2 zGg;=l!DPGRVB;jF3#lTo{ZdKV;Nq eHefKGe2__cvjX=tw#lD3#U>XBicEeW7zO|Y)D;5& delta 47 zcmbQFx=m%n2`)}&1`h^T24@EE$%g#;lepO!O(q}Ymfl>)J%w#D8@JfxJVBAkHbP+l DI!p|_