<commit_before>use std::fs::{self, File, OpenOptions};
use std::io::*;
use std::io;
use std::path::{Path, PathBuf, Display};

use term::color::CYAN;
use fs2::{FileExt, lock_contended_error};

use util::{CargoResult, ChainError, Config, human};

pub struct FileLock {
    f: Option<File>,
    path: PathBuf,
    state: State,
}

#[derive(PartialEq)]
enum State {
    Unlocked,
    Shared,
    Exclusive,
}

impl FileLock {
    /// Returns the underlying file handle of this lock.
    pub fn file(&self) -> &File {
        self.f.as_ref().unwrap()
    }

    /// Returns the underlying path that this lock points to.
    ///
    /// Note that special care must be taken to ensure that the path is not
    /// referenced outside the lifetime of this lock.
    pub fn path(&self) -> &Path {
        assert!(self.state != State::Unlocked);
        &self.path
    }

    /// Returns the parent path containing this file
    pub fn parent(&self) -> &Path {
        assert!(self.state != State::Unlocked);
        self.path.parent().unwrap()
    }

    /// Removes all sibling files to this locked file.
    ///
    /// This can be useful if a directory is locked with a sentinel file but it
    /// needs to be cleared out as it may be corrupt.
    pub fn remove_siblings(&self) -> io::Result<()> {
        let path = self.path();
        for entry in try!(path.parent().unwrap().read_dir()) {
            let entry = try!(entry);
            if Some(&entry.file_name()[..]) == path.file_name() {
                continue
            }
            let kind = try!(entry.file_type());
            if kind.is_dir() {
                try!(fs::remove_dir_all(entry.path()));
            } else {
                try!(fs::remove_file(entry.path()));
            }
        }
        Ok(())
    }
}

impl Read for FileLock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file().read(buf)
    }
}

impl Seek for FileLock {
    fn seek(&mut self, to: SeekFrom) -> io::Result<u64> {
        self.file().seek(to)
    }
}

impl Write for FileLock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file().flush()
    }
}

impl Drop for FileLock {
    fn drop(&mut self) {
        if self.state != State::Unlocked {
            if let Some(f) = self.f.take() {
                let _ = f.unlock();
            }
        }
    }
}

/// A "filesystem" is intended to be a globally shared, hence locked, resource
/// in Cargo.
///
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
#[derive(Clone, Debug)]
pub struct Filesystem {
    root: PathBuf,
}

impl Filesystem {
    /// Creates a new filesystem to be rooted at the given path.
    pub fn new(path: PathBuf) -> Filesystem {
        Filesystem { root: path }
    }

    /// Like `Path::join`, creates a new filesystem rooted at this filesystem
    /// joined with the given path.
    pub fn join<T: AsRef<Path>>(&self, other: T) -> Filesystem {
        Filesystem::new(self.root.join(other))
    }

    /// Like `Path::push`, pushes a new path component onto this filesystem.
    pub fn push<T: AsRef<Path>>(&mut self, other: T) {
        self.root.push(other);
    }

    /// Consumes this filesystem and returns the underlying `PathBuf`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn into_path_unlocked(self) -> PathBuf {
        self.root
    }

    /// Creates the directory pointed to by this filesystem.
    ///
    /// Handles errors where other Cargo processes are also attempting to
    /// concurrently create this directory.
    pub fn create_dir(&self) -> io::Result<()> {
        return create_dir_all(&self.root);
    }

    /// Returns an adaptor that can be used to print the path of this
    /// filesystem.
    pub fn display(&self) -> Display {
        self.root.display()
    }

    /// Opens exclusive access to a file, returning the locked version of a
    /// file.
    ///
    /// This function will create a file at `path` if it doesn't already exist
    /// (including intermediate directories), and then it will acquire an
    /// exclusive lock on `path`. If the process must block waiting for the
    /// lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has
    /// read/write access to the underlying file.
    pub fn open_rw<P>(&self,
                      path: P,
                      config: &Config,
                      msg: &str) -> CargoResult<FileLock>
        where P: AsRef<Path>
    {
        self.open(path.as_ref(),
                  OpenOptions::new().read(true).write(true).create(true),
                  State::Exclusive,
                  config,
                  msg)
    }

    /// Opens shared access to a file, returning the locked version of a file.
    ///
    /// This function will fail if `path` doesn't already exist, but if it does
    /// then it will acquire a shared lock on `path`. If the process must block
    /// waiting for the lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has read
    /// access to the underlying file. Any writes to the file will return an
    /// error.
    pub fn open_ro<P>(&self,
                      path: P,
                      config: &Config,
                      msg: &str) -> CargoResult<FileLock>
        where P: AsRef<Path>
    {
        self.open(path.as_ref(),
                  OpenOptions::new().read(true),
                  State::Shared,
                  config,
                  msg)
    }

    fn open(&self,
            path: &Path,
            opts: &OpenOptions,
            state: State,
            config: &Config,
            msg: &str) -> CargoResult<FileLock> {
        let path = self.root.join(path);

        // If we want an exclusive lock then if we fail because of NotFound it's
        // likely because an intermediate directory didn't exist, so try to
        // create the directory and then continue.
        let f = try!(opts.open(&path).or_else(|e| {
            if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
                try!(create_dir_all(path.parent().unwrap()));
                opts.open(&path)
            } else {
                Err(e)
            }
        }).chain_error(|| {
            human(format!("failed to open: {}", path.display()))
        }));
        match state {
            State::Exclusive => {
                try!(acquire(config, msg, &path,
                             &|| f.try_lock_exclusive(),
                             &|| f.lock_exclusive()));
            }
            State::Shared => {
                try!(acquire(config, msg, &path,
                             &|| f.try_lock_shared(),
                             &|| f.lock_shared()));
            }
            State::Unlocked => {}

        }
        Ok(FileLock { f: Some(f), path: path, state: state })
    }
}

/// Acquires a lock on a file in a "nice" manner.
///
/// Almost all long-running blocking actions in Cargo have a status message
/// associated with them as we're not sure how long they'll take. Whenever a
/// conflicted file lock happens, this is the case (we're not sure when the lock
/// will be released).
///
/// This function will acquire the lock on a `path`, printing out a nice message
/// to the console if we have to wait for it. It will first attempt to use `try`
/// to acquire a lock on the crate, and in the case of contention it will emit a
/// status message based on `msg` to `config`'s shell, and then use `block` to
/// block waiting to acquire a lock.
///
/// Returns an error if the lock could not be acquired or if any error other
/// than a contention error happens.
fn acquire(config: &Config,
           msg: &str,
           path: &Path,
           try: &Fn() -> io::Result<()>,
           block: &Fn() -> io::Result<()>) -> CargoResult<()> {
    match try() {
        Ok(()) => return Ok(()),
        Err(e) => {
            if e.raw_os_error() != lock_contended_error().raw_os_error() {
                return Err(human(e)).chain_error(|| {
                    human(format!("failed to lock file: {}", path.display()))
                })
            }
        }
    }
    let msg = format!("waiting for file lock on {}", msg);
    try!(config.shell().err().say_status("Blocking", &msg, CYAN, true));

    block().chain_error(|| {
        human(format!("failed to lock file: {}", path.display()))
    })
}

fn create_dir_all(path: &Path) -> io::Result<()> {
    match create_dir(path) {
        Ok(()) => return Ok(()),
        Err(e) => {
            if e.kind() == io::ErrorKind::NotFound {
                if let Some(p) = path.parent() {
                    return create_dir_all(p).and_then(|()| create_dir(path))
                }
            }
            Err(e)
        }
    }
}

fn create_dir(path: &Path) -> io::Result<()> {
    match fs::create_dir(path) {
        Ok(()) => Ok(()),
        Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
        Err(e) => Err(e),
    }
}
<commit_msg>Auto merge of #2623 - alexcrichton:nfs-lol-no, r=brson<commit_after>use std::fs::{self, File, OpenOptions};
use std::io::*;
use std::io;
use std::path::{Path, PathBuf, Display};

use term::color::CYAN;
use fs2::{FileExt, lock_contended_error};

use util::{CargoResult, ChainError, Config, human};

pub struct FileLock {
    f: Option<File>,
    path: PathBuf,
    state: State,
}

#[derive(PartialEq)]
enum State {
    Unlocked,
    Shared,
    Exclusive,
}

impl FileLock {
    /// Returns the underlying file handle of this lock.
    pub fn file(&self) -> &File {
        self.f.as_ref().unwrap()
    }

    /// Returns the underlying path that this lock points to.
    ///
    /// Note that special care must be taken to ensure that the path is not
    /// referenced outside the lifetime of this lock.
    pub fn path(&self) -> &Path {
        assert!(self.state != State::Unlocked);
        &self.path
    }

    /// Returns the parent path containing this file
    pub fn parent(&self) -> &Path {
        assert!(self.state != State::Unlocked);
        self.path.parent().unwrap()
    }

    /// Removes all sibling files to this locked file.
    ///
    /// This can be useful if a directory is locked with a sentinel file but it
    /// needs to be cleared out as it may be corrupt.
    pub fn remove_siblings(&self) -> io::Result<()> {
        let path = self.path();
        for entry in try!(path.parent().unwrap().read_dir()) {
            let entry = try!(entry);
            if Some(&entry.file_name()[..]) == path.file_name() {
                continue
            }
            let kind = try!(entry.file_type());
            if kind.is_dir() {
                try!(fs::remove_dir_all(entry.path()));
            } else {
                try!(fs::remove_file(entry.path()));
            }
        }
        Ok(())
    }
}

impl Read for FileLock {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.file().read(buf)
    }
}

impl Seek for FileLock {
    fn seek(&mut self, to: SeekFrom) -> io::Result<u64> {
        self.file().seek(to)
    }
}

impl Write for FileLock {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.file().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.file().flush()
    }
}

impl Drop for FileLock {
    fn drop(&mut self) {
        if self.state != State::Unlocked {
            if let Some(f) = self.f.take() {
                let _ = f.unlock();
            }
        }
    }
}

