<commit_before>use devices::{NodeId, ServiceId, ServiceKind, Service, Input, Output};
use util::Exactly;
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selectors::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<NodeId>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: NodeId) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selectors.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selectors::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = InputSelector::new()
///   .with_parent(NodeId::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selectors.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selectors.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) =>
                return false,
            (&None, &Some(_)) => return false,
            _ => return true
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<commit_msg>Doctest fixup<commit_after>use devices::{NodeId, ServiceId, ServiceKind, Service, Input, Output};
use util::Exactly;
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<NodeId>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: NodeId) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = InputSelector::new()
///   .with_parent(NodeId::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<ServiceId>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<NodeId>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: ServiceId) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: NodeId) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) =>
                return false,
            (&None, &Some(_)) => return false,
            _ => return true
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<|endoftext|>


<commit_before>use cargo_platform::Platform;
use log::trace;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
use std::path::PathBuf;
use std::rc::Rc;

use crate::core::{PackageId, SourceId, Summary};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::OptVersionReq;

/// Information about a dependency requested by a Cargo manifest.
/// Cheap to copy.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Dependency {
    inner: Rc<Inner>,
}

/// The data underlying a `Dependency`.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
struct Inner {
    name: InternedString,
    source_id: SourceId,
    /// Source ID for the registry as specified in the manifest.
    ///
    /// This will be None if it is not specified (crates.io dependency).
    /// This is different from `source_id` for example when both a `path` and
    /// `registry` is specified. Or in the case of a crates.io dependency,
    /// `source_id` will be crates.io and this will be None.
    registry_id: Option<SourceId>,
    req: OptVersionReq,
    specified_req: bool,
    kind: DepKind,
    only_match_name: bool,
    explicit_name_in_toml: Option<InternedString>,

    optional: bool,
    public: bool,
    default_features: bool,
    features: Vec<InternedString>,

    // This dependency should be used only for this platform.
    // `None` means *all platforms*.
    platform: Option<Platform>,
}

#[derive(Serialize)]
struct SerializedDependency<'a> {
    name: &'a str,
    source: SourceId,
    req: String,
    kind: DepKind,
    rename: Option<&'a str>,

    optional: bool,
    uses_default_features: bool,
    features: &'a [InternedString],
    target: Option<&'a Platform>,
    /// The registry URL this dependency is from.
    /// If None, then it comes from the default registry (crates.io).
    registry: Option<&'a str>,

    /// The file system path for a local path dependency.
    #[serde(skip_serializing_if = "Option::is_none")]
    path: Option<PathBuf>,
}

impl ser::Serialize for Dependency {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        let registry_id = self.registry_id();
        SerializedDependency {
            name: &*self.package_name(),
            source: self.source_id(),
            req: self.version_req().to_string(),
            kind: self.kind(),
            optional: self.is_optional(),
            uses_default_features: self.uses_default_features(),
            features: self.features(),
            target: self.platform(),
            rename: self.explicit_name_in_toml().map(|s| s.as_str()),
            registry: registry_id.as_ref().map(|sid| sid.url().as_str()),
            path: self.source_id().local_path(),
        }
        .serialize(s)
    }
}

#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
pub enum DepKind {
    Normal,
    Development,
    Build,
}

impl ser::Serialize for DepKind {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        match *self {
            DepKind::Normal => None,
            DepKind::Development => Some("dev"),
            DepKind::Build => Some("build"),
        }
        .serialize(s)
    }
}

impl Dependency {
    /// Attempt to create a `Dependency` from an entry in the manifest.
    pub fn parse(
        name: impl Into<InternedString>,
        version: Option<&str>,
        source_id: SourceId,
    ) -> CargoResult<Dependency> {
        let name = name.into();
        let (specified_req, version_req) = match version {
            Some(v) => match VersionReq::parse(v) {
                Ok(req) => (true, OptVersionReq::Req(req)),
                Err(err) => {
                    return Err(anyhow::Error::new(err).context(format!(
                        "failed to parse the version requirement `{}` for dependency `{}`",
                        v, name,
                    )))
                }
            },
            None => (false, OptVersionReq::Any),
        };

        let mut ret = Dependency::new_override(name, source_id);
        {
            let ptr = Rc::make_mut(&mut ret.inner);
            ptr.only_match_name = false;
            ptr.req = version_req;
            ptr.specified_req = specified_req;
        }
        Ok(ret)
    }

    pub fn new_override(name: InternedString, source_id: SourceId) -> Dependency {
        assert!(!name.is_empty());
        Dependency {
            inner: Rc::new(Inner {
                name,
                source_id,
                registry_id: None,
                req: OptVersionReq::Any,
                kind: DepKind::Normal,
                only_match_name: true,
                optional: false,
                public: false,
                features: Vec::new(),
                default_features: true,
                specified_req: false,
                platform: None,
                explicit_name_in_toml: None,
            }),
        }
    }

    pub fn version_req(&self) -> &OptVersionReq {
        &self.inner.req
    }

    /// This is the name of this `Dependency` as listed in `Cargo.toml`.
    ///
    /// Or in other words, this is what shows up in the `[dependencies]` section
    /// on the left hand side. This is *not* the name of the package that's
    /// being depended on as the dependency can be renamed. For that use
    /// `package_name` below.
    ///
    /// Both of the dependencies below return `foo` for `name_in_toml`:
    ///
    /// ```toml
    /// [dependencies]
    /// foo = "0.1"
    /// ```
    ///
    /// and ...
    ///
    /// ```toml
    /// [dependencies]
    /// foo = { version = "0.1", package = 'bar' }
    /// ```
    pub fn name_in_toml(&self) -> InternedString {
        self.explicit_name_in_toml().unwrap_or(self.inner.name)
    }

