/******************************************************************************
 * PhyDrive.c
 *
 * Copyright (c) 2020, longpanda <admin@ventoy.net>
 * Copyright (c) 2011-2020, Pete Batard <pete@akeo.ie>
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 */
 
#include <Windows.h>
#include <time.h>
#include <winternl.h>
#include <commctrl.h>
#include <initguid.h>
#include "resource.h"
#include "Language.h"
#include "Ventoy2Disk.h"
#include "fat_filelib.h"
#include "ff.h"
#include "DiskService.h"

static int g_backup_bin_index = 0;

static DWORD GetVentoyVolumeName(int PhyDrive, UINT64 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:%llu PartOffset:%llu", PhyDrive, (ULONGLONG)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, BOOL *pSecureBoot)
{
    int rc = 0;
    HANDLE hDrive;
    void *flfile;

    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))
    {
        Log("attach media success...");
        rc = GetVentoyVersionFromFatFile(VerBuf, BufLen);
    }
    else
    {
        Log("attach media failed...");
        rc = 1;
    }

    Log("GetVentoyVerInPhyDrive rc=%d...", rc);
    if (rc == 0)
    {
        Log("VentoyVerInPhyDrive %d is <%s>...", pDriveInfo->PhyDrive, VerBuf);

        flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
        if (flfile)
        {
            *pSecureBoot = TRUE;
            fl_fclose(flfile);
        }
    }

    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);
			}
		}

        file = fl_fopen("/EFI/BOOT/grubia32_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/BOOTIA32.EFI");
            fl_remove("/EFI/BOOT/grubia32.efi");
            fl_remove("/EFI/BOOT/grubia32_real.efi");
            fl_remove("/EFI/BOOT/mmia32.efi");            

            file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
            Log("Open bootia32 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;
    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);

    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(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
        {
            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 (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(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(Pos);
                if (i % 2 == 0)
                {
                    Pos++;
                }
            }
        }
        else
        {
            rc = 1;
            Log("decompress finished failed");
            goto End;
        }
    }

End:

    if (data) free(data);
    if (CheckBuf)free(CheckBuf);

    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;

    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");
        return 0;
    }
    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 (!VDS_DeleteAllPartitions(pPhyDrive->PhyDrive))
    {
        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 2MB space
    pTmpBuf = malloc(SIZE_2MB);
    if (!pTmpBuf)
    {
        Log("Failed to alloc memory.");
        rc = 1;
        goto End;
    }
    memset(pTmpBuf, 0, SIZE_2MB);

    SET_FILE_POS(512);
    bRet = WriteFile(hDrive, pTmpBuf, SIZE_2MB - 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(pPhyDrive->SizeInBytes - SIZE_2MB);
    bRet = WriteFile(hDrive, pTmpBuf, SIZE_2MB, &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);

    if (pPhyDrive->SizeInBytes > 2199023255552ULL)
    {
        VTOY_GPT_INFO *pGptInfo;
        VTOY_GPT_HDR BackupHead;
        LARGE_INTEGER liCurrentPosition;

        pGptInfo = (VTOY_GPT_INFO *)pTmpBuf;

        VentoyFillWholeGpt(pPhyDrive->SizeInBytes, pGptInfo);

        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
    {
        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));

        VentoyFillMBRLocation(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, 2048, 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 InstallVentoy2FileImage(PHY_DRIVE_INFO *pPhyDrive, int PartStyle)
{
    int i;
    int rc = 1;
    int Len = 0;
    int dataLen = 0;
    UINT size = 0;
    UINT segnum = 0;
    UINT32 chksum = 0;
    UINT64 data_offset = 0;
    UINT64 Part2StartSector = 0;
    UINT64 Part1StartSector = 0;
    UINT64 Part1SectorCount = 0;
    UINT8 *pData = NULL;    
    UINT8 *pBkGptPartTbl = NULL;
    BYTE *ImgBuf = NULL;
    MBR_HEAD *pMBR = NULL;
    VTSI_FOOTER *pImgFooter = NULL;
    VTSI_SEGMENT *pSegment = NULL;
    VTOY_GPT_INFO *pGptInfo = NULL;
    VTOY_GPT_HDR *pBkGptHdr = NULL;
    FILE *fp = NULL;

    Log("InstallVentoy2FileImage %s PhyDrive%d <<%s %s %dGB>>",
        PartStyle ? "GPT" : "MBR", pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId,
        GetHumanReadableGBSize(pPhyDrive->SizeInBytes));

    PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN);

    size = SIZE_1MB + VENTOY_EFI_PART_SIZE + 33 * 512 + VTSI_IMG_MAX_SEG * sizeof(VTSI_SEGMENT) + sizeof(VTSI_FOOTER);

    pData = (UINT8 *)malloc(size);
    if (!pData)
    {
        Log("malloc image buffer failed %d.", size);
        goto End;
    }

    pImgFooter = (VTSI_FOOTER *)(pData + size - sizeof(VTSI_FOOTER));
    pSegment = (VTSI_SEGMENT *)((UINT8 *)pImgFooter - VTSI_IMG_MAX_SEG * sizeof(VTSI_SEGMENT));
    memset(pImgFooter, 0, sizeof(VTSI_FOOTER));
    memset(pSegment, 0, VTSI_IMG_MAX_SEG * sizeof(VTSI_SEGMENT));

    PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START);

    Log("Writing Boot Image ............................. ");
    if (ReadWholeFileToBuf(VENTOY_FILE_STG1_IMG, 0, (void **)&ImgBuf, &Len))
    {
        Log("Failed to read stage1 img");
        goto End;
    }

    unxz(ImgBuf, Len, NULL, NULL, pData, &dataLen, unxz_error);
    SAFE_FREE(ImgBuf);

    Log("decompress %s len:%d", VENTOY_FILE_STG1_IMG, dataLen);

    if (PartStyle)
    {
        pData[500] = 35;//update blocklist
        memmove(pData + 34 * 512, pData, SIZE_1MB - 512 * 34);
        memset(pData, 0, 34 * 512);

        pGptInfo = (VTOY_GPT_INFO *)pData;
        memset(pGptInfo, 0, sizeof(VTOY_GPT_INFO));
        VentoyFillGpt(pPhyDrive->SizeInBytes, pGptInfo);

        pBkGptPartTbl = pData + SIZE_1MB + VENTOY_EFI_PART_SIZE;
        memset(pBkGptPartTbl, 0, 33 * 512);

        memcpy(pBkGptPartTbl, pGptInfo->PartTbl, 32 * 512);
        pBkGptHdr = (VTOY_GPT_HDR *)(pBkGptPartTbl + 32 * 512);
        VentoyFillBackupGptHead(pGptInfo, pBkGptHdr);

        Part1StartSector = pGptInfo->PartTbl[0].StartLBA;
        Part1SectorCount = pGptInfo->PartTbl[0].LastLBA - Part1StartSector + 1;
        Part2StartSector = pGptInfo->PartTbl[1].StartLBA;

        Log("Write GPT Info OK ...");
    }
    else
    {
        memmove(pData + 512, pData, SIZE_1MB - 512);
        memset(pData, 0, 512);

        pMBR = (MBR_HEAD *)pData;
        VentoyFillMBR(pPhyDrive->SizeInBytes, pMBR, PartStyle);
        Part1StartSector = pMBR->PartTbl[0].StartSectorId;
        Part1SectorCount = pMBR->PartTbl[0].SectorCount;
        Part2StartSector = pMBR->PartTbl[1].StartSectorId;

        Log("Write MBR OK ...");
    }

    Log("Writing EFI part Image ............................. ");
    rc = ReadWholeFileToBuf(VENTOY_FILE_DISK_IMG, 0, (void **)&ImgBuf, &Len);
    if (rc)
    {
        Log("Failed to read img file %p %u", ImgBuf, Len);
        goto End;
    }

    PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + 28);
    memset(g_part_img_buf, 0, sizeof(g_part_img_buf));
    unxz(ImgBuf, Len, NULL, NULL, pData + SIZE_1MB, &dataLen, unxz_error);
    if (dataLen == Len)
    {
        Log("decompress finished success");
        g_part_img_buf[0] = pData + SIZE_1MB;

        VentoyProcSecureBoot(g_SecureBoot);
    }
    else
    {
        Log("decompress finished failed");
        goto End;
    }

    fopen_s(&fp, "VentoySparseImg.vtsi", "wb+");
    if (!fp)
    {
        Log("Failed to create Ventoy img file");
        goto End;
    }

    Log("Writing stage1 data ............................. ");

    fwrite(pData, 1, SIZE_1MB, fp);

    pSegment[0].disk_start_sector = 0;
    pSegment[0].sector_num = SIZE_1MB / 512;
    pSegment[0].data_offset = data_offset;
    data_offset += pSegment[0].sector_num * 512;

    disk_io_set_param(INVALID_HANDLE_VALUE, Part1StartSector + Part1SectorCount);// include the 2048 sector gap
    disk_io_set_imghook(fp, pSegment + 1, VTSI_IMG_MAX_SEG - 1, data_offset);

    Log("Formatting part1 exFAT ...");
    if (0 != FormatPart1exFAT(pPhyDrive->SizeInBytes))
    {
        Log("FormatPart1exFAT failed.");
        disk_io_reset_imghook(&segnum, &data_offset);
        goto End;
    }

    disk_io_reset_imghook(&segnum, &data_offset);
    segnum++;

    Log("current segment number:%d dataoff:%ld", segnum, (long)data_offset);

    //write data
    Log("Writing part2 data ............................. ");
    fwrite(pData + SIZE_1MB, 1, VENTOY_EFI_PART_SIZE, fp);
    pSegment[segnum].disk_start_sector = Part2StartSector;
    pSegment[segnum].sector_num = VENTOY_EFI_PART_SIZE / 512;
    pSegment[segnum].data_offset = data_offset;
    data_offset += pSegment[segnum].sector_num * 512;
    segnum++;

    if (PartStyle)
    {
        Log("Writing backup gpt table ............................. ");
        fwrite(pBkGptPartTbl, 1, 33 * 512, fp);
        pSegment[segnum].disk_start_sector = pPhyDrive->SizeInBytes / 512 - 33;
        pSegment[segnum].sector_num = 33;
        pSegment[segnum].data_offset = data_offset;
        data_offset += pSegment[segnum].sector_num * 512;
        segnum++;
    }

    Log("Writing segment metadata ............................. ");

    for (i = 0; i < (int)segnum; i++)
    {
        Log("SEG[%d]:  PhySector:%llu SectorNum:%llu DataOffset:%llu(sector:%llu)", i, pSegment[i].disk_start_sector, pSegment[i].sector_num,
            pSegment[i].data_offset, pSegment[i].data_offset / 512);
    }

    dataLen = segnum * sizeof(VTSI_SEGMENT);
    fwrite(pSegment, 1, dataLen, fp);

    if (dataLen % 512)
    {
        //pData + SIZE_1MB - 8192 is a temp data buffer with zero
        fwrite(pData + SIZE_1MB - 8192, 1, 512 - (dataLen % 512), fp);
    }

    //Fill footer
    pImgFooter->magic = VTSI_IMG_MAGIC;
    pImgFooter->version = 1;
    pImgFooter->disk_size = pPhyDrive->SizeInBytes;
    memcpy(&pImgFooter->disk_signature, pPhyDrive->MBR.BootCode + 0x1b8, 4);
    pImgFooter->segment_num = segnum;
    pImgFooter->segment_offset = data_offset;

    for (i = 0, chksum = 0; i < (int)(segnum * sizeof(VTSI_SEGMENT)); i++)
    {
        chksum += *((UINT8 *)pSegment + i);
    }
    pImgFooter->segment_chksum = ~chksum;

    for (i = 0, chksum = 0; i < sizeof(VTSI_FOOTER); i++)
    {
        chksum += *((UINT8 *)pImgFooter + i);
    }
    pImgFooter->foot_chksum = ~chksum;

    Log("Writing footer segnum(%u)  segoffset(%llu) ......................", segnum, data_offset);
    Log("disk_size=%llu disk_signature=%lx segment_offset=%llu", pImgFooter->disk_size, pImgFooter->disk_signature, pImgFooter->segment_offset);

    fwrite(pImgFooter, 1, sizeof(VTSI_FOOTER), fp);
    fclose(fp);

    Log("Writing Ventoy image file finished, the file size should be %llu .", data_offset + 512 + ((dataLen + 511) / 512 * 512));

    rc = 0;

End:

    PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME);

    Log("retcode:%d\n", rc);

    SAFE_FREE(pData);
    SAFE_FREE(ImgBuf);
    
    return rc;
}