/// A "filesystem" is intended to be a globally shared, hence locked, resource
/// in Cargo.
///
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
#[derive(Clone, Debug)]
pub struct Filesystem {
    root: PathBuf,
}

impl Filesystem {
    /// Creates a new filesystem to be rooted at the given path.
    pub fn new(path: PathBuf) -> Filesystem {
        Filesystem { root: path }
    }

    /// Like `Path::join`, creates a new filesystem rooted at this filesystem
    /// joined with the given path.
    pub fn join<T: AsRef<Path>>(&self, other: T) -> Filesystem {
        Filesystem::new(self.root.join(other))
    }

    /// Like `Path::push`, pushes a new path component onto this filesystem.
    pub fn push<T: AsRef<Path>>(&mut self, other: T) {
        self.root.push(other);
    }

    /// Consumes this filesystem and returns the underlying `PathBuf`.
    ///
    /// Note that this is a relatively dangerous operation and should be used
    /// with great caution!.
    pub fn into_path_unlocked(self) -> PathBuf {
        self.root
    }

    /// Creates the directory pointed to by this filesystem.
    ///
    /// Handles errors where other Cargo processes are also attempting to
    /// concurrently create this directory.
    pub fn create_dir(&self) -> io::Result<()> {
        return create_dir_all(&self.root);
    }

    /// Returns an adaptor that can be used to print the path of this
    /// filesystem.
    pub fn display(&self) -> Display {
        self.root.display()
    }

    /// Opens exclusive access to a file, returning the locked version of a
    /// file.
    ///
    /// This function will create a file at `path` if it doesn't already exist
    /// (including intermediate directories), and then it will acquire an
    /// exclusive lock on `path`. If the process must block waiting for the
    /// lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has
    /// read/write access to the underlying file.
    pub fn open_rw<P>(&self,
                      path: P,
                      config: &Config,
                      msg: &str) -> CargoResult<FileLock>
        where P: AsRef<Path>
    {
        self.open(path.as_ref(),
                  OpenOptions::new().read(true).write(true).create(true),
                  State::Exclusive,
                  config,
                  msg)
    }

    /// Opens shared access to a file, returning the locked version of a file.
    ///
    /// This function will fail if `path` doesn't already exist, but if it does
    /// then it will acquire a shared lock on `path`. If the process must block
    /// waiting for the lock, the `msg` is printed to `config`.
    ///
    /// The returned file can be accessed to look at the path and also has read
    /// access to the underlying file. Any writes to the file will return an
    /// error.
    pub fn open_ro<P>(&self,
                      path: P,
                      config: &Config,
                      msg: &str) -> CargoResult<FileLock>
        where P: AsRef<Path>
    {
        self.open(path.as_ref(),
                  OpenOptions::new().read(true),
                  State::Shared,
                  config,
                  msg)
    }

    fn open(&self,
            path: &Path,
            opts: &OpenOptions,
            state: State,
            config: &Config,
            msg: &str) -> CargoResult<FileLock> {
        let path = self.root.join(path);

        // If we want an exclusive lock then if we fail because of NotFound it's
        // likely because an intermediate directory didn't exist, so try to
        // create the directory and then continue.
        let f = try!(opts.open(&path).or_else(|e| {
            if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
                try!(create_dir_all(path.parent().unwrap()));
                opts.open(&path)
            } else {
                Err(e)
            }
        }).chain_error(|| {
            human(format!("failed to open: {}", path.display()))
        }));
        match state {
            State::Exclusive => {
                try!(acquire(config, msg, &path,
                             &|| f.try_lock_exclusive(),
                             &|| f.lock_exclusive()));
            }
            State::Shared => {
                try!(acquire(config, msg, &path,
                             &|| f.try_lock_shared(),
                             &|| f.lock_shared()));
            }
            State::Unlocked => {}

        }
        Ok(FileLock { f: Some(f), path: path, state: state })
    }
}

/// Acquires a lock on a file in a "nice" manner.
///
/// Almost all long-running blocking actions in Cargo have a status message
/// associated with them as we're not sure how long they'll take. Whenever a
/// conflicted file lock happens, this is the case (we're not sure when the lock
/// will be released).
///
/// This function will acquire the lock on a `path`, printing out a nice message
/// to the console if we have to wait for it. It will first attempt to use `try`
/// to acquire a lock on the crate, and in the case of contention it will emit a
/// status message based on `msg` to `config`'s shell, and then use `block` to
/// block waiting to acquire a lock.
///
/// Returns an error if the lock could not be acquired or if any error other
/// than a contention error happens.
fn acquire(config: &Config,
           msg: &str,
           path: &Path,
           try: &Fn() -> io::Result<()>,
           block: &Fn() -> io::Result<()>) -> CargoResult<()> {

    // File locking on Unix is currently implemented via `flock`, which is known
    // to be broken on NFS. We could in theory just ignore errors that happen on
    // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
    // forever**, even if the nonblocking flag is passed!
    //
    // As a result, we just skip all file locks entirely on NFS mounts. That
    // should avoid calling any `flock` functions at all, and it wouldn't work
    // there anyway.
    //
    // [1]: https://github.com/rust-lang/cargo/issues/2615
    if is_on_nfs_mount(path) {
        return Ok(())
    }

    match try() {
        Ok(()) => return Ok(()),
        Err(e) => {
            if e.raw_os_error() != lock_contended_error().raw_os_error() {
                return Err(human(e)).chain_error(|| {
                    human(format!("failed to lock file: {}", path.display()))
                })
            }
        }
    }
    let msg = format!("waiting for file lock on {}", msg);
    try!(config.shell().err().say_status("Blocking", &msg, CYAN, true));

    return block().chain_error(|| {
        human(format!("failed to lock file: {}", path.display()))
    });

    #[cfg(target_os = "linux")]
    fn is_on_nfs_mount(path: &Path) -> bool {
        use std::ffi::CString;
        use std::mem;
        use std::os::unix::prelude::*;
        use libc;

        let path = match CString::new(path.as_os_str().as_bytes()) {
            Ok(path) => path,
            Err(_) => return false,
        };

        unsafe {
            let mut buf: libc::statfs = mem::zeroed();
            let r = libc::statfs(path.as_ptr(), &mut buf);

            r == 0 && buf.f_type == libc::NFS_SUPER_MAGIC
        }
    }

    #[cfg(not(target_os = "linux"))]
    fn is_on_nfs_mount(_path: &Path) -> bool {
        false
    }
}

fn create_dir_all(path: &Path) -> io::Result<()> {
    match create_dir(path) {
        Ok(()) => return Ok(()),
        Err(e) => {
            if e.kind() == io::ErrorKind::NotFound {
                if let Some(p) = path.parent() {
                    return create_dir_all(p).and_then(|()| create_dir(path))
                }
            }
            Err(e)
        }
    }
}

fn create_dir(path: &Path) -> io::Result<()> {
    match fs::create_dir(path) {
        Ok(()) => Ok(()),
        Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
        Err(e) => Err(e),
    }
}
<|endoftext|>


<commit_before>use x86_64::structures::paging as pg;
use x86_64::structures::paging::page_table::PageTableFlags as Flags;

use super::super::paging::PageMap;
use super::super::prelude::*;

#[derive(Debug)]
pub struct Stack {
    pub top: PhysAddr,
    pub bottom: PhysAddr,
}

impl Stack {
    fn new(top: PhysAddr, bottom: PhysAddr) -> Stack {
        assert!(top > bottom);
        Stack { top, bottom }
    }
}

pub struct StackAllocator {
    range: pg::page::PageRangeInclusive<pg::Size2MiB>,
}

impl StackAllocator {
    pub fn new(range: pg::page::PageRangeInclusive<pg::Size2MiB>) -> Self {
        Self { range }
    }

    /// Requires that the kernel page table is active
    pub fn alloc_stack<A: pg::FrameAllocator<pg::Size2MiB>>(
        &mut self, page_map: &mut PageMap, frame_allocator: &mut A, size_in_pages: usize,
    ) -> Option<Stack> {
        assert!(size_in_pages > 0);

        // Copy the range, since we only want to change it on success
        let mut range = self.range;

        // try to allocate the stack pages and a guard page
        let guard_page = range.next();

        let stack_start = range.next();
        let stack_end = if size_in_pages == 1 {
            stack_start
        } else {
            // index starts at 0 and we have already allocated the start page
            range.nth(size_in_pages - 2)
        };

        if let (Some(_), Some(start), Some(end)) = (guard_page, stack_start, stack_end) {
            // Success

            // Write back updated range
            self.range = range;

            // Map stack pages to physical frames
            for page in Page::range_inclusive(start, end) {
                let frame = frame_allocator
                    .allocate_frame()
                    .expect("Could not allocate stack frame");

                unsafe {
                    page_map
                        .map_to(
                            PT_VADDR,
                            page,
                            frame,
                            Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
                        )
                        .flush();
                }
            }

            // Create a new stack
            let new_top = end.start_address() + Page::SIZE;
            // TODO: Check that virtual and physical addresses match, or map
            Some(Stack::new(
                PhysAddr::new(new_top.as_u64()),
                PhysAddr::new(start.start_address().as_u64()),
            ))
        } else {
            // Not enough pages
            None
        }
    }
}
<commit_msg>MM: Change kernel stack allocator to use VirtAddrs<commit_after>use x86_64::structures::paging as pg;
use x86_64::structures::paging::page_table::PageTableFlags as Flags;

use super::super::paging::PageMap;
use super::super::prelude::*;

#[derive(Debug)]
pub struct Stack {
    pub top: VirtAddr,
    pub bottom: VirtAddr,
}

impl Stack {
    fn new(top: VirtAddr, bottom: VirtAddr) -> Stack {
        assert!(top > bottom);
        Stack { top, bottom }
    }
}

/// Doesn't (need to) support deallocation
pub struct StackAllocator {
    range: pg::page::PageRangeInclusive<pg::Size2MiB>,
}

