<commit_before>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        self.file
            .find_section_by_name(".interp")
            .and_then(|interp_section| {
                let data = interp_section.get_data(&self.file).ok()?;
                match data {
                    SectionData::Undefined(val) => {
                        if val.len() < 2 {
                            return None;
                        }
                        Some(core::str::from_utf8(&val[..val.len() - 1]).ok()?)
                    }
                    _ => None,
                }
            })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        relocation_section.map_or(
            Ok(()), // neither section found
            |rela_section_dyn| {
                let data = rela_section_dyn.get_data(&self.file)?;
                match data {
                    SectionData::Rela64(rela_entries) => {
                        // Now we finally have a list of relocation we're supposed to perform:
                        for entry in rela_entries {
                            //let _typ = RelocationType::from(entry.get_type());
                            // Does the entry blong to the current header?
                            //loader.relocate(RelaEntry::Rela64(entry))?;
                            loader.relocate(RelocationEntry {
                                rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                                offset: entry.get_offset(),
                                info: entry.get_symbol_table_index(),
                                addend: Some(entry.get_addend()),
                            })?;
                        }

                        Ok(())
                    }
                    SectionData::Rela32(rela_entries) => {
                        trace!("Relocation entries: {:?}", rela_entries);

                        // Now we finally have a list of relocation we're supposed to perform:
                        for entry in rela_entries {
                            loader.relocate(RelocationEntry {
                                rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                                offset: entry.get_offset() as u64,
                                info: entry.get_symbol_table_index(),
                                addend: Some(entry.get_addend() as u64),
                            })?;
                        }
                        Ok(())
                    }
                    SectionData::Rel32(rela_entries) => {
                        trace!("Relocation entries: {:?}", rela_entries);

                        // Now we finally have a list of relocation we're supposed to perform:
                        for entry in rela_entries {
                            loader.relocate(RelocationEntry {
                                rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                                offset: entry.get_offset() as u64,
                                info: entry.get_symbol_table_index(),
                                addend: None,
                            })?;
                        }
                        Ok(())
                    }
                    _ => Err(ElfLoaderErr::UnsupportedSectionData),
                }
            },
        )
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;
        let mut flags1 = Default::default();
        let mut rela: u64 = 0;
        let mut rela_size: u64 = 0;

        match segment {
            SegmentData::Dynamic64(dyn_entries) => {
                for dyn_entry in dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    match tag {
                        Tag::Needed => {
                            trace!(
                                "Required library {:?}",
                                file.get_dyn_string(dyn_entry.get_val()? as u32)
                            )
                        }
                        Tag::Rela => rela = dyn_entry.get_ptr()?,
                        Tag::RelaSize => rela_size = dyn_entry.get_val()?,
                        Tag::Flags1 => {
                            flags1 =
                                unsafe { DynamicFlags1::from_bits_unchecked(dyn_entry.get_val()?) };
                        }
                        _ => trace!("unsupported {:?}", dyn_entry),
                    }
                }
            }
            SegmentData::Dynamic32(dyn_entries) => {
                for dyn_entry in dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    match tag {
                        Tag::Needed => {
                            trace!(
                                "Required library {:?}",
                                file.get_dyn_string(dyn_entry.get_val()?)
                            )
                        }
                        Tag::Rela => rela = dyn_entry.get_ptr()?.into(),
                        Tag::RelaSize => rela_size = dyn_entry.get_val()?.into(),
                        Tag::Flags => {
                            flags1 = unsafe {
                                DynamicFlags1::from_bits_unchecked(dyn_entry.get_val()? as u64)
                            };
                        }
                        _ => trace!("unsupported {:?}", dyn_entry),
                    }
                }
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            rela_size,
            rela,
            flags1
        );

        Ok(Some(DynamicInfo {
            flags1,
            rela,
            rela_size,
        }))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<commit_msg>Reorganize maybe_relocate to remove duplicate code<commit_after>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        self.file
            .find_section_by_name(".interp")
            .and_then(|interp_section| {
                let data = interp_section.get_data(&self.file).ok()?;
                match data {
                    SectionData::Undefined(val) => {
                        if val.len() < 2 {
                            return None;
                        }
                        Some(core::str::from_utf8(&val[..val.len() - 1]).ok()?)
                    }
                    _ => None,
                }
            })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        // Helper macro to call loader.relocate() on all entries
        macro_rules! iter_entries_and_relocate {
            ($rela_entries:expr, $create_addend:ident) => {
                for entry in $rela_entries {
                    loader.relocate(RelocationEntry {
                        rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                        offset: entry.get_offset() as u64,
                        info: entry.get_symbol_table_index(),
                        addend: $create_addend!(entry),
                    })?;
                }
            };
        }

        // Construct from Rel<T> entries. Does not contain an addend.
        macro_rules! rel_entry {
            ($entry:expr) => {
                None
            };
        }

        // Construct from Rela<T> entries. Contains an addend.
        macro_rules! rela_entry {
            ($entry:expr) => {
                Some($entry.get_addend() as u64)
            };
        }

        // If either section exists apply the relocations
        relocation_section.map(|rela_section_dyn| {
            let data = rela_section_dyn.get_data(&self.file)?;
            match data {
                SectionData::Rel32(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela32(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                SectionData::Rel64(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela64(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                _ => return Err(ElfLoaderErr::UnsupportedSectionData),
            }
            Ok(())
        });

        Ok(())
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;
        let mut flags1 = Default::default();
        let mut rela: u64 = 0;
        let mut rela_size: u64 = 0;

        match segment {
            SegmentData::Dynamic64(dyn_entries) => {
                for dyn_entry in dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    match tag {
                        Tag::Needed => {
                            trace!(
                                "Required library {:?}",
                                file.get_dyn_string(dyn_entry.get_val()? as u32)
                            )
                        }
                        Tag::Rela => rela = dyn_entry.get_ptr()?,
                        Tag::RelaSize => rela_size = dyn_entry.get_val()?,
                        Tag::Flags1 => {
                            flags1 =
                                unsafe { DynamicFlags1::from_bits_unchecked(dyn_entry.get_val()?) };
                        }
                        _ => trace!("unsupported {:?}", dyn_entry),
                    }
                }
            }
            SegmentData::Dynamic32(dyn_entries) => {
                for dyn_entry in dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    match tag {
                        Tag::Needed => {
                            trace!(
                                "Required library {:?}",
                                file.get_dyn_string(dyn_entry.get_val()?)
                            )
                        }
                        Tag::Rela => rela = dyn_entry.get_ptr()?.into(),
                        Tag::RelaSize => rela_size = dyn_entry.get_val()?.into(),
                        Tag::Flags => {
                            flags1 = unsafe {
                                DynamicFlags1::from_bits_unchecked(dyn_entry.get_val()? as u64)
                            };
                        }
                        _ => trace!("unsupported {:?}", dyn_entry),
                    }
                }
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            rela_size,
            rela,
            flags1
        );

        Ok(Some(DynamicInfo {
            flags1,
            rela,
            rela_size,
        }))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(rustc_private, rustdoc)]

extern crate syntax;
extern crate rustdoc;
extern crate serialize as rustc_serialize;

use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fs::{read_dir, File};
use std::io::{Read, Write};
use std::path::Path;
use std::path::PathBuf;

use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};

use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle, PLAYGROUND};
use rustc_serialize::json;

