<commit_before>use std::mem;

use rustc::ty::{self, layout::{self, Size, Align}};
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc::mir;

use rand::RngCore;

use crate::*;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}

pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Gets an instance for a path.
    fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
        let this = self.eval_context_ref();
        this.tcx
            .crates()
            .iter()
            .find(|&&krate| this.tcx.original_crate_name(krate).as_str() == path[0])
            .and_then(|krate| {
                let krate = DefId {
                    krate: *krate,
                    index: CRATE_DEF_INDEX,
                };
                let mut items = this.tcx.item_children(krate);
                let mut path_it = path.iter().skip(1).peekable();

                while let Some(segment) = path_it.next() {
                    for item in mem::replace(&mut items, Default::default()).iter() {
                        if item.ident.name.as_str() == *segment {
                            if path_it.peek().is_none() {
                                return Some(ty::Instance::mono(this.tcx.tcx, item.res.def_id()));
                            }

                            items = this.tcx.item_children(item.res.def_id());
                            break;
                        }
                    }
                }
                None
            })
            .ok_or_else(|| {
                let path = path.iter().map(|&s| s.to_owned()).collect();
                err_unsup!(PathNotFound(path)).into()
            })
    }

    /// Write a 0 of the appropriate size to `dest`.
    fn write_null(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
        self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
    }

    /// Test if this immediate equals 0.
    fn is_null(&self, val: Scalar<Tag>) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_ref();
        let null = Scalar::from_int(0, this.memory().pointer_size());
        this.ptr_eq(val, null)
    }

    /// Turn a Scalar into an Option<NonNullScalar>
    fn test_null(&self, val: Scalar<Tag>) -> InterpResult<'tcx, Option<Scalar<Tag>>> {
        let this = self.eval_context_ref();
        Ok(if this.is_null(val)? {
            None
        } else {
            Some(val)
        })
    }

    /// Get the `Place` for a local
    fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> {
        let this = self.eval_context_mut();
        let place = mir::Place { base: mir::PlaceBase::Local(local), projection: None };
        this.eval_place(&place)
    }

    /// Generate some random bytes, and write them to `dest`.
    fn gen_random(
        &mut self,
        ptr: Scalar<Tag>,
        len: usize,
    ) -> InterpResult<'tcx>  {
        // Some programs pass in a null pointer and a length of 0
        // to their platform's random-generation function (e.g. getrandom())
        // on Linux. For compatibility with these programs, we don't perform
        // any additional checks - it's okay if the pointer is invalid,
        // since we wouldn't actually be writing to it.
        if len == 0 {
            return Ok(());
        }
        let this = self.eval_context_mut();

        let ptr = this.memory().check_ptr_access(
            ptr,
            Size::from_bytes(len as u64),
            Align::from_bytes(1).unwrap()
        )?.expect("we already checked for size 0");

        let mut data = vec![0; len];

        if this.machine.communicate {
            // Fill the buffer using the host's rng.
            getrandom::getrandom(&mut data)
                .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?;
        }
        else {
            let rng = this.memory_mut().extra.rng.get_mut();
            rng.fill_bytes(&mut data);
        }

        let tcx = &{this.tcx.tcx};
        this.memory_mut().get_mut(ptr.alloc_id)?.write_bytes(tcx, ptr, &data)
    }

    /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter
    /// will be true if this is frozen, false if this is in an `UnsafeCell`.
    fn visit_freeze_sensitive(
        &self,
        place: MPlaceTy<'tcx, Tag>,
        size: Size,
        mut action: impl FnMut(Pointer<Tag>, Size, bool) -> InterpResult<'tcx>,
    ) -> InterpResult<'tcx> {
        let this = self.eval_context_ref();
        trace!("visit_frozen(place={:?}, size={:?})", *place, size);
        debug_assert_eq!(size,
            this.size_and_align_of_mplace(place)?
            .map(|(size, _)| size)
            .unwrap_or_else(|| place.layout.size)
        );
        // Store how far we proceeded into the place so far. Everything to the left of
        // this offset has already been handled, in the sense that the frozen parts
        // have had `action` called on them.
        let mut end_ptr = place.ptr.assert_ptr();
        // Called when we detected an `UnsafeCell` at the given offset and size.
        // Calls `action` and advances `end_ptr`.
        let mut unsafe_cell_action = |unsafe_cell_ptr: Scalar<Tag>, unsafe_cell_size: Size| {
            let unsafe_cell_ptr = unsafe_cell_ptr.assert_ptr();
            debug_assert_eq!(unsafe_cell_ptr.alloc_id, end_ptr.alloc_id);
            debug_assert_eq!(unsafe_cell_ptr.tag, end_ptr.tag);
            // We assume that we are given the fields in increasing offset order,
            // and nothing else changes.
            let unsafe_cell_offset = unsafe_cell_ptr.offset;
            let end_offset = end_ptr.offset;
            assert!(unsafe_cell_offset >= end_offset);
            let frozen_size = unsafe_cell_offset - end_offset;
            // Everything between the end_ptr and this `UnsafeCell` is frozen.
            if frozen_size != Size::ZERO {
                action(end_ptr, frozen_size, /*frozen*/true)?;
            }
            // This `UnsafeCell` is NOT frozen.
            if unsafe_cell_size != Size::ZERO {
                action(unsafe_cell_ptr, unsafe_cell_size, /*frozen*/false)?;
            }
            // Update end end_ptr.
            end_ptr = unsafe_cell_ptr.wrapping_offset(unsafe_cell_size, this);
            // Done
            Ok(())
        };
        // Run a visitor
        {
            let mut visitor = UnsafeCellVisitor {
                ecx: this,
                unsafe_cell_action: |place| {
                    trace!("unsafe_cell_action on {:?}", place.ptr);
                    // We need a size to go on.
                    let unsafe_cell_size = this.size_and_align_of_mplace(place)?
                        .map(|(size, _)| size)
                        // for extern types, just cover what we can
                        .unwrap_or_else(|| place.layout.size);
                    // Now handle this `UnsafeCell`, unless it is empty.
                    if unsafe_cell_size != Size::ZERO {
                        unsafe_cell_action(place.ptr, unsafe_cell_size)
                    } else {
                        Ok(())
                    }
                },
            };
            visitor.visit_value(place)?;
        }
        // The part between the end_ptr and the end of the place is also frozen.
        // So pretend there is a 0-sized `UnsafeCell` at the end.
        unsafe_cell_action(place.ptr.ptr_wrapping_offset(size, this), Size::ZERO)?;
        // Done!
        return Ok(());

        /// Visiting the memory covered by a `MemPlace`, being aware of
        /// whether we are inside an `UnsafeCell` or not.
        struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
            where F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
        {
            ecx: &'ecx MiriEvalContext<'mir, 'tcx>,
            unsafe_cell_action: F,
        }

        impl<'ecx, 'mir, 'tcx, F>
            ValueVisitor<'mir, 'tcx, Evaluator<'tcx>>
        for
            UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
        where
            F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
        {
            type V = MPlaceTy<'tcx, Tag>;

            #[inline(always)]
            fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> {
                &self.ecx
            }

            // Hook to detect `UnsafeCell`.
            fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
                let is_unsafe_cell = match v.layout.ty.sty {
                    ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(),
                    _ => false,
                };
                if is_unsafe_cell {
                    // We do not have to recurse further, this is an `UnsafeCell`.
                    (self.unsafe_cell_action)(v)
                } else if self.ecx.type_is_freeze(v.layout.ty) {
                    // This is `Freeze`, there cannot be an `UnsafeCell`
                    Ok(())
                } else {
                    // We want to not actually read from memory for this visit. So, before
                    // walking this value, we have to make sure it is not a
                    // `Variants::Multiple`.
                    match v.layout.variants {
                        layout::Variants::Multiple { .. } => {
                            // A multi-variant enum, or generator, or so.
                            // Treat this like a union: without reading from memory,
                            // we cannot determine the variant we are in. Reading from
                            // memory would be subject to Stacked Borrows rules, leading
                            // to all sorts of "funny" recursion.
                            self.visit_union(v)
                        }
                        layout::Variants::Single { .. } => {
                            // Proceed further
                            self.walk_value(v)
                        }
                    }
                }
            }

            // Make sure we visit aggregrates in increasing offset order.
            fn visit_aggregate(
                &mut self,
                place: MPlaceTy<'tcx, Tag>,
                fields: impl Iterator<Item=InterpResult<'tcx, MPlaceTy<'tcx, Tag>>>,
            ) -> InterpResult<'tcx> {
                match place.layout.fields {
                    layout::FieldPlacement::Array { .. } => {
                        // For the array layout, we know the iterator will yield sorted elements so
                        // we can avoid the allocation.
                        self.walk_aggregate(place, fields)
                    }
                    layout::FieldPlacement::Arbitrary { .. } => {
                        // Gather the subplaces and sort them before visiting.
                        let mut places = fields.collect::<InterpResult<'tcx, Vec<MPlaceTy<'tcx, Tag>>>>()?;
                        places.sort_by_key(|place| place.ptr.assert_ptr().offset);
                        self.walk_aggregate(place, places.into_iter().map(Ok))
                    }
                    layout::FieldPlacement::Union { .. } => {
                        // Uh, what?
                        bug!("a union is not an aggregate we should ever visit")
                    }
                }
            }

            // We have to do *something* for unions.
            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                // With unions, we fall back to whatever the type says, to hopefully be consistent
                // with LLVM IR.
                // FIXME: are we consistent, and is this really the behavior we want?
                let frozen = self.ecx.type_is_freeze(v.layout.ty);
                if frozen {
                    Ok(())
                } else {
                    (self.unsafe_cell_action)(v)
                }
            }

            // We should never get to a primitive, but always short-circuit somewhere above.
            fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                bug!("we should always short-circuit before coming to a primitive")
            }
        }
    }
}
<commit_msg>small optimization<commit_after>use std::mem;

