From 5b449f4e1e43bc59109da5a37edf5ec911d3df8e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 13 Apr 2020 02:13:58 +0200 Subject: IO, error handling --- safe_libc/src/boxed.rs | 2 +- safe_libc/src/errno.rs | 29 ++++++++++++++ safe_libc/src/lib.rs | 1 + safe_libc/src/stdio.rs | 102 +++++++++++++++++++++++++++++++++++++++--------- safe_libc/src/string.rs | 23 +++++++---- src/main.rs | 7 ++-- 6 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 safe_libc/src/errno.rs diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs index fa3e45d..4a38b70 100644 --- a/safe_libc/src/boxed.rs +++ b/safe_libc/src/boxed.rs @@ -11,7 +11,7 @@ fn alloc(len: usize) -> *mut T { let align = mem::align_of::(); util::must_succeed( unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) + libc::memalign(align, size) } ).cast() } diff --git a/safe_libc/src/errno.rs b/safe_libc/src/errno.rs new file mode 100644 index 0000000..be944d1 --- /dev/null +++ b/safe_libc/src/errno.rs @@ -0,0 +1,29 @@ +use crate::string; + +use core::fmt; + +#[derive(Clone, Copy, Debug)] +#[repr(transparent)] +pub struct Errno(pub libc::c_int); + +#[inline] +pub fn errno() -> Errno { + unsafe { Errno(*libc::__errno_location()) } +} + +impl fmt::Display for Errno { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut buf = [0u8; 1024]; + let cstr = unsafe { + if libc::strerror_r(self.0, buf.as_mut_ptr().cast(), buf.len()) != 0 { + return Err(fmt::Error); + } + string::CStr::from_bytes_with_nul_unchecked(&buf) + }; + match cstr.to_str() { + Err(_) => Err(fmt::Error), + Ok(s) => f.write_str(s), + } + } +} diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs index 0ab0e89..aec5acc 100644 --- a/safe_libc/src/lib.rs +++ b/safe_libc/src/lib.rs @@ -5,6 +5,7 @@ pub use libc::*; mod util; pub mod boxed; +pub mod errno; pub mod stdio; pub mod string; diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs index 271f023..2d19725 100644 --- a/safe_libc/src/stdio.rs +++ b/safe_libc/src/stdio.rs @@ -1,47 +1,111 @@ -use crate as libc; -use crate::string; +use crate::{self as libc, errno, string}; use core::fmt; +use core::ops::{Deref, DerefMut}; -pub struct OStream { - file: *mut libc::FILE +pub struct Error { + pub errno: errno::Errno, } +pub type Result = core::result::Result; + +#[inline] +fn check_io(ok: bool) -> Result<()> { + if ok { + Ok(()) + } else { + Err(Error { + errno: errno::errno(), + }) + } +} + +pub struct BasicOStream(*mut libc::FILE); + +unsafe impl Sync for BasicOStream {} +unsafe impl Send for BasicOStream {} + +pub type Stdout = BasicOStream; +pub type Stderr = BasicOStream; + #[inline] -pub unsafe fn stdout() -> OStream { - OStream { file: libc::stdout } +pub fn stdout() -> Stdout { + BasicOStream(unsafe { libc::stdout }) } #[inline] -pub unsafe fn stderr() -> OStream { - OStream { file: libc::stderr } +pub fn stderr() -> Stderr { + BasicOStream(unsafe { libc::stderr }) } -impl OStream { +impl BasicOStream { #[inline] - pub fn write(&mut self, b: &[u8]) { - unsafe { + pub fn flush(&mut self) -> Result<()> { + check_io(unsafe { + libc::fflush(self.0) + } == 0) + } + + #[inline] + pub fn write(&mut self, b: &[u8]) -> Result<()> { + check_io(unsafe { libc::fwrite( b.as_ptr().cast(), 1, b.len(), - self.file, - ); + self.0, + ) + } == b.len()) + } + + #[inline] + pub fn puts(&mut self, s: &string::CStr) -> Result<()> { + check_io(unsafe { + libc::fputs(s.as_ptr(), self.0) + } != libc::EOF) + } +} + +impl fmt::Write for BasicOStream { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + if self.write(s.as_bytes()).is_err() { + return Err(fmt::Error); } + Ok(()) + } +} + +pub struct OStream(BasicOStream); + +impl OStream { + #[inline] + pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream { + OStream(BasicOStream(file)) } +} +impl Drop for OStream { #[inline] - pub fn puts(&mut self, s: &string::CStr) { + fn drop(&mut self) { unsafe { - libc::fputs(s.as_ptr(), self.file); + libc::fclose((self.0).0); } } } -impl fmt::Write for OStream { +impl Deref for OStream { + type Target = BasicOStream; + #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes()); - Ok(()) + fn deref(&self) -> &BasicOStream { + &self.0 + } +} + +impl DerefMut for OStream { + #[inline] + fn deref_mut(&mut self) -> &mut BasicOStream { + &mut self.0 } } diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs index 8f973ad..53f8f87 100644 --- a/safe_libc/src/string.rs +++ b/safe_libc/src/string.rs @@ -1,12 +1,11 @@ -use crate as libc; -use crate::util; -use crate::boxed::CBox; +use crate::{self as libc, util, boxed::CBox}; use core::slice; use core::ops::{Deref, DerefMut}; //pub struct FromBytesWithNulError {} +#[repr(transparent)] pub struct CStr { inner: libc::c_char } impl CStr { @@ -43,7 +42,7 @@ impl CStr { #[inline] pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) as usize } + unsafe { libc::strlen(self.as_ptr()) } } #[inline] @@ -57,7 +56,7 @@ impl CStr { } #[inline] - pub fn as_bytes(&self) -> &[u8] { + pub fn to_bytes(&self) -> &[u8] { unsafe { slice::from_raw_parts( self.as_ptr().cast(), self.len(), @@ -65,13 +64,23 @@ impl CStr { } #[inline] - pub fn as_mut_bytes(&mut self) -> &mut [u8] { + pub fn to_mut_bytes(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut( self.as_mut_ptr().cast(), self.len(), ) } } + #[inline] + pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> { + core::str::from_utf8(self.to_bytes()) + } + + #[inline] + pub fn to_mut_str(&mut self) -> Result<&mut str, core::str::Utf8Error> { + core::str::from_utf8_mut(self.to_mut_bytes()) + } + #[inline] pub fn to_owned(self: &CStr) -> CString { CString::from(self) @@ -127,7 +136,7 @@ impl From<&[u8]> for CString { CString::from_raw_unchecked( util::must_succeed(libc::strndup( s.as_ptr().cast(), - s.len() as libc::size_t, + s.len(), )) ) } diff --git a/src/main.rs b/src/main.rs index c79f1da..26882ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,11 @@ use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = unsafe { libc::stdio::stdout() }; + let mut stdout = libc::stdio::stdout(); { let foo = cstr!("Foo!\n"); - stdout.puts(foo); + let _ = stdout.puts(foo); } { @@ -29,11 +29,12 @@ pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) - let b = libc::boxed::CBox::new(42); let _ = writeln!(stdout, "Bar: {}", b); } + 0 } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); + let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info); unsafe { libc::abort() } } -- cgit v1.2.3