enum OutputFormat {
    HTML(HTMLFormatter),
    Markdown(MarkdownFormatter),
    Unknown(String),
}

impl OutputFormat {
    fn from(format: &str) -> OutputFormat {
        match &*format.to_lowercase() {
            "html"     => OutputFormat::HTML(HTMLFormatter),
            "markdown" => OutputFormat::Markdown(MarkdownFormatter),
            s          => OutputFormat::Unknown(s.to_owned()),
        }
    }
}

trait Formatter {
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>>;
    fn title(&self, output: &mut Write) -> Result<(), Box<Error>>;
    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>>;
    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>>;
}

struct HTMLFormatter;
struct MarkdownFormatter;

impl Formatter for HTMLFormatter {
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, r##"<!DOCTYPE html>

<title>Rust Compiler Error Index</title>
<meta charset="utf-8">

<link rel="stylesheet" type="text/css" href="main.css"/>
<link rel="stylesheet" type="text/css" href="rust.css"/>

"##)?;
        Ok(())
    }

    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "
Rust Compiler Error Index
\n")?;
        Ok(())
    }

    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>> {
        // Enclose each error in a div so they can be shown/hidden en masse.
        let desc_desc = match info.description {
            Some(_) => "error-described",
            None => "error-undescribed",
        };
        let use_desc = match info.use_site {
            Some(_) => "error-used",
            None => "error-unused",
        };
        write!(output, "
", desc_desc, use_desc)?;

        // Error title (with self-link).
        write!(output,
               "
{0}
\n",
               err_code)?;

        // Description rendered as markdown.
        match info.description {
            Some(ref desc) => write!(output, "{}", Markdown(desc, MarkdownOutputStyle::Fancy))?,
            None => write!(output, "
No description.
\n")?,
        }

        write!(output, "
\n")?;
        Ok(())
    }

    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "\n")?;
        Ok(())
    }
}

impl Formatter for MarkdownFormatter {
    #[allow(unused_variables)]
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
        Ok(())
    }

    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "# Rust Compiler Error Index\n")?;
        Ok(())
    }

    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>> {
        Ok(match info.description {
            Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
            None => (),
        })
    }

    #[allow(unused_variables)]
    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
        Ok(())
    }
}

/// Load all the metadata files from `metadata_dir` into an in-memory map.
fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
    let mut all_errors = BTreeMap::new();

    for entry in read_dir(metadata_dir)? {
        let path = entry?.path();

        let mut metadata_str = String::new();
        File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str))?;

        let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;

        for (err_code, info) in some_errors {
            all_errors.insert(err_code, info);
        }
    }

    Ok(all_errors)
}