    /// The name of the package that this `Dependency` depends on.
    ///
    /// Usually this is what's written on the left hand side of a dependencies
    /// section, but it can also be renamed via the `package` key.
    ///
    /// Both of the dependencies below return `foo` for `package_name`:
    ///
    /// ```toml
    /// [dependencies]
    /// foo = "0.1"
    /// ```
    ///
    /// and ...
    ///
    /// ```toml
    /// [dependencies]
    /// bar = { version = "0.1", package = 'foo' }
    /// ```
    pub fn package_name(&self) -> InternedString {
        self.inner.name
    }

    pub fn source_id(&self) -> SourceId {
        self.inner.source_id
    }

    pub fn registry_id(&self) -> Option<SourceId> {
        self.inner.registry_id
    }

    pub fn set_registry_id(&mut self, registry_id: SourceId) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).registry_id = Some(registry_id);
        self
    }

    pub fn kind(&self) -> DepKind {
        self.inner.kind
    }

    pub fn is_public(&self) -> bool {
        self.inner.public
    }

    /// Sets whether the dependency is public.
    pub fn set_public(&mut self, public: bool) -> &mut Dependency {
        if public {
            // Setting 'public' only makes sense for normal dependencies
            assert_eq!(self.kind(), DepKind::Normal);
        }
        Rc::make_mut(&mut self.inner).public = public;
        self
    }

    pub fn specified_req(&self) -> bool {
        self.inner.specified_req
    }

    /// If none, this dependencies must be built for all platforms.
    /// If some, it must only be built for the specified platform.
    pub fn platform(&self) -> Option<&Platform> {
        self.inner.platform.as_ref()
    }

    /// The renamed name of this dependency, if any.
    ///
    /// If the `package` key is used in `Cargo.toml` then this returns the same
    /// value as `name_in_toml`.
    pub fn explicit_name_in_toml(&self) -> Option<InternedString> {
        self.inner.explicit_name_in_toml
    }

    pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
        if self.is_public() {
            // Setting 'public' only makes sense for normal dependencies
            assert_eq!(kind, DepKind::Normal);
        }
        Rc::make_mut(&mut self.inner).kind = kind;
        self
    }

    /// Sets the list of features requested for the package.
    pub fn set_features(
        &mut self,
        features: impl IntoIterator<Item = impl Into<InternedString>>,
    ) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
        self
    }

    /// Sets whether the dependency requests default features of the package.
    pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).default_features = default_features;
        self
    }

    /// Sets whether the dependency is optional.
    pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).optional = optional;
        self
    }

    /// Sets the source ID for this dependency.
    pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).source_id = id;
        self
    }

    /// Sets the version requirement for this dependency.
    pub fn set_version_req(&mut self, req: VersionReq) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).req = OptVersionReq::Req(req);
        self
    }

    pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).platform = platform;
        self
    }

    pub fn set_explicit_name_in_toml(
        &mut self,
        name: impl Into<InternedString>,
    ) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
        self
    }

    /// Locks this dependency to depending on the specified package ID.
    pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
        assert_eq!(self.inner.source_id, id.source_id());
        assert!(self.inner.req.matches(id.version()));
        trace!(
            "locking dep from `{}` with `{}` at {} to {}",
            self.package_name(),
            self.version_req(),
            self.source_id(),
            id
        );
        let me = Rc::make_mut(&mut self.inner);
        me.req = OptVersionReq::exact(id.version());

        // Only update the `precise` of this source to preserve other
        // information about dependency's source which may not otherwise be
        // tested during equality/hashing.
        me.source_id = me
            .source_id
            .with_precise(id.source_id().precise().map(|s| s.to_string()));
        self
    }

    /// Returns `true` if this is a "locked" dependency, basically whether it has
    /// an exact version req.
    pub fn is_locked(&self) -> bool {
        self.inner.req.is_exact()
    }

    /// Returns `false` if the dependency is only used to build the local package.
    pub fn is_transitive(&self) -> bool {
        match self.inner.kind {
            DepKind::Normal | DepKind::Build => true,
            DepKind::Development => false,
        }
    }

    pub fn is_build(&self) -> bool {
        matches!(self.inner.kind, DepKind::Build)
    }

    pub fn is_optional(&self) -> bool {
        self.inner.optional
    }

    /// Returns `true` if the default features of the dependency are requested.
    pub fn uses_default_features(&self) -> bool {
        self.inner.default_features
    }
    /// Returns the list of features that are requested by the dependency.
    pub fn features(&self) -> &[InternedString] {
        &self.inner.features
    }

    /// Returns `true` if the package (`sum`) can fulfill this dependency request.
    pub fn matches(&self, sum: &Summary) -> bool {
        self.matches_id(sum.package_id())
    }

    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
        self.package_name() == id.name() && self.version_req().matches(id.version())
    }

    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    pub fn matches_id(&self, id: PackageId) -> bool {
        self.inner.name == id.name()
            && (self.inner.only_match_name
                || (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
    }

    pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
        if self.source_id() != to_replace {
            self
        } else {
            self.set_source_id(replace_with);
            self
        }
    }
}
<commit_msg>simply 'if' block<commit_after>use cargo_platform::Platform;
use log::trace;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
use std::path::PathBuf;
use std::rc::Rc;

use crate::core::{PackageId, SourceId, Summary};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::OptVersionReq;

/// Information about a dependency requested by a Cargo manifest.
/// Cheap to copy.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Dependency {
    inner: Rc<Inner>,
}

