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