//! Temporary files and directories
-use io::{fs, IoResult};
+use io::{fs, IoError, IoErrorKind, IoResult};
use io;
-use libc;
+use iter::{IteratorExt, range};
use ops::Drop;
use option::Option;
use option::Option::{None, Some};
use os;
use path::{Path, GenericPath};
+use rand::{Rng, thread_rng};
use result::Result::{Ok, Err};
-use sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
+use str::StrExt;
+use string::String;
/// A wrapper for a path to temporary directory implementing automatic
/// scope-based deletion.
///
/// {
/// // create a temporary directory
-/// let tmpdir = match TempDir::new("mysuffix") {
+/// let tmpdir = match TempDir::new("myprefix") {
/// Ok(dir) => dir,
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
/// };
/// }
/// {
/// // create a temporary directory, this time using a custom path
-/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "mysuffix") {
+/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") {
/// Ok(dir) => dir,
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
/// };
/// }
/// {
/// // create a temporary directory
-/// let tmpdir = match TempDir::new("mysuffix") {
+/// let tmpdir = match TempDir::new("myprefix") {
/// Ok(dir) => dir,
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
/// };
disarmed: bool
}
+// How many times should we (re)try finding an unused random name? It should be
+// enough that an attacker will run out of luck before we run out of patience.
+const NUM_RETRIES: u32 = 1 << 31;
+// How many characters should we include in a random file name? It needs to
+// be enough to dissuade an attacker from trying to preemptively create names
+// of that length, but not so huge that we unnecessarily drain the random number
+// generator of entropy.
+const NUM_RAND_CHARS: uint = 12;
+
impl TempDir {
/// Attempts to make a temporary directory inside of `tmpdir` whose name
- /// will have the suffix `suffix`. The directory will be automatically
+ /// will have the prefix `prefix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, `Err` is returned.
- pub fn new_in(tmpdir: &Path, suffix: &str) -> IoResult<TempDir> {
+ pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> {
if !tmpdir.is_absolute() {
let abs_tmpdir = try!(os::make_absolute(tmpdir));
- return TempDir::new_in(&abs_tmpdir, suffix);
+ return TempDir::new_in(&abs_tmpdir, prefix);
}
- static CNT: AtomicUint = ATOMIC_UINT_INIT;
-
- let mut attempts = 0u;
- loop {
- let filename =
- format!("rs-{}-{}-{}",
- unsafe { libc::getpid() },
- CNT.fetch_add(1, Ordering::SeqCst),
- suffix);
- let p = tmpdir.join(filename);
- match fs::mkdir(&p, io::USER_RWX) {
- Err(error) => {
- if attempts >= 1000 {
- return Err(error)
- }
- attempts += 1;
- }
- Ok(()) => return Ok(TempDir { path: Some(p), disarmed: false })
+ let mut rng = thread_rng();
+ for _ in range(0, NUM_RETRIES) {
+ let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect();
+ let leaf = if prefix.len() > 0 {
+ format!("{}.{}", prefix, suffix)
+ } else {
+ // If we're given an empty string for a prefix, then creating a
+ // directory starting with "." would lead to it being
+ // semi-invisible on some systems.
+ suffix
+ };
+ let path = tmpdir.join(leaf);
+ match fs::mkdir(&path, io::USER_RWX) {
+ Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }),
+ Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (),
+ Err(e) => return Err(e)
}
}
+
+ return Err(IoError{
+ kind: IoErrorKind::PathAlreadyExists,
+ desc:"Exhausted",
+ detail: None});
}
/// Attempts to make a temporary directory inside of `os::tmpdir()` whose
- /// name will have the suffix `suffix`. The directory will be automatically
+ /// name will have the prefix `prefix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, `Err` is returned.
- pub fn new(suffix: &str) -> IoResult<TempDir> {
- TempDir::new_in(&os::tmpdir(), suffix)
+ pub fn new(prefix: &str) -> IoResult<TempDir> {
+ TempDir::new_in(&os::tmpdir(), prefix)
}
/// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.