Skip to content
Commits on Source (4)
...@@ -81,6 +81,7 @@ struct __dirstream { ...@@ -81,6 +81,7 @@ struct __dirstream {
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
#include <mutex> #include <mutex>
#include <cstdlib>
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <sys/socket.h> #include <sys/socket.h>
......
...@@ -66,6 +66,18 @@ udata(fuse_req_t req) { ...@@ -66,6 +66,18 @@ udata(fuse_req_t req) {
return (struct u_data*) fuse_req_userdata(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[] = { static const struct fuse_opt lo_opts[] = {
{"writeback", offsetof(struct u_data, writeback), 1}, {"writeback", offsetof(struct u_data, writeback), 1},
{"no_writeback", offsetof(struct u_data, writeback), 0}, {"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) { ...@@ -135,7 +147,7 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
fuse_reply_err(req, ENOENT); fuse_reply_err(req, ENOENT);
return; 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()); 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) { ...@@ -152,7 +164,6 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) {
struct stat st; struct stat st;
int rc = gkfs::syscall::gkfs_stat(child, &st); int rc = gkfs::syscall::gkfs_stat(child, &st);
if(rc < 0) { if(rc < 0) {
fuse_log(FUSE_LOG_DEBUG, "that error 2 \n", parent);
fuse_reply_err(req, ENOENT); fuse_reply_err(req, ENOENT);
return; return;
} }
...@@ -240,6 +251,18 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { ...@@ -240,6 +251,18 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_reply_open(req, 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 static void
read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi) { 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, ...@@ -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); fuse_reply_err(req, ENOENT);
return; return;
} }
std::string path = parent_inode->path + name; std::string path = get_path(parent_inode, name);
int rc = gkfs::syscall::gkfs_create(path, mode); int rc = gkfs::syscall::gkfs_create(path, mode);
if(rc == -1) { if(rc == -1) {
fuse_log(FUSE_LOG_DEBUG, "here here mode %i flags %i \n", mode, 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, ...@@ -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); 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 static void
opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { opendir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
fuse_log(FUSE_LOG_DEBUG, "opendir handler \n"); 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, ...@@ -432,6 +483,24 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
free(buf); 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 /// releases file descriptor, not connected to lookup_count
static void static void
release_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { 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) { ...@@ -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"); fuse_log(FUSE_LOG_DEBUG, "flush handler \n");
auto* inode = get_inode(ino); auto* inode = get_inode(ino);
if(!inode) { if(!inode) {
fuse_log(FUSE_LOG_DEBUG, "flush here \n");
fuse_reply_err(req, ENOENT); fuse_reply_err(req, ENOENT);
return; return;
} }
int lc = gkfs::syscall::gkfs_fsync(fi->fh); int lc = gkfs::syscall::gkfs_fsync(fi->fh);
if(lc < 0) { if(lc < 0) {
fuse_log(FUSE_LOG_DEBUG, "flush there \n");
fuse_reply_err(req, 1); fuse_reply_err(req, 1);
return; 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); fuse_reply_err(req, 0);
} }
...@@ -507,7 +596,9 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, ...@@ -507,7 +596,9 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name,
fuse_reply_err(req, ENOENT); fuse_reply_err(req, ENOENT);
return; 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); int rc = gkfs::syscall::gkfs_create(path, mode | S_IFDIR);
if(rc == -1) { if(rc == -1) {
fuse_reply_err(req, 1); fuse_reply_err(req, 1);
...@@ -529,10 +620,99 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, ...@@ -529,10 +620,99 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name,
e.attr_timeout = ud->timeout; e.attr_timeout = ud->timeout;
e.entry_timeout = ud->timeout; e.entry_timeout = ud->timeout;
fuse_reply_entry(req, &e); 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); 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 static void
init_gekkofs() { init_gekkofs() {
// TODO how to handle mount point // TODO how to handle mount point
...@@ -557,6 +737,7 @@ init_gekkofs() { ...@@ -557,6 +737,7 @@ init_gekkofs() {
} }
ino_map[FUSE_ROOT_ID] = {root_path, {}, 1}; ino_map[FUSE_ROOT_ID] = {root_path, {}, 1};
ino_map[FUSE_ROOT_ID].st = st; ino_map[FUSE_ROOT_ID].st = st;
path_map[root_path] = FUSE_ROOT_ID;
std::cout << "root node allocated" << std::endl; std::cout << "root node allocated" << std::endl;
} }
...@@ -567,18 +748,19 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ...@@ -567,18 +748,19 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
ops->setattr = setattr_handler; ops->setattr = setattr_handler;
ops->open = open_handler; ops->open = open_handler;
ops->create = create_handler; ops->create = create_handler;
// ops->unlink ops->unlink = unlink_handler;
ops->forget = forget_handler; ops->forget = forget_handler;
// ops->forget_multi // ops->forget_multi
// ops->readlink ops->readlink = readlink_handler;
// ops->mknod // ops->mknod
// ops->symlink ops->symlink = symlink_handler;
// ops->rename // ops->rename
// ops->link // ops->link
ops->flush = flush_handler; ops->flush = flush_handler;
ops->release = release_handler; ops->release = release_handler;
// ops->fsync ops->fsync = fsync_handler;
// ops->write_buf // ops->write_buf
ops->lseek = lseek_handler;
// xattr // xattr
// ops->setxattr // ops->setxattr
...@@ -589,10 +771,10 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ...@@ -589,10 +771,10 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
// directory // directory
ops->lookup = lookup_handler; ops->lookup = lookup_handler;
ops->mkdir = mkdir_handler; ops->mkdir = mkdir_handler;
// ops->rmdir ops->rmdir = rmdir_handler;
ops->readdir = readdir_handler; ops->readdir = readdir_handler;
ops->opendir = opendir_handler; ops->opendir = opendir_handler;
// ops->releasedir ops->releasedir = releasedir_handler;
// ops->fsyncdir = nullptr; // ops->fsyncdir = nullptr;
// ops->readdirplus // ops->readdirplus
...@@ -601,13 +783,14 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ...@@ -601,13 +783,14 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
ops->read = read_handler; ops->read = read_handler;
// permission // permission
// ops->access ops->access = access_handler;
// misc // misc
ops->init = init_handler; ops->init = init_handler;
ops->destroy = destroy_handler; ops->destroy = destroy_handler;
ops->statfs = nullptr; ops->tmpfile = tmpfile_handler;
// ops->statfs = nullptr;
// ops->flock // ops->flock
// ops->getlk // ops->getlk
// ops->setlk // ops->setlk
...@@ -617,14 +800,13 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ...@@ -617,14 +800,13 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
// ops->retrive_reply // ops->retrive_reply
// ops->fallocate // ops->fallocate
// ops->copy_file_range // ops->copy_file_range
// ops->lseek
// ops->tmpfile
// ops->statx // ops->statx
} }
void 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(opts.mountpoint);
free(ud.source);
fuse_opt_free_args(&args); fuse_opt_free_args(&args);
std::cout << "# Resources released" << std::endl; std::cout << "# Resources released" << std::endl;
} }
...@@ -667,13 +849,13 @@ main(int argc, char* argv[]) { ...@@ -667,13 +849,13 @@ main(int argc, char* argv[]) {
fuse_cmdline_help(); fuse_cmdline_help();
fuse_lowlevel_help(); fuse_lowlevel_help();
passthrough_ll_help(); passthrough_ll_help();
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} else if(opts.show_version) { } else if(opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion()); printf("FUSE library version %s\n", fuse_pkgversion());
fuse_lowlevel_version(); fuse_lowlevel_version();
ret = 0; ret = 0;
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} }
...@@ -681,7 +863,7 @@ main(int argc, char* argv[]) { ...@@ -681,7 +863,7 @@ main(int argc, char* argv[]) {
printf("usage: %s [options] <mountpoint>\n", argv[0]); printf("usage: %s [options] <mountpoint>\n", argv[0]);
printf(" %s --help\n", argv[0]); printf(" %s --help\n", argv[0]);
ret = 1; ret = 1;
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} }
...@@ -709,25 +891,26 @@ main(int argc, char* argv[]) { ...@@ -709,25 +891,26 @@ main(int argc, char* argv[]) {
} }
ud.source = strdup(opts.mountpoint);
init_gekkofs(); init_gekkofs();
se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), &ud); se = fuse_session_new(&args, &ll_ops, sizeof(ll_ops), &ud);
if(se == nullptr) { if(se == nullptr) {
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} }
if(fuse_set_signal_handlers(se) != 0) { if(fuse_set_signal_handlers(se) != 0) {
err_cleanup2(*se); err_cleanup2(*se);
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} }
if(fuse_session_mount(se, opts.mountpoint) != 0) { if(fuse_session_mount(se, opts.mountpoint) != 0) {
err_cleanup3(*se); err_cleanup3(*se);
err_cleanup2(*se); err_cleanup2(*se);
err_cleanup1(opts, args); err_cleanup1(opts, args, ud);
return 0; return 0;
} }
......
...@@ -809,7 +809,7 @@ gkfs_statvfs(struct statvfs* buf) { ...@@ -809,7 +809,7 @@ gkfs_statvfs(struct statvfs* buf) {
* @param fd * @param fd
* @param offset * @param offset
* @param whence * @param whence
* @return 0 on success, -1 on failure * @return position on success, -1 on failure
*/ */
off_t off_t
gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) { 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) { ...@@ -822,7 +822,7 @@ gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) {
* @param gkfs_fd * @param gkfs_fd
* @param offset * @param offset
* @param whence * @param whence
* @return 0 on success, -1 on failure * @return position on success, -1 on failure
*/ */
off_t off_t
gkfs_lseek(shared_ptr<gkfs::filemap::OpenFile> gkfs_fd, off_t offset, 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) { ...@@ -1835,6 +1835,10 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
errno = ENOTSUP; errno = ENOTSUP;
return -1; return -1;
} }
} else {
// target is not in gekkofs
errno = ENOENT;
return -1;
} }
if(check_parent_dir(path)) { if(check_parent_dir(path)) {
...@@ -1871,7 +1875,7 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) { ...@@ -1871,7 +1875,7 @@ gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
* @param path * @param path
* @param buf * @param buf
* @param bufsize * @param bufsize
* @return 0 on success or -1 on error * @return path size on success or -1 on error
*/ */
int int
gkfs_readlink(const std::string& path, char* buf, int bufsize) { gkfs_readlink(const std::string& path, char* buf, int bufsize) {
......
...@@ -34,7 +34,7 @@ from pathlib import Path ...@@ -34,7 +34,7 @@ from pathlib import Path
from harness.logger import logger, initialize_logging, finalize_logging from harness.logger import logger, initialize_logging, finalize_logging
from harness.cli import add_cli_options, set_default_log_formatter from harness.cli import add_cli_options, set_default_log_formatter
from harness.workspace import Workspace, FileCreator 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 from harness.reporter import report_test_status, report_test_headline, report_assertion_pass
def pytest_configure(config): def pytest_configure(config):
...@@ -225,3 +225,15 @@ def gkfwd_client_factory(test_workspace): ...@@ -225,3 +225,15 @@ def gkfwd_client_factory(test_workspace):
""" """
return FwdClientCreator(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 ...@@ -40,6 +40,7 @@ from harness.logger import logger
nonexisting = "nonexisting" nonexisting = "nonexisting"
# somehow are multiple test causing an error in the fuse client...
def test_read(gkfs_daemon, fuse_client): def test_read(gkfs_daemon, fuse_client):
file = gkfs_daemon.mountdir / "file" file = gkfs_daemon.mountdir / "file"
...@@ -47,6 +48,7 @@ def test_read(gkfs_daemon, fuse_client): ...@@ -47,6 +48,7 @@ def test_read(gkfs_daemon, fuse_client):
dir = gkfs_daemon.mountdir / "dir" dir = gkfs_daemon.mountdir / "dir"
# creation and removal
sh.bash("-c", "echo baum > " + str(file)) sh.bash("-c", "echo baum > " + str(file))
assert sh.ls(fuse_client.mountdir) == "file\n" assert sh.ls(fuse_client.mountdir) == "file\n"
assert sh.cat(file) == "baum\n" assert sh.cat(file) == "baum\n"
...@@ -55,3 +57,32 @@ def test_read(gkfs_daemon, fuse_client): ...@@ -55,3 +57,32 @@ def test_read(gkfs_daemon, fuse_client):
sh.truncate("-s", "20", str(file2)) sh.truncate("-s", "20", str(file2))
assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n" assert sh.wc("-c", str(file2)) == "20 " + str(file2) + "\n"
sh.mkdir(str(dir)) 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"