Check remote and local member-file ACL refusals separately
This commit is contained in:
1 parent
b3d3df5c57
commit
59b3b9bd34
1 file changed
+58
-6
@@ -53,6 +53,8 @@ struct network_test {
|
||||
unsigned preservation_case;
|
||||
char preserved_temp[864];
|
||||
unsigned mutation_index, busy_responses, dropped_response;
|
||||
unsigned deleted_wire, deleted_replies; // биты STAT/QUOTA: запрос дошёл до сервера и получил отказ
|
||||
uint64_t deletion_started; // монотонное время публикации удаления для ожидания репликации
|
||||
etcp_recv_fn reader_receive;
|
||||
struct api_case api[8];
|
||||
void* timer;
|
||||
@@ -91,6 +93,16 @@ static void capture_log(int level, const char* category, const char* message) {
|
||||
/* Задержка диска начинается с первого DATA; сценарий не зависит от текста и уровня логов. */
|
||||
static int server_receive_ready(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, const uint8_t* payload, size_t bytes) {
|
||||
struct network_test* t = active;
|
||||
if (bytes >= MF_WIRE_HEADER && payload[1] == MF_REQUEST && mf_get64(payload + 5) >= 3000 && mf_get64(payload + 5) <= 3001) {
|
||||
struct mf_message q;
|
||||
if (mf_decode(payload, bytes, &q) || group != GROUP || peer != t->ids[READER] || q.owner != t->ids[AUTHOR] ||
|
||||
q.command != (q.request_id == 3000 ? MF_STAT : MF_QUOTA) || mf_server_allowed(inst->member_files, group, peer, &q)) {
|
||||
fail(t, "deleted reader wire request identity or server ACL"); return 0;
|
||||
}
|
||||
t->deleted_wire |= 1U << (q.request_id - 3000);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: deleted reader reached server cmd=%s id=%llu allowed=0 wire_mask=%u",
|
||||
mf_command_text(q.command), (unsigned long long)q.request_id, t->deleted_wire);
|
||||
}
|
||||
if (!t->slow_queued && bytes >= MF_WIRE_HEADER && payload[1] == MF_REQUEST && payload[2] == MF_STREAM_DATA) {
|
||||
t->slow_queued = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: first DATA received; delaying disk worker");
|
||||
@@ -166,6 +178,20 @@ static void response(void* arg, const struct mf_message* r) {
|
||||
struct network_test* t = arg; t->response = *r; t->answered = 1;
|
||||
}
|
||||
|
||||
/* Ответы двух независимых запросов могут прийти в любом порядке; каждый обязан быть пустым отказом. */
|
||||
static void deleted_response(void* arg, const struct mf_message* r) {
|
||||
struct network_test* t = arg;
|
||||
if (r->request_id < 3000 || r->request_id > 3001 || r->owner != t->ids[AUTHOR] ||
|
||||
r->command != (r->request_id == 3000 ? MF_STAT : MF_QUOTA) || r->status != MF_ACCESS_DENIED || r->data_len) {
|
||||
fail(t, "deleted reader response identity/status or leaked data"); return;
|
||||
}
|
||||
unsigned bit = 1U << (r->request_id - 3000);
|
||||
if (t->deleted_replies & bit) { fail(t, "duplicate deleted reader response"); return; }
|
||||
t->deleted_replies |= bit;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: deleted reader denied cmd=%s id=%llu replies_mask=%u",
|
||||
mf_command_text(r->command), (unsigned long long)r->request_id, t->deleted_replies);
|
||||
}
|
||||
|
||||
/* Потеря первого результата проверяет повтор с тем же ID без повторного mkdir. */
|
||||
static void mutation_receive(struct ETCP_CONN* conn, struct ll_entry* e) {
|
||||
struct network_test* t = active;
|
||||
@@ -418,12 +444,38 @@ static void tick(void* arg) {
|
||||
if (!t->api[0].finished || !t->api[1].finished || t->nodes[SERVER]->member_files->server_transfer_count) goto again;
|
||||
if (t->api[0].status != MF_IO_ERROR || t->api[1].status != MF_IO_ERROR) { fail(t, "restart completion status"); return; }
|
||||
if (put_member(t, SERVER, READER, 1)) { fail(t, "signed deletion"); return; }
|
||||
t->phase = 7; request(t, MF_STAT, t->ids[AUTHOR]);
|
||||
} else if (t->phase == 7 && t->answered) {
|
||||
if (t->response.status != MF_ACCESS_DENIED) { fail(t, "deleted reader accepted"); return; }
|
||||
t->phase = 21; request(t, MF_QUOTA, t->ids[AUTHOR]);
|
||||
} else if (t->phase == 21 && t->answered) {
|
||||
if (t->response.status != MF_ACCESS_DENIED || t->response.data_len) { fail(t, "quota leaked to deleted reader"); return; }
|
||||
int reader_allowed = member_files_member(t->nodes[READER], GROUP, t->ids[READER], 0);
|
||||
int server_allowed = member_files_member(t->nodes[SERVER], GROUP, t->ids[READER], 0);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: deletion published reader_local_allowed=%d server_reader_allowed=%d",
|
||||
reader_allowed, server_allowed);
|
||||
if (!reader_allowed || server_allowed) { fail(t, "deleted reader ACL fixture"); return; }
|
||||
t->phase = 7; t->deletion_started = get_time_tb();
|
||||
// Оба запроса стартуют до следующего poll: последующая репликация не меняет проверяемый серверный отказ.
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
struct mf_message q = { .command = i ? MF_QUOTA : MF_STAT, .owner = t->ids[AUTHOR], .request_id = 3000 + i };
|
||||
if (!i) snprintf(q.path, sizeof(q.path), "file.bin");
|
||||
if (!member_files_request(t->nodes[READER], GROUP, t->ids[SERVER], &q, deleted_response, t)) {
|
||||
fail(t, "deleted reader remote request start"); return;
|
||||
}
|
||||
}
|
||||
} else if (t->phase == 7 || t->phase == 21) {
|
||||
if (get_time_tb() - t->deletion_started >= 100000) { fail(t, "deleted reader replies/replication timeout"); return; }
|
||||
if (t->deleted_replies != 3) goto again;
|
||||
if (t->deleted_wire != 3) { fail(t, "deleted reader denial did not exercise server ACL"); return; }
|
||||
t->phase = 21;
|
||||
if (member_files_member(t->nodes[READER], GROUP, t->ids[READER], 0)) goto again;
|
||||
if (!member_files_member(t->nodes[READER], GROUP, t->ids[SERVER], 1)) { fail(t, "storage role lost during deletion"); return; }
|
||||
unsigned pending = t->nodes[READER]->member_files->pending_count;
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
struct mf_message q = { .command = i ? MF_QUOTA : MF_STAT, .owner = t->ids[AUTHOR], .request_id = 4000 + i };
|
||||
if (!i) snprintf(q.path, sizeof(q.path), "file.bin");
|
||||
if (member_files_request(t->nodes[READER], GROUP, t->ids[SERVER], &q, deleted_response, t)) {
|
||||
fail(t, "deleted reader local request accepted"); return;
|
||||
}
|
||||
}
|
||||
if (t->nodes[READER]->member_files->pending_count != pending) { fail(t, "local ACL refusal created pending requests"); return; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: deleted reader local_allowed=0 storage_allowed=1 wire=%u replies=%u pending=%u",
|
||||
t->deleted_wire, t->deleted_replies, pending);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files network: PASS progress=%u ticks=%u peak_inflight=%u pauses=%d",
|
||||
t->progress, t->ticks, t->peak_inflight, t->receive_pauses);
|
||||
uasync_stop(t->ua); return;
|
||||
|
||||
Reference in new issue
Block a user