Skip to content

Commit

Permalink
std: Add a new fs module
Browse files Browse the repository at this point in the history
This commit is an implementation of [RFC 739][rfc] which adds a new `std::fs`
module to the standard library. This module provides much of the same
functionality as `std::old_io::fs` but it has many tweaked APIs as well as uses
the new `std::path` module.

[rfc]: rust-lang/rfcs#739
  • Loading branch information
alexcrichton committed Feb 10, 2015
1 parent 0b56e9b commit 6bfbad9
Show file tree
Hide file tree
Showing 15 changed files with 2,564 additions and 13 deletions.
1,501 changes: 1,501 additions & 0 deletions src/libstd/fs.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/libstd/io/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
//! contained in this module.

pub use super::{Read, ReadExt, Write, WriteExt, BufRead, BufReadExt};
pub use fs::PathExt;

// FIXME: pub use as `Seek` when the name isn't in the actual prelude any more
pub use super::Seek as NewSeek;
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub mod ffi;
pub mod fmt;
pub mod old_io;
pub mod io;
pub mod fs;
pub mod os;
pub mod env;
pub mod path;
Expand Down
6 changes: 6 additions & 0 deletions src/libstd/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,12 @@ impl cmp::Ord for PathBuf {
}
}

impl AsOsStr for PathBuf {
fn as_os_str(&self) -> &OsStr {
&self.inner[]
}
}

/// A slice of a path (akin to `str`).
///
/// This type supports a number of operations for inspecting a path, including
Expand Down
10 changes: 10 additions & 0 deletions src/libstd/sys/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,30 @@ pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where
}

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInner<Inner: ?Sized> {
fn as_inner(&self) -> &Inner;
}

/// A trait for viewing representations from std types
#[doc(hidden)]
pub trait AsInnerMut<Inner: ?Sized> {
fn as_inner_mut(&mut self) -> &mut Inner;
}

/// A trait for extracting representations from std types
#[doc(hidden)]
pub trait IntoInner<Inner> {
fn into_inner(self) -> Inner;
}

/// A trait for creating std types from internal representations
#[doc(hidden)]
pub trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self;
}

#[doc(hidden)]
pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
fn program(&self) -> &CString;
fn args(&self) -> &[CString];
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/sys/unix/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ extern {
buf: *mut libc::c_char,
buflen: libc::size_t,
result: *mut *mut passwd) -> libc::c_int;

pub fn utimes(filename: *const libc::c_char,
times: *const libc::timeval) -> libc::c_int;
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
Expand Down
48 changes: 44 additions & 4 deletions src/libstd/sys/unix/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@

#![unstable(feature = "std_misc")]

use vec::Vec;
use sys::os_str::Buf;
use sys_common::{AsInner, IntoInner, FromInner};
use ffi::{OsStr, OsString};
use fs::{Permissions, OpenOptions};
use fs;
use libc;
use mem;
use sys::os_str::Buf;
use sys_common::{AsInner, AsInnerMut, IntoInner, FromInner};
use vec::Vec;

use old_io;

Expand All @@ -54,6 +57,12 @@ impl AsRawFd for old_io::fs::File {
}
}

impl AsRawFd for fs::File {
fn as_raw_fd(&self) -> Fd {
self.as_inner().fd().raw()
}
}

