From 038b4197b8215d9243e2de98de91fa5e804a5817 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 6 Feb 2026 20:39:26 +0100 Subject: [PATCH 01/10] fuse --- .gitlab-ci.yml | 33 +++++++++---------- docker/0.9.6/deps/Dockerfile | 1 + docker/0.9.6/release/Dockerfile | 6 ++-- tests/integration/CMakeLists.txt | 10 ++++++ .../integration/fuse/test_basic_operations.py | 6 ++-- tests/integration/harness/gkfs.py | 18 +++++++--- 6 files changed, 46 insertions(+), 28 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8567a53d..f5b7e9cc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,14 +28,14 @@ variables: # base image -image: gekkofs/core:0.9.6 +image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/core:0.9.6 ################################################################################ ## Validating ################################################################################ check format: stage: lint - image: gekkofs/linter:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh @@ -49,7 +49,7 @@ check format: ################################################################################ gkfs: stage: build - image: gekkofs/deps:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/deps:0.9.6 interruptible: true needs: [] script: @@ -62,9 +62,6 @@ gkfs: - sed -i 's/constexpr bool use_dentry_cache = false;/constexpr bool use_dentry_cache = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto zero_buffer_before_read = false;/constexpr auto zero_buffer_before_read = true;/g' "${CI_PROJECT_DIR}/include/config.hpp" #- sed -i 's/constexpr auto implicit_data_removal = true;/constexpr auto implicit_data_removal = false;/g' "${CI_PROJECT_DIR}/include/config.hpp" - # install libfuse - - apt-get update - - apt-get install -y libfuse3-dev fuse3 # use ccache - ccache --zero-stats -M 750MiB -F 800 --evict-older-than 10d - /usr/sbin/update-ccache-symlinks @@ -91,7 +88,7 @@ gkfs: ## == tests for scripts ==================== scripts: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 needs: [] script: - mkdir -p ${BUILD_PATH}/tests/scripts @@ -106,7 +103,7 @@ scripts: ## == integration tests for gkfs =========== gkfs:allintegration: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] @@ -154,7 +151,7 @@ gkfs:allintegration: ## == integration tests for gkfs =========== gkfs:integration: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] # we need to remove gkfs dependencies on manual parallel: @@ -212,7 +209,7 @@ gkfs:integration: ## == integration tests for gkfwd ========== gkfwd:integration: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] parallel: @@ -270,7 +267,7 @@ gkfwd:integration: ## == unit tests for gkfs ================== gkfs:unit: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -310,7 +307,7 @@ gkfs:unit: ## == unit tests for gkfs ================== gkfs:app: stage: test - image: gekkofs/apps:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/apps:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -351,7 +348,7 @@ gkfs:app: ## == java tests for gkfs ================== gkfs:java: stage: test - image: gekkofs/java:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/java:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -392,7 +389,7 @@ gkfs:java: ## == python tests for gkfs ================== gkfs:python: stage: test - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -435,7 +432,7 @@ gkfs:python: ################################################################################ documentation: stage: docs - image: gekkofs/docs:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/docs:0.9.6 needs: [] rules: # we only build the documentation automatically if we are on the @@ -468,7 +465,7 @@ documentation: ## == coverage baseline ==================== coverage:baseline: stage: report - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] @@ -499,7 +496,7 @@ coverage:baseline: coverage: stage: report - image: gekkofs/testing:0.9.6 + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 #needs: [ 'coverage:baseline', 'gkfs:integration', 'gkfs:unit', 'gkfwd:integration'] needs: [ 'coverage:baseline', 'gkfs:allintegration', 'gkfs:unit', 'gkfs:app', 'gkfs:java', 'gkfs:python' ] script: @@ -557,7 +554,7 @@ cppcheck: ## for DEPLOY_KEY_FILE, DEPLOY_USERNAME, DEPLOY_GROUP, DEPLOY_SERVER and ## DEPLOY_PATH must be defined as protected variables. deploy: - image: bscstorage/deployer + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}bscstorage/deployer stage: deploy needs: [ 'documentation' ] only: diff --git a/docker/0.9.6/deps/Dockerfile b/docker/0.9.6/deps/Dockerfile index f6ceceb4..1d9e62cc 100644 --- a/docker/0.9.6/deps/Dockerfile +++ b/docker/0.9.6/deps/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get update && \ python3-venv \ python3-setuptools \ libnuma-dev libyaml-dev libcurl4-openssl-dev \ + libfuse3-dev fuse3 \ procps && \ rm -rf /var/lib/apt/lists/* && \ apt-get clean && apt-get autoclean diff --git a/docker/0.9.6/release/Dockerfile b/docker/0.9.6/release/Dockerfile index 218325dd..9d97ab04 100644 --- a/docker/0.9.6/release/Dockerfile +++ b/docker/0.9.6/release/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Builder stage -FROM debian:bookworm-slim AS builder +FROM debian:trixie-slim AS builder # Install build dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -25,6 +25,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4-openssl-dev \ libffi-dev \ zlib1g-dev \ + libfuse3-dev \ python3 \ perl \ patch \ @@ -82,7 +83,7 @@ RUN cmake \ make install # Runtime stage -FROM debian:bookworm-slim +FROM debian:trixie-slim # Install runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -94,6 +95,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libcap2 \ libzmq5 \ liburing2 \ + fuse3 \ && rm -rf /var/lib/apt/lists/* # Copy GekkoFS artifacts diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 7526770e..f7c4d228 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -171,6 +171,16 @@ if (GKFS_BUILD_FUSE) WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE fuse/ ) + + if (GKFS_INSTALL_TESTS) + install(DIRECTORY fuse + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration + FILES_MATCHING + REGEX ".*\\.py" + PATTERN "__pycache__" EXCLUDE + PATTERN ".pytest_cache" EXCLUDE + ) + endif () endif () if (GKFS_INSTALL_TESTS) diff --git a/tests/integration/fuse/test_basic_operations.py b/tests/integration/fuse/test_basic_operations.py index 8c0142a8..7a48942f 100644 --- a/tests/integration/fuse/test_basic_operations.py +++ b/tests/integration/fuse/test_basic_operations.py @@ -58,13 +58,13 @@ def test_read(gkfs_daemon, fuse_client): assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n" sh.mkdir(str(dir)) assert sh.ls(fuse_client.mountdir) == "dir file file2\n" - sh.cd(str(dir)) + os.chdir(str(dir)) assert sh.pwd() == str(dir) + "\n" sh.mkdir("-p", "foo/bar") assert sh.ls() == "foo\n" - sh.cd("foo") + os.chdir("foo") sh.rmdir("bar") - sh.cd("..") + os.chdir("..") sh.rmdir("foo") sh.rm(str(file2)) assert sh.ls(fuse_client.mountdir) == "dir file\n" diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index e420d616..83b72597 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -29,6 +29,7 @@ import warnings import os, shutil, sys, re, pytest, signal import random, socket, netifaces, time import subprocess +import sh from pathlib import Path from itertools import islice from time import perf_counter @@ -1850,8 +1851,10 @@ class ShellFwdClient: class FuseClient: def __init__(self, workspace): self._workspace = workspace - #self._cmd = sh.Command("printenv", ["/usr/bin/"])#self._workspace.bindirs) - self._cmd = sh.Command(gkfs_fuse_client, self._workspace.bindirs) + cmd_path = find_command(gkfs_fuse_client, self._workspace.bindirs) + if not cmd_path: + raise Exception(f"Command {gkfs_fuse_client} not found") + self._cmd = sh.Command(str(cmd_path)) self._env = os.environ.copy() self._metadir = self.rootdir @@ -1867,8 +1870,9 @@ class FuseClient: self._env.update(self._patched_env) def run(self): - - args = [ "-f", "-s", self._workspace.mountdir, "-o", "auto_unmount" ] + + # sh module does not accept Path objects in arguments, so we must convert them to strings + args = [ "-f", "-s", str(self._workspace.mountdir), "-o", "auto_unmount" ] print(f"spawning fuse client") print(f"cmdline: {self._cmd} " + " ".join(map(str, args))) @@ -1890,7 +1894,11 @@ class FuseClient: def shutdown(self): try: - self._proc.terminate() + # self._proc.terminate() + # use fusermount to unmount the filesystem + # this will trigger the fuse client to exit + # and coverage data to be flushed + sh.fusermount("-u", "-z", self._workspace.mountdir) time.sleep(1) # give fuse time to unmount err = self._proc.wait() except ProcessLookupError: -- GitLab From f9f5aaa4a8d3ff411c01c8d159eb5020fcc47330 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 6 Feb 2026 21:22:57 +0100 Subject: [PATCH 02/10] corrected images --- .gitlab-ci.yml | 30 +-- src/daemon/handler/srv_metadata.cpp | 6 +- .../fuse/test_standard_compliance.py | 173 ++++++++++++++++++ 3 files changed, 191 insertions(+), 18 deletions(-) create mode 100644 tests/integration/fuse/test_standard_compliance.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5b7e9cc..cb7fc721 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,14 +28,14 @@ variables: # base image -image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/core:0.9.6 +image: gekkofs/core:0.9.6 ################################################################################ ## Validating ################################################################################ check format: stage: lint - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/linter:0.9.6 + image: gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh @@ -49,7 +49,7 @@ check format: ################################################################################ gkfs: stage: build - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/deps:0.9.6 + image: gekkofs/deps:0.9.6 interruptible: true needs: [] script: @@ -88,7 +88,7 @@ gkfs: ## == tests for scripts ==================== scripts: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 needs: [] script: - mkdir -p ${BUILD_PATH}/tests/scripts @@ -103,7 +103,7 @@ scripts: ## == integration tests for gkfs =========== gkfs:allintegration: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] @@ -151,7 +151,7 @@ gkfs:allintegration: ## == integration tests for gkfs =========== gkfs:integration: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] # we need to remove gkfs dependencies on manual parallel: @@ -209,7 +209,7 @@ gkfs:integration: ## == integration tests for gkfwd ========== gkfwd:integration: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] parallel: @@ -267,7 +267,7 @@ gkfwd:integration: ## == unit tests for gkfs ================== gkfs:unit: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -307,7 +307,7 @@ gkfs:unit: ## == unit tests for gkfs ================== gkfs:app: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/apps:0.9.6 + image: gekkofs/apps:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -348,7 +348,7 @@ gkfs:app: ## == java tests for gkfs ================== gkfs:java: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/java:0.9.6 + image: gekkofs/java:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -389,7 +389,7 @@ gkfs:java: ## == python tests for gkfs ================== gkfs:python: stage: test - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 needs: ['gkfs'] script: ## Add path to mkfs.kreon @@ -432,7 +432,7 @@ gkfs:python: ################################################################################ documentation: stage: docs - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/docs:0.9.6 + image: gekkofs/docs:0.9.6 needs: [] rules: # we only build the documentation automatically if we are on the @@ -465,7 +465,7 @@ documentation: ## == coverage baseline ==================== coverage:baseline: stage: report - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 interruptible: true needs: ['gkfs'] @@ -496,7 +496,7 @@ coverage:baseline: coverage: stage: report - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}gekkofs/testing:0.9.6 + image: gekkofs/testing:0.9.6 #needs: [ 'coverage:baseline', 'gkfs:integration', 'gkfs:unit', 'gkfwd:integration'] needs: [ 'coverage:baseline', 'gkfs:allintegration', 'gkfs:unit', 'gkfs:app', 'gkfs:java', 'gkfs:python' ] script: @@ -554,7 +554,7 @@ cppcheck: ## for DEPLOY_KEY_FILE, DEPLOY_USERNAME, DEPLOY_GROUP, DEPLOY_SERVER and ## DEPLOY_PATH must be defined as protected variables. deploy: - image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}bscstorage/deployer + image: bscstorage/deployer stage: deploy needs: [ 'documentation' ] only: diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index 19f3fba9..a1a4efa0 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -1070,9 +1070,9 @@ rpc_srv_read_data_inline(const tl::request& req, if(md.size() > 0 && stored_data.empty()) { // Inline data key missing despite non-zero metadata size - // Treat as empty file or error state - out.count = 0; - out.err = 0; + // It might be that the file is in chunks (e.g., after a + // truncate) + out.err = EAGAIN; } else if(in.offset >= stored_data.size()) { // EOF out.count = 0; diff --git a/tests/integration/fuse/test_standard_compliance.py b/tests/integration/fuse/test_standard_compliance.py new file mode 100644 index 00000000..7e182126 --- /dev/null +++ b/tests/integration/fuse/test_standard_compliance.py @@ -0,0 +1,173 @@ +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sh +import sys +import pytest +import time +import hashlib +import random +import string +from harness.logger import logger + +def calculate_md5(file_path): + hash_md5 = hashlib.md5() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + +def generate_random_data(size): + return ''.join(random.choices(string.ascii_letters + string.digits, k=size)).encode() + +def test_large_io(gkfs_daemon, fuse_client): + """Test reading and writing large files spanning multiple chunks.""" + + # 2MB file (assuming default chunksize of 512KB, this covers multiple chunks) + file_size = 2 * 1024 * 1024 + file_path = gkfs_daemon.mountdir / "large_file" + + # Generate random data + data = generate_random_data(file_size) + + # Write data + with open(file_path, "wb") as f: + f.write(data) + + # Verify size via stat + st = os.stat(file_path) + assert st.st_size == file_size + + # Verify content md5 + assert calculate_md5(file_path) == hashlib.md5(data).hexdigest() + + # Read back and verify + with open(file_path, "rb") as f: + read_data = f.read() + assert read_data == data + +@pytest.mark.xfail(reason="Known issue: fuse_client shrink truncate returns invalid content") +def test_truncate(gkfs_daemon, fuse_client): + """Test file truncation (extend and shrink).""" + + file_path = gkfs_daemon.mountdir / "truncate_file" + + # Start with small content + initial_data = b"start" + with open(file_path, "wb") as f: + f.write(initial_data) + + # Extend + new_size = 100 * 1024 # 100KB + os.truncate(file_path, new_size) + + assert os.stat(file_path).st_size == new_size + + # Verify hole is zero-filled + with open(file_path, "rb") as f: + content = f.read() + assert content[:len(initial_data)] == initial_data + assert content[len(initial_data):] == b'\0' * (new_size - len(initial_data)) + + # Shrink + shrink_size = 3 + os.truncate(file_path, shrink_size) + assert os.stat(file_path).st_size == shrink_size + + # Force flush to ensure consistency + fd = os.open(file_path, os.O_RDONLY) + os.fsync(fd) + os.close(fd) + + with open(file_path, "rb") as f: + content = f.read() + assert content == initial_data[:shrink_size] + +def test_metadata(gkfs_daemon, fuse_client): + """Verify metadata correctness.""" + + file_path = gkfs_daemon.mountdir / "meta_file" + + # Create file + with open(file_path, "w") as f: + f.write("test") + + # Check stat + st = os.stat(file_path) + assert stat.S_ISREG(st.st_mode) + assert st.st_size == 4 + + # Check permissions (basic check) + # Note: FUSE permissions depend on mount options, but should be consistent + original_mode = st.st_mode + new_mode = 0o777 + try: + os.chmod(file_path, new_mode) + # Refresh stat + st = os.stat(file_path) + # Mask with 0o777 to check permission bits only + # GekkoFS might not support chmod fully in all backends or FUSE might mask it + if (st.st_mode & 0o777) != new_mode: + logger.warning(f"chmod mismatch: expected {oct(new_mode)}, got {oct(st.st_mode & 0o777)}") + except OSError: + pass # Optional support + +def test_directories_nested(gkfs_daemon, fuse_client): + """Test nested directory operations.""" + + top_dir = gkfs_daemon.mountdir / "top" + nested_dir = top_dir / "nested" / "deep" + + os.makedirs(nested_dir) + + assert os.path.exists(nested_dir) + assert os.path.isdir(nested_dir) + + # Create file in deep dir + file_path = nested_dir / "deep_file" + with open(file_path, "w") as f: + f.write("deep") + + assert os.path.exists(file_path) + + # List directory + entries = os.listdir(nested_dir) + assert "deep_file" in entries + + # Remove + os.remove(file_path) + os.removedirs(nested_dir) # Should remove all empty parents up to 'top' if empty? + # removedirs removes parents if they become empty. + # 'top' is in mountdir. + + assert not os.path.exists(nested_dir) + +def test_overwrite_middle(gkfs_daemon, fuse_client): + """Test overwriting data in the middle of a file.""" + + file_path = gkfs_daemon.mountdir / "overwrite_file" + + # Write 10KB + size = 10 * 1024 + data = b'A' * size + with open(file_path, "wb") as f: + f.write(data) + + # Overwrite middle 2KB with 'B' + offset = 4 * 1024 + overwrite_len = 2 * 1024 + new_data = b'B' * overwrite_len + + with open(file_path, "r+b") as f: + f.seek(offset) + f.write(new_data) + + # Verify + expected = b'A' * offset + b'B' * overwrite_len + b'A' * (size - offset - overwrite_len) + with open(file_path, "rb") as f: + content = f.read() + + assert content == expected -- GitLab From a3d50b7248e04260ffe098b6ccdd32215356130e Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 6 Feb 2026 22:04:30 +0100 Subject: [PATCH 03/10] removed str --- tests/integration/harness/gkfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 83b72597..8ba22ed1 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -1872,7 +1872,7 @@ class FuseClient: def run(self): # sh module does not accept Path objects in arguments, so we must convert them to strings - args = [ "-f", "-s", str(self._workspace.mountdir), "-o", "auto_unmount" ] + args = [ "-f", "-s", self._workspace.mountdir, "-o", "auto_unmount" ] print(f"spawning fuse client") print(f"cmdline: {self._cmd} " + " ".join(map(str, args))) -- GitLab From d0104b7d945536b909cf12d0ed86bc48954d79aa Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 6 Feb 2026 22:26:51 +0100 Subject: [PATCH 04/10] Update file gkfs.py --- tests/integration/harness/gkfs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 8ba22ed1..d9d827a3 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -1851,10 +1851,11 @@ class ShellFwdClient: class FuseClient: def __init__(self, workspace): self._workspace = workspace - cmd_path = find_command(gkfs_fuse_client, self._workspace.bindirs) - if not cmd_path: - raise Exception(f"Command {gkfs_fuse_client} not found") - self._cmd = sh.Command(str(cmd_path)) + self._cmd = sh.Command(gkfs_fuse_client, self._workspace.bindirs) + #cmd_path = find_command(gkfs_fuse_client, self._workspace.bindirs) + #if not cmd_path: + # raise Exception(f"Command {gkfs_fuse_client} not found") + #self._cmd = sh.Command(str(cmd_path)) self._env = os.environ.copy() self._metadir = self.rootdir -- GitLab From fff4399df376e333839704e641f89b55f38b5420 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 09:31:50 +0100 Subject: [PATCH 05/10] Update file gkfs.py --- .../fuse/test_standard_compliance.py | 1 - tests/integration/harness/gkfs.py | 27 ++++++++----------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/tests/integration/fuse/test_standard_compliance.py b/tests/integration/fuse/test_standard_compliance.py index 7e182126..af186968 100644 --- a/tests/integration/fuse/test_standard_compliance.py +++ b/tests/integration/fuse/test_standard_compliance.py @@ -49,7 +49,6 @@ def test_large_io(gkfs_daemon, fuse_client): read_data = f.read() assert read_data == data -@pytest.mark.xfail(reason="Known issue: fuse_client shrink truncate returns invalid content") def test_truncate(gkfs_daemon, fuse_client): """Test file truncation (extend and shrink).""" diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index d9d827a3..9f3e0342 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -1851,11 +1851,10 @@ class ShellFwdClient: class FuseClient: def __init__(self, workspace): self._workspace = workspace - self._cmd = sh.Command(gkfs_fuse_client, self._workspace.bindirs) - #cmd_path = find_command(gkfs_fuse_client, self._workspace.bindirs) - #if not cmd_path: - # raise Exception(f"Command {gkfs_fuse_client} not found") - #self._cmd = sh.Command(str(cmd_path)) + + self._cmd = find_command(gkfs_fuse_client, self._workspace.bindirs) + if not self._cmd: + raise Exception(f"Command {gkfs_fuse_client} not found") self._env = os.environ.copy() self._metadir = self.rootdir @@ -1878,14 +1877,12 @@ class FuseClient: print(f"spawning fuse client") print(f"cmdline: {self._cmd} " + " ".join(map(str, args))) print(f"patched env:\n{pformat(self._patched_env)}") - - self._proc = self._cmd( - args, - _env=self._env, - _out='/dev/null', - _err='/dev/null', - _bg=True, - _ok_code=list(range(0, 256)) + + self._proc = subprocess.Popen( + [str(self._cmd)] + [str(a) for a in args], + env=self._env, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, ) print(f"fuse client process spawned (PID={self._proc.pid})") @@ -1899,14 +1896,12 @@ class FuseClient: # use fusermount to unmount the filesystem # this will trigger the fuse client to exit # and coverage data to be flushed - sh.fusermount("-u", "-z", self._workspace.mountdir) + subprocess.run(["fusermount", "-u", "-z", str(self._workspace.mountdir)], check=True) time.sleep(1) # give fuse time to unmount err = self._proc.wait() except ProcessLookupError: print("Fuse client already gone at shutdown") pass - except sh.SignalException_SIGTERM: - pass except Exception: pass -- GitLab From 9fb133735f1605a4d0b3b351f30713a656a9262a Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 09:46:55 +0100 Subject: [PATCH 06/10] fix --- .gitlab-ci.yml | 2 +- src/daemon/handler/srv_metadata.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cb7fc721..0958e563 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ image: gekkofs/core:0.9.6 ################################################################################ check format: stage: lint - image: gekkofs/linter:0.9.6 + image: storage.bsc.es/gitlab/hpc/dependency_proxy/containers/gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index a1a4efa0..39cb095e 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -44,6 +44,7 @@ * @endinternal */ +#include #include #include #include @@ -1073,6 +1074,7 @@ rpc_srv_read_data_inline(const tl::request& req, // It might be that the file is in chunks (e.g., after a // truncate) out.err = EAGAIN; + out.count = 0; } else if(in.offset >= stored_data.size()) { // EOF out.count = 0; -- GitLab From 19d86c9b8235f654909abfe830d9b0b89d1f80a2 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 09:57:57 +0100 Subject: [PATCH 07/10] fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0958e563..dc370c34 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ image: gekkofs/core:0.9.6 ################################################################################ check format: stage: lint - image: storage.bsc.es/gitlab/hpc/dependency_proxy/containers/gekkofs/linter:0.9.6 + image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh -- GitLab From 44354ff6449406fb3544225e421ca6ea0fbed875 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 10:01:51 +0100 Subject: [PATCH 08/10] fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc370c34..0958e563 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ image: gekkofs/core:0.9.6 ################################################################################ check format: stage: lint - image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/gekkofs/linter:0.9.6 + image: storage.bsc.es/gitlab/hpc/dependency_proxy/containers/gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh -- GitLab From 693e6442e7d399d3866d584372d840b7399197e1 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 10:20:42 +0100 Subject: [PATCH 09/10] fix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0958e563..dc370c34 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ image: gekkofs/core:0.9.6 ################################################################################ check format: stage: lint - image: storage.bsc.es/gitlab/hpc/dependency_proxy/containers/gekkofs/linter:0.9.6 + image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh -- GitLab From 8989463ba8a897bb51b1a1cc8f42489d8c5267df Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Sat, 7 Feb 2026 10:23:25 +0100 Subject: [PATCH 10/10] unfix --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc370c34..cb7fc721 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ image: gekkofs/core:0.9.6 ################################################################################ check format: stage: lint - image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/gekkofs/linter:0.9.6 + image: gekkofs/linter:0.9.6 needs: [] script: - ${SCRIPTS_DIR}/check_format.sh -- GitLab