Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions crates/librqbit/src/storage/filesystem/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl TorrentStorage for FilesystemStorage {

fn pwrite_all(&self, file_id: usize, offset: u64, buf: &[u8]) -> anyhow::Result<()> {
let of = self.opened_files.get(file_id).context("no such file")?;
of.ensure_writeable()?;
#[cfg(windows)]
return of.try_mark_sparse()?.pwrite_all(offset, buf);
#[cfg(not(windows))]
Expand Down Expand Up @@ -137,17 +138,33 @@ impl TorrentStorage for FilesystemStorage {
continue;
};
std::fs::create_dir_all(full_path.parent().context("bug: no parent")?)?;
let f = if shared.options.allow_overwrite {
if shared.options.allow_overwrite {
let (file, writeable) = match
OpenOptions::new()
.create(true)
.truncate(false)
.read(true)
.write(true)
.open(&full_path)
.with_context(|| format!("error opening {full_path:?} in read/write mode"))?
{
Ok(file) => (file, true),
Err(e) => {
warn!(?full_path, "error opening file in create+write mode: {e:?}");
// open the file in read-only mode, will reopen in write mode later.
(
OpenOptions::new()
.create(false)
.read(true)
.open(&full_path)
.with_context(|| format!("error opening {full_path:?}"))?,
false,
)
}
};
files.push(OpenedFile::new(full_path.clone(), file, writeable));
} else {
// create_new does not seem to work with read(true), so calling this twice.
OpenOptions::new()
let file = OpenOptions::new()
.create_new(true)
.write(true)
.open(&full_path)
Expand All @@ -157,9 +174,10 @@ impl TorrentStorage for FilesystemStorage {
&full_path
)
})?;
OpenOptions::new().read(true).write(true).open(&full_path)?
OpenOptions::new().read(true).write(true).open(&full_path)?;
let writeable = true;
files.push(OpenedFile::new(full_path.clone(), file, writeable));
};
files.push(OpenedFile::new(full_path.clone(), f));
}

self.opened_files = files;
Expand Down
34 changes: 33 additions & 1 deletion crates/librqbit/src/storage/filesystem/opened_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{
io::IoSlice,
ops::{Deref, DerefMut},
path::PathBuf,
sync::atomic::{AtomicBool, Ordering},
};

use anyhow::Context;
Expand Down Expand Up @@ -124,34 +125,65 @@ impl DerefMut for OpenedFileLocked {

#[derive(Debug)]
pub(crate) struct OpenedFile {
path: PathBuf,
file: RwLock<OpenedFileLocked>,
is_writeable: AtomicBool,
}

impl OpenedFile {
pub fn new(path: PathBuf, f: File) -> Self {
pub fn new(path: PathBuf, f: File, is_writeable: bool) -> Self {
Self {
path: path.clone(),
file: RwLock::new(OpenedFileLocked {
path,
fd: Some(f),
#[cfg(windows)]
tried_marking_sparse: false,
}),
is_writeable: AtomicBool::new(is_writeable),
}
}

pub fn new_dummy() -> Self {
Self {
path: PathBuf::from(""),
file: RwLock::new(Default::default()),
is_writeable: AtomicBool::new(false),
}
}

pub fn take_clone(&self) -> anyhow::Result<Self> {
let f = std::mem::take(&mut *self.file.write());
Ok(Self {
path: self.path.clone(),
file: RwLock::new(f),
is_writeable: AtomicBool::new(self.is_writeable.load(Ordering::SeqCst)),
})
}

pub fn ensure_writeable(&self) -> anyhow::Result<()> {
match self
.is_writeable
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Relaxed)
{
Ok(_) => {
// Updated, need to reopen writeable
let mut file = self.file.write();
let new_fd = std::fs::OpenOptions::new()
.write(true)
.create(false)
.open(&self.path)
.with_context(|| format!("error opening {:?} in write mode", self.path))?;
file.fd = Some(new_fd);
}
Err(_) => {
// Didn't update, no need to reopen
}
}

Ok(())
}

pub fn lock_read(&self) -> crate::Result<impl Deref<Target = File>> {
RwLockReadGuard::try_map(self.file.read(), |f| f.as_ref())
.ok()
Expand Down
2 changes: 2 additions & 0 deletions crates/librqbit_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
name = "librqbit-core"
version = "5.0.0"
edition = "2024"
# https://github.com/rust-lang/rust/issues/53667
rust-version = "1.88"
description = "Important utilities used throughout librqbit useful for working with torrents."
license = "Apache-2.0"
documentation = "https://docs.rs/librqbit-core"
Expand Down