<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-feature-values`][font-feature-values] at-rule.
//!
//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule

use Atom;
use computed_values::font_family::FamilyName;
use cssparser::{AtRuleParser, AtRuleType, BasicParseError, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier};
use error_reporting::{ContextualParseError, ParseErrorReporter};
use parser::{ParserContext, ParserErrorContext, Parse};
use selectors::parser::SelectorParseError;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
use style_traits::{ParseError, StyleParseError, ToCss};
use stylesheets::CssRuleType;

/// A @font-feature-values block declaration.
/// It is `<ident>: <integer>+`.
/// This struct can take 3 value types.
/// - `SingleValue` is to keep just one unsigned integer value.
/// - `PairValues` is to keep one or two unsigned integer values.
/// - `VectorValues` is to keep a list of unsigned integer values.
#[derive(Clone, Debug, PartialEq)]
pub struct FFVDeclaration<T> {
    /// An `<ident>` for declaration name.
    pub name: Atom,
    /// An `<integer>+` for declaration value.
    pub value: T,
}

impl<T: ToCss> ToCss for FFVDeclaration<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        serialize_identifier(&self.name.to_string(), dest)?;
        dest.write_str(": ")?;
        self.value.to_css(dest)?;
        dest.write_str(";")
    }
}

/// A @font-feature-values block declaration value that keeps one value.
#[derive(Clone, Debug, PartialEq)]
pub struct SingleValue(pub u32);

impl Parse for SingleValue {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<SingleValue, ParseError<'i>> {
        match *input.next()? {
            Token::Number { int_value: Some(v), .. } if v >= 0 => Ok(SingleValue(v as u32)),
            ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
        }
    }
}

impl ToCss for SingleValue {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)
    }
}

/// A @font-feature-values block declaration value that keeps one or two values.
#[derive(Clone, Debug, PartialEq)]
pub struct PairValues(pub u32, pub Option<u32>);

impl Parse for PairValues {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<PairValues, ParseError<'i>> {
        let first = match *input.next()? {
            Token::Number { int_value: Some(a), .. } if a >= 0 => a as u32,
            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
        };
        match input.next() {
            Ok(&Token::Number { int_value: Some(b), .. }) if b >= 0 => {
                Ok(PairValues(first, Some(b as u32)))
            }
            // It can't be anything other than number.
            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
            // It can be just one value.
            Err(_) => Ok(PairValues(first, None))
        }
    }
}

impl ToCss for PairValues {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)?;
        if let Some(second) = self.1 {
            write!(dest, " {}", second)?;
        }
        Ok(())
    }
}

/// A @font-feature-values block declaration value that keeps a list of values.
#[derive(Clone, Debug, PartialEq)]
pub struct VectorValues(pub Vec<u32>);

impl Parse for VectorValues {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<VectorValues, ParseError<'i>> {
        let mut vec = vec![];
        loop {
            match input.next() {
                Ok(&Token::Number { int_value: Some(a), .. }) if a >= 0 => {
                    vec.push(a as u32);
                },
                // It can't be anything other than number.
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(_) => break,
            }
        }

        if vec.len() == 0 {
            return Err(BasicParseError::EndOfInput.into());
        }

        Ok(VectorValues(vec))
    }
}

impl ToCss for VectorValues {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        let first = iter.next();
        if let Some(first) = first {
            write!(dest, "{}", first)?;
            for value in iter {
                dest.write_str(" ")?;
                write!(dest, "{}", value)?;
            }
        }
        Ok(())
    }
}

/// Parses a list of `FamilyName`s.
pub fn parse_family_name_list<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                  -> Result<Vec<FamilyName>, ParseError<'i>> {
    input.parse_comma_separated(|i| FamilyName::parse(context, i)).map_err(|e| e.into())
}

/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.
/// (`<ident>: <integer>+`)
struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
    context: &'a ParserContext<'b>,
    declarations: &'a mut Vec<FFVDeclaration<T>>,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
    type Prelude = ();
    type AtRule = ();
    type Error = SelectorParseError<'i, StyleParseError<'i>>;
}

impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
    where T: Parse
{
    type Declaration = ();
    type Error = SelectorParseError<'i, StyleParseError<'i>>;

    fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                       -> Result<(), ParseError<'i>> {
        let value = input.parse_entirely(|i| T::parse(self.context, i))?;
        let new = FFVDeclaration {
            name: Atom::from(&*name),
            value: value,
        };
        update_or_push(&mut self.declarations, new);
        Ok(())
    }
}

macro_rules! font_feature_values_blocks {
    (
        blocks = [
            $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident: $ty: ty, )*
        ]
    ) => {
        /// The [`@font-feature-values`][font-feature-values] at-rule.
        ///
        /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFeatureValuesRule {
            /// Font family list for @font-feature-values rule.
            /// Family names cannot contain generic families. FamilyName
            /// also accepts only non-generic names.
            pub family_names: Vec<FamilyName>,
            $(
                #[$doc]
                pub $ident: Vec<FFVDeclaration<$ty>>,
            )*
            /// The line and column of the rule's source code.
            pub source_location: SourceLocation,
        }

        impl FontFeatureValuesRule {
            /// Creates an empty FontFeatureValuesRule with given location and family name list.
            fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
                FontFeatureValuesRule {
                    family_names: family_names,
                    $(
                        $ident: vec![],
                    )*
                    source_location: location,
                }
            }

            /// Parses a `FontFeatureValuesRule`.
            pub fn parse<R>(context: &ParserContext,
                            error_context: &ParserErrorContext<R>,
                            input: &mut Parser,
                            family_names: Vec<FamilyName>,
                            location: SourceLocation)
                            -> FontFeatureValuesRule
                where R: ParseErrorReporter
            {
                let mut rule = FontFeatureValuesRule::new(family_names, location);

                {
                    let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
                        context: context,
                        error_context: error_context,
                        rule: &mut rule,
                    });
                    while let Some(result) = iter.next() {
                        if let Err(err) = result {
                            let error = ContextualParseError::UnsupportedRule(err.slice, err.error);
                            context.log_css_error(error_context, err.location, error);
                        }
                    }
                }
                rule
            }

            /// Prints font family names.
            pub fn font_family_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                let mut iter = self.family_names.iter();
                iter.next().unwrap().to_css(dest)?;
                for val in iter {
                    dest.write_str(", ")?;
                    val.to_css(dest)?;
                }
                Ok(())
            }

            /// Prints inside of `@font-feature-values` block.
            pub fn value_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                $(
                    if self.$ident.len() > 0 {
                        dest.write_str(concat!("@", $name, " {\n"))?;
                        let iter = self.$ident.iter();
                        for val in iter {
                            val.to_css(dest)?;
                            dest.write_str("\n")?
                        }
                        dest.write_str("}\n")?
                    }
                )*
                Ok(())
            }
        }

        impl ToCssWithGuard for FontFeatureValuesRule {
            fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
                where W: fmt::Write
            {
                dest.write_str("@font-feature-values ")?;
                self.font_family_to_css(dest)?;
                dest.write_str(" {\n")?;
                self.value_to_css(dest)?;
                dest.write_str("}")
            }
        }

        /// Updates with new value if same `ident` exists, otherwise pushes to the vector.
        fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
            let position = vec.iter().position(|ref val| val.name == element.name);
            if let Some(index) = position {
                vec[index].value = element.value;
            } else {
                vec.push(element);
            }
        }

        /// Keeps the information about block type like @swash, @styleset etc.
        enum BlockType {
            $(
                $ident_camel,
            )*
        }

        /// Parser for `FontFeatureValuesRule`. Parses all blocks
        /// <feature-type> {
        ///   <feature-value-declaration-list>
        /// }
        /// <feature-type> = @stylistic | @historical-forms | @styleset |
        /// @character-variant | @swash | @ornaments | @annotation
        struct FontFeatureValuesRuleParser<'a, R: 'a> {
            context: &'a ParserContext<'a>,
            error_context: &'a ParserErrorContext<'a, R>,
            rule: &'a mut FontFeatureValuesRule,
        }

        /// Default methods reject all qualified rules.
        impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
            type Prelude = ();
            type QualifiedRule = ();
            type Error = SelectorParseError<'i, StyleParseError<'i>>;
        }

        impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
            type Prelude = BlockType;
            type AtRule = ();
            type Error = SelectorParseError<'i, StyleParseError<'i>>;

            fn parse_prelude<'t>(&mut self,
                                 name: CowRcStr<'i>,
                                 _input: &mut Parser<'i, 't>)
                                 -> Result<AtRuleType<Self::Prelude, Self::AtRule>, ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name => Ok(AtRuleType::WithBlock(BlockType::$ident_camel)),
                    )*
                    _ => Err(BasicParseError::AtRuleBodyInvalid.into()),
                }
            }

            fn parse_block<'t>(
                &mut self,
                prelude: Self::Prelude,
                input: &mut Parser<'i, 't>
            ) -> Result<Self::AtRule, ParseError<'i>> {
                debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
                match prelude {
                    $(
                        BlockType::$ident_camel => {
                            let parser = FFVDeclarationsParser {
                                context: &self.context,
                                declarations: &mut self.rule.$ident,
                            };

                            let mut iter = DeclarationListParser::new(input, parser);
                            while let Some(declaration) = iter.next() {
                                if let Err(err) = declaration {
                                    let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
                                        err.slice, err.error);
                                    self.context.log_css_error(self.error_context, err.location, error);
                                }
                            }
                        },
                    )*
                }

                Ok(())
            }
        }
    }
}

font_feature_values_blocks! {
    blocks = [
        #[doc = "A @swash blocksck. \
                 Specifies a feature name that will work with the swash() \
                 functional notation of font-variant-alternates."]
        "swash" swash / Swash: SingleValue,

        #[doc = "A @stylistic block. \
                 Specifies a feature name that will work with the annotation() \
                 functional notation of font-variant-alternates."]
        "stylistic" stylistic / Stylistic: SingleValue,

        #[doc = "A @ornaments block. \
                 Specifies a feature name that will work with the ornaments() ] \
                 functional notation of font-variant-alternates."]
        "ornaments" ornaments / Ornaments: SingleValue,

        #[doc = "A @annotation block. \
                 Specifies a feature name that will work with the stylistic() \
                 functional notation of font-variant-alternates."]
        "annotation" annotation / Annotation: SingleValue,

        #[doc = "A @character-variant block. \
                 Specifies a feature name that will work with the styleset() \
                 functional notation of font-variant-alternates. The value can be a pair."]
        "character-variant" character_variant / CharacterVariant: PairValues,

        #[doc = "A @styleset block. \
                 Specifies a feature name that will work with the character-variant() \
                 functional notation of font-variant-alternates. The value can be a list."]
        "styleset" styleset / Styleset: VectorValues,
    ]
}
<commit_msg>Convert the @font-feature-values declarations to lowercase<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-feature-values`][font-feature-values] at-rule.
//!
//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule

