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

Implement proper file closing #22849

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/libstd/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ impl File {
OpenOptions::new().write(true).create(true).truncate(true).open(path)
}

/// Close the file.
///
/// Use this function if you want to avoid panicking when the destructor of
/// the file is run.
pub fn close(self) -> io::Result<()> {
self.inner.close()
}

/// Returns the original path that was used to open this file.
pub fn path(&self) -> Option<&Path> {
Some(&self.path)
Expand Down Expand Up @@ -262,8 +270,8 @@ impl OpenOptions {
///
/// * Opening a file that does not exist with read access.
/// * Attempting to open a file with access that the user lacks
/// permissions for
/// * Filesystem-level errors (full disk, etc)
/// permissions for.
/// * Filesystem-level errors (full disk, etc).
pub fn open<P: AsPath + ?Sized>(&self, path: &P) -> io::Result<File> {
let path = path.as_path();
let inner = try!(fs_imp::File::open(path, &self.0));
Expand All @@ -273,7 +281,7 @@ impl OpenOptions {
// tradition before the introduction of opendir(3). We explicitly
// reject it because there are few use cases.
if cfg!(not(any(target_os = "linux", target_os = "android"))) &&
try!(inner.file_attr()).is_dir() {
try!(inner.file_attr()).is_dir() {
Err(Error::new(ErrorKind::InvalidInput, "is a directory", None))
} else {
Ok(File { path: path.to_path_buf(), inner: inner })
Expand Down
32 changes: 23 additions & 9 deletions src/libstd/sys/unix/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ impl FileDesc {
FileDesc { fd: fd }
}

pub fn close(self) -> io::Result<()> {
let fd = self.fd;
// Don't run the destructor after running the `close` function.
unsafe { mem::forget(self) };

// Closing stdio file handles makes no sense, so never do it. Note that
// this is relied upon in the destructor.
if fd <= libc::STDERR_FILENO {
return Ok(());
}

// Also, note that closing the file descriptor is never retried on
// error. 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).
try!(cvt(unsafe { libc::close(fd) }));
Ok(())
}

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

/// Extract the actual filedescriptor without closing it.
Expand Down Expand Up @@ -60,14 +80,8 @@ impl AsInner<c_int> for FileDesc {

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 {
let _ = unsafe { libc::close(self.fd) };
}
// Close the file descriptor.
let fd = mem::replace(self, FileDesc::new(libc::STDIN_FILENO));
fd.close().unwrap();
}
}
4 changes: 4 additions & 0 deletions src/libstd/sys/unix/fs2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ impl File {
Ok(File(FileDesc::new(fd)))
}

pub fn close(self) -> io::Result<()> {
self.0.close()
}

pub fn file_attr(&self) -> io::Result<FileAttr> {
let mut stat: libc::stat = unsafe { mem::zeroed() };
try!(cvt(unsafe { libc::fstat(self.0.raw(), &mut stat) }));
Expand Down
19 changes: 17 additions & 2 deletions src/libstd/sys/windows/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

use prelude::v1::*;

use libc::{self, HANDLE};
use io;
use io::ErrorKind;
use libc::{self, HANDLE};
use mem;
use ptr;
use sys::cvt;

Expand All @@ -26,6 +27,19 @@ impl Handle {
Handle(handle)
}

pub fn close(self) -> io::Result<()> {
let handle = self.0;
unsafe { mem::forget(self) };

// Don't try to close the invalid handle, this is relied upon in the destructor.
if handle == libc::INVALID_HANLE_VALUE {
return Ok(());
}

try!(cvt(unsafe { libc::CloseHandle(self.0); }));
Ok(())
}

pub fn raw(&self) -> HANDLE { self.0 }

pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
Expand All @@ -39,7 +53,8 @@ impl Handle {

impl Drop for Handle {
fn drop(&mut self) {
unsafe { let _ = libc::CloseHandle(self.0); }
let handle = mem::replace(self, Handle::new(libc::INVALID_HANDLE_VALUE));
handle.close().unwrap();
}
}

Expand Down