/// The data underlying a `Dependency`.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
struct Inner {
    name: InternedString,
    source_id: SourceId,
    /// Source ID for the registry as specified in the manifest.
    ///
    /// This will be None if it is not specified (crates.io dependency).
    /// This is different from `source_id` for example when both a `path` and
    /// `registry` is specified. Or in the case of a crates.io dependency,
    /// `source_id` will be crates.io and this will be None.
    registry_id: Option<SourceId>,
    req: OptVersionReq,
    specified_req: bool,
    kind: DepKind,
    only_match_name: bool,
    explicit_name_in_toml: Option<InternedString>,

    optional: bool,
    public: bool,
    default_features: bool,
    features: Vec<InternedString>,

    // This dependency should be used only for this platform.
    // `None` means *all platforms*.
    platform: Option<Platform>,
}

#[derive(Serialize)]
struct SerializedDependency<'a> {
    name: &'a str,
    source: SourceId,
    req: String,
    kind: DepKind,
    rename: Option<&'a str>,

    optional: bool,
    uses_default_features: bool,
    features: &'a [InternedString],
    target: Option<&'a Platform>,
    /// The registry URL this dependency is from.
    /// If None, then it comes from the default registry (crates.io).
    registry: Option<&'a str>,

    /// The file system path for a local path dependency.
    #[serde(skip_serializing_if = "Option::is_none")]
    path: Option<PathBuf>,
}

impl ser::Serialize for Dependency {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        let registry_id = self.registry_id();
        SerializedDependency {
            name: &*self.package_name(),
            source: self.source_id(),
            req: self.version_req().to_string(),
            kind: self.kind(),
            optional: self.is_optional(),
            uses_default_features: self.uses_default_features(),
            features: self.features(),
            target: self.platform(),
            rename: self.explicit_name_in_toml().map(|s| s.as_str()),
            registry: registry_id.as_ref().map(|sid| sid.url().as_str()),
            path: self.source_id().local_path(),
        }
        .serialize(s)
    }
}

#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
pub enum DepKind {
    Normal,
    Development,
    Build,
}

impl ser::Serialize for DepKind {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        match *self {
            DepKind::Normal => None,
            DepKind::Development => Some("dev"),
            DepKind::Build => Some("build"),
        }
        .serialize(s)
    }
}

impl Dependency {
    /// Attempt to create a `Dependency` from an entry in the manifest.
    pub fn parse(
        name: impl Into<InternedString>,
        version: Option<&str>,
        source_id: SourceId,
    ) -> CargoResult<Dependency> {
        let name = name.into();
        let (specified_req, version_req) = match version {
            Some(v) => match VersionReq::parse(v) {
                Ok(req) => (true, OptVersionReq::Req(req)),
                Err(err) => {
                    return Err(anyhow::Error::new(err).context(format!(
                        "failed to parse the version requirement `{}` for dependency `{}`",
                        v, name,
                    )))
                }
            },
            None => (false, OptVersionReq::Any),
        };

        let mut ret = Dependency::new_override(name, source_id);
        {
            let ptr = Rc::make_mut(&mut ret.inner);
            ptr.only_match_name = false;
            ptr.req = version_req;
            ptr.specified_req = specified_req;
        }
        Ok(ret)
    }

    pub fn new_override(name: InternedString, source_id: SourceId) -> Dependency {
        assert!(!name.is_empty());
        Dependency {
            inner: Rc::new(Inner {
                name,
                source_id,
                registry_id: None,
                req: OptVersionReq::Any,
                kind: DepKind::Normal,
                only_match_name: true,
                optional: false,
                public: false,
                features: Vec::new(),
                default_features: true,
                specified_req: false,
                platform: None,
                explicit_name_in_toml: None,
            }),
        }
    }

    pub fn version_req(&self) -> &OptVersionReq {
        &self.inner.req
    }

    /// This is the name of this `Dependency` as listed in `Cargo.toml`.
    ///
    /// Or in other words, this is what shows up in the `[dependencies]` section
    /// on the left hand side. This is *not* the name of the package that's
    /// being depended on as the dependency can be renamed. For that use
    /// `package_name` below.
    ///
    /// Both of the dependencies below return `foo` for `name_in_toml`:
    ///
    /// ```toml
    /// [dependencies]
    /// foo = "0.1"
    /// ```
    ///
    /// and ...
    ///
    /// ```toml
    /// [dependencies]
    /// foo = { version = "0.1", package = 'bar' }
    /// ```
    pub fn name_in_toml(&self) -> InternedString {
        self.explicit_name_in_toml().unwrap_or(self.inner.name)
    }

    /// The name of the package that this `Dependency` depends on.
    ///
    /// Usually this is what's written on the left hand side of a dependencies
    /// section, but it can also be renamed via the `package` key.
    ///
    /// Both of the dependencies below return `foo` for `package_name`:
    ///
    /// ```toml
    /// [dependencies]
    /// foo = "0.1"
    /// ```
    ///
    /// and ...
    ///
    /// ```toml
    /// [dependencies]
    /// bar = { version = "0.1", package = 'foo' }
    /// ```
    pub fn package_name(&self) -> InternedString {
        self.inner.name
    }

    pub fn source_id(&self) -> SourceId {
        self.inner.source_id
    }

    pub fn registry_id(&self) -> Option<SourceId> {
        self.inner.registry_id
    }