/// Output an HTML page for the errors in `err_map` to `output_path`.
fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
                                   formatter: T) -> Result<(), Box<Error>> {
    let mut output_file = File::create(output_path)?;

    formatter.header(&mut output_file)?;
    formatter.title(&mut output_file)?;

    for (err_code, info) in err_map {
        formatter.error_code_block(&mut output_file, info, err_code)?;
    }

    formatter.footer(&mut output_file)
}

fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<Error>> {
    let build_arch = env::var("CFG_BUILD")?;
    let metadata_dir = get_metadata_dir(&build_arch);
    let err_map = load_all_errors(&metadata_dir)?;
    match format {
        OutputFormat::Unknown(s)  => panic!("Unknown output format: {}", s),
        OutputFormat::HTML(h)     => render_error_page(&err_map, dst, h)?,
        OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
    }
    Ok(())
}

fn parse_args() -> (OutputFormat, PathBuf) {
    let mut args = env::args().skip(1);
    let format = args.next().map(|a| OutputFormat::from(&a))
                            .unwrap_or(OutputFormat::from("html"));
    let dst = args.next().map(PathBuf::from).unwrap_or_else(|| {
        match format {
            OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
            OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
            OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
        }
    });
    (format, dst)
}

fn main() {
    PLAYGROUND.with(|slot| {
        *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
    });
    let (format, dst) = parse_args();
    if let Err(e) = main_with_result(format, &dst) {
        panic!("{}", e.description());
    }
}
<commit_msg>Remove unused feature from error index generator<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(rustc_private)]

extern crate syntax;
extern crate rustdoc;
extern crate serialize as rustc_serialize;

use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fs::{read_dir, File};
use std::io::{Read, Write};
use std::path::Path;
use std::path::PathBuf;

use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};

use rustdoc::html::markdown::{Markdown, MarkdownOutputStyle, PLAYGROUND};
use rustc_serialize::json;

enum OutputFormat {
    HTML(HTMLFormatter),
    Markdown(MarkdownFormatter),
    Unknown(String),
}

impl OutputFormat {
    fn from(format: &str) -> OutputFormat {
        match &*format.to_lowercase() {
            "html"     => OutputFormat::HTML(HTMLFormatter),
            "markdown" => OutputFormat::Markdown(MarkdownFormatter),
            s          => OutputFormat::Unknown(s.to_owned()),
        }
    }
}

trait Formatter {
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>>;
    fn title(&self, output: &mut Write) -> Result<(), Box<Error>>;
    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>>;
    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>>;
}

struct HTMLFormatter;
struct MarkdownFormatter;

impl Formatter for HTMLFormatter {
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, r##"<!DOCTYPE html>

<title>Rust Compiler Error Index</title>
<meta charset="utf-8">

<link rel="stylesheet" type="text/css" href="main.css"/>
<link rel="stylesheet" type="text/css" href="rust.css"/>

"##)?;
        Ok(())
    }

    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "
Rust Compiler Error Index
\n")?;
        Ok(())
    }

    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>> {
        // Enclose each error in a div so they can be shown/hidden en masse.
        let desc_desc = match info.description {
            Some(_) => "error-described",
            None => "error-undescribed",
        };
        let use_desc = match info.use_site {
            Some(_) => "error-used",
            None => "error-unused",
        };
        write!(output, "
", desc_desc, use_desc)?;

        // Error title (with self-link).
        write!(output,
               "
{0}
\n",
               err_code)?;

        // Description rendered as markdown.
        match info.description {
            Some(ref desc) => write!(output, "{}", Markdown(desc, MarkdownOutputStyle::Fancy))?,
            None => write!(output, "
No description.
\n")?,
        }

        write!(output, "
\n")?;
        Ok(())
    }

    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "\n")?;
        Ok(())
    }
}

impl Formatter for MarkdownFormatter {
    #[allow(unused_variables)]
    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
        Ok(())
    }

    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
        write!(output, "# Rust Compiler Error Index\n")?;
        Ok(())
    }

    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
                        err_code: &str) -> Result<(), Box<Error>> {
        Ok(match info.description {
            Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
            None => (),
        })
    }

    #[allow(unused_variables)]
    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
        Ok(())
    }
}

/// Load all the metadata files from `metadata_dir` into an in-memory map.
fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
    let mut all_errors = BTreeMap::new();

    for entry in read_dir(metadata_dir)? {
        let path = entry?.path();

        let mut metadata_str = String::new();
        File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str))?;

        let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;

        for (err_code, info) in some_errors {
            all_errors.insert(err_code, info);
        }
    }

    Ok(all_errors)
}

/// Output an HTML page for the errors in `err_map` to `output_path`.
fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
                                   formatter: T) -> Result<(), Box<Error>> {
    let mut output_file = File::create(output_path)?;

    formatter.header(&mut output_file)?;
    formatter.title(&mut output_file)?;

    for (err_code, info) in err_map {
        formatter.error_code_block(&mut output_file, info, err_code)?;
    }

    formatter.footer(&mut output_file)
}

fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<Error>> {
    let build_arch = env::var("CFG_BUILD")?;
    let metadata_dir = get_metadata_dir(&build_arch);
    let err_map = load_all_errors(&metadata_dir)?;
    match format {
        OutputFormat::Unknown(s)  => panic!("Unknown output format: {}", s),
        OutputFormat::HTML(h)     => render_error_page(&err_map, dst, h)?,
        OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
    }
    Ok(())
}

fn parse_args() -> (OutputFormat, PathBuf) {
    let mut args = env::args().skip(1);
    let format = args.next().map(|a| OutputFormat::from(&a))
                            .unwrap_or(OutputFormat::from("html"));
    let dst = args.next().map(PathBuf::from).unwrap_or_else(|| {
        match format {
            OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
            OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
            OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
        }
    });
    (format, dst)
}

fn main() {
    PLAYGROUND.with(|slot| {
        *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
    });
    let (format, dst) = parse_args();
    if let Err(e) = main_with_result(format, &dst) {
        panic!("{}", e.description());
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else {
        const TARGET_VERSION: &'static str = "1.5.0";
    }
}

fn main() {
    let mut include_dirs = Vec::new();
    if let Ok(lib) = env::var("LIBGCRYPT_LIB") {
        if let Some(include) = env::var_os("LIBGCRYPT_INCLUDE_DIR") {
            include_dirs.push(include);
        }

        let mode = match env::var_os("LIBGCRYPT_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };

        build_shim(&include_dirs);
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("LIBGCRYPT_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if try_build() || try_config("libgcrypt-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    command.args(&["--cflags", "--libs"]);
    if let Some(output) = output(&mut command) {
        let mut include_dirs = Vec::new();
        parse_config_output(&output, &mut include_dirs);
        build_shim(&include_dirs);
        return true;
    }
    false
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libgcrypt");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--build", &host,
                "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    build_shim(&[
       PathBuf::from(gpgerror_root).join("include"),
       PathBuf::from(dst.clone()).join("include"),
    ]);

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=gcrypt");
    println!("cargo:root={}", &dst);
    true
}

#[cfg(feature = "shim")]
fn build_shim<P: AsRef<Path>>(include_dirs: &[P]) {
    let mut config = gcc::Config::new();
    for path in include_dirs.iter() {
        config.include(path);
    }
    config.flag("-Wno-deprecated-declarations").file("shim.c").compile("libgcrypt_shim.a");
}

#[cfg(not(feature = "shim"))]
fn build_shim<P: AsRef<Path>>(_include_dirs: &[P]) {}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("gcrypt version `{}` is less than requested `{}`",
                       version, TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn parse_config_output(output: &str, include_dirs: &mut Vec<OsString>) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-I" => include_dirs.push(val.into()),
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}
<commit_msg>Make build.rs compatible with msys<commit_after>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else {
        const TARGET_VERSION: &'static str = "1.5.0";
    }
}

fn main() {
    let mut include_dirs = Vec::new();
    if let Ok(lib) = env::var("LIBGCRYPT_LIB") {
        if let Some(include) = env::var_os("LIBGCRYPT_INCLUDE_DIR") {
            include_dirs.push(include);
        }

        let mode = match env::var_os("LIBGCRYPT_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };

        build_shim(&include_dirs);
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("LIBGCRYPT_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if try_build() || try_config("libgcrypt-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    command.args(&["--cflags", "--libs"]);
    if let Some(output) = output(&mut command) {
        let mut include_dirs = Vec::new();
        parse_config_output(&output, &mut include_dirs);
        build_shim(&include_dirs);
        return true;
    }
    false
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libgcrypt");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(msys_compatible(src.join("configure")))
        .args(&["--build", &gnu_target(&host),
                "--host", &gnu_target(&target),
                "--enable-static",
                "--disable-shared",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", &msys_compatible(&gpgerror_root)),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    build_shim(&[
       PathBuf::from(gpgerror_root).join("include"),
       PathBuf::from(dst.clone()).join("include"),
    ]);

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=gcrypt");
    println!("cargo:root={}", &dst);
    true
}

#[cfg(feature = "shim")]
fn build_shim<P: AsRef<Path>>(include_dirs: &[P]) {
    let mut config = gcc::Config::new();
    for path in include_dirs.iter() {
        config.include(msys_compatible(path));
    }
    config.flag("-Wno-deprecated-declarations").file("shim.c").compile("libgcrypt_shim.a");
}

#[cfg(not(feature = "shim"))]
fn build_shim<P: AsRef<Path>>(_include_dirs: &[P]) {}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("gcrypt version `{}` is less than requested `{}`",
                       version, TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn parse_config_output(output: &str, include_dirs: &mut Vec<OsString>) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-I" => include_dirs.push(val.into()),
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}

fn msys_compatible<P: AsRef<Path>>(path: P) -> String {
    let path = path.as_ref().to_str().unwrap();
    if !cfg!(windows) {
        return path.to_string();
    }
    path.replace("C:\\", "/c/").replace("\\", "/")
}

fn gnu_target(target: &str) -> String {
    match target {
        "i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(),
        "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(),
        s => s.to_string(),
    }
}
<|endoftext|>


<commit_before>use std::os;
use std::io::{Open, Write};
use std::io::stdio::{stdout, stderr};
use std::io::fs::File;
use std::default::Default;

use argparse::{ArgumentParser, Store, List, StoreTrue};
use collections::treemap::TreeMap;

use super::monitor::Monitor;
use super::env::Environ;
use super::linux::{ensure_dir, RunOptions, run_container, CPipe};
use super::options::env_options;
use libc::funcs::posix88::unistd::getuid;

pub fn run_chroot(env: &mut Environ, args: Vec<String>)
    -> Result<int, String>
{
    let mut root: Path = Path::new("");
    let mut command: String = "".to_string();
    let mut cmdargs: Vec<String> = Vec::new();
    let mut ropts: RunOptions = Default::default();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut root)
            .add_argument("newroot", box Store::<Path>,
                "The new root directory")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command");
        ap.refer(&mut ropts.writeable)
            .add_option(["--writeable"], box StoreTrue,
                "Mount container as writeable. Useful mostly in scripts \
                 building containers itself");
        ap.refer(&mut ropts.inventory)
            .add_option(["--inventory"], box StoreTrue,
                "Mount inventory folder of vagga inside container \
                 /tmp/vagga-inventory");
        env_options(env, &mut ap);
        ap.stop_on_first_argument(true);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&root.join(*dir)));
    }

    let mut runenv = TreeMap::new();
    for &(ref k, ref v) in os::env().iter() {
        runenv.insert(k.clone(), v.clone());
    }
    env.populate_environ(&mut runenv);

    let pipe = try!(CPipe::new());
    let mut monitor = Monitor::new(true);

    let pid = try!(run_container(&pipe, env, &root, &ropts,
        &env.work_dir, &command, cmdargs.as_slice(), &runenv));

    // TODO(tailhook) set uid map from config
    let uid = unsafe { getuid() };
    let uid_map = format!("0 {} 1", uid);
    debug!("Writing uid_map: {}", uid_map);
    match File::open_mode(&Path::new("/proc")
                      .join(pid.to_str())
                      .join("uid_map"), Open, Write)
            .write_str(uid_map.as_slice()) {
        Ok(()) => {}
        Err(e) => return Err(format!(
            "Error writing uid mapping: {}", e)),
    }

    try!(pipe.wakeup());

    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    return Ok(monitor.get_status());
}
<commit_msg>Safety check for `vagga _chroot`<commit_after>use std::os;
use std::io::{Open, Write};
use std::io::stdio::{stdout, stderr};
use std::io::fs::File;
use std::default::Default;

