Merge branch 'out2' into 'master'
Expand Out wrapper and use it in utsname. See merge request redox-os/relibc!736
This commit is contained in:
@@ -2,12 +2,14 @@
|
||||
|
||||
use crate::{
|
||||
error::ResultExt,
|
||||
out::Out,
|
||||
platform::{Pal, Sys, types::*},
|
||||
};
|
||||
|
||||
pub const UTSLENGTH: usize = 65;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, OutProject)]
|
||||
pub struct utsname {
|
||||
pub sysname: [c_char; UTSLENGTH],
|
||||
pub nodename: [c_char; UTSLENGTH],
|
||||
@@ -19,5 +21,7 @@ pub struct utsname {
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn uname(uts: *mut utsname) -> c_int {
|
||||
Sys::uname(uts).map(|()| 0).or_minus_one_errno()
|
||||
Sys::uname(Out::nonnull(uts))
|
||||
.map(|()| 0)
|
||||
.or_minus_one_errno()
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ pub extern "C" fn gethostid() -> c_long {
|
||||
pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int {
|
||||
let mut uts = mem::MaybeUninit::<sys_utsname::utsname>::uninit();
|
||||
// TODO
|
||||
let err = Sys::uname(uts.as_mut_ptr())
|
||||
let err = Sys::uname(Out::from_uninit_mut(&mut uts))
|
||||
.map(|()| 0)
|
||||
.or_minus_one_errno();
|
||||
if err < 0 {
|
||||
|
||||
@@ -273,6 +273,8 @@ pub mod error;
|
||||
mod impls;
|
||||
pub mod prelude;
|
||||
|
||||
use crate::out::Out;
|
||||
|
||||
pub use self::{buffered::*, cursor::*, error::*};
|
||||
|
||||
use self::prelude::*;
|
||||
@@ -550,6 +552,12 @@ pub trait Read {
|
||||
/// ```
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
|
||||
fn read_out(&mut self, mut out: Out<[u8]>) -> Result<usize> {
|
||||
// XXX: Technically incorrect but hopefully not UB
|
||||
let slice: &mut [u8] = unsafe { &mut *out.as_mut_ptr() };
|
||||
self.read(slice)
|
||||
}
|
||||
|
||||
/// Determines if this `Read`er can work with buffers of uninitialized
|
||||
/// memory.
|
||||
///
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#![feature(asm_const)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(macro_derive)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
@@ -343,3 +343,70 @@ macro_rules! strto_float_impl {
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Project an `Out<struct X { field: Type }>` to `struct X { field: Out<Type> }`.
|
||||
///
|
||||
/// It is allowed to include only a subset of the struct's fields. The struct must implement
|
||||
/// `OutProject`.
|
||||
#[macro_export]
|
||||
macro_rules! out_project {
|
||||
{
|
||||
let $struct:ty { $($field:ident : $fieldty:ty),*$(,)? } = $src:ident;
|
||||
} => {
|
||||
// Verify $src actually has type Out<$struct>. Also verify it implements `OutProject`. This
|
||||
// excludes
|
||||
//
|
||||
// - the case where $src is Out<&Struct>, where it would be very UB to just construct a
|
||||
// writable reference to $src.$field, or a smart pointer
|
||||
// - the case where there are unaligned fields where it would be UB to call ptr::write to
|
||||
// them (requiring packed structs)
|
||||
{
|
||||
fn ensure_type<U: $crate::out::OutProject>(_t: &$crate::out::Out<U>) {}
|
||||
ensure_type::<$struct>(&$src);
|
||||
}
|
||||
// Verify there are no duplicate struct fields. This is not strictly necessary as Out lacks
|
||||
// the noalias requirement, but forbidding the same field to occur multiple times would
|
||||
// allow both cases. The compiler will reject any struct that reuses the same identifier.
|
||||
const _: () = {
|
||||
$(
|
||||
if ::core::mem::offset_of!($struct, $field) % ::core::mem::align_of::<$fieldty>() != 0 {
|
||||
panic!(concat!("unaligned field ", stringify!($field), " of struct ", stringify!($struct), "."));
|
||||
}
|
||||
)*
|
||||
struct S {
|
||||
$(
|
||||
$field: $fieldty
|
||||
),*
|
||||
}
|
||||
};
|
||||
|
||||
// Finally, create an Out<$fieldty> for each field.
|
||||
$(
|
||||
// getting the pointer to $field is safe
|
||||
let $field = unsafe { &raw mut (*$crate::out::Out::<_>::as_mut_ptr(&mut $src)).$field };
|
||||
)*
|
||||
$(
|
||||
let mut $field: $crate::out::Out<$fieldty> = unsafe {
|
||||
// SAFETY: the only guarantee is that the pointer is valid and writable for the
|
||||
// duration of 'b where $src: Out<'b, T>. But if so, and T is a struct, that
|
||||
// must also be true for all the struct fields.
|
||||
$crate::out::Out::with_lifetime_of(
|
||||
$crate::out::Out::nonnull($field),
|
||||
&$src,
|
||||
)
|
||||
};
|
||||
)*
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! OutProject {
|
||||
derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident {
|
||||
$(
|
||||
$(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty
|
||||
),*$(,)?
|
||||
} } => {
|
||||
// SAFETY: As simple as it is, OutProject is valid for any struct, and the pattern we have
|
||||
// matched above ensures $name is one.
|
||||
unsafe impl $crate::out::OutProject for $name {}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-1
@@ -73,6 +73,15 @@ impl<'a, T> Out<'a, T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, T, const N: usize> Out<'a, [T; N]> {
|
||||
#[inline]
|
||||
pub fn as_slice_mut<'b>(&'b mut self) -> Out<'b, [T]> {
|
||||
unsafe {
|
||||
let ptr: *mut [T; N] = self.as_mut_ptr();
|
||||
Out::from_raw_parts(ptr.cast::<T>(), N)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, T> Out<'a, [T]> {
|
||||
/// # Safety
|
||||
///
|
||||
@@ -90,6 +99,9 @@ impl<'a, T> Out<'a, [T]> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.ptr.as_ptr().len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
// TODO: Maybe strengthen lifetimes?
|
||||
#[inline]
|
||||
pub fn split_at_checked<'b>(&'b mut self, n: usize) -> Option<[Out<'b, [T]>; 2]> {
|
||||
@@ -142,8 +154,26 @@ impl<'a, T> Out<'a, [T]> {
|
||||
.copy_from_nonoverlapping(src.as_ptr(), src.len());
|
||||
}
|
||||
}
|
||||
pub fn copy_common_length_from_slice(&mut self, src: &[T]) -> usize
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let l = src.len().min(self.len());
|
||||
self.split_at_checked(l).unwrap()[0].copy_from_slice(&src[..l]);
|
||||
l
|
||||
}
|
||||
// TODO: better API, impl RangeBounds, also fn get(usize) -> Out<T>
|
||||
pub fn subslice<'b>(&'b mut self, start: usize, end: usize) -> Out<[T]> {
|
||||
assert!(start <= end);
|
||||
assert!(end <= self.len());
|
||||
unsafe { Self::from_raw_parts(self.as_mut_ptr().as_mut_ptr().add(start), end - start) }
|
||||
}
|
||||
pub fn index<'b>(&'b mut self, i: usize) -> Out<T> {
|
||||
assert!(i <= self.len());
|
||||
unsafe { Out::nonnull(self.as_mut_ptr().as_mut_ptr().add(i)) }
|
||||
}
|
||||
}
|
||||
// TODO: different trait?
|
||||
// TODO: use bytemuck
|
||||
impl<T: plain::Plain> Out<'_, [T]> {
|
||||
pub fn zero(&mut self) {
|
||||
let l = self.ptr.len();
|
||||
@@ -154,7 +184,28 @@ impl<T: plain::Plain> Out<'_, [T]> {
|
||||
self.ptr.as_mut_ptr().write_bytes(0, l)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn cast_slice_to<'b, U>(mut self) -> Out<'b, [U]>
|
||||
where
|
||||
T: CastSlice<U>,
|
||||
{
|
||||
assert_eq!(self.as_mut_ptr().as_mut_ptr() as usize % align_of::<U>(), 0);
|
||||
|
||||
let byte_length = self.as_mut_ptr().len() * size_of::<T>();
|
||||
|
||||
unsafe {
|
||||
Out::from_raw_parts(
|
||||
self.as_mut_ptr().as_mut_ptr().cast(),
|
||||
byte_length / size_of::<U>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: use bytemuck
|
||||
pub unsafe trait CastSlice<U> {}
|
||||
unsafe impl CastSlice<i8> for u8 {}
|
||||
unsafe impl CastSlice<u8> for i8 {}
|
||||
|
||||
impl<T: ?Sized> fmt::Pointer for Out<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:p}", self.ptr)
|
||||
@@ -165,3 +216,16 @@ impl<T: ?Sized> fmt::Debug for Out<'_, T> {
|
||||
write!(f, "[Out: {:p}]", self.ptr)
|
||||
}
|
||||
}
|
||||
/// Marker trait for types where it is sound to turn `Out<struct { ... }>` into `struct { ...:
|
||||
/// Out<...> }` by simply referencing fields. This is safe for any struct but must not be
|
||||
/// implemented for `Deref` types so that `Out<&struct { ... }>` is never projected in a way that
|
||||
/// adds mutability.
|
||||
pub unsafe trait OutProject {}
|
||||
|
||||
impl<'a, T: ?Sized> Out<'a, T> {
|
||||
pub unsafe fn with_lifetime_of<'b, U: ?Sized>(mut self, u: &'b U) -> Out<'b, T> {
|
||||
Out::nonnull(self.as_mut_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: unit tests
|
||||
|
||||
@@ -729,8 +729,8 @@ impl Pal for Sys {
|
||||
unsafe { syscall!(UMASK, mask) as mode_t }
|
||||
}
|
||||
|
||||
unsafe fn uname(utsname: *mut utsname) -> Result<()> {
|
||||
e_raw(unsafe { syscall!(UNAME, utsname, 0) }).map(|_| ())
|
||||
fn uname(mut utsname: Out<utsname>) -> Result<()> {
|
||||
e_raw(unsafe { syscall!(UNAME, utsname.as_mut_ptr(), 0) }).map(|_| ())
|
||||
}
|
||||
|
||||
fn unlink(path: CStr) -> Result<()> {
|
||||
|
||||
@@ -255,7 +255,7 @@ pub trait Pal {
|
||||
// Always successful
|
||||
fn umask(mask: mode_t) -> mode_t;
|
||||
|
||||
unsafe fn uname(utsname: *mut utsname) -> Result<()>;
|
||||
fn uname(utsname: Out<utsname>) -> Result<()>;
|
||||
|
||||
fn unlink(path: CStr) -> Result<()>;
|
||||
|
||||
|
||||
+30
-28
@@ -1013,8 +1013,8 @@ impl Pal for Sys {
|
||||
(redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX
|
||||
}
|
||||
|
||||
unsafe fn uname(utsname: *mut utsname) -> Result<(), Errno> {
|
||||
fn gethostname(name: &mut [u8]) -> io::Result<()> {
|
||||
fn uname(mut utsname: Out<utsname>) -> Result<(), Errno> {
|
||||
fn gethostname(mut name: Out<[u8]>) -> io::Result<()> {
|
||||
if name.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1024,21 +1024,26 @@ impl Pal for Sys {
|
||||
let mut read = 0;
|
||||
let name_len = name.len();
|
||||
loop {
|
||||
match file.read(&mut name[read..name_len - 1])? {
|
||||
match file.read_out(name.subslice(read, name_len - 1))? {
|
||||
0 => break,
|
||||
n => read += n,
|
||||
}
|
||||
}
|
||||
name[read] = 0;
|
||||
name.index(read).write(0);
|
||||
Ok(())
|
||||
}
|
||||
out_project! {
|
||||
let utsname {
|
||||
nodename: [c_char; UTSLENGTH],
|
||||
sysname: [c_char; UTSLENGTH],
|
||||
release: [c_char; UTSLENGTH],
|
||||
machine: [c_char; UTSLENGTH],
|
||||
version: [c_char; UTSLENGTH],
|
||||
domainname: [c_char; UTSLENGTH],
|
||||
} = utsname;
|
||||
}
|
||||
|
||||
match gethostname(unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
(*utsname).nodename.as_mut_ptr() as *mut u8,
|
||||
(*utsname).nodename.len(),
|
||||
)
|
||||
}) {
|
||||
match gethostname(nodename.as_slice_mut().cast_slice_to::<u8>()) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return Err(Errno(EIO)),
|
||||
}
|
||||
@@ -1050,36 +1055,33 @@ impl Pal for Sys {
|
||||
};
|
||||
let mut lines = BufReader::new(&mut file).lines();
|
||||
|
||||
let mut read_line = |dst: &mut [c_char]| {
|
||||
let mut read_line = |mut dst: Out<[u8]>| {
|
||||
// TODO: set nul byte without allocating CString
|
||||
let line = match lines.next() {
|
||||
Some(Ok(l)) => match CString::new(l) {
|
||||
Ok(l) => l,
|
||||
Err(_) => return Err(Errno(EIO)),
|
||||
},
|
||||
Some(Ok(l)) => CString::new(l).map_err(|_| Errno(EIO))?,
|
||||
None | Some(Err(_)) => return Err(Errno(EIO)),
|
||||
};
|
||||
|
||||
let line_slice: &[c_char] = unsafe { mem::transmute(line.as_bytes_with_nul()) };
|
||||
|
||||
if line_slice.len() <= UTSLENGTH {
|
||||
dst[..line_slice.len()].copy_from_slice(line_slice);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errno(EIO))
|
||||
let line_slice: &[u8] = line.as_bytes_with_nul();
|
||||
if line_slice.len() > UTSLENGTH {
|
||||
return Err(Errno(EIO));
|
||||
}
|
||||
|
||||
dst.copy_common_length_from_slice(line_slice);
|
||||
Ok(())
|
||||
};
|
||||
|
||||
unsafe {
|
||||
read_line(&mut (*utsname).sysname)?;
|
||||
read_line(&mut (*utsname).release)?;
|
||||
read_line(&mut (*utsname).machine)?;
|
||||
read_line(sysname.as_slice_mut().cast_slice_to::<u8>())?;
|
||||
read_line(release.as_slice_mut().cast_slice_to::<u8>())?;
|
||||
read_line(machine.as_slice_mut().cast_slice_to::<u8>())?;
|
||||
|
||||
// Version is not provided
|
||||
ptr::write_bytes((*utsname).version.as_mut_ptr(), 0, UTSLENGTH);
|
||||
version.as_slice_mut().zero();
|
||||
|
||||
// Redox doesn't provide domainname in sys:uname
|
||||
//read_line(&mut (*utsname).domainname)?;
|
||||
ptr::write_bytes((*utsname).domainname.as_mut_ptr(), 0, UTSLENGTH);
|
||||
//read_line(domainname.as_slice_mut())?;
|
||||
domainname.as_slice_mut().zero();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user