From e1a3f86a24824ac0aaf5fee6e34038374a6a6d88 Mon Sep 17 00:00:00 2001 From: Igor Katson Date: Mon, 10 Jul 2023 12:18:08 +0100 Subject: [PATCH] Try to workaround #17 - windows not letting to open the file while rqbit has it --- crates/librqbit/src/torrent_manager.rs | 8 +++--- crates/librqbit/src/torrent_state.rs | 34 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/crates/librqbit/src/torrent_manager.rs b/crates/librqbit/src/torrent_manager.rs index 12dc1bf..6c5b961 100644 --- a/crates/librqbit/src/torrent_manager.rs +++ b/crates/librqbit/src/torrent_manager.rs @@ -173,10 +173,10 @@ impl TorrentManager { options: Option, ) -> anyhow::Result { let options = options.unwrap_or_default(); - let files = { + let (files, filenames) = { let mut files = Vec::>>::with_capacity(info.iter_file_lengths()?.count()); - + let mut filenames = Vec::new(); for (path_bits, _) in info.iter_filenames_and_lengths()? { let mut full_path = out.as_ref().to_owned(); let relative_path = path_bits @@ -200,9 +200,10 @@ impl TorrentManager { .with_context(|| format!("error creating {:?}", &full_path))?; OpenOptions::new().read(true).write(true).open(&full_path)? }; + filenames.push(full_path); files.push(Arc::new(Mutex::new(file))) } - files + (files, filenames) }; let peer_id = options.peer_id.unwrap_or_else(generate_peer_id); @@ -270,6 +271,7 @@ impl TorrentManager { info_hash, peer_id, files, + filenames, chunk_tracker, lengths, initial_check_results.have_bytes, diff --git a/crates/librqbit/src/torrent_state.rs b/crates/librqbit/src/torrent_state.rs index 2ee5b45..2bb39a6 100644 --- a/crates/librqbit/src/torrent_state.rs +++ b/crates/librqbit/src/torrent_state.rs @@ -2,6 +2,7 @@ use std::{ collections::{HashMap, HashSet}, fs::File, net::SocketAddr, + path::PathBuf, sync::{ atomic::{AtomicU64, Ordering}, Arc, @@ -232,6 +233,7 @@ pub struct TorrentState { info: TorrentMetaV1Info, locked: Arc>, files: Vec>>, + filenames: Vec, info_hash: Id20, peer_id: Id20, lengths: Lengths, @@ -253,6 +255,7 @@ impl TorrentState { info_hash: Id20, peer_id: Id20, files: Vec>>, + filenames: Vec, chunk_tracker: ChunkTracker, lengths: Lengths, have_bytes: u64, @@ -271,6 +274,7 @@ impl TorrentState { chunks: chunk_tracker, })), files, + filenames, stats: AtomicStats { have: AtomicU64::new(have_bytes), ..Default::default() @@ -894,6 +898,35 @@ impl PeerHandler { } } + fn reopen_read_only(&self) -> anyhow::Result<()> { + fn dummy_file() -> anyhow::Result { + #[cfg(target_os = "windows")] + const DEVNULL: &str = "NUL"; + #[cfg(not(target_os = "windows"))] + const DEVNULL: &str = "/dev/null"; + + std::fs::OpenOptions::new() + .read(true) + .open(DEVNULL) + .with_context(|| format!("error opening {}", DEVNULL)) + } + + for (file, filename) in self.state.files.iter().zip(self.state.filenames.iter()) { + let mut g = file.lock(); + // this should close the original file + // putting in a block just in case to guarantee drop. + { + *g = dummy_file()?; + } + *g = std::fs::OpenOptions::new() + .read(true) + .open(filename) + .with_context(|| format!("error re-opening {:?} readonly", filename))?; + debug!("reopened {:?} read-only", filename); + } + Ok(()) + } + fn on_i_am_unchoked(&self, handle: PeerHandle) { debug!("we are unchoked by {}", handle); let mut g = self.state.locked.write(); @@ -1032,6 +1065,7 @@ impl PeerHandler { if self.state.get_left_to_download() == 0 { self.state.finished_notify.notify_waiters(); + self.reopen_read_only()?; } self.state.maybe_transmit_haves(chunk_info.piece_index);