use argparse::{ArgumentParser, Store, List, StoreTrue};
use collections::treemap::TreeMap;

use super::monitor::Monitor;
use super::env::Environ;
use super::linux::{ensure_dir, RunOptions, run_container, CPipe};
use super::options::env_options;
use libc::funcs::posix88::unistd::getuid;

pub fn run_chroot(env: &mut Environ, args: Vec<String>)
    -> Result<int, String>
{
    let mut root: Path = Path::new("");
    let mut command: String = "".to_string();
    let mut cmdargs: Vec<String> = Vec::new();
    let mut ropts: RunOptions = Default::default();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut root)
            .add_argument("newroot", box Store::<Path>,
                "The new root directory")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command");
        ap.refer(&mut ropts.writeable)
            .add_option(["--writeable"], box StoreTrue,
                "Mount container as writeable. Useful mostly in scripts \
                 building containers itself");
        ap.refer(&mut ropts.inventory)
            .add_option(["--inventory"], box StoreTrue,
                "Mount inventory folder of vagga inside container \
                 /tmp/vagga-inventory");
        env_options(env, &mut ap);
        ap.stop_on_first_argument(true);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    if !env.project_root.is_ancestor_of(&root) {
        return Err(format!("Trying to chroot into wrong folder: {}",
            root.display()));
    }

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&root.join(*dir)));
    }

    let mut runenv = TreeMap::new();
    for &(ref k, ref v) in os::env().iter() {
        runenv.insert(k.clone(), v.clone());
    }
    env.populate_environ(&mut runenv);

    let pipe = try!(CPipe::new());
    let mut monitor = Monitor::new(true);

    let pid = try!(run_container(&pipe, env, &root, &ropts,
        &env.work_dir, &command, cmdargs.as_slice(), &runenv));

    // TODO(tailhook) set uid map from config
    let uid = unsafe { getuid() };
    let uid_map = format!("0 {} 1", uid);
    debug!("Writing uid_map: {}", uid_map);
    match File::open_mode(&Path::new("/proc")
                      .join(pid.to_str())
                      .join("uid_map"), Open, Write)
            .write_str(uid_map.as_slice()) {
        Ok(()) => {}
        Err(e) => return Err(format!(
            "Error writing uid mapping: {}", e)),
    }

    try!(pipe.wakeup());

    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    return Ok(monitor.get_status());
}
<|endoftext|>