use Atom;
use computed_values::font_family::FamilyName;
use cssparser::{AtRuleParser, AtRuleType, BasicParseError, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier};
use error_reporting::{ContextualParseError, ParseErrorReporter};
use parser::{ParserContext, ParserErrorContext, Parse};
use selectors::parser::SelectorParseError;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt;
use style_traits::{ParseError, StyleParseError, ToCss};
use stylesheets::CssRuleType;

/// A @font-feature-values block declaration.
/// It is `<ident>: <integer>+`.
/// This struct can take 3 value types.
/// - `SingleValue` is to keep just one unsigned integer value.
/// - `PairValues` is to keep one or two unsigned integer values.
/// - `VectorValues` is to keep a list of unsigned integer values.
#[derive(Clone, Debug, PartialEq)]
pub struct FFVDeclaration<T> {
    /// An `<ident>` for declaration name.
    pub name: Atom,
    /// An `<integer>+` for declaration value.
    pub value: T,
}

impl<T: ToCss> ToCss for FFVDeclaration<T> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        serialize_identifier(&self.name.to_string(), dest)?;
        dest.write_str(": ")?;
        self.value.to_css(dest)?;
        dest.write_str(";")
    }
}

/// A @font-feature-values block declaration value that keeps one value.
#[derive(Clone, Debug, PartialEq)]
pub struct SingleValue(pub u32);

impl Parse for SingleValue {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<SingleValue, ParseError<'i>> {
        match *input.next()? {
            Token::Number { int_value: Some(v), .. } if v >= 0 => Ok(SingleValue(v as u32)),
            ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
        }
    }
}

impl ToCss for SingleValue {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)
    }
}

/// A @font-feature-values block declaration value that keeps one or two values.
#[derive(Clone, Debug, PartialEq)]
pub struct PairValues(pub u32, pub Option<u32>);

impl Parse for PairValues {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<PairValues, ParseError<'i>> {
        let first = match *input.next()? {
            Token::Number { int_value: Some(a), .. } if a >= 0 => a as u32,
            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
        };
        match input.next() {
            Ok(&Token::Number { int_value: Some(b), .. }) if b >= 0 => {
                Ok(PairValues(first, Some(b as u32)))
            }
            // It can't be anything other than number.
            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
            // It can be just one value.
            Err(_) => Ok(PairValues(first, None))
        }
    }
}

impl ToCss for PairValues {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)?;
        if let Some(second) = self.1 {
            write!(dest, " {}", second)?;
        }
        Ok(())
    }
}

/// A @font-feature-values block declaration value that keeps a list of values.
#[derive(Clone, Debug, PartialEq)]
pub struct VectorValues(pub Vec<u32>);

impl Parse for VectorValues {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<VectorValues, ParseError<'i>> {
        let mut vec = vec![];
        loop {
            match input.next() {
                Ok(&Token::Number { int_value: Some(a), .. }) if a >= 0 => {
                    vec.push(a as u32);
                },
                // It can't be anything other than number.
                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
                Err(_) => break,
            }
        }

        if vec.len() == 0 {
            return Err(BasicParseError::EndOfInput.into());
        }

        Ok(VectorValues(vec))
    }
}

impl ToCss for VectorValues {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        let first = iter.next();
        if let Some(first) = first {
            write!(dest, "{}", first)?;
            for value in iter {
                dest.write_str(" ")?;
                write!(dest, "{}", value)?;
            }
        }
        Ok(())
    }
}

/// Parses a list of `FamilyName`s.
pub fn parse_family_name_list<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                  -> Result<Vec<FamilyName>, ParseError<'i>> {
    input.parse_comma_separated(|i| FamilyName::parse(context, i)).map_err(|e| e.into())
}

/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.
/// (`<ident>: <integer>+`)
struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
    context: &'a ParserContext<'b>,
    declarations: &'a mut Vec<FFVDeclaration<T>>,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
    type Prelude = ();
    type AtRule = ();
    type Error = SelectorParseError<'i, StyleParseError<'i>>;
}

impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
    where T: Parse
{
    type Declaration = ();
    type Error = SelectorParseError<'i, StyleParseError<'i>>;

    fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                       -> Result<(), ParseError<'i>> {
        let value = input.parse_entirely(|i| T::parse(self.context, i))?;
        let new = FFVDeclaration {
            name: Atom::from(&*name).to_ascii_lowercase(),
            value: value,
        };
        update_or_push(&mut self.declarations, new);
        Ok(())
    }
}

