fix(schema): rename struct Data -> XceData to unshadow capnp builtin blob type

controller.capnp declared `struct Data $C.mapname("xce_data_t")` (an
OPCode_get query-result tagged union) at file scope, which shadows
Cap'n Proto's builtin Data blob type for every later `: Data` field in
the same file. This silently miscompiled 7 fields across 6 structs as
a struct-pointer type instead of a raw byte blob: UpgradeConfigRequest
.sha256/.bytes, ReplConfigReply.sha256/.bytes, ReplConfigPushRequest
.sha256/.bytes, ReplEnvelope.payload, SnapshotTransfer's chunk
payload, WalCatchupEntry.payload/.batchPayloads, and CtrlHelloRequest
.key. Production C code had already routed around it for years via
hand-rolled raw capn_ptr manipulation (see request.c's own comments);
nothing before now needed the generated codec/accessors to actually
work for these fields.

Renamed the local struct to XceData (capnp-level name only; the
$C.mapname("xce_data_t") C typedef is unchanged) and explicitly
repointed getdata/pushmsg at XceData by name, since those two fields
correctly want the local tagged union. Updated every derived
generated-identifier reference across 19 hand-written files spanning
controller, lib, scheduler, and the Go apiserver/xcecli CGo bindings.
Fixed 5 call sites in lib/request.c that had been typed against the
old workaround alias but now legitimately receive the correct
capn_data type (a pure type-name swap -- Data_ptr/XceData_ptr and
capn_data share the identical {capn_ptr p} layout).

Verified via full rebuild+test: lib (1/1), controller (3/3, incl. the
job-array push regression tests), scheduler (15/15), Go apiserver +
xcecli (all packages green), and a direct pycapnp round-trip proving
UpgradeConfigRequest.sha256/.bytes now encode/decode correctly.

Bug: bug-1461 (.wolf/buglog.json). Unblocks admin-config-tool task 3.3.
This commit is contained in:
Rongsong Shen 2026-07-22 09:22:04 +00:00
parent 7ed3a3941f
commit e53c086ec5
21 changed files with 144 additions and 144 deletions

View file

@ -230,17 +230,17 @@ void* xce_configdata_ptr(xce_configdata_t* d)
void* xce_data_ptr(xce_data_t* d)
{
switch (d->table) {
case Data_data_schedjobs:
case XceData_data_schedjobs:
return d->data.jobs;
case Data_data_schedworkers:
case XceData_data_schedworkers:
return d->data.workers;
case Data_data_jobs:
case XceData_data_jobs:
return d->data.jobs;
case Data_data_workers:
case XceData_data_workers:
return d->data.workers;
case Data_data_resources:
case XceData_data_resources:
return d->data.resources;
case Data_data_resvals:
case XceData_data_resvals:
return d->data.resvals;
default:
break;
@ -345,17 +345,17 @@ void* xce_reply_data_ptr(xce_reply_t* reply)
char* xce_dataset_table(int table)
{
switch (table) {
case Data_data_schedjobs:
case XceData_data_schedjobs:
return "schedjobs";
case Data_data_jobs:
case XceData_data_jobs:
return "jobs";
case Data_data_workers:
case XceData_data_workers:
return "workers";
case Data_data_schedworkers:
case XceData_data_schedworkers:
return "schedworkers";
case Data_data_resources:
case XceData_data_resources:
return "resources";
case Data_data_resvals:
case XceData_data_resvals:
return "resvals";
default:
break;

View file

@ -60,7 +60,7 @@ int xce_encode(xce_request_t* req, io_packet_t* pkt)
* malloc'd buffer. Returns NULL (*out_len = 0) for absent/empty fields or
* allocation failure. Used by xce_decode() to pre-extract raw payload
* bytes before capn_free() destroys the segment. */
static uint8_t *extract_data_bytes(Data_ptr dp, size_t *out_len)
static uint8_t *extract_data_bytes(capn_data dp, size_t *out_len)
{
*out_len = 0;
@ -100,7 +100,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
/* OPCode_upgradeConfig carries sha256 and bytes as List(UInt8) on the wire.
* decode_UpgradeConfigRequest() intentionally leaves raw_sha256/raw_bytes as
* NULL (the project's Data union cannot carry raw bytes via decode_Data_ptr).
* NULL (the project's Data union cannot carry raw bytes via decode_XceData_ptr).
* Extract them here using UpgradeConfigRequest_get_* + capn_getv8 BEFORE
* capn_free() destroys the capnp segment. */
if (rq.opcode == OPCode_upgradeConfig
@ -112,7 +112,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
/* sha256 */
{
Data_ptr dp = UpgradeConfigRequest_get_sha256(u_ptr);
capn_data dp = UpgradeConfigRequest_get_sha256(u_ptr);
capn_list8 sha_list;
sha_list.p = dp.p;
if (sha_list.p.type != CAPN_NULL) {
@ -130,7 +130,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
/* bytes */
{
Data_ptr dp = UpgradeConfigRequest_get_bytes(u_ptr);
capn_data dp = UpgradeConfigRequest_get_bytes(u_ptr);
capn_list8 bytes_list;
bytes_list.p = dp.p;
if (bytes_list.p.type != CAPN_NULL) {
@ -158,7 +158,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
/* sha256 */
{
Data_ptr dp = ReplConfigPushRequest_get_sha256(r_ptr);
capn_data dp = ReplConfigPushRequest_get_sha256(r_ptr);
capn_list8 sha_list; sha_list.p = dp.p;
if (sha_list.p.type != CAPN_NULL && capn_len(sha_list) > 0) {
int slen = capn_len(sha_list);
@ -172,7 +172,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
/* bytes */
{
Data_ptr dp = ReplConfigPushRequest_get_bytes(r_ptr);
capn_data dp = ReplConfigPushRequest_get_bytes(r_ptr);
capn_list8 bytes_list; bytes_list.p = dp.p;
if (bytes_list.p.type != CAPN_NULL && capn_len(bytes_list) > 0) {
int blen = capn_len(bytes_list);
@ -963,7 +963,7 @@ static int ssl_read_all(SSL *ssl, void *dst, size_t len)
* version_tag, sha256 and bytes.
*
* Bypasses xce_decode (which would route sha256/bytes through
* decode_Data_ptr → xce_data_t and lose the raw byte content).
* decode_XceData_ptr → xce_data_t and lose the raw byte content).
* Instead, reads the capnp message directly into capn, walks down
* Request → RequestReply → ReplConfigReply, and pulls sha256/bytes
* via capn_len + capn_getv8 on the raw capn_list8.
@ -1050,8 +1050,8 @@ int xce_replconfig_reply_ssl_decode(SSL *ssl,
*out_up_to_date = (int)reply_struct.upToDate;
*out_tag = reply_struct.versionTag;
/* Pull sha256 and bytes via raw capn_list8 access (NOT decode_Data
* which would corrupt via xce_data_t). Data_ptr and capn_list8
/* Pull sha256 and bytes via raw capn_list8 access (NOT decode_XceData
* which would corrupt via xce_data_t). XceData_ptr and capn_list8
* share {capn_ptr p} layout. */
sha_list.p = reply_struct.sha256.p;
bytes_list.p = reply_struct.bytes.p;
@ -1406,16 +1406,16 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
data1.table = data->table;
switch (data->table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
nrows = data->data.jobs->n_total;
data1.data.jobs = (xce_jobs_data_t*)calloc(1, sizeof(xce_jobs_data_t));
if (data1.data.jobs == NULL) {
rc = -ENOMEM;
}
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
nrows = data->data.workers->n_total;
data1.data.workers
= (xce_workers_data_t*)calloc(1, sizeof(xce_workers_data_t));
@ -1423,9 +1423,9 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
rc = -ENOMEM;
}
break;
case Data_data_resources:
case XceData_data_resources:
break;
case Data_data_resvals:
case XceData_data_resvals:
break;
default:
rc = -EINVAL;
@ -1455,8 +1455,8 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
}
switch (data->table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
data1.data.jobs->n_total = data->data.jobs->n_total;
data1.data.jobs->pos = pos;
data1.data.jobs->count = nc;
@ -1466,8 +1466,8 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
data1.data.jobs->jobs = &(data->data.jobs->jobs[pos]);
}
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
data1.data.workers->n_total = data->data.workers->n_total;
data1.data.workers->pos = pos;
data1.data.workers->count = nc;
@ -1478,9 +1478,9 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
= &(data->data.workers->workers[pos]);
}
break;
case Data_data_resources:
case XceData_data_resources:
break;
case Data_data_resvals:
case XceData_data_resvals:
break;
default:
break;
@ -1497,12 +1497,12 @@ int xce_push_data_req(endpoint_t* ep, xce_peerinfo_t* from, xce_data_t* data,
} while ((pos < nrows) && (checkstatus >= 0));
switch (data->table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
FREEUP(data1.data.jobs);
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
FREEUP(data1.data.workers);
break;
default:
@ -1555,24 +1555,24 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
}
switch (getdata.table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
nrows = data->data.jobs->n_total;
getdata.data.jobs
= (xce_jobs_data_t*)calloc(1, sizeof(xce_jobs_data_t));
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
nrows = data->data.workers->n_total;
getdata.data.workers
= (xce_workers_data_t*)calloc(1, sizeof(xce_workers_data_t));
break;
case Data_data_resources:
case XceData_data_resources:
nrows = data->data.resources->n_total;
getdata.data.resources
= (xce_resource_data_t*)calloc(1, sizeof(xce_resource_data_t));
break;
case Data_data_resvals:
case XceData_data_resvals:
nrows = data->data.resvals->n_total;
getdata.data.resvals
= (xce_resval_data_t*)calloc(1, sizeof(xce_resval_data_t));
@ -1607,8 +1607,8 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
}
switch (getdata.table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
getdata.data.jobs->n_total = data->data.jobs->n_total;
getdata.data.jobs->pos = pos;
getdata.data.jobs->count = nc;
@ -1618,8 +1618,8 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
getdata.data.jobs->jobs = &(data->data.jobs->jobs[pos]);
}
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
getdata.data.workers->n_total = data->data.workers->n_total;
getdata.data.workers->pos = pos;
getdata.data.workers->count = nc;
@ -1630,7 +1630,7 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
= &(data->data.workers->workers[pos]);
}
break;
case Data_data_resources:
case XceData_data_resources:
getdata.data.resources->n_total = data->data.resources->n_total;
getdata.data.resources->pos = pos;
getdata.data.workers->count = nc;
@ -1641,7 +1641,7 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
= &(data->data.resources->resources[pos]);
}
break;
case Data_data_resvals:
case XceData_data_resvals:
getdata.data.resvals->n_total = data->data.resvals->n_total;
getdata.data.resvals->pos = pos;
getdata.data.resvals->count = nc;
@ -1665,18 +1665,18 @@ int xce_reply_get(endpoint_t* ep, xce_peerinfo_t* from, xce_reply_t* reply,
} while ((pos < nrows) && (checkstatus >= 0));
switch (getdata.table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
FREEUP(getdata.data.jobs);
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
FREEUP(getdata.data.workers);
break;
case Data_data_resources:
case XceData_data_resources:
FREEUP(getdata.data.resources);
break;
case Data_data_resvals:
case XceData_data_resvals:
FREEUP(getdata.data.resvals);
break;
default:
@ -1696,8 +1696,8 @@ int extract_data(dataset_t* dataset, xce_data_t* getdata)
TRACE("%s ...", __func__);
switch (getdata->table) {
case Data_data_schedjobs:
case Data_data_jobs:
case XceData_data_schedjobs:
case XceData_data_jobs:
if (getdata->data.jobs->count <= 0) {
return 1;
}
@ -1716,8 +1716,8 @@ int extract_data(dataset_t* dataset, xce_data_t* getdata)
memset(getdata->data.jobs->jobs, 0x0, count * sizeof(xce_job_t*));
break;
case Data_data_schedworkers:
case Data_data_workers:
case XceData_data_schedworkers:
case XceData_data_workers:
if (getdata->data.workers->count <= 0) {
return 1;
}
@ -1737,7 +1737,7 @@ int extract_data(dataset_t* dataset, xce_data_t* getdata)
count * sizeof(xce_worker_t*));
break;
case Data_data_resources:
case XceData_data_resources:
if (getdata->data.resources->count <= 0) {
return 1;
}
@ -1758,7 +1758,7 @@ int extract_data(dataset_t* dataset, xce_data_t* getdata)
memset(getdata->data.resources->resources, 0x0,
count * sizeof(xce_resource_t*));
break;
case Data_data_resvals:
case XceData_data_resvals:
if (getdata->data.resvals->count <= 0) {
return 1;
}
@ -1778,7 +1778,7 @@ int extract_data(dataset_t* dataset, xce_data_t* getdata)
memset(getdata->data.resvals->vals, 0x0, count * sizeof(xce_resval_t*));
break;
case Data_data_jobArray:
case XceData_data_jobArray:
/* job-array-single-wal-record task 5.3b: single, unchunked JobSubmit
* descriptor (base jobid, array_size, one shared spec) -- unlike the
* LIST-shaped cases above, no pagination/accumulation loop is needed:

View file

@ -177,11 +177,11 @@ static void test_init_reply_by_req_zeroes_reply(void)
}
/* ── job-array-single-wal-record task 5.3b: extract_data() wire dispatch for
* Data_data_jobArray ─────────────────────────────────────────────────────
* XceData_data_jobArray ─────────────────────────────────────────────────────
* extract_data() is the shared inbound-push reassembly step every receiver
* (scheduler, API server) passes through inside vtask_entry() (xcectx.c)
* before its own handler ever runs. Its switch had no case for
* Data_data_jobArray -- it fell into `default: rc = -EINVAL`, and
* XceData_data_jobArray -- it fell into `default: rc = -EINVAL`, and
* vtask_entry()'s `if (callnext)` treats ANY nonzero return (including a
* negative errno) as "reassembly complete, proceed" -- so the real payload
* was silently freed and the receiver was handed an empty, zeroed
@ -220,7 +220,7 @@ static void test_extract_data_job_array_passes_through_intact(void)
job_array.submit = &submit;
memset(&data, 0, sizeof(data));
data.table = Data_data_jobArray;
data.table = XceData_data_jobArray;
data.data.job_array = &job_array;
memset(&request, 0, sizeof(request));
@ -236,7 +236,7 @@ static void test_extract_data_job_array_passes_through_intact(void)
TEST_ASSERT_EQUAL_INT(0, xce_decode(pkt, &decoded)); /* consumes/frees pkt */
TEST_ASSERT_EQUAL_INT(Request_body_pushmsg, decoded.body_which);
TEST_ASSERT_NOT_NULL(decoded.body.pushmsg);
TEST_ASSERT_EQUAL_INT(Data_data_jobArray, decoded.body.pushmsg->table);
TEST_ASSERT_EQUAL_INT(XceData_data_jobArray, decoded.body.pushmsg->table);
/* Act: this is exactly what vtask_entry() calls on every inbound push. */
memset(&dataset, 0, sizeof(dataset));
@ -287,6 +287,6 @@ void test_request(void)
RUN_TEST(test_ha_heartbeat_req_roundtrip_fields);
RUN_TEST(test_nop_req_remains_bodyless);
printf("\n>> extract_data() Data_data_jobArray wire dispatch tests\n\n");
printf("\n>> extract_data() XceData_data_jobArray wire dispatch tests\n\n");
RUN_TEST(test_extract_data_job_array_passes_through_intact);
}

View file

@ -153,7 +153,7 @@ int xce_get(xce_ctx_t* ctx, int table, void*** data)
int xce_reslist(xce_ctx_t* ctx, xce_resource_t*** res)
{
int table = Data_data_resources;
int table = XceData_data_resources;
return xce_get(ctx, table, (void***)res);
}
@ -174,11 +174,11 @@ int xce_resval(xce_ctx_t* ctx, char* host, char* res, xce_resval_t*** vals)
}
};
xce_get_t get = {
.table = Data_data_resvals,
.table = XceData_data_resvals,
.condition = &condition
};
dataset_t getreply = {
.table = Data_data_resvals,
.table = XceData_data_resvals,
.nrows = 0,
.rows = NULL
};
@ -244,7 +244,7 @@ int xce_join(xce_ctx_t* ctx, int role, hostattrs_t* attrs)
int xce_workerlist(xce_ctx_t* ctx, xce_worker_t*** workers)
{
int table = Data_data_workers;
int table = XceData_data_workers;
return xce_get(ctx, table, (void***)workers);
}
@ -326,9 +326,9 @@ int xce_joblist_ex(xce_ctx_t* ctx, int sched, xce_job_t*** jobs)
int table;
if (sched) {
table = Data_data_schedjobs;
table = XceData_data_schedjobs;
} else {
table = Data_data_jobs;
table = XceData_data_jobs;
}
return xce_get(ctx, table, (void***)jobs);
@ -714,7 +714,7 @@ int xce_jobinfo(xce_ctx_t* ctx, uint64_t jobid, xce_job_t** job)
}
};
xce_get_t get = {
.table = Data_data_jobs,
.table = XceData_data_jobs,
.condition = &condition
};
/* clang-format on */