From 5053c8d42ff6a9b43c598345d67a2bfca23e0cca Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Thu, 21 May 2026 13:51:52 +0000 Subject: [PATCH] feat(config-repl): wire handle_upgrade_config() via raw packet interception Pre-decode opcode peek in myreq_handler() captures raw packet bytes for OP_upgradeConfig before xce_decode() frees the capnp segment. Passes raw bytes to xce_upgrade_config_req_extract_raw() to extract sha256 and config blob, then delegates to do_upgrade_config_with_bytes(). Removes Task 11 dependency. Phase 2 still gated by CONF_ENABLE_UPGRADE_CONFIG. --- src/controller/cluster.c | 30 ++++++++++++++++++- src/controller/config_repl.c | 56 ++++++++++++++++++++---------------- src/controller/config_repl.h | 7 +++-- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src/controller/cluster.c b/src/controller/cluster.c index 2b6244cc..08408501 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -8,6 +8,7 @@ #include "cluster.h" #include "config_repl.h" +#include "controller.capnp.h" #include "dataq.h" #include "htab.h" #include "kvmgr.h" @@ -158,6 +159,32 @@ static int myreq_handler(vtask_t* task) xce_request_t request; xce_reply_t reply; + /* Peek at opcode to decide whether to save raw bytes before xce_decode() + * frees the capnp segment. OPCode_upgradeConfig requires the raw packet + * for xce_upgrade_config_req_extract_raw(). */ + uint8_t *raw_pkt_copy = NULL; + size_t raw_pkt_sz = 0; + { + io_packet_t *pkt = data->u.iodata.pkt; + if (pkt && pkt->data && pkt->sz > 0) { + struct capn peek_c; + struct Request peek_rq; + Request_ptr peek_rp; + capn_init_mem(&peek_c, (const uint8_t *)pkt->data, (size_t)pkt->sz, 0); + peek_rp.p = capn_getp(capn_root(&peek_c), 0, 1); + read_Request(&peek_rq, peek_rp); + capn_free(&peek_c); + + if (peek_rq.opcode == OPCode_upgradeConfig) { + raw_pkt_copy = (uint8_t *)malloc((size_t)pkt->sz); + if (raw_pkt_copy) { + memcpy(raw_pkt_copy, pkt->data, (size_t)pkt->sz); + raw_pkt_sz = (size_t)pkt->sz; + } + } + } + } + rc = xce_decode(data->u.iodata.pkt, &request); TRACE("%s cpu#%d opcode %s", __func__, task->vcpu, @@ -233,7 +260,7 @@ static int myreq_handler(vtask_t* task) rc = -EAGAIN; break; } - rc = handle_upgrade_config(c, ep, &request); + rc = handle_upgrade_config(c, ep, &request, raw_pkt_copy, raw_pkt_sz); break; #else { @@ -257,6 +284,7 @@ static int myreq_handler(vtask_t* task) } free_Request(&request); + free(raw_pkt_copy); /* NULL-safe: free(NULL) is a no-op */ return rc; } diff --git a/src/controller/config_repl.c b/src/controller/config_repl.c index ca8865dc..64b4cc76 100644 --- a/src/controller/config_repl.c +++ b/src/controller/config_repl.c @@ -1131,13 +1131,8 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, /* * do_upgrade_config_with_bytes: core upgrade logic called after the raw - * capnp bytes have been extracted from the wire. - * - * Task 11 dependency note: the upgrade_config_req_t.bytes and .sha256 fields - * are typed as xce_data_t* (a discriminated cluster-data union) in the current - * typemap. Extracting raw byte content from them requires the Task 11 capnp - * Data-type shims. This helper accepts the bytes directly so that the logic - * can be tested independently of the typemap shim work. + * capnp bytes have been extracted from the wire via + * xce_upgrade_config_req_extract_raw(). * * Steps: * 0. Validate size limits @@ -1298,28 +1293,41 @@ static int do_upgrade_config_with_bytes(cluster_t *c, endpoint_t *ep, } #if CONF_ENABLE_UPGRADE_CONFIG -int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req) +int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req, + const uint8_t *raw_pkt, size_t raw_pkt_sz) { - /* - * Task 11 dependency: upgrade_config_req_t.bytes and .sha256 are typed - * as xce_data_t* (discriminated cluster-data union). Extracting raw - * byte content requires the Task 11 capnp Data-type shims. - * - * Until Task 11 lands this handler rejects all upgrade requests with - * -ENOSYS even when CONF_ENABLE_UPGRADE_CONFIG=1. The - * do_upgrade_config_with_bytes() helper above contains the full - * implementation and will be wired here in Task 11. - */ - (void)c; - send_upgrade_error(ep, req, c, - "OP_upgradeConfig byte extraction requires Task 11" - " typemap shims -- not yet available"); - return -ENOSYS; + uint8_t *in_bytes = NULL; + size_t in_len = 0; + uint8_t *in_sha = NULL; + + int rc = xce_upgrade_config_req_extract_raw(raw_pkt, raw_pkt_sz, + &in_bytes, &in_len, &in_sha); + if (rc != 0 || !in_bytes || !in_sha) { + send_upgrade_error(ep, req, c, + "Failed to extract bytes from UpgradeConfigRequest"); + free(in_bytes); + free(in_sha); + return rc != 0 ? rc : -EINVAL; + } + if (in_len == 0) { + free(in_bytes); + free(in_sha); + send_upgrade_error(ep, req, c, "bytes or sha256 field missing"); + return -EINVAL; + } + + rc = do_upgrade_config_with_bytes(c, ep, req, in_bytes, in_len, in_sha); + free(in_bytes); + free(in_sha); + return rc; } #else -int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req) +int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req, + const uint8_t *raw_pkt, size_t raw_pkt_sz) { (void)c; + (void)raw_pkt; + (void)raw_pkt_sz; xce_reply_t reply; init_reply_by_req(&reply, req); reply.status = -ENOSYS; diff --git a/src/controller/config_repl.h b/src/controller/config_repl.h index 32c4fd02..174e63f8 100644 --- a/src/controller/config_repl.h +++ b/src/controller/config_repl.h @@ -115,10 +115,13 @@ int push_config_to_standbys(cluster_t *c, const uint8_t *bytes, size_t blen, /* * handle_upgrade_config: handle an OP_upgradeConfig request from an admin tool. - * Validates, stores, and fans out the new config blob; triggers restart. + * raw_pkt/raw_pkt_sz are the on-wire capnp bytes captured before xce_decode() + * freed the capnp segment; xce_upgrade_config_req_extract_raw() uses them to + * recover sha256 and the config blob. * Only callable when CONF_ENABLE_UPGRADE_CONFIG is set. */ -int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req); +int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req, + const uint8_t *raw_pkt, size_t raw_pkt_sz); /* * sanitise_cn: copy at most maxlen printable ASCII chars from src to a