Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std: Stabilize a number of new fs features #25844

Merged
merged 1 commit into from Jun 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions RELEASES.md
@@ -1,3 +1,21 @@
Version 1.1.0 (July 2015)
========================

* NNNN changes, numerous bugfixes

Libraries
---------

* The [`std::fs` module has been expanded][fs-expand] to expand the set of
functionality exposed:
* `DirEntry` now supports optimizations like `file_type` and `metadata` which
don't incur a syscall on some platforms.
* A `symlink_metadata` function has been added.
* The `fs::Metadata` structure now lowers to its OS counterpart, providing
access to all underlying information.

[fs-expand]: https://github.com/rust-lang/rust/pull/25844

Version 1.0.0 (May 2015)
========================

Expand Down
1 change: 0 additions & 1 deletion src/librustc_trans/lib.rs
Expand Up @@ -38,7 +38,6 @@
#![feature(staged_api)]
#![feature(unicode)]
#![feature(path_ext)]
#![feature(fs)]
#![feature(path_relative_from)]
#![feature(std_misc)]

Expand Down
88 changes: 31 additions & 57 deletions src/libstd/fs.rs
Expand Up @@ -148,7 +148,7 @@ pub struct OpenOptions(fs_imp::OpenOptions);
pub struct Permissions(fs_imp::FilePermissions);

/// An structure representing a type of file with accessors for each file type.
#[unstable(feature = "file_type", reason = "recently added API")]
#[stable(feature = "file_type", since = "1.1.0")]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct FileType(fs_imp::FileType);

Expand Down Expand Up @@ -208,14 +208,6 @@ impl File {
OpenOptions::new().write(true).create(true).truncate(true).open(path)
}

/// Returns `None`.
#[unstable(feature = "file_path",
reason = "this abstraction was imposed by this library and was removed")]
#[deprecated(since = "1.0.0", reason = "abstraction was removed")]
pub fn path(&self) -> Option<&Path> {
None
}

/// Attempts to sync all OS-internal metadata to disk.
///
/// This function will attempt to ensure that all in-core data reaches the
Expand Down Expand Up @@ -501,7 +493,7 @@ impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions {

impl Metadata {
/// Returns the file type for this metadata.
#[unstable(feature = "file_type", reason = "recently added API")]
#[stable(feature = "file_type", since = "1.1.0")]
pub fn file_type(&self) -> FileType {
FileType(self.0.file_type())
}
Expand Down Expand Up @@ -575,38 +567,6 @@ impl Metadata {
pub fn permissions(&self) -> Permissions {
Permissions(self.0.perm())
}

/// Returns the most recent access time for a file.
///
/// The return value is in milliseconds since the epoch.
#[unstable(feature = "fs_time",
reason = "the return type of u64 is not quite appropriate for \
this method and may change if the standard library \
gains a type to represent a moment in time")]
#[deprecated(since = "1.1.0",
reason = "use os::platform::fs::MetadataExt extension traits")]
pub fn accessed(&self) -> u64 {
self.adjust_time(self.0.accessed())
}

/// Returns the most recent modification time for a file.
///
/// The return value is in milliseconds since the epoch.
#[unstable(feature = "fs_time",
reason = "the return type of u64 is not quite appropriate for \
this method and may change if the standard library \
gains a type to represent a moment in time")]
#[deprecated(since = "1.1.0",
reason = "use os::platform::fs::MetadataExt extension traits")]
pub fn modified(&self) -> u64 {
self.adjust_time(self.0.modified())
}

fn adjust_time(&self, val: u64) -> u64 {
// FILETIME (what `val` represents) is in 100ns intervals and there are
// 10000 intervals in a millisecond.
if cfg!(windows) {val / 10000} else {val}
}
}

impl AsInner<fs_imp::FileAttr> for Metadata {
Expand Down Expand Up @@ -663,15 +623,17 @@ impl Permissions {
}
}

#[unstable(feature = "file_type", reason = "recently added API")]
impl FileType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably missed the discussion but why is this not an (extensible) enum? This seems like a really good candidate. Yes, some of the variants won't make sense on certain platforms but I don't think that's really an issue (we'll just have extra variants) and being able to match on filetype would be really nice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variants:

