void unlink_callback()

in turbonfs/src/rpc_task.cpp [2031:2142]


void unlink_callback(
    struct rpc_context *rpc,
    int rpc_status,
    void *data,
    void *private_data)
{
    rpc_task *task = (rpc_task*) private_data;
    assert(task->magic == RPC_TASK_MAGIC);

    auto res = (REMOVE3res*)data;

#if 0
    /*
     * Don't inject jukebox for non-idempotent requests.
     */
    INJECT_JUKEBOX(res, task);
#endif

    const fuse_ino_t parent_ino =
        task->rpc_api->unlink_task.get_parent_ino();
    struct nfs_inode *parent_inode =
        task->get_client()->get_nfs_inode_from_ino(parent_ino);
    // Are we unlinking a silly-renamed file?
    const bool for_silly_rename =
        task->rpc_api->unlink_task.get_for_silly_rename();
    int status = task->status(rpc_status, NFS_STATUS(res));
    const bool noent_is_success =
        (rpc_pdu_is_retransmitted(rpc_get_pdu(rpc)) &&
         aznfsc_cfg.sys.nodrc.remove_noent_as_success);

    /*
     * Now that the request has completed, we can query libnfs for the
     * dispatch time.
     */
    task->get_stats().on_rpc_complete(rpc_get_pdu(rpc), NFS_STATUSX(rpc_status, res));

    if (NFS_STATUS(res) == NFS3ERR_JUKEBOX) {
        task->get_client()->jukebox_retry(task);
    } else if (NFS_STATUS(res) == NFS3ERR_NOENT && noent_is_success) {
        AZLogWarn("[{}/{}] unlink_callback{}: Treating NFS3ERR_NOENT as "
                  "success for a retransmitted REMOVE RPC",
                  parent_ino, task->rpc_api->unlink_task.get_file_name(),
                  for_silly_rename ? "(silly_rename)" : "");
        status = 0;
        goto handle_success;
    } else {
handle_success:
        /*
         * REMOVE3res_u.resok and REMOVE3res_u.resfail have the same layout,
         * so we can safely access REMOVE3res_u.resok for both success and
         * failure cases.
         */
        static_assert(sizeof(res->REMOVE3res_u.resok) ==
                      sizeof(res->REMOVE3res_u.resfail));

        if (status == 0) {
            if (!res->REMOVE3res_u.resok.dir_wcc.after.attributes_follow) {
                AZLogDebug("[{}] Postop attributes not received for unlink, "
                           "invalidating parent attribute cache", parent_ino);

                /*
                 * Since the post-op attributes are not populated for the parent
                 * directory, invalidate the cache as the attributes may no longer
                 * be valid since unlink() would have changed the parent directory
                 * attributes.
                 */
                parent_inode->invalidate_attribute_cache();
            }

            /*
             * See comment above readdirectory_cache::lookuponly, why we don't
             * need to call UPDATE_INODE_ATTR() to invalidate the
             * readdirectory_cache.
             */
            if (aznfsc_cfg.cache.attr.user.enable) {
                UPDATE_INODE_WCC(parent_inode, res->REMOVE3res_u.resok.dir_wcc);
            } else {
                UPDATE_INODE_ATTR(parent_inode, res->REMOVE3res_u.resok.dir_wcc.after);
            }
        } else {
            AZLogError("[{}/{}] unlink_callback{}: failed with status {}",
                       parent_ino, task->rpc_api->unlink_task.get_file_name(),
                       for_silly_rename ? "(silly_rename)" : "", status);
        }

        if (task->get_fuse_req()) {
            task->reply_error(status);
        } else {
            /*
             * Only when rename_callback() calls inode->release() for silly
             * renamed file, do we pass fuse req as nullptr, as that's not
             * in response to a user request.
             */
            assert(for_silly_rename);
            task->free_rpc_task();
        }

        /*
         * Drop parent directory refcnt taken in rename_callback().
         * Note that we drop the refcnt irrespective of the unlink status.
         * This is done as fuse ignores any error returns from release()
         * which means the inode will be forgotten and hence we must drop
         * the parent directory inode ref which was taken to have a valid
         * parent directory inode till the child inode is present.
         * For jukebox we will retry the rename and drop the parent dir
         * ref when the unlink completes.
         */
        if (for_silly_rename) {
            parent_inode->decref();
        }
    }
}