use rustc::ty::{self, layout::{self, Size, Align}};
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc::mir;

use rand::RngCore;

use crate::*;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}

pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    /// Gets an instance for a path.
    fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
        let this = self.eval_context_ref();
        this.tcx
            .crates()
            .iter()
            .find(|&&krate| this.tcx.original_crate_name(krate).as_str() == path[0])
            .and_then(|krate| {
                let krate = DefId {
                    krate: *krate,
                    index: CRATE_DEF_INDEX,
                };
                let mut items = this.tcx.item_children(krate);
                let mut path_it = path.iter().skip(1).peekable();

                while let Some(segment) = path_it.next() {
                    for item in mem::replace(&mut items, Default::default()).iter() {
                        if item.ident.name.as_str() == *segment {
                            if path_it.peek().is_none() {
                                return Some(ty::Instance::mono(this.tcx.tcx, item.res.def_id()));
                            }

                            items = this.tcx.item_children(item.res.def_id());
                            break;
                        }
                    }
                }
                None
            })
            .ok_or_else(|| {
                let path = path.iter().map(|&s| s.to_owned()).collect();
                err_unsup!(PathNotFound(path)).into()
            })
    }

    /// Write a 0 of the appropriate size to `dest`.
    fn write_null(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
        self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
    }

    /// Test if this immediate equals 0.
    fn is_null(&self, val: Scalar<Tag>) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_ref();
        let null = Scalar::from_int(0, this.memory().pointer_size());
        this.ptr_eq(val, null)
    }

    /// Turn a Scalar into an Option<NonNullScalar>
    fn test_null(&self, val: Scalar<Tag>) -> InterpResult<'tcx, Option<Scalar<Tag>>> {
        let this = self.eval_context_ref();
        Ok(if this.is_null(val)? {
            None
        } else {
            Some(val)
        })
    }

    /// Get the `Place` for a local
    fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> {
        let this = self.eval_context_mut();
        let place = mir::Place { base: mir::PlaceBase::Local(local), projection: None };
        this.eval_place(&place)
    }

    /// Generate some random bytes, and write them to `dest`.
    fn gen_random(
        &mut self,
        ptr: Scalar<Tag>,
        len: usize,
    ) -> InterpResult<'tcx>  {
        // Some programs pass in a null pointer and a length of 0
        // to their platform's random-generation function (e.g. getrandom())
        // on Linux. For compatibility with these programs, we don't perform
        // any additional checks - it's okay if the pointer is invalid,
        // since we wouldn't actually be writing to it.
        if len == 0 {
            return Ok(());
        }
        let this = self.eval_context_mut();

        let ptr = this.memory().check_ptr_access(
            ptr,
            Size::from_bytes(len as u64),
            Align::from_bytes(1).unwrap()
        )?.expect("we already checked for size 0");

        let mut data = vec![0; len];

        if this.machine.communicate {
            // Fill the buffer using the host's rng.
            getrandom::getrandom(&mut data)
                .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?;
        }
        else {
            let rng = this.memory_mut().extra.rng.get_mut();
            rng.fill_bytes(&mut data);
        }

        let tcx = &{this.tcx.tcx};
        this.memory_mut().get_mut(ptr.alloc_id)?.write_bytes(tcx, ptr, &data)
    }

    /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter
    /// will be true if this is frozen, false if this is in an `UnsafeCell`.
    fn visit_freeze_sensitive(
        &self,
        place: MPlaceTy<'tcx, Tag>,
        size: Size,
        mut action: impl FnMut(Pointer<Tag>, Size, bool) -> InterpResult<'tcx>,
    ) -> InterpResult<'tcx> {
        let this = self.eval_context_ref();
        trace!("visit_frozen(place={:?}, size={:?})", *place, size);
        debug_assert_eq!(size,
            this.size_and_align_of_mplace(place)?
            .map(|(size, _)| size)
            .unwrap_or_else(|| place.layout.size)
        );
        // Store how far we proceeded into the place so far. Everything to the left of
        // this offset has already been handled, in the sense that the frozen parts
        // have had `action` called on them.
        let mut end_ptr = place.ptr.assert_ptr();
        // Called when we detected an `UnsafeCell` at the given offset and size.
        // Calls `action` and advances `end_ptr`.
        let mut unsafe_cell_action = |unsafe_cell_ptr: Scalar<Tag>, unsafe_cell_size: Size| {
            let unsafe_cell_ptr = unsafe_cell_ptr.assert_ptr();
            debug_assert_eq!(unsafe_cell_ptr.alloc_id, end_ptr.alloc_id);
            debug_assert_eq!(unsafe_cell_ptr.tag, end_ptr.tag);
            // We assume that we are given the fields in increasing offset order,
            // and nothing else changes.
            let unsafe_cell_offset = unsafe_cell_ptr.offset;
            let end_offset = end_ptr.offset;
            assert!(unsafe_cell_offset >= end_offset);
            let frozen_size = unsafe_cell_offset - end_offset;
            // Everything between the end_ptr and this `UnsafeCell` is frozen.
            if frozen_size != Size::ZERO {
                action(end_ptr, frozen_size, /*frozen*/true)?;
            }
            // This `UnsafeCell` is NOT frozen.
            if unsafe_cell_size != Size::ZERO {
                action(unsafe_cell_ptr, unsafe_cell_size, /*frozen*/false)?;
            }
            // Update end end_ptr.
            end_ptr = unsafe_cell_ptr.wrapping_offset(unsafe_cell_size, this);
            // Done
            Ok(())
        };
        // Run a visitor
        {
            let mut visitor = UnsafeCellVisitor {
                ecx: this,
                unsafe_cell_action: |place| {
                    trace!("unsafe_cell_action on {:?}", place.ptr);
                    // We need a size to go on.
                    let unsafe_cell_size = this.size_and_align_of_mplace(place)?
                        .map(|(size, _)| size)
                        // for extern types, just cover what we can
                        .unwrap_or_else(|| place.layout.size);
                    // Now handle this `UnsafeCell`, unless it is empty.
                    if unsafe_cell_size != Size::ZERO {
                        unsafe_cell_action(place.ptr, unsafe_cell_size)
                    } else {
                        Ok(())
                    }
                },
            };
            visitor.visit_value(place)?;
        }
        // The part between the end_ptr and the end of the place is also frozen.
        // So pretend there is a 0-sized `UnsafeCell` at the end.
        unsafe_cell_action(place.ptr.ptr_wrapping_offset(size, this), Size::ZERO)?;
        // Done!
        return Ok(());

        /// Visiting the memory covered by a `MemPlace`, being aware of
        /// whether we are inside an `UnsafeCell` or not.
        struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
            where F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
        {
            ecx: &'ecx MiriEvalContext<'mir, 'tcx>,
            unsafe_cell_action: F,
        }

        impl<'ecx, 'mir, 'tcx, F>
            ValueVisitor<'mir, 'tcx, Evaluator<'tcx>>
        for
            UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
        where
            F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
        {
            type V = MPlaceTy<'tcx, Tag>;

            #[inline(always)]
            fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> {
                &self.ecx
            }

            // Hook to detect `UnsafeCell`.
            fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
                let is_unsafe_cell = match v.layout.ty.sty {
                    ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(),
                    _ => false,
                };
                if is_unsafe_cell {
                    // We do not have to recurse further, this is an `UnsafeCell`.
                    (self.unsafe_cell_action)(v)
                } else if self.ecx.type_is_freeze(v.layout.ty) {
                    // This is `Freeze`, there cannot be an `UnsafeCell`
                    Ok(())
                } else {
                    // We want to not actually read from memory for this visit. So, before
                    // walking this value, we have to make sure it is not a
                    // `Variants::Multiple`.
                    match v.layout.variants {
                        layout::Variants::Multiple { .. } => {
                            // A multi-variant enum, or generator, or so.
                            // Treat this like a union: without reading from memory,
                            // we cannot determine the variant we are in. Reading from
                            // memory would be subject to Stacked Borrows rules, leading
                            // to all sorts of "funny" recursion.
                            // We only end up here if the type is *not* freeze, so we just call the
                            // `UnsafeCell` action.
                            (self.unsafe_cell_action)(v)
                        }
                        layout::Variants::Single { .. } => {
                            // Proceed further, try to find where exactly that `UnsafeCell`
                            // is hiding.
                            self.walk_value(v)
                        }
                    }
                }
            }

            // Make sure we visit aggregrates in increasing offset order.
            fn visit_aggregate(
                &mut self,
                place: MPlaceTy<'tcx, Tag>,
                fields: impl Iterator<Item=InterpResult<'tcx, MPlaceTy<'tcx, Tag>>>,
            ) -> InterpResult<'tcx> {
                match place.layout.fields {
                    layout::FieldPlacement::Array { .. } => {
                        // For the array layout, we know the iterator will yield sorted elements so
                        // we can avoid the allocation.
                        self.walk_aggregate(place, fields)
                    }
                    layout::FieldPlacement::Arbitrary { .. } => {
                        // Gather the subplaces and sort them before visiting.
                        let mut places = fields.collect::<InterpResult<'tcx, Vec<MPlaceTy<'tcx, Tag>>>>()?;
                        places.sort_by_key(|place| place.ptr.assert_ptr().offset);
                        self.walk_aggregate(place, places.into_iter().map(Ok))
                    }
                    layout::FieldPlacement::Union { .. } => {
                        // Uh, what?
                        bug!("a union is not an aggregate we should ever visit")
                    }
                }
            }

            // We have to do *something* for unions.
            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                // With unions, we fall back to whatever the type says, to hopefully be consistent
                // with LLVM IR.
                // FIXME: are we consistent, and is this really the behavior we want?
                let frozen = self.ecx.type_is_freeze(v.layout.ty);
                if frozen {
                    Ok(())
                } else {
                    (self.unsafe_cell_action)(v)
                }
            }

            // We should never get to a primitive, but always short-circuit somewhere above.
            fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
            {
                bug!("we should always short-circuit before coming to a primitive")
            }
        }
    }
}
<|endoftext|>