    pub fn set_registry_id(&mut self, registry_id: SourceId) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).registry_id = Some(registry_id);
        self
    }

    pub fn kind(&self) -> DepKind {
        self.inner.kind
    }

    pub fn is_public(&self) -> bool {
        self.inner.public
    }

    /// Sets whether the dependency is public.
    pub fn set_public(&mut self, public: bool) -> &mut Dependency {
        if public {
            // Setting 'public' only makes sense for normal dependencies
            assert_eq!(self.kind(), DepKind::Normal);
        }
        Rc::make_mut(&mut self.inner).public = public;
        self
    }

    pub fn specified_req(&self) -> bool {
        self.inner.specified_req
    }

    /// If none, this dependencies must be built for all platforms.
    /// If some, it must only be built for the specified platform.
    pub fn platform(&self) -> Option<&Platform> {
        self.inner.platform.as_ref()
    }

    /// The renamed name of this dependency, if any.
    ///
    /// If the `package` key is used in `Cargo.toml` then this returns the same
    /// value as `name_in_toml`.
    pub fn explicit_name_in_toml(&self) -> Option<InternedString> {
        self.inner.explicit_name_in_toml
    }

    pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
        if self.is_public() {
            // Setting 'public' only makes sense for normal dependencies
            assert_eq!(kind, DepKind::Normal);
        }
        Rc::make_mut(&mut self.inner).kind = kind;
        self
    }

    /// Sets the list of features requested for the package.
    pub fn set_features(
        &mut self,
        features: impl IntoIterator<Item = impl Into<InternedString>>,
    ) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
        self
    }

    /// Sets whether the dependency requests default features of the package.
    pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).default_features = default_features;
        self
    }

    /// Sets whether the dependency is optional.
    pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).optional = optional;
        self
    }

    /// Sets the source ID for this dependency.
    pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).source_id = id;
        self
    }

    /// Sets the version requirement for this dependency.
    pub fn set_version_req(&mut self, req: VersionReq) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).req = OptVersionReq::Req(req);
        self
    }

    pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).platform = platform;
        self
    }

    pub fn set_explicit_name_in_toml(
        &mut self,
        name: impl Into<InternedString>,
    ) -> &mut Dependency {
        Rc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
        self
    }

    /// Locks this dependency to depending on the specified package ID.
    pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
        assert_eq!(self.inner.source_id, id.source_id());
        assert!(self.inner.req.matches(id.version()));
        trace!(
            "locking dep from `{}` with `{}` at {} to {}",
            self.package_name(),
            self.version_req(),
            self.source_id(),
            id
        );
        let me = Rc::make_mut(&mut self.inner);
        me.req = OptVersionReq::exact(id.version());

        // Only update the `precise` of this source to preserve other
        // information about dependency's source which may not otherwise be
        // tested during equality/hashing.
        me.source_id = me
            .source_id
            .with_precise(id.source_id().precise().map(|s| s.to_string()));
        self
    }

    /// Returns `true` if this is a "locked" dependency, basically whether it has
    /// an exact version req.
    pub fn is_locked(&self) -> bool {
        self.inner.req.is_exact()
    }

    /// Returns `false` if the dependency is only used to build the local package.
    pub fn is_transitive(&self) -> bool {
        match self.inner.kind {
            DepKind::Normal | DepKind::Build => true,
            DepKind::Development => false,
        }
    }

    pub fn is_build(&self) -> bool {
        matches!(self.inner.kind, DepKind::Build)
    }

    pub fn is_optional(&self) -> bool {
        self.inner.optional
    }

    /// Returns `true` if the default features of the dependency are requested.
    pub fn uses_default_features(&self) -> bool {
        self.inner.default_features
    }
    /// Returns the list of features that are requested by the dependency.
    pub fn features(&self) -> &[InternedString] {
        &self.inner.features
    }

    /// Returns `true` if the package (`sum`) can fulfill this dependency request.
    pub fn matches(&self, sum: &Summary) -> bool {
        self.matches_id(sum.package_id())
    }

    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
        self.package_name() == id.name() && self.version_req().matches(id.version())
    }

    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    pub fn matches_id(&self, id: PackageId) -> bool {
        self.inner.name == id.name()
            && (self.inner.only_match_name
                || (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
    }

    pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
        if self.source_id() == to_replace {
            self.set_source_id(replace_with);
        }
        self
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually drop the handle.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // We want to read at most `count` bytes
            let mut bytes = vec![0; count as usize];
            let result = handle.file.read(&mut bytes).map(|c| c as i64);
            // If reading to `bytes` did not fail, we write those bytes to the buffer.
            if result.is_ok() {
                this.memory.write_bytes(buf, bytes)?;
            }
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| c as i64);
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<commit_msg>Check for usize to i64 overflows<commit_after>use std::collections::HashMap;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually drop the handle.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Reading zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // We want to read at most `count` bytes
            let mut bytes = vec![0; count as usize];
            let result = handle.file.read(&mut bytes);

            if let Ok(c) = result {
                // Check that we read less than `i64::MAX` bytes.
                if c > (i64::max_value() as usize) {
                    throw_unsup_format!("Number of read bytes {} is larger than the maximum value", c);
                }
                // If reading to `bytes` did not fail, we write those bytes to the buffer.
                this.memory.write_bytes(buf, bytes)?
            }

            this.try_unwrap_io_result(result.map(|c| c as i64))
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let count = this.read_scalar(count_op)?.to_usize(&*this.tcx)?;
        // Writing zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes);

            if let Ok(c) = result {
                // Check that we wrote less than `i64::MAX` bytes.
                if c > (i64::max_value() as usize) {
                    throw_unsup_format!("Number of written bytes {} is larger than the maximum value", c);
                }
            }

            this.try_unwrap_io_result(result.map(|c| c as i64))
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<|endoftext|>


<commit_before>use std::slice;
use alloc::borrow::Cow;
use alloc::fmt;
use alloc::vec::Vec;

#[cfg(feature = "compression")]
use flate2::{Decompress, FlushDecompress};

use goblin::{elf, strtab};
#[cfg(feature = "compression")]
use goblin::container;
#[cfg(feature = "compression")]
use scroll::{self, Pread};
#[cfg(feature = "compression")]
use scroll::ctx::TryFromCtx;

use {DebugFileInfo, Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind,
     SymbolMap};

/// An ELF object file.
#[derive(Debug)]
pub struct ElfFile<'data> {
    elf: elf::Elf<'data>,
    data: &'data [u8],
}

