1use crate::data::Error;
2use crate::pwp;
3use mtorrent_utils::warn_stopwatch;
4use sha1_smol::Sha1;
5use std::collections::BTreeMap;
6use std::path::{Path, PathBuf};
7use std::{cmp, fs, io};
8use tokio::sync::{mpsc, oneshot};
9
10pub fn new_async_storage(
13 parent_dir: impl AsRef<Path>,
14 length_path_it: impl Iterator<Item = (usize, PathBuf)>,
15) -> Result<(StorageClient, StorageServer), Error> {
16 let storage = Storage::new(parent_dir, length_path_it)?;
17 let (client, server) = async_generic_storage(storage);
18 Ok((client, StorageServer(server)))
19}
20
21pub struct StorageServer(GenericStorageServer<fs::File>);
25
26impl StorageServer {
27 pub async fn run(self) {
30 self.0.run().await;
31 }
32}
33
34#[derive(Clone)]
36pub struct StorageClient {
37 channel: mpsc::UnboundedSender<Command>,
38}
39
40impl StorageClient {
41 pub async fn write_block(&self, global_offset: usize, data: Vec<u8>) -> Result<(), Error> {
44 let (result_sender, result_receiver) = oneshot::channel::<WriteResult>();
45 self.channel.send(Command::WriteBlock {
46 global_offset,
47 data,
48 callback: Some(result_sender),
49 })?;
50 result_receiver.await?
51 }
52
53 pub fn start_write_block(&self, global_offset: usize, data: Vec<u8>) -> Result<(), Error> {
57 self.channel.send(Command::WriteBlock {
58 global_offset,
59 data,
60 callback: None,
61 })?;
62 Ok(())
63 }
64
65 pub async fn read_block(&self, global_offset: usize, length: usize) -> Result<Vec<u8>, Error> {
68 let (result_sender, result_receiver) = oneshot::channel::<ReadResult>();
69 self.channel.send(Command::ReadBlock {
70 global_offset,
71 length,
72 callback: result_sender,
73 })?;
74 result_receiver.await?
75 }
76
77 pub async fn verify_block(
81 &self,
82 global_offset: usize,
83 length: usize,
84 expected_sha1: &[u8; 20],
85 ) -> Result<bool, Error> {
86 let _sw = warn_stopwatch!("Verification of {length} bytes");
87 let (result_sender, result_receiver) = oneshot::channel::<VerifyResult>();
88 self.channel.send(Command::VerifyBlock {
89 global_offset,
90 length,
91 expected_sha1: *expected_sha1,
92 callback: result_sender,
93 })?;
94 result_receiver.await?
95 }
96}
97
98#[cfg(feature = "mocks")]
99#[doc(hidden)]
100pub fn new_mock_storage(total_size: usize) -> StorageClient {
101 let (tx, mut rx) = mpsc::unbounded_channel::<Command>();
102 tokio::task::spawn(async move {
103 while let Some(cmd) = rx.recv().await {
104 match cmd {
105 Command::WriteBlock {
106 global_offset,
107 data,
108 callback,
109 } => {
110 if let Some(cb) = callback {
111 cb.send(if global_offset + data.len() < total_size {
112 Ok(())
113 } else {
114 Err(Error::InvalidLocation)
115 })
116 .unwrap();
117 }
118 }
119 Command::ReadBlock {
120 global_offset,
121 length,
122 callback,
123 } => {
124 callback
125 .send(if global_offset + length < total_size {
126 Ok(vec![0; length])
127 } else {
128 Err(Error::InvalidLocation)
129 })
130 .unwrap();
131 }
132 Command::VerifyBlock {
133 global_offset,
134 length,
135 expected_sha1: _,
136 callback,
137 } => {
138 callback
139 .send(if global_offset + length < total_size {
140 Ok(true)
141 } else {
142 Err(Error::InvalidLocation)
143 })
144 .unwrap();
145 }
146 }
147 }
148 });
149 StorageClient { channel: tx }
150}
151
152fn async_generic_storage<F: RandomAccessReadWrite>(
155 storage: GenericStorage<F>,
156) -> (StorageClient, GenericStorageServer<F>) {
157 let (tx, rx) = mpsc::unbounded_channel::<Command>();
158 (
159 StorageClient { channel: tx },
160 GenericStorageServer {
161 channel: rx,
162 storage,
163 },
164 )
165}
166
167type WriteResult = Result<(), Error>;
168type ReadResult = Result<Vec<u8>, Error>;
169type VerifyResult = Result<bool, Error>;
170
171#[allow(clippy::enum_variant_names)]
172#[derive(Debug)]
173enum Command {
174 WriteBlock {
175 global_offset: usize,
176 data: Vec<u8>,
177 callback: Option<oneshot::Sender<WriteResult>>,
178 },
179 ReadBlock {
180 global_offset: usize,
181 length: usize,
182 callback: oneshot::Sender<ReadResult>,
183 },
184 VerifyBlock {
185 global_offset: usize,
186 length: usize,
187 expected_sha1: [u8; 20],
188 callback: oneshot::Sender<VerifyResult>,
189 },
190}
191
192struct GenericStorageServer<F: RandomAccessReadWrite> {
193 channel: mpsc::UnboundedReceiver<Command>,
194 storage: GenericStorage<F>,
195}
196
197impl<F: RandomAccessReadWrite> GenericStorageServer<F> {
198 async fn run(mut self) {
199 while let Some(cmd) = self.channel.recv().await {
200 self.handle_cmd(cmd);
201 }
202 }
203
204 fn handle_cmd(&self, cmd: Command) {
205 match cmd {
206 Command::WriteBlock {
207 global_offset,
208 data,
209 callback,
210 } => {
211 let result = self.storage.write_block(global_offset, data);
212 if let Some(callback) = callback {
213 let _ = callback.send(result);
214 } else if let Err(e) = result {
215 log::error!("Failed to write block: {e}");
216 }
217 }
218 Command::ReadBlock {
219 global_offset,
220 length,
221 callback,
222 } => {
223 let result = self.storage.read_block(global_offset, length);
224 let _ = callback.send(result);
225 }
226 Command::VerifyBlock {
227 global_offset,
228 length,
229 expected_sha1,
230 callback,
231 } => {
232 let end = global_offset + length;
233 let mut buffer = [0u8; pwp::MAX_BLOCK_SIZE];
234 let mut sha1 = Sha1::new();
235 let result = (global_offset..end)
236 .step_by(buffer.len())
237 .try_for_each(|offset| {
238 let bytes_to_read = cmp::min(buffer.len(), end - offset);
239 let dest = &mut buffer[..bytes_to_read];
240 self.storage.read_block_into(offset, dest).map(|_| sha1.update(dest))
241 })
242 .map(|_| {
243 let computed_sha1: [u8; 20] = sha1.digest().bytes();
244 computed_sha1 == expected_sha1
245 });
246 let _ = callback.send(result);
247 }
248 }
249 }
250}
251
252pub(super) type Storage = GenericStorage<fs::File>;
255
256pub(super) struct GenericStorage<F: RandomAccessReadWrite> {
257 files: BTreeMap<usize, F>,
258}
259
260impl Storage {
261 pub(super) fn new<I: Iterator<Item = (usize, PathBuf)>, P: AsRef<Path>>(
262 parent_dir: P,
263 length_path_it: I,
264 ) -> Result<Self, Error> {
265 let open_file = |(length, path): (usize, PathBuf)| -> io::Result<(usize, fs::File)> {
266 let path = parent_dir.as_ref().join(path);
267 if let Some(prefix) = path.parent() {
268 fs::create_dir_all(prefix)?;
269 }
270 let file = fs::OpenOptions::new()
271 .write(true)
272 .read(true)
273 .create(true)
274 .truncate(false)
275 .open(path)?;
276 file.set_len(length as u64)?;
277 Ok((length, file))
278 };
279
280 Self::from_length_file_pairs(length_path_it.map(open_file))
281 }
282}
283
284impl<F: RandomAccessReadWrite> GenericStorage<F> {
285 fn from_length_file_pairs<I: Iterator<Item = io::Result<(usize, F)>>>(
286 length_file_it: I,
287 ) -> Result<Self, Error> {
288 let mut filemap = BTreeMap::new();
289 let mut offset = 0usize;
290
291 for result in length_file_it {
292 let (length, file) = result?;
293 filemap.insert(offset, file);
294 offset += length;
295 }
296 if let Some((_offset, file)) = filemap.last_key_value() {
297 let fd_clone = file.try_clone()?;
298 filemap.insert(offset, fd_clone);
299 }
300 Ok(Self { files: filemap })
301 }
302
303 pub(super) fn write_block(&self, global_offset: usize, block: Vec<u8>) -> Result<(), Error> {
304 self.write_block_from(global_offset, &block)?;
305 Ok(())
306 }
307
308 pub(super) fn read_block(&self, global_offset: usize, length: usize) -> Result<Vec<u8>, Error> {
309 let mut dest = vec![0u8; length];
310 self.read_block_into(global_offset, &mut dest)?;
311 Ok(dest)
312 }
313
314 fn find_file_and_offset(&self, global_offset: usize) -> Result<(usize, &F, usize), Error> {
315 let next_start_offset = {
316 let (offset, _) =
317 self.files.range(global_offset + 1..).next().ok_or(Error::InvalidLocation)?;
318 *offset
319 };
320 let (start_offset, file) = {
321 let (offset, file) =
322 self.files.range(..=global_offset).last().ok_or(Error::InvalidLocation)?;
323 (*offset, file)
324 };
325 Ok((start_offset, file, next_start_offset))
326 }
327
328 fn write_block_from(&self, global_offset: usize, src: &[u8]) -> Result<(), Error> {
329 let (start_offset, file, next_start_offset) = self.find_file_and_offset(global_offset)?;
330 let local_offset = global_offset - start_offset;
331
332 let available_space = next_start_offset - global_offset;
333 if src.len() <= available_space {
334 file.write_all_at_offset(src, local_offset as u64)?;
335 Ok(())
336 } else {
337 let (left, right) = src.split_at(available_space);
338 file.write_all_at_offset(left, local_offset as u64)?;
339 self.write_block_from(next_start_offset, right)
340 }
341 }
342
343 fn read_block_into(&self, global_offset: usize, dest: &mut [u8]) -> Result<(), Error> {
344 let (start_offset, file, next_start_offset) = self.find_file_and_offset(global_offset)?;
345 let local_offset = global_offset - start_offset;
346
347 let available_space = next_start_offset - global_offset;
348 if dest.len() <= available_space {
349 file.read_all_at_offset(dest, local_offset as u64)?;
350 Ok(())
351 } else {
352 let (left, right) = dest.split_at_mut(available_space);
353 file.read_all_at_offset(left, local_offset as u64)?;
354 self.read_block_into(next_start_offset, right)
355 }
356 }
357}
358
359pub(super) trait RandomAccessReadWrite {
360 fn read_at_offset(&self, dest: &mut [u8], offset: u64) -> io::Result<usize>;
361 fn write_at_offset(&self, src: &[u8], offset: u64) -> io::Result<usize>;
362 fn try_clone(&self) -> io::Result<Self>
363 where
364 Self: Sized;
365
366 fn read_all_at_offset(&self, mut dest: &mut [u8], mut offset: u64) -> io::Result<()> {
367 while !dest.is_empty() {
368 let bytes_read = self.read_at_offset(dest, offset)?;
369 if bytes_read == 0 {
370 return Err(io::Error::new(
371 io::ErrorKind::UnexpectedEof,
372 "failed to fill whole buffer",
373 ));
374 }
375 dest = &mut dest[bytes_read..];
376 offset += bytes_read as u64;
377 }
378 Ok(())
379 }
380 fn write_all_at_offset(&self, mut src: &[u8], mut offset: u64) -> io::Result<()> {
381 while !src.is_empty() {
382 let bytes_written = self.write_at_offset(src, offset)?;
383 if bytes_written == 0 {
384 return Err(io::Error::new(
385 io::ErrorKind::WriteZero,
386 "failed to write whole buffer",
387 ));
388 }
389 src = &src[bytes_written..];
390 offset += bytes_written as u64;
391 }
392 Ok(())
393 }
394}
395
396#[cfg(unix)]
397impl RandomAccessReadWrite for fs::File {
398 fn read_at_offset(&self, dest: &mut [u8], offset: u64) -> io::Result<usize> {
399 use std::os::unix::prelude::*;
400 self.read_at(dest, offset)
401 }
402
403 fn write_at_offset(&self, src: &[u8], offset: u64) -> io::Result<usize> {
404 use std::os::unix::prelude::*;
405 self.write_at(src, offset)
406 }
407
408 fn try_clone(&self) -> io::Result<Self>
409 where
410 Self: Sized,
411 {
412 self.try_clone()
413 }
414}
415
416#[cfg(windows)]
417impl RandomAccessReadWrite for fs::File {
418 fn read_at_offset(&self, dest: &mut [u8], offset: u64) -> io::Result<usize> {
419 use std::os::windows::prelude::*;
420 self.seek_read(dest, offset)
421 }
422
423 fn write_at_offset(&self, src: &[u8], offset: u64) -> io::Result<usize> {
424 use std::os::windows::prelude::*;
425 self.seek_write(src, offset)
426 }
427
428 fn try_clone(&self) -> io::Result<Self>
429 where
430 Self: Sized,
431 {
432 self.try_clone()
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439 use std::io::{Cursor, Read, Seek, SeekFrom, Write};
440 use std::iter;
441 use tokio::task;
442
443 type FakeFile = std::cell::RefCell<Cursor<Vec<u8>>>;
444
445 fn fake_length_file_pair(content: Vec<u8>) -> io::Result<(usize, FakeFile)> {
446 Ok((content.len(), std::cell::RefCell::new(Cursor::new(content))))
447 }
448
449 impl RandomAccessReadWrite for FakeFile {
450 fn read_at_offset(&self, dest: &mut [u8], offset: u64) -> io::Result<usize> {
451 self.borrow_mut().seek(SeekFrom::Start(offset))?;
452 self.borrow_mut().read(dest)
453 }
454
455 fn write_at_offset(&self, src: &[u8], offset: u64) -> io::Result<usize> {
456 self.borrow_mut().seek(SeekFrom::Start(offset))?;
457 self.borrow_mut().write(src)
458 }
459
460 fn try_clone(&self) -> io::Result<Self>
461 where
462 Self: Sized,
463 {
464 Ok(self.clone())
465 }
466 }
467
468 #[test]
469 fn test_write_piece_within_one_file() {
470 let s = GenericStorage::from_length_file_pairs(
471 iter::repeat_with(|| fake_length_file_pair(vec![0u8; 10])).take(3),
472 )
473 .unwrap();
474
475 s.write_block(16, vec![1u8, 2u8, 3u8, 4u8]).unwrap();
476
477 assert_eq!(&vec![0u8; 10], s.files.get(&0).unwrap().borrow().get_ref());
478 assert_eq!(
479 &vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, 4u8],
480 s.files.get(&10).unwrap().borrow().get_ref()
481 );
482 assert_eq!(&vec![0u8; 10], s.files.get(&20).unwrap().borrow().get_ref());
483 }
484
485 #[test]
486 fn test_write_piece_on_file_boundary() {
487 let s = GenericStorage::from_length_file_pairs(
488 iter::repeat_with(|| fake_length_file_pair(vec![0u8; 10])).take(3),
489 )
490 .unwrap();
491
492 s.write_block(17, vec![1u8, 2u8, 3u8, 4u8, 5u8]).unwrap();
493
494 assert_eq!(&vec![0u8; 10], s.files.get(&0).unwrap().borrow().get_ref());
495 assert_eq!(
496 &vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8],
497 s.files.get(&10).unwrap().borrow().get_ref()
498 );
499 assert_eq!(
500 &vec![4u8, 5u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
501 s.files.get(&20).unwrap().borrow().get_ref()
502 );
503 }
504
505 #[test]
506 fn test_read_piece_within_one_file() {
507 let s = GenericStorage::from_length_file_pairs(
508 iter::repeat_with(|| fake_length_file_pair((0u8..10u8).collect())).take(3),
509 )
510 .unwrap();
511
512 let dest = s.read_block(12, 3).unwrap();
513 assert_eq!(vec![2u8, 3u8, 4u8], dest);
514
515 assert_eq!(&(0u8..10u8).collect::<Vec<u8>>(), s.files.get(&0).unwrap().borrow().get_ref());
516 assert_eq!(&(0u8..10u8).collect::<Vec<u8>>(), s.files.get(&10).unwrap().borrow().get_ref());
517 assert_eq!(&(0u8..10u8).collect::<Vec<u8>>(), s.files.get(&20).unwrap().borrow().get_ref());
518 }
519
520 #[test]
521 fn test_read_piece_on_file_boundary() {
522 let s = GenericStorage::from_length_file_pairs(
523 iter::repeat_with(|| fake_length_file_pair((1u8..=10u8).collect())).take(2),
524 )
525 .unwrap();
526
527 let dest = s.read_block(8, 3).unwrap();
528 assert_eq!(vec![9u8, 10u8, 1u8], dest);
529
530 assert_eq!(&(1u8..=10u8).collect::<Vec<u8>>(), s.files.get(&0).unwrap().borrow().get_ref());
531 assert_eq!(
532 &(1u8..=10u8).collect::<Vec<u8>>(),
533 s.files.get(&10).unwrap().borrow().get_ref()
534 );
535 }
536
537 #[test]
538 fn test_past_the_end_read_fails() {
539 let s = GenericStorage::from_length_file_pairs(iter::once(fake_length_file_pair(
540 (1u8..=10u8).collect(),
541 )))
542 .unwrap();
543 assert!(matches!(s.read_block(5, 10), Err(Error::InvalidLocation)));
544 assert!(matches!(s.read_block(11, 5), Err(Error::InvalidLocation)));
545 }
546
547 #[tokio::test]
548 async fn test_async_detached_write_then_read_on_file_boundary() {
549 task::LocalSet::new()
550 .run_until(async {
551 let s = GenericStorage::from_length_file_pairs(
552 iter::repeat_with(|| fake_length_file_pair(vec![0u8; 10])).take(2),
553 )
554 .unwrap();
555
556 let (client, server) = async_generic_storage(s);
557
558 task::spawn_local(async move {
559 server.run().await;
560 });
561
562 let initial_data = client.read_block(8, 3).await.unwrap();
564 assert_eq!(vec![0u8, 0u8, 0u8], initial_data);
565
566 client.start_write_block(8, vec![9u8, 10u8, 1u8]).unwrap();
568
569 let final_data = client.read_block(8, 3).await.unwrap();
571 assert_eq!(vec![9u8, 10u8, 1u8], final_data);
572 })
573 .await;
574 }
575
576 #[tokio::test]
577 async fn test_async_verify_block() {
578 let sha1_0_10 =
579 b"\x49\x41\x79\x71\x4a\x6c\xd6\x27\x23\x9d\xfe\xde\xdf\x2d\xe9\xef\x99\x4c\xaf\x03";
580 let sha1_10_20 =
581 b"\xdd\xd1\x27\x8d\x28\xaf\x87\xc7\x58\x84\xf5\x5b\x71\xfb\xb4\xa1\x23\x1a\xf2\xe5";
582 task::LocalSet::new()
583 .run_until(async {
584 let s = GenericStorage::from_length_file_pairs(iter::once(fake_length_file_pair(
585 (0u8..20u8).collect(),
586 )))
587 .unwrap();
588
589 let (client, server) = async_generic_storage(s);
590
591 task::spawn_local(async move {
592 server.run().await;
593 });
594
595 let verify_success = client.verify_block(0, 10, sha1_0_10).await.unwrap();
596 assert!(verify_success);
597
598 let verify_success = client.verify_block(0, 10, sha1_10_20).await.unwrap();
599 assert!(!verify_success);
600
601 let verify_success = client.verify_block(10, 10, sha1_10_20).await.unwrap();
602 assert!(verify_success);
603
604 let verify_success = client.verify_block(10, 10, sha1_0_10).await.unwrap();
605 assert!(!verify_success);
606 })
607 .await;
608 }
609}