int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int PartStyle, int TryId)
{
    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;
    UINT64 Part1StartSector = 0;
    UINT64 Part1SectorCount = 0;
    UINT64 Part2StartSector = 0;

    Log("InstallVentoy2PhyDrive try%d %s PhyDrive%d <<%s %s %dGB>>", TryId,
        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);

    if (PartStyle)
    {
        VentoyFillGpt(pPhyDrive->SizeInBytes, pGptInfo);
        Part1StartSector = pGptInfo->PartTbl[0].StartLBA;
        Part1SectorCount = pGptInfo->PartTbl[0].LastLBA - Part1StartSector + 1;
        Part2StartSector = pGptInfo->PartTbl[1].StartLBA;
    }
    else
    {
        VentoyFillMBR(pPhyDrive->SizeInBytes, &MBR, PartStyle);
        Part1StartSector = MBR.PartTbl[0].StartSectorId;
        Part1SectorCount = MBR.PartTbl[0].SectorCount;
        Part2StartSector = MBR.PartTbl[1].StartSectorId;
    }

    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 (!VDS_DeleteAllPartitions(pPhyDrive->PhyDrive))
    {
        Log("Notice: Could not delete partitions: 0x%x", GetLastError());
		rc = 1;
		goto End;
    }

    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, Part1StartSector + Part1SectorCount);// include the 2048 sector gap

    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, Part2StartSector))
    {
        Log("FormatPart2Fat failed.");
        rc = 1;
        goto End;
    }

    PROGRESS_BAR_SET_POS(PT_WRITE_STG1_IMG);
    Log("Writing Boot Image ............................. ");
    if (WriteGrubStage1ToPhyDrive(hDrive, PartStyle) != 0)
    {
        Log("WriteGrubStage1ToPhyDrive failed.");
        rc = 1;
        goto End;
    }

    PROGRESS_BAR_SET_POS(PT_WRITE_PART_TABLE);
    Log("Writing 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 ...");
        memcpy(&(pPhyDrive->MBR), &(pGptInfo->MBR), 512);
    }
    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 ...");
        memcpy(&(pPhyDrive->MBR), &MBR, 512);
    }

    //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, Part1StartSector, 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;
}

