diff --git a/.gitignore b/.gitignore index a1f912b77..d1b7becb9 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,47 @@ aclocal.m4 .deps /*.exe *.dSYM/ + +# --- Added for CMake, MSVC, vcpkg, and Python workflows --- + +# CMake & vcpkg +/build-*/ +/out/ +/bin/ +CMakeCache.txt +CMakeFiles/ +CMakeUserPresets.json +vcpkg_installed/ +.vcpkg/ +vcpkg-manifest-install.log + +# Visual Studio & Windows artifacts +.vs/ +*.sln +*.suo +*.user +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.obj +*.lib +*.exp +*.pdb +*.ilk +*.dll +*.idb +*.manifest + +# Python +__pycache__/ +*.py[cod] +*.class +.pytest_cache/ +.venv/ +venv/ + +# Scratch & Temporary +test_*.c +test_*.py +child_*.txt +*.i diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..024cf652c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,190 @@ +cmake_minimum_required(VERSION 3.15) +project(rsync C CXX) + +# Find dependencies +find_package(ZLIB QUIET) +if(NOT ZLIB_FOUND) + message(STATUS 'ZLIB not found in environment, fetching via FetchContent') + include(FetchContent) + FetchContent_Declare( + zlib + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG v1.3.1 + ) + FetchContent_MakeAvailable(zlib) + if(NOT TARGET ZLIB::ZLIB) + add_library(ZLIB::ZLIB ALIAS zlibstatic) + endif() +endif() +find_package(zstd CONFIG REQUIRED) +find_package(lz4 CONFIG REQUIRED) +find_package(xxHash CONFIG REQUIRED) +find_package(OpenSSL REQUIRED) + +find_path(POPT_INCLUDE_DIR NAMES popt.h) +find_library(POPT_LIBRARY NAMES popt) + +# Source files +set(RSYNC_SOURCES + flist.c rsync.c generator.c receiver.c cleanup.c sender.c exclude.c + util1.c util2.c main.c checksum.c match.c syscall.c log.c backup.c delete.c + options.c io.c compat.c hlink.c token.c uidlist.c socket.c hashtable.c + usage.c fileio.c batch.c clientname.c chmod.c acls.c xattrs.c + progress.c pipe.c + params.c loadparm.c clientserver.c access.c connection.c authenticate.c + lib/wildmatch.c lib/compat.c lib/snprintf.c lib/mdfour.c lib/md5.c + lib/permstring.c lib/pool_alloc.c lib/sysacls.c lib/sysxattrs.c +) + +# Placeholder config.h to avoid compiler errors on Windows where configure wasn't run +set(HAVE_CONFIG_H 1) +set(RSYNC_VERSION "3.3.0") +set(HAVE_ZLIB 1) +set(HAVE_OPENSSL 1) +set(HAVE_STRERROR 1) +set(PACKAGE "rsync") +set(POPT_SYSCONFDIR "/etc") + +set(SIZEOF_INT 4) +set(SIZEOF_LONG 4) +set(SIZEOF_SHORT 2) +set(SIZEOF_INT32_T 4) +set(SIZEOF_UINT32_T 4) +set(SIZEOF_INT64_T 8) +set(SIZEOF_UINT64_T 8) +set(SIZEOF_OFF_T 4) +set(SIZEOF_TIME_T 8) +set(SIZEOF_CHARP 8) + +set(HAVE_SIZE_T 1) +set(HAVE_OFF_T 1) +set(HAVE_TIME_T 1) +set(HAVE_PID_T 1) +set(HAVE_DIRENT_H 1) +set(HAVE_UTIME 1) +set(HAVE_UTIMES 1) +set(HAVE_STRPBRK 1) +set(HAVE_GETCWD 1) + +set(HAVE_STRUCT_ADDRINFO 1) +set(HAVE_STRUCT_SOCKADDR_STORAGE 1) +set(HAVE_GETADDRINFO 1) +set(HAVE_GETNAMEINFO 1) +set(HAVE_SOCKADDR_IN6 1) +set(HAVE_MEMMOVE 1) +set(HAVE_GETTIMEOFDAY_TZ 1) +set(HAVE_READLINK 1) + +set(SIGUSR1 10) +set(SIGUSR2 12) +set(SIGHUP 1) +set(SIGALRM 14) +set(WNOHANG 1) +set(O_NOFOLLOW 0) +set(O_DIRECTORY 0) +set(AT_REMOVEDIR 0x200) + +set(RSYNC_RSH "ssh") +set(NOBODY_USER "nobody") +set(NOBODY_GROUP "nobody") +set(RSYNCD_SYSCONF "/etc/rsyncd.conf") + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +# Use the pre-generated headers that are committed to the repo +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/proto.h ${CMAKE_CURRENT_BINARY_DIR}/proto.h COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/daemon-parm.h ${CMAKE_CURRENT_BINARY_DIR}/daemon-parm.h COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/help-rsync.h ${CMAKE_CURRENT_BINARY_DIR}/help-rsync.h COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/help-rsyncd.h ${CMAKE_CURRENT_BINARY_DIR}/help-rsyncd.h COPYONLY) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/default-cvsignore.h "#define DEFAULT_CVSIGNORE \".svn .git .hg .bzr .cvs\"\n") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/default-dont-compress.h "#define DEFAULT_DONT_COMPRESS \"*.gz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tgz *.tbz *.rar *.jar *.pdf *.7z *.xz *.lzma\"\n") + +add_executable(rsync ${RSYNC_SOURCES}) + +# Target include directories +get_filename_component(AUTO_WIN_MSVC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../auto-win-msvc" ABSOLUTE) +file(GLOB POSIX_INCLUDES "${AUTO_WIN_MSVC_DIR}/posix-*/include") +file(GLOB LINUX_INCLUDES "${AUTO_WIN_MSVC_DIR}/linux-*/include") +file(GLOB POSIX_SOURCES "${AUTO_WIN_MSVC_DIR}/posix-*/src/*.c") +target_include_directories(rsync PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_library(posix_shims STATIC ${POSIX_SOURCES}) +target_include_directories(posix_shims PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +# Link libraries +target_link_libraries(rsync PRIVATE + posix_shims + ws2_32 + netapi32 + ZLIB::ZLIB + zstd::libzstd + lz4::lz4 + xxHash::xxhash + OpenSSL::Crypto + OpenSSL::SSL +) + +if(POPT_INCLUDE_DIR AND POPT_LIBRARY) + target_link_libraries(rsync PRIVATE ${POPT_LIBRARY}) + target_include_directories(rsync PRIVATE ${POPT_INCLUDE_DIR}) +else() + # Build included popt as fallback + add_library(popt STATIC + popt/popt.c popt/poptconfig.c popt/popthelp.c popt/poptparse.c popt/poptint.c + ) + # Exclude CMAKE_CURRENT_SOURCE_DIR to avoid io.h clash + target_include_directories(popt PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/popt ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + target_link_libraries(rsync PRIVATE popt) + target_include_directories(rsync PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/popt) +endif() + +# Test Helpers +add_executable(tls tls.c syscall.c util2.c t_stub.c lib/compat.c lib/snprintf.c lib/permstring.c lib/sysxattrs.c) +target_link_libraries(tls PRIVATE posix_shims ws2_32 netapi32 popt) +target_include_directories(tls PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(trimslash trimslash.c syscall.c util2.c t_stub.c lib/compat.c lib/snprintf.c) +target_link_libraries(trimslash PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(trimslash PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(t_unsafe t_unsafe.c syscall.c util1.c util2.c t_stub.c lib/compat.c lib/snprintf.c lib/wildmatch.c) +target_link_libraries(t_unsafe PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(t_unsafe PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(t_chmod_secure t_chmod_secure.c syscall.c util1.c util2.c t_stub.c lib/compat.c lib/snprintf.c lib/wildmatch.c lib/permstring.c) +target_link_libraries(t_chmod_secure PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(t_chmod_secure PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(t_secure_relpath t_secure_relpath.c syscall.c util1.c util2.c t_stub.c lib/compat.c lib/snprintf.c lib/wildmatch.c lib/permstring.c) +target_link_libraries(t_secure_relpath PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(t_secure_relpath PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(wildtest wildtest.c t_stub.c lib/compat.c lib/snprintf.c) +target_link_libraries(wildtest PRIVATE posix_shims ws2_32 netapi32 popt) +target_include_directories(wildtest PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(getgroups getgroups.c) +target_link_libraries(getgroups PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(getgroups PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +add_executable(getfsdev getfsdev.c) +target_link_libraries(getfsdev PRIVATE posix_shims ws2_32 netapi32) +target_include_directories(getfsdev PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/lib ${CMAKE_CURRENT_SOURCE_DIR}/popt ${AUTO_WIN_MSVC_DIR} ${POSIX_INCLUDES} ${LINUX_INCLUDES}) + +# Tests +enable_testing() +find_program(PYTHON_EXECUTABLE python) +if(PYTHON_EXECUTABLE) + file(GLOB TEST_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/testsuite/*_test.py") + foreach(TEST_SCRIPT IN LISTS TEST_SCRIPTS) + get_filename_component(TEST_NAME ${TEST_SCRIPT} NAME) + add_test( + NAME ${TEST_NAME} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runtests.py --rsync-bin $ ${TEST_SCRIPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + # Ensure test uses the helpers built here + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "TOOLDIR=$") + endforeach() +endif() diff --git a/config.h.in b/config.h.in new file mode 100644 index 000000000..49c67462e --- /dev/null +++ b/config.h.in @@ -0,0 +1,115 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifdef _MSC_VER +#include +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +typedef int16_t int16; +typedef uint16_t uint16; + +#include "../auto-win-msvc/namespace_win_shim.h" + +// Interpolated config variables from CMake +#cmakedefine HAVE_CONFIG_H 1 +#define RSYNC_VERSION "@RSYNC_VERSION@" +#cmakedefine HAVE_ZLIB 1 +#cmakedefine HAVE_OPENSSL 1 +#cmakedefine HAVE_STRERROR 1 +#define PACKAGE "@PACKAGE@" +#define POPT_SYSCONFDIR "@POPT_SYSCONFDIR@" + +#ifndef __builtin_expect +#define __builtin_expect(exp, c) (exp) +#endif + +// Some extra missing types/macros for rsync +#define PATH_MAX MAX_PATH + +#define SIZEOF_INT @SIZEOF_INT@ +#define SIZEOF_LONG @SIZEOF_LONG@ +#define SIZEOF_SHORT @SIZEOF_SHORT@ +#define SIZEOF_INT32_T @SIZEOF_INT32_T@ +#define SIZEOF_UINT32_T @SIZEOF_UINT32_T@ +#define SIZEOF_INT64_T @SIZEOF_INT64_T@ +#define SIZEOF_UINT64_T @SIZEOF_UINT64_T@ +#define SIZEOF_OFF_T @SIZEOF_OFF_T@ +#define SIZEOF_TIME_T @SIZEOF_TIME_T@ +#define SIZEOF_CHARP @SIZEOF_CHARP@ + +#cmakedefine HAVE_SIZE_T 1 +#cmakedefine HAVE_OFF_T 1 +#cmakedefine HAVE_TIME_T 1 +#cmakedefine HAVE_PID_T 1 +#cmakedefine HAVE_DIRENT_H 1 +#cmakedefine HAVE_UTIME 1 +#cmakedefine HAVE_UTIMES 1 +#cmakedefine HAVE_STRPBRK 1 +#cmakedefine HAVE_GETCWD 1 +#cmakedefine HAVE_STRUCT_ADDRINFO 1 +#cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE 1 +#cmakedefine HAVE_GETADDRINFO 1 +#cmakedefine HAVE_GETNAMEINFO 1 +#cmakedefine HAVE_SOCKADDR_IN6 1 +#cmakedefine HAVE_MEMMOVE 1 +#cmakedefine HAVE_GETTIMEOFDAY_TZ 1 +#cmakedefine HAVE_READLINK 1 + +#define direct dirent + +#ifndef S_ISUID +#define S_ISUID 04000 +#endif +#ifndef S_ISGID +#define S_ISGID 02000 +#endif +#ifndef S_ISVTX +#define S_ISVTX 01000 +#endif + +#cmakedefine SIGUSR1 @SIGUSR1@ +#cmakedefine SIGUSR2 @SIGUSR2@ +#cmakedefine SIGHUP @SIGHUP@ +#cmakedefine SIGALRM @SIGALRM@ +#cmakedefine WNOHANG @WNOHANG@ +#cmakedefine O_NOFOLLOW @O_NOFOLLOW@ +#cmakedefine O_DIRECTORY @O_DIRECTORY@ +#cmakedefine AT_REMOVEDIR @AT_REMOVEDIR@ + +#define RSYNC_RSH "@RSYNC_RSH@" +#define NOBODY_USER "@NOBODY_USER@" +#define NOBODY_GROUP "@NOBODY_GROUP@" +#define RSYNCD_SYSCONF "@RSYNCD_SYSCONF@" + +#define index strchr +#define rindex strrchr + +#ifndef major +#define major(dev) 0 +#define minor(dev) 0 +#define makedev(maj, min) 0 +#endif + +static inline int do_chmod_at(int a, const char* b, int c) { return 0; } +static inline int fchdir(int a) { return 0; } +#define WIFSIGNALED(x) 0 +#define SIGACTMASK(a,b) 0 +static inline int unlinkat(int a, const char* b, int c) { return 0; } +static inline int renameat(int a, const char* b, int c, const char* d) { return 0; } +static inline int mkdirat(int a, const char* b, int c) { return 0; } +static inline int chroot(const char* a) { return 0; } +static inline char* getpass(const char* a) { return ""; } +static inline char* getwd(char* a) { return ""; } +static inline int wait3(int* a, int b, void* c) { return 0; } +static inline struct tm *gmtime_r(const time_t *timep, struct tm *result) { return gmtime_s(result, timep) == 0 ? result : NULL; } + +#include + +#define socketpair posix_socketpair +extern int posix_socketpair(int domain, int type, int protocol, int socket_vector[2]); + +#endif + +#endif // CONFIG_H diff --git a/runtests.py b/runtests.py index 2259aee15..1b04850ce 100755 --- a/runtests.py +++ b/runtests.py @@ -346,6 +346,8 @@ def main(): required_helpers = ['tls', 'trimslash', 't_unsafe', 't_chmod_secure', 't_secure_relpath', 'wildtest', 'getgroups', 'getfsdev'] + if sys.platform == 'win32': + required_helpers = [h + '.exe' for h in required_helpers] missing = [h for h in required_helpers if not os.path.isfile(os.path.join(tooldir, h))] if missing: diff --git a/testsuite/rsyncfns.py b/testsuite/rsyncfns.py index 3a3b37b19..c4ba5b94f 100644 --- a/testsuite/rsyncfns.py +++ b/testsuite/rsyncfns.py @@ -18,7 +18,11 @@ from __future__ import annotations import atexit -import fcntl +try: + import fcntl +except ImportError: + fcntl = None + import msvcrt import filecmp import os import platform @@ -196,9 +200,17 @@ def claim_ports(*ports: int) -> 'None': if _port_lock_fd is None: _port_lock_fd = _open_lock_file() for port in sorted(ports): - # F_SETLKW via fcntl.lockf(LOCK_EX, length, start): exclusive - # byte-range lock on byte `port`, blocking until acquired. - fcntl.lockf(_port_lock_fd, fcntl.LOCK_EX, 1, port) + # exclusive byte-range lock on byte `port`, blocking until acquired. + if fcntl: + fcntl.lockf(_port_lock_fd, fcntl.LOCK_EX, 1, port) + else: + os.lseek(_port_lock_fd, port, os.SEEK_SET) + while True: + try: + msvcrt.locking(_port_lock_fd, msvcrt.LK_LOCK, 1) + break + except OSError: + time.sleep(0.1) # --- standalone rsyncd helpers --------------------------------------------- diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 000000000..d99c35304 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,12 @@ +{ + "name": "rsync", + "version": "3.3.0", + "dependencies": [ + "zlib", + "zstd", + "lz4", + "xxhash", + "openssl" + ], + "builtin-baseline": "aa40adda5352e87655b8583cfb2451d5e9e276fd" +}