diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b628797fd75b1098ba6be0d1286b9b5940110f..a792df8a15f0db0c2cbaeb5cf95bedf825ce9813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Tests to cover proxy and (malleability) ([!222](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/222)) - New fd generation method ([!225](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/202)) - Use LIBGKFS_PROTECT_FD=1 to enable the original method of assignation and protection. + - Use LIBGKFS_RANGE_FD=1 to enable virtual fds from 10000, but without protecting and relocating fds. + - ([!251](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/251)) - Lock system (server level) ([!245](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/245)) - Use PROTECT_FILES_GENERATOR=1 and PROTECT_FILES_CONSUMER=1 to enable. Generator, creates transparent .lockgekko files that blocks the open (for some seconds) of any consumer. Multiple opens / closes for generator are managed. - Basic mmap support ([!247](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/245)) diff --git a/README.md b/README.md index c15fd6f9a10fe059fee0ff541c82e1d75d483fd0..88b0b86d3a600a4a367b622f7014e1693400fe35 100644 --- a/README.md +++ b/README.md @@ -610,6 +610,7 @@ until the file is closed. The cache does not impact the consistency of the file ##### Protecting FDs When the user creates a fd, this is protected from normal fds with a recolocation. This theoretically protects the fd from being closed from outside. However a new fd assignation system has been developed and is activated by default. - `LIBGKFS_PROTECT_FD=1` - Enable the original method of assignation and protection. +- `LIBGKFS_RANGE_FD=1`- Enables FDs from 10000, virtual, but does not protect them. ##### Lightweight File-Locking (server side) Using two environment variables diff --git a/include/client/env.hpp b/include/client/env.hpp index 5140d8f1110ab8a82288415f059c47b34c7f2cc7..e64fa5db380e799e002fb08cf6cdb681595d27ba 100644 --- a/include/client/env.hpp +++ b/include/client/env.hpp @@ -73,6 +73,8 @@ static constexpr auto PROTECT_FILES_GENERATOR = ADD_PREFIX("PROTECT_FILES_GENERATOR"); static constexpr auto PROTECT_FILES_CONSUMER = ADD_PREFIX("PROTECT_FILES_CONSUMER"); +static constexpr auto RANGE_FD = ADD_PREFIX("RANGE_FD"); + static constexpr auto NUM_REPL = ADD_PREFIX("NUM_REPL"); static constexpr auto PROXY_PID_FILE = ADD_PREFIX("PROXY_PID_FILE"); namespace cache { diff --git a/include/client/preload_context.hpp b/include/client/preload_context.hpp index 245e15c28fcf80c21e10922c9cb5be71308a73bc..434ac707fdc7b587109f47afc1b617b14b53947c 100644 --- a/include/client/preload_context.hpp +++ b/include/client/preload_context.hpp @@ -142,6 +142,8 @@ private: bool protect_fds_{false}; bool protect_files_generator_{false}; bool protect_files_consumer_{false}; + bool range_fd_{false}; + std::shared_ptr write_metrics_; std::shared_ptr read_metrics_; @@ -336,6 +338,12 @@ public: void protect_files_consumer(bool protect); + bool + range_fd() const; + + void + range_fd(bool fd); + const std::shared_ptr write_metrics(); diff --git a/src/client/open_file_map.cpp b/src/client/open_file_map.cpp index 1a5193a45a45004063141e136da00ae5001cee0c..c54020660e0dbd3a17d54f8cf1283eb4efe71648 100644 --- a/src/client/open_file_map.cpp +++ b/src/client/open_file_map.cpp @@ -162,7 +162,16 @@ OpenFileMap::safe_generate_fd_idx_() { } } } else { - // Some architectures do not support SYS_open + // Return a virtual fd from 10000, but avoid doing all the FD movements + if(CTX->range_fd()) { + if(fd_validation_needed) { + while(exist(fd)) { + fd = generate_fd_idx(); + } + } + return fd; + } + fd = syscall_no_intercept(SYS_openat, AT_FDCWD, "/dev/null", O_RDWR, S_IRUSR | S_IWUSR); } @@ -185,9 +194,13 @@ OpenFileMap::remove(const int fd) { return false; } files_.erase(fd); + if(!CTX->protect_fds()) { - // We close the dev null fd - close(fd); + if(!CTX->range_fd()) { + // We close the dev null fd + close(fd); + return true; + } } if(fd_validation_needed && files_.empty()) { fd_validation_needed = false; diff --git a/src/client/preload.cpp b/src/client/preload.cpp index 76426a89c54e1eb814bf3d9ec415968c270ac3c4..dc8a4dabfea5e65055e91d35abda7af4b6e2e809 100644 --- a/src/client/preload.cpp +++ b/src/client/preload.cpp @@ -398,6 +398,11 @@ init_preload() { if(gkfs::env::var_is_set(gkfs::env::PROTECT_FD)) { CTX->protect_fds(true); LOG(INFO, "Protecting user fds"); + } else { + // Another alternative is to use start issuing fds from gekko from a + // offset. but without protecting the FDs + CTX->range_fd(gkfs::env::var_is_set(gkfs::env::RANGE_FD)); + LOG(INFO, "Moving FDs to range"); } if(CTX->protect_fds()) { diff --git a/src/client/preload_context.cpp b/src/client/preload_context.cpp index 7a0f49f18cf9e7e570ba76fb453465a9c221aec1..465ed926fb4bb1ccbbad03cdaa4ed7629730aca4 100644 --- a/src/client/preload_context.cpp +++ b/src/client/preload_context.cpp @@ -677,6 +677,16 @@ PreloadContext::protect_files_consumer(bool protect) { protect_files_consumer_ = protect; } +bool +PreloadContext::range_fd() const { + return range_fd_; +} + +void +PreloadContext::range_fd(bool fd) { + range_fd_ = fd; +} + const std::shared_ptr PreloadContext::write_metrics() { diff --git a/tests/apps/CMakeLists.txt b/tests/apps/CMakeLists.txt index ecbcc9f41e7104a75a4be9a5441d644c11bedef2..e79fd67bdd439f472a2f3a83075544f8f1de0aad 100644 --- a/tests/apps/CMakeLists.txt +++ b/tests/apps/CMakeLists.txt @@ -26,67 +26,51 @@ # SPDX-License-Identifier: GPL-3.0-or-later # ################################################################################ -add_test( - NAME s3d_io_test - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/s3d-io.sh -) +# --- Common Test Properties --- +set(DEFAULT_TEST_TIMEOUT 9000) +set(DEFAULT_TEST_LABELS "apps::all") +set(DEFAULT_TEST_WORKING_DIR ${CMAKE_CURRENT_BINARY_DIR}) -# Optionally set test properties (timeout, environment, etc.) -set_tests_properties(s3d_io_test - PROPERTIES - TIMEOUT 9000 - LABELS "apps::all" - # ENVIRONMENT "LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/fortran/gfortran-mod-15:$ENV{LD_LIBRARY_PATH}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) +# --- Helper Function to Add Tests with Common Properties --- +# This function simplifies adding new tests that share common properties. +# Usage: gekko_add_test( [EXTRA_PROPERTIES ...]) +function(gekko_add_test TEST_NAME SCRIPT_FILENAME) + add_test( + NAME ${TEST_NAME} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${SCRIPT_FILENAME} + WORKING_DIRECTORY ${DEFAULT_TEST_WORKING_DIR} + ) + + set_tests_properties(${TEST_NAME} + PROPERTIES + TIMEOUT ${DEFAULT_TEST_TIMEOUT} + LABELS ${DEFAULT_TEST_LABELS} + ) +endfunction() -add_test( - NAME s3d_io_test_large - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/s3d-io-large.sh -) -# Optionally set test properties (timeout, environment, etc.) -set_tests_properties(s3d_io_test_large - PROPERTIES - TIMEOUT 9000 - LABELS "apps::all" - # ENVIRONMENT "LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/fortran/gfortran-mod-15:$ENV{LD_LIBRARY_PATH}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) +# --- Test Definitions --- +gekko_add_test(s3d_io_test s3d-io.sh) +# To add the specific environment for this test (if needed): +# set_tests_properties(s3d_io_test APPEND PROPERTIES ENVIRONMENT "${FORTRAN_TEST_ENV}") -add_test( - NAME wacomm - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/wacomm.sh -) +gekko_add_test(s3d_io_test_environment s3d-io-env.sh) -# Optionally set test properties (timeout, environment, etc.) -set_tests_properties(wacomm - PROPERTIES - TIMEOUT 9000 - LABELS "apps::all" - # ENVIRONMENT "LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/fortran/gfortran-mod-15:$ENV{LD_LIBRARY_PATH}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) +gekko_add_test(s3d_io_test_large s3d-io-large.sh) -add_test( - NAME lockfile - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lockfile.sh -) +gekko_add_test(wacomm wacomm.sh) -set_tests_properties(lockfile - PROPERTIES - TIMEOUT 9000 - LABELS "apps::all" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) +gekko_add_test(lockfile lockfile.sh) +# --- Installation of Test Scripts --- if(GKFS_INSTALL_TESTS) - install(DIRECTORY . + install( + DIRECTORY . # Installs files from the current source directory DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/apps FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE FILES_MATCHING - PATTERN "*.sh" - PATTERN "*" EXCLUDE + PATTERN "*.sh" # Only install .sh files + ) endif() \ No newline at end of file diff --git a/tests/apps/s3d-io-env.sh b/tests/apps/s3d-io-env.sh new file mode 100755 index 0000000000000000000000000000000000000000..776e39162b3b6989c877632a6d9bacd0fbfccd0f --- /dev/null +++ b/tests/apps/s3d-io-env.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# Typical command line : /builds/gitlab/hpc/gekkofs/gkfs/install/bin/gkfs_daemon --mountdir /builds/gitlab/hpc/gekkofs/gkfs/build/tests/run/popen-gw0/test_write_gkfs_daemon_rocksdb0/mnt --rootdir /builds/gitlab/hpc/gekkofs/gkfs/build/tests/run/popen-gw0/test_write_gkfs_daemon_rocksdb0/root -l lo:22543 --metadir /builds/gitlab/hpc/gekkofs/gkfs/build/tests/run/popen-gw0/test_write_gkfs_daemon_rocksdb0/root --dbbackend rocksdb --output-stats /builds/gitlab/hpc/gekkofs/gkfs/build/tests/run/popen-gw0/test_write_gkfs_daemon_rocksdb0/logs/stats.log --enable-collection --enable-chunkstats + +## Environment variables +export IO=/builds/gitlab/hpc/gekkofs/gkfs/install +export GKFS=$IO/lib/libgkfs_intercept.so +export GKFS_LIBC=$IO/lib/libgkfs_libc_intercept.so +export DAEMON=$IO/bin/gkfs_daemon + +export OUTSIDE=$IO/tests/app/s3d-env/original +export MNT=$IO/tests/app/s3d-env/mnt +export ROOT=$IO/tests/app/s3d-env/root +export GKFS_HOSTS_FILE=$ROOT/gkfs_hosts_s3d_env.txt +export LIBGKFS_HOSTS_FILE=$GKFS_HOSTS_FILE +export LD_LIBRARY_PATH=/root/wacommplusplus/build/external/lib:$IO/lib/:$LD_LIBRARY_PATH +export APP=/root/S3D-IO/s3d_io.x +export PARAMS="10 10 10 1 1 1 0 F" + +rm -rf $MNT +rm -rf $ROOT + +mkdir -p $MNT $ROOT + +# Start gkfs_daemon in the background +echo "Starting gkfs_daemon..." +$DAEMON -r $ROOT -m $MNT & +DAEMON_PID=$! + +sleep 5 + +# Check if the daemon is running +if ! ps -p $DAEMON_PID > /dev/null; then + echo "Error: gkfs_daemon failed to start! PID: $DAEMON_PID" + exit 1 +fi + + +# Run s3d-io.x with LD_PRELOAD and capture exit code +echo "Running s3d-io.x..." + +LD_PRELOAD=$GKFS mkdir $MNT/syscall $MNT/libc $OUTSIDE + +echo "Step 1 - PROTECT_FDs " + +LIBGKFS_PROTECT_FD=1 LD_PRELOAD=$GKFS $APP $PARAMS $MNT/syscall + +LIBGKFS_PROTECT_FD=1 LD_PRELOAD=$GKFS_LIBC $APP $PARAMS $MNT/libc + +$APP $PARAMS $OUTSIDE 2>&1 > /dev/null + +compare_sizes() { +diff -q <(find "$1" -type f -exec stat -c%s {} + | sort -k2) <(find "$2" -type f -exec stat -c%s {} + | sort -k2) +} +export LD_PRELOAD=$GKFS +compare_sizes "$OUTSIDE" "$MNT/libc" +TEST_DIFF1_PROTECT_FD=$? + +compare_sizes "$OUTSIDE" "$MNT/syscall" +TEST_DIFF2_PROTECT_FD=$? +unset LD_PRELOAD +# remove costly data +LD_PRELOAD=$GKFS rm -rf $OUTSIDE +LD_PRELOAD=$GKFS rm -rf $MNT/libc +LD_PRELOAD=$GKFS rm -rf $MNT/syscall + +LD_PRELOAD=$GKFS mkdir $MNT/syscall $MNT/libc $OUTSIDE + +echo "Step 2 - RANGE_FDs " + +LIBGKFS_RANGE_FD=1 LD_PRELOAD=$GKFS $APP $PARAMS $MNT/syscall + +LIBGKFS_RANGE_FD=1 LD_PRELOAD=$GKFS_LIBC $APP $PARAMS $MNT/libc + +$APP $PARAMS $OUTSIDE 2>&1 > /dev/null + +compare_sizes() { +diff -q <(find "$1" -type f -exec stat -c%s {} + | sort -k2) <(find "$2" -type f -exec stat -c%s {} + | sort -k2) +} +export LD_PRELOAD=$GKFS +compare_sizes "$OUTSIDE" "$MNT/libc" +TEST_DIFF1_RANGE_FD=$? + +compare_sizes "$OUTSIDE" "$MNT/syscall" +TEST_DIFF2_RANGE_FD=$? +unset LD_PRELOAD +# remove costly data +LD_PRELOAD=$GKFS rm -rf $OUTSIDE +LD_PRELOAD=$GKFS rm -rf $MNT/libc +LD_PRELOAD=$GKFS rm -rf $MNT/syscall + + + +# Stop the daemon +kill $DAEMON_PID + +# Check test success +if [ $TEST_DIFF1_PROTECT_FD -eq 0 ]; then + echo "Data from LIBC Interception (PROTECT_FD) == Original!" +else + echo "Data from LIBC Interception (PROTECT_FD) != Original!" +fi + +if [ $TEST_DIFF2_PROTECT_FD -eq 0 ]; then + echo "Data from SYSCALL Interception (PROTECT_FD) == Original!" +else + echo "Data from SYSCALL Interception (PROTECT_FD) != Original!" +fi + + +# Check test success +if [ $TEST_DIFF1_RANGE_FD -eq 0 ]; then + echo "Data from LIBC Interception (RANGE_FD) == Original!" +else + echo "Data from LIBC Interception (RANGE_FD) != Original!" +fi + +if [ $TEST_DIFF2_RANGE_FD -eq 0 ]; then + echo "Data from SYSCALL Interception (RANGE_FD) == Original!" +else + echo "Data from SYSCALL Interception (RANGE_FD) != Original!" +fi + +# Final exit code logic +if [ $TEST_DIFF1_PROTECT_FD -eq 0 ] && [ $TEST_DIFF2_PROTECT_FD -eq 0 ] && [ $TEST_DIFF1_RANGE_FD -eq 0 ] && [ $TEST_DIFF2_RANGE_FD -eq 0 ]; then + exit 0 +else + exit 1 +fi \ No newline at end of file