<commit_before>use neb::server::*;
use neb::ram::schema::LocalSchemasCache;
use bifrost::rpc;
use bifrost::raft;
use bifrost::membership::server::Membership;
use bifrost::raft::client::RaftClient;
use bifrost::membership::member::MemberService;
use bifrost::raft::state_machine::{master as sm_master};
use bifrost::rpc::Server;
use std::rc::Rc;
use std::sync::Arc;
use futures::prelude::*;
use std::env;
use neb::ram::schema::Schema;
use neb::ram::schema::Field;
use dovahkiin::types::type_id_of;
use neb::client;
use neb::ram::types::*;
use dovahkiin::types::custom_types::id::Id;
use dovahkiin::types::custom_types::map::Map;
use neb::ram::cell::Cell;

#[test]
pub fn init () {
    NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 16 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &String::from("127.0.0.1:5100"),
    &String::from("test"));
}

#[test]
pub fn smoke_test () {
    const DATA: &'static str = "DATA";
    let num = env::var("NEB_KV_SMOKE_TEST_ITEMS")
        .unwrap_or("1000".to_string())
        .parse::<u64>().unwrap();
    env_logger::init();
    let server_addr = String::from("127.0.0.1:5300");
    let server_group = String::from("smoke_test");
    let server = NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 512 * 1024 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &server_addr,
    &server_group);
    let schema_id = 123;
    let schema = Schema {
        id: schema_id,
        name: String::from("schema"),
        key_field: None,
        str_key_field: None,
        is_dynamic: false,
        fields: Field::new(
            "*", 0, false, false,
            Some(vec![
                Field::new(DATA, type_id_of(Type::U64), false, false, None)
            ])
        )
    };

    let client = Arc::new(client::AsyncClient::new(
        &server.rpc, &vec!(server_addr),
        &server_group).unwrap());
    client.new_schema_with_id(schema).wait();

    for i in 0..num * 2 {
        // intense upsert, half delete
        let id = Id::new(0, num);
        let mut value = Value::Map(Map::new());
        value[DATA] = Value::U64(num);
        let cell = Cell::new_with_id(schema_id, &id, value);
        client.upsert_cell(cell).wait();
        if i % 2 == 0 {
            client.remove_cell(id).wait();
        }
    }
}<commit_msg>verify by read<commit_after>use neb::server::*;
use neb::ram::schema::LocalSchemasCache;
use bifrost::rpc;
use bifrost::raft;
use bifrost::membership::server::Membership;
use bifrost::raft::client::RaftClient;
use bifrost::membership::member::MemberService;
use bifrost::raft::state_machine::{master as sm_master};
use bifrost::rpc::Server;
use std::rc::Rc;
use std::sync::Arc;
use futures::prelude::*;
use std::env;
use neb::ram::schema::Schema;
use neb::ram::schema::Field;
use dovahkiin::types::type_id_of;
use neb::client;
use neb::ram::types::*;
use dovahkiin::types::custom_types::id::Id;
use dovahkiin::types::custom_types::map::Map;
use neb::ram::cell::Cell;

#[test]
pub fn init () {
    NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 16 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &String::from("127.0.0.1:5100"),
    &String::from("test"));
}

#[test]
pub fn smoke_test () {
    const DATA: &'static str = "DATA";
    let num = env::var("NEB_KV_SMOKE_TEST_ITEMS")
        .unwrap_or("1000".to_string())
        .parse::<u64>().unwrap();
    env_logger::init();
    let server_addr = String::from("127.0.0.1:5300");
    let server_group = String::from("smoke_test");
    let server = NebServer::new_from_opts(&ServerOptions {
        chunk_count: 1,
        memory_size: 512 * 1024 * 1024,
        backup_storage: None,
        wal_storage: None
    },
    &server_addr,
    &server_group);
    let schema_id = 123;
    let schema = Schema {
        id: schema_id,
        name: String::from("schema"),
        key_field: None,
        str_key_field: None,
        is_dynamic: false,
        fields: Field::new(
            "*", 0, false, false,
            Some(vec![
                Field::new(DATA, type_id_of(Type::U64), false, false, None)
            ])
        )
    };

    let client = Arc::new(client::AsyncClient::new(
        &server.rpc, &vec!(server_addr),
        &server_group).unwrap());
    client.new_schema_with_id(schema).wait();

    for i in 0..num * 2 {
        // intense upsert, half delete
        let id = Id::new(0, num);
        let mut value = Value::Map(Map::new());
        value[DATA] = Value::U64(num);
        let cell = Cell::new_with_id(schema_id, &id, value);
        client.upsert_cell(cell).wait();

        // verify
        let read_cell = client.read_cell(id).wait().unwrap().unwrap();
        assert_eq!(*(read_cell.data[DATA].U64().unwrap()), num);

        if i % 2 == 0 {
            client.remove_cell(id).wait();
        }
    }
}<|endoftext|>


<commit_before>use alloc::arc::{Arc, Weak};
use collections::{BTreeMap, VecDeque};
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};

use sync::WaitCondition;
use syscall::error::{Error, Result, EAGAIN, EBADF, EINVAL, EPIPE};
use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
use syscall::scheme::Scheme;

/// Pipes list
pub static PIPE_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
static PIPE_NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
static PIPES: Once<RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)>> = Once::new();

/// Initialize pipes, called if needed
fn init_pipes() -> RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    RwLock::new((BTreeMap::new(), BTreeMap::new()))
}

/// Get the global pipes list, const
fn pipes() -> RwLockReadGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    PIPES.call_once(init_pipes).read()
}

/// Get the global schemes list, mutable
fn pipes_mut() -> RwLockWriteGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    PIPES.call_once(init_pipes).write()
}

pub fn pipe(flags: usize) -> (usize, usize) {
    let mut pipes = pipes_mut();
    let read_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
    let write_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
    let read = PipeRead::new(flags);
    let write = PipeWrite::new(flags, &read);
    pipes.0.insert(read_id, Arc::new(read));
    pipes.1.insert(write_id, Arc::new(write));
    (read_id, write_id)
}

pub struct PipeScheme;

impl PipeScheme {
    pub fn new(scheme_id: SchemeId) -> PipeScheme {
        PIPE_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
        PipeScheme
    }
}

impl Scheme for PipeScheme {
    fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
        let mut pipes = pipes_mut();

        let read_option = if let Some(pipe) = pipes.0.get(&id) {
            Some(pipe.dup()?)
        } else {
            None
        };
        if let Some(pipe) = read_option {
            let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
            pipes.0.insert(pipe_id, Arc::new(pipe));
            return Ok(pipe_id);
        }

