diff --git a/src/chat/chat_attachment.c b/src/chat/chat_attachment.c index c12502d7..ef779a9b 100644 --- a/src/chat/chat_attachment.c +++ b/src/chat/chat_attachment.c @@ -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); diff --git a/src/dm/dm_media.c b/src/dm/dm_media.c index 8b015715..71e39096 100644 --- a/src/dm/dm_media.c +++ b/src/dm/dm_media.c @@ -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); } }