<commit_before>#![deny(unused_import_braces, unused_imports,
        unused_comparisons, unused_must_use,
        unused_variables, non_shorthand_field_patterns,
        unreachable_code)]

extern crate trie;
extern crate sputnikvm;
extern crate sha3;
extern crate block;
extern crate rlp;
extern crate bigint;

use bigint::{H256, U256, M256, Address};
use sputnikvm::{ValidTransaction, HeaderParams, Memory, TransactionVM, VM,
                AccountCommitment, Patch, AccountState, AccountChange};
use sputnikvm::errors::{PreExecutionError, RequireError};
use sha3::{Keccak256, Digest};
use trie::{FixedSecureTrie, DatabaseGuard, MemoryDatabase, Database, DatabaseOwned};
use block::{Account, Transaction};
use std::collections::HashMap;
use std::cmp::min;

#[derive(Debug)]
pub struct Stateful<'a, D: 'a> {
    database: &'a D,
    root: H256,
}

impl<'a, D: 'a> Clone for Stateful<'a, D> {
    fn clone(&self) -> Self {
        Self {
            database: self.database.clone(),
            root: self.root.clone(),
        }
    }
}

impl<'a, D> Stateful<'a, D> {
    pub fn new(database: &'a D, root: H256) -> Self {
        Self {
            database,
            root
        }
    }

    pub fn empty(database: &'a D) -> Self {
        Self::new(database, MemoryDatabase::new().create_empty().root())
    }
}

impl<'b, D: DatabaseOwned> Stateful<'b, D> {
    fn is_empty_hash(hash: H256) -> bool {
        hash == H256::from(Keccak256::digest(&[]).as_slice())
    }

    pub fn code(&self, hash: H256) -> Option<Vec<u8>> {
        let code_hashes = self.database.create_guard();

        if Self::is_empty_hash(hash) {
            Some(Vec::new())
        } else {
            code_hashes.get(hash)
        }
    }

    pub fn step<V: VM>(
        &self, vm: &mut V, block_number: U256, most_recent_block_hashes: &[H256]
    ) {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block_number, U256::from(256)));

        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.step() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block_number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }
    }

    pub fn call<M: Memory + Default, P: Patch>(
        &self, transaction: ValidTransaction, block: HeaderParams,
        most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M, P> {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block.number, U256::from(256)));

        let mut vm = TransactionVM::new(transaction, block.clone());
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block.number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }

        vm
    }

    pub fn transit(
        &mut self, accounts: &[AccountChange]
    ) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for account in accounts {
            match account.clone() {
                AccountChange::Full {
                    nonce, address, balance, changing_storage, code
                } => {
                    let changing_storage: HashMap<U256, M256> = changing_storage.into();

                    let mut account: Account = state.get(&address).unwrap();

                    let mut storage_trie = self.database.create_fixed_secure_trie(account.storage_root);
                    for (key, value) in changing_storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    account.balance = balance;
                    account.nonce = nonce;
                    account.storage_root = storage_trie.root();
                    assert!(account.code_hash == H256::from(Keccak256::digest(&code).as_slice()));

                    state.insert(address, account);
                },
                AccountChange::IncreaseBalance(address, value) => {
                    match state.get(&address) {
                        Some(mut account) => {
                            account.balance = account.balance + value;
                            state.insert(address, account);
                        },
                        None => {
                            let account = Account {
                                nonce: U256::zero(),
                                balance: value,
                                storage_root: self.database.create_empty().root(),
                                code_hash: H256::from(Keccak256::digest(&[]).as_slice())
                            };
                            state.insert(address, account);
                        }
                    }
                },
                AccountChange::DecreaseBalance(address, value) => {
                    let mut account: Account = state.get(&address).unwrap();

                    account.balance = account.balance - value;

                    state.insert(address, account);
                },
                AccountChange::Create {
                    nonce, address, balance, storage, code, exists
                } => {
                    if !exists {
                        state.remove(&address);
                    } else {
                        let storage: HashMap<U256, M256> = storage.into();

                        let mut storage_trie = self.database.create_fixed_secure_empty();
                        for (key, value) in storage {
                            if value == M256::zero() {
                                storage_trie.remove(&H256::from(key));
                            } else {
                                storage_trie.insert(H256::from(key), value);
                            }
                        }

                        let code_hash = H256::from(Keccak256::digest(&code).as_slice());
                        code_hashes.set(code_hash, code);

                        let account = Account {
                            nonce: nonce,
                            balance: balance,
                            storage_root: storage_trie.root(),
                            code_hash
                        };

                        state.insert(address, account);
                    }
                },
            }
        }

        self.root = state.root();
    }

    pub fn execute<M: Memory + Default, P: Patch>(
        &mut self, transaction: ValidTransaction, block: HeaderParams,
        most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M, P> {
        let vm = self.call::<_, P>(transaction, block, most_recent_block_hashes);
        let mut accounts = Vec::new();
        for account in vm.accounts() {
            accounts.push(account.clone());
        }
        self.transit(&accounts);
        vm
    }

    pub fn to_valid<P: Patch>(
        &self, transaction: Transaction,
    ) -> Result<ValidTransaction, PreExecutionError> {
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();
        let mut account_state = AccountState::default();

        loop {
            match ValidTransaction::from_transaction::<P>(&transaction, &account_state) {
                Ok(val) => return val,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            account_state.commit(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(_)) => {
                    panic!()
                },
            }
        }
    }

    pub fn root(&self) -> H256 {
        self.root
    }

    pub fn state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.database.create_fixed_secure_trie::<Address, Account>(root)
    }

    pub fn state<'a>(&'a self) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.state_of(self.root())
    }

    pub fn storage_state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256> {
        self.database.create_fixed_secure_trie::<H256, M256>(root)
    }

    pub fn storage_state<'a>(&'a self, address: Address) -> Option<FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256>> {
        let state = self.state();
        let account = state.get(&address);

        match account {
            Some(account) => {
                Some(self.storage_state_of(account.storage_root))
            },
            None => None,
        }
    }
}