        let write_option = if let Some(pipe) = pipes.1.get(&id) {
            Some(pipe.dup()?)
        } else {
            None
        };
        if let Some(pipe) = write_option {
            let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
            pipes.1.insert(pipe_id, Arc::new(pipe));
            return Ok(pipe_id);
        }

        Err(Error::new(EBADF))
    }

    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
        // Clone to prevent deadlocks
        let pipe = {
            let pipes = pipes();
            pipes.0.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
        };

        pipe.read(buf)
    }

    fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
        // Clone to prevent deadlocks
        let pipe = {
            let pipes = pipes();
            pipes.1.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
        };

        pipe.write(buf)
    }

    fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
        let pipes = pipes();

        if let Some(pipe) = pipes.0.get(&id) {
            return pipe.fcntl(cmd, arg);
        }

        if let Some(pipe) = pipes.1.get(&id) {
            return pipe.fcntl(cmd, arg);
        }

        Err(Error::new(EBADF))
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"pipe:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn fsync(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }

    fn close(&self, id: usize) -> Result<usize> {
        let mut pipes = pipes_mut();

        drop(pipes.0.remove(&id));
        drop(pipes.1.remove(&id));

        Ok(0)
    }
}

/// Read side of a pipe
pub struct PipeRead {
    flags: AtomicUsize,
    condition: Arc<WaitCondition>,
    vec: Arc<Mutex<VecDeque<u8>>>
}

impl PipeRead {
    pub fn new(flags: usize) -> Self {
        PipeRead {
            flags: AtomicUsize::new(flags),
            condition: Arc::new(WaitCondition::new()),
            vec: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    fn dup(&self) -> Result<Self> {
        Ok(PipeRead {
            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
            condition: self.condition.clone(),
            vec: self.vec.clone()
        })
    }

    fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
        match cmd {
            F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
            F_SETFL => {
                self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
                Ok(0)
            },
            _ => Err(Error::new(EINVAL))
        }
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        loop {
            {
                let mut vec = self.vec.lock();

                let mut i = 0;
                while i < buf.len() {
                    if let Some(b) = vec.pop_front() {
                        buf[i] = b;
                        i += 1;
                    } else {
                        break;
                    }
                }

                if i > 0 {
                    return Ok(i);
                }
            }

            if Arc::weak_count(&self.vec) == 0 {
                return Ok(0);
            } else if self.flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
                return Err(Error::new(EAGAIN));
            } else {
                self.condition.wait();
            }
        }
    }
}

/// Read side of a pipe
pub struct PipeWrite {
    flags: AtomicUsize,
    condition: Arc<WaitCondition>,
    vec: Option<Weak<Mutex<VecDeque<u8>>>>
}

impl PipeWrite {
    pub fn new(flags: usize, read: &PipeRead) -> Self {
        PipeWrite {
            flags: AtomicUsize::new(flags),
            condition: read.condition.clone(),
            vec: Some(Arc::downgrade(&read.vec)),
        }
    }

    fn dup(&self) -> Result<Self> {
        Ok(PipeWrite {
            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
            condition: self.condition.clone(),
            vec: self.vec.clone()
        })
    }

    fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
        match cmd {
            F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
            F_SETFL => {
                self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
                Ok(0)
            },
            _ => Err(Error::new(EINVAL))
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        if let Some(ref vec_weak) = self.vec {
            if let Some(vec_lock) = vec_weak.upgrade() {
                let mut vec = vec_lock.lock();

                for &b in buf.iter() {
                    vec.push_back(b);
                }

                self.condition.notify();

                Ok(buf.len())
            } else {
                Err(Error::new(EPIPE))
            }
        } else {
            panic!("PipeWrite dropped before write");
        }
    }
}

impl Drop for PipeWrite {
    fn drop(&mut self) {
        drop(self.vec.take());
        self.condition.notify();
    }
}
<commit_msg>pipe: make read() return when write end is closed<commit_after>use alloc::arc::{Arc, Weak};
use collections::{BTreeMap, VecDeque};
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
use scheme::{AtomicSchemeId, ATOMIC_SCHEMEID_INIT, SchemeId};

use sync::WaitCondition;
use syscall::error::{Error, Result, EAGAIN, EBADF, EINVAL, EPIPE};
use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
use syscall::scheme::Scheme;

/// Pipes list
pub static PIPE_SCHEME_ID: AtomicSchemeId = ATOMIC_SCHEMEID_INIT;
static PIPE_NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
static PIPES: Once<RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)>> = Once::new();

/// Initialize pipes, called if needed
fn init_pipes() -> RwLock<(BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    RwLock::new((BTreeMap::new(), BTreeMap::new()))
}

/// Get the global pipes list, const
fn pipes() -> RwLockReadGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    PIPES.call_once(init_pipes).read()
}

/// Get the global schemes list, mutable
fn pipes_mut() -> RwLockWriteGuard<'static, (BTreeMap<usize, Arc<PipeRead>>, BTreeMap<usize, Arc<PipeWrite>>)> {
    PIPES.call_once(init_pipes).write()
}

pub fn pipe(flags: usize) -> (usize, usize) {
    let mut pipes = pipes_mut();
    let read_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
    let write_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
    let read = PipeRead::new(flags);
    let write = PipeWrite::new(flags, &read);
    pipes.0.insert(read_id, Arc::new(read));
    pipes.1.insert(write_id, Arc::new(write));
    (read_id, write_id)
}

pub struct PipeScheme;

impl PipeScheme {
    pub fn new(scheme_id: SchemeId) -> PipeScheme {
        PIPE_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
        PipeScheme
    }
}

impl Scheme for PipeScheme {
    fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
        let mut pipes = pipes_mut();

        let read_option = if let Some(pipe) = pipes.0.get(&id) {
            Some(pipe.dup()?)
        } else {
            None
        };
        if let Some(pipe) = read_option {
            let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
            pipes.0.insert(pipe_id, Arc::new(pipe));
            return Ok(pipe_id);
        }

        let write_option = if let Some(pipe) = pipes.1.get(&id) {
            Some(pipe.dup()?)
        } else {
            None
        };
        if let Some(pipe) = write_option {
            let pipe_id = PIPE_NEXT_ID.fetch_add(1, Ordering::SeqCst);
            pipes.1.insert(pipe_id, Arc::new(pipe));
            return Ok(pipe_id);
        }

