diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..294f2a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode/ +*.o +*.res +*.exe diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b3971f4 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +ARCH:=x86_64 +CC=$(ARCH)-w64-mingw32-gcc +WINDRES=$(ARCH)-w64-mingw32-windres +CFLAGS=-municode -O2 +LDFLAGS=$(CFLAGS) -mconsole -lwinmm -ldismapi + +all: enabler + +%.res: %.rc + $(WINDRES) -o $@ -i $< + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) + +enabler: main.o resource.res + $(CC) -o enabler.exe main.o resource.res $(LDFLAGS) + +clean: + $(RM) resource.res + $(RM) main.o + $(RM) enabler.exe + +.PHONY: all diff --git a/README.md b/README.md new file mode 100644 index 0000000..41bee3d --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# ReFS Enabler (Untested) +Patch `boot.wim` to enable Windows 11 or Windows Server vNext to be able to install on ReFS partitions natively. +## Usage +1. Extract or copy `boot.wim` from installation media(ISO or USB drive). +2. Download latest prebuilt package from [Releases](https://github.com/QianNangong/ReFS-Enabler/releases) page. +3. Drop `boot.wim` into `enabler.exe`. +4. Copy `boot.wim` back into the installation media. +5. Done! +## System requirements +Any Windows OS from Windows 7. +## Suitable image version +- Windows 11 build 25281 +- Windows Server vNext build 25284 diff --git a/admin.manifest b/admin.manifest new file mode 100644 index 0000000..6156032 --- /dev/null +++ b/admin.manifest @@ -0,0 +1,21 @@ + + + + + Description of your application + + + + + + + + + + \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..583e8e6 --- /dev/null +++ b/main.c @@ -0,0 +1,173 @@ +#include +#include + +#include +#include + +static const WCHAR CHARSET[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +UINT WINAPI GenerateRandomString(PWSTR lpBuffer, UINT* dwLength) +{ + if (*dwLength == 0) + { + *dwLength = 8; + } + for (UINT i = 0; i < *dwLength; ++i) + { + lpBuffer[i] = CHARSET[rand() % (sizeof(CHARSET) / sizeof(WCHAR))]; + } + return *dwLength; +} + +PWSTR WINAPI GetTempMountPath(void) +{ + static WCHAR szTempMountPath[32767] = { 0 }; + if (szTempMountPath[0] != L'\0') + { + return szTempMountPath; + } + WCHAR szSysTempPath[MAX_PATH]; + GetTempPathW(MAX_PATH, szSysTempPath); + WCHAR szTempBuf[9] = { 0 }; + UINT nCount = 8; + GenerateRandomString(szTempBuf, &nCount); + snwprintf(szTempMountPath, 32767, L"\\\\?\\%s\\%s", szSysTempPath, szTempBuf); + if (!CreateDirectoryW(szTempMountPath, NULL)) + { + szTempMountPath[0] = '\0'; + return GetTempMountPath(); + } + return szTempMountPath; +} + +PWSTR WINAPI MountImage(PCWSTR pWimFile, UINT nIndex) +{ + if (FAILED(DismMountImage(pWimFile, GetTempMountPath(), nIndex, NULL, DismImageIndex, DISM_MOUNT_READWRITE, NULL, NULL, NULL))) + { + return NULL; + } + return GetTempMountPath(); +} + +BOOL WINAPI EnableReFSInRegistry(PCWSTR pMountPath) +{ + HKEY hSystemRootKey = INVALID_HANDLE_VALUE; + HKEY hFeatureKey = INVALID_HANDLE_VALUE; + WCHAR szRegistryKeyFile[32768] = { 0 }; + snwprintf(szRegistryKeyFile, 32767, L"%s\\Windows\\System32\\config\\SYSTEM"); + LRESULT lr = RegLoadAppKeyW(szRegistryKeyFile, &hSystemRootKey, KEY_ALL_ACCESS, REG_PROCESS_APPKEY, 0); + if (lr != ERROR_SUCCESS || hSystemRootKey == INVALID_HANDLE_VALUE) + { + + return FALSE; + } + lr = RegCreateKeyExW(hSystemRootKey, L"ControlSet001\\Control\\FeatureManagement\\Overrides\\8\\3689412748", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hFeatureKey, NULL); + if (lr != ERROR_SUCCESS || hFeatureKey == INVALID_HANDLE_VALUE) + { + RegCloseKey(hSystemRootKey); + return FALSE; + } + const DWORD dwEnabledState = 2; + const DWORD dwEnabledStateOptions = 0; + RegSetKeyValueW(hFeatureKey, NULL, L"EnabledState", REG_DWORD, &dwEnabledState, sizeof(DWORD)); + RegSetKeyValueW(hFeatureKey, NULL, L"EnabledStateOptions", REG_DWORD, &dwEnabledStateOptions, sizeof(DWORD)); + RegCloseKey(hFeatureKey); + RegCloseKey(hFeatureKey); + return TRUE; +} + +BOOL WINAPI UnmountImage(PCWSTR pMountPath) +{ + return SUCCEEDED(DismUnmountImage(pMountPath, DISM_COMMIT_IMAGE, NULL, NULL, NULL)); +} + +UINT WINAPI EnableReFS(PCWSTR pWimFile) +{ + UINT nImgCount = 0; + DismImageInfo* pImgInfo = NULL; + if (FAILED(DismGetImageInfo(pWimFile, &pImgInfo, &nImgCount))) + { + fwprintf(stderr, L"Failed to open wim file %s. HRESULT = 0x%08lX", pWimFile, GetLastError()); + return 0; + } + if (nImgCount == 0) + { + fwprintf(stderr, L"No suitable image found in %s.", pWimFile); + return 0; + } + UINT nEnabled = 0; + for (UINT i = 0; i < nImgCount; ++i) + { + BOOL bSuccess = TRUE; + DismImageInfo info = pImgInfo[i]; + if (info.MajorVersion < 10 || info.Build < 25281 || !lstrcmpW(L"WindowsPE", info.EditionId) || !lstrcmpW(L"WindowsPE", info.InstallationType)) + { + continue; + } + fwprintf(stdout, L"Enable ReFS Installation on image \"%s\" #%u...\r\n", info.ImageName, info.ImageIndex); + PWSTR pszMountPath = MountImage(pWimFile, i); + if (pszMountPath == NULL) + { + fwprintf(stderr, L"[#%u] Failed to mount image. HRESULT = 0x%08lX\r\n", info.ImageIndex, GetLastError()); + continue; + } + else + { + fwprintf(stdout, L"[#%u] Image mounted.\r\n", info.ImageIndex); + } + if (!EnableReFSInRegistry(pszMountPath)) + { + fwprintf(stderr, L"[#%u] Failed to enable ReFS in registry. HRESULT = 0x%08lx\r\n", info.ImageIndex, GetLastError()); + bSuccess = FALSE; + } + else + { + fwprintf(stdout, L"[#%u] Feature 42189933 enabled.\r\n", info.ImageIndex); + } + if (!UnmountImage(pszMountPath)) + { + fwprintf(stderr, L"[#%u] Failed to unmount image. HRESULT = 0x%08lx\r\n", info.ImageIndex, GetLastError()); + continue; + } + else + { + fwprintf(stdout, L"[#%u] Image committed and unmounted.\r\n", info.ImageIndex); + } + if (bSuccess) + { + ++nEnabled; + } + } + DismDelete(pImgInfo); + RemoveDirectoryW(GetTempMountPath()); + return nEnabled; +} + +int wmain(int argc, const wchar_t* argv[]) +{ + if (argc != 2) + { + fwprintf(stderr, L"Usage:\r\n\t%s path\\to\\boot.wim", argv[0]); + return 0; + } + + if (FAILED(DismInitialize(DismLogErrorsWarnings, NULL, NULL))) + { + fwprintf(stderr, L"Failed to initialize Dism. HRESULT = 0x%08lX", GetLastError()); + return 0; + } + srand((unsigned int)timeGetTime()); + WCHAR szWimFile[32768] = { 0 }; + snwprintf(szWimFile, 32767, L"\\\\?\\%s", argv[1]); + UINT nEnabled = EnableReFS(szWimFile); + if (nEnabled != 0) + { + fwprintf(stdout, L"Done! %d image%s enabled.", nEnabled, nEnabled == 1 ? L"": L"s"); + } + else + { + fwprintf(stderr, L"No suitable image found in %s.", argv[1]); + } + DismShutdown(); + return 0; +} \ No newline at end of file diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..ab7a0c2 --- /dev/null +++ b/resource.rc @@ -0,0 +1,2 @@ +#include +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "admin.manifest" \ No newline at end of file