pub type MemoryStateful<'a> = Stateful<'a, MemoryDatabase>;
<commit_msg>Allow getting database from stateful<commit_after>#![deny(unused_import_braces, unused_imports,
        unused_comparisons, unused_must_use,
        unused_variables, non_shorthand_field_patterns,
        unreachable_code)]

extern crate trie;
extern crate sputnikvm;
extern crate sha3;
extern crate block;
extern crate rlp;
extern crate bigint;

use bigint::{H256, U256, M256, Address};
use sputnikvm::{ValidTransaction, HeaderParams, Memory, TransactionVM, VM,
                AccountCommitment, Patch, AccountState, AccountChange};
use sputnikvm::errors::{PreExecutionError, RequireError};
use sha3::{Keccak256, Digest};
use trie::{FixedSecureTrie, DatabaseGuard, MemoryDatabase, Database, DatabaseOwned};
use block::{Account, Transaction};
use std::collections::HashMap;
use std::cmp::min;

#[derive(Debug)]
pub struct Stateful<'a, D: 'a> {
    database: &'a D,
    root: H256,
}

impl<'a, D: 'a> Clone for Stateful<'a, D> {
    fn clone(&self) -> Self {
        Self {
            database: self.database.clone(),
            root: self.root.clone(),
        }
    }
}

impl<'a, D> Stateful<'a, D> {
    pub fn new(database: &'a D, root: H256) -> Self {
        Self {
            database,
            root
        }
    }

    pub fn empty(database: &'a D) -> Self {
        Self::new(database, MemoryDatabase::new().create_empty().root())
    }
}

impl<'b, D: DatabaseOwned> Stateful<'b, D> {
    fn is_empty_hash(hash: H256) -> bool {
        hash == H256::from(Keccak256::digest(&[]).as_slice())
    }

