static int v9fs_receive_response(V9fsProxy *proxy, int type,
                                 int *status, void *response)
{
    int retval;
    ProxyHeader header;
    struct iovec *reply = &proxy->in_iovec;

    *status = 0;
    reply->iov_len = 0;
    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
    if (retval < 0) {
        return retval;
    }
    reply->iov_len = PROXY_HDR_SZ;
    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
    /*
     * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
     * return -ENOBUFS
     */
    if (header.size > PROXY_MAX_IO_SZ) {
        int count;
        while (header.size > 0) {
            count = MIN(PROXY_MAX_IO_SZ, header.size);
            count = socket_read(proxy->sockfd, reply->iov_base, count);
            if (count < 0) {
                return count;
            }
            header.size -= count;
        }
        *status = -ENOBUFS;
        return 0;
    }

    retval = socket_read(proxy->sockfd,
                         reply->iov_base + PROXY_HDR_SZ, header.size);
    if (retval < 0) {
        return retval;
    }
    reply->iov_len += header.size;
    /* there was an error during processing request */
    if (header.type == T_ERROR) {
        int ret;
        ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
        if (ret < 0) {
            *status = ret;
        }
        return 0;
    }

    switch (type) {
    case T_LSTAT: {
        ProxyStat prstat;
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
                                 "qqqdddqqqqqqqqqq", &prstat.st_dev,
                                 &prstat.st_ino, &prstat.st_nlink,
                                 &prstat.st_mode, &prstat.st_uid,
                                 &prstat.st_gid, &prstat.st_rdev,
                                 &prstat.st_size, &prstat.st_blksize,
                                 &prstat.st_blocks,
                                 &prstat.st_atim_sec, &prstat.st_atim_nsec,
                                 &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
                                 &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
        prstat_to_stat(response, &prstat);
        break;
    }
    case T_STATFS: {
        ProxyStatFS prstfs;
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
                                 "qqqqqqqqqqq", &prstfs.f_type,
                                 &prstfs.f_bsize, &prstfs.f_blocks,
                                 &prstfs.f_bfree, &prstfs.f_bavail,
                                 &prstfs.f_files, &prstfs.f_ffree,
                                 &prstfs.f_fsid[0], &prstfs.f_fsid[1],
                                 &prstfs.f_namelen, &prstfs.f_frsize);
        prstatfs_to_statfs(response, &prstfs);
        break;
    }
    case T_READLINK: {
        V9fsString target;
        v9fs_string_init(&target);
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
        strcpy(response, target.data);
        v9fs_string_free(&target);
        break;
    }
    case T_LGETXATTR:
    case T_LLISTXATTR: {
        V9fsString xattr;
        v9fs_string_init(&xattr);
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
        memcpy(response, xattr.data, xattr.size);
        v9fs_string_free(&xattr);
        break;
    }
    case T_GETVERSION:
        proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
        break;
    default:
        return -1;
    }
    if (retval < 0) {
        *status  = retval;
    }
    return 0;
}

static int v9fs_receive_response(V9fsProxy *proxy, int type,
                                 int *status, void *response)
{
    int retval;
    ProxyHeader header;
    struct iovec *reply = &proxy->in_iovec;

    *status = 0;
    reply->iov_len = 0;
    retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ);
    if (retval < 0) {
        return retval;
    }
    reply->iov_len = PROXY_HDR_SZ;
    proxy_unmarshal(reply, 0, "dd", &header.type, &header.size);
    /*
     * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and
     * return -ENOBUFS
     */
    if (header.size > PROXY_MAX_IO_SZ) {
        int count;
        while (header.size > 0) {
            count = MIN(PROXY_MAX_IO_SZ, header.size);
            count = socket_read(proxy->sockfd, reply->iov_base, count);
            if (count < 0) {
                return count;
            }
            header.size -= count;
        }
        *status = -ENOBUFS;
        return 0;
    }

    retval = socket_read(proxy->sockfd,
                         reply->iov_base + PROXY_HDR_SZ, header.size);
    if (retval < 0) {
        return retval;
    }
    reply->iov_len += header.size;
    /* there was an error during processing request */
    if (header.type == T_ERROR) {
        int ret;
        ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status);
        if (ret < 0) {
            *status = ret;
        }
        return 0;
    }

    switch (type) {
    case T_LSTAT: {
        ProxyStat prstat;
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
                                 "qqqdddqqqqqqqqqq", &prstat.st_dev,
                                 &prstat.st_ino, &prstat.st_nlink,
                                 &prstat.st_mode, &prstat.st_uid,
                                 &prstat.st_gid, &prstat.st_rdev,
                                 &prstat.st_size, &prstat.st_blksize,
                                 &prstat.st_blocks,
                                 &prstat.st_atim_sec, &prstat.st_atim_nsec,
                                 &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
                                 &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
        prstat_to_stat(response, &prstat);
        break;
    }
    case T_STATFS: {
        ProxyStatFS prstfs;
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ,
                                 "qqqqqqqqqqq", &prstfs.f_type,
                                 &prstfs.f_bsize, &prstfs.f_blocks,
                                 &prstfs.f_bfree, &prstfs.f_bavail,
                                 &prstfs.f_files, &prstfs.f_ffree,
                                 &prstfs.f_fsid[0], &prstfs.f_fsid[1],
                                 &prstfs.f_namelen, &prstfs.f_frsize);
        prstatfs_to_statfs(response, &prstfs);
        break;
    }
    case T_READLINK: {
        V9fsString target;
        v9fs_string_init(&target);
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target);
        strcpy(response, target.data);
        v9fs_string_free(&target);
        break;
    }
    case T_LGETXATTR:
    case T_LLISTXATTR: {
        V9fsString xattr;
        v9fs_string_init(&xattr);
        retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr);
        memcpy(response, xattr.data, xattr.size);
        v9fs_string_free(&xattr);
        break;
    }
    case T_GETVERSION:
        proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response);
        break;
    default:
        return -1;
    }
    if (retval < 0) {
        *status  = retval;
    }
    return 0;
}
