1 /******************************************************************************
2 * ventoy_util.c ---- ventoy util
3 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include <ventoy_define.h>
23 #include <ventoy_util.h>
24 #include <ventoy_disk.h>
25 #include "fat_filelib.h"
27 static void TrimString(CHAR
*String
)
31 size_t Len
= strlen(String
);
35 if (String
[Len
- 1] != ' ' && String
[Len
- 1] != '\t')
43 while (*Pos1
== ' ' || *Pos1
== '\t')
57 void ventoy_gen_preudo_uuid(void *uuid
)
59 CoCreateGuid((GUID
*)uuid
);
62 static int IsUTF8Encode(const char *src
)
65 const UCHAR
*Byte
= (const UCHAR
*)src
;
67 for (i
= 0; i
< MAX_PATH
&& Byte
[i
]; i
++)
78 static int Utf8ToUtf16(const char* src
, WCHAR
* dst
)
80 return MultiByteToWideChar(CP_UTF8
, 0, src
, -1, dst
, MAX_PATH
* sizeof(WCHAR
));
83 static int Utf16ToUtf8(const WCHAR
* src
, CHAR
* dst
)
85 int size
= WideCharToMultiByte(CP_UTF8
, 0, src
, -1, dst
, MAX_PATH
, NULL
, 0);
91 int ventoy_path_case(char *path
, int slash
)
98 HANDLE handle
= INVALID_HANDLE_VALUE
;
99 WCHAR Buffer
[MAX_PATH
+ 16];
100 WCHAR FilePathW
[MAX_PATH
];
101 CHAR FilePathA
[MAX_PATH
];
102 FILE_NAME_INFO
*pInfo
= NULL
;
104 if (g_sysinfo
.pathcase
== 0)
109 if (path
== NULL
|| path
[0] == '/' || path
[0] == '\\')
114 isUTF8
= IsUTF8Encode(path
);
117 Utf8ToUtf16(path
, FilePathW
);
118 handle
= CreateFileW(FilePathW
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
122 handle
= CreateFileA(path
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, 0);
125 if (handle
!= INVALID_HANDLE_VALUE
)
127 bRet
= GetFileInformationByHandleEx(handle
, FileNameInfo
, Buffer
, sizeof(Buffer
));
130 pInfo
= (FILE_NAME_INFO
*)Buffer
;
134 for (i
= 0; i
< (int)(pInfo
->FileNameLength
/ sizeof(WCHAR
)); i
++)
136 if (pInfo
->FileName
[i
] == L
'\\')
138 pInfo
->FileName
[i
] = L
'/';
143 pInfo
->FileName
[(pInfo
->FileNameLength
/ sizeof(WCHAR
))] = 0;
145 memset(FilePathA
, 0, sizeof(FilePathA
));
146 Utf16ToUtf8(pInfo
->FileName
, FilePathA
);
148 if (FilePathA
[1] == ':')
157 for (i
= 0; i
< MAX_PATH
&& j
< MAX_PATH
; i
++, j
++)
159 if (FilePathA
[j
] == 0)
164 if (path
[i
] != FilePathA
[j
])
166 path
[i
] = FilePathA
[j
];
172 CHECK_CLOSE_HANDLE(handle
);
180 int ventoy_is_directory_exist(const char *Fmt
, ...)
185 CHAR FilePathA
[MAX_PATH
];
186 WCHAR FilePathW
[MAX_PATH
];
189 vsnprintf_s(FilePathA
, sizeof(FilePathA
), sizeof(FilePathA
), Fmt
, Arg
);
192 UTF8
= IsUTF8Encode(FilePathA
);
196 Utf8ToUtf16(FilePathA
, FilePathW
);
197 Attr
= GetFileAttributesW(FilePathW
);
201 Attr
= GetFileAttributesA(FilePathA
);
204 if (Attr
!= INVALID_FILE_ATTRIBUTES
&& (Attr
& FILE_ATTRIBUTE_DIRECTORY
))
212 int ventoy_is_file_exist(const char *Fmt
, ...)
218 CHAR FilePathA
[MAX_PATH
];
219 WCHAR FilePathW
[MAX_PATH
];
222 vsnprintf_s(FilePathA
, sizeof(FilePathA
), sizeof(FilePathA
), Fmt
, Arg
);
225 UTF8
= IsUTF8Encode(FilePathA
);
228 Utf8ToUtf16(FilePathA
, FilePathW
);
229 hFile
= CreateFileW(FilePathW
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
230 Attr
= GetFileAttributesW(FilePathW
);
234 hFile
= CreateFileA(FilePathA
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
235 Attr
= GetFileAttributesA(FilePathA
);
238 if (INVALID_HANDLE_VALUE
== hFile
)
244 if (Attr
& FILE_ATTRIBUTE_DIRECTORY
)
252 const char * ventoy_get_os_language(void)
254 if (GetUserDefaultUILanguage() == 0x0804)
265 int GetPhyDriveByLogicalDrive(int DriveLetter
, UINT64
*Offset
)
270 VOLUME_DISK_EXTENTS DiskExtents
;
273 scnprintf(PhyPath
, sizeof(PhyPath
), "\\\\.\\%C:", (CHAR
)DriveLetter
);
275 Handle
= CreateFileA(PhyPath
, 0, 0, 0, OPEN_EXISTING
, 0, 0);
276 if (Handle
== INVALID_HANDLE_VALUE
)
278 vlog("CreateFileA %s failed %u\n", PhyPath
, LASTERR
);
282 Ret
= DeviceIoControl(Handle
,
283 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
,
287 (DWORD
)(sizeof(DiskExtents
)),
291 if (!Ret
|| DiskExtents
.NumberOfDiskExtents
== 0)
293 vlog("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTSfailed %u\n", LASTERR
);
294 CHECK_CLOSE_HANDLE(Handle
);
297 CHECK_CLOSE_HANDLE(Handle
);
301 *Offset
= (UINT64
)(DiskExtents
.Extents
[0].StartingOffset
.QuadPart
);
304 return (int)DiskExtents
.Extents
[0].DiskNumber
;
307 int GetPhyDriveInfo(int PhyDrive
, UINT64
*Size
, CHAR
*Vendor
, CHAR
*Product
)
313 HANDLE Handle
= INVALID_HANDLE_VALUE
;
314 GET_LENGTH_INFORMATION LengthInfo
;
315 STORAGE_PROPERTY_QUERY Query
;
316 STORAGE_DESCRIPTOR_HEADER DevDescHeader
;
317 STORAGE_DEVICE_DESCRIPTOR
*pDevDesc
= NULL
;
319 sprintf_s(Drive
, sizeof(Drive
), "\\\\.\\PhysicalDrive%d", PhyDrive
);
320 Handle
= CreateFileA(Drive
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
321 if (Handle
== INVALID_HANDLE_VALUE
)
323 vlog("CreateFileA %s failed %u\n", Drive
, LASTERR
);
328 bRet
= DeviceIoControl(Handle
,
329 IOCTL_DISK_GET_LENGTH_INFO
, NULL
,
337 vlog("IOCTL_DISK_GET_LENGTH_INFO failed %u\n", LASTERR
);
343 *Size
= (UINT64
)LengthInfo
.Length
.QuadPart
;
346 Query
.PropertyId
= StorageDeviceProperty
;
347 Query
.QueryType
= PropertyStandardQuery
;
349 bRet
= DeviceIoControl(Handle
,
350 IOCTL_STORAGE_QUERY_PROPERTY
,
354 sizeof(STORAGE_DESCRIPTOR_HEADER
),
359 vlog("IOCTL_STORAGE_QUERY_PROPERTY failed %u\n", LASTERR
);
363 if (DevDescHeader
.Size
< sizeof(STORAGE_DEVICE_DESCRIPTOR
))
365 vlog("DevDescHeader.size invalid %u\n", DevDescHeader
.Size
);
369 pDevDesc
= (STORAGE_DEVICE_DESCRIPTOR
*)malloc(DevDescHeader
.Size
);
372 vlog("malloc failed\n");
376 bRet
= DeviceIoControl(Handle
,
377 IOCTL_STORAGE_QUERY_PROPERTY
,
386 vlog("IOCTL_STORAGE_QUERY_PROPERTY failed %u\n", LASTERR
);
390 if (pDevDesc
->VendorIdOffset
&& Vendor
)
392 strcpy_s(Vendor
, 128, (char *)pDevDesc
+ pDevDesc
->VendorIdOffset
);
396 if (pDevDesc
->ProductIdOffset
&& Product
)
398 strcpy_s(Product
, 128, (char *)pDevDesc
+ pDevDesc
->ProductIdOffset
);
405 CHECK_FREE(pDevDesc
);
406 CHECK_CLOSE_HANDLE(Handle
);
411 int ventoy_get_file_size(const char *file
)
416 hFile
= CreateFileA(file
, 0, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
417 if (hFile
!= INVALID_HANDLE_VALUE
)
419 Size
= (int)GetFileSize(hFile
, NULL
);
420 CHECK_CLOSE_HANDLE(hFile
);
427 static HANDLE g_FatPhyDrive
;
428 static UINT64 g_Part2StartSec
;
430 const CHAR
* ParseVentoyVersionFromString(CHAR
*Buf
)
434 static CHAR LocalVersion
[64] = { 0 };
436 Pos
= strstr(Buf
, "VENTOY_VERSION=");
439 Pos
+= strlen("VENTOY_VERSION=");
446 while (*End
!= 0 && *End
!= '"' && *End
!= '\r' && *End
!= '\n')
453 sprintf_s(LocalVersion
, sizeof(LocalVersion
), "%s", Pos
);
459 static int GetVentoyVersionFromFatFile(CHAR
*VerBuf
, size_t BufLen
)
466 flfile
= fl_fopen("/grub/grub.cfg", "rb");
469 fl_fseek(flfile
, 0, SEEK_END
);
470 size
= (int)fl_ftell(flfile
);
472 fl_fseek(flfile
, 0, SEEK_SET
);
474 buf
= (char *)malloc(size
+ 1);
477 fl_fread(buf
, 1, size
, flfile
);
481 sprintf_s(VerBuf
, BufLen
, "%s", ParseVentoyVersionFromString(buf
));
491 static int VentoyFatDiskRead(uint32 Sector
, uint8
*Buffer
, uint32 SectorCount
)
496 LARGE_INTEGER liCurrentPosition
;
498 liCurrentPosition
.QuadPart
= Sector
+ g_Part2StartSec
;
499 liCurrentPosition
.QuadPart
*= 512;
500 SetFilePointerEx(g_FatPhyDrive
, liCurrentPosition
, &liCurrentPosition
, FILE_BEGIN
);
502 ReadSize
= (DWORD
)(SectorCount
* 512);
504 bRet
= ReadFile(g_FatPhyDrive
, Buffer
, ReadSize
, &dwSize
, NULL
);
505 if (bRet
== FALSE
|| dwSize
!= ReadSize
)
513 static int GetVentoyVersion(int PhyDrive
, ventoy_disk
*disk
)
519 HANDLE Handle
= INVALID_HANDLE_VALUE
;
520 VTOY_GPT_INFO
*pGPT
= NULL
;
525 0xEB, 0x63, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
526 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
527 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
528 0x56, 0x54, 0x00, 0x47, 0x65, 0x00, 0x48, 0x44, 0x00, 0x52, 0x64, 0x00, 0x20, 0x45, 0x72, 0x0D,
531 sprintf_s(Drive
, sizeof(Drive
), "\\\\.\\PhysicalDrive%d", PhyDrive
);
532 Handle
= CreateFileA(Drive
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
533 if (Handle
== INVALID_HANDLE_VALUE
)
535 vlog("CreateFileA %s failed %u\n", Drive
, LASTERR
);
539 pGPT
= zalloc(sizeof(VTOY_GPT_INFO
));
545 bRet
= ReadFile(Handle
, pGPT
, sizeof(VTOY_GPT_INFO
), &dwBytes
, NULL
);
546 if (!bRet
|| dwBytes
!= sizeof(VTOY_GPT_INFO
))
548 vlog("ReadFile failed %u\n", LASTERR
);
552 if (memcmp(pGPT
->MBR
.BootCode
, MbrData
, 0x30) || memcmp(pGPT
->MBR
.BootCode
+ 0x190, MbrData
+ 0x30, 0x10))
554 vlog("Invalid MBR Code %u\n", LASTERR
);
558 if (pGPT
->MBR
.PartTbl
[0].FsFlag
== 0xEE)
560 if (memcmp(pGPT
->Head
.Signature
, "EFI PART", 8))
562 vlog("Invalid GPT Signature\n");
566 Part2Offset
= pGPT
->PartTbl
[1].StartLBA
;
567 disk
->cur_part_style
= 1;
571 Part2Offset
= pGPT
->MBR
.PartTbl
[1].StartSectorId
;
572 disk
->cur_part_style
= 0;
576 g_FatPhyDrive
= Handle
;
577 g_Part2StartSec
= Part2Offset
;
581 if (0 == fl_attach_media(VentoyFatDiskRead
, NULL
))
583 ret
= GetVentoyVersionFromFatFile(disk
->cur_ventoy_ver
, sizeof(disk
->cur_ventoy_ver
));
586 flfile
= fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
589 disk
->cur_secureboot
= 1;
599 CHECK_CLOSE_HANDLE(Handle
);
603 int CheckRuntimeEnvironment(char Letter
, ventoy_disk
*disk
)
609 CHAR Vendor
[128] = { 0 };
610 CHAR Product
[128] = { 0 };
611 CHAR FsName
[MAX_PATH
];
613 PhyDrive
= GetPhyDriveByLogicalDrive(Letter
, &Offset
);
616 vlog("GetPhyDriveByLogicalDrive failed %d %llu\n", PhyDrive
, (ULONGLONG
)Offset
);
619 if (Offset
!= 1048576)
621 vlog("Partition offset is NOT 1MB. This is NOT ventoy image partition (%llu)\n", (ULONGLONG
)Offset
);
625 if (GetPhyDriveInfo(PhyDrive
, &Offset
, Vendor
, Product
) != 0)
627 vlog("GetPhyDriveInfo failed\n");
631 sprintf_s(disk
->cur_capacity
, sizeof(disk
->cur_capacity
), "%dGB", (int)ventoy_get_human_readable_gb(Offset
));
632 sprintf_s(disk
->cur_model
, sizeof(disk
->cur_model
), "%s %s", Vendor
, Product
);
634 scnprintf(Drive
, sizeof(Drive
), "%C:\\", Letter
);
635 if (0 == GetVolumeInformationA(Drive
, NULL
, 0, NULL
, NULL
, &FsFlag
, FsName
, MAX_PATH
))
637 vlog("GetVolumeInformationA failed %u\n", LASTERR
);
641 if (_stricmp(FsName
, "NTFS") == 0)
646 strlcpy(disk
->cur_fsname
, FsName
);
648 if (GetVentoyVersion(PhyDrive
, disk
) != 0)
650 vlog("GetVentoyVersion failed %u\n", LASTERR
);
659 static volatile int g_thread_stop
= 0;
660 static HANDLE g_writeback_thread
;
661 static HANDLE g_writeback_event
;
663 DWORD WINAPI
ventoy_local_thread_run(LPVOID lpParameter
)
665 ventoy_http_writeback_pf callback
= (ventoy_http_writeback_pf
)lpParameter
;
669 WaitForSingleObject(g_writeback_event
, INFINITE
);
684 void ventoy_set_writeback_event(void)
686 SetEvent(g_writeback_event
);
690 int ventoy_start_writeback_thread(ventoy_http_writeback_pf callback
)
693 g_writeback_event
= CreateEventA(NULL
, FALSE
, FALSE
, "VTOYWRBK");
694 g_writeback_thread
= CreateThread(NULL
, 0, ventoy_local_thread_run
, callback
, 0, NULL
);
700 void ventoy_stop_writeback_thread(void)
703 ventoy_set_writeback_event();
705 WaitForSingleObject(g_writeback_thread
, INFINITE
);
707 CHECK_CLOSE_HANDLE(g_writeback_thread
);
708 CHECK_CLOSE_HANDLE(g_writeback_event
);
711 int ventoy_read_file_to_buf(const char *FileName
, int ExtLen
, void **Bufer
, int *BufLen
)
719 WCHAR FilePathW
[MAX_PATH
];
721 UTF8
= IsUTF8Encode(FileName
);
724 Utf8ToUtf16(FileName
, FilePathW
);
725 hFile
= CreateFileW(FilePathW
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
729 hFile
= CreateFileA(FileName
, GENERIC_READ
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
732 if (hFile
== INVALID_HANDLE_VALUE
)
734 vlog("Failed to open %s %u\n", FileName
, LASTERR
);
738 Size
= (int)GetFileSize(hFile
, NULL
);
739 buffer
= malloc(Size
+ ExtLen
);
742 vlog("Failed to alloc file buffer\n");
747 bRet
= ReadFile(hFile
, buffer
, (DWORD
)Size
, &dwBytes
, NULL
);
748 if ((!bRet
) || ((DWORD
)Size
!= dwBytes
))
750 vlog("Failed to read file <%s> %u err:%u", FileName
, dwBytes
, LASTERR
);
763 int ventoy_write_buf_to_file(const char *FileName
, void *Bufer
, int BufLen
)
769 hFile
= CreateFileA(FileName
, GENERIC_READ
| GENERIC_WRITE
, 0, 0, CREATE_ALWAYS
, 0, 0);
770 if (hFile
== INVALID_HANDLE_VALUE
)
772 vlog("CreateFile %s failed %u\n", FileName
, LASTERR
);
776 bRet
= WriteFile(hFile
, Bufer
, (DWORD
)BufLen
, &dwBytes
, NULL
);
778 if ((!bRet
) || ((DWORD
)BufLen
!= dwBytes
))
780 vlog("Failed to write file <%s> %u err:%u", FileName
, dwBytes
, LASTERR
);
785 FlushFileBuffers(hFile
);
791 int ventoy_copy_file(const char *a
, const char *b
)
793 CopyFileA(a
, b
, FALSE
);