/// An iterator over the segments of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    iter: slice::Iter<'file, elf::ProgramHeader>,
}

/// A segment of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSegment<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    segment: &'file elf::ProgramHeader,
}

/// An iterator over the sections of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSectionIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    iter: slice::Iter<'file, elf::SectionHeader>,
}

/// A section of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSection<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    section: &'file elf::SectionHeader,
}

/// An iterator over the symbols of an `ElfFile`.
pub struct ElfSymbolIterator<'data, 'file>
where
    'data: 'file,
{
    strtab: &'file strtab::Strtab<'data>,
    symbols: elf::sym::SymIterator<'data>,
    section_kinds: Vec<SectionKind>,
}

impl<'data> ElfFile<'data> {
    /// Get the ELF headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn elf(&self) -> &elf::Elf<'data> {
        &self.elf
    }

    /// Parse the raw ELF file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let elf = elf::Elf::parse(data).map_err(|_| "Could not parse ELF header")?;
        Ok(ElfFile { elf, data })
    }

    #[cfg(feature = "compression")]
    fn maybe_decompress_data(&self, header: &elf::SectionHeader) -> Cow<'data, [u8]> {
        let data = &self.data[header.sh_offset as usize..][..header.sh_size as usize];
        if (header.sh_flags & elf::section_header::SHF_COMPRESSED as u64) == 0 {
            Cow::Borrowed(data)
        } else {
            let container = match self.elf.header.container() {
                Ok(c) => c,
                Err(_) => return Cow::Borrowed(data),
            };
            let endianness = match self.elf.header.endianness() {
                Ok(e) => e,
                Err(_) => return Cow::Borrowed(data),
            };
            let ctx = container::Ctx::new(container, endianness);
            let (compression_type, uncompressed_size, compressed_data) =
                match elf::compression_header::CompressionHeader::try_from_ctx(data, ctx) {
                    Ok((chdr, size)) => (chdr.ch_type, chdr.ch_size, &data[size..]),
                    Err(_) => return Cow::Borrowed(data),
                };
            if compression_type != elf::compression_header::ELFCOMPRESS_ZLIB {
                return Cow::Borrowed(data);
            }

            let mut decompressed = Vec::with_capacity(uncompressed_size as usize);
            let mut decompress = Decompress::new(true);
            if let Err(_) = decompress.decompress_vec(
                compressed_data, &mut decompressed, FlushDecompress::Finish) {
                return Cow::Borrowed(data);
            }
            Cow::Owned(decompressed)
        }
    }

    #[cfg(not(feature = "compression"))]
    fn maybe_decompress_data(&self, header: &elf::SectionHeader) -> Cow<'data, [u8]> {
        let data = &self.data[header.sh_offset as usize..][..header.sh_size as usize];
        Cow::Borrowed(data)
    }

    #[cfg(feature = "compression")]
    /// Try GNU-style "ZLIB" header decompression.
    fn maybe_decompress_data_gnu(&self, data: Cow<'data, [u8]>) -> Cow<'data, [u8]> {
        // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally
        // huge allocations. This also reduces the chance of accidentally matching on a
        // .debug_str that happens to start with "ZLIB".
        if data.len() < 12 || &data[..8] != b"ZLIB\0\0\0\0" {
            return data;
        }
        let uncompressed_size: u32 = data.pread_with(8, scroll::BE).unwrap();
        let mut decompressed = Vec::with_capacity(uncompressed_size as usize);
        let mut decompress = Decompress::new(true);
        if let Err(_) = decompress.decompress_vec(
            &data[12..], &mut decompressed, FlushDecompress::Finish) {
            return data;
        }
        Cow::Owned(decompressed)
    }

    #[cfg(feature = "compression")]
    /// Try GNU-style "ZLIB" header decompression.
    fn try_zdebug_section_data(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        if !section_name.starts_with(".debug_") {
            return None;
        }
        let z_name = format!(".zdebug_{}", &section_name[7..]);
        // Note that we accept data in .zdebug_ that isn't actually compressed.
        self.section_data_by_name(&z_name).map(|data| self.maybe_decompress_data_gnu(data))
    }

    #[cfg(not(feature = "compression"))]
    fn try_zdebug_section_data(&self, _section_name: &str) -> Option<Cow<'data, [u8]>> {
        None
    }
}

