1 /******************************************************************************
4 * Copyright (c) 2020, longpanda <admin@ventoy.net>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "Ventoy2Disk.h"
23 void Log(const char *Fmt
, ...)
32 Len
+= safe_sprintf(szBuf
,
33 "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
34 Sys
.wYear
, Sys
.wMonth
, Sys
.wDay
,
35 Sys
.wHour
, Sys
.wMinute
, Sys
.wSecond
,
39 Len
+= vsnprintf_s(szBuf
+ Len
, sizeof(szBuf
)-Len
, sizeof(szBuf
)-Len
, Fmt
, Arg
);
42 //printf("%s\n", szBuf);
45 fopen_s(&File
, VENTOY_FILE_LOG
, "a+");
48 fwrite(szBuf
, 1, Len
, File
);
49 fwrite("\n", 1, 1, File
);
56 BOOL
IsPathExist(BOOL Dir
, const char *Fmt
, ...)
61 CHAR FilePath
[MAX_PATH
];
64 vsnprintf_s(FilePath
, sizeof(FilePath
), sizeof(FilePath
), Fmt
, Arg
);
67 hFile
= CreateFileA(FilePath
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
68 if (INVALID_HANDLE_VALUE
== hFile
)
75 Attr
= GetFileAttributesA(FilePath
);
79 if ((Attr
& FILE_ATTRIBUTE_DIRECTORY
) == 0)
86 if (Attr
& FILE_ATTRIBUTE_DIRECTORY
)
96 int ReadWholeFileToBuf(const CHAR
*FileName
, int ExtLen
, void **Bufer
, int *BufLen
)
102 fopen_s(&File
, FileName
, "rb");
105 Log("Failed to open file %s", FileName
);
109 fseek(File
, 0, SEEK_END
);
110 FileSize
= (int)ftell(File
);
112 Data
= malloc(FileSize
+ ExtLen
);
119 fseek(File
, 0, SEEK_SET
);
120 fread(Data
, 1, FileSize
, File
);
130 const CHAR
* GetLocalVentoyVersion(void)
136 static CHAR LocalVersion
[64] = { 0 };
138 if (LocalVersion
[0] == 0)
140 rc
= ReadWholeFileToBuf(VENTOY_FILE_VERSION
, 1, (void **)&Buf
, &FileSize
);
147 for (Pos
= Buf
; *Pos
; Pos
++)
149 if (*Pos
== '\r' || *Pos
== '\n')
156 safe_sprintf(LocalVersion
, "%s", Buf
);
163 const CHAR
* ParseVentoyVersionFromString(CHAR
*Buf
)
167 static CHAR LocalVersion
[64] = { 0 };
169 Pos
= strstr(Buf
, "VENTOY_VERSION=");
172 Pos
+= strlen("VENTOY_VERSION=");
179 while (*End
!= 0 && *End
!= '"' && *End
!= '\r' && *End
!= '\n')
186 safe_sprintf(LocalVersion
, "%s", Pos
);
195 typedef BOOL(WINAPI
*LPFN_ISWOW64PROCESS
)(HANDLE
, PBOOL
);
196 LPFN_ISWOW64PROCESS fnIsWow64Process
;
197 BOOL bIsWow64
= FALSE
;
199 fnIsWow64Process
= (LPFN_ISWOW64PROCESS
)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
200 if (NULL
!= fnIsWow64Process
)
202 fnIsWow64Process(GetCurrentProcess(), &bIsWow64
);
208 void DumpWindowsVersion(void)
213 ULONGLONG MajorEqual
, MinorEqual
;
214 OSVERSIONINFOEXA Ver1
, Ver2
;
215 const CHAR
*Ver
= NULL
;
216 CHAR WinVer
[256] = { 0 };
218 memset(&Ver1
, 0, sizeof(Ver1
));
219 memset(&Ver2
, 0, sizeof(Ver2
));
221 Ver1
.dwOSVersionInfoSize
= sizeof(Ver1
);
223 // suppress the C4996 warning for GetVersionExA
224 #pragma warning(push)
225 #pragma warning(disable:4996)
226 if (!GetVersionExA((OSVERSIONINFOA
*)&Ver1
))
228 memset(&Ver1
, 0, sizeof(Ver1
));
229 Ver1
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
230 if (!GetVersionExA((OSVERSIONINFOA
*)&Ver1
))
237 if (Ver1
.dwPlatformId
== VER_PLATFORM_WIN32_NT
)
239 if (Ver1
.dwMajorVersion
> 6 || (Ver1
.dwMajorVersion
== 6 && Ver1
.dwMinorVersion
>= 2))
241 // GetVersionEx() has problem on some Windows version
243 MajorEqual
= VerSetConditionMask(0, VER_MAJORVERSION
, VER_EQUAL
);
244 for (Major
= Ver1
.dwMajorVersion
; Major
<= 9; Major
++)
246 memset(&Ver2
, 0, sizeof(Ver2
));
247 Ver2
.dwOSVersionInfoSize
= sizeof(Ver2
);
248 Ver2
.dwMajorVersion
= Major
;
250 if (!VerifyVersionInfoA(&Ver2
, VER_MAJORVERSION
, MajorEqual
))
255 if (Ver1
.dwMajorVersion
< Major
)
257 Ver1
.dwMajorVersion
= Major
;
258 Ver1
.dwMinorVersion
= 0;
261 MinorEqual
= VerSetConditionMask(0, VER_MINORVERSION
, VER_EQUAL
);
262 for (Minor
= Ver1
.dwMinorVersion
; Minor
<= 9; Minor
++)
264 memset(&Ver2
, 0, sizeof(Ver2
));
266 Ver2
.dwOSVersionInfoSize
= sizeof(Ver2
);
267 Ver2
.dwMinorVersion
= Minor
;
269 if (!VerifyVersionInfoA(&Ver2
, VER_MINORVERSION
, MinorEqual
))
274 Ver1
.dwMinorVersion
= Minor
;
282 if (Ver1
.dwMajorVersion
<= 0xF && Ver1
.dwMinorVersion
<= 0xF)
284 WsVer
= (Ver1
.wProductType
<= VER_NT_WORKSTATION
);
285 switch ((Ver1
.dwMajorVersion
<< 4) | Ver2
.dwMinorVersion
)
294 Ver
= GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003";
299 Ver
= WsVer
? "Vista" : "Server 2008";
304 Ver
= WsVer
? "7" : "Server 2008 R2";
309 Ver
= WsVer
? "8" : "Server 2012";
314 Ver
= WsVer
? "8.1" : "Server 2012 R2";
319 Ver
= WsVer
? "10 (Preview 1)" : "Server 10 (Preview 1)";
324 Ver
= WsVer
? "10" : ((Ver1
.dwBuildNumber
> 15000) ? "Server 2019" : "Server 2016");
336 Bit
= IsWow64() ? 64 : 32;
338 if (Ver1
.wServicePackMinor
)
340 safe_sprintf(WinVer
, "Windows %s SP%u.%u %d-bit", Ver
, Ver1
.wServicePackMajor
, Ver1
.wServicePackMinor
, Bit
);
342 else if (Ver1
.wServicePackMajor
)
344 safe_sprintf(WinVer
, "Windows %s SP%u %d-bit", Ver
, Ver1
.wServicePackMajor
, Bit
);
348 safe_sprintf(WinVer
, "Windows %s %d-bit", Ver
, Bit
);
351 if (((Ver1
.dwMajorVersion
<< 4) | Ver2
.dwMinorVersion
) >= 0x62)
353 Log("Windows Version : %s (Build %u)", WinVer
, Ver1
.dwBuildNumber
);
357 Log("Windows Version : %s", WinVer
);
363 BOOL
IsVentoyLogicalDrive(CHAR DriveLetter
)
366 CONST CHAR
*Files
[] =
368 "EFI\\BOOT\\BOOTX64.EFI",
369 "grub\\themes\\ventoy\\theme.txt",
370 "ventoy\\ventoy.cpio",
373 for (i
= 0; i
< sizeof(Files
) / sizeof(Files
[0]); i
++)
375 if (!IsFileExist("%C:\\%s", DriveLetter
, Files
[i
]))
385 int VentoyFillMBRLocation(UINT64 DiskSizeInBytes
, UINT32 StartSectorId
, UINT32 SectorCount
, PART_TABLE
*Table
)
394 while (nHead
!= 0 && (DiskSizeInBytes
/ 512 / nSector
/ nHead
) > 1024)
396 nHead
= (BYTE
)nHead
* 2;
404 Cylinder
= StartSectorId
/ nSector
/ nHead
;
405 Head
= StartSectorId
/ nSector
% nHead
;
406 Sector
= StartSectorId
% nSector
+ 1;
408 Table
->StartHead
= Head
;
409 Table
->StartSector
= Sector
;
410 Table
->StartCylinder
= Cylinder
;
412 EndSectorId
= StartSectorId
+ SectorCount
- 1;
413 Cylinder
= EndSectorId
/ nSector
/ nHead
;
414 Head
= EndSectorId
/ nSector
% nHead
;
415 Sector
= EndSectorId
% nSector
+ 1;
417 Table
->EndHead
= Head
;
418 Table
->EndSector
= Sector
;
419 Table
->EndCylinder
= Cylinder
;
421 Table
->StartSectorId
= StartSectorId
;
422 Table
->SectorCount
= SectorCount
;
427 int VentoyFillMBR(UINT64 DiskSizeBytes
, MBR_HEAD
*pMBR
, int PartStyle
)
431 UINT32 DiskSignature
;
432 UINT32 DiskSectorCount
;
433 UINT32 PartSectorCount
;
434 UINT32 PartStartSector
;
435 UINT32 ReservedSector
;
437 VentoyGetLocalBootImg(pMBR
);
441 memcpy(&DiskSignature
, &Guid
, sizeof(UINT32
));
443 Log("Disk signature: 0x%08x", DiskSignature
);
445 *((UINT32
*)(pMBR
->BootCode
+ 0x1B8)) = DiskSignature
;
447 if (DiskSizeBytes
/ 512 > 0xFFFFFFFF)
449 DiskSectorCount
= 0xFFFFFFFF;
453 DiskSectorCount
= (UINT32
)(DiskSizeBytes
/ 512);
456 ReservedValue
= GetReservedSpaceInMB();
457 if (ReservedValue
<= 0)
463 ReservedSector
= (UINT32
)(ReservedValue
* 2048);
468 ReservedSector
+= 33; // backup GPT part table
471 // check aligned with 4KB
472 if (IsPartNeed4KBAlign())
474 UINT64 sectors
= DiskSizeBytes
/ 512;
477 Log("Disk need to align with 4KB %u", (UINT32
)(sectors
% 8));
478 ReservedSector
+= (UINT32
)(sectors
% 8);
482 Log("ReservedSector: %u", ReservedSector
);
485 PartStartSector
= VENTOY_PART1_START_SECTOR
;
486 PartSectorCount
= DiskSectorCount
- ReservedSector
- VENTOY_EFI_PART_SIZE
/ 512 - PartStartSector
;
487 VentoyFillMBRLocation(DiskSizeBytes
, PartStartSector
, PartSectorCount
, pMBR
->PartTbl
);
489 pMBR
->PartTbl
[0].Active
= 0x80; // bootable
490 pMBR
->PartTbl
[0].FsFlag
= 0x07; // exFAT/NTFS/HPFS
493 PartStartSector
+= PartSectorCount
;
494 PartSectorCount
= VENTOY_EFI_PART_SIZE
/ 512;
495 VentoyFillMBRLocation(DiskSizeBytes
, PartStartSector
, PartSectorCount
, pMBR
->PartTbl
+ 1);
497 pMBR
->PartTbl
[1].Active
= 0x00;
498 pMBR
->PartTbl
[1].FsFlag
= 0xEF; // EFI System Partition
507 static int VentoyFillProtectMBR(UINT64 DiskSizeBytes
, MBR_HEAD
*pMBR
)
510 UINT32 DiskSignature
;
511 UINT64 DiskSectorCount
;
513 VentoyGetLocalBootImg(pMBR
);
517 memcpy(&DiskSignature
, &Guid
, sizeof(UINT32
));
519 Log("Disk signature: 0x%08x", DiskSignature
);
521 *((UINT32
*)(pMBR
->BootCode
+ 0x1B8)) = DiskSignature
;
523 DiskSectorCount
= DiskSizeBytes
/ 512 - 1;
524 if (DiskSectorCount
> 0xFFFFFFFF)
526 DiskSectorCount
= 0xFFFFFFFF;
529 memset(pMBR
->PartTbl
, 0, sizeof(pMBR
->PartTbl
));
531 pMBR
->PartTbl
[0].Active
= 0x00;
532 pMBR
->PartTbl
[0].FsFlag
= 0xee; // EE
534 pMBR
->PartTbl
[0].StartHead
= 0;
535 pMBR
->PartTbl
[0].StartSector
= 1;
536 pMBR
->PartTbl
[0].StartCylinder
= 0;
537 pMBR
->PartTbl
[0].EndHead
= 254;
538 pMBR
->PartTbl
[0].EndSector
= 63;
539 pMBR
->PartTbl
[0].EndCylinder
= 1023;
541 pMBR
->PartTbl
[0].StartSectorId
= 1;
542 pMBR
->PartTbl
[0].SectorCount
= (UINT32
)DiskSectorCount
;
547 pMBR
->BootCode
[92] = 0x22;
552 int VentoyFillWholeGpt(UINT64 DiskSizeBytes
, VTOY_GPT_INFO
*pInfo
)
554 UINT64 Part1SectorCount
= 0;
555 UINT64 DiskSectorCount
= DiskSizeBytes
/ 512;
556 VTOY_GPT_HDR
*Head
= &pInfo
->Head
;
557 VTOY_GPT_PART_TBL
*Table
= pInfo
->PartTbl
;
558 static GUID WindowsDataPartType
= { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
560 VentoyFillProtectMBR(DiskSizeBytes
, &pInfo
->MBR
);
562 Part1SectorCount
= DiskSectorCount
- 33 - 2048;
564 memcpy(Head
->Signature
, "EFI PART", 8);
565 Head
->Version
[2] = 0x01;
568 Head
->EfiStartLBA
= 1;
569 Head
->EfiBackupLBA
= DiskSectorCount
- 1;
570 Head
->PartAreaStartLBA
= 34;
571 Head
->PartAreaEndLBA
= DiskSectorCount
- 34;
572 CoCreateGuid(&Head
->DiskGuid
);
573 Head
->PartTblStartLBA
= 2;
574 Head
->PartTblTotNum
= 128;
575 Head
->PartTblEntryLen
= 128;
578 memcpy(&(Table
[0].PartType
), &WindowsDataPartType
, sizeof(GUID
));
579 CoCreateGuid(&(Table
[0].PartGuid
));
580 Table
[0].StartLBA
= 2048;
581 Table
[0].LastLBA
= 2048 + Part1SectorCount
- 1;
583 memcpy(Table
[0].Name
, L
"Data", 4 * 2);
586 Head
->PartTblCrc
= VentoyCrc32(Table
, sizeof(pInfo
->PartTbl
));
587 Head
->Crc
= VentoyCrc32(Head
, Head
->Length
);
592 int VentoyFillGpt(UINT64 DiskSizeBytes
, VTOY_GPT_INFO
*pInfo
)
594 INT64 ReservedValue
= 0;
595 UINT64 ModSectorCount
= 0;
596 UINT64 ReservedSector
= 33;
597 UINT64 Part1SectorCount
= 0;
598 UINT64 DiskSectorCount
= DiskSizeBytes
/ 512;
599 VTOY_GPT_HDR
*Head
= &pInfo
->Head
;
600 VTOY_GPT_PART_TBL
*Table
= pInfo
->PartTbl
;
601 static GUID WindowsDataPartType
= { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
602 static GUID EspPartType
= { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
603 static GUID BiosGrubPartType
= { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
605 VentoyFillProtectMBR(DiskSizeBytes
, &pInfo
->MBR
);
607 ReservedValue
= GetReservedSpaceInMB();
608 if (ReservedValue
> 0)
610 ReservedSector
+= ReservedValue
* 2048;
613 Part1SectorCount
= DiskSectorCount
- ReservedSector
- (VENTOY_EFI_PART_SIZE
/ 512) - 2048;
615 ModSectorCount
= (Part1SectorCount
% 8);
618 Log("Part1SectorCount:%llu is not aligned by 4KB (%llu)", (ULONGLONG
)Part1SectorCount
, (ULONGLONG
)ModSectorCount
);
621 // check aligned with 4KB
622 if (IsPartNeed4KBAlign())
626 Log("Disk need to align with 4KB %u", (UINT32
)ModSectorCount
);
627 Part1SectorCount
-= ModSectorCount
;
631 Log("no need to align with 4KB");
635 memcpy(Head
->Signature
, "EFI PART", 8);
636 Head
->Version
[2] = 0x01;
639 Head
->EfiStartLBA
= 1;
640 Head
->EfiBackupLBA
= DiskSectorCount
- 1;
641 Head
->PartAreaStartLBA
= 34;
642 Head
->PartAreaEndLBA
= DiskSectorCount
- 34;
643 CoCreateGuid(&Head
->DiskGuid
);
644 Head
->PartTblStartLBA
= 2;
645 Head
->PartTblTotNum
= 128;
646 Head
->PartTblEntryLen
= 128;
649 memcpy(&(Table
[0].PartType
), &WindowsDataPartType
, sizeof(GUID
));
650 CoCreateGuid(&(Table
[0].PartGuid
));
651 Table
[0].StartLBA
= 2048;
652 Table
[0].LastLBA
= 2048 + Part1SectorCount
- 1;
654 memcpy(Table
[0].Name
, L
"Ventoy", 6 * 2);
656 // to fix windows issue
657 //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
658 memcpy(&(Table
[1].PartType
), &WindowsDataPartType
, sizeof(GUID
));
659 CoCreateGuid(&(Table
[1].PartGuid
));
660 Table
[1].StartLBA
= Table
[0].LastLBA
+ 1;
661 Table
[1].LastLBA
= Table
[1].StartLBA
+ VENTOY_EFI_PART_SIZE
/ 512 - 1;
662 Table
[1].Attr
= 0x8000000000000001ULL
;
663 memcpy(Table
[1].Name
, L
"VTOYEFI", 7 * 2);
666 memcpy(&(Table
[2].PartType
), &BiosGrubPartType
, sizeof(GUID
));
667 CoCreateGuid(&(Table
[2].PartGuid
));
668 Table
[2].StartLBA
= 34;
669 Table
[2].LastLBA
= 2047;
674 Head
->PartTblCrc
= VentoyCrc32(Table
, sizeof(pInfo
->PartTbl
));
675 Head
->Crc
= VentoyCrc32(Head
, Head
->Length
);
680 int VentoyFillBackupGptHead(VTOY_GPT_INFO
*pInfo
, VTOY_GPT_HDR
*pHead
)
685 memcpy(pHead
, &pInfo
->Head
, sizeof(VTOY_GPT_HDR
));
687 LBA
= pHead
->EfiStartLBA
;
688 BackupLBA
= pHead
->EfiBackupLBA
;
690 pHead
->EfiStartLBA
= BackupLBA
;
691 pHead
->EfiBackupLBA
= LBA
;
692 pHead
->PartTblStartLBA
= BackupLBA
+ 1 - 33;
695 pHead
->Crc
= VentoyCrc32(pHead
, pHead
->Length
);
700 CHAR
GetFirstUnusedDriveLetter(void)
703 DWORD Drives
= GetLogicalDrives();
715 const CHAR
* GetBusTypeString(STORAGE_BUS_TYPE Type
)
719 case BusTypeUnknown
: return "unknown";
720 case BusTypeScsi
: return "SCSI";
721 case BusTypeAtapi
: return "Atapi";
722 case BusTypeAta
: return "ATA";
723 case BusType1394
: return "1394";
724 case BusTypeSsa
: return "SSA";
725 case BusTypeFibre
: return "Fibre";
726 case BusTypeUsb
: return "USB";
727 case BusTypeRAID
: return "RAID";
728 case BusTypeiScsi
: return "iSCSI";
729 case BusTypeSas
: return "SAS";
730 case BusTypeSata
: return "SATA";
731 case BusTypeSd
: return "SD";
732 case BusTypeMmc
: return "MMC";
733 case BusTypeVirtual
: return "Virtual";
734 case BusTypeFileBackedVirtual
: return "FileBackedVirtual";
735 case BusTypeSpaces
: return "Spaces";
736 case BusTypeNvme
: return "Nvme";
741 int VentoyGetLocalBootImg(MBR_HEAD
*pMBR
)
745 static int Loaded
= 0;
750 memcpy(pMBR
, &MBR
, 512);
754 if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG
, 0, (void **)&ImgBuf
, &Len
))
756 Log("Copy boot img success");
757 memcpy(pMBR
, ImgBuf
, 512);
760 CoCreateGuid((GUID
*)(pMBR
->BootCode
+ 0x180));
762 memcpy(&MBR
, pMBR
, 512);
769 Log("Copy boot img failed");
774 int GetHumanReadableGBSize(UINT64 SizeBytes
)
779 double GB
= SizeBytes
* 1.0 / 1000 / 1000 / 1000;
781 if ((SizeBytes
% 1073741824) == 0)
783 return (int)(SizeBytes
/ 1073741824);
786 for (i
= 0; i
< 12; i
++)
790 Delta
= (Pow2
- GB
) / Pow2
;
794 Delta
= (GB
- Pow2
) / Pow2
;
808 void TrimString(CHAR
*String
)
812 size_t Len
= strlen(String
);
816 if (String
[Len
- 1] != ' ' && String
[Len
- 1] != '\t')
824 while (*Pos1
== ' ' || *Pos1
== '\t')
838 int GetRegDwordValue(HKEY Key
, LPCSTR SubKey
, LPCSTR ValueName
, DWORD
*pValue
)
846 lRet
= RegOpenKeyExA(Key
, SubKey
, 0, KEY_QUERY_VALUE
, &hKey
);
847 Log("RegOpenKeyExA <%s> Ret:%ld", SubKey
, lRet
);
849 if (ERROR_SUCCESS
== lRet
)
851 Size
= sizeof(Value
);
852 lRet
= RegQueryValueExA(hKey
, ValueName
, NULL
, &Type
, (LPBYTE
)&Value
, &Size
);
853 Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName
, lRet
, Size
, Value
);
866 int GetPhysicalDriveCount(void)
871 if (GetRegDwordValue(HKEY_LOCAL_MACHINE
, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value
) == 0)
876 Log("GetPhysicalDriveCount: %d", Count
);