mirror of
https://github.com/chatmail/core.git
synced 2026-05-09 01:46:30 +03:00
Housekeeping: delete the blobs backup dir (#4104)
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Do not block async task executor while decrypting the messages. #4079
|
- Do not block async task executor while decrypting the messages. #4079
|
||||||
|
- Housekeeping: delete the blobs backup dir #4123
|
||||||
|
|
||||||
### API-Changes
|
### API-Changes
|
||||||
- jsonrpc: add more advanced API to send a message. #4097
|
- jsonrpc: add more advanced API to send a message. #4097
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ use crate::{e2ee, tools};
|
|||||||
|
|
||||||
// Name of the database file in the backup.
|
// Name of the database file in the backup.
|
||||||
const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite";
|
const DBFILE_BACKUP_NAME: &str = "dc_database_backup.sqlite";
|
||||||
const BLOBS_BACKUP_NAME: &str = "blobs_backup";
|
pub(crate) const BLOBS_BACKUP_NAME: &str = "blobs_backup";
|
||||||
|
|
||||||
/// Import/export command.
|
/// Import/export command.
|
||||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||||
|
|||||||
152
src/sql.rs
152
src/sql.rs
@@ -15,6 +15,7 @@ use crate::constants::DC_CHAT_ID_TRASH;
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::debug_logging::set_debug_logging_xdc;
|
use crate::debug_logging::set_debug_logging_xdc;
|
||||||
use crate::ephemeral::start_ephemeral_timers;
|
use crate::ephemeral::start_ephemeral_timers;
|
||||||
|
use crate::imex::BLOBS_BACKUP_NAME;
|
||||||
use crate::log::LogExt;
|
use crate::log::LogExt;
|
||||||
use crate::message::{Message, MsgId, Viewtype};
|
use crate::message::{Message, MsgId, Viewtype};
|
||||||
use crate::param::{Param, Params};
|
use crate::param::{Param, Params};
|
||||||
@@ -792,74 +793,93 @@ pub async fn remove_unused_files(context: &Context) -> Result<()> {
|
|||||||
.context("housekeeping: failed to SELECT value FROM config")?;
|
.context("housekeeping: failed to SELECT value FROM config")?;
|
||||||
|
|
||||||
info!(context, "{} files in use.", files_in_use.len(),);
|
info!(context, "{} files in use.", files_in_use.len(),);
|
||||||
/* go through directory and delete unused files */
|
/* go through directories and delete unused files */
|
||||||
let p = context.get_blobdir();
|
let blobdir = context.get_blobdir();
|
||||||
match tokio::fs::read_dir(p).await {
|
for p in [&blobdir.join(BLOBS_BACKUP_NAME), blobdir] {
|
||||||
Ok(mut dir_handle) => {
|
match tokio::fs::read_dir(p).await {
|
||||||
/* avoid deletion of files that are just created to build a message object */
|
Ok(mut dir_handle) => {
|
||||||
let diff = std::time::Duration::from_secs(60 * 60);
|
/* avoid deletion of files that are just created to build a message object */
|
||||||
let keep_files_newer_than = std::time::SystemTime::now()
|
let diff = std::time::Duration::from_secs(60 * 60);
|
||||||
.checked_sub(diff)
|
let keep_files_newer_than = std::time::SystemTime::now()
|
||||||
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
.checked_sub(diff)
|
||||||
|
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
|
||||||
|
|
||||||
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
while let Ok(Some(entry)) = dir_handle.next_entry().await {
|
||||||
let name_f = entry.file_name();
|
let name_f = entry.file_name();
|
||||||
let name_s = name_f.to_string_lossy();
|
let name_s = name_f.to_string_lossy();
|
||||||
|
|
||||||
if is_file_in_use(&files_in_use, None, &name_s)
|
if p == blobdir
|
||||||
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
&& (is_file_in_use(&files_in_use, None, &name_s)
|
||||||
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
||||||
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
|
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
||||||
{
|
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s))
|
||||||
continue;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
unreferenced_count += 1;
|
|
||||||
|
|
||||||
if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
|
|
||||||
let recently_created =
|
|
||||||
stats.created().map_or(false, |t| t > keep_files_newer_than);
|
|
||||||
let recently_modified = stats
|
|
||||||
.modified()
|
|
||||||
.map_or(false, |t| t > keep_files_newer_than);
|
|
||||||
let recently_accessed = stats
|
|
||||||
.accessed()
|
|
||||||
.map_or(false, |t| t > keep_files_newer_than);
|
|
||||||
|
|
||||||
if recently_created || recently_modified || recently_accessed {
|
|
||||||
info!(
|
|
||||||
context,
|
|
||||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
|
||||||
unreferenced_count,
|
|
||||||
entry.file_name(),
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
info!(
|
if let Ok(stats) = tokio::fs::metadata(entry.path()).await {
|
||||||
context,
|
if stats.is_dir() {
|
||||||
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
if let Err(e) = tokio::fs::remove_dir(entry.path()).await {
|
||||||
unreferenced_count,
|
// The dir could be created not by a user, but by a desktop
|
||||||
entry.file_name()
|
// environment f.e. So, no warning.
|
||||||
);
|
info!(
|
||||||
let path = entry.path();
|
context,
|
||||||
if let Err(err) = delete_file(context, &path).await {
|
"Housekeeping: Cannot rmdir {}: {:#}",
|
||||||
error!(
|
entry.path().display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unreferenced_count += 1;
|
||||||
|
let recently_created =
|
||||||
|
stats.created().map_or(false, |t| t > keep_files_newer_than);
|
||||||
|
let recently_modified = stats
|
||||||
|
.modified()
|
||||||
|
.map_or(false, |t| t > keep_files_newer_than);
|
||||||
|
let recently_accessed = stats
|
||||||
|
.accessed()
|
||||||
|
.map_or(false, |t| t > keep_files_newer_than);
|
||||||
|
|
||||||
|
if p == blobdir
|
||||||
|
&& (recently_created || recently_modified || recently_accessed)
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
||||||
|
unreferenced_count,
|
||||||
|
entry.file_name(),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreferenced_count += 1;
|
||||||
|
}
|
||||||
|
info!(
|
||||||
context,
|
context,
|
||||||
"Failed to delete unused file {}: {:#}.",
|
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
||||||
path.display(),
|
unreferenced_count,
|
||||||
err
|
entry.file_name()
|
||||||
);
|
);
|
||||||
|
let path = entry.path();
|
||||||
|
if let Err(err) = delete_file(context, &path).await {
|
||||||
|
error!(
|
||||||
|
context,
|
||||||
|
"Failed to delete unused file {}: {:#}.",
|
||||||
|
path.display(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(err) => {
|
||||||
Err(err) => {
|
warn!(
|
||||||
warn!(
|
context,
|
||||||
context,
|
"Housekeeping: Cannot read dir {}: {:#}",
|
||||||
"Housekeeping: Cannot open {}. ({})",
|
p.display(),
|
||||||
context.get_blobdir().display(),
|
err
|
||||||
err
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1061,6 +1081,18 @@ mod tests {
|
|||||||
assert_eq!(loaded_draft.unwrap().text.unwrap(), "This is my draft");
|
assert_eq!(loaded_draft.unwrap().text.unwrap(), "This is my draft");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests that `housekeeping` deletes the blobs backup dir which is created normally by
|
||||||
|
/// `imex::import_backup`.
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_housekeeping_delete_blobs_backup_dir() {
|
||||||
|
let t = TestContext::new_alice().await;
|
||||||
|
let dir = t.get_blobdir().join(BLOBS_BACKUP_NAME);
|
||||||
|
tokio::fs::create_dir(&dir).await.unwrap();
|
||||||
|
tokio::fs::write(dir.join("f"), "").await.unwrap();
|
||||||
|
housekeeping(&t).await.unwrap();
|
||||||
|
tokio::fs::create_dir(&dir).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Regression test.
|
/// Regression test.
|
||||||
///
|
///
|
||||||
/// Previously the code checking for existence of `config` table
|
/// Previously the code checking for existence of `config` table
|
||||||
|
|||||||
Reference in New Issue
Block a user