diff --git a/CMakeLists.txt b/CMakeLists.txt index 99e6d29..d55ee21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project (Unlocker) +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules) + SET(UNLOCKER_STATIC_LIBS_WIN ON CACHE BOOL "Links statically") # Set to OFF for dynamic linking IF (MSVC) @@ -26,13 +28,21 @@ endif() include_directories(${CURL_INCLUDE_DIRS}) -find_package(LibArchive REQUIRED) +#find_package(LibArchive REQUIRED) -if(LibArchive_FOUND) - message (STATUS "LibArchive found, version ${LibArchive_VERSION}") -endif() +#if(LibArchive_FOUND) +# message (STATUS "LibArchive found, version ${LibArchive_VERSION}") +#endif() + +#include_directories(${LibArchive_INCLUDE_DIRS}) -include_directories(${LibArchive_INCLUDE_DIRS}) +find_package(LibZip REQUIRED) + +include_directories(${LIBZIP_INCLUDE_DIRS}) + +if(LIBZIP_FOUND) + message (STATUS "LibZip found") +endif() # main include files include_directories ("${PROJECT_SOURCE_DIR}/include") @@ -47,18 +57,18 @@ set (SOURCE_FILES src/unlocker.cpp / src/installinfoutils.cpp / src/servicestoputils.cpp / src/patchutils.cpp / - ) + src/tar.cpp ) IF (MSVC) IF (UNLOCKER_STATIC_LIBS_WIN) # Preprocessor definitions needed to avoid name mangling when statically importing libs on MSVC compiler # Name mangling is needed if libraries are built dynamically with MSVC # Should not be an issue with other compilers - add_compile_definitions( "CURL_STATICLIB" "LIBARCHIVE_STATIC") + add_compile_definitions( "CURL_STATICLIB" ) ENDIF() ENDIF (MSVC) -add_executable(Unlocker ${SOURCE_FILES}) +add_executable(Unlocker ${SOURCE_FILES} ) set_target_properties(Unlocker PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON) @@ -68,7 +78,8 @@ endif() target_link_libraries (Unlocker ${ZLIB_LIBRARIES}) target_link_libraries (Unlocker ${CURL_LIBRARIES}) -target_link_libraries (Unlocker ${LibArchive_LIBRARIES}) +#target_link_libraries (Unlocker ${LibArchive_LIBRARIES}) +target_link_libraries (Unlocker ${LIBZIP_LIBRARY}) if(WIN32) target_link_libraries (Unlocker ws2_32 Wldap32) @@ -91,7 +102,7 @@ ENDIF(MSVC) set (TEST_SOURCES tests/test_patch.cpp / src/debugutils.cpp / src/patchutils.cpp / - ) + src/tar.cpp ) add_executable( TestPatch ${TEST_SOURCES} ) diff --git a/README.md b/README.md index 044927c..eabd6f1 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ I created the C++ version of this tool to avoid issues between different Python * ```--install``` *(Default behavior when called with no arguments)* Installs the patch. * ```--uninstall``` Uninstalls the patch. Works only if the patch was previously installed and the *backup* folder is intact +* ```--download-tools``` Only downloads the tools in the `tools` folder * ```--help``` Shows a list of the available options **I tested the program on Windows with VMware Player 15.0.** I plan to test it on Linux soon, though as of now I don't know if it works there, or with other VMware versions. @@ -41,9 +42,9 @@ The program can be compiled on Windows with CMake, provided you get or compile t The program needs the following libraries: * libcurl -* zlib -* libarchive +* libzip +The tar extraction is handled internally, while the zip decompression needs the `libzip` library. `libcurl` is used for downloading the tools. **Windows:** Run *CMake* on the source folder to generate a Visual Studio Project. The CMakeList is set up to compile with static libraries, if you have shared ones change *CMakeLists.txt* file accordingly (**UNLOCKER_STATIC_LIBS_WIN** variable). Point the CMake utility to the proper library/headers paths when prompted or add them to your PATH. diff --git a/include/archiveutils.h b/include/archiveutils.h index dacbd81..b97fd38 100644 --- a/include/archiveutils.h +++ b/include/archiveutils.h @@ -2,21 +2,30 @@ #define ARCHIVEUTILS_H #include "filesystem.hpp" - +/* #include #include - +*/ #ifdef __linux__ #include #endif +#define LIBZIP_STATIC + +#include "zipconf.h" +#include "zip.h" +#include "tar.h" #include "debugutils.h" +#define AR_BUFFER_SIZE 4096 + namespace Archive { - int copy_data(struct archive* ar, struct archive* aw); + /*int copy_data(struct archive* ar, struct archive* aw); bool extract(const char* from, const char* filename, const char* to); - bool extract_s(fs::path from, std::string filename, fs::path to); + bool extract_s(fs::path from, std::string filename, fs::path to);*/ + bool extract_zip(fs::path from, std::string filename, fs::path to); + bool extract_tar(fs::path from, std::string filename, fs::path to); } #endif // ARCHIVEUTILS_H diff --git a/include/config.h b/include/config.h index 9eeef27..409a770 100644 --- a/include/config.h +++ b/include/config.h @@ -7,12 +7,14 @@ */ // Program options -#define PROG_VERSION "v1.1.2" +#define PROG_VERSION "v1.1.3" // Install - Default option #define INSTALL_OPTION "--install" // Uninstall #define UNINSTALL_OPTION "--uninstall" +// Only download tools +#define DOWNLOADONLY_OPTION "--download-tools" // Show help message #define HELP_OPTION "--help" diff --git a/include/tar.h b/include/tar.h new file mode 100644 index 0000000..30fe82c --- /dev/null +++ b/include/tar.h @@ -0,0 +1,78 @@ +#ifndef TAR_H +#define TAR_H + +#include +#include +#include +#include + + +enum tar_type_flag { + REGTYPE = '0', /* regular file */ + AREGTYPE = '\0', /* regular file */ + LNKTYPE = '1', /* link */ + SYMTYPE = '2', /* reserved */ + CHRTYPE = '3', /* character special */ + BLKTYPE = '4', /* block special */ + DIRTYPE = '5', /* directory */ + FIFOTYPE = '6', /* FIFO special */ + CONTTYPE = '7', /* reserved */ + XHDTYPE = 'x', /* Extended header referring to the next file in the archive */ + XGLTYPE = 'g' /* Global extended header */ +}; + +struct tar_posix_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + char pad[12]; /* 500 */ + /* 512 */ +}; + +class Tar +{ +public: + + struct File + { + std::string name; + int mode; + int ownerId; + int groupId; + int size; + int lastModified; + tar_type_flag typeflag; + std::string linkedName; + size_t position; + }; + + Tar(std::string filename); + ~Tar(); + void extract(const File& file, std::string to); + bool extract(std::string fileName, std::string to); + const std::vector& getFileList() const; +private: + FILE* tarfile = NULL; + std::vector fileList; + + int oct_to_int(char* oct); + int oct_to_sz(char* oct); + + static constexpr int EXTRACT_BUFFER_SZ = 1024 * 16; +}; + +#endif // TAR_H \ No newline at end of file diff --git a/modules/FindLibZip.cmake b/modules/FindLibZip.cmake new file mode 100644 index 0000000..cf7157e --- /dev/null +++ b/modules/FindLibZip.cmake @@ -0,0 +1,34 @@ +FIND_PATH(LIBZIP_INCLUDE_DIR + zip.h + "$ENV{LIB_DIR}/include" + "$ENV{INCLUDE}" + /usr/local/include + /usr/include +) + +FIND_PATH(LIBZIP_CONF_INCLUDE_DIR + zipconf.h + "$ENV{LIB_DIR}/include" + "$ENV{LIB_DIR}/lib/libzip/include" + "$ENV{LIB}/lib/libzip/include" + /usr/local/lib/libzip/include + /usr/lib/libzip/include + /usr/local/include + /usr/include + "$ENV{INCLUDE}" +) + +FIND_LIBRARY(LIBZIP_LIBRARY NAMES zip PATHS "$ENV{LIB_DIR}/lib" "$ENV{LIB}" /usr/local/lib /usr/lib ) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibZip DEFAULT_MSG + LIBZIP_LIBRARY LIBZIP_INCLUDE_DIR LIBZIP_CONF_INCLUDE_DIR) + +SET(LIBZIP_INCLUDE_DIRS ${LIBZIP_INCLUDE_DIR} ${LIBZIP_CONF_INCLUDE_DIR}) +MARK_AS_ADVANCED(LIBZIP_LIBRARY LIBZIP_INCLUDE_DIR LIBZIP_CONF_INCLUDE_DIR LIBZIP_INCLUDE_DIRS) + +IF (LIBZIP_FOUND) + MESSAGE(STATUS "Found libzip: ${LIBZIP_LIBRARY}") +ELSE (LIPZIP_FOUND) + MESSAGE(FATAL_ERROR "Could not find libzip") +ENDIF (LIBZIP_FOUND) \ No newline at end of file diff --git a/src/archiveutils.cpp b/src/archiveutils.cpp index e9e73b2..112326e 100644 --- a/src/archiveutils.cpp +++ b/src/archiveutils.cpp @@ -1,9 +1,10 @@ #include "archiveutils.h" +#include /** Helper function to extract a file using filesystem::path and std::string */ -bool Archive::extract_s(fs::path from, std::string filename, fs::path to) +/*bool Archive::extract_s(fs::path from, std::string filename, fs::path to) { std::string from_s = from.string(); std::string to_s = to.string(); @@ -32,12 +33,12 @@ int Archive::copy_data(struct archive* ar, struct archive* aw) return (r); } } -} +}*/ /** Extract from archive "from", the file "filename" into path "to" (filename has to be included here too) */ -bool Archive::extract(const char* from, const char* filename, const char* to) +/*bool Archive::extract(const char* from, const char* filename, const char* to) { struct archive* a; struct archive* ext; @@ -45,7 +46,7 @@ bool Archive::extract(const char* from, const char* filename, const char* to) int flags; int r; - /* Select which attributes we want to restore. */ + // Select which attributes we want to restore. flags = ARCHIVE_EXTRACT_TIME; flags |= ARCHIVE_EXTRACT_PERM; flags |= ARCHIVE_EXTRACT_ACL; @@ -57,7 +58,7 @@ bool Archive::extract(const char* from, const char* filename, const char* to) ext = archive_write_disk_new(); archive_write_disk_set_options(ext, flags); archive_write_disk_set_standard_lookup(ext); - if ((r = archive_read_open_filename(a, from, 10240))) + if ((r = archive_read_open_filename(a, from, 10240)) != ARCHIVE_OK) return false; for (;;) { r = archive_read_next_header(a, &entry); @@ -81,7 +82,7 @@ bool Archive::extract(const char* from, const char* filename, const char* to) else if (archive_entry_size(entry) > 0) { r = copy_data(a, ext); if (r < ARCHIVE_OK) - logerr(archive_error_string(ext)); + return false; if (r < ARCHIVE_WARN) return false; } @@ -99,4 +100,59 @@ bool Archive::extract(const char* from, const char* filename, const char* to) archive_write_close(ext); archive_write_free(ext); return true; +}*/ + +bool Archive::extract_tar(fs::path from, std::string filename, fs::path to) +{ + std::string from_s = from.string(), to_s = to.string(); + + try + { + Tar tarfile(from_s); + if (!tarfile.extract(filename, to_s)) { + fprintf(stderr, "Error while extracting %s. Not in the archive\n", filename.c_str()); + return false; + } + return true; + } + catch (const std::exception& exc) + { + fprintf(stderr, "%s\n", exc.what()); + return false; + } } + +bool Archive::extract_zip(fs::path from, std::string filename, fs::path to) +{ + std::string from_s = from.string(), to_s = to.string(); + const char* from_c = from_s.c_str(), * to_c = to_s.c_str(); + int zerr = ZIP_ER_OK; + zip_t* in_archive = zip_open(from_c, ZIP_RDONLY, &zerr); + + if (zerr != ZIP_ER_OK) { + fprintf(stderr, "Error while opening %s\n", from_c); + return false; + } + + zip_file_t* target_file = zip_fopen(in_archive, filename.c_str(), 0); + + if (target_file == NULL) { + fprintf(stderr, "Error while opening %s in %s. %s\n", filename.c_str(), from_c, zip_strerror(in_archive)); + zip_close(in_archive); + return false; + } + + FILE* out_f = fopen(to_c, "wb"); + + char buffer[AR_BUFFER_SIZE]; + int sz = 0; + while ((sz = zip_fread(target_file, buffer, AR_BUFFER_SIZE)) > 0) { + fwrite(buffer, sizeof(char), sz, out_f); + } + + fclose(out_f); + + zip_fclose(target_file); + zip_close(in_archive); + return true; +} \ No newline at end of file diff --git a/src/tar.cpp b/src/tar.cpp new file mode 100644 index 0000000..7de8e8d --- /dev/null +++ b/src/tar.cpp @@ -0,0 +1,126 @@ +#include "tar.h" + +Tar::Tar(std::string filename) +{ + tarfile = fopen(filename.c_str(), "rb"); + + if (tarfile == NULL) + { + throw std::runtime_error("Couldn't open " + filename); + } + + tar_posix_header fileheader = {}; + + int readsz = 0; + while ((readsz = fread(reinterpret_cast(&fileheader), 1, sizeof(fileheader), tarfile)) == 512) + { + int checksum = 0; + char* header_data = reinterpret_cast(&fileheader); + + for (int i = 0; i < sizeof(tar_posix_header); i++) + checksum += ((unsigned char*)(&(fileheader)))[i]; + for (int i = 0; i < 8; i++) + checksum += (' ' - (unsigned char)fileheader.chksum[i]); + + int header_checksum = oct_to_int(fileheader.chksum); + + if (checksum != header_checksum) { + continue; + } + + File file; + file.name = std::string(fileheader.name); + + switch (fileheader.size[0]) { + case 0xFF: // base-256 + throw std::runtime_error("base-256 size encoding unsupported"); + break; + case 0x80: + throw std::runtime_error("base-256 size encoding unsupported"); + break; + default: // octal + file.size = oct_to_sz(fileheader.size); + break; + } + file.groupId = oct_to_int(fileheader.gid); + file.mode = oct_to_int(fileheader.mode); + file.ownerId = oct_to_int(fileheader.uid); + file.lastModified = oct_to_int(fileheader.mtime); + file.position = ftell(tarfile); + file.typeflag = static_cast(fileheader.typeflag); + + fileList.emplace_back(file); + + int toskip = (std::ceil(file.size / 512.0) * 512); + fseek(tarfile, toskip, SEEK_CUR); + } + + fseek(tarfile, 0, SEEK_SET); +} + +Tar::~Tar() +{ + if (tarfile != NULL) { + fclose(tarfile); + tarfile = NULL; + } +} + +void Tar::extract(const File& file, std::string to) +{ + if (file.typeflag != REGTYPE && file.typeflag != AREGTYPE) { + throw std::runtime_error("Can't extract this type of file. Only regular files supported"); + } + + fseek(tarfile, file.position, SEEK_SET); + int size = file.size; + + FILE* outfile = fopen(to.c_str(), "wb"); + + if (outfile != NULL) { + char* buffer = new char[EXTRACT_BUFFER_SZ]; + + while (size > 0) { + int tocopy = std::min(EXTRACT_BUFFER_SZ, size); + fread(buffer, 1, tocopy, tarfile); + fwrite(buffer, 1, tocopy, outfile); + + size -= tocopy; + } + delete[] buffer; + fclose(outfile); + } + else { + throw std::runtime_error("Error while opening " + to + " for extracting"); + } +} + + +bool Tar::extract(std::string fileName, std::string to) +{ + for (const File& file : fileList) { + if (file.name == fileName) { + extract(file, to); + return true; + } + } + return false; +} + +const std::vector& Tar::getFileList() const +{ + return fileList; +} + + +int Tar::oct_to_int(char* oct) +{ + int res; + return sscanf(oct, "%o", &res) == 1 ? res : 0; +} + +int Tar::oct_to_sz(char* oct) +{ + size_t res; + return sscanf(oct, "%zo", &res) == 1 ? res : 0; +} diff --git a/src/unlocker.cpp b/src/unlocker.cpp index f03ffca..cc15805 100644 --- a/src/unlocker.cpp +++ b/src/unlocker.cpp @@ -93,6 +93,8 @@ int main(int argc, const char* argv[]) showhelp(); else if (stricmp(arg, INSTALL_OPTION) == 0) install(); + else if (stricmp(arg, DOWNLOADONLY_OPTION) == 0) + downloadTools(fs::path(".") / TOOLS_DOWNLOAD_FOLDER); else { logd("Unrecognized command."); @@ -310,6 +312,7 @@ void showhelp() << "Run the program with one of these options:" << std::endl << " --install (default): install the patch" << std::endl << " --uninstall: remove the patch" << std::endl + << " --download-tools: only download the tools" << std::endl << " --help: show this help message" << std::endl; } @@ -615,7 +618,7 @@ void preparePatch(fs::path backupPath) logerr("OS not supported"); exit(1); // Better stop before messing things up #endif -} + } // Download tools into "path" void downloadTools(fs::path path) @@ -669,8 +672,8 @@ void downloadTools(fs::path path) if (toolsAvailable) // if tools were successfully downloaded, extract them to destination folder { - success = Archive::extract_s(temppath / FUSION_DEF_TOOLS_NAME, FUSION_DEF_TOOLS_ZIP, temppath / FUSION_DEF_TOOLS_ZIP); - success &= Archive::extract_s(temppath / FUSION_DEF_PRE15_TOOLS_NAME, FUSION_DEF_PRE15_TOOLS_ZIP, temppath / FUSION_DEF_PRE15_TOOLS_ZIP); + success = Archive::extract_tar(temppath / FUSION_DEF_TOOLS_NAME, FUSION_DEF_TOOLS_ZIP, temppath / FUSION_DEF_TOOLS_ZIP); + success &= Archive::extract_tar(temppath / FUSION_DEF_PRE15_TOOLS_NAME, FUSION_DEF_PRE15_TOOLS_ZIP, temppath / FUSION_DEF_PRE15_TOOLS_ZIP); if (!success) { @@ -678,8 +681,8 @@ void downloadTools(fs::path path) continue; } - success = Archive::extract_s(temppath / FUSION_DEF_TOOLS_ZIP, FUSION_TAR_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); - success &= Archive::extract_s(temppath / FUSION_DEF_PRE15_TOOLS_ZIP, FUSION_TAR_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); + success = Archive::extract_zip(temppath / FUSION_DEF_TOOLS_ZIP, FUSION_TAR_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); + success &= Archive::extract_zip(temppath / FUSION_DEF_PRE15_TOOLS_ZIP, FUSION_TAR_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); // Cleanup zips fs::remove(temppath / FUSION_DEF_TOOLS_ZIP); @@ -710,7 +713,7 @@ void downloadTools(fs::path path) fs::path temppath = fs::temp_directory_path(); - success = Archive::extract_s(temppath / FUSION_DEF_CORE_NAME, FUSION_DEF_CORE_NAME_ZIP, temppath / FUSION_DEF_CORE_NAME_ZIP); + success = Archive::extract_tar(temppath / FUSION_DEF_CORE_NAME, FUSION_DEF_CORE_NAME_ZIP, temppath / FUSION_DEF_CORE_NAME_ZIP); if (!success) { logerr("Couldn't extract from the tar file"); // Error in the tar file, try the next version number @@ -719,8 +722,8 @@ void downloadTools(fs::path path) logd("Extracting from .zip to destination folder ..."); - success = Archive::extract_s(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); - success = Archive::extract_s(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); + success = Archive::extract_zip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_TOOLS_ISO, path / FUSION_ZIP_TOOLS_NAME); + success = Archive::extract_zip(temppath / FUSION_DEF_CORE_NAME_ZIP, FUSION_ZIP_PRE15_TOOLS_ISO, path / FUSION_ZIP_PRE15_TOOLS_NAME); // Cleanup zip file fs::remove(temppath / FUSION_DEF_CORE_NAME_ZIP); diff --git a/tests/test_patch.cpp b/tests/test_patch.cpp index ef2e5f0..0a315c8 100644 --- a/tests/test_patch.cpp +++ b/tests/test_patch.cpp @@ -11,9 +11,12 @@ #include #include #include +#include "filesystem.hpp" #include "patchutils.h" #include "installinfoutils.h" +#include "tar.h" +void test_tar(); void do_test_patch(); void test_failed(bool to_cleanup = true); void test_equalness(); @@ -30,6 +33,7 @@ int main(int argc, char** argv) std::cout << "Basepath for test files is: " << basepath_str << "\n"; } + test_tar(); do_test_patch(); test_equalness(); cleanup(); @@ -39,6 +43,12 @@ int main(int argc, char** argv) return 0; } +void test_tar() +{ + Tar tarfile((fs::temp_directory_path() / "com.vmware.fusion.zip.tar").string()); + tarfile.extract("com.vmware.fusion.zip", (fs::temp_directory_path() / "com.vmware.fusion.zip").string()); +} + void do_test_patch() { fs::path basepath = ".";