enum FileType {
    // Common
    Directory, File, Symlink,
    // Windows Only
    ReparsePoint,
    // Unix Only
    CharDev, BlockDev, NamedPipe, Socket,
    #[doc(hidden)]
    __non_exhaustive
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're currently not necessarily considering a hidden/unstable variant to be an "unstable enum", it was mostly just how ErrorKind worked out. The set of file types also varies quite a bit across platforms, and the extra types made somewhat more sense to provide through extension traits instead of via this enum.

Finally, using an opaque structure allows one day accessing the raw bits used to determine the file type which may contain some unknown information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually makes it easier to write cross-platform code because the coder doesn't need to care about the platform: they just handle all cases on all platforms (when possible).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's somewhat of a fine line between being able to write cross-platform code while still understand what is indeed cross platform. If we were to have an exhaustive enum which is the union of all file types on all platforms, it's suddenly not clear what file types come up on which platforms. Which is to say that it's not clear to me that having a union is indeed more cross platform as you're probably still going to have platform-specific code handling various file types on various platforms.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're currently not necessarily considering a hidden/unstable variant to be an "unstable enum", it was mostly just how ErrorKind worked out.

Given that we're already doing this, I don't see why we shouldn't do it here.

The set of file types also varies quite a bit across platforms, and the extra types made somewhat more sense to provide through extension traits instead of via this enum.

The problem with extension traits is that they force the user to care about the platform. With an enum, I could write:

fn format_filetype(s: &mut String, entry: &DirEntry) {
   match entry.file_type() {
        File => s.push('f'),
        NamedPipe => s.push('p'),
        ReparsePoint => s.push('r'),
        Other => s.push('?'),
        // ...
   }
}

With extension traits, I need one function per platform (each one importing a different extension trait). Extension traits are useful when some set of functionality is nonsensical on a platform but in this case, the extra file types make sense, they just aren't used.

Finally, using an opaque structure allows one day accessing the raw bits used to determine the file type which may contain some unknown information.

This unknown information is stored in the Metadata structure. Are you worried about unknown file types or something? Pretty much by definition, a file should only have one type so an enum should always be able to describe it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm not re-loading before posting... For now, I feel that the enum is small enough that this isn't really an issue however, I can see this becoming an issue in the future as more platforms are added and this enum grows. So I see your point.

/// Test whether this file type represents a directory.
#[stable(feature = "file_type", since = "1.1.0")]
pub fn is_dir(&self) -> bool { self.0.is_dir() }

/// Test whether this file type represents a regular file.
#[stable(feature = "file_type", since = "1.1.0")]
pub fn is_file(&self) -> bool { self.0.is_file() }

/// Test whether this file type represents a symbolic link.
#[stable(feature = "file_type", since = "1.1.0")]
pub fn is_symlink(&self) -> bool { self.0.is_symlink() }
}

Expand Down Expand Up @@ -736,7 +698,7 @@ impl DirEntry {
/// On Windows this function is cheap to call (no extra system calls
/// needed), but on Unix platforms this function is the equivalent of
/// calling `symlink_metadata` on the path.
#[unstable(feature = "dir_entry_ext", reason = "recently added API")]
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
pub fn metadata(&self) -> io::Result<Metadata> {
self.0.metadata().map(Metadata)
}
Expand All @@ -751,14 +713,14 @@ impl DirEntry {
/// On Windows and most Unix platforms this function is free (no extra
/// system calls needed), but some Unix platforms may require the equivalent
/// call to `symlink_metadata` to learn about the target file type.
#[unstable(feature = "dir_entry_ext", reason = "recently added API")]
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
pub fn file_type(&self) -> io::Result<FileType> {
self.0.file_type().map(FileType)
}

/// Returns the bare file name of this directory entry without any other
/// leading path component.
#[unstable(feature = "dir_entry_ext", reason = "recently added API")]
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
pub fn file_name(&self) -> OsString {
self.0.file_name()
}
Expand Down Expand Up @@ -828,7 +790,6 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// # Examples
///
/// ```rust
/// #![feature(symlink_metadata)]
/// # fn foo() -> std::io::Result<()> {
/// use std::fs;
///
Expand All @@ -837,7 +798,7 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "symlink_metadata", reason = "recently added API")]
#[stable(feature = "symlink_metadata", since = "1.1.0")]
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
fs_imp::lstat(path.as_ref()).map(Metadata)
}
Expand Down Expand Up @@ -1270,7 +1231,6 @@ pub fn set_file_times<P: AsRef<Path>>(path: P, accessed: u64,
/// # Examples
///
/// ```
/// # #![feature(fs)]
/// # fn foo() -> std::io::Result<()> {
/// use std::fs;
///
Expand All @@ -1286,14 +1246,13 @@ pub fn set_file_times<P: AsRef<Path>>(path: P, accessed: u64,
/// This function will return an error if the provided `path` doesn't exist, if
/// the process lacks permissions to change the attributes of the file, or if
/// some other I/O error is encountered.
#[unstable(feature = "fs",
reason = "a more granual ability to set specific permissions may \
be exposed on the Permissions structure itself and this \
method may not always exist")]
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
#[stable(feature = "set_permissions", since = "1.1.0")]
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions)
-> io::Result<()> {
fs_imp::set_perm(path.as_ref(), perm.0)
}