impl<'data, 'file> Object<'data, 'file> for ElfFile<'data>
where
    'data: 'file,
{
    type Segment = ElfSegment<'data, 'file>;
    type SegmentIterator = ElfSegmentIterator<'data, 'file>;
    type Section = ElfSection<'data, 'file>;
    type SectionIterator = ElfSectionIterator<'data, 'file>;
    type SymbolIterator = ElfSymbolIterator<'data, 'file>;

    fn machine(&self) -> Machine {
        match self.elf.header.e_machine {
            elf::header::EM_ARM => Machine::Arm,
            elf::header::EM_AARCH64 => Machine::Arm64,
            elf::header::EM_386 => Machine::X86,
            elf::header::EM_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> ElfSegmentIterator<'data, 'file> {
        ElfSegmentIterator {
            file: self,
            iter: self.elf.program_headers.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        for header in &self.elf.section_headers {
            if let Some(Ok(name)) = self.elf.shdr_strtab.get(header.sh_name) {
                if name == section_name {
                    return Some(self.maybe_decompress_data(header));
                }
            }
        }
        self.try_zdebug_section_data(section_name)
    }

    fn sections(&'file self) -> ElfSectionIterator<'data, 'file> {
        ElfSectionIterator {
            file: self,
            iter: self.elf.section_headers.iter(),
        }
    }

    fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file> {
        ElfSymbolIterator {
            strtab: &self.elf.strtab,
            symbols: self.elf.syms.iter(),
            section_kinds: self.sections().map(|x| x.kind()).collect(),
        }
    }

    fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file> {
        ElfSymbolIterator {
            strtab: &self.elf.dynstrtab,
            symbols: self.elf.dynsyms.iter(),
            section_kinds: self.sections().map(|x| x.kind()).collect(),
        }
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        let mut symbols: Vec<_> = self.symbols().filter(SymbolMap::filter).collect();
        symbols.sort_by_key(|x| x.address);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        self.elf.little_endian
    }

    fn has_debug_symbols(&self) -> bool {
        self.section_data_by_name(".debug_info").is_some()
    }

    fn debug_file_info(&self) -> Option<DebugFileInfo> { None }

    fn entry(&self) -> u64 {
        self.elf.entry
    }
}

impl<'data, 'file> Iterator for ElfSegmentIterator<'data, 'file> {
    type Item = ElfSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(segment) = self.iter.next() {
            if segment.p_type == elf::program_header::PT_LOAD {
                return Some(ElfSegment {
                    file: self.file,
                    segment,
                });
            }
        }
        None
    }
}

impl<'data, 'file> ObjectSegment<'data> for ElfSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.segment.p_vaddr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.segment.p_memsz
    }

    fn data(&self) -> &'data [u8] {
        &self.file.data[self.segment.p_offset as usize..][..self.segment.p_filesz as usize]
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        None
    }
}

impl<'data, 'file> Iterator for ElfSectionIterator<'data, 'file> {
    type Item = ElfSection<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| {
            ElfSection {
                file: self.file,
                section,
            }
        })
    }
}

impl<'data, 'file> ObjectSection<'data> for ElfSection<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.section.sh_addr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.section.sh_size
    }

    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(if self.section.sh_type == elf::section_header::SHT_NOBITS {
            &[]
        } else {
            &self.file.data[self.section.sh_offset as usize..][..self.section.sh_size as usize]
        })
    }

    fn name(&self) -> Option<&str> {
        self.file
            .elf
            .shdr_strtab
            .get(self.section.sh_name)
            .and_then(Result::ok)
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        None
    }

    fn kind(&self) -> SectionKind {
        match self.section.sh_type {
            elf::section_header::SHT_PROGBITS => {
                if self.section.sh_flags & u64::from(elf::section_header::SHF_ALLOC) == 0 {
                    SectionKind::Unknown
                } else if self.section.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0
                {
                    SectionKind::Text
                } else if self.section.sh_flags & u64::from(elf::section_header::SHF_WRITE) != 0 {
                    SectionKind::Data
                } else {
                    SectionKind::ReadOnlyData
                }
            }
            elf::section_header::SHT_NOBITS => SectionKind::UninitializedData,
            _ => SectionKind::Unknown,
        }
    }
}

impl<'data, 'file> fmt::Debug for ElfSymbolIterator<'data, 'file> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ElfSymbolIterator").finish()
    }
}

impl<'data, 'file> Iterator for ElfSymbolIterator<'data, 'file> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        self.symbols.next().map(|symbol| {
            let name = self.strtab.get(symbol.st_name).and_then(Result::ok);
            let kind = match elf::sym::st_type(symbol.st_info) {
                elf::sym::STT_OBJECT => SymbolKind::Data,
                elf::sym::STT_FUNC => SymbolKind::Text,
                elf::sym::STT_SECTION => SymbolKind::Section,
                elf::sym::STT_FILE => SymbolKind::File,
                elf::sym::STT_COMMON => SymbolKind::Common,
                elf::sym::STT_TLS => SymbolKind::Tls,
                _ => SymbolKind::Unknown,
            };
            let section_kind = if symbol.st_shndx == elf::section_header::SHN_UNDEF as usize {
                None
            } else {
                self.section_kinds.get(symbol.st_shndx).cloned()
            };
            Symbol {
                name,
                address: symbol.st_value,
                size: symbol.st_size,
                kind,
                section_kind,
                global: elf::sym::st_bind(symbol.st_info) != elf::sym::STB_LOCAL,
            }
        })
    }
}
<commit_msg>Don't decompress in ElfFile::has_debug_symbols()<commit_after>use std::slice;
use alloc::borrow::Cow;
use alloc::fmt;
use alloc::vec::Vec;

#[cfg(feature = "compression")]
use flate2::{Decompress, FlushDecompress};

use goblin::{elf, strtab};
#[cfg(feature = "compression")]
use goblin::container;
#[cfg(feature = "compression")]
use scroll::{self, Pread};
#[cfg(feature = "compression")]
use scroll::ctx::TryFromCtx;

use {DebugFileInfo, Machine, Object, ObjectSection, ObjectSegment, SectionKind, Symbol, SymbolKind,
     SymbolMap};

/// An ELF object file.
#[derive(Debug)]
pub struct ElfFile<'data> {
    elf: elf::Elf<'data>,
    data: &'data [u8],
}

/// An iterator over the segments of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSegmentIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    iter: slice::Iter<'file, elf::ProgramHeader>,
}