        Err(Error::new(EBADF))
    }

    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
        // Clone to prevent deadlocks
        let pipe = {
            let pipes = pipes();
            pipes.0.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
        };

        pipe.read(buf)
    }

    fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
        // Clone to prevent deadlocks
        let pipe = {
            let pipes = pipes();
            pipes.1.get(&id).map(|pipe| pipe.clone()).ok_or(Error::new(EBADF))?
        };

        pipe.write(buf)
    }

    fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
        let pipes = pipes();

        if let Some(pipe) = pipes.0.get(&id) {
            return pipe.fcntl(cmd, arg);
        }

        if let Some(pipe) = pipes.1.get(&id) {
            return pipe.fcntl(cmd, arg);
        }

        Err(Error::new(EBADF))
    }

    fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
        let mut i = 0;
        let scheme_path = b"pipe:";
        while i < buf.len() && i < scheme_path.len() {
            buf[i] = scheme_path[i];
            i += 1;
        }
        Ok(i)
    }

    fn fsync(&self, _id: usize) -> Result<usize> {
        Ok(0)
    }

    fn close(&self, id: usize) -> Result<usize> {
        let mut pipes = pipes_mut();

        drop(pipes.0.remove(&id));
        drop(pipes.1.remove(&id));

        Ok(0)
    }
}

/// Read side of a pipe
pub struct PipeRead {
    flags: AtomicUsize,
    condition: Arc<WaitCondition>,
    vec: Arc<Mutex<VecDeque<u8>>>
}

impl PipeRead {
    pub fn new(flags: usize) -> Self {
        PipeRead {
            flags: AtomicUsize::new(flags),
            condition: Arc::new(WaitCondition::new()),
            vec: Arc::new(Mutex::new(VecDeque::new())),
        }
    }

    fn dup(&self) -> Result<Self> {
        Ok(PipeRead {
            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
            condition: self.condition.clone(),
            vec: self.vec.clone()
        })
    }

    fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
        match cmd {
            F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
            F_SETFL => {
                self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
                Ok(0)
            },
            _ => Err(Error::new(EINVAL))
        }
    }

    fn read(&self, buf: &mut [u8]) -> Result<usize> {
        let mut resumed = false;

        loop {
            {
                let mut vec = self.vec.lock();

                let mut i = 0;
                while i < buf.len() {
                    if let Some(b) = vec.pop_front() {
                        buf[i] = b;
                        i += 1;
                    } else {
                        break;
                    }
                }

                if i > 0 || resumed {
                    return Ok(i);
                }
            }

            if Arc::weak_count(&self.vec) == 0 {
                return Ok(0);
            } else if self.flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
                return Err(Error::new(EAGAIN));
            } else {
                self.condition.wait();
                resumed = true;
            }
        }
    }
}

/// Read side of a pipe
pub struct PipeWrite {
    flags: AtomicUsize,
    condition: Arc<WaitCondition>,
    vec: Option<Weak<Mutex<VecDeque<u8>>>>
}

impl PipeWrite {
    pub fn new(flags: usize, read: &PipeRead) -> Self {
        PipeWrite {
            flags: AtomicUsize::new(flags),
            condition: read.condition.clone(),
            vec: Some(Arc::downgrade(&read.vec)),
        }
    }

    fn dup(&self) -> Result<Self> {
        Ok(PipeWrite {
            flags: AtomicUsize::new(self.flags.load(Ordering::SeqCst)),
            condition: self.condition.clone(),
            vec: self.vec.clone()
        })
    }

    fn fcntl(&self, cmd: usize, arg: usize) -> Result<usize> {
        match cmd {
            F_GETFL => Ok(self.flags.load(Ordering::SeqCst)),
            F_SETFL => {
                self.flags.store(arg & ! O_ACCMODE, Ordering::SeqCst);
                Ok(0)
            },
            _ => Err(Error::new(EINVAL))
        }
    }

    fn write(&self, buf: &[u8]) -> Result<usize> {
        if let Some(ref vec_weak) = self.vec {
            if let Some(vec_lock) = vec_weak.upgrade() {
                let mut vec = vec_lock.lock();

                for &b in buf.iter() {
                    vec.push_back(b);
                }

                if buf.len() > 0 {
                    self.condition.notify();
                }

                Ok(buf.len())
            } else {
                Err(Error::new(EPIPE))
            }
        } else {
            panic!("PipeWrite dropped before write");
        }
    }
}

impl Drop for PipeWrite {
    fn drop(&mut self) {
        drop(self.vec.take());
        self.condition.notify();
    }
}
<|endoftext|>


<commit_before>//! Storage Engine trait and several implementations
//!
//!

use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::fs::File;
use std::io;

/// Storage Engine Interface.
///
/// A storage engine, like MyISAM and InnoDB, is responsible for reading and
/// writing data to disk, maintain and use indices, check for data corruption
/// and repair corrupt files.
///
/// Each table in a database may use a different storage engine.
pub trait Engine {

}

/// A database table
///
/// Through this type, you can retreive certain meta information about the
/// table (like column names, column types, storage engine, ...). It's `access`
/// method locks the table globally and returns a storage engine to access
/// the table data.
#[derive(Debug)]
enum FileMode{LoadDefault, SaveDefault,}

#[derive(Debug)]
pub struct Table {
    engine_id: u8,
    version_nmbr: u8,
    magic_nmbr: u8,
    column_nmbr: u16,
    //columns: Vec<Column>,

}

impl Table {
    pub fn load(database: &str, table: &str) -> Result<(), io::Error> {
        // TODO: Read the .tbl file from disk and parse it
        let mut file = try!(Self::open_file(database,table,FileMode::LoadDefault));

        Ok(())
    }

    pub fn create_new() -> Table {
        Table { engine_id: 3 , version_nmbr:  1, magic_nmbr: 170, column_nmbr: 0}
    }

    pub fn save(&self,database: &str, table: &str) -> Result<(), io::Error> {
        let mut file = try!(Self::open_file(database,table,FileMode::SaveDefault));

        try!(file.write_u8(self.magic_nmbr));
        try!(file.write_u8(self.version_nmbr));
        try!(file.write_u8(self.engine_id));
        try!(file.write_u16::<BigEndian>(self.column_nmbr));

        println!("I Wrote my File");
        Ok(())
    }

    fn open_file(database: &str, table: &str, mode: FileMode) -> Result<(File), io::Error> {

        OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(table)
    }

    pub fn columns(&self) -> &[Column] {
        // TODO: Return real columns
        &[]
    }
}

/// A table column. Has a name, a type, ...
pub struct Column;

