Skip to content
Commits on Source (4)
......@@ -81,6 +81,7 @@ struct __dirstream {
#include <unordered_map>
#include <string>
#include <mutex>
#include <cstdlib>
#ifdef __FreeBSD__
#include <sys/socket.h>
......
......@@ -66,6 +66,18 @@ udata(fuse_req_t req) {
return (struct u_data*) fuse_req_userdata(req);
}
static std::string
get_path(const Inode* inode, const char* name) {
if (name[0] == '/') {
return std::string(name);
}
if(inode->path == "/") {
return inode->path + name;
} else {
return inode->path + "/" + name;
}
}
static const struct fuse_opt lo_opts[] = {
{"writeback", offsetof(struct u_data, writeback), 1},
{"no_writeback", offsetof(struct u_data, writeback), 0},
......@@ -135,7 +147,7 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
fuse_reply_err(req, ENOENT);
return;
}
std::string child = parent_inode->path + name;
std::string child = get_path(parent_inode, name);
fuse_log(FUSE_LOG_DEBUG, "lookup %s\n", child.c_str());
......@@ -152,7 +164,6 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
struct stat st;
int rc = gkfs::syscall::gkfs_stat(child, &st);
if(rc < 0) {
fuse_log(FUSE_LOG_DEBUG, "that error 2 \n", parent);
fuse_reply_err(req, ENOENT);
return;
}
......@@ -240,6 +251,18 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_reply_open(req, fi);
}
static void
lseek_handler(fuse_req_t req, fuse_ino_t ino, off_t off, int whence,
struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "lseek handler \n");
int lc = gkfs::syscall::gkfs_lseek(fi->fh, off, whence);
if(lc < 0) {
fuse_reply_err(req, 1);
return;
}
fuse_reply_lseek(req, lc);
}
static void
read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi) {
......@@ -299,7 +322,7 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
fuse_reply_err(req, ENOENT);
return;
}
std::string path = parent_inode->path + name;
std::string path = get_path(parent_inode, name);
int rc = gkfs::syscall::gkfs_create(path, mode);
if(rc == -1) {
fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode,
......@@ -331,6 +354,34 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode,
fuse_reply_create(req, &e, fi);
}
// create a normal file with generated name?
// fake tmp file because it will be listed in the directory it is created in
static void
tmpfile_handler(fuse_req_t req, fuse_ino_t parent, mode_t mode,
struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "tmpfile handler \n");
fuse_reply_err(req, ENOTSUP);
}
/// TODO normally, the file should only be removed if the lookup count is zero,
/// problem?
static void
unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
fuse_log(FUSE_LOG_DEBUG, "unlink handler \n");
auto* parent_inode = get_inode(parent);
if(!parent_inode) {
fuse_reply_err(req, ENOENT);
return;
}
std::string path = get_path(parent_inode, name);
int rc = gkfs::syscall::gkfs_remove(path);
if(rc == -1) {
fuse_reply_err(req, 1);
return;
}
fuse_reply_err(req, 0);
}
static void
opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "opendir handler \n");
......@@ -432,6 +483,24 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
free(buf);
}
static void
releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "releasedir handler \n");
GkfsDir* dir_ptr = reinterpret_cast<GkfsDir*>(fi->fh);
if(CTX->interception_enabled() && CTX->file_map()->exist(dir_ptr->fd)) {
int fd = dir_ptr->fd;
int ret = gkfs::syscall::gkfs_close(fd); // Close GekkoFS internal FD
if(dir_ptr->path) { // Check if path was strdup'd
free(dir_ptr->path);
}
free(dir_ptr); // Free the DIR struct itself
fuse_reply_err(req, ret);
return;
}
fuse_reply_err(req, 0);
}
/// releases file descriptor, not connected to lookup_count
static void
release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
......@@ -484,17 +553,37 @@ flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "flush handler \n");
auto* inode = get_inode(ino);
if(!inode) {
fuse_log(FUSE_LOG_DEBUG, "flush here \n");
fuse_reply_err(req, ENOENT);
return;
}
int lc = gkfs::syscall::gkfs_fsync(fi->fh);
if(lc < 0) {
fuse_log(FUSE_LOG_DEBUG, "flush there \n");
fuse_reply_err(req, 1);
return;
}
fuse_log(FUSE_LOG_DEBUG, "flush success \n");
fuse_reply_err(req, 0);
}
static void
fsync_handler(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info* fi) {
// on datasync > 0, metadata should not be flushed
flush_handler(req, ino, fi);
}
static void
access_handler(fuse_req_t req, fuse_ino_t ino, int mask) {
fuse_log(FUSE_LOG_DEBUG, "access handler \n");
auto* inode = get_inode(ino);
if(!inode) {
fuse_reply_err(req, ENOENT);
return;
}
int lc = gkfs::syscall::gkfs_access(inode->path, mask, true);
if(lc < 0) {
fuse_reply_err(req, 1);
return;
}
fuse_reply_err(req, 0);
}
......@@ -507,7 +596,9 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name,
fuse_reply_err(req, ENOENT);
return;
}
std::string path = parent_inode->path + name;
std::string path = get_path(parent_inode, name);
fuse_log(FUSE_LOG_DEBUG, "mkdir parent %s name %s\n",
parent_inode->path.c_str(), name);
int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR);
if(rc == -1) {
fuse_reply_err(req, 1);
......@@ -529,10 +620,99 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name,
e.attr_timeout = ud->timeout;
e.entry_timeout = ud->timeout;
fuse_reply_entry(req, &e);
fuse_log(FUSE_LOG_DEBUG, "flush success \n");
}
static void
rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
auto* parent_inode = get_inode(parent);
if(!parent_inode) {
fuse_reply_err(req, ENOENT);
return;
}
std::string path = get_path(parent_inode, name);
int rc = gkfs::syscall::gkfs_rmdir(path);
if(rc == -1) {
fuse_reply_err(req, 1);
return;
}
fuse_reply_err(req, 0);
}
static void
readlink_handler(fuse_req_t req, fuse_ino_t ino) {
#ifdef HAS_SYMLINKS
auto* inode = get_inode(ino);
if(!inode) {
fuse_reply_err(req, ENOENT);
return;
}
char link[PATH_MAX];
int rc = gkfs::syscall::gkfs_readlink(inode->path, link, PATH_MAX);
if(rc == -1) {
fuse_reply_err(req, 1);
return;
}
link[rc] = '\0';
fuse_reply_readlink(req, link);
#else
fuse_reply_err(req, ENOTSUP);
#endif
}
static void
symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent,
const char* name) {
#ifdef HAS_SYMLINKS
fuse_log(FUSE_LOG_DEBUG, "symlink handler linkname %s name %s\n", linkname,
name);
auto* ud = udata(req);
auto* parent_inode = get_inode(parent);
if(!parent_inode) {
fuse_log(FUSE_LOG_DEBUG, "symlink parent inode ino %i\n", parent);
fuse_reply_err(req, ENOENT);
return;
}
std::string path = get_path(parent_inode, name);
std::string target = get_path(parent_inode, linkname);
if (target.rfind(ud->source, 0) == 0) {
// starts with mount path
target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str());
}
fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(),
target.c_str());
int rc = gkfs::syscall::gkfs_mk_symlink(path, target);
if(rc < 0) {
fuse_reply_err(req, 1);
return;
}
// Stat the new symlink so we can reply with entry info
struct stat st;
if(gkfs::syscall::gkfs_stat(path, &st) < 0) {
fuse_log(FUSE_LOG_DEBUG, "stat failed\n");
fuse_reply_err(req, errno);
return;
}
fuse_log(FUSE_LOG_DEBUG, "stat mode %i, iflink %i\n", st.st_mode, S_IFLNK);
// TODO this meta is not saved and therefore on restart gone
// this shows the link on ls -l
st.st_mode = S_IFLNK | 0777; // mark as symlink + full perms
fuse_entry_param e = {};
e.ino = alloc_inode(path);
e.attr = st;
st.st_size = strlen(target.c_str());
e.attr_timeout = ud->timeout;
e.entry_timeout = ud->timeout;
fuse_reply_entry(req, &e);
#else
fuse_reply_err(req, ENOTSUP);
#endif
}
static void
init_gekkofs() {
// TODO how to handle mount point
......@@ -557,6 +737,7 @@ init_gekkofs() {
}
ino_map[FUSE_ROOT_ID] = {root_path, {}, 1};
ino_map[FUSE_ROOT_ID].st = st;
path_map[root_path] = FUSE_ROOT_ID;
std::cout << "root node allocated" << std::endl;
}
......@@ -567,18 +748,19 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
ops->setattr = setattr_handler;
ops->open = open_handler;
ops->create = create_handler;
// ops->unlink
ops->unlink = unlink_handler;
ops->forget = forget_handler;
// ops->forget_multi
// ops->readlink
ops->readlink = readlink_handler;
// ops->mknod
// ops->symlink
ops->symlink = symlink_handler;
// ops->rename
// ops->link
ops->flush = flush_handler;
ops->release = release_handler;
// ops->fsync
ops->fsync = fsync_handler;
// ops->write_buf
ops->lseek = lseek_handler;
// xattr
// ops->setxattr
......@@ -589,10 +771,10 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
// directory
ops->lookup = lookup_handler;
ops->mkdir = mkdir_handler;
// ops->rmdir
ops->rmdir = rmdir_handler;
ops->readdir = readdir_handler;
ops->opendir = opendir_handler;
// ops->releasedir
ops->releasedir = releasedir_handler;
// ops->fsyncdir = nullptr;
// ops->readdirplus
......@@ -601,13 +783,14 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
ops->read = read_handler;
// permission
// ops->access
ops->access = access_handler;
// misc
ops->init = init_handler;
ops->destroy = destroy_handler;
ops->statfs = nullptr;
ops->tmpfile = tmpfile_handler;
// ops->statfs = nullptr;
// ops->flock
// ops->getlk
// ops->setlk
......@@ -617,14 +800,13 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
// ops->retrive_reply
// ops->fallocate
// ops->copy_file_range
// ops->lseek
// ops->tmpfile
// ops->statx
}
void
err_cleanup1(fuse_cmdline_opts opts, fuse_args& args) {
err_cleanup1(fuse_cmdline_opts opts, fuse_args& args, struct u_data& ud) {
free(opts.mountpoint);
free(ud.source);
fuse_opt_free_args(&args);
std::cout << "# Resources released" << std::endl;
}
......@@ -667,13 +849,13 @@ main(int argc, char* argv[]) {
fuse_cmdline_help();
fuse_lowlevel_help();
passthrough_ll_help();
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
} else if(opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
fuse_lowlevel_version();
ret = 0;
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
}
......@@ -681,7 +863,7 @@ main(int argc, char* argv[]) {
printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]);
ret = 1;
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
}
......@@ -709,25 +891,26 @@ main(int argc, char* argv[]) {
}
ud.source = strdup(opts.mountpoint);
init_gekkofs();
se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), &ud);
if(se == nullptr) {
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
}
if(fuse_set_signal_handlers(se) != 0) {
err_cleanup2(*se);
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
}
if(fuse_session_mount(se, opts.mountpoint) != 0) {
err_cleanup3(*se);
err_cleanup2(*se);
err_cleanup1(opts, args);
err_cleanup1(opts, args, ud);
return 0;
}
......
......@@ -809,7 +809,7 @@ gkfs_statvfs(struct statvfs* buf) {
* @param fd
* @param offset
* @param whence
* @return 0 on success, -1 on failure
* @return position on success, -1 on failure
*/
off_t
gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) {
......@@ -822,7 +822,7 @@ gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) {
* @param gkfs_fd
* @param offset
* @param whence
* @return 0 on success, -1 on failure
* @return position on success, -1 on failure
*/
off_t
gkfs_lseek(shared_ptr<gkfs::filemap::OpenFile> gkfs_fd, off_t offset,
......@@ -1835,6 +1835,10 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
errno = ENOTSUP;
return -1;
}
} else {
// target is not in gekkofs
errno = ENOENT;
return -1;
}
if(check_parent_dir(path)) {
......@@ -1871,7 +1875,7 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
* @param path
* @param buf
* @param bufsize
* @return 0 on success or -1 on error
* @return path size on success or -1 on error
*/
int
gkfs_readlink(const std::string& path, char* buf, int bufsize) {
......
......@@ -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()
......@@ -40,6 +40,7 @@ from harness.logger import logger
nonexisting = "nonexisting"
# somehow are multiple test causing an error in the fuse client...
def test_read(gkfs_daemon, fuse_client):
file = gkfs_daemon.mountdir / "file"
......@@ -47,6 +48,7 @@ def test_read(gkfs_daemon, fuse_client):
dir = gkfs_daemon.mountdir / "dir"
# creation and removal
sh.bash("-c", "echo baum > " + str(file))
assert sh.ls(fuse_client.mountdir) == "file\n"
assert sh.cat(file) == "baum\n"
......@@ -55,3 +57,32 @@ def test_read(gkfs_daemon, fuse_client):
sh.truncate("-s", "20", str(file2))
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))
assert sh.pwd() == str(dir) + "\n"
sh.mkdir("-p", "foo/bar")
assert sh.ls() == "foo\n"
sh.cd("foo")
sh.rmdir("bar")
sh.cd("..")
sh.rmdir("foo")
sh.rm(str(file2))
assert sh.ls(fuse_client.mountdir) == "dir file\n"
# lseek test (TODO doesn't use the lseek handler)
path = gkfs_daemon.mountdir / "lseek_file"
with open(path, "wb") as f:
f.write(b"HelloWorld") # 10 bytes
fd = os.open(path, os.O_RDONLY)
pos = os.lseek(fd, 5, os.SEEK_SET) # absolute seek
assert pos == 5
data = os.read(fd, 5)
assert data == b"World"
os.close(fd)
os.remove(path)
# symlink
assert sh.pwd() == str(dir) + "\n"
sh.ln("-s", str(file), "link")
assert sh.ls(str(dir)) == "link\n"
assert sh.cat("link") == "baum\n"