impl AsRawFd for old_io::pipe::PipeStream {
fn as_raw_fd(&self) -> Fd {
self.as_inner().fd()
Expand Down Expand Up @@ -123,18 +132,49 @@ impl OsStringExt for OsString {

// Unix-specific extensions to `OsStr`.
pub trait OsStrExt {
fn from_byte_slice(slice: &[u8]) -> &OsStr;
fn as_byte_slice(&self) -> &[u8];
}

impl OsStrExt for OsStr {
fn from_byte_slice(slice: &[u8]) -> &OsStr {
unsafe { mem::transmute(slice) }
}
fn as_byte_slice(&self) -> &[u8] {
&self.as_inner().inner
}
}

// Unix-specific extensions to `Permissions`
pub trait PermissionsExt {
fn set_mode(&mut self, mode: i32);
}

impl PermissionsExt for Permissions {
fn set_mode(&mut self, mode: i32) {
*self = FromInner::from_inner(FromInner::from_inner(mode));
}
}

// Unix-specific extensions to `OpenOptions`
pub trait OpenOptionsExt {
/// Set the mode bits that a new file will be created with.
///
/// 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.
fn mode(&mut self, mode: i32) -> &mut Self;
}

impl OpenOptionsExt for OpenOptions {
fn mode(&mut self, mode: i32) -> &mut OpenOptions {
self.as_inner_mut().mode(mode); self
}
}

/// A prelude for conveniently writing platform-specific code.
///
/// Includes all extension traits, and some important type definitions.
pub mod prelude {
pub use super::{Fd, AsRawFd};
#[doc(no_inline)]
pub use super::{Fd, AsRawFd, OsStrExt, OsStringExt, PermissionsExt};
}
70 changes: 70 additions & 0 deletions src/libstd/sys/unix/fd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// 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.

use core::prelude::*;
use io::prelude::*;

use io;
use libc::{self, c_int, size_t, c_void};
use mem;
use sys::cvt;

pub type fd_t = c_int;

pub struct FileDesc {
fd: c_int,
}

impl FileDesc {
pub fn new(fd: c_int) -> FileDesc {
FileDesc { fd: fd }
}

pub fn raw(&self) -> c_int { self.fd }

/// Extract the actual filedescriptor without closing it.
pub fn into_raw(self) -> c_int {
let fd = self.fd;
unsafe { mem::forget(self) };
fd
}

pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let ret = try!(cvt(unsafe {
libc::read(self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as size_t)
}));
Ok(ret as usize)
}

pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let ret = try!(cvt(unsafe {
libc::write(self.fd,
buf.as_ptr() as *const c_void,
buf.len() as size_t)
}));
Ok(ret as usize)
}
}

impl Drop for FileDesc {
fn drop(&mut self) {
// closing stdio file handles makes no sense, so never do it. Also, note
// that errors are ignored when closing a file descriptor. The reason
// for this is that if an error occurs we don't actually know if the
// file descriptor was closed or not, and if we retried (for something
// like EINTR), we might close another valid file descriptor (opened
// after we closed ours.
if self.fd > libc::STDERR_FILENO {

This comment has been minimized.

Copy link
@l0kod

l0kod Feb 12, 2015

The file descriptor closing should not depend on the file descriptor value.

Replacing the fd: c_int with an enum FdKind { AutoClose(c_int), KeepOpen(c_int) } is more consistent and should remove some weird bugs.

This comment has been minimized.

Copy link
@alexcrichton

alexcrichton Feb 12, 2015

Author Owner

What weird bugs are you referring to? This was added because it caused weird bugs.

This comment has been minimized.

Copy link
@l0kod

l0kod Feb 12, 2015

We don't really control file descriptor numbers. For example, when you start a process without open file descriptor (no stdin/stdout/stderr), then the next open file (descriptor) will match this no-close behavior. A process without std{in,out,err} should not behave differently than another "standard" process. With this code, the first three open files could leak or at least not be properly closed.
Anyway, something like a fn new(fd: FdKind) seems cleaner and more deterministic, isn't it? Or at least an extra argument like the previous FileDesc::new(fd: c_int, close_on_drop: bool).

This comment has been minimized.

Copy link
@alexcrichton

alexcrichton Feb 14, 2015

Author Owner

I would like to keep FileDesc a literal wrapper around c_int for now as I feel it will benefit future extensibility as well. I would also be fine removing this check and tracking down the spots in our own codebase where we may accidentally close stdout/stdin/stderr and remove those.

Put another way, I would be ok to have FileDesc always close on drop and then we could use an Option + into_raw wherever we need to internally (to cancel from a destructor).

This comment has been minimized.

Copy link
@l0kod

l0kod Feb 15, 2015

Put another way, I would be ok to have FileDesc always close on drop and then we could use an Option + into_raw wherever we need to internally (to cancel from a destructor).

👍

Not sure to see the difference with into_inner but that's definitely the way to follow.

let _ = unsafe { libc::close(self.fd) };
}
}
}
Loading

0 comments on commit 6bfbad9

Please sign in to comment.