Newer
Older
#include <config.hpp>
#include <client/preload_util.hpp>
#include <client/logging.hpp>
#include <client/gkfs_functions.hpp>
#include <client/rpc/forward_metadata.hpp>
#include <client/rpc/forward_data.hpp>
#include <global/path_util.hpp>
extern "C" {
#include <dirent.h> // used for file types in the getdents{,64}() functions
#include <linux/kernel.h> // used for definition of alignment macros
#include <sys/statfs.h>
#include <sys/statvfs.h>
}
using namespace std;
/*
* Macro used within getdents{,64} functions.
* __ALIGN_KERNEL defined in linux/kernel.h
*/
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
* linux_dirent is used in getdents() but is privately defined in the linux
* kernel: fs/readdir.c.
*/
struct linux_dirent {
unsigned long d_ino;
unsigned long d_off;
unsigned short d_reclen;
char d_name[1];
};
/*
* linux_dirent64 is used in getdents64() and defined in the linux kernel:
* include/linux/dirent.h. However, it is not part of the kernel-headers and
* cannot be imported.
*/
struct linux_dirent64 {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
// forbids zero-size array 'd_name'
Ramon Nou
committed
struct dirent_extended {
size_t size;
time_t ctime;
unsigned short d_reclen;
unsigned char d_type;
char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
// forbids zero-size array 'd_name'
};
* Checks if metadata for parent directory exists (can be disabled with
* CREATE_CHECK_PARENTS). errno may be set
* @param path
* @return 0 on success, -1 on failure
*/
int
check_parent_dir(const std::string& path) {
auto p_comp = gkfs::path::dirname(path);
auto md = gkfs::utils::get_metadata(p_comp);
if(!md) {
if(errno == ENOENT) {
LOG(DEBUG, "Parent component does not exist: '{}'", p_comp);
} else {
LOG(ERROR, "Failed to get metadata for parent component '{}': {}",
path, strerror(errno));
if(!S_ISDIR(md->mode())) {
LOG(DEBUG, "Parent component is not a directory: '{}'", p_comp);
errno = ENOTDIR;
return -1;
}
#endif // CREATE_CHECK_PARENTS
return 0;
}
/**
* gkfs wrapper for open() system calls
* errno may be set
* @param path
* @param mode
* @param flags
* @return 0 on success, -1 on failure
*/
int
gkfs_open(const std::string& path, mode_t mode, int flags) {
LOG(ERROR, "`O_PATH` flag is not supported");
errno = ENOTSUP;
return -1;
}
LOG(ERROR, "`O_APPEND` flag is not supported");
errno = ENOTSUP;
return -1;
}
// metadata object filled during create or stat
gkfs::metadata::Metadata md{};
if(flags & O_CREAT) {
if(flags & O_DIRECTORY) {
LOG(ERROR, "O_DIRECTORY use with O_CREAT. NOT SUPPORTED");
// no access check required here. If one is using our FS they have the
// permissions.
auto err = gkfs_create(path, mode | S_IFREG);
if(err) {
if(errno == EEXIST) {
// file exists, O_CREAT was set
if(flags & O_EXCL) {
// File exists and O_EXCL & O_CREAT was set
return -1;
}
// file exists, O_CREAT was set O_EXCL wasnt, so function does
// not fail this case is actually undefined as per `man 2 open`
auto md_ = gkfs::utils::get_metadata(path);
if(!md_) {
LOG(ERROR,
"Could not get metadata after creating file '{}': '{}'",
path, strerror(errno));
return -1;
}
md = *md_;
} else {
LOG(ERROR, "Error creating file: '{}'", strerror(errno));
return -1;
}
} else {
// file was successfully created. Add to filemap
return CTX->file_map()->add(
std::make_shared<gkfs::filemap::OpenFile>(path, flags));
auto md_ = gkfs::utils::get_metadata(path);
if(!md_) {
if(errno != ENOENT) {
LOG(ERROR, "Error stating existing file '{}'", path);
// file doesn't exists and O_CREAT was not set
return -1;
if(md.is_link()) {
if(flags & O_NOFOLLOW) {
LOG(WARNING, "Symlink found and O_NOFOLLOW flag was specified");
errno = ELOOP;
return -1;
return gkfs_open(md.target_path(), mode, flags);
if(S_ISDIR(md.mode())) {
return gkfs_opendir(path);
}
/*** Regular file exists ***/
assert(S_ISREG(md.mode()));
if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) {
if(gkfs_truncate(path, md.size(), 0)) {
LOG(ERROR, "Error truncating file");
return -1;
return CTX->file_map()->add(
std::make_shared<gkfs::filemap::OpenFile>(path, flags));
Loading full blame...