impl StackAllocator {
    pub fn new(range: pg::page::PageRangeInclusive<pg::Size2MiB>) -> Self {
        Self { range }
    }

    /// Allocates a new stack, including a guard page, and maps it.
    /// Requires that the kernel page table is active.
    pub fn alloc_stack<A: pg::FrameAllocator<pg::Size2MiB>>(
        &mut self, page_map: &mut PageMap, frame_allocator: &mut A, size_in_pages: usize,
    ) -> Option<Stack> {
        assert!(size_in_pages > 0);

        // Copy the range, since we only want to change it on success
        let mut range = self.range;

        // try to allocate the stack pages and a guard page
        let guard_page = range.next();

        let stack_start = range.next();
        let stack_end = if size_in_pages == 1 {
            stack_start
        } else {
            // index starts at 0 and we have already allocated the start page
            range.nth(size_in_pages - 2)
        };

        if let (Some(_), Some(start), Some(end)) = (guard_page, stack_start, stack_end) {
            // Success

            // Write back updated range
            self.range = range;

            // Map stack pages to physical frames
            for page in Page::range_inclusive(start, end) {
                let frame = frame_allocator
                    .allocate_frame()
                    .expect("Could not allocate stack frame");

                unsafe {
                    page_map
                        .map_to(
                            PT_VADDR,
                            page,
                            frame,
                            Flags::PRESENT | Flags::WRITABLE | Flags::NO_EXECUTE,
                        )
                        .flush();
                }
            }

            // Create a new stack
            let new_top = end.start_address() + Page::SIZE;
            Some(Stack::new(new_top, start.start_address()))
        } else {
            // Not enough pages
            None
        }
    }
}
<|endoftext|>


<commit_before>//! Node attributes.

use std::io::Read;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Warning};
use parser::binary::event::NodeHeader;
pub use self::array::{ArrayAttribute, ArrayAttributeReader};
pub use self::special::{SpecialAttribute, SpecialAttributeType};

mod array;
mod special;

/// Node attribute.
#[derive(Debug)]
pub struct Attributes<'a, R: 'a> {
    /// Number of all attributes.
    num_attributes: u64,
    /// Number of rest attributes.
    rest_attributes: u64,
    /// End offset of the previous attribute.
    prev_attr_end: Option<u64>,
    /// Parser.
    parser: &'a mut BinaryParser<R>,
}

impl<'a, R: 'a + Read> Attributes<'a, R> {
    /// Returns number of all attributes.
    pub fn num_attributes(&self) -> u64 {
        self.num_attributes
    }

    /// Returns number of rest attributes.
    pub fn rest_attributes(&self) -> u64 {
        self.rest_attributes
    }

    /// Returns the next attribute if available.
    pub fn next_attribute(&mut self) -> Result<Option<Attribute<R>>> {
        if self.rest_attributes == 0 {
            return Ok(None);
        }

        // Skip unread part of the previous attribute if available.
        if let Some(prev_attr_end) = self.prev_attr_end {
            try!(self.parser.source.skip_to(prev_attr_end));
            self.prev_attr_end = None;
        }

        self.rest_attributes -= 1;
        let type_code = try!(self.parser.source.read_u8());
        let position = self.parser.source.count();
        match type_code {
            // Primitive type attributes.
            b'C' => {
                let raw = try!(self.parser.source.read_u8());
                let val = (raw & 0x01) == 1;
                if raw != b'T' && raw != b'Y' {
                    self.parser.warn(Warning::InvalidBooleanAttributeValue {
                        got: raw,
                        assumed: val,
                        position: position,
                    });
                }
                Ok(Some(Attribute::Primitive(PrimitiveAttribute::Bool(val))))
            },
            b'Y' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I16(try!(self.parser.source.read_i16()))))),
            b'I' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I32(try!(self.parser.source.read_i32()))))),
            b'L' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::I64(try!(self.parser.source.read_i64()))))),
            b'F' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F32(try!(self.parser.source.read_f32()))))),
            b'D' => Ok(Some(Attribute::Primitive(PrimitiveAttribute::F64(try!(self.parser.source.read_f64()))))),
            // Special type attributes.
            b'R' | b'S' => {
                let (attr, end_offset) = try!(special::read_special_attribute(self.parser, type_code));
                self.prev_attr_end = Some(end_offset);
                Ok(Some(Attribute::Special(attr)))
            },
            // Array type attributes.
            b'b' | b'i' | b'l' | b'f' | b'd' => {
                let (attr, end_offset) = try!(array::read_array_attribute(self.parser, type_code));
                self.prev_attr_end = Some(end_offset);
                Ok(Some(Attribute::Array(attr)))
            },
            // Unknown type attributes.
            _ => unimplemented!(),
        }
    }
}

/// Creates a new `Attributes`.
pub fn new_attributes<'a, R: 'a>(
    parser: &'a mut BinaryParser<R>,
    header: &NodeHeader
) -> Attributes<'a, R> {
    Attributes {
        num_attributes: header.num_attributes,
        rest_attributes: header.num_attributes,
        prev_attr_end: None,
        parser: parser,
    }
}

/// Node attribute.
#[derive(Debug)]
pub enum Attribute<'a, R: 'a> {
    /// Primitive type value.
    Primitive(PrimitiveAttribute),
    /// Array type value.
    Array(ArrayAttribute<'a, R>),
    /// Special type value.
    Special(SpecialAttribute<'a, R>),
}

/// Attribute type of primitive value.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PrimitiveAttribute {
    /// Single `bool`.
    Bool(bool),
    /// Single `i16`.
    I16(i16),
    /// Single `i32`.
    I32(i32),
    /// Single `i64`.
    I64(i64),
    /// Single `f32`.
    F32(f32),
    /// Single `f64`.
    F64(f64),
}

impl PrimitiveAttribute {
    /// Converts floating-point number into `f32`.
    pub fn as_f32(&self) -> Option<f32> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v),
            PrimitiveAttribute::F64(v) => Some(v as f32),
            _ => None,
        }
    }

    /// Converts floating-point number into `f32`.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v as f64),
            PrimitiveAttribute::F64(v) => Some(v),
            _ => None,
        }
    }
}
<commit_msg>Simplify code by implementing `From<*> for Attribute`<commit_after>//! Node attributes.

use std::io::Read;

use parser::binary::BinaryParser;
use parser::binary::error::{Result, Warning};
use parser::binary::event::NodeHeader;
pub use self::array::{ArrayAttribute, ArrayAttributeReader};
pub use self::special::{SpecialAttribute, SpecialAttributeType};

mod array;
mod special;

/// Node attribute.
#[derive(Debug)]
pub struct Attributes<'a, R: 'a> {
    /// Number of all attributes.
    num_attributes: u64,
    /// Number of rest attributes.
    rest_attributes: u64,
    /// End offset of the previous attribute.
    prev_attr_end: Option<u64>,
    /// Parser.
    parser: &'a mut BinaryParser<R>,
}

impl<'a, R: 'a + Read> Attributes<'a, R> {
    /// Returns number of all attributes.
    pub fn num_attributes(&self) -> u64 {
        self.num_attributes
    }

    /// Returns number of rest attributes.
    pub fn rest_attributes(&self) -> u64 {
        self.rest_attributes
    }

    /// Returns the next attribute if available.
    pub fn next_attribute(&mut self) -> Result<Option<Attribute<R>>> {
        if self.rest_attributes == 0 {
            return Ok(None);
        }

        // Skip unread part of the previous attribute if available.
        if let Some(prev_attr_end) = self.prev_attr_end {
            try!(self.parser.source.skip_to(prev_attr_end));
            self.prev_attr_end = None;
        }

        self.rest_attributes -= 1;
        let type_code = try!(self.parser.source.read_u8());
        let position = self.parser.source.count();
        match type_code {
            // Primitive type attributes.
            b'C' => {
                let raw = try!(self.parser.source.read_u8());
                let val = (raw & 0x01) == 1;
                if raw != b'T' && raw != b'Y' {
                    self.parser.warn(Warning::InvalidBooleanAttributeValue {
                        got: raw,
                        assumed: val,
                        position: position,
                    });
                }
                Ok(Some(PrimitiveAttribute::Bool(val).into()))
            },
            b'Y' => Ok(Some(PrimitiveAttribute::I16(try!(self.parser.source.read_i16())).into())),
            b'I' => Ok(Some(PrimitiveAttribute::I32(try!(self.parser.source.read_i32())).into())),
            b'L' => Ok(Some(PrimitiveAttribute::I64(try!(self.parser.source.read_i64())).into())),
            b'F' => Ok(Some(PrimitiveAttribute::F32(try!(self.parser.source.read_f32())).into())),
            b'D' => Ok(Some(PrimitiveAttribute::F64(try!(self.parser.source.read_f64())).into())),
            // Special type attributes.
            b'R' | b'S' => {
                let (attr, end_offset) = try!(special::read_special_attribute(self.parser, type_code));
                self.prev_attr_end = Some(end_offset);
                Ok(Some(attr.into()))
            },
            // Array type attributes.
            b'b' | b'i' | b'l' | b'f' | b'd' => {
                let (attr, end_offset) = try!(array::read_array_attribute(self.parser, type_code));
                self.prev_attr_end = Some(end_offset);
                Ok(Some(attr.into()))
            },
            // Unknown type attributes.
            _ => unimplemented!(),
        }
    }
}

impl<'a, R: 'a> From<PrimitiveAttribute> for Attribute<'a, R> {
    fn from(a: PrimitiveAttribute) -> Self {
        Attribute::Primitive(a)
    }
}

impl<'a, R: 'a> From<SpecialAttribute<'a, R>> for Attribute<'a, R> {
    fn from(a: SpecialAttribute<'a, R>) -> Self {
        Attribute::Special(a)
    }
}

impl<'a, R: 'a> From<ArrayAttribute<'a, R>> for Attribute<'a, R> {
    fn from(a: ArrayAttribute<'a, R>) -> Self {
        Attribute::Array(a)
    }
}