#[unstable(feature = "dir_builder", reason = "recently added API")]
impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all
/// platforms and also non-recursive.
Expand Down Expand Up @@ -2066,9 +2025,24 @@ mod tests {
// These numbers have to be bigger than the time in the day to account
// for timezones Windows in particular will fail in certain timezones
// with small enough values
check!(fs::set_file_times(&path, 100000, 200000));
assert_eq!(check!(path.metadata()).accessed(), 100000);
assert_eq!(check!(path.metadata()).modified(), 200000);
check!(fs::set_file_times(&path, 100_000, 200_000));

check(&check!(path.metadata()));

#[cfg(unix)]
fn check(metadata: &fs::Metadata) {
use os::unix::prelude::*;
assert_eq!(metadata.atime(), 100);
assert_eq!(metadata.atime_nsec(), 0);
assert_eq!(metadata.mtime(), 200);
assert_eq!(metadata.mtime_nsec(), 0);
}
#[cfg(windows)]
fn check(metadata: &fs::Metadata) {
use os::windows::prelude::*;
assert_eq!(metadata.last_access_time(), 100_000 * 10_000);
assert_eq!(metadata.last_write_time(), 200_000 * 10_000);
}
}

#[test]
Expand Down
3 changes: 2 additions & 1 deletion src/libstd/os/android/mod.rs
Expand Up @@ -10,10 +10,11 @@

//! Android-specific definitions

#![unstable(feature = "raw_ext", reason = "recently added API")]
#![stable(feature = "raw_ext", since = "1.1.0")]

pub mod raw;

pub mod fs {
#![stable(feature = "raw_ext", since = "1.1.0")]
pub use sys::fs::MetadataExt;
}
59 changes: 58 additions & 1 deletion src/libstd/os/android/raw.rs
Expand Up @@ -10,6 +10,8 @@

//! Android-specific raw type definitions

#![stable(feature = "raw_ext", since = "1.1.0")]

#[doc(inline)]
pub use self::arch::{dev_t, mode_t, blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t};

Expand All @@ -18,36 +20,64 @@ mod arch {
use os::raw::{c_uint, c_uchar, c_ulonglong, c_longlong, c_ulong};
use os::unix::raw::{uid_t, gid_t};

#[stable(feature = "raw_ext", since = "1.1.0")]
pub type dev_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type mode_t = u16;

#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blkcnt_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blksize_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type ino_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type nlink_t = u16;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type off_t = i32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type time_t = i32;

#[repr(C)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_dev: c_ulonglong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __pad0: [c_uchar; 4],
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __st_ino: ino_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mode: c_uint,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_nlink: c_uint,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_uid: uid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_gid: gid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_rdev: c_ulonglong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __pad3: [c_uchar; 4],
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_size: c_longlong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blksize: blksize_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blocks: c_ulonglong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_atime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_atime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mtime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mtime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ctime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ctime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ino: c_ulonglong,
}

Expand All @@ -59,37 +89,64 @@ mod arch {
use os::raw::{c_uchar, c_ulong};
use os::unix::raw::{uid_t, gid_t};

#[stable(feature = "raw_ext", since = "1.1.0")]
pub type dev_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type mode_t = u32;

#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blkcnt_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blksize_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type ino_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type nlink_t = u32;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type off_t = i64;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type time_t = i64;

#[repr(C)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_dev: dev_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __pad0: [c_uchar; 4],
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __st_ino: ino_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mode: mode_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_nlink: nlink_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_uid: uid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_gid: gid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_rdev: dev_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub __pad3: [c_uchar; 4],
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_size: off_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blksize: blksize_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blocks: blkcnt_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_atime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_atime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mtime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mtime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ctime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ctime_nsec: c_ulong,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ino: ino_t,
}

}
3 changes: 2 additions & 1 deletion src/libstd/os/bitrig/mod.rs
Expand Up @@ -10,10 +10,11 @@

//! Bitrig-specific definitions

#![unstable(feature = "raw_ext", reason = "recently added API")]
#![stable(feature = "raw_ext", since = "1.1.0")]

pub mod raw;

pub mod fs {
#![stable(feature = "raw_ext", since = "1.1.0")]
pub use sys::fs::MetadataExt;
}