<commit_before>use neb::ram::schema::Field;
use neb::ram::types::{TypeId, Id, key_hash, Map, Value};
use neb::ram::cell::{Cell, WriteError, ReadError};
use neb::client::{Client as NebClient};
use neb::client::transaction::TxnError;
use bifrost::rpc::RPCError;

use server::schema::{MorpheusSchema, SchemaType, SchemaContainer, SchemaError};
use graph::vertex::Vertex;

use std::sync::Arc;
use serde::Serialize;

pub mod vertex;
pub mod edge;

pub enum NewVertexError {
    SchemaNotFound,
    SchemaNotVertex,
    CannotGenerateCellByData,
    DataNotMap,
    RPCError(RPCError),
    WriteError(WriteError),
}

pub enum ReadVertexError {
    RPCError(RPCError),
    ReadError(ReadError),
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum CellType {
    Vertex,
    Edge(edge::EdgeType)
}

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from("next"), TypeId::Id as u32, true, false, None),
            Field::new(&String::from("list"), TypeId::Id as u32, false, true, None)
        ];
}

fn vertex_to_cell_for_write(schemas: &Arc<SchemaContainer>, vertex: Vertex) -> Result<Cell, NewVertexError> {
    let schema_id = vertex.schema;
    if let Some(stype) = schemas.schema_type(schema_id) {
        if stype != SchemaType::Vertex {
            return Err(NewVertexError::SchemaNotVertex)
        }
    } else {
        return Err(NewVertexError::SchemaNotFound)
    }
    let neb_schema = match schemas.get_neb_schema(schema_id) {
        Some(schema) => schema,
        None => return Err(NewVertexError::SchemaNotFound)
    };
    let mut data = {
        match vertex.data {
            Value::Map(mut map) => map,
            _ => return Err(NewVertexError::DataNotMap)
        }
    };
    data.insert_key_id(*vertex::INBOUND_KEY_ID, Value::Id(Id::unit_id()));
    data.insert_key_id(*vertex::OUTBOUND_KEY_ID, Value::Id(Id::unit_id()));
    data.insert_key_id(*vertex::INDIRECTED_KEY_ID, Value::Id(Id::unit_id()));
    match Cell::new(&neb_schema, Value::Map(data)) {
        Some(cell) => Ok(cell),
        None => return Err(NewVertexError::CannotGenerateCellByData)
    }
}

pub struct Graph {
    schemas: Arc<SchemaContainer>,
    neb_client: Arc<NebClient>
}

impl Graph {
    pub fn new_vertex_group(&self, schema: &mut MorpheusSchema) -> Result<(), SchemaError> {
        schema.schema_type = SchemaType::Vertex;
        self.schemas.new_schema(schema)
    }
    pub fn new_edge_group(&self, schema: &mut MorpheusSchema, edge_type: edge::EdgeType) -> Result<(), SchemaError> {
        if edge_type == edge::EdgeType::Simple {
            return Err(SchemaError::SimpleEdgeShouldNotHaveSchema)
        }
        schema.schema_type = SchemaType::Edge(edge_type);
        self.schemas.new_schema(schema)
    }
    pub fn new_vertex(&self, vertex: Vertex) -> Result<Vertex, NewVertexError> {
        let mut cell = vertex_to_cell_for_write(&self.schemas, vertex)?;
        let header = match self.neb_client.write_cell(&cell) {
            Ok(Ok(header)) => header,
            Ok(Err(e)) => return Err(NewVertexError::WriteError(e)),
            Err(e) => return Err(NewVertexError::RPCError(e))
        };
        cell.header = header;
        Ok(vertex::cell_to_vertex(cell))
    }
    // TODO: Update edge list
    pub fn remove_vertex(&self, id: &Id) -> Result<(), TxnError> {
        self.neb_client.transaction(|mut txn| {
            vertex::txn_remove(&mut txn, id)
        })
    }
    pub fn remove_vertex_by_key<K>(&self, schema_id: u32, key: &K) -> Result<(), TxnError>
        where K: Serialize {
        let id = Cell::encode_cell_key(schema_id, key);
        self.remove_vertex(&id)
    }
    pub fn update_vertex<U>(&self, id: &Id, update: U) -> Result<(), TxnError>
        where U: Fn(Vertex) -> Option<Vertex> {
        self.neb_client.transaction(|txn|{
            vertex::txn_update(txn, id, &update)
        })
    }
    pub fn update_vertex_by_key<K, U>(&self, schema_id: u32, key: &K, update: U)
        -> Result<(), TxnError>
        where K: Serialize, U: Fn(Vertex) -> Option<Vertex>{
        let id = Cell::encode_cell_key(schema_id, key);
        self.update_vertex(&id, update)
    }

    pub fn read_vertex(&self, id: &Id) -> Result<Option<Vertex>, ReadVertexError> {
        match self.neb_client.read_cell(id) {
            Err(e) => Err(ReadVertexError::RPCError(e)),
            Ok(Err(ReadError::CellDoesNotExisted)) => Ok(None),
            Ok(Err(e)) => Err(ReadVertexError::ReadError(e)),
            Ok(Ok(cell)) => Ok(Some(vertex::cell_to_vertex(cell)))
        }
    }
}<commit_msg>get vertex<commit_after>use neb::ram::schema::Field;
use neb::ram::types::{TypeId, Id, key_hash, Map, Value};
use neb::ram::cell::{Cell, WriteError, ReadError};
use neb::client::{Client as NebClient};
use neb::client::transaction::TxnError;
use bifrost::rpc::RPCError;

use server::schema::{MorpheusSchema, SchemaType, SchemaContainer, SchemaError};
use graph::vertex::Vertex;

use std::sync::Arc;
use serde::Serialize;

pub mod vertex;
pub mod edge;

pub enum NewVertexError {
    SchemaNotFound,
    SchemaNotVertex,
    CannotGenerateCellByData,
    DataNotMap,
    RPCError(RPCError),
    WriteError(WriteError),
}

pub enum ReadVertexError {
    RPCError(RPCError),
    ReadError(ReadError),
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum CellType {
    Vertex,
    Edge(edge::EdgeType)
}

lazy_static! {
    pub static ref ID_LINKED_LIST: Vec<Field> = vec![
            Field::new(&String::from("next"), TypeId::Id as u32, true, false, None),
            Field::new(&String::from("list"), TypeId::Id as u32, false, true, None)
        ];
}

fn vertex_to_cell_for_write(schemas: &Arc<SchemaContainer>, vertex: Vertex) -> Result<Cell, NewVertexError> {
    let schema_id = vertex.schema;
    if let Some(stype) = schemas.schema_type(schema_id) {
        if stype != SchemaType::Vertex {
            return Err(NewVertexError::SchemaNotVertex)
        }
    } else {
        return Err(NewVertexError::SchemaNotFound)
    }
    let neb_schema = match schemas.get_neb_schema(schema_id) {
        Some(schema) => schema,
        None => return Err(NewVertexError::SchemaNotFound)
    };
    let mut data = {
        match vertex.data {
            Value::Map(mut map) => map,
            _ => return Err(NewVertexError::DataNotMap)
        }
    };
    data.insert_key_id(*vertex::INBOUND_KEY_ID, Value::Id(Id::unit_id()));
    data.insert_key_id(*vertex::OUTBOUND_KEY_ID, Value::Id(Id::unit_id()));
    data.insert_key_id(*vertex::INDIRECTED_KEY_ID, Value::Id(Id::unit_id()));
    match Cell::new(&neb_schema, Value::Map(data)) {
        Some(cell) => Ok(cell),
        None => return Err(NewVertexError::CannotGenerateCellByData)
    }
}

pub struct Graph {
    schemas: Arc<SchemaContainer>,
    neb_client: Arc<NebClient>
}

impl Graph {
    pub fn new_vertex_group(&self, schema: &mut MorpheusSchema) -> Result<(), SchemaError> {
        schema.schema_type = SchemaType::Vertex;
        self.schemas.new_schema(schema)
    }
    pub fn new_edge_group(&self, schema: &mut MorpheusSchema, edge_type: edge::EdgeType) -> Result<(), SchemaError> {
        if edge_type == edge::EdgeType::Simple {
            return Err(SchemaError::SimpleEdgeShouldNotHaveSchema)
        }
        schema.schema_type = SchemaType::Edge(edge_type);
        self.schemas.new_schema(schema)
    }
    pub fn new_vertex(&self, vertex: Vertex) -> Result<Vertex, NewVertexError> {
        let mut cell = vertex_to_cell_for_write(&self.schemas, vertex)?;
        let header = match self.neb_client.write_cell(&cell) {
            Ok(Ok(header)) => header,
            Ok(Err(e)) => return Err(NewVertexError::WriteError(e)),
            Err(e) => return Err(NewVertexError::RPCError(e))
        };
        cell.header = header;
        Ok(vertex::cell_to_vertex(cell))
    }
    // TODO: Update edge list
    pub fn remove_vertex(&self, id: &Id) -> Result<(), TxnError> {
        self.neb_client.transaction(|mut txn| {
            vertex::txn_remove(&mut txn, id)
        })
    }
    pub fn remove_vertex_by_key<K>(&self, schema_id: u32, key: &K) -> Result<(), TxnError>
        where K: Serialize {
        let id = Cell::encode_cell_key(schema_id, key);
        self.remove_vertex(&id)
    }
    pub fn update_vertex<U>(&self, id: &Id, update: U) -> Result<(), TxnError>
        where U: Fn(Vertex) -> Option<Vertex> {
        self.neb_client.transaction(|txn|{
            vertex::txn_update(txn, id, &update)
        })
    }
    pub fn update_vertex_by_key<K, U>(&self, schema_id: u32, key: &K, update: U)
        -> Result<(), TxnError>
        where K: Serialize, U: Fn(Vertex) -> Option<Vertex>{
        let id = Cell::encode_cell_key(schema_id, key);
        self.update_vertex(&id, update)
    }

