Skip to content

Commit bc645f4

Browse files
committed
tweak: generate temp filenames ourselves
1 parent a40995d commit bc645f4

File tree

5 files changed

+61
-74
lines changed

5 files changed

+61
-74
lines changed

Cargo.lock

Lines changed: 2 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,17 @@ edition = "2018"
1313
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1414

1515
[dependencies]
16-
tempfile = "3.2.0"
1716
sled = "0.34.6"
1817
hex = "0.4.3"
1918
md5 = "0.7.0"
19+
rand = {version = "0.8.3", features = ["small_rng", "getrandom"], default-features = false}
2020
bson = {version = "1.2.2", features = ["u2i"]}
2121
serde = {version = "1.0.125", features = ["derive"]}
2222
tokio = {version = "1.5.0", features = ["rt", "fs", "io-util"]}
2323

2424
[dev-dependencies]
2525
tokio = {version = "1.5.0", features = ["full"]}
2626
criterion = {version = "0.3.4", features = ["async_tokio", "html_reports"]}
27-
rand = {version = "0.8.3", features = ["small_rng"], default-features = false}
2827

2928
[lib]
3029
path = "src/lib.rs"

src/cache.rs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ use std::io;
33
use std::path;
44
use tokio::fs as afs;
55

6+
/// Creates a writeable and persistent temporary file in the path provided, returning the path and
7+
/// file handle.
8+
async fn tempfile(dir: &path::Path) -> Result<(afs::File, path::PathBuf)> {
9+
let tmppath = crate::tmp::tmppath_in(dir);
10+
let tmp = afs::OpenOptions::new()
11+
.write(true)
12+
.create(true)
13+
.truncate(true)
14+
.open(&tmppath)
15+
.await
16+
.map_err(ForcepError::Io)?;
17+
Ok((tmp, tmppath))
18+
}
19+
620
/// The main component of `forceps`, acts as the API for interacting with the on-disk API.
721
///
822
/// This structure exposes `read`, `write`, and misc metadata operations. `read` and `write` are
@@ -43,6 +57,7 @@ pub struct CacheBuilder {
4357
path: path::PathBuf,
4458
}
4559

60+
4661
impl Cache {
4762
/// Creates a new Cache instance based on the CacheBuilder
4863
async fn new(builder: CacheBuilder) -> Result<Self> {
@@ -78,29 +93,6 @@ impl Cache {
7893
buf
7994
}
8095

81-
/// Creates a temporary file in the `self.path` directory of the cache, returning the file
82-
/// handle and the path of the file created.
83-
async fn open_tempfile(&self) -> Result<(afs::File, path::PathBuf)> {
84-
let tmp_in = self.path.clone();
85-
86-
// spawn a blocking task to create the temporary file
87-
tokio::task::spawn_blocking(move || {
88-
// create a new named temporary file and persist it to capture the file handle and path
89-
tempfile::NamedTempFile::new_in(&tmp_in)
90-
.map_err(ForcepError::Io)
91-
.and_then(|x| x.keep().map_err(|e| ForcepError::Io(e.into())))
92-
.map(|(file, path)| (afs::File::from_std(file), path))
93-
})
94-
.await
95-
.map_err(|_| {
96-
ForcepError::Io(io::Error::new(
97-
io::ErrorKind::Other,
98-
"cannot join blocking task",
99-
))
100-
})
101-
.and_then(|x| x)
102-
}
103-
10496
/// Reads an entry from the database, returning a vector of bytes that represent the entry.
10597
///
10698
/// # Not Found
@@ -175,7 +167,7 @@ impl Cache {
175167
let key = key.as_ref();
176168
let value = value.as_ref();
177169

178-
let (tmp, tmp_path) = self.open_tempfile().await?;
170+
let (tmp, tmp_path) = tempfile(&self.path).await?;
179171
// write all data to a temporary file
180172
{
181173
let mut writer = tokio::io::BufWriter::new(tmp);

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ impl std::fmt::Display for ForcepError {
8383
}
8484
impl std::error::Error for ForcepError {}
8585

86+
mod tmp;
87+
8688
mod cache;
8789
pub use cache::{Cache, CacheBuilder};
8890

src/tmp.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use rand::{
2+
prelude::*,
3+
distributions::Alphanumeric
4+
};
5+
use std::cell::UnsafeCell;
6+
use std::path;
7+
use std::ffi::{OsString, OsStr};
8+
use std::str;
9+
10+
std::thread_local! {
11+
static RNG: UnsafeCell<SmallRng> = UnsafeCell::new(SmallRng::from_entropy());
12+
}
13+
14+
/// Reimplementation of [this][tmpfile]. Licensed under Apache license
15+
///
16+
/// [tmpfile]: https://github.com/Stebalien/tempfile/blob/c361acc1213605eb26be0ad6de4c0f29cb7491f0/src/util.rs#L17
17+
fn tmpname(prefix: &OsStr, rand_len: usize) -> OsString {
18+
let mut buf = OsString::with_capacity(prefix.len() + rand_len);
19+
buf.push(prefix);
20+
21+
// Push each character in one-by-one. Unfortunately, this is the only
22+
// safe(ish) simple way to do this without allocating a temporary
23+
// String/Vec.
24+
RNG.with(|rng| unsafe {
25+
(&mut *rng.get())
26+
.sample_iter(&Alphanumeric)
27+
.take(rand_len)
28+
.for_each(|b| buf.push(str::from_utf8_unchecked(&[b as u8])))
29+
});
30+
buf
31+
}
32+
33+
/// Creates a randomized path in a directory that can be used as a temporary file
34+
pub(crate) fn tmppath_in(dir: &path::Path) -> path::PathBuf {
35+
const LEN: usize = 10;
36+
let mut buf = path::PathBuf::new();
37+
buf.push(dir);
38+
buf.push(&tmpname(OsStr::new("tmp"), LEN));
39+
buf
40+
}

0 commit comments

Comments
 (0)