Auto merge of #30872 - pitdicker:expand_open_options, r=alexcrichton

Tracking issue: #30014

This implements the RFC and makes a few other changes.
I have added a few extra tests, and made the Windows and
Unix code as similar as possible.

Part of the RFC mentions the unstable OpenOptionsExt trait
on Windows (see #27720). I have added a few extra methods
to future-proof it for CreateFile2.
This commit is contained in:
bors
2016-01-20 17:19:21 +00:00
6 changed files with 471 additions and 168 deletions

View File

@@ -118,8 +118,38 @@ pub trait OpenOptionsExt {
///
/// If a new file is created as part of a `File::open_opts` call then this
/// specified `mode` will be used as the permission bits for the new file.
/// If no `mode` is set, the default of `0o666` will be used.
/// The operating system masks out bits with the systems `umask`, to produce
/// the final permissions.
#[stable(feature = "fs_ext", since = "1.1.0")]
fn mode(&mut self, mode: raw::mode_t) -> &mut Self;
/// Pass custom flags to the `flags` agument of `open`.
///
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
/// ensure they do not interfere with the access mode set by Rusts options.
///
/// Custom flags can only set flags, not remove flags set by Rusts options.
/// This options overwrites any previously set custom flags.
///
/// # Examples
///
/// ```rust,ignore
/// extern crate libc;
/// use std::fs::OpenOptions;
/// use std::os::unix::fs::OpenOptionsExt;
///
/// let mut options = OpenOptions::new();
/// options.write(true);
/// if cfg!(unix) {
/// options.custom_flags(libc::O_NOFOLLOW);
/// }
/// let file = options.open("foo.txt");
/// ```
#[unstable(feature = "expand_open_options",
reason = "recently added",
issue = "30014")]
fn custom_flags(&mut self, flags: i32) -> &mut Self;
}
#[stable(feature = "fs_ext", since = "1.1.0")]
@@ -127,6 +157,10 @@ impl OpenOptionsExt for OpenOptions {
fn mode(&mut self, mode: raw::mode_t) -> &mut OpenOptions {
self.as_inner_mut().mode(mode); self
}
fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
self.as_inner_mut().custom_flags(flags); self
}
}
// Hm, why are there casts here to the returned type, shouldn't the types always
@@ -281,4 +315,3 @@ impl DirBuilderExt for fs::DirBuilder {
self
}
}

View File

@@ -50,9 +50,15 @@ pub struct DirEntry {
#[derive(Clone)]
pub struct OpenOptions {
flags: c_int,
// generic
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
// system-specific
custom_flags: i32,
mode: mode_t,
}
@@ -233,44 +239,61 @@ impl DirEntry {
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {
flags: libc::O_CLOEXEC,
// generic
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
// system-specific
custom_flags: 0,
mode: 0o666,
}
}
pub fn read(&mut self, read: bool) {
self.read = read;
}
pub fn read(&mut self, read: bool) { self.read = read; }
pub fn write(&mut self, write: bool) { self.write = write; }
pub fn append(&mut self, append: bool) { self.append = append; }
pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; }
pub fn create(&mut self, create: bool) { self.create = create; }
pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; }
pub fn write(&mut self, write: bool) {
self.write = write;
}
pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; }
pub fn mode(&mut self, mode: raw::mode_t) { self.mode = mode as mode_t; }
pub fn append(&mut self, append: bool) {
self.flag(libc::O_APPEND, append);
}
pub fn truncate(&mut self, truncate: bool) {
self.flag(libc::O_TRUNC, truncate);
}
pub fn create(&mut self, create: bool) {
self.flag(libc::O_CREAT, create);
}
pub fn mode(&mut self, mode: raw::mode_t) {
self.mode = mode as mode_t;
}
fn flag(&mut self, bit: c_int, on: bool) {
if on {
self.flags |= bit;
} else {
self.flags &= !bit;
fn get_access_mode(&self) -> io::Result<c_int> {
match (self.read, self.write, self.append) {
(true, false, false) => Ok(libc::O_RDONLY),
(false, true, false) => Ok(libc::O_WRONLY),
(true, true, false) => Ok(libc::O_RDWR),
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
}
}
fn get_creation_mode(&self) -> io::Result<c_int> {
match (self.write, self.append) {
(true, false) => {}
(false, false) =>
if self.truncate || self.create || self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
},
(_, true) =>
if self.truncate && !self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
},
}
Ok(match (self.create, self.truncate, self.create_new) {
(false, false, false) => 0,
(true, false, false) => libc::O_CREAT,
(false, true, false) => libc::O_TRUNC,
(true, true, false) => libc::O_CREAT | libc::O_TRUNC,
(_, _, true) => libc::O_CREAT | libc::O_EXCL,
})
}
}
impl File {
@@ -280,12 +303,10 @@ impl File {
}
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
let flags = opts.flags | match (opts.read, opts.write) {
(true, true) => libc::O_RDWR,
(false, true) => libc::O_WRONLY,
(true, false) |
(false, false) => libc::O_RDONLY,
};
let flags = libc::O_CLOEXEC |
try!(opts.get_access_mode()) |
try!(opts.get_creation_mode()) |
(opts.custom_flags as c_int & !libc::O_ACCMODE);
let fd = try!(cvt_r(|| unsafe {
libc::open(path.as_ptr(), flags, opts.mode as c_int)
}));