/// Creates a new `Attributes`.
pub fn new_attributes<'a, R: 'a>(
    parser: &'a mut BinaryParser<R>,
    header: &NodeHeader
) -> Attributes<'a, R> {
    Attributes {
        num_attributes: header.num_attributes,
        rest_attributes: header.num_attributes,
        prev_attr_end: None,
        parser: parser,
    }
}

/// Node attribute.
#[derive(Debug)]
pub enum Attribute<'a, R: 'a> {
    /// Primitive type value.
    Primitive(PrimitiveAttribute),
    /// Array type value.
    Array(ArrayAttribute<'a, R>),
    /// Special type value.
    Special(SpecialAttribute<'a, R>),
}

/// Attribute type of primitive value.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PrimitiveAttribute {
    /// Single `bool`.
    Bool(bool),
    /// Single `i16`.
    I16(i16),
    /// Single `i32`.
    I32(i32),
    /// Single `i64`.
    I64(i64),
    /// Single `f32`.
    F32(f32),
    /// Single `f64`.
    F64(f64),
}

impl PrimitiveAttribute {
    /// Converts floating-point number into `f32`.
    pub fn as_f32(&self) -> Option<f32> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v),
            PrimitiveAttribute::F64(v) => Some(v as f32),
            _ => None,
        }
    }

    /// Converts floating-point number into `f32`.
    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            PrimitiveAttribute::F32(v) => Some(v as f64),
            PrimitiveAttribute::F64(v) => Some(v),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::{Result, Write};
use case::CaseExt;

use analysis::ref_mode::RefMode;
use analysis::rust_type::parameter_rust_type;
use codegen::general::{self, version_condition};
use env::Env;
use file_saver::*;
use library::*;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use traits::*;
use version::Version;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  env.config.target_path.join(file_name_sys("lib"));

    println!("Generating file {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_lib(w, env));
}

fn generate_lib(w: &mut Write, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib(w));
    }
    match &*env.config.library_name {
        "GLib" => try!(statics::only_for_glib(w)),
        "GObject" => try!(statics::only_for_gobject(w)),
        "Gtk" => try!(statics::only_for_gtk(w)),
        _ => (),
    }
    try!(writeln!(w, ""));

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let records = prepare(ns);
    let classes = prepare(ns);
    let interfaces = prepare(ns);
    let bitfields = prepare(ns);
    let enums = prepare(ns);
    let unions = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, env, &enums));
    try!(generate_constants(w, env, &ns.constants));
    try!(generate_bitfields(w, env, &bitfields));
    try!(generate_unions(w, &unions));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &records));
    try!(generate_classes_structs(w, env, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_enums_funcs(w, env, &enums));
    try!(functions::generate_bitfields_funcs(w, env, &bitfields));
    try!(functions::generate_unions_funcs(w, env, &unions));
    try!(functions::generate_records_funcs(w, env, &records));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));
    try!(functions::generate_other_funcs(w, env, &ns.functions));

    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates(w: &mut Write, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0};", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &Namespace) -> Vec<&T>
where Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases(w: &mut Write, env: &Env, items: &[&Alias])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Aliases"));
    }
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            x @ Err(..) => ("//", x.into_string()),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }
    if !items.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_bitfields(w: &mut Write, env: &Env, items: &[&Bitfield])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Flags"));
    }
    for item in items {
        let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
        let config = env.config.objects.get(&full_name);

        try!(writeln!(w, "bitflags! {{\n\t#[repr(C)]\n\tflags {}: c_uint {{", item.c_type));
        for member in &item.members {
            let member_config = config.as_ref()
                .map(|c| c.members.matched(&member.name)).unwrap_or(vec![]);
            let version = member_config.iter().filter_map(|m| m.version).next();

            try!(version_condition(w, env, version, false, 2));
            let val: i64 = member.value.parse().unwrap();
            try!(writeln!(w, "\t\tconst {} = {},", member.c_identifier, val as u32));
        }
        try!(writeln!(w, "\t}}\n}}"));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_constants(w: &mut Write, env: &Env, constants: &[Constant]) -> Result<()> {
    if !constants.is_empty() {
        try!(writeln!(w, "// Constants"));
    }
    for constant in constants {
        let direction = ParameterDirection::In;
        let ref_mode = RefMode::of(&env.library, constant.typ, direction);
        let (mut comment, mut type_) =
            match parameter_rust_type(env, constant.typ, direction,
                                      Nullable(false), ref_mode) {
                Ok(x) => ("", x),
                x @ Err(..) => ("//", x.into_string()),
            };
        if env.type_status_sys(&format!("{}.{}", env.config.library_name,
            constant.name)).ignored() {
            comment = "//";
        }
        let mut value = constant.value.clone();
        if type_ == "&str" {
            type_ = "&'static str".into();
            value = format!("r##\"{}\"##", value);
        } else if type_ == "Glyph" && env.config.library_name == "Pango"  {
            //Fix single constant alias
            type_ = "PangoGlyph".into();
        }
        try!(writeln!(w, "{}pub const {}: {} = {};", comment,
            constant.c_identifier, type_, value));
    }
    if !constants.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums(w: &mut Write, env: &Env, items: &[&Enumeration])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Enums"));
    }
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = c_int;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
        let config = env.config.objects.get(&full_name);

        let mut vals: HashMap<String, (String, Option<Version>)> = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.c_type));
        for member in &item.members {
            let member_config = config.as_ref()
                .map(|c| c.members.matched(&member.name)).unwrap_or(vec![]);
            let is_alias = member_config.iter().any(|m| m.alias);
            let version = member_config.iter().filter_map(|m| m.version).next();

            if is_alias || vals.get(&member.value).is_some() {
                continue;
            }
            try!(version_condition(w, env, version, false, 1));
            try!(writeln!(w, "\t{} = {},",
                          &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), (member.name.clone(), version.clone()));
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            if let Some(&(ref value, version)) = vals.get(&member.value) {
                try!(version_condition(w, env, version, false, 0));
                try!(writeln!(w, "pub const {}: {} = {1}::{};", member.c_identifier, item.c_type,
                          &prepare_enum_member_name(value)));
            }
        }
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions(w: &mut Write, items: &[&Union])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Unions"));
    }
    for item in items {
        if let Some(ref c_type) = item.c_type {
            try!(writeln!(w, "pub type {} = c_void; // union", c_type));
        }
    }
    if !items.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs(w: &mut Write, env: &Env, classes: &[&Class]) -> Result<()> {
    if !classes.is_empty() {
        try!(writeln!(w, "// Classes"));
    }
    for klass in classes {
        let (lines, commented) = generate_fields(env, &klass.name, &klass.fields);

        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{comment}#[repr(C)]\n{comment}pub struct {name}(c_void);\n", comment=comment, name=klass.c_type));
        }
        else {
            try!(writeln!(w, "{comment}#[repr(C)]\n{comment}pub struct {name} {{", comment=comment, name=klass.c_type));

            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }

    Ok(())
}

fn generate_interfaces_structs(w: &mut Write, interfaces: &[&Interface]) -> Result<()> {
    if !interfaces.is_empty() {
        try!(writeln!(w, "// Interfaces"));
    }
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {}(c_void);", interface.c_type));
    }
    if !interfaces.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_records(w: &mut Write, env: &Env, records: &[&Record]) -> Result<()> {
    if !records.is_empty() {
        try!(writeln!(w, "// Records"));
    }
    for record in records {
        let (lines, commented) = generate_fields(env, &record.name, &record.fields);

        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {}(c_void);\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}

fn generate_fields(env: &Env, struct_name: &str, fields: &[Field]) -> (Vec<String>, bool){
    let mut lines = Vec::new();
    let mut commented = false;
    let mut truncated = false;

    for field in fields {
        let is_union = env.library.type_(field.typ).maybe_ref_as::<Union>().is_some();
        let is_bits = field.bits.is_some();
        if !truncated && (is_union || is_bits) {
            warn!("Field `{}::{}` not expressible in Rust, truncated",
                  struct_name, field.name);
            lines.push(format!("\t_truncated_record_marker: c_void,"));
            truncated = true;
        }
        if truncated {
            if is_union {
                lines.push(format!("\t//union,"));
            }
            else {
                let bits = field.bits.map(|n| format!(": {}", n)).unwrap_or("".into());
                lines.push(
                    format!("\t//{}: {}{},", field.name,
                            field.c_type.as_ref().map(|s| &s[..]).unwrap_or("fn"), bits));
            };
            continue;
        }

        let vis = if field.private { "" } else { "pub " };

        if let Some(ref c_type) = field.c_type {
            let name = mangle_keywords(&*field.name);
            let c_type = ffi_type(env, field.typ, c_type);
            if c_type.is_err() {
                commented = true;
            }
            lines.push(format!("\t{}{}: {},", vis, name, c_type.into_string()));
        }
        else {
            let name = mangle_keywords(&*field.name);
            if let Some(ref func) =
                env.library.type_(field.typ).maybe_ref_as::<Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("\t{}{}: Option<unsafe extern \"C\" fn{}>,", vis, name, sig));
                    commented |= com;
                }
            else if let Some(c_type) = env.library.type_(field.typ).get_glib_name() {
                warn!("Field `{}::{}` missing c:type assumed `{}`",
                      struct_name, field.name, c_type);
                let c_type = ffi_type(env, field.typ, c_type);
                if c_type.is_err() {
                    commented = true;
                }
                lines.push(format!("\t{}{}: {},", vis, name, c_type.into_string()));
            }
            else {
                lines.push(format!("\t{}{}: [{:?} {}],",
                                   vis, name, field.typ, field.typ.full_name(&env.library)));
                commented = true;
            }
        }
    }
    (lines, commented)
}
<commit_msg>Fix GValue generation in gobject-sys<commit_after>use std::collections::HashMap;
use std::io::{Result, Write};
use case::CaseExt;

use analysis::ref_mode::RefMode;
use analysis::rust_type::parameter_rust_type;
use codegen::general::{self, version_condition};
use env::Env;
use file_saver::*;
use library::*;
use nameutil::*;
use super::ffi_type::ffi_type;
use super::functions;
use super::statics;
use traits::*;
use version::Version;

