Skip to content
Commits on Source (4)
......@@ -97,6 +97,31 @@ struct __dirstream {
#include <client/preload_context.hpp>
#include <client/user_functions.hpp>
// TODO do we really need the stat here? no i dont think so
struct Inode {
std::string path;
struct stat st;
uint64_t lookup_count;
};
enum {
CACHE_NEVER,
CACHE_NORMAL,
CACHE_ALWAYS,
};
struct u_data {
pthread_mutex_t mutex;
int debug;
int writeback;
int flock;
int xattr;
char* source;
double timeout;
int cache;
int timeout_set;
};
struct GkfsDir { // Hypothetical structure that might be used if DIR is cast
int fd;
long int tell_pos; // for telldir/seekdir
......@@ -104,68 +129,4 @@ struct GkfsDir { // Hypothetical structure that might be used if DIR is cast
// other members libc DIR might have
};
/*
* Creates files on the underlying file system in response to a FUSE_MKNOD
* operation
*/
static inline int
mknod_wrapper(int dirfd, const char* path, const char* link, int mode,
dev_t rdev) {
fuse_log(FUSE_LOG_DEBUG, "mknod_wrapper \n");
int res = -1;
if(S_ISREG(mode)) {
// res = lol_openat(dirfd, path, mode, O_CREAT | O_EXCL | O_WRONLY);
fuse_log(FUSE_LOG_DEBUG, "lol_openat internal %s\n", res);
if(res >= 0)
res = gkfs::syscall::gkfs_close(res);
} else if(S_ISDIR(mode)) {
// GKFS_PATH_OPERATION(create, dirfd, path, mode | S_IFDIR)
// res = gkfs::syscall::gkfs_create(resolved, mode | S_IFDIR);
// res = mkdirat(dirfd, path, mode);
} else if(S_ISLNK(mode) && link != NULL) {
fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n");
errno = ENOTSUP;
return -1;
// res = symlinkat(link, dirfd, path);
} else if(S_ISFIFO(mode)) {
fuse_log(FUSE_LOG_ERR, "fifo in mknod_wrapper not supported\n");
errno = ENOTSUP;
return -1;
// res = mkfifoat(dirfd, path, mode);
#ifdef __FreeBSD__
} else if(S_ISSOCK(mode)) {
struct sockaddr_un su;
int fd;
if(strlen(path) >= sizeof(su.sun_path)) {
errno = ENAMETOOLONG;
return -1;
}
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(fd >= 0) {
/*
* We must bind the socket to the underlying file
* system to create the socket file, even though
* we'll never listen on this socket.
*/
su.sun_family = AF_UNIX;
strncpy(su.sun_path, path, sizeof(su.sun_path));
res = bindat(dirfd, fd, (struct sockaddr*) &su, sizeof(su));
if(res == 0)
close(fd);
} else {
res = -1;
}
#endif
} else {
fuse_log(FUSE_LOG_ERR, "mknodat in mknod_wrapper not supported\n");
errno = ENOTSUP;
return -1;
// res = mknodat(dirfd, path, mode, rdev);
}
return res;
}
#endif // GKFS_CLIENT_FUSE_CONTEXT_HPP
This diff is collapsed.
......@@ -143,6 +143,15 @@ if (GKFS_RENAME_SUPPORT)
)
endif ()
if (GKFS_BUILD_FUSE)
gkfs_add_python_test(
NAME test_fuse_client
PYTHON_VERSION 3.6
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration
SOURCE fuse/
)
endif ()
if (GKFS_INSTALL_TESTS)
install(DIRECTORY harness
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration
......
......@@ -34,7 +34,7 @@ from pathlib import Path
from harness.logger import logger, initialize_logging, finalize_logging
from harness.cli import add_cli_options, set_default_log_formatter
from harness.workspace import Workspace, FileCreator
from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator
from harness.gkfs import Daemon, Client, ClientLibc, Proxy, ShellClient, ShellClientLibc, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator, FuseClient
from harness.reporter import report_test_status, report_test_headline, report_assertion_pass
def pytest_configure(config):
......@@ -225,3 +225,15 @@ def gkfwd_client_factory(test_workspace):
"""
return FwdClientCreator(test_workspace)
@pytest.fixture
def fuse_client(test_workspace):
"""
Sets up a gekkofs fuse client environment so that
operations (system calls, library calls, ...) can
be requested from a co-running daemon.
"""
fuse_client = FuseClient(test_workspace)
yield fuse_client.run()
fuse_client.shutdown()
################################################################################
# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain #
# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany #
# #
# This software was partially supported by the #
# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). #
# #
# This software was partially supported by the #
# ADA-FS project under the SPPEXA project funded by the DFG. #
# #
# This file is part of GekkoFS. #
# #
# GekkoFS is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# GekkoFS is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with GekkoFS. If not, see <https://www.gnu.org/licenses/>. #
# #
# SPDX-License-Identifier: GPL-3.0-or-later #
################################################################################
import harness
from pathlib import Path
import errno
import stat
import os
import ctypes
import sh
import sys
import pytest
import time
from harness.logger import logger
nonexisting = "nonexisting"
def test_read(gkfs_daemon, fuse_client):
file = gkfs_daemon.mountdir / "file"
file2 = gkfs_daemon.mountdir / "file2"
dir = gkfs_daemon.mountdir / "dir"
sh.bash("-c", "echo baum > " + str(file))
assert sh.ls(fuse_client.mountdir) == "file\n"
assert sh.cat(file) == "baum\n"
sh.touch(str(file2))
assert sh.wc("-c", str(file2)) == "0 " + str(file2) + "\n"
sh.truncate("-s", "20", str(file2))
assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n"
sh.mkdir(str(dir))
......@@ -74,6 +74,7 @@ gkfwd_client_log_level = 'all'
gkfwd_client_log_syscall_filter = 'epoll_wait,epoll_create'
gkfwd_daemon_active_log_pattern = r'Startup successful. Daemon is ready.'
gkfs_fuse_client = 'fuse_client'
def get_ip_addr(iface):
return netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
......@@ -1637,3 +1638,78 @@ class ShellFwdClient:
@property
def cwd(self):
return self._workspace.twd
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)
self._env = os.environ.copy()
self._metadir = self.rootdir
libdirs = ':'.join(
filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] +
[str(p) for p in self._workspace.libdirs]))
self._patched_env = {
'LD_LIBRARY_PATH' : libdirs,
'GKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file),
'LIBGKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file), # TODO wtf why? see gkfs::env::HOSTS_FILE
}
self._env.update(self._patched_env)
def run(self):
args = [ "-f", "-s", self._workspace.mountdir, "-o", "auto_unmount" ]
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))
)
print(f"fuse client process spawned (PID={self._proc.pid})")
time.sleep(2) # give fuse time to register mount
return self
def shutdown(self):
try:
self._proc.terminate()
err = self._proc.wait()
except sh.SignalException_SIGTERM:
pass
except Exception:
raise
@property
def cwd(self):
return self._workspace.twd
@property
def rootdir(self):
return self._workspace.rootdir
@property
def mountdir(self):
return self._workspace.mountdir
@property
def bindirs(self):
return self._workspace.bindirs
@property
def logdir(self):
return self._workspace.logdir
@property
def env(self):
return self._env