static BOOL BackupDataBeforeCleanDisk(int PhyDrive, UINT64 DiskSize, BYTE **pBackup)
{
	DWORD dwSize;
	DWORD dwStatus;
	BOOL Return = FALSE;
	BOOL ret = FALSE;
	BYTE *backup = NULL;
	HANDLE hDrive = INVALID_HANDLE_VALUE;
	LARGE_INTEGER liCurPosition;
	LARGE_INTEGER liNewPosition;

	Log("BackupDataBeforeCleanDisk %d", PhyDrive);

	backup = malloc(SIZE_1MB * 4);
	if (!backup)
	{
		goto out;
	}

	hDrive = GetPhysicalHandle(PhyDrive, FALSE, FALSE, FALSE);
	if (hDrive == INVALID_HANDLE_VALUE)
	{
		goto out;
	}

	//read first 2MB
	dwStatus = SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
	if (dwStatus != 0)
	{
		goto out;
	}
	
	dwSize = 0;
	ret = ReadFile(hDrive, backup, SIZE_2MB, &dwSize, NULL);
	if ((!ret) || (dwSize != SIZE_2MB))
	{
		Log("Failed to read %d %u 0x%x", ret, dwSize, LASTERR);
		goto out;
	}
	
	//read last 2MB
	liCurPosition.QuadPart = DiskSize - SIZE_2MB;
	liNewPosition.QuadPart = 0;
	if (0 == SetFilePointerEx(hDrive, liCurPosition, &liNewPosition, FILE_BEGIN) ||
		liNewPosition.QuadPart != liCurPosition.QuadPart)
	{
		goto out;
	}

	dwSize = 0;
	ret = ReadFile(hDrive, backup + SIZE_2MB, SIZE_2MB, &dwSize, NULL);
	if ((!ret) || (dwSize != SIZE_2MB))
	{
		Log("Failed to read %d %u 0x%x", ret, dwSize, LASTERR);
		goto out;
	}

	*pBackup = backup;
	backup = NULL; //For don't free later
	Return = TRUE;

out:
	CHECK_CLOSE_HANDLE(hDrive);
	if (backup)
		free(backup);

	return Return;
}