    pub fn read_vertex(&self, id: &Id) -> Result<Option<Vertex>, ReadVertexError> {
        match self.neb_client.read_cell(id) {
            Err(e) => Err(ReadVertexError::RPCError(e)),
            Ok(Err(ReadError::CellDoesNotExisted)) => Ok(None),
            Ok(Err(e)) => Err(ReadVertexError::ReadError(e)),
            Ok(Ok(cell)) => Ok(Some(vertex::cell_to_vertex(cell)))
        }
    }

    pub fn get_vertex<K>(&self, schema_id: u32, key: &K) -> Result<Option<Vertex>, ReadVertexError>
        where K: Serialize {
        let id = Cell::encode_cell_key(schema_id, key);
        self.read_vertex(&id)
    }
}<|endoftext|>


<commit_before>use crate::value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

use serde::{Deserialize, Serialize};

/// Enumeration referencing all the available AMQP types
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    /// A bool
    Boolean,
    /// An i8
    ShortShortInt,
    /// A u8
    ShortShortUInt,
    /// An i16
    ShortInt,
    /// A u16
    ShortUInt,
    /// An i32
    LongInt,
    /// A u32
    LongUInt,
    /// An i64
    LongLongInt,
    /// A u64
    LongLongUInt,
    /// An f32
    Float,
    /// An f64
    Double,
    /// A decimal value represented by a scale and a value
    DecimalValue,
    /// Deprecated, a String
    ShortString,
    /// A String
    LongString,
    /// An array of AMQPValue
    FieldArray,
    /// A timestamp (u32)
    Timestamp,
    /// A Map<String, AMQPValue>
    FieldTable,
    /// An array of bytes, RabbitMQ specific
    ByteArray, /* ByteArray is specific to RabbitMQ */
    /// No value
    Void,
}

impl AMQPType {
    /// Get the AMQPType corresponding to the given id.
    /// We don't strictly follow the spec here but rather the RabbitMQ implementation
    /// 's' means ShortInt (like 'U') instead of ShortString
    /// 'l' and 'L' both mean LongLongInt (no LongLongUInt)
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            's' |
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            'L' |
            'l' => Some(AMQPType::LongLongInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'x' => Some(AMQPType::ByteArray),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    /// Get the id from an AMQPType
    /// We don't strictly follow the spec here but rather the RabbitMQ implementation
    /// ShortString doesn't have an id, we return '_' instead
    /// ShortInt is supposed to be 'U' but we use 's'
    /// LongLongUInt is supposed to be 'L' but we return 'l' as LongLongInt
    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean        => 't',
            AMQPType::ShortShortInt  => 'b',
            AMQPType::ShortShortUInt => 'B',
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            AMQPType::ShortInt       => 's',
            AMQPType::ShortUInt      => 'u',
            AMQPType::LongInt        => 'I',
            AMQPType::LongUInt       => 'i',
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            AMQPType::LongLongInt    |
            AMQPType::LongLongUInt   => 'l',
            AMQPType::Float          => 'f',
            AMQPType::Double         => 'd',
            AMQPType::DecimalValue   => 'D',
            /* ShortString only exists for internal usage, we shouldn't ever have to use this */
            AMQPType::ShortString    => '_',
            AMQPType::LongString     => 'S',
            AMQPType::FieldArray     => 'A',
            AMQPType::Timestamp      => 'T',
            AMQPType::FieldTable     => 'F',
            AMQPType::ByteArray      => 'x',
            AMQPType::Void           => 'V',
        }
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// A bool
pub type Boolean        = bool;
/// An i8
pub type ShortShortInt  = i8;
/// A u8
pub type ShortShortUInt = u8;
/// An i16
pub type ShortInt       = i16;
/// A u16
pub type ShortUInt      = u16;
/// An i32
pub type LongInt        = i32;
/// A u32
pub type LongUInt       = u32;
/// An i64
pub type LongLongInt    = i64;
/// A u64
pub type LongLongUInt   = u64;
/// A f32
pub type Float          = f32;
/// A f64
pub type Double         = f64;
/// A String (deprecated)
pub type ShortString    = String;
/// A String
pub type LongString     = String;
/// An array of AMQPValue
pub type FieldArray     = Vec<AMQPValue>;
/// A timestamp (u32)
pub type Timestamp      = LongLongUInt;
/// A Map<String, AMQPValue>
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
/// An array of bytes (RabbitMQ specific)
pub type ByteArray      = Vec<u8>;
/// No value
pub type Void           = ();

/// A Decimal value composed of a scale and a value
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    /// The scale of the value
    pub scale: ShortShortUInt,
    /// The actual value
    pub value: LongUInt,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_type_from_id() {
        assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
        assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
        assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
        assert_eq!(AMQPType::from_id('z'), None);
    }

