Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ if(KVSPACE_BUILD_TESTS)
target_link_libraries(test_slot_view PRIVATE kvspace-c)
add_test(NAME slot_view COMMAND test_slot_view)

add_executable(test_inplace_body_only tests/test_inplace_body_only.c)
target_include_directories(test_inplace_body_only PRIVATE src)
target_link_libraries(test_inplace_body_only PRIVATE kvspace-c)
add_test(NAME inplace_body_only COMMAND test_inplace_body_only)

add_executable(test_art_alignment tests/test_art_alignment.c)
target_include_directories(test_art_alignment PRIVATE src)
target_compile_definitions(test_art_alignment PRIVATE P2_TEST_HOOKS)
Expand Down
2 changes: 1 addition & 1 deletion src/durable_abi.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ int kvspaceSetPart(void *h, const char *key, uint32_t offset,
uint32_t err_cap) {
int32_t total = 0;
uint8_t *d = kvspaceShmGet((kvspace_t *)h, key, 1, &total);
if (!d || total < 0 || offset + buf_len > (uint32_t)total) {
if (!d || total < 0 || !kvspaceShmPartInBody(d, total, offset, buf_len)) {
if (err && err_cap)
snprintf(err, err_cap, "kvspace: set-part out of range at %s", key);
return 1;
Expand Down
30 changes: 24 additions & 6 deletions src/kvspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ int kvspaceShmViewWrite(kvspace_t *kv, kvspaceViewToken_t *tok,
return -1;
if (kvspaceShmViewCheck(kv, tok) != 0)
return 1;
if (len > tok->body_cap)
if (len > tok->body_cap || len != tok->value_len)
return 1;
kv_gens_t *g = gens_at(kv, (int32_t)tok->node_id);
if (!g)
Expand Down Expand Up @@ -2788,6 +2788,22 @@ uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref,
return raw;
}

/* 定位写的前置条件:只允许改 body [8, 8+len),head 与 attr 一律不可写。 */
int kvspaceShmPartInBody(const uint8_t *raw, int32_t total, uint32_t offset,
uint32_t buf_len) {
if (!raw || total < (int32_t)KVSPACE_XVALUE_HEAD_LEN)
return 0;
kvspaceXvalueHead h;
memcpy(&h, raw, sizeof h);
const uint64_t len = kvspaceXvalueHeadLen(&h);
if (len > (uint64_t)total - KVSPACE_XVALUE_HEAD_LEN)
return 0;
if (offset < KVSPACE_XVALUE_HEAD_LEN)
return 0;
const uint64_t rel = (uint64_t)offset - KVSPACE_XVALUE_HEAD_LEN;
return rel <= len && (uint64_t)buf_len <= len - rel;
}

int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, uint32_t offset,
const uint8_t *buf, uint32_t buf_len) {
Expand All @@ -2814,10 +2830,12 @@ int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
/* per-box 物理容量:损坏 head 的 len 可能大于 box 但仍在全局内,
* 不做这一步会跨 box 写坏相邻值。 */
uint64_t cap = sbo_allocated_size(kv->sbo_meta, off);
const uint64_t rel =
offset >= KVSPACE_XVALUE_HEAD_LEN
? (uint64_t)offset - KVSPACE_XVALUE_HEAD_LEN
: UINT64_MAX;
if (cap <= data_size - off && len <= cap - KVSPACE_XVALUE_HEAD_LEN &&
(uint64_t)offset <= KVSPACE_XVALUE_HEAD_LEN + len &&
(uint64_t)buf_len <=
KVSPACE_XVALUE_HEAD_LEN + len - (uint64_t)offset) {
rel <= len && (uint64_t)buf_len <= len - rel) {
kv_gens_t *g = gens_at(kv, id);
if (g)
__atomic_add_fetch(&g->value_gen, 1u,
Expand All @@ -2838,7 +2856,7 @@ int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
return -1;
int32_t rl = 0;
uint8_t *d = kvspaceShmGet(kv, key_fallback, 0, &rl);
if (!d || offset + buf_len > (uint32_t)rl)
if (!d || !kvspaceShmPartInBody(d, rl, offset, buf_len))
return -1;
kvspaceShmResolveRef(kv, key_fallback, ref);
kv_gens_t *g = gens_at(kv, (int32_t)ref->block_id);
Expand All @@ -2852,7 +2870,7 @@ int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
uint8_t *raw;
int32_t rl;
if (read_value(kv, AV_OFF(hv), &raw, &rl) < 0 ||
offset + buf_len > (uint32_t)rl)
!kvspaceShmPartInBody(raw, rl, offset, buf_len))
return -1;
kv_gens_t *g2 = gens_at(kv, id);
if (g2)
Expand Down
3 changes: 3 additions & 0 deletions src/kvspace_shm.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ uint8_t *kvspaceShmGetByRef(kvspace_t *kv, kvspaceRef_t *ref,
/* shm slot view:返回映射内 head/body 与整 box 物理容量。0=成功,-1=不可视槽。 */
int kvspaceShmXvalueBodyView(kvspace_t *kv, const kvspaceRef_t *ref,
const char *key_fallback, kvspaceXvalueBodyView_t *out);
/* 定位写的前置条件:只允许改 body [8, 8+len),head 与 attr 一律不可写。 */
int kvspaceShmPartInBody(const uint8_t *raw, int32_t total, uint32_t offset,
uint32_t buf_len);
int kvspaceShmSetPartByRef(kvspace_t *kv, kvspaceRef_t *ref,
const char *key_fallback, uint32_t offset,
const uint8_t *buf, uint32_t buf_len);
Expand Down
160 changes: 160 additions & 0 deletions tests/test_inplace_body_only.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/* 原地写契约:只允许改 body [8, 8+len),head 与 attr 必须逐字节不变;
改 head/attr 或改 body 长度的入口一律硬错。各入口用独立键,互不阻断。 */
#include <sys/types.h>

#include "test_util.h"

#include <kvspace/kvspace.h>
#include <kvspace/xvalue_head.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Value {
uint8_t *raw;
uint32_t len;
};

static void put_i64(void *h, const char *key) {
uint8_t *wire = NULL;
uint32_t wlen = 0;
CHECK(kvspaceNewInt64(42, "int64", &wire, &wlen) == 0, "new int64");
CHECK(kvspaceSetValue(h, key, wire, wlen, NULL, 0) == 0, "set %s", key);
free(wire);
}

static void snap(void *h, const char *key, struct Value *out) {
uint8_t *raw = NULL;
out->len = 0;
if (kvspaceGet(h, key, 0, &raw, &out->len) != 0 || !raw) {
out->raw = NULL;
return;
}
out->raw = malloc(out->len);
memcpy(out->raw, raw, out->len);
}

static int head_attr_same(const struct Value *a, const struct Value *b) {
return a->len == b->len && memcmp(a->raw, b->raw, 8) == 0 &&
memcmp(a->raw + 16, b->raw + 16, a->len - 16) == 0;
}

static void run(void) {
char dir[] = "/tmp/kvspace-inplace-XXXXXX";
char *made = mkdtemp(dir);
REQUIRE(made != NULL, "mkdtemp");
char dsn[256];
snprintf(dsn, sizeof dsn, "shm://%s/db", dir);
void *h = kvspaceConnect(dsn);
REQUIRE(h != NULL, "connect");

/* 1. ViewWrite 改长度:拒绝且不改值;等长接受,head/attr 不变。 */
put_i64(h, "/vw");
struct Value vw0;
snap(h, "/vw", &vw0);
REQUIRE(vw0.raw != NULL && vw0.len == 26, "base /vw");
const int qrc = kvspaceViewQuiesce(h, 0);
CHECK(qrc == 0, "quiesce");
kvspaceViewToken_t tok;
memset(&tok, 0, sizeof tok);
const int vrc = kvspaceViewResolve(h, "/vw", &tok);
REQUIRE(vrc == 0, "view resolve");
CHECK(tok.value_len == 8, "token value_len");
uint8_t big[16];
memset(big, 0xAA, sizeof big);
const int vbad = kvspaceViewWrite(h, &tok, big, 16);
CHECK(vbad != 0, "ViewWrite(len=16) must fail");
struct Value vw1;
snap(h, "/vw", &vw1);
CHECK(vw1.raw != NULL, "rejected ViewWrite destroyed value");
if (vw1.raw) {
CHECK(vw1.len == vw0.len && head_attr_same(&vw1, &vw0),
"rejected ViewWrite changed head/attr");
CHECK(memcmp(vw1.raw + 8, vw0.raw + 8, 8) == 0,
"rejected ViewWrite changed body");
int64_t five = 5;
const int vok = kvspaceViewWrite(h, &tok, (const uint8_t *)&five, 8);
CHECK(vok == 0, "ViewWrite(len=8)");
struct Value vw2;
snap(h, "/vw", &vw2);
CHECK(vw2.raw != NULL && vw2.len == vw0.len && vw2.raw != NULL,
"ViewWrite geometry");
if (vw2.raw) {
CHECK(head_attr_same(&vw2, &vw0), "ViewWrite changed head/attr");
CHECK(*(int64_t *)(vw2.raw + 8) == 5, "ViewWrite body value");
free(vw2.raw);
}
}

/* 2. WriteInPlace 改 body 长度:硬错;等长接受,head/attr 不变。 */
put_i64(h, "/wi");
struct Value wi0;
snap(h, "/wi", &wi0);
REQUIRE(wi0.raw != NULL && wi0.len == 26, "base /wi");
uint8_t *body = NULL;
const int wshort = kvspaceWriteInPlace(h, "/wi", 0, 4, &body, NULL, 0);
CHECK(wshort != 0, "WriteInPlace(body_len=4) must fail");
const int wok = kvspaceWriteInPlace(h, "/wi", 0, 8, &body, NULL, 0);
CHECK(wok == 0, "WriteInPlace(body_len=8)");
if (wok == 0) {
int64_t seven = 7;
memcpy(body, &seven, 8);
struct Value wi1;
snap(h, "/wi", &wi1);
REQUIRE(wi1.raw != NULL && wi1.len == wi0.len, "in-place geometry");
CHECK(memcmp(wi1.raw, wi0.raw, 8) == 0, "WriteInPlace changed head");
CHECK(memcmp(wi1.raw + 16, wi0.raw + 16, wi0.len - 16) == 0,
"WriteInPlace changed attr");
CHECK(*(int64_t *)(wi1.raw + 8) == 7, "WriteInPlace body value");
free(wi1.raw);
}

/* 3. SetPart 写 head:硬错,整值不变。 */
put_i64(h, "/sp");
struct Value sp0;
snap(h, "/sp", &sp0);
REQUIRE(sp0.raw != NULL && sp0.len == 26, "base /sp");
uint8_t patch[8] = {0xde, 0xad, 0xbe, 0xef, 0, 0, 0, 0};
char err[128] = {0};
const int sp_rc = kvspaceSetPart(h, "/sp", 0, patch, 8, err, sizeof err);
CHECK(sp_rc != 0, "SetPart(offset=0) must fail");
struct Value sp1;
snap(h, "/sp", &sp1);
CHECK(sp1.raw != NULL, "value intact after rejected head write");
if (sp1.raw)
CHECK(sp1.len == sp0.len && memcmp(sp1.raw, sp0.raw, sp0.len) == 0,
"head write changed value");

/* 4. SetPart 写 body:接受;head/attr 逐字节不变,body 变。 */
if (sp1.raw) {
int64_t nine = 99;
CHECK(kvspaceSetPart(h, "/sp", 8, (const uint8_t *)&nine, 8, NULL, 0) ==
0,
"SetPart(body) must succeed");
struct Value sp2;
snap(h, "/sp", &sp2);
REQUIRE(sp2.raw != NULL && sp2.len == sp0.len, "body write geometry");
CHECK(memcmp(sp2.raw, sp0.raw, 8) == 0, "body write changed head");
CHECK(memcmp(sp2.raw + 16, sp0.raw + 16, sp0.len - 16) == 0,
"body write changed attr");
CHECK(*(int64_t *)(sp2.raw + 8) == 99, "body write body value");
free(sp2.raw);
}

free(vw0.raw); free(vw1.raw);
free(wi0.raw);
free(sp0.raw); free(sp1.raw);
kvspaceClose(h);
}

int main(void) {
run();
if (failures) {
fprintf(stderr, "%d failure(s)\n", failures);
return 1;
}
puts("inplace_body_only: ok");
return 0;
}
12 changes: 8 additions & 4 deletions tests/test_slot_view.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ int main(void) {
kvspaceXvalueHead h17;
memset(&h17, 0, sizeof h17);
kvspaceXvalueHeadSetLen(&h17, 17);
CHECK(kvspaceShmSetPartByRef(kv, &s17, sk, 2,
(const uint8_t *)&h17, sizeof h17) == 0);
int32_t s17len = 0;
uint8_t *s17raw = kvspaceShmGet(kv, sk, 0, &s17len);
CHECK(s17raw != NULL && s17len >= (int32_t)(2 + sizeof h17));
memcpy(s17raw + 2, &h17, sizeof h17);
kvspaceXvalueBodyView_t v17;
CHECK(kvspaceShmXvalueBodyView(kv, &s17, sk, &v17) == -1);
CHECK(kvspaceShmXvalueBodyView(kv, &s17, NULL, &v17) == -1);
Expand All @@ -144,8 +146,10 @@ int main(void) {
kvspaceRef_t xr;
memset(&xr, 0, sizeof xr);
CHECK(kvspaceShmResolveRef(kv, "/a/corrupt", &xr) == 0);
CHECK(kvspaceShmSetPartByRef(kv, &xr, "/a/corrupt", 2,
(const uint8_t *)&big, sizeof big) == 0);
int32_t xrlen = 0;
uint8_t *xrraw = kvspaceShmGet(kv, "/a/corrupt", 0, &xrlen);
CHECK(xrraw != NULL && xrlen >= (int32_t)(2 + sizeof big));
memcpy(xrraw + 2, &big, sizeof big);
kvspaceRef_t cr;
memset(&cr, 0, sizeof cr);
CHECK(kvspaceShmResolveRef(kv, "/a/corrupt", &cr) == 0);
Expand Down
Loading