fix sync download failing when temp dir on different mount

https://forums.ankiweb.net/t/problems-with-2-1-41-on-arch-linux/8103
This commit is contained in:
Damien Elmes 2021-03-09 11:44:49 +10:00
parent b81e2c0265
commit 7ce75479b2
4 changed files with 29 additions and 10 deletions

View file

@ -189,7 +189,7 @@ impl Backend {
fn download(&self) -> Result<Vec<u8>> { fn download(&self) -> Result<Vec<u8>> {
let server = Box::new(self.col_into_server()?); let server = Box::new(self.col_into_server()?);
let mut rt = Runtime::new().unwrap(); let mut rt = Runtime::new().unwrap();
let file = rt.block_on(server.full_download())?; let file = rt.block_on(server.full_download(None))?;
let path = file.into_temp_path().keep()?; let path = file.into_temp_path().keep()?;
Ok(path.to_str().expect("path was not in utf8").into()) Ok(path.to_str().expect("path was not in utf8").into())
} }

View file

@ -142,11 +142,19 @@ impl SyncServer for HTTPSyncClient {
Ok(()) Ok(())
} }
/// Download collection into a temporary file, returning it. /// Download collection into a temporary file, returning it. Caller should
/// Caller should persist the file in the correct path after checking it. /// persist the file in the correct path after checking it. Progress func
/// Progress func must be set first. /// must be set first. The caller should pass the collection's folder in as
async fn full_download(mut self: Box<Self>) -> Result<NamedTempFile> { /// the temp folder if it wishes to atomically .persist() it.
let mut temp_file = NamedTempFile::new()?; async fn full_download(
mut self: Box<Self>,
col_folder: Option<&Path>,
) -> Result<NamedTempFile> {
let mut temp_file = if let Some(folder) = col_folder {
NamedTempFile::new_in(folder)
} else {
NamedTempFile::new()
}?;
let (size, mut stream) = self.download_inner().await?; let (size, mut stream) = self.download_inner().await?;
let mut progress = FullSyncProgress { let mut progress = FullSyncProgress {
transferred_bytes: 0, transferred_bytes: 0,
@ -410,7 +418,7 @@ mod test {
syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| { syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| {
println!("progress: {:?}", progress); println!("progress: {:?}", progress);
}))); })));
let out_path = syncer.full_download().await?; let out_path = syncer.full_download(None).await?;
let mut syncer = Box::new(HTTPSyncClient::new(None, 0)); let mut syncer = Box::new(HTTPSyncClient::new(None, 0));
syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| { syncer.set_full_sync_progress_fn(Some(Box::new(|progress, _throttle| {

View file

@ -672,8 +672,11 @@ impl Collection {
pub(crate) async fn full_download_inner(self, server: Box<dyn SyncServer>) -> Result<()> { pub(crate) async fn full_download_inner(self, server: Box<dyn SyncServer>) -> Result<()> {
let col_path = self.col_path.clone(); let col_path = self.col_path.clone();
let col_folder = col_path
.parent()
.ok_or_else(|| AnkiError::invalid_input("couldn't get col_folder"))?;
self.close(false)?; self.close(false)?;
let out_file = server.full_download().await?; let out_file = server.full_download(Some(col_folder)).await?;
// check file ok // check file ok
let db = open_and_check_sqlite_file(out_file.path())?; let db = open_and_check_sqlite_file(out_file.path())?;
db.execute_batch("update col set ls=mod")?; db.execute_batch("update col set ls=mod")?;

View file

@ -36,7 +36,10 @@ pub trait SyncServer {
/// If `can_consume` is true, the local server will move or remove the file, instead /// If `can_consume` is true, the local server will move or remove the file, instead
/// creating a copy. The remote server ignores this argument. /// creating a copy. The remote server ignores this argument.
async fn full_upload(self: Box<Self>, col_path: &Path, can_consume: bool) -> Result<()>; async fn full_upload(self: Box<Self>, col_path: &Path, can_consume: bool) -> Result<()>;
async fn full_download(self: Box<Self>) -> Result<NamedTempFile>; /// If the calling code intends to .persist() the named temp file to
/// atomically update the collection, it should pass in the collection's
/// folder, as .persist() can't work across filesystems.
async fn full_download(self: Box<Self>, temp_folder: Option<&Path>) -> Result<NamedTempFile>;
} }
pub struct LocalServer { pub struct LocalServer {
@ -199,7 +202,12 @@ impl SyncServer for LocalServer {
fs::rename(col_path, &target_col_path).map_err(Into::into) fs::rename(col_path, &target_col_path).map_err(Into::into)
} }
async fn full_download(mut self: Box<Self>) -> Result<NamedTempFile> { /// The provided folder is ignored, as in the server case the local data
/// will be sent over the network, instead of written into a local file.
async fn full_download(
mut self: Box<Self>,
_col_folder: Option<&Path>,
) -> Result<NamedTempFile> {
// bump usn/mod & close // bump usn/mod & close
self.col.transact(None, |col| col.storage.increment_usn())?; self.col.transact(None, |col| col.storage.increment_usn())?;
let col_path = self.col.col_path.clone(); let col_path = self.col.col_path.clone();