pub fn generate(env: &Env) {
    println!("generating sys for {}", env.config.library_name);

    let path =  env.config.target_path.join(file_name_sys("lib"));

    println!("Generating file {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_lib(w, env));
}

fn generate_lib(w: &mut Write, env: &Env) -> Result<()>{
    try!(general::start_comments(w, &env.config));
    try!(statics::begin(w));

    try!(generate_extern_crates(w, env));
    try!(statics::after_extern_crates(w));

    if env.config.library_name != "GLib" {
        try!(statics::use_glib(w));
    }
    match &*env.config.library_name {
        "GLib" => try!(statics::only_for_glib(w)),
        "GObject" => try!(statics::only_for_gobject(w)),
        "Gtk" => try!(statics::only_for_gtk(w)),
        _ => (),
    }
    try!(writeln!(w, ""));

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let records = prepare(ns);
    let classes = prepare(ns);
    let interfaces = prepare(ns);
    let bitfields = prepare(ns);
    let enums = prepare(ns);
    let unions = prepare(ns);

    try!(generate_aliases(w, env, &prepare(ns)));
    try!(generate_enums(w, env, &enums));
    try!(generate_constants(w, env, &ns.constants));
    try!(generate_bitfields(w, env, &bitfields));
    try!(generate_unions(w, &unions));
    try!(functions::generate_callbacks(w, env, &prepare(ns)));
    try!(generate_records(w, env, &records));
    try!(generate_classes_structs(w, env, &classes));
    try!(generate_interfaces_structs(w, &interfaces));

    try!(writeln!(w, "extern \"C\" {{"));
    try!(functions::generate_enums_funcs(w, env, &enums));
    try!(functions::generate_bitfields_funcs(w, env, &bitfields));
    try!(functions::generate_unions_funcs(w, env, &unions));
    try!(functions::generate_records_funcs(w, env, &records));
    try!(functions::generate_classes_funcs(w, env, &classes));
    try!(functions::generate_interfaces_funcs(w, env, &interfaces));
    try!(functions::generate_other_funcs(w, env, &ns.functions));

    try!(writeln!(w, "\n}}"));

    Ok(())
}

fn generate_extern_crates(w: &mut Write, env: &Env) -> Result<()>{
    for library_name in &env.config.external_libraries {
        try!(writeln!(w, "extern crate {0}_sys as {0};", crate_name(library_name)));
    }

    Ok(())
}

fn prepare<T: Ord>(ns: &Namespace) -> Vec<&T>
where Type: MaybeRef<T> {
    let mut vec: Vec<&T> = Vec::with_capacity(ns.types.len());
    for typ in ns.types.iter().filter_map(|t| t.as_ref()) {
        if let Some(ref x) = typ.maybe_ref() {
            vec.push(x);
        }
    }
    vec.sort();
    vec
}

fn generate_aliases(w: &mut Write, env: &Env, items: &[&Alias])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Aliases"));
    }
    for item in items {
        let (comment, c_type) = match ffi_type(env, item.typ, &item.target_c_type) {
            Ok(x) => ("", x),
            x @ Err(..) => ("//", x.into_string()),
        };
        try!(writeln!(w, "{}pub type {} = {};", comment, item.c_identifier, c_type));
    }
    if !items.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_bitfields(w: &mut Write, env: &Env, items: &[&Bitfield])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Flags"));
    }
    for item in items {
        let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
        let config = env.config.objects.get(&full_name);

        try!(writeln!(w, "bitflags! {{\n\t#[repr(C)]\n\tflags {}: c_uint {{", item.c_type));
        for member in &item.members {
            let member_config = config.as_ref()
                .map(|c| c.members.matched(&member.name)).unwrap_or(vec![]);
            let version = member_config.iter().filter_map(|m| m.version).next();

            try!(version_condition(w, env, version, false, 2));
            let val: i64 = member.value.parse().unwrap();
            try!(writeln!(w, "\t\tconst {} = {},", member.c_identifier, val as u32));
        }
        try!(writeln!(w, "\t}}\n}}"));
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_constants(w: &mut Write, env: &Env, constants: &[Constant]) -> Result<()> {
    if !constants.is_empty() {
        try!(writeln!(w, "// Constants"));
    }
    for constant in constants {
        let direction = ParameterDirection::In;
        let ref_mode = RefMode::of(&env.library, constant.typ, direction);
        let (mut comment, mut type_) =
            match parameter_rust_type(env, constant.typ, direction,
                                      Nullable(false), ref_mode) {
                Ok(x) => ("", x),
                x @ Err(..) => ("//", x.into_string()),
            };
        if env.type_status_sys(&format!("{}.{}", env.config.library_name,
            constant.name)).ignored() {
            comment = "//";
        }
        let mut value = constant.value.clone();
        if type_ == "&str" {
            type_ = "&'static str".into();
            value = format!("r##\"{}\"##", value);
        } else if type_ == "Glyph" && env.config.library_name == "Pango"  {
            //Fix single constant alias
            type_ = "PangoGlyph".into();
        }
        try!(writeln!(w, "{}pub const {}: {} = {};", comment,
            constant.c_identifier, type_, value));
    }
    if !constants.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_enums(w: &mut Write, env: &Env, items: &[&Enumeration])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Enums"));
    }
    for item in items {
        if item.members.len() == 1 {
            try!(writeln!(w, "pub type {} = c_int;", item.name));
            try!(writeln!(w, "pub const {}: {} = {};",
                          item.members[0].c_identifier, item.name, item.members[0].value));
            try!(writeln!(w, "pub type {} = {};", item.c_type, item.name));
            try!(writeln!(w, ""));
            continue;
        }

        let full_name = format!("{}.{}", env.namespaces.main().name, item.name);
        let config = env.config.objects.get(&full_name);

        let mut vals: HashMap<String, (String, Option<Version>)> = HashMap::new();
        try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(C)]"));
        try!(writeln!(w, "pub enum {} {{", item.c_type));
        for member in &item.members {
            let member_config = config.as_ref()
                .map(|c| c.members.matched(&member.name)).unwrap_or(vec![]);
            let is_alias = member_config.iter().any(|m| m.alias);
            let version = member_config.iter().filter_map(|m| m.version).next();

            if is_alias || vals.get(&member.value).is_some() {
                continue;
            }
            try!(version_condition(w, env, version, false, 1));
            try!(writeln!(w, "\t{} = {},",
                          &prepare_enum_member_name(&member.name), member.value));
            vals.insert(member.value.clone(), (member.name.clone(), version.clone()));
        }
        try!(writeln!(w, "}}"));
        for member in &item.members {
            if let Some(&(ref value, version)) = vals.get(&member.value) {
                try!(version_condition(w, env, version, false, 0));
                try!(writeln!(w, "pub const {}: {} = {1}::{};", member.c_identifier, item.c_type,
                          &prepare_enum_member_name(value)));
            }
        }
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_unions(w: &mut Write, items: &[&Union])
        -> Result<()> {
    if !items.is_empty() {
        try!(writeln!(w, "// Unions"));
    }
    for item in items {
        if let Some(ref c_type) = item.c_type {
            try!(writeln!(w, "pub type {} = c_void; // union", c_type));
        }
    }
    if !items.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn prepare_enum_member_name(name: &str) -> String {
    let cameled = name.to_camel();
    if name.chars().next().unwrap().is_digit(10) {
        format!("_{}", cameled)
    } else {
        cameled
    }
}

fn generate_classes_structs(w: &mut Write, env: &Env, classes: &[&Class]) -> Result<()> {
    if !classes.is_empty() {
        try!(writeln!(w, "// Classes"));
    }
    for klass in classes {
        let (lines, commented) = generate_fields(env, &klass.name, &klass.fields);

        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{comment}#[repr(C)]\n{comment}pub struct {name}(c_void);\n", comment=comment, name=klass.c_type));
        }
        else {
            try!(writeln!(w, "{comment}#[repr(C)]\n{comment}pub struct {name} {{", comment=comment, name=klass.c_type));

            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }

    Ok(())
}

fn generate_interfaces_structs(w: &mut Write, interfaces: &[&Interface]) -> Result<()> {
    if !interfaces.is_empty() {
        try!(writeln!(w, "// Interfaces"));
    }
    for interface in interfaces {
        try!(writeln!(w, "#[repr(C)]\npub struct {}(c_void);", interface.c_type));
    }
    if !interfaces.is_empty() {
        try!(writeln!(w, ""));
    }

    Ok(())
}

fn generate_records(w: &mut Write, env: &Env, records: &[&Record]) -> Result<()> {
    if !records.is_empty() {
        try!(writeln!(w, "// Records"));
    }
    for record in records {
        let (lines, commented) = generate_fields(env, &record.name, &record.fields);

        let comment = if commented { "//" } else { "" };
        if lines.is_empty() {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {}(c_void);\n", comment, record.c_type));
        }
        else {
            try!(writeln!(w, "{}#[repr(C)]\n{0}pub struct {} {{", comment, record.c_type));
            for line in lines {
                try!(writeln!(w, "{}{}", comment, line));
            }
            try!(writeln!(w, "{}}}\n", comment));
        }
    }
    Ok(())
}

fn generate_fields(env: &Env, struct_name: &str, fields: &[Field]) -> (Vec<String>, bool){
    let mut lines = Vec::new();
    let mut commented = false;
    let mut truncated = false;

    //TODO: remove after GObject-2.0.gir fixed
    // Fix for wrong GValue size on i686-pc-windows-gnu due `c:type="gpointer"` in data field
    // instead guint64
    let is_gvalue = struct_name == "Value";

    for field in fields {
        let is_union = env.library.type_(field.typ).maybe_ref_as::<Union>().is_some();
        let is_bits = field.bits.is_some();
        if !truncated && (is_union || is_bits) {
            warn!("Field `{}::{}` not expressible in Rust, truncated",
                  struct_name, field.name);
            lines.push(format!("\t_truncated_record_marker: c_void,"));
            truncated = true;
        }
        if truncated {
            if is_union {
                lines.push(format!("\t//union,"));
            }
            else {
                let bits = field.bits.map(|n| format!(": {}", n)).unwrap_or("".into());
                lines.push(
                    format!("\t//{}: {}{},", field.name,
                            field.c_type.as_ref().map(|s| &s[..]).unwrap_or("fn"), bits));
            };
            continue;
        }

        let vis = if field.private { "" } else { "pub " };

        if let Some(ref c_type) = field.c_type {
            let name = mangle_keywords(&*field.name);
            let mut c_type = ffi_type(env, field.typ, c_type);
            if c_type.is_err() {
                commented = true;
            }
            if is_gvalue && field.name == "data" {
                c_type = Ok("[u64; 2]".to_owned());
            }
            lines.push(format!("\t{}{}: {},", vis, name, c_type.into_string()));
        }
        else {
            let name = mangle_keywords(&*field.name);
            if let Some(ref func) =
                env.library.type_(field.typ).maybe_ref_as::<Function>() {
                    let (com, sig) = functions::function_signature(env, func, true);
                    lines.push(format!("\t{}{}: Option<unsafe extern \"C\" fn{}>,", vis, name, sig));
                    commented |= com;
                }
            else if let Some(c_type) = env.library.type_(field.typ).get_glib_name() {
                warn!("Field `{}::{}` missing c:type assumed `{}`",
                      struct_name, field.name, c_type);
                let c_type = ffi_type(env, field.typ, c_type);
                if c_type.is_err() {
                    commented = true;
                }
                lines.push(format!("\t{}{}: {},", vis, name, c_type.into_string()));
            }
            else {
                lines.push(format!("\t{}{}: [{:?} {}],",
                                   vis, name, field.typ, field.typ.full_name(&env.library)));
                commented = true;
            }
        }
    }
    (lines, commented)
}
<|endoftext|>


<commit_before>// run-pass
// ignore-cloudabi
// ignore-emscripten
// ignore-sgx

#![feature(rustc_private)]
#![feature(setgroups)]

extern crate libc;
use std::process::Command;
use std::os::unix::process::CommandExt;

fn main() {
    let max_ngroups = unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) };
    let max_ngroups = max_ngroups as u32 + 1;
    let vec: Vec<u32> = (0..max_ngroups).collect();
    let p = Command::new("/bin/id").groups(&vec[..]).spawn();
    assert!(p.is_err());
}
<commit_msg>Update src/test/ui/command/command-setgroups.rs<commit_after>// run-pass
// ignore-cloudabi
// ignore-emscripten
// ignore-sgx