    #[test]
    fn test_type_get_id() {
        assert_eq!(AMQPType::LongLongInt.get_id(),  'l');
        assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
        assert_eq!(AMQPType::ShortString.get_id(),  '_');
    }

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<commit_msg>types: impl more traits<commit_after>use crate::value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

use serde::{Deserialize, Serialize};

/// Enumeration referencing all the available AMQP types
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum AMQPType {
    /// A bool
    Boolean,
    /// An i8
    ShortShortInt,
    /// A u8
    ShortShortUInt,
    /// An i16
    ShortInt,
    /// A u16
    ShortUInt,
    /// An i32
    LongInt,
    /// A u32
    LongUInt,
    /// An i64
    LongLongInt,
    /// A u64
    LongLongUInt,
    /// An f32
    Float,
    /// An f64
    Double,
    /// A decimal value represented by a scale and a value
    DecimalValue,
    /// Deprecated, a String
    ShortString,
    /// A String
    LongString,
    /// An array of AMQPValue
    FieldArray,
    /// A timestamp (u32)
    Timestamp,
    /// A Map<String, AMQPValue>
    FieldTable,
    /// An array of bytes, RabbitMQ specific
    ByteArray, /* ByteArray is specific to RabbitMQ */
    /// No value
    Void,
}

impl AMQPType {
    /// Get the AMQPType corresponding to the given id.
    /// We don't strictly follow the spec here but rather the RabbitMQ implementation
    /// 's' means ShortInt (like 'U') instead of ShortString
    /// 'l' and 'L' both mean LongLongInt (no LongLongUInt)
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            's' |
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            'L' |
            'l' => Some(AMQPType::LongLongInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'x' => Some(AMQPType::ByteArray),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    /// Get the id from an AMQPType
    /// We don't strictly follow the spec here but rather the RabbitMQ implementation
    /// ShortString doesn't have an id, we return '_' instead
    /// ShortInt is supposed to be 'U' but we use 's'
    /// LongLongUInt is supposed to be 'L' but we return 'l' as LongLongInt
    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean        => 't',
            AMQPType::ShortShortInt  => 'b',
            AMQPType::ShortShortUInt => 'B',
            /* Specs says 'U', RabbitMQ says 's' (which means ShortString in specs) */
            AMQPType::ShortInt       => 's',
            AMQPType::ShortUInt      => 'u',
            AMQPType::LongInt        => 'I',
            AMQPType::LongUInt       => 'i',
            /* RabbitMQ treats both 'l' and 'L' as LongLongInt and ignores LongLongUInt */
            AMQPType::LongLongInt    |
            AMQPType::LongLongUInt   => 'l',
            AMQPType::Float          => 'f',
            AMQPType::Double         => 'd',
            AMQPType::DecimalValue   => 'D',
            /* ShortString only exists for internal usage, we shouldn't ever have to use this */
            AMQPType::ShortString    => '_',
            AMQPType::LongString     => 'S',
            AMQPType::FieldArray     => 'A',
            AMQPType::Timestamp      => 'T',
            AMQPType::FieldTable     => 'F',
            AMQPType::ByteArray      => 'x',
            AMQPType::Void           => 'V',
        }
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// A bool
pub type Boolean        = bool;
/// An i8
pub type ShortShortInt  = i8;
/// A u8
pub type ShortShortUInt = u8;
/// An i16
pub type ShortInt       = i16;
/// A u16
pub type ShortUInt      = u16;
/// An i32
pub type LongInt        = i32;
/// A u32
pub type LongUInt       = u32;
/// An i64
pub type LongLongInt    = i64;
/// A u64
pub type LongLongUInt   = u64;
/// A f32
pub type Float          = f32;
/// A f64
pub type Double         = f64;
/// A String (deprecated)
pub type ShortString    = String;
/// A String
pub type LongString     = String;
/// An array of AMQPValue
pub type FieldArray     = Vec<AMQPValue>;
/// A timestamp (u32)
pub type Timestamp      = LongLongUInt;
/// A Map<String, AMQPValue>
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
/// An array of bytes (RabbitMQ specific)
pub type ByteArray      = Vec<u8>;
/// No value
pub type Void           = ();

/// A Decimal value composed of a scale and a value
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DecimalValue {
    /// The scale of the value
    pub scale: ShortShortUInt,
    /// The actual value
    pub value: LongUInt,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_type_from_id() {
        assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
        assert_eq!(AMQPType::from_id('S'), Some(AMQPType::LongString));
        assert_eq!(AMQPType::from_id('s'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('U'), Some(AMQPType::ShortInt));
        assert_eq!(AMQPType::from_id('l'), Some(AMQPType::LongLongInt));
        assert_eq!(AMQPType::from_id('z'), None);
    }

    #[test]
    fn test_type_get_id() {
        assert_eq!(AMQPType::LongLongInt.get_id(),  'l');
        assert_eq!(AMQPType::LongLongUInt.get_id(), 'l');
        assert_eq!(AMQPType::ShortString.get_id(),  '_');
    }

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<|endoftext|>


<commit_before>use core_foundation::base::OSStatus;
use security_framework::certificate::SecCertificate;
use security_framework::policy::SecPolicy;
use security_framework::secure_transport::ProtocolSide;
use security_framework::trust::{SecTrust, TrustResult};

use ValidationResult;

pub fn validate_cert_chain(encoded_certs: &[&[u8]], hostname: &str) -> ValidationResult {
    let mut certs = Vec::new();
    for encoded_cert in encoded_certs {
        let cert = SecCertificate::from_der(encoded_cert);
        match cert {
            Ok(cert) => certs.push(cert),
            // This is remarkably difficult to hit: OS X mostly parses the cert
            // lazily. Still possible though.
            Err(_) => return ValidationResult::MalformedCertificateInChain,
        };
    }

    let ssl_policy = match SecPolicy::for_ssl(ProtocolSide::Client, hostname) {
        Ok(policy) => policy,
        Err(_) => return ValidationResult::ErrorDuringValidation,
    };
    let trust = match SecTrust::create_with_certificates(&certs[..], &[ssl_policy]) {
        Ok(trust) => trust,
        Err(status) => return os_status_to_validation_result(status.code())
    };

    // Errors here are really unexpected.
    match trust.evaluate() {
        Ok(result) => trust_result_to_validation_result(result),
        Err(status) => os_status_to_validation_result(status.code()),
    }
}

// Convert a TrustResult to a ValidationResult.
fn trust_result_to_validation_result(trust_result: TrustResult) -> ValidationResult {
    match trust_result {
        TrustResult::Invalid | TrustResult::Unspecified => ValidationResult::Trusted,
        _ => ValidationResult::NotTrusted,
    }
}

// Convert OSStatus codes to validation results. Used to handle errors.
#[allow(non_upper_case_globals)]
fn os_status_to_validation_result(status: OSStatus) -> ValidationResult {
    match status {
        errSecNotAvailable | errSecNoSecurityModule | errSecNoPolicyModule =>
            ValidationResult::MissingFunctionality,
        errSecAuthFailed => ValidationResult::UserAuthenticationRequired,
        _ => ValidationResult::ErrorDuringValidation,
    }
}

// Define some error constants, because rust-security-framework doesn't.
#[allow(non_upper_case_globals)]
const errSecNotAvailable: OSStatus = -25291;
#[allow(non_upper_case_globals)]
const errSecNoSecurityModule: OSStatus = -25313;
#[allow(non_upper_case_globals)]
const errSecNoPolicyModule: OSStatus = -25314;
#[allow(non_upper_case_globals)]
const errSecAuthFailed: OSStatus = -25293;

#[cfg(test)]
mod test {
    use osx::validate_cert_chain;
    use test::{expired_chain, certifi_chain, self_signed_chain};
    use ValidationResult;

    #[test]
    fn can_validate_good_chain() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(&chain, "certifi.io");
        assert_eq!(valid, ValidationResult::Trusted);
    }

    #[test]
    fn fails_on_bad_hostname() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(&chain, "lukasa.co.uk");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn fails_on_bad_cert() {
        let mut good_chain = certifi_chain();
        let originals = good_chain.split_first_mut().unwrap();
        let leaf = originals.0;
        let intermediates = originals.1;

        // Deliberately truncate the leaf cert.
        let mut certs = vec![&leaf[1..50]];
        certs.extend(intermediates.iter());
        let valid = validate_cert_chain(&certs, "certifi.io");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn fails_on_expired_cert() {
        let chain = expired_chain();
        let valid = validate_cert_chain(&chain, "expired.badssl.com");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn test_fails_on_self_signed() {
        let chain = self_signed_chain();
        let valid = validate_cert_chain(&chain, "self-signed.badssl.com");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn test_fails_on_invalid_asn1() {
        // We need to mutate the first byte of the original cert.
        let mut chain = certifi_chain();
        let mut first_chain = chain[0].to_vec();
        first_chain[0] = 0xff;
        let mut chain_builder = vec![first_chain.as_slice()];
        chain_builder.append(&mut chain[1..].to_vec());
        let new_chain = chain_builder.as_slice();

        let valid = validate_cert_chain(&new_chain, "certifi.io");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }
}
<commit_msg>Make sure we trust explicitly trusted certs on OS X<commit_after>use core_foundation::base::OSStatus;
use security_framework::certificate::SecCertificate;
use security_framework::policy::SecPolicy;
use security_framework::secure_transport::ProtocolSide;
use security_framework::trust::{SecTrust, TrustResult};

use ValidationResult;

pub fn validate_cert_chain(encoded_certs: &[&[u8]], hostname: &str) -> ValidationResult {
    let mut certs = Vec::new();
    for encoded_cert in encoded_certs {
        let cert = SecCertificate::from_der(encoded_cert);
        match cert {
            Ok(cert) => certs.push(cert),
            // This is remarkably difficult to hit: OS X mostly parses the cert
            // lazily. Still possible though.
            Err(_) => return ValidationResult::MalformedCertificateInChain,
        };
    }

    let ssl_policy = match SecPolicy::for_ssl(ProtocolSide::Client, hostname) {
        Ok(policy) => policy,
        Err(_) => return ValidationResult::ErrorDuringValidation,
    };
    let trust = match SecTrust::create_with_certificates(&certs[..], &[ssl_policy]) {
        Ok(trust) => trust,
        Err(status) => return os_status_to_validation_result(status.code())
    };

    // Errors here are really unexpected.
    match trust.evaluate() {
        Ok(result) => trust_result_to_validation_result(result),
        Err(status) => os_status_to_validation_result(status.code()),
    }
}

// Convert a TrustResult to a ValidationResult.
fn trust_result_to_validation_result(trust_result: TrustResult) -> ValidationResult {
    match trust_result {
        TrustResult::Proceed | TrustResult::Unspecified => ValidationResult::Trusted,
        _ => ValidationResult::NotTrusted,
    }
}

// Convert OSStatus codes to validation results. Used to handle errors.
#[allow(non_upper_case_globals)]
fn os_status_to_validation_result(status: OSStatus) -> ValidationResult {
    match status {
        errSecNotAvailable | errSecNoSecurityModule | errSecNoPolicyModule =>
            ValidationResult::MissingFunctionality,
        errSecAuthFailed => ValidationResult::UserAuthenticationRequired,
        _ => ValidationResult::ErrorDuringValidation,
    }
}

// Define some error constants, because rust-security-framework doesn't.
#[allow(non_upper_case_globals)]
const errSecNotAvailable: OSStatus = -25291;
#[allow(non_upper_case_globals)]
const errSecNoSecurityModule: OSStatus = -25313;
#[allow(non_upper_case_globals)]
const errSecNoPolicyModule: OSStatus = -25314;
#[allow(non_upper_case_globals)]
const errSecAuthFailed: OSStatus = -25293;

#[cfg(test)]
mod test {
    use osx::validate_cert_chain;
    use test::{expired_chain, certifi_chain, self_signed_chain};
    use ValidationResult;

    #[test]
    fn can_validate_good_chain() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(&chain, "certifi.io");
        assert_eq!(valid, ValidationResult::Trusted);
    }

    #[test]
    fn fails_on_bad_hostname() {
        let chain = certifi_chain();
        let valid = validate_cert_chain(&chain, "lukasa.co.uk");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn fails_on_bad_cert() {
        let mut good_chain = certifi_chain();
        let originals = good_chain.split_first_mut().unwrap();
        let leaf = originals.0;
        let intermediates = originals.1;

        // Deliberately truncate the leaf cert.
        let mut certs = vec![&leaf[1..50]];
        certs.extend(intermediates.iter());
        let valid = validate_cert_chain(&certs, "certifi.io");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn fails_on_expired_cert() {
        let chain = expired_chain();
        let valid = validate_cert_chain(&chain, "expired.badssl.com");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn test_fails_on_self_signed() {
        let chain = self_signed_chain();
        let valid = validate_cert_chain(&chain, "self-signed.badssl.com");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }

    #[test]
    fn test_fails_on_invalid_asn1() {
        // We need to mutate the first byte of the original cert.
        let mut chain = certifi_chain();
        let mut first_chain = chain[0].to_vec();
        first_chain[0] = 0xff;
        let mut chain_builder = vec![first_chain.as_slice()];
        chain_builder.append(&mut chain[1..].to_vec());
        let new_chain = chain_builder.as_slice();

        let valid = validate_cert_chain(&new_chain, "certifi.io");
        assert_eq!(valid, ValidationResult::NotTrusted);
    }
}
<|endoftext|>


<commit_before>//! WASM bindings for Pact models

use anyhow::anyhow;
use log::debug;
use pact_models::pact::load_pact_from_json;
use pact_models::prelude::v4::V4Pact;
use wasm_bindgen::prelude::*;

/// Library version
#[wasm_bindgen(js_name = libVersion)]
pub fn lib_version() -> String {
  option_env!("CARGO_PKG_VERSION").unwrap_or_default().to_string()
}

/// Struct for a Pact (request/response or message)
#[wasm_bindgen]
#[derive(Debug)]
pub struct Pact {
  pact: Box<dyn pact_models::pact::Pact + Send + Sync>,
}

#[wasm_bindgen]
impl Pact {
  /// Consumer side of the pact
  #[wasm_bindgen(getter)]
  pub fn consumer(&self) -> String {
    self.pact.consumer().name
  }

  /// Provider side of the pact
  #[wasm_bindgen(getter)]
  pub fn provider(&self) -> String {
    self.pact.provider().name
  }

  /// Provider side of the pact
  #[wasm_bindgen(getter, js_name = specificationVersion)]
  pub fn specification_version(&self) -> String {
    self.pact.specification_version().to_string()
  }

  /// Parse a Pact from JSON
  pub fn parse(json: String) -> Result<Pact, JsValue> {
    match serde_json::from_str(&json) {
      Ok(json) => load_pact_from_json("<JSON>", &json).map(|pact| Pact { pact }),
      Err(err) => Err(anyhow!("Failed to parse Pact JSON: {}", err))
    }.map_err(|err| {
      JsValue::from(err.to_string())
    })
  }
}

impl PartialEq for Pact {
  fn eq(&self, other: &Self) -> bool {
    if let Ok(pact) = self.pact.as_v4_pact() {
      if let Ok(ref other) = other.pact.as_v4_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else if let Ok(pact) = self.pact.as_request_response_pact() {
      if let Ok(ref other) = other.pact.as_request_response_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else if let Ok(pact) = self.pact.as_message_pact() {
      if let Ok(ref other) = other.pact.as_message_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else {
      false
    }
  }
}

impl Clone for Pact {
  fn clone(&self) -> Self {
    Pact { pact: self.pact.boxed() }
  }
}

impl Default for Pact {
  fn default() -> Self {
    Pact { pact: pact_models::pact::Pact::boxed(&V4Pact::default()) }
  }
}
<commit_msg>feat: add functions to return plugin details to the WASM interface<commit_after>//! WASM bindings for Pact models

use std::rc::Rc;

use anyhow::anyhow;
use log::debug;
use pact_models::pact::load_pact_from_json;
use pact_models::plugins::PluginData;
use pact_models::v4::pact::V4Pact;
use pact_models::v4::synch_http::SynchronousHttp;
use wasm_bindgen::prelude::*;

/// Library version
#[wasm_bindgen(js_name = libVersion)]
pub fn lib_version() -> String {
  option_env!("CARGO_PKG_VERSION").unwrap_or_default().to_string()
}

/// Struct for a Pact Interaction (request/response or message)
#[wasm_bindgen]
#[derive(Debug)]
pub struct PactInteraction {
  interaction: Box<dyn pact_models::interaction::Interaction + Send + Sync>
}

#[wasm_bindgen]
impl PactInteraction {
  /// Interaction key
  #[wasm_bindgen(getter)]
  pub fn key(&self) -> String {
    self.interaction.as_v4().map(|i| i.key().unwrap_or_default())
      .unwrap_or_default()
  }

  /// Description
  #[wasm_bindgen(getter)]
  pub fn description(&self) -> String {
    self.interaction.description().clone()
  }

  /// Interaction Type
  #[wasm_bindgen(getter, js_name=type)]
  pub fn interaction_type(&self) -> String {
    if let Some(v4) = self.interaction.as_v4() {
      v4.type_of()
    } else if self.interaction.is_message() {
      "Message".to_string()
    } else {
      "HTTP".to_string()
    }
  }

  /// Interaction Markup
  #[wasm_bindgen(getter, js_name=markup)]
  pub fn interaction_markup(&self) -> Option<String> {
    if let Some(v4) = self.interaction.as_v4() {
      Some(v4.interaction_markup().markup)
    } else {
      None
    }
  }

  /// Interaction Markup Type
  #[wasm_bindgen(getter, js_name=markupType)]
  pub fn interaction_markup_type(&self) -> Option<String> {
    if let Some(v4) = self.interaction.as_v4() {
      Some(v4.interaction_markup().markup_type)
    } else {
      None
    }
  }
}

impl PartialEq for PactInteraction {
  fn eq(&self, other: &Self) -> bool {
    if let Some(interaction) = self.interaction.as_v4() {
      if let Some(ref other) = other.interaction.as_v4() {
        interaction.eq(other)
      } else {
        false
      }
    } else if let Some(interaction) = self.interaction.as_request_response() {
      if let Some(ref other) = other.interaction.as_request_response() {
        interaction.eq(other)
      } else {
        false
      }
    } else if let Some(interaction) = self.interaction.as_message() {
      if let Some(ref other) = other.interaction.as_message() {
        interaction.eq(other)
      } else {
        false
      }
    } else {
      false
    }
  }
}

impl Clone for PactInteraction {
  fn clone(&self) -> Self {
    PactInteraction { interaction: self.interaction.boxed() }
  }
}

impl Default for PactInteraction {
  fn default() -> Self {
    PactInteraction { interaction: pact_models::interaction::Interaction::boxed(&SynchronousHttp::default()) }
  }
}

/// Details of plugins used with the Pact
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct PactPlugin {
  plugin_data: Rc<PluginData>
}

impl PactPlugin {
  pub fn new(plugin_data: &PluginData) -> Self {
    PactPlugin {
      plugin_data: Rc::new(plugin_data.clone())
    }
  }
}

#[wasm_bindgen]
impl PactPlugin {
  /// Plugin name
  #[wasm_bindgen(getter)]
  pub fn name(&self) -> String {
    self.plugin_data.name.clone()
  }

  /// Plugin version
  #[wasm_bindgen(getter)]
  pub fn version(&self) -> String {
    self.plugin_data.version.clone()
  }
}

/// Struct for a Pact (request/response or message)
#[wasm_bindgen]
#[derive(Debug)]
pub struct Pact {
  pact: Box<dyn pact_models::pact::Pact + Send + Sync>,
}

#[wasm_bindgen]
impl Pact {
  /// Consumer side of the pact
  #[wasm_bindgen(getter)]
  pub fn consumer(&self) -> String {
    self.pact.consumer().name
  }

  /// Provider side of the pact
  #[wasm_bindgen(getter)]
  pub fn provider(&self) -> String {
    self.pact.provider().name
  }

  /// Provider side of the pact
  #[wasm_bindgen(getter, js_name = specificationVersion)]
  pub fn specification_version(&self) -> String {
    self.pact.specification_version().to_string()
  }

  /// Parse a Pact from JSON
  pub fn parse(json: String) -> Result<Pact, JsValue> {
    match serde_json::from_str(&json) {
      Ok(json) => load_pact_from_json("<JSON>", &json).map(|pact| Pact { pact }),
      Err(err) => Err(anyhow!("Failed to parse Pact JSON: {}", err))
    }.map_err(|err| {
      JsValue::from(err.to_string())
    })
  }

  /// Interactions in the Pact
  #[wasm_bindgen(js_name = numInteractions)]
  pub fn num_interactions(&self) -> usize {
    self.pact.interactions().len()
  }

  /// Interactions in the Pact
  #[wasm_bindgen]
  pub fn interaction(&self, index: usize) -> Option<PactInteraction> {
    self.pact.interactions().get(index).map(|interaction| {
      PactInteraction { interaction: interaction.boxed() }
    })
  }

  /// Plugin data in the pact
  #[wasm_bindgen(js_name = hasPlugins)]
  pub fn has_plugins(&self) -> bool {
    !self.pact.plugin_data().is_empty()
  }

  /// Plugin data in the pact
  #[wasm_bindgen(js_name = numPlugins)]
  pub fn num_plugins(&self) -> usize {
    self.pact.plugin_data().len()
  }

  /// Plugin data in the pact
  #[wasm_bindgen]
  pub fn plugin(&self, index: usize) -> Option<PactPlugin> {
    self.pact.plugin_data().get(index).map(|plugin| {
      PactPlugin::new(plugin)
    })
  }
}

impl PartialEq for Pact {
  fn eq(&self, other: &Self) -> bool {
    if let Ok(pact) = self.pact.as_v4_pact() {
      if let Ok(ref other) = other.pact.as_v4_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else if let Ok(pact) = self.pact.as_request_response_pact() {
      if let Ok(ref other) = other.pact.as_request_response_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else if let Ok(pact) = self.pact.as_message_pact() {
      if let Ok(ref other) = other.pact.as_message_pact() {
        pact.eq(other)
      } else {
        false
      }
    } else {
      false
    }
  }
}

impl Clone for Pact {
  fn clone(&self) -> Self {
    Pact { pact: self.pact.boxed() }
  }
}

impl Default for Pact {
  fn default() -> Self {
    Pact { pact: pact_models::pact::Pact::boxed(&V4Pact::default()) }
  }
}
<|endoftext|>


<commit_before>use bytes::Buf;
use iovec::IoVec;

/// A `Buf` wrapping a static byte slice.
#[derive(Debug)]
pub(crate) struct StaticBuf(pub(crate) &'static [u8]);

impl Buf for StaticBuf {
    #[inline]
    fn remaining(&self) -> usize {
        self.0.len()
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        self.0
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.0 = &self.0[cnt..];
    }

    #[inline]
    fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
        if dst.is_empty() {
            return 0;
        } else {
            dst[0] = self.0.into();
            return 1;
        }
    }
}

<commit_msg>refactor(common): ensure empty StaticBufs don't make IoVecs<commit_after>use bytes::Buf;
use iovec::IoVec;

/// A `Buf` wrapping a static byte slice.
#[derive(Debug)]
pub(crate) struct StaticBuf(pub(crate) &'static [u8]);

impl Buf for StaticBuf {
    #[inline]
    fn remaining(&self) -> usize {
        self.0.len()
    }

    #[inline]
    fn bytes(&self) -> &[u8] {
        self.0
    }

    #[inline]
    fn advance(&mut self, cnt: usize) {
        self.0 = &self.0[cnt..];
    }

    #[inline]
    fn bytes_vec<'t>(&'t self, dst: &mut [&'t IoVec]) -> usize {
        if dst.is_empty() || self.0.is_empty() {
            0
        } else {
            dst[0] = self.0.into();
            1
        }
    }
}

<|endoftext|>


<commit_before>use core::prelude::*;

use aspace::mapflag;
use con;
use con::write;
use cpu;
use process;
use process::Handle;
use process::Process;
use start32::kernel_base;
use util::abort;

static log_syscall : bool = false;
static log_transfer_message : bool = false;
static log_recv : bool = false;

pub mod nr {
    #![allow(dead_code)]
    pub static RECV : uint = 0;
    pub static MAP : uint = 1;
    pub static PFAULT : uint = 2;
    pub static UNMAP : uint = 3;
    pub static HMOD : uint = 4;
    pub static NEWPROC : uint = 5;
    pub static WRITE : uint = 6;
    pub static PORTIO : uint = 7;
    pub static GRANT : uint = 8;
    pub static PULSE : uint = 9;

    pub static USER : uint = 16;

    pub static MSG_MASK : uint = 0xff;
    pub static MSG_KIND_MASK : uint = 0x300;
    pub static MSG_KIND_SEND : uint = 0x000;
    pub static MSG_KIND_CALL : uint = 0x100;
}

// Note: tail-called from the syscall code, "return" by switching to a process.
#[no_mangle]
pub fn syscall(
    arg0: uint,
    arg1: uint,
    arg2: uint,
    arg3: uint,
    arg4: uint,
    arg5: uint,
    nr : uint, // saved_rax
) -> ! {
    use syscall::nr::*;

    let p = unsafe { cpu().get_process() };
    // FIXME cpu.leave_proc?
    p.unset(process::Running);
    p.set(process::FastRet);

    if log_syscall {
        write("syscall! nr=");
        con::writeUInt(nr);
        write(" from process ");
        con::writeMutPtr(p);
        con::newline();
    }

    match nr {
    RECV => ipc_recv(p, arg0),
    MAP => syscall_map(p, arg0, arg1, arg2, arg3, arg4),
    PFAULT => syscall_pfault(p, arg1, arg2), // arg0 is always 0
    HMOD => syscall_hmod(p, arg0, arg1, arg2),
    PORTIO => syscall_portio(p, arg0, arg1, arg2),
    WRITE => {
        con::putc(arg0 as u8 as char);
        cpu().syscall_return(p, 0);
    },
    _ if nr >= USER => {
        match nr & MSG_KIND_MASK {
            MSG_KIND_CALL => ipc_call(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            MSG_KIND_SEND => ipc_send(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            _ => abort("Unknown IPC kind")
        }
    },
    _ => abort("Unhandled syscall"),
    }

    if p.is_runnable() {
        abort("process not blocked at return");
    }

    unsafe { cpu().run(); }
}

fn ipc_call(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
    arg3: uint, arg4: uint, arg5: uint) {
    write("ipc_call to ");
    con::writeUInt(to);
    con::newline();

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        write("==> process ");
        con::writeMutPtr(h.process());
        con::newline();
        p.set(process::InSend);
        p.set(process::InRecv);
        p.regs.rdi = to;
        send_or_block(h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_call: no recipient")
    }
    write("ipc_call: blocked\n");
}

fn transfer_set_handle(target: &mut Process, source: &mut Process) {
    let mut rcpt = target.regs.rdi;
    let from = source.regs.rdi;

    let h = source.find_handle(from).unwrap();
    if rcpt == 0 {
        rcpt = h.other().unwrap().id();
    } else if !target.find_handle(rcpt).is_some() {
        match h.other() {
            // Already associated, "junk" the fresh handle and just update the
            // recipient-side handle.
            Some(g) => rcpt = g.id(),
            // Associate handles now.
            None => {
                let g = target.new_handle(rcpt, source);
                g.associate(h);
            },
        }
    } else {
        // TODO Assert that rcpt <-> from. (But the caller is responsible for
        // checking that first.)
    }
    target.regs.rdi = rcpt;
}

fn transfer_message(target: &mut Process, source: &mut Process) -> ! {
    transfer_set_handle(target, source);

    if log_transfer_message {
        write("transfer_message ");
        con::writeMutPtr(target);
        write(" <- ");
        con::writeMutPtr(source);
        con::newline();
    }

    target.regs.rax = source.regs.rax;
    target.regs.rdi = source.regs.rdi;
    target.regs.rsi = source.regs.rsi;
    target.regs.rdx = source.regs.rdx;
    target.regs.r8 = source.regs.r8;
    target.regs.r9 = source.regs.r9;
    target.regs.r10 = source.regs.r10;

    target.unset(process::InRecv);
    source.unset(process::InSend);

    let c = cpu();
    c.queue(target);
    if source.ipc_state() == 0 {
        c.queue(source);
    }
    unsafe { c.run(); }
}

fn send_or_block(h : &mut Handle, msg: uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    match h.other() {
        Some(g) => {
            let p = h.process();
            let sender = g.process();

            // Save regs - either we'll copy these in transfer_message or we'll
            // need to store them until later on when the transfer can finish.
            sender.regs.rax = msg;
            sender.regs.rdi = h.id();
            sender.regs.rsi = arg1;
            sender.regs.rdx = arg2;
            sender.regs.r10 = arg3;
            sender.regs.r8 = arg4;
            sender.regs.r9 = arg5;

            // p is the recipient, the sender is in g.process().
            if p.ipc_state() == process::InRecv.mask() {
                let rcpt = h.process().regs.rdi;
                // Check the receiving process' receipt handle
                //   0 ==> transfer
                //   !0, connected to our handle ==> transfer
                //   !0, fresh ==> transfer
                //   !0 otherwise ==> block
                if rcpt == 0
                || rcpt == g.id()
                || !p.find_handle(rcpt).is_some() {
                    transfer_message(p, sender);
                }
            }

            p.add_waiter(sender)
        },
        None => abort("sending to unconnected handle"),
    }
}

fn ipc_send(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        p.set(process::InSend);
        send_or_block(h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_send: no recipient")
    }
}

fn ipc_recv(p : &mut Process, from : uint) {
    let mut handle = None;
    if from != 0 {
        handle = p.find_handle(from);
    }

    if log_recv {
        con::writeMutPtr(p);
        write(" recv from ");
        con::writeUInt(from);
    }

    p.set(process::InRecv);
    p.regs.rdi = from;
    match handle {
        Some(h) => {
            if log_recv {
                write(" ==> process ");
                con::writeMutPtr(h.process());
                con::newline();
            }
            recv(p, h)
        },
        None => {
            if log_recv && from != 0 {
                write(" ==> fresh\n");
            }
            recv_from_any(p, from)
        }
    }
}

fn recv(p: &mut Process, handle: &mut Handle) {
    let rcpt = handle.process();
    if rcpt.is(process::InSend) {
        abort("recv-from-specific not implemented");
    } else {
        rcpt.add_waiter(p);
    }
}

fn recv_from_any(p : &mut Process, _id: uint) {
    let mut sender = None;
    for waiter in p.waiters.iter() {
        if waiter.is(process::InSend) {
            sender = Some(waiter);
            break;
        }
    }
    match sender {
        Some(s) => {
            p.remove_waiter(s);
            transfer_message(p, s);
        },
        None => ()
    }

    if log_recv {
        write("recv: no waiters for ");
        con::writeMutPtr(p);
        con::newline();
    }

    // TODO Look for pending pulse
    // 2. Look for pending pulses
    // 3. Switch next
}

fn syscall_map(p: &mut Process, handle: uint, mut prot: uint, addr: uint, mut offset: uint, size: uint) {
    prot &= mapflag::UserAllowed;
    // TODO Check (and return failure) on:
    // * unaligned addr, offset, size (must be page-aligned)
    if (prot & mapflag::DMA) == mapflag::DMA {
        offset = match cpu().memory.alloc_frame() {
            None => 0,
            Some(p) => p as uint - kernel_base,
        }
    }

    p.aspace().map_range(addr, addr + size, handle, (offset - addr) | prot);

    if (prot & mapflag::Phys) == 0 {
        offset = 0;
    }
    cpu().syscall_return(p, offset);
}

fn syscall_pfault(p : &mut Process, mut vaddr: uint, access: uint) {
    vaddr &= !0xfff;

    // set fault address
    p.fault_addr = vaddr;
    p.regs.rsi = vaddr;
    p.regs.rdx = access & mapflag::RWX;
    // Look up vaddr, get handle, offset and flags
    let card = p.aspace().mapcard_find_def(vaddr);
    p.regs.rsi += card.offset; // proc.rsi is now translated into offset
    p.regs.rdi = card.handle;
    p.set(process::PFault);

    // Now do the equivalent of sendrcv with rdi=handle, rsi=offset, rdx=flags
}

fn syscall_hmod(p : &mut Process, id: uint, rename: uint, copy: uint) {
    let handle = p.find_handle(id);
    match handle {
    None => return,
    Some(h) => {
        // Fresh/dissociated handle for the same process as the original
        if copy != 0 {
            p.new_handle(copy, h.process());
        }
        if rename != 0 {
            p.rename_handle(h, rename);
        } else {
            p.delete_handle(h);
        }
    }
    }
}

fn syscall_portio(p : &mut Process, port : uint, op : uint, data: uint) -> ! {
    let mut res : uint = 0;
    unsafe { match op {
    0x01 => asm!("inb %dx, %al" : "={al}"(res) : "{dx}"(port)),
    0x02 => asm!("inw %dx, %ax" : "={ax}"(res) : "{dx}"(port)),
    0x04 => asm!("inl %dx, %eax" : "={eax}"(res) : "{dx}"(port)),
    0x11 => asm!("outb %al, %dx" :: "{al}"(data), "{dx}"(port)),
    0x12 => asm!("outw %ax, %dx" :: "{ax}"(data), "{dx}"(port)),
    0x14 => asm!("outl %eax, %dx" :: "{eax}"(data), "{dx}"(port)),
    _ => abort("unhandled portio operation")
    } }
    cpu().syscall_return(p, res);
}
<commit_msg>Logging for port I/O syscall<commit_after>use core::prelude::*;

use aspace::mapflag;
use con;
use con::write;
use cpu;
use process;
use process::Handle;
use process::Process;
use start32::kernel_base;
use util::abort;

static log_syscall : bool = false;
static log_transfer_message : bool = false;
static log_portio : bool = false;

static log_recv : bool = false;

pub mod nr {
    #![allow(dead_code)]
    pub static RECV : uint = 0;
    pub static MAP : uint = 1;
    pub static PFAULT : uint = 2;
    pub static UNMAP : uint = 3;
    pub static HMOD : uint = 4;
    pub static NEWPROC : uint = 5;
    pub static WRITE : uint = 6;
    pub static PORTIO : uint = 7;
    pub static GRANT : uint = 8;
    pub static PULSE : uint = 9;

    pub static USER : uint = 16;

    pub static MSG_MASK : uint = 0xff;
    pub static MSG_KIND_MASK : uint = 0x300;
    pub static MSG_KIND_SEND : uint = 0x000;
    pub static MSG_KIND_CALL : uint = 0x100;
}

// Note: tail-called from the syscall code, "return" by switching to a process.
#[no_mangle]
pub fn syscall(
    arg0: uint,
    arg1: uint,
    arg2: uint,
    arg3: uint,
    arg4: uint,
    arg5: uint,
    nr : uint, // saved_rax
) -> ! {
    use syscall::nr::*;

    let p = unsafe { cpu().get_process() };
    // FIXME cpu.leave_proc?
    p.unset(process::Running);
    p.set(process::FastRet);

    if log_syscall {
        write("syscall! nr=");
        con::writeUInt(nr);
        write(" from process ");
        con::writeMutPtr(p);
        con::newline();
    }

    match nr {
    RECV => ipc_recv(p, arg0),
    MAP => syscall_map(p, arg0, arg1, arg2, arg3, arg4),
    PFAULT => syscall_pfault(p, arg1, arg2), // arg0 is always 0
    HMOD => syscall_hmod(p, arg0, arg1, arg2),
    PORTIO => syscall_portio(p, arg0, arg1, arg2),
    WRITE => {
        con::putc(arg0 as u8 as char);
        cpu().syscall_return(p, 0);
    },
    _ if nr >= USER => {
        match nr & MSG_KIND_MASK {
            MSG_KIND_CALL => ipc_call(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            MSG_KIND_SEND => ipc_send(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            _ => abort("Unknown IPC kind")
        }
    },
    _ => abort("Unhandled syscall"),
    }

    if p.is_runnable() {
        abort("process not blocked at return");
    }

    unsafe { cpu().run(); }
}

fn ipc_call(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
    arg3: uint, arg4: uint, arg5: uint) {
    write("ipc_call to ");
    con::writeUInt(to);
    con::newline();

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        write("==> process ");
        con::writeMutPtr(h.process());
        con::newline();
        p.set(process::InSend);
        p.set(process::InRecv);
        p.regs.rdi = to;
        send_or_block(h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_call: no recipient")
    }
    write("ipc_call: blocked\n");
}

fn transfer_set_handle(target: &mut Process, source: &mut Process) {
    let mut rcpt = target.regs.rdi;
    let from = source.regs.rdi;

    let h = source.find_handle(from).unwrap();
    if rcpt == 0 {
        rcpt = h.other().unwrap().id();
    } else if !target.find_handle(rcpt).is_some() {
        match h.other() {
            // Already associated, "junk" the fresh handle and just update the
            // recipient-side handle.
            Some(g) => rcpt = g.id(),
            // Associate handles now.
            None => {
                let g = target.new_handle(rcpt, source);
                g.associate(h);
            },
        }
    } else {
        // TODO Assert that rcpt <-> from. (But the caller is responsible for
        // checking that first.)
    }
    target.regs.rdi = rcpt;
}

fn transfer_message(target: &mut Process, source: &mut Process) -> ! {
    transfer_set_handle(target, source);

    if log_transfer_message {
        write("transfer_message ");
        con::writeMutPtr(target);
        write(" <- ");
        con::writeMutPtr(source);
        con::newline();
    }

    target.regs.rax = source.regs.rax;
    target.regs.rdi = source.regs.rdi;
    target.regs.rsi = source.regs.rsi;
    target.regs.rdx = source.regs.rdx;
    target.regs.r8 = source.regs.r8;
    target.regs.r9 = source.regs.r9;
    target.regs.r10 = source.regs.r10;

    target.unset(process::InRecv);
    source.unset(process::InSend);

    let c = cpu();
    c.queue(target);
    if source.ipc_state() == 0 {
        c.queue(source);
    }
    unsafe { c.run(); }
}

fn send_or_block(h : &mut Handle, msg: uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    match h.other() {
        Some(g) => {
            let p = h.process();
            let sender = g.process();

            // Save regs - either we'll copy these in transfer_message or we'll
            // need to store them until later on when the transfer can finish.
            sender.regs.rax = msg;
            sender.regs.rdi = h.id();
            sender.regs.rsi = arg1;
            sender.regs.rdx = arg2;
            sender.regs.r10 = arg3;
            sender.regs.r8 = arg4;
            sender.regs.r9 = arg5;

            // p is the recipient, the sender is in g.process().
            if p.ipc_state() == process::InRecv.mask() {
                let rcpt = h.process().regs.rdi;
                // Check the receiving process' receipt handle
                //   0 ==> transfer
                //   !0, connected to our handle ==> transfer
                //   !0, fresh ==> transfer
                //   !0 otherwise ==> block
                if rcpt == 0
                || rcpt == g.id()
                || !p.find_handle(rcpt).is_some() {
                    transfer_message(p, sender);
                }
            }

            p.add_waiter(sender)
        },
        None => abort("sending to unconnected handle"),
    }
}

fn ipc_send(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        p.set(process::InSend);
        send_or_block(h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_send: no recipient")
    }
}

fn ipc_recv(p : &mut Process, from : uint) {
    let mut handle = None;
    if from != 0 {
        handle = p.find_handle(from);
    }

    if log_recv {
        con::writeMutPtr(p);
        write(" recv from ");
        con::writeUInt(from);
    }

    p.set(process::InRecv);
    p.regs.rdi = from;
    match handle {
        Some(h) => {
            if log_recv {
                write(" ==> process ");
                con::writeMutPtr(h.process());
                con::newline();
            }
            recv(p, h)
        },
        None => {
            if log_recv && from != 0 {
                write(" ==> fresh\n");
            }
            recv_from_any(p, from)
        }
    }
}

fn recv(p: &mut Process, handle: &mut Handle) {
    let rcpt = handle.process();
    if rcpt.is(process::InSend) {
        abort("recv-from-specific not implemented");
    } else {
        rcpt.add_waiter(p);
    }
}

fn recv_from_any(p : &mut Process, _id: uint) {
    let mut sender = None;
    for waiter in p.waiters.iter() {
        if waiter.is(process::InSend) {
            sender = Some(waiter);
            break;
        }
    }
    match sender {
        Some(s) => {
            p.remove_waiter(s);
            transfer_message(p, s);
        },
        None => ()
    }

    if log_recv {
        write("recv: no waiters for ");
        con::writeMutPtr(p);
        con::newline();
    }

    // TODO Look for pending pulse
    // 2. Look for pending pulses
    // 3. Switch next
}

fn syscall_map(p: &mut Process, handle: uint, mut prot: uint, addr: uint, mut offset: uint, size: uint) {
    prot &= mapflag::UserAllowed;
    // TODO Check (and return failure) on:
    // * unaligned addr, offset, size (must be page-aligned)
    if (prot & mapflag::DMA) == mapflag::DMA {
        offset = match cpu().memory.alloc_frame() {
            None => 0,
            Some(p) => p as uint - kernel_base,
        }
    }

    p.aspace().map_range(addr, addr + size, handle, (offset - addr) | prot);

    if (prot & mapflag::Phys) == 0 {
        offset = 0;
    }
    cpu().syscall_return(p, offset);
}

fn syscall_pfault(p : &mut Process, mut vaddr: uint, access: uint) {
    vaddr &= !0xfff;

    // set fault address
    p.fault_addr = vaddr;
    p.regs.rsi = vaddr;
    p.regs.rdx = access & mapflag::RWX;
    // Look up vaddr, get handle, offset and flags
    let card = p.aspace().mapcard_find_def(vaddr);
    p.regs.rsi += card.offset; // proc.rsi is now translated into offset
    p.regs.rdi = card.handle;
    p.set(process::PFault);

    // Now do the equivalent of sendrcv with rdi=handle, rsi=offset, rdx=flags
}

fn syscall_hmod(p : &mut Process, id: uint, rename: uint, copy: uint) {
    let handle = p.find_handle(id);
    match handle {
    None => return,
    Some(h) => {
        // Fresh/dissociated handle for the same process as the original
        if copy != 0 {
            p.new_handle(copy, h.process());
        }
        if rename != 0 {
            p.rename_handle(h, rename);
        } else {
            p.delete_handle(h);
        }
    }
    }
}

fn syscall_portio(p : &mut Process, port : uint, op : uint, data: uint) -> ! {
    let mut res : uint = 0;
    unsafe { match op {
    0x01 => asm!("inb %dx, %al" : "={al}"(res) : "{dx}"(port)),
    0x02 => asm!("inw %dx, %ax" : "={ax}"(res) : "{dx}"(port)),
    0x04 => asm!("inl %dx, %eax" : "={eax}"(res) : "{dx}"(port)),
    0x11 => asm!("outb %al, %dx" :: "{al}"(data), "{dx}"(port)),
    0x12 => asm!("outw %ax, %dx" :: "{ax}"(data), "{dx}"(port)),
    0x14 => asm!("outl %eax, %dx" :: "{eax}"(data), "{dx}"(port)),
    _ => abort("unhandled portio operation")
    } }
    if log_portio {
        write("portio: port="); con::writeHex(port & 0xffff);
        write(" op="); con::writeUInt(op);
        write(" data="); con::writeHex(data);
        write(" res="); con::writeHex(res);
        con::newline();
    }
    cpu().syscall_return(p, res);
}
<|endoftext|>