macro_rules! font_feature_values_blocks {
    (
        blocks = [
            $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident: $ty: ty, )*
        ]
    ) => {
        /// The [`@font-feature-values`][font-feature-values] at-rule.
        ///
        /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFeatureValuesRule {
            /// Font family list for @font-feature-values rule.
            /// Family names cannot contain generic families. FamilyName
            /// also accepts only non-generic names.
            pub family_names: Vec<FamilyName>,
            $(
                #[$doc]
                pub $ident: Vec<FFVDeclaration<$ty>>,
            )*
            /// The line and column of the rule's source code.
            pub source_location: SourceLocation,
        }

        impl FontFeatureValuesRule {
            /// Creates an empty FontFeatureValuesRule with given location and family name list.
            fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
                FontFeatureValuesRule {
                    family_names: family_names,
                    $(
                        $ident: vec![],
                    )*
                    source_location: location,
                }
            }

            /// Parses a `FontFeatureValuesRule`.
            pub fn parse<R>(context: &ParserContext,
                            error_context: &ParserErrorContext<R>,
                            input: &mut Parser,
                            family_names: Vec<FamilyName>,
                            location: SourceLocation)
                            -> FontFeatureValuesRule
                where R: ParseErrorReporter
            {
                let mut rule = FontFeatureValuesRule::new(family_names, location);

                {
                    let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
                        context: context,
                        error_context: error_context,
                        rule: &mut rule,
                    });
                    while let Some(result) = iter.next() {
                        if let Err(err) = result {
                            let error = ContextualParseError::UnsupportedRule(err.slice, err.error);
                            context.log_css_error(error_context, err.location, error);
                        }
                    }
                }
                rule
            }

            /// Prints font family names.
            pub fn font_family_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                let mut iter = self.family_names.iter();
                iter.next().unwrap().to_css(dest)?;
                for val in iter {
                    dest.write_str(", ")?;
                    val.to_css(dest)?;
                }
                Ok(())
            }

            /// Prints inside of `@font-feature-values` block.
            pub fn value_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                $(
                    if self.$ident.len() > 0 {
                        dest.write_str(concat!("@", $name, " {\n"))?;
                        let iter = self.$ident.iter();
                        for val in iter {
                            val.to_css(dest)?;
                            dest.write_str("\n")?
                        }
                        dest.write_str("}\n")?
                    }
                )*
                Ok(())
            }
        }

        impl ToCssWithGuard for FontFeatureValuesRule {
            fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
                where W: fmt::Write
            {
                dest.write_str("@font-feature-values ")?;
                self.font_family_to_css(dest)?;
                dest.write_str(" {\n")?;
                self.value_to_css(dest)?;
                dest.write_str("}")
            }
        }

        /// Updates with new value if same `ident` exists, otherwise pushes to the vector.
        fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
            let position = vec.iter().position(|ref val| val.name == element.name);
            if let Some(index) = position {
                vec[index].value = element.value;
            } else {
                vec.push(element);
            }
        }

        /// Keeps the information about block type like @swash, @styleset etc.
        enum BlockType {
            $(
                $ident_camel,
            )*
        }

        /// Parser for `FontFeatureValuesRule`. Parses all blocks
        /// <feature-type> {
        ///   <feature-value-declaration-list>
        /// }
        /// <feature-type> = @stylistic | @historical-forms | @styleset |
        /// @character-variant | @swash | @ornaments | @annotation
        struct FontFeatureValuesRuleParser<'a, R: 'a> {
            context: &'a ParserContext<'a>,
            error_context: &'a ParserErrorContext<'a, R>,
            rule: &'a mut FontFeatureValuesRule,
        }

        /// Default methods reject all qualified rules.
        impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
            type Prelude = ();
            type QualifiedRule = ();
            type Error = SelectorParseError<'i, StyleParseError<'i>>;
        }

        impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
            type Prelude = BlockType;
            type AtRule = ();
            type Error = SelectorParseError<'i, StyleParseError<'i>>;

            fn parse_prelude<'t>(&mut self,
                                 name: CowRcStr<'i>,
                                 _input: &mut Parser<'i, 't>)
                                 -> Result<AtRuleType<Self::Prelude, Self::AtRule>, ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name => Ok(AtRuleType::WithBlock(BlockType::$ident_camel)),
                    )*
                    _ => Err(BasicParseError::AtRuleBodyInvalid.into()),
                }
            }

            fn parse_block<'t>(
                &mut self,
                prelude: Self::Prelude,
                input: &mut Parser<'i, 't>
            ) -> Result<Self::AtRule, ParseError<'i>> {
                debug_assert_eq!(self.context.rule_type(), CssRuleType::FontFeatureValues);
                match prelude {
                    $(
                        BlockType::$ident_camel => {
                            let parser = FFVDeclarationsParser {
                                context: &self.context,
                                declarations: &mut self.rule.$ident,
                            };

                            let mut iter = DeclarationListParser::new(input, parser);
                            while let Some(declaration) = iter.next() {
                                if let Err(err) = declaration {
                                    let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
                                        err.slice, err.error);
                                    self.context.log_css_error(self.error_context, err.location, error);
                                }
                            }
                        },
                    )*
                }

                Ok(())
            }
        }
    }
}

font_feature_values_blocks! {
    blocks = [
        #[doc = "A @swash blocksck. \
                 Specifies a feature name that will work with the swash() \
                 functional notation of font-variant-alternates."]
        "swash" swash / Swash: SingleValue,

        #[doc = "A @stylistic block. \
                 Specifies a feature name that will work with the annotation() \
                 functional notation of font-variant-alternates."]
        "stylistic" stylistic / Stylistic: SingleValue,

        #[doc = "A @ornaments block. \
                 Specifies a feature name that will work with the ornaments() ] \
                 functional notation of font-variant-alternates."]
        "ornaments" ornaments / Ornaments: SingleValue,

        #[doc = "A @annotation block. \
                 Specifies a feature name that will work with the stylistic() \
                 functional notation of font-variant-alternates."]
        "annotation" annotation / Annotation: SingleValue,

        #[doc = "A @character-variant block. \
                 Specifies a feature name that will work with the styleset() \
                 functional notation of font-variant-alternates. The value can be a pair."]
        "character-variant" character_variant / CharacterVariant: PairValues,

        #[doc = "A @styleset block. \
                 Specifies a feature name that will work with the character-variant() \
                 functional notation of font-variant-alternates. The value can be a list."]
        "styleset" styleset / Styleset: VectorValues,
    ]
}
<|endoftext|>


<commit_before>use nn::{Module, ModuleStruct, ModDelegate, ModIntf, Parameter};
use autograd::{Variable, Conv2dFArgs};
use std::marker::PhantomData;

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv2dArgs<T: Default> {
    in_features: usize,
    out_features: usize,
    kernel_size: (usize, usize),
    #[builder(default="vec![1, 1]")]
    pub stride: Vec<u32>,
    #[builder(default="vec![0, 0]")]
    pub padding: Vec<u32>,
    #[builder(default="vec![1, 1]")]
    pub dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
    #[builder(default="true")]
    bias: bool,
    #[builder(default="PhantomData")]
    phantom: PhantomData<T>,
}
impl<T: Default> Conv2dArgsBuilder<T> {
    pub fn done(self) -> Conv2d<T> {
        let args = self.build().unwrap();
        Conv2d::new(args)
    }
}

#[derive(ModParse)]
pub struct Conv2d<T: Default> {
    delegate: Module<T>,
    weight: Parameter<T>,
    bias: Option<Parameter<T>>,
    #[ignore]
    args: Conv2dFArgs,
}

impl<T: Default> Conv2d<T> {
    pub fn build(in_features: usize,
                 out_features: usize,
                 kernel_size: (usize, usize))
                 -> Conv2dArgsBuilder<T> {
        Conv2dArgsBuilder::default()
            .in_features(in_features)
            .out_features(out_features)
            .kernel_size(kernel_size)
    }
    pub fn new(args: Conv2dArgs<T>) -> Conv2d<T> {
        let bias = if args.bias {
            Some(Parameter::new(vec![args.out_features]))
        } else {
            None
        };
        let fargs = Conv2dFArgs {
            kernel_size: vec![args.kernel_size.0, args.kernel_size.1],
            stride: args.stride.clone(),
            padding: args.padding.clone(),
            dilation: args.dilation.clone(),
            groups: args.groups,
        };
        Conv2d {
                delegate: Module::new(),
                weight: Parameter::new(vec![args.out_features, args.in_features]),
                bias: bias,
                args: fargs,
            }
            .init_module()
    }
}
impl_mod_delegate!(Conv2d);

impl<T: Default> ModIntf<T> for Conv2d<T> {
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T> {
        input.clone()
    }
}
<commit_msg>implement forward<commit_after>use nn::{Module, ModuleStruct, ModDelegate, ModIntf, Parameter};
use autograd::{Variable, Conv2dFArgs};
use std::marker::PhantomData;
use nn::functional as F;

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct Conv2dArgs<T: Default> {
    in_features: usize,
    out_features: usize,
    kernel_size: (usize, usize),
    #[builder(default="vec![1, 1]")]
    pub stride: Vec<u32>,
    #[builder(default="vec![0, 0]")]
    pub padding: Vec<u32>,
    #[builder(default="vec![1, 1]")]
    pub dilation: Vec<u32>,
    #[builder(default="1")]
    groups: u32,
    #[builder(default="true")]
    bias: bool,
    #[builder(default="PhantomData")]
    phantom: PhantomData<T>,
}
impl<T: Default> Conv2dArgsBuilder<T> {
    pub fn done(self) -> Conv2d<T> {
        let args = self.build().unwrap();
        Conv2d::new(args)
    }
}

