From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-17.3 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,NICE_REPLY_A,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 81B22C07E95 for ; Tue, 20 Jul 2021 06:08:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 60DFA6113A for ; Tue, 20 Jul 2021 06:08:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233438AbhGTF1z (ORCPT ); Tue, 20 Jul 2021 01:27:55 -0400 Received: from smtp-out1.suse.de ([195.135.220.28]:42360 "EHLO smtp-out1.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232637AbhGTF1x (ORCPT ); Tue, 20 Jul 2021 01:27:53 -0400 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id F421D22340; Tue, 20 Jul 2021 06:08:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1626761311; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Tv7HFGtNxQECSbzMI1pF6MiuuLlRqPrgT5IzSKy6gos=; b=YZYI/JAeYYm6RMu+Hg5pwv2PW9qi2XT0yPgnoPquX3SucbjwpAY6Ze+F6aXD7VIlQprCbu fXO6M2OxDeV2Jgl3TnrhAiTvVWTOy4X8yHUmrWWvsJxpW910iqrP3EJ2DjWwR6gypyOVUJ yjeRh/2BPMjy3nMuQyrB6vvVuQ5iv14= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1626761311; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Tv7HFGtNxQECSbzMI1pF6MiuuLlRqPrgT5IzSKy6gos=; b=crRCOZdXUpRakzCQ/SgfzHF/aKc4vp+Fyylzloa3jLxrNe0DShCX0k5CvGtUDCBK7CKBbO tvbvrPgEQqHTlFCA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id E401F13D57; Tue, 20 Jul 2021 06:08:30 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id b/NcN15o9mAYagAAMHmgww (envelope-from ); Tue, 20 Jul 2021 06:08:30 +0000 To: Sagi Grimberg , Christoph Hellwig Cc: Keith Busch , linux-nvme@lists.infradead.org, Herbert Xu , "David S . Miller" , linux-crypto@vger.kernel.org References: <20210716110428.9727-1-hare@suse.de> <20210716110428.9727-10-hare@suse.de> From: Hannes Reinecke Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication Message-ID: <8fab8ca5-318d-67ab-4a5d-5d4253c22282@suse.de> Date: Tue, 20 Jul 2021 08:08:30 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org On 7/19/21 10:38 PM, Sagi Grimberg wrote: > > > On 7/16/21 4:04 AM, Hannes Reinecke wrote: >> Implement support for NVMe-oF In-Band authentication. This patch >> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash' >> to the 'host' configfs directory. The 'dhchap_key' needs to be >> specified in the format outlined in the base spec. >> Augmented challenge support is not implemented, and concatenation >> with TLS encryption is not supported. >> >> Signed-off-by: Hannes Reinecke >> --- >>   drivers/nvme/target/Kconfig            |  10 + >>   drivers/nvme/target/Makefile           |   1 + >>   drivers/nvme/target/admin-cmd.c        |   4 + >>   drivers/nvme/target/auth.c             | 352 +++++++++++++++++++ >>   drivers/nvme/target/configfs.c         |  71 +++- >>   drivers/nvme/target/core.c             |   8 + >>   drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++ >>   drivers/nvme/target/fabrics-cmd.c      |  30 +- >>   drivers/nvme/target/nvmet.h            |  71 ++++ >>   9 files changed, 1004 insertions(+), 3 deletions(-) >>   create mode 100644 drivers/nvme/target/auth.c >>   create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c >> >> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig >> index 4be2ececbc45..d5656ef1559e 100644 >> --- a/drivers/nvme/target/Kconfig >> +++ b/drivers/nvme/target/Kconfig >> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP >>         devices over TCP. >>           If unsure, say N. >> + >> +config NVME_TARGET_AUTH >> +    bool "NVMe over Fabrics In-band Authentication support" >> +    depends on NVME_TARGET >> +    select CRYPTO_SHA256 >> +    select CRYPTO_SHA512 >> +    help >> +      This enables support for NVMe over Fabrics In-band Authentication >> + >> +      If unsure, say N. >> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile >> index 9837e580fa7e..c66820102493 100644 >> --- a/drivers/nvme/target/Makefile >> +++ b/drivers/nvme/target/Makefile >> @@ -13,6 +13,7 @@ nvmet-y        += core.o configfs.o admin-cmd.o >> fabrics-cmd.o \ >>               discovery.o io-cmd-file.o io-cmd-bdev.o >>   nvmet-$(CONFIG_NVME_TARGET_PASSTHRU)    += passthru.o >>   nvmet-$(CONFIG_BLK_DEV_ZONED)        += zns.o >> +nvmet-$(CONFIG_NVME_TARGET_AUTH)    += fabrics-cmd-auth.o auth.o >>   nvme-loop-y    += loop.o >>   nvmet-rdma-y    += rdma.o >>   nvmet-fc-y    += fc.o >> diff --git a/drivers/nvme/target/admin-cmd.c >> b/drivers/nvme/target/admin-cmd.c >> index 0cb98f2bbc8c..320cefc64ee0 100644 >> --- a/drivers/nvme/target/admin-cmd.c >> +++ b/drivers/nvme/target/admin-cmd.c >> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req) >>         if (nvme_is_fabrics(cmd)) >>           return nvmet_parse_fabrics_cmd(req); >> + >> +    if (unlikely(!nvmet_check_auth_status(req))) >> +        return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; >> + >>       if (nvmet_req_subsys(req)->type == NVME_NQN_DISC) >>           return nvmet_parse_discovery_cmd(req); >>   diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c >> new file mode 100644 >> index 000000000000..00c7d051dfb1 >> --- /dev/null >> +++ b/drivers/nvme/target/auth.c >> @@ -0,0 +1,352 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * NVMe over Fabrics DH-HMAC-CHAP authentication. >> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. >> + * All rights reserved. >> + */ >> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "nvmet.h" >> +#include "../host/auth.h" >> + >> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret) >> +{ >> +    if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1) >> +        return -EINVAL; >> +    if (host->dhchap_key_hash > 3) { >> +        pr_warn("Invalid DH-HMAC-CHAP hash id %d\n", >> +             host->dhchap_key_hash); >> +        return -EINVAL; >> +    } >> +    if (host->dhchap_key_hash > 0) { >> +        /* Validate selected hash algorithm */ >> +        const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash); >> + >> +        if (!crypto_has_shash(hmac, 0, 0)) { >> +            pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac); > > pr_err > >> +            host->dhchap_key_hash = -1; >> +            return -EAGAIN; > > Why EAGAIN? > What else? ENOTSUPP? >> +        } >> +        /* Use this hash as default */ >> +        if (!host->dhchap_hash_id) >> +            host->dhchap_hash_id = host->dhchap_key_hash; > > Why? > Because there is no mechanism how the controller selects the DHCHAP hmac algorithm. The host will send a list of supported hmac algorithms, and the controller has to pick one of them. And as we are sure that the hmac algorithm from the PSK will be supported on the controller I set that as default (if nothing was specified otherwise). >> +    } >> +    host->dhchap_secret = kstrdup(secret, GFP_KERNEL); >> +    if (!host->dhchap_secret) >> +        return -ENOMEM; >> +    /* Default to SHA256 */ >> +    if (!host->dhchap_hash_id) >> +        host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256; > > What is the thought here? > This case is triggered when the user specifies a PSK without a hmac algorithm (ie DHHC-1:00:XXXXX). Then the above mechanism doesn't work, but we still have to specify a default HMAC algorithm such that the selection mechanism can work. >> + >> +    pr_debug("Using hash %s\n", >> +         nvme_auth_hmac_name(host->dhchap_hash_id)); >> +    return 0; >> +} >> + >> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id) >> +{ >> +    int ret = -ENOTSUPP; >> + >> +    if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL) >> +        return 0; >> + >> +    return ret; >> +} >> + >> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req) >> +{ >> +    int ret = 0; >> +    struct nvmet_host_link *p; >> +    struct nvmet_host *host = NULL; >> +    const char *hash_name; >> + >> +    down_read(&nvmet_config_sem); >> +    if (ctrl->subsys->type == NVME_NQN_DISC) >> +        goto out_unlock; >> + >> +    list_for_each_entry(p, &ctrl->subsys->hosts, entry) { >> +        pr_debug("check %s\n", nvmet_host_name(p->host)); >> +        if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn)) >> +            continue; >> +        host = p->host; >> +        break; >> +    } >> +    if (!host) { >> +        pr_debug("host %s not found\n", ctrl->hostnqn); >> +        ret = -EPERM; > > I think you should propogate the nvme status code instead... > I _thought_ it got translated into one; but yeah, can do. >> +        goto out_unlock; >> +    } >> +    if (!host->dhchap_secret) { >> +        pr_debug("No authentication provided\n"); >> +        goto out_unlock; >> +    } >> + >> +    hash_name = nvme_auth_hmac_name(host->dhchap_hash_id); >> +    if (!hash_name) { > > Can this actually happen? > Good question. I don't think so; will be changing it into a WARN_ON(). >> +        pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id); > > warning, not debug. > See above. Yes. >> +        ret = -EINVAL; >> +        goto out_unlock; >> +    } >> +    ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0, >> +                         CRYPTO_ALG_ALLOCATES_MEMORY); >> +    if (IS_ERR(ctrl->shash_tfm)) { >> +        pr_debug("failed to allocate shash %s\n", hash_name); >> +        ret = PTR_ERR(ctrl->shash_tfm); >> +        ctrl->shash_tfm = NULL; >> +        goto out_unlock; >> +    } >> + >> +    ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret, >> +                            &ctrl->dhchap_key_len); >> +    if (IS_ERR(ctrl->dhchap_key)) { >> +        pr_debug("failed to extract host key, error %d\n", ret); >> +        ret = PTR_ERR(ctrl->dhchap_key); >> +        ctrl->dhchap_key = NULL; >> +        goto out_free_hash; >> +    } >> +    if (host->dhchap_key_hash) { >> +        struct crypto_shash *key_tfm; >> + >> +        hash_name = nvme_auth_hmac_name(host->dhchap_key_hash); >> +        key_tfm = crypto_alloc_shash(hash_name, 0, 0); >> +        if (IS_ERR(key_tfm)) { >> +            ret = PTR_ERR(key_tfm); >> +            goto out_free_hash; >> +        } else { >> +            SHASH_DESC_ON_STACK(shash, key_tfm); >> + >> +            shash->tfm = key_tfm; >> +            ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key, >> +                          ctrl->dhchap_key_len); >> +            crypto_shash_init(shash); >> +            crypto_shash_update(shash, ctrl->subsys->subsysnqn, >> +                        strlen(ctrl->subsys->subsysnqn)); >> +            crypto_shash_update(shash, "NVMe-over-Fabrics", 17); >> +            crypto_shash_final(shash, ctrl->dhchap_key); >> +            crypto_free_shash(key_tfm); >> +        } >> +    } >> +    pr_debug("%s: using key %*ph\n", __func__, >> +         (int)ctrl->dhchap_key_len, ctrl->dhchap_key); >> +    ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key, >> +                  ctrl->dhchap_key_len); >> +out_free_hash: >> +    if (ret) { >> +        if (ctrl->dhchap_key) { >> +            kfree(ctrl->dhchap_key); >> +            ctrl->dhchap_key = NULL; >> +        } >> +        crypto_free_shash(ctrl->shash_tfm); >> +        ctrl->shash_tfm = NULL; >> +    } >> +out_unlock: >> +    up_read(&nvmet_config_sem); >> + >> +    return ret; >> +} >> + >> +void nvmet_auth_sq_free(struct nvmet_sq *sq) >> +{ >> +    if (sq->dhchap_c1) >> +        kfree(sq->dhchap_c1); > > just kfree, no need to if > Yeah. >> +    if (sq->dhchap_c2) >> +        kfree(sq->dhchap_c2); >> +    if (sq->dhchap_skey) >> +        kfree(sq->dhchap_skey); >> +} >> + >> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl) > > Shouldn't this be nvmet_destroy_auth? reset indicates > it can be reused again... > Oh, it should. I've coded nvmet_destroy_auth() pretty late in the game, so I missed that one. >> +{ >> +    if (ctrl->shash_tfm) { >> +        crypto_free_shash(ctrl->shash_tfm); >> +        ctrl->shash_tfm = NULL; >> +    } >> +    if (ctrl->dh_tfm) { >> +        crypto_free_kpp(ctrl->dh_tfm); >> +        ctrl->dh_tfm = NULL; >> +    } >> +    if (ctrl->dhchap_key) { >> +        kfree(ctrl->dhchap_key); >> +        ctrl->dhchap_key = NULL; >> +    } >> +} >> + >> +bool nvmet_check_auth_status(struct nvmet_req *req) >> +{ >> +    if (req->sq->ctrl->shash_tfm && >> +        !req->sq->authenticated) >> +        return false; >> +    return true; >> +} >> + >> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, >> +             unsigned int shash_len) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm); >> +    u8 *challenge = req->sq->dhchap_c1; >> +    u8 buf[4]; >> +    int ret; >> + >> +    if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) { >> +        ret = -ENOTSUPP; >> +        goto out; >> +    } >> + >> +    shash->tfm = ctrl->shash_tfm; >> +    ret = crypto_shash_init(shash); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, challenge, shash_len); >> +    if (ret) >> +        goto out; >> +    put_unaligned_le32(req->sq->dhchap_s1, buf); >> +    ret = crypto_shash_update(shash, buf, 4); >> +    if (ret) >> +        goto out; >> +    put_unaligned_le16(req->sq->dhchap_tid, buf); >> +    ret = crypto_shash_update(shash, buf, 2); >> +    if (ret) >> +        goto out; >> +    memset(buf, 0, 4); >> +    ret = crypto_shash_update(shash, buf, 1); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, "HostHost", 8); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, ctrl->hostnqn, >> strlen(ctrl->hostnqn)); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, buf, 1); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, ctrl->subsysnqn, >> +                  strlen(ctrl->subsysnqn)); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_final(shash, response); >> +out: >> +    if (challenge != req->sq->dhchap_c1) >> +        kfree(challenge); > > What about actually failing? > Ho-hum. Of course. >> +    return 0; >> +} >> + >> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, >> +             unsigned int shash_len) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm); >> +    u8 *challenge = req->sq->dhchap_c2; >> +    u8 buf[4]; >> +    int ret; >> + >> +    pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__, >> +         ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid); >> +    pr_debug("%s: ctrl %d challenge %*ph\n", __func__, >> +         ctrl->cntlid, shash_len, req->sq->dhchap_c2); >> +    pr_debug("%s: ctrl %d subsysnqn %s\n", __func__, >> +         ctrl->cntlid, ctrl->subsysnqn); >> +    pr_debug("%s: ctrl %d hostnqn %s\n", __func__, >> +         ctrl->cntlid, ctrl->hostnqn); >> + >> +    if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) { >> +        ret = -ENOTSUPP; >> +        goto out; >> +    } >> + >> +    shash->tfm = ctrl->shash_tfm; >> +    ret = crypto_shash_init(shash); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, challenge, shash_len); >> +    if (ret) >> +        goto out; >> +    put_unaligned_le32(req->sq->dhchap_s2, buf); >> +    ret = crypto_shash_update(shash, buf, 4); >> +    if (ret) >> +        goto out; >> +    put_unaligned_le16(req->sq->dhchap_tid, buf); >> +    ret = crypto_shash_update(shash, buf, 2); >> +    if (ret) >> +        goto out; >> +    memset(buf, 0, 4); >> +    ret = crypto_shash_update(shash, buf, 1); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, "Controller", 10); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, ctrl->subsysnqn, >> +                strlen(ctrl->subsysnqn)); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, buf, 1); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_update(shash, ctrl->hostnqn, >> strlen(ctrl->hostnqn)); >> +    if (ret) >> +        goto out; >> +    ret = crypto_shash_final(shash, response); >> +out: >> +    if (challenge != req->sq->dhchap_c2) >> +        kfree(challenge); >> +    return 0; >> +} >> + >> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, >> +                u8 *pkey, int pkey_size) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    struct kpp_request *kpp_req; >> +    struct crypto_wait wait; >> +    struct scatterlist src, dst; >> +    int ret; >> + >> +    req->sq->dhchap_skey_len = >> +        nvme_auth_dhgroup_privkey_size(ctrl->dh_gid); >> +    req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, >> GFP_KERNEL); >> +    if (!req->sq->dhchap_skey) >> +        return -ENOMEM; >> +    kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL); >> +    if (!kpp_req) { >> +        kfree(req->sq->dhchap_skey); >> +        req->sq->dhchap_skey = NULL; >> +        return -ENOMEM; >> +    } >> + >> +    pr_debug("%s: host public key %*ph\n", __func__, >> +         (int)pkey_size, pkey); >> +    crypto_init_wait(&wait); >> +    sg_init_one(&src, pkey, pkey_size); >> +    kpp_request_set_input(kpp_req, &src, pkey_size); >> +    sg_init_one(&dst, req->sq->dhchap_skey, >> +        req->sq->dhchap_skey_len); >> +    kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len); >> +    kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG, >> +                 crypto_req_done, &wait); >> + >> +    ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req), >> &wait); >> +    kpp_request_free(kpp_req); >> +    if (ret) >> +        pr_debug("failed to compute shared secred, err %d\n", ret); >> +    else >> +        pr_debug("%s: shared secret %*ph\n", __func__, >> +             (int)req->sq->dhchap_skey_len, >> +             req->sq->dhchap_skey); >> + >> +    return ret; >> +} >> diff --git a/drivers/nvme/target/configfs.c >> b/drivers/nvme/target/configfs.c >> index 273555127188..e0760911a761 100644 >> --- a/drivers/nvme/target/configfs.c >> +++ b/drivers/nvme/target/configfs.c >> @@ -11,8 +11,13 @@ >>   #include >>   #include >>   #include >> +#include >> +#include >>     #include "nvmet.h" >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +#include "../host/auth.h" >> +#endif >>     static const struct config_item_type nvmet_host_type; >>   static const struct config_item_type nvmet_subsys_type; >> @@ -1656,10 +1661,71 @@ static const struct config_item_type >> nvmet_ports_type = { >>   static struct config_group nvmet_subsystems_group; >>   static struct config_group nvmet_ports_group; >>   -static void nvmet_host_release(struct config_item *item) >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item, >> +        char *page) >> +{ >> +    u8 *dhchap_secret = to_host(item)->dhchap_secret; >> + >> +    if (!dhchap_secret) >> +        return sprintf(page, "\n"); >> +    return sprintf(page, "%s\n", dhchap_secret); >> +} >> + >> +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item, >> +        const char *page, size_t count) >>   { >>       struct nvmet_host *host = to_host(item); >> +    int ret; >>   +    ret = nvmet_auth_set_host_key(host, page); >> +    if (ret < 0) >> +        return ret; >> +    return count; >> +} >> + >> +CONFIGFS_ATTR(nvmet_host_, dhchap_key); >> + >> +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item, >> +        char *page) >> +{ >> +    struct nvmet_host *host = to_host(item); >> +    const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id); >> + >> +    return sprintf(page, "%s\n", hash_name ? hash_name : "none"); >> +} >> + >> +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item, >> +        const char *page, size_t count) >> +{ >> +    struct nvmet_host *host = to_host(item); >> +    int hmac_id; >> + >> +    hmac_id = nvme_auth_hmac_id(page); >> +    if (hmac_id < 0) >> +        return -EINVAL; >> +    if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0)) >> +        return -ENOTSUPP; >> +    host->dhchap_hash_id = hmac_id; >> +    return count; >> +} >> + >> +CONFIGFS_ATTR(nvmet_host_, dhchap_hash); >> + >> +static struct configfs_attribute *nvmet_host_attrs[] = { >> +    &nvmet_host_attr_dhchap_key, >> +    &nvmet_host_attr_dhchap_hash, >> +    NULL, >> +}; >> +#endif /* CONFIG_NVME_TARGET_AUTH */ >> + >> +static void nvmet_host_release(struct config_item *item) >> +{ >> +    struct nvmet_host *host = to_host(item); >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +    if (host->dhchap_secret) >> +        kfree(host->dhchap_secret); > > No need for if condition. > Ok. >> +#endif >>       kfree(host); >>   } >>   @@ -1669,6 +1735,9 @@ static struct configfs_item_operations >> nvmet_host_item_ops = { >>     static const struct config_item_type nvmet_host_type = { >>       .ct_item_ops        = &nvmet_host_item_ops, >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +    .ct_attrs        = nvmet_host_attrs, >> +#endif >>       .ct_owner        = THIS_MODULE, >>   }; >>   diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c >> index 163f7dc1a929..b5d7971f566b 100644 >> --- a/drivers/nvme/target/core.c >> +++ b/drivers/nvme/target/core.c >> @@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq) >>       wait_for_completion(&sq->confirm_done); >>       wait_for_completion(&sq->free_done); >>       percpu_ref_exit(&sq->ref); >> +    nvmet_auth_sq_free(sq); >>         if (ctrl) { >>           /* >> @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req) >>                  req->cmd->common.opcode, req->sq->qid); >>           return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR; >>       } >> + >> +    if (unlikely(!nvmet_check_auth_status(req))) { >> +        pr_warn("qid %d not authenticated\n", req->sq->qid); >> +        return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; >> +    } >>       return 0; >>   } >>   @@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref) >>       flush_work(&ctrl->async_event_work); >>       cancel_work_sync(&ctrl->fatal_err_work); >>   +    nvmet_reset_auth(ctrl); >> + >>       ida_simple_remove(&cntlid_ida, ctrl->cntlid); >>         nvmet_async_events_free(ctrl); >> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c >> b/drivers/nvme/target/fabrics-cmd-auth.c >> new file mode 100644 >> index 000000000000..962f9f5e9d89 >> --- /dev/null >> +++ b/drivers/nvme/target/fabrics-cmd-auth.c >> @@ -0,0 +1,460 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling. >> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. >> + * All rights reserved. >> + */ >> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt >> +#include >> +#include >> +#include >> +#include >> +#include "nvmet.h" >> +#include "../host/auth.h" >> + >> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req) >> +{ >> +    /* Initialize in-band authentication */ >> +    req->sq->authenticated = false; >> +    req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; >> +    req->cqe->result.u32 |= 0x2 << 16; > > Can you add a define for this: NVME_CONNECT_AUTHREQ_INBAND > Sure. >> +} >> + >> +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    struct nvmf_auth_dhchap_negotiate_data *data = d; >> +    int i, hash_id, null_dh = -1; >> + >> +    pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d >> halen %d dhlen %d\n", >> +         __func__, ctrl->cntlid, req->sq->qid, >> +         data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid, >> +         data->auth_protocol[0].dhchap.halen, >> +         data->auth_protocol[0].dhchap.dhlen); >> +    req->sq->dhchap_tid = le16_to_cpu(data->t_id); >> +    if (data->sc_c) >> +        return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH; >> + >> +    if (data->napd != 1) >> +        return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; >> + >> +    if (data->auth_protocol[0].dhchap.authid != 0x01) >> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; >> + >> +    hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm)); >> +    for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) { >> +        pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n", >> +             __func__, ctrl->cntlid, req->sq->qid, >> +             data->auth_protocol[0].dhchap.idlist[i], hash_id); >> +        if (hash_id != data->auth_protocol[0].dhchap.idlist[i]) >> +            continue; >> +        req->sq->dhchap_hash_id = hash_id; >> +        req->sq->dhchap_hash_len = >> crypto_shash_digestsize(ctrl->shash_tfm); >> +        break; >> +    } >> +    if (req->sq->dhchap_hash_id == 0) { >> +        pr_debug("%s: ctrl %d qid %d: no usable hash found\n", >> +             __func__, ctrl->cntlid, req->sq->qid); >> +        return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; >> +    } >> + >> +    for (i = data->auth_protocol[0].dhchap.halen; >> +         i < data->auth_protocol[0].dhchap.halen + >> +             data->auth_protocol[0].dhchap.dhlen; i++) { >> +        int dhgid = data->auth_protocol[0].dhchap.idlist[i]; >> + >> +        if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) { >> +            null_dh = dhgid; >> +            continue; >> +        } >> +        if (nvmet_setup_dhgroup(ctrl, dhgid) == 0) >> +            break; >> +    } >> +    if (!ctrl->dh_tfm && null_dh < 0) { >> +        pr_debug("%s: ctrl %d qid %d: no DH group selected\n", >> +             __func__, ctrl->cntlid, req->sq->qid); >> +        return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; >> +    } >> +    if (ctrl->dh_gid == -1) { >> +        ctrl->dh_gid = null_dh; >> +        ctrl->dh_tfm = NULL; >> +    } >> +    pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n", >> +         __func__, ctrl->cntlid, req->sq->qid, >> +         nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid); >> +    return 0; >> +} >> + >> +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    struct nvmf_auth_dhchap_reply_data *data = d; >> +    u8 *response; >> + >> +    pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n", >> +         __func__, ctrl->cntlid, req->sq->qid, >> +         data->hl, data->cvalid, data->dhvlen); >> +    if (data->hl != req->sq->dhchap_hash_len) >> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; >> + >> +    if (data->dhvlen) { >> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; >> +    } >> + >> +    response = kmalloc(data->hl, GFP_KERNEL); >> +    if (!response) >> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED; >> + >> +    if (nvmet_auth_host_hash(req, response, data->hl) < 0) { >> +        pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n", >> +             ctrl->cntlid, req->sq->qid); >> +        kfree(response); >> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED; >> +    } >> + >> +    if (memcmp(data->rval, response, data->hl)) { >> +        pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n", >> +            ctrl->cntlid, req->sq->qid); >> +        kfree(response); >> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED; >> +    } >> +    kfree(response); >> +    pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n", >> +        ctrl->cntlid, req->sq->qid); >> +    if (data->cvalid) { >> +        req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL); >> +        if (!req->sq->dhchap_c2) >> +            return NVME_AUTH_DHCHAP_FAILURE_FAILED; >> +        memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl); >> + >> +        pr_debug("ctrl %d qid %d challenge %*ph\n", >> +             ctrl->cntlid, req->sq->qid, data->hl, >> +             req->sq->dhchap_c2); >> +        req->sq->dhchap_s2 = le32_to_cpu(data->seqnum); >> +    } else >> +        req->sq->dhchap_c2 = NULL; >> + >> +    return 0; >> +} >> + >> +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d) >> +{ >> +    struct nvmf_auth_dhchap_failure_data *data = d; >> + >> +    return data->reason_code_explanation; >> +} >> + >> +void nvmet_execute_auth_send(struct nvmet_req *req) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    struct nvmf_auth_dhchap_success2_data *data; >> +    void *d; >> +    u32 tl; >> +    u16 status = 0; >> + >> +    if (req->cmd->auth_send.secp != >> NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_send_command, secp); >> +        goto done; >> +    } >> +    if (req->cmd->auth_send.spsp0 != 0x01) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_send_command, spsp0); >> +        goto done; >> +    } >> +    if (req->cmd->auth_send.spsp1 != 0x01) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_send_command, spsp1); >> +        goto done; >> +    } >> +    tl = le32_to_cpu(req->cmd->auth_send.tl); >> +    if (!tl) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_send_command, tl); >> +        goto done; >> +    } >> +    if (!nvmet_check_transfer_len(req, tl)) { >> +        pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl); >> +        return; >> +    } >> + >> +    d = kmalloc(tl, GFP_KERNEL); >> +    if (!d) { >> +        status = NVME_SC_INTERNAL; >> +        goto done; >> +    } >> + >> +    status = nvmet_copy_from_sgl(req, 0, d, tl); >> +    if (status) { >> +        kfree(d); >> +        goto done; >> +    } >> + > > This whole block below should move to something like > nvmet_process_auth_send_data() > Ok. >> +    data = d; >> +    pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__, >> +         ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id, >> +         req->sq->dhchap_step); >> +    if (data->auth_type != NVME_AUTH_COMMON_MESSAGES && >> +        data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) { >> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +        req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +    } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) { >> +        if (data->auth_id != req->sq->dhchap_step) { >> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +            req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +        } else if (data->auth_id != >> NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) { >> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +            req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +        } else { >> +            /* Validate negotiation parameters */ >> +            status = nvmet_auth_negotiate(req, d); >> +            if (status == 0) >> +                req->sq->dhchap_step = >> NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE; >> +            else { >> +                req->sq->dhchap_step = >> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +                req->sq->dhchap_status = status; >> +                status = 0; >> +            } >> +        } >> +    } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) { >> +        if (data->auth_id != req->sq->dhchap_step) { >> +            pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n", >> +                 __func__, ctrl->cntlid, req->sq->qid, >> +                 data->auth_id, req->sq->dhchap_step); >> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +            req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +        } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) { >> +            pr_debug("%s: ctrl %d qid %d invalid transaction %d >> (expected %d)\n", >> +                 __func__, ctrl->cntlid, req->sq->qid, >> +                 le16_to_cpu(data->t_id), >> +                 req->sq->dhchap_tid); >> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +            req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD; >> +        } else { >> +            switch (data->auth_id) { >> +            case NVME_AUTH_DHCHAP_MESSAGE_REPLY: >> +                status = nvmet_auth_reply(req, d); >> +                if (status == 0) >> +                    req->sq->dhchap_step = >> NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; >> +                else { >> +                    req->sq->dhchap_step = >> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +                    req->sq->dhchap_status = status; >> +                    status = 0; >> +                } >> +                break; >> +            case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2: >> +                req->sq->authenticated = true; >> +                pr_debug("%s: ctrl %d qid %d authenticated\n", >> +                     __func__, ctrl->cntlid, req->sq->qid); >> +                break; >> +            case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2: >> +                status = nvmet_auth_failure2(req, d); >> +                if (status) { >> +                    pr_warn("ctrl %d qid %d: DH-HMAC-CHAP negotiation >> failed (%d)\n", >> +                        ctrl->cntlid, req->sq->qid, >> +                        status); >> +                    req->sq->dhchap_status = status; >> +                    status = 0; >> +                } >> +                break; >> +            default: >> +                req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +                req->sq->dhchap_step = >> NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; >> +                break; >> +            } >> +        } >> +    } else { >> +        req->sq->dhchap_status = >> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE; >> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; >> +    } >> +    kfree(d); >> +done: >> +    pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__, >> +         ctrl->cntlid, req->sq->qid, >> +         req->sq->dhchap_status, req->sq->dhchap_step); >> +    if (status) >> +        pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n", >> +             __func__, ctrl->cntlid, req->sq->qid, >> +             status, req->error_loc); >> +    req->cqe->result.u64 = 0; >> +    nvmet_req_complete(req, status); >> +    if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 && >> +        req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) >> +        return; >> +    /* Final states, clear up variables */ >> +    kfree(req->sq->dhchap_c1); >> +    kfree(req->sq->dhchap_c2); >> +    if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) >> +        nvmet_ctrl_fatal_error(ctrl); >> +} >> + >> +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al) > > nvmet_auth_set_challenge > Ok. >> +{ >> +    struct nvmf_auth_dhchap_challenge_data *data = d; >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    int ret = 0; >> +    int data_size = sizeof(*d) + req->sq->dhchap_hash_len; >> + >> +    if (al < data_size) { >> +        pr_debug("%s: buffer too small (al %d need %d)\n", __func__, >> +             al, data_size); >> +        return -EINVAL; >> +    } >> +    memset(data, 0, data_size); >> +    req->sq->dhchap_s1 = ctrl->dhchap_seqnum++; >> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; >> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE; >> +    data->t_id = cpu_to_le16(req->sq->dhchap_tid); >> +    data->hashid = req->sq->dhchap_hash_id; >> +    data->hl = req->sq->dhchap_hash_len; >> +    data->seqnum = cpu_to_le32(req->sq->dhchap_s1); >> +    req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL); >> +    if (!req->sq->dhchap_c1) >> +        return -ENOMEM; >> +    get_random_bytes(req->sq->dhchap_c1, data->hl); >> +    memcpy(data->cval, req->sq->dhchap_c1, data->hl); >> +    pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen >> %d\n", >> +         __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, >> +         req->sq->dhchap_tid, data->hl, data->dhvlen); >> +    return ret; >> +} >> + >> +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al) >> +{ >> +    struct nvmf_auth_dhchap_success1_data *data = d; >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> + >> +    WARN_ON(al < sizeof(*data)); >> +    memset(data, 0, sizeof(*data)); >> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; >> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; >> +    data->t_id = cpu_to_le16(req->sq->dhchap_tid); >> +    data->hl = req->sq->dhchap_hash_len; >> +    if (req->sq->dhchap_c2) { >> +        if (nvmet_auth_ctrl_hash(req, data->rval, data->hl)) >> +            return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; >> +        data->rvalid = 1; >> +        pr_debug("ctrl %d qid %d response %*ph\n", >> +             ctrl->cntlid, req->sq->qid, data->hl, data->rval); >> +    } >> +    return 0; >> +} >> + >> +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) >> +{ >> +    struct nvmf_auth_dhchap_failure_data *data = d; >> + >> +    WARN_ON(al < sizeof(*data)); >> +    data->auth_type = NVME_AUTH_COMMON_MESSAGES; >> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +    data->t_id = cpu_to_le32(req->sq->dhchap_tid); >> +    data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED; >> +    data->reason_code_explanation = req->sq->dhchap_status; >> +} >> + >> +void nvmet_execute_auth_receive(struct nvmet_req *req) >> +{ >> +    struct nvmet_ctrl *ctrl = req->sq->ctrl; >> +    void *d; >> +    u32 al; >> +    u16 status = 0; >> + >> +    if (req->cmd->auth_receive.secp != >> NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_receive_command, secp); >> +        goto done; >> +    } >> +    if (req->cmd->auth_receive.spsp0 != 0x01) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_receive_command, spsp0); >> +        goto done; >> +    } >> +    if (req->cmd->auth_receive.spsp1 != 0x01) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_receive_command, spsp1); >> +        goto done; >> +    } >> +    al = le32_to_cpu(req->cmd->auth_receive.al); >> +    if (!al) { >> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; >> +        req->error_loc = >> +            offsetof(struct nvmf_auth_receive_command, al); >> +        goto done; >> +    } >> +    if (!nvmet_check_transfer_len(req, al)) { >> +        pr_debug("%s: transfer length mismatch (%u)\n", __func__, al); >> +        return; >> +    } >> + >> +    d = kmalloc(al, GFP_KERNEL); >> +    if (!d) { >> +        status = NVME_SC_INTERNAL; >> +        goto done; >> +    } >> +    pr_debug("%s: ctrl %d qid %d step %x\n", __func__, >> +         ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); >> +    switch (req->sq->dhchap_step) { >> +    case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: >> +        status = nvmet_auth_challenge(req, d, al); >> +        if (status < 0) { >> +            pr_warn("ctrl %d qid %d: challenge error (%d)\n", >> +                ctrl->cntlid, req->sq->qid, status); >> +            status = NVME_SC_INTERNAL; >> +            break; >> +        } >> +        if (status) { >> +            req->sq->dhchap_status = status; >> +            nvmet_auth_failure1(req, d, al); >> +            pr_warn("ctrl %d qid %d: challenge status (%x)\n", >> +                ctrl->cntlid, req->sq->qid, >> +                req->sq->dhchap_status); >> +            status = 0; >> +            break; >> +        } >> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY; >> +        break; >> +    case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: >> +        status = nvmet_auth_success1(req, d, al); >> +        if (status) { >> +            req->sq->dhchap_status = status; >> +            nvmet_auth_failure1(req, d, al); >> +            pr_warn("ctrl %d qid %d: success1 status (%x)\n", >> +                ctrl->cntlid, req->sq->qid, >> +                req->sq->dhchap_status); >> +            break; >> +        } >> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2; >> +        break; >> +    case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1: >> +        nvmet_auth_failure1(req, d, al); >> +        pr_warn("ctrl %d qid %d failure1 (%x)\n", >> +            ctrl->cntlid, req->sq->qid, req->sq->dhchap_status); >> +        break; >> +    default: >> +        pr_warn("ctrl %d qid %d unhandled step (%d)\n", >> +            ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); >> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; >> +        req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED; >> +        nvmet_auth_failure1(req, d, al); >> +        status = 0; >> +        break; >> +    } >> + >> +    status = nvmet_copy_to_sgl(req, 0, d, al); >> +    kfree(d); >> +done: >> +    req->cqe->result.u64 = 0; >> +    nvmet_req_complete(req, status); >> +    if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { >> +        kfree(req->sq->dhchap_c1); >> +        kfree(req->sq->dhchap_c2); >> +        nvmet_ctrl_fatal_error(ctrl); >> +    } >> +} >> diff --git a/drivers/nvme/target/fabrics-cmd.c >> b/drivers/nvme/target/fabrics-cmd.c >> index 7d0f3523fdab..53fb853cd8fe 100644 >> --- a/drivers/nvme/target/fabrics-cmd.c >> +++ b/drivers/nvme/target/fabrics-cmd.c >> @@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req) >>       case nvme_fabrics_type_property_get: >>           req->execute = nvmet_execute_prop_get; >>           break; >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +    case nvme_fabrics_type_auth_send: >> +        req->execute = nvmet_execute_auth_send; >> +        break; >> +    case nvme_fabrics_type_auth_receive: >> +        req->execute = nvmet_execute_auth_receive; >> +        break; >> +#endif >>       default: >>           pr_debug("received unknown capsule type 0x%x\n", >>               cmd->fabrics.fctype); >> @@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct >> nvmet_req *req) >>       struct nvmf_connect_data *d; >>       struct nvmet_ctrl *ctrl = NULL; >>       u16 status = 0; >> +    int ret; >>         if (!nvmet_check_transfer_len(req, sizeof(struct >> nvmf_connect_data))) >>           return; >> @@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct >> nvmet_req *req) >>         uuid_copy(&ctrl->hostid, &d->hostid); >>   +    ret = nvmet_setup_auth(ctrl, req); >> +    if (ret < 0) { >> +        pr_err("Failed to setup authentication, error %d\n", ret); >> +        nvmet_ctrl_put(ctrl); >> +        if (ret == -EPERM) >> +            status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR); >> +        else >> +            status = NVME_SC_INTERNAL; >> +        goto out; >> +    } >> + >>       status = nvmet_install_queue(ctrl, req); >>       if (status) { >>           nvmet_ctrl_put(ctrl); >>           goto out; >>       } >>   -    pr_info("creating controller %d for subsystem %s for NQN %s%s.\n", >> +    pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n", >>           ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, >> -        ctrl->pi_support ? " T10-PI is enabled" : ""); >> +        ctrl->pi_support ? " T10-PI is enabled" : "", >> +        nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : ""); >>       req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid); >>   +    if (nvmet_has_auth(ctrl)) >> +        nvmet_init_auth(ctrl, req); >>   out: >>       kfree(d); >>   complete: >> @@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct >> nvmet_req *req) >>       } >>         pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid); >> +    req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid); > > Is this related to the patch? > Have to check. I thought it did, but now that you mention it... >> +    if (nvmet_has_auth(ctrl)) >> +        nvmet_init_auth(ctrl, req); >>     out: >>       kfree(d); >> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h >> index 06dd3d537f07..ef8815e137d7 100644 >> --- a/drivers/nvme/target/nvmet.h >> +++ b/drivers/nvme/target/nvmet.h >> @@ -108,6 +108,20 @@ struct nvmet_sq { >>       u16            size; >>       u32            sqhd; >>       bool            sqhd_disabled; >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +    bool            authenticated; >> +    u16            dhchap_tid; >> +    u16            dhchap_status; >> +    int            dhchap_step; >> +    u8            dhchap_hash_id; >> +    u8            dhchap_hash_len; >> +    u8            *dhchap_c1; >> +    u8            *dhchap_c2; >> +    u32            dhchap_s1; >> +    u32            dhchap_s2; >> +    u8            *dhchap_skey; >> +    int            dhchap_skey_len; >> +#endif >>       struct completion    free_done; >>       struct completion    confirm_done; >>   }; >> @@ -209,6 +223,15 @@ struct nvmet_ctrl { >>       u64            err_counter; >>       struct nvme_error_slot    slots[NVMET_ERROR_LOG_SLOTS]; >>       bool            pi_support; >> +#ifdef CONFIG_NVME_TARGET_AUTH >> +    u32            dhchap_seqnum; >> +    u8            *dhchap_key; >> +    size_t            dhchap_key_len; >> +    struct crypto_shash    *shash_tfm; >> +    struct crypto_kpp    *dh_tfm; >> +    u32            dh_gid; >> +    u32            dh_keysize; >> +#endif >>   }; >>     struct nvmet_subsys { >> @@ -270,6 +293,10 @@ static inline struct nvmet_subsys >> *namespaces_to_subsys( >>     struct nvmet_host { >>       struct config_group    group; >> +    u8            *dhchap_secret; >> +    u8            dhchap_key_hash; >> +    u8            dhchap_hash_id; >> +    u8            dhchap_dhgroup_id; >>   }; >>     static inline struct nvmet_host *to_host(struct config_item *item) >> @@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct >> nvmet_req *req, struct bio *bio) >>           bio_put(bio); >>   } >>   +#ifdef CONFIG_NVME_TARGET_AUTH >> +void nvmet_execute_auth_send(struct nvmet_req *req); >> +void nvmet_execute_auth_receive(struct nvmet_req *req); >> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char >> *secret); >> +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash); >> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req); >> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req); >> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl); >> +void nvmet_auth_sq_free(struct nvmet_sq *sq); >> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id); >> +bool nvmet_check_auth_status(struct nvmet_req *req); >> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, >> +             unsigned int hash_len); >> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, >> +             unsigned int hash_len); >> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl) >> +{ >> +    return ctrl->shash_tfm != NULL; >> +} >> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req, >> +                u8 *buf, int buf_size); >> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, >> +                u8 *buf, int buf_size); >> +#else >> +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl, >> +                   struct nvmet_req *req) >> +{ >> +    return 0; >> +} >> +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl, >> +                   struct nvmet_req *req) {}; >> +static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {}; >> +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {}; >> +static inline bool nvmet_check_auth_status(struct nvmet_req *req) >> +{ >> +    return true; >> +} >> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl) >> +{ >> +    return false; >> +} >> +static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) { >> return NULL; } >> +#endif >> + >>   #endif /* _NVMET_H */ >> > Thanks for the review! Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@suse.de +49 911 74053 688 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), GF: Felix Imendörffer From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,NICE_REPLY_A,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D9317C07E95 for ; Tue, 20 Jul 2021 06:09:01 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 589CD6113A for ; Tue, 20 Jul 2021 06:09:01 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 589CD6113A Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=suse.de Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-nvme-bounces+linux-nvme=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:MIME-Version:Date: Message-ID:Subject:From:References:Cc:To:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=tqt7J/Fei+ZMBB0P1C8fR8p9vDvHAsraw4LoEUOt4pM=; b=CZwmHwlr7aqKxwtl8VO/AaUKFX dM3UT0SDCHA5kGssNIcvJENZp89ot1yq0mhVnNaQdbWmf6St2v/uwu1ufOkBLnKHb4lT9egQlxYEK NArCjxpoGLfS/G75u3/fE8dhAl2EpUbKy5mGNGQNp9DRMT3yMG+rcz7b5tRAybmWKm77oSDnH3IFk 9xTCaXLB4S4Km9PN8F//KRH574OXq1qEig7q8TsBZoEhaNYERpRLda+pef6huIcf6Z4ikVqTyei+l Nhy8UmjoEwryl05WdrqMijpemeG8rEcGR6IKQHedQHPxQoaOE0EPqQFdqry+xQBaB5VGMp2CM17S2 vClAJ3uA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1m5ivn-00BxG6-VF; Tue, 20 Jul 2021 06:08:39 +0000 Received: from smtp-out1.suse.de ([195.135.220.28]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1m5ivg-00BxF6-TJ for linux-nvme@lists.infradead.org; Tue, 20 Jul 2021 06:08:38 +0000 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id F421D22340; Tue, 20 Jul 2021 06:08:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1626761311; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Tv7HFGtNxQECSbzMI1pF6MiuuLlRqPrgT5IzSKy6gos=; b=YZYI/JAeYYm6RMu+Hg5pwv2PW9qi2XT0yPgnoPquX3SucbjwpAY6Ze+F6aXD7VIlQprCbu fXO6M2OxDeV2Jgl3TnrhAiTvVWTOy4X8yHUmrWWvsJxpW910iqrP3EJ2DjWwR6gypyOVUJ yjeRh/2BPMjy3nMuQyrB6vvVuQ5iv14= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1626761311; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Tv7HFGtNxQECSbzMI1pF6MiuuLlRqPrgT5IzSKy6gos=; b=crRCOZdXUpRakzCQ/SgfzHF/aKc4vp+Fyylzloa3jLxrNe0DShCX0k5CvGtUDCBK7CKBbO tvbvrPgEQqHTlFCA== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id E401F13D57; Tue, 20 Jul 2021 06:08:30 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id b/NcN15o9mAYagAAMHmgww (envelope-from ); Tue, 20 Jul 2021 06:08:30 +0000 To: Sagi Grimberg , Christoph Hellwig Cc: Keith Busch , linux-nvme@lists.infradead.org, Herbert Xu , "David S . Miller" , linux-crypto@vger.kernel.org References: <20210716110428.9727-1-hare@suse.de> <20210716110428.9727-10-hare@suse.de> From: Hannes Reinecke Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication Message-ID: <8fab8ca5-318d-67ab-4a5d-5d4253c22282@suse.de> Date: Tue, 20 Jul 2021 08:08:30 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 MIME-Version: 1.0 In-Reply-To: Content-Language: en-US X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210719_230833_484017_E02301A5 X-CRM114-Status: GOOD ( 36.58 ) X-BeenThere: linux-nvme@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "Linux-nvme" Errors-To: linux-nvme-bounces+linux-nvme=archiver.kernel.org@lists.infradead.org T24gNy8xOS8yMSAxMDozOCBQTSwgU2FnaSBHcmltYmVyZyB3cm90ZToKPiAKPiAKPiBPbiA3LzE2 LzIxIDQ6MDQgQU0sIEhhbm5lcyBSZWluZWNrZSB3cm90ZToKPj4gSW1wbGVtZW50IHN1cHBvcnQg Zm9yIE5WTWUtb0YgSW4tQmFuZCBhdXRoZW50aWNhdGlvbi4gVGhpcyBwYXRjaAo+PiBhZGRzIHR3 byBhZGRpdGlvbmFsIGNvbmZpZ2ZzIGVudHJpZXMgJ2RoY2hhcF9rZXknIGFuZCAnZGhjaGFwX2hh c2gnCj4+IHRvIHRoZSAnaG9zdCcgY29uZmlnZnMgZGlyZWN0b3J5LiBUaGUgJ2RoY2hhcF9rZXkn IG5lZWRzIHRvIGJlCj4+IHNwZWNpZmllZCBpbiB0aGUgZm9ybWF0IG91dGxpbmVkIGluIHRoZSBi YXNlIHNwZWMuCj4+IEF1Z21lbnRlZCBjaGFsbGVuZ2Ugc3VwcG9ydCBpcyBub3QgaW1wbGVtZW50 ZWQsIGFuZCBjb25jYXRlbmF0aW9uCj4+IHdpdGggVExTIGVuY3J5cHRpb24gaXMgbm90IHN1cHBv cnRlZC4KPj4KPj4gU2lnbmVkLW9mZi1ieTogSGFubmVzIFJlaW5lY2tlIDxoYXJlQHN1c2UuZGU+ Cj4+IC0tLQo+PiDCoCBkcml2ZXJzL252bWUvdGFyZ2V0L0tjb25maWfCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIHzCoCAxMCArCj4+IMKgIGRyaXZlcnMvbnZtZS90YXJnZXQvTWFrZWZpbGXCoMKgwqDC oMKgwqDCoMKgwqDCoCB8wqDCoCAxICsKPj4gwqAgZHJpdmVycy9udm1lL3RhcmdldC9hZG1pbi1j bWQuY8KgwqDCoMKgwqDCoMKgIHzCoMKgIDQgKwo+PiDCoCBkcml2ZXJzL252bWUvdGFyZ2V0L2F1 dGguY8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB8IDM1MiArKysrKysrKysrKysrKysrKysrCj4+ IMKgIGRyaXZlcnMvbnZtZS90YXJnZXQvY29uZmlnZnMuY8KgwqDCoMKgwqDCoMKgwqAgfMKgIDcx ICsrKy0KPj4gwqAgZHJpdmVycy9udm1lL3RhcmdldC9jb3JlLmPCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgfMKgwqAgOCArCj4+IMKgIGRyaXZlcnMvbnZtZS90YXJnZXQvZmFicmljcy1jbWQtYXV0 aC5jIHwgNDYwICsrKysrKysrKysrKysrKysrKysrKysrKysKPj4gwqAgZHJpdmVycy9udm1lL3Rh cmdldC9mYWJyaWNzLWNtZC5jwqDCoMKgwqDCoCB8wqAgMzAgKy0KPj4gwqAgZHJpdmVycy9udm1l L3RhcmdldC9udm1ldC5owqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB8wqAgNzEgKysrKwo+PiDCoCA5 IGZpbGVzIGNoYW5nZWQsIDEwMDQgaW5zZXJ0aW9ucygrKSwgMyBkZWxldGlvbnMoLSkKPj4gwqAg Y3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvbnZtZS90YXJnZXQvYXV0aC5jCj4+IMKgIGNyZWF0 ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL252bWUvdGFyZ2V0L2ZhYnJpY3MtY21kLWF1dGguYwo+Pgo+ PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9udm1lL3RhcmdldC9LY29uZmlnIGIvZHJpdmVycy9udm1l L3RhcmdldC9LY29uZmlnCj4+IGluZGV4IDRiZTJlY2VjYmM0NS4uZDU2NTZlZjE1NTllIDEwMDY0 NAo+PiAtLS0gYS9kcml2ZXJzL252bWUvdGFyZ2V0L0tjb25maWcKPj4gKysrIGIvZHJpdmVycy9u dm1lL3RhcmdldC9LY29uZmlnCj4+IEBAIC04NSwzICs4NSwxMyBAQCBjb25maWcgTlZNRV9UQVJH RVRfVENQCj4+IMKgwqDCoMKgwqDCoMKgIGRldmljZXMgb3ZlciBUQ1AuCj4+IMKgIMKgwqDCoMKg wqDCoMKgIElmIHVuc3VyZSwgc2F5IE4uCj4+ICsKPj4gK2NvbmZpZyBOVk1FX1RBUkdFVF9BVVRI Cj4+ICvCoMKgwqAgYm9vbCAiTlZNZSBvdmVyIEZhYnJpY3MgSW4tYmFuZCBBdXRoZW50aWNhdGlv biBzdXBwb3J0Igo+PiArwqDCoMKgIGRlcGVuZHMgb24gTlZNRV9UQVJHRVQKPj4gK8KgwqDCoCBz ZWxlY3QgQ1JZUFRPX1NIQTI1Ngo+PiArwqDCoMKgIHNlbGVjdCBDUllQVE9fU0hBNTEyCj4+ICvC oMKgwqAgaGVscAo+PiArwqDCoMKgwqDCoCBUaGlzIGVuYWJsZXMgc3VwcG9ydCBmb3IgTlZNZSBv dmVyIEZhYnJpY3MgSW4tYmFuZCBBdXRoZW50aWNhdGlvbgo+PiArCj4+ICvCoMKgwqDCoMKgIElm IHVuc3VyZSwgc2F5IE4uCj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL252bWUvdGFyZ2V0L01ha2Vm aWxlIGIvZHJpdmVycy9udm1lL3RhcmdldC9NYWtlZmlsZQo+PiBpbmRleCA5ODM3ZTU4MGZhN2Uu LmM2NjgyMDEwMjQ5MyAxMDA2NDQKPj4gLS0tIGEvZHJpdmVycy9udm1lL3RhcmdldC9NYWtlZmls ZQo+PiArKysgYi9kcml2ZXJzL252bWUvdGFyZ2V0L01ha2VmaWxlCj4+IEBAIC0xMyw2ICsxMyw3 IEBAIG52bWV0LXnCoMKgwqDCoMKgwqDCoCArPSBjb3JlLm8gY29uZmlnZnMubyBhZG1pbi1jbWQu bwo+PiBmYWJyaWNzLWNtZC5vIFwKPj4gwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZGlzY292 ZXJ5Lm8gaW8tY21kLWZpbGUubyBpby1jbWQtYmRldi5vCj4+IMKgIG52bWV0LSQoQ09ORklHX05W TUVfVEFSR0VUX1BBU1NUSFJVKcKgwqDCoCArPSBwYXNzdGhydS5vCj4+IMKgIG52bWV0LSQoQ09O RklHX0JMS19ERVZfWk9ORUQpwqDCoMKgwqDCoMKgwqAgKz0gem5zLm8KPj4gK252bWV0LSQoQ09O RklHX05WTUVfVEFSR0VUX0FVVEgpwqDCoMKgICs9IGZhYnJpY3MtY21kLWF1dGgubyBhdXRoLm8K Pj4gwqAgbnZtZS1sb29wLXnCoMKgwqAgKz0gbG9vcC5vCj4+IMKgIG52bWV0LXJkbWEtecKgwqDC oCArPSByZG1hLm8KPj4gwqAgbnZtZXQtZmMtecKgwqDCoCArPSBmYy5vCj4+IGRpZmYgLS1naXQg YS9kcml2ZXJzL252bWUvdGFyZ2V0L2FkbWluLWNtZC5jCj4+IGIvZHJpdmVycy9udm1lL3Rhcmdl dC9hZG1pbi1jbWQuYwo+PiBpbmRleCAwY2I5OGYyYmJjOGMuLjMyMGNlZmM2NGVlMCAxMDA2NDQK Pj4gLS0tIGEvZHJpdmVycy9udm1lL3RhcmdldC9hZG1pbi1jbWQuYwo+PiArKysgYi9kcml2ZXJz L252bWUvdGFyZ2V0L2FkbWluLWNtZC5jCj4+IEBAIC0xMDA4LDYgKzEwMDgsMTAgQEAgdTE2IG52 bWV0X3BhcnNlX2FkbWluX2NtZChzdHJ1Y3QgbnZtZXRfcmVxICpyZXEpCj4+IMKgIMKgwqDCoMKg wqAgaWYgKG52bWVfaXNfZmFicmljcyhjbWQpKQo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJu IG52bWV0X3BhcnNlX2ZhYnJpY3NfY21kKHJlcSk7Cj4+ICsKPj4gK8KgwqDCoCBpZiAodW5saWtl bHkoIW52bWV0X2NoZWNrX2F1dGhfc3RhdHVzKHJlcSkpKQo+PiArwqDCoMKgwqDCoMKgwqAgcmV0 dXJuIE5WTUVfU0NfQVVUSF9SRVFVSVJFRCB8IE5WTUVfU0NfRE5SOwo+PiArCj4+IMKgwqDCoMKg wqAgaWYgKG52bWV0X3JlcV9zdWJzeXMocmVxKS0+dHlwZSA9PSBOVk1FX05RTl9ESVNDKQo+PiDC oMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJuIG52bWV0X3BhcnNlX2Rpc2NvdmVyeV9jbWQocmVxKTsK Pj4gwqAgZGlmZiAtLWdpdCBhL2RyaXZlcnMvbnZtZS90YXJnZXQvYXV0aC5jIGIvZHJpdmVycy9u dm1lL3RhcmdldC9hdXRoLmMKPj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQKPj4gaW5kZXggMDAwMDAw MDAwMDAwLi4wMGM3ZDA1MWRmYjEKPj4gLS0tIC9kZXYvbnVsbAo+PiArKysgYi9kcml2ZXJzL252 bWUvdGFyZ2V0L2F1dGguYwo+PiBAQCAtMCwwICsxLDM1MiBAQAo+PiArLy8gU1BEWC1MaWNlbnNl LUlkZW50aWZpZXI6IEdQTC0yLjAKPj4gKy8qCj4+ICsgKiBOVk1lIG92ZXIgRmFicmljcyBESC1I TUFDLUNIQVAgYXV0aGVudGljYXRpb24uCj4+ICsgKiBDb3B5cmlnaHQgKGMpIDIwMjAgSGFubmVz IFJlaW5lY2tlLCBTVVNFIFNvZnR3YXJlIFNvbHV0aW9ucy4KPj4gKyAqIEFsbCByaWdodHMgcmVz ZXJ2ZWQuCj4+ICsgKi8KPj4gKyNkZWZpbmUgcHJfZm10KGZtdCkgS0JVSUxEX01PRE5BTUUgIjog IiBmbXQKPj4gKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4KPj4gKyNpbmNsdWRlIDxsaW51eC9p bml0Lmg+Cj4+ICsjaW5jbHVkZSA8bGludXgvc2xhYi5oPgo+PiArI2luY2x1ZGUgPGxpbnV4L2Vy ci5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9oYXNoLmg+Cj4+ICsjaW5jbHVkZSA8Y3J5cHRvL2tw cC5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9kaC5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9mZmRo ZS5oPgo+PiArI2luY2x1ZGUgPGxpbnV4L2NyYzMyLmg+Cj4+ICsjaW5jbHVkZSA8bGludXgvYmFz ZTY0Lmg+Cj4+ICsjaW5jbHVkZSA8bGludXgvY3R5cGUuaD4KPj4gKyNpbmNsdWRlIDxsaW51eC9y YW5kb20uaD4KPj4gKyNpbmNsdWRlIDxhc20vdW5hbGlnbmVkLmg+Cj4+ICsKPj4gKyNpbmNsdWRl ICJudm1ldC5oIgo+PiArI2luY2x1ZGUgIi4uL2hvc3QvYXV0aC5oIgo+PiArCj4+ICtpbnQgbnZt ZXRfYXV0aF9zZXRfaG9zdF9rZXkoc3RydWN0IG52bWV0X2hvc3QgKmhvc3QsIGNvbnN0IGNoYXIg KnNlY3JldCkKPj4gK3sKPj4gK8KgwqDCoCBpZiAoc3NjYW5mKHNlY3JldCwgIkRISEMtMTolaGhk OiUqcyIsICZob3N0LT5kaGNoYXBfa2V5X2hhc2gpICE9IDEpCj4+ICvCoMKgwqDCoMKgwqDCoCBy ZXR1cm4gLUVJTlZBTDsKPj4gK8KgwqDCoCBpZiAoaG9zdC0+ZGhjaGFwX2tleV9oYXNoID4gMykg ewo+PiArwqDCoMKgwqDCoMKgwqAgcHJfd2FybigiSW52YWxpZCBESC1ITUFDLUNIQVAgaGFzaCBp ZCAlZFxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBob3N0LT5kaGNoYXBfa2V5X2hh c2gpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIC1FSU5WQUw7Cj4+ICvCoMKgwqAgfQo+PiAr wqDCoMKgIGlmIChob3N0LT5kaGNoYXBfa2V5X2hhc2ggPiAwKSB7Cj4+ICvCoMKgwqDCoMKgwqDC oCAvKiBWYWxpZGF0ZSBzZWxlY3RlZCBoYXNoIGFsZ29yaXRobSAqLwo+PiArwqDCoMKgwqDCoMKg wqAgY29uc3QgY2hhciAqaG1hYyA9IG52bWVfYXV0aF9obWFjX25hbWUoaG9zdC0+ZGhjaGFwX2tl eV9oYXNoKTsKPj4gKwo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKCFjcnlwdG9faGFzX3NoYXNoKGht YWMsIDAsIDApKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHByX3dhcm4oIkRILUhNQUMt Q0hBUCBoYXNoICVzIHVuc3VwcG9ydGVkXG4iLCBobWFjKTsKPiAKPiBwcl9lcnIKPiAKPj4gK8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqAgaG9zdC0+ZGhjaGFwX2tleV9oYXNoID0gLTE7Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiAtRUFHQUlOOwo+IAo+IFdoeSBFQUdBSU4/Cj4gCgpX aGF0IGVsc2U/IEVOT1RTVVBQPwoKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDC oMKgIC8qIFVzZSB0aGlzIGhhc2ggYXMgZGVmYXVsdCAqLwo+PiArwqDCoMKgwqDCoMKgwqAgaWYg KCFob3N0LT5kaGNoYXBfaGFzaF9pZCkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaG9zdC0+ ZGhjaGFwX2hhc2hfaWQgPSBob3N0LT5kaGNoYXBfa2V5X2hhc2g7Cj4gCj4gV2h5Pwo+IAoKQmVj YXVzZSB0aGVyZSBpcyBubyBtZWNoYW5pc20gaG93IHRoZSBjb250cm9sbGVyIHNlbGVjdHMgdGhl IERIQ0hBUCBobWFjCmFsZ29yaXRobS4KVGhlIGhvc3Qgd2lsbCBzZW5kIGEgbGlzdCBvZiBzdXBw b3J0ZWQgaG1hYyBhbGdvcml0aG1zLCBhbmQgdGhlCmNvbnRyb2xsZXIgaGFzIHRvIHBpY2sgb25l IG9mIHRoZW0uCgpBbmQgYXMgd2UgYXJlIHN1cmUgdGhhdCB0aGUgaG1hYyBhbGdvcml0aG0gZnJv bSB0aGUgUFNLIHdpbGwgYmUKc3VwcG9ydGVkIG9uIHRoZSBjb250cm9sbGVyIEkgc2V0IHRoYXQg YXMgZGVmYXVsdCAoaWYgbm90aGluZyB3YXMKc3BlY2lmaWVkIG90aGVyd2lzZSkuCgo+PiArwqDC oMKgIH0KPj4gK8KgwqDCoCBob3N0LT5kaGNoYXBfc2VjcmV0ID0ga3N0cmR1cChzZWNyZXQsIEdG UF9LRVJORUwpOwo+PiArwqDCoMKgIGlmICghaG9zdC0+ZGhjaGFwX3NlY3JldCkKPj4gK8KgwqDC oMKgwqDCoMKgIHJldHVybiAtRU5PTUVNOwo+PiArwqDCoMKgIC8qIERlZmF1bHQgdG8gU0hBMjU2 ICovCj4+ICvCoMKgwqAgaWYgKCFob3N0LT5kaGNoYXBfaGFzaF9pZCkKPj4gK8KgwqDCoMKgwqDC oMKgIGhvc3QtPmRoY2hhcF9oYXNoX2lkID0gTlZNRV9BVVRIX0RIQ0hBUF9IQVNIX1NIQTI1NjsK PiAKPiBXaGF0IGlzIHRoZSB0aG91Z2h0IGhlcmU/Cj4gCgpUaGlzIGNhc2UgaXMgdHJpZ2dlcmVk IHdoZW4gdGhlIHVzZXIgc3BlY2lmaWVzIGEgUFNLIHdpdGhvdXQgYSBobWFjCmFsZ29yaXRobSAo aWUgREhIQy0xOjAwOlhYWFhYKS4KVGhlbiB0aGUgYWJvdmUgbWVjaGFuaXNtIGRvZXNuJ3Qgd29y aywgYnV0IHdlIHN0aWxsIGhhdmUgdG8gc3BlY2lmeSBhCmRlZmF1bHQgSE1BQyBhbGdvcml0aG0g c3VjaCB0aGF0IHRoZSBzZWxlY3Rpb24gbWVjaGFuaXNtIGNhbiB3b3JrLgoKPj4gKwo+PiArwqDC oMKgIHByX2RlYnVnKCJVc2luZyBoYXNoICVzXG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoCBudm1l X2F1dGhfaG1hY19uYW1lKGhvc3QtPmRoY2hhcF9oYXNoX2lkKSk7Cj4+ICvCoMKgwqAgcmV0dXJu IDA7Cj4+ICt9Cj4+ICsKPj4gK2ludCBudm1ldF9zZXR1cF9kaGdyb3VwKHN0cnVjdCBudm1ldF9j dHJsICpjdHJsLCBpbnQgZGhncm91cF9pZCkKPj4gK3sKPj4gK8KgwqDCoCBpbnQgcmV0ID0gLUVO T1RTVVBQOwo+PiArCj4+ICvCoMKgwqAgaWYgKGRoZ3JvdXBfaWQgPT0gTlZNRV9BVVRIX0RIQ0hB UF9ESEdST1VQX05VTEwpCj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gMDsKPj4gKwo+PiArwqDC oMKgIHJldHVybiByZXQ7Cj4+ICt9Cj4+ICsKPj4gK2ludCBudm1ldF9zZXR1cF9hdXRoKHN0cnVj dCBudm1ldF9jdHJsICpjdHJsLCBzdHJ1Y3QgbnZtZXRfcmVxICpyZXEpCj4+ICt7Cj4+ICvCoMKg wqAgaW50IHJldCA9IDA7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWV0X2hvc3RfbGluayAqcDsKPj4g K8KgwqDCoCBzdHJ1Y3QgbnZtZXRfaG9zdCAqaG9zdCA9IE5VTEw7Cj4+ICvCoMKgwqAgY29uc3Qg Y2hhciAqaGFzaF9uYW1lOwo+PiArCj4+ICvCoMKgwqAgZG93bl9yZWFkKCZudm1ldF9jb25maWdf c2VtKTsKPj4gK8KgwqDCoCBpZiAoY3RybC0+c3Vic3lzLT50eXBlID09IE5WTUVfTlFOX0RJU0Mp Cj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dF91bmxvY2s7Cj4+ICsKPj4gK8KgwqDCoCBsaXN0 X2Zvcl9lYWNoX2VudHJ5KHAsICZjdHJsLT5zdWJzeXMtPmhvc3RzLCBlbnRyeSkgewo+PiArwqDC oMKgwqDCoMKgwqAgcHJfZGVidWcoImNoZWNrICVzXG4iLCBudm1ldF9ob3N0X25hbWUocC0+aG9z dCkpOwo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKHN0cmNtcChudm1ldF9ob3N0X25hbWUocC0+aG9z dCksIGN0cmwtPmhvc3RucW4pKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjb250aW51ZTsK Pj4gK8KgwqDCoMKgwqDCoMKgIGhvc3QgPSBwLT5ob3N0Owo+PiArwqDCoMKgwqDCoMKgwqAgYnJl YWs7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmICghaG9zdCkgewo+PiArwqDCoMKgwqDCoMKg wqAgcHJfZGVidWcoImhvc3QgJXMgbm90IGZvdW5kXG4iLCBjdHJsLT5ob3N0bnFuKTsKPj4gK8Kg wqDCoMKgwqDCoMKgIHJldCA9IC1FUEVSTTsKPiAKPiBJIHRoaW5rIHlvdSBzaG91bGQgcHJvcG9n YXRlIHRoZSBudm1lIHN0YXR1cyBjb2RlIGluc3RlYWQuLi4KPiAKCkkgX3Rob3VnaHRfIGl0IGdv dCB0cmFuc2xhdGVkIGludG8gb25lOyBidXQgeWVhaCwgY2FuIGRvLgoKPj4gK8KgwqDCoMKgwqDC oMKgIGdvdG8gb3V0X3VubG9jazsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgaWYgKCFob3N0LT5k aGNoYXBfc2VjcmV0KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiTm8gYXV0aGVudGlj YXRpb24gcHJvdmlkZWRcbiIpOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBvdXRfdW5sb2NrOwo+ PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIGhhc2hfbmFtZSA9IG52bWVfYXV0aF9obWFjX25h bWUoaG9zdC0+ZGhjaGFwX2hhc2hfaWQpOwo+PiArwqDCoMKgIGlmICghaGFzaF9uYW1lKSB7Cj4g Cj4gQ2FuIHRoaXMgYWN0dWFsbHkgaGFwcGVuPwo+IAoKR29vZCBxdWVzdGlvbi4gSSBkb24ndCB0 aGluayBzbzsgd2lsbCBiZSBjaGFuZ2luZyBpdCBpbnRvIGEgV0FSTl9PTigpLgoKPj4gK8KgwqDC oMKgwqDCoMKgIHByX2RlYnVnKCJIYXNoIElEICVkIGludmFsaWRcbiIsIGhvc3QtPmRoY2hhcF9o YXNoX2lkKTsKPiAKPiB3YXJuaW5nLCBub3QgZGVidWcuCj4gCgpTZWUgYWJvdmUuIFllcy4KCj4+ ICvCoMKgwqDCoMKgwqDCoCByZXQgPSAtRUlOVkFMOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBv dXRfdW5sb2NrOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBjdHJsLT5zaGFzaF90Zm0gPSBjcnlw dG9fYWxsb2Nfc2hhc2goaGFzaF9uYW1lLCAwLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIENSWVBUT19BTEdfQUxMT0NBVEVTX01FTU9SWSk7Cj4+ ICvCoMKgwqAgaWYgKElTX0VSUihjdHJsLT5zaGFzaF90Zm0pKSB7Cj4+ICvCoMKgwqDCoMKgwqDC oCBwcl9kZWJ1ZygiZmFpbGVkIHRvIGFsbG9jYXRlIHNoYXNoICVzXG4iLCBoYXNoX25hbWUpOwo+ PiArwqDCoMKgwqDCoMKgwqAgcmV0ID0gUFRSX0VSUihjdHJsLT5zaGFzaF90Zm0pOwo+PiArwqDC oMKgwqDCoMKgwqAgY3RybC0+c2hhc2hfdGZtID0gTlVMTDsKPj4gK8KgwqDCoMKgwqDCoMKgIGdv dG8gb3V0X3VubG9jazsKPj4gK8KgwqDCoCB9Cj4+ICsKPj4gK8KgwqDCoCBjdHJsLT5kaGNoYXBf a2V5ID0gbnZtZV9hdXRoX2V4dHJhY3Rfc2VjcmV0KGhvc3QtPmRoY2hhcF9zZWNyZXQsCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgJmN0 cmwtPmRoY2hhcF9rZXlfbGVuKTsKPj4gK8KgwqDCoCBpZiAoSVNfRVJSKGN0cmwtPmRoY2hhcF9r ZXkpKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiZmFpbGVkIHRvIGV4dHJhY3QgaG9z dCBrZXksIGVycm9yICVkXG4iLCByZXQpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0ID0gUFRSX0VS UihjdHJsLT5kaGNoYXBfa2V5KTsKPj4gK8KgwqDCoMKgwqDCoMKgIGN0cmwtPmRoY2hhcF9rZXkg PSBOVUxMOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBvdXRfZnJlZV9oYXNoOwo+PiArwqDCoMKg IH0KPj4gK8KgwqDCoCBpZiAoaG9zdC0+ZGhjaGFwX2tleV9oYXNoKSB7Cj4+ICvCoMKgwqDCoMKg wqDCoCBzdHJ1Y3QgY3J5cHRvX3NoYXNoICprZXlfdGZtOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDC oCBoYXNoX25hbWUgPSBudm1lX2F1dGhfaG1hY19uYW1lKGhvc3QtPmRoY2hhcF9rZXlfaGFzaCk7 Cj4+ICvCoMKgwqDCoMKgwqDCoCBrZXlfdGZtID0gY3J5cHRvX2FsbG9jX3NoYXNoKGhhc2hfbmFt ZSwgMCwgMCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpZiAoSVNfRVJSKGtleV90Zm0pKSB7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldCA9IFBUUl9FUlIoa2V5X3RmbSk7Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIGdvdG8gb3V0X2ZyZWVfaGFzaDsKPj4gK8KgwqDCoMKgwqDCoMKgIH0g ZWxzZSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIFNIQVNIX0RFU0NfT05fU1RBQ0soc2hh c2gsIGtleV90Zm0pOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNoYXNoLT50Zm0g PSBrZXlfdGZtOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hf c2V0a2V5KGtleV90Zm0sIGN0cmwtPmRoY2hhcF9rZXksCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjdHJsLT5kaGNoYXBfa2V5X2xlbik7Cj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNyeXB0b19zaGFzaF9pbml0KHNoYXNoKTsKPj4gK8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqAgY3J5cHRvX3NoYXNoX3VwZGF0ZShzaGFzaCwgY3RybC0+c3Vi c3lzLT5zdWJzeXNucW4sCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgIHN0cmxlbihjdHJsLT5zdWJzeXMtPnN1YnN5c25xbikpOwo+PiArwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBjcnlwdG9fc2hhc2hfdXBkYXRlKHNoYXNoLCAiTlZNZS1vdmVyLUZhYnJp Y3MiLCAxNyk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNyeXB0b19zaGFzaF9maW5hbChz aGFzaCwgY3RybC0+ZGhjaGFwX2tleSk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNyeXB0 b19mcmVlX3NoYXNoKGtleV90Zm0pOwo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArwqDCoMKgIH0K Pj4gK8KgwqDCoCBwcl9kZWJ1ZygiJXM6IHVzaW5nIGtleSAlKnBoXG4iLCBfX2Z1bmNfXywKPj4g K8KgwqDCoMKgwqDCoMKgwqAgKGludCljdHJsLT5kaGNoYXBfa2V5X2xlbiwgY3RybC0+ZGhjaGFw X2tleSk7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3NldGtleShjdHJsLT5zaGFzaF90 Zm0sIGN0cmwtPmRoY2hhcF9rZXksCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGN0cmwtPmRoY2hhcF9rZXlfbGVuKTsKPj4gK291dF9mcmVlX2hhc2g6Cj4+ICvCoMKgwqAg aWYgKHJldCkgewo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKGN0cmwtPmRoY2hhcF9rZXkpIHsKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAga2ZyZWUoY3RybC0+ZGhjaGFwX2tleSk7Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGN0cmwtPmRoY2hhcF9rZXkgPSBOVUxMOwo+PiArwqDCoMKgwqDC oMKgwqAgfQo+PiArwqDCoMKgwqDCoMKgwqAgY3J5cHRvX2ZyZWVfc2hhc2goY3RybC0+c2hhc2hf dGZtKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGN0cmwtPnNoYXNoX3RmbSA9IE5VTEw7Cj4+ICvCoMKg wqAgfQo+PiArb3V0X3VubG9jazoKPj4gK8KgwqDCoCB1cF9yZWFkKCZudm1ldF9jb25maWdfc2Vt KTsKPj4gKwo+PiArwqDCoMKgIHJldHVybiByZXQ7Cj4+ICt9Cj4+ICsKPj4gK3ZvaWQgbnZtZXRf YXV0aF9zcV9mcmVlKHN0cnVjdCBudm1ldF9zcSAqc3EpCj4+ICt7Cj4+ICvCoMKgwqAgaWYgKHNx LT5kaGNoYXBfYzEpCj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShzcS0+ZGhjaGFwX2MxKTsKPiAK PiBqdXN0IGtmcmVlLCBubyBuZWVkIHRvIGlmCj4gCgpZZWFoLgoKPj4gK8KgwqDCoCBpZiAoc3Et PmRoY2hhcF9jMikKPj4gK8KgwqDCoMKgwqDCoMKgIGtmcmVlKHNxLT5kaGNoYXBfYzIpOwo+PiAr wqDCoMKgIGlmIChzcS0+ZGhjaGFwX3NrZXkpCj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShzcS0+ ZGhjaGFwX3NrZXkpOwo+PiArfQo+PiArCj4+ICt2b2lkIG52bWV0X3Jlc2V0X2F1dGgoc3RydWN0 IG52bWV0X2N0cmwgKmN0cmwpCj4gCj4gU2hvdWxkbid0IHRoaXMgYmUgbnZtZXRfZGVzdHJveV9h dXRoPyByZXNldCBpbmRpY2F0ZXMKPiBpdCBjYW4gYmUgcmV1c2VkIGFnYWluLi4uCj4gCgpPaCwg aXQgc2hvdWxkLiBJJ3ZlIGNvZGVkIG52bWV0X2Rlc3Ryb3lfYXV0aCgpIHByZXR0eSBsYXRlIGlu IHRoZSBnYW1lLApzbyBJIG1pc3NlZCB0aGF0IG9uZS4KCj4+ICt7Cj4+ICvCoMKgwqAgaWYgKGN0 cmwtPnNoYXNoX3RmbSkgewo+PiArwqDCoMKgwqDCoMKgwqAgY3J5cHRvX2ZyZWVfc2hhc2goY3Ry bC0+c2hhc2hfdGZtKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGN0cmwtPnNoYXNoX3RmbSA9IE5VTEw7 Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmIChjdHJsLT5kaF90Zm0pIHsKPj4gK8KgwqDCoMKg wqDCoMKgIGNyeXB0b19mcmVlX2twcChjdHJsLT5kaF90Zm0pOwo+PiArwqDCoMKgwqDCoMKgwqAg Y3RybC0+ZGhfdGZtID0gTlVMTDsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgaWYgKGN0cmwtPmRo Y2hhcF9rZXkpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGtmcmVlKGN0cmwtPmRoY2hhcF9rZXkpOwo+ PiArwqDCoMKgwqDCoMKgwqAgY3RybC0+ZGhjaGFwX2tleSA9IE5VTEw7Cj4+ICvCoMKgwqAgfQo+ PiArfQo+PiArCj4+ICtib29sIG52bWV0X2NoZWNrX2F1dGhfc3RhdHVzKHN0cnVjdCBudm1ldF9y ZXEgKnJlcSkKPj4gK3sKPj4gK8KgwqDCoCBpZiAocmVxLT5zcS0+Y3RybC0+c2hhc2hfdGZtICYm Cj4+ICvCoMKgwqDCoMKgwqDCoCAhcmVxLT5zcS0+YXV0aGVudGljYXRlZCkKPj4gK8KgwqDCoMKg wqDCoMKgIHJldHVybiBmYWxzZTsKPj4gK8KgwqDCoCByZXR1cm4gdHJ1ZTsKPj4gK30KPj4gKwo+ PiAraW50IG52bWV0X2F1dGhfaG9zdF9oYXNoKHN0cnVjdCBudm1ldF9yZXEgKnJlcSwgdTggKnJl c3BvbnNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGludCBzaGFzaF9s ZW4pCj4+ICt7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWV0X2N0cmwgKmN0cmwgPSByZXEtPnNxLT5j dHJsOwo+PiArwqDCoMKgIFNIQVNIX0RFU0NfT05fU1RBQ0soc2hhc2gsIGN0cmwtPnNoYXNoX3Rm bSk7Cj4+ICvCoMKgwqAgdTggKmNoYWxsZW5nZSA9IHJlcS0+c3EtPmRoY2hhcF9jMTsKPj4gK8Kg wqDCoCB1OCBidWZbNF07Cj4+ICvCoMKgwqAgaW50IHJldDsKPj4gKwo+PiArwqDCoMKgIGlmIChj dHJsLT5kaF9naWQgIT0gTlZNRV9BVVRIX0RIQ0hBUF9ESEdST1VQX05VTEwpIHsKPj4gK8KgwqDC oMKgwqDCoMKgIHJldCA9IC1FTk9UU1VQUDsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+ PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIHNoYXNoLT50Zm0gPSBjdHJsLT5zaGFzaF90Zm07 Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX2luaXQoc2hhc2gpOwo+PiArwqDCoMKgIGlm IChyZXQpCj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsKPj4gK8KgwqDCoCByZXQgPSBjcnlw dG9fc2hhc2hfdXBkYXRlKHNoYXNoLCBjaGFsbGVuZ2UsIHNoYXNoX2xlbik7Cj4+ICvCoMKgwqAg aWYgKHJldCkKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+PiArwqDCoMKgIHB1dF91bmFs aWduZWRfbGUzMihyZXEtPnNxLT5kaGNoYXBfczEsIGJ1Zik7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5 cHRvX3NoYXNoX3VwZGF0ZShzaGFzaCwgYnVmLCA0KTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiAr wqDCoMKgwqDCoMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcHV0X3VuYWxpZ25lZF9sZTE2KHJl cS0+c3EtPmRoY2hhcF90aWQsIGJ1Zik7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3Vw ZGF0ZShzaGFzaCwgYnVmLCAyKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDCoMKg wqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgbWVtc2V0KGJ1ZiwgMCwgNCk7Cj4+ICvCoMKgwqAgcmV0 ID0gY3J5cHRvX3NoYXNoX3VwZGF0ZShzaGFzaCwgYnVmLCAxKTsKPj4gK8KgwqDCoCBpZiAocmV0 KQo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3No YXNoX3VwZGF0ZShzaGFzaCwgIkhvc3RIb3N0IiwgOCk7Cj4+ICvCoMKgwqAgaWYgKHJldCkKPj4g K8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF91 cGRhdGUoc2hhc2gsIGN0cmwtPmhvc3RucW4sCj4+IHN0cmxlbihjdHJsLT5ob3N0bnFuKSk7Cj4+ ICvCoMKgwqAgaWYgKHJldCkKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+PiArwqDCoMKg IHJldCA9IGNyeXB0b19zaGFzaF91cGRhdGUoc2hhc2gsIGJ1ZiwgMSk7Cj4+ICvCoMKgwqAgaWYg KHJldCkKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+PiArwqDCoMKgIHJldCA9IGNyeXB0 b19zaGFzaF91cGRhdGUoc2hhc2gsIGN0cmwtPnN1YnN5c25xbiwKPj4gK8KgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqAgc3RybGVuKGN0cmwtPnN1YnN5c25xbikpOwo+PiArwqDCoMKg IGlmIChyZXQpCj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsKPj4gK8KgwqDCoCByZXQgPSBj cnlwdG9fc2hhc2hfZmluYWwoc2hhc2gsIHJlc3BvbnNlKTsKPj4gK291dDoKPj4gK8KgwqDCoCBp ZiAoY2hhbGxlbmdlICE9IHJlcS0+c3EtPmRoY2hhcF9jMSkKPj4gK8KgwqDCoMKgwqDCoMKgIGtm cmVlKGNoYWxsZW5nZSk7Cj4gCj4gV2hhdCBhYm91dCBhY3R1YWxseSBmYWlsaW5nPwo+IAoKSG8t aHVtLiBPZiBjb3Vyc2UuCgo+PiArwqDCoMKgIHJldHVybiAwOwo+PiArfQo+PiArCj4+ICtpbnQg bnZtZXRfYXV0aF9jdHJsX2hhc2goc3RydWN0IG52bWV0X3JlcSAqcmVxLCB1OCAqcmVzcG9uc2Us Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgdW5zaWduZWQgaW50IHNoYXNoX2xlbikKPj4g K3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZXRfY3RybCAqY3RybCA9IHJlcS0+c3EtPmN0cmw7Cj4+ ICvCoMKgwqAgU0hBU0hfREVTQ19PTl9TVEFDSyhzaGFzaCwgY3RybC0+c2hhc2hfdGZtKTsKPj4g K8KgwqDCoCB1OCAqY2hhbGxlbmdlID0gcmVxLT5zcS0+ZGhjaGFwX2MyOwo+PiArwqDCoMKgIHU4 IGJ1Zls0XTsKPj4gK8KgwqDCoCBpbnQgcmV0Owo+PiArCj4+ICvCoMKgwqAgcHJfZGVidWcoIiVz OiBjdHJsICVkIGhhc2ggc2VxICVkIHRyYW5zYWN0aW9uICV1XG4iLCBfX2Z1bmNfXywKPj4gK8Kg wqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlkLCByZXEtPnNxLT5kaGNoYXBfczIsIHJlcS0+c3Et PmRoY2hhcF90aWQpOwo+PiArwqDCoMKgIHByX2RlYnVnKCIlczogY3RybCAlZCBjaGFsbGVuZ2Ug JSpwaFxuIiwgX19mdW5jX18sCj4+ICvCoMKgwqDCoMKgwqDCoMKgIGN0cmwtPmNudGxpZCwgc2hh c2hfbGVuLCByZXEtPnNxLT5kaGNoYXBfYzIpOwo+PiArwqDCoMKgIHByX2RlYnVnKCIlczogY3Ry bCAlZCBzdWJzeXNucW4gJXNcbiIsIF9fZnVuY19fLAo+PiArwqDCoMKgwqDCoMKgwqDCoCBjdHJs LT5jbnRsaWQsIGN0cmwtPnN1YnN5c25xbik7Cj4+ICvCoMKgwqAgcHJfZGVidWcoIiVzOiBjdHJs ICVkIGhvc3RucW4gJXNcbiIsIF9fZnVuY19fLAo+PiArwqDCoMKgwqDCoMKgwqDCoCBjdHJsLT5j bnRsaWQsIGN0cmwtPmhvc3RucW4pOwo+PiArCj4+ICvCoMKgwqAgaWYgKGN0cmwtPmRoX2dpZCAh PSBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfTlVMTCkgewo+PiArwqDCoMKgwqDCoMKgwqAgcmV0 ID0gLUVOT1RTVVBQOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgfQo+ PiArCj4+ICvCoMKgwqAgc2hhc2gtPnRmbSA9IGN0cmwtPnNoYXNoX3RmbTsKPj4gK8KgwqDCoCBy ZXQgPSBjcnlwdG9fc2hhc2hfaW5pdChzaGFzaCk7Cj4+ICvCoMKgwqAgaWYgKHJldCkKPj4gK8Kg wqDCoMKgwqDCoMKgIGdvdG8gb3V0Owo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF91cGRh dGUoc2hhc2gsIGNoYWxsZW5nZSwgc2hhc2hfbGVuKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiAr wqDCoMKgwqDCoMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcHV0X3VuYWxpZ25lZF9sZTMyKHJl cS0+c3EtPmRoY2hhcF9zMiwgYnVmKTsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hfdXBk YXRlKHNoYXNoLCBidWYsIDQpOwo+PiArwqDCoMKgIGlmIChyZXQpCj4+ICvCoMKgwqDCoMKgwqDC oCBnb3RvIG91dDsKPj4gK8KgwqDCoCBwdXRfdW5hbGlnbmVkX2xlMTYocmVxLT5zcS0+ZGhjaGFw X3RpZCwgYnVmKTsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hfdXBkYXRlKHNoYXNoLCBi dWYsIDIpOwo+PiArwqDCoMKgIGlmIChyZXQpCj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsK Pj4gK8KgwqDCoCBtZW1zZXQoYnVmLCAwLCA0KTsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hh c2hfdXBkYXRlKHNoYXNoLCBidWYsIDEpOwo+PiArwqDCoMKgIGlmIChyZXQpCj4+ICvCoMKgwqDC oMKgwqDCoCBnb3RvIG91dDsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hfdXBkYXRlKHNo YXNoLCAiQ29udHJvbGxlciIsIDEwKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDC oMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3VwZGF0ZShzaGFz aCwgY3RybC0+c3Vic3lzbnFuLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0 cmxlbihjdHJsLT5zdWJzeXNucW4pKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDC oMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3VwZGF0ZShzaGFz aCwgYnVmLCAxKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBv dXQ7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3VwZGF0ZShzaGFzaCwgY3RybC0+aG9z dG5xbiwKPj4gc3RybGVuKGN0cmwtPmhvc3RucW4pKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiAr wqDCoMKgwqDCoMKgwqAgZ290byBvdXQ7Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX2Zp bmFsKHNoYXNoLCByZXNwb25zZSk7Cj4+ICtvdXQ6Cj4+ICvCoMKgwqAgaWYgKGNoYWxsZW5nZSAh PSByZXEtPnNxLT5kaGNoYXBfYzIpCj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShjaGFsbGVuZ2Up Owo+PiArwqDCoMKgIHJldHVybiAwOwo+PiArfQo+PiArCj4+ICtpbnQgbnZtZXRfYXV0aF9jdHJs X3Nlc3NrZXkoc3RydWN0IG52bWV0X3JlcSAqcmVxLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgIHU4ICpwa2V5LCBpbnQgcGtleV9zaXplKQo+PiArewo+PiArwqDCoMKgIHN0cnVj dCBudm1ldF9jdHJsICpjdHJsID0gcmVxLT5zcS0+Y3RybDsKPj4gK8KgwqDCoCBzdHJ1Y3Qga3Bw X3JlcXVlc3QgKmtwcF9yZXE7Cj4+ICvCoMKgwqAgc3RydWN0IGNyeXB0b193YWl0IHdhaXQ7Cj4+ ICvCoMKgwqAgc3RydWN0IHNjYXR0ZXJsaXN0IHNyYywgZHN0Owo+PiArwqDCoMKgIGludCByZXQ7 Cj4+ICsKPj4gK8KgwqDCoCByZXEtPnNxLT5kaGNoYXBfc2tleV9sZW4gPQo+PiArwqDCoMKgwqDC oMKgwqAgbnZtZV9hdXRoX2RoZ3JvdXBfcHJpdmtleV9zaXplKGN0cmwtPmRoX2dpZCk7Cj4+ICvC oMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3NrZXkgPSBremFsbG9jKHJlcS0+c3EtPmRoY2hhcF9za2V5 X2xlbiwKPj4gR0ZQX0tFUk5FTCk7Cj4+ICvCoMKgwqAgaWYgKCFyZXEtPnNxLT5kaGNoYXBfc2tl eSkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRU5PTUVNOwo+PiArwqDCoMKgIGtwcF9yZXEg PSBrcHBfcmVxdWVzdF9hbGxvYyhjdHJsLT5kaF90Zm0sIEdGUF9LRVJORUwpOwo+PiArwqDCoMKg IGlmICgha3BwX3JlcSkgewo+PiArwqDCoMKgwqDCoMKgwqAga2ZyZWUocmVxLT5zcS0+ZGhjaGFw X3NrZXkpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3NrZXkgPSBOVUxMOwo+ PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIC1FTk9NRU07Cj4+ICvCoMKgwqAgfQo+PiArCj4+ICvC oMKgwqAgcHJfZGVidWcoIiVzOiBob3N0IHB1YmxpYyBrZXkgJSpwaFxuIiwgX19mdW5jX18sCj4+ ICvCoMKgwqDCoMKgwqDCoMKgIChpbnQpcGtleV9zaXplLCBwa2V5KTsKPj4gK8KgwqDCoCBjcnlw dG9faW5pdF93YWl0KCZ3YWl0KTsKPj4gK8KgwqDCoCBzZ19pbml0X29uZSgmc3JjLCBwa2V5LCBw a2V5X3NpemUpOwo+PiArwqDCoMKgIGtwcF9yZXF1ZXN0X3NldF9pbnB1dChrcHBfcmVxLCAmc3Jj LCBwa2V5X3NpemUpOwo+PiArwqDCoMKgIHNnX2luaXRfb25lKCZkc3QsIHJlcS0+c3EtPmRoY2hh cF9za2V5LAo+PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3NrZXlfbGVuKTsKPj4g K8KgwqDCoCBrcHBfcmVxdWVzdF9zZXRfb3V0cHV0KGtwcF9yZXEsICZkc3QsIHJlcS0+c3EtPmRo Y2hhcF9za2V5X2xlbik7Cj4+ICvCoMKgwqAga3BwX3JlcXVlc3Rfc2V0X2NhbGxiYWNrKGtwcF9y ZXEsIENSWVBUT19URk1fUkVRX01BWV9CQUNLTE9HLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgY3J5cHRvX3JlcV9kb25lLCAmd2FpdCk7Cj4+ICsKPj4gK8KgwqDCoCByZXQg PSBjcnlwdG9fd2FpdF9yZXEoY3J5cHRvX2twcF9jb21wdXRlX3NoYXJlZF9zZWNyZXQoa3BwX3Jl cSksCj4+ICZ3YWl0KTsKPj4gK8KgwqDCoCBrcHBfcmVxdWVzdF9mcmVlKGtwcF9yZXEpOwo+PiAr wqDCoMKgIGlmIChyZXQpCj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiZmFpbGVkIHRvIGNv bXB1dGUgc2hhcmVkIHNlY3JlZCwgZXJyICVkXG4iLCByZXQpOwo+PiArwqDCoMKgIGVsc2UKPj4g K8KgwqDCoMKgwqDCoMKgIHByX2RlYnVnKCIlczogc2hhcmVkIHNlY3JldCAlKnBoXG4iLCBfX2Z1 bmNfXywKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAoaW50KXJlcS0+c3EtPmRoY2hhcF9z a2V5X2xlbiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfc2tl eSk7Cj4+ICsKPj4gK8KgwqDCoCByZXR1cm4gcmV0Owo+PiArfQo+PiBkaWZmIC0tZ2l0IGEvZHJp dmVycy9udm1lL3RhcmdldC9jb25maWdmcy5jCj4+IGIvZHJpdmVycy9udm1lL3RhcmdldC9jb25m aWdmcy5jCj4+IGluZGV4IDI3MzU1NTEyNzE4OC4uZTA3NjA5MTFhNzYxIDEwMDY0NAo+PiAtLS0g YS9kcml2ZXJzL252bWUvdGFyZ2V0L2NvbmZpZ2ZzLmMKPj4gKysrIGIvZHJpdmVycy9udm1lL3Rh cmdldC9jb25maWdmcy5jCj4+IEBAIC0xMSw4ICsxMSwxMyBAQAo+PiDCoCAjaW5jbHVkZSA8bGlu dXgvY3R5cGUuaD4KPj4gwqAgI2luY2x1ZGUgPGxpbnV4L3BjaS5oPgo+PiDCoCAjaW5jbHVkZSA8 bGludXgvcGNpLXAycGRtYS5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9oYXNoLmg+Cj4+ICsjaW5j bHVkZSA8Y3J5cHRvL2twcC5oPgo+PiDCoCDCoCAjaW5jbHVkZSAibnZtZXQuaCIKPj4gKyNpZmRl ZiBDT05GSUdfTlZNRV9UQVJHRVRfQVVUSAo+PiArI2luY2x1ZGUgIi4uL2hvc3QvYXV0aC5oIgo+ PiArI2VuZGlmCj4+IMKgIMKgIHN0YXRpYyBjb25zdCBzdHJ1Y3QgY29uZmlnX2l0ZW1fdHlwZSBu dm1ldF9ob3N0X3R5cGU7Cj4+IMKgIHN0YXRpYyBjb25zdCBzdHJ1Y3QgY29uZmlnX2l0ZW1fdHlw ZSBudm1ldF9zdWJzeXNfdHlwZTsKPj4gQEAgLTE2NTYsMTAgKzE2NjEsNzEgQEAgc3RhdGljIGNv bnN0IHN0cnVjdCBjb25maWdfaXRlbV90eXBlCj4+IG52bWV0X3BvcnRzX3R5cGUgPSB7Cj4+IMKg IHN0YXRpYyBzdHJ1Y3QgY29uZmlnX2dyb3VwIG52bWV0X3N1YnN5c3RlbXNfZ3JvdXA7Cj4+IMKg IHN0YXRpYyBzdHJ1Y3QgY29uZmlnX2dyb3VwIG52bWV0X3BvcnRzX2dyb3VwOwo+PiDCoCAtc3Rh dGljIHZvaWQgbnZtZXRfaG9zdF9yZWxlYXNlKHN0cnVjdCBjb25maWdfaXRlbSAqaXRlbSkKPj4g KyNpZmRlZiBDT05GSUdfTlZNRV9UQVJHRVRfQVVUSAo+PiArc3RhdGljIHNzaXplX3QgbnZtZXRf aG9zdF9kaGNoYXBfa2V5X3Nob3coc3RydWN0IGNvbmZpZ19pdGVtICppdGVtLAo+PiArwqDCoMKg wqDCoMKgwqAgY2hhciAqcGFnZSkKPj4gK3sKPj4gK8KgwqDCoCB1OCAqZGhjaGFwX3NlY3JldCA9 IHRvX2hvc3QoaXRlbSktPmRoY2hhcF9zZWNyZXQ7Cj4+ICsKPj4gK8KgwqDCoCBpZiAoIWRoY2hh cF9zZWNyZXQpCj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gc3ByaW50ZihwYWdlLCAiXG4iKTsK Pj4gK8KgwqDCoCByZXR1cm4gc3ByaW50ZihwYWdlLCAiJXNcbiIsIGRoY2hhcF9zZWNyZXQpOwo+ PiArfQo+PiArCj4+ICtzdGF0aWMgc3NpemVfdCBudm1ldF9ob3N0X2RoY2hhcF9rZXlfc3RvcmUo c3RydWN0IGNvbmZpZ19pdGVtICppdGVtLAo+PiArwqDCoMKgwqDCoMKgwqAgY29uc3QgY2hhciAq cGFnZSwgc2l6ZV90IGNvdW50KQo+PiDCoCB7Cj4+IMKgwqDCoMKgwqAgc3RydWN0IG52bWV0X2hv c3QgKmhvc3QgPSB0b19ob3N0KGl0ZW0pOwo+PiArwqDCoMKgIGludCByZXQ7Cj4+IMKgICvCoMKg wqAgcmV0ID0gbnZtZXRfYXV0aF9zZXRfaG9zdF9rZXkoaG9zdCwgcGFnZSk7Cj4+ICvCoMKgwqAg aWYgKHJldCA8IDApCj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gcmV0Owo+PiArwqDCoMKgIHJl dHVybiBjb3VudDsKPj4gK30KPj4gKwo+PiArQ09ORklHRlNfQVRUUihudm1ldF9ob3N0XywgZGhj aGFwX2tleSk7Cj4+ICsKPj4gK3N0YXRpYyBzc2l6ZV90IG52bWV0X2hvc3RfZGhjaGFwX2hhc2hf c2hvdyhzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0sCj4+ICvCoMKgwqDCoMKgwqDCoCBjaGFyICpw YWdlKQo+PiArewo+PiArwqDCoMKgIHN0cnVjdCBudm1ldF9ob3N0ICpob3N0ID0gdG9faG9zdChp dGVtKTsKPj4gK8KgwqDCoCBjb25zdCBjaGFyICpoYXNoX25hbWUgPSBudm1lX2F1dGhfaG1hY19u YW1lKGhvc3QtPmRoY2hhcF9oYXNoX2lkKTsKPj4gKwo+PiArwqDCoMKgIHJldHVybiBzcHJpbnRm KHBhZ2UsICIlc1xuIiwgaGFzaF9uYW1lID8gaGFzaF9uYW1lIDogIm5vbmUiKTsKPj4gK30KPj4g Kwo+PiArc3RhdGljIHNzaXplX3QgbnZtZXRfaG9zdF9kaGNoYXBfaGFzaF9zdG9yZShzdHJ1Y3Qg Y29uZmlnX2l0ZW0gKml0ZW0sCj4+ICvCoMKgwqDCoMKgwqDCoCBjb25zdCBjaGFyICpwYWdlLCBz aXplX3QgY291bnQpCj4+ICt7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWV0X2hvc3QgKmhvc3QgPSB0 b19ob3N0KGl0ZW0pOwo+PiArwqDCoMKgIGludCBobWFjX2lkOwo+PiArCj4+ICvCoMKgwqAgaG1h Y19pZCA9IG52bWVfYXV0aF9obWFjX2lkKHBhZ2UpOwo+PiArwqDCoMKgIGlmIChobWFjX2lkIDwg MCkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArwqDCoMKgIGlmICghY3J5 cHRvX2hhc19zaGFzaChudm1lX2F1dGhfaG1hY19uYW1lKGhtYWNfaWQpLCAwLCAwKSkKPj4gK8Kg wqDCoMKgwqDCoMKgIHJldHVybiAtRU5PVFNVUFA7Cj4+ICvCoMKgwqAgaG9zdC0+ZGhjaGFwX2hh c2hfaWQgPSBobWFjX2lkOwo+PiArwqDCoMKgIHJldHVybiBjb3VudDsKPj4gK30KPj4gKwo+PiAr Q09ORklHRlNfQVRUUihudm1ldF9ob3N0XywgZGhjaGFwX2hhc2gpOwo+PiArCj4+ICtzdGF0aWMg c3RydWN0IGNvbmZpZ2ZzX2F0dHJpYnV0ZSAqbnZtZXRfaG9zdF9hdHRyc1tdID0gewo+PiArwqDC oMKgICZudm1ldF9ob3N0X2F0dHJfZGhjaGFwX2tleSwKPj4gK8KgwqDCoCAmbnZtZXRfaG9zdF9h dHRyX2RoY2hhcF9oYXNoLAo+PiArwqDCoMKgIE5VTEwsCj4+ICt9Owo+PiArI2VuZGlmIC8qIENP TkZJR19OVk1FX1RBUkdFVF9BVVRIICovCj4+ICsKPj4gK3N0YXRpYyB2b2lkIG52bWV0X2hvc3Rf cmVsZWFzZShzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0ZW0pCj4+ICt7Cj4+ICvCoMKgwqAgc3RydWN0 IG52bWV0X2hvc3QgKmhvc3QgPSB0b19ob3N0KGl0ZW0pOwo+PiArI2lmZGVmIENPTkZJR19OVk1F X1RBUkdFVF9BVVRICj4+ICvCoMKgwqAgaWYgKGhvc3QtPmRoY2hhcF9zZWNyZXQpCj4+ICvCoMKg wqDCoMKgwqDCoCBrZnJlZShob3N0LT5kaGNoYXBfc2VjcmV0KTsKPiAKPiBObyBuZWVkIGZvciBp ZiBjb25kaXRpb24uCj4gCgpPay4KCj4+ICsjZW5kaWYKPj4gwqDCoMKgwqDCoCBrZnJlZShob3N0 KTsKPj4gwqAgfQo+PiDCoCBAQCAtMTY2OSw2ICsxNzM1LDkgQEAgc3RhdGljIHN0cnVjdCBjb25m aWdmc19pdGVtX29wZXJhdGlvbnMKPj4gbnZtZXRfaG9zdF9pdGVtX29wcyA9IHsKPj4gwqAgwqAg c3RhdGljIGNvbnN0IHN0cnVjdCBjb25maWdfaXRlbV90eXBlIG52bWV0X2hvc3RfdHlwZSA9IHsK Pj4gwqDCoMKgwqDCoCAuY3RfaXRlbV9vcHPCoMKgwqDCoMKgwqDCoCA9ICZudm1ldF9ob3N0X2l0 ZW1fb3BzLAo+PiArI2lmZGVmIENPTkZJR19OVk1FX1RBUkdFVF9BVVRICj4+ICvCoMKgwqAgLmN0 X2F0dHJzwqDCoMKgwqDCoMKgwqAgPSBudm1ldF9ob3N0X2F0dHJzLAo+PiArI2VuZGlmCj4+IMKg wqDCoMKgwqAgLmN0X293bmVywqDCoMKgwqDCoMKgwqAgPSBUSElTX01PRFVMRSwKPj4gwqAgfTsK Pj4gwqAgZGlmZiAtLWdpdCBhL2RyaXZlcnMvbnZtZS90YXJnZXQvY29yZS5jIGIvZHJpdmVycy9u dm1lL3RhcmdldC9jb3JlLmMKPj4gaW5kZXggMTYzZjdkYzFhOTI5Li5iNWQ3OTcxZjU2NmIgMTAw NjQ0Cj4+IC0tLSBhL2RyaXZlcnMvbnZtZS90YXJnZXQvY29yZS5jCj4+ICsrKyBiL2RyaXZlcnMv bnZtZS90YXJnZXQvY29yZS5jCj4+IEBAIC03OTMsNiArNzkzLDcgQEAgdm9pZCBudm1ldF9zcV9k ZXN0cm95KHN0cnVjdCBudm1ldF9zcSAqc3EpCj4+IMKgwqDCoMKgwqAgd2FpdF9mb3JfY29tcGxl dGlvbigmc3EtPmNvbmZpcm1fZG9uZSk7Cj4+IMKgwqDCoMKgwqAgd2FpdF9mb3JfY29tcGxldGlv bigmc3EtPmZyZWVfZG9uZSk7Cj4+IMKgwqDCoMKgwqAgcGVyY3B1X3JlZl9leGl0KCZzcS0+cmVm KTsKPj4gK8KgwqDCoCBudm1ldF9hdXRoX3NxX2ZyZWUoc3EpOwo+PiDCoCDCoMKgwqDCoMKgIGlm IChjdHJsKSB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoCAvKgo+PiBAQCAtMTI2NCw2ICsxMjY1LDEx IEBAIHUxNiBudm1ldF9jaGVja19jdHJsX3N0YXR1cyhzdHJ1Y3QgbnZtZXRfcmVxICpyZXEpCj4+ IMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJlcS0+Y21kLT5jb21tb24ub3Bjb2Rl LCByZXEtPnNxLT5xaWQpOwo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJuIE5WTUVfU0NfQ01E X1NFUV9FUlJPUiB8IE5WTUVfU0NfRE5SOwo+PiDCoMKgwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKg IGlmICh1bmxpa2VseSghbnZtZXRfY2hlY2tfYXV0aF9zdGF0dXMocmVxKSkpIHsKPj4gK8KgwqDC oMKgwqDCoMKgIHByX3dhcm4oInFpZCAlZCBub3QgYXV0aGVudGljYXRlZFxuIiwgcmVxLT5zcS0+ cWlkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1FX1NDX0FVVEhfUkVRVUlSRUQgfCBO Vk1FX1NDX0ROUjsKPj4gK8KgwqDCoCB9Cj4+IMKgwqDCoMKgwqAgcmV0dXJuIDA7Cj4+IMKgIH0K Pj4gwqAgQEAgLTE0NTYsNiArMTQ2Miw4IEBAIHN0YXRpYyB2b2lkIG52bWV0X2N0cmxfZnJlZShz dHJ1Y3Qga3JlZiAqcmVmKQo+PiDCoMKgwqDCoMKgIGZsdXNoX3dvcmsoJmN0cmwtPmFzeW5jX2V2 ZW50X3dvcmspOwo+PiDCoMKgwqDCoMKgIGNhbmNlbF93b3JrX3N5bmMoJmN0cmwtPmZhdGFsX2Vy cl93b3JrKTsKPj4gwqAgK8KgwqDCoCBudm1ldF9yZXNldF9hdXRoKGN0cmwpOwo+PiArCj4+IMKg wqDCoMKgwqAgaWRhX3NpbXBsZV9yZW1vdmUoJmNudGxpZF9pZGEsIGN0cmwtPmNudGxpZCk7Cj4+ IMKgIMKgwqDCoMKgwqAgbnZtZXRfYXN5bmNfZXZlbnRzX2ZyZWUoY3RybCk7Cj4+IGRpZmYgLS1n aXQgYS9kcml2ZXJzL252bWUvdGFyZ2V0L2ZhYnJpY3MtY21kLWF1dGguYwo+PiBiL2RyaXZlcnMv bnZtZS90YXJnZXQvZmFicmljcy1jbWQtYXV0aC5jCj4+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+ IGluZGV4IDAwMDAwMDAwMDAwMC4uOTYyZjlmNWU5ZDg5Cj4+IC0tLSAvZGV2L251bGwKPj4gKysr IGIvZHJpdmVycy9udm1lL3RhcmdldC9mYWJyaWNzLWNtZC1hdXRoLmMKPj4gQEAgLTAsMCArMSw0 NjAgQEAKPj4gKy8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wCj4+ICsvKgo+PiAr ICogTlZNZSBvdmVyIEZhYnJpY3MgREgtSE1BQy1DSEFQIGF1dGhlbnRpY2F0aW9uIGNvbW1hbmQg aGFuZGxpbmcuCj4+ICsgKiBDb3B5cmlnaHQgKGMpIDIwMjAgSGFubmVzIFJlaW5lY2tlLCBTVVNF IFNvZnR3YXJlIFNvbHV0aW9ucy4KPj4gKyAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuCj4+ICsgKi8K Pj4gKyNkZWZpbmUgcHJfZm10KGZtdCkgS0JVSUxEX01PRE5BTUUgIjogIiBmbXQKPj4gKyNpbmNs dWRlIDxsaW51eC9ibGtkZXYuaD4KPj4gKyNpbmNsdWRlIDxsaW51eC9yYW5kb20uaD4KPj4gKyNp bmNsdWRlIDxjcnlwdG8vaGFzaC5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9rcHAuaD4KPj4gKyNp bmNsdWRlICJudm1ldC5oIgo+PiArI2luY2x1ZGUgIi4uL2hvc3QvYXV0aC5oIgo+PiArCj4+ICt2 b2lkIG52bWV0X2luaXRfYXV0aChzdHJ1Y3QgbnZtZXRfY3RybCAqY3RybCwgc3RydWN0IG52bWV0 X3JlcSAqcmVxKQo+PiArewo+PiArwqDCoMKgIC8qIEluaXRpYWxpemUgaW4tYmFuZCBhdXRoZW50 aWNhdGlvbiAqLwo+PiArwqDCoMKgIHJlcS0+c3EtPmF1dGhlbnRpY2F0ZWQgPSBmYWxzZTsKPj4g K8KgwqDCoCByZXEtPnNxLT5kaGNoYXBfc3RlcCA9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9O RUdPVElBVEU7Cj4+ICvCoMKgwqAgcmVxLT5jcWUtPnJlc3VsdC51MzIgfD0gMHgyIDw8IDE2Owo+ IAo+IENhbiB5b3UgYWRkIGEgZGVmaW5lIGZvciB0aGlzOiBOVk1FX0NPTk5FQ1RfQVVUSFJFUV9J TkJBTkQKPiAKClN1cmUuCgo+PiArfQo+PiArCj4+ICtzdGF0aWMgdTE2IG52bWV0X2F1dGhfbmVn b3RpYXRlKHN0cnVjdCBudm1ldF9yZXEgKnJlcSwgdm9pZCAqZCkKPj4gK3sKPj4gK8KgwqDCoCBz dHJ1Y3QgbnZtZXRfY3RybCAqY3RybCA9IHJlcS0+c3EtPmN0cmw7Cj4+ICvCoMKgwqAgc3RydWN0 IG52bWZfYXV0aF9kaGNoYXBfbmVnb3RpYXRlX2RhdGEgKmRhdGEgPSBkOwo+PiArwqDCoMKgIGlu dCBpLCBoYXNoX2lkLCBudWxsX2RoID0gLTE7Cj4+ICsKPj4gK8KgwqDCoCBwcl9kZWJ1ZygiJXM6 IGN0cmwgJWQgcWlkICVkOiBkYXRhIHNjX2QgJWQgbmFwZCAlZCBhdXRoaWQgJWQKPj4gaGFsZW4g JWQgZGhsZW4gJWRcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgIF9fZnVuY19fLCBjdHJsLT5jbnRs aWQsIHJlcS0+c3EtPnFpZCwKPj4gK8KgwqDCoMKgwqDCoMKgwqAgZGF0YS0+c2NfYywgZGF0YS0+ bmFwZCwgZGF0YS0+YXV0aF9wcm90b2NvbFswXS5kaGNoYXAuYXV0aGlkLAo+PiArwqDCoMKgwqDC oMKgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5oYWxlbiwKPj4gK8KgwqDCoMKg wqDCoMKgwqAgZGF0YS0+YXV0aF9wcm90b2NvbFswXS5kaGNoYXAuZGhsZW4pOwo+PiArwqDCoMKg IHJlcS0+c3EtPmRoY2hhcF90aWQgPSBsZTE2X3RvX2NwdShkYXRhLT50X2lkKTsKPj4gK8KgwqDC oCBpZiAoZGF0YS0+c2NfYykKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1FX0FVVEhfREhD SEFQX0ZBSUxVUkVfQ09OQ0FUX01JU01BVENIOwo+PiArCj4+ICvCoMKgwqAgaWYgKGRhdGEtPm5h cGQgIT0gMSkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1FX0FVVEhfREhDSEFQX0ZBSUxV UkVfSEFTSF9VTlVTQUJMRTsKPj4gKwo+PiArwqDCoMKgIGlmIChkYXRhLT5hdXRoX3Byb3RvY29s WzBdLmRoY2hhcC5hdXRoaWQgIT0gMHgwMSkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1F X0FVVEhfREhDSEFQX0ZBSUxVUkVfSU5WQUxJRF9QQVlMT0FEOwo+PiArCj4+ICvCoMKgwqAgaGFz aF9pZCA9IG52bWVfYXV0aF9obWFjX2lkKGNyeXB0b19zaGFzaF9hbGdfbmFtZShjdHJsLT5zaGFz aF90Zm0pKTsKPj4gK8KgwqDCoCBmb3IgKGkgPSAwOyBpIDwgZGF0YS0+YXV0aF9wcm90b2NvbFsw XS5kaGNoYXAuaGFsZW47IGkrKykgewo+PiArwqDCoMKgwqDCoMKgwqAgcHJfZGVidWcoIiVzOiBj dHJsICVkIHFpZCAlZCBjaGVja2luZyBoYXNoICVkIGZvciAlZFxuIiwKPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBfX2Z1bmNfXywgY3RybC0+Y250bGlkLCByZXEtPnNxLT5xaWQsCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZGF0YS0+YXV0aF9wcm90b2NvbFswXS5kaGNoYXAuaWRs aXN0W2ldLCBoYXNoX2lkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmIChoYXNoX2lkICE9IGRhdGEt PmF1dGhfcHJvdG9jb2xbMF0uZGhjaGFwLmlkbGlzdFtpXSkKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAgY29udGludWU7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfaGFzaF9p ZCA9IGhhc2hfaWQ7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfaGFzaF9sZW4g PQo+PiBjcnlwdG9fc2hhc2hfZGlnZXN0c2l6ZShjdHJsLT5zaGFzaF90Zm0pOwo+PiArwqDCoMKg wqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmIChyZXEtPnNxLT5kaGNo YXBfaGFzaF9pZCA9PSAwKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiJXM6IGN0cmwg JWQgcWlkICVkOiBubyB1c2FibGUgaGFzaCBmb3VuZFxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBfX2Z1bmNfXywgY3RybC0+Y250bGlkLCByZXEtPnNxLT5xaWQpOwo+PiArwqDCoMKg wqDCoMKgwqAgcmV0dXJuIE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9IQVNIX1VOVVNBQkxFOwo+ PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIGZvciAoaSA9IGRhdGEtPmF1dGhfcHJvdG9jb2xb MF0uZGhjaGFwLmhhbGVuOwo+PiArwqDCoMKgwqDCoMKgwqDCoCBpIDwgZGF0YS0+YXV0aF9wcm90 b2NvbFswXS5kaGNoYXAuaGFsZW4gKwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRhdGEt PmF1dGhfcHJvdG9jb2xbMF0uZGhjaGFwLmRobGVuOyBpKyspIHsKPj4gK8KgwqDCoMKgwqDCoMKg IGludCBkaGdpZCA9IGRhdGEtPmF1dGhfcHJvdG9jb2xbMF0uZGhjaGFwLmlkbGlzdFtpXTsKPj4g Kwo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKGRoZ2lkID09IE5WTUVfQVVUSF9ESENIQVBfREhHUk9V UF9OVUxMKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIG51bGxfZGggPSBkaGdpZDsKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY29udGludWU7Cj4+ICvCoMKgwqDCoMKgwqDCoCB9Cj4+ ICvCoMKgwqDCoMKgwqDCoCBpZiAobnZtZXRfc2V0dXBfZGhncm91cChjdHJsLCBkaGdpZCkgPT0g MCkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqAgfQo+PiArwqDC oMKgIGlmICghY3RybC0+ZGhfdGZtICYmIG51bGxfZGggPCAwKSB7Cj4+ICvCoMKgwqDCoMKgwqDC oCBwcl9kZWJ1ZygiJXM6IGN0cmwgJWQgcWlkICVkOiBubyBESCBncm91cCBzZWxlY3RlZFxuIiwK Pj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBfX2Z1bmNfXywgY3RybC0+Y250bGlkLCByZXEt PnNxLT5xaWQpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIE5WTUVfQVVUSF9ESENIQVBfRkFJ TFVSRV9ESEdST1VQX1VOVVNBQkxFOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBpZiAoY3RybC0+ ZGhfZ2lkID09IC0xKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBjdHJsLT5kaF9naWQgPSBudWxsX2Ro Owo+PiArwqDCoMKgwqDCoMKgwqAgY3RybC0+ZGhfdGZtID0gTlVMTDsKPj4gK8KgwqDCoCB9Cj4+ ICvCoMKgwqAgcHJfZGVidWcoIiVzOiBjdHJsICVkIHFpZCAlZDogREggZ3JvdXAgJXMgKCVkKVxu IiwKPj4gK8KgwqDCoMKgwqDCoMKgwqAgX19mdW5jX18sIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+ cWlkLAo+PiArwqDCoMKgwqDCoMKgwqDCoCBudm1lX2F1dGhfZGhncm91cF9uYW1lKGN0cmwtPmRo X2dpZCksIGN0cmwtPmRoX2dpZCk7Cj4+ICvCoMKgwqAgcmV0dXJuIDA7Cj4+ICt9Cj4+ICsKPj4g K3N0YXRpYyB1MTYgbnZtZXRfYXV0aF9yZXBseShzdHJ1Y3QgbnZtZXRfcmVxICpyZXEsIHZvaWQg KmQpCj4+ICt7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWV0X2N0cmwgKmN0cmwgPSByZXEtPnNxLT5j dHJsOwo+PiArwqDCoMKgIHN0cnVjdCBudm1mX2F1dGhfZGhjaGFwX3JlcGx5X2RhdGEgKmRhdGEg PSBkOwo+PiArwqDCoMKgIHU4ICpyZXNwb25zZTsKPj4gKwo+PiArwqDCoMKgIHByX2RlYnVnKCIl czogY3RybCAlZCBxaWQgJWQ6IGRhdGEgaGwgJWQgY3ZhbGlkICVkIGRodmxlbiAlZFxuIiwKPj4g K8KgwqDCoMKgwqDCoMKgwqAgX19mdW5jX18sIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+cWlkLAo+ PiArwqDCoMKgwqDCoMKgwqDCoCBkYXRhLT5obCwgZGF0YS0+Y3ZhbGlkLCBkYXRhLT5kaHZsZW4p Owo+PiArwqDCoMKgIGlmIChkYXRhLT5obCAhPSByZXEtPnNxLT5kaGNoYXBfaGFzaF9sZW4pCj4+ ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOVkFMSURf UEFZTE9BRDsKPj4gKwo+PiArwqDCoMKgIGlmIChkYXRhLT5kaHZsZW4pIHsKPj4gK8KgwqDCoMKg wqDCoMKgIHJldHVybiBOVk1FX0FVVEhfREhDSEFQX0ZBSUxVUkVfSU5WQUxJRF9QQVlMT0FEOwo+ PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIHJlc3BvbnNlID0ga21hbGxvYyhkYXRhLT5obCwg R0ZQX0tFUk5FTCk7Cj4+ICvCoMKgwqAgaWYgKCFyZXNwb25zZSkKPj4gK8KgwqDCoMKgwqDCoMKg IHJldHVybiBOVk1FX0FVVEhfREhDSEFQX0ZBSUxVUkVfRkFJTEVEOwo+PiArCj4+ICvCoMKgwqAg aWYgKG52bWV0X2F1dGhfaG9zdF9oYXNoKHJlcSwgcmVzcG9uc2UsIGRhdGEtPmhsKSA8IDApIHsK Pj4gK8KgwqDCoMKgwqDCoMKgIHByX2RlYnVnKCJjdHJsICVkIHFpZCAlZCBESC1ITUFDLUNIQVAg aGFzaCBmYWlsZWRcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlk LCByZXEtPnNxLT5xaWQpOwo+PiArwqDCoMKgwqDCoMKgwqAga2ZyZWUocmVzcG9uc2UpOwo+PiAr wqDCoMKgwqDCoMKgwqAgcmV0dXJuIE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9GQUlMRUQ7Cj4+ ICvCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqAgaWYgKG1lbWNtcChkYXRhLT5ydmFsLCByZXNwb25z ZSwgZGF0YS0+aGwpKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9pbmZvKCJjdHJsICVkIHFpZCAl ZCBESC1ITUFDLUNIQVAgcmVzcG9uc2UgbWlzbWF0Y2hcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+cWlkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGtm cmVlKHJlc3BvbnNlKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1FX0FVVEhfREhDSEFQ X0ZBSUxVUkVfRkFJTEVEOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBrZnJlZShyZXNwb25zZSk7 Cj4+ICvCoMKgwqAgcHJfaW5mbygiY3RybCAlZCBxaWQgJWQgREgtSE1BQy1DSEFQIGhvc3QgYXV0 aGVudGljYXRlZFxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+ cWlkKTsKPj4gK8KgwqDCoCBpZiAoZGF0YS0+Y3ZhbGlkKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBy ZXEtPnNxLT5kaGNoYXBfYzIgPSBrbWFsbG9jKGRhdGEtPmhsLCBHRlBfS0VSTkVMKTsKPj4gK8Kg wqDCoMKgwqDCoMKgIGlmICghcmVxLT5zcS0+ZGhjaGFwX2MyKQo+PiArwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCByZXR1cm4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0ZBSUxFRDsKPj4gK8KgwqDC oMKgwqDCoMKgIG1lbWNweShyZXEtPnNxLT5kaGNoYXBfYzIsIGRhdGEtPnJ2YWwgKyBkYXRhLT5o bCwgZGF0YS0+aGwpOwo+PiArCj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiY3RybCAlZCBx aWQgJWQgY2hhbGxlbmdlICUqcGhcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3Ry bC0+Y250bGlkLCByZXEtPnNxLT5xaWQsIGRhdGEtPmhsLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9jMik7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5k aGNoYXBfczIgPSBsZTMyX3RvX2NwdShkYXRhLT5zZXFudW0pOwo+PiArwqDCoMKgIH0gZWxzZQo+ PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX2MyID0gTlVMTDsKPj4gKwo+PiArwqDC oMKgIHJldHVybiAwOwo+PiArfQo+PiArCj4+ICtzdGF0aWMgdTE2IG52bWV0X2F1dGhfZmFpbHVy ZTIoc3RydWN0IG52bWV0X3JlcSAqcmVxLCB2b2lkICpkKQo+PiArewo+PiArwqDCoMKgIHN0cnVj dCBudm1mX2F1dGhfZGhjaGFwX2ZhaWx1cmVfZGF0YSAqZGF0YSA9IGQ7Cj4+ICsKPj4gK8KgwqDC oCByZXR1cm4gZGF0YS0+cmVhc29uX2NvZGVfZXhwbGFuYXRpb247Cj4+ICt9Cj4+ICsKPj4gK3Zv aWQgbnZtZXRfZXhlY3V0ZV9hdXRoX3NlbmQoc3RydWN0IG52bWV0X3JlcSAqcmVxKQo+PiArewo+ PiArwqDCoMKgIHN0cnVjdCBudm1ldF9jdHJsICpjdHJsID0gcmVxLT5zcS0+Y3RybDsKPj4gK8Kg wqDCoCBzdHJ1Y3QgbnZtZl9hdXRoX2RoY2hhcF9zdWNjZXNzMl9kYXRhICpkYXRhOwo+PiArwqDC oMKgIHZvaWQgKmQ7Cj4+ICvCoMKgwqAgdTMyIHRsOwo+PiArwqDCoMKgIHUxNiBzdGF0dXMgPSAw Owo+PiArCj4+ICvCoMKgwqAgaWYgKHJlcS0+Y21kLT5hdXRoX3NlbmQuc2VjcCAhPQo+PiBOVk1F X0FVVEhfREhDSEFQX1BST1RPQ09MX0lERU5USUZJRVIpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHN0 YXR1cyA9IE5WTUVfU0NfSU5WQUxJRF9GSUVMRCB8IE5WTUVfU0NfRE5SOwo+PiArwqDCoMKgwqDC oMKgwqAgcmVxLT5lcnJvcl9sb2MgPQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBvZmZzZXRv ZihzdHJ1Y3QgbnZtZl9hdXRoX3NlbmRfY29tbWFuZCwgc2VjcCk7Cj4+ICvCoMKgwqDCoMKgwqDC oCBnb3RvIGRvbmU7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmIChyZXEtPmNtZC0+YXV0aF9z ZW5kLnNwc3AwICE9IDB4MDEpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHN0YXR1cyA9IE5WTUVfU0Nf SU5WQUxJRF9GSUVMRCB8IE5WTUVfU0NfRE5SOwo+PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5lcnJv cl9sb2MgPQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBvZmZzZXRvZihzdHJ1Y3QgbnZtZl9h dXRoX3NlbmRfY29tbWFuZCwgc3BzcDApOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBkb25lOwo+ PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBpZiAocmVxLT5jbWQtPmF1dGhfc2VuZC5zcHNwMSAhPSAw eDAxKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBzdGF0dXMgPSBOVk1FX1NDX0lOVkFMSURfRklFTEQg fCBOVk1FX1NDX0ROUjsKPj4gK8KgwqDCoMKgwqDCoMKgIHJlcS0+ZXJyb3JfbG9jID0KPj4gK8Kg wqDCoMKgwqDCoMKgwqDCoMKgwqAgb2Zmc2V0b2Yoc3RydWN0IG52bWZfYXV0aF9zZW5kX2NvbW1h bmQsIHNwc3AxKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gZG9uZTsKPj4gK8KgwqDCoCB9Cj4+ ICvCoMKgwqAgdGwgPSBsZTMyX3RvX2NwdShyZXEtPmNtZC0+YXV0aF9zZW5kLnRsKTsKPj4gK8Kg wqDCoCBpZiAoIXRsKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBzdGF0dXMgPSBOVk1FX1NDX0lOVkFM SURfRklFTEQgfCBOVk1FX1NDX0ROUjsKPj4gK8KgwqDCoMKgwqDCoMKgIHJlcS0+ZXJyb3JfbG9j ID0KPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgb2Zmc2V0b2Yoc3RydWN0IG52bWZfYXV0aF9z ZW5kX2NvbW1hbmQsIHRsKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gZG9uZTsKPj4gK8KgwqDC oCB9Cj4+ICvCoMKgwqAgaWYgKCFudm1ldF9jaGVja190cmFuc2Zlcl9sZW4ocmVxLCB0bCkpIHsK Pj4gK8KgwqDCoMKgwqDCoMKgIHByX2RlYnVnKCIlczogdHJhbnNmZXIgbGVuZ3RoIG1pc21hdGNo ICgldSlcbiIsIF9fZnVuY19fLCB0bCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm47Cj4+ICvC oMKgwqAgfQo+PiArCj4+ICvCoMKgwqAgZCA9IGttYWxsb2ModGwsIEdGUF9LRVJORUwpOwo+PiAr wqDCoMKgIGlmICghZCkgewo+PiArwqDCoMKgwqDCoMKgwqAgc3RhdHVzID0gTlZNRV9TQ19JTlRF Uk5BTDsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gZG9uZTsKPj4gK8KgwqDCoCB9Cj4+ICsKPj4g K8KgwqDCoCBzdGF0dXMgPSBudm1ldF9jb3B5X2Zyb21fc2dsKHJlcSwgMCwgZCwgdGwpOwo+PiAr wqDCoMKgIGlmIChzdGF0dXMpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGtmcmVlKGQpOwo+PiArwqDC oMKgwqDCoMKgwqAgZ290byBkb25lOwo+PiArwqDCoMKgIH0KPj4gKwo+IAo+IFRoaXMgd2hvbGUg YmxvY2sgYmVsb3cgc2hvdWxkIG1vdmUgdG8gc29tZXRoaW5nIGxpa2UKPiBudm1ldF9wcm9jZXNz X2F1dGhfc2VuZF9kYXRhKCkKPiAKCk9rLgoKPj4gK8KgwqDCoCBkYXRhID0gZDsKPj4gK8KgwqDC oCBwcl9kZWJ1ZygiJXM6IGN0cmwgJWQgcWlkICVkIHR5cGUgJWQgaWQgJWQgc3RlcCAleFxuIiwg X19mdW5jX18sCj4+ICvCoMKgwqDCoMKgwqDCoMKgIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+cWlk LCBkYXRhLT5hdXRoX3R5cGUsIGRhdGEtPmF1dGhfaWQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgIHJl cS0+c3EtPmRoY2hhcF9zdGVwKTsKPj4gK8KgwqDCoCBpZiAoZGF0YS0+YXV0aF90eXBlICE9IE5W TUVfQVVUSF9DT01NT05fTUVTU0FHRVMgJiYKPj4gK8KgwqDCoMKgwqDCoMKgIGRhdGEtPmF1dGhf dHlwZSAhPSBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VTKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBy ZXEtPnNxLT5kaGNoYXBfc3RlcCA9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9GQUlMVVJFMTsK Pj4gK8KgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGF0dXMgPQo+PiBOVk1FX0FVVEhf REhDSEFQX0ZBSUxVUkVfSU5WQUxJRF9NRVNTQUdFOwo+PiArwqDCoMKgIH0gZWxzZSBpZiAoZGF0 YS0+YXV0aF90eXBlID09IE5WTUVfQVVUSF9DT01NT05fTUVTU0FHRVMpIHsKPj4gK8KgwqDCoMKg wqDCoMKgIGlmIChkYXRhLT5hdXRoX2lkICE9IHJlcS0+c3EtPmRoY2hhcF9zdGVwKSB7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGVwID0gTlZNRV9BVVRIX0RI Q0hBUF9NRVNTQUdFX0ZBSUxVUkUxOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNx LT5kaGNoYXBfc3RhdHVzID0KPj4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOVkFMSURfTUVT U0FHRTsKPj4gK8KgwqDCoMKgwqDCoMKgIH0gZWxzZSBpZiAoZGF0YS0+YXV0aF9pZCAhPQo+PiBO Vk1FX0FVVEhfREhDSEFQX01FU1NBR0VfTkVHT1RJQVRFKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGVwID0gTlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFX0ZB SUxVUkUxOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfc3RhdHVz ID0KPj4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOVkFMSURfTUVTU0FHRTsKPj4gK8KgwqDC oMKgwqDCoMKgIH0gZWxzZSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIC8qIFZhbGlkYXRl IG5lZ290aWF0aW9uIHBhcmFtZXRlcnMgKi8KPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgc3Rh dHVzID0gbnZtZXRfYXV0aF9uZWdvdGlhdGUocmVxLCBkKTsKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAgaWYgKHN0YXR1cyA9PSAwKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IHJlcS0+c3EtPmRoY2hhcF9zdGVwID0KPj4gTlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFX0NIQUxM RU5HRTsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZWxzZSB7Cj4+ICvCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0ZXAgPQo+PiBOVk1FX0FVVEhfREhD SEFQX01FU1NBR0VfRkFJTFVSRTE7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAg cmVxLT5zcS0+ZGhjaGFwX3N0YXR1cyA9IHN0YXR1czsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoCBzdGF0dXMgPSAwOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB9Cj4+ICvC oMKgwqDCoMKgwqDCoCB9Cj4+ICvCoMKgwqAgfSBlbHNlIGlmIChkYXRhLT5hdXRoX3R5cGUgPT0g TlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFUykgewo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKGRhdGEt PmF1dGhfaWQgIT0gcmVxLT5zcS0+ZGhjaGFwX3N0ZXApIHsKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAgcHJfZGVidWcoIiVzOiBjdHJsICVkIHFpZCAlZCBzdGVwIG1pc21hdGNoICglZCAhPSAl ZClcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBfX2Z1bmNfXywgY3Ry bC0+Y250bGlkLCByZXEtPnNxLT5xaWQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCBkYXRhLT5hdXRoX2lkLCByZXEtPnNxLT5kaGNoYXBfc3RlcCk7Cj4+ICvCoMKgwqDCoMKg wqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGVwID0gTlZNRV9BVVRIX0RIQ0hBUF9NRVNT QUdFX0ZBSUxVUkUxOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBf c3RhdHVzID0KPj4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOVkFMSURfTUVTU0FHRTsKPj4g K8KgwqDCoMKgwqDCoMKgIH0gZWxzZSBpZiAobGUxNl90b19jcHUoZGF0YS0+dF9pZCkgIT0gcmVx LT5zcS0+ZGhjaGFwX3RpZCkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1Zygi JXM6IGN0cmwgJWQgcWlkICVkIGludmFsaWQgdHJhbnNhY3Rpb24gJWQKPj4gKGV4cGVjdGVkICVk KVxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIF9fZnVuY19fLCBjdHJs LT5jbnRsaWQsIHJlcS0+c3EtPnFpZCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGxlMTZfdG9fY3B1KGRhdGEtPnRfaWQpLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3RpZCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IHJlcS0+c3EtPmRoY2hhcF9zdGVwID0gTlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFX0ZBSUxVUkUx Owo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfc3RhdHVzID0KPj4g TlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOVkFMSURfUEFZTE9BRDsKPj4gK8KgwqDCoMKgwqDC oMKgIH0gZWxzZSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN3aXRjaCAoZGF0YS0+YXV0 aF9pZCkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjYXNlIE5WTUVfQVVUSF9ESENIQVBf TUVTU0FHRV9SRVBMWToKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzdGF0dXMg PSBudm1ldF9hdXRoX3JlcGx5KHJlcSwgZCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgaWYgKHN0YXR1cyA9PSAwKQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0ZXAgPQo+PiBOVk1FX0FVVEhfREhDSEFQX01FU1NB R0VfU1VDQ0VTUzE7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZWxzZSB7Cj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBf c3RlcCA9Cj4+IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9GQUlMVVJFMTsKPj4gK8KgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGF0dXMgPSBz dGF0dXM7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzdGF0dXMg PSAwOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY2Fz ZSBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VfU1VDQ0VTUzI6Cj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+YXV0aGVudGljYXRlZCA9IHRydWU7Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcHJfZGVidWcoIiVzOiBjdHJsICVkIHFpZCAlZCBhdXRo ZW50aWNhdGVkXG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oCBfX2Z1bmNfXywgY3RybC0+Y250bGlkLCByZXEtPnNxLT5xaWQpOwo+PiArwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjYXNl IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9GQUlMVVJFMjoKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBzdGF0dXMgPSBudm1ldF9hdXRoX2ZhaWx1cmUyKHJlcSwgZCk7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgaWYgKHN0YXR1cykgewo+PiArwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcHJfd2FybigiY3RybCAlZCBxaWQgJWQ6IERI LUhNQUMtQ0hBUCBuZWdvdGlhdGlvbgo+PiBmYWlsZWQgKCVkKVxuIiwKPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlkLCByZXEtPnNx LT5xaWQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IHN0YXR1cyk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEt PnNxLT5kaGNoYXBfc3RhdHVzID0gc3RhdHVzOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgc3RhdHVzID0gMDsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCB9Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGRlZmF1bHQ6Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0YXR1cyA9Cj4+IE5WTUVfQVVUSF9ESENIQVBfRkFJTFVS RV9JTlZBTElEX01FU1NBR0U7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmVx LT5zcS0+ZGhjaGFwX3N0ZXAgPQo+PiBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VfRkFJTFVSRTI7 Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqDCoMKg wqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoCB9IGVsc2Ugewo+ PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0YXR1cyA9Cj4+IE5WTUVfQVVUSF9E SENIQVBfRkFJTFVSRV9JTlZBTElEX01FU1NBR0U7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPnNx LT5kaGNoYXBfc3RlcCA9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9GQUlMVVJFMjsKPj4gK8Kg wqDCoCB9Cj4+ICvCoMKgwqAga2ZyZWUoZCk7Cj4+ICtkb25lOgo+PiArwqDCoMKgIHByX2RlYnVn KCIlczogY3RybCAlZCBxaWQgJWQgZGhjaGFwIHN0YXR1cyAleCBzdGVwICV4XG4iLCBfX2Z1bmNf XywKPj4gK8KgwqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlkLCByZXEtPnNxLT5xaWQsCj4+ICvC oMKgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGF0dXMsIHJlcS0+c3EtPmRoY2hhcF9z dGVwKTsKPj4gK8KgwqDCoCBpZiAoc3RhdHVzKQo+PiArwqDCoMKgwqDCoMKgwqAgcHJfZGVidWco IiVzOiBjdHJsICVkIHFpZCAlZCBudm1lIHN0YXR1cyAleCBlcnJvciBsb2MgJWRcbiIsCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgX19mdW5jX18sIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+ cWlkLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0YXR1cywgcmVxLT5lcnJvcl9sb2Mp Owo+PiArwqDCoMKgIHJlcS0+Y3FlLT5yZXN1bHQudTY0ID0gMDsKPj4gK8KgwqDCoCBudm1ldF9y ZXFfY29tcGxldGUocmVxLCBzdGF0dXMpOwo+PiArwqDCoMKgIGlmIChyZXEtPnNxLT5kaGNoYXBf c3RlcCAhPSBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VfU1VDQ0VTUzIgJiYKPj4gK8KgwqDCoMKg wqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGVwICE9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9G QUlMVVJFMikKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybjsKPj4gK8KgwqDCoCAvKiBGaW5hbCBz dGF0ZXMsIGNsZWFyIHVwIHZhcmlhYmxlcyAqLwo+PiArwqDCoMKgIGtmcmVlKHJlcS0+c3EtPmRo Y2hhcF9jMSk7Cj4+ICvCoMKgwqAga2ZyZWUocmVxLT5zcS0+ZGhjaGFwX2MyKTsKPj4gK8KgwqDC oCBpZiAocmVxLT5zcS0+ZGhjaGFwX3N0ZXAgPT0gTlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFX0ZB SUxVUkUyKQo+PiArwqDCoMKgwqDCoMKgwqAgbnZtZXRfY3RybF9mYXRhbF9lcnJvcihjdHJsKTsK Pj4gK30KPj4gKwo+PiArc3RhdGljIGludCBudm1ldF9hdXRoX2NoYWxsZW5nZShzdHJ1Y3QgbnZt ZXRfcmVxICpyZXEsIHZvaWQgKmQsIGludCBhbCkKPiAKPiBudm1ldF9hdXRoX3NldF9jaGFsbGVu Z2UKPiAKCk9rLgoKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZl9hdXRoX2RoY2hhcF9jaGFs bGVuZ2VfZGF0YSAqZGF0YSA9IGQ7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWV0X2N0cmwgKmN0cmwg PSByZXEtPnNxLT5jdHJsOwo+PiArwqDCoMKgIGludCByZXQgPSAwOwo+PiArwqDCoMKgIGludCBk YXRhX3NpemUgPSBzaXplb2YoKmQpICsgcmVxLT5zcS0+ZGhjaGFwX2hhc2hfbGVuOwo+PiArCj4+ ICvCoMKgwqAgaWYgKGFsIDwgZGF0YV9zaXplKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1 ZygiJXM6IGJ1ZmZlciB0b28gc21hbGwgKGFsICVkIG5lZWQgJWQpXG4iLCBfX2Z1bmNfXywKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBhbCwgZGF0YV9zaXplKTsKPj4gK8KgwqDCoMKgwqDC oMKgIHJldHVybiAtRUlOVkFMOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBtZW1zZXQoZGF0YSwg MCwgZGF0YV9zaXplKTsKPj4gK8KgwqDCoCByZXEtPnNxLT5kaGNoYXBfczEgPSBjdHJsLT5kaGNo YXBfc2VxbnVtKys7Cj4+ICvCoMKgwqAgZGF0YS0+YXV0aF90eXBlID0gTlZNRV9BVVRIX0RIQ0hB UF9NRVNTQUdFUzsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX2lkID0gTlZNRV9BVVRIX0RIQ0hBUF9N RVNTQUdFX0NIQUxMRU5HRTsKPj4gK8KgwqDCoCBkYXRhLT50X2lkID0gY3B1X3RvX2xlMTYocmVx LT5zcS0+ZGhjaGFwX3RpZCk7Cj4+ICvCoMKgwqAgZGF0YS0+aGFzaGlkID0gcmVxLT5zcS0+ZGhj aGFwX2hhc2hfaWQ7Cj4+ICvCoMKgwqAgZGF0YS0+aGwgPSByZXEtPnNxLT5kaGNoYXBfaGFzaF9s ZW47Cj4+ICvCoMKgwqAgZGF0YS0+c2VxbnVtID0gY3B1X3RvX2xlMzIocmVxLT5zcS0+ZGhjaGFw X3MxKTsKPj4gK8KgwqDCoCByZXEtPnNxLT5kaGNoYXBfYzEgPSBrbWFsbG9jKGRhdGEtPmhsLCBH RlBfS0VSTkVMKTsKPj4gK8KgwqDCoCBpZiAoIXJlcS0+c3EtPmRoY2hhcF9jMSkKPj4gK8KgwqDC oMKgwqDCoMKgIHJldHVybiAtRU5PTUVNOwo+PiArwqDCoMKgIGdldF9yYW5kb21fYnl0ZXMocmVx LT5zcS0+ZGhjaGFwX2MxLCBkYXRhLT5obCk7Cj4+ICvCoMKgwqAgbWVtY3B5KGRhdGEtPmN2YWws IHJlcS0+c3EtPmRoY2hhcF9jMSwgZGF0YS0+aGwpOwo+PiArwqDCoMKgIHByX2RlYnVnKCIlczog Y3RybCAlZCBxaWQgJWQgc2VxICVkIHRyYW5zYWN0aW9uICVkIGhsICVkIGRodmxlbgo+PiAlZFxu IiwKPj4gK8KgwqDCoMKgwqDCoMKgwqAgX19mdW5jX18sIGN0cmwtPmNudGxpZCwgcmVxLT5zcS0+ cWlkLCByZXEtPnNxLT5kaGNoYXBfczEsCj4+ICvCoMKgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRo Y2hhcF90aWQsIGRhdGEtPmhsLCBkYXRhLT5kaHZsZW4pOwo+PiArwqDCoMKgIHJldHVybiByZXQ7 Cj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyBpbnQgbnZtZXRfYXV0aF9zdWNjZXNzMShzdHJ1Y3QgbnZt ZXRfcmVxICpyZXEsIHZvaWQgKmQsIGludCBhbCkKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZt Zl9hdXRoX2RoY2hhcF9zdWNjZXNzMV9kYXRhICpkYXRhID0gZDsKPj4gK8KgwqDCoCBzdHJ1Y3Qg bnZtZXRfY3RybCAqY3RybCA9IHJlcS0+c3EtPmN0cmw7Cj4+ICsKPj4gK8KgwqDCoCBXQVJOX09O KGFsIDwgc2l6ZW9mKCpkYXRhKSk7Cj4+ICvCoMKgwqAgbWVtc2V0KGRhdGEsIDAsIHNpemVvZigq ZGF0YSkpOwo+PiArwqDCoMKgIGRhdGEtPmF1dGhfdHlwZSA9IE5WTUVfQVVUSF9ESENIQVBfTUVT U0FHRVM7Cj4+ICvCoMKgwqAgZGF0YS0+YXV0aF9pZCA9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FH RV9TVUNDRVNTMTsKPj4gK8KgwqDCoCBkYXRhLT50X2lkID0gY3B1X3RvX2xlMTYocmVxLT5zcS0+ ZGhjaGFwX3RpZCk7Cj4+ICvCoMKgwqAgZGF0YS0+aGwgPSByZXEtPnNxLT5kaGNoYXBfaGFzaF9s ZW47Cj4+ICvCoMKgwqAgaWYgKHJlcS0+c3EtPmRoY2hhcF9jMikgewo+PiArwqDCoMKgwqDCoMKg wqAgaWYgKG52bWV0X2F1dGhfY3RybF9oYXNoKHJlcSwgZGF0YS0+cnZhbCwgZGF0YS0+aGwpKQo+ PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJF X0hBU0hfVU5VU0FCTEU7Cj4+ICvCoMKgwqDCoMKgwqDCoCBkYXRhLT5ydmFsaWQgPSAxOwo+PiAr wqDCoMKgwqDCoMKgwqAgcHJfZGVidWcoImN0cmwgJWQgcWlkICVkIHJlc3BvbnNlICUqcGhcbiIs Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlkLCByZXEtPnNxLT5xaWQs IGRhdGEtPmhsLCBkYXRhLT5ydmFsKTsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgcmV0dXJuIDA7 Cj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyB2b2lkIG52bWV0X2F1dGhfZmFpbHVyZTEoc3RydWN0IG52 bWV0X3JlcSAqcmVxLCB2b2lkICpkLCBpbnQgYWwpCj4+ICt7Cj4+ICvCoMKgwqAgc3RydWN0IG52 bWZfYXV0aF9kaGNoYXBfZmFpbHVyZV9kYXRhICpkYXRhID0gZDsKPj4gKwo+PiArwqDCoMKgIFdB Uk5fT04oYWwgPCBzaXplb2YoKmRhdGEpKTsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3R5cGUgPSBO Vk1FX0FVVEhfQ09NTU9OX01FU1NBR0VTOwo+PiArwqDCoMKgIGRhdGEtPmF1dGhfaWQgPSBOVk1F X0FVVEhfREhDSEFQX01FU1NBR0VfRkFJTFVSRTE7Cj4+ICvCoMKgwqAgZGF0YS0+dF9pZCA9IGNw dV90b19sZTMyKHJlcS0+c3EtPmRoY2hhcF90aWQpOwo+PiArwqDCoMKgIGRhdGEtPnJlYXNvbl9j b2RlID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX1JFQVNPTl9GQUlMRUQ7Cj4+ICvCoMKgwqAg ZGF0YS0+cmVhc29uX2NvZGVfZXhwbGFuYXRpb24gPSByZXEtPnNxLT5kaGNoYXBfc3RhdHVzOwo+ PiArfQo+PiArCj4+ICt2b2lkIG52bWV0X2V4ZWN1dGVfYXV0aF9yZWNlaXZlKHN0cnVjdCBudm1l dF9yZXEgKnJlcSkKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZXRfY3RybCAqY3RybCA9IHJl cS0+c3EtPmN0cmw7Cj4+ICvCoMKgwqAgdm9pZCAqZDsKPj4gK8KgwqDCoCB1MzIgYWw7Cj4+ICvC oMKgwqAgdTE2IHN0YXR1cyA9IDA7Cj4+ICsKPj4gK8KgwqDCoCBpZiAocmVxLT5jbWQtPmF1dGhf cmVjZWl2ZS5zZWNwICE9Cj4+IE5WTUVfQVVUSF9ESENIQVBfUFJPVE9DT0xfSURFTlRJRklFUikg ewo+PiArwqDCoMKgwqDCoMKgwqAgc3RhdHVzID0gTlZNRV9TQ19JTlZBTElEX0ZJRUxEIHwgTlZN RV9TQ19ETlI7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPmVycm9yX2xvYyA9Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIG9mZnNldG9mKHN0cnVjdCBudm1mX2F1dGhfcmVjZWl2ZV9jb21tYW5k LCBzZWNwKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gZG9uZTsKPj4gK8KgwqDCoCB9Cj4+ICvC oMKgwqAgaWYgKHJlcS0+Y21kLT5hdXRoX3JlY2VpdmUuc3BzcDAgIT0gMHgwMSkgewo+PiArwqDC oMKgwqDCoMKgwqAgc3RhdHVzID0gTlZNRV9TQ19JTlZBTElEX0ZJRUxEIHwgTlZNRV9TQ19ETlI7 Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPmVycm9yX2xvYyA9Cj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgIG9mZnNldG9mKHN0cnVjdCBudm1mX2F1dGhfcmVjZWl2ZV9jb21tYW5kLCBzcHNwMCk7 Cj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIGRvbmU7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlm IChyZXEtPmNtZC0+YXV0aF9yZWNlaXZlLnNwc3AxICE9IDB4MDEpIHsKPj4gK8KgwqDCoMKgwqDC oMKgIHN0YXR1cyA9IE5WTUVfU0NfSU5WQUxJRF9GSUVMRCB8IE5WTUVfU0NfRE5SOwo+PiArwqDC oMKgwqDCoMKgwqAgcmVxLT5lcnJvcl9sb2MgPQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBv ZmZzZXRvZihzdHJ1Y3QgbnZtZl9hdXRoX3JlY2VpdmVfY29tbWFuZCwgc3BzcDEpOwo+PiArwqDC oMKgwqDCoMKgwqAgZ290byBkb25lOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBhbCA9IGxlMzJf dG9fY3B1KHJlcS0+Y21kLT5hdXRoX3JlY2VpdmUuYWwpOwo+PiArwqDCoMKgIGlmICghYWwpIHsK Pj4gK8KgwqDCoMKgwqDCoMKgIHN0YXR1cyA9IE5WTUVfU0NfSU5WQUxJRF9GSUVMRCB8IE5WTUVf U0NfRE5SOwo+PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5lcnJvcl9sb2MgPQo+PiArwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBvZmZzZXRvZihzdHJ1Y3QgbnZtZl9hdXRoX3JlY2VpdmVfY29tbWFuZCwg YWwpOwo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBkb25lOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDC oCBpZiAoIW52bWV0X2NoZWNrX3RyYW5zZmVyX2xlbihyZXEsIGFsKSkgewo+PiArwqDCoMKgwqDC oMKgwqAgcHJfZGVidWcoIiVzOiB0cmFuc2ZlciBsZW5ndGggbWlzbWF0Y2ggKCV1KVxuIiwgX19m dW5jX18sIGFsKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybjsKPj4gK8KgwqDCoCB9Cj4+ICsK Pj4gK8KgwqDCoCBkID0ga21hbGxvYyhhbCwgR0ZQX0tFUk5FTCk7Cj4+ICvCoMKgwqAgaWYgKCFk KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBzdGF0dXMgPSBOVk1FX1NDX0lOVEVSTkFMOwo+PiArwqDC oMKgwqDCoMKgwqAgZ290byBkb25lOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBwcl9kZWJ1Zygi JXM6IGN0cmwgJWQgcWlkICVkIHN0ZXAgJXhcbiIsIF9fZnVuY19fLAo+PiArwqDCoMKgwqDCoMKg wqDCoCBjdHJsLT5jbnRsaWQsIHJlcS0+c3EtPnFpZCwgcmVxLT5zcS0+ZGhjaGFwX3N0ZXApOwo+ PiArwqDCoMKgIHN3aXRjaCAocmVxLT5zcS0+ZGhjaGFwX3N0ZXApIHsKPj4gK8KgwqDCoCBjYXNl IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9DSEFMTEVOR0U6Cj4+ICvCoMKgwqDCoMKgwqDCoCBz dGF0dXMgPSBudm1ldF9hdXRoX2NoYWxsZW5nZShyZXEsIGQsIGFsKTsKPj4gK8KgwqDCoMKgwqDC oMKgIGlmIChzdGF0dXMgPCAwKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHByX3dhcm4o ImN0cmwgJWQgcWlkICVkOiBjaGFsbGVuZ2UgZXJyb3IgKCVkKVxuIiwKPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoCBjdHJsLT5jbnRsaWQsIHJlcS0+c3EtPnFpZCwgc3RhdHVzKTsK Pj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgc3RhdHVzID0gTlZNRV9TQ19JTlRFUk5BTDsKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqDCoMKgwqDCoCB9Cj4+ICvC oMKgwqDCoMKgwqDCoCBpZiAoc3RhdHVzKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJl cS0+c3EtPmRoY2hhcF9zdGF0dXMgPSBzdGF0dXM7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg IG52bWV0X2F1dGhfZmFpbHVyZTEocmVxLCBkLCBhbCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIHByX3dhcm4oImN0cmwgJWQgcWlkICVkOiBjaGFsbGVuZ2Ugc3RhdHVzICgleClcbiIsCj4+ ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3RybC0+Y250bGlkLCByZXEtPnNxLT5x aWQsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0 YXR1cyk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0YXR1cyA9IDA7Cj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArwqDCoMKgwqDC oMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0ZXAgPSBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VfUkVQ TFk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gK8KgwqDCoCBjYXNlIE5WTUVfQVVUSF9E SENIQVBfTUVTU0FHRV9TVUNDRVNTMToKPj4gK8KgwqDCoMKgwqDCoMKgIHN0YXR1cyA9IG52bWV0 X2F1dGhfc3VjY2VzczEocmVxLCBkLCBhbCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpZiAoc3RhdHVz KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJlcS0+c3EtPmRoY2hhcF9zdGF0dXMgPSBz dGF0dXM7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIG52bWV0X2F1dGhfZmFpbHVyZTEocmVx LCBkLCBhbCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHByX3dhcm4oImN0cmwgJWQgcWlk ICVkOiBzdWNjZXNzMSBzdGF0dXMgKCV4KVxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoCBjdHJsLT5jbnRsaWQsIHJlcS0+c3EtPnFpZCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNoYXBfc3RhdHVzKTsKPj4gK8KgwqDCoMKgwqDCoMKg wqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqDCoMKgwqDCoCB9Cj4+ICvCoMKgwqDCoMKgwqDCoCBy ZXEtPnNxLT5kaGNoYXBfc3RlcCA9IE5WTUVfQVVUSF9ESENIQVBfTUVTU0FHRV9TVUNDRVNTMjsK Pj4gK8KgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiArwqDCoMKgIGNhc2UgTlZNRV9BVVRIX0RIQ0hB UF9NRVNTQUdFX0ZBSUxVUkUxOgo+PiArwqDCoMKgwqDCoMKgwqAgbnZtZXRfYXV0aF9mYWlsdXJl MShyZXEsIGQsIGFsKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHByX3dhcm4oImN0cmwgJWQgcWlkICVk IGZhaWx1cmUxICgleClcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGN0cmwtPmNudGxp ZCwgcmVxLT5zcS0+cWlkLCByZXEtPnNxLT5kaGNoYXBfc3RhdHVzKTsKPj4gK8KgwqDCoMKgwqDC oMKgIGJyZWFrOwo+PiArwqDCoMKgIGRlZmF1bHQ6Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl93YXJu KCJjdHJsICVkIHFpZCAlZCB1bmhhbmRsZWQgc3RlcCAoJWQpXG4iLAo+PiArwqDCoMKgwqDCoMKg wqDCoMKgwqDCoCBjdHJsLT5jbnRsaWQsIHJlcS0+c3EtPnFpZCwgcmVxLT5zcS0+ZGhjaGFwX3N0 ZXApOwo+PiArwqDCoMKgwqDCoMKgwqAgcmVxLT5zcS0+ZGhjaGFwX3N0ZXAgPSBOVk1FX0FVVEhf REhDSEFQX01FU1NBR0VfRkFJTFVSRTE7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPnNxLT5kaGNo YXBfc3RhdHVzID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0ZBSUxFRDsKPj4gK8KgwqDCoMKg wqDCoMKgIG52bWV0X2F1dGhfZmFpbHVyZTEocmVxLCBkLCBhbCk7Cj4+ICvCoMKgwqDCoMKgwqDC oCBzdGF0dXMgPSAwOwo+PiArwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqAgfQo+PiAr Cj4+ICvCoMKgwqAgc3RhdHVzID0gbnZtZXRfY29weV90b19zZ2wocmVxLCAwLCBkLCBhbCk7Cj4+ ICvCoMKgwqAga2ZyZWUoZCk7Cj4+ICtkb25lOgo+PiArwqDCoMKgIHJlcS0+Y3FlLT5yZXN1bHQu dTY0ID0gMDsKPj4gK8KgwqDCoCBudm1ldF9yZXFfY29tcGxldGUocmVxLCBzdGF0dXMpOwo+PiAr wqDCoMKgIGlmIChyZXEtPnNxLT5kaGNoYXBfc3RlcCA9PSBOVk1FX0FVVEhfREhDSEFQX01FU1NB R0VfRkFJTFVSRTEpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGtmcmVlKHJlcS0+c3EtPmRoY2hhcF9j MSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShyZXEtPnNxLT5kaGNoYXBfYzIpOwo+PiArwqDC oMKgwqDCoMKgwqAgbnZtZXRfY3RybF9mYXRhbF9lcnJvcihjdHJsKTsKPj4gK8KgwqDCoCB9Cj4+ ICt9Cj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL252bWUvdGFyZ2V0L2ZhYnJpY3MtY21kLmMKPj4g Yi9kcml2ZXJzL252bWUvdGFyZ2V0L2ZhYnJpY3MtY21kLmMKPj4gaW5kZXggN2QwZjM1MjNmZGFi Li41M2ZiODUzY2Q4ZmUgMTAwNjQ0Cj4+IC0tLSBhL2RyaXZlcnMvbnZtZS90YXJnZXQvZmFicmlj cy1jbWQuYwo+PiArKysgYi9kcml2ZXJzL252bWUvdGFyZ2V0L2ZhYnJpY3MtY21kLmMKPj4gQEAg LTkzLDYgKzkzLDE0IEBAIHUxNiBudm1ldF9wYXJzZV9mYWJyaWNzX2NtZChzdHJ1Y3QgbnZtZXRf cmVxICpyZXEpCj4+IMKgwqDCoMKgwqAgY2FzZSBudm1lX2ZhYnJpY3NfdHlwZV9wcm9wZXJ0eV9n ZXQ6Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPmV4ZWN1dGUgPSBudm1ldF9leGVjdXRlX3By b3BfZ2V0Owo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICsjaWZkZWYgQ09ORklHX05W TUVfVEFSR0VUX0FVVEgKPj4gK8KgwqDCoCBjYXNlIG52bWVfZmFicmljc190eXBlX2F1dGhfc2Vu ZDoKPj4gK8KgwqDCoMKgwqDCoMKgIHJlcS0+ZXhlY3V0ZSA9IG52bWV0X2V4ZWN1dGVfYXV0aF9z ZW5kOwo+PiArwqDCoMKgwqDCoMKgwqAgYnJlYWs7Cj4+ICvCoMKgwqAgY2FzZSBudm1lX2ZhYnJp Y3NfdHlwZV9hdXRoX3JlY2VpdmU6Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXEtPmV4ZWN1dGUgPSBu dm1ldF9leGVjdXRlX2F1dGhfcmVjZWl2ZTsKPj4gK8KgwqDCoMKgwqDCoMKgIGJyZWFrOwo+PiAr I2VuZGlmCj4+IMKgwqDCoMKgwqAgZGVmYXVsdDoKPj4gwqDCoMKgwqDCoMKgwqDCoMKgIHByX2Rl YnVnKCJyZWNlaXZlZCB1bmtub3duIGNhcHN1bGUgdHlwZSAweCV4XG4iLAo+PiDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBjbWQtPmZhYnJpY3MuZmN0eXBlKTsKPj4gQEAgLTE1NSw2ICsxNjMs NyBAQCBzdGF0aWMgdm9pZCBudm1ldF9leGVjdXRlX2FkbWluX2Nvbm5lY3Qoc3RydWN0Cj4+IG52 bWV0X3JlcSAqcmVxKQo+PiDCoMKgwqDCoMKgIHN0cnVjdCBudm1mX2Nvbm5lY3RfZGF0YSAqZDsK Pj4gwqDCoMKgwqDCoCBzdHJ1Y3QgbnZtZXRfY3RybCAqY3RybCA9IE5VTEw7Cj4+IMKgwqDCoMKg wqAgdTE2IHN0YXR1cyA9IDA7Cj4+ICvCoMKgwqAgaW50IHJldDsKPj4gwqAgwqDCoMKgwqDCoCBp ZiAoIW52bWV0X2NoZWNrX3RyYW5zZmVyX2xlbihyZXEsIHNpemVvZihzdHJ1Y3QKPj4gbnZtZl9j b25uZWN0X2RhdGEpKSkKPj4gwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybjsKPj4gQEAgLTE5Nywx NyArMjA2LDMxIEBAIHN0YXRpYyB2b2lkIG52bWV0X2V4ZWN1dGVfYWRtaW5fY29ubmVjdChzdHJ1 Y3QKPj4gbnZtZXRfcmVxICpyZXEpCj4+IMKgIMKgwqDCoMKgwqAgdXVpZF9jb3B5KCZjdHJsLT5o b3N0aWQsICZkLT5ob3N0aWQpOwo+PiDCoCArwqDCoMKgIHJldCA9IG52bWV0X3NldHVwX2F1dGgo Y3RybCwgcmVxKTsKPj4gK8KgwqDCoCBpZiAocmV0IDwgMCkgewo+PiArwqDCoMKgwqDCoMKgwqAg cHJfZXJyKCJGYWlsZWQgdG8gc2V0dXAgYXV0aGVudGljYXRpb24sIGVycm9yICVkXG4iLCByZXQp Owo+PiArwqDCoMKgwqDCoMKgwqAgbnZtZXRfY3RybF9wdXQoY3RybCk7Cj4+ICvCoMKgwqDCoMKg wqDCoCBpZiAocmV0ID09IC1FUEVSTSkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgc3RhdHVz ID0gKE5WTUVfU0NfQ09OTkVDVF9JTlZBTElEX0hPU1QgfCBOVk1FX1NDX0ROUik7Cj4+ICvCoMKg wqDCoMKgwqDCoCBlbHNlCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHN0YXR1cyA9IE5WTUVf U0NfSU5URVJOQUw7Cj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsKPj4gK8KgwqDCoCB9Cj4+ ICsKPj4gwqDCoMKgwqDCoCBzdGF0dXMgPSBudm1ldF9pbnN0YWxsX3F1ZXVlKGN0cmwsIHJlcSk7 Cj4+IMKgwqDCoMKgwqAgaWYgKHN0YXR1cykgewo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgbnZtZXRf Y3RybF9wdXQoY3RybCk7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsKPj4gwqDCoMKg wqDCoCB9Cj4+IMKgIC3CoMKgwqAgcHJfaW5mbygiY3JlYXRpbmcgY29udHJvbGxlciAlZCBmb3Ig c3Vic3lzdGVtICVzIGZvciBOUU4gJXMlcy5cbiIsCj4+ICvCoMKgwqAgcHJfaW5mbygiY3JlYXRp bmcgY29udHJvbGxlciAlZCBmb3Igc3Vic3lzdGVtICVzIGZvciBOUU4gJXMlcyVzLlxuIiwKPj4g wqDCoMKgwqDCoMKgwqDCoMKgIGN0cmwtPmNudGxpZCwgY3RybC0+c3Vic3lzLT5zdWJzeXNucW4s IGN0cmwtPmhvc3RucW4sCj4+IC3CoMKgwqDCoMKgwqDCoCBjdHJsLT5waV9zdXBwb3J0ID8gIiBU MTAtUEkgaXMgZW5hYmxlZCIgOiAiIik7Cj4+ICvCoMKgwqDCoMKgwqDCoCBjdHJsLT5waV9zdXBw b3J0ID8gIiBUMTAtUEkgaXMgZW5hYmxlZCIgOiAiIiwKPj4gK8KgwqDCoMKgwqDCoMKgIG52bWV0 X2hhc19hdXRoKGN0cmwpID8gIiB3aXRoIERILUhNQUMtQ0hBUCIgOiAiIik7Cj4+IMKgwqDCoMKg wqAgcmVxLT5jcWUtPnJlc3VsdC51MTYgPSBjcHVfdG9fbGUxNihjdHJsLT5jbnRsaWQpOwo+PiDC oCArwqDCoMKgIGlmIChudm1ldF9oYXNfYXV0aChjdHJsKSkKPj4gK8KgwqDCoMKgwqDCoMKgIG52 bWV0X2luaXRfYXV0aChjdHJsLCByZXEpOwo+PiDCoCBvdXQ6Cj4+IMKgwqDCoMKgwqAga2ZyZWUo ZCk7Cj4+IMKgIGNvbXBsZXRlOgo+PiBAQCAtMjY3LDYgKzI5MCw5IEBAIHN0YXRpYyB2b2lkIG52 bWV0X2V4ZWN1dGVfaW9fY29ubmVjdChzdHJ1Y3QKPj4gbnZtZXRfcmVxICpyZXEpCj4+IMKgwqDC oMKgwqAgfQo+PiDCoCDCoMKgwqDCoMKgIHByX2RlYnVnKCJhZGRpbmcgcXVldWUgJWQgdG8gY3Ry bCAlZC5cbiIsIHFpZCwgY3RybC0+Y250bGlkKTsKPj4gK8KgwqDCoCByZXEtPmNxZS0+cmVzdWx0 LnUxNiA9IGNwdV90b19sZTE2KGN0cmwtPmNudGxpZCk7Cj4gCj4gSXMgdGhpcyByZWxhdGVkIHRv IHRoZSBwYXRjaD8KPiAKCkhhdmUgdG8gY2hlY2suIEkgdGhvdWdodCBpdCBkaWQsIGJ1dCBub3cg dGhhdCB5b3UgbWVudGlvbiBpdC4uLgoKPj4gK8KgwqDCoCBpZiAobnZtZXRfaGFzX2F1dGgoY3Ry bCkpCj4+ICvCoMKgwqDCoMKgwqDCoCBudm1ldF9pbml0X2F1dGgoY3RybCwgcmVxKTsKPj4gwqAg wqAgb3V0Ogo+PiDCoMKgwqDCoMKgIGtmcmVlKGQpOwo+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9u dm1lL3RhcmdldC9udm1ldC5oIGIvZHJpdmVycy9udm1lL3RhcmdldC9udm1ldC5oCj4+IGluZGV4 IDA2ZGQzZDUzN2YwNy4uZWY4ODE1ZTEzN2Q3IDEwMDY0NAo+PiAtLS0gYS9kcml2ZXJzL252bWUv dGFyZ2V0L252bWV0LmgKPj4gKysrIGIvZHJpdmVycy9udm1lL3RhcmdldC9udm1ldC5oCj4+IEBA IC0xMDgsNiArMTA4LDIwIEBAIHN0cnVjdCBudm1ldF9zcSB7Cj4+IMKgwqDCoMKgwqAgdTE2wqDC oMKgwqDCoMKgwqDCoMKgwqDCoCBzaXplOwo+PiDCoMKgwqDCoMKgIHUzMsKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgc3FoZDsKPj4gwqDCoMKgwqDCoCBib29swqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBz cWhkX2Rpc2FibGVkOwo+PiArI2lmZGVmIENPTkZJR19OVk1FX1RBUkdFVF9BVVRICj4+ICvCoMKg wqAgYm9vbMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgYXV0aGVudGljYXRlZDsKPj4gK8KgwqDCoCB1 MTbCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRoY2hhcF90aWQ7Cj4+ICvCoMKgwqAgdTE2wqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBkaGNoYXBfc3RhdHVzOwo+PiArwqDCoMKgIGludMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgZGhjaGFwX3N0ZXA7Cj4+ICvCoMKgwqAgdTjCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGRoY2hhcF9oYXNoX2lkOwo+PiArwqDCoMKgIHU4wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBk aGNoYXBfaGFzaF9sZW47Cj4+ICvCoMKgwqAgdTjCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICpkaGNo YXBfYzE7Cj4+ICvCoMKgwqAgdTjCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICpkaGNoYXBfYzI7Cj4+ ICvCoMKgwqAgdTMywqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkaGNoYXBfczE7Cj4+ICvCoMKgwqAg dTMywqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkaGNoYXBfczI7Cj4+ICvCoMKgwqAgdTjCoMKgwqDC oMKgwqDCoMKgwqDCoMKgICpkaGNoYXBfc2tleTsKPj4gK8KgwqDCoCBpbnTCoMKgwqDCoMKgwqDC oMKgwqDCoMKgIGRoY2hhcF9za2V5X2xlbjsKPj4gKyNlbmRpZgo+PiDCoMKgwqDCoMKgIHN0cnVj dCBjb21wbGV0aW9uwqDCoMKgIGZyZWVfZG9uZTsKPj4gwqDCoMKgwqDCoCBzdHJ1Y3QgY29tcGxl dGlvbsKgwqDCoCBjb25maXJtX2RvbmU7Cj4+IMKgIH07Cj4+IEBAIC0yMDksNiArMjIzLDE1IEBA IHN0cnVjdCBudm1ldF9jdHJsIHsKPj4gwqDCoMKgwqDCoCB1NjTCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGVycl9jb3VudGVyOwo+PiDCoMKgwqDCoMKgIHN0cnVjdCBudm1lX2Vycm9yX3Nsb3TCoMKg wqAgc2xvdHNbTlZNRVRfRVJST1JfTE9HX1NMT1RTXTsKPj4gwqDCoMKgwqDCoCBib29swqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBwaV9zdXBwb3J0Owo+PiArI2lmZGVmIENPTkZJR19OVk1FX1RBUkdF VF9BVVRICj4+ICvCoMKgwqAgdTMywqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkaGNoYXBfc2VxbnVt Owo+PiArwqDCoMKgIHU4wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAqZGhjaGFwX2tleTsKPj4gK8Kg wqDCoCBzaXplX3TCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRoY2hhcF9rZXlfbGVuOwo+PiArwqDC oMKgIHN0cnVjdCBjcnlwdG9fc2hhc2jCoMKgwqAgKnNoYXNoX3RmbTsKPj4gK8KgwqDCoCBzdHJ1 Y3QgY3J5cHRvX2twcMKgwqDCoCAqZGhfdGZtOwo+PiArwqDCoMKgIHUzMsKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgZGhfZ2lkOwo+PiArwqDCoMKgIHUzMsKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgZGhf a2V5c2l6ZTsKPj4gKyNlbmRpZgo+PiDCoCB9Owo+PiDCoCDCoCBzdHJ1Y3QgbnZtZXRfc3Vic3lz IHsKPj4gQEAgLTI3MCw2ICsyOTMsMTAgQEAgc3RhdGljIGlubGluZSBzdHJ1Y3QgbnZtZXRfc3Vi c3lzCj4+ICpuYW1lc3BhY2VzX3RvX3N1YnN5cygKPj4gwqAgwqAgc3RydWN0IG52bWV0X2hvc3Qg ewo+PiDCoMKgwqDCoMKgIHN0cnVjdCBjb25maWdfZ3JvdXDCoMKgwqAgZ3JvdXA7Cj4+ICvCoMKg wqAgdTjCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICpkaGNoYXBfc2VjcmV0Owo+PiArwqDCoMKgIHU4 wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkaGNoYXBfa2V5X2hhc2g7Cj4+ICvCoMKgwqAgdTjCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGRoY2hhcF9oYXNoX2lkOwo+PiArwqDCoMKgIHU4wqDCoMKgwqDC oMKgwqDCoMKgwqDCoCBkaGNoYXBfZGhncm91cF9pZDsKPj4gwqAgfTsKPj4gwqAgwqAgc3RhdGlj IGlubGluZSBzdHJ1Y3QgbnZtZXRfaG9zdCAqdG9faG9zdChzdHJ1Y3QgY29uZmlnX2l0ZW0gKml0 ZW0pCj4+IEBAIC02NTksNCArNjg2LDQ4IEBAIHN0YXRpYyBpbmxpbmUgdm9pZCBudm1ldF9yZXFf YmlvX3B1dChzdHJ1Y3QKPj4gbnZtZXRfcmVxICpyZXEsIHN0cnVjdCBiaW8gKmJpbykKPj4gwqDC oMKgwqDCoMKgwqDCoMKgIGJpb19wdXQoYmlvKTsKPj4gwqAgfQo+PiDCoCArI2lmZGVmIENPTkZJ R19OVk1FX1RBUkdFVF9BVVRICj4+ICt2b2lkIG52bWV0X2V4ZWN1dGVfYXV0aF9zZW5kKHN0cnVj dCBudm1ldF9yZXEgKnJlcSk7Cj4+ICt2b2lkIG52bWV0X2V4ZWN1dGVfYXV0aF9yZWNlaXZlKHN0 cnVjdCBudm1ldF9yZXEgKnJlcSk7Cj4+ICtpbnQgbnZtZXRfYXV0aF9zZXRfaG9zdF9rZXkoc3Ry dWN0IG52bWV0X2hvc3QgKmhvc3QsIGNvbnN0IGNoYXIKPj4gKnNlY3JldCk7Cj4+ICtpbnQgbnZt ZXRfYXV0aF9zZXRfaG9zdF9oYXNoKHN0cnVjdCBudm1ldF9ob3N0ICpob3N0LCBjb25zdCBjaGFy ICpoYXNoKTsKPj4gK2ludCBudm1ldF9zZXR1cF9hdXRoKHN0cnVjdCBudm1ldF9jdHJsICpjdHJs LCBzdHJ1Y3QgbnZtZXRfcmVxICpyZXEpOwo+PiArdm9pZCBudm1ldF9pbml0X2F1dGgoc3RydWN0 IG52bWV0X2N0cmwgKmN0cmwsIHN0cnVjdCBudm1ldF9yZXEgKnJlcSk7Cj4+ICt2b2lkIG52bWV0 X3Jlc2V0X2F1dGgoc3RydWN0IG52bWV0X2N0cmwgKmN0cmwpOwo+PiArdm9pZCBudm1ldF9hdXRo X3NxX2ZyZWUoc3RydWN0IG52bWV0X3NxICpzcSk7Cj4+ICtpbnQgbnZtZXRfc2V0dXBfZGhncm91 cChzdHJ1Y3QgbnZtZXRfY3RybCAqY3RybCwgaW50IGRoZ3JvdXBfaWQpOwo+PiArYm9vbCBudm1l dF9jaGVja19hdXRoX3N0YXR1cyhzdHJ1Y3QgbnZtZXRfcmVxICpyZXEpOwo+PiAraW50IG52bWV0 X2F1dGhfaG9zdF9oYXNoKHN0cnVjdCBudm1ldF9yZXEgKnJlcSwgdTggKnJlc3BvbnNlLAo+PiAr wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHVuc2lnbmVkIGludCBoYXNoX2xlbik7Cj4+ICtpbnQg bnZtZXRfYXV0aF9jdHJsX2hhc2goc3RydWN0IG52bWV0X3JlcSAqcmVxLCB1OCAqcmVzcG9uc2Us Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgdW5zaWduZWQgaW50IGhhc2hfbGVuKTsKPj4g K3N0YXRpYyBpbmxpbmUgYm9vbCBudm1ldF9oYXNfYXV0aChzdHJ1Y3QgbnZtZXRfY3RybCAqY3Ry bCkKPj4gK3sKPj4gK8KgwqDCoCByZXR1cm4gY3RybC0+c2hhc2hfdGZtICE9IE5VTEw7Cj4+ICt9 Cj4+ICtpbnQgbnZtZXRfYXV0aF9jdHJsX2V4cG9uZW50aWFsKHN0cnVjdCBudm1ldF9yZXEgKnJl cSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1OCAqYnVmLCBpbnQgYnVmX3Np emUpOwo+PiAraW50IG52bWV0X2F1dGhfY3RybF9zZXNza2V5KHN0cnVjdCBudm1ldF9yZXEgKnJl cSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCB1OCAqYnVmLCBpbnQgYnVmX3Np emUpOwo+PiArI2Vsc2UKPj4gK3N0YXRpYyBpbmxpbmUgaW50IG52bWV0X3NldHVwX2F1dGgoc3Ry dWN0IG52bWV0X2N0cmwgKmN0cmwsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqAgc3RydWN0IG52bWV0X3JlcSAqcmVxKQo+PiArewo+PiArwqDCoMKgIHJldHVybiAwOwo+ PiArfQo+PiArc3RhdGljIGlubGluZSB2b2lkIG52bWV0X2luaXRfYXV0aChzdHJ1Y3QgbnZtZXRf Y3RybCAqY3RybCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBzdHJ1 Y3QgbnZtZXRfcmVxICpyZXEpIHt9Owo+PiArc3RhdGljIGlubGluZSB2b2lkIG52bWV0X3Jlc2V0 X2F1dGgoc3RydWN0IG52bWV0X2N0cmwgKmN0cmwpIHt9Owo+PiArc3RhdGljIGlubGluZSB2b2lk IG52bWV0X2F1dGhfc3FfZnJlZShzdHJ1Y3QgbnZtZXRfc3EgKnNxKSB7fTsKPj4gK3N0YXRpYyBp bmxpbmUgYm9vbCBudm1ldF9jaGVja19hdXRoX3N0YXR1cyhzdHJ1Y3QgbnZtZXRfcmVxICpyZXEp Cj4+ICt7Cj4+ICvCoMKgwqAgcmV0dXJuIHRydWU7Cj4+ICt9Cj4+ICtzdGF0aWMgaW5saW5lIGJv b2wgbnZtZXRfaGFzX2F1dGgoc3RydWN0IG52bWV0X2N0cmwgKmN0cmwpCj4+ICt7Cj4+ICvCoMKg wqAgcmV0dXJuIGZhbHNlOwo+PiArfQo+PiArc3RhdGljIGlubGluZSBjb25zdCBjaGFyICpudm1l dF9kaGNoYXBfZGhncm91cF9uYW1lKGludCBkaGdpZCkgewo+PiByZXR1cm4gTlVMTDsgfQo+PiAr I2VuZGlmCj4+ICsKPj4gwqAgI2VuZGlmIC8qIF9OVk1FVF9IICovCj4+Cj4gCgpUaGFua3MgZm9y IHRoZSByZXZpZXchCgpDaGVlcnMsCgpIYW5uZXMKLS0gCkRyLiBIYW5uZXMgUmVpbmVja2UJCSAg ICAgICAgICAgS2VybmVsIFN0b3JhZ2UgQXJjaGl0ZWN0CmhhcmVAc3VzZS5kZQkJCSAgICAgICAg ICAgICAgICAgICs0OSA5MTEgNzQwNTMgNjg4ClNVU0UgU29mdHdhcmUgU29sdXRpb25zIEdlcm1h bnkgR21iSCwgTWF4ZmVsZHN0ci4gNSwgOTA0MDkgTsO8cm5iZXJnCkhSQiAzNjgwOSAoQUcgTsO8 cm5iZXJnKSwgR0Y6IEZlbGl4IEltZW5kw7ZyZmZlcgoKX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX18KTGludXgtbnZtZSBtYWlsaW5nIGxpc3QKTGludXgtbnZt ZUBsaXN0cy5pbmZyYWRlYWQub3JnCmh0dHA6Ly9saXN0cy5pbmZyYWRlYWQub3JnL21haWxtYW4v bGlzdGluZm8vbGludXgtbnZtZQo=