Newer
Older
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);
/*
* 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
current_dirp = reinterpret_cast<struct linux_dirent64*>(
reinterpret_cast<char*>(dirp) + written);
current_dirp->d_ino =
std::hash<std::string>()(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;
}
errno = EINVAL;
return -1;
}
open_dir->pos(pos);
return written;
}
/**
* 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.
*/
auto target_md = gkfs::util::get_metadata(target_path, false);
if(target_md != nullptr) {
if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) {
LOG(DEBUG, "Target path is a directory. Not supported");
errno = ENOTSUP;
return -1;
}
}
if(check_parent_dir(path)) {
auto link_md = gkfs::util::get_metadata(path, false);
if(link_md != nullptr) {
errno = EEXIST;
return -1;
}
auto err = gkfs::rpc::forward_mk_symlink(path, target_path);
/**
* 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::util::get_metadata(path, false);
LOG(DEBUG, "Named link doesn't exist");
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;
}
} // namespace syscall
} // namespace gkfs