/// A segment of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSegment<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    segment: &'file elf::ProgramHeader,
}

/// An iterator over the sections of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSectionIterator<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    iter: slice::Iter<'file, elf::SectionHeader>,
}

/// A section of an `ElfFile`.
#[derive(Debug)]
pub struct ElfSection<'data, 'file>
where
    'data: 'file,
{
    file: &'file ElfFile<'data>,
    section: &'file elf::SectionHeader,
}

/// An iterator over the symbols of an `ElfFile`.
pub struct ElfSymbolIterator<'data, 'file>
where
    'data: 'file,
{
    strtab: &'file strtab::Strtab<'data>,
    symbols: elf::sym::SymIterator<'data>,
    section_kinds: Vec<SectionKind>,
}

impl<'data> ElfFile<'data> {
    /// Get the ELF headers of the file.
    // TODO: this is temporary to allow access to features this crate doesn't provide yet
    #[inline]
    pub fn elf(&self) -> &elf::Elf<'data> {
        &self.elf
    }

    /// Parse the raw ELF file data.
    pub fn parse(data: &'data [u8]) -> Result<Self, &'static str> {
        let elf = elf::Elf::parse(data).map_err(|_| "Could not parse ELF header")?;
        Ok(ElfFile { elf, data })
    }

    #[cfg(feature = "compression")]
    fn maybe_decompress_data(&self, header: &elf::SectionHeader) -> Cow<'data, [u8]> {
        let data = &self.data[header.sh_offset as usize..][..header.sh_size as usize];
        if (header.sh_flags & elf::section_header::SHF_COMPRESSED as u64) == 0 {
            Cow::Borrowed(data)
        } else {
            let container = match self.elf.header.container() {
                Ok(c) => c,
                Err(_) => return Cow::Borrowed(data),
            };
            let endianness = match self.elf.header.endianness() {
                Ok(e) => e,
                Err(_) => return Cow::Borrowed(data),
            };
            let ctx = container::Ctx::new(container, endianness);
            let (compression_type, uncompressed_size, compressed_data) =
                match elf::compression_header::CompressionHeader::try_from_ctx(data, ctx) {
                    Ok((chdr, size)) => (chdr.ch_type, chdr.ch_size, &data[size..]),
                    Err(_) => return Cow::Borrowed(data),
                };
            if compression_type != elf::compression_header::ELFCOMPRESS_ZLIB {
                return Cow::Borrowed(data);
            }

            let mut decompressed = Vec::with_capacity(uncompressed_size as usize);
            let mut decompress = Decompress::new(true);
            if let Err(_) = decompress.decompress_vec(
                compressed_data, &mut decompressed, FlushDecompress::Finish) {
                return Cow::Borrowed(data);
            }
            Cow::Owned(decompressed)
        }
    }

    #[cfg(not(feature = "compression"))]
    fn maybe_decompress_data(&self, header: &elf::SectionHeader) -> Cow<'data, [u8]> {
        let data = &self.data[header.sh_offset as usize..][..header.sh_size as usize];
        Cow::Borrowed(data)
    }

    #[cfg(feature = "compression")]
    /// Try GNU-style "ZLIB" header decompression.
    fn maybe_decompress_data_gnu(&self, data: Cow<'data, [u8]>) -> Cow<'data, [u8]> {
        // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally
        // huge allocations. This also reduces the chance of accidentally matching on a
        // .debug_str that happens to start with "ZLIB".
        if data.len() < 12 || &data[..8] != b"ZLIB\0\0\0\0" {
            return data;
        }
        let uncompressed_size: u32 = data.pread_with(8, scroll::BE).unwrap();
        let mut decompressed = Vec::with_capacity(uncompressed_size as usize);
        let mut decompress = Decompress::new(true);
        if let Err(_) = decompress.decompress_vec(
            &data[12..], &mut decompressed, FlushDecompress::Finish) {
            return data;
        }
        Cow::Owned(decompressed)
    }

    #[cfg(feature = "compression")]
    /// Try GNU-style "ZLIB" header decompression.
    fn try_zdebug_section_data(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        if !section_name.starts_with(".debug_") {
            return None;
        }
        let z_name = format!(".zdebug_{}", &section_name[7..]);
        // Note that we accept data in .zdebug_ that isn't actually compressed.
        self.section_data_by_name(&z_name).map(|data| self.maybe_decompress_data_gnu(data))
    }

    #[cfg(not(feature = "compression"))]
    fn try_zdebug_section_data(&self, _section_name: &str) -> Option<Cow<'data, [u8]>> {
        None
    }
}

