diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4a76835..36231bf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,47 +1,43 @@
-on: push
-jobs:
- build-mingw:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@master
- - env:
- MSYSTEM: MINGW64
- run: |
- $ErrorActionPreference = "Continue"
- $msys2_filename = "msys2-base-x86_64-20190524"
- Invoke-WebRequest -Uri "http://repo.msys2.org/distrib/x86_64/$($msys2_filename).tar.xz" -OutFile "$Env:TEMP\$($msys2_filename).tar.xz"
- 7z x -o"$Env:TEMP" "$Env:TEMP\$($msys2_filename).tar.xz"
- 7z x -oC:\ "$Env:TEMP\$($msys2_filename).tar"
- C:\msys64\usr\bin\bash.exe -l -c "pacman -Syu --noconfirm --noprogressbar"
- C:\msys64\usr\bin\bash.exe -l -c "pacman -Syu --needed --noconfirm --noprogressbar base-devel git mingw-w64-x86_64-toolchain mingw-w64-x86_64-libarchive mingw-w64-x86_64-boost mingw-w64-x86_64-tinyxml2"
- git fetch --unshallow --tags
- $version = (git describe --tags).Substring(1)
- C:\msys64\usr\bin\bash.exe -l -c "cd /d/a/LxRunOffline/LxRunOffline && make"
- if ($LASTEXITCODE -ne 0) { Exit 1 }
- C:\msys64\mingw64\bin\strip.exe LxRunOffline.exe
- 7z a release.zip .\LxRunOffline.exe .\LICENSE
- curl.exe -fsS -T release.zip -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/LxRunOffline/LxRunOffline-v$version-mingw.zip
- curl.exe -fsS -X POST -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/publish
- build-msvc:
- runs-on: windows-latest
- steps:
- - uses: actions/checkout@master
- - env:
- VCPKG_DEFAULT_TRIPLET: x64-windows-static
- run: |
- $ErrorActionPreference = "Continue"
- pushd $Env:VCPKG_INSTALLATION_ROOT
- git pull
- .\bootstrap-vcpkg.bat
- popd
- vcpkg integrate install
- vcpkg install libarchive boost-program-options boost-format tinyxml2
- git fetch --unshallow --tags
- $version = (git describe --tags).Substring(1)
- $slashIndex = $version.IndexOf('-')
- $fileVersion = $(if ($slashIndex -ne -1) { $version.Substring(0, $slashIndex) } else { $version }).Replace('.', ',') + ",0"
- & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" /p:Configuration=Release /p:DefineVersion=LXRUNOFFLINE_VERSION=\`"v$version\`" /p:DefineFileVersion=`"LXRUNOFFLINE_FILE_VERSION=$fileVersion`;LXRUNOFFLINE_FILE_VERSION_STR=\\\`"$version\\\`"`"
- if ($LASTEXITCODE -ne 0) { Exit 1 }
- 7z a release.zip .\x64\Release\LxRunOffline.exe .\LICENSE
- curl.exe -fsS -T release.zip -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/LxRunOffline/LxRunOffline-v$version-msvc.zip
- curl.exe -fsS -X POST -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/publish
+on: push
+jobs:
+ build-mingw:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@master
+ - env:
+ MSYSTEM: MINGW64
+ run: |
+ $ErrorActionPreference = "Continue"
+ C:\msys64\usr\bin\bash.exe -l -c "pacman -Syu --noconfirm --noprogressbar"
+ C:\msys64\usr\bin\bash.exe -l -c "pacman -Syu --needed --noconfirm --noprogressbar base-devel cmake git mingw-w64-x86_64-toolchain mingw-w64-x86_64-libarchive mingw-w64-x86_64-boost mingw-w64-x86_64-tinyxml2"
+ git fetch --unshallow --tags
+ C:\msys64\usr\bin\bash.exe -l -c "cd /d/a/LxRunOffline/LxRunOffline && cmake -G 'MSYS Makefiles' -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release . && make DESTDIR=release install/strip -j"
+ if ($LASTEXITCODE -ne 0) { Exit 1 }
+ 7z a release.zip .\release\*
+ $version = (git describe --tags).Substring(1)
+ curl.exe -fsS -T release.zip -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/LxRunOffline/LxRunOffline-v$version-mingw.zip
+ curl.exe -fsS -X POST -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/publish
+ build-msvc:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@master
+ - env:
+ VCPKG_DEFAULT_TRIPLET: x64-windows-static
+ run: |
+ $ErrorActionPreference = "Continue"
+ pushd $Env:VCPKG_INSTALLATION_ROOT
+ git pull
+ .\bootstrap-vcpkg.bat
+ popd
+ vcpkg integrate install
+ vcpkg install libarchive boost-program-options boost-format tinyxml2
+ git fetch --unshallow --tags
+ cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" & set' |
+ foreach { if ($_ -match "=") { $v = $_.split("="); Set-Item -Force -Path "Env:\$($v[0])" -Value "$($v[1])" } }
+ cmake -G "NMake Makefiles" -DCMAKE_TOOLCHAIN_FILE="$Env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET="$Env:VCPKG_DEFAULT_TRIPLET" -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release .
+ nmake DESTDIR=release install
+ if ($LASTEXITCODE -ne 0) { Exit 1 }
+ 7z a release.zip .\release\*
+ $version = (git describe --tags).Substring(1)
+ curl.exe -fsS -T release.zip -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/LxRunOffline/LxRunOffline-v$version-msvc.zip
+ curl.exe -fsS -X POST -u ddosolitary:${{ secrets.BINTRAY_KEY }} https://api.bintray.com/content/ddosolitary/dev-releases/default/default/publish
diff --git a/.gitignore b/.gitignore
index 1d467c6..7bdda93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1 @@
-Debug/
-Release/
-.vs/
-.vscode/
-*.user
-*.log
-*.o
-*.gch
-*.exe
+cmake-build-*/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b1520cb
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/LxRunOffline.iml b/.idea/LxRunOffline.iml
new file mode 100644
index 0000000..6d70257
--- /dev/null
+++ b/.idea/LxRunOffline.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..11165e5
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c5a41a1
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..9661ac7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9af0e50
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.17)
+
+execute_process(
+ COMMAND git describe --tags
+ OUTPUT_VARIABLE LxRunOffline_VERSION_STR
+ RESULT_VARIABLE GIT_RESULT
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+if(GIT_RESULT EQUAL 0)
+ string(SUBSTRING ${LxRunOffline_VERSION_STR} 1 -1 VERSION)
+ string(FIND ${VERSION} - VERSION_SLASH_OFFSET)
+ string(SUBSTRING ${VERSION} 0 ${VERSION_SLASH_OFFSET} VERSION)
+else()
+ message(WARNING "Unable to retrieve version using git.")
+ set(LxRunOffline_VERSION_STR "unknown version")
+ set(VERSION 0.0.0)
+endif()
+
+project(LxRunOffline VERSION ${VERSION})
+
+option(LXRUNOFFLINE_STATIC "Link statically" ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+add_compile_definitions(UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS)
+
+if (NOT CMAKE_BUILD_TYPE STREQUAL Debug)
+ # Build fails when linking Boost statically using MinGW.
+ if(NOT LXRUNOFFLINE_STATIC OR NOT MINGW)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
+ endif()
+endif()
+
+if(MSVC)
+ add_compile_definitions(NOMINMAX)
+ add_compile_options(/W3 /wd4068)
+ set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)
+ if(NOT LXRUNOFFLINE_STATIC)
+ string(APPEND CMAKE_MSVC_RUNTIME_LIBRARY DLL)
+ endif()
+elseif(MINGW)
+ add_link_options(-municode)
+ add_compile_options(-Wall -Wextra -Wpedantic -Wno-unknown-pragmas -Wno-parentheses)
+ if(LXRUNOFFLINE_STATIC)
+ add_link_options(-static -static-libgcc -static-libstdc++)
+ list(PREPEND CMAKE_FIND_LIBRARY_SUFFIXES .a)
+ endif()
+else()
+ message(WARNING "Only MinGW and MSVC compilers are supported.")
+endif()
+
+add_subdirectory(src)
+
+install(FILES LICENSE LICENSE-3RD-PARTY DESTINATION .)
diff --git a/LICENSE b/LICENSE
index a386b91..3eeaacf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,21 @@
-The MIT License (MIT)
-
-Copyright (c) 2016-2020 DDoSolitary
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+The MIT License (MIT)
+
+Copyright (c) 2016-2020 DDoSolitary
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE-3RD-PARTY b/LICENSE-3RD-PARTY
index bc2f98f..4df0847 100644
--- a/LICENSE-3RD-PARTY
+++ b/LICENSE-3RD-PARTY
@@ -1,63 +1,63 @@
-Boost
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-
-libarchive
-
-Copyright (c) 2003-2009
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer
- in this position and unchanged.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-TinyXML-2
-
-This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
+Boost
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+libarchive
+
+Copyright (c) 2003-2009
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer
+ in this position and unchanged.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+TinyXML-2
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
diff --git a/LxRunOffline.sln b/LxRunOffline.sln
deleted file mode 100644
index 90974a7..0000000
--- a/LxRunOffline.sln
+++ /dev/null
@@ -1,25 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27520.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LxRunOffline", "LxRunOffline\LxRunOffline.vcxproj", "{5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}.Debug|x64.ActiveCfg = Debug|x64
- {5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}.Debug|x64.Build.0 = Debug|x64
- {5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}.Release|x64.ActiveCfg = Release|x64
- {5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}.Release|x64.Build.0 = Release|x64
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {59503436-8BF3-4B2C-987A-7E8B87FC699F}
- EndGlobalSection
-EndGlobal
diff --git a/LxRunOffline/LxRunOffline.vcxproj b/LxRunOffline/LxRunOffline.vcxproj
deleted file mode 100644
index ae387f9..0000000
--- a/LxRunOffline/LxRunOffline.vcxproj
+++ /dev/null
@@ -1,127 +0,0 @@
-
-
-
-
- Debug
- x64
-
-
- Release
- x64
-
-
-
- 15.0
- {5A161CAE-91C2-4427-8C42-ED4F2C24DB1A}
- Win32Proj
- LxRunOffline
- 10.0
- x64-windows-static
-
-
-
- Application
- true
- v142
- Unicode
-
-
- Application
- false
- v142
- true
- Unicode
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
- false
-
-
-
- Use
- Level3
- Disabled
- true
- _DEBUG;_CONSOLE;$(DefineVersion);%(PreprocessorDefinitions)
- true
- MultiThreadedDebug
-
-
- Console
- true
- crypt32.lib;ws2_32.lib;%(AdditionalDependencies)
-
-
- _UNICODE;UNICODE;$(DefineFileVersion);%(PreprocessorDefinitions)
-
-
-
-
- Use
- Level3
- MaxSpeed
- true
- true
- true
- NDEBUG;_CONSOLE;$(DefineVersion);%(PreprocessorDefinitions)
- true
- MultiThreaded
-
-
- Console
- true
- true
- true
- crypt32.lib;ws2_32.lib;%(AdditionalDependencies)
-
-
- _UNICODE;UNICODE;$(DefineFileVersion);%(PreprocessorDefinitions)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create
- Create
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LxRunOffline/LxRunOffline.vcxproj.filters b/LxRunOffline/LxRunOffline.vcxproj.filters
deleted file mode 100644
index d51a858..0000000
--- a/LxRunOffline/LxRunOffline.vcxproj.filters
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
-
-
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
-
-
-
-
-
diff --git a/LxRunOffline/stdafx.cpp b/LxRunOffline/stdafx.cpp
deleted file mode 100644
index 5f644fd..0000000
--- a/LxRunOffline/stdafx.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "stdafx.h"
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 960dbc2..0000000
--- a/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-CC := g++
-CPPFLAGS := -std=c++17 -DUNICODE -D_UNICODE -D_WIN32_WINNT=0x0A00
-LDFLAGS := -municode -static -static-libgcc -static-libstdc++
-LDLIBS := -lntdll -lole32 -luuid -larchive -lboost_program_options-mt -ltinyxml2 -lexpat -lbz2 -llz4 -liconv -llzma -lz -lnettle -lzstd -lbcrypt
-
-PROJ := LxRunOffline
-SRCS := $(filter-out $(PROJ)/stdafx.cpp, $(wildcard $(PROJ)/*.cpp))
-OBJS := $(patsubst $(PROJ)/%.cpp, $(PROJ)/%.o, $(SRCS)) $(PROJ)/resources.o
-TARGET := $(PROJ).exe
-OUTPUT := $(OBJS) $(PROJ)/stdafx.h.gch $(TARGET)
-
-VERSION := $(shell git describe --tags | cut -c 2-)
-FILE_VERSION := $(shell echo $(VERSION) | grep -o "^[^-]*" | tr . ,),0
-
-$(TARGET): $(OBJS)
- $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
-
-$(PROJ)/%.o: $(PROJ)/%.cpp $(PROJ)/stdafx.h.gch
- $(CC) $(CPPFLAGS) -DLXRUNOFFLINE_VERSION='"'v$(VERSION)'"' -c -o $@ $<
-
-$(PROJ)/stdafx.h.gch: $(PROJ)/stdafx.cpp $(PROJ)/stdafx.h
- $(CC) $(CPPFLAGS) -x c++-header -o $@ $<
-
-$(PROJ)/resources.o: $(PROJ)/resources.rc $(PROJ)/app.manifest
- windres -DLXRUNOFFLINE_FILE_VERSION=$(FILE_VERSION) -DLXRUNOFFLINE_FILE_VERSION_STR='\"'$(VERSION)'\"' $< $@
-
-clean:
- rm -rf $(OUTPUT)
diff --git a/choco/tools/chocolateyInstall.ps1 b/choco/tools/chocolateyInstall.ps1
index b28ac25..15e383e 100644
--- a/choco/tools/chocolateyInstall.ps1
+++ b/choco/tools/chocolateyInstall.ps1
@@ -1,4 +1,4 @@
-$version = [Environment]::OSVersion.Version
+$version = [Environment]::OSVersion.Version
if ($version.Major -ne 10 -or $version.Build -lt 17134) {
throw "This package requires Windows 10 v1803 or later."
}
diff --git a/choco/tools/chocolateyUninstall.ps1 b/choco/tools/chocolateyUninstall.ps1
index 99d5a10..92b46eb 100644
--- a/choco/tools/chocolateyUninstall.ps1
+++ b/choco/tools/chocolateyUninstall.ps1
@@ -1,4 +1,4 @@
-$unzipLocation = Join-Path $Env:ChocolateyToolsLocation 'lxrunoffline'
+$unzipLocation = Join-Path $Env:ChocolateyToolsLocation 'lxrunoffline'
if (Test-Path $unzipLocation) {
rm -Recurse $unzipLocation
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..59ddc6b
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,58 @@
+add_executable(LxRunOffline
+ error.cpp
+ fs.cpp
+ main.cpp
+ path.cpp
+ reg.cpp
+ shortcut.cpp
+ utils.cpp
+ res/resources.rc)
+
+configure_file(config.h.in config.h)
+target_include_directories(LxRunOffline PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+
+target_precompile_headers(LxRunOffline PRIVATE stdafx.h)
+target_link_libraries(LxRunOffline PRIVATE ntdll)
+if(MSVC)
+ target_link_libraries(LxRunOffline PRIVATE ws2_32 crypt32)
+endif()
+
+find_package(LibArchive REQUIRED)
+target_link_libraries(LxRunOffline PRIVATE LibArchive::LibArchive)
+if (LXRUNOFFLINE_STATIC AND MINGW)
+ find_package(ZLIB REQUIRED)
+ find_package(BZip2 REQUIRED)
+ find_package(LibLZMA REQUIRED)
+ find_package(EXPAT REQUIRED)
+ find_package(Iconv REQUIRED)
+ find_library(LZ4_LIBRARY lz4 REQUIRED)
+ find_library(ZSTD_LIBRARY zstd REQUIRED)
+ target_link_libraries(LibArchive::LibArchive INTERFACE
+ ZLIB::ZLIB
+ BZip2::BZip2
+ LibLZMA::LibLZMA
+ EXPAT::EXPAT
+ Iconv::Iconv
+ ${LZ4_LIBRARY}
+ ${ZSTD_LIBRARY}
+ bcrypt)
+endif()
+
+if(MSVC)
+ find_package(tinyxml2 REQUIRED)
+ target_link_libraries(LxRunOffline PRIVATE tinyxml2::tinyxml2)
+else()
+ # The config provided by tinyxml2 doesn't support static linking so we neeed to find it manually.
+ find_library(TINYXML2_LIBRARY tinyxml2 REQUIRED)
+ find_path(TINYXML2_INCLUDE_DIR tinyxml2.h REQUIRED)
+ target_link_libraries(LxRunOffline PRIVATE ${TINYXML2_LIBRARY})
+ target_include_directories(LxRunOffline PRIVATE ${TINYXML2_INCLUDE_DIR})
+ if(CMAKE_BUILD_TYPE STREQUAL Debug)
+ target_compile_definitions(LxRunOffline PRIVATE TINYXML2_DEBUG)
+ endif()
+endif()
+
+find_package(Boost REQUIRED COMPONENTS program_options)
+target_link_libraries(LxRunOffline PRIVATE Boost::headers Boost::program_options)
+
+install(TARGETS LxRunOffline RUNTIME DESTINATION .)
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..73a2919
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,4 @@
+#define LXRUNOFFLINE_VERSION_MAJOR @LxRunOffline_VERSION_MAJOR@
+#define LXRUNOFFLINE_VERSION_MINOR @LxRunOffline_VERSION_MINOR@
+#define LXRUNOFFLINE_VERSION_PATCH @LxRunOffline_VERSION_PATCH@
+#define LXRUNOFFLINE_VERSION_STR "@LxRunOffline_VERSION_STR@"
diff --git a/LxRunOffline/error.cpp b/src/error.cpp
similarity index 97%
rename from LxRunOffline/error.cpp
rename to src/error.cpp
index 01bec3d..417ef2a 100644
--- a/LxRunOffline/error.cpp
+++ b/src/error.cpp
@@ -1,114 +1,114 @@
-#include "stdafx.h"
-#include "error.h"
-
-const wstr msg_table[] = {
- L"Couldn't open the file \"%1%\".",
- L"Couldn't open the directory \"%1%\".",
- L"Couldn't create the file \"%1%\".",
- L"Couldn't create the directory \"%1%\".",
- L"Couldn't delete the file \"%1%\".",
- L"Couldn't delete the directory \"%1%\".",
- L"Couldn't get contents of the directory \"%1%\".",
- L"Couldn't get information of the file \"%1%\".",
- L"Couldn't get size of the file \"%1%\".",
- L"Couldn't get the extended attribute \"%1%\" of the file or directory \"%2%\".",
- L"Couldn't set the extended attribute \"%1%\" of the file or directory \"%2%\".",
- L"The extended attribute \"%1%\" of the file or directory \"%2%\" is invalid.",
- L"Couldn't set the case sensitive attribute of the directory \"%1%\".",
- L"Couldn't get time information of the file or directory \"%1%\".",
- L"Couldn't set time information of the file or directory \"%1%\".",
- L"Couldn't get reparse information of the file \"%1%\".",
- L"Couldn't set reparse information of the file \"%1%\".",
- L"The symlink file \"%1%\" has an invalid length %2%.",
- L"Couldn't create the hard link from \"%1%\" to \"%2%\".",
- L"Couldn't read from the file \"%1%\".",
- L"Couldn't write to the file \"%1%\".",
- L"Couldn't recognize the path \"%1%\".",
- L"Couldn't convert the encoding of a string.",
- L"Error occurred while processing the archive: %1%",
- L"Couldn't get Windows version information. \"%1%\"",
- L"Windows 10 v%1% (v10.0.%2%) or later is required. Please upgrade your system.",
- L"Couldn't open or create the registry key \"%1%\".",
- L"Couldn't delete the registry key \"%1%\".",
- L"Couldn't get subkeys of the registry key \"%1%\".",
- L"Couldn't get the value \"%2%\" of the registry key \"%1%\".",
- L"Couldn't set the value \"%2%\" of the registry key \"%1%\".",
- L"Couldn't delete the value \"%2%\" of the registry key \"%1%\".",
- L"Couldn't create a GUID.",
- L"Couldn't convert a GUID to a string.",
- L"Couldn't find the distro \"%1%\".",
- L"The distro \"%1%\" already exists.",
- L"The distro \"%1%\" has running processes and can't be operated. \"wsl -t \" or \"wsl --shutdown\" might help.",
- L"Couldn't find a valid default distribution.",
- L"No action is specified.",
- L"The action \"%1%\" is not recognized.",
- L"Couldn't load wslapi.dll. Please make sure that WSL has been installed.",
- L"Error occurred when trying to launch the distro \"%1%\".",
- L"Error occurred when creating the shortcut.",
- L"The environment variable \"%1%\" is invalid.",
- L"The environment variable \"%1%\" already exists.",
- L"Environment variable \"%1%\" not found.",
- L"Error occurred while processing the config file: %1%",
- L"Filesystem version %1% is not recognized.",
- L"Failed to detect filesystem version of the directory \"%1%\".",
- L"Installing to the root directory \"%1%\" is known to cause issues.",
- L"The configuration flags are invalid.",
- L"The action/argument \"%1%\" doesn't support WSL2.",
- L"Copying or moving into a subdirectory of the source directory is not allowed."
-};
-
-lro_error::lro_error(const err_msg msg_code, std::vector msg_args, const HRESULT err_code)
- : msg_code(msg_code), msg_args(std::move(msg_args)), err_code(err_code) {}
-
-
-lro_error lro_error::from_hresult(const err_msg msg_code, std::vector msg_args, const HRESULT err_code) {
- return lro_error(msg_code, std::move(msg_args), err_code);
-
-}
-
-lro_error lro_error::from_win32(const err_msg msg_code, std::vector msg_args, const uint32_t err_code) {
- return from_hresult(msg_code, std::move(msg_args), HRESULT_FROM_WIN32(err_code));
-}
-
-lro_error lro_error::from_win32_last(const err_msg msg_code, std::vector msg_args) {
- return from_win32(msg_code, std::move(msg_args), GetLastError());
-}
-
-lro_error lro_error::from_nt(const err_msg msg_code, std::vector msg_args, const NTSTATUS err_code) {
- return from_hresult(msg_code, std::move(msg_args), HRESULT_FROM_NT(err_code));
-}
-
-lro_error lro_error::from_other(const err_msg msg_code, std::vector msg_args) {
- return from_hresult(msg_code, std::move(msg_args), S_OK);
-}
-
-wstr lro_error::format() const {
- std::wstringstream ss;
-
- auto fmt = boost::wformat(msg_table[static_cast(msg_code)]);
- for (crwstr s : msg_args) fmt = fmt % s;
- ss << fmt << std::endl;
-
- if (err_code != 0) {
- ss << L"Reason: ";
- if ((err_code & FACILITY_NT_BIT) != 0) {
- auto stat = err_code & ~FACILITY_NT_BIT;
- wchar_t *buf = nullptr;
- auto f = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS;
- auto hm = LoadLibrary(L"ntdll.dll");
- auto ok = hm && FormatMessage(f, hm, stat, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast(&buf), 0, nullptr);
- if (hm) FreeLibrary(hm);
- if (ok) {
- ss << buf;
- LocalFree(buf);
- } else {
- ss << L"Unknown NTSTATUS: " << L"0x" << std::setfill(L'0') << std::setw(8) << std::hex << stat;
- }
- } else {
- _com_error ce(err_code);
- ss << ce.ErrorMessage();
- }
- }
-
- return ss.str();
-}
+#include "stdafx.h"
+#include "error.h"
+
+const wstr msg_table[] = {
+ L"Couldn't open the file \"%1%\".",
+ L"Couldn't open the directory \"%1%\".",
+ L"Couldn't create the file \"%1%\".",
+ L"Couldn't create the directory \"%1%\".",
+ L"Couldn't delete the file \"%1%\".",
+ L"Couldn't delete the directory \"%1%\".",
+ L"Couldn't get contents of the directory \"%1%\".",
+ L"Couldn't get information of the file \"%1%\".",
+ L"Couldn't get size of the file \"%1%\".",
+ L"Couldn't get the extended attribute \"%1%\" of the file or directory \"%2%\".",
+ L"Couldn't set the extended attribute \"%1%\" of the file or directory \"%2%\".",
+ L"The extended attribute \"%1%\" of the file or directory \"%2%\" is invalid.",
+ L"Couldn't set the case sensitive attribute of the directory \"%1%\".",
+ L"Couldn't get time information of the file or directory \"%1%\".",
+ L"Couldn't set time information of the file or directory \"%1%\".",
+ L"Couldn't get reparse information of the file \"%1%\".",
+ L"Couldn't set reparse information of the file \"%1%\".",
+ L"The symlink file \"%1%\" has an invalid length %2%.",
+ L"Couldn't create the hard link from \"%1%\" to \"%2%\".",
+ L"Couldn't read from the file \"%1%\".",
+ L"Couldn't write to the file \"%1%\".",
+ L"Couldn't recognize the path \"%1%\".",
+ L"Couldn't convert the encoding of a string.",
+ L"Error occurred while processing the archive: %1%",
+ L"Couldn't get Windows version information. \"%1%\"",
+ L"Windows 10 v%1% (v10.0.%2%) or later is required. Please upgrade your system.",
+ L"Couldn't open or create the registry key \"%1%\".",
+ L"Couldn't delete the registry key \"%1%\".",
+ L"Couldn't get subkeys of the registry key \"%1%\".",
+ L"Couldn't get the value \"%2%\" of the registry key \"%1%\".",
+ L"Couldn't set the value \"%2%\" of the registry key \"%1%\".",
+ L"Couldn't delete the value \"%2%\" of the registry key \"%1%\".",
+ L"Couldn't create a GUID.",
+ L"Couldn't convert a GUID to a string.",
+ L"Couldn't find the distro \"%1%\".",
+ L"The distro \"%1%\" already exists.",
+ L"The distro \"%1%\" has running processes and can't be operated. \"wsl -t \" or \"wsl --shutdown\" might help.",
+ L"Couldn't find a valid default distribution.",
+ L"No action is specified.",
+ L"The action \"%1%\" is not recognized.",
+ L"Couldn't load wslapi.dll. Please make sure that WSL has been installed.",
+ L"Error occurred when trying to launch the distro \"%1%\".",
+ L"Error occurred when creating the shortcut.",
+ L"The environment variable \"%1%\" is invalid.",
+ L"The environment variable \"%1%\" already exists.",
+ L"Environment variable \"%1%\" not found.",
+ L"Error occurred while processing the config file: %1%",
+ L"Filesystem version %1% is not recognized.",
+ L"Failed to detect filesystem version of the directory \"%1%\".",
+ L"Installing to the root directory \"%1%\" is known to cause issues.",
+ L"The configuration flags are invalid.",
+ L"The action/argument \"%1%\" doesn't support WSL2.",
+ L"Copying or moving into a subdirectory of the source directory is not allowed."
+};
+
+lro_error::lro_error(const err_msg msg_code, std::vector msg_args, const HRESULT err_code)
+ : msg_code(msg_code), msg_args(std::move(msg_args)), err_code(err_code) {}
+
+
+lro_error lro_error::from_hresult(const err_msg msg_code, std::vector msg_args, const HRESULT err_code) {
+ return lro_error(msg_code, std::move(msg_args), err_code);
+
+}
+
+lro_error lro_error::from_win32(const err_msg msg_code, std::vector msg_args, const uint32_t err_code) {
+ return from_hresult(msg_code, std::move(msg_args), HRESULT_FROM_WIN32(err_code));
+}
+
+lro_error lro_error::from_win32_last(const err_msg msg_code, std::vector msg_args) {
+ return from_win32(msg_code, std::move(msg_args), GetLastError());
+}
+
+lro_error lro_error::from_nt(const err_msg msg_code, std::vector msg_args, const NTSTATUS err_code) {
+ return from_hresult(msg_code, std::move(msg_args), HRESULT_FROM_NT(err_code));
+}
+
+lro_error lro_error::from_other(const err_msg msg_code, std::vector msg_args) {
+ return from_hresult(msg_code, std::move(msg_args), S_OK);
+}
+
+wstr lro_error::format() const {
+ std::wstringstream ss;
+
+ auto fmt = boost::wformat(msg_table[static_cast(msg_code)]);
+ for (crwstr s : msg_args) fmt = fmt % s;
+ ss << fmt << std::endl;
+
+ if (err_code != 0) {
+ ss << L"Reason: ";
+ if ((err_code & FACILITY_NT_BIT) != 0) {
+ auto stat = err_code & ~FACILITY_NT_BIT;
+ wchar_t *buf = nullptr;
+ auto f = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS;
+ auto hm = LoadLibrary(L"ntdll.dll");
+ auto ok = hm && FormatMessage(f, hm, stat, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), reinterpret_cast(&buf), 0, nullptr);
+ if (hm) FreeLibrary(hm);
+ if (ok) {
+ ss << buf;
+ LocalFree(buf);
+ } else {
+ ss << L"Unknown NTSTATUS: " << L"0x" << std::setfill(L'0') << std::setw(8) << std::hex << stat;
+ }
+ } else {
+ _com_error ce(err_code);
+ ss << ce.ErrorMessage();
+ }
+ }
+
+ return ss.str();
+}
diff --git a/LxRunOffline/error.h b/src/error.h
similarity index 94%
rename from LxRunOffline/error.h
rename to src/error.h
index d632380..40334d1 100644
--- a/LxRunOffline/error.h
+++ b/src/error.h
@@ -1,74 +1,74 @@
-#pragma once
-#include "stdafx.h"
-
-enum class err_msg {
- err_open_file,
- err_open_dir,
- err_create_file,
- err_create_dir,
- err_delete_file,
- err_delete_dir,
- err_enum_dir,
- err_file_info,
- err_file_size,
- err_get_ea,
- err_set_ea,
- err_invalid_ea,
- err_set_cs,
- err_get_ft,
- err_set_ft,
- err_get_reparse,
- err_set_reparse,
- err_symlink_length,
- err_hard_link,
- err_read_file,
- err_write_file,
- err_transform_path,
- err_convert_encoding,
- err_archive,
- err_get_version,
- err_version_old,
- err_open_key,
- err_delete_key,
- err_enum_key,
- err_get_key_value,
- err_set_key_value,
- err_delete_key_value,
- err_create_guid,
- err_convert_guid,
- err_distro_not_found,
- err_distro_exists,
- err_distro_running,
- err_no_default_distro,
- err_no_action,
- err_invalid_action,
- err_no_wslapi,
- err_launch_distro,
- err_create_shortcut,
- err_invalid_env,
- err_env_exists,
- err_env_not_found,
- err_config_file,
- err_fs_version,
- err_fs_detect,
- err_root_dir,
- err_invalid_flags,
- err_wsl2_unsupported,
- err_copy_subdir
-};
-
-class lro_error : public std::exception {
- lro_error(const err_msg msg_code, std::vector msg_args, HRESULT err_code);
-public:
- err_msg msg_code;
- std::vector msg_args;
- HRESULT err_code;
-
- static lro_error from_hresult(err_msg msg_code, std::vector msg_args, HRESULT err_code);
- static lro_error from_win32(err_msg msg_code, std::vector msg_args, uint32_t err_code);
- static lro_error from_win32_last(err_msg msg_code, std::vector msg_args);
- static lro_error from_nt(err_msg msg_code, std::vector msg_args, NTSTATUS err_code);
- static lro_error from_other(err_msg msg_code, std::vector msg_args);
-
- wstr format() const;
-};
+#pragma once
+#include "stdafx.h"
+
+enum class err_msg {
+ err_open_file,
+ err_open_dir,
+ err_create_file,
+ err_create_dir,
+ err_delete_file,
+ err_delete_dir,
+ err_enum_dir,
+ err_file_info,
+ err_file_size,
+ err_get_ea,
+ err_set_ea,
+ err_invalid_ea,
+ err_set_cs,
+ err_get_ft,
+ err_set_ft,
+ err_get_reparse,
+ err_set_reparse,
+ err_symlink_length,
+ err_hard_link,
+ err_read_file,
+ err_write_file,
+ err_transform_path,
+ err_convert_encoding,
+ err_archive,
+ err_get_version,
+ err_version_old,
+ err_open_key,
+ err_delete_key,
+ err_enum_key,
+ err_get_key_value,
+ err_set_key_value,
+ err_delete_key_value,
+ err_create_guid,
+ err_convert_guid,
+ err_distro_not_found,
+ err_distro_exists,
+ err_distro_running,
+ err_no_default_distro,
+ err_no_action,
+ err_invalid_action,
+ err_no_wslapi,
+ err_launch_distro,
+ err_create_shortcut,
+ err_invalid_env,
+ err_env_exists,
+ err_env_not_found,
+ err_config_file,
+ err_fs_version,
+ err_fs_detect,
+ err_root_dir,
+ err_invalid_flags,
+ err_wsl2_unsupported,
+ err_copy_subdir
+};
+
+class lro_error : public std::exception {
+ lro_error(const err_msg msg_code, std::vector msg_args, HRESULT err_code);
+public:
+ err_msg msg_code;
+ std::vector msg_args;
+ HRESULT err_code;
+
+ static lro_error from_hresult(err_msg msg_code, std::vector msg_args, HRESULT err_code);
+ static lro_error from_win32(err_msg msg_code, std::vector msg_args, uint32_t err_code);
+ static lro_error from_win32_last(err_msg msg_code, std::vector msg_args);
+ static lro_error from_nt(err_msg msg_code, std::vector msg_args, NTSTATUS err_code);
+ static lro_error from_other(err_msg msg_code, std::vector msg_args);
+
+ wstr format() const;
+};
diff --git a/LxRunOffline/fs.cpp b/src/fs.cpp
similarity index 96%
rename from LxRunOffline/fs.cpp
rename to src/fs.cpp
index 5ff5f43..da05de4 100644
--- a/LxRunOffline/fs.cpp
+++ b/src/fs.cpp
@@ -1,726 +1,726 @@
-#include "stdafx.h"
-#include "error.h"
-#include "fs.h"
-#include "ntdll.h"
-#include "utils.h"
-
-enum class enum_dir_type {
- enter,
- exit,
- file
-};
-
-struct lxattrb {
- uint16_t flags;
- uint16_t ver;
- uint32_t mode;
- uint32_t uid;
- uint32_t gid;
- uint32_t rdev;
- uint32_t atime_nsec;
- uint32_t mtime_nsec;
- uint32_t ctime_nsec;
- uint64_t atime;
- uint64_t mtime;
- uint64_t ctime;
-};
-
-IO_STATUS_BLOCK iostat;
-
-bool check_archive(archive *pa, const int stat) {
- if (stat == ARCHIVE_OK) return true;
- if (stat == ARCHIVE_EOF) return false;
- const auto es = archive_error_string(pa);
- std::wstringstream ss;
- if (es) ss << es;
- else ss << L"Unknown error " << archive_errno(pa);
- if (stat == ARCHIVE_WARN) {
- log_warning(ss.str());
- return true;
- }
- throw lro_error::from_other(err_msg::err_archive, { ss.str() });
-}
-
-unique_ptr_del open_file(crwstr path, const bool is_dir, const bool create, const bool no_share = false) {
- const auto h = CreateFile(
- path.c_str(),
- MAXIMUM_ALLOWED, no_share ? 0 : FILE_SHARE_READ, nullptr,
- create ? CREATE_NEW : OPEN_EXISTING,
- is_dir ? FILE_FLAG_BACKUP_SEMANTICS : FILE_FLAG_OPEN_REPARSE_POINT, nullptr
- );
- if (h == INVALID_HANDLE_VALUE) {
- if (is_dir) throw lro_error::from_win32_last(err_msg::err_open_dir, { path });
- throw lro_error::from_win32_last(create ? err_msg::err_create_file : err_msg::err_open_file, { path });
- }
- return unique_ptr_del(h, &CloseHandle);
-}
-
-void create_recursive(crwstr path) {
- for (auto i = path.find(L'\\', 7); i != wstr::npos; i = path.find(L'\\', i + 1)) {
- auto p = path.substr(0, i);
- if (!CreateDirectory(p.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
- throw lro_error::from_win32_last(err_msg::err_create_dir, { p });
- }
- }
-}
-
-uint64_t get_file_size(HANDLE hf) {
- LARGE_INTEGER sz;
- if (!GetFileSizeEx(hf, &sz)) throw lro_error::from_win32_last(err_msg::err_file_size, {});
- return sz.QuadPart;
-}
-
-void grant_delete_child(HANDLE hf) {
- PACL pa;
- PSECURITY_DESCRIPTOR pdb;
- if (GetSecurityInfo(hf, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &pa, nullptr, &pdb)) return;
- unique_ptr_del pd(pdb, &LocalFree);
- EXPLICIT_ACCESS ea;
- BuildExplicitAccessWithName(&ea, const_cast(L"CURRENT_USER"), FILE_DELETE_CHILD, GRANT_ACCESS, CONTAINER_INHERIT_ACE);
- PACL pnab;
- if (SetEntriesInAcl(1, &ea, pa, &pnab)) return;
- const unique_ptr_del pna(pnab, &LocalFree);
- SetSecurityInfo(hf, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, pna.get(), nullptr);
-}
-
-void set_cs_info(HANDLE hd) {
- FILE_CASE_SENSITIVE_INFORMATION info = {};
- auto stat = NtQueryInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
- if (!stat && (info.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR)) return;
- info.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
- stat = NtSetInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
- if (stat == STATUS_ACCESS_DENIED) {
- grant_delete_child(hd);
- stat = NtSetInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
- }
- if (stat) throw lro_error::from_nt(err_msg::err_set_cs, {}, stat);
-}
-
-template
-T get_ea(HANDLE hf, const char *name) {
- const auto nl = static_cast(strlen(name));
- const auto gil = static_cast((FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + nl + 1));
- const auto bgi = std::make_unique(gil);
- const auto pgi = reinterpret_cast(bgi.get());
- const auto il = static_cast(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + nl + 1 + sizeof(T));
- const auto bi = std::make_unique(il);
- const auto pi = reinterpret_cast(bi.get());
- pgi->NextEntryOffset = 0;
- pgi->EaNameLength = nl;
- strcpy(pgi->EaName, name);
- const auto stat = NtQueryEaFile(
- hf, &iostat,
- pi, il, true,
- pgi, gil, nullptr, true
- );
- if (stat) throw lro_error::from_nt(err_msg::err_get_ea, {}, stat);
- if (pi->EaValueLength != sizeof(T)) {
- throw lro_error::from_other(err_msg::err_invalid_ea, { from_utf8(name) });
- }
- T t = {};
- memcpy(&t, pi->EaName + nl + 1, sizeof(T));
- return t;
-}
-
-template
-void set_ea(HANDLE hf, const char *name, const T &data) {
- const auto nl = static_cast(strlen(name));
- const auto il = static_cast(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + nl + 1 + sizeof(T));
- const auto bi = std::make_unique(il);
- const auto pi = reinterpret_cast(bi.get());
- pi->NextEntryOffset = 0;
- pi->Flags = 0;
- pi->EaNameLength = nl;
- pi->EaValueLength = sizeof(T);
- strcpy(pi->EaName, name);
- memcpy(pi->EaName + nl + 1, &data, sizeof(T));
- const auto stat = NtSetEaFile(hf, &iostat, pi, il);
- if (stat) throw lro_error::from_nt(err_msg::err_set_ea, { from_utf8(name) }, stat);
-}
-
-void find_close_safe(HANDLE hs) {
- if (hs != INVALID_HANDLE_VALUE) FindClose(hs);
-}
-
-void enum_directory(file_path &path, const bool rootfs_first, std::function action) {
- std::function enum_rec;
- enum_rec = [&](const bool is_root) {
- try {
- set_cs_info(open_file(path.data, true, false).get());
- } catch (lro_error &e) {
- if (e.msg_code == err_msg::err_set_cs) e.msg_args.push_back(path.data);
- throw;
- }
- action(enum_dir_type::enter);
- const auto os = path.data.size();
- if (is_root) {
- path.data += L"rootfs\\";
- enum_rec(false);
- path.data.resize(os);
- }
- WIN32_FIND_DATA data;
- path.data += L'*';
- const unique_ptr_del hs(FindFirstFile(path.data.c_str(), &data), &find_close_safe);
- path.data.resize(os);
- if (hs.get() == INVALID_HANDLE_VALUE) {
- throw lro_error::from_win32_last(err_msg::err_enum_dir, { path.data });
- }
- while (true) {
- if (wcscmp(data.cFileName, L".") != 0 && wcscmp(data.cFileName, L"..") != 0 && (!is_root || wcscmp(data.cFileName, L"rootfs") != 0)) {
- path.data += data.cFileName;
- if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- path.data += L'\\';
- enum_rec(false);
- } else {
- action(enum_dir_type::file);
- }
- path.data.resize(os);
- }
- if (!FindNextFile(hs.get(), &data)) {
- if (GetLastError() == ERROR_NO_MORE_FILES) {
- action(enum_dir_type::exit);
- return;
- }
- throw lro_error::from_win32_last(err_msg::err_enum_dir, { path.data });
- }
- }
- };
- enum_rec(rootfs_first);
-}
-
-void time_u2f(const unix_time &ut, LARGE_INTEGER &ft) {
- ft.QuadPart = ut.sec * 10000000 + ut.nsec / 100 + 116444736000000000;
-}
-
-void time_f2u(const LARGE_INTEGER &ft, unix_time &ut) {
- const auto t = ft.QuadPart - 116444736000000000;
- ut.sec = static_cast(t / 10000000);
- ut.nsec = static_cast(t % 10000000 * 100);
-}
-
-archive_writer::archive_writer(crwstr archive_path)
- : pa(archive_write_new(), &archive_write_free), pe(archive_entry_new(), &archive_entry_free) {
- path = std::make_unique();
- target_path = std::make_unique();
- check_archive(pa.get(), archive_write_set_format_gnutar(pa.get()));
- check_archive(pa.get(), archive_write_add_filter_gzip(pa.get()));
- check_archive(pa.get(), archive_write_open_filename_w(pa.get(), archive_path.c_str()));
-}
-
-void archive_writer::write_entry(const file_attr &attr) {
- auto up = to_utf8(path->data);
- archive_entry_set_pathname(pe.get(), up.get());
- archive_entry_set_uid(pe.get(), attr.uid);
- archive_entry_set_gid(pe.get(), attr.gid);
- archive_entry_set_mode(pe.get(), attr.mode);
- archive_entry_set_size(pe.get(), attr.size);
- archive_entry_set_atime(pe.get(), attr.at.sec, attr.at.nsec);
- archive_entry_set_mtime(pe.get(), attr.mt.sec, attr.mt.nsec);
- archive_entry_set_ctime(pe.get(), attr.ct.sec, attr.ct.nsec);
- check_archive(pa.get(), archive_write_header(pa.get(), pe.get()));
- archive_entry_clear(pe.get());
-}
-
-bool archive_writer::check_attr(const file_attr *attr) {
- if (!attr) {
- warn_ignored(path->data);
- ignored_files.insert(path->data);
- return false;
- }
- return true;
-}
-
-void archive_writer::warn_ignored(crwstr path) {
- log_warning((boost::wformat(L"Ignoring the file \"%1%\" which doesn't have WSL attributes.") % path).str());
-}
-
-bool archive_writer::write_new_file(const file_attr *attr) {
- if (!check_attr(attr)) return false;
- write_entry(*attr);
- return true;
-}
-
-void archive_writer::write_file_data(const char *buf, uint32_t size) {
- if (size) {
- if (archive_write_data(pa.get(), buf, size) < 0) {
- check_archive(pa.get(), ARCHIVE_FATAL);
- }
- }
-}
-
-void archive_writer::write_directory(const file_attr *attr) {
- if (!check_attr(attr)) return;
- if (!path->data.empty()) write_entry(*attr);
-}
-
-void archive_writer::write_symlink(const file_attr *attr, const char *target) {
- if (!check_attr(attr)) return;
- archive_entry_set_symlink(pe.get(), target);
- write_entry(*attr);
-}
-
-void archive_writer::write_hard_link() {
- if (ignored_files.find(target_path->data) != ignored_files.end()) {
- warn_ignored(path->data);
- return;
- }
- const auto up = to_utf8(path->data);
- archive_entry_set_pathname(pe.get(), up.get());
- const auto ut = to_utf8(target_path->data);
- archive_entry_set_hardlink(pe.get(), ut.get());
- check_archive(pa.get(), archive_write_header(pa.get(), pe.get()));
- archive_entry_clear(pe.get());
-}
-
-bool archive_writer::check_source_path(const file_path &sp) const {
- return true;
-}
-
-wsl_writer::wsl_writer() : hf_data(nullptr) {}
-
-void wsl_writer::write_data(HANDLE hf, const char *buf, const uint32_t size) const {
- DWORD wc;
- if (!WriteFile(hf, buf, size, &wc, nullptr)) {
- throw lro_error::from_win32_last(err_msg::err_write_file, { path->data });
- }
-}
-
-bool wsl_writer::write_new_file(const file_attr *attr) {
- hf_data = open_file(path->data, false, true);
- write_attr(hf_data.get(), attr);
- return true;
-}
-
-void wsl_writer::write_file_data(const char *buf, const uint32_t size) {
- if (size) write_data(hf_data.get(), buf, size);
- else hf_data.reset();
-}
-
-void wsl_writer::write_directory(const file_attr *attr) {
- if (!CreateDirectory(path->data.c_str(), nullptr)) {
- const auto e = lro_error::from_win32_last(err_msg::err_create_dir, { path->data });
- if (GetLastError() != ERROR_ALREADY_EXISTS) throw lro_error(e);
- log_warning(e.format());
- }
- const auto hf = open_file(path->data, true, false);
- write_attr(hf.get(), attr);
- try {
- set_cs_info(hf.get());
- } catch (lro_error &e) {
- e.msg_args.push_back(path->data);
- throw;
- }
-}
-
-void wsl_writer::write_symlink(const file_attr *attr, const char *target) {
- const auto hf = open_file(path->data, false, true);
- write_attr(hf.get(), attr);
- write_symlink_data(hf.get(), target);
-}
-
-void wsl_writer::write_hard_link() {
- if (!CreateHardLink(path->data.c_str(), target_path->data.c_str(), nullptr)) {
- throw lro_error::from_win32_last(err_msg::err_hard_link, { path->data, target_path->data });
- }
-}
-
-bool wsl_writer::check_source_path(const file_path &sp) const {
- // base_len of a linux_path is always 0, so it will be safely ignored.
- return path->data.compare(0, std::min(path->base_len, sp.base_len), sp.data, 0, sp.base_len);
-}
-
-wsl_v1_writer::wsl_v1_writer(crwstr base_path) {
- path = std::make_unique(base_path);
- target_path = std::make_unique(base_path);
- create_recursive(path->data);
-}
-
-void wsl_v1_writer::write_attr(HANDLE hf, const file_attr *attr) {
- if (!attr) return;
- try {
- set_ea(hf, "LXATTRB", lxattrb {
- 0, 1,
- attr->mode, attr->uid, attr->gid,
- 0,
- attr->at.nsec, attr->mt.nsec, attr->ct.nsec,
- attr->at.sec, attr->mt.sec, attr->ct.sec
- });
- } catch (lro_error &e) {
- e.msg_args.push_back(path->data);
- throw;
- }
-}
-
-void wsl_v1_writer::write_symlink_data(HANDLE hf, const char *target) const {
- write_data(hf, target, static_cast(strlen(target)));
-}
-
-wsl_v2_writer::wsl_v2_writer(crwstr base_path) {
- path = std::make_unique(base_path);
- target_path = std::make_unique(base_path);
- create_recursive(path->data);
-}
-
-void wsl_v2_writer::real_write_attr(HANDLE hf, const file_attr &attr, crwstr path) const {
- try {
- set_ea(hf, "$LXUID", attr.uid);
- set_ea(hf, "$LXGID", attr.gid);
- set_ea(hf, "$LXMOD", attr.mode);
- } catch (lro_error &e) {
- e.msg_args.push_back(path);
- throw;
- }
- FILE_BASIC_INFO info;
- time_u2f(attr.at, info.LastAccessTime);
- time_u2f(attr.mt, info.LastWriteTime);
- time_u2f(attr.ct, info.ChangeTime);
- info.CreationTime = info.ChangeTime;
- info.FileAttributes = 0;
- if (!SetFileInformationByHandle(hf, FileBasicInfo, &info, sizeof(FILE_BASIC_INFO))) {
- throw lro_error::from_win32_last(err_msg::err_set_ft, { path });
- }
-}
-
-void wsl_v2_writer::write_attr(HANDLE hf, const file_attr *attr) {
- if (!attr) return;
- if ((attr->mode & AE_IFDIR) == AE_IFDIR) {
- while (!dir_attr.empty()) {
- auto p = dir_attr.top();
- if (!path->data.compare(0, p.first.size(), p.first)) break;
- dir_attr.pop();
- real_write_attr(open_file(p.first, true, false).get(), p.second, p.first);
- }
- dir_attr.push(std::make_pair(path->data, *attr));
- } else real_write_attr(hf, *attr, path->data);
-}
-
-void wsl_v2_writer::write_symlink_data(HANDLE hf, const char *target) const {
- const auto pl = strlen(target);
- const auto dl = static_cast(pl + 4);
- const auto bl = static_cast(FIELD_OFFSET(REPARSE_DATA_BUFFER, DataBuffer) + dl);
- const auto buf = std::make_unique(bl);
- const auto pb = reinterpret_cast(buf.get());
- pb->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
- pb->ReparseDataLength = dl;
- pb->Reserved = 0;
- uint32_t v = 2;
- memcpy(pb->DataBuffer, &v, 4);
- memcpy(pb->DataBuffer + 4, target, pl);
- DWORD cnt;
- if (!DeviceIoControl(hf, FSCTL_SET_REPARSE_POINT, pb, bl, nullptr, 0, &cnt, nullptr)) {
- throw lro_error::from_win32_last(err_msg::err_set_reparse, { path->data });
- }
-}
-
-wsl_v2_writer::~wsl_v2_writer() {
- try {
- while (!dir_attr.empty()) {
- auto p = dir_attr.top();
- dir_attr.pop();
- real_write_attr(open_file(p.first, true, false).get(), p.second, p.first);
- }
- } catch (const lro_error &e) {
- log_error(e.format());
- } catch (const std::exception &e) {
- log_error(from_utf8(e.what()));
- }
-}
-
-wsl_legacy_writer::wsl_legacy_writer(crwstr base_path) {
- path = std::make_unique(base_path);
- target_path = std::make_unique(base_path);
- create_recursive(path->data);
-}
-
-archive_reader::archive_reader(wstr archive_path, wstr root_path)
- : archive_path(std::move(archive_path)), root_path(std::move(root_path)) {}
-
-void archive_reader::run(fs_writer &writer) {
- auto as = get_file_size(open_file(archive_path, false, false).get());
- unique_ptr_del pa(archive_read_new(), &archive_read_free);
- check_archive(pa.get(), archive_read_support_filter_all(pa.get()));
- check_archive(pa.get(), archive_read_support_format_all(pa.get()));
- check_archive(pa.get(), archive_read_open_filename_w(pa.get(), archive_path.c_str(), BUFSIZ));
- linux_path p;
- if (p.convert(*writer.path)) {
- file_attr attr { 0040755, 0, 0, 0, {}, {}, {} };
- writer.write_directory(&attr);
- }
- archive_entry *pe;
- while (check_archive(pa.get(), archive_read_next_header(pa.get(), &pe))) {
- print_progress(static_cast(archive_filter_bytes(pa.get(), -1)) / as);
- auto up = archive_entry_pathname(pe);
- auto wp = archive_entry_pathname_w(pe);
- if (up) p = linux_path(from_utf8(up), root_path);
- else if (wp) p = linux_path(wp, root_path);
- else throw lro_error::from_other(err_msg::err_convert_encoding, {});
- if (!p.convert(*writer.path)) continue;
- auto utp = archive_entry_hardlink(pe);
- auto wtp = archive_entry_hardlink_w(pe);
- if (utp || wtp) {
- linux_path tp;
- if (utp) tp = linux_path(from_utf8(utp), root_path);
- else tp = linux_path(wtp, root_path);
- if (tp.convert(*writer.target_path)) writer.write_hard_link();
- continue;
- }
- auto type = archive_entry_filetype(pe);
- if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
- log_warning((boost::wformat(L"Ignoring an unsupported file \"%1%\" of type %2$07o.") % p.data % type).str());
- continue;
- }
- auto pst = archive_entry_stat(pe);
- unix_time mt {
- static_cast(pst->st_mtime),
- static_cast(archive_entry_mtime_nsec(pe))
- };
- file_attr attr {
- static_cast(pst->st_mode), static_cast(pst->st_uid), static_cast(pst->st_gid), static_cast(pst->st_size),
- archive_entry_atime_is_set(pe) ? unix_time { static_cast(pst->st_atime), static_cast(archive_entry_atime_nsec(pe)) } : mt,
- mt,
- archive_entry_ctime_is_set(pe) ? unix_time { static_cast(pst->st_ctime), static_cast(archive_entry_ctime_nsec(pe)) } : mt
- };
- if (type == AE_IFREG) {
- if (!writer.write_new_file(&attr)) continue;
- const void *buf;
- size_t cnt;
- int64_t off;
- while (check_archive(pa.get(), archive_read_data_block(pa.get(), &buf, &cnt, &off))) {
- writer.write_file_data(reinterpret_cast(buf), static_cast(cnt));
- }
- writer.write_file_data(nullptr, 0);
- } else if (type == AE_IFLNK) {
- auto tp = archive_entry_symlink(pe);
- std::unique_ptr ptp = nullptr;
- if (!tp) {
- ptp = to_utf8(archive_entry_symlink_w(pe));
- tp = ptp.get();
- }
- writer.write_symlink(&attr, tp);
- } else { // AE_IFDIR
- writer.write_directory(&attr);
- }
- }
-}
-
-bool wsl_reader::is_legacy() const {
- return false;
-}
-
-void wsl_reader::run(fs_writer &writer) {
- std::map> id_map;
- char buf[BUFSIZ];
- auto is_root = true;
- enum_directory(*path, is_legacy(), [&](enum_dir_type t) {
- if (t == enum_dir_type::exit) return;
- if (t == enum_dir_type::enter && is_root) {
- is_root = false;
- return;
- }
- if (!path->convert(*writer.path)) return;
- const auto dir = t == enum_dir_type::enter;
- const auto hf = open_file(path->data, dir, false);
- if (!dir) {
- BY_HANDLE_FILE_INFORMATION info;
- if (!GetFileInformationByHandle(hf.get(), &info)) {
- throw lro_error::from_win32_last(err_msg::err_file_info, { path->data });
- }
- if (info.nNumberOfLinks > 1) {
- const auto id = info.nFileIndexLow + (static_cast(info.nFileIndexHigh) << 32);
- if (id_map.count(id)) {
- if (id_map[id]->convert(*writer.target_path)) writer.write_hard_link();
- return;
- } else id_map[id] = path->clone();
- }
- }
- const auto attr = read_attr(hf.get());
- if (dir) writer.write_directory(attr.get());
- else {
- const auto type = attr ? attr->mode & AE_IFMT : AE_IFREG;
- if (type == AE_IFLNK) {
- const auto tb = read_symlink_data(hf.get());
- if (tb) writer.write_symlink(attr.get(), tb.get());
- else log_warning((boost::wformat(L"Ignoring an invalid symlink \"%1%\".") % path->data).str());
- return;
- } else if (type == AE_IFREG) {
- if (!writer.write_new_file(attr.get())) return;
- DWORD rc;
- do {
- if (!ReadFile(hf.get(), buf, BUFSIZ, &rc, nullptr)) {
- throw lro_error::from_win32_last(err_msg::err_read_file, { path->data });
- }
- writer.write_file_data(buf, rc);
- } while (rc);
- } else log_warning((boost::wformat(L"Ignoring an unsupported file \"%1%\" of type %2$07o.") % path->data % type).str());
- }
- });
-}
-
-void wsl_reader::run_checked(fs_writer &writer) {
- if (!writer.check_source_path(*path)) {
- throw lro_error::from_other(err_msg::err_copy_subdir, {});
- }
- run(writer);
-}
-
-wsl_v1_reader::wsl_v1_reader(crwstr base) {
- path = std::make_unique(base);
-}
-
-std::unique_ptr wsl_v1_reader::read_attr(HANDLE hf) const {
- try {
- const auto ea = get_ea(hf, "LXATTRB");
- return std::make_unique(file_attr {
- ea.mode, ea.uid, ea.gid, get_file_size(hf),
- { ea.atime, ea.atime_nsec },
- { ea.mtime, ea.mtime_nsec },
- { ea.ctime, ea.ctime_nsec }
- });
- } catch (lro_error &e) {
- if (e.msg_code == err_msg::err_invalid_ea) return nullptr;
- e.msg_args.push_back(path->data);
- throw;
- }
-}
-
-std::unique_ptr wsl_v1_reader::read_symlink_data(HANDLE hf) const {
- uint64_t sz;
- try {
- sz = get_file_size(hf);
- } catch (lro_error &e) {
- e.msg_args.push_back(path->data);
- throw;
- }
- if (sz > 65536) throw lro_error::from_other(err_msg::err_symlink_length, { path->data, std::to_wstring(sz) });
- auto buf = std::make_unique(sz + 1);
- DWORD rc;
- for (uint32_t off = 0; off < sz; off += rc) {
- if (!ReadFile(hf, buf.get() + off, static_cast(sz - off), &rc, nullptr)) {
- throw lro_error::from_win32_last(err_msg::err_read_file, { path->data });
- }
- }
- buf[sz] = 0;
- return buf;
-}
-
-wsl_v2_reader::wsl_v2_reader(crwstr base) {
- path = std::make_unique(base);
-}
-
-std::unique_ptr wsl_v2_reader::read_attr(HANDLE hf) const {
- std::unique_ptr attr(new file_attr);
- try {
- attr->uid = get_ea(hf, "$LXUID");
- attr->gid = get_ea(hf, "$LXGID");
- attr->mode = get_ea(hf, "$LXMOD");
- attr->size = get_file_size(hf);
- } catch (lro_error &e) {
- if (e.msg_code == err_msg::err_invalid_ea) return nullptr;
- e.msg_args.push_back(path->data);
- throw;
- }
- FILE_BASIC_INFO info;
- if (!GetFileInformationByHandleEx(hf, FileBasicInfo, &info, sizeof info)) {
- throw lro_error::from_win32_last(err_msg::err_get_ft, { path->data });
- }
- time_f2u(info.LastAccessTime, attr->at);
- time_f2u(info.LastWriteTime, attr->mt);
- time_f2u(info.ChangeTime, attr->ct);
- return attr;
-}
-
-std::unique_ptr wsl_v2_reader::read_symlink_data(HANDLE hf) const {
- const auto buf = std::make_unique(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
- DWORD cnt;
- if (!DeviceIoControl(hf, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &cnt, nullptr)) {
- if (GetLastError() == ERROR_NOT_A_REPARSE_POINT) return nullptr;
- throw lro_error::from_win32_last(err_msg::err_get_reparse, { path->data });
- }
- const auto pb = reinterpret_cast(buf.get());
- if (pb->ReparseTag != IO_REPARSE_TAG_LX_SYMLINK) return nullptr;
- const auto pl = pb->ReparseDataLength - 4;
- auto s = std::make_unique(static_cast(pl) + 1);
- memcpy(s.get(), pb->DataBuffer + 4, pl);
- s[pl] = 0;
- return s;
-}
-
-bool wsl_legacy_reader::is_legacy() const {
- return true;
-}
-
-wsl_legacy_reader::wsl_legacy_reader(crwstr base) {
- path = std::make_unique(base);
-}
-
-template
-bool has_ea(crwstr path, const char *name, const bool ignore_error) {
- try {
- get_ea(open_file(path, true, false).get(), name);
- return true;
- } catch (const lro_error &e) {
- if (e.msg_code == err_msg::err_invalid_ea || ignore_error) return false;
- throw;
- }
-}
-
-uint32_t detect_version(crwstr path) {
- wsl_v2_path p1(path), p2(path);
- p1.data += L"rootfs\\";
- p2.data += L"home\\";
- if (has_ea(p1.data, "$LXUID", false)) return 2;
- if (has_ea(p2.data, "LXATTRB", true)) return 0;
- if (has_ea(p1.data, "LXATTRB", false)) return 1;
- throw lro_error::from_other(err_msg::err_fs_detect, { path });
-}
-
-bool detect_wsl2(crwstr path) {
- const wsl_v2_path p(path);
- try {
- open_file(p.data + L"ext4.vhdx", false, false);
- return true;
- } catch (const lro_error &e) {
- if (e.msg_code == err_msg::err_open_file && e.err_code == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) return false;
- throw;
- }
-}
-
-std::unique_ptr select_wsl_writer(const uint32_t version, crwstr path) {
- if (version == 0) return std::make_unique(path);
- if (version == 1) return std::make_unique(path);
- if (version == 2) return std::make_unique(path);
- throw lro_error::from_other(err_msg::err_fs_version, { std::to_wstring(version) });
-}
-
-std::unique_ptr select_wsl_reader(const uint32_t version, crwstr path) {
- if (version == 0) return std::make_unique(path);
- if (version == 1) return std::make_unique(path);
- if (version == 2) return std::make_unique(path);
- throw lro_error::from_other(err_msg::err_fs_version, { std::to_wstring(version) });
-}
-
-bool move_directory(crwstr source_path, crwstr target_path) {
- return MoveFile(source_path.c_str(), target_path.c_str());
-}
-
-void delete_directory(crwstr path) {
- wsl_v2_path p(path);
- enum_directory(p, false, [&](const enum_dir_type t) {
- if (t == enum_dir_type::enter) return;
- const auto dir = t == enum_dir_type::exit;
- if (!(dir ? RemoveDirectory : DeleteFile)(p.data.c_str())) {
- throw lro_error::from_win32_last(dir ? err_msg::err_delete_dir : err_msg::err_delete_file, { p.data });
- }
- });
-}
-
-bool check_in_use(crwstr path) {
- try {
- open_file(path, false, false, true);
- } catch (const lro_error &e) {
- if (e.msg_code == err_msg::err_open_file && e.err_code == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) {
- return true;
- }
- }
- return false;
-}
+#include "stdafx.h"
+#include "error.h"
+#include "fs.h"
+#include "ntdll.h"
+#include "utils.h"
+
+enum class enum_dir_type {
+ enter,
+ exit,
+ file
+};
+
+struct lxattrb {
+ uint16_t flags;
+ uint16_t ver;
+ uint32_t mode;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t rdev;
+ uint32_t atime_nsec;
+ uint32_t mtime_nsec;
+ uint32_t ctime_nsec;
+ uint64_t atime;
+ uint64_t mtime;
+ uint64_t ctime;
+};
+
+IO_STATUS_BLOCK iostat;
+
+bool check_archive(archive *pa, const int stat) {
+ if (stat == ARCHIVE_OK) return true;
+ if (stat == ARCHIVE_EOF) return false;
+ const auto es = archive_error_string(pa);
+ std::wstringstream ss;
+ if (es) ss << es;
+ else ss << L"Unknown error " << archive_errno(pa);
+ if (stat == ARCHIVE_WARN) {
+ log_warning(ss.str());
+ return true;
+ }
+ throw lro_error::from_other(err_msg::err_archive, { ss.str() });
+}
+
+unique_ptr_del open_file(crwstr path, const bool is_dir, const bool create, const bool no_share = false) {
+ const auto h = CreateFile(
+ path.c_str(),
+ MAXIMUM_ALLOWED, no_share ? 0 : FILE_SHARE_READ, nullptr,
+ create ? CREATE_NEW : OPEN_EXISTING,
+ is_dir ? FILE_FLAG_BACKUP_SEMANTICS : FILE_FLAG_OPEN_REPARSE_POINT, nullptr
+ );
+ if (h == INVALID_HANDLE_VALUE) {
+ if (is_dir) throw lro_error::from_win32_last(err_msg::err_open_dir, { path });
+ throw lro_error::from_win32_last(create ? err_msg::err_create_file : err_msg::err_open_file, { path });
+ }
+ return unique_ptr_del(h, &CloseHandle);
+}
+
+void create_recursive(crwstr path) {
+ for (auto i = path.find(L'\\', 7); i != wstr::npos; i = path.find(L'\\', i + 1)) {
+ auto p = path.substr(0, i);
+ if (!CreateDirectory(p.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
+ throw lro_error::from_win32_last(err_msg::err_create_dir, { p });
+ }
+ }
+}
+
+uint64_t get_file_size(HANDLE hf) {
+ LARGE_INTEGER sz;
+ if (!GetFileSizeEx(hf, &sz)) throw lro_error::from_win32_last(err_msg::err_file_size, {});
+ return sz.QuadPart;
+}
+
+void grant_delete_child(HANDLE hf) {
+ PACL pa;
+ PSECURITY_DESCRIPTOR pdb;
+ if (GetSecurityInfo(hf, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &pa, nullptr, &pdb)) return;
+ unique_ptr_del pd(pdb, &LocalFree);
+ EXPLICIT_ACCESS ea;
+ BuildExplicitAccessWithName(&ea, const_cast(L"CURRENT_USER"), FILE_DELETE_CHILD, GRANT_ACCESS, CONTAINER_INHERIT_ACE);
+ PACL pnab;
+ if (SetEntriesInAcl(1, &ea, pa, &pnab)) return;
+ const unique_ptr_del pna(pnab, &LocalFree);
+ SetSecurityInfo(hf, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, pna.get(), nullptr);
+}
+
+void set_cs_info(HANDLE hd) {
+ FILE_CASE_SENSITIVE_INFORMATION info = {};
+ auto stat = NtQueryInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
+ if (!stat && (info.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR)) return;
+ info.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
+ stat = NtSetInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
+ if (stat == STATUS_ACCESS_DENIED) {
+ grant_delete_child(hd);
+ stat = NtSetInformationFile(hd, &iostat, &info, sizeof info, FileCaseSensitiveInformation);
+ }
+ if (stat) throw lro_error::from_nt(err_msg::err_set_cs, {}, stat);
+}
+
+template
+T get_ea(HANDLE hf, const char *name) {
+ const auto nl = static_cast(strlen(name));
+ const auto gil = static_cast((FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + nl + 1));
+ const auto bgi = std::make_unique(gil);
+ const auto pgi = reinterpret_cast(bgi.get());
+ const auto il = static_cast(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + nl + 1 + sizeof(T));
+ const auto bi = std::make_unique(il);
+ const auto pi = reinterpret_cast(bi.get());
+ pgi->NextEntryOffset = 0;
+ pgi->EaNameLength = nl;
+ strcpy(pgi->EaName, name);
+ const auto stat = NtQueryEaFile(
+ hf, &iostat,
+ pi, il, true,
+ pgi, gil, nullptr, true
+ );
+ if (stat) throw lro_error::from_nt(err_msg::err_get_ea, {}, stat);
+ if (pi->EaValueLength != sizeof(T)) {
+ throw lro_error::from_other(err_msg::err_invalid_ea, { from_utf8(name) });
+ }
+ T t = {};
+ memcpy(&t, pi->EaName + nl + 1, sizeof(T));
+ return t;
+}
+
+template
+void set_ea(HANDLE hf, const char *name, const T &data) {
+ const auto nl = static_cast(strlen(name));
+ const auto il = static_cast(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + nl + 1 + sizeof(T));
+ const auto bi = std::make_unique(il);
+ const auto pi = reinterpret_cast(bi.get());
+ pi->NextEntryOffset = 0;
+ pi->Flags = 0;
+ pi->EaNameLength = nl;
+ pi->EaValueLength = sizeof(T);
+ strcpy(pi->EaName, name);
+ memcpy(pi->EaName + nl + 1, &data, sizeof(T));
+ const auto stat = NtSetEaFile(hf, &iostat, pi, il);
+ if (stat) throw lro_error::from_nt(err_msg::err_set_ea, { from_utf8(name) }, stat);
+}
+
+void find_close_safe(HANDLE hs) {
+ if (hs != INVALID_HANDLE_VALUE) FindClose(hs);
+}
+
+void enum_directory(file_path &path, const bool rootfs_first, std::function action) {
+ std::function enum_rec;
+ enum_rec = [&](const bool is_root) {
+ try {
+ set_cs_info(open_file(path.data, true, false).get());
+ } catch (lro_error &e) {
+ if (e.msg_code == err_msg::err_set_cs) e.msg_args.push_back(path.data);
+ throw;
+ }
+ action(enum_dir_type::enter);
+ const auto os = path.data.size();
+ if (is_root) {
+ path.data += L"rootfs\\";
+ enum_rec(false);
+ path.data.resize(os);
+ }
+ WIN32_FIND_DATA data;
+ path.data += L'*';
+ const unique_ptr_del hs(FindFirstFile(path.data.c_str(), &data), &find_close_safe);
+ path.data.resize(os);
+ if (hs.get() == INVALID_HANDLE_VALUE) {
+ throw lro_error::from_win32_last(err_msg::err_enum_dir, { path.data });
+ }
+ while (true) {
+ if (wcscmp(data.cFileName, L".") != 0 && wcscmp(data.cFileName, L"..") != 0 && (!is_root || wcscmp(data.cFileName, L"rootfs") != 0)) {
+ path.data += data.cFileName;
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ path.data += L'\\';
+ enum_rec(false);
+ } else {
+ action(enum_dir_type::file);
+ }
+ path.data.resize(os);
+ }
+ if (!FindNextFile(hs.get(), &data)) {
+ if (GetLastError() == ERROR_NO_MORE_FILES) {
+ action(enum_dir_type::exit);
+ return;
+ }
+ throw lro_error::from_win32_last(err_msg::err_enum_dir, { path.data });
+ }
+ }
+ };
+ enum_rec(rootfs_first);
+}
+
+void time_u2f(const unix_time &ut, LARGE_INTEGER &ft) {
+ ft.QuadPart = ut.sec * 10000000 + ut.nsec / 100 + 116444736000000000;
+}
+
+void time_f2u(const LARGE_INTEGER &ft, unix_time &ut) {
+ const auto t = ft.QuadPart - 116444736000000000;
+ ut.sec = static_cast(t / 10000000);
+ ut.nsec = static_cast(t % 10000000 * 100);
+}
+
+archive_writer::archive_writer(crwstr archive_path)
+ : pa(archive_write_new(), &archive_write_free), pe(archive_entry_new(), &archive_entry_free) {
+ path = std::make_unique();
+ target_path = std::make_unique();
+ check_archive(pa.get(), archive_write_set_format_gnutar(pa.get()));
+ check_archive(pa.get(), archive_write_add_filter_gzip(pa.get()));
+ check_archive(pa.get(), archive_write_open_filename_w(pa.get(), archive_path.c_str()));
+}
+
+void archive_writer::write_entry(const file_attr &attr) {
+ auto up = to_utf8(path->data);
+ archive_entry_set_pathname(pe.get(), up.get());
+ archive_entry_set_uid(pe.get(), attr.uid);
+ archive_entry_set_gid(pe.get(), attr.gid);
+ archive_entry_set_mode(pe.get(), attr.mode);
+ archive_entry_set_size(pe.get(), attr.size);
+ archive_entry_set_atime(pe.get(), attr.at.sec, attr.at.nsec);
+ archive_entry_set_mtime(pe.get(), attr.mt.sec, attr.mt.nsec);
+ archive_entry_set_ctime(pe.get(), attr.ct.sec, attr.ct.nsec);
+ check_archive(pa.get(), archive_write_header(pa.get(), pe.get()));
+ archive_entry_clear(pe.get());
+}
+
+bool archive_writer::check_attr(const file_attr *attr) {
+ if (!attr) {
+ warn_ignored(path->data);
+ ignored_files.insert(path->data);
+ return false;
+ }
+ return true;
+}
+
+void archive_writer::warn_ignored(crwstr path) {
+ log_warning((boost::wformat(L"Ignoring the file \"%1%\" which doesn't have WSL attributes.") % path).str());
+}
+
+bool archive_writer::write_new_file(const file_attr *attr) {
+ if (!check_attr(attr)) return false;
+ write_entry(*attr);
+ return true;
+}
+
+void archive_writer::write_file_data(const char *buf, uint32_t size) {
+ if (size) {
+ if (archive_write_data(pa.get(), buf, size) < 0) {
+ check_archive(pa.get(), ARCHIVE_FATAL);
+ }
+ }
+}
+
+void archive_writer::write_directory(const file_attr *attr) {
+ if (!check_attr(attr)) return;
+ if (!path->data.empty()) write_entry(*attr);
+}
+
+void archive_writer::write_symlink(const file_attr *attr, const char *target) {
+ if (!check_attr(attr)) return;
+ archive_entry_set_symlink(pe.get(), target);
+ write_entry(*attr);
+}
+
+void archive_writer::write_hard_link() {
+ if (ignored_files.find(target_path->data) != ignored_files.end()) {
+ warn_ignored(path->data);
+ return;
+ }
+ const auto up = to_utf8(path->data);
+ archive_entry_set_pathname(pe.get(), up.get());
+ const auto ut = to_utf8(target_path->data);
+ archive_entry_set_hardlink(pe.get(), ut.get());
+ check_archive(pa.get(), archive_write_header(pa.get(), pe.get()));
+ archive_entry_clear(pe.get());
+}
+
+bool archive_writer::check_source_path(const file_path &) const {
+ return true;
+}
+
+wsl_writer::wsl_writer() : hf_data(nullptr) {}
+
+void wsl_writer::write_data(HANDLE hf, const char *buf, const uint32_t size) const {
+ DWORD wc;
+ if (!WriteFile(hf, buf, size, &wc, nullptr)) {
+ throw lro_error::from_win32_last(err_msg::err_write_file, { path->data });
+ }
+}
+
+bool wsl_writer::write_new_file(const file_attr *attr) {
+ hf_data = open_file(path->data, false, true);
+ write_attr(hf_data.get(), attr);
+ return true;
+}
+
+void wsl_writer::write_file_data(const char *buf, const uint32_t size) {
+ if (size) write_data(hf_data.get(), buf, size);
+ else hf_data.reset();
+}
+
+void wsl_writer::write_directory(const file_attr *attr) {
+ if (!CreateDirectory(path->data.c_str(), nullptr)) {
+ const auto e = lro_error::from_win32_last(err_msg::err_create_dir, { path->data });
+ if (GetLastError() != ERROR_ALREADY_EXISTS) throw lro_error(e);
+ log_warning(e.format());
+ }
+ const auto hf = open_file(path->data, true, false);
+ write_attr(hf.get(), attr);
+ try {
+ set_cs_info(hf.get());
+ } catch (lro_error &e) {
+ e.msg_args.push_back(path->data);
+ throw;
+ }
+}
+
+void wsl_writer::write_symlink(const file_attr *attr, const char *target) {
+ const auto hf = open_file(path->data, false, true);
+ write_attr(hf.get(), attr);
+ write_symlink_data(hf.get(), target);
+}
+
+void wsl_writer::write_hard_link() {
+ if (!CreateHardLink(path->data.c_str(), target_path->data.c_str(), nullptr)) {
+ throw lro_error::from_win32_last(err_msg::err_hard_link, { path->data, target_path->data });
+ }
+}
+
+bool wsl_writer::check_source_path(const file_path &sp) const {
+ // base_len of a linux_path is always 0, so it will be safely ignored.
+ return path->data.compare(0, std::min(path->base_len, sp.base_len), sp.data, 0, sp.base_len);
+}
+
+wsl_v1_writer::wsl_v1_writer(crwstr base_path) {
+ path = std::make_unique(base_path);
+ target_path = std::make_unique(base_path);
+ create_recursive(path->data);
+}
+
+void wsl_v1_writer::write_attr(HANDLE hf, const file_attr *attr) {
+ if (!attr) return;
+ try {
+ set_ea(hf, "LXATTRB", lxattrb {
+ 0, 1,
+ attr->mode, attr->uid, attr->gid,
+ 0,
+ attr->at.nsec, attr->mt.nsec, attr->ct.nsec,
+ attr->at.sec, attr->mt.sec, attr->ct.sec
+ });
+ } catch (lro_error &e) {
+ e.msg_args.push_back(path->data);
+ throw;
+ }
+}
+
+void wsl_v1_writer::write_symlink_data(HANDLE hf, const char *target) const {
+ write_data(hf, target, static_cast(strlen(target)));
+}
+
+wsl_v2_writer::wsl_v2_writer(crwstr base_path) {
+ path = std::make_unique(base_path);
+ target_path = std::make_unique(base_path);
+ create_recursive(path->data);
+}
+
+void wsl_v2_writer::real_write_attr(HANDLE hf, const file_attr &attr, crwstr path) const {
+ try {
+ set_ea(hf, "$LXUID", attr.uid);
+ set_ea(hf, "$LXGID", attr.gid);
+ set_ea(hf, "$LXMOD", attr.mode);
+ } catch (lro_error &e) {
+ e.msg_args.push_back(path);
+ throw;
+ }
+ FILE_BASIC_INFO info;
+ time_u2f(attr.at, info.LastAccessTime);
+ time_u2f(attr.mt, info.LastWriteTime);
+ time_u2f(attr.ct, info.ChangeTime);
+ info.CreationTime = info.ChangeTime;
+ info.FileAttributes = 0;
+ if (!SetFileInformationByHandle(hf, FileBasicInfo, &info, sizeof(FILE_BASIC_INFO))) {
+ throw lro_error::from_win32_last(err_msg::err_set_ft, { path });
+ }
+}
+
+void wsl_v2_writer::write_attr(HANDLE hf, const file_attr *attr) {
+ if (!attr) return;
+ if ((attr->mode & AE_IFDIR) == AE_IFDIR) {
+ while (!dir_attr.empty()) {
+ auto p = dir_attr.top();
+ if (!path->data.compare(0, p.first.size(), p.first)) break;
+ dir_attr.pop();
+ real_write_attr(open_file(p.first, true, false).get(), p.second, p.first);
+ }
+ dir_attr.push(std::make_pair(path->data, *attr));
+ } else real_write_attr(hf, *attr, path->data);
+}
+
+void wsl_v2_writer::write_symlink_data(HANDLE hf, const char *target) const {
+ const auto pl = strlen(target);
+ const auto dl = static_cast(pl + 4);
+ const auto bl = static_cast(FIELD_OFFSET(REPARSE_DATA_BUFFER, DataBuffer) + dl);
+ const auto buf = std::make_unique(bl);
+ const auto pb = reinterpret_cast(buf.get());
+ pb->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
+ pb->ReparseDataLength = dl;
+ pb->Reserved = 0;
+ uint32_t v = 2;
+ memcpy(pb->DataBuffer, &v, 4);
+ memcpy(pb->DataBuffer + 4, target, pl);
+ DWORD cnt;
+ if (!DeviceIoControl(hf, FSCTL_SET_REPARSE_POINT, pb, bl, nullptr, 0, &cnt, nullptr)) {
+ throw lro_error::from_win32_last(err_msg::err_set_reparse, { path->data });
+ }
+}
+
+wsl_v2_writer::~wsl_v2_writer() {
+ try {
+ while (!dir_attr.empty()) {
+ auto p = dir_attr.top();
+ dir_attr.pop();
+ real_write_attr(open_file(p.first, true, false).get(), p.second, p.first);
+ }
+ } catch (const lro_error &e) {
+ log_error(e.format());
+ } catch (const std::exception &e) {
+ log_error(from_utf8(e.what()));
+ }
+}
+
+wsl_legacy_writer::wsl_legacy_writer(crwstr base_path) {
+ path = std::make_unique(base_path);
+ target_path = std::make_unique(base_path);
+ create_recursive(path->data);
+}
+
+archive_reader::archive_reader(wstr archive_path, wstr root_path)
+ : archive_path(std::move(archive_path)), root_path(std::move(root_path)) {}
+
+void archive_reader::run(fs_writer &writer) {
+ auto as = get_file_size(open_file(archive_path, false, false).get());
+ unique_ptr_del pa(archive_read_new(), &archive_read_free);
+ check_archive(pa.get(), archive_read_support_filter_all(pa.get()));
+ check_archive(pa.get(), archive_read_support_format_all(pa.get()));
+ check_archive(pa.get(), archive_read_open_filename_w(pa.get(), archive_path.c_str(), BUFSIZ));
+ linux_path p;
+ if (p.convert(*writer.path)) {
+ file_attr attr { 0040755, 0, 0, 0, {}, {}, {} };
+ writer.write_directory(&attr);
+ }
+ archive_entry *pe;
+ while (check_archive(pa.get(), archive_read_next_header(pa.get(), &pe))) {
+ print_progress(static_cast(archive_filter_bytes(pa.get(), -1)) / as);
+ auto up = archive_entry_pathname(pe);
+ auto wp = archive_entry_pathname_w(pe);
+ if (up) p = linux_path(from_utf8(up), root_path);
+ else if (wp) p = linux_path(wp, root_path);
+ else throw lro_error::from_other(err_msg::err_convert_encoding, {});
+ if (!p.convert(*writer.path)) continue;
+ auto utp = archive_entry_hardlink(pe);
+ auto wtp = archive_entry_hardlink_w(pe);
+ if (utp || wtp) {
+ linux_path tp;
+ if (utp) tp = linux_path(from_utf8(utp), root_path);
+ else tp = linux_path(wtp, root_path);
+ if (tp.convert(*writer.target_path)) writer.write_hard_link();
+ continue;
+ }
+ auto type = archive_entry_filetype(pe);
+ if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
+ log_warning((boost::wformat(L"Ignoring an unsupported file \"%1%\" of type %2$07o.") % p.data % type).str());
+ continue;
+ }
+ auto pst = archive_entry_stat(pe);
+ unix_time mt {
+ static_cast(pst->st_mtime),
+ static_cast(archive_entry_mtime_nsec(pe))
+ };
+ file_attr attr {
+ static_cast(pst->st_mode), static_cast(pst->st_uid), static_cast(pst->st_gid), static_cast(pst->st_size),
+ archive_entry_atime_is_set(pe) ? unix_time { static_cast(pst->st_atime), static_cast(archive_entry_atime_nsec(pe)) } : mt,
+ mt,
+ archive_entry_ctime_is_set(pe) ? unix_time { static_cast(pst->st_ctime), static_cast(archive_entry_ctime_nsec(pe)) } : mt
+ };
+ if (type == AE_IFREG) {
+ if (!writer.write_new_file(&attr)) continue;
+ const void *buf;
+ size_t cnt;
+ int64_t off;
+ while (check_archive(pa.get(), archive_read_data_block(pa.get(), &buf, &cnt, &off))) {
+ writer.write_file_data(reinterpret_cast(buf), static_cast(cnt));
+ }
+ writer.write_file_data(nullptr, 0);
+ } else if (type == AE_IFLNK) {
+ auto tp = archive_entry_symlink(pe);
+ std::unique_ptr ptp = nullptr;
+ if (!tp) {
+ ptp = to_utf8(archive_entry_symlink_w(pe));
+ tp = ptp.get();
+ }
+ writer.write_symlink(&attr, tp);
+ } else { // AE_IFDIR
+ writer.write_directory(&attr);
+ }
+ }
+}
+
+bool wsl_reader::is_legacy() const {
+ return false;
+}
+
+void wsl_reader::run(fs_writer &writer) {
+ std::map> id_map;
+ char buf[BUFSIZ];
+ auto is_root = true;
+ enum_directory(*path, is_legacy(), [&](enum_dir_type t) {
+ if (t == enum_dir_type::exit) return;
+ if (t == enum_dir_type::enter && is_root) {
+ is_root = false;
+ return;
+ }
+ if (!path->convert(*writer.path)) return;
+ const auto dir = t == enum_dir_type::enter;
+ const auto hf = open_file(path->data, dir, false);
+ if (!dir) {
+ BY_HANDLE_FILE_INFORMATION info;
+ if (!GetFileInformationByHandle(hf.get(), &info)) {
+ throw lro_error::from_win32_last(err_msg::err_file_info, { path->data });
+ }
+ if (info.nNumberOfLinks > 1) {
+ const auto id = info.nFileIndexLow + (static_cast(info.nFileIndexHigh) << 32);
+ if (id_map.count(id)) {
+ if (id_map[id]->convert(*writer.target_path)) writer.write_hard_link();
+ return;
+ } else id_map[id] = path->clone();
+ }
+ }
+ const auto attr = read_attr(hf.get());
+ if (dir) writer.write_directory(attr.get());
+ else {
+ const auto type = attr ? attr->mode & AE_IFMT : AE_IFREG;
+ if (type == AE_IFLNK) {
+ const auto tb = read_symlink_data(hf.get());
+ if (tb) writer.write_symlink(attr.get(), tb.get());
+ else log_warning((boost::wformat(L"Ignoring an invalid symlink \"%1%\".") % path->data).str());
+ return;
+ } else if (type == AE_IFREG) {
+ if (!writer.write_new_file(attr.get())) return;
+ DWORD rc;
+ do {
+ if (!ReadFile(hf.get(), buf, BUFSIZ, &rc, nullptr)) {
+ throw lro_error::from_win32_last(err_msg::err_read_file, { path->data });
+ }
+ writer.write_file_data(buf, rc);
+ } while (rc);
+ } else log_warning((boost::wformat(L"Ignoring an unsupported file \"%1%\" of type %2$07o.") % path->data % type).str());
+ }
+ });
+}
+
+void wsl_reader::run_checked(fs_writer &writer) {
+ if (!writer.check_source_path(*path)) {
+ throw lro_error::from_other(err_msg::err_copy_subdir, {});
+ }
+ run(writer);
+}
+
+wsl_v1_reader::wsl_v1_reader(crwstr base) {
+ path = std::make_unique(base);
+}
+
+std::unique_ptr wsl_v1_reader::read_attr(HANDLE hf) const {
+ try {
+ const auto ea = get_ea(hf, "LXATTRB");
+ return std::make_unique(file_attr {
+ ea.mode, ea.uid, ea.gid, get_file_size(hf),
+ { ea.atime, ea.atime_nsec },
+ { ea.mtime, ea.mtime_nsec },
+ { ea.ctime, ea.ctime_nsec }
+ });
+ } catch (lro_error &e) {
+ if (e.msg_code == err_msg::err_invalid_ea) return nullptr;
+ e.msg_args.push_back(path->data);
+ throw;
+ }
+}
+
+std::unique_ptr wsl_v1_reader::read_symlink_data(HANDLE hf) const {
+ uint64_t sz;
+ try {
+ sz = get_file_size(hf);
+ } catch (lro_error &e) {
+ e.msg_args.push_back(path->data);
+ throw;
+ }
+ if (sz > 65536) throw lro_error::from_other(err_msg::err_symlink_length, { path->data, std::to_wstring(sz) });
+ auto buf = std::make_unique(sz + 1);
+ DWORD rc;
+ for (uint32_t off = 0; off < sz; off += rc) {
+ if (!ReadFile(hf, buf.get() + off, static_cast(sz - off), &rc, nullptr)) {
+ throw lro_error::from_win32_last(err_msg::err_read_file, { path->data });
+ }
+ }
+ buf[sz] = 0;
+ return buf;
+}
+
+wsl_v2_reader::wsl_v2_reader(crwstr base) {
+ path = std::make_unique(base);
+}
+
+std::unique_ptr wsl_v2_reader::read_attr(HANDLE hf) const {
+ std::unique_ptr attr(new file_attr);
+ try {
+ attr->uid = get_ea(hf, "$LXUID");
+ attr->gid = get_ea(hf, "$LXGID");
+ attr->mode = get_ea(hf, "$LXMOD");
+ attr->size = get_file_size(hf);
+ } catch (lro_error &e) {
+ if (e.msg_code == err_msg::err_invalid_ea) return nullptr;
+ e.msg_args.push_back(path->data);
+ throw;
+ }
+ FILE_BASIC_INFO info;
+ if (!GetFileInformationByHandleEx(hf, FileBasicInfo, &info, sizeof info)) {
+ throw lro_error::from_win32_last(err_msg::err_get_ft, { path->data });
+ }
+ time_f2u(info.LastAccessTime, attr->at);
+ time_f2u(info.LastWriteTime, attr->mt);
+ time_f2u(info.ChangeTime, attr->ct);
+ return attr;
+}
+
+std::unique_ptr wsl_v2_reader::read_symlink_data(HANDLE hf) const {
+ const auto buf = std::make_unique(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ DWORD cnt;
+ if (!DeviceIoControl(hf, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &cnt, nullptr)) {
+ if (GetLastError() == ERROR_NOT_A_REPARSE_POINT) return nullptr;
+ throw lro_error::from_win32_last(err_msg::err_get_reparse, { path->data });
+ }
+ const auto pb = reinterpret_cast(buf.get());
+ if (pb->ReparseTag != IO_REPARSE_TAG_LX_SYMLINK) return nullptr;
+ const auto pl = pb->ReparseDataLength - 4;
+ auto s = std::make_unique(static_cast(pl) + 1);
+ memcpy(s.get(), pb->DataBuffer + 4, pl);
+ s[pl] = 0;
+ return s;
+}
+
+bool wsl_legacy_reader::is_legacy() const {
+ return true;
+}
+
+wsl_legacy_reader::wsl_legacy_reader(crwstr base) {
+ path = std::make_unique(base);
+}
+
+template
+bool has_ea(crwstr path, const char *name, const bool ignore_error) {
+ try {
+ get_ea(open_file(path, true, false).get(), name);
+ return true;
+ } catch (const lro_error &e) {
+ if (e.msg_code == err_msg::err_invalid_ea || ignore_error) return false;
+ throw;
+ }
+}
+
+uint32_t detect_version(crwstr path) {
+ wsl_v2_path p1(path), p2(path);
+ p1.data += L"rootfs\\";
+ p2.data += L"home\\";
+ if (has_ea(p1.data, "$LXUID", false)) return 2;
+ if (has_ea(p2.data, "LXATTRB", true)) return 0;
+ if (has_ea(p1.data, "LXATTRB", false)) return 1;
+ throw lro_error::from_other(err_msg::err_fs_detect, { path });
+}
+
+bool detect_wsl2(crwstr path) {
+ const wsl_v2_path p(path);
+ try {
+ open_file(p.data + L"ext4.vhdx", false, false);
+ return true;
+ } catch (const lro_error &e) {
+ if (e.msg_code == err_msg::err_open_file && e.err_code == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) return false;
+ throw;
+ }
+}
+
+std::unique_ptr select_wsl_writer(const uint32_t version, crwstr path) {
+ if (version == 0) return std::make_unique(path);
+ if (version == 1) return std::make_unique(path);
+ if (version == 2) return std::make_unique(path);
+ throw lro_error::from_other(err_msg::err_fs_version, { std::to_wstring(version) });
+}
+
+std::unique_ptr select_wsl_reader(const uint32_t version, crwstr path) {
+ if (version == 0) return std::make_unique(path);
+ if (version == 1) return std::make_unique(path);
+ if (version == 2) return std::make_unique(path);
+ throw lro_error::from_other(err_msg::err_fs_version, { std::to_wstring(version) });
+}
+
+bool move_directory(crwstr source_path, crwstr target_path) {
+ return MoveFile(source_path.c_str(), target_path.c_str());
+}
+
+void delete_directory(crwstr path) {
+ wsl_v2_path p(path);
+ enum_directory(p, false, [&](const enum_dir_type t) {
+ if (t == enum_dir_type::enter) return;
+ const auto dir = t == enum_dir_type::exit;
+ if (!(dir ? RemoveDirectory : DeleteFile)(p.data.c_str())) {
+ throw lro_error::from_win32_last(dir ? err_msg::err_delete_dir : err_msg::err_delete_file, { p.data });
+ }
+ });
+}
+
+bool check_in_use(crwstr path) {
+ try {
+ open_file(path, false, false, true);
+ } catch (const lro_error &e) {
+ if (e.msg_code == err_msg::err_open_file && e.err_code == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/LxRunOffline/fs.h b/src/fs.h
similarity index 96%
rename from LxRunOffline/fs.h
rename to src/fs.h
index 168f0ea..19401f9 100644
--- a/LxRunOffline/fs.h
+++ b/src/fs.h
@@ -1,140 +1,140 @@
-#pragma once
-#include "stdafx.h"
-#include "path.h"
-
-struct unix_time {
- uint64_t sec;
- uint32_t nsec;
-};
-
-struct file_attr {
- uint32_t mode, uid, gid;
- uint64_t size;
- unix_time at, mt, ct;
-};
-
-class fs_writer {
-public:
- std::unique_ptr path, target_path;
- virtual ~fs_writer() = default;
- virtual bool write_new_file(const file_attr *) = 0;
- virtual void write_file_data(const char *, uint32_t) = 0;
- virtual void write_directory(const file_attr *) = 0;
- virtual void write_symlink(const file_attr *, const char *) = 0;
- virtual void write_hard_link() = 0;
- virtual bool check_source_path(const file_path &) const = 0;
-};
-
-class archive_writer : public fs_writer {
- unique_ptr_del pa;
- unique_ptr_del pe;
- std::set ignored_files;
- void write_entry(const file_attr &);
- bool check_attr(const file_attr *);
- static void warn_ignored(crwstr);
-public:
- explicit archive_writer(crwstr);
- bool write_new_file(const file_attr *) override;
- void write_file_data(const char *, uint32_t) override;
- void write_directory(const file_attr *) override;
- void write_symlink(const file_attr *, const char *) override;
- void write_hard_link() override;
- bool check_source_path(const file_path &) const override;
-};
-
-class wsl_writer : public fs_writer {
-protected:
- unique_ptr_del hf_data;
- void write_data(HANDLE, const char *, uint32_t) const;
- virtual void write_attr(HANDLE, const file_attr *) = 0;
- virtual void write_symlink_data(HANDLE, const char *) const = 0;
- wsl_writer();
-public:
- bool write_new_file(const file_attr *) override;
- void write_file_data(const char *, uint32_t) override;
- void write_directory(const file_attr *) override;
- void write_symlink(const file_attr *, const char *) override;
- void write_hard_link() override;
- bool check_source_path(const file_path &) const override;
-};
-
-class wsl_v1_writer : public wsl_writer {
-protected:
- wsl_v1_writer() = default;
- void write_attr(HANDLE, const file_attr *) override;
- void write_symlink_data(HANDLE, const char *) const override;
-public:
- explicit wsl_v1_writer(crwstr);
-};
-
-class wsl_v2_writer : public wsl_writer {
- std::stack> dir_attr;
- void real_write_attr(HANDLE, const file_attr &, crwstr) const;
-protected:
- void write_attr(HANDLE, const file_attr *) override;
- void write_symlink_data(HANDLE, const char *) const override;
-public:
- explicit wsl_v2_writer(crwstr);
- ~wsl_v2_writer() override;
-};
-
-class wsl_legacy_writer : public wsl_v1_writer {
-public:
- explicit wsl_legacy_writer(crwstr);
-};
-
-class fs_reader {
-public:
- virtual ~fs_reader() = default;
- virtual void run(fs_writer &writer) = 0;
-};
-
-class archive_reader : public fs_reader {
- const wstr archive_path, root_path;
-public:
- archive_reader(wstr, wstr);
- void run(fs_writer &) override;
-};
-
-class wsl_reader : public fs_reader {
-protected:
- std::unique_ptr path;
- virtual std::unique_ptr read_attr(HANDLE) const = 0;
- virtual std::unique_ptr read_symlink_data(HANDLE) const = 0;
- virtual bool is_legacy() const;
-public:
- void run(fs_writer &) override;
- void run_checked(fs_writer &);
-};
-
-class wsl_v1_reader : public wsl_reader {
-protected:
- wsl_v1_reader() = default;
- std::unique_ptr read_attr(HANDLE) const override;
- std::unique_ptr read_symlink_data(HANDLE) const override;
-public:
- explicit wsl_v1_reader(crwstr);
-};
-
-class wsl_v2_reader : public wsl_reader {
-protected:
- std::unique_ptr read_attr(HANDLE) const override;
- std::unique_ptr read_symlink_data(HANDLE) const override;
-public:
- explicit wsl_v2_reader(crwstr);
-};
-
-class wsl_legacy_reader : public wsl_v1_reader {
-protected:
- bool is_legacy() const override;
-public:
- explicit wsl_legacy_reader(crwstr);
-};
-
-uint32_t detect_version(crwstr path);
-bool detect_wsl2(crwstr path);
-std::unique_ptr select_wsl_writer(uint32_t version, crwstr path);
-std::unique_ptr select_wsl_reader(uint32_t version, crwstr path);
-bool move_directory(crwstr source_path, crwstr target_path);
-void delete_directory(crwstr path);
-bool check_in_use(crwstr path);
+#pragma once
+#include "stdafx.h"
+#include "path.h"
+
+struct unix_time {
+ uint64_t sec;
+ uint32_t nsec;
+};
+
+struct file_attr {
+ uint32_t mode, uid, gid;
+ uint64_t size;
+ unix_time at, mt, ct;
+};
+
+class fs_writer {
+public:
+ std::unique_ptr path, target_path;
+ virtual ~fs_writer() = default;
+ virtual bool write_new_file(const file_attr *) = 0;
+ virtual void write_file_data(const char *, uint32_t) = 0;
+ virtual void write_directory(const file_attr *) = 0;
+ virtual void write_symlink(const file_attr *, const char *) = 0;
+ virtual void write_hard_link() = 0;
+ virtual bool check_source_path(const file_path &) const = 0;
+};
+
+class archive_writer : public fs_writer {
+ unique_ptr_del pa;
+ unique_ptr_del pe;
+ std::set ignored_files;
+ void write_entry(const file_attr &);
+ bool check_attr(const file_attr *);
+ static void warn_ignored(crwstr);
+public:
+ explicit archive_writer(crwstr);
+ bool write_new_file(const file_attr *) override;
+ void write_file_data(const char *, uint32_t) override;
+ void write_directory(const file_attr *) override;
+ void write_symlink(const file_attr *, const char *) override;
+ void write_hard_link() override;
+ bool check_source_path(const file_path &) const override;
+};
+
+class wsl_writer : public fs_writer {
+protected:
+ unique_ptr_del hf_data;
+ void write_data(HANDLE, const char *, uint32_t) const;
+ virtual void write_attr(HANDLE, const file_attr *) = 0;
+ virtual void write_symlink_data(HANDLE, const char *) const = 0;
+ wsl_writer();
+public:
+ bool write_new_file(const file_attr *) override;
+ void write_file_data(const char *, uint32_t) override;
+ void write_directory(const file_attr *) override;
+ void write_symlink(const file_attr *, const char *) override;
+ void write_hard_link() override;
+ bool check_source_path(const file_path &) const override;
+};
+
+class wsl_v1_writer : public wsl_writer {
+protected:
+ wsl_v1_writer() = default;
+ void write_attr(HANDLE, const file_attr *) override;
+ void write_symlink_data(HANDLE, const char *) const override;
+public:
+ explicit wsl_v1_writer(crwstr);
+};
+
+class wsl_v2_writer : public wsl_writer {
+ std::stack> dir_attr;
+ void real_write_attr(HANDLE, const file_attr &, crwstr) const;
+protected:
+ void write_attr(HANDLE, const file_attr *) override;
+ void write_symlink_data(HANDLE, const char *) const override;
+public:
+ explicit wsl_v2_writer(crwstr);
+ ~wsl_v2_writer() override;
+};
+
+class wsl_legacy_writer : public wsl_v1_writer {
+public:
+ explicit wsl_legacy_writer(crwstr);
+};
+
+class fs_reader {
+public:
+ virtual ~fs_reader() = default;
+ virtual void run(fs_writer &writer) = 0;
+};
+
+class archive_reader : public fs_reader {
+ const wstr archive_path, root_path;
+public:
+ archive_reader(wstr, wstr);
+ void run(fs_writer &) override;
+};
+
+class wsl_reader : public fs_reader {
+protected:
+ std::unique_ptr path;
+ virtual std::unique_ptr read_attr(HANDLE) const = 0;
+ virtual std::unique_ptr read_symlink_data(HANDLE) const = 0;
+ virtual bool is_legacy() const;
+public:
+ void run(fs_writer &) override;
+ void run_checked(fs_writer &);
+};
+
+class wsl_v1_reader : public wsl_reader {
+protected:
+ wsl_v1_reader() = default;
+ std::unique_ptr read_attr(HANDLE) const override;
+ std::unique_ptr read_symlink_data(HANDLE) const override;
+public:
+ explicit wsl_v1_reader(crwstr);
+};
+
+class wsl_v2_reader : public wsl_reader {
+protected:
+ std::unique_ptr read_attr(HANDLE) const override;
+ std::unique_ptr read_symlink_data(HANDLE) const override;
+public:
+ explicit wsl_v2_reader(crwstr);
+};
+
+class wsl_legacy_reader : public wsl_v1_reader {
+protected:
+ bool is_legacy() const override;
+public:
+ explicit wsl_legacy_reader(crwstr);
+};
+
+uint32_t detect_version(crwstr path);
+bool detect_wsl2(crwstr path);
+std::unique_ptr select_wsl_writer(uint32_t version, crwstr path);
+std::unique_ptr select_wsl_reader(uint32_t version, crwstr path);
+bool move_directory(crwstr source_path, crwstr target_path);
+void delete_directory(crwstr path);
+bool check_in_use(crwstr path);
diff --git a/LxRunOffline/main.cpp b/src/main.cpp
similarity index 96%
rename from LxRunOffline/main.cpp
rename to src/main.cpp
index 1d38b45..3e9c12c 100644
--- a/LxRunOffline/main.cpp
+++ b/src/main.cpp
@@ -1,340 +1,339 @@
-#include "stdafx.h"
-#include "error.h"
-#include "fs.h"
-#include "reg.h"
-#include "shortcut.h"
-#include "utils.h"
-
-namespace po = boost::program_options;
-
-void check_running(crwstr name) {
- const auto p = get_distro_dir(name);
- if (check_in_use(p + L"\\rootfs\\init") || check_in_use(p + L"\\ext4.vhdx")) {
- throw lro_error::from_other(err_msg::err_distro_running, { name });
- }
-}
-
-#ifdef __MINGW32__
-extern "C"
-#endif
-int wmain(int argc, wchar_t **argv) {
- const auto out_mode = _setmode(_fileno(stdout), _O_U16TEXT);
- const auto err_mode = _setmode(_fileno(stderr), _O_U16TEXT);
- if (out_mode == -1 || err_mode == -1) {
- log_warning(L"Failed to set output mode to UTF-16.");
- }
-
- wstr name;
- po::options_description desc("Options");
- desc.add_options()(",n", po::wvalue(&name)->required(), "Name of the distribution");
- po::variables_map vm;
- auto parse_args = [&]() {
- po::store(po::parse_command_line(argc - 1, argv + 1, desc), vm);
- po::notify(vm);
- };
-
- try {
- if (win_build < 17134) {
- throw lro_error::from_other(err_msg::err_version_old, { L"1803", L"17134" });
- }
- if (argc < 2) {
- throw lro_error::from_other(err_msg::err_no_action, {});
-#ifdef LXRUNOFFLINE_VERSION
- } else if (!wcscmp(argv[1], L"version")) {
- std::wcout << L"LxRunOffline " << LXRUNOFFLINE_VERSION << '\n';
-#endif
- } else if (!wcscmp(argv[1], L"l") || !wcscmp(argv[1], L"list")) {
- for (crwstr s : list_distros()) {
- std::wcout << s << '\n';
- }
- } else if (!wcscmp(argv[1], L"gd") || !wcscmp(argv[1], L"get-default")) {
- std::wcout << get_default_distro() << '\n';
- } else if (!wcscmp(argv[1], L"sd") || !wcscmp(argv[1], L"set-default")) {
- parse_args();
- set_default_distro(name);
- } else if (!wcscmp(argv[1], L"i") || !wcscmp(argv[1], L"install")) {
- wstr dir, file, root, conf_path;
- uint32_t ver;
- bool shortcut;
- desc.add_options()
- (",d", po::wvalue(&dir)->required(), "The directory to install the distribution into.")
- (",f", po::wvalue(&file)->required(), "The tar file containing the root filesystem of the distribution to be installed. If a file of the same name with a .xml extension exists and \"-c\" isn't specified, that file will be imported as a config file.")
- (",r", po::wvalue(&root), "The directory in the tar file to extract. This argument is optional.")
- (",c", po::wvalue(&conf_path), "The config file to use. This argument is optional.")
- (",v", po::wvalue(&ver)->default_value(win_build >= 17763 ? 2 : 1), "The version of filesystem to use, latest available one if not specified.")
- (",s", po::bool_switch(&shortcut), "Create a shortcut for this distribution on Desktop.");
- parse_args();
- reg_config conf;
- if (!conf_path.empty()) conf.load_file(conf_path);
- else {
- try {
- conf.load_file(file + L".xml");
- } catch (const lro_error &e) {
- if (e.msg_code == err_msg::err_open_file) {
- if (e.err_code != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
- log_warning(e.format());
- }
- } else throw;
- }
- }
- register_distro(name, dir, ver);
- conf.configure_distro(name, config_all);
- auto writer = select_wsl_writer(ver, dir);
- archive_reader(file, root).run(*writer);
- if (shortcut) {
- wchar_t *s;
- auto hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, nullptr, &s);
- if (FAILED(hr)) throw lro_error::from_hresult(err_msg::err_create_shortcut, {}, hr);
- unique_ptr_del dp(s, &CoTaskMemFree);
- create_shortcut(name, dp.get() + (L'\\' + name + L".lnk"), L"");
- }
- log_warning(L"Love this tool? Would you like to make a donation: https://github.com/DDoSolitary/LxRunOffline/blob/master/README.md#donation");
- } else if (!wcscmp(argv[1], L"ui") || !wcscmp(argv[1], L"uninstall")) {
- parse_args();
- check_running(name);
- auto dir = get_distro_dir(name);
- unregister_distro(name);
- delete_directory(dir);
- } else if (!wcscmp(argv[1], L"rg") || !wcscmp(argv[1], L"register")) {
- wstr dir, conf_path;
- desc.add_options()
- (",d", po::wvalue(&dir)->required(), "The directory containing the distribution.")
- (",c", po::wvalue(&conf_path), "The config file to use. This argument is optional.");
- parse_args();
- const auto is_wsl2 = detect_wsl2(dir);
- reg_config conf(is_wsl2);
- if (!conf_path.empty()) conf.load_file(conf_path);
- register_distro(name, dir, is_wsl2 ? 2 : detect_version(dir));
- conf.configure_distro(name, config_all);
- } else if (!wcscmp(argv[1], L"ur") || !wcscmp(argv[1], L"unregister")) {
- parse_args();
- unregister_distro(name);
- } else if (!wcscmp(argv[1], L"m") || !wcscmp(argv[1], L"move")) {
- wstr dir;
- desc.add_options()(",d", po::wvalue(&dir)->required(), "The directory to move the distribution to.");
- parse_args();
- check_running(name);
- auto sp = get_distro_dir(name);
- if (!move_directory(sp, dir)) {
- auto ver = get_distro_version(name);
- auto writer = select_wsl_writer(ver, dir);
- select_wsl_reader(ver, sp)->run_checked(*writer);
- delete_directory(sp);
- }
- set_distro_dir(name, dir);
- } else if (!wcscmp(argv[1], L"d") || !wcscmp(argv[1], L"duplicate")) {
- wstr new_name, dir, conf_path;
- uint32_t ver;
- desc.add_options()
- (",d", po::wvalue(&dir)->required(), "The directory to copy the distribution to.")
- (",N", po::wvalue(&new_name)->required(), "Name of the new distribution.")
- (",c", po::wvalue(&conf_path), "The config file to use. This argument is optional.")
- (",v", po::wvalue(&ver)->default_value(-1), "The version of filesystem to use, same as source if not specified.");
- parse_args();
- reg_config conf;
- conf.load_distro(name, config_all);
- auto is_wsl2 = conf.is_wsl2();
- if (!conf_path.empty()) conf.load_file(conf_path);
- is_wsl2 |= conf.is_wsl2();
- if (is_wsl2 && ~ver) throw lro_error::from_other(err_msg::err_wsl2_unsupported, { L"-v" });
- auto ov = get_distro_version(name);
- auto nv = ~ver ? ver : ov;
- register_distro(new_name, dir, nv);
- conf.configure_distro(new_name, config_all);
- auto writer = select_wsl_writer(nv, dir);
- select_wsl_reader(ov, get_distro_dir(name))->run_checked(*writer);
- } else if (!wcscmp(argv[1], L"e") || !wcscmp(argv[1], L"export")) {
- wstr file;
- desc.add_options()(",f", po::wvalue(&file)->required(), "Path to the .tar.gz file to export to. A config file will also be exported to this file name with a .xml extension.");
- parse_args();
- reg_config conf;
- conf.load_distro(name, config_all);
- if (conf.is_wsl2()) throw lro_error::from_other(err_msg::err_wsl2_unsupported, { L"export" });
- archive_writer writer(file);
- select_wsl_reader(get_distro_version(name), get_distro_dir(name))->run(writer);
- conf.save_file(file + L".xml");
- } else if (!wcscmp(argv[1], L"r") || !wcscmp(argv[1], L"run")) {
- wstr cmd;
- bool no_cwd;
- desc.add_options()
- (",c", po::wvalue(&cmd), "The command to run. Launch default shell if not specified.")
- (",w", po::bool_switch(&no_cwd), "Don't use the working directory in Windows for the Linux process.");
- parse_args();
- auto hw = LoadLibraryEx(L"wslapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
- if (hw == INVALID_HANDLE_VALUE) throw lro_error::from_win32_last(err_msg::err_no_wslapi, {});
- auto launch = reinterpret_cast(GetProcAddress(hw, "WslLaunchInteractive"));
- if (!launch) throw lro_error::from_win32_last(err_msg::err_no_wslapi, {});
- DWORD code;
- auto hr = launch(name.c_str(), cmd.empty() ? nullptr : cmd.c_str(), !no_cwd, &code);
- if (FAILED(hr)) throw lro_error::from_hresult(err_msg::err_launch_distro, { name }, hr);
- return code;
- } else if (!wcscmp(argv[1], L"di") || !wcscmp(argv[1], L"get-dir")) {
- parse_args();
- std::wcout << get_distro_dir(name);
- } else if (!wcscmp(argv[1], L"gv") || !wcscmp(argv[1], L"get-version")) {
- parse_args();
- std::wcout << get_distro_version(name);
- } else if (!wcscmp(argv[1], L"ge") || !wcscmp(argv[1], L"get-env")) {
- parse_args();
- reg_config conf;
- conf.load_distro(name, config_env);
- for (crwstr s : conf.env) {
- std::wcout << s << '\n';
- }
- } else if (!wcscmp(argv[1], L"se") || !wcscmp(argv[1], L"set-env")) {
- reg_config conf;
- desc.add_options()(",v", po::wvalue>(&conf.env)->required(), "Environment variables to be set. This argument can be specified multiple times.");
- parse_args();
- conf.configure_distro(name, config_env);
- } else if (!wcscmp(argv[1], L"ae") || !wcscmp(argv[1], L"add-env")) {
- wstr env;
- bool force;
- desc.add_options()
- (",v", po::wvalue(&env)->required(), "The environment variable to add.")
- (",f", po::bool_switch(&force), "Overwrite if the environment variable already exists.");
- parse_args();
- auto p = env.find(L'=');
- if (p == wstr::npos) throw lro_error::from_other(err_msg::err_invalid_env, { env });
- auto env_name = env.substr(0, p + 1);
- reg_config conf;
- conf.load_distro(name, config_env);
- auto it = std::find_if(conf.env.begin(), conf.env.end(), [&](crwstr s) {
- return !s.compare(0, env_name.size(), env_name);
- });
- if (it != conf.env.end()) {
- if (force) conf.env.erase(it);
- else throw lro_error::from_other(err_msg::err_env_exists, { *it });
- }
- conf.env.push_back(env);
- conf.configure_distro(name, config_env);
- } else if (!wcscmp(argv[1], L"re") || !wcscmp(argv[1], L"remove-env")) {
- wstr env_name;
- desc.add_options()(",v", po::wvalue