// # Some information for the `storage` working group:
//
// You work at the very bottom of the database: The thing that actually
// writes the data disk. Everything in this module is used only by the
// query execution module.
//
// The file layout may look like this:
// We have some `db_dir` where everything lives. In that directory, there are
// subdirectories for every database. In each of those subdirs is optionally
// a file `db.meta` that contains information about the database (such as
// permissions). The tables of each database are saved in *.tbl files that
// live inside the database directory.
//
// Your task is to provide types and method to:
// - read a specific table from a specific
//   database from file
// - create a new table in a database
// - query meta information about a table (columns for example)
// - lock a table for reading/writing it's data through a storage engine
//
// The other main task is to:
// - specify the storage engine interface
// - implement a simple storage engine (heap/flatfiles)
//
<commit_msg>Add some documentation and comments Add Column functionality Add self-save function to Column and enabled it in save function of table Add simple Datatype for Debug reasons Add configuration for open_file<commit_after>//! Storage Engine trait and several implementations
//!
//!

use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::fs::File;
use std::io;

/// Storage Engine Interface.
///
/// A storage engine, like MyISAM and InnoDB, is responsible for reading and
/// writing data to disk, maintain and use indices, check for data corruption
/// and repair corrupt files.
///
/// Each table in a database may use a different storage engine.
pub trait Engine {

}

/// A database table
///
/// Through this type, you can retreive certain meta information about the
/// table (like column names, column types, storage engine, ...). It's `access`
/// method locks the table globally and returns a storage engine to access
/// the table data.

/// A Enum for File Modes
///
/// Can be used to define the save and load configuration of open_file
#[derive(Debug)]
enum FileMode{LoadDefault, SaveDefault,}

/// A Enum for Datatypes (will be removed later)
#[repr(u8)]
#[derive(Clone,Copy,Debug)]
enum DataType{ Integer = 1, Float = 2,}
impl DataType{
    pub fn value(&self) -> u8{
       *self as u8
    }
}
#[derive(Debug)]
pub struct Table {
    engine_id: u8,
    version_nmbr: u8,
    magic_nmbr: u8,
    column_nmbr: u16,
    columns: Vec<Column>,

}

impl Table {
    pub fn load(database: &str, table: &str) -> Result<(), io::Error> {
        // TODO: Read the .tbl file from disk and parse it
        let mut file = try!(Self::open_file(database,table,FileMode::LoadDefault));

        Ok(())
    }

    pub fn create_new() -> Table {
        let mut t: Vec<Column> = Vec::new();
        t.push(Column::create_new());
        Table { engine_id: 3 , version_nmbr:  1, magic_nmbr: 170, column_nmbr: 1, columns: t}
    }

    pub fn save(&self,database: &str, table: &str) -> Result<(), io::Error> {
        //call for open file
        let mut file = try!(Self::open_file(database,table,FileMode::SaveDefault));

        //write information from table struct to file
        try!(file.write_u8(self.magic_nmbr));
        try!(file.write_u8(self.version_nmbr));
        try!(file.write_u8(self.engine_id));
        try!(file.write_u16::<BigEndian>(self.column_nmbr));

        //tell columns to write itself in the file
        let mf = &mut file;
        for i in &self.columns{
            i.save_to_file(mf);
        }

        //debug message all okay
        println!("I Wrote my File");
        Ok(())
    }

    fn open_file(database: &str, table: &str, mode: FileMode) -> Result<(File), io::Error> {
        //create new file or open new one
        match mode {
            FileMode::SaveDefault => OpenOptions::new()
                .write(true)
                .create(true)
                .open(table),
            FileMode::LoadDefault => OpenOptions::new()
                .read(true)
                .open(table),
            }
    }

    pub fn columns(&self) -> &[Column] {
        // TODO: Return real columns
        &[]
    }
}

/// A table column. Has a name, a type, ...
#[derive(Debug)]
pub struct Column{
    name: String, //name of column
    data_type: DataType, //name of the data type that is contained in this column
}

impl Column{
    pub fn save_to_file(&self, file: &mut File) -> Result<(), io::Error> {

        //write column information down
        try!(file.write_u8(self.data_type.value()));
        try!(file.write_u8(self.name.len() as u8));
        try!(file.write_all(self.name.as_bytes()));

        Ok(())
    }

    pub fn create_new() -> Column{
        Column{name: "duh".to_string(), data_type: DataType::Integer}
    }
}

// # Some information for the `storage` working group:
//
// You work at the very bottom of the database: The thing that actually
// writes the data disk. Everything in this module is used only by the
// query execution module.
//
// The file layout may look like this:
// We have some `db_dir` where everything lives. In that directory, there are
// subdirectories for every database. In each of those subdirs is optionally
// a file `db.meta` that contains information about the database (such as
// permissions). The tables of each database are saved in *.tbl files that
// live inside the database directory.
//
// Your task is to provide types and method to:
// - read a specific table from a specific
//   database from file
// - create a new table in a database
// - query meta information about a table (columns for example)
// - lock a table for reading/writing it's data through a storage engine
//
// The other main task is to:
// - specify the storage engine interface
// - implement a simple storage engine (heap/flatfiles)
//
<|endoftext|>


<commit_before>//! Tests of the interactive console installer

pub mod mock;

use crate::mock::clitools::{
    self, expect_stderr_ok, expect_stdout_ok, set_current_dist_date, Config, SanitizedOutput,
    Scenario,
};
use crate::mock::{get_path, restore_path};
use lazy_static::lazy_static;
use rustup::utils::raw;
use std::fs;
use std::io::Write;
use std::process::Stdio;
use std::sync::Mutex;

pub fn setup_(complex: bool, f: &dyn Fn(&Config)) {
    let scenario = if complex {
        Scenario::UnavailableRls
    } else {
        Scenario::SimpleV2
    };
    clitools::setup(scenario, &|config| {
        // Lock protects environment variables
        lazy_static! {
            static ref LOCK: Mutex<()> = Mutex::new(());
        }
        let _g = LOCK.lock();

        // An windows these tests mess with the user's PATH. Save
        // and restore them here to keep from trashing things.
        let saved_path = get_path();
        let _g = scopeguard::guard(saved_path, restore_path);

        f(config);
    });
}

pub fn setup(f: &dyn Fn(&Config)) {
    setup_(false, f)
}

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    run_input_with_env(config, args, input, &[])
}

fn run_input_with_env(
    config: &Config,
    args: &[&str],
    input: &str,
    env: &[(&str, &str)],
) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    for (key, value) in env.iter() {
        cmd.env(key, value);
    }

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn smoke_test() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok);
    });
}

