From ebc23cb9022be707c522e920e6dbeaa4ea865d75 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 18 Jul 2025 18:03:17 +0200 Subject: [PATCH 1/5] fix syscall tls recursion --- src/client/intercept.cpp | 73 ++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/src/client/intercept.cpp b/src/client/intercept.cpp index 1ae6e9860..32c9c92b0 100644 --- a/src/client/intercept.cpp +++ b/src/client/intercept.cpp @@ -36,7 +36,7 @@ SPDX-License-Identifier: LGPL-3.0-or-later */ - +#include #include #include #include @@ -74,7 +74,13 @@ void (*intercept_hook_point_post_kernel)(long syscall_number, long arg0, #endif namespace { -thread_local bool reentrance_guard_flag; +static pthread_key_t reentrance_guard_key; +static pthread_once_t key_once_control = PTHREAD_ONCE_INIT; +static void make_key() { + // This function will be called exactly once per process. + pthread_key_create(&reentrance_guard_key, NULL); +} + thread_local gkfs::syscall::info saved_syscall_info; constexpr void @@ -533,8 +539,9 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, case SYS_execve: // If we do not set this to false, we are in trouble with vforks - reentrance_guard_flag = false; - *result = syscall_no_intercept_wrapper( +pthread_once(&key_once_control, make_key); + pthread_setspecific(reentrance_guard_key, NULL); + *result = syscall_no_intercept_wrapper( syscall_number, reinterpret_cast(arg0), reinterpret_cast(arg1), reinterpret_cast(arg2)); @@ -543,8 +550,9 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, #ifdef SYS_execveat case SYS_execveat: // If we do not set this to false, we are in trouble with vforks - reentrance_guard_flag = false; - *result = syscall_no_intercept_wrapper( +pthread_once(&key_once_control, make_key); + pthread_setspecific(reentrance_guard_key, NULL); + *result = syscall_no_intercept_wrapper( syscall_number, arg0, reinterpret_cast(arg1), reinterpret_cast(arg2), reinterpret_cast(arg3), arg4); @@ -1011,12 +1019,19 @@ socketcall_wrapper(long syscall_number, long& arg0, long& arg1, long& arg2, } #endif - void hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long result) { + + pthread_once(&key_once_control, make_key); + if (pthread_getspecific(reentrance_guard_key) != NULL) { + return; + } + pthread_setspecific(reentrance_guard_key, (void*) 1); + if(::get_current_syscall_info() == gkfs::syscall::no_info) { + pthread_setspecific(reentrance_guard_key, NULL); return; } @@ -1029,6 +1044,8 @@ hook_forwarded_syscall(long syscall_number, long arg0, long arg1, long arg2, syscall_number, args, result); ::reset_current_syscall_info(); + + pthread_setspecific(reentrance_guard_key, NULL); } void @@ -1042,12 +1059,13 @@ hook_clone_at_child(unsigned long flags, void* child_stack, int* ptid, static_cast(newtls), 0}; #endif - reentrance_guard_flag = true; + pthread_once(&key_once_control, make_key); + pthread_setspecific(reentrance_guard_key, (void*) 1); LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed, SYS_clone, args, 0); - reentrance_guard_flag = false; + pthread_setspecific(reentrance_guard_key, NULL); } void @@ -1061,18 +1079,20 @@ hook_clone_at_parent(unsigned long flags, void* child_stack, int* ptid, static_cast(newtls), 0}; #endif - reentrance_guard_flag = true; + pthread_once(&key_once_control, make_key); + pthread_setspecific(reentrance_guard_key, (void*) 1); LOG(SYSCALL, ::get_current_syscall_info() | gkfs::syscall::executed, SYS_clone, args, returned_pid); - reentrance_guard_flag = false; + pthread_setspecific(reentrance_guard_key, NULL); } } // namespace namespace gkfs::preload { +// This function is inside 'namespace gkfs::preload' int internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, @@ -1085,7 +1105,8 @@ internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1, arg3, arg4, arg5); #endif - if(reentrance_guard_flag) { + pthread_once(&key_once_control, make_key); + if (pthread_getspecific(reentrance_guard_key) != NULL) { ::save_current_syscall_info(gkfs::syscall::from_internal_code | gkfs::syscall::to_kernel | gkfs::syscall::not_executed); @@ -1094,15 +1115,14 @@ internal_hook_guard_wrapper(long syscall_number, long arg0, long arg1, int was_hooked = 0; - reentrance_guard_flag = true; + pthread_setspecific(reentrance_guard_key, (void*) 1); was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5, syscall_return_value); - reentrance_guard_flag = false; + pthread_setspecific(reentrance_guard_key, NULL); return was_hooked; } - /* * hook_guard_wrapper -- a wrapper which can notice reentrance. * @@ -1122,6 +1142,17 @@ hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5, long* syscall_return_value) { + // --- START OF REVISED FIX for hook_guard_wrapper --- + pthread_once(&key_once_control, make_key); // Ensure the key is created + if (pthread_getspecific(reentrance_guard_key) != NULL) { + // If the guard is set, forward the syscall to the kernel and return. + return gkfs::syscall::forward_to_kernel; + } + // Set the guard to a non-NULL value. + pthread_setspecific(reentrance_guard_key, (void*) 1); + // --- END OF REVISED FIX for hook_guard_wrapper --- + + assert(CTX->interception_enabled()); #ifdef SYS_socketcall @@ -1132,19 +1163,11 @@ hook_guard_wrapper(long syscall_number, long arg0, long arg1, long arg2, int was_hooked = 0; - if(reentrance_guard_flag) { - - was_hooked = hook_internal(syscall_number, arg0, arg1, arg2, arg3, arg4, - arg5, syscall_return_value); - return was_hooked; - } - - reentrance_guard_flag = true; - was_hooked = ::hook(syscall_number, arg0, arg1, arg2, arg3, arg4, arg5, syscall_return_value); - reentrance_guard_flag = false; + // Unset the guard before returning + pthread_setspecific(reentrance_guard_key, NULL); return was_hooked; } -- GitLab From 753a19015e88df21e8456d63e724a00166e40fc5 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Fri, 1 Aug 2025 12:43:37 +0200 Subject: [PATCH 2/5] enable_chunk_read_before_write --- src/client/gkfs_functions.cpp | 1594 +++++++++++++++++---------------- 1 file changed, 823 insertions(+), 771 deletions(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 95405e0cc..62e704a8a 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -1104,669 +1104,700 @@ gkfs_do_write(gkfs::filemap::OpenFile& file, const char* buf, size_t count, count > gkfs::config::proxy::fwd_io_count_threshold) { ret_write = gkfs::rpc::forward_write_proxy(*path, buf, offset, count); } else { - ret_write = gkfs::rpc::forward_write(*path, buf, offset, count, 0); - } - err = ret_write.first; - write_size = ret_write.second; + // If we are going to write chunk 0 (0 to chunksize), with an offset > + // 0, and we are using ofi+verbs Do a forward_read from 0 to offset, + // concat read buffer (0..offset) with the rest of the chunk and write + // the first chunk. Then do a forward write of the rest of the data. + if(gkfs::config::io::enable_chunk_read_before_write && + gkfs::config::rpc::chunksize > 0 && + offset < gkfs::config::rpc::chunksize && + gkfs::config::rpc::chunksize < count) { + // Read the first chunk + char* read_buf = new char[offset]; + std::set failed; + auto ret_read = gkfs::rpc::forward_read(*path, read_buf, 0, offset, + 0, failed); + if(ret_read.first != 0) { + LOG(ERROR, "Failed to read first chunk for RMW: {}", + ret_read.first); + delete[] read_buf; + errno = ret_read.first; + return -1; + } - if(num_replicas > 0) { - auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset, - count, num_replicas); + // Create a new buffer for the first chunk write + char* first_chunk_write_buf = + new char[gkfs::config::rpc::chunksize]; + // Copy existing data up to offset + memcpy(first_chunk_write_buf, read_buf, offset); + // Copy new data into the first chunk + memcpy(first_chunk_write_buf + offset, buf, + gkfs::config::rpc::chunksize - offset); + + // Write the first chunk + ret_write = + gkfs::rpc::forward_write(*path, first_chunk_write_buf, 0, + gkfs::config::rpc::chunksize, 0); + delete[] read_buf; + delete[] first_chunk_write_buf; + + if(ret_write.first != 0) { + LOG(ERROR, "Failed to write first chunk during RMW: {}", + ret_write.first); + errno = ret_write.first; + return -1; + } - if(err and ret_write_repl.first == 0) { - // We succesfully write the data to some replica - err = ret_write_repl.first; - // Write size will be wrong - write_size = ret_write_repl.second; - } - } + // Write the rest of the data + if(count > gkfs::config::rpc::chunksize) { + ret_write = gkfs::rpc::forward_write( + *path, buf + (gkfs::config::rpc::chunksize - offset), + gkfs::config::rpc::chunksize, + count - (gkfs::config::rpc::chunksize - offset), 0); + } + } else { - if(err) { - LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", err); - errno = err; - return -1; - } - if(update_pos) { - // Update offset in file descriptor in the file map - file.pos(offset + write_size); - } - if(static_cast(write_size) != count) { - LOG(WARNING, - "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'", - write_size, count); - } - return write_size; // return written size -} -/** - * Wrapper function for all gkfs write operations - * errno may be set - * @param file - * @param buf - * @param count - * @param offset - * @param update_pos pos should only be updated for some write - * operations (see man 2 pwrite) - * @return written size or -1 on error - */ -ssize_t -gkfs_write_ws(gkfs::filemap::OpenFile& file, const char* buf, size_t count, - off64_t offset, bool update_pos) { + ret_write = gkfs::rpc::forward_write(*path, buf, offset, count, 0); + } + err = ret_write.first; + write_size = ret_write.second; + + if(num_replicas > 0) { + auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset, + count, num_replicas); + + if(err and ret_write_repl.first == 0) { + // We succesfully write the data to some replica + err = ret_write_repl.first; + // Write size will be wrong + write_size = ret_write_repl.second; + } + } + + if(err) { + LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", + err); + errno = err; + return -1; + } + if(update_pos) { + // Update offset in file descriptor in the file map + file.pos(offset + write_size); + } + if(static_cast(write_size) != count) { + LOG(WARNING, + "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'", + write_size, count); + } + return write_size; // return written size + } + + /** + * Wrapper function for all gkfs write operations + * errno may be set + * @param file + * @param buf + * @param count + * @param offset + * @param update_pos pos should only be updated for some write + * operations (see man 2 pwrite) + * @return written size or -1 on error + */ + ssize_t gkfs_write_ws(gkfs::filemap::OpenFile & file, const char* buf, + size_t count, off64_t offset, bool update_pos) { #ifdef GKFS_ENABLE_CLIENT_METRICS - auto start_t = std::chrono::high_resolution_clock::now(); - auto written = gkfs_do_write(file, buf, count, offset, update_pos); - CTX->write_metrics()->add_event(written, start_t); - return written; + auto start_t = std::chrono::high_resolution_clock::now(); + auto written = gkfs_do_write(file, buf, count, offset, update_pos); + CTX->write_metrics()->add_event(written, start_t); + return written; #else - return gkfs_do_write(file, buf, count, offset, update_pos); + return gkfs_do_write(file, buf, count, offset, update_pos); #endif -} + } -/** - * gkfs wrapper for pwrite() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @param offset - * @return written size or -1 on error - */ -ssize_t -gkfs_pwrite(int fd, const void* buf, size_t count, off64_t offset) { - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - return gkfs_write_ws(*file, reinterpret_cast(buf), count, - offset); -} + /** + * gkfs wrapper for pwrite() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @param offset + * @return written size or -1 on error + */ + ssize_t gkfs_pwrite(int fd, const void* buf, size_t count, off64_t offset) { + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + return gkfs_write_ws(*file, reinterpret_cast(buf), count, + offset); + } -/** - * gkfs wrapper for write() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @return written size or -1 on error - */ -ssize_t -gkfs_write(int fd, const void* buf, size_t count) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - // call pwrite and update pos - auto ret = gkfs_write_ws(*gkfs_fd, reinterpret_cast(buf), - count, gkfs_fd->pos(), true); - return ret; -} + /** + * gkfs wrapper for write() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @return written size or -1 on error + */ + ssize_t gkfs_write(int fd, const void* buf, size_t count) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + // call pwrite and update pos + auto ret = gkfs_write_ws(*gkfs_fd, reinterpret_cast(buf), + count, gkfs_fd->pos(), true); + return ret; + } + + /** + * gkfs wrapper for pwritev() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @param offset + * @return written size or -1 on error + */ + ssize_t gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, + off_t offset) { -/** - * gkfs wrapper for pwritev() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @param offset - * @return written size or -1 on error - */ -ssize_t -gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) { + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + auto pos = offset; // keep track of current position + ssize_t written = 0; + ssize_t ret; + for(int i = 0; i < iovcnt; ++i) { + auto count = (iov + i)->iov_len; + if(count == 0) { + continue; + } + auto buf = (iov + i)->iov_base; + ret = gkfs_write_ws(*file, reinterpret_cast(buf), count, + pos); + if(ret == -1) { + break; + } + written += ret; + pos += ret; - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - auto pos = offset; // keep track of current position - ssize_t written = 0; - ssize_t ret; - for(int i = 0; i < iovcnt; ++i) { - auto count = (iov + i)->iov_len; - if(count == 0) { - continue; - } - auto buf = (iov + i)->iov_base; - ret = gkfs_write_ws(*file, reinterpret_cast(buf), count, pos); - if(ret == -1) { - break; + if(static_cast(ret) < count) { + break; + } } - written += ret; - pos += ret; - if(static_cast(ret) < count) { - break; + if(written == 0) { + return -1; } + return written; } - if(written == 0) { - return -1; - } - return written; -} - -/** - * gkfs wrapper for writev() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @return written size or -1 on error - */ -ssize_t -gkfs_writev(int fd, const struct iovec* iov, int iovcnt) { + /** + * gkfs wrapper for writev() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @return written size or -1 on error + */ + ssize_t gkfs_writev(int fd, const struct iovec* iov, int iovcnt) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_pwritev(fd, iov, iovcnt, pos); - assert(ret != 0); - if(ret < 0) { - return -1; + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_pwritev(fd, iov, iovcnt, pos); + assert(ret != 0); + if(ret < 0) { + return -1; + } + gkfs_fd->pos(pos + ret); + return ret; } - gkfs_fd->pos(pos + ret); - return ret; -} -/** - * Actual read function for all gkfs read operations - * @param file - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ -ssize_t -gkfs_do_read(const gkfs::filemap::OpenFile& file, char* buf, size_t count, - off64_t offset) { - if(file.type() != gkfs::filemap::FileType::regular) { - assert(file.type() == gkfs::filemap::FileType::directory); - LOG(WARNING, "Cannot read from directory"); - errno = EISDIR; - return -1; - } + /** + * Actual read function for all gkfs read operations + * @param file + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ + ssize_t gkfs_do_read(const gkfs::filemap::OpenFile& file, char* buf, + size_t count, off64_t offset) { + if(file.type() != gkfs::filemap::FileType::regular) { + assert(file.type() == gkfs::filemap::FileType::directory); + LOG(WARNING, "Cannot read from directory"); + errno = EISDIR; + return -1; + } - // Zeroing buffer before read is only relevant for sparse files. - // Otherwise sparse regions contain invalid data. - if constexpr(gkfs::config::io::zero_buffer_before_read) { - memset(buf, 0, sizeof(char) * count); - } + // Zeroing buffer before read is only relevant for sparse files. + // Otherwise sparse regions contain invalid data. + if constexpr(gkfs::config::io::zero_buffer_before_read) { + memset(buf, 0, sizeof(char) * count); + } - pair ret; - if(gkfs::config::proxy::fwd_io && CTX->use_proxy() && - count > gkfs::config::proxy::fwd_io_count_threshold) { - ret = gkfs::rpc::forward_read_proxy(file.path(), buf, offset, count); - } else { - std::set failed; // set with failed targets. - if(CTX->get_replicas() != 0) { + pair ret; + if(gkfs::config::proxy::fwd_io && CTX->use_proxy() && + count > gkfs::config::proxy::fwd_io_count_threshold) { + ret = gkfs::rpc::forward_read_proxy(file.path(), buf, offset, + count); + } else { + std::set failed; // set with failed targets. + if(CTX->get_replicas() != 0) { - ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, - CTX->get_replicas(), failed); - while(ret.first == EIO) { ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, CTX->get_replicas(), failed); - LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", - ret.first); - } + while(ret.first == EIO) { + ret = gkfs::rpc::forward_read(file.path(), buf, offset, + count, CTX->get_replicas(), + failed); + LOG(WARNING, + "gkfs::rpc::forward_read() failed with ret '{}'", + ret.first); + } - } else { - ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, 0, - failed); + } else { + ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, + 0, failed); + } } + auto err = ret.first; + if(err) { + LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err); + errno = err; + return -1; + } + // XXX check that we don't try to read past end of the file + return ret.second; // return read size } - auto err = ret.first; - if(err) { - LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err); - errno = err; - return -1; - } - // XXX check that we don't try to read past end of the file - return ret.second; // return read size -} -/** - * Wrapper function for all gkfs read operations - * @param file - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ -ssize_t -gkfs_read_ws(const gkfs::filemap::OpenFile& file, char* buf, size_t count, - off64_t offset) { + /** + * Wrapper function for all gkfs read operations + * @param file + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ + ssize_t gkfs_read_ws(const gkfs::filemap::OpenFile& file, char* buf, + size_t count, off64_t offset) { #ifdef GKFS_ENABLE_CLIENT_METRICS - auto start_t = std::chrono::high_resolution_clock::now(); - auto read = gkfs_do_read(file, buf, count, offset); - CTX->read_metrics()->add_event(read, start_t); - return read; + auto start_t = std::chrono::high_resolution_clock::now(); + auto read = gkfs_do_read(file, buf, count, offset); + CTX->read_metrics()->add_event(read, start_t); + return read; #else - return gkfs_do_read(file, buf, count, offset); + return gkfs_do_read(file, buf, count, offset); #endif -} + } -/** - * gkfs wrapper for pread() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ -ssize_t -gkfs_pread(int fd, void* buf, size_t count, off64_t offset) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - return gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, offset); -} + /** + * gkfs wrapper for pread() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ + ssize_t gkfs_pread(int fd, void* buf, size_t count, off64_t offset) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + return gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, + offset); + } -/** - * gkfs wrapper for read() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @return read size or -1 on error - */ -ssize_t -gkfs_read(int fd, void* buf, size_t count) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, pos); - // Update offset in file descriptor in the file map - if(ret > 0) { - gkfs_fd->pos(pos + ret); + /** + * gkfs wrapper for read() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @return read size or -1 on error + */ + ssize_t gkfs_read(int fd, void* buf, size_t count) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, + pos); + // Update offset in file descriptor in the file map + if(ret > 0) { + gkfs_fd->pos(pos + ret); + } + return ret; } - return ret; -} -/** - * gkfs wrapper for preadv() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @param offset - * @return read size or -1 on error - */ -ssize_t -gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) { + /** + * gkfs wrapper for preadv() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @param offset + * @return read size or -1 on error + */ + ssize_t gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, + off_t offset) { - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - auto pos = offset; // keep track of current position - ssize_t read = 0; - ssize_t ret; - for(int i = 0; i < iovcnt; ++i) { - auto count = (iov + i)->iov_len; - if(count == 0) { - continue; - } - auto buf = (iov + i)->iov_base; - ret = gkfs_read_ws(*file, reinterpret_cast(buf), count, pos); - if(ret == -1) { - break; - } - read += ret; - pos += ret; + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + auto pos = offset; // keep track of current position + ssize_t read = 0; + ssize_t ret; + for(int i = 0; i < iovcnt; ++i) { + auto count = (iov + i)->iov_len; + if(count == 0) { + continue; + } + auto buf = (iov + i)->iov_base; + ret = gkfs_read_ws(*file, reinterpret_cast(buf), count, pos); + if(ret == -1) { + break; + } + read += ret; + pos += ret; - if(static_cast(ret) < count) { - break; + if(static_cast(ret) < count) { + break; + } } - } - if(read == 0) { - return -1; + if(read == 0) { + return -1; + } + return read; } - return read; -} -/** - * gkfs wrapper for readv() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @return read size or -1 on error - */ -ssize_t -gkfs_readv(int fd, const struct iovec* iov, int iovcnt) { + /** + * gkfs wrapper for readv() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @return read size or -1 on error + */ + ssize_t gkfs_readv(int fd, const struct iovec* iov, int iovcnt) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_preadv(fd, iov, iovcnt, pos); - assert(ret != 0); - if(ret < 0) { - return -1; + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_preadv(fd, iov, iovcnt, pos); + assert(ret != 0); + if(ret < 0) { + return -1; + } + gkfs_fd->pos(pos + ret); + return ret; } - gkfs_fd->pos(pos + ret); - return ret; -} -/** - * wrapper function for opening directories - * errno may be set - * @param path - * @return 0 on success or -1 on error - */ -int -gkfs_opendir(const std::string& path) { - auto md = gkfs::utils::get_metadata(path); - if(!md) { - return -1; - } + /** + * wrapper function for opening directories + * errno may be set + * @param path + * @return 0 on success or -1 on error + */ + int gkfs_opendir(const std::string& path) { + auto md = gkfs::utils::get_metadata(path); + if(!md) { + return -1; + } - if(!S_ISDIR(md->mode())) { - LOG(DEBUG, "{}() Path is not a directory", __func__); - errno = ENOTDIR; - return -1; - } - pair> ret{}; - // Use cache: Get all entries from all servers for the basic - // metadata this is used in get_metadata() later to avoid stat RPCs - if(CTX->use_dentry_cache()) { - ret.second = make_shared(path); - std::vector, - bool, size_t, time_t>>>>>> - dcache_futures; - LOG(DEBUG, - "{}() Sending async dirents for path '{}' to '{}' daemons ...", - __func__, path, CTX->hosts().size()); - // Launch RPC calls asynchronously - for(uint64_t i = 0; i < CTX->hosts().size(); i++) { - dcache_futures.push_back(std::async(std::launch::async, [&, i]() { - if(gkfs::config::proxy::fwd_get_dirents_single && - CTX->use_proxy()) { - return gkfs::rpc::forward_get_dirents_single_proxy(path, i); - } else { - return gkfs::rpc::forward_get_dirents_single(path, i); + if(!S_ISDIR(md->mode())) { + LOG(DEBUG, "{}() Path is not a directory", __func__); + errno = ENOTDIR; + return -1; + } + pair> ret{}; + // Use cache: Get all entries from all servers for the basic + // metadata this is used in get_metadata() later to avoid stat RPCs + if(CTX->use_dentry_cache()) { + ret.second = make_shared(path); + std::vector, + bool, size_t, time_t>>>>>> + dcache_futures; + LOG(DEBUG, + "{}() Sending async dirents for path '{}' to '{}' daemons ...", + __func__, path, CTX->hosts().size()); + // Launch RPC calls asynchronously + for(uint64_t i = 0; i < CTX->hosts().size(); i++) { + dcache_futures.push_back(std::async(std::launch::async, [&, + i]() { + if(gkfs::config::proxy::fwd_get_dirents_single && + CTX->use_proxy()) { + return gkfs::rpc::forward_get_dirents_single_proxy(path, + i); + } else { + return gkfs::rpc::forward_get_dirents_single(path, i); + } + })); + } + int cnt = 0; + // Collect and process results + ret.second->add(".", gkfs::filemap::FileType::directory); + ret.second->add("..", gkfs::filemap::FileType::directory); + for(auto& fut : dcache_futures) { + auto res = fut.get(); // Wait for the RPC result + auto& open_dir = *res.second; + for(auto& dentry : open_dir) { + // type returns as a boolean. true if it is a directory + LOG(DEBUG, "name: {} type: {} size: {} ctime: {}", + get<0>(dentry), get<1>(dentry), get<2>(dentry), + get<3>(dentry)); + auto ftype = get<1>(dentry) + ? gkfs::filemap::FileType::directory + : gkfs::filemap::FileType::regular; + // filename, is_dir, size, ctime + ret.second->add(get<0>(dentry), ftype); + CTX->dentry_cache()->insert( + path, get<0>(dentry), + gkfs::cache::dir::cache_entry{ftype, get<2>(dentry), + get<3>(dentry)}); + cnt++; } - })); - } - int cnt = 0; - // Collect and process results - ret.second->add(".", gkfs::filemap::FileType::directory); - ret.second->add("..", gkfs::filemap::FileType::directory); - for(auto& fut : dcache_futures) { - auto res = fut.get(); // Wait for the RPC result - auto& open_dir = *res.second; - for(auto& dentry : open_dir) { - // type returns as a boolean. true if it is a directory - LOG(DEBUG, "name: {} type: {} size: {} ctime: {}", - get<0>(dentry), get<1>(dentry), get<2>(dentry), - get<3>(dentry)); - auto ftype = get<1>(dentry) ? gkfs::filemap::FileType::directory - : gkfs::filemap::FileType::regular; - // filename, is_dir, size, ctime - ret.second->add(get<0>(dentry), ftype); - CTX->dentry_cache()->insert( - path, get<0>(dentry), - gkfs::cache::dir::cache_entry{ftype, get<2>(dentry), - get<3>(dentry)}); - cnt++; + ret.first = res.first; } - ret.first = res.first; + LOG(DEBUG, + "{}() Unpacked dirents for path '{}' counted '{}' entries", + __func__, path, cnt); + } else { + ret = gkfs::rpc::forward_get_dirents(path); } - LOG(DEBUG, "{}() Unpacked dirents for path '{}' counted '{}' entries", - __func__, path, cnt); - } else { - ret = gkfs::rpc::forward_get_dirents(path); - } - auto err = ret.first; - if(err) { - errno = err; - return -1; + auto err = ret.first; + if(err) { + errno = err; + return -1; + } + assert(ret.second); + return CTX->file_map()->add(ret.second); } - assert(ret.second); - return CTX->file_map()->add(ret.second); -} -/** - * gkfs wrapper for rmdir() system calls - * errno may be set - * @param path - * @return 0 on success or -1 on error - */ -int -gkfs_rmdir(const std::string& path) { - int err; - // check that directory is empty if a strict dir hierarchy is - // enforced - // TODO rename #define + /** + * gkfs wrapper for rmdir() system calls + * errno may be set + * @param path + * @return 0 on success or -1 on error + */ + int gkfs_rmdir(const std::string& path) { + int err; + // check that directory is empty if a strict dir hierarchy is + // enforced + // TODO rename #define #if GKFS_CREATE_CHECK_PARENTS - auto md = gkfs::utils::get_metadata(path); - if(!md) { - LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, strerror(errno)); - return -1; - } - if(!S_ISDIR(md->mode())) { - LOG(DEBUG, "{}() Path is not a directory", __func__); - errno = ENOTDIR; - return -1; - } - auto ret = gkfs::rpc::forward_get_dirents(path); - err = ret.first; - if(err) { - errno = err; - return -1; - } - assert(ret.second); - auto open_dir = ret.second; - if(open_dir->size() != 2) { - errno = ENOTEMPTY; - return -1; - } + auto md = gkfs::utils::get_metadata(path); + if(!md) { + LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, + strerror(errno)); + return -1; + } + if(!S_ISDIR(md->mode())) { + LOG(DEBUG, "{}() Path is not a directory", __func__); + errno = ENOTDIR; + return -1; + } + auto ret = gkfs::rpc::forward_get_dirents(path); + err = ret.first; + if(err) { + errno = err; + return -1; + } + assert(ret.second); + auto open_dir = ret.second; + if(open_dir->size() != 2) { + errno = ENOTEMPTY; + return -1; + } #endif - if(gkfs::config::proxy::fwd_remove && CTX->use_proxy()) { - err = gkfs::rpc::forward_remove_proxy(path, true); - } else { - err = gkfs::rpc::forward_remove(path, true, CTX->get_replicas()); - } - if(err) { - errno = err; - return -1; - } - return 0; -} - -/** - * gkfs wrapper for getdents() system calls - * errno may be set - * @param fd - * @param dirp - * @param count - * @return 0 on success or -1 on error - */ -int -gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) { - // Get opendir object (content was downloaded with opendir() call) - auto open_dir = CTX->file_map()->get_dir(fd); - if(open_dir == nullptr) { - // Cast did not succeeded: open_file is a regular file - errno = EBADF; - return -1; - } - - // get directory position of which entries to return - auto pos = open_dir->pos(); - if(pos >= open_dir->size()) { + if(gkfs::config::proxy::fwd_remove && CTX->use_proxy()) { + err = gkfs::rpc::forward_remove_proxy(path, true); + } else { + err = gkfs::rpc::forward_remove(path, true, CTX->get_replicas()); + } + if(err) { + errno = err; + return -1; + } return 0; } - unsigned int written = 0; - struct linux_dirent* current_dirp = nullptr; - while(pos < open_dir->size()) { - // get dentry fir current position - auto de = open_dir->getdent(pos); - if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - pos++; - continue; - } - } - /* - * Calculate the total dentry size within the kernel struct - * `linux_dirent` depending on the file name size. The size is - * then aligned to the size of `long` boundary. This line was - * originally defined in the linux kernel: fs/readdir.c in - * function filldir(): int reclen = ALIGN(offsetof(struct - * linux_dirent, d_name) + namlen - * + 2, sizeof(long)); However, since d_name is null-terminated - * and de.name().size() does not include space for the - * null-terminator, we add 1. Thus, + 3 in total. - */ - auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) + - de.name().size() + 3, - sizeof(long)); - if(total_size > (count - written)) { - // no enough space left on user buffer to insert next dirent - break; + /** + * gkfs wrapper for getdents() system calls + * errno may be set + * @param fd + * @param dirp + * @param count + * @return 0 on success or -1 on error + */ + int gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, + unsigned int count) { + // Get opendir object (content was downloaded with opendir() call) + auto open_dir = CTX->file_map()->get_dir(fd); + if(open_dir == nullptr) { + // Cast did not succeeded: open_file is a regular file + errno = EBADF; + return -1; } - current_dirp = reinterpret_cast( - reinterpret_cast(dirp) + written); - current_dirp->d_ino = - std::hash()(open_dir->path() + "/" + de.name()); - - current_dirp->d_reclen = total_size; - - current_dirp->d_type = - ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG - : DT_DIR); - - LOG(DEBUG, "name {}: {}", pos, de.name()); - std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); - ++pos; - current_dirp->d_off = pos; - written += total_size; - } - if(written == 0) { - errno = EINVAL; - return -1; - } - // set directory position for next getdents() call - open_dir->pos(pos); - return written; -} + // get directory position of which entries to return + auto pos = open_dir->pos(); + if(pos >= open_dir->size()) { + return 0; + } -/** - * gkfs wrapper for getdents64() system calls - * errno may be set - * @param fd - * @param dirp - * @param count - * @return 0 on success or -1 on error - */ -int -gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, - unsigned int count) { - auto open_dir = CTX->file_map()->get_dir(fd); - if(open_dir == nullptr) { - // Cast did not succeeded: open_file is a regular file - errno = EBADF; - return -1; - } - auto pos = open_dir->pos(); - if(pos >= open_dir->size()) { - return 0; - } - unsigned int written = 0; - struct linux_dirent64* current_dirp = nullptr; - while(pos < open_dir->size()) { - auto de = open_dir->getdent(pos); - if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - pos++; - continue; + unsigned int written = 0; + struct linux_dirent* current_dirp = nullptr; + while(pos < open_dir->size()) { + // get dentry fir current position + auto de = open_dir->getdent(pos); + if(CTX->protect_files_consumer() or + CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + pos++; + continue; + } } + /* + * Calculate the total dentry size within the kernel struct + * `linux_dirent` depending on the file name size. The size is + * then aligned to the size of `long` boundary. This line was + * originally defined in the linux kernel: fs/readdir.c in + * function filldir(): int reclen = ALIGN(offsetof(struct + * linux_dirent, d_name) + namlen + * + 2, sizeof(long)); However, since d_name is null-terminated + * and de.name().size() does not include space for the + * null-terminator, we add 1. Thus, + 3 in total. + */ + auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) + + de.name().size() + 3, + sizeof(long)); + if(total_size > (count - written)) { + // no enough space left on user buffer to insert next dirent + break; + } + current_dirp = reinterpret_cast( + reinterpret_cast(dirp) + written); + current_dirp->d_ino = std::hash()(open_dir->path() + + "/" + de.name()); + + current_dirp->d_reclen = total_size; + + current_dirp->d_type = + ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG + : DT_DIR); + + LOG(DEBUG, "name {}: {}", pos, de.name()); + std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); + ++pos; + current_dirp->d_off = pos; + written += total_size; } - /* - * Calculate the total dentry size within the kernel struct - * `linux_dirent` depending on the file name size. The size is - * then aligned to the size of `long` boundary. - * - * This line was originally defined in the linux kernel: - * fs/readdir.c in function filldir64(): int reclen = - * ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, - * sizeof(u64)); We keep + 1 because: Since d_name is - * null-terminated and de.name().size() does not include space - * for the null-terminator, we add 1. Since d_name in our - * `struct linux_dirent64` definition is not a zero-size array - * (as opposed to the kernel version), we subtract 1. Thus, it - * stays + 1. - */ - auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) + - de.name().size() + 1, - sizeof(uint64_t)); - if(total_size > (count - written)) { - // no enough space left on user buffer to insert next dirent - break; - } - current_dirp = reinterpret_cast( - reinterpret_cast(dirp) + written); - current_dirp->d_ino = - std::hash()(open_dir->path() + "/" + de.name()); - - current_dirp->d_reclen = total_size; - current_dirp->d_type = - ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG - : DT_DIR); - - LOG(DEBUG, "name {}: {}", pos, de.name()); - std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); - ++pos; - current_dirp->d_off = pos; - written += total_size; - } - if(written == 0) { - errno = EINVAL; - return -1; - } - open_dir->pos(pos); - return written; -} + if(written == 0) { + errno = EINVAL; + return -1; + } + // set directory position for next getdents() call + open_dir->pos(pos); + return written; + } + + /** + * gkfs wrapper for getdents64() system calls + * errno may be set + * @param fd + * @param dirp + * @param count + * @return 0 on success or -1 on error + */ + int gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, + unsigned int count) { + auto open_dir = CTX->file_map()->get_dir(fd); + if(open_dir == nullptr) { + // Cast did not succeeded: open_file is a regular file + errno = EBADF; + return -1; + } + auto pos = open_dir->pos(); + if(pos >= open_dir->size()) { + return 0; + } + unsigned int written = 0; + struct linux_dirent64* current_dirp = nullptr; + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos); + if(CTX->protect_files_consumer() or + CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + pos++; + continue; + } + } + /* + * Calculate the total dentry size within the kernel struct + * `linux_dirent` depending on the file name size. The size is + * then aligned to the size of `long` boundary. + * + * This line was originally defined in the linux kernel: + * fs/readdir.c in function filldir64(): int reclen = + * ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, + * sizeof(u64)); We keep + 1 because: Since d_name is + * null-terminated and de.name().size() does not include space + * for the null-terminator, we add 1. Since d_name in our + * `struct linux_dirent64` definition is not a zero-size array + * (as opposed to the kernel version), we subtract 1. Thus, it + * stays + 1. + */ + auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) + + de.name().size() + 1, + sizeof(uint64_t)); + if(total_size > (count - written)) { + // no enough space left on user buffer to insert next dirent + break; + } + current_dirp = reinterpret_cast( + reinterpret_cast(dirp) + written); + current_dirp->d_ino = std::hash()(open_dir->path() + + "/" + de.name()); + + current_dirp->d_reclen = total_size; + current_dirp->d_type = + ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG + : DT_DIR); + + LOG(DEBUG, "name {}: {}", pos, de.name()); + std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); + ++pos; + current_dirp->d_off = pos; + written += total_size; + } -int -gkfs_fsync(unsigned int fd) { - auto file = CTX->file_map()->get(fd); - if(!file) { - errno = 0; - return 0; - } - // flush write size cache to be server consistent - if(CTX->use_write_size_cache()) { - auto err = CTX->write_size_cache()->flush(file->path(), true).first; - if(err) { - LOG(ERROR, "{}() write_size_cache() failed with err '{}'", __func__, - err); - errno = err; + if(written == 0) { + errno = EINVAL; return -1; } + open_dir->pos(pos); + return written; } - errno = 0; - return 0; -} -/** - * @brief Closes an fd. To be used externally - * - * @param fd - * @return int - */ -int -gkfs_close(unsigned int fd) { - auto file = CTX->file_map()->get(fd); - if(file) { + int gkfs_fsync(unsigned int fd) { + auto file = CTX->file_map()->get(fd); + if(!file) { + errno = 0; + return 0; + } // flush write size cache to be server consistent if(CTX->use_write_size_cache()) { auto err = CTX->write_size_cache()->flush(file->path(), true).first; @@ -1777,214 +1808,235 @@ gkfs_close(unsigned int fd) { return -1; } } - if(CTX->use_dentry_cache() && - gkfs::config::cache::clear_dentry_cache_on_close) { - // clear cache for directory - if(CTX->file_map()->get(fd)->type() == - gkfs::filemap::FileType::directory) { - CTX->dentry_cache()->clear_dir( - CTX->file_map()->get(fd)->path()); + errno = 0; + return 0; + } + + /** + * @brief Closes an fd. To be used externally + * + * @param fd + * @return int + */ + int gkfs_close(unsigned int fd) { + auto file = CTX->file_map()->get(fd); + if(file) { + // flush write size cache to be server consistent + if(CTX->use_write_size_cache()) { + auto err = CTX->write_size_cache() + ->flush(file->path(), true) + .first; + if(err) { + LOG(ERROR, "{}() write_size_cache() failed with err '{}'", + __func__, err); + errno = err; + return -1; + } + } + if(CTX->use_dentry_cache() && + gkfs::config::cache::clear_dentry_cache_on_close) { + // clear cache for directory + if(CTX->file_map()->get(fd)->type() == + gkfs::filemap::FileType::directory) { + CTX->dentry_cache()->clear_dir( + CTX->file_map()->get(fd)->path()); + } + } + + if(CTX->protect_files_generator()) { + auto path = CTX->file_map()->get(fd)->path(); + generate_lock_file(path, false); } + // No call to the daemon is required + CTX->file_map()->remove(fd); + return 0; } - if(CTX->protect_files_generator()) { - auto path = CTX->file_map()->get(fd)->path(); - generate_lock_file(path, false); + if(CTX->is_internal_fd(fd)) { + // the client application (for some reason) is trying to close + // an internal fd: ignore it + LOG(ERROR, "{}() closing an internal fd '{}'", __func__, fd); + errno = EACCES; + return -1; } - // No call to the daemon is required - CTX->file_map()->remove(fd); - return 0; - } - if(CTX->is_internal_fd(fd)) { - // the client application (for some reason) is trying to close - // an internal fd: ignore it - LOG(ERROR, "{}() closing an internal fd '{}'", __func__, fd); - errno = EACCES; return -1; } - return -1; -} - #ifdef HAS_SYMLINKS -/** - * gkfs wrapper for make symlink() system calls - * errno may be set - * - * * NOTE: Currently unused - * - * @param path - * @param target_path - * @return 0 on success or -1 on error - */ -int -gkfs_mk_symlink(const std::string& path, const std::string& target_path) { - /* The following check is not POSIX compliant. - * In POSIX the target is not checked at all. - * Here if the target is a directory we raise a NOTSUP error. - * So that application know we don't support link to directory. + /** + * gkfs wrapper for make symlink() system calls + * errno may be set + * + * * NOTE: Currently unused + * + * @param path + * @param target_path + * @return 0 on success or -1 on error */ - auto target_md = gkfs::utils::get_metadata(target_path, false); - std::string new_path = target_path; - if(target_md) { - auto trg_mode = target_md->mode(); - if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) { - assert(S_ISDIR(trg_mode)); - LOG(DEBUG, "Target path is a directory. Not supported"); - errno = ENOTSUP; + int gkfs_mk_symlink(const std::string& path, + const std::string& target_path) { + /* The following check is not POSIX compliant. + * In POSIX the target is not checked at all. + * Here if the target is a directory we raise a NOTSUP error. + * So that application know we don't support link to directory. + */ + auto target_md = gkfs::utils::get_metadata(target_path, false); + std::string new_path = target_path; + if(target_md) { + auto trg_mode = target_md->mode(); + if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) { + assert(S_ISDIR(trg_mode)); + LOG(DEBUG, "Target path is a directory. Not supported"); + errno = ENOTSUP; + return -1; + } + } + + if(check_parent_dir(path)) { return -1; } - } - if(check_parent_dir(path)) { - return -1; + // Path should exists + auto link_md = gkfs::utils::get_metadata(path, false); + if(link_md) { + LOG(DEBUG, "Link does exists: '{}'", path); + errno = EEXIST; + return -1; + } + LOG(DEBUG, "Create file: {}", path); + // create target_path file (we create it regular) + auto create = gkfs_create(path, 0); + if(create) { + return -1; + } + auto err = gkfs::rpc::forward_mk_symlink(path, target_path); + if(err) { + errno = err; + return -1; + } + return 0; } - // Path should exists - auto link_md = gkfs::utils::get_metadata(path, false); - if(link_md) { - LOG(DEBUG, "Link does exists: '{}'", path); - errno = EEXIST; - return -1; - } - LOG(DEBUG, "Create file: {}", path); - // create target_path file (we create it regular) - auto create = gkfs_create(path, 0); - if(create) { - return -1; - } - auto err = gkfs::rpc::forward_mk_symlink(path, target_path); - if(err) { - errno = err; - return -1; - } - return 0; -} + /** + * gkfs wrapper for reading symlinks + * errno may be set + * + * NOTE: Currently unused + * + * @param path + * @param buf + * @param bufsize + * @return 0 on success or -1 on error + */ + int gkfs_readlink(const std::string& path, char* buf, int bufsize) { + auto md = gkfs::utils::get_metadata(path, false); + if(!md) { + LOG(DEBUG, "Named link doesn't exist"); + return -1; + } + if(!(md->is_link())) { + LOG(DEBUG, "The named file is not a symbolic link"); + errno = EINVAL; + return -1; + } + int path_size = md->target_path().size() + CTX->mountdir().size(); + if(path_size >= bufsize) { + LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ", + bufsize, path_size, md->target_path()); + errno = ENAMETOOLONG; + return -1; + } -/** - * gkfs wrapper for reading symlinks - * errno may be set - * - * NOTE: Currently unused - * - * @param path - * @param buf - * @param bufsize - * @return 0 on success or -1 on error - */ -int -gkfs_readlink(const std::string& path, char* buf, int bufsize) { - auto md = gkfs::utils::get_metadata(path, false); - if(!md) { - LOG(DEBUG, "Named link doesn't exist"); - return -1; + CTX->mountdir().copy(buf, CTX->mountdir().size()); + std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str()); + return path_size; } - if(!(md->is_link())) { - LOG(DEBUG, "The named file is not a symbolic link"); - errno = EINVAL; - return -1; - } - int path_size = md->target_path().size() + CTX->mountdir().size(); - if(path_size >= bufsize) { - LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ", - bufsize, path_size, md->target_path()); - errno = ENAMETOOLONG; - return -1; - } - - CTX->mountdir().copy(buf, CTX->mountdir().size()); - std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str()); - return path_size; -} #endif -std::vector -gkfs_get_file_list(const std::string& path) { - auto ret = gkfs::rpc::forward_get_dirents(path); - auto err = ret.first; - if(err) { - errno = err; - return {}; - } + std::vector gkfs_get_file_list(const std::string& path) { + auto ret = gkfs::rpc::forward_get_dirents(path); + auto err = ret.first; + if(err) { + errno = err; + return {}; + } - auto open_dir = ret.second; + auto open_dir = ret.second; - std::vector file_list; - unsigned int pos = 0; + std::vector file_list; + unsigned int pos = 0; - while(pos < open_dir->size()) { - auto de = open_dir->getdent(pos++); - if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - continue; + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos++); + if(CTX->protect_files_consumer() or + CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + continue; + } } + file_list.push_back(de.name()); } - file_list.push_back(de.name()); + return file_list; } - return file_list; -} -void* -gkfs_mmap(void* addr, size_t length, int prot, int flags, int fd, - off_t offset) { - void* ptr = malloc(length); - if(ptr == nullptr) { - return MAP_FAILED; - } - // store info on mmap_set - mmap_set.insert(std::make_tuple(ptr, fd, length, offset)); - gkfs::syscall::gkfs_pread(fd, ptr, length, offset); - return ptr; -} - -int -gkfs_msync(void* addr, size_t length, int flags) { - // check if addr is from gekkofs (mmap_set) - // if so, get the fd and offset - // pwrite length to the original offset - - for(const auto& tuple : mmap_set) { - if(std::get<0>(tuple) == addr) { - int fd = std::get<1>(tuple); - off_t offset = std::get<3>(tuple); - gkfs::syscall::gkfs_pwrite(fd, addr, length, offset); - return 0; + void* gkfs_mmap(void* addr, size_t length, int prot, int flags, int fd, + off_t offset) { + void* ptr = malloc(length); + if(ptr == nullptr) { + return MAP_FAILED; + } + // store info on mmap_set + mmap_set.insert(std::make_tuple(ptr, fd, length, offset)); + gkfs::syscall::gkfs_pread(fd, ptr, length, offset); + return ptr; + } + + int gkfs_msync(void* addr, size_t length, int flags) { + // check if addr is from gekkofs (mmap_set) + // if so, get the fd and offset + // pwrite length to the original offset + + for(const auto& tuple : mmap_set) { + if(std::get<0>(tuple) == addr) { + int fd = std::get<1>(tuple); + off_t offset = std::get<3>(tuple); + gkfs::syscall::gkfs_pwrite(fd, addr, length, offset); + return 0; + } } + return -1; } - return -1; -} -int -gkfs_munmap(void* addr, size_t length) { - // check if addr is from gekkofs (mmap_set) - // if so, get the fd and offset - // pwrite length to the original offset - // return - // if not just go to the normal msync - if(mmap_set.size() != 0) { - // use find_if std::algorithm - // if found, call msync - - auto it = std::find_if( - mmap_set.begin(), mmap_set.end(), - [&addr](const std::tuple& t) { - return std::get<0>(t) == addr; - }); - if(it != mmap_set.end()) { - gkfs_msync(addr, length, 0); - free(addr); - mmap_set.erase(it); - return 0; + int gkfs_munmap(void* addr, size_t length) { + // check if addr is from gekkofs (mmap_set) + // if so, get the fd and offset + // pwrite length to the original offset + // return + // if not just go to the normal msync + if(mmap_set.size() != 0) { + // use find_if std::algorithm + // if found, call msync + + auto it = std::find_if( + mmap_set.begin(), mmap_set.end(), + [&addr](const std::tuple& t) { + return std::get<0>(t) == addr; + }); + if(it != mmap_set.end()) { + gkfs_msync(addr, length, 0); + free(addr); + mmap_set.erase(it); + return 0; + } } + return -1; } - return -1; -} } // namespace gkfs::syscall -- GitLab From 422d630f8c2a2667de7742d0eeb31fa3b5d329ff Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 4 Aug 2025 09:12:59 +0200 Subject: [PATCH 3/5] fix verbs --- src/client/gkfs_functions.cpp | 1541 +++++++++++++++++---------------- 1 file changed, 772 insertions(+), 769 deletions(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 62e704a8a..5018368fc 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -1160,644 +1160,668 @@ gkfs_do_write(gkfs::filemap::OpenFile& file, const char* buf, size_t count, ret_write = gkfs::rpc::forward_write(*path, buf, offset, count, 0); } - err = ret_write.first; - write_size = ret_write.second; - - if(num_replicas > 0) { - auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset, - count, num_replicas); - - if(err and ret_write_repl.first == 0) { - // We succesfully write the data to some replica - err = ret_write_repl.first; - // Write size will be wrong - write_size = ret_write_repl.second; - } - } + } + err = ret_write.first; + write_size = ret_write.second; - if(err) { - LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", - err); - errno = err; - return -1; - } - if(update_pos) { - // Update offset in file descriptor in the file map - file.pos(offset + write_size); - } - if(static_cast(write_size) != count) { - LOG(WARNING, - "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'", - write_size, count); + if(num_replicas > 0) { + auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset, + count, num_replicas); + + if(err and ret_write_repl.first == 0) { + // We succesfully write the data to some replica + err = ret_write_repl.first; + // Write size will be wrong + write_size = ret_write_repl.second; } - return write_size; // return written size - } - - /** - * Wrapper function for all gkfs write operations - * errno may be set - * @param file - * @param buf - * @param count - * @param offset - * @param update_pos pos should only be updated for some write - * operations (see man 2 pwrite) - * @return written size or -1 on error - */ - ssize_t gkfs_write_ws(gkfs::filemap::OpenFile & file, const char* buf, - size_t count, off64_t offset, bool update_pos) { + } + + if(err) { + LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", err); + errno = err; + return -1; + } + if(update_pos) { + // Update offset in file descriptor in the file map + file.pos(offset + write_size); + } + if(static_cast(write_size) != count) { + LOG(WARNING, + "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'", + write_size, count); + } + return write_size; // return written size +} + +/** + * Wrapper function for all gkfs write operations + * errno may be set + * @param file + * @param buf + * @param count + * @param offset + * @param update_pos pos should only be updated for some write + * operations (see man 2 pwrite) + * @return written size or -1 on error + */ +ssize_t +gkfs_write_ws(gkfs::filemap::OpenFile& file, const char* buf, size_t count, + off64_t offset, bool update_pos) { #ifdef GKFS_ENABLE_CLIENT_METRICS - auto start_t = std::chrono::high_resolution_clock::now(); - auto written = gkfs_do_write(file, buf, count, offset, update_pos); - CTX->write_metrics()->add_event(written, start_t); - return written; + auto start_t = std::chrono::high_resolution_clock::now(); + auto written = gkfs_do_write(file, buf, count, offset, update_pos); + CTX->write_metrics()->add_event(written, start_t); + return written; #else - return gkfs_do_write(file, buf, count, offset, update_pos); + return gkfs_do_write(file, buf, count, offset, update_pos); #endif - } +} - /** - * gkfs wrapper for pwrite() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @param offset - * @return written size or -1 on error - */ - ssize_t gkfs_pwrite(int fd, const void* buf, size_t count, off64_t offset) { - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - return gkfs_write_ws(*file, reinterpret_cast(buf), count, - offset); - } +/** + * gkfs wrapper for pwrite() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @param offset + * @return written size or -1 on error + */ +ssize_t +gkfs_pwrite(int fd, const void* buf, size_t count, off64_t offset) { + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + return gkfs_write_ws(*file, reinterpret_cast(buf), count, + offset); +} - /** - * gkfs wrapper for write() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @return written size or -1 on error - */ - ssize_t gkfs_write(int fd, const void* buf, size_t count) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - // call pwrite and update pos - auto ret = gkfs_write_ws(*gkfs_fd, reinterpret_cast(buf), - count, gkfs_fd->pos(), true); - return ret; - } - - /** - * gkfs wrapper for pwritev() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @param offset - * @return written size or -1 on error - */ - ssize_t gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, - off_t offset) { +/** + * gkfs wrapper for write() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @return written size or -1 on error + */ +ssize_t +gkfs_write(int fd, const void* buf, size_t count) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + // call pwrite and update pos + auto ret = gkfs_write_ws(*gkfs_fd, reinterpret_cast(buf), + count, gkfs_fd->pos(), true); + return ret; +} - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - auto pos = offset; // keep track of current position - ssize_t written = 0; - ssize_t ret; - for(int i = 0; i < iovcnt; ++i) { - auto count = (iov + i)->iov_len; - if(count == 0) { - continue; - } - auto buf = (iov + i)->iov_base; - ret = gkfs_write_ws(*file, reinterpret_cast(buf), count, - pos); - if(ret == -1) { - break; - } - written += ret; - pos += ret; +/** + * gkfs wrapper for pwritev() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @param offset + * @return written size or -1 on error + */ +ssize_t +gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) { - if(static_cast(ret) < count) { - break; - } + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + auto pos = offset; // keep track of current position + ssize_t written = 0; + ssize_t ret; + for(int i = 0; i < iovcnt; ++i) { + auto count = (iov + i)->iov_len; + if(count == 0) { + continue; + } + auto buf = (iov + i)->iov_base; + ret = gkfs_write_ws(*file, reinterpret_cast(buf), count, pos); + if(ret == -1) { + break; } + written += ret; + pos += ret; - if(written == 0) { - return -1; + if(static_cast(ret) < count) { + break; } - return written; } - /** - * gkfs wrapper for writev() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @return written size or -1 on error - */ - ssize_t gkfs_writev(int fd, const struct iovec* iov, int iovcnt) { + if(written == 0) { + return -1; + } + return written; +} - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_pwritev(fd, iov, iovcnt, pos); - assert(ret != 0); - if(ret < 0) { - return -1; - } - gkfs_fd->pos(pos + ret); - return ret; +/** + * gkfs wrapper for writev() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @return written size or -1 on error + */ +ssize_t +gkfs_writev(int fd, const struct iovec* iov, int iovcnt) { + + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_pwritev(fd, iov, iovcnt, pos); + assert(ret != 0); + if(ret < 0) { + return -1; } + gkfs_fd->pos(pos + ret); + return ret; +} - /** - * Actual read function for all gkfs read operations - * @param file - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ - ssize_t gkfs_do_read(const gkfs::filemap::OpenFile& file, char* buf, - size_t count, off64_t offset) { - if(file.type() != gkfs::filemap::FileType::regular) { - assert(file.type() == gkfs::filemap::FileType::directory); - LOG(WARNING, "Cannot read from directory"); - errno = EISDIR; - return -1; - } +/** + * Actual read function for all gkfs read operations + * @param file + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ +ssize_t +gkfs_do_read(const gkfs::filemap::OpenFile& file, char* buf, size_t count, + off64_t offset) { + if(file.type() != gkfs::filemap::FileType::regular) { + assert(file.type() == gkfs::filemap::FileType::directory); + LOG(WARNING, "Cannot read from directory"); + errno = EISDIR; + return -1; + } - // Zeroing buffer before read is only relevant for sparse files. - // Otherwise sparse regions contain invalid data. - if constexpr(gkfs::config::io::zero_buffer_before_read) { - memset(buf, 0, sizeof(char) * count); - } + // Zeroing buffer before read is only relevant for sparse files. + // Otherwise sparse regions contain invalid data. + if constexpr(gkfs::config::io::zero_buffer_before_read) { + memset(buf, 0, sizeof(char) * count); + } - pair ret; - if(gkfs::config::proxy::fwd_io && CTX->use_proxy() && - count > gkfs::config::proxy::fwd_io_count_threshold) { - ret = gkfs::rpc::forward_read_proxy(file.path(), buf, offset, - count); - } else { - std::set failed; // set with failed targets. - if(CTX->get_replicas() != 0) { + pair ret; + if(gkfs::config::proxy::fwd_io && CTX->use_proxy() && + count > gkfs::config::proxy::fwd_io_count_threshold) { + ret = gkfs::rpc::forward_read_proxy(file.path(), buf, offset, count); + } else { + std::set failed; // set with failed targets. + if(CTX->get_replicas() != 0) { + ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, + CTX->get_replicas(), failed); + while(ret.first == EIO) { ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, CTX->get_replicas(), failed); - while(ret.first == EIO) { - ret = gkfs::rpc::forward_read(file.path(), buf, offset, - count, CTX->get_replicas(), - failed); - LOG(WARNING, - "gkfs::rpc::forward_read() failed with ret '{}'", - ret.first); - } - - } else { - ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, - 0, failed); + LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", + ret.first); } + + } else { + ret = gkfs::rpc::forward_read(file.path(), buf, offset, count, 0, + failed); } - auto err = ret.first; - if(err) { - LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err); - errno = err; - return -1; - } - // XXX check that we don't try to read past end of the file - return ret.second; // return read size } + auto err = ret.first; + if(err) { + LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err); + errno = err; + return -1; + } + // XXX check that we don't try to read past end of the file + return ret.second; // return read size +} - /** - * Wrapper function for all gkfs read operations - * @param file - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ - ssize_t gkfs_read_ws(const gkfs::filemap::OpenFile& file, char* buf, - size_t count, off64_t offset) { +/** + * Wrapper function for all gkfs read operations + * @param file + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ +ssize_t +gkfs_read_ws(const gkfs::filemap::OpenFile& file, char* buf, size_t count, + off64_t offset) { #ifdef GKFS_ENABLE_CLIENT_METRICS - auto start_t = std::chrono::high_resolution_clock::now(); - auto read = gkfs_do_read(file, buf, count, offset); - CTX->read_metrics()->add_event(read, start_t); - return read; + auto start_t = std::chrono::high_resolution_clock::now(); + auto read = gkfs_do_read(file, buf, count, offset); + CTX->read_metrics()->add_event(read, start_t); + return read; #else - return gkfs_do_read(file, buf, count, offset); + return gkfs_do_read(file, buf, count, offset); #endif - } +} - /** - * gkfs wrapper for pread() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @param offset - * @return read size or -1 on error - */ - ssize_t gkfs_pread(int fd, void* buf, size_t count, off64_t offset) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - return gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, - offset); - } +/** + * gkfs wrapper for pread() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @param offset + * @return read size or -1 on error + */ +ssize_t +gkfs_pread(int fd, void* buf, size_t count, off64_t offset) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + return gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, offset); +} - /** - * gkfs wrapper for read() system calls - * errno may be set - * @param fd - * @param buf - * @param count - * @return read size or -1 on error - */ - ssize_t gkfs_read(int fd, void* buf, size_t count) { - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, - pos); - // Update offset in file descriptor in the file map - if(ret > 0) { - gkfs_fd->pos(pos + ret); - } - return ret; +/** + * gkfs wrapper for read() system calls + * errno may be set + * @param fd + * @param buf + * @param count + * @return read size or -1 on error + */ +ssize_t +gkfs_read(int fd, void* buf, size_t count) { + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_read_ws(*gkfs_fd, reinterpret_cast(buf), count, pos); + // Update offset in file descriptor in the file map + if(ret > 0) { + gkfs_fd->pos(pos + ret); } + return ret; +} - /** - * gkfs wrapper for preadv() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @param offset - * @return read size or -1 on error - */ - ssize_t gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, - off_t offset) { - - auto file = CTX->file_map()->get(fd); - if(!file) - return 0; - auto pos = offset; // keep track of current position - ssize_t read = 0; - ssize_t ret; - for(int i = 0; i < iovcnt; ++i) { - auto count = (iov + i)->iov_len; - if(count == 0) { - continue; - } - auto buf = (iov + i)->iov_base; - ret = gkfs_read_ws(*file, reinterpret_cast(buf), count, pos); - if(ret == -1) { - break; - } - read += ret; - pos += ret; +/** + * gkfs wrapper for preadv() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @param offset + * @return read size or -1 on error + */ +ssize_t +gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) { - if(static_cast(ret) < count) { - break; - } + auto file = CTX->file_map()->get(fd); + if(!file) + return 0; + auto pos = offset; // keep track of current position + ssize_t read = 0; + ssize_t ret; + for(int i = 0; i < iovcnt; ++i) { + auto count = (iov + i)->iov_len; + if(count == 0) { + continue; + } + auto buf = (iov + i)->iov_base; + ret = gkfs_read_ws(*file, reinterpret_cast(buf), count, pos); + if(ret == -1) { + break; } + read += ret; + pos += ret; - if(read == 0) { - return -1; + if(static_cast(ret) < count) { + break; } - return read; } - /** - * gkfs wrapper for readv() system calls - * errno may be set - * @param fd - * @param iov - * @param iovcnt - * @return read size or -1 on error - */ - ssize_t gkfs_readv(int fd, const struct iovec* iov, int iovcnt) { + if(read == 0) { + return -1; + } + return read; +} - auto gkfs_fd = CTX->file_map()->get(fd); - if(!gkfs_fd) - return 0; - auto pos = gkfs_fd->pos(); // retrieve the current offset - auto ret = gkfs_preadv(fd, iov, iovcnt, pos); - assert(ret != 0); - if(ret < 0) { - return -1; - } - gkfs_fd->pos(pos + ret); - return ret; +/** + * gkfs wrapper for readv() system calls + * errno may be set + * @param fd + * @param iov + * @param iovcnt + * @return read size or -1 on error + */ +ssize_t +gkfs_readv(int fd, const struct iovec* iov, int iovcnt) { + + auto gkfs_fd = CTX->file_map()->get(fd); + if(!gkfs_fd) + return 0; + auto pos = gkfs_fd->pos(); // retrieve the current offset + auto ret = gkfs_preadv(fd, iov, iovcnt, pos); + assert(ret != 0); + if(ret < 0) { + return -1; } + gkfs_fd->pos(pos + ret); + return ret; +} - /** - * wrapper function for opening directories - * errno may be set - * @param path - * @return 0 on success or -1 on error - */ - int gkfs_opendir(const std::string& path) { - auto md = gkfs::utils::get_metadata(path); - if(!md) { - return -1; - } +/** + * wrapper function for opening directories + * errno may be set + * @param path + * @return 0 on success or -1 on error + */ +int +gkfs_opendir(const std::string& path) { + auto md = gkfs::utils::get_metadata(path); + if(!md) { + return -1; + } - if(!S_ISDIR(md->mode())) { - LOG(DEBUG, "{}() Path is not a directory", __func__); - errno = ENOTDIR; - return -1; - } - pair> ret{}; - // Use cache: Get all entries from all servers for the basic - // metadata this is used in get_metadata() later to avoid stat RPCs - if(CTX->use_dentry_cache()) { - ret.second = make_shared(path); - std::vector, - bool, size_t, time_t>>>>>> - dcache_futures; - LOG(DEBUG, - "{}() Sending async dirents for path '{}' to '{}' daemons ...", - __func__, path, CTX->hosts().size()); - // Launch RPC calls asynchronously - for(uint64_t i = 0; i < CTX->hosts().size(); i++) { - dcache_futures.push_back(std::async(std::launch::async, [&, - i]() { - if(gkfs::config::proxy::fwd_get_dirents_single && - CTX->use_proxy()) { - return gkfs::rpc::forward_get_dirents_single_proxy(path, - i); - } else { - return gkfs::rpc::forward_get_dirents_single(path, i); - } - })); - } - int cnt = 0; - // Collect and process results - ret.second->add(".", gkfs::filemap::FileType::directory); - ret.second->add("..", gkfs::filemap::FileType::directory); - for(auto& fut : dcache_futures) { - auto res = fut.get(); // Wait for the RPC result - auto& open_dir = *res.second; - for(auto& dentry : open_dir) { - // type returns as a boolean. true if it is a directory - LOG(DEBUG, "name: {} type: {} size: {} ctime: {}", - get<0>(dentry), get<1>(dentry), get<2>(dentry), - get<3>(dentry)); - auto ftype = get<1>(dentry) - ? gkfs::filemap::FileType::directory - : gkfs::filemap::FileType::regular; - // filename, is_dir, size, ctime - ret.second->add(get<0>(dentry), ftype); - CTX->dentry_cache()->insert( - path, get<0>(dentry), - gkfs::cache::dir::cache_entry{ftype, get<2>(dentry), - get<3>(dentry)}); - cnt++; + if(!S_ISDIR(md->mode())) { + LOG(DEBUG, "{}() Path is not a directory", __func__); + errno = ENOTDIR; + return -1; + } + pair> ret{}; + // Use cache: Get all entries from all servers for the basic + // metadata this is used in get_metadata() later to avoid stat RPCs + if(CTX->use_dentry_cache()) { + ret.second = make_shared(path); + std::vector, + bool, size_t, time_t>>>>>> + dcache_futures; + LOG(DEBUG, + "{}() Sending async dirents for path '{}' to '{}' daemons ...", + __func__, path, CTX->hosts().size()); + // Launch RPC calls asynchronously + for(uint64_t i = 0; i < CTX->hosts().size(); i++) { + dcache_futures.push_back(std::async(std::launch::async, [&, i]() { + if(gkfs::config::proxy::fwd_get_dirents_single && + CTX->use_proxy()) { + return gkfs::rpc::forward_get_dirents_single_proxy(path, i); + } else { + return gkfs::rpc::forward_get_dirents_single(path, i); } - ret.first = res.first; + })); + } + int cnt = 0; + // Collect and process results + ret.second->add(".", gkfs::filemap::FileType::directory); + ret.second->add("..", gkfs::filemap::FileType::directory); + for(auto& fut : dcache_futures) { + auto res = fut.get(); // Wait for the RPC result + auto& open_dir = *res.second; + for(auto& dentry : open_dir) { + // type returns as a boolean. true if it is a directory + LOG(DEBUG, "name: {} type: {} size: {} ctime: {}", + get<0>(dentry), get<1>(dentry), get<2>(dentry), + get<3>(dentry)); + auto ftype = get<1>(dentry) ? gkfs::filemap::FileType::directory + : gkfs::filemap::FileType::regular; + // filename, is_dir, size, ctime + ret.second->add(get<0>(dentry), ftype); + CTX->dentry_cache()->insert( + path, get<0>(dentry), + gkfs::cache::dir::cache_entry{ftype, get<2>(dentry), + get<3>(dentry)}); + cnt++; } - LOG(DEBUG, - "{}() Unpacked dirents for path '{}' counted '{}' entries", - __func__, path, cnt); - } else { - ret = gkfs::rpc::forward_get_dirents(path); - } - auto err = ret.first; - if(err) { - errno = err; - return -1; + ret.first = res.first; } - assert(ret.second); - return CTX->file_map()->add(ret.second); + LOG(DEBUG, "{}() Unpacked dirents for path '{}' counted '{}' entries", + __func__, path, cnt); + } else { + ret = gkfs::rpc::forward_get_dirents(path); + } + auto err = ret.first; + if(err) { + errno = err; + return -1; } + assert(ret.second); + return CTX->file_map()->add(ret.second); +} - /** - * gkfs wrapper for rmdir() system calls - * errno may be set - * @param path - * @return 0 on success or -1 on error - */ - int gkfs_rmdir(const std::string& path) { - int err; - // check that directory is empty if a strict dir hierarchy is - // enforced - // TODO rename #define +/** + * gkfs wrapper for rmdir() system calls + * errno may be set + * @param path + * @return 0 on success or -1 on error + */ +int +gkfs_rmdir(const std::string& path) { + int err; + // check that directory is empty if a strict dir hierarchy is + // enforced + // TODO rename #define #if GKFS_CREATE_CHECK_PARENTS - auto md = gkfs::utils::get_metadata(path); - if(!md) { - LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, - strerror(errno)); - return -1; - } - if(!S_ISDIR(md->mode())) { - LOG(DEBUG, "{}() Path is not a directory", __func__); - errno = ENOTDIR; - return -1; - } - auto ret = gkfs::rpc::forward_get_dirents(path); - err = ret.first; - if(err) { - errno = err; - return -1; - } - assert(ret.second); - auto open_dir = ret.second; - if(open_dir->size() != 2) { - errno = ENOTEMPTY; - return -1; - } + auto md = gkfs::utils::get_metadata(path); + if(!md) { + LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, strerror(errno)); + return -1; + } + if(!S_ISDIR(md->mode())) { + LOG(DEBUG, "{}() Path is not a directory", __func__); + errno = ENOTDIR; + return -1; + } + auto ret = gkfs::rpc::forward_get_dirents(path); + err = ret.first; + if(err) { + errno = err; + return -1; + } + assert(ret.second); + auto open_dir = ret.second; + if(open_dir->size() != 2) { + errno = ENOTEMPTY; + return -1; + } #endif - if(gkfs::config::proxy::fwd_remove && CTX->use_proxy()) { - err = gkfs::rpc::forward_remove_proxy(path, true); - } else { - err = gkfs::rpc::forward_remove(path, true, CTX->get_replicas()); - } - if(err) { - errno = err; - return -1; - } - return 0; + if(gkfs::config::proxy::fwd_remove && CTX->use_proxy()) { + err = gkfs::rpc::forward_remove_proxy(path, true); + } else { + err = gkfs::rpc::forward_remove(path, true, CTX->get_replicas()); + } + if(err) { + errno = err; + return -1; } + return 0; +} - /** - * gkfs wrapper for getdents() system calls - * errno may be set - * @param fd - * @param dirp - * @param count - * @return 0 on success or -1 on error - */ - int gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, - unsigned int count) { - // Get opendir object (content was downloaded with opendir() call) - auto open_dir = CTX->file_map()->get_dir(fd); - if(open_dir == nullptr) { - // Cast did not succeeded: open_file is a regular file - errno = EBADF; - return -1; - } +/** + * gkfs wrapper for getdents() system calls + * errno may be set + * @param fd + * @param dirp + * @param count + * @return 0 on success or -1 on error + */ +int +gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) { + // Get opendir object (content was downloaded with opendir() call) + auto open_dir = CTX->file_map()->get_dir(fd); + if(open_dir == nullptr) { + // Cast did not succeeded: open_file is a regular file + errno = EBADF; + return -1; + } - // get directory position of which entries to return - auto pos = open_dir->pos(); - if(pos >= open_dir->size()) { - return 0; - } + // get directory position of which entries to return + auto pos = open_dir->pos(); + if(pos >= open_dir->size()) { + return 0; + } - unsigned int written = 0; - struct linux_dirent* current_dirp = nullptr; - while(pos < open_dir->size()) { - // get dentry fir current position - auto de = open_dir->getdent(pos); - if(CTX->protect_files_consumer() or - CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - pos++; - continue; - } - } - /* - * Calculate the total dentry size within the kernel struct - * `linux_dirent` depending on the file name size. The size is - * then aligned to the size of `long` boundary. This line was - * originally defined in the linux kernel: fs/readdir.c in - * function filldir(): int reclen = ALIGN(offsetof(struct - * linux_dirent, d_name) + namlen - * + 2, sizeof(long)); However, since d_name is null-terminated - * and de.name().size() does not include space for the - * null-terminator, we add 1. Thus, + 3 in total. - */ - auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) + - de.name().size() + 3, - sizeof(long)); - if(total_size > (count - written)) { - // no enough space left on user buffer to insert next dirent - break; + unsigned int written = 0; + struct linux_dirent* current_dirp = nullptr; + while(pos < open_dir->size()) { + // get dentry fir current position + auto de = open_dir->getdent(pos); + if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + pos++; + continue; } - current_dirp = reinterpret_cast( - reinterpret_cast(dirp) + written); - current_dirp->d_ino = std::hash()(open_dir->path() + - "/" + de.name()); - - current_dirp->d_reclen = total_size; - - current_dirp->d_type = - ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG - : DT_DIR); - - LOG(DEBUG, "name {}: {}", pos, de.name()); - std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); - ++pos; - current_dirp->d_off = pos; - written += total_size; - } - - if(written == 0) { - errno = EINVAL; - return -1; } - // set directory position for next getdents() call - open_dir->pos(pos); - return written; - } - - /** - * gkfs wrapper for getdents64() system calls - * errno may be set - * @param fd - * @param dirp - * @param count - * @return 0 on success or -1 on error - */ - int gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, - unsigned int count) { - auto open_dir = CTX->file_map()->get_dir(fd); - if(open_dir == nullptr) { - // Cast did not succeeded: open_file is a regular file - errno = EBADF; - return -1; - } - auto pos = open_dir->pos(); - if(pos >= open_dir->size()) { - return 0; + /* + * Calculate the total dentry size within the kernel struct + * `linux_dirent` depending on the file name size. The size is + * then aligned to the size of `long` boundary. This line was + * originally defined in the linux kernel: fs/readdir.c in + * function filldir(): int reclen = ALIGN(offsetof(struct + * linux_dirent, d_name) + namlen + * + 2, sizeof(long)); However, since d_name is null-terminated + * and de.name().size() does not include space for the + * null-terminator, we add 1. Thus, + 3 in total. + */ + auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) + + de.name().size() + 3, + sizeof(long)); + if(total_size > (count - written)) { + // no enough space left on user buffer to insert next dirent + break; } - unsigned int written = 0; - struct linux_dirent64* current_dirp = nullptr; - while(pos < open_dir->size()) { - auto de = open_dir->getdent(pos); - if(CTX->protect_files_consumer() or - CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - pos++; - continue; - } - } - /* - * Calculate the total dentry size within the kernel struct - * `linux_dirent` depending on the file name size. The size is - * then aligned to the size of `long` boundary. - * - * This line was originally defined in the linux kernel: - * fs/readdir.c in function filldir64(): int reclen = - * ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, - * sizeof(u64)); We keep + 1 because: Since d_name is - * null-terminated and de.name().size() does not include space - * for the null-terminator, we add 1. Since d_name in our - * `struct linux_dirent64` definition is not a zero-size array - * (as opposed to the kernel version), we subtract 1. Thus, it - * stays + 1. - */ - auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) + - de.name().size() + 1, - sizeof(uint64_t)); - if(total_size > (count - written)) { - // no enough space left on user buffer to insert next dirent - break; + current_dirp = reinterpret_cast( + reinterpret_cast(dirp) + written); + current_dirp->d_ino = + std::hash()(open_dir->path() + "/" + de.name()); + + current_dirp->d_reclen = total_size; + + current_dirp->d_type = + ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG + : DT_DIR); + + LOG(DEBUG, "name {}: {}", pos, de.name()); + std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); + ++pos; + current_dirp->d_off = pos; + written += total_size; + } + + if(written == 0) { + errno = EINVAL; + return -1; + } + // set directory position for next getdents() call + open_dir->pos(pos); + return written; +} + +/** + * gkfs wrapper for getdents64() system calls + * errno may be set + * @param fd + * @param dirp + * @param count + * @return 0 on success or -1 on error + */ +int +gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, + unsigned int count) { + auto open_dir = CTX->file_map()->get_dir(fd); + if(open_dir == nullptr) { + // Cast did not succeeded: open_file is a regular file + errno = EBADF; + return -1; + } + auto pos = open_dir->pos(); + if(pos >= open_dir->size()) { + return 0; + } + unsigned int written = 0; + struct linux_dirent64* current_dirp = nullptr; + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos); + if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + pos++; + continue; } - current_dirp = reinterpret_cast( - reinterpret_cast(dirp) + written); - current_dirp->d_ino = std::hash()(open_dir->path() + - "/" + de.name()); - - current_dirp->d_reclen = total_size; - current_dirp->d_type = - ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG - : DT_DIR); - - LOG(DEBUG, "name {}: {}", pos, de.name()); - std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); - ++pos; - current_dirp->d_off = pos; - written += total_size; } + /* + * Calculate the total dentry size within the kernel struct + * `linux_dirent` depending on the file name size. The size is + * then aligned to the size of `long` boundary. + * + * This line was originally defined in the linux kernel: + * fs/readdir.c in function filldir64(): int reclen = + * ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, + * sizeof(u64)); We keep + 1 because: Since d_name is + * null-terminated and de.name().size() does not include space + * for the null-terminator, we add 1. Since d_name in our + * `struct linux_dirent64` definition is not a zero-size array + * (as opposed to the kernel version), we subtract 1. Thus, it + * stays + 1. + */ + auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) + + de.name().size() + 1, + sizeof(uint64_t)); + if(total_size > (count - written)) { + // no enough space left on user buffer to insert next dirent + break; + } + current_dirp = reinterpret_cast( + reinterpret_cast(dirp) + written); + current_dirp->d_ino = + std::hash()(open_dir->path() + "/" + de.name()); - if(written == 0) { - errno = EINVAL; + current_dirp->d_reclen = total_size; + current_dirp->d_type = + ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG + : DT_DIR); + + LOG(DEBUG, "name {}: {}", pos, de.name()); + std::strcpy(&(current_dirp->d_name[0]), de.name().c_str()); + ++pos; + current_dirp->d_off = pos; + written += total_size; + } + + if(written == 0) { + errno = EINVAL; + return -1; + } + open_dir->pos(pos); + return written; +} + +int +gkfs_fsync(unsigned int fd) { + auto file = CTX->file_map()->get(fd); + if(!file) { + errno = 0; + return 0; + } + // flush write size cache to be server consistent + if(CTX->use_write_size_cache()) { + auto err = CTX->write_size_cache()->flush(file->path(), true).first; + if(err) { + LOG(ERROR, "{}() write_size_cache() failed with err '{}'", __func__, + err); + errno = err; return -1; } - open_dir->pos(pos); - return written; } + errno = 0; + return 0; +} - int gkfs_fsync(unsigned int fd) { - auto file = CTX->file_map()->get(fd); - if(!file) { - errno = 0; - return 0; - } +/** + * @brief Closes an fd. To be used externally + * + * @param fd + * @return int + */ +int +gkfs_close(unsigned int fd) { + auto file = CTX->file_map()->get(fd); + if(file) { // flush write size cache to be server consistent if(CTX->use_write_size_cache()) { auto err = CTX->write_size_cache()->flush(file->path(), true).first; @@ -1808,235 +1832,214 @@ gkfs_do_write(gkfs::filemap::OpenFile& file, const char* buf, size_t count, return -1; } } - errno = 0; - return 0; - } - - /** - * @brief Closes an fd. To be used externally - * - * @param fd - * @return int - */ - int gkfs_close(unsigned int fd) { - auto file = CTX->file_map()->get(fd); - if(file) { - // flush write size cache to be server consistent - if(CTX->use_write_size_cache()) { - auto err = CTX->write_size_cache() - ->flush(file->path(), true) - .first; - if(err) { - LOG(ERROR, "{}() write_size_cache() failed with err '{}'", - __func__, err); - errno = err; - return -1; - } - } - if(CTX->use_dentry_cache() && - gkfs::config::cache::clear_dentry_cache_on_close) { - // clear cache for directory - if(CTX->file_map()->get(fd)->type() == - gkfs::filemap::FileType::directory) { - CTX->dentry_cache()->clear_dir( - CTX->file_map()->get(fd)->path()); - } - } - - if(CTX->protect_files_generator()) { - auto path = CTX->file_map()->get(fd)->path(); - generate_lock_file(path, false); + if(CTX->use_dentry_cache() && + gkfs::config::cache::clear_dentry_cache_on_close) { + // clear cache for directory + if(CTX->file_map()->get(fd)->type() == + gkfs::filemap::FileType::directory) { + CTX->dentry_cache()->clear_dir( + CTX->file_map()->get(fd)->path()); } - // No call to the daemon is required - CTX->file_map()->remove(fd); - return 0; } - if(CTX->is_internal_fd(fd)) { - // the client application (for some reason) is trying to close - // an internal fd: ignore it - LOG(ERROR, "{}() closing an internal fd '{}'", __func__, fd); - errno = EACCES; - return -1; + if(CTX->protect_files_generator()) { + auto path = CTX->file_map()->get(fd)->path(); + generate_lock_file(path, false); } + // No call to the daemon is required + CTX->file_map()->remove(fd); + return 0; + } + if(CTX->is_internal_fd(fd)) { + // the client application (for some reason) is trying to close + // an internal fd: ignore it + LOG(ERROR, "{}() closing an internal fd '{}'", __func__, fd); + errno = EACCES; return -1; } + return -1; +} + #ifdef HAS_SYMLINKS - /** - * gkfs wrapper for make symlink() system calls - * errno may be set - * - * * NOTE: Currently unused - * - * @param path - * @param target_path - * @return 0 on success or -1 on error +/** + * gkfs wrapper for make symlink() system calls + * errno may be set + * + * * NOTE: Currently unused + * + * @param path + * @param target_path + * @return 0 on success or -1 on error + */ +int +gkfs_mk_symlink(const std::string& path, const std::string& target_path) { + /* The following check is not POSIX compliant. + * In POSIX the target is not checked at all. + * Here if the target is a directory we raise a NOTSUP error. + * So that application know we don't support link to directory. */ - int gkfs_mk_symlink(const std::string& path, - const std::string& target_path) { - /* The following check is not POSIX compliant. - * In POSIX the target is not checked at all. - * Here if the target is a directory we raise a NOTSUP error. - * So that application know we don't support link to directory. - */ - auto target_md = gkfs::utils::get_metadata(target_path, false); - std::string new_path = target_path; - if(target_md) { - auto trg_mode = target_md->mode(); - if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) { - assert(S_ISDIR(trg_mode)); - LOG(DEBUG, "Target path is a directory. Not supported"); - errno = ENOTSUP; - return -1; - } - } - - if(check_parent_dir(path)) { + auto target_md = gkfs::utils::get_metadata(target_path, false); + std::string new_path = target_path; + if(target_md) { + auto trg_mode = target_md->mode(); + if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) { + assert(S_ISDIR(trg_mode)); + LOG(DEBUG, "Target path is a directory. Not supported"); + errno = ENOTSUP; return -1; } + } - // Path should exists - auto link_md = gkfs::utils::get_metadata(path, false); - if(link_md) { - LOG(DEBUG, "Link does exists: '{}'", path); - errno = EEXIST; - return -1; - } - LOG(DEBUG, "Create file: {}", path); - // create target_path file (we create it regular) - auto create = gkfs_create(path, 0); - if(create) { - return -1; - } - auto err = gkfs::rpc::forward_mk_symlink(path, target_path); - if(err) { - errno = err; - return -1; - } - return 0; + if(check_parent_dir(path)) { + return -1; } - /** - * gkfs wrapper for reading symlinks - * errno may be set - * - * NOTE: Currently unused - * - * @param path - * @param buf - * @param bufsize - * @return 0 on success or -1 on error - */ - int gkfs_readlink(const std::string& path, char* buf, int bufsize) { - auto md = gkfs::utils::get_metadata(path, false); - if(!md) { - LOG(DEBUG, "Named link doesn't exist"); - return -1; - } - if(!(md->is_link())) { - LOG(DEBUG, "The named file is not a symbolic link"); - errno = EINVAL; - return -1; - } - int path_size = md->target_path().size() + CTX->mountdir().size(); - if(path_size >= bufsize) { - LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ", - bufsize, path_size, md->target_path()); - errno = ENAMETOOLONG; - return -1; - } + // Path should exists + auto link_md = gkfs::utils::get_metadata(path, false); + if(link_md) { + LOG(DEBUG, "Link does exists: '{}'", path); + errno = EEXIST; + return -1; + } + LOG(DEBUG, "Create file: {}", path); + // create target_path file (we create it regular) + auto create = gkfs_create(path, 0); + if(create) { + return -1; + } + auto err = gkfs::rpc::forward_mk_symlink(path, target_path); + if(err) { + errno = err; + return -1; + } + return 0; +} - CTX->mountdir().copy(buf, CTX->mountdir().size()); - std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str()); - return path_size; +/** + * gkfs wrapper for reading symlinks + * errno may be set + * + * NOTE: Currently unused + * + * @param path + * @param buf + * @param bufsize + * @return 0 on success or -1 on error + */ +int +gkfs_readlink(const std::string& path, char* buf, int bufsize) { + auto md = gkfs::utils::get_metadata(path, false); + if(!md) { + LOG(DEBUG, "Named link doesn't exist"); + return -1; } + if(!(md->is_link())) { + LOG(DEBUG, "The named file is not a symbolic link"); + errno = EINVAL; + return -1; + } + int path_size = md->target_path().size() + CTX->mountdir().size(); + if(path_size >= bufsize) { + LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ", + bufsize, path_size, md->target_path()); + errno = ENAMETOOLONG; + return -1; + } + + CTX->mountdir().copy(buf, CTX->mountdir().size()); + std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str()); + return path_size; +} #endif - std::vector gkfs_get_file_list(const std::string& path) { - auto ret = gkfs::rpc::forward_get_dirents(path); - auto err = ret.first; - if(err) { - errno = err; - return {}; - } +std::vector +gkfs_get_file_list(const std::string& path) { + auto ret = gkfs::rpc::forward_get_dirents(path); + auto err = ret.first; + if(err) { + errno = err; + return {}; + } - auto open_dir = ret.second; + auto open_dir = ret.second; - std::vector file_list; - unsigned int pos = 0; + std::vector file_list; + unsigned int pos = 0; - while(pos < open_dir->size()) { - auto de = open_dir->getdent(pos++); - if(CTX->protect_files_consumer() or - CTX->protect_files_generator()) { - // if de.name ends with lockgekko jump to the next file - if(de.name().size() >= 10 && - de.name().substr(de.name().size() - 10) == ".lockgekko") { - continue; - } + while(pos < open_dir->size()) { + auto de = open_dir->getdent(pos++); + if(CTX->protect_files_consumer() or CTX->protect_files_generator()) { + // if de.name ends with lockgekko jump to the next file + if(de.name().size() >= 10 && + de.name().substr(de.name().size() - 10) == ".lockgekko") { + continue; } - file_list.push_back(de.name()); } - return file_list; + file_list.push_back(de.name()); } + return file_list; +} - void* gkfs_mmap(void* addr, size_t length, int prot, int flags, int fd, - off_t offset) { - void* ptr = malloc(length); - if(ptr == nullptr) { - return MAP_FAILED; - } - // store info on mmap_set - mmap_set.insert(std::make_tuple(ptr, fd, length, offset)); - gkfs::syscall::gkfs_pread(fd, ptr, length, offset); - return ptr; - } - - int gkfs_msync(void* addr, size_t length, int flags) { - // check if addr is from gekkofs (mmap_set) - // if so, get the fd and offset - // pwrite length to the original offset - - for(const auto& tuple : mmap_set) { - if(std::get<0>(tuple) == addr) { - int fd = std::get<1>(tuple); - off_t offset = std::get<3>(tuple); - gkfs::syscall::gkfs_pwrite(fd, addr, length, offset); - return 0; - } +void* +gkfs_mmap(void* addr, size_t length, int prot, int flags, int fd, + off_t offset) { + void* ptr = malloc(length); + if(ptr == nullptr) { + return MAP_FAILED; + } + // store info on mmap_set + mmap_set.insert(std::make_tuple(ptr, fd, length, offset)); + gkfs::syscall::gkfs_pread(fd, ptr, length, offset); + return ptr; +} + +int +gkfs_msync(void* addr, size_t length, int flags) { + // check if addr is from gekkofs (mmap_set) + // if so, get the fd and offset + // pwrite length to the original offset + + for(const auto& tuple : mmap_set) { + if(std::get<0>(tuple) == addr) { + int fd = std::get<1>(tuple); + off_t offset = std::get<3>(tuple); + gkfs::syscall::gkfs_pwrite(fd, addr, length, offset); + return 0; } - return -1; } + return -1; +} - int gkfs_munmap(void* addr, size_t length) { - // check if addr is from gekkofs (mmap_set) - // if so, get the fd and offset - // pwrite length to the original offset - // return - // if not just go to the normal msync - if(mmap_set.size() != 0) { - // use find_if std::algorithm - // if found, call msync - - auto it = std::find_if( - mmap_set.begin(), mmap_set.end(), - [&addr](const std::tuple& t) { - return std::get<0>(t) == addr; - }); - if(it != mmap_set.end()) { - gkfs_msync(addr, length, 0); - free(addr); - mmap_set.erase(it); - return 0; - } +int +gkfs_munmap(void* addr, size_t length) { + // check if addr is from gekkofs (mmap_set) + // if so, get the fd and offset + // pwrite length to the original offset + // return + // if not just go to the normal msync + if(mmap_set.size() != 0) { + // use find_if std::algorithm + // if found, call msync + + auto it = std::find_if( + mmap_set.begin(), mmap_set.end(), + [&addr](const std::tuple& t) { + return std::get<0>(t) == addr; + }); + if(it != mmap_set.end()) { + gkfs_msync(addr, length, 0); + free(addr); + mmap_set.erase(it); + return 0; } - return -1; } + return -1; +} } // namespace gkfs::syscall -- GitLab From 14f199888acdc445a0a109cb3e0a1ea2b5f6b161 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 4 Aug 2025 09:22:20 +0200 Subject: [PATCH 4/5] add config.hpp --- include/config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/config.hpp b/include/config.hpp index c1ba919ca..dbb066f07 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -83,6 +83,7 @@ namespace io { * If buffer is not zeroed, sparse regions contain invalid data. */ constexpr auto zero_buffer_before_read = false; +constexpr auto enable_chunk_read_before_write = true; /* * When the daemon handler serves a read request, it starts tasklets (for each * chunk) from the io pool to read all chunks of that read request in parallel. -- GitLab From 231414b6116b7c99d3b12e755e28029f2328f8b8 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 4 Aug 2025 10:42:38 +0200 Subject: [PATCH 5/5] bugfix --- src/client/gkfs_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 5018368fc..d59f482a9 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -1109,7 +1109,7 @@ gkfs_do_write(gkfs::filemap::OpenFile& file, const char* buf, size_t count, // concat read buffer (0..offset) with the rest of the chunk and write // the first chunk. Then do a forward write of the rest of the data. if(gkfs::config::io::enable_chunk_read_before_write && - gkfs::config::rpc::chunksize > 0 && + offset > 0 && offset < gkfs::config::rpc::chunksize && gkfs::config::rpc::chunksize < count) { // Read the first chunk -- GitLab