Publish attachment output once and preserve ownership through cancellation

This commit is contained in:
evgeny committed 2026-10-24 14:41:39 +02:00
1 parent 92d52d25a8
commit 4350b54769
2 files changed
+24 -15

No files matched your search

+19 -10
View File
@@ -23,11 +23,13 @@ enum { AK_REQUEST = 1, AK_REQUEST_ACK, AK_GRANT, AK_DENY, AK_RESULT_ACK };
struct ak_file {
struct UTUN_INSTANCE* inst;
char channel[32], source[1024], cipher[1024], plain[1024], temporary[1040], type[32];
char channel[32], source[1024], cipher[1024], plain[1024], type[32];
struct file_io_temp output; // Единственный собственный temp; стабильный адрес до completion.
uint8_t id[16], key[32], message[32], hash[32], reply[32];
struct attachment_info info;
uint64_t author, request;
int encrypt, error;
int plain_prepared, published; // Только реально подготовленный snapshot и выполненный rename дают право очистки.
};
/* Все ошибки SQLite содержат конкретную операцию. */
@@ -121,12 +123,18 @@ static int ak_parent(const char* path) {
static void ak_file_work(void* arg) {
struct ak_file* job = arg; job->error = -1;
if (ak_parent(job->plain) || ak_parent(job->cipher)) return;
snprintf(job->temporary, sizeof(job->temporary), "%s.part", job->encrypt ? job->cipher : job->plain);
if (job->encrypt) {
if (ma_copy_file(job->source, job->plain) || encrypted_file_sync(job->plain) || encrypted_file_sync_directory(job->plain) ||
encrypted_file_write(job->plain, job->temporary, job->key, job->author, job->id, &job->info, job->type)) return;
} else if (encrypted_file_read(job->cipher, job->temporary, job->key, job->author, job->id, job->hash, &job->info, job->type)) return;
if (encrypted_file_publish(job->temporary, job->encrypt ? job->cipher : job->plain)) return;
if (ma_copy_file(job->source, job->plain)) return;
job->plain_prepared = 1;
if (file_io_sync_file(job->plain) || file_io_sync_directory(job->plain) ||
encrypted_file_write(job->plain, job->cipher, &job->output, job->key, job->author, job->id, &job->info, job->type)) return;
} else if (encrypted_file_read(job->cipher, job->plain, &job->output, job->key, job->author, job->id,
job->hash, &job->info, job->type)) return;
int error = file_io_temp_publish(&job->output, job->encrypt ? job->plain : job->cipher, job->encrypt ? job->cipher : job->plain);
job->published = !job->output.temporary[0]; // Rename уже выполнен и при последующем отказе directory sync.
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "attachment keys: output publication encrypt=%d installed=%d error=%d",
job->encrypt, job->published, error);
if (error) return;
job->error = 0;
}
@@ -174,11 +182,12 @@ static void ak_file_done(void* arg, int error) {
if (job->encrypt) chat_event_submitted(inst, job->request, 0);
}
if (job->encrypt && remove(job->source) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: prepared source cleanup failed");
if ((!success || !valid) && job->temporary[0] && remove(job->temporary) && errno != ENOENT)
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: temporary cleanup failed");
file_io_temp_discard(&job->output);
if (!valid) {
if (remove(job->plain) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel plaintext cleanup failed");
if (job->encrypt && remove(job->cipher) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel ciphertext cleanup failed");
if (((job->encrypt && job->plain_prepared) || (!job->encrypt && job->published)) && remove(job->plain) && errno != ENOENT)
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel plaintext cleanup failed");
if (job->encrypt && job->published && remove(job->cipher) && errno != ENOENT)
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel ciphertext cleanup failed");
}
if (valid) {
if (success && !job->encrypt) chat_media_cache_enforce(inst);
+5 -5
View File
@@ -154,7 +154,7 @@ static void dm_cleanup_pump(struct dm_media_state* media);
static void dm_cleanup_work(void* arg) {
struct dm_cleanup_job* job = arg;
job->error = dm_file_remove(job->path);
if (!job->error) job->error = encrypted_file_sync_directory(job->path);
if (!job->error) job->error = file_io_sync_directory(job->path);
}
static void dm_cleanup_done(void* arg, int error) {
@@ -488,7 +488,7 @@ static void dm_file_work(void* arg) {
struct dm_file_job* job = arg;
job->error = -1;
if (job->work == DM_FILE_DELETE) {
if (!dm_file_remove(job->cipher) && !dm_file_remove(job->temp)) job->error = encrypted_file_sync_directory(job->cipher);
if (!dm_file_remove(job->cipher) && !dm_file_remove(job->temp)) job->error = file_io_sync_directory(job->cipher);
return;
}
if (job->work == DM_FILE_PREPARE) {
@@ -530,9 +530,9 @@ done:
if (out && fclose(out)) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: worker output close failed"); job->error = -1; }
if (!job->error) {
if (job->work == DM_FILE_PREPARE) {
job->error = encrypted_file_publish(job->temp, job->cipher);
if (!job->error) job->error = encrypted_file_sync_directory(job->plain);
} else job->error = encrypted_file_publish(job->work == DM_FILE_CUSTODY ? job->cipher : job->temp, job->output);
job->error = file_io_publish(job->temp, job->cipher);
if (!job->error) job->error = file_io_sync_directory(job->plain);
} else job->error = file_io_publish(job->work == DM_FILE_CUSTODY ? job->cipher : job->temp, job->output);
}
}