2013-01-16 05:28:39 -05:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 16:48:01 -08:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2012-07-04 22:53:12 +01:00
|
|
|
//! Temporary files and directories
|
2015-02-03 20:11:38 +11:00
|
|
|
#![allow(deprecated)] // rand
|
2011-11-08 23:35:15 -05:00
|
|
|
|
2015-01-27 12:20:58 -08:00
|
|
|
use env;
|
|
|
|
use iter::{IteratorExt};
|
2015-01-22 16:27:48 -08:00
|
|
|
use old_io::{fs, IoError, IoErrorKind, IoResult};
|
|
|
|
use old_io;
|
2014-03-14 11:16:10 -07:00
|
|
|
use ops::Drop;
|
2014-11-28 11:57:41 -05:00
|
|
|
use option::Option::{None, Some};
|
2015-01-27 12:20:58 -08:00
|
|
|
use option::Option;
|
2015-01-29 14:03:36 -08:00
|
|
|
use old_path::{Path, GenericPath};
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
use rand::{Rng, thread_rng};
|
2014-11-28 11:57:41 -05:00
|
|
|
use result::Result::{Ok, Err};
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
use str::StrExt;
|
|
|
|
use string::String;
|
2011-11-08 23:35:15 -05:00
|
|
|
|
2013-10-11 15:55:37 +02:00
|
|
|
/// A wrapper for a path to temporary directory implementing automatic
|
2013-12-15 16:34:14 +11:00
|
|
|
/// scope-based deletion.
|
2014-12-16 19:15:05 +01:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
2014-12-17 07:21:29 +01:00
|
|
|
/// ```no_run
|
2015-01-22 16:27:48 -08:00
|
|
|
/// use std::old_io::TempDir;
|
2014-12-16 19:15:05 +01:00
|
|
|
///
|
|
|
|
/// {
|
|
|
|
/// // create a temporary directory
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
/// let tmpdir = match TempDir::new("myprefix") {
|
2014-12-16 19:15:05 +01:00
|
|
|
/// Ok(dir) => dir,
|
|
|
|
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // get the path of the temporary directory without affecting the wrapper
|
|
|
|
/// let tmppath = tmpdir.path();
|
|
|
|
///
|
2014-12-17 07:21:29 +01:00
|
|
|
/// println!("The path of temporary directory is {}", tmppath.display());
|
2014-12-16 19:15:05 +01:00
|
|
|
///
|
|
|
|
/// // the temporary directory is automatically removed when tmpdir goes
|
|
|
|
/// // out of scope at the end of the block
|
|
|
|
/// }
|
|
|
|
/// {
|
|
|
|
/// // create a temporary directory, this time using a custom path
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") {
|
2014-12-16 19:15:05 +01:00
|
|
|
/// Ok(dir) => dir,
|
|
|
|
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // get the path of the temporary directory and disable automatic deletion in the wrapper
|
|
|
|
/// let tmppath = tmpdir.into_inner();
|
|
|
|
///
|
2014-12-17 07:21:29 +01:00
|
|
|
/// println!("The path of the not-so-temporary directory is {}", tmppath.display());
|
2014-12-16 19:15:05 +01:00
|
|
|
///
|
|
|
|
/// // the temporary directory is not removed here
|
|
|
|
/// // because the directory is detached from the wrapper
|
|
|
|
/// }
|
|
|
|
/// {
|
|
|
|
/// // create a temporary directory
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
/// let tmpdir = match TempDir::new("myprefix") {
|
2014-12-16 19:15:05 +01:00
|
|
|
/// Ok(dir) => dir,
|
|
|
|
/// Err(e) => panic!("couldn't create temporary directory: {}", e)
|
|
|
|
/// };
|
|
|
|
///
|
|
|
|
/// // close the temporary directory manually and check the result
|
|
|
|
/// match tmpdir.close() {
|
|
|
|
/// Ok(_) => println!("success!"),
|
|
|
|
/// Err(e) => panic!("couldn't remove temporary directory: {}", e)
|
|
|
|
/// };
|
|
|
|
/// }
|
|
|
|
/// ```
|
2013-10-11 15:55:37 +02:00
|
|
|
pub struct TempDir {
|
2014-05-14 17:49:14 -07:00
|
|
|
path: Option<Path>,
|
|
|
|
disarmed: bool
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
// 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;
|
|
|
|
|
2013-10-11 15:55:37 +02:00
|
|
|
impl TempDir {
|
|
|
|
/// Attempts to make a temporary directory inside of `tmpdir` whose name
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
/// will have the prefix `prefix`. The directory will be automatically
|
2013-10-11 15:55:37 +02:00
|
|
|
/// deleted once the returned wrapper is destroyed.
|
|
|
|
///
|
2014-08-30 13:12:47 +01:00
|
|
|
/// If no directory can be created, `Err` is returned.
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> {
|
2013-10-11 15:55:37 +02:00
|
|
|
if !tmpdir.is_absolute() {
|
2015-01-27 12:20:58 -08:00
|
|
|
let cur_dir = try!(env::current_dir());
|
|
|
|
return TempDir::new_in(&cur_dir.join(tmpdir), prefix);
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
let mut rng = thread_rng();
|
2015-01-26 15:46:12 -05:00
|
|
|
for _ in 0..NUM_RETRIES {
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
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);
|
2015-01-22 16:27:48 -08:00
|
|
|
match fs::mkdir(&path, old_io::USER_RWX) {
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }),
|
|
|
|
Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (),
|
|
|
|
Err(e) => return Err(e)
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
}
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
|
|
|
|
return Err(IoError{
|
|
|
|
kind: IoErrorKind::PathAlreadyExists,
|
|
|
|
desc:"Exhausted",
|
|
|
|
detail: None});
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Attempts to make a temporary directory inside of `os::tmpdir()` whose
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
/// name will have the prefix `prefix`. The directory will be automatically
|
2013-10-11 15:55:37 +02:00
|
|
|
/// deleted once the returned wrapper is destroyed.
|
|
|
|
///
|
2014-08-30 13:12:47 +01:00
|
|
|
/// If no directory can be created, `Err` is returned.
|
Make temporary directory names non-deterministic.
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:
for i in range(0u, 1001) {
let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
match fs::mkdir(tp, io::USER_RWX) { _ => () }
}
Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.
This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).
As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
2015-01-03 21:49:01 +00:00
|
|
|
pub fn new(prefix: &str) -> IoResult<TempDir> {
|
2015-01-27 12:20:58 -08:00
|
|
|
TempDir::new_in(&env::temp_dir(), prefix)
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
|
|
|
|
/// This discards the wrapper so that the automatic deletion of the
|
|
|
|
/// temporary directory is prevented.
|
2014-11-20 09:23:43 -08:00
|
|
|
pub fn into_inner(self) -> Path {
|
2013-10-11 15:55:37 +02:00
|
|
|
let mut tmpdir = self;
|
2014-08-18 17:52:38 -07:00
|
|
|
tmpdir.path.take().unwrap()
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Access the wrapped `std::path::Path` to the temporary directory.
|
|
|
|
pub fn path<'a>(&'a self) -> &'a Path {
|
2014-09-17 10:47:05 -07:00
|
|
|
self.path.as_ref().unwrap()
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
2014-05-14 17:49:14 -07:00
|
|
|
|
|
|
|
/// Close and remove the temporary directory
|
|
|
|
///
|
|
|
|
/// Although `TempDir` removes the directory on drop, in the destructor
|
|
|
|
/// any errors are ignored. To detect errors cleaning up the temporary
|
|
|
|
/// directory, call `close` instead.
|
|
|
|
pub fn close(mut self) -> IoResult<()> {
|
|
|
|
self.cleanup_dir()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cleanup_dir(&mut self) -> IoResult<()> {
|
|
|
|
assert!(!self.disarmed);
|
|
|
|
self.disarmed = true;
|
|
|
|
match self.path {
|
|
|
|
Some(ref p) => {
|
|
|
|
fs::rmdir_recursive(p)
|
|
|
|
}
|
|
|
|
None => Ok(())
|
|
|
|
}
|
|
|
|
}
|
2013-10-11 15:55:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for TempDir {
|
|
|
|
fn drop(&mut self) {
|
2014-05-14 17:49:14 -07:00
|
|
|
if !self.disarmed {
|
|
|
|
let _ = self.cleanup_dir();
|
2011-11-08 23:35:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-17 19:05:07 -08:00
|
|
|
|
2013-09-13 21:41:28 -07:00
|
|
|
// the tests for this module need to change the path using change_dir,
|
|
|
|
// and this doesn't play nicely with other tests so these unit tests are located
|
|
|
|
// in src/test/run-pass/tempfile.rs
|