#![feature(rustc_private)]
#![feature(setgroups)]

extern crate libc;
use std::process::Command;
use std::os::unix::process::CommandExt;

fn main() {
    #[cfg(unix)]
    run()
}

#[cfg(unix)]
fn run() {
    let max_ngroups = unsafe { libc::sysconf(libc::_SC_NGROUPS_MAX) };
    let max_ngroups = max_ngroups as u32 + 1;
    let vec: Vec<u32> = (0..max_ngroups).collect();
    let p = Command::new("/bin/id").groups(&vec[..]).spawn();
    assert!(p.is_err());
}
<|endoftext|>


<commit_before>// run-pass

use std::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        let zero: Wrapping<$type> = Wrapping(0);
        let one: Wrapping<$type> = Wrapping(1);
        let min: Wrapping<$type> = Wrapping($min);
        let max: Wrapping<$type> = Wrapping($max);

        wrapping_operation!(min, max + one);
        wrapping_assignment!(min, max += one);
        wrapping_operation!(max, min - one);
        wrapping_assignment!(max, min -= one);
        wrapping_operation!(max, max * one);
        wrapping_assignment!(max, max *= one);
        wrapping_operation!(max, max / one);
        wrapping_assignment!(max, max /= one);
        wrapping_operation!(zero, max % one);
        wrapping_assignment!(zero, max %= one);
        wrapping_operation!(zero, zero & max);
        wrapping_assignment!(zero, zero &= max);
        wrapping_operation!(max, zero | max);
        wrapping_assignment!(max, zero |= max);
        wrapping_operation!(zero, max ^ max);
        wrapping_assignment!(zero, max ^= max);
        wrapping_operation!(zero, zero << 1usize);
        wrapping_assignment!(zero, zero <<= 1usize);
        wrapping_operation!(zero, zero >> 1usize);
        wrapping_assignment!(zero, zero >>= 1usize);
        wrapping_operation!(zero, -zero);
        wrapping_operation!(max, !min);
    };
}

fn main() {
    wrapping_test!(i8, std::i8::MIN, std::i8::MAX);
    wrapping_test!(i16, std::i16::MIN, std::i16::MAX);
    wrapping_test!(i32, std::i32::MIN, std::i32::MAX);
    wrapping_test!(i64, std::i64::MIN, std::i64::MAX);
    wrapping_test!(i128, std::i128::MIN, std::i128::MAX);
    wrapping_test!(isize, std::isize::MIN, std::isize::MAX);
    wrapping_test!(u8, std::u8::MIN, std::u8::MAX);
    wrapping_test!(u16, std::u16::MIN, std::u16::MAX);
    wrapping_test!(u32, std::u32::MIN, std::u32::MAX);
    wrapping_test!(u64, std::u64::MIN, std::u64::MAX);
    wrapping_test!(u128, std::u128::MIN, std::u128::MAX);
    wrapping_test!(usize, std::usize::MIN, std::usize::MAX);
}
<commit_msg>Skip wrapping arithmetic tests on emscripten for 128 bit integers<commit_after>// run-pass

use std::num::Wrapping;

macro_rules! wrapping_operation {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        assert_eq!($result, $lhs $op $rhs);
        assert_eq!($result, &$lhs $op $rhs);
        assert_eq!($result, $lhs $op &$rhs);
        assert_eq!($result, &$lhs $op &$rhs);
    };
    ($result:expr, $op:tt $expr:expr) => {
        assert_eq!($result, $op $expr);
        assert_eq!($result, $op &$expr);
    };
}

macro_rules! wrapping_assignment {
    ($result:expr, $lhs:ident $op:tt $rhs:expr) => {
        let mut lhs1 = $lhs;
        lhs1 $op $rhs;
        assert_eq!($result, lhs1);

        let mut lhs2 = $lhs;
        lhs2 $op &$rhs;
        assert_eq!($result, lhs2);
    };
}

macro_rules! wrapping_test {
    ($type:ty, $min:expr, $max:expr) => {
        let zero: Wrapping<$type> = Wrapping(0);
        let one: Wrapping<$type> = Wrapping(1);
        let min: Wrapping<$type> = Wrapping($min);
        let max: Wrapping<$type> = Wrapping($max);

        wrapping_operation!(min, max + one);
        wrapping_assignment!(min, max += one);
        wrapping_operation!(max, min - one);
        wrapping_assignment!(max, min -= one);
        wrapping_operation!(max, max * one);
        wrapping_assignment!(max, max *= one);
        wrapping_operation!(max, max / one);
        wrapping_assignment!(max, max /= one);
        wrapping_operation!(zero, max % one);
        wrapping_assignment!(zero, max %= one);
        wrapping_operation!(zero, zero & max);
        wrapping_assignment!(zero, zero &= max);
        wrapping_operation!(max, zero | max);
        wrapping_assignment!(max, zero |= max);
        wrapping_operation!(zero, max ^ max);
        wrapping_assignment!(zero, max ^= max);
        wrapping_operation!(zero, zero << 1usize);
        wrapping_assignment!(zero, zero <<= 1usize);
        wrapping_operation!(zero, zero >> 1usize);
        wrapping_assignment!(zero, zero >>= 1usize);
        wrapping_operation!(zero, -zero);
        wrapping_operation!(max, !min);
    };
}