impl<'data, 'file> Object<'data, 'file> for ElfFile<'data>
where
    'data: 'file,
{
    type Segment = ElfSegment<'data, 'file>;
    type SegmentIterator = ElfSegmentIterator<'data, 'file>;
    type Section = ElfSection<'data, 'file>;
    type SectionIterator = ElfSectionIterator<'data, 'file>;
    type SymbolIterator = ElfSymbolIterator<'data, 'file>;

    fn machine(&self) -> Machine {
        match self.elf.header.e_machine {
            elf::header::EM_ARM => Machine::Arm,
            elf::header::EM_AARCH64 => Machine::Arm64,
            elf::header::EM_386 => Machine::X86,
            elf::header::EM_X86_64 => Machine::X86_64,
            _ => Machine::Other,
        }
    }

    fn segments(&'file self) -> ElfSegmentIterator<'data, 'file> {
        ElfSegmentIterator {
            file: self,
            iter: self.elf.program_headers.iter(),
        }
    }

    fn section_data_by_name(&self, section_name: &str) -> Option<Cow<'data, [u8]>> {
        for header in &self.elf.section_headers {
            if let Some(Ok(name)) = self.elf.shdr_strtab.get(header.sh_name) {
                if name == section_name {
                    return Some(self.maybe_decompress_data(header));
                }
            }
        }
        self.try_zdebug_section_data(section_name)
    }

    fn sections(&'file self) -> ElfSectionIterator<'data, 'file> {
        ElfSectionIterator {
            file: self,
            iter: self.elf.section_headers.iter(),
        }
    }

    fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file> {
        ElfSymbolIterator {
            strtab: &self.elf.strtab,
            symbols: self.elf.syms.iter(),
            section_kinds: self.sections().map(|x| x.kind()).collect(),
        }
    }

    fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file> {
        ElfSymbolIterator {
            strtab: &self.elf.dynstrtab,
            symbols: self.elf.dynsyms.iter(),
            section_kinds: self.sections().map(|x| x.kind()).collect(),
        }
    }

    fn symbol_map(&self) -> SymbolMap<'data> {
        let mut symbols: Vec<_> = self.symbols().filter(SymbolMap::filter).collect();
        symbols.sort_by_key(|x| x.address);
        SymbolMap { symbols }
    }

    #[inline]
    fn is_little_endian(&self) -> bool {
        self.elf.little_endian
    }

    fn has_debug_symbols(&self) -> bool {
        for header in &self.elf.section_headers {
            if let Some(Ok(name)) = self.elf.shdr_strtab.get(header.sh_name) {
                if name == ".debug_info" || name == ".zdebug_info" {
                    return true;
                }
            }
        }
        false
    }

    fn debug_file_info(&self) -> Option<DebugFileInfo> { None }

    fn entry(&self) -> u64 {
        self.elf.entry
    }
}

impl<'data, 'file> Iterator for ElfSegmentIterator<'data, 'file> {
    type Item = ElfSegment<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(segment) = self.iter.next() {
            if segment.p_type == elf::program_header::PT_LOAD {
                return Some(ElfSegment {
                    file: self.file,
                    segment,
                });
            }
        }
        None
    }
}

impl<'data, 'file> ObjectSegment<'data> for ElfSegment<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.segment.p_vaddr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.segment.p_memsz
    }

    fn data(&self) -> &'data [u8] {
        &self.file.data[self.segment.p_offset as usize..][..self.segment.p_filesz as usize]
    }

    #[inline]
    fn name(&self) -> Option<&str> {
        None
    }
}

impl<'data, 'file> Iterator for ElfSectionIterator<'data, 'file> {
    type Item = ElfSection<'data, 'file>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|section| {
            ElfSection {
                file: self.file,
                section,
            }
        })
    }
}

impl<'data, 'file> ObjectSection<'data> for ElfSection<'data, 'file> {
    #[inline]
    fn address(&self) -> u64 {
        self.section.sh_addr
    }

    #[inline]
    fn size(&self) -> u64 {
        self.section.sh_size
    }

    fn data(&self) -> Cow<'data, [u8]> {
        Cow::from(if self.section.sh_type == elf::section_header::SHT_NOBITS {
            &[]
        } else {
            &self.file.data[self.section.sh_offset as usize..][..self.section.sh_size as usize]
        })
    }

    fn name(&self) -> Option<&str> {
        self.file
            .elf
            .shdr_strtab
            .get(self.section.sh_name)
            .and_then(Result::ok)
    }

    #[inline]
    fn segment_name(&self) -> Option<&str> {
        None
    }

    fn kind(&self) -> SectionKind {
        match self.section.sh_type {
            elf::section_header::SHT_PROGBITS => {
                if self.section.sh_flags & u64::from(elf::section_header::SHF_ALLOC) == 0 {
                    SectionKind::Unknown
                } else if self.section.sh_flags & u64::from(elf::section_header::SHF_EXECINSTR) != 0
                {
                    SectionKind::Text
                } else if self.section.sh_flags & u64::from(elf::section_header::SHF_WRITE) != 0 {
                    SectionKind::Data
                } else {
                    SectionKind::ReadOnlyData
                }
            }
            elf::section_header::SHT_NOBITS => SectionKind::UninitializedData,
            _ => SectionKind::Unknown,
        }
    }
}

impl<'data, 'file> fmt::Debug for ElfSymbolIterator<'data, 'file> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ElfSymbolIterator").finish()
    }
}

impl<'data, 'file> Iterator for ElfSymbolIterator<'data, 'file> {
    type Item = Symbol<'data>;

    fn next(&mut self) -> Option<Self::Item> {
        self.symbols.next().map(|symbol| {
            let name = self.strtab.get(symbol.st_name).and_then(Result::ok);
            let kind = match elf::sym::st_type(symbol.st_info) {
                elf::sym::STT_OBJECT => SymbolKind::Data,
                elf::sym::STT_FUNC => SymbolKind::Text,
                elf::sym::STT_SECTION => SymbolKind::Section,
                elf::sym::STT_FILE => SymbolKind::File,
                elf::sym::STT_COMMON => SymbolKind::Common,
                elf::sym::STT_TLS => SymbolKind::Tls,
                _ => SymbolKind::Unknown,
            };
            let section_kind = if symbol.st_shndx == elf::section_header::SHN_UNDEF as usize {
                None
            } else {
                self.section_kinds.get(symbol.st_shndx).cloned()
            };
            Symbol {
                name,
                address: symbol.st_value,
                size: symbol.st_size,
                kind,
                section_kind,
                global: elf::sym::st_bind(symbol.st_info) != elf::sym::STB_LOCAL,
            }
        })
    }
}
<|endoftext|>