mirror of
https://github.com/ventoy/Ventoy.git
synced 2024-12-27 15:45:58 -05:00
636 lines
15 KiB
C
636 lines
15 KiB
C
/******************************************************************************
|
|
* Utility.c
|
|
*
|
|
* Copyright (c) 2020, longpanda <admin@ventoy.net>
|
|
*
|
|
* 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 "Ventoy2Disk.h"
|
|
|
|
void Log(const char *Fmt, ...)
|
|
{
|
|
va_list Arg;
|
|
int Len = 0;
|
|
FILE *File = NULL;
|
|
SYSTEMTIME Sys;
|
|
char szBuf[1024];
|
|
|
|
GetLocalTime(&Sys);
|
|
Len += safe_sprintf(szBuf,
|
|
"[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
|
|
Sys.wYear, Sys.wMonth, Sys.wDay,
|
|
Sys.wHour, Sys.wMinute, Sys.wSecond,
|
|
Sys.wMilliseconds);
|
|
|
|
va_start(Arg, Fmt);
|
|
Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
|
|
va_end(Arg);
|
|
|
|
//printf("%s\n", szBuf);
|
|
|
|
#if 1
|
|
fopen_s(&File, VENTOY_FILE_LOG, "a+");
|
|
if (File)
|
|
{
|
|
fwrite(szBuf, 1, Len, File);
|
|
fwrite("\n", 1, 1, File);
|
|
fclose(File);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
|
|
{
|
|
va_list Arg;
|
|
HANDLE hFile;
|
|
DWORD Attr;
|
|
CHAR FilePath[MAX_PATH];
|
|
|
|
va_start(Arg, Fmt);
|
|
vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
|
|
va_end(Arg);
|
|
|
|
hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
Attr = GetFileAttributesA(FilePath);
|
|
|
|
if (Dir)
|
|
{
|
|
if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Attr & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)
|
|
{
|
|
int FileSize;
|
|
FILE *File = NULL;
|
|
void *Data = NULL;
|
|
|
|
fopen_s(&File, FileName, "rb");
|
|
if (File == NULL)
|
|
{
|
|
Log("Failed to open file %s", FileName);
|
|
return 1;
|
|
}
|
|
|
|
fseek(File, 0, SEEK_END);
|
|
FileSize = (int)ftell(File);
|
|
|
|
Data = malloc(FileSize + ExtLen);
|
|
if (!Data)
|
|
{
|
|
fclose(File);
|
|
return 1;
|
|
}
|
|
|
|
fseek(File, 0, SEEK_SET);
|
|
fread(Data, 1, FileSize, File);
|
|
|
|
fclose(File);
|
|
|
|
*Bufer = Data;
|
|
*BufLen = FileSize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const CHAR* GetLocalVentoyVersion(void)
|
|
{
|
|
int rc;
|
|
int FileSize;
|
|
CHAR *Pos = NULL;
|
|
CHAR *Buf = NULL;
|
|
static CHAR LocalVersion[64] = { 0 };
|
|
|
|
if (LocalVersion[0] == 0)
|
|
{
|
|
rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);
|
|
if (rc)
|
|
{
|
|
return "";
|
|
}
|
|
Buf[FileSize] = 0;
|
|
|
|
for (Pos = Buf; *Pos; Pos++)
|
|
{
|
|
if (*Pos == '\r' || *Pos == '\n')
|
|
{
|
|
*Pos = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
safe_sprintf(LocalVersion, "%s", Buf);
|
|
free(Buf);
|
|
}
|
|
|
|
return LocalVersion;
|
|
}
|
|
|
|
const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
|
|
{
|
|
CHAR *Pos = NULL;
|
|
CHAR *End = NULL;
|
|
static CHAR LocalVersion[64] = { 0 };
|
|
|
|
Pos = strstr(Buf, "VENTOY_VERSION=");
|
|
if (Pos)
|
|
{
|
|
Pos += strlen("VENTOY_VERSION=");
|
|
if (*Pos == '"')
|
|
{
|
|
Pos++;
|
|
}
|
|
|
|
End = Pos;
|
|
while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
|
|
{
|
|
End++;
|
|
}
|
|
|
|
*End = 0;
|
|
|
|
safe_sprintf(LocalVersion, "%s", Pos);
|
|
return LocalVersion;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
BOOL IsWow64(void)
|
|
{
|
|
typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
|
|
LPFN_ISWOW64PROCESS fnIsWow64Process;
|
|
BOOL bIsWow64 = FALSE;
|
|
|
|
fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
|
|
if (NULL != fnIsWow64Process)
|
|
{
|
|
fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
|
|
}
|
|
|
|
return bIsWow64;
|
|
}
|
|
|
|
void DumpWindowsVersion(void)
|
|
{
|
|
int Bit;
|
|
BOOL WsVer;
|
|
DWORD Major, Minor;
|
|
ULONGLONG MajorEqual, MinorEqual;
|
|
OSVERSIONINFOEXA Ver1, Ver2;
|
|
const CHAR *Ver = NULL;
|
|
CHAR WinVer[256] = { 0 };
|
|
|
|
memset(&Ver1, 0, sizeof(Ver1));
|
|
memset(&Ver2, 0, sizeof(Ver2));
|
|
|
|
Ver1.dwOSVersionInfoSize = sizeof(Ver1);
|
|
|
|
// suppress the C4996 warning for GetVersionExA
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4996)
|
|
if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
|
|
{
|
|
memset(&Ver1, 0, sizeof(Ver1));
|
|
Ver1.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
|
if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
#pragma warning(pop)
|
|
|
|
if (Ver1.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
if (Ver1.dwMajorVersion > 6 || (Ver1.dwMajorVersion == 6 && Ver1.dwMinorVersion >= 2))
|
|
{
|
|
// GetVersionEx() has problem on some Windows version
|
|
|
|
MajorEqual = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
|
|
for (Major = Ver1.dwMajorVersion; Major <= 9; Major++)
|
|
{
|
|
memset(&Ver2, 0, sizeof(Ver2));
|
|
Ver2.dwOSVersionInfoSize = sizeof(Ver2);
|
|
Ver2.dwMajorVersion = Major;
|
|
|
|
if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Ver1.dwMajorVersion < Major)
|
|
{
|
|
Ver1.dwMajorVersion = Major;
|
|
Ver1.dwMinorVersion = 0;
|
|
}
|
|
|
|
MinorEqual = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
|
|
for (Minor = Ver1.dwMinorVersion; Minor <= 9; Minor++)
|
|
{
|
|
memset(&Ver2, 0, sizeof(Ver2));
|
|
|
|
Ver2.dwOSVersionInfoSize = sizeof(Ver2);
|
|
Ver2.dwMinorVersion = Minor;
|
|
|
|
if (!VerifyVersionInfoA(&Ver2, VER_MINORVERSION, MinorEqual))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Ver1.dwMinorVersion = Minor;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Ver1.dwMajorVersion <= 0xF && Ver1.dwMinorVersion <= 0xF)
|
|
{
|
|
WsVer = (Ver1.wProductType <= VER_NT_WORKSTATION);
|
|
switch ((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion)
|
|
{
|
|
case 0x51:
|
|
{
|
|
Ver = "XP";
|
|
break;
|
|
}
|
|
case 0x52:
|
|
{
|
|
Ver = GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003";
|
|
break;
|
|
}
|
|
case 0x60:
|
|
{
|
|
Ver = WsVer ? "Vista" : "Server 2008";
|
|
break;
|
|
}
|
|
case 0x61:
|
|
{
|
|
Ver = WsVer ? "7" : "Server 2008 R2";
|
|
break;
|
|
}
|
|
case 0x62:
|
|
{
|
|
Ver = WsVer ? "8" : "Server 2012";
|
|
break;
|
|
}
|
|
case 0x63:
|
|
{
|
|
Ver = WsVer ? "8.1" : "Server 2012 R2";
|
|
break;
|
|
}
|
|
case 0x64:
|
|
{
|
|
Ver = WsVer ? "10 (Preview 1)" : "Server 10 (Preview 1)";
|
|
break;
|
|
}
|
|
case 0xA0:
|
|
{
|
|
Ver = WsVer ? "10" : ((Ver1.dwBuildNumber > 15000) ? "Server 2019" : "Server 2016");
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Ver = "10 or later";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Bit = IsWow64() ? 64 : 32;
|
|
|
|
if (Ver1.wServicePackMinor)
|
|
{
|
|
safe_sprintf(WinVer, "Windows %s SP%u.%u %d-bit", Ver, Ver1.wServicePackMajor, Ver1.wServicePackMinor, Bit);
|
|
}
|
|
else if (Ver1.wServicePackMajor)
|
|
{
|
|
safe_sprintf(WinVer, "Windows %s SP%u %d-bit", Ver, Ver1.wServicePackMajor, Bit);
|
|
}
|
|
else
|
|
{
|
|
safe_sprintf(WinVer, "Windows %s %d-bit", Ver, Bit);
|
|
}
|
|
|
|
if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62)
|
|
{
|
|
Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber);
|
|
}
|
|
else
|
|
{
|
|
Log("Windows Version : %s", WinVer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL IsVentoyLogicalDrive(CHAR DriveLetter)
|
|
{
|
|
int i;
|
|
CONST CHAR *Files[] =
|
|
{
|
|
"EFI\\BOOT\\BOOTX64.EFI",
|
|
"grub\\themes\\ventoy\\theme.txt",
|
|
"ventoy\\ventoy.cpio",
|
|
};
|
|
|
|
for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)
|
|
{
|
|
if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int VentoyFillLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
|
|
{
|
|
BYTE Head;
|
|
BYTE Sector;
|
|
BYTE nSector = 63;
|
|
BYTE nHead = 8;
|
|
UINT32 Cylinder;
|
|
UINT32 EndSectorId;
|
|
|
|
while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
|
|
{
|
|
nHead = (BYTE)nHead * 2;
|
|
}
|
|
|
|
if (nHead == 0)
|
|
{
|
|
nHead = 255;
|
|
}
|
|
|
|
Cylinder = StartSectorId / nSector / nHead;
|
|
Head = StartSectorId / nSector % nHead;
|
|
Sector = StartSectorId % nSector + 1;
|
|
|
|
Table->StartHead = Head;
|
|
Table->StartSector = Sector;
|
|
Table->StartCylinder = Cylinder;
|
|
|
|
EndSectorId = StartSectorId + SectorCount - 1;
|
|
Cylinder = EndSectorId / nSector / nHead;
|
|
Head = EndSectorId / nSector % nHead;
|
|
Sector = EndSectorId % nSector + 1;
|
|
|
|
Table->EndHead = Head;
|
|
Table->EndSector = Sector;
|
|
Table->EndCylinder = Cylinder;
|
|
|
|
Table->StartSectorId = StartSectorId;
|
|
Table->SectorCount = SectorCount;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)
|
|
{
|
|
UINT32 DiskSectorCount;
|
|
UINT32 PartSectorCount;
|
|
UINT32 PartStartSector;
|
|
|
|
VentoyGetLocalBootImg(pMBR);
|
|
|
|
DiskSectorCount = (UINT32)(DiskSizeBytes / 512);
|
|
|
|
//Part1
|
|
PartStartSector = VENTOY_PART1_START_SECTOR;
|
|
PartSectorCount = DiskSectorCount - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;
|
|
VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);
|
|
|
|
pMBR->PartTbl[0].Active = 0x00;
|
|
pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
|
|
|
|
//Part2
|
|
PartStartSector += PartSectorCount;
|
|
PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
|
|
VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
|
|
|
|
pMBR->PartTbl[1].Active = 0x80; // bootable
|
|
pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
|
|
|
|
pMBR->Byte55 = 0x55;
|
|
pMBR->ByteAA = 0xAA;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CHAR GetFirstUnusedDriveLetter(void)
|
|
{
|
|
CHAR Letter = 'D';
|
|
DWORD Drives = GetLogicalDrives();
|
|
|
|
Drives >>= 3;
|
|
while (Drives & 0x1)
|
|
{
|
|
Letter++;
|
|
Drives >>= 1;
|
|
}
|
|
|
|
return Letter;
|
|
}
|
|
|
|
const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)
|
|
{
|
|
switch (Type)
|
|
{
|
|
case BusTypeUnknown: return "unknown";
|
|
case BusTypeScsi: return "SCSI";
|
|
case BusTypeAtapi: return "Atapi";
|
|
case BusTypeAta: return "ATA";
|
|
case BusType1394: return "1394";
|
|
case BusTypeSsa: return "SSA";
|
|
case BusTypeFibre: return "Fibre";
|
|
case BusTypeUsb: return "USB";
|
|
case BusTypeRAID: return "RAID";
|
|
case BusTypeiScsi: return "iSCSI";
|
|
case BusTypeSas: return "SAS";
|
|
case BusTypeSata: return "SATA";
|
|
case BusTypeSd: return "SD";
|
|
case BusTypeMmc: return "MMC";
|
|
case BusTypeVirtual: return "Virtual";
|
|
case BusTypeFileBackedVirtual: return "FileBackedVirtual";
|
|
case BusTypeSpaces: return "Spaces";
|
|
case BusTypeNvme: return "Nvme";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
|
|
{
|
|
int Len = 0;
|
|
BYTE *ImgBuf = NULL;
|
|
static int Loaded = 0;
|
|
static MBR_HEAD MBR;
|
|
|
|
if (Loaded)
|
|
{
|
|
memcpy(pMBR, &MBR, 512);
|
|
return 0;
|
|
}
|
|
|
|
if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))
|
|
{
|
|
Log("Copy boot img success");
|
|
memcpy(pMBR, ImgBuf, 512);
|
|
free(ImgBuf);
|
|
|
|
CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));
|
|
|
|
memcpy(&MBR, pMBR, 512);
|
|
Loaded = 1;
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
Log("Copy boot img failed");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int GetHumanReadableGBSize(UINT64 SizeBytes)
|
|
{
|
|
int i;
|
|
int Pow2 = 1;
|
|
double Delta;
|
|
double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
|
|
|
|
for (i = 0; i < 12; i++)
|
|
{
|
|
if (Pow2 > GB)
|
|
{
|
|
Delta = (Pow2 - GB) / Pow2;
|
|
}
|
|
else
|
|
{
|
|
Delta = (GB - Pow2) / Pow2;
|
|
}
|
|
|
|
if (Delta < 0.05)
|
|
{
|
|
return Pow2;
|
|
}
|
|
|
|
Pow2 <<= 1;
|
|
}
|
|
|
|
return (int)GB;
|
|
}
|
|
|
|
void TrimString(CHAR *String)
|
|
{
|
|
CHAR *Pos1 = String;
|
|
CHAR *Pos2 = String;
|
|
size_t Len = strlen(String);
|
|
|
|
while (Len > 0)
|
|
{
|
|
if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
|
|
{
|
|
break;
|
|
}
|
|
String[Len - 1] = 0;
|
|
Len--;
|
|
}
|
|
|
|
while (*Pos1 == ' ' || *Pos1 == '\t')
|
|
{
|
|
Pos1++;
|
|
}
|
|
|
|
while (*Pos1)
|
|
{
|
|
*Pos2++ = *Pos1++;
|
|
}
|
|
*Pos2++ = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)
|
|
{
|
|
HKEY hKey;
|
|
DWORD Type;
|
|
DWORD Size;
|
|
LSTATUS lRet;
|
|
DWORD Value;
|
|
|
|
lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);
|
|
Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);
|
|
|
|
if (ERROR_SUCCESS == lRet)
|
|
{
|
|
Size = sizeof(Value);
|
|
lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);
|
|
Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value);
|
|
|
|
*pValue = Value;
|
|
RegCloseKey(hKey);
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int GetPhysicalDriveCount(void)
|
|
{
|
|
DWORD Value;
|
|
int Count = 0;
|
|
|
|
if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)
|
|
{
|
|
Count = (int)Value;
|
|
}
|
|
|
|
Log("GetPhysicalDriveCount: %d", Count);
|
|
return Count;
|
|
}
|
|
|
|
|
|
|