#[derive(ModParse)]
pub struct Conv2d<T: Default> {
    delegate: Module<T>,
    weight: Parameter<T>,
    bias: Option<Parameter<T>>,
    #[ignore]
    args: Conv2dFArgs,
}

impl<T: Default> Conv2d<T> {
    pub fn build(in_features: usize,
                 out_features: usize,
                 kernel_size: (usize, usize))
                 -> Conv2dArgsBuilder<T> {
        Conv2dArgsBuilder::default()
            .in_features(in_features)
            .out_features(out_features)
            .kernel_size(kernel_size)
    }
    pub fn new(args: Conv2dArgs<T>) -> Conv2d<T> {
        let bias = if args.bias {
            Some(Parameter::new(vec![args.out_features]))
        } else {
            None
        };
        let fargs = Conv2dFArgs {
            kernel_size: vec![args.kernel_size.0, args.kernel_size.1],
            stride: args.stride.clone(),
            padding: args.padding.clone(),
            dilation: args.dilation.clone(),
            groups: args.groups,
        };
        Conv2d {
                delegate: Module::new(),
                weight: Parameter::new(vec![args.out_features, args.in_features]),
                bias: bias,
                args: fargs,
            }
            .init_module()
    }
}
impl_mod_delegate!(Conv2d);

impl<T: Default> ModIntf<T> for Conv2d<T> {
    fn forward(&mut self, input: &mut Variable<T>) -> Variable<T> {
        let mut bias = if let Some(ref mut biasp) = self.bias {
            Some(&mut biasp.v)
        } else {
            None
        };
        F::conv2d(input, &mut self.weight.v, bias, &mut self.args)
    }
}
<|endoftext|>


<commit_before>//! Test that `$var:expr` captures function correctly.

use expect_test::expect;

use crate::macro_expansion_tests::check;

