]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - Ventoy2Disk/Ventoy2Disk/Utility.c
1.0.60 release
[Ventoy.git] / Ventoy2Disk / Ventoy2Disk / Utility.c
1 /******************************************************************************
2 * Utility.c
3 *
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 * Copyright (c) 2011-2020, Pete Batard <pete@akeo.ie>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include <Windows.h>
22 #include "Ventoy2Disk.h"
23
24 void TraceOut(const char *Fmt, ...)
25 {
26 va_list Arg;
27 int Len = 0;
28 FILE *File = NULL;
29 char szBuf[1024];
30
31 va_start(Arg, Fmt);
32 Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
33 va_end(Arg);
34
35 fopen_s(&File, VENTOY_FILE_LOG, "a+");
36 if (File)
37 {
38 fwrite(szBuf, 1, Len, File);
39 fclose(File);
40 }
41 }
42
43 void Log(const char *Fmt, ...)
44 {
45 va_list Arg;
46 int Len = 0;
47 FILE *File = NULL;
48 SYSTEMTIME Sys;
49 char szBuf[1024];
50
51 GetLocalTime(&Sys);
52 Len += safe_sprintf(szBuf,
53 "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
54 Sys.wYear, Sys.wMonth, Sys.wDay,
55 Sys.wHour, Sys.wMinute, Sys.wSecond,
56 Sys.wMilliseconds);
57
58 va_start(Arg, Fmt);
59 Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
60 va_end(Arg);
61
62 //printf("%s\n", szBuf);
63
64 #if 1
65 fopen_s(&File, VENTOY_FILE_LOG, "a+");
66 if (File)
67 {
68 fwrite(szBuf, 1, Len, File);
69 fwrite("\n", 1, 1, File);
70 fclose(File);
71 }
72 #endif
73
74 }
75
76 BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
77 {
78 va_list Arg;
79 HANDLE hFile;
80 DWORD Attr;
81 CHAR FilePath[MAX_PATH];
82
83 va_start(Arg, Fmt);
84 vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
85 va_end(Arg);
86
87 hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
88 if (INVALID_HANDLE_VALUE == hFile)
89 {
90 return FALSE;
91 }
92
93 CloseHandle(hFile);
94
95 Attr = GetFileAttributesA(FilePath);
96
97 if (Dir)
98 {
99 if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
100 {
101 return FALSE;
102 }
103 }
104 else
105 {
106 if (Attr & FILE_ATTRIBUTE_DIRECTORY)
107 {
108 return FALSE;
109 }
110 }
111
112 return TRUE;
113 }
114
115 int SaveBufToFile(const CHAR *FileName, const void *Buffer, int BufLen)
116 {
117 FILE *File = NULL;
118 void *Data = NULL;
119
120 fopen_s(&File, FileName, "wb");
121 if (File == NULL)
122 {
123 Log("Failed to open file %s", FileName);
124 return 1;
125 }
126
127 fwrite(Buffer, 1, BufLen, File);
128 fclose(File);
129 return 0;
130 }
131
132 int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)
133 {
134 int FileSize;
135 FILE *File = NULL;
136 void *Data = NULL;
137
138 fopen_s(&File, FileName, "rb");
139 if (File == NULL)
140 {
141 Log("Failed to open file %s", FileName);
142 return 1;
143 }
144
145 fseek(File, 0, SEEK_END);
146 FileSize = (int)ftell(File);
147
148 Data = malloc(FileSize + ExtLen);
149 if (!Data)
150 {
151 fclose(File);
152 return 1;
153 }
154
155 fseek(File, 0, SEEK_SET);
156 fread(Data, 1, FileSize, File);
157
158 fclose(File);
159
160 *Bufer = Data;
161 *BufLen = FileSize;
162
163 return 0;
164 }
165
166 const CHAR* GetLocalVentoyVersion(void)
167 {
168 int rc;
169 int FileSize;
170 CHAR *Pos = NULL;
171 CHAR *Buf = NULL;
172 static CHAR LocalVersion[64] = { 0 };
173
174 if (LocalVersion[0] == 0)
175 {
176 rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);
177 if (rc)
178 {
179 return "";
180 }
181 Buf[FileSize] = 0;
182
183 for (Pos = Buf; *Pos; Pos++)
184 {
185 if (*Pos == '\r' || *Pos == '\n')
186 {
187 *Pos = 0;
188 break;
189 }
190 }
191
192 safe_sprintf(LocalVersion, "%s", Buf);
193 free(Buf);
194 }
195
196 return LocalVersion;
197 }
198
199 const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
200 {
201 CHAR *Pos = NULL;
202 CHAR *End = NULL;
203 static CHAR LocalVersion[64] = { 0 };
204
205 Pos = strstr(Buf, "VENTOY_VERSION=");
206 if (Pos)
207 {
208 Pos += strlen("VENTOY_VERSION=");
209 if (*Pos == '"')
210 {
211 Pos++;
212 }
213
214 End = Pos;
215 while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
216 {
217 End++;
218 }
219
220 *End = 0;
221
222 safe_sprintf(LocalVersion, "%s", Pos);
223 return LocalVersion;
224 }
225
226 return "";
227 }
228
229 BOOL IsWow64(void)
230 {
231 typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
232 LPFN_ISWOW64PROCESS fnIsWow64Process;
233 BOOL bIsWow64 = FALSE;
234
235 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
236 if (NULL != fnIsWow64Process)
237 {
238 fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
239 }
240
241 return bIsWow64;
242 }
243
244 /*
245 * Some code and functions in the file are copied from rufus.
246 * https://github.com/pbatard/rufus
247 */
248
249 /* Windows versions */
250 enum WindowsVersion {
251 WINDOWS_UNDEFINED = -1,
252 WINDOWS_UNSUPPORTED = 0,
253 WINDOWS_XP = 0x51,
254 WINDOWS_2003 = 0x52, // Also XP_64
255 WINDOWS_VISTA = 0x60, // Also Server 2008
256 WINDOWS_7 = 0x61, // Also Server 2008_R2
257 WINDOWS_8 = 0x62, // Also Server 2012
258 WINDOWS_8_1 = 0x63, // Also Server 2012_R2
259 WINDOWS_10_PREVIEW1 = 0x64,
260 WINDOWS_10 = 0xA0, // Also Server 2016, also Server 2019
261 WINDOWS_11 = 0xB0, // Also Server 2022
262 WINDOWS_MAX
263 };
264
265 static const char* GetEdition(DWORD ProductType)
266 {
267 // From: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getproductinfo
268 // These values can be found in the winnt.h header.
269 switch (ProductType) {
270 case 0x00000000: return ""; // Undefined
271 case 0x00000001: return "Ultimate";
272 case 0x00000002: return "Home Basic";
273 case 0x00000003: return "Home Premium";
274 case 0x00000004: return "Enterprise";
275 case 0x00000005: return "Home Basic N";
276 case 0x00000006: return "Business";
277 case 0x00000007: return "Server Standard";
278 case 0x00000008: return "Server Datacenter";
279 case 0x00000009: return "Smallbusiness Server";
280 case 0x0000000A: return "Server Enterprise";
281 case 0x0000000B: return "Starter";
282 case 0x0000000C: return "Server Datacenter (Core)";
283 case 0x0000000D: return "Server Standard (Core)";
284 case 0x0000000E: return "Server Enterprise (Core)";
285 case 0x00000010: return "Business N";
286 case 0x00000011: return "Web Server";
287 case 0x00000012: return "HPC Edition";
288 case 0x00000013: return "Storage Server (Essentials)";
289 case 0x0000001A: return "Home Premium N";
290 case 0x0000001B: return "Enterprise N";
291 case 0x0000001C: return "Ultimate N";
292 case 0x00000022: return "Home Server";
293 case 0x00000024: return "Server Standard without Hyper-V";
294 case 0x00000025: return "Server Datacenter without Hyper-V";
295 case 0x00000026: return "Server Enterprise without Hyper-V";
296 case 0x00000027: return "Server Datacenter without Hyper-V (Core)";
297 case 0x00000028: return "Server Standard without Hyper-V (Core)";
298 case 0x00000029: return "Server Enterprise without Hyper-V (Core)";
299 case 0x0000002A: return "Hyper-V Server";
300 case 0x0000002F: return "Starter N";
301 case 0x00000030: return "Pro";
302 case 0x00000031: return "Pro N";
303 case 0x00000034: return "Server Solutions Premium";
304 case 0x00000035: return "Server Solutions Premium (Core)";
305 case 0x00000040: return "Server Hyper Core V";
306 case 0x00000042: return "Starter E";
307 case 0x00000043: return "Home Basic E";
308 case 0x00000044: return "Premium E";
309 case 0x00000045: return "Pro E";
310 case 0x00000046: return "Enterprise E";
311 case 0x00000047: return "Ultimate E";
312 case 0x00000048: return "Enterprise (Eval)";
313 case 0x0000004F: return "Server Standard (Eval)";
314 case 0x00000050: return "Server Datacenter (Eval)";
315 case 0x00000054: return "Enterprise N (Eval)";
316 case 0x00000057: return "Thin PC";
317 case 0x00000058: case 0x00000059: case 0x0000005A: case 0x0000005B: case 0x0000005C: return "Embedded";
318 case 0x00000062: return "Home N";
319 case 0x00000063: return "Home China";
320 case 0x00000064: return "Home Single Language";
321 case 0x00000065: return "Home";
322 case 0x00000067: return "Pro with Media Center";
323 case 0x00000069: case 0x0000006A: case 0x0000006B: case 0x0000006C: return "Embedded";
324 case 0x0000006F: return "Home Connected";
325 case 0x00000070: return "Pro Student";
326 case 0x00000071: return "Home Connected N";
327 case 0x00000072: return "Pro Student N";
328 case 0x00000073: return "Home Connected Single Language";
329 case 0x00000074: return "Home Connected China";
330 case 0x00000079: return "Education";
331 case 0x0000007A: return "Education N";
332 case 0x0000007D: return "Enterprise LTSB";
333 case 0x0000007E: return "Enterprise LTSB N";
334 case 0x0000007F: return "Pro S";
335 case 0x00000080: return "Pro S N";
336 case 0x00000081: return "Enterprise LTSB (Eval)";
337 case 0x00000082: return "Enterprise LTSB N (Eval)";
338 case 0x0000008A: return "Pro Single Language";
339 case 0x0000008B: return "Pro China";
340 case 0x0000008C: return "Enterprise Subscription";
341 case 0x0000008D: return "Enterprise Subscription N";
342 case 0x00000091: return "Server Datacenter SA (Core)";
343 case 0x00000092: return "Server Standard SA (Core)";
344 case 0x00000095: return "Utility VM";
345 case 0x000000A1: return "Pro for Workstations";
346 case 0x000000A2: return "Pro for Workstations N";
347 case 0x000000A4: return "Pro for Education";
348 case 0x000000A5: return "Pro for Education N";
349 case 0x000000AB: return "Enterprise G"; // I swear Microsoft are just making up editions...
350 case 0x000000AC: return "Enterprise G N";
351 case 0x000000B6: return "Home OS";
352 case 0x000000B7: return "Cloud E";
353 case 0x000000B8: return "Cloud E N";
354 case 0x000000BD: return "Lite";
355 case 0xABCDABCD: return "(Unlicensed)";
356 default: return "(Unknown Edition)";
357 }
358 }
359
360 #define is_x64 IsWow64
361 #define static_strcpy safe_strcpy
362 #define REGKEY_HKCU HKEY_CURRENT_USER
363 #define REGKEY_HKLM HKEY_LOCAL_MACHINE
364 static int nWindowsVersion = WINDOWS_UNDEFINED;
365 static int nWindowsBuildNumber = -1;
366 static char WindowsVersionStr[128] = "";
367
368 /* Helpers for 32 bit registry operations */
369
370 /*
371 * Read a generic registry key value. If a short key_name is used, assume that
372 * it belongs to the application and create the app subkey if required
373 */
374 static __inline BOOL _GetRegistryKey(HKEY key_root, const char* key_name, DWORD reg_type,
375 LPBYTE dest, DWORD dest_size)
376 {
377 const char software_prefix[] = "SOFTWARE\\";
378 char long_key_name[MAX_PATH] = { 0 };
379 BOOL r = FALSE;
380 size_t i;
381 LONG s;
382 HKEY hSoftware = NULL, hApp = NULL;
383 DWORD dwType = -1, dwSize = dest_size;
384
385 memset(dest, 0, dest_size);
386
387 if (key_name == NULL)
388 return FALSE;
389
390 for (i = strlen(key_name); i>0; i--) {
391 if (key_name[i] == '\\')
392 break;
393 }
394
395 if (i > 0) {
396 // Prefix with "SOFTWARE" if needed
397 if (_strnicmp(key_name, software_prefix, sizeof(software_prefix)-1) != 0) {
398 if (i + sizeof(software_prefix) >= sizeof(long_key_name))
399 return FALSE;
400 strcpy_s(long_key_name, sizeof(long_key_name), software_prefix);
401 strcat_s(long_key_name, sizeof(long_key_name), key_name);
402 long_key_name[sizeof(software_prefix)+i - 1] = 0;
403 }
404 else {
405 if (i >= sizeof(long_key_name))
406 return FALSE;
407 static_strcpy(long_key_name, key_name);
408 long_key_name[i] = 0;
409 }
410 i++;
411 if (RegOpenKeyExA(key_root, long_key_name, 0, KEY_READ, &hApp) != ERROR_SUCCESS) {
412 hApp = NULL;
413 goto out;
414 }
415 }
416 else {
417 if (RegOpenKeyExA(key_root, "SOFTWARE", 0, KEY_READ | KEY_CREATE_SUB_KEY, &hSoftware) != ERROR_SUCCESS) {
418 hSoftware = NULL;
419 goto out;
420 }
421 }
422
423 s = RegQueryValueExA(hApp, &key_name[i], NULL, &dwType, (LPBYTE)dest, &dwSize);
424 // No key means default value of 0 or empty string
425 if ((s == ERROR_FILE_NOT_FOUND) || ((s == ERROR_SUCCESS) && (dwType == reg_type) && (dwSize > 0))) {
426 r = TRUE;
427 }
428 out:
429 if (hSoftware != NULL)
430 RegCloseKey(hSoftware);
431 if (hApp != NULL)
432 RegCloseKey(hApp);
433 return r;
434 }
435
436 #define GetRegistryKey32(root, key, pval) _GetRegistryKey(root, key, REG_DWORD, (LPBYTE)pval, sizeof(DWORD))
437 static __inline INT32 ReadRegistryKey32(HKEY root, const char* key) {
438 DWORD val;
439 GetRegistryKey32(root, key, &val);
440 return (INT32)val;
441 }
442
443 /*
444 * Modified from smartmontools' os_win32.cpp
445 */
446 void GetWindowsVersion(void)
447 {
448 OSVERSIONINFOEXA vi, vi2;
449 DWORD dwProductType;
450 const char* w = 0;
451 const char* w64 = "32 bit";
452 char *vptr;
453 size_t vlen;
454 unsigned major, minor;
455 ULONGLONG major_equal, minor_equal;
456 BOOL ws;
457
458 nWindowsVersion = WINDOWS_UNDEFINED;
459 static_strcpy(WindowsVersionStr, "Windows Undefined");
460
461 // suppress the C4996 warning for GetVersionExA
462 #pragma warning(push)
463 #pragma warning(disable:4996)
464
465 memset(&vi, 0, sizeof(vi));
466 vi.dwOSVersionInfoSize = sizeof(vi);
467 if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
468 memset(&vi, 0, sizeof(vi));
469 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
470 if (!GetVersionExA((OSVERSIONINFOA *)&vi))
471 return;
472 }
473
474 #pragma warning(pop)
475
476 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
477
478 if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
479 // Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
480 // See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
481 // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS
482 // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor...
483
484 major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
485 for (major = vi.dwMajorVersion; major <= 9; major++) {
486 memset(&vi2, 0, sizeof(vi2));
487 vi2.dwOSVersionInfoSize = sizeof(vi2); vi2.dwMajorVersion = major;
488 if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
489 continue;
490 if (vi.dwMajorVersion < major) {
491 vi.dwMajorVersion = major; vi.dwMinorVersion = 0;
492 }
493
494 minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
495 for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
496 memset(&vi2, 0, sizeof(vi2)); vi2.dwOSVersionInfoSize = sizeof(vi2);
497 vi2.dwMinorVersion = minor;
498 if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
499 continue;
500 vi.dwMinorVersion = minor;
501 break;
502 }
503
504 break;
505 }
506 }
507
508 if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
509 ws = (vi.wProductType <= VER_NT_WORKSTATION);
510 nWindowsVersion = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
511 switch (nWindowsVersion) {
512 case WINDOWS_XP: w = "XP";
513 break;
514 case WINDOWS_2003: w = (ws ? "XP_64" : (!GetSystemMetrics(89) ? "Server 2003" : "Server 2003_R2"));
515 break;
516 case WINDOWS_VISTA: w = (ws ? "Vista" : "Server 2008");
517 break;
518 case WINDOWS_7: w = (ws ? "7" : "Server 2008_R2");
519 break;
520 case WINDOWS_8: w = (ws ? "8" : "Server 2012");
521 break;
522 case WINDOWS_8_1: w = (ws ? "8.1" : "Server 2012_R2");
523 break;
524 case WINDOWS_10_PREVIEW1: w = (ws ? "10 (Preview 1)" : "Server 10 (Preview 1)");
525 break;
526 // Starting with Windows 10 Preview 2, the major is the same as the public-facing version
527 case WINDOWS_10:
528 if (vi.dwBuildNumber < 20000) {
529 w = (ws ? "10" : ((vi.dwBuildNumber < 17763) ? "Server 2016" : "Server 2019"));
530 break;
531 }
532 nWindowsVersion = WINDOWS_11;
533 // Fall through
534 case WINDOWS_11: w = (ws ? "11" : "Server 2022");
535 break;
536 default:
537 if (nWindowsVersion < WINDOWS_XP)
538 nWindowsVersion = WINDOWS_UNSUPPORTED;
539 else
540 w = "12 or later";
541 break;
542 }
543 }
544 }
545
546 if (is_x64())
547 w64 = "64-bit";
548
549 GetProductInfo(vi.dwMajorVersion, vi.dwMinorVersion, vi.wServicePackMajor, vi.wServicePackMinor, &dwProductType);
550 vptr = WindowsVersionStr;
551 vlen = sizeof(WindowsVersionStr) - 1;
552
553 if (!w)
554 sprintf_s(vptr, vlen, "%s %u.%u %s", (vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"),
555 (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
556 else if (vi.wServicePackMinor)
557 sprintf_s(vptr, vlen, "%s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
558 else if (vi.wServicePackMajor)
559 sprintf_s(vptr, vlen, "%s SP%u %s", w, vi.wServicePackMajor, w64);
560 else
561 sprintf_s(vptr, vlen, "%s%s%s, %s",
562 w, (dwProductType != PRODUCT_UNDEFINED) ? " " : "", GetEdition(dwProductType), w64);
563
564 // Add the build number (including UBR if available) for Windows 8.0 and later
565 nWindowsBuildNumber = vi.dwBuildNumber;
566 if (nWindowsVersion >= 0x62) {
567 int nUbr = ReadRegistryKey32(REGKEY_HKLM, "Software\\Microsoft\\Windows NT\\CurrentVersion\\UBR");
568 vptr = WindowsVersionStr + strlen(WindowsVersionStr);
569 vlen = sizeof(WindowsVersionStr) - strlen(WindowsVersionStr) - 1;
570 if (nUbr > 0)
571 sprintf_s(vptr, vlen, " (Build %d.%d)", nWindowsBuildNumber, nUbr);
572 else
573 sprintf_s(vptr, vlen, " (Build %d)", nWindowsBuildNumber);
574 }
575 }
576
577
578
579 void DumpWindowsVersion(void)
580 {
581 GetWindowsVersion();
582 Log("Windows Version: <<Windows %s>>", WindowsVersionStr);
583 return;
584 }
585
586
587 BOOL IsVentoyLogicalDrive(CHAR DriveLetter)
588 {
589 int i;
590 CONST CHAR *Files[] =
591 {
592 "EFI\\BOOT\\BOOTX64.EFI",
593 "grub\\themes\\ventoy\\theme.txt",
594 "ventoy\\ventoy.cpio",
595 };
596
597 for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)
598 {
599 if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))
600 {
601 return FALSE;
602 }
603 }
604
605 return TRUE;
606 }
607
608
609 int VentoyFillMBRLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
610 {
611 BYTE Head;
612 BYTE Sector;
613 BYTE nSector = 63;
614 BYTE nHead = 8;
615 UINT32 Cylinder;
616 UINT32 EndSectorId;
617
618 while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
619 {
620 nHead = (BYTE)nHead * 2;
621 }
622
623 if (nHead == 0)
624 {
625 nHead = 255;
626 }
627
628 Cylinder = StartSectorId / nSector / nHead;
629 Head = StartSectorId / nSector % nHead;
630 Sector = StartSectorId % nSector + 1;
631
632 Table->StartHead = Head;
633 Table->StartSector = Sector;
634 Table->StartCylinder = Cylinder;
635
636 EndSectorId = StartSectorId + SectorCount - 1;
637 Cylinder = EndSectorId / nSector / nHead;
638 Head = EndSectorId / nSector % nHead;
639 Sector = EndSectorId % nSector + 1;
640
641 Table->EndHead = Head;
642 Table->EndSector = Sector;
643 Table->EndCylinder = Cylinder;
644
645 Table->StartSectorId = StartSectorId;
646 Table->SectorCount = SectorCount;
647
648 return 0;
649 }
650
651 int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR, int PartStyle)
652 {
653 GUID Guid;
654 int ReservedValue;
655 UINT32 DiskSignature;
656 UINT32 DiskSectorCount;
657 UINT32 PartSectorCount;
658 UINT32 PartStartSector;
659 UINT32 ReservedSector;
660
661 VentoyGetLocalBootImg(pMBR);
662
663 CoCreateGuid(&Guid);
664
665 memcpy(&DiskSignature, &Guid, sizeof(UINT32));
666
667 Log("Disk signature: 0x%08x", DiskSignature);
668
669 *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;
670 memcpy(pMBR->BootCode + 0x180, &Guid, 16);
671
672 if (DiskSizeBytes / 512 > 0xFFFFFFFF)
673 {
674 DiskSectorCount = 0xFFFFFFFF;
675 }
676 else
677 {
678 DiskSectorCount = (UINT32)(DiskSizeBytes / 512);
679 }
680
681 ReservedValue = GetReservedSpaceInMB();
682 if (ReservedValue <= 0)
683 {
684 ReservedSector = 0;
685 }
686 else
687 {
688 ReservedSector = (UINT32)(ReservedValue * 2048);
689 }
690
691 if (PartStyle)
692 {
693 ReservedSector += 33; // backup GPT part table
694 }
695
696 // check aligned with 4KB
697 if (IsPartNeed4KBAlign())
698 {
699 UINT64 sectors = DiskSizeBytes / 512;
700 if (sectors % 8)
701 {
702 Log("Disk need to align with 4KB %u", (UINT32)(sectors % 8));
703 ReservedSector += (UINT32)(sectors % 8);
704 }
705 }
706
707 Log("ReservedSector: %u", ReservedSector);
708
709 //Part1
710 PartStartSector = VENTOY_PART1_START_SECTOR;
711 PartSectorCount = DiskSectorCount - ReservedSector - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;
712 VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);
713
714 pMBR->PartTbl[0].Active = 0x80; // bootable
715 pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
716
717 //Part2
718 PartStartSector += PartSectorCount;
719 PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
720 VentoyFillMBRLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
721
722 pMBR->PartTbl[1].Active = 0x00;
723 pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
724
725 pMBR->Byte55 = 0x55;
726 pMBR->ByteAA = 0xAA;
727
728 return 0;
729 }
730
731
732 static int VentoyFillProtectMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)
733 {
734 GUID Guid;
735 UINT32 DiskSignature;
736 UINT64 DiskSectorCount;
737
738 VentoyGetLocalBootImg(pMBR);
739
740 CoCreateGuid(&Guid);
741
742 memcpy(&DiskSignature, &Guid, sizeof(UINT32));
743
744 Log("Disk signature: 0x%08x", DiskSignature);
745
746 *((UINT32 *)(pMBR->BootCode + 0x1B8)) = DiskSignature;
747 memcpy(pMBR->BootCode + 0x180, &Guid, 16);
748
749 DiskSectorCount = DiskSizeBytes / 512 - 1;
750 if (DiskSectorCount > 0xFFFFFFFF)
751 {
752 DiskSectorCount = 0xFFFFFFFF;
753 }
754
755 memset(pMBR->PartTbl, 0, sizeof(pMBR->PartTbl));
756
757 pMBR->PartTbl[0].Active = 0x00;
758 pMBR->PartTbl[0].FsFlag = 0xee; // EE
759
760 pMBR->PartTbl[0].StartHead = 0;
761 pMBR->PartTbl[0].StartSector = 1;
762 pMBR->PartTbl[0].StartCylinder = 0;
763 pMBR->PartTbl[0].EndHead = 254;
764 pMBR->PartTbl[0].EndSector = 63;
765 pMBR->PartTbl[0].EndCylinder = 1023;
766
767 pMBR->PartTbl[0].StartSectorId = 1;
768 pMBR->PartTbl[0].SectorCount = (UINT32)DiskSectorCount;
769
770 pMBR->Byte55 = 0x55;
771 pMBR->ByteAA = 0xAA;
772
773 pMBR->BootCode[92] = 0x22;
774
775 return 0;
776 }
777
778 int VentoyFillWholeGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)
779 {
780 UINT64 Part1SectorCount = 0;
781 UINT64 DiskSectorCount = DiskSizeBytes / 512;
782 VTOY_GPT_HDR *Head = &pInfo->Head;
783 VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;
784 static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
785
786 VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);
787
788 Part1SectorCount = DiskSectorCount - 33 - 2048;
789
790 memcpy(Head->Signature, "EFI PART", 8);
791 Head->Version[2] = 0x01;
792 Head->Length = 92;
793 Head->Crc = 0;
794 Head->EfiStartLBA = 1;
795 Head->EfiBackupLBA = DiskSectorCount - 1;
796 Head->PartAreaStartLBA = 34;
797 Head->PartAreaEndLBA = DiskSectorCount - 34;
798 CoCreateGuid(&Head->DiskGuid);
799 Head->PartTblStartLBA = 2;
800 Head->PartTblTotNum = 128;
801 Head->PartTblEntryLen = 128;
802
803
804 memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));
805 CoCreateGuid(&(Table[0].PartGuid));
806 Table[0].StartLBA = 2048;
807 Table[0].LastLBA = 2048 + Part1SectorCount - 1;
808 Table[0].Attr = 0;
809 memcpy(Table[0].Name, L"Data", 4 * 2);
810
811 //Update CRC
812 Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));
813 Head->Crc = VentoyCrc32(Head, Head->Length);
814
815 return 0;
816 }
817
818 int VentoyFillGpt(UINT64 DiskSizeBytes, VTOY_GPT_INFO *pInfo)
819 {
820 INT64 ReservedValue = 0;
821 UINT64 ModSectorCount = 0;
822 UINT64 ReservedSector = 33;
823 UINT64 Part1SectorCount = 0;
824 UINT64 DiskSectorCount = DiskSizeBytes / 512;
825 VTOY_GPT_HDR *Head = &pInfo->Head;
826 VTOY_GPT_PART_TBL *Table = pInfo->PartTbl;
827 static GUID WindowsDataPartType = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
828 static GUID EspPartType = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
829 static GUID BiosGrubPartType = { 0x21686148, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } };
830
831 VentoyFillProtectMBR(DiskSizeBytes, &pInfo->MBR);
832
833 ReservedValue = GetReservedSpaceInMB();
834 if (ReservedValue > 0)
835 {
836 ReservedSector += ReservedValue * 2048;
837 }
838
839 Part1SectorCount = DiskSectorCount - ReservedSector - (VENTOY_EFI_PART_SIZE / 512) - 2048;
840
841 ModSectorCount = (Part1SectorCount % 8);
842 if (ModSectorCount)
843 {
844 Log("Part1SectorCount:%llu is not aligned by 4KB (%llu)", (ULONGLONG)Part1SectorCount, (ULONGLONG)ModSectorCount);
845 }
846
847 // check aligned with 4KB
848 if (IsPartNeed4KBAlign())
849 {
850 if (ModSectorCount)
851 {
852 Log("Disk need to align with 4KB %u", (UINT32)ModSectorCount);
853 Part1SectorCount -= ModSectorCount;
854 }
855 else
856 {
857 Log("no need to align with 4KB");
858 }
859 }
860
861 memcpy(Head->Signature, "EFI PART", 8);
862 Head->Version[2] = 0x01;
863 Head->Length = 92;
864 Head->Crc = 0;
865 Head->EfiStartLBA = 1;
866 Head->EfiBackupLBA = DiskSectorCount - 1;
867 Head->PartAreaStartLBA = 34;
868 Head->PartAreaEndLBA = DiskSectorCount - 34;
869 CoCreateGuid(&Head->DiskGuid);
870 Head->PartTblStartLBA = 2;
871 Head->PartTblTotNum = 128;
872 Head->PartTblEntryLen = 128;
873
874
875 memcpy(&(Table[0].PartType), &WindowsDataPartType, sizeof(GUID));
876 CoCreateGuid(&(Table[0].PartGuid));
877 Table[0].StartLBA = 2048;
878 Table[0].LastLBA = 2048 + Part1SectorCount - 1;
879 Table[0].Attr = 0;
880 memcpy(Table[0].Name, L"Ventoy", 6 * 2);
881
882 // to fix windows issue
883 //memcpy(&(Table[1].PartType), &EspPartType, sizeof(GUID));
884 memcpy(&(Table[1].PartType), &WindowsDataPartType, sizeof(GUID));
885 CoCreateGuid(&(Table[1].PartGuid));
886 Table[1].StartLBA = Table[0].LastLBA + 1;
887 Table[1].LastLBA = Table[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1;
888 Table[1].Attr = 0xC000000000000001ULL;
889 memcpy(Table[1].Name, L"VTOYEFI", 7 * 2);
890
891 #if 0
892 memcpy(&(Table[2].PartType), &BiosGrubPartType, sizeof(GUID));
893 CoCreateGuid(&(Table[2].PartGuid));
894 Table[2].StartLBA = 34;
895 Table[2].LastLBA = 2047;
896 Table[2].Attr = 0;
897 #endif
898
899 //Update CRC
900 Head->PartTblCrc = VentoyCrc32(Table, sizeof(pInfo->PartTbl));
901 Head->Crc = VentoyCrc32(Head, Head->Length);
902
903 return 0;
904 }
905
906 int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
907 {
908 UINT64 LBA;
909 UINT64 BackupLBA;
910
911 memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
912
913 LBA = pHead->EfiStartLBA;
914 BackupLBA = pHead->EfiBackupLBA;
915
916 pHead->EfiStartLBA = BackupLBA;
917 pHead->EfiBackupLBA = LBA;
918 pHead->PartTblStartLBA = BackupLBA + 1 - 33;
919
920 pHead->Crc = 0;
921 pHead->Crc = VentoyCrc32(pHead, pHead->Length);
922
923 return 0;
924 }
925
926 CHAR GetFirstUnusedDriveLetter(void)
927 {
928 CHAR Letter = 'D';
929 DWORD Drives = GetLogicalDrives();
930
931 Drives >>= 3;
932 while (Drives & 0x1)
933 {
934 Letter++;
935 Drives >>= 1;
936 }
937
938 return Letter;
939 }
940
941 const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)
942 {
943 switch (Type)
944 {
945 case BusTypeUnknown: return "unknown";
946 case BusTypeScsi: return "SCSI";
947 case BusTypeAtapi: return "Atapi";
948 case BusTypeAta: return "ATA";
949 case BusType1394: return "1394";
950 case BusTypeSsa: return "SSA";
951 case BusTypeFibre: return "Fibre";
952 case BusTypeUsb: return "USB";
953 case BusTypeRAID: return "RAID";
954 case BusTypeiScsi: return "iSCSI";
955 case BusTypeSas: return "SAS";
956 case BusTypeSata: return "SATA";
957 case BusTypeSd: return "SD";
958 case BusTypeMmc: return "MMC";
959 case BusTypeVirtual: return "Virtual";
960 case BusTypeFileBackedVirtual: return "FileBackedVirtual";
961 case BusTypeSpaces: return "Spaces";
962 case BusTypeNvme: return "Nvme";
963 }
964 return "unknown";
965 }
966
967 int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
968 {
969 int Len = 0;
970 BYTE *ImgBuf = NULL;
971 static int Loaded = 0;
972 static MBR_HEAD MBR;
973
974 if (Loaded)
975 {
976 memcpy(pMBR, &MBR, 512);
977 return 0;
978 }
979
980 if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))
981 {
982 Log("Copy boot img success");
983 memcpy(pMBR, ImgBuf, 512);
984 free(ImgBuf);
985
986 CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));
987
988 memcpy(&MBR, pMBR, 512);
989 Loaded = 1;
990
991 return 0;
992 }
993 else
994 {
995 Log("Copy boot img failed");
996 return 1;
997 }
998 }
999
1000 int GetHumanReadableGBSize(UINT64 SizeBytes)
1001 {
1002 int i;
1003 int Pow2 = 1;
1004 double Delta;
1005 double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
1006
1007 if ((SizeBytes % 1073741824) == 0)
1008 {
1009 return (int)(SizeBytes / 1073741824);
1010 }
1011
1012 for (i = 0; i < 12; i++)
1013 {
1014 if (Pow2 > GB)
1015 {
1016 Delta = (Pow2 - GB) / Pow2;
1017 }
1018 else
1019 {
1020 Delta = (GB - Pow2) / Pow2;
1021 }
1022
1023 if (Delta < 0.05)
1024 {
1025 return Pow2;
1026 }
1027
1028 Pow2 <<= 1;
1029 }
1030
1031 return (int)GB;
1032 }
1033
1034 void TrimString(CHAR *String)
1035 {
1036 CHAR *Pos1 = String;
1037 CHAR *Pos2 = String;
1038 size_t Len = strlen(String);
1039
1040 while (Len > 0)
1041 {
1042 if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
1043 {
1044 break;
1045 }
1046 String[Len - 1] = 0;
1047 Len--;
1048 }
1049
1050 while (*Pos1 == ' ' || *Pos1 == '\t')
1051 {
1052 Pos1++;
1053 }
1054
1055 while (*Pos1)
1056 {
1057 *Pos2++ = *Pos1++;
1058 }
1059 *Pos2++ = 0;
1060
1061 return;
1062 }
1063
1064 int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)
1065 {
1066 HKEY hKey;
1067 DWORD Type;
1068 DWORD Size;
1069 LSTATUS lRet;
1070 DWORD Value;
1071
1072 lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);
1073 Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);
1074
1075 if (ERROR_SUCCESS == lRet)
1076 {
1077 Size = sizeof(Value);
1078 lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);
1079 Log("RegQueryValueExA <%s> ret:%u Size:%u Value:%u", ValueName, lRet, Size, Value);
1080
1081 *pValue = Value;
1082 RegCloseKey(hKey);
1083
1084 return 0;
1085 }
1086 else
1087 {
1088 return 1;
1089 }
1090 }
1091
1092 int GetPhysicalDriveCount(void)
1093 {
1094 DWORD Value;
1095 int Count = 0;
1096
1097 if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)
1098 {
1099 Count = (int)Value;
1100 }
1101
1102 Log("GetPhysicalDriveCount: %d", Count);
1103 return Count;
1104 }
1105
1106
1107