fn main() {
    wrapping_test!(i8, std::i8::MIN, std::i8::MAX);
    wrapping_test!(i16, std::i16::MIN, std::i16::MAX);
    wrapping_test!(i32, std::i32::MIN, std::i32::MAX);
    wrapping_test!(i64, std::i64::MIN, std::i64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(i128, std::i128::MIN, std::i128::MAX);
    wrapping_test!(isize, std::isize::MIN, std::isize::MAX);
    wrapping_test!(u8, std::u8::MIN, std::u8::MAX);
    wrapping_test!(u16, std::u16::MIN, std::u16::MAX);
    wrapping_test!(u32, std::u32::MIN, std::u32::MAX);
    wrapping_test!(u64, std::u64::MIN, std::u64::MAX);
    #[cfg(not(target_os = "emscripten"))]
    wrapping_test!(u128, std::u128::MIN, std::u128::MAX);
    wrapping_test!(usize, std::usize::MIN, std::usize::MAX);
}
<|endoftext|>


<commit_before>use std::net::IpAddr;
use std::path::PathBuf;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::os::unix::io::RawFd;

use regex::{Regex, Captures};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use quire::validate::{Structure, Sequence, Scalar, Numeric, Enum};
use quire::validate::{Mapping, Nothing};
use id_map::{IdMap, IdMapExt, mapping_validator};

use sandbox_config::SandboxConfig;
use utils::{in_range};

pub const DEFAULT_KILL_TIMEOUT: f32 = 5.;

lazy_static! {
    static ref VARIABLE_REGEX: Regex = Regex::new(r#"@\{[^}]*\}"#).unwrap();
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct TmpfsInfo {
    pub size: usize,
    pub mode: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct PersistentInfo {
    pub path: PathBuf,
    pub mkdir: bool,
    pub mode: u32,
    pub user: u32,
    pub group: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct StatedirInfo {
    pub path: PathBuf,
    pub mode: u32,
    pub user: u32,
    pub group: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub enum Volume {
    Readonly(PathBuf),
    Persistent(PersistentInfo),
    Tmpfs(TmpfsInfo),
    Statedir(StatedirInfo),
}

#[derive(RustcDecodable, RustcEncodable, Serialize, Debug, PartialEq, Eq)]
#[derive(Clone, Copy)]
pub enum ContainerKind {
    Daemon,
    Command,
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct ResolvConf {
    pub copy_from_host: bool,
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct HostsFile {
    pub copy_from_host: bool,
    pub localhost: Option<bool>,
    pub public_hostname: Option<bool>,
}

#[derive(Clone, Debug)]
pub struct Host(pub IpAddr);

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct TcpPort {
    pub host: Host,
    pub fd: RawFd,
    pub reuse_addr: bool,
    pub reuse_port: bool,
    pub listen_backlog: usize,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub enum Variable {
    TcpPort,
}

#[derive(RustcDecodable)]
pub struct ContainerConfig {
    pub kind: ContainerKind,
    pub variables: BTreeMap<String, Variable>,
    pub volumes: BTreeMap<String, Volume>,
    pub user_id: u32,
    pub group_id: u32,
    pub restart_timeout: f32,
    pub kill_timeout: f32,
    pub memory_limit: u64,
    pub fileno_limit: u64,
    pub cpu_shares: usize,
    pub executable: String,
    pub arguments: Vec<String>,
    pub environ: BTreeMap<String, String>,
    pub workdir: PathBuf,
    pub resolv_conf: ResolvConf,
    pub hosts_file: HostsFile,
    pub uid_map: Vec<IdMap>,
    pub gid_map: Vec<IdMap>,
    pub stdout_stderr_file: Option<PathBuf>,
    pub interactive: bool,
    pub restart_process_only: bool,
    pub tcp_ports: HashMap<String, TcpPort>,
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct InstantiatedConfig {
    pub kind: ContainerKind,
    pub volumes: BTreeMap<String, Volume>,
    pub user_id: u32,
    pub group_id: u32,
    pub restart_timeout: f32,
    pub kill_timeout: f32,
    pub memory_limit: u64,
    pub fileno_limit: u64,
    pub cpu_shares: usize,
    pub executable: String,
    pub arguments: Vec<String>,
    pub environ: BTreeMap<String, String>,
    pub workdir: PathBuf,
    pub resolv_conf: ResolvConf,
    pub hosts_file: HostsFile,
    pub uid_map: Vec<IdMap>,
    pub gid_map: Vec<IdMap>,
    pub stdout_stderr_file: Option<PathBuf>,
    pub interactive: bool,
    pub restart_process_only: bool,
    pub tcp_ports: HashMap<u16, TcpPort>,
}

pub struct Variables<'a> {
    pub user_vars: &'a HashMap<String, String>,
    pub lithos_name: &'a str,
    pub lithos_config_filename: &'a str,
}

impl InstantiatedConfig {
    pub fn map_uid(&self, internal_uid: u32) -> Option<u32> {
        self.uid_map.map_id(internal_uid)
    }
    pub fn map_gid(&self, internal_gid: u32) -> Option<u32> {
        self.gid_map.map_id(internal_gid)
    }
}

impl ContainerConfig {
    pub fn validator<'x>() -> Structure<'x> {
        Structure::new()
        .member("kind", Scalar::new().default("Daemon"))
        .member("variables", Mapping::new(
            Scalar::new(),
            Enum::new()
                .option("TcpPort", Nothing)
        ))
        .member("volumes", Mapping::new(
                Scalar::new(),
                volume_validator()))
        .member("user_id", Numeric::new())
        .member("group_id", Numeric::new().default(0))
        .member("memory_limit", Numeric::new().default(0x7fffffffffffffffi64))
        .member("fileno_limit", Numeric::new().default(1024))
        .member("cpu_shares", Numeric::new().default(1024))
        .member("restart_timeout", Numeric::new().min(0).max(86400).default(1))
        .member("kill_timeout",
            Numeric::new().min(0).max(86400)
                .default(DEFAULT_KILL_TIMEOUT as i64))
        .member("executable", Scalar::new())
        .member("arguments", Sequence::new(Scalar::new()))
        .member("environ", Mapping::new(
                Scalar::new(),
                Scalar::new()))
        .member("workdir", Scalar::new().default("/"))
        .member("resolv_conf", Structure::new()
            .member("copy_from_host", Scalar::new().default(true)))
        .member("hosts_file", Structure::new()
            .member("copy_from_host", Scalar::new().default(false))
            .member("localhost", Scalar::new().optional())
            .member("public_hostname", Scalar::new().optional()))
        .member("uid_map", mapping_validator())
        .member("gid_map", mapping_validator())
        .member("stdout_stderr_file", Scalar::new().optional())
        .member("interactive", Scalar::new().default(false))
        .member("restart_process_only", Scalar::new().default(false))
        .member("tcp_ports", Mapping::new(
            Numeric::new().min(1).max(65535),
            Structure::new()
                .member("host", Scalar::new().default("0.0.0.0"))
                .member("fd", Numeric::new().min(0).optional())
                .member("reuse_addr", Scalar::new().default(true))
                .member("reuse_port", Scalar::new().default(false))
                .member("listen_backlog", Scalar::new().default(128))
            ))
    }
    pub fn instantiate(&self, variables: &Variables)
        -> Result<InstantiatedConfig, Vec<String>>
    {
        let mut errors1 = HashSet::new();
        let mut errors2 = HashSet::new();
        let result = {
            let mut replacer = |capt: &Captures| {
                let varname = capt.get(0).unwrap().as_str();
                let val = variables.user_vars.get(varname).map(|x| x.clone())
                    .or_else(|| match varname {
                        "lithos:name"
                        => Some(variables.lithos_name.to_string()),
                        "lithos:config_filename"
                        => Some(variables.lithos_config_filename.to_string()),
                        _ => None,
                    });
                match val {
                    Some(x) => x,
                    None => {
                        errors1.insert(format!("unknown variable {:?}", varname));
                        return format!("<<no var {:?}>>", varname);
                    }
                }
            };
            InstantiatedConfig {
                kind: self.kind.clone(),
                volumes: self.volumes.clone(),
                user_id: self.user_id.clone(),
                group_id: self.group_id.clone(),
                restart_timeout: self.restart_timeout.clone(),
                kill_timeout: self.kill_timeout.clone(),
                memory_limit: self.memory_limit.clone(),
                fileno_limit: self.fileno_limit.clone(),
                cpu_shares: self.cpu_shares.clone(),
                executable: self.executable.clone(),
                arguments: self.arguments.iter()
                    .map(|x| VARIABLE_REGEX.replace(&x, &mut replacer).into())
                    .collect(),
                environ: self.environ.iter()
                    .map(|(key, val)| {
                        (key.clone(),
                         VARIABLE_REGEX.replace(&val, &mut replacer).into())
                    })
                    .collect(),
                workdir: self.workdir.clone(),
                resolv_conf: self.resolv_conf.clone(),
                hosts_file: self.hosts_file.clone(),
                uid_map: self.uid_map.clone(),
                gid_map: self.gid_map.clone(),
                stdout_stderr_file: self.stdout_stderr_file.clone(),
                interactive: self.interactive.clone(),
                restart_process_only: self.restart_process_only.clone(),
                tcp_ports: self.tcp_ports.iter()
                    .map(|(key, val)| {
                        let s = VARIABLE_REGEX.replace(&key, &mut replacer);
                        let port = match s.parse::<u16>() {
                            Ok(x) => x,
                            Err(e) => {
                                errors2.insert(format!("Bad port {:?}: {}",
                                    key, e));
                                return (0, val.clone());
                            }
                        };
                        (port, val.clone())
                    })
                    .collect(),
            }
        };
        if errors1.len() > 0 || errors2.len() > 0 {
            return Err(errors1.into_iter().chain(errors2.into_iter())
                       .collect());
        } else {
            return Ok(result);
        }
    }
}

pub fn volume_validator<'x>() -> Enum<'x> {
    Enum::new()
    .option("Persistent",  Structure::new()
        .member("path",  Scalar::new().default("/"))
        .member("mkdir",  Scalar::new().default(false))
        .member("mode",  Numeric::new().min(0).max(0o1777).default(0o777))
        .member("user",  Numeric::new().default(0))
        .member("group",  Numeric::new().default(0)))
    .option("Readonly", Scalar::new())
    .option("Tmpfs", Structure::new()
        .member("size", Numeric::new().min(0).default(100*1024*1024))
        .member("mode", Numeric::new().min(0).max(0o1777).default(0o777)))
    .option("Statedir", Structure::new()
        .member("path", Scalar::new().default("/"))
        .member("mode", Numeric::new().min(0).max(0o1777).default(0o777))
        .member("user", Numeric::new().default(0))
        .member("group", Numeric::new().default(0)))
}

impl Decodable for Host {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        try!(d.read_str()).parse().map(Host)
            .map_err(|x| d.error(&format!("{}", x)))
    }
}

impl Encodable for Host {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        format!("{}", self.0).encode(s)
    }
}

impl Variable {
    pub fn validate(&self, value: &str, sandbox: &SandboxConfig)
        -> Result<(), String>
    {
        let port = value.parse::<u16>()
            .map_err(|e| format!("invalid TcpPort {:?}: {}", value, e))?;
        if !in_range(&sandbox.allow_tcp_ports, port as u32) {
            return Err(format!("TcpPort {:?} is not in allowed range", port));
        }
        Ok(())
    }
}
<commit_msg>Fixes variable substitution<commit_after>use std::net::IpAddr;
use std::path::PathBuf;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::os::unix::io::RawFd;

use regex::{Regex, Captures};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use quire::validate::{Structure, Sequence, Scalar, Numeric, Enum};
use quire::validate::{Mapping, Nothing};
use id_map::{IdMap, IdMapExt, mapping_validator};

use sandbox_config::SandboxConfig;
use utils::{in_range};

pub const DEFAULT_KILL_TIMEOUT: f32 = 5.;

lazy_static! {
    static ref VARIABLE_REGEX: Regex = Regex::new(r#"@\{([^}]*)\}"#).unwrap();
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct TmpfsInfo {
    pub size: usize,
    pub mode: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct PersistentInfo {
    pub path: PathBuf,
    pub mkdir: bool,
    pub mode: u32,
    pub user: u32,
    pub group: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub struct StatedirInfo {
    pub path: PathBuf,
    pub mode: u32,
    pub user: u32,
    pub group: u32,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub enum Volume {
    Readonly(PathBuf),
    Persistent(PersistentInfo),
    Tmpfs(TmpfsInfo),
    Statedir(StatedirInfo),
}

#[derive(RustcDecodable, RustcEncodable, Serialize, Debug, PartialEq, Eq)]
#[derive(Clone, Copy)]
pub enum ContainerKind {
    Daemon,
    Command,
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct ResolvConf {
    pub copy_from_host: bool,
}

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct HostsFile {
    pub copy_from_host: bool,
    pub localhost: Option<bool>,
    pub public_hostname: Option<bool>,
}

#[derive(Clone, Debug)]
pub struct Host(pub IpAddr);

#[derive(RustcDecodable, RustcEncodable, Clone)]
pub struct TcpPort {
    pub host: Host,
    pub fd: RawFd,
    pub reuse_addr: bool,
    pub reuse_port: bool,
    pub listen_backlog: usize,
}

#[derive(RustcDecodable, RustcEncodable, Clone, PartialEq, Eq)]
pub enum Variable {
    TcpPort,
}

#[derive(RustcDecodable)]
pub struct ContainerConfig {
    pub kind: ContainerKind,
    pub variables: BTreeMap<String, Variable>,
    pub volumes: BTreeMap<String, Volume>,
    pub user_id: u32,
    pub group_id: u32,
    pub restart_timeout: f32,
    pub kill_timeout: f32,
    pub memory_limit: u64,
    pub fileno_limit: u64,
    pub cpu_shares: usize,
    pub executable: String,
    pub arguments: Vec<String>,
    pub environ: BTreeMap<String, String>,
    pub workdir: PathBuf,
    pub resolv_conf: ResolvConf,
    pub hosts_file: HostsFile,
    pub uid_map: Vec<IdMap>,
    pub gid_map: Vec<IdMap>,
    pub stdout_stderr_file: Option<PathBuf>,
    pub interactive: bool,
    pub restart_process_only: bool,
    pub tcp_ports: HashMap<String, TcpPort>,
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct InstantiatedConfig {
    pub kind: ContainerKind,
    pub volumes: BTreeMap<String, Volume>,
    pub user_id: u32,
    pub group_id: u32,
    pub restart_timeout: f32,
    pub kill_timeout: f32,
    pub memory_limit: u64,
    pub fileno_limit: u64,
    pub cpu_shares: usize,
    pub executable: String,
    pub arguments: Vec<String>,
    pub environ: BTreeMap<String, String>,
    pub workdir: PathBuf,
    pub resolv_conf: ResolvConf,
    pub hosts_file: HostsFile,
    pub uid_map: Vec<IdMap>,
    pub gid_map: Vec<IdMap>,
    pub stdout_stderr_file: Option<PathBuf>,
    pub interactive: bool,
    pub restart_process_only: bool,
    pub tcp_ports: HashMap<u16, TcpPort>,
}

pub struct Variables<'a> {
    pub user_vars: &'a HashMap<String, String>,
    pub lithos_name: &'a str,
    pub lithos_config_filename: &'a str,
}

impl InstantiatedConfig {
    pub fn map_uid(&self, internal_uid: u32) -> Option<u32> {
        self.uid_map.map_id(internal_uid)
    }
    pub fn map_gid(&self, internal_gid: u32) -> Option<u32> {
        self.gid_map.map_id(internal_gid)
    }
}

impl ContainerConfig {
    pub fn validator<'x>() -> Structure<'x> {
        Structure::new()
        .member("kind", Scalar::new().default("Daemon"))
        .member("variables", Mapping::new(
            Scalar::new(),
            Enum::new()
                .option("TcpPort", Nothing)
        ))
        .member("volumes", Mapping::new(
                Scalar::new(),
                volume_validator()))
        .member("user_id", Numeric::new())
        .member("group_id", Numeric::new().default(0))
        .member("memory_limit", Numeric::new().default(0x7fffffffffffffffi64))
        .member("fileno_limit", Numeric::new().default(1024))
        .member("cpu_shares", Numeric::new().default(1024))
        .member("restart_timeout", Numeric::new().min(0).max(86400).default(1))
        .member("kill_timeout",
            Numeric::new().min(0).max(86400)
                .default(DEFAULT_KILL_TIMEOUT as i64))
        .member("executable", Scalar::new())
        .member("arguments", Sequence::new(Scalar::new()))
        .member("environ", Mapping::new(
                Scalar::new(),
                Scalar::new()))
        .member("workdir", Scalar::new().default("/"))
        .member("resolv_conf", Structure::new()
            .member("copy_from_host", Scalar::new().default(true)))
        .member("hosts_file", Structure::new()
            .member("copy_from_host", Scalar::new().default(false))
            .member("localhost", Scalar::new().optional())
            .member("public_hostname", Scalar::new().optional()))
        .member("uid_map", mapping_validator())
        .member("gid_map", mapping_validator())
        .member("stdout_stderr_file", Scalar::new().optional())
        .member("interactive", Scalar::new().default(false))
        .member("restart_process_only", Scalar::new().default(false))
        .member("tcp_ports", Mapping::new(
            Scalar::new(),
            Structure::new()
                .member("host", Scalar::new().default("0.0.0.0"))
                .member("fd", Numeric::new().min(0).optional())
                .member("reuse_addr", Scalar::new().default(true))
                .member("reuse_port", Scalar::new().default(false))
                .member("listen_backlog", Scalar::new().default(128))
            ))
    }
    pub fn instantiate(&self, variables: &Variables)
        -> Result<InstantiatedConfig, Vec<String>>
    {
        let mut errors1 = HashSet::new();
        let mut errors2 = HashSet::new();
        let result = {
            let mut replacer = |capt: &Captures| {
                let varname = capt.get(1).unwrap().as_str();
                let val = variables.user_vars.get(varname).map(|x| x.clone())
                    .or_else(|| match varname {
                        "lithos:name"
                        => Some(variables.lithos_name.to_string()),
                        "lithos:config_filename"
                        => Some(variables.lithos_config_filename.to_string()),
                        _ => None,
                    });
                match val {
                    Some(x) => x,
                    None => {
                        errors1.insert(format!("unknown variable {:?}", varname));
                        return format!("<<no var {:?}>>", varname);
                    }
                }
            };
            InstantiatedConfig {
                kind: self.kind.clone(),
                volumes: self.volumes.clone(),
                user_id: self.user_id.clone(),
                group_id: self.group_id.clone(),
                restart_timeout: self.restart_timeout.clone(),
                kill_timeout: self.kill_timeout.clone(),
                memory_limit: self.memory_limit.clone(),
                fileno_limit: self.fileno_limit.clone(),
                cpu_shares: self.cpu_shares.clone(),
                executable: self.executable.clone(),
                arguments: self.arguments.iter()
                    .map(|x| VARIABLE_REGEX.replace(&x, &mut replacer).into())
                    .collect(),
                environ: self.environ.iter()
                    .map(|(key, val)| {
                        (key.clone(),
                         VARIABLE_REGEX.replace(&val, &mut replacer).into())
                    })
                    .collect(),
                workdir: self.workdir.clone(),
                resolv_conf: self.resolv_conf.clone(),
                hosts_file: self.hosts_file.clone(),
                uid_map: self.uid_map.clone(),
                gid_map: self.gid_map.clone(),
                stdout_stderr_file: self.stdout_stderr_file.clone(),
                interactive: self.interactive.clone(),
                restart_process_only: self.restart_process_only.clone(),
                tcp_ports: self.tcp_ports.iter()
                    .map(|(key, val)| {
                        let s = VARIABLE_REGEX.replace(&key, &mut replacer);
                        let port = match s.parse::<u16>() {
                            Ok(x) => x,
                            Err(e) => {
                                errors2.insert(format!("Bad port {:?}: {}",
                                    key, e));
                                return (0, val.clone());
                            }
                        };
                        (port, val.clone())
                    })
                    .collect(),
            }
        };
        if errors1.len() > 0 || errors2.len() > 0 {
            return Err(errors1.into_iter().chain(errors2.into_iter())
                       .collect());
        } else {
            return Ok(result);
        }
    }
}

pub fn volume_validator<'x>() -> Enum<'x> {
    Enum::new()
    .option("Persistent",  Structure::new()
        .member("path",  Scalar::new().default("/"))
        .member("mkdir",  Scalar::new().default(false))
        .member("mode",  Numeric::new().min(0).max(0o1777).default(0o777))
        .member("user",  Numeric::new().default(0))
        .member("group",  Numeric::new().default(0)))
    .option("Readonly", Scalar::new())
    .option("Tmpfs", Structure::new()
        .member("size", Numeric::new().min(0).default(100*1024*1024))
        .member("mode", Numeric::new().min(0).max(0o1777).default(0o777)))
    .option("Statedir", Structure::new()
        .member("path", Scalar::new().default("/"))
        .member("mode", Numeric::new().min(0).max(0o1777).default(0o777))
        .member("user", Numeric::new().default(0))
        .member("group", Numeric::new().default(0)))
}

impl Decodable for Host {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        try!(d.read_str()).parse().map(Host)
            .map_err(|x| d.error(&format!("{}", x)))
    }
}

impl Encodable for Host {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        format!("{}", self.0).encode(s)
    }
}

impl Variable {
    pub fn validate(&self, value: &str, sandbox: &SandboxConfig)
        -> Result<(), String>
    {
        let port = value.parse::<u16>()
            .map_err(|e| format!("invalid TcpPort {:?}: {}", value, e))?;
        if !in_range(&sandbox.allow_tcp_ports, port as u32) {
            return Err(format!("TcpPort {:?} is not in allowed range", port));
        }
        Ok(())
    }
}
<|endoftext|>