Browse Source

Validate quota scan failures and deny unauthorized quota queries

master
evgeny 6 days ago
parent
commit
092e3e3cdc
  1. 11
      src/member_files/storage.c
  2. 19
      tests/test_member_files.c
  3. 3
      tests/test_member_files_network.c

11
src/member_files/storage.c

@ -365,9 +365,16 @@ static int store_command(struct mf_storage* s, uint64_t group, uint64_t peer, co
struct mf_disk_info info = {0};
int rc, fd;
if (q->command == MF_QUOTA) {
if (*q->path || *q->target || q->cursor || q->data_len) return MF_BAD_PACKET;
if (*q->path || *q->target || q->cursor || q->data_len) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: invalid quota request owner=%llu path=%s target=%s cursor=%llu data=%u",
(unsigned long long)q->owner, q->path, q->target, (unsigned long long)q->cursor, q->data_len);
return MF_BAD_PACKET;
}
struct store_user* user = store_user_get(s, q->owner, 0);
if (user && !user->valid) return MF_IO_ERROR;
if (user && !user->valid) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: quota account invalid owner=%llu; restart scan required", (unsigned long long)q->owner);
return MF_IO_ERROR;
}
mf_put64(r->data, user ? user->used : 0); mf_put64(r->data + 8, s->quota);
mf_put64(r->data + 16, user ? user->reserved : 0); r->data_len = MF_QUOTA_BYTES;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: quota response owner=%llu used=%llu reserved=%llu limit=%llu",

19
tests/test_member_files.c

@ -240,6 +240,23 @@ static void quota_test(const char* directory) {
check_quota(s, 10, 202, 30, 0, 10); mf_storage_destroy(s);
}
static void quota_scan_failure_test(const char* directory) {
char root[1200], rogue[1400]; snprintf(root, sizeof(root), "%s/quota-invalid", directory);
struct mf_storage* s = mf_storage_create(root, 40, 200000);
EXPECT(s && !mf_storage_initialize(s), "scan failure fixture startup"); if (!s) return;
quota_begin(s, 10, 101, "persist", 12, 51, NULL, MF_OK); quota_commit(s, 10); mf_storage_destroy(s);
snprintf(rogue, sizeof(rogue), "%s/rogue.bin", root);
FILE* file = fopen(rogue, "wb"); EXPECT(file != NULL, "invalid namespace fixture"); if (file) fclose(file);
s = mf_storage_create(root, 40, 200000); EXPECT(s != NULL, "scan failure storage allocation"); if (!s) return;
EXPECT(mf_storage_initialize(s) == MF_INVALID_PATH, "invalid namespace rejects partial startup count");
memset(&q, 0, sizeof(q)); q.owner = 101;
run(s, 10, 101, MF_QUOTA, MF_INVALID_PATH); EXPECT(!r.data_len, "failed scan does not expose partial quota");
mf_storage_destroy(s); test_unlink(rogue);
s = mf_storage_create(root, 40, 200000);
EXPECT(s && !mf_storage_initialize(s), "restart after namespace repair");
if (s) { check_quota(s, 20, 101, 12, 0, 40); mf_storage_destroy(s); }
}
static void worker_test(void) {
struct worker_test t = {0}; t.ua = uasync_create(); t.caller = pthread_self(); t.worker = mf_worker_create(t.ua);
EXPECT(t.worker != NULL, "worker start");
@ -256,7 +273,7 @@ int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG);
char directory[1024] = "/tmp/utun_member_files_XXXXXX";
if (test_mkdtemp(directory)) return 1;
protocol_test(); storage_test(directory); quota_test(directory); worker_test();
protocol_test(); storage_test(directory); quota_test(directory); quota_scan_failure_test(directory); worker_test();
#ifndef _WIN32
large_offset_test(directory);
#endif

3
tests/test_member_files_network.c

@ -282,6 +282,9 @@ static void tick(void* arg) {
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; }
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;

Loading…
Cancel
Save