Skip to content

Commit fee457d

Browse files
committed
std: Fix some behavior without stdio handles
On all platforms, reading from stdin where the actual stdin isn't present should return 0 bytes as having been read rather than the entire buffer. On Windows, handle the case where we're inheriting stdio handles but one of them isn't present. Currently the behavior is to fail returning an I/O error but instead this commit corrects it to detecting this situation and propagating the non-set handle. Closes #31167
1 parent 6866f13 commit fee457d

File tree

3 files changed

+133
-4
lines changed

3 files changed

+133
-4
lines changed

src/libstd/io/stdio.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl<W: io::Write> io::Write for Maybe<W> {
112112
impl<R: io::Read> io::Read for Maybe<R> {
113113
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
114114
match *self {
115-
Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), buf.len()),
115+
Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), 0),
116116
Maybe::Fake => Ok(0)
117117
}
118118
}

src/libstd/sys/windows/process.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,15 @@ fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec<u16>) {
351351
impl Stdio {
352352
fn to_handle(&self, stdio_id: c::DWORD) -> io::Result<Handle> {
353353
match *self {
354+
// If no stdio handle is available, then inherit means that it
355+
// should still be unavailable so propagate the
356+
// INVALID_HANDLE_VALUE.
354357
Stdio::Inherit => {
355-
stdio::get(stdio_id).and_then(|io| {
356-
io.handle().duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
357-
})
358+
match stdio::get(stdio_id) {
359+
Ok(io) => io.handle().duplicate(0, true,
360+
c::DUPLICATE_SAME_ACCESS),
361+
Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
362+
}
358363
}
359364
Stdio::Raw(handle) => {
360365
RawHandle::new(handle).duplicate(0, true, c::DUPLICATE_SAME_ACCESS)

src/test/run-pass/no-stdio.rs

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(libc)]
12+
13+
extern crate libc;
14+
15+
use std::process::{Command, Stdio};
16+
use std::env;
17+
use std::io::{self, Read, Write};
18+
19+
#[cfg(unix)]
20+
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
21+
let doit = |a| {
22+
let r = libc::dup(a);
23+
assert!(r >= 0);
24+
return r
25+
};
26+
let a = doit(0);
27+
let b = doit(1);
28+
let c = doit(2);
29+
30+
assert!(libc::close(0) >= 0);
31+
assert!(libc::close(1) >= 0);
32+
assert!(libc::close(2) >= 0);
33+
34+
let r = f();
35+
36+
assert!(libc::dup2(a, 0) >= 0);
37+
assert!(libc::dup2(b, 1) >= 0);
38+
assert!(libc::dup2(c, 2) >= 0);
39+
40+
return r
41+
}
42+
43+
#[cfg(windows)]
44+
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
45+
type DWORD = u32;
46+
type HANDLE = *mut u8;
47+
type BOOL = i32;
48+
49+
const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD;
50+
const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
51+
const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
52+
const INVALID_HANDLE_VALUE: HANDLE = !0 as HANDLE;
53+
54+
extern "system" {
55+
fn GetStdHandle(which: DWORD) -> HANDLE;
56+
fn SetStdHandle(which: DWORD, handle: HANDLE) -> BOOL;
57+
}
58+
59+
let doit = |id| {
60+
let handle = GetStdHandle(id);
61+
assert!(handle != INVALID_HANDLE_VALUE);
62+
assert!(SetStdHandle(id, INVALID_HANDLE_VALUE) != 0);
63+
return handle
64+
};
65+
66+
let a = doit(STD_INPUT_HANDLE);
67+
let b = doit(STD_OUTPUT_HANDLE);
68+
let c = doit(STD_ERROR_HANDLE);
69+
70+
let r = f();
71+
72+
let doit = |id, handle| {
73+
assert!(SetStdHandle(id, handle) != 0);
74+
};
75+
doit(STD_INPUT_HANDLE, a);
76+
doit(STD_OUTPUT_HANDLE, b);
77+
doit(STD_ERROR_HANDLE, c);
78+
79+
return r
80+
}
81+
82+
fn main() {
83+
if env::args().len() > 1 {
84+
println!("test");
85+
assert!(io::stdout().write(b"test\n").is_ok());
86+
assert!(io::stderr().write(b"test\n").is_ok());
87+
assert_eq!(io::stdin().read(&mut [0; 10]).unwrap(), 0);
88+
return
89+
}
90+
91+
// First, make sure reads/writes without stdio work if stdio itself is
92+
// missing.
93+
let (a, b, c) = unsafe {
94+
without_stdio(|| {
95+
let a = io::stdout().write(b"test\n");
96+
let b = io::stderr().write(b"test\n");
97+
let c = io::stdin().read(&mut [0; 10]);
98+
99+
(a, b, c)
100+
})
101+
};
102+
103+
assert_eq!(a.unwrap(), 5);
104+
assert_eq!(b.unwrap(), 5);
105+
assert_eq!(c.unwrap(), 0);
106+
107+
// Second, spawn a child and do some work with "null" descriptors to make
108+
// sure it's ok
109+
let me = env::current_exe().unwrap();
110+
let status = Command::new(&me)
111+
.arg("next")
112+
.stdin(Stdio::null())
113+
.stdout(Stdio::null())
114+
.stderr(Stdio::null())
115+
.status().unwrap();
116+
assert!(status.success(), "{:?} isn't a success", status);
117+
118+
// Finally, close everything then spawn a child to make sure everything is
119+
// *still* ok.
120+
let status = unsafe {
121+
without_stdio(|| Command::new(&me).arg("next").status())
122+
}.unwrap();
123+
assert!(status.success(), "{:?} isn't a success", status);
124+
}

0 commit comments

Comments
 (0)