#[test]
fn update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message.
#[test]
fn blank_lines_around_stderr_log_output_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");

        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

  stable installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        assert!(out
            .stdout
            .contains("This path needs to be in your PATH environment variable"));

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn with_no_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--default-toolchain=none"], "\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly-2015-01-02"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\nnightly\n\n\n\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\nno\n\n\n");
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\n\nn\n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_forces_and_skips_rls() {
    setup_(true, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    setup(&|config| {
        expect_stderr_ok(
            config,
            &["rustup-init", "-y", "--profile", "complete"],
            "warning: downloading with complete profile",
        );
    });
}

fn create_rustup_sh_metadata(config: &Config) {
    let rustup_dir = config.homedir.join(".rustup");
    fs::create_dir_all(&rustup_dir).unwrap();
    let version_file = rustup_dir.join("rustup-version");
    raw::write_file(&version_file, "").unwrap();
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "no\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "yes\n\n\n");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
        assert!(!out.stdout.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ))
    })
}

#[test]
fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init", "-y"], "");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stderr.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(!out.stdout.contains("Continue? (y/N)"));
    })
}
<commit_msg>Added test for `RUSTUP_INIT_SKIP_CHECKING_EXISTENCE`.<commit_after>//! Tests of the interactive console installer

pub mod mock;

use crate::mock::clitools::{
    self, expect_stderr_ok, expect_stdout_ok, set_current_dist_date, Config, SanitizedOutput,
    Scenario,
};
use crate::mock::{get_path, restore_path};
use lazy_static::lazy_static;
use rustup::utils::raw;
use std::fs;
use std::io::Write;
use std::process::Stdio;
use std::sync::Mutex;

pub fn setup_(complex: bool, f: &dyn Fn(&Config)) {
    let scenario = if complex {
        Scenario::UnavailableRls
    } else {
        Scenario::SimpleV2
    };
    clitools::setup(scenario, &|config| {
        // Lock protects environment variables
        lazy_static! {
            static ref LOCK: Mutex<()> = Mutex::new(());
        }
        let _g = LOCK.lock();

        // An windows these tests mess with the user's PATH. Save
        // and restore them here to keep from trashing things.
        let saved_path = get_path();
        let _g = scopeguard::guard(saved_path, restore_path);

        f(config);
    });
}

pub fn setup(f: &dyn Fn(&Config)) {
    setup_(false, f)
}

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    run_input_with_env(config, args, input, &[])
}

fn run_input_with_env(
    config: &Config,
    args: &[&str],
    input: &str,
    env: &[(&str, &str)],
) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    for (key, value) in env.iter() {
        cmd.env(key, value);
    }

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn smoke_test() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok);
    });
}

#[test]
fn update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message.
#[test]
fn blank_lines_around_stderr_log_output_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");

        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

  stable installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        assert!(out
            .stdout
            .contains("This path needs to be in your PATH environment variable"));

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn with_no_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--default-toolchain=none"], "\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly-2015-01-02"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\nnightly\n\n\n\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\nno\n\n\n");
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\n\nn\n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_forces_and_skips_rls() {
    setup_(true, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    setup(&|config| {
        expect_stderr_ok(
            config,
            &["rustup-init", "-y", "--profile", "complete"],
            "warning: downloading with complete profile",
        );
    });
}

fn create_rustup_sh_metadata(config: &Config) {
    let rustup_dir = config.homedir.join(".rustup");
    fs::create_dir_all(&rustup_dir).unwrap();
    let version_file = rustup_dir.join("rustup-version");
    raw::write_file(&version_file, "").unwrap();
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "no\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "yes\n\n\n");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
        assert!(!out.stdout.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ))
    })
}

#[test]
fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init", "-y"], "");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stderr.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(!out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_succeed_if_rustup_sh_already_installed_env_var_set() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input_with_env(
            config,
            &["rustup-init", "-y"],
            "",
            &[("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS", "yes")],
        );
        assert!(out.ok);
        assert!(!out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(!out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(!out.stderr.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(!out.stdout.contains("Continue? (y/N)"));
    })
}
<|endoftext|>


<commit_before>//! Tests of the interactive console installer

pub mod mock;

use crate::mock::clitools::{
    self, expect_stderr_ok, expect_stdout_ok, set_current_dist_date, Config, SanitizedOutput,
    Scenario,
};
use crate::mock::{get_path, restore_path};
use lazy_static::lazy_static;
use std::io::Write;
use std::process::Stdio;
use std::sync::Mutex;

pub fn setup_(complex: bool, f: &dyn Fn(&Config)) {
    let scenario = if complex {
        Scenario::UnavailableRls
    } else {
        Scenario::SimpleV2
    };
    clitools::setup(scenario, &|config| {
        // Lock protects environment variables
        lazy_static! {
            static ref LOCK: Mutex<()> = Mutex::new(());
        }
        let _g = LOCK.lock();

        // An windows these tests mess with the user's PATH. Save
        // and restore them here to keep from trashing things.
        let saved_path = get_path();
        let _g = scopeguard::guard(saved_path, restore_path);

        f(config);
    });
}

pub fn setup(f: &dyn Fn(&Config)) {
    setup_(false, f)
}

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn smoke_test() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok);
    });
}

#[test]
fn update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message.
#[test]
fn blank_lines_around_stderr_log_output_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");

        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

  stable installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        assert!(out
            .stdout
            .contains("This path needs to be in your PATH environment variable"));

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn with_no_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--default-toolchain=none"], "\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly-2015-01-02"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\nnightly\n\n\n\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\nno\n\n\n");
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\n\nn\n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_forces_and_skips_rls() {
    setup_(true, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    setup(&|config| {
        expect_stderr_ok(
            config,
            &["rustup-init", "-y", "--profile", "complete"],
            "warning: downloading with complete profile",
        );
    });
}
<commit_msg>Added tests for ignorable errors.<commit_after>//! Tests of the interactive console installer

pub mod mock;

use crate::mock::clitools::{
    self, expect_stderr_ok, expect_stdout_ok, set_current_dist_date, Config, SanitizedOutput,
    Scenario,
};
use crate::mock::{get_path, restore_path};
use lazy_static::lazy_static;
use rustup::utils::raw;
use std::fs;
use std::io::Write;
use std::process::Stdio;
use std::sync::Mutex;

pub fn setup_(complex: bool, f: &dyn Fn(&Config)) {
    let scenario = if complex {
        Scenario::UnavailableRls
    } else {
        Scenario::SimpleV2
    };
    clitools::setup(scenario, &|config| {
        // Lock protects environment variables
        lazy_static! {
            static ref LOCK: Mutex<()> = Mutex::new(());
        }
        let _g = LOCK.lock();

        // An windows these tests mess with the user's PATH. Save
        // and restore them here to keep from trashing things.
        let saved_path = get_path();
        let _g = scopeguard::guard(saved_path, restore_path);

        f(config);
    });
}

pub fn setup(f: &dyn Fn(&Config)) {
    setup_(false, f)
}

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn smoke_test() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok);
    });
}