static BOOL WriteBackupDataToDisk(HANDLE hDrive, UINT64 Offset, BYTE *Data, DWORD Length)
{
	DWORD dwSize = 0;
	BOOL ret = FALSE;
	LARGE_INTEGER liCurPosition;
	LARGE_INTEGER liNewPosition;

	Log("WriteBackupDataToDisk %llu %p %u", Offset, Data, Length);

	liCurPosition.QuadPart = Offset;
	liNewPosition.QuadPart = 0;
	if (0 == SetFilePointerEx(hDrive, liCurPosition, &liNewPosition, FILE_BEGIN) ||
		liNewPosition.QuadPart != liCurPosition.QuadPart)
	{
		return FALSE;
	}

	ret = WriteFile(hDrive, Data, Length, &dwSize, NULL);
	if ((!ret) || dwSize != Length)
	{
		Log("Failed to write %d %u %u", ret, dwSize, LASTERR);
		return FALSE;
	}

	Log("WriteBackupDataToDisk %llu %p %u success", Offset, Data, Length);
	return TRUE;
}


int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive, int TryId)
{
	int i;
	int rc = 0;
	int MaxRetry = 4;
	BOOL ForceMBR = FALSE;
	BOOL Esp2Basic = FALSE;
	BOOL ChangeAttr = FALSE;
	BOOL CleanDisk = FALSE;
	BOOL bWriteBack = TRUE;
	HANDLE hVolume;
	HANDLE hDrive;
	DWORD Status;
	DWORD dwSize;
	BOOL bRet;
	CHAR DriveName[] = "?:\\";
	CHAR DriveLetters[MAX_PATH] = { 0 };
	CHAR BackBinFile[MAX_PATH];
	UINT64 StartSector;
	UINT64 ReservedMB = 0;
	MBR_HEAD BootImg;
	MBR_HEAD MBR;
	BYTE *pBackup = NULL;
	VTOY_GPT_INFO *pGptInfo = NULL;
	UINT8 ReservedData[4096];

	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);

	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);
	}

	//Read Reserved Data
	SetFilePointer(hDrive, 512 * 2040, NULL, FILE_BEGIN);
	ReadFile(hDrive, ReservedData, sizeof(ReservedData), &dwSize, NULL);

	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);

	if (pPhyDrive->PartStyle == 1)
	{
		Log("TryId=%d EFI GPT partition type is 0x%llx", TryId, pPhyDrive->Part2GPTAttr);
		PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART);

		if (TryId == 1)
		{
			Log("Change GPT partition type to ESP");
			if (VDS_ChangeVtoyEFI2ESP(pPhyDrive->PhyDrive, StartSector * 512))
			{
				Esp2Basic = TRUE;
				Sleep(3000);
			}
		}
		else if (TryId == 2)
		{
			Log("Change GPT partition attribute");
			if (VDS_ChangeVtoyEFIAttr(pPhyDrive->PhyDrive, 0x8000000000000001))
			{
				ChangeAttr = TRUE;
				Sleep(2000);
			}
		}
		else if (TryId == 3)
		{
			Log("Clean disk GPT partition table");
			if (BackupDataBeforeCleanDisk(pPhyDrive->PhyDrive, pPhyDrive->SizeInBytes, &pBackup))
			{
				sprintf_s(BackBinFile, sizeof(BackBinFile), ".\\ventoy\\phydrive%d_%u_%d.bin",
					pPhyDrive->PhyDrive, GetCurrentProcessId(), g_backup_bin_index++);
				SaveBufToFile(BackBinFile, pBackup, 4 * SIZE_1MB);
				Log("Save backup data to %s", BackBinFile);

				Log("Success to backup data before clean");
				CleanDisk = TRUE;
				if (!VDS_CleanDisk(pPhyDrive->PhyDrive))
				{
					Sleep(3000);
					DSPT_CleanDisk(pPhyDrive->PhyDrive);
				}
				Sleep(3000);
			}
			else
			{
				Log("Failed to backup data before clean");
			}
		}
	}
	
    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;

	//If we change VTOYEFI to ESP, it can not have s volume name, so don't try to get it.
	if (CleanDisk)
	{
		//writeback the last 2MB
		if (!WriteBackupDataToDisk(hDrive, pPhyDrive->SizeInBytes - SIZE_2MB, pBackup + SIZE_2MB, SIZE_2MB))
		{
			bWriteBack = FALSE;
		}
		Status = ERROR_NOT_FOUND;
	}
	else 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);

        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);
        }
        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_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;
    }

	bRet = TryWritePart2(hDrive, StartSector);
	if (FALSE == bRet && Esp2Basic)
	{
		Log("TryWritePart2 agagin ...");
		Sleep(3000);
		bRet = TryWritePart2(hDrive, StartSector);
	}

	if (!bRet)
    {
		if (pPhyDrive->PartStyle == 0)
		{
			ForceMBR = TRUE;

			Log("Try write failed, now delete partition 2 for MBR...");
			CHECK_CLOSE_HANDLE(hDrive);

			Log("Now delete partition 2...");
			VDS_DeleteVtoyEFIPartition(pPhyDrive->PhyDrive);

			hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE);
			if (hDrive == INVALID_HANDLE_VALUE)
			{
				Log("Failed to GetPhysicalHandle for write.");
				rc = 1;
				goto End;
			}
		}
		else
		{
			Log("TryWritePart2 failed ....");
			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;
    }

    //write reserved data
    SetFilePointer(hDrive, 512 * 2040, NULL, FILE_BEGIN);    
    bRet = WriteFile(hDrive, ReservedData, sizeof(ReservedData), &dwSize, NULL);
    Log("Write resv data ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR);

    // 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);
        }
    }

	if (CleanDisk)
	{
		if (!WriteBackupDataToDisk(hDrive, 4 * 512, pBackup + 4 * 512, SIZE_2MB - 4 * 512))
		{
			bWriteBack = FALSE;
		}

		if (!WriteBackupDataToDisk(hDrive, 0, pBackup, 4 * 512))
		{
			bWriteBack = FALSE;
		}

		free(pBackup);

		if (bWriteBack)
		{
			Log("Write success, now delete %s", BackBinFile);
			DeleteFileA(BackBinFile);
		}
	}

    //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 (Esp2Basic)
    {
		Log("Recover GPT partition type to basic");
		VDS_ChangeVtoyEFI2Basic(pPhyDrive->PhyDrive, StartSector * 512);
    }

	if (pPhyDrive->PartStyle == 1)
	{
		if (ChangeAttr || ((pPhyDrive->Part2GPTAttr >> 56) != 0xC0))
		{
			Log("Change EFI partition attr %u <0x%llx> to <0x%llx>", ChangeAttr, pPhyDrive->Part2GPTAttr, 0xC000000000000001ULL);
			if (VDS_ChangeVtoyEFIAttr(pPhyDrive->PhyDrive, 0xC000000000000001ULL))
			{
				Log("Change EFI partition attr success");
				pPhyDrive->Part2GPTAttr = 0xC000000000000001ULL;
			}
			else
			{
				Log("Change EFI partition attr failed");
			}
		}
	}

    if (pGptInfo)
    {
        free(pGptInfo);
    }
    
    return rc;
}


