void nfs_client::reply_entry()

in turbonfs/src/nfs_client.cpp [1989:2114]


void nfs_client::reply_entry(
    struct rpc_task *task,
    const nfs_fh3 *fh,
    const struct fattr3 *fattr,
    const struct fuse_file_info *file)
{
    assert(task->magic == RPC_TASK_MAGIC);

    enum fuse_opcode optype = task->get_op_type();
    struct nfs_inode *inode = nullptr;
    fuse_entry_param entry;
    /*
     * Kernel must cache lookup result.
     */
    const bool cache_positive =
        (aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_ALL ||
         aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_POS);

    memset(&entry, 0, sizeof(entry));

    if (fh) {
        const fuse_ino_t parent_ino = task->rpc_api->get_parent_ino();
        struct nfs_inode *parent_inode = get_nfs_inode_from_ino(parent_ino);
        /*
         * This will grab a lookupcnt ref on the inode, which will be freed
         * from fuse forget callback.
         */
        inode = get_nfs_inode(fh, fattr);

        entry.ino = inode->get_fuse_ino();
        entry.generation = inode->get_generation();
        /*
         * This takes shared lock on inode->ilock_1.
         */
        entry.attr = inode->get_attr();
        if (cache_positive) {
            entry.attr_timeout = inode->get_actimeo();
            entry.entry_timeout = inode->get_actimeo();
        } else {
            entry.attr_timeout = 0;
            entry.entry_timeout = 0;
        }

        /*
         * If it's a proxy task, optype of the original task should be used.
         * Currently we use proxy task only for LOOKUP, so assert for that.
         */
        if (task->get_proxy_op_type() != (fuse_opcode) 0) {
            AZLogDebug("Completing proxy task {} -> {}",
                       rpc_task::fuse_opcode_to_string(optype),
                       rpc_task::fuse_opcode_to_string(
                           task->get_proxy_op_type()));

            assert(optype == FUSE_LOOKUP);
            optype = task->get_proxy_op_type();
            // LOOKUP cannot be proxying a LOOKUP.
            assert(optype != FUSE_LOOKUP);
        }

        /*
         * Note: reply_create()/reply_entry() below will increment
         *       forget_expected just before replying to fuse, so we log the
         *       updated count here.
         */
        AZLogDebug("[{}] <{}> Returning ino {} to fuse (filename: {}, "
                   "lookupcnt: {}, dircachecnt: {}, forget_expected: {})",
                   parent_ino,
                   rpc_task::fuse_opcode_to_string(optype),
                   inode->get_fuse_ino(),
                   task->rpc_api->get_file_name(),
                   inode->lookupcnt.load(),
                   inode->dircachecnt.load(),
                   inode->forget_expected.load() + 1);

        /*
         * This is the common place where we return inode to fuse.
         * After this fuse can call any of the functions that might need file
         * or dir cache, so allocate them now.
         */
        switch (optype) {
            case FUSE_CREATE:
                assert(file);
                inode->on_fuse_open(optype);
                break;
            case FUSE_LOOKUP:
            case FUSE_MKNOD:
            case FUSE_MKDIR:
            case FUSE_SYMLINK:
                assert(!file);
                inode->on_fuse_lookup(optype);
                break;
            default:
                AZLogError("[{}] Invalid optype: {}",
                           inode->get_fuse_ino(), (int) optype);
                assert(0);
        }

        /*
         * Add lookup results to DNLC cache.
         */
        parent_inode->dnlc_add(task->rpc_api->get_file_name(), inode);
    } else {
        /*
         * The only valid case where reply_entry() is called with null fh
         * is the case where lookup yielded "not found". We are using the
         * fuse support for negative dentry where we should respond with
         * success but ino set to 0 to convey to fuse that it must cache
         * the negative dentry for entry_timeout period.
         * This caching helps to improve performance by avoiding repeated
         * lookup requests for entries that are known not to exist.
         *
         * TODO: See if negative entries must be cached for lesser time.
         */
        assert(aznfsc_cfg.lookupcache_int == AZNFSCFG_LOOKUPCACHE_ALL);
        assert(!fattr);

        entry.attr_timeout = aznfsc_cfg.actimeo;
        entry.entry_timeout = aznfsc_cfg.actimeo;
    }

    if (file) {
        task->reply_create(&entry, file);
    } else {
        task->reply_entry(&entry);
    }
}