#[test]
fn unary_minus_is_a_literal() {
    check(
        r#"
macro_rules! m { ($x:literal) => (literal!();); ($x:tt) => (not_a_literal!();); }
m!(92);
m!(-92);
m!(-9.2);
m!(--92);
"#,
        expect![[r#"
macro_rules! m { ($x:literal) => (literal!();); ($x:tt) => (not_a_literal!();); }
literal!();
literal!();
literal!();
/* error: leftover tokens */not_a_literal!();
"#]],
    )
}

#[test]
fn test_expand_bad_literal() {
    check(
        r#"
macro_rules! m { ($i:literal) => {}; }
m!(&k");
"#,
        expect![[r#"
macro_rules! m { ($i:literal) => {}; }
/* error: Failed to lower macro args to token tree */"#]],
    );
}

#[test]
fn test_empty_comments() {
    check(
        r#"
macro_rules! m{ ($fmt:expr) => (); }
m!(/**/);
"#,
        expect![[r#"
macro_rules! m{ ($fmt:expr) => (); }
/* error: expected Expr */
"#]],
    );
}

#[test]
fn asi() {
    // Thanks, Christopher!
    //
    // https://internals.rust-lang.org/t/understanding-decisions-behind-semicolons/15181/29
    check(
        r#"
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }

fn main() {
    asi! {
        let a = 2
        let b = 5
        drop(b-a)
        println!("{}", a+b)
    }
}
"#,
        expect![[r#"
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }

fn main() {
    let a = 2let b = 5drop(b-a)println!("{}", a+b)
}
"#]],
    )
}

#[test]
fn stmt_boundaries() {
    // FIXME: this actually works OK under rustc.
    check(
        r#"
macro_rules! m {
    ($($s:stmt)*) => (stringify!($($s |)*);)
}
m!(;;92;let x = 92; loop {};);
"#,
        expect![[r#"
macro_rules! m {
    ($($s:stmt)*) => (stringify!($($s |)*);)
}
stringify!(;
|;
|92|;
|let x = 92|;
|loop {}
|;
|);
"#]],
    );
}

#[test]
fn range_patterns() {
    // FIXME: rustc thinks there are three patterns here, not one.
    check(
        r#"
macro_rules! m {
    ($($p:pat)*) => (stringify!($($p |)*);)
}
m!(.. .. ..);
"#,
        expect![[r#"
macro_rules! m {
    ($($p:pat)*) => (stringify!($($p |)*);)
}
stringify!(.. .. ..|);
"#]],
    );
}
<commit_msg>minor: add missing test<commit_after>//! Test that `$var:expr` captures function correctly.

use expect_test::expect;

use crate::macro_expansion_tests::check;

#[test]
fn unary_minus_is_a_literal() {
    check(
        r#"
macro_rules! m { ($x:literal) => (literal!();); ($x:tt) => (not_a_literal!();); }
m!(92);
m!(-92);
m!(-9.2);
m!(--92);
"#,
        expect![[r#"
macro_rules! m { ($x:literal) => (literal!();); ($x:tt) => (not_a_literal!();); }
literal!();
literal!();
literal!();
/* error: leftover tokens */not_a_literal!();
"#]],
    )
}

#[test]
fn test_expand_bad_literal() {
    check(
        r#"
macro_rules! m { ($i:literal) => {}; }
m!(&k");
"#,
        expect![[r#"
macro_rules! m { ($i:literal) => {}; }
/* error: Failed to lower macro args to token tree */"#]],
    );
}

#[test]
fn test_empty_comments() {
    check(
        r#"
macro_rules! m{ ($fmt:expr) => (); }
m!(/**/);
"#,
        expect![[r#"
macro_rules! m{ ($fmt:expr) => (); }
/* error: expected Expr */
"#]],
    );
}

#[test]
fn asi() {
    // Thanks, Christopher!
    //
    // https://internals.rust-lang.org/t/understanding-decisions-behind-semicolons/15181/29
    check(
        r#"
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }

fn main() {
    asi! {
        let a = 2
        let b = 5
        drop(b-a)
        println!("{}", a+b)
    }
}
"#,
        expect![[r#"
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }

fn main() {
    let a = 2let b = 5drop(b-a)println!("{}", a+b)
}
"#]],
    )
}

#[test]
fn stmt_boundaries() {
    // FIXME: this actually works OK under rustc.
    check(
        r#"
macro_rules! m {
    ($($s:stmt)*) => (stringify!($($s |)*);)
}
m!(;;92;let x = 92; loop {};);
"#,
        expect![[r#"
macro_rules! m {
    ($($s:stmt)*) => (stringify!($($s |)*);)
}
stringify!(;
|;
|92|;
|let x = 92|;
|loop {}
|;
|);
"#]],
    );
}

#[test]
fn range_patterns() {
    // FIXME: rustc thinks there are three patterns here, not one.
    check(
        r#"
macro_rules! m {
    ($($p:pat)*) => (stringify!($($p |)*);)
}
m!(.. .. ..);
"#,
        expect![[r#"
macro_rules! m {
    ($($p:pat)*) => (stringify!($($p |)*);)
}
stringify!(.. .. ..|);
"#]],
    );
}

#[test]
fn trailing_vis() {
    check(
        r#"
macro_rules! m { ($($i:ident)? $vis:vis) => () }
m!(x pub);
"#,
        expect![[r#"
macro_rules! m { ($($i:ident)? $vis:vis) => () }

"#]],
    )
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io::Write;
use std::io::stderr;

use ansi_term::Colour::Red;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "\n").ok();
}

/// Convenience function: calls `trace_error()` with `e` and afterwards `std::process::exit()`
/// with `code`
pub fn trace_error_exit(e: &Error, code: i32) {
    use std::process::exit;

    debug!("Tracing error...");
    trace_error(e);
    debug!("Calling exit()");
    exit(code);
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    let msg = Red.blink().paint(format!("{}/{} Levels of errors will be printed\n",
                                        (if max > n { n } else { max }), n));
    write!(stderr(), "{}", msg).ok();
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "").ok();
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        match print_trace_maxdepth(idx - 1, e.cause().unwrap(), max) {
            None    => write!(stderr(), "\n").ok(),
            Some(_) => write!(stderr(), " -- caused:\n").ok(),
        };
    } else {
        write!(stderr(), "\n").ok();
    }
    write!(stderr(), "{}: {}", Red.paint(format!("ERROR[{:>4}]", idx)), e.description()).ok();
    e.cause()
}

/// Count errors in `Error::cause()` recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", idx)), e.description());
    if e.cause().is_some() {
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<commit_msg>Make trace_error_exit() diverging<commit_after>use std::error::Error;
use std::io::Write;
use std::io::stderr;

use ansi_term::Colour::Red;

/// Print an Error type and its cause recursively
///
/// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
/// which each recursion into the errors cause. The error description is used to visualize what
/// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
/// line.
///
/// Example output:
///
/// ```ignore
/// Error    1 : Some error -- caused by:
/// Error    2 : Some other error -- caused by:
/// Error    3 : Yet another Error -- caused by:
/// ...
///
/// Error <NNNN> : <Error description>
/// ```
pub fn trace_error(e: &Error) {
    print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
    write!(stderr(), "\n").ok();
}

/// Convenience function: calls `trace_error()` with `e` and afterwards `std::process::exit()`
/// with `code`
pub fn trace_error_exit(e: &Error, code: i32) -> ! {
    use std::process::exit;

    debug!("Tracing error...");
    trace_error(e);
    debug!("Calling exit()");
    exit(code);
}

/// Print an Error type and its cause recursively, but only `max` levels
///
/// Output is the same as for `trace_error()`, though there are only `max` levels printed.
pub fn trace_error_maxdepth(e: &Error, max: u64) {
    let n = count_error_causes(e);
    let msg = Red.blink().paint(format!("{}/{} Levels of errors will be printed\n",
                                        (if max > n { n } else { max }), n));
    write!(stderr(), "{}", msg).ok();
    print_trace_maxdepth(n, e, max);
    write!(stderr(), "").ok();
}

/// Print an Error type and its cause recursively with the debug!() macro
///
/// Output is the same as for `trace_error()`.
pub fn trace_error_dbg(e: &Error) {
    print_trace_dbg(0, e);
}

/// Helper function for `trace_error()` and `trace_error_maxdepth()`.
///
/// Returns the cause of the last processed error in the recursion, so `None` if all errors where
/// processed.
fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
    if e.cause().is_some() && idx > 0 {
        match print_trace_maxdepth(idx - 1, e.cause().unwrap(), max) {
            None    => write!(stderr(), "\n").ok(),
            Some(_) => write!(stderr(), " -- caused:\n").ok(),
        };
    } else {
        write!(stderr(), "\n").ok();
    }
    write!(stderr(), "{}: {}", Red.paint(format!("ERROR[{:>4}]", idx)), e.description()).ok();
    e.cause()
}

/// Count errors in `Error::cause()` recursively
fn count_error_causes(e: &Error) -> u64 {
    1 + if e.cause().is_some() { count_error_causes(e.cause().unwrap()) } else { 0 }
}

fn print_trace_dbg(idx: u64, e: &Error) {
    debug!("{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", idx)), e.description());
    if e.cause().is_some() {
        print_trace_dbg(idx + 1, e.cause().unwrap());
    }
}

<|endoftext|>


<commit_before>// min-lldb-version: 310

// We have to ignore android because of this issue:
// https://github.com/rust-lang/rust/issues/74847
// ignore-android

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:info args
// gdb-check:No arguments.
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:frame variable
// lldbg-check:(unsigned long) = 111 (unsigned long) = 222
// lldbr-check:(unsigned long) = 111 (unsigned long) = 222
// lldb-command:continue

#![feature(naked_functions)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]

fn main() {
    naked(111, 222);
}

#[naked]
fn naked(x: usize, y: usize) {
    zzz(); // #break
}

fn zzz() { () }
<commit_msg>Don't call a function in function-arguments-naked.rs<commit_after>// min-lldb-version: 310

// We have to ignore android because of this issue:
// https://github.com/rust-lang/rust/issues/74847
// ignore-android

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run

// gdb-command:info args
// gdb-check:No arguments.
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:frame variable
// lldbg-check:(unsigned long) = 111 (unsigned long) = 222
// lldbr-check:(unsigned long) = 111 (unsigned long) = 222
// lldb-command:continue

#![feature(naked_functions)]
#![feature(omit_gdb_pretty_printer_section)]
#![omit_gdb_pretty_printer_section]

fn main() {
    naked(111, 222);
}

#[naked]
fn naked(x: usize, y: usize) {
    // #break
}
<|endoftext|>


<commit_before>use bzip2::bufread::BzDecoder;
use error::*;
use flate2::bufread::MultiGzDecoder;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::{BufReader, SeekFrom};
use std::path::Path;
use xz2::bufread::XzDecoder;

/// Configure behaviour of the [`file_open_read_with_options`] function.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
#[derive(Clone, Debug)]
pub struct ReadOptions {
    buffer_capacity: Option<usize>,
    open_options: OpenOptions,
}

impl ReadOptions {
    /// Create a new `ReadOptions` with default settings.
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the capacity of the [`BufReader`] to `capacity` in Bytes.
    ///
    /// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
    pub fn set_buffer_capacity(mut self, capacity: usize) -> Self {
        self.buffer_capacity = Some(capacity);
        self
    }

    /// Specify a set of [`OpenOptions`] to use.
    ///
    /// The option `read` will always be overwritten to `true` and `write` will always be set to
    /// `false`.
    ///
    /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
    pub fn set_open_options(mut self, open_options: OpenOptions) -> Self {
        self.open_options = open_options;
        self
    }
}

impl Default for ReadOptions {
    fn default() -> Self {
        Self {
            buffer_capacity: None,
            open_options: OpenOptions::new(),
        }
    }
}

/// Create reader for uncompressed or compressed files transparently.
///
/// See [`file_open_read_with_options`] for the full documentation.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
pub fn file_open_read<P>(file: P) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), ReadOptions::default())
}

/// Create reader for uncompressed or compressed files transparently.
///
/// This function opens the given `file` and tries to determine the filetype by reading the magic
/// bytes from the start of the file. If a known archive type, like xz, gz, or bz2, is found this
/// function will transparent create a reader which decompresses the data on the fly.
///
/// File I/O will always be buffered using a [`BufReader`].
///
/// The behaviour of this function can be configured using [`ReadOptions`]. See the documentation
/// on the struct for details.
///
/// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`ReadOptions`]: ./struct.ReadOptions.html
pub fn file_open_read_with_options<P>(file: P, options: ReadOptions) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), options)
}

fn file_open_read_with_option_do(file: &Path, mut options: ReadOptions) -> Result<Box<Read>> {
    if !file.is_file() {
        return Err(ErrorKind::PathNotAFile(file.to_path_buf()).into());
    }

    let f = options
        .open_options
        .read(true)
        .write(false)
        .open(file)
        .chain_err(|| format!("Could not open file {:?}", file))?;
    let mut bufread = if let Some(size) = options.buffer_capacity {
        BufReader::with_capacity(size, f)
    } else {
        BufReader::new(f)
    };

    // read magic bytes
    let mut buffer = [0; 6];
    bufread.read_exact(&mut buffer).chain_err(|| {
        format!("Failed to read file header of {:?}", file)
    })?;
    // reset the read position
    bufread.seek(SeekFrom::Start(0)).chain_err(
        || "Failed to seek to start of file.",
    )?;

    // check if file if XZ compressed
    if buffer[..6] == [0xfd, '7' as u8, 'z' as u8, 'X' as u8, 'Z' as u8, 0x00] {
        debug!("File {:?} is detected to have type `xz`", file);
        Ok(Box::new(XzDecoder::new(bufread)))
    } else if buffer[..2] == [0x1f, 0x8b] {
        debug!("File {:?} is detected to have type `gz`", file);
        Ok(Box::new(MultiGzDecoder::new(bufread).chain_err(
            || "Failed to create the gz reader",
        )?))
    } else if buffer[..3] == ['B' as u8, 'Z' as u8, 'h' as u8] {
        debug!("File {:?} is detected to have type `bz2`", file);
        Ok(Box::new(BzDecoder::new(bufread)))
    } else {
        debug!("Open file {:?} as plaintext", file);
        Ok(Box::new(bufread))
    }
}
<commit_msg>Add a convenience method for writing archive/plaintext files<commit_after>use bzip2;
use bzip2::bufread::BzDecoder;
use bzip2::write::BzEncoder;
use error::*;
use flate2;
use flate2::bufread::MultiGzDecoder;
use flate2::write::GzEncoder;
use std::borrow::Borrow;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter, SeekFrom};
use std::path::Path;
use xz2::bufread::XzDecoder;
use xz2::stream::{Check, MtStreamBuilder};
use xz2::write::XzEncoder;

/// Configure behaviour of the [`file_open_read_with_options`] function.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
#[derive(Clone, Debug)]
pub struct ReadOptions {
    buffer_capacity: Option<usize>,
    open_options: OpenOptions,
}

impl ReadOptions {
    /// Create a new `ReadOptions` with default settings.
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the capacity of the [`BufReader`] to `capacity` in Bytes.
    ///
    /// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
    pub fn set_buffer_capacity(mut self, capacity: usize) -> Self {
        self.buffer_capacity = Some(capacity);
        self
    }

    /// Specify a set of [`OpenOptions`] to use.
    ///
    /// The option `read` will always be overwritten to `true` and `write` will always be set to
    /// `false`.
    ///
    /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
    pub fn set_open_options<B>(mut self, open_options: B) -> Self
    where
        B: Borrow<OpenOptions>,
    {
        self.open_options = open_options.borrow().clone();
        self
    }
}

impl Default for ReadOptions {
    fn default() -> Self {
        Self {
            buffer_capacity: None,
            open_options: OpenOptions::new(),
        }
    }
}

/// Create reader for uncompressed or compressed files transparently.
///
/// See [`file_open_read_with_options`] for the full documentation.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
pub fn file_open_read<P>(file: P) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), ReadOptions::default())
}

/// Create reader for uncompressed or compressed files transparently.
///
/// This function opens the given `file` and tries to determine the filetype by reading the magic
/// bytes from the start of the file. If a known archive type, like xz, gz, or bz2, is found this
/// function will transparent create a reader which decompresses the data on the fly.
///
/// File I/O will always be buffered using a [`BufReader`].
///
/// The behaviour of this function can be configured using [`ReadOptions`]. See the documentation
/// on the struct for details.
///
/// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`ReadOptions`]: ./struct.ReadOptions.html
pub fn file_open_read_with_options<P>(file: P, options: ReadOptions) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), options)
}

fn file_open_read_with_option_do(file: &Path, mut options: ReadOptions) -> Result<Box<Read>> {
    if !file.is_file() {
        return Err(ErrorKind::PathNotAFile(file.to_path_buf()).into());
    }

    let f = options
        .open_options
        .read(true)
        .write(false)
        .open(file)
        .chain_err(|| format!("Could not open file {:?}", file))?;
    let mut bufread = if let Some(size) = options.buffer_capacity {
        BufReader::with_capacity(size, f)
    } else {
        BufReader::new(f)
    };

    // read magic bytes
    let mut buffer = [0; 6];
    bufread.read_exact(&mut buffer).chain_err(|| {
        format!("Failed to read file header of {:?}", file)
    })?;
    // reset the read position
    bufread.seek(SeekFrom::Start(0)).chain_err(
        || "Failed to seek to start of file.",
    )?;

    // check if file if XZ compressed
    if buffer[..6] == [0xfd, '7' as u8, 'z' as u8, 'X' as u8, 'Z' as u8, 0x00] {
        debug!("File {:?} is detected to have type `xz`", file);
        Ok(Box::new(XzDecoder::new(bufread)))
    } else if buffer[..2] == [0x1f, 0x8b] {
        debug!("File {:?} is detected to have type `gz`", file);
        Ok(Box::new(MultiGzDecoder::new(bufread).chain_err(
            || "Failed to create the gz reader",
        )?))
    } else if buffer[..3] == ['B' as u8, 'Z' as u8, 'h' as u8] {
        debug!("File {:?} is detected to have type `bz2`", file);
        Ok(Box::new(BzDecoder::new(bufread)))
    } else {
        debug!("Open file {:?} as plaintext", file);
        Ok(Box::new(bufread))
    }
}

/// Configure behaviour of the [`file_open_write`] function.
///
/// # Defaults
///
/// ```text
/// WriteOptions {
///     buffer_capacity: None,
///     compression_level: Compression::Default,
///     filetype: FileType::PlainText,
///     open_options: OpenOptions::new(),
///     threads: 1,
/// };
/// ```
///
/// [`file_open_write`]: ./fn.file_open_write.html
#[derive(Clone, Debug)]
pub struct WriteOptions {
    buffer_capacity: Option<usize>,
    compression_level: Compression,
    filetype: FileType,
    open_options: OpenOptions,
    threads: u32,
}

impl WriteOptions {
    /// Create a new `WriteOptions` with default settings.
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the capacity of the [`BufReader`] to `capacity` in Bytes.
    ///
    /// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
    pub fn set_buffer_capacity(mut self, capacity: usize) -> Self {
        self.buffer_capacity = Some(capacity);
        self
    }

    /// Sets the compression level for archives.
    ///
    /// This configures the compression level used. This option has no effect if the [`FileType`]
    /// is `PlainText`. See [`Compression`] for a description of the possible values.
    ///
    /// [`Compression`]: ./enum.Compression.html
    /// [`FileType`]: ./enum.FileType.html
    pub fn set_compression_level(mut self, compression: Compression) -> Self {
        self.compression_level = compression;
        self
    }

    /// Sets the output filetype.
    ///
    /// This specifies if the file will be plaintext or which archive form will be used. See
    /// [`FileType`] for details on the possible values.
    ///
    /// [`FileType`]: ./enum.FileType.html
    pub fn set_filetype(mut self, ty: FileType) -> Self {
        self.filetype = ty;
        self
    }

    /// Specify a set of [`OpenOptions`] to use.
    ///
    /// The option `read` will always be overwritten to `false` and `write` will always be set to
    /// `true`.
    ///
    /// This allows to specify flags like `append` or `truncate` while writing.
    ///
    /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
    pub fn set_open_options<B>(mut self, open_options: B) -> Self
    where
        B: Borrow<OpenOptions>,
    {
        self.open_options = open_options.borrow().clone();
        self
    }

    /// Specify the maximal number of threads used for compression.
    ///
    /// This gives a hint to the encoder that threading is wanted. This feature is currently only
    /// used with `xz`. The writer will the value of `threads` as a maximal number.
    ///
    /// Setting this option to `0` has the same effect as setting it to `1`.
    pub fn set_threads(mut self, mut threads: u32) -> Self {
        if threads == 0 {
            threads = 1;
        }
        self.threads = threads;
        self
    }
}

impl Default for WriteOptions {
    fn default() -> Self {
        Self {
            buffer_capacity: None,
            compression_level: Compression::default(),
            filetype: FileType::default(),
            open_options: OpenOptions::new(),
            threads: 1,
        }
    }
}

impl PartialEq for WriteOptions {
    fn eq(&self, other: &Self) -> bool {
        self.buffer_capacity == other.buffer_capacity &&
            self.compression_level == other.compression_level &&
            self.filetype == other.filetype && self.threads == other.threads
    }
}

impl Eq for WriteOptions {}

/// Specify the output filetype.
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum FileType {
    /// Create a `bz2` compressed archive.
    Bz2,
    /// Create a `gz` compressed archive.
    Gz,
    /// Create a plaintext file.<br />
    /// This is the default variant.
    PlainText,
    /// Create a `xz` compressed archive.
    Xz,
}

impl Default for FileType {
    /// Returns the `PlainText` variant.
    fn default() -> Self {
        FileType::PlainText
    }
}

/// Specify the compression level used.
///
/// There are three presets provided, `Fastest`, `Default`, and `Best`. They correspond to the
/// settings in `bzip2`, `gzip`, and `xz`. `Default` corresponds to the value 6.
///
/// For `bzip2` and `gzip` the `Numeric` values are mapped as follows:
///
/// |      Numeric |              bzip2 |      gzip |
/// | -----------: | -----------------: | --------: |
/// |            0 | `<No compression>` | `Fastest` |
/// |            1 |          `Fastest` | `Fastest` |
/// |            2 |          `Fastest` | `Fastest` |
/// |            3 |          `Fastest` | `Fastest` |
/// |            4 |          `Default` | `Default` |
/// |            5 |          `Default` | `Default` |
/// |            6 |          `Default` | `Default` |
/// |            7 |             `Best` |    `Best` |
/// |            8 |             `Best` |    `Best` |
/// |            9 |             `Best` |    `Best` |
/// | other values |             `Best` |    `Best` |
///
/// For `xz` `Numeric` values in the range `0-9` (inclusive) are valid. The named variants are
/// mapped to `0` for `Fastest`, `6` for `Default`, and `9` for `Best`.
///
/// Be aware that the result in compression ratio and time/memory consumption is highly dependent
/// on the chosen filetype.
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Compression {
    /// Provide the fastest compression possible.
    Fastest,
    /// Use a reasonable default, which does not consume too much CPU/memory.
    Default,
    /// Provide the best compression possible.
    Best,
    /// Fine-grained controll over the compression for the `xz` algorithm. Allowed values are `0-9`.
    Numeric(u8),
}

impl Default for Compression {
    /// Returns the `Default` variant.
    fn default() -> Self {
        Compression::Default
    }
}

impl Into<bzip2::Compression> for Compression {
    fn into(self) -> bzip2::Compression {
        use bzip2::Compression::*;

        match self {
            Compression::Fastest => Fastest,
            Compression::Numeric(n) if n <= 3 => Fastest,
            Compression::Default => Default,
            Compression::Numeric(n) if 4 <= n && n <= 6 => Default,
            Compression::Best => Best,
            Compression::Numeric(n) if 7 <= n && n <= 9 => Best,

            // catchall for all value > 9
            Compression::Numeric(_) => Best,
        }
    }
}

impl Into<flate2::Compression> for Compression {
    fn into(self) -> flate2::Compression {
        use flate2::Compression::*;

        match self {
            Compression::Numeric(0) => None,
            Compression::Fastest => Fast,
            Compression::Numeric(n) if 1 <= n && n <= 3 => Fast,
            Compression::Default => Default,
            Compression::Numeric(n) if 4 <= n && n <= 6 => Default,
            Compression::Best => Best,
            Compression::Numeric(n) if 7 <= n && n <= 9 => Best,

            // catchall for all value > 9
            Compression::Numeric(_) => Best,
        }
    }
}

/// Implementation detail to convert a [`Compression`] into a `u32` in the range `0-9` (inclusive).
///
/// [`Compression`]: ./enum.Compression.html
struct XzCompression(u32);
impl Into<XzCompression> for Compression {
    fn into(self) -> XzCompression {
        match self {
            Compression::Fastest => XzCompression(0),
            Compression::Default => XzCompression(6),
            Compression::Best => XzCompression(9),
            Compression::Numeric(n) => XzCompression(clamp(n as u32, 0, 9)),
        }
    }
}

// TODO consider using num for this
// https://docs.rs/num/0.1.40/num/fn.clamp.html
fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
    if input < min {
        min
    } else if input > max {
        max
    } else {
        input
    }
}

/// Create writers for plaintext or compressed files.
///
/// This function can open a file with different compressors enabled. It hides the complexity of
/// creating the correct writer behind the [`WriteOptions`] builder. See the documentation on the
/// struct for more details.
///
/// File I/O will always be buffered using a [`BufReader`].
///
/// Flushing the writer will not write all the data to file. Archives require some finalizer which
/// is only written if the writer is being dropped.
///
/// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`WriteOptions`]: ./struct.WriteOptions.html
pub fn file_open_write<P>(file: P, mut options: WriteOptions) -> Result<Box<Write>>
where
    P: AsRef<Path>,
{
    use self::FileType::*;
    let file = file.as_ref();

    let f = options
        .open_options
        .read(false)
        .write(true)
        .open(file)
        .chain_err(|| format!("Could not open file {:?}", file))?;
    let bufwrite = if let Some(size) = options.buffer_capacity {
        BufWriter::with_capacity(size, f)
    } else {
        BufWriter::new(f)
    };

    match options.filetype {
        Bz2 => {
            let level = options.compression_level.into();
            Ok(Box::new(BzEncoder::new(bufwrite, level)))
        }
        Gz => {
            let level = options.compression_level.into();
            Ok(Box::new(GzEncoder::new(bufwrite, level)))
        }
        PlainText => Ok(Box::new(bufwrite)),
        Xz => {
            let level: XzCompression = options.compression_level.into();
            let threads = clamp(options.threads, 1, u32::max_value());
            if threads == 1 {
                Ok(Box::new(XzEncoder::new(bufwrite, level.0)))
            } else {
                let stream = MtStreamBuilder::new()
                    .preset(level.0)
                    .threads(threads)
                    // let LZMA2 choose the best blocksize
                    .block_size(0)
                    // use the same value as the xz command line tool
                    .timeout_ms(300)
                    .check(Check::Crc64)
                    .encoder()
                    .chain_err(|| "Failed to initialize the xz multithreaded stream")?;
                Ok(Box::new(XzEncoder::new_stream(bufwrite, stream)))
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate regex;

extern crate sys_util;

mod cgroup;
mod env;

use std::ffi::OsStr;
use std::fs::{canonicalize, metadata};
use std::io;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::path::PathBuf;
use std::result;

use env::Env;

pub const KVM_FD: i32 = 3;
pub const DEV_NET_TUN_FD: i32 = 4;
pub const LISTENER_FD: i32 = 5;

const SOCKET_FILE_NAME: &str = "api.socket";

#[derive(Debug)]
pub enum Error {
    Canonicalize(PathBuf, io::Error),
    CgroupLineNotFound(&'static str, &'static str),
    CgroupLineNotUnique(&'static str, &'static str),
    Chroot(i32),
    Copy(PathBuf, PathBuf, io::Error),
    CreateDir(PathBuf, io::Error),
    Exec(io::Error),
    FileCreate(PathBuf, io::Error),
    FileName(PathBuf),
    FileOpen(PathBuf, io::Error),
    Gid(String),
    Metadata(PathBuf, io::Error),
    NotAFile(PathBuf),
    NotAFolder(PathBuf),
    OpenDevKvm(sys_util::Error),
    OpenDevNetTun(sys_util::Error),
    ReadLine(PathBuf, io::Error),
    RegEx(regex::Error),
    Uid(String),
    UnexpectedKvmFd(i32),
    UnexpectedDevNetTunFd(i32),
    UnexpectedListenerFd(i32),
    UnixListener(io::Error),
    UnsetCloexec(sys_util::Error),
    Write(PathBuf, io::Error),
}

pub type Result<T> = result::Result<T, Error>;

pub struct JailerArgs<'a> {
    id: &'a str,
    exec_file_path: PathBuf,
    chroot_base_dir: PathBuf,
    uid: u32,
    gid: u32,
}

impl<'a> JailerArgs<'a> {
    pub fn new(
        id: &'a str,
        exec_file: &str,
        chroot_base: &str,
        uid: &str,
        gid: &str,
    ) -> Result<Self> {
        let exec_file_path =
            canonicalize(exec_file).map_err(|e| Error::Canonicalize(PathBuf::from(exec_file), e))?;

        if !metadata(&exec_file_path)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_file()
        {
            return Err(Error::NotAFile(exec_file_path));
        }

        let chroot_base_dir = canonicalize(chroot_base)
            .map_err(|e| Error::Canonicalize(PathBuf::from(chroot_base), e))?;

        if !metadata(&chroot_base_dir)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_dir()
        {
            return Err(Error::NotAFolder(chroot_base_dir));
        }

        let uid = uid.parse::<u32>()
            .map_err(|_| Error::Uid(String::from(uid)))?;
        let gid = gid.parse::<u32>()
            .map_err(|_| Error::Gid(String::from(gid)))?;

        Ok(JailerArgs {
            id,
            exec_file_path,
            chroot_base_dir,
            uid,
            gid,
        })
    }

    pub fn exec_file_name(&self) -> Result<&OsStr> {
        self.exec_file_path
            .file_name()
            .ok_or_else(|| Error::FileName(self.exec_file_path.clone()))
    }
}

pub fn run(args: JailerArgs) -> Result<()> {
    // We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
    // passed on to Firecracker post exec, and used via knowing their values in advance.

    // TODO: use dup2 to make sure we're actually getting 3, 4, and 5?

    // TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
    // starve the host of resources? (cgroups should take care of that, but do they currently?)

    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe { libc::open("/dev/kvm\0".as_ptr() as *const libc::c_char, libc::O_RDWR) };
    if ret < 0 {
        return Err(Error::OpenDevKvm(sys_util::Error::last()));
    }
    if ret != KVM_FD {
        return Err(Error::UnexpectedKvmFd(ret));
    }

    // TODO: is RDWR required for /dev/tun (most likely)?
    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe {
        libc::open(
            "/dev/net/tun\0".as_ptr() as *const libc::c_char,
            libc::O_RDWR | libc::O_NONBLOCK,
        )
    };
    if ret < 0 {
        return Err(Error::OpenDevNetTun(sys_util::Error::last()));
    }
    if ret != DEV_NET_TUN_FD {
        return Err(Error::UnexpectedDevNetTunFd(ret));
    }

    let env = Env::new(args)?;

    let listener = UnixListener::bind(env.chroot_dir().join(SOCKET_FILE_NAME))
        .map_err(|e| Error::UnixListener(e))?;

    let listener_fd = listener.as_raw_fd();
    if listener_fd != LISTENER_FD {
        return Err(Error::UnexpectedListenerFd(listener_fd));
    }

    // It turns out Rust is so safe, it opens everything with CLOSE_ON_EXEC.

    // TODO: So as of today (20180612), FD_CLOEXEC is the only file descriptor flag, so setting
    // flags to 0 should clear that and only that. Maybe at some point it would make sense to
    // get the flags first, clear FD_CLOEXEC, and set the resulting flags.

    // This is safe because we know the fd is valid.
    if unsafe { libc::fcntl(listener_fd, libc::F_SETFD, 0) } < 0 {
        return Err(Error::UnsetCloexec(sys_util::Error::last()));
    }

    env.run()
}
<commit_msg>jailer: id must be alphanumeric<commit_after>extern crate libc;
extern crate regex;

extern crate sys_util;

mod cgroup;
mod env;

use std::ffi::OsStr;
use std::fs::{canonicalize, metadata};
use std::io;
use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::path::PathBuf;
use std::result;

use env::Env;

pub const KVM_FD: i32 = 3;
pub const DEV_NET_TUN_FD: i32 = 4;
pub const LISTENER_FD: i32 = 5;

const SOCKET_FILE_NAME: &str = "api.socket";

#[derive(Debug)]
pub enum Error {
    Canonicalize(PathBuf, io::Error),
    CgroupLineNotFound(&'static str, &'static str),
    CgroupLineNotUnique(&'static str, &'static str),
    Chroot(i32),
    Copy(PathBuf, PathBuf, io::Error),
    CreateDir(PathBuf, io::Error),
    Exec(io::Error),
    FileCreate(PathBuf, io::Error),
    FileName(PathBuf),
    FileOpen(PathBuf, io::Error),
    Gid(String),
    Metadata(PathBuf, io::Error),
    NotAFile(PathBuf),
    NotAFolder(PathBuf),
    NotAlphanumeric(String),
    OpenDevKvm(sys_util::Error),
    OpenDevNetTun(sys_util::Error),
    ReadLine(PathBuf, io::Error),
    RegEx(regex::Error),
    Uid(String),
    UnexpectedKvmFd(i32),
    UnexpectedDevNetTunFd(i32),
    UnexpectedListenerFd(i32),
    UnixListener(io::Error),
    UnsetCloexec(sys_util::Error),
    Write(PathBuf, io::Error),
}

pub type Result<T> = result::Result<T, Error>;

pub struct JailerArgs<'a> {
    id: &'a str,
    exec_file_path: PathBuf,
    chroot_base_dir: PathBuf,
    uid: u32,
    gid: u32,
}

impl<'a> JailerArgs<'a> {
    pub fn new(
        id: &'a str,
        exec_file: &str,
        chroot_base: &str,
        uid: &str,
        gid: &str,
    ) -> Result<Self> {
        // Maybe it's a good idea to restrict the id to alphanumeric strings.
        for c in id.chars() {
            if !c.is_alphanumeric() {
                return Err(Error::NotAlphanumeric(id.to_string()));
            }
        }

        let exec_file_path =
            canonicalize(exec_file).map_err(|e| Error::Canonicalize(PathBuf::from(exec_file), e))?;

        if !metadata(&exec_file_path)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_file()
        {
            return Err(Error::NotAFile(exec_file_path));
        }

        let chroot_base_dir = canonicalize(chroot_base)
            .map_err(|e| Error::Canonicalize(PathBuf::from(chroot_base), e))?;

        if !metadata(&chroot_base_dir)
            .map_err(|e| Error::Metadata(exec_file_path.clone(), e))?
            .is_dir()
        {
            return Err(Error::NotAFolder(chroot_base_dir));
        }

        let uid = uid.parse::<u32>()
            .map_err(|_| Error::Uid(String::from(uid)))?;
        let gid = gid.parse::<u32>()
            .map_err(|_| Error::Gid(String::from(gid)))?;

        Ok(JailerArgs {
            id,
            exec_file_path,
            chroot_base_dir,
            uid,
            gid,
        })
    }

    pub fn exec_file_name(&self) -> Result<&OsStr> {
        self.exec_file_path
            .file_name()
            .ok_or_else(|| Error::FileName(self.exec_file_path.clone()))
    }
}

pub fn run(args: JailerArgs) -> Result<()> {
    // We open /dev/kvm, /dev/tun, and create the listening socket. These file descriptors will be
    // passed on to Firecracker post exec, and used via knowing their values in advance.

    // TODO: use dup2 to make sure we're actually getting 3, 4, and 5?

    // TODO: can a malicious guest that takes over firecracker use its access to the KVM fd to
    // starve the host of resources? (cgroups should take care of that, but do they currently?)

    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe { libc::open("/dev/kvm\0".as_ptr() as *const libc::c_char, libc::O_RDWR) };
    if ret < 0 {
        return Err(Error::OpenDevKvm(sys_util::Error::last()));
    }
    if ret != KVM_FD {
        return Err(Error::UnexpectedKvmFd(ret));
    }

    // TODO: is RDWR required for /dev/tun (most likely)?
    // Safe because we use a constant null-terminated string and verify the result.
    let ret = unsafe {
        libc::open(
            "/dev/net/tun\0".as_ptr() as *const libc::c_char,
            libc::O_RDWR | libc::O_NONBLOCK,
        )
    };
    if ret < 0 {
        return Err(Error::OpenDevNetTun(sys_util::Error::last()));
    }
    if ret != DEV_NET_TUN_FD {
        return Err(Error::UnexpectedDevNetTunFd(ret));
    }

    let env = Env::new(args)?;

    let listener = UnixListener::bind(env.chroot_dir().join(SOCKET_FILE_NAME))
        .map_err(|e| Error::UnixListener(e))?;

    let listener_fd = listener.as_raw_fd();
    if listener_fd != LISTENER_FD {
        return Err(Error::UnexpectedListenerFd(listener_fd));
    }

    // It turns out Rust is so safe, it opens everything with CLOSE_ON_EXEC.

    // TODO: So as of today (20180612), FD_CLOEXEC is the only file descriptor flag, so setting
    // flags to 0 should clear that and only that. Maybe at some point it would make sense to
    // get the flags first, clear FD_CLOEXEC, and set the resulting flags.

    // This is safe because we know the fd is valid.
    if unsafe { libc::fcntl(listener_fd, libc::F_SETFD, 0) } < 0 {
        return Err(Error::UnsetCloexec(sys_util::Error::last()));
    }

    env.run()
}
<|endoftext|>