Commit 16646a84 authored by Julius Athenstaedt's avatar Julius Athenstaedt
Browse files

readdirplus handler for fuse

parent 79252371
Loading
Loading
Loading
Loading
Loading
+129 −6
Original line number Diff line number Diff line
@@ -200,8 +200,8 @@ init_handler(void* userdata, struct fuse_conn_info* conn) {
        conn->want |= FUSE_CAP_ASYNC_DIO;
    }

    // conn->want |= FUSE_CAP_READDIRPLUS;
    // conn->want |= FUSE_CAP_READDIRPLUS_AUTO;
    conn->want |= FUSE_CAP_READDIRPLUS;
    conn->want |= FUSE_CAP_READDIRPLUS_AUTO;

    if(ud->splice) {
        conn->want |= FUSE_CAP_SPLICE_READ;
@@ -530,14 +530,13 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
    DEBUG_INFO(ud, "readdir handler");

    auto open_dir = CTX->file_map()->get_dir(fi->fh);

    DEBUG_INFO(ud, "read dir %s", open_dir->path().c_str());

    if(open_dir == nullptr) {
        fuse_reply_err(req, EBADF);
        return;
    }

    DEBUG_INFO(ud, "read dir %s", open_dir->path().c_str());

    // Allocate a buffer to accumulate entries
    char* buf = static_cast<char*>(malloc(size));
    if(!buf) {
@@ -602,6 +601,130 @@ readdir_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
    free(buf);
}

static void
readdirplus_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
                    struct fuse_file_info* fi) {
    auto* ud = udata(req);
    DEBUG_INFO(ud, "readdirplus handler");

    auto open_dir = CTX->file_map()->get_dir(fi->fh);
    if(open_dir == nullptr) {
        fuse_reply_err(req, EBADF);
        return;
    }

    DEBUG_INFO(ud, "read dirplus %s", open_dir->path().c_str());

    // Allocate a buffer to accumulate entries
    char* buf = static_cast<char*>(malloc(size));
    if(!buf) {
        fuse_reply_err(req, ENOMEM);
        return;
    }

    size_t bytes_filled = 0;
    size_t pos = off;

    // Only include GekkoFS entries (skip standard files)
    while(pos < open_dir->size()) {
        auto de = open_dir->getdent(pos);

        // --- TODO maybe refactor into fn with lookup_handler
        std::string child = open_dir->path() == "/"
                                    ? "/" + de.name()
                                    : open_dir->path() + "/" + de.name();
        DEBUG_INFO(ud, "read dirplus child %s", child.c_str());

        struct stat st{};
        fuse_entry_param e = {};

        if(de.name() == "." || de.name() == "..") {
            st.st_ino = std::hash<std::string>()(open_dir->path() + "/" +
                                                 de.name());
            st.st_mode = (de.type() == gkfs::filemap::FileType::regular)
                                 ? S_IFREG
                                 : S_IFDIR;
        } else {
            int rc = gkfs::syscall::gkfs_stat(child, &st);
            if(rc < 0) {
                fuse_reply_err(req, ENOENT);
                return;
            }

            // See if we already have this path
            // TODO Do we have to lock here? The test ends up in a deadlock!
            // std::lock_guard<std::mutex> lk(ino_mutex);
            auto it = path_map.find(child);
            fuse_ino_t ino;
            if(it != path_map.end()) {
                ino = it->second;
                auto inode = get_inode(ino);
                if(inode) {
                    inode->lookup_count++;
                }
            } else {
                ino = alloc_inode(child);
            }

            e.ino = ino;
        }
        e.attr = st;
        e.attr_timeout = ud->timeout;
        e.entry_timeout = ud->timeout;
        // ---

        size_t entry_size = fuse_add_direntry_plus(
                req, buf + bytes_filled, size - bytes_filled, de.name().c_str(),
                &e, pos + 1);

        if(entry_size > size - bytes_filled)
            break; // not enough space left

        bytes_filled += entry_size;
        pos += 1;
    }

    // FIFO entries
    if(ud->fifo) {
        int i = 0;
        for(auto const& kv : local_fifos) {
            if(i < off) {
                i++;
                continue;
            }
            std::filesystem::path vpath = kv.first;
            const struct stat& st = kv.second;

            // check if FIFO belongs to this directory
            if(vpath.parent_path() != open_dir->path())
                continue;

            std::string fname = vpath.filename();

            fuse_entry_param e{};
            e.ino = st.st_ino;
            e.attr = st;
            e.attr_timeout = ud->timeout;
            e.entry_timeout = ud->timeout;

            size_t entry_size = fuse_add_direntry_plus(
                    req, buf + bytes_filled, size - bytes_filled, fname.c_str(),
                    &e, pos + 1);

            if(entry_size > size - bytes_filled)
                break;

            bytes_filled += entry_size;
            pos += 1;
        }
    }

    open_dir->pos(pos); // update internal position if needed

    fuse_reply_buf(req, buf, bytes_filled);
    free(buf);
}

static void
releasedir_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
    auto* ud = udata(req);
@@ -1021,7 +1144,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) {
    ops->readdir = readdir_handler;
    ops->opendir = opendir_handler;
    ops->releasedir = releasedir_handler;
    // ops->readdirplus = readdirplus_handler;
    ops->readdirplus = readdirplus_handler;
    // ops->fsyncdir = nullptr;

    // I/O