1//! The file system notebooks and their replicas live in: std's natively. In the browser,
2//! which gives a module none, it is `web`'s, which keeps SQLite's files beside the notebooks'.
3
4#[cfg(not(target_arch = "wasm32"))]
5pub use onestore::{confirm_file, place_file, read_file, read_file_limited, supersede_file};
6#[cfg(not(target_arch = "wasm32"))]
7pub use std::{fs::*, path::absolute, process::id as process_id};
8
9/// Moves the file `from` to `to`, refusing to replace a file there. Android forbids apps hard
10/// links, the usual way to do this.
11#[cfg(not(target_arch = "wasm32"))]
12pub fn rename_new(
13 from: impl AsRef<std::path::Path>,
14 to: impl AsRef<std::path::Path>,
15) -> std::io::Result<()> {
16 tempfile::TempPath::try_from_path(from.as_ref())?
17 .persist_noclobber(to)
18 .map_err(|error| {
19 let _ = error.path.keep();
20 error.error
21 })
22}
23
24/// `Transaction::commit_file` through this file system.
25#[cfg(not(target_arch = "wasm32"))]
26pub fn commit_file(
27 transaction: &onestore::Transaction,
28 path: impl AsRef<std::path::Path>,
29) -> Result<(), onestore::CommitError> {
30 transaction.commit_file(path)
31}
32
33#[cfg(target_arch = "wasm32")]
34mod web;
35#[cfg(target_arch = "wasm32")]
36pub use web::*;
37
38/// Reads a regular file within `limit`, including when it grows during the read.
39pub fn read_limited(path: impl AsRef<std::path::Path>, limit: usize) -> std::io::Result<Vec<u8>> {
40 use std::io::Read;
41 let file = File::open(path)?;
42 let metadata = file.metadata()?;
43 if !metadata.is_file() {
44 return Err(std::io::ErrorKind::InvalidInput.into());
45 }
46 if metadata.len() > limit as u64 {
47 return Err(std::io::ErrorKind::FileTooLarge.into());
48 }
49 let mut bytes = Vec::new();
50 file.take((limit as u64).saturating_add(1))
51 .read_to_end(&mut bytes)?;
52 if bytes.len() > limit {
53 return Err(std::io::ErrorKind::FileTooLarge.into());
54 }
55 Ok(bytes)
56}
57
58#[cfg(not(target_arch = "wasm32"))]
59pub async fn durable() -> std::io::Result<()> {
60 Ok(())
61}
62
63#[cfg(all(test, not(target_arch = "wasm32")))]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn bounded_reads_refuse_an_eight_gib_file() {
69 let file = tempfile::NamedTempFile::new().unwrap();
70 write(file.path(), b"hello").unwrap();
71 assert_eq!(read_limited(file.path(), 5).unwrap(), b"hello");
72 assert_eq!(
73 read_limited(file.path(), 4).unwrap_err().kind(),
74 std::io::ErrorKind::FileTooLarge
75 );
76 file.as_file().set_len(8 << 30).unwrap();
77 assert_eq!(
78 read_limited(file.path(), crate::MAX_FILE_BYTES)
79 .unwrap_err()
80 .kind(),
81 std::io::ErrorKind::FileTooLarge
82 );
83 let folder = tempfile::tempdir().unwrap();
84 assert!(read_limited(folder.path(), crate::MAX_FILE_BYTES).is_err());
85 }
86}