Skip to content

Commit

Permalink
A couple of fixes.
Browse files Browse the repository at this point in the history
1. Expose the dev/inode numbers on Unix. This is useful for doing a
   quick comparison that can lead to a false positive but not a false
   negative.
2. If the underlying file handle is a "std" (stdin/stdout/stderr) handle,
   then be careful not to drop it since it will close the stream.
  • Loading branch information
BurntSushi committed Jan 9, 2017
1 parent 0b3e449 commit bbf6959
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 25 deletions.
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ impl Handle {
pub fn as_file_mut(&mut self) -> &mut File {
self.0.as_file_mut()
}

/// Return the underlying device number of this handle.
#[cfg(unix)]
pub fn dev(&self) -> u64 {
self.0.dev()
}

/// Return the underlying inode number of this handle.
#[cfg(unix)]
pub fn ino(&self) -> u64 {
self.0.ino()
}
}

/// Returns true if the two file paths may correspond to the same file.
Expand Down
47 changes: 37 additions & 10 deletions src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@ use libc;

#[derive(Debug)]
pub struct Handle {
file: File,
file: Option<File>,
// If is_std is true, then we don't drop the corresponding File since it
// will close the handle.
is_std: bool,
dev: u64,
ino: u64,
}

impl Drop for Handle {
fn drop(&mut self) {
if self.is_std {
self.file.take().unwrap().into_raw_fd();
}
}
}

impl Eq for Handle {}

impl PartialEq for Handle {
Expand All @@ -23,13 +34,13 @@ impl PartialEq for Handle {

impl AsRawFd for ::Handle {
fn as_raw_fd(&self) -> RawFd {
self.0.file.as_raw_fd()
self.0.file.as_ref().take().unwrap().as_raw_fd()
}
}

impl IntoRawFd for ::Handle {
fn into_raw_fd(self) -> RawFd {
self.0.file.into_raw_fd()
fn into_raw_fd(mut self) -> RawFd {
self.0.file.take().unwrap().into_raw_fd()
}
}

Expand All @@ -41,29 +52,45 @@ impl Handle {
pub fn from_file(file: File) -> io::Result<Handle> {
let md = try!(file.metadata());
Ok(Handle {
file: file,
file: Some(file),
is_std: false,
dev: md.dev(),
ino: md.ino(),
})
}

pub fn from_std(file: File) -> io::Result<Handle> {
Handle::from_file(file).map(|mut h| {
h.is_std = true;
h
})
}

pub fn stdin() -> io::Result<Handle> {
Handle::from_file(unsafe { File::from_raw_fd(libc::STDIN_FILENO) })
Handle::from_std(unsafe { File::from_raw_fd(libc::STDIN_FILENO) })
}

pub fn stdout() -> io::Result<Handle> {
Handle::from_file(unsafe { File::from_raw_fd(libc::STDOUT_FILENO) })
Handle::from_std(unsafe { File::from_raw_fd(libc::STDOUT_FILENO) })
}

pub fn stderr() -> io::Result<Handle> {
Handle::from_file(unsafe { File::from_raw_fd(libc::STDERR_FILENO) })
Handle::from_std(unsafe { File::from_raw_fd(libc::STDERR_FILENO) })
}

pub fn as_file(&self) -> &File {
&self.file
self.file.as_ref().take().unwrap()
}

pub fn as_file_mut(&mut self) -> &mut File {
&mut self.file
self.file.as_mut().take().unwrap()
}

pub fn dev(&self) -> u64 {
self.dev
}

pub fn ino(&self) -> u64 {
self.ino
}
}
42 changes: 27 additions & 15 deletions src/win.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ use winapi::winerror::ERROR_INVALID_HANDLE;

#[derive(Debug)]
pub struct Handle {
file: File,
file: Option<File>,
// If is_std is true, then we don't drop the corresponding File since it
// will close the handle.
is_std: bool,
key: Option<Key>,
}

Expand All @@ -68,8 +71,14 @@ struct Key {
volume: DWORD,
idx_high: DWORD,
idx_low: DWORD,
size_high: DWORD,
size_low: DWORD,
}

impl Drop for Handle {
fn drop(&mut self) {
if self.is_std {
self.file.take().unwrap().into_raw_handle();
}
}
}

impl Eq for Handle {}
Expand All @@ -85,13 +94,13 @@ impl PartialEq for Handle {

impl AsRawHandle for ::Handle {
fn as_raw_handle(&self) -> RawHandle {
self.0.file.as_raw_handle()
self.0.file.as_ref().take().unwrap().as_raw_handle()
}
}

impl IntoRawHandle for ::Handle {
fn into_raw_handle(self) -> RawHandle {
self.0.file.into_raw_handle()
fn into_raw_handle(mut self) -> RawHandle {
self.0.file.take().unwrap().into_raw_handle()
}
}

Expand All @@ -106,12 +115,12 @@ impl Handle {
}

pub fn from_file(file: File) -> io::Result<Handle> {
file_info(&file).map(|info| Handle::from_file_info(file, info))
file_info(&file).map(|info| Handle::from_file_info(file, false, info))
}

fn from_std_handle(file: File) -> io::Result<Handle> {
let err = match file_info(&file) {
Ok(info) => return Ok(Handle::from_file_info(file, info)),
Ok(info) => return Ok(Handle::from_file_info(file, true, info)),
Err(err) => err,
};
if err.raw_os_error() == Some(ERROR_INVALID_HANDLE as i32) {
Expand All @@ -120,20 +129,23 @@ impl Handle {
// We don't really care. The only thing we care about is that
// this handle is never equivalent to any other handle, which is
// accomplished by setting key to None.
return Ok(Handle { file: file, key: None });
return Ok(Handle { file: Some(file), is_std: true, key: None });
}
Err(err)
}

fn from_file_info(file: File, info: BY_HANDLE_FILE_INFORMATION) -> Handle {
fn from_file_info(
file: File,
is_std: bool,
info: BY_HANDLE_FILE_INFORMATION,
) -> Handle {
Handle {
file: file,
file: Some(file),
is_std: is_std,
key: Some(Key {
volume: info.dwVolumeSerialNumber,
idx_high: info.nFileIndexHigh,
idx_low: info.nFileIndexLow,
size_high: info.nFileSizeHigh,
size_low: info.nFileSizeLow,
}),
}
}
Expand All @@ -157,11 +169,11 @@ impl Handle {
}

pub fn as_file(&self) -> &File {
&self.file
self.file.as_ref().take().unwrap()
}

pub fn as_file_mut(&mut self) -> &mut File {
&mut self.file
self.file.as_mut().take().unwrap()
}
}

Expand Down

0 comments on commit bbf6959

Please sign in to comment.