    pub fn database(&self) -> &'b D {
        self.database
    }

    pub fn code(&self, hash: H256) -> Option<Vec<u8>> {
        let code_hashes = self.database.create_guard();

        if Self::is_empty_hash(hash) {
            Some(Vec::new())
        } else {
            code_hashes.get(hash)
        }
    }

    pub fn step<V: VM>(
        &self, vm: &mut V, block_number: U256, most_recent_block_hashes: &[H256]
    ) {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block_number, U256::from(256)));

        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.step() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block_number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }
    }

    pub fn call<M: Memory + Default, P: Patch>(
        &self, transaction: ValidTransaction, block: HeaderParams,
        most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M, P> {
        assert!(U256::from(most_recent_block_hashes.len()) >=
                min(block.number, U256::from(256)));

        let mut vm = TransactionVM::new(transaction, block.clone());
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();

        loop {
            match vm.fire() {
                Ok(()) => break,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            vm.commit_account(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            vm.commit_account(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(number)) => {
                    let index = (block.number - number).as_usize();
                    vm.commit_blockhash(number, most_recent_block_hashes[index]).unwrap();
                },
            }
        }

        vm
    }

    pub fn transit(
        &mut self, accounts: &[AccountChange]
    ) {
        let mut state = self.database.create_fixed_secure_trie(self.root);
        let mut code_hashes = self.database.create_guard();

        for account in accounts {
            match account.clone() {
                AccountChange::Full {
                    nonce, address, balance, changing_storage, code
                } => {
                    let changing_storage: HashMap<U256, M256> = changing_storage.into();

                    let mut account: Account = state.get(&address).unwrap();

                    let mut storage_trie = self.database.create_fixed_secure_trie(account.storage_root);
                    for (key, value) in changing_storage {
                        if value == M256::zero() {
                            storage_trie.remove(&H256::from(key));
                        } else {
                            storage_trie.insert(H256::from(key), value);
                        }
                    }

                    account.balance = balance;
                    account.nonce = nonce;
                    account.storage_root = storage_trie.root();
                    assert!(account.code_hash == H256::from(Keccak256::digest(&code).as_slice()));

                    state.insert(address, account);
                },
                AccountChange::IncreaseBalance(address, value) => {
                    match state.get(&address) {
                        Some(mut account) => {
                            account.balance = account.balance + value;
                            state.insert(address, account);
                        },
                        None => {
                            let account = Account {
                                nonce: U256::zero(),
                                balance: value,
                                storage_root: self.database.create_empty().root(),
                                code_hash: H256::from(Keccak256::digest(&[]).as_slice())
                            };
                            state.insert(address, account);
                        }
                    }
                },
                AccountChange::DecreaseBalance(address, value) => {
                    let mut account: Account = state.get(&address).unwrap();

                    account.balance = account.balance - value;

                    state.insert(address, account);
                },
                AccountChange::Create {
                    nonce, address, balance, storage, code, exists
                } => {
                    if !exists {
                        state.remove(&address);
                    } else {
                        let storage: HashMap<U256, M256> = storage.into();

                        let mut storage_trie = self.database.create_fixed_secure_empty();
                        for (key, value) in storage {
                            if value == M256::zero() {
                                storage_trie.remove(&H256::from(key));
                            } else {
                                storage_trie.insert(H256::from(key), value);
                            }
                        }

                        let code_hash = H256::from(Keccak256::digest(&code).as_slice());
                        code_hashes.set(code_hash, code);

                        let account = Account {
                            nonce: nonce,
                            balance: balance,
                            storage_root: storage_trie.root(),
                            code_hash
                        };

                        state.insert(address, account);
                    }
                },
            }
        }

        self.root = state.root();
    }

    pub fn execute<M: Memory + Default, P: Patch>(
        &mut self, transaction: ValidTransaction, block: HeaderParams,
        most_recent_block_hashes: &[H256]
    ) -> TransactionVM<M, P> {
        let vm = self.call::<_, P>(transaction, block, most_recent_block_hashes);
        let mut accounts = Vec::new();
        for account in vm.accounts() {
            accounts.push(account.clone());
        }
        self.transit(&accounts);
        vm
    }

    pub fn to_valid<P: Patch>(
        &self, transaction: Transaction,
    ) -> Result<ValidTransaction, PreExecutionError> {
        let state = self.database.create_fixed_secure_trie(self.root);
        let code_hashes = self.database.create_guard();
        let mut account_state = AccountState::default();

        loop {
            match ValidTransaction::from_transaction::<P>(&transaction, &account_state) {
                Ok(val) => return val,
                Err(RequireError::Account(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Full {
                                nonce: account.nonce,
                                address: address,
                                balance: account.balance,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountCode(address)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let code = if Self::is_empty_hash(account.code_hash) {
                                Vec::new()
                            } else {
                                code_hashes.get(account.code_hash).unwrap()
                            };

                            account_state.commit(AccountCommitment::Code {
                                address: address,
                                code: code,
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    let account: Option<Account> = state.get(&address);

                    match account {
                        Some(account) => {
                            let storage = self.database.create_fixed_secure_trie(account.storage_root);
                            let value = storage.get(&H256::from(index)).unwrap_or(M256::zero());

                            account_state.commit(AccountCommitment::Storage {
                                address: address,
                                index, value
                            }).unwrap();
                        },
                        None => {
                            account_state.commit(AccountCommitment::Nonexist(address)).unwrap();
                        },
                    }
                },
                Err(RequireError::Blockhash(_)) => {
                    panic!()
                },
            }
        }
    }

    pub fn root(&self) -> H256 {
        self.root
    }

    pub fn state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.database.create_fixed_secure_trie::<Address, Account>(root)
    }

    pub fn state<'a>(&'a self) -> FixedSecureTrie<<D as Database<'a>>::Guard, Address, Account> {
        self.state_of(self.root())
    }

    pub fn storage_state_of<'a>(&'a self, root: H256) -> FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256> {
        self.database.create_fixed_secure_trie::<H256, M256>(root)
    }

    pub fn storage_state<'a>(&'a self, address: Address) -> Option<FixedSecureTrie<<D as Database<'a>>::Guard, H256, M256>> {
        let state = self.state();
        let account = state.get(&address);

        match account {
            Some(account) => {
                Some(self.storage_state_of(account.storage_root))
            },
            None => None,
        }
    }
}

pub type MemoryStateful<'a> = Stateful<'a, MemoryDatabase>;
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that the type of `sum` falls back to `i32` here,
// and that the for loop desugaring doesn't interfere with
// that.

// ignore-test

fn main() {
    let mut sum = 0;
    for i in Vec::new() {
        sum += i;
    }
}
<commit_msg>readd test<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that the type of `sum` falls back to `i32` here,
// and that the for loop desugaring doesn't interfere with
// that.

fn main() {
    let mut sum = 0;
    for i in Vec::new() {
        sum += &i;
    }
}
<|endoftext|>