|
|
|
|
@ -73,7 +73,7 @@ static void mf_local_deliver(void* arg) {
|
|
|
|
|
int member_files_member(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t node, int storage) { |
|
|
|
|
struct TOPO_GROUP* g = inst && inst->topo_groups ? topo_groups_find(inst->topo_groups, group) : NULL; |
|
|
|
|
if (!g || g->group_type != TOPO_GROUP_TYPE_CHAT || !node || !inst->topo_sqlite_db) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: ACL missing CHAT group=%llu node=%llu role=%d", |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: ACL missing CHAT group=%llu node=%llu role=%d", |
|
|
|
|
(unsigned long long)group, (unsigned long long)node, storage); return 0; |
|
|
|
|
} |
|
|
|
|
char sql[256]; |
|
|
|
|
@ -90,19 +90,22 @@ int member_files_member(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t nod
|
|
|
|
|
int storage_role = tags && json_flat_get(tags, "storage", role, sizeof(role)) == 0 && !strcmp(role, "yes"); |
|
|
|
|
int allowed = rc == SQLITE_ROW && !sqlite3_column_int(st, 1) && !sqlite3_column_int(st, 3) && |
|
|
|
|
(!storage || (storage_role && sqlite3_column_bytes(st, 2) == 64)); |
|
|
|
|
if (!allowed) DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, |
|
|
|
|
"member_files: ACL denied group=%llu node=%llu require_storage=%d row=%d storage=%d source=%d signature=%d deleted=%d", |
|
|
|
|
(unsigned long long)group, (unsigned long long)node, storage, rc == SQLITE_ROW, |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, |
|
|
|
|
"member_files: ACL allowed=%d group=%llu node=%llu require_storage=%d row=%d storage=%d source=%d signature=%d deleted=%d", |
|
|
|
|
allowed, (unsigned long long)group, (unsigned long long)node, storage, rc == SQLITE_ROW, |
|
|
|
|
storage_role, sqlite3_column_int(st, 1), sqlite3_column_bytes(st, 2), sqlite3_column_int(st, 3)); |
|
|
|
|
if (rc != SQLITE_ROW && rc != SQLITE_DONE) DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: membership step failed rc=%d", rc); |
|
|
|
|
sqlite3_finalize(st); return allowed; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int mf_server_allowed(struct member_files* f, uint64_t group, uint64_t peer, const struct mf_message* q) { |
|
|
|
|
return chat_setting_get_int(f->inst, "member_files_enabled", 1) && |
|
|
|
|
member_files_member(f->inst, group, f->inst->node_id, 1) && member_files_member(f->inst, group, peer, 0) && |
|
|
|
|
(!q->owner || member_files_member(f->inst, group, q->owner, 0)) && |
|
|
|
|
(q->command < MF_BEGIN_WRITE || (q->owner && q->owner == peer)); |
|
|
|
|
int enabled = chat_setting_get_int(f->inst, "member_files_enabled", 1); |
|
|
|
|
int allowed = enabled && member_files_member(f->inst, group, f->inst->node_id, 1) && |
|
|
|
|
member_files_member(f->inst, group, peer, 0) && (!q->owner || member_files_member(f->inst, group, q->owner, 0)) && |
|
|
|
|
(q->command < MF_BEGIN_WRITE || (q->owner && q->owner == peer)); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: server access id=%llu cmd=%s enabled=%d allowed=%d owner_is_peer=%d", |
|
|
|
|
(unsigned long long)q->request_id, mf_command_text(q->command), enabled, allowed, q->owner == peer); |
|
|
|
|
return allowed; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Router подтверждает доставку отдельно; здесь отправка всегда соблюдает порог очереди. */ |
|
|
|
|
@ -114,20 +117,29 @@ static int mf_send(struct member_files* f, uint64_t group, uint64_t peer, const
|
|
|
|
|
local->files = f; local->group = group; local->message = *m; |
|
|
|
|
local->task = uasync_call_soon(f->inst->ua, local, mf_local_deliver); |
|
|
|
|
if (!local->task) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: local delivery scheduling failed"); u_free(local); return -1; } |
|
|
|
|
local->next = f->locals; f->locals = local; f->local_count++; return 0; |
|
|
|
|
local->next = f->locals; f->locals = local; f->local_count++; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: local queued id=%llu cmd=%s kind=%u count=%u", |
|
|
|
|
(unsigned long long)m->request_id, mf_command_text(m->command), m->kind, f->local_count); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
struct ETCP_ROUTER_CONN* rc = etcp_router_conn_get(f->inst, group, peer, ETCP_RT_ID_MEMBER_FILES); |
|
|
|
|
if (!rc) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: router allocation failed"); return -1; } |
|
|
|
|
queue_set_threshold(rc->send_q, 0, 0); etcp_router_set_max_inflight(rc, 16); |
|
|
|
|
if (!etcp_router_send_q_has_room(f->inst, group, peer, ETCP_RT_ID_MEMBER_FILES)) return 1; |
|
|
|
|
if (!etcp_router_send_q_has_room(f->inst, group, peer, ETCP_RT_ID_MEMBER_FILES)) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: tx waiting id=%llu cmd=%s send_q=%u inflight=%u sync=%u no_route=%u", |
|
|
|
|
(unsigned long long)m->request_id, mf_command_text(m->command), rc->send_q->count, rc->inflight_q->count, |
|
|
|
|
rc->peer_sync_done, rc->no_route); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
uint8_t wire[MF_WIRE_MAX]; |
|
|
|
|
int n = mf_encode(m, wire, sizeof(wire)); |
|
|
|
|
if (n < 0) return -1; |
|
|
|
|
uint32_t seq = rc->tx_seq; |
|
|
|
|
int result = etcp_router_conn_send(rc, wire, (size_t)n, ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED); |
|
|
|
|
if (!result) DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, |
|
|
|
|
"member_files: tx id=%llu kind=%u cmd=%u group=%llx peer=%llx owner=%llx status=%u bytes=%d", |
|
|
|
|
(unsigned long long)m->request_id, m->kind, m->command, (unsigned long long)group, |
|
|
|
|
(unsigned long long)peer, (unsigned long long)m->owner, m->status, n); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, |
|
|
|
|
"member_files: tx id=%llu kind=%u cmd=%s group=%llx peer=%llx owner=%llx status=%s bytes=%d seq=%u accepted=%d", |
|
|
|
|
(unsigned long long)m->request_id, m->kind, mf_command_text(m->command), (unsigned long long)group, |
|
|
|
|
(unsigned long long)peer, (unsigned long long)m->owner, mf_status_text(m->status), n, seq, result == 0); |
|
|
|
|
if (result) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: send failed group=%llu peer=%llu cmd=%u rc=%d", |
|
|
|
|
(unsigned long long)group, (unsigned long long)peer, m->command, result); |
|
|
|
|
return result ? -1 : 0; |
|
|
|
|
@ -145,8 +157,9 @@ static void mf_pending_finish(struct mf_pending* p, const struct mf_message* res
|
|
|
|
|
struct mf_message result; |
|
|
|
|
if (!response) { result = p->request; result.kind = MF_RESPONSE; result.status = (uint16_t)status; result.data_len = 0; response = &result; } |
|
|
|
|
mf_response_fn cb = p->callback; void* arg = p->arg; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: request done id=%llu cmd=%u status=%s pending=%u", |
|
|
|
|
(unsigned long long)p->request.request_id, p->request.command, mf_status_text(response->status), f->pending_count); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: request done id=%llu cmd=%s status=%s pending=%u elapsed_ms=%llu", |
|
|
|
|
(unsigned long long)p->request.request_id, mf_command_text(p->request.command), mf_status_text(response->status), |
|
|
|
|
f->pending_count, (unsigned long long)((get_time_tb() - p->started) / 10)); |
|
|
|
|
u_free(p); cb(arg, response); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -170,7 +183,7 @@ static void mf_client_ready(struct ll_queue* q, void* arg) { (void)q; mf_pending
|
|
|
|
|
static void mf_connected(struct CONN_MGR_HANDLE* h, uint64_t peer, uint64_t group, enum conn_mgr_event event, void* arg) { |
|
|
|
|
(void)h; (void)peer; (void)group; |
|
|
|
|
struct mf_pending* p = arg; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: CM event=%d request=%llu", event, (unsigned long long)p->request.request_id); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: CM event=%d request=%llu", event, (unsigned long long)p->request.request_id); |
|
|
|
|
if (event == CONN_EVENT_UP) mf_pending_wake(p); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -199,6 +212,11 @@ struct mf_pending* member_files_request(struct UTUN_INSTANCE* inst, uint64_t gro
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: duplicate local request id"); u_free(p); return NULL; |
|
|
|
|
} |
|
|
|
|
p->next = f->pending; f->pending = p; f->pending_count++; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, |
|
|
|
|
"member_files: request id=%llu cmd=%s group=%llx server=%llx owner=%llx path=%s offset=%llu size=%llu pending=%u", |
|
|
|
|
(unsigned long long)p->request.request_id, mf_command_text(p->request.command), (unsigned long long)group, |
|
|
|
|
(unsigned long long)server, (unsigned long long)p->request.owner, p->request.path, |
|
|
|
|
(unsigned long long)p->request.offset, (unsigned long long)p->request.size, f->pending_count); |
|
|
|
|
if (server != inst->node_id && conn_mgr_open(inst, group, server, mf_connected, p, &p->cm) != 0) { |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: CM launch failed server=%llu", (unsigned long long)server); |
|
|
|
|
if (p->pump) uasync_call_soon_cancel(inst->ua, p->pump); |
|
|
|
|
@ -240,8 +258,8 @@ static void mf_server_ready(struct ll_queue* q, void* arg) {
|
|
|
|
|
|
|
|
|
|
static void mf_server_work(void* arg) { |
|
|
|
|
struct mf_job* j = arg; |
|
|
|
|
if (j->request.command) DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: disk start id=%llu cmd=%u group=%llx peer=%llx path=%s", |
|
|
|
|
(unsigned long long)j->request.request_id, j->request.command, (unsigned long long)j->group, |
|
|
|
|
if (j->request.command) DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: disk start id=%llu cmd=%s group=%llx peer=%llx path=%s", |
|
|
|
|
(unsigned long long)j->request.request_id, mf_command_text(j->request.command), (unsigned long long)j->group, |
|
|
|
|
(unsigned long long)j->peer, j->request.path); |
|
|
|
|
if (j->request.command) mf_storage_execute(j->files->storage, j->group, j->peer, &j->request, &j->response); |
|
|
|
|
else mf_storage_sweep(j->files->storage, get_time_tb()); |
|
|
|
|
@ -252,8 +270,8 @@ static void mf_server_done(void* arg, int error) {
|
|
|
|
|
struct member_files* f = j->files; f->worker = NULL; |
|
|
|
|
if (f->closing || !j->request.command) { mf_job_free(j); if (!f->closing) mf_server_pump(f); return; } |
|
|
|
|
if (error) { j->response = j->request; j->response.kind = MF_RESPONSE; j->response.status = MF_BUSY; j->response.data_len = 0; } |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: disk done id=%llu cmd=%u status=%u error=%d jobs=%u", |
|
|
|
|
(unsigned long long)j->request.request_id, j->request.command, j->response.status, error, f->job_count); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: disk done id=%llu cmd=%s status=%s error=%d jobs=%u", |
|
|
|
|
(unsigned long long)j->request.request_id, mf_command_text(j->request.command), mf_status_text(j->response.status), error, f->job_count); |
|
|
|
|
j->state = 2; mf_server_pump(f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -312,7 +330,11 @@ static void mf_server_pump(struct member_files* f) {
|
|
|
|
|
} |
|
|
|
|
j = next; |
|
|
|
|
} |
|
|
|
|
if (f->worker) return; |
|
|
|
|
if (f->worker) { |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "member_files: server waiting for disk id=%llu cmd=%s jobs=%u", |
|
|
|
|
(unsigned long long)f->worker->request.request_id, mf_command_text(f->worker->request.command), f->job_count); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
for (j = f->jobs; j; j = j->next) if (!j->state) break; |
|
|
|
|
if (!j && f->sweep_due && f->job_count < MF_MAX_JOBS) { |
|
|
|
|
j = u_calloc(1, sizeof(*j)); |
|
|
|
|
@ -325,8 +347,8 @@ static void mf_server_pump(struct member_files* f) {
|
|
|
|
|
j->response.data_len = 0; j->state = 2; mf_server_pump(f); return; |
|
|
|
|
} |
|
|
|
|
j->state = 1; f->worker = j; |
|
|
|
|
if (j->request.command) DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: disk queued id=%llu cmd=%u jobs=%u", |
|
|
|
|
(unsigned long long)j->request.request_id, j->request.command, f->job_count); |
|
|
|
|
if (j->request.command) DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: disk queued id=%llu cmd=%s jobs=%u", |
|
|
|
|
(unsigned long long)j->request.request_id, mf_command_text(j->request.command), f->job_count); |
|
|
|
|
mf_worker_submit(f->disk, mf_server_work, j, mf_server_done, j); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -359,8 +381,8 @@ static void mf_tick(void* arg) {
|
|
|
|
|
/* Общая обработка проверенного сетевого сообщения или команды внутри экземпляра. */ |
|
|
|
|
static void mf_dispatch(struct member_files* f, uint64_t group, uint64_t peer, const struct mf_message* message) { |
|
|
|
|
struct mf_message m = *message; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: rx id=%llu kind=%u cmd=%u group=%llx peer=%llx owner=%llx jobs=%u worker=%p", |
|
|
|
|
(unsigned long long)m.request_id, m.kind, m.command, (unsigned long long)group, |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: rx id=%llu kind=%u cmd=%s group=%llx peer=%llx owner=%llx jobs=%u worker=%p", |
|
|
|
|
(unsigned long long)m.request_id, m.kind, mf_command_text(m.command), (unsigned long long)group, |
|
|
|
|
(unsigned long long)peer, (unsigned long long)m.owner, f->job_count, (void*)f->worker); |
|
|
|
|
if (m.kind == MF_RESPONSE) { |
|
|
|
|
for (struct mf_pending* p = f->pending; p; p = p->next) { |
|
|
|
|
@ -394,7 +416,11 @@ static void mf_dispatch(struct member_files* f, uint64_t group, uint64_t peer, c
|
|
|
|
|
|
|
|
|
|
static void mf_recv(struct ETCP_CONN* conn, struct ll_entry* e) { |
|
|
|
|
struct member_files* f = conn ? conn->instance->member_files : NULL; |
|
|
|
|
if (!f || f->closing || !e || !e->dgram || e->len <= ROUTER_SVC_PAYLOAD_OFF) goto out; |
|
|
|
|
if (!f || f->closing || !e || !e->dgram || e->len <= ROUTER_SVC_PAYLOAD_OFF) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: recv discarded context=%p closing=%d bytes=%u", |
|
|
|
|
(void*)f, f ? f->closing : 0, e ? e->len : 0); |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
uint64_t group, peer; |
|
|
|
|
memcpy(&group, e->dgram + ROUTER_SVC_GROUP_OFF, 8); memcpy(&peer, e->dgram + ROUTER_SVC_SRC_OFF, 8); |
|
|
|
|
if ((e->dgram[ROUTER_SVC_FLAGS_OFF] & (ROUTER_FLAG_SIGNED | ROUTER_FLAG_ENCRYPTED)) != |
|
|
|
|
@ -482,6 +508,8 @@ static void mf_gui_done(void* arg, const struct mf_message* response) {
|
|
|
|
|
struct mf_gui_request* q = arg; |
|
|
|
|
uint8_t event[16 + MF_WIRE_MAX]; mf_put64(event, q->group); mf_put64(event + 8, q->server); |
|
|
|
|
int n = mf_encode(response, event + 16, MF_WIRE_MAX); |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI result id=%llu cmd=%s status=%s bytes=%d", |
|
|
|
|
(unsigned long long)response->request_id, mf_command_text(response->command), mf_status_text(response->status), n); |
|
|
|
|
if (n > 0) chat_event_post(q->inst, CHAT_EVT_MEMBER_FILES_RESPONSE, event, n + 16); |
|
|
|
|
u_free(q); |
|
|
|
|
} |
|
|
|
|
|