#[test]
fn update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message.
#[test]
fn blank_lines_around_stderr_log_output_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "\n\n");

        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

  stable installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    setup(&|config| {
        run_input(config, &["rustup-init"], "\n\n");
        let out = run_input(config, &["rustup-init"], "\n\n");
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        assert!(out
            .stdout
            .contains("This path needs to be in your PATH environment variable"));

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn with_no_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init", "--default-toolchain=none"], "\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init", "--default-toolchain=nightly-2015-01-02"],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\nnightly\n\n\n\n\n");
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\nno\n\n\n");
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    setup(&|config| {
        let out = run_input(
            config,
            &["rustup-init"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    setup(&|config| {
        let out = run_input(config, &["rustup-init"], "2\n\n\n\n\nn\n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_forces_and_skips_rls() {
    setup_(true, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    setup(&|config| {
        expect_stderr_ok(
            config,
            &["rustup-init", "-y", "--profile", "complete"],
            "warning: downloading with complete profile",
        );
    });
}

fn create_rustup_sh_metadata(config: &Config) {
    let rustup_dir = config.homedir.join(".rustup");
    fs::create_dir_all(&rustup_dir).unwrap();
    let version_file = rustup_dir.join("rustup-version");
    raw::write_file(&version_file, "").unwrap();
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "no\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init"], "yes\n\n\n");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
        assert!(!out.stdout.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ))
    })
}

#[test]
fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() {
    setup(&|config| {
        create_rustup_sh_metadata(&config);
        let out = run_input(config, &["rustup-init", "-y"], "");
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stderr.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(!out.stdout.contains("Continue? (y/N)"));
    })
}

#[ignore] // Can't test environment variable.
#[test]
fn test_succeed_if_rustup_sh_already_installed_env_var_set() {}
<|endoftext|>


<commit_before>use std::io;
use std::ffi::{CString, OsStr, OsString};
use std::os::unix::io::RawFd;
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
use std::mem;

use libc::consts::os::posix88::EPERM;
use libc::{c_char, ssize_t, size_t, c_int, c_void};
use util::{path_to_c, allocate_loop};

const EXTATTR_NAMESPACE_USER_STRING: &'static str = "user";
const EXTATTR_NAMESPACE_SYSTEM_STRING: &'static str = "system";
const EXTATTR_NAMESPACE_NAMES: [&'static str; 3] =
    ["empty", EXTATTR_NAMESPACE_USER_STRING, EXTATTR_NAMESPACE_SYSTEM_STRING];
const EXTATTR_NAMESPACE_USER: c_int = 1;
const EXTATTR_NAMESPACE_SYSTEM: c_int = 2;

extern "C" {
    pub fn extattr_list_fd(fd: c_int,
                           attrnamespace: c_int,
                           data: *mut c_void,
                           nbytes: size_t)
                           -> ssize_t;
    pub fn extattr_get_fd(fd: c_int,
                          attrnamespace: c_int,
                          attrname: *const c_char,
                          data: *mut c_void,
                          nbytes: size_t)
                          -> ssize_t;
    pub fn extattr_delete_fd(fd: c_int, attrname: c_int, attrname: *const c_char) -> c_int;
    pub fn extattr_set_fd(fd: c_int,
                          attrname: c_int,
                          attrname: *const c_char,
                          data: *const c_void,
                          nbytes: size_t)
                          -> ssize_t;

    pub fn extattr_list_link(path: *const c_char,
                             attrnamespace: c_int,
                             data: *mut c_void,
                             nbytes: size_t)
                             -> ssize_t;
    pub fn extattr_get_link(path: *const c_char,
                            attrnamespace: c_int,
                            attrname: *const c_char,
                            data: *mut c_void,
                            nbytes: size_t)
                            -> ssize_t;
    pub fn extattr_delete_link(path: *const c_char,
                               attrname: c_int,
                               attrname: *const c_char)
                               -> c_int;
    pub fn extattr_set_link(path: *const c_char,
                            attrname: c_int,
                            attrname: *const c_char,
                            data: *const c_void,
                            nbytes: size_t)
                            -> ssize_t;
}

pub struct XAttrs {
    user_attrs: Box<[u8]>,
    system_attrs: Box<[u8]>,
    offset: usize,
}

impl Clone for XAttrs {
    fn clone(&self) -> Self {
        XAttrs {
            user_attrs: Vec::from(&*self.user_attrs).into_boxed_slice(),
            system_attrs: Vec::from(&*self.system_attrs).into_boxed_slice(),
            offset: self.offset,
        }
    }

    fn clone_from(&mut self, other: &XAttrs) {
        self.offset = other.offset;

        let mut data = mem::replace(&mut self.user_attrs, Box::new([])).into_vec();
        data.extend(other.user_attrs.iter().cloned());
        self.user_attrs = data.into_boxed_slice();

        data = mem::replace(&mut self.system_attrs, Box::new([])).into_vec();
        data.extend(other.system_attrs.iter().cloned());
        self.system_attrs = data.into_boxed_slice();
    }
}

impl Iterator for XAttrs {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        if self.user_attrs.is_empty() && self.system_attrs.is_empty() {
            return None;
        }

        if self.offset == self.user_attrs.len() + self.system_attrs.len() {
            return None;
        }

        let data = if self.offset < self.system_attrs.len() {
            &self.system_attrs[self.offset..]
        } else {
            &self.user_attrs[self.offset - self.system_attrs.len()..]
        };

        let siz = data[0] as usize;

        self.offset += siz + 1;
        if self.offset < self.system_attrs.len() {
            Some(prefix_namespace(OsStr::from_bytes(&data[1..siz + 1]),
                                  EXTATTR_NAMESPACE_SYSTEM))
        } else {
            Some(prefix_namespace(OsStr::from_bytes(&data[1..siz + 1]), EXTATTR_NAMESPACE_USER))
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.user_attrs.len() + self.system_attrs.len() == self.offset {
            (0, Some(0))
        } else {
            (1, None)
        }
    }
}

fn name_to_ns(name: &OsStr) -> io::Result<(c_int, CString)> {
    let mut groups = name.as_bytes().splitn(2, |&b| b == b'.').take(2);
    let nsname = match groups.next() {
        Some(s) => s,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "couldn't find namespace")),
    };

    let propname = match groups.next() {
        Some(s) => s,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "couldn't find attribute")),
    };

    let ns_int = match EXTATTR_NAMESPACE_NAMES.iter().position(|&s| s.as_bytes() == nsname) {
        Some(i) => i,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "no matching namespace")),
    };

    Ok((ns_int as c_int, try!(CString::new(propname))))
}

