diff --git a/rslib/src/collection/backup.rs b/rslib/src/collection/backup.rs index 631988c43..f0b5e9897 100644 --- a/rslib/src/collection/backup.rs +++ b/rslib/src/collection/backup.rs @@ -16,7 +16,7 @@ use itertools::Itertools; use tracing::error; use crate::import_export::package::export_colpkg_from_data; -use crate::io::read_file; +use crate::io::read_locked_db_file; use crate::pb::config::preferences::BackupLimits; use crate::prelude::*; @@ -39,7 +39,7 @@ impl Collection { } else { let tr = self.tr.clone(); self.storage.checkpoint()?; - let col_data = read_file(&self.col_path)?; + let col_data = read_locked_db_file(&self.col_path)?; self.update_last_backup_timestamp()?; Ok(Some(thread::spawn(move || { backup_inner(&col_data, &backup_folder, limits, &tr) diff --git a/rslib/src/io.rs b/rslib/src/io.rs index 3957802f5..0e2243be7 100644 --- a/rslib/src/io.rs +++ b/rslib/src/io.rs @@ -2,6 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::fs::File; +use std::io::Read; +use std::io::Seek; use std::path::Component; use std::path::Path; @@ -63,6 +65,34 @@ pub(crate) fn read_file(path: impl AsRef) -> Result> { }) } +/// Like [read_file], but skips the section that is potentially locked by +/// SQLite. +pub(crate) fn read_locked_db_file(path: impl AsRef) -> Result> { + read_locked_db_file_inner(&path).context(FileIoSnafu { + path: path.as_ref(), + op: FileOp::Read, + }) +} + +const LOCKED_SECTION_START_BYTE: usize = 1024 * 1024 * 1024; +const LOCKED_SECTION_LEN_BYTES: usize = 512; +const LOCKED_SECTION_END_BYTE: usize = LOCKED_SECTION_START_BYTE + LOCKED_SECTION_LEN_BYTES; + +fn read_locked_db_file_inner(path: impl AsRef) -> std::io::Result> { + let size = std::fs::metadata(&path)?.len() as usize; + if size < LOCKED_SECTION_END_BYTE { + return std::fs::read(path); + } + + let mut file = File::open(&path)?; + let mut buf = vec![0; size]; + file.read_exact(&mut buf[..LOCKED_SECTION_START_BYTE])?; + file.seek(std::io::SeekFrom::Current(LOCKED_SECTION_LEN_BYTES as i64))?; + file.read_exact(&mut buf[LOCKED_SECTION_END_BYTE..])?; + + Ok(buf) +} + pub(crate) fn new_tempfile() -> Result { NamedTempFile::new().context(FileIoSnafu { path: std::env::temp_dir(),