File tree 5 files changed +86
-4
lines changed
5 files changed +86
-4
lines changed Original file line number Diff line number Diff line change @@ -48,3 +48,7 @@ test = true
48
48
name = " test-mount"
49
49
path = " test/test_mount.rs"
50
50
harness = false
51
+
52
+ [[test ]]
53
+ name = " test-ptymaster-drop"
54
+ path = " test/test_ptymaster_drop.rs"
Original file line number Diff line number Diff line change @@ -45,10 +45,17 @@ impl IntoRawFd for PtyMaster {
45
45
46
46
impl Drop for PtyMaster {
47
47
fn drop ( & mut self ) {
48
- // Errors when closing are ignored because we don't actually know if the file descriptor
49
- // was closed. If we retried, it's possible that descriptor was reallocated in the mean
50
- // time and the wrong file descriptor could be closed.
51
- let _ = :: unistd:: close ( self . 0 ) ;
48
+ // On drop, we ignore errors like EINTR and EIO because there's no clear
49
+ // way to handle them, we can't return anything, and (on FreeBSD at
50
+ // least) the file descriptor is deallocated in these cases. However,
51
+ // we must panic on EBADF, because it is always an error to close an
52
+ // invalid file descriptor. That frequently indicates a double-close
53
+ // condition, which can cause confusing errors for future I/O
54
+ // operations.
55
+ let e = :: unistd:: close ( self . 0 ) ;
56
+ if e == Err ( Error :: Sys ( Errno :: EBADF ) ) {
57
+ panic ! ( "Closing an invalid file descriptor!" ) ;
58
+ } ;
52
59
}
53
60
}
54
61
Original file line number Diff line number Diff line change @@ -670,6 +670,40 @@ pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> {
670
670
} )
671
671
}
672
672
673
+ /// Close a raw file descriptor
674
+ ///
675
+ /// Be aware that many Rust types implicitly close-on-drop, including
676
+ /// `std::fs::File`. Explicitly closing them with this method too can result in
677
+ /// a double-close condition, which can cause confusing `EBADF` errors in
678
+ /// seemingly unrelated code. Caveat programmer.
679
+ ///
680
+ /// # Examples
681
+ ///
682
+ /// ```no_run
683
+ /// extern crate tempfile;
684
+ /// extern crate nix;
685
+ ///
686
+ /// use std::os::unix::io::AsRawFd;
687
+ /// use nix::unistd::close;
688
+ ///
689
+ /// fn main() {
690
+ /// let mut f = tempfile::tempfile().unwrap();
691
+ /// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
692
+ /// }
693
+ /// ```
694
+ ///
695
+ /// ```rust
696
+ /// extern crate tempfile;
697
+ /// extern crate nix;
698
+ ///
699
+ /// use std::os::unix::io::IntoRawFd;
700
+ /// use nix::unistd::close;
701
+ ///
702
+ /// fn main() {
703
+ /// let mut f = tempfile::tempfile().unwrap();
704
+ /// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
705
+ /// }
706
+ /// ```
673
707
pub fn close ( fd : RawFd ) -> Result < ( ) > {
674
708
let res = unsafe { libc:: close ( fd) } ;
675
709
Errno :: result ( res) . map ( drop)
Original file line number Diff line number Diff line change
1
+ use std:: io:: Write ;
1
2
use std:: path:: Path ;
2
3
use std:: os:: unix:: prelude:: * ;
4
+ use tempfile:: tempfile;
3
5
4
6
use nix:: fcntl:: { O_RDWR , open} ;
5
7
use nix:: pty:: * ;
6
8
use nix:: sys:: stat;
7
9
use nix:: sys:: termios:: * ;
8
10
use nix:: unistd:: { write, close} ;
9
11
12
+ /// Regression test for Issue #659
13
+ /// This is the correct way to explicitly close a PtyMaster
14
+ #[ test]
15
+ fn test_explicit_close ( ) {
16
+ let mut f = {
17
+ let m = posix_openpt ( O_RDWR ) . unwrap ( ) ;
18
+ close ( m. into_raw_fd ( ) ) . unwrap ( ) ;
19
+ tempfile ( ) . unwrap ( )
20
+ } ;
21
+ // This should work. But if there's been a double close, then it will
22
+ // return EBADF
23
+ f. write ( b"whatever" ) . unwrap ( ) ;
24
+ }
25
+
10
26
/// Test equivalence of `ptsname` and `ptsname_r`
11
27
#[ test]
12
28
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
Original file line number Diff line number Diff line change
1
+ extern crate nix;
2
+
3
+ use nix:: fcntl:: O_RDWR ;
4
+ use nix:: pty:: * ;
5
+ use nix:: unistd:: close;
6
+ use std:: os:: unix:: io:: AsRawFd ;
7
+
8
+ /// Regression test for Issue #659
9
+ /// PtyMaster should panic rather than double close the file descriptor
10
+ /// This must run in its own test process because it deliberately creates a race
11
+ /// condition.
12
+ #[ test]
13
+ #[ should_panic( expected = "Closing an invalid file descriptor!" ) ]
14
+ // In Travis on i686-unknown-linux-musl, this test gets SIGABRT. I don't know
15
+ // why. It doesn't happen on any other target, and it doesn't happen on my PC.
16
+ #[ cfg_attr( all( target_env = "musl" , target_arch = "x86" ) , ignore) ]
17
+ fn test_double_close ( ) {
18
+ let m = posix_openpt ( O_RDWR ) . unwrap ( ) ;
19
+ close ( m. as_raw_fd ( ) ) . unwrap ( ) ;
20
+ drop ( m) ; // should panic here
21
+ }
You can’t perform that action at this time.
0 commit comments