fn prefix_namespace(attr: &OsStr, ns: c_int) -> OsString {
    let nsname = EXTATTR_NAMESPACE_NAMES[ns as usize];
    let mut v = Vec::with_capacity(nsname.as_bytes().len() + attr.as_bytes().len() + 1);
    v.extend(nsname.as_bytes());
    v.extend(".".as_bytes());
    v.extend(attr.as_bytes());
    OsString::from_vec(v)
}

pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>> {
    let (ns, name) = try!(name_to_ns(name));
    unsafe {
        allocate_loop(|buf, len| {
            extattr_get_fd(fd, ns, name.as_ptr(), buf as *mut c_void, len as size_t)
        })
    }
}

pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let ret = unsafe {
        extattr_set_fd(fd,
                       ns,
                       name.as_ptr(),
                       value.as_ptr() as *const c_void,
                       value.len() as size_t)
    };
    if ret == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let ret = unsafe { extattr_delete_fd(fd, ns, name.as_ptr()) };
    if ret != 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn list_fd(fd: RawFd) -> io::Result<XAttrs> {
    let sysvec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_fd(fd,
                            EXTATTR_NAMESPACE_SYSTEM,
                            buf as *mut c_void,
                            len as size_t)
        });
        // On FreeBSD, system attributes require root privileges to view. However,
        // to mimic the behavior of listxattr in linux and osx, we need to query
        // them anyway and return empty results if we get EPERM
        match res {
            Ok(v) => v,
            Err(err) => {
                if err.raw_os_error() == Some(EPERM) {
                    Vec::new()
                } else {
                    return Err(err);
                }
            }
        }
    };

    let uservec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_fd(fd,
                            EXTATTR_NAMESPACE_USER,
                            buf as *mut c_void,
                            len as size_t)
        });
        match res {
            Ok(v) => v,
            Err(err) => return Err(err),
        }
    };

    Ok(XAttrs {
        system_attrs: sysvec.into_boxed_slice(),
        user_attrs: uservec.into_boxed_slice(),
        offset: 0,
    })
}

pub fn get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    unsafe {
        allocate_loop(|buf, len| {
            extattr_get_link(path.as_ptr(),
                             ns,
                             name.as_ptr(),
                             buf as *mut c_void,
                             len as size_t)
        })
    }
}

pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    let ret = unsafe {
        extattr_set_link(path.as_ptr(),
                         ns,
                         name.as_ptr(),
                         value.as_ptr() as *const c_void,
                         value.len() as size_t)
    };
    if ret == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    let ret = unsafe { extattr_delete_link(path.as_ptr(), ns, name.as_ptr()) };
    if ret != 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn list_path(path: &Path) -> io::Result<XAttrs> {
    let path = try!(path_to_c(path));
    let sysvec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_link(path.as_ptr(),
                              EXTATTR_NAMESPACE_SYSTEM,
                              buf as *mut c_void,
                              len as size_t)
        });
        // On FreeBSD, system attributes require root privileges to view. However,
        // to mimic the behavior of listxattr in linux and osx, we need to query
        // them anyway and return empty results if we get EPERM
        match res {
            Ok(v) => v,
            Err(err) => {
                if err.raw_os_error() == Some(EPERM) {
                    Vec::new()
                } else {
                    return Err(err);
                }
            }
        }
    };

    let uservec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_link(path.as_ptr(),
                              EXTATTR_NAMESPACE_USER,
                              buf as *mut c_void,
                              len as size_t)
        });
        match res {
            Ok(v) => v,
            Err(err) => return Err(err),
        }
    };

    Ok(XAttrs {
        system_attrs: sysvec.into_boxed_slice(),
        user_attrs: uservec.into_boxed_slice(),
        offset: 0,
    })
}
<commit_msg>Fixed bug with freebsd module due to libc upgrade<commit_after>use std::io;
use std::ffi::{CString, OsStr, OsString};
use std::os::unix::io::RawFd;
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
use std::mem;

use libc::{c_char, ssize_t, size_t, c_int, c_void, EPERM};
use util::{path_to_c, allocate_loop};

const EXTATTR_NAMESPACE_USER_STRING: &'static str = "user";
const EXTATTR_NAMESPACE_SYSTEM_STRING: &'static str = "system";
const EXTATTR_NAMESPACE_NAMES: [&'static str; 3] =
    ["empty", EXTATTR_NAMESPACE_USER_STRING, EXTATTR_NAMESPACE_SYSTEM_STRING];
const EXTATTR_NAMESPACE_USER: c_int = 1;
const EXTATTR_NAMESPACE_SYSTEM: c_int = 2;

extern "C" {
    pub fn extattr_list_fd(fd: c_int,
                           attrnamespace: c_int,
                           data: *mut c_void,
                           nbytes: size_t)
                           -> ssize_t;
    pub fn extattr_get_fd(fd: c_int,
                          attrnamespace: c_int,
                          attrname: *const c_char,
                          data: *mut c_void,
                          nbytes: size_t)
                          -> ssize_t;
    pub fn extattr_delete_fd(fd: c_int, attrname: c_int, attrname: *const c_char) -> c_int;
    pub fn extattr_set_fd(fd: c_int,
                          attrname: c_int,
                          attrname: *const c_char,
                          data: *const c_void,
                          nbytes: size_t)
                          -> ssize_t;

    pub fn extattr_list_link(path: *const c_char,
                             attrnamespace: c_int,
                             data: *mut c_void,
                             nbytes: size_t)
                             -> ssize_t;
    pub fn extattr_get_link(path: *const c_char,
                            attrnamespace: c_int,
                            attrname: *const c_char,
                            data: *mut c_void,
                            nbytes: size_t)
                            -> ssize_t;
    pub fn extattr_delete_link(path: *const c_char,
                               attrname: c_int,
                               attrname: *const c_char)
                               -> c_int;
    pub fn extattr_set_link(path: *const c_char,
                            attrname: c_int,
                            attrname: *const c_char,
                            data: *const c_void,
                            nbytes: size_t)
                            -> ssize_t;
}

pub struct XAttrs {
    user_attrs: Box<[u8]>,
    system_attrs: Box<[u8]>,
    offset: usize,
}

impl Clone for XAttrs {
    fn clone(&self) -> Self {
        XAttrs {
            user_attrs: Vec::from(&*self.user_attrs).into_boxed_slice(),
            system_attrs: Vec::from(&*self.system_attrs).into_boxed_slice(),
            offset: self.offset,
        }
    }

    fn clone_from(&mut self, other: &XAttrs) {
        self.offset = other.offset;

        let mut data = mem::replace(&mut self.user_attrs, Box::new([])).into_vec();
        data.extend(other.user_attrs.iter().cloned());
        self.user_attrs = data.into_boxed_slice();

        data = mem::replace(&mut self.system_attrs, Box::new([])).into_vec();
        data.extend(other.system_attrs.iter().cloned());
        self.system_attrs = data.into_boxed_slice();
    }
}

impl Iterator for XAttrs {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        if self.user_attrs.is_empty() && self.system_attrs.is_empty() {
            return None;
        }

        if self.offset == self.user_attrs.len() + self.system_attrs.len() {
            return None;
        }

        let data = if self.offset < self.system_attrs.len() {
            &self.system_attrs[self.offset..]
        } else {
            &self.user_attrs[self.offset - self.system_attrs.len()..]
        };

        let siz = data[0] as usize;

        self.offset += siz + 1;
        if self.offset < self.system_attrs.len() {
            Some(prefix_namespace(OsStr::from_bytes(&data[1..siz + 1]),
                                  EXTATTR_NAMESPACE_SYSTEM))
        } else {
            Some(prefix_namespace(OsStr::from_bytes(&data[1..siz + 1]), EXTATTR_NAMESPACE_USER))
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.user_attrs.len() + self.system_attrs.len() == self.offset {
            (0, Some(0))
        } else {
            (1, None)
        }
    }
}

fn name_to_ns(name: &OsStr) -> io::Result<(c_int, CString)> {
    let mut groups = name.as_bytes().splitn(2, |&b| b == b'.').take(2);
    let nsname = match groups.next() {
        Some(s) => s,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "couldn't find namespace")),
    };

    let propname = match groups.next() {
        Some(s) => s,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "couldn't find attribute")),
    };

    let ns_int = match EXTATTR_NAMESPACE_NAMES.iter().position(|&s| s.as_bytes() == nsname) {
        Some(i) => i,
        None => return Err(io::Error::new(io::ErrorKind::InvalidInput, "no matching namespace")),
    };

    Ok((ns_int as c_int, try!(CString::new(propname))))
}

fn prefix_namespace(attr: &OsStr, ns: c_int) -> OsString {
    let nsname = EXTATTR_NAMESPACE_NAMES[ns as usize];
    let mut v = Vec::with_capacity(nsname.as_bytes().len() + attr.as_bytes().len() + 1);
    v.extend(nsname.as_bytes());
    v.extend(".".as_bytes());
    v.extend(attr.as_bytes());
    OsString::from_vec(v)
}

pub fn get_fd(fd: RawFd, name: &OsStr) -> io::Result<Vec<u8>> {
    let (ns, name) = try!(name_to_ns(name));
    unsafe {
        allocate_loop(|buf, len| {
            extattr_get_fd(fd, ns, name.as_ptr(), buf as *mut c_void, len as size_t)
        })
    }
}

pub fn set_fd(fd: RawFd, name: &OsStr, value: &[u8]) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let ret = unsafe {
        extattr_set_fd(fd,
                       ns,
                       name.as_ptr(),
                       value.as_ptr() as *const c_void,
                       value.len() as size_t)
    };
    if ret == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn remove_fd(fd: RawFd, name: &OsStr) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let ret = unsafe { extattr_delete_fd(fd, ns, name.as_ptr()) };
    if ret != 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn list_fd(fd: RawFd) -> io::Result<XAttrs> {
    let sysvec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_fd(fd,
                            EXTATTR_NAMESPACE_SYSTEM,
                            buf as *mut c_void,
                            len as size_t)
        });
        // On FreeBSD, system attributes require root privileges to view. However,
        // to mimic the behavior of listxattr in linux and osx, we need to query
        // them anyway and return empty results if we get EPERM
        match res {
            Ok(v) => v,
            Err(err) => {
                if err.raw_os_error() == Some(EPERM) {
                    Vec::new()
                } else {
                    return Err(err);
                }
            }
        }
    };

    let uservec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_fd(fd,
                            EXTATTR_NAMESPACE_USER,
                            buf as *mut c_void,
                            len as size_t)
        });
        match res {
            Ok(v) => v,
            Err(err) => return Err(err),
        }
    };

    Ok(XAttrs {
        system_attrs: sysvec.into_boxed_slice(),
        user_attrs: uservec.into_boxed_slice(),
        offset: 0,
    })
}

pub fn get_path(path: &Path, name: &OsStr) -> io::Result<Vec<u8>> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    unsafe {
        allocate_loop(|buf, len| {
            extattr_get_link(path.as_ptr(),
                             ns,
                             name.as_ptr(),
                             buf as *mut c_void,
                             len as size_t)
        })
    }
}

pub fn set_path(path: &Path, name: &OsStr, value: &[u8]) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    let ret = unsafe {
        extattr_set_link(path.as_ptr(),
                         ns,
                         name.as_ptr(),
                         value.as_ptr() as *const c_void,
                         value.len() as size_t)
    };
    if ret == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn remove_path(path: &Path, name: &OsStr) -> io::Result<()> {
    let (ns, name) = try!(name_to_ns(name));
    let path = try!(path_to_c(path));
    let ret = unsafe { extattr_delete_link(path.as_ptr(), ns, name.as_ptr()) };
    if ret != 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

pub fn list_path(path: &Path) -> io::Result<XAttrs> {
    let path = try!(path_to_c(path));
    let sysvec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_link(path.as_ptr(),
                              EXTATTR_NAMESPACE_SYSTEM,
                              buf as *mut c_void,
                              len as size_t)
        });
        // On FreeBSD, system attributes require root privileges to view. However,
        // to mimic the behavior of listxattr in linux and osx, we need to query
        // them anyway and return empty results if we get EPERM
        match res {
            Ok(v) => v,
            Err(err) => {
                if err.raw_os_error() == Some(EPERM) {
                    Vec::new()
                } else {
                    return Err(err);
                }
            }
        }
    };

    let uservec = unsafe {
        let res = allocate_loop(|buf, len| {
            extattr_list_link(path.as_ptr(),
                              EXTATTR_NAMESPACE_USER,
                              buf as *mut c_void,
                              len as size_t)
        });
        match res {
            Ok(v) => v,
            Err(err) => return Err(err),
        }
    };

    Ok(XAttrs {
        system_attrs: sysvec.into_boxed_slice(),
        user_attrs: uservec.into_boxed_slice(),
        offset: 0,
    })
}
<|endoftext|>