Skip to content
gkfs_functions.cpp 32.3 KiB
Newer Older
/**
 * 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);
        /*
         * 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;
    }

    if(written == 0) {
        errno = EINVAL;
        return -1;
    }
    open_dir->pos(pos);
    return written;
}


#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.
     */
    auto target_md = gkfs::utils::get_metadata(target_path, false);
        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 link_md = gkfs::utils::get_metadata(path, false);
Alberto Miranda's avatar
Alberto Miranda committed
        LOG(DEBUG, "Link exists: '{}'", path);
        errno = EEXIST;
        return -1;
    }
Marc Vef's avatar
Marc Vef committed
    auto err = gkfs::rpc::forward_mk_symlink(path, target_path);
Marc Vef's avatar
Marc Vef committed
        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);
        LOG(DEBUG, "Named link doesn't exist");
    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;
}

Marc Vef's avatar
Marc Vef committed
} // namespace gkfs::syscall


/* This function defines an extension of the dirents prepared to do a find-like
 * function The function only sends the request to the specified server
 */
extern "C" int
gkfs_getsingleserverdir(const char* path, struct dirent_extended* dirp,
                        unsigned int count, int server) {

    auto ret = gkfs::rpc::forward_get_dirents_single(path, server);
    auto err = ret.first;
    if(err) {
        errno = err;
        return -1;
    }

    auto open_dir = ret.second;
    unsigned int pos = 0;
    unsigned int written = 0;
    struct dirent_extended* current_dirp = nullptr;
    while(pos < open_dir.size()) {
        auto de = open_dir[pos];
        /*
         * Calculate the total dentry size within the 'dirent_extended`
         * depending on the file name size. The size is then aligned to the size
         * of `long` boundary.
         */
        auto total_size = ALIGN(offsetof(struct dirent_extended, d_name) +
                                        (get<0>(de)).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<struct dirent_extended*>(
                reinterpret_cast<char*>(dirp) + written);

        current_dirp->d_reclen = total_size;
        current_dirp->d_type = get<1>(de);
        current_dirp->size = get<2>(de);
        current_dirp->ctime = get<3>(de);

        LOG(DEBUG, "name {}: {} {} {} {} / size {}", pos, get<0>(de),
            get<1>(de), get<2>(de), get<3>(de), total_size);
        std::strcpy(&(current_dirp->d_name[0]), (get<0>(de)).c_str());
        ++pos;
        written += total_size;
    }

    if(written == 0) {
        errno = EINVAL;
        return -1;
    }
    return written;
}