<commit_before>#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![allow(missing_copy_implementations)]

use std::mem;

use std::ffi::CString;
use std::ffi::NulError;
use cassandra::batch::Batch;
use cassandra::future::{CloseFuture, Future, PreparedFuture, ResultFuture, SessionFuture};
use cassandra::error::CassError;
use cassandra::statement::Statement;
use cassandra::schema::schema_meta::SchemaMeta;
use cassandra::metrics::SessionMetrics;
use cassandra::cluster::Cluster;
use cassandra::util::Protected;

use cassandra_sys::CassSession as _Session;
use cassandra_sys::cass_session_free;
use cassandra_sys::cass_session_close;
use cassandra_sys::cass_session_connect;
use cassandra_sys::cass_session_prepare;
use cassandra_sys::cass_session_execute;
use cassandra_sys::cass_session_execute_batch;
use cassandra_sys::cass_session_get_schema_meta;
use cassandra_sys::cass_session_connect_keyspace;
use cassandra_sys::cass_session_get_metrics;

///A session object is used to execute queries and maintains cluster state through
///the control connection. The control connection is used to auto-discover nodes and
///monitor cluster changes (topology and schema). Each session also maintains multiple
/// /pools of connections to cluster nodes which are used to query the cluster.
///
///Instances of the session object are thread-safe to execute queries.
#[derive(Debug)]
pub struct Session(pub *mut _Session);
unsafe impl Sync for Session {}
unsafe impl Send for Session {}

impl Protected<*mut _Session> for Session {
    fn inner(&self) -> *mut _Session {
        self.0
    }
    fn build(inner: *mut _Session) -> Self {
        Session(inner)
    }
}

impl Drop for Session {
    /// Frees a session instance. If the session is still connected it will be synchronously
    /// closed before being deallocated.
    fn drop(&mut self) {
        debug!("dropping session");
        unsafe { cass_session_free(self.0) }
    }
}

impl Session {
    //    /// Create a new Cassanda session.
    //    /// It's recommended to use Cluster.connect() instead
    //    // FIXME find a way to hide this from external api
    //    pub fn new() -> Session {
    //        unsafe { Session(cass_session_new()) }
    //    }
    //
    //    pub fn new2() -> *mut _Session {
    //        unsafe { cass_session_new() }
    //    }

    /// Connects a session.
    pub fn connect(self, cluster: &Cluster) -> SessionFuture {
        unsafe { SessionFuture::build(cass_session_connect(self.0, cluster.inner())) }
    }

    ///Connects a session and sets the keyspace.
    pub fn connect_keyspace(&self, cluster: &Cluster, keyspace: &str) -> Result<Future, NulError> {
        unsafe {
            Ok(Future::build(cass_session_connect_keyspace(self.0,
                                                           cluster.inner(),
                                                           try!(CString::new(keyspace)).as_ptr())))
        }
    }

    ///Closes the session instance, outputs a close future which can
    ///be used to determine when the session has been terminated. This allows
    ///in-flight requests to finish.
    pub fn close(self) -> CloseFuture {
        unsafe { CloseFuture::build(cass_session_close(self.0)) }
    }

    ///Create a prepared statement.
    pub fn prepare(&self, query: &str) -> Result<PreparedFuture, CassError> {
        unsafe {
            Ok(PreparedFuture::build(cass_session_prepare(self.0, CString::new(query).expect("must be utf8").as_ptr())))
        }
    }

    //    ///Execute a query or bound statement.
    //    pub fn execute(&self, statement: &str, parameter_count: u64) -> ResultFuture {
    //        unsafe {
    //            ResultFuture::build(cass_session_execute(self.0,
    //            Statement::new(statement, parameter_count).inner()))
    //        }
    //    }

    /// Execute a batch statement.
    pub fn execute_batch(&self, batch: Batch) -> ResultFuture {
        ResultFuture::build(unsafe { cass_session_execute_batch(self.0, batch.inner()) })
    }

    /// Execute a statement.
    pub fn execute(&self, statement: &Statement) -> ResultFuture {
        unsafe { ResultFuture::build(cass_session_execute(self.0, statement.inner())) }
    }

    ///Gets a snapshot of this session's schema metadata. The returned
    ///snapshot of the schema metadata is not updated. This function
    ///must be called again to retrieve any schema changes since the
    ///previous call.
    pub fn get_schema_meta(&self) -> SchemaMeta {
        unsafe { SchemaMeta::build(cass_session_get_schema_meta(self.0)) }
    }

    ///Gets a copy of this session's performance/diagnostic metrics.
    pub fn get_metrics(&self) -> SessionMetrics {
        unsafe {
            let mut metrics = mem::zeroed();
            cass_session_get_metrics(self.0, &mut metrics);
            SessionMetrics::build(&metrics)
        }
    }

    //    pub fn get_schema(&self) -> Schema {
    //        unsafe { Schema(cass_session_get_schema(self.0)) }
    //    }
}
<commit_msg>Expose Session::new<commit_after>#![allow(non_camel_case_types)]
#![allow(dead_code)]
#![allow(missing_copy_implementations)]

use std::mem;

use std::ffi::CString;
use std::ffi::NulError;
use cassandra::batch::Batch;
use cassandra::future::{CloseFuture, Future, PreparedFuture, ResultFuture, SessionFuture};
use cassandra::error::CassError;
use cassandra::statement::Statement;
use cassandra::schema::schema_meta::SchemaMeta;
use cassandra::metrics::SessionMetrics;
use cassandra::cluster::Cluster;
use cassandra::util::Protected;

use cassandra_sys::CassSession as _Session;
use cassandra_sys::cass_session_free;
use cassandra_sys::cass_session_close;
use cassandra_sys::cass_session_connect;
use cassandra_sys::cass_session_prepare;
use cassandra_sys::cass_session_execute;
use cassandra_sys::cass_session_execute_batch;
use cassandra_sys::cass_session_get_schema_meta;
use cassandra_sys::cass_session_connect_keyspace;
use cassandra_sys::cass_session_get_metrics;
use cassandra_sys::cass_session_new;

///A session object is used to execute queries and maintains cluster state through
///the control connection. The control connection is used to auto-discover nodes and
///monitor cluster changes (topology and schema). Each session also maintains multiple
/// /pools of connections to cluster nodes which are used to query the cluster.
///
///Instances of the session object are thread-safe to execute queries.
#[derive(Debug)]
pub struct Session(pub *mut _Session);
unsafe impl Sync for Session {}
unsafe impl Send for Session {}

impl Protected<*mut _Session> for Session {
    fn inner(&self) -> *mut _Session {
        self.0
    }
    fn build(inner: *mut _Session) -> Self {
        Session(inner)
    }
}

impl Drop for Session {
    /// Frees a session instance. If the session is still connected it will be synchronously
    /// closed before being deallocated.
    fn drop(&mut self) {
        debug!("dropping session");
        unsafe { cass_session_free(self.0) }
    }
}

impl Session {
    /// Create a new Cassanda session.
    /// It's recommended to use Cluster.connect() instead
    pub fn new() -> Session {
        unsafe { Session(cass_session_new()) }
    }

    //    pub fn new2() -> *mut _Session {
    //        unsafe { cass_session_new() }
    //    }

    /// Connects a session.
    pub fn connect(self, cluster: &Cluster) -> SessionFuture {
        unsafe { SessionFuture::build(cass_session_connect(self.0, cluster.inner())) }
    }

    ///Connects a session and sets the keyspace.
    pub fn connect_keyspace(&self, cluster: &Cluster, keyspace: &str) -> Result<Future, NulError> {
        unsafe {
            Ok(Future::build(cass_session_connect_keyspace(self.0,
                                                           cluster.inner(),
                                                           try!(CString::new(keyspace)).as_ptr())))
        }
    }

    ///Closes the session instance, outputs a close future which can
    ///be used to determine when the session has been terminated. This allows
    ///in-flight requests to finish.
    pub fn close(self) -> CloseFuture {
        unsafe { CloseFuture::build(cass_session_close(self.0)) }
    }

    ///Create a prepared statement.
    pub fn prepare(&self, query: &str) -> Result<PreparedFuture, CassError> {
        unsafe {
            Ok(PreparedFuture::build(cass_session_prepare(self.0, CString::new(query).expect("must be utf8").as_ptr())))
        }
    }

    //    ///Execute a query or bound statement.
    //    pub fn execute(&self, statement: &str, parameter_count: u64) -> ResultFuture {
    //        unsafe {
    //            ResultFuture::build(cass_session_execute(self.0,
    //            Statement::new(statement, parameter_count).inner()))
    //        }
    //    }

    /// Execute a batch statement.
    pub fn execute_batch(&self, batch: Batch) -> ResultFuture {
        ResultFuture::build(unsafe { cass_session_execute_batch(self.0, batch.inner()) })
    }

    /// Execute a statement.
    pub fn execute(&self, statement: &Statement) -> ResultFuture {
        unsafe { ResultFuture::build(cass_session_execute(self.0, statement.inner())) }
    }

    ///Gets a snapshot of this session's schema metadata. The returned
    ///snapshot of the schema metadata is not updated. This function
    ///must be called again to retrieve any schema changes since the
    ///previous call.
    pub fn get_schema_meta(&self) -> SchemaMeta {
        unsafe { SchemaMeta::build(cass_session_get_schema_meta(self.0)) }
    }

    ///Gets a copy of this session's performance/diagnostic metrics.
    pub fn get_metrics(&self) -> SessionMetrics {
        unsafe {
            let mut metrics = mem::zeroed();
            cass_session_get_metrics(self.0, &mut metrics);
            SessionMetrics::build(&metrics)
        }
    }

    //    pub fn get_schema(&self) -> Schema {
    //        unsafe { Schema(cass_session_get_schema(self.0)) }
    //    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write};

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use prost;
use prost_types;

use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};

use super::{ReadError, WriteError};

/// A Fleetspeak client connection object.
///
/// This connection owns two buffers: one for input and one for output. Usually,
/// one does not want to instantiate connection object themselves. Since the
/// Fleetspeak client will spawn the service process and provide it with file
/// descriptors to talk to, for user convenience there is a standard global
/// Fleetspeak client connection object that uses these descriptors.
pub struct Connection<R, W> {
    input: R,
    output: W,
}

pub struct Packet<M> {
    pub service: String,
    pub kind: Option<String>,
    pub data: M,
}

impl<R: Read, W: Write> Connection<R, W> {

    /// Creates a new Fleetspeak connection.
    ///
    /// This function will perform a handshake procedure in order to verify
    /// correctness of the input and the output buffers. If the handshake
    /// procedure fails, an error is reported.
    pub fn new(input: R, output: W) -> std::io::Result<Self> {
        let mut conn = Connection {
            input: input,
            output: output,
        };
        conn.handshake()?;

        Ok(conn)
    }

    /// Sends a heartbeat information through this connection.
    ///
    /// All client services should heartbeat from time to time. Otherwise, from
    /// the Fleetspeak perspective, the service is unresponsive and should be
    /// restarted.
    ///
    /// The exact frequency of the required heartbeat is defined in the service
    /// configuration file.
    pub fn heartbeat(&mut self) -> Result<(), WriteError> {
        let msg = Message {
            message_type: String::from("Heartbeat"),
            destination: Some(Address {
                service_name: String::from("system"),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Sends the startup information through this connection.
    ///
    /// All clients are required to send this information on startup. If the
    /// client does not receive this information quickly enough, the service
    /// will be killed.
    ///
    /// The `version` string should contain a self-reported version of the
    /// service. This data is used primarily for statistics.
    pub fn startup(&mut self, version: &str) -> Result<(), WriteError> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: String::from(version),
        };

        let mut buf = Vec::new();
        prost::Message::encode(&data, &mut buf)?;

        let msg = Message {
            message_type: String::from("StartupData"),
            destination: Some(Address {
                service_name: String::from("system"),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf,
                type_url: String::from("type.googleapis.com/fleetspeak.channel.StartupData"),
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Sends the message to the Fleetspeak server through this connection.
    ///
    /// The message is sent to the server-side `service` and tagged with the
    /// `kind` type. Note that this message type is rather irrelevant for
    /// Fleetspeak and it is up to the service what to do with this information.
    pub fn send<M>(&mut self, packet: Packet<M>) -> Result<(), WriteError>
    where
        M: prost::Message,
    {
        let mut buf = Vec::new();
        prost::Message::encode(&packet.data, &mut buf)?;

        let msg = Message {
            message_type: packet.kind.unwrap_or_else(String::new),
            destination: Some(Address {
                service_name: packet.service,
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf,
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Receives the message from the Fleetspeak server through this connection.
    ///
    /// This function will block until there is a message to be read in the
    /// input. Errors are reported in case of any I/O failure or if the read
    /// message was malformed (e.g. it cannot be parsed to the expected type).
    pub fn receive<M>(&mut self) -> Result<Packet<M>, ReadError>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // While missing source address might not be consider a critical error
        // in most cases, for our own sanity we just disregard such messages.
        // Allowing such behaviour might indicate a more severe problem with
        // Fleetspeak and ignoring it simply masks the issue. This might be
        // reconsidered in the future.
        let service = match msg.source {
            Some(addr) => addr.service_name,
            None => return Err(ReadError::malformed("missing source address")),
        };

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = msg.data.unwrap_or_else(Default::default);

        Ok(Packet {
            service: service,
            kind: Some(msg.message_type),
            data: prost::Message::decode(&data.value[..])?
        })
    }

    /// Emits a raw Fleetspeak message to the server through this connection.
    ///
    /// This method does not perform any validation of the message being emitted
    /// and assumes that all the required fields are present.
    ///
    /// Note that this call will fail only if the message cannot be written to
    /// the output or cannot be properly encoded but will succeed even if the
    /// message is not what the server expects.
    fn emit(&mut self, msg: Message) -> Result<(), WriteError> {
        let mut buf = Vec::new();
        prost::Message::encode(&msg, &mut buf)?;

        self.output.write_u32::<LittleEndian>(buf.len() as u32)?;
        self.output.write(&buf)?;
        self.write_magic()?;
        self.output.flush()?;

        Ok(())
    }

    /// Collects a raw Fleetspeeak message from this connection.
    ///
    /// This function will block until there is a message to be read from the
    /// input. It will fail in case of any I/O error or if the message cannot
    /// be parsed as a Fleetspeak message.
    fn collect(&mut self) -> Result<Message, ReadError> {
        let len = self.input.read_u32::<LittleEndian>()? as usize;
        let mut buf = vec!(0; len);
        self.input.read_exact(&mut buf[..])?;
        self.read_magic()?;

        Ok(prost::Message::decode(&buf[..])?)
    }

    /// Executes the handshake procedure.
    fn handshake(&mut self) -> std::io::Result<()> {
        self.write_magic()?;
        self.output.flush()?;
        self.read_magic()?;

        Ok(())
    }

    /// Writes the Fleetspeak magic to the output buffer.
    fn write_magic(&mut self) -> Result<(), WriteError> {
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    /// Reads the Fleetspeak magic from the input buffer.
    fn read_magic(&mut self) -> Result<(), ReadError> {
        let magic = self.input.read_u32::<LittleEndian>()?;
        if magic != MAGIC {
            return Err(ReadError::Magic(magic));
        }

        Ok(())
    }
}

const MAGIC: u32 = 0xf1ee1001;

#[cfg(test)]
mod tests {
    use std::io::Cursor;
    use super::*;

    #[test]
    fn handshake_good_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(MAGIC).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_ok());

        let mut cur = Cursor::new(&mut buf_out[..]);
        assert_eq!(cur.read_u32::<LittleEndian>().unwrap(), MAGIC);
    }

    #[test]
    fn handshake_bad_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(0xf1ee1337).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_err());
    }
}
<commit_msg>Write documentation for packets.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write};

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use prost;
use prost_types;

use fleetspeak_proto::common::{Message, Address};
use fleetspeak_proto::channel::{StartupData};

use super::{ReadError, WriteError};

/// A Fleetspeak client connection object.
///
/// This connection owns two buffers: one for input and one for output. Usually,
/// one does not want to instantiate connection object themselves. Since the
/// Fleetspeak client will spawn the service process and provide it with file
/// descriptors to talk to, for user convenience there is a standard global
/// Fleetspeak client connection object that uses these descriptors.
pub struct Connection<R, W> {
    input: R,
    output: W,
}

/// A Fleetspeak client communication packet.
///
/// This structure represents incoming or outgoing packet objects delivered by
/// Fleetspeak. This is a simplified version of the underlying Protocol Buffers
/// message that exposes too much irrelevant fields and makes the protocol easy
/// to misuse.
pub struct Packet<M> {
    /// A name of the server-side service that sent or should receive the data.
    pub service: String,
    /// An optional message type that can be used by the server-side service.
    pub kind: Option<String>,
    /// A message to sent to the specified service.
    pub data: M,
}

impl<R: Read, W: Write> Connection<R, W> {

    /// Creates a new Fleetspeak connection.
    ///
    /// This function will perform a handshake procedure in order to verify
    /// correctness of the input and the output buffers. If the handshake
    /// procedure fails, an error is reported.
    pub fn new(input: R, output: W) -> std::io::Result<Self> {
        let mut conn = Connection {
            input: input,
            output: output,
        };
        conn.handshake()?;

        Ok(conn)
    }

    /// Sends a heartbeat information through this connection.
    ///
    /// All client services should heartbeat from time to time. Otherwise, from
    /// the Fleetspeak perspective, the service is unresponsive and should be
    /// restarted.
    ///
    /// The exact frequency of the required heartbeat is defined in the service
    /// configuration file.
    pub fn heartbeat(&mut self) -> Result<(), WriteError> {
        let msg = Message {
            message_type: String::from("Heartbeat"),
            destination: Some(Address {
                service_name: String::from("system"),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Sends the startup information through this connection.
    ///
    /// All clients are required to send this information on startup. If the
    /// client does not receive this information quickly enough, the service
    /// will be killed.
    ///
    /// The `version` string should contain a self-reported version of the
    /// service. This data is used primarily for statistics.
    pub fn startup(&mut self, version: &str) -> Result<(), WriteError> {
        let data = StartupData {
            pid: std::process::id() as i64,
            version: String::from(version),
        };

        let mut buf = Vec::new();
        prost::Message::encode(&data, &mut buf)?;

        let msg = Message {
            message_type: String::from("StartupData"),
            destination: Some(Address {
                service_name: String::from("system"),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf,
                type_url: String::from("type.googleapis.com/fleetspeak.channel.StartupData"),
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Sends the message to the Fleetspeak server through this connection.
    ///
    /// The message is sent to the server-side `service` and tagged with the
    /// `kind` type. Note that this message type is rather irrelevant for
    /// Fleetspeak and it is up to the service what to do with this information.
    pub fn send<M>(&mut self, packet: Packet<M>) -> Result<(), WriteError>
    where
        M: prost::Message,
    {
        let mut buf = Vec::new();
        prost::Message::encode(&packet.data, &mut buf)?;

        let msg = Message {
            message_type: packet.kind.unwrap_or_else(String::new),
            destination: Some(Address {
                service_name: packet.service,
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: buf,
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    /// Receives the message from the Fleetspeak server through this connection.
    ///
    /// This function will block until there is a message to be read in the
    /// input. Errors are reported in case of any I/O failure or if the read
    /// message was malformed (e.g. it cannot be parsed to the expected type).
    pub fn receive<M>(&mut self) -> Result<Packet<M>, ReadError>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // While missing source address might not be consider a critical error
        // in most cases, for our own sanity we just disregard such messages.
        // Allowing such behaviour might indicate a more severe problem with
        // Fleetspeak and ignoring it simply masks the issue. This might be
        // reconsidered in the future.
        let service = match msg.source {
            Some(addr) => addr.service_name,
            None => return Err(ReadError::malformed("missing source address")),
        };

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = msg.data.unwrap_or_else(Default::default);

        Ok(Packet {
            service: service,
            kind: Some(msg.message_type),
            data: prost::Message::decode(&data.value[..])?
        })
    }

    /// Emits a raw Fleetspeak message to the server through this connection.
    ///
    /// This method does not perform any validation of the message being emitted
    /// and assumes that all the required fields are present.
    ///
    /// Note that this call will fail only if the message cannot be written to
    /// the output or cannot be properly encoded but will succeed even if the
    /// message is not what the server expects.
    fn emit(&mut self, msg: Message) -> Result<(), WriteError> {
        let mut buf = Vec::new();
        prost::Message::encode(&msg, &mut buf)?;

        self.output.write_u32::<LittleEndian>(buf.len() as u32)?;
        self.output.write(&buf)?;
        self.write_magic()?;
        self.output.flush()?;

        Ok(())
    }

    /// Collects a raw Fleetspeeak message from this connection.
    ///
    /// This function will block until there is a message to be read from the
    /// input. It will fail in case of any I/O error or if the message cannot
    /// be parsed as a Fleetspeak message.
    fn collect(&mut self) -> Result<Message, ReadError> {
        let len = self.input.read_u32::<LittleEndian>()? as usize;
        let mut buf = vec!(0; len);
        self.input.read_exact(&mut buf[..])?;
        self.read_magic()?;

        Ok(prost::Message::decode(&buf[..])?)
    }

    /// Executes the handshake procedure.
    fn handshake(&mut self) -> std::io::Result<()> {
        self.write_magic()?;
        self.output.flush()?;
        self.read_magic()?;

        Ok(())
    }

    /// Writes the Fleetspeak magic to the output buffer.
    fn write_magic(&mut self) -> Result<(), WriteError> {
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    /// Reads the Fleetspeak magic from the input buffer.
    fn read_magic(&mut self) -> Result<(), ReadError> {
        let magic = self.input.read_u32::<LittleEndian>()?;
        if magic != MAGIC {
            return Err(ReadError::Magic(magic));
        }

        Ok(())
    }
}

const MAGIC: u32 = 0xf1ee1001;

#[cfg(test)]
mod tests {
    use std::io::Cursor;
    use super::*;

    #[test]
    fn handshake_good_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(MAGIC).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_ok());

        let mut cur = Cursor::new(&mut buf_out[..]);
        assert_eq!(cur.read_u32::<LittleEndian>().unwrap(), MAGIC);
    }

    #[test]
    fn handshake_bad_magic() {
        let mut buf_in = [0; 1024];
        let mut buf_out = [0; 1024];

        let mut cur = Cursor::new(&mut buf_in[..]);
        assert!(cur.write_u32::<LittleEndian>(0xf1ee1337).is_ok());

        let cur_in = Cursor::new(&mut buf_in[..]);
        let cur_out = Cursor::new(&mut buf_out[..]);
        assert!(Connection::new(cur_in, cur_out).is_err());
    }
}
<|endoftext|>


<commit_before>use dns::{DnsError, Result};

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OpCode {
    QUERY  = 0x00,
    IQUERY = 0x08,
    STATUS = 0x10,
    NOTIFY = 0x20,
    UPDATE = 0x28,
}

impl OpCode {
    #[inline]
    pub fn unpack(v: u8) -> Result<OpCode> {
        Ok(match v {
            0x00 => OpCode::QUERY,
            0x08 => OpCode::IQUERY,
            0x10 => OpCode::STATUS,
            0x20 => OpCode::NOTIFY,
            0x28 => OpCode::UPDATE,
            _    => return Err(DnsError::BadOpCode),
        })
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RCode {
    NOERROR  = 0x00,
    FORMERR  = 0x01,
    SERVFAIL = 0x02,
    NXDOMAIN = 0x03,
    NOTIMPL  = 0x04,
    REFUSED  = 0x05,
    YXDOMAIN = 0x06,
    YXRRSET  = 0x07,
    NXRRSET  = 0x08,
    NOTAUTH  = 0x09,
    NOTZONE  = 0x0a,
    BADSIG   = 0x10, // or BADVERS ??
    BADKEY   = 0x11,
    BADTIME  = 0x12,
    BADMODE  = 0x13,
    BADNAME  = 0x14,
    BADALG   = 0x15,
    BADTRUNC = 0x16,
}

impl RCode {
    #[inline]
    pub fn unpack(v: u8) -> Result<RCode> {
        Ok(match v {
            0x00 => RCode::NOERROR,
            0x01 => RCode::FORMERR,
            0x02 => RCode::SERVFAIL,
            0x03 => RCode::NXDOMAIN,
            0x04 => RCode::NOTIMPL,
            0x05 => RCode::REFUSED,
            0x06 => RCode::YXDOMAIN,
            0x07 => RCode::YXRRSET,
            0x08 => RCode::NXRRSET,
            0x09 => RCode::NOTAUTH,
            0x0a => RCode::NOTZONE,
            0x10 => RCode::BADSIG,
            0x11 => RCode::BADKEY,
            0x12 => RCode::BADTIME,
            0x13 => RCode::BADMODE,
            0x14 => RCode::BADNAME,
            0x15 => RCode::BADALG,
            0x16 => RCode::BADTRUNC,
            _    => return Err(DnsError::BadRCode),
        })
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Class {
    IN   = 0x01, // INET
    CH   = 0x03, // ClassCHAOS
    HS   = 0x04, // HESIOD
    NONE = 0xfe,
    ANY  = 0xff,
}

impl Class {
    #[inline]
    pub fn unpack(v: u16) -> Result<Class> {
        Ok(match v {
            0x01 => Class::IN,
            0x03 => Class::CH,
            0x04 => Class::HS,
            0xfe => Class::NONE,
            0xff => Class::ANY,
            _ => return Err(DnsError::BadClass)
        })
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RType {
    NONE       = 0x0000,
    A          = 0x0001,
    NS         = 0x0002,
    MD         = 0x0003,
    MF         = 0x0004,
    CNAME      = 0x0005,
    SOA        = 0x0006,
    MB         = 0x0007,
    MG         = 0x0008,
    MR         = 0x0009,
    NULL       = 0x000A,
    WKS        = 0x000B,
    PTR        = 0x000C,
    HINFO      = 0x000D,
    MINFO      = 0x000E,
    MX         = 0x000F,
    TXT        = 0x0010,
    RP         = 0x0011,
    AFSDB      = 0x0012,
    X25        = 0x0013,
    ISDN       = 0x0014,
    RT         = 0x0015,
    NSAP       = 0x0016,
    NSAPPTR    = 0x0017,
    SIG        = 0x0018,
    KEY        = 0x0019,
    PX         = 0x001A,
    GPOS       = 0x001B,
    AAAA       = 0x001C,
    LOC        = 0x001D,
    NXT        = 0x001E,
    EID        = 0x001F,
    NIMLOC     = 0x0020,
    SRV        = 0x0021,
    ATMA       = 0x0022,
    NAPTR      = 0x0023,
    KX         = 0x0024,
    CERT       = 0x0025,
    DNAME      = 0x0026,
    SINK       = 0x0027,
    OPT        = 0x0028,
    APL        = 0x0029,
    DS         = 0x002A,
    SSHFP      = 0x002B,
    IPSECKEY   = 0x002C,
    RRSIG      = 0x002D,
    NSEC       = 0x002E,
    DNSKEY     = 0x002F,
    DHCID      = 0x0030,
    NSEC3      = 0x0031,
    NSEC3PARAM = 0x0032,
    TLSA       = 0x0033,
    HIP        = 0x0036,
    NINFO      = 0x0037,
    RKEY       = 0x0038,
    TALINK     = 0x0039,
    CDS        = 0x003A,
    CDNSKEY    = 0x003B,
    OPENPGPKEY = 0x003C,
    SPF        = 0x0063,
    UINFO      = 0x0064,
    UID        = 0x0065,
    GID        = 0x0066,
    UNSPEC     = 0x0067,
    NID        = 0x0068,
    L32        = 0x0069,
    L64        = 0x006A,
    LP         = 0x006B,
    EUI48      = 0x006C,
    EUI64      = 0x006D,

    TKEY       = 0x00F9,
    TSIG       = 0x00FA,

    IXFR       = 0x00FB,
    AXFR       = 0x00FC,
    MAILB      = 0x00FD,
    MAILA      = 0x00FE,
    ANY        = 0x00FF,

    URI        = 0x0100,
    CAA        = 0x0101,
    TA         = 0x8000,
    DLV        = 0x8001,
    Reserved   = 0xFFFF,
}

impl RType {
    #[inline]
    pub fn unpack(v: u16) -> Result<RType> {
        Ok(match v {
            0x0000 => RType::NONE, // check if valid?
            0x0001 => RType::A,
            0x0002 => RType::NS,
            0x0003 => RType::MD,
            0x0004 => RType::MF,
            0x0005 => RType::CNAME,
            0x0006 => RType::SOA,
            0x0007 => RType::MB,
            0x0008 => RType::MG,
            0x0009 => RType::MR,
            0x000A => RType::NULL,
            0x000B => RType::WKS,
            0x000C => RType::PTR,
            0x000D => RType::HINFO,
            0x000E => RType::MINFO,
            0x000F => RType::MX,
            0x0010 => RType::TXT,
            0x0011 => RType::RP,
            0x0012 => RType::AFSDB,
            0x0013 => RType::X25,
            0x0014 => RType::ISDN,
            0x0015 => RType::RT,
            0x0016 => RType::NSAP,
            0x0017 => RType::NSAPPTR,
            0x0018 => RType::SIG,
            0x0019 => RType::KEY,
            0x001A => RType::PX,
            0x001B => RType::GPOS,
            0x001C => RType::AAAA,
            0x001D => RType::LOC,
            0x001E => RType::NXT,
            0x001F => RType::EID,
            0x0020 => RType::NIMLOC,
            0x0021 => RType::SRV,
            0x0022 => RType::ATMA,
            0x0023 => RType::NAPTR,
            0x0024 => RType::KX,
            0x0025 => RType::CERT,
            0x0026 => RType::DNAME,
            0x0027 => RType::SINK,
            0x0028 => RType::OPT,
            0x0029 => RType::APL,
            0x002A => RType::DS,
            0x002B => RType::SSHFP,
            0x002C => RType::IPSECKEY,
            0x002D => RType::RRSIG,
            0x002E => RType::NSEC,
            0x002F => RType::DNSKEY,
            0x0030 => RType::DHCID,
            0x0031 => RType::NSEC3,
            0x0032 => RType::NSEC3PARAM,
            0x0033 => RType::TLSA,
            0x0036 => RType::HIP,
            0x0037 => RType::NINFO,
            0x0038 => RType::RKEY,
            0x0039 => RType::TALINK,
            0x003A => RType::CDS,
            0x003B => RType::CDNSKEY,
            0x003C => RType::OPENPGPKEY,
            0x0063 => RType::SPF,
            0x0064 => RType::UINFO,
            0x0065 => RType::UID,
            0x0066 => RType::GID,
            0x0067 => RType::UNSPEC,
            0x0068 => RType::NID,
            0x0069 => RType::L32,
            0x006A => RType::L64,
            0x006B => RType::LP,
            0x006C => RType::EUI48,
            0x006D => RType::EUI64,

            0x00F9 => RType::TKEY,
            0x00FA => RType::TSIG,

            // QType only
            0x00FB => RType::IXFR,
            0x00FC => RType::AXFR,
            0x00FD => RType::MAILB,
            0x00FE => RType::MAILA,
            0x00FF => RType::ANY,

            0x0100 => RType::URI,
            0x0101 => RType::CAA,
            0x8000 => RType::TA,
            0x8001 => RType::DLV,

            0xFFFF => RType::Reserved,

            _ => return Err(DnsError::BadRType)
        })
    }
}
<commit_msg>Tidying up types<commit_after>use dns::{DnsError, Result};

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum OpCode {
    QUERY  = 0x00,
    IQUERY = 0x08,
    STATUS = 0x10,
    NOTIFY = 0x20,
    UPDATE = 0x28,
}

impl OpCode {
    #[inline]
    pub fn unpack(v: u8) -> Result<OpCode> {
        Ok(match v {
            0x00 => OpCode::QUERY,
            0x08 => OpCode::IQUERY,
            0x10 => OpCode::STATUS,
            0x20 => OpCode::NOTIFY,
            0x28 => OpCode::UPDATE,
            _    => return Err(DnsError::BadOpCode),
        })
    }
}

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RCode {
    NOERROR  = 0x00,
    FORMERR  = 0x01,
    SERVFAIL = 0x02,
    NXDOMAIN = 0x03,
    NOTIMPL  = 0x04,
    REFUSED  = 0x05,
    YXDOMAIN = 0x06,
    YXRRSET  = 0x07,
    NXRRSET  = 0x08,
    NOTAUTH  = 0x09,
    NOTZONE  = 0x0a,
    BADSIG   = 0x10, // or BADVERS
    BADKEY   = 0x11,
    BADTIME  = 0x12,
    BADMODE  = 0x13,
    BADNAME  = 0x14,
    BADALG   = 0x15,
    BADTRUNC = 0x16,
}

impl RCode {
    #[inline]
    pub fn unpack(v: u8) -> Result<RCode> {
        Ok(match v {
            0x00 => RCode::NOERROR,
            0x01 => RCode::FORMERR,
            0x02 => RCode::SERVFAIL,
            0x03 => RCode::NXDOMAIN,
            0x04 => RCode::NOTIMPL,
            0x05 => RCode::REFUSED,
            0x06 => RCode::YXDOMAIN,
            0x07 => RCode::YXRRSET,
            0x08 => RCode::NXRRSET,
            0x09 => RCode::NOTAUTH,
            0x0a => RCode::NOTZONE,
            0x10 => RCode::BADSIG,
            0x11 => RCode::BADKEY,
            0x12 => RCode::BADTIME,
            0x13 => RCode::BADMODE,
            0x14 => RCode::BADNAME,
            0x15 => RCode::BADALG,
            0x16 => RCode::BADTRUNC,
            _    => return Err(DnsError::BadRCode),
        })
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Class {
    IN   = 0x01, // INET
    CH   = 0x03, // ClassCHAOS
    HS   = 0x04, // HESIOD
    NONE = 0xfe,
    ANY  = 0xff,
}

impl Class {
    #[inline]
    pub fn unpack(v: u16) -> Result<Class> {
        Ok(match v {
            0x01 => Class::IN,
            0x03 => Class::CH,
            0x04 => Class::HS,
            0xfe => Class::NONE,
            0xff => Class::ANY,
            _ => return Err(DnsError::BadClass)
        })
    }
}

#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RType {
    ZERO       = 0x0000,
    A          = 0x0001,
    NS         = 0x0002,
    MD         = 0x0003,
    MF         = 0x0004,
    CNAME      = 0x0005,
    SOA        = 0x0006,
    MB         = 0x0007,
    MG         = 0x0008,
    MR         = 0x0009,
    NULL       = 0x000A,
    WKS        = 0x000B,
    PTR        = 0x000C,
    HINFO      = 0x000D,
    MINFO      = 0x000E,
    MX         = 0x000F,
    TXT        = 0x0010,
    RP         = 0x0011,
    AFSDB      = 0x0012,
    X25        = 0x0013,
    ISDN       = 0x0014,
    RT         = 0x0015,
    NSAP       = 0x0016,
    NSAPPTR    = 0x0017,
    SIG        = 0x0018,
    KEY        = 0x0019,
    PX         = 0x001A,
    GPOS       = 0x001B,
    AAAA       = 0x001C,
    LOC        = 0x001D,
    NXT        = 0x001E,
    EID        = 0x001F,
    NIMLOC     = 0x0020,
    SRV        = 0x0021,
    ATMA       = 0x0022,
    NAPTR      = 0x0023,
    KX         = 0x0024,
    CERT       = 0x0025,
    DNAME      = 0x0026,
    SINK       = 0x0027,
    OPT        = 0x0028,
    APL        = 0x0029,
    DS         = 0x002A,
    SSHFP      = 0x002B,
    IPSECKEY   = 0x002C,
    RRSIG      = 0x002D,
    NSEC       = 0x002E,
    DNSKEY     = 0x002F,
    DHCID      = 0x0030,
    NSEC3      = 0x0031,
    NSEC3PARAM = 0x0032,
    TLSA       = 0x0033,
    HIP        = 0x0036,
    NINFO      = 0x0037,
    RKEY       = 0x0038,
    TALINK     = 0x0039,
    CDS        = 0x003A,
    CDNSKEY    = 0x003B,
    OPENPGPKEY = 0x003C,
    SPF        = 0x0063,
    UINFO      = 0x0064,
    UID        = 0x0065,
    GID        = 0x0066,
    UNSPEC     = 0x0067,
    NID        = 0x0068,
    L32        = 0x0069,
    L64        = 0x006A,
    LP         = 0x006B,
    EUI48      = 0x006C,
    EUI64      = 0x006D,

    TKEY       = 0x00F9,
    TSIG       = 0x00FA,

    IXFR       = 0x00FB,
    AXFR       = 0x00FC,
    MAILB      = 0x00FD,
    MAILA      = 0x00FE,
//    ANY        = 0x00FF,

    URI        = 0x0100,
    CAA        = 0x0101,
    TA         = 0x8000,
    DLV        = 0x8001,
    Reserved   = 0xFFFF,
}

impl RType {
    #[inline]
    pub fn unpack(v: u16) -> Result<RType> {
        Ok(match v {
            0x0000 => RType::ZERO,
            0x0001 => RType::A,
            0x0002 => RType::NS,
            0x0003 => RType::MD,
            0x0004 => RType::MF,
            0x0005 => RType::CNAME,
            0x0006 => RType::SOA,
            0x0007 => RType::MB,
            0x0008 => RType::MG,
            0x0009 => RType::MR,
            0x000A => RType::NULL,
            0x000B => RType::WKS,
            0x000C => RType::PTR,
            0x000D => RType::HINFO,
            0x000E => RType::MINFO,
            0x000F => RType::MX,
            0x0010 => RType::TXT,
            0x0011 => RType::RP,
            0x0012 => RType::AFSDB,
            0x0013 => RType::X25,
            0x0014 => RType::ISDN,
            0x0015 => RType::RT,
            0x0016 => RType::NSAP,
            0x0017 => RType::NSAPPTR,
            0x0018 => RType::SIG,
            0x0019 => RType::KEY,
            0x001A => RType::PX,
            0x001B => RType::GPOS,
            0x001C => RType::AAAA,
            0x001D => RType::LOC,
            0x001E => RType::NXT,
            0x001F => RType::EID,
            0x0020 => RType::NIMLOC,
            0x0021 => RType::SRV,
            0x0022 => RType::ATMA,
            0x0023 => RType::NAPTR,
            0x0024 => RType::KX,
            0x0025 => RType::CERT,
            0x0026 => RType::DNAME,
            0x0027 => RType::SINK,
            0x0028 => RType::OPT,
            0x0029 => RType::APL,
            0x002A => RType::DS,
            0x002B => RType::SSHFP,
            0x002C => RType::IPSECKEY,
            0x002D => RType::RRSIG,
            0x002E => RType::NSEC,
            0x002F => RType::DNSKEY,
            0x0030 => RType::DHCID,
            0x0031 => RType::NSEC3,
            0x0032 => RType::NSEC3PARAM,
            0x0033 => RType::TLSA,
            0x0036 => RType::HIP,
            0x0037 => RType::NINFO,
            0x0038 => RType::RKEY,
            0x0039 => RType::TALINK,
            0x003A => RType::CDS,
            0x003B => RType::CDNSKEY,
            0x003C => RType::OPENPGPKEY,
            0x0063 => RType::SPF,
            0x0064 => RType::UINFO,
            0x0065 => RType::UID,
            0x0066 => RType::GID,
            0x0067 => RType::UNSPEC,
            0x0068 => RType::NID,
            0x0069 => RType::L32,
            0x006A => RType::L64,
            0x006B => RType::LP,
            0x006C => RType::EUI48,
            0x006D => RType::EUI64,

            0x00F9 => RType::TKEY,
            0x00FA => RType::TSIG,

            // QType only
            0x00FB => RType::IXFR,
            0x00FC => RType::AXFR,
            0x00FD => RType::MAILB,
            0x00FE => RType::MAILA,
//            0x00FF => RType::ANY,

            0x0100 => RType::URI,
            0x0101 => RType::CAA,
            0x8000 => RType::TA,
            0x8001 => RType::DLV,

            0xFFFF => RType::Reserved,

            _ => return Err(DnsError::BadRType)
        })
    }
}
<|endoftext|>


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

// Manage the linear volume that stores metadata on pool levels 5-7.

use std::{
    convert::From,
    fs::{create_dir, create_dir_all, read_dir, remove_dir, remove_file, rename, OpenOptions},
    io::{prelude::*, ErrorKind},
    path::{Path, PathBuf},
};

use nix::{
    mount::{mount, umount, MsFlags},
    sys::stat::stat,
};
use retry::{delay::Fixed, retry_with_index};

use devicemapper::{DmDevice, DmOptions, LinearDev, LinearDevTargetParams, Sectors, TargetLine};

use crate::{
    engine::{
        strat_engine::{
            cmd::create_fs,
            dm::get_dm,
            ns::NS_TMPFS_LOCATION,
            serde_structs::FilesystemSave,
            thinpool::filesystem::{fs_usage, StratFilesystem},
        },
        types::{FilesystemUuid, Name, PoolUuid, StratisUuid},
    },
    stratis::{StratisError, StratisResult},
};

const FILESYSTEM_DIR: &str = "filesystems";

#[derive(Debug)]
pub struct MetadataVol {
    dev: LinearDev,
    mount_pt: PathBuf,
}

impl MetadataVol {
    /// Minimum allocation size for a file is a block which will be 4k in this
    /// set up.
    const XFS_MIN_FILE_ALLOC_SIZE: Sectors = Sectors(8);

    /// Initialize a new Metadata Volume.
    pub fn initialize(pool_uuid: PoolUuid, dev: LinearDev) -> StratisResult<MetadataVol> {
        create_fs(&dev.devnode(), Some(StratisUuid::Pool(pool_uuid)), false)?;
        MetadataVol::setup(pool_uuid, dev)
    }

    /// Set up an existing Metadata Volume.
    pub fn setup(pool_uuid: PoolUuid, dev: LinearDev) -> StratisResult<MetadataVol> {
        let filename = format!(".mdv-{}", uuid_to_string!(pool_uuid));
        let mount_pt: PathBuf = vec![NS_TMPFS_LOCATION, &filename].iter().collect();

        let mdv = MetadataVol { dev, mount_pt };

        {
            if let Err(err) = create_dir_all(&mdv.mount_pt) {
                if err.kind() != ErrorKind::AlreadyExists {
                    return Err(From::from(err));
                }
            }

            match mount(
                Some(&mdv.dev.devnode()),
                &mdv.mount_pt,
                Some("xfs"),
                MsFlags::empty(),
                None as Option<&str>,
            ) {
                Err(nix::Error::EBUSY) => {
                    // The device is already mounted at the specified mount point
                    Ok(())
                }
                Err(err) => Err(err),
                Ok(_) => Ok(()),
            }?;

            let filesystem_path = mdv.mount_pt.join(FILESYSTEM_DIR);

            if let Err(err) = create_dir(&filesystem_path) {
                if err.kind() != ErrorKind::AlreadyExists {
                    return Err(From::from(err));
                }
            }

            let _ = remove_temp_files(&filesystem_path)?;
        }

        Ok(mdv)
    }

    /// Save info on a new filesystem to persistent storage, or update
    /// the existing info on a filesystem.
    // Write to a temp file and then rename to actual filename, to
    // ensure file contents are not truncated if operation is
    // interrupted.
    pub fn save_fs(
        &self,
        name: &Name,
        uuid: FilesystemUuid,
        fs: &StratFilesystem,
    ) -> StratisResult<()> {
        let data = serde_json::to_string(&fs.record(name, uuid))?;
        let path = self
            .mount_pt
            .join(FILESYSTEM_DIR)
            .join(uuid_to_string!(uuid))
            .with_extension("json");

        let temp_path = path.with_extension("temp");

        // Braces to ensure f is closed before renaming
        {
            let mut f = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&temp_path)?;
            f.write_all(data.as_bytes())?;

            // Try really hard to make sure it goes to disk
            f.sync_all()?;
        }

        rename(temp_path, path)?;

        Ok(())
    }

    /// Remove info on a filesystem from persistent storage.
    pub fn rm_fs(&self, fs_uuid: FilesystemUuid) -> StratisResult<()> {
        let fs_path = self
            .mount_pt
            .join(FILESYSTEM_DIR)
            .join(uuid_to_string!(fs_uuid))
            .with_extension("json");

        if let Err(err) = remove_file(fs_path) {
            if err.kind() != ErrorKind::NotFound {
                return Err(From::from(err));
            }
        }

        Ok(())
    }

    /// Get list of filesystems stored on the MDV.
    pub fn filesystems(&self) -> StratisResult<Vec<FilesystemSave>> {
        let mut filesystems = Vec::new();

        for dir_e in read_dir(self.mount_pt.join(FILESYSTEM_DIR))? {
            let dir_e = dir_e?;

            if dir_e.path().ends_with(".temp") {
                continue;
            }

            let mut f = OpenOptions::new().read(true).open(&dir_e.path())?;
            let mut data = Vec::new();
            f.read_to_end(&mut data)?;

            filesystems.push(serde_json::from_slice(&data)?);
        }

        Ok(filesystems)
    }

    /// Tear down a Metadata Volume.
    pub fn teardown(&mut self) -> StratisResult<()> {
        if let Err(e) = retry_with_index(Fixed::from_millis(100).take(2), |i| {
            trace!("MDV unmount attempt {}", i);
            umount(&self.mount_pt)
        }) {
            return Err(match e {
                retry::Error::Internal(msg) => StratisError::Msg(msg),
                retry::Error::Operation { error, .. } => StratisError::Chained(
                    "Failed to unmount MDV".to_string(),
                    Box::new(StratisError::from(error)),
                ),
            });
        }

        if let Err(err) = remove_dir(&self.mount_pt) {
            warn!("Could not remove MDV mount point: {}", err);
        }

        self.dev.teardown(get_dm())?;

        Ok(())
    }

    /// Suspend the metadata volume DM devices
    pub fn suspend(&mut self) -> StratisResult<()> {
        self.dev.suspend(get_dm(), DmOptions::default())?;
        Ok(())
    }

    /// Resume the metadata volume DM devices
    pub fn resume(&mut self) -> StratisResult<()> {
        self.dev.resume(get_dm())?;
        Ok(())
    }

    /// Get a reference to the backing device
    pub fn device(&self) -> &LinearDev {
        &self.dev
    }

    /// Set the table of the backing device
    pub fn set_table(
        &mut self,
        table: Vec<TargetLine<LinearDevTargetParams>>,
    ) -> StratisResult<()> {
        self.dev.set_table(get_dm(), table)?;
        Ok(())
    }

    /// The maximum number of filesystems that can be recorded in the MDV.
    pub fn max_fs_limit(&self) -> StratisResult<u64> {
        let (total_size, _) = fs_usage(&self.mount_pt)?;
        Ok(total_size.sectors() / Self::XFS_MIN_FILE_ALLOC_SIZE)
    }
}

impl Drop for MetadataVol {
    fn drop(&mut self) {
        fn drop_failure(mount_pt: &PathBuf) -> StratisResult<()> {
            let mtpt_stat = match stat(mount_pt) {
                Ok(s) => s,
                Err(e) => match e {
                    nix::errno::Errno::ENOENT => return Ok(()),
                    e => return Err(StratisError::Nix(e)),
                },
            };
            let parent_stat = match stat(&mount_pt.join("..")) {
                Ok(s) => s,
                Err(e) => match e {
                    nix::errno::Errno::ENOENT => return Ok(()),
                    e => return Err(StratisError::Nix(e)),
                },
            };

            if mtpt_stat.st_dev != parent_stat.st_dev {
                if let Err(e) = retry_with_index(Fixed::from_millis(100).take(2), |i| {
                    trace!("MDV unmount attempt {}", i);
                    umount(mount_pt)
                }) {
                    Err(match e {
                        retry::Error::Internal(msg) => StratisError::Msg(msg),
                        retry::Error::Operation { error, .. } => StratisError::Chained(
                            "Failed to unmount MDV".to_string(),
                            Box::new(StratisError::from(error)),
                        ),
                    })
                } else {
                    Ok(())
                }
            } else {
                Ok(())
            }
        }

        if let Err(e) = drop_failure(&self.mount_pt) {
            warn!(
                "Failed to unmount MDV; some cleanup may not be able to be done: {}",
                e
            );
        }
    }
}

/// Remove temp files from the designated directory.
/// Returns an error if the directory can not be read.
/// Persists if an individual directory entry can not be read due to an
/// intermittent IO error.
/// Returns the following summary values:
///  * the number of temp files found
///  * paths of those unremoved, if any
fn remove_temp_files(dir: &Path) -> StratisResult<(u64, Vec<PathBuf>)> {
    let mut found = 0;
    let mut failed = Vec::new();
    for path in read_dir(dir)?
        .filter_map(|e| e.ok()) // Just ignore entry on intermittent IO error
        .map(|e| e.path())
        .filter(|p| p.ends_with(".temp"))
    {
        found += 1;
        remove_file(&path).unwrap_or_else(|_| failed.push(path));
    }
    Ok((found, failed))
}
<commit_msg>fsync() directory after renaming, creating, and deleting files in MDV<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Manage the linear volume that stores metadata on pool levels 5-7.

use std::{
    convert::From,
    fs::{
        create_dir, create_dir_all, read_dir, remove_dir, remove_file, rename, File, OpenOptions,
    },
    io::{prelude::*, ErrorKind},
    path::{Path, PathBuf},
};

use nix::{
    mount::{mount, umount, MsFlags},
    sys::stat::stat,
};
use retry::{delay::Fixed, retry_with_index};

use devicemapper::{DmDevice, DmOptions, LinearDev, LinearDevTargetParams, Sectors, TargetLine};

use crate::{
    engine::{
        strat_engine::{
            cmd::create_fs,
            dm::get_dm,
            ns::NS_TMPFS_LOCATION,
            serde_structs::FilesystemSave,
            thinpool::filesystem::{fs_usage, StratFilesystem},
        },
        types::{FilesystemUuid, Name, PoolUuid, StratisUuid},
    },
    stratis::{StratisError, StratisResult},
};

const FILESYSTEM_DIR: &str = "filesystems";

#[derive(Debug)]
pub struct MetadataVol {
    dev: LinearDev,
    mount_pt: PathBuf,
}

impl MetadataVol {
    /// Minimum allocation size for a file is a block which will be 4k in this
    /// set up.
    const XFS_MIN_FILE_ALLOC_SIZE: Sectors = Sectors(8);

    /// Initialize a new Metadata Volume.
    pub fn initialize(pool_uuid: PoolUuid, dev: LinearDev) -> StratisResult<MetadataVol> {
        create_fs(&dev.devnode(), Some(StratisUuid::Pool(pool_uuid)), false)?;
        MetadataVol::setup(pool_uuid, dev)
    }

    /// Set up an existing Metadata Volume.
    pub fn setup(pool_uuid: PoolUuid, dev: LinearDev) -> StratisResult<MetadataVol> {
        let filename = format!(".mdv-{}", uuid_to_string!(pool_uuid));
        let mount_pt: PathBuf = vec![NS_TMPFS_LOCATION, &filename].iter().collect();

        let mdv = MetadataVol { dev, mount_pt };

        {
            if let Err(err) = create_dir_all(&mdv.mount_pt) {
                if err.kind() != ErrorKind::AlreadyExists {
                    return Err(From::from(err));
                }
            }

            match mount(
                Some(&mdv.dev.devnode()),
                &mdv.mount_pt,
                Some("xfs"),
                MsFlags::empty(),
                None as Option<&str>,
            ) {
                Err(nix::Error::EBUSY) => {
                    // The device is already mounted at the specified mount point
                    Ok(())
                }
                Err(err) => Err(err),
                Ok(_) => Ok(()),
            }?;

            let filesystem_path = mdv.mount_pt.join(FILESYSTEM_DIR);

            if let Err(err) = create_dir(&filesystem_path) {
                if err.kind() != ErrorKind::AlreadyExists {
                    return Err(From::from(err));
                }
            }

            let _ = remove_temp_files(&filesystem_path)?;
        }

        Ok(mdv)
    }

    /// Save info on a new filesystem to persistent storage, or update
    /// the existing info on a filesystem.
    // Write to a temp file and then rename to actual filename, to
    // ensure file contents are not truncated if operation is
    // interrupted.
    pub fn save_fs(
        &self,
        name: &Name,
        uuid: FilesystemUuid,
        fs: &StratFilesystem,
    ) -> StratisResult<()> {
        let data = serde_json::to_string(&fs.record(name, uuid))?;
        let path = self
            .mount_pt
            .join(FILESYSTEM_DIR)
            .join(uuid_to_string!(uuid))
            .with_extension("json");

        let temp_path = path.with_extension("temp");

        {
            let mut mdv_record_file = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&temp_path)?;

            mdv_record_file.write_all(data.as_bytes())?;
            // This ultimately results in an fsync() on the file.
            mdv_record_file.sync_all()?;
        }

        rename(temp_path, path)?;

        // fsync() on directory needs to occur after the rename because the rename
        // is atomic but not durable until the fsync on the directory metadata is
        // forced to disk.
        //
        // Failing to do so can result in a zero-sized file if a crash occurs.
        File::open(self.mount_pt.join(FILESYSTEM_DIR))?.sync_all()?;

        Ok(())
    }

    /// Remove info on a filesystem from persistent storage.
    pub fn rm_fs(&self, fs_uuid: FilesystemUuid) -> StratisResult<()> {
        let fs_path = self
            .mount_pt
            .join(FILESYSTEM_DIR)
            .join(uuid_to_string!(fs_uuid))
            .with_extension("json");

        let needs_fsync = if let Err(err) = remove_file(fs_path) {
            if err.kind() == ErrorKind::NotFound {
                false
            } else {
                return Err(From::from(err));
            }
        } else {
            true
        };

        if needs_fsync {
            // Make file unlink durable.
            File::open(self.mount_pt.join(FILESYSTEM_DIR))?.sync_all()?;
        }

        Ok(())
    }

    /// Get list of filesystems stored on the MDV.
    pub fn filesystems(&self) -> StratisResult<Vec<FilesystemSave>> {
        let mut filesystems = Vec::new();

        for dir_e in read_dir(self.mount_pt.join(FILESYSTEM_DIR))? {
            let dir_e = dir_e?;

            if dir_e.path().ends_with(".temp") {
                continue;
            }

            let mut f = OpenOptions::new().read(true).open(&dir_e.path())?;
            let mut data = Vec::new();
            f.read_to_end(&mut data)?;

            filesystems.push(serde_json::from_slice(&data)?);
        }

        Ok(filesystems)
    }

    /// Tear down a Metadata Volume.
    pub fn teardown(&mut self) -> StratisResult<()> {
        if let Err(e) = retry_with_index(Fixed::from_millis(100).take(2), |i| {
            trace!("MDV unmount attempt {}", i);
            umount(&self.mount_pt)
        }) {
            return Err(match e {
                retry::Error::Internal(msg) => StratisError::Msg(msg),
                retry::Error::Operation { error, .. } => StratisError::Chained(
                    "Failed to unmount MDV".to_string(),
                    Box::new(StratisError::from(error)),
                ),
            });
        }

        if let Err(err) = remove_dir(&self.mount_pt) {
            warn!("Could not remove MDV mount point: {}", err);
        }

        self.dev.teardown(get_dm())?;

        Ok(())
    }

    /// Suspend the metadata volume DM devices
    pub fn suspend(&mut self) -> StratisResult<()> {
        self.dev.suspend(get_dm(), DmOptions::default())?;
        Ok(())
    }

    /// Resume the metadata volume DM devices
    pub fn resume(&mut self) -> StratisResult<()> {
        self.dev.resume(get_dm())?;
        Ok(())
    }

    /// Get a reference to the backing device
    pub fn device(&self) -> &LinearDev {
        &self.dev
    }

    /// Set the table of the backing device
    pub fn set_table(
        &mut self,
        table: Vec<TargetLine<LinearDevTargetParams>>,
    ) -> StratisResult<()> {
        self.dev.set_table(get_dm(), table)?;
        Ok(())
    }

    /// The maximum number of filesystems that can be recorded in the MDV.
    pub fn max_fs_limit(&self) -> StratisResult<u64> {
        let (total_size, _) = fs_usage(&self.mount_pt)?;
        Ok(total_size.sectors() / Self::XFS_MIN_FILE_ALLOC_SIZE)
    }
}

impl Drop for MetadataVol {
    fn drop(&mut self) {
        fn drop_failure(mount_pt: &PathBuf) -> StratisResult<()> {
            let mtpt_stat = match stat(mount_pt) {
                Ok(s) => s,
                Err(e) => match e {
                    nix::errno::Errno::ENOENT => return Ok(()),
                    e => return Err(StratisError::Nix(e)),
                },
            };
            let parent_stat = match stat(&mount_pt.join("..")) {
                Ok(s) => s,
                Err(e) => match e {
                    nix::errno::Errno::ENOENT => return Ok(()),
                    e => return Err(StratisError::Nix(e)),
                },
            };

            if mtpt_stat.st_dev != parent_stat.st_dev {
                if let Err(e) = retry_with_index(Fixed::from_millis(100).take(2), |i| {
                    trace!("MDV unmount attempt {}", i);
                    umount(mount_pt)
                }) {
                    Err(match e {
                        retry::Error::Internal(msg) => StratisError::Msg(msg),
                        retry::Error::Operation { error, .. } => StratisError::Chained(
                            "Failed to unmount MDV".to_string(),
                            Box::new(StratisError::from(error)),
                        ),
                    })
                } else {
                    Ok(())
                }
            } else {
                Ok(())
            }
        }

        if let Err(e) = drop_failure(&self.mount_pt) {
            warn!(
                "Failed to unmount MDV; some cleanup may not be able to be done: {}",
                e
            );
        }
    }
}

/// Remove temp files from the designated directory.
/// Returns an error if the directory can not be read.
/// Persists if an individual directory entry can not be read due to an
/// intermittent IO error.
/// Returns the following summary values:
///  * the number of temp files found
///  * paths of those unremoved, if any
fn remove_temp_files(dir: &Path) -> StratisResult<(u64, Vec<PathBuf>)> {
    let mut found = 0;
    let mut failed = Vec::new();
    for path in read_dir(dir)?
        .filter_map(|e| e.ok()) // Just ignore entry on intermittent IO error
        .map(|e| e.path())
        .filter(|p| p.ends_with(".temp"))
    {
        found += 1;
        remove_file(&path).unwrap_or_else(|_| failed.push(path));
    }
    Ok((found, failed))
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

use std::collections::HashSet;
use std::ffi::OsStr;
use std::process::{Command, ExitStatus, Output};

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

struct Result {
    stdout: String,
    stderr: String,
    status: ExitStatus,
}

impl From<Output> for Result {
    fn from(output: Output) -> Result {
        Result {
            status: output.status,
            stdout: String::from_utf8(output.stdout).expect("Failed to decode stdout"),
            stderr: String::from_utf8(output.stderr).expect("Failed to decode stderr"),
        }
    }
}

fn run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--")
        .args(args)
        .output()
        .expect("Failed to run xkpwgen for testing")
        .into()
}

fn assert_run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    let result = run(args);
    assert!(result.status.success(),
            "xkpwgen failed with output:
stdout:
{}
stderr:
{}
",
            result.stdout,
            result.stderr);
    result
}

fn all_words<'a>(s: &'a str, sep: &str) -> Vec<&'a str> {
    s.lines().flat_map(|w| w.split(sep)).collect()
}

macro_rules! repeat_run {
    ($result:ident, $command:expr, $body:block) => {
        {
            for _ in 0..10 {
                let $result = assert_run($command);
                $body;
            }
        }
    };

    ($result:ident, $body:block) => {
        {
        let command: Vec<String> = Vec::new();
        repeat_run!($result, &command, $body);
        }
    };
}

#[test]
fn it_includes_license_and_warranty_in_help() {
    let result = assert_run(&["--help"]);
    assert!(result.stdout.contains("GNU GPL version 3 or later"),
            "License name missing in output:
{}",
            result.stdout);
    assert!(result.stdout.contains("<http://gnu.org/licenses/gpl.html>"),
            "License URL missing in output:
{}",
            result.stdout);
    assert!(result.stdout.contains("There is NO WARRANTY, to the extent permitted by law."),
            "Warranty disclaimer missing in output:
{}",
            result.stdout);
}

#[test]
fn it_uses_only_words_from_the_wordlist() {
    let words = EFF_WORDLIST.lines().collect::<HashSet<_>>();
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(words.contains(word), "Word {} not in EFF wordlist!", word);
        }
    });
}

#[test]
fn it_generates_five_phrases_by_default() {
    repeat_run!(result, {
        assert_eq!(result.stdout.lines().count(), 5);
    })
}

#[test]
fn it_generates_the_given_number_of_passwords() {
    repeat_run!(result, &["-n", "10"], {
        assert_eq!(result.stdout.lines().count(), 10);
    })
}

#[test]
fn it_generates_four_words_per_password_by_default() {
    repeat_run!(result, {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 4);
        }
    })
}

#[test]
fn it_generates_the_given_number_of_words_per_password() {
    repeat_run!(result, &["-l", "9"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 9)
        }
    });
}

#[test]
fn it_generates_no_words_with_whitespace() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(!word.contains(|c: char| c.is_whitespace()),
                    "Word {} contained whitespace!",
                    word);
        }
    })
}

#[test]
fn it_generates_no_empty_words() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(word.len() > 0, "Got empty word");
        }
    })
}

#[test]
fn it_has_7776_words_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().count(), 7776);
}

#[test]
fn it_has_no_empty_word_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    for word in stdout.lines() {
        assert!(word.len() > 0, "Got empty word");
    }
}

#[test]
fn it_has_no_word_with_space_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    for word in stdout.lines() {
        assert!(!word.contains(|c: char| c.is_whitespace()),
                "Word {} contained whitespace!",
                word);
    }
}

#[test]
fn it_uses_the_original_eff_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().collect::<Vec<_>>(),
               EFF_WORDLIST.lines().collect::<Vec<_>>());
}
<commit_msg>Test that the wordlist has no duplicates<commit_after>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

use std::collections::HashSet;
use std::ffi::OsStr;
use std::process::{Command, ExitStatus, Output};

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

struct Result {
    stdout: String,
    stderr: String,
    status: ExitStatus,
}

impl From<Output> for Result {
    fn from(output: Output) -> Result {
        Result {
            status: output.status,
            stdout: String::from_utf8(output.stdout).expect("Failed to decode stdout"),
            stderr: String::from_utf8(output.stderr).expect("Failed to decode stderr"),
        }
    }
}

fn run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--")
        .args(args)
        .output()
        .expect("Failed to run xkpwgen for testing")
        .into()
}

fn assert_run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    let result = run(args);
    assert!(result.status.success(),
            "xkpwgen failed with output:
stdout:
{}
stderr:
{}
",
            result.stdout,
            result.stderr);
    result
}

fn all_words<'a>(s: &'a str, sep: &str) -> Vec<&'a str> {
    s.lines().flat_map(|w| w.split(sep)).collect()
}

macro_rules! repeat_run {
    ($result:ident, $command:expr, $body:block) => {
        {
            for _ in 0..10 {
                let $result = assert_run($command);
                $body;
            }
        }
    };

    ($result:ident, $body:block) => {
        {
        let command: Vec<String> = Vec::new();
        repeat_run!($result, &command, $body);
        }
    };
}

#[test]
fn it_includes_license_and_warranty_in_help() {
    let result = assert_run(&["--help"]);
    assert!(result.stdout.contains("GNU GPL version 3 or later"),
            "License name missing in output:
{}",
            result.stdout);
    assert!(result.stdout.contains("<http://gnu.org/licenses/gpl.html>"),
            "License URL missing in output:
{}",
            result.stdout);
    assert!(result.stdout.contains("There is NO WARRANTY, to the extent permitted by law."),
            "Warranty disclaimer missing in output:
{}",
            result.stdout);
}

#[test]
fn it_uses_only_words_from_the_wordlist() {
    let words = EFF_WORDLIST.lines().collect::<HashSet<_>>();
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(words.contains(word), "Word {} not in EFF wordlist!", word);
        }
    });
}

#[test]
fn it_generates_five_phrases_by_default() {
    repeat_run!(result, {
        assert_eq!(result.stdout.lines().count(), 5);
    })
}

#[test]
fn it_generates_the_given_number_of_passwords() {
    repeat_run!(result, &["-n", "10"], {
        assert_eq!(result.stdout.lines().count(), 10);
    })
}

#[test]
fn it_generates_four_words_per_password_by_default() {
    repeat_run!(result, {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 4);
        }
    })
}

#[test]
fn it_generates_the_given_number_of_words_per_password() {
    repeat_run!(result, &["-l", "9"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 9)
        }
    });
}

#[test]
fn it_generates_no_words_with_whitespace() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(!word.contains(|c: char| c.is_whitespace()),
                    "Word {} contained whitespace!",
                    word);
        }
    })
}

#[test]
fn it_generates_no_empty_words() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(word.len() > 0, "Got empty word");
        }
    })
}

#[test]
fn it_has_7776_words_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().count(), 7776);
}

#[test]
fn it_has_no_duplicate_words_in_the_wordlist() {
    let mut seen_words = HashSet::with_capacity(8000);
    let mut duplicate_words = HashSet::new();
    for word in EFF_WORDLIST.lines() {
        if !seen_words.insert(word) {
            duplicate_words.insert(word);
        }
    }

    assert!(duplicate_words.is_empty(),
            "Duplicate words found: {}",
            duplicate_words.into_iter().collect::<Vec<_>>().join(" "));
}

#[test]
fn it_has_no_empty_word_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    for word in stdout.lines() {
        assert!(word.len() > 0, "Got empty word");
    }
}

#[test]
fn it_has_no_word_with_space_in_the_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    for word in stdout.lines() {
        assert!(!word.contains(|c: char| c.is_whitespace()),
                "Word {} contained whitespace!",
                word);
    }
}

#[test]
fn it_uses_the_original_eff_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().collect::<Vec<_>>(),
               EFF_WORDLIST.lines().collect::<Vec<_>>());
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::process::Command;

#[test]
fn fmt() {
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    // Skip this test if rustup nightly is unavailable
    let rustup_output = Command::new("rustup")
        .args(&["component", "list", "--toolchain", "nightly"])
        .output()
        .unwrap();
    assert!(rustup_output.status.success());
    let component_output = String::from_utf8_lossy(&rustup_output.stdout);
    if !component_output.contains("rustfmt") {
        return;
    }

    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let dev_dir = root_dir.join("clippy_dev");
    let target_dir = root_dir.join("target");
    let target_dir = target_dir.to_str().unwrap();
    let output = Command::new("cargo")
        .current_dir(dev_dir)
        .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
        .output()
        .unwrap();

    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(
        output.status.success(),
        "Formatting check failed. Run `cargo dev fmt` to update formatting."
    );
}
<commit_msg>Ignore fmt test in GHA CI<commit_after>use std::path::PathBuf;
use std::process::Command;

#[test]
fn fmt() {
    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("GHA_CI").is_some() {
        return;
    }

    // Skip this test if rustup nightly is unavailable
    let rustup_output = Command::new("rustup")
        .args(&["component", "list", "--toolchain", "nightly"])
        .output()
        .unwrap();
    assert!(rustup_output.status.success());
    let component_output = String::from_utf8_lossy(&rustup_output.stdout);
    if !component_output.contains("rustfmt") {
        return;
    }

    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let dev_dir = root_dir.join("clippy_dev");
    let target_dir = root_dir.join("target");
    let target_dir = target_dir.to_str().unwrap();
    let output = Command::new("cargo")
        .current_dir(dev_dir)
        .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
        .output()
        .unwrap();

    println!("status: {}", output.status);
    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

    assert!(
        output.status.success(),
        "Formatting check failed. Run `cargo dev fmt` to update formatting."
    );
}
<|endoftext|>


<commit_before>extern crate sorting;

use std::fmt::Debug;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: Debug + PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            println!("array[sorted_indices[i={:?}]]={:?}   array[sorted_indices[i+1={:?}]]={:?}",
                i, array[sorted_indices[i]], i+1, array[sorted_indices[i+1]]);
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate a vector of double precision values.
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let sorted_indices = sorting::simplesorts::insertion::sort(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices, vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12]);
}
<commit_msg>Get rid of debug statements.<commit_after>extern crate sorting;

use std::fmt::Debug;

/// Verify order of input vector as sorted by indices.
///
/// # Details
///
/// This function takes as arguments a vector of data and a vector representing the indices of the
/// first vector making it sorted.
///
/// The elements of the sorted vector are compared to the next ones to make sure they are less
/// or equal.
///
/// # Panics
///
/// The sorting of the data vector is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// let data: Vec<i32> = vec![4, 2, 3, 1];
/// let sorted_indices: Vec<usize> = vec![3, 1, 2, 0];
/// verify_sorting(&data, &sorted_indices);
/// ```
///
fn verify_sorting<T: PartialOrd>(array: &Vec<T>, sorted_indices: &Vec<usize>) {
    let n = array.len();
    assert_eq!(n , sorted_indices.len());

    if !array.is_empty() {
        for i in 0..n-1 {
            assert!(array[sorted_indices[i]] <= array[sorted_indices[i+1]]);
        }
    }
}

/// Call the specified sorting function on an empty vector of specific type.
///
/// # Details
///
/// This function takes as arguments a pointer to a sorting function acting and call
/// it on an empty vector of type T. The call should succeed and the index vector should
/// be empty as well.
///
/// # Panics
///
/// The emptiness of the sorted list is enforced using `assert!()`.
///
/// # Examples
///
/// ```
/// test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
/// ```
///
fn test_empty_vec<T: PartialOrd>(sorting_fct: fn(&Vec<T>) -> Vec<usize>) {
    let to_sort: Vec<T> = vec![];
    let sorted_indices = sorting_fct(&to_sort);
    assert_eq!(sorted_indices, vec![]);
    assert!(sorted_indices.is_empty());
}

/// Validate against empty vector (i8).
#[test]
fn simple_insertion_empty_vec_i8() {
    test_empty_vec::<i8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i16() {
    test_empty_vec::<i16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i32).
#[test]
fn simple_insertion_empty_vec_i32() {
    test_empty_vec::<i32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (i64).
#[test]
fn simple_insertion_empty_vec_i64() {
    test_empty_vec::<i64>(sorting::simplesorts::insertion::sort);
}

/// Validate against empty vector (u8).
#[test]
fn simple_insertion_empty_vec_u8() {
    test_empty_vec::<u8>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u16() {
    test_empty_vec::<u16>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u32).
#[test]
fn simple_insertion_empty_vec_u32() {
    test_empty_vec::<u32>(sorting::simplesorts::insertion::sort);
}
/// Validate against empty vector (u64).
#[test]
fn simple_insertion_empty_vec_u64() {
    test_empty_vec::<u64>(sorting::simplesorts::insertion::sort);
}

/// Validate a vector of double precision values.
#[test]
fn simple_insertion_vec_f64() {
    let to_sort: Vec<f64> = vec![6.0,   5.0,  3.0,  1.0,  2.4, 4.0, 10.0, 7.0,
                                 3.42, 32.2, 44.2, 56.3, 67.9, 3.2, 44.2, 2.0];

    let sorted_indices = sorting::simplesorts::insertion::sort(&to_sort);

    verify_sorting(&to_sort, &sorted_indices);

    assert_eq!(sorted_indices, vec![3, 15, 4, 2, 13, 8, 5, 1, 0, 7, 6, 9, 10, 14, 11, 12]);
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Fibonacci_sequence
#[cfg(not(test))]
fn main() {
    let fns=vec![
        (fib_recursive, "recursive implementation"),
        (fib_tail_recursive, "tail recursive implementation"),
        (fib_iterative, "iterative implementation")];

    for &(f, desc) in fns.iter() {
        let r: Vec<u64> = range(0u64, 10).map(|i| f(i)).collect();
        println!("{}:\n {}\n", desc, r);
    }
}

// Fibonacci "classic" recursive version
// not tail recursive (it's going to blow the stack
// for n too high)
fn fib_recursive(n: u64) -> u64 {
    match n {
        n if n<2 => n,
        m => fib_recursive(m - 1) + fib_recursive(m-2)
    }
}

// tail recursive version
fn fib_tail_recursive(n: u64) -> u64 {
        fn in_fib(n : u64, current : u64, next : u64) -> u64 {
            match n {
                0 => current,
                _ => in_fib(n - 1, next, current + next)
            }
        }
        in_fib(n, 0, 1)
}

// iterative version
fn fib_iterative(n: u64) -> u64 {
    let (mut fib1, mut fib2) = (0u64, 1u64);
    let mut fib = n;

    for _ in range(1u64,n) {
        fib = fib1 + fib2;
        fib1 = fib2;
        fib2 = fib;
    }
    fib
}

// helper function to test that all versions of the fib function
// return the expected values.
#[cfg(test)]
fn tester(f: fn(u64)->u64) {
    let exp = [0u64,1,1,2,3,5,8,13,21,34];
    for i in range(0u64, 10) {
        let ret=f(i);
        assert_eq!(ret, exp[i as uint]);
    }
}

#[test]
fn fib_values() {
    let fns=vec![fib_recursive, fib_tail_recursive, fib_iterative];
    fns.iter()
        .advance(|&f| {tester(f); true});
}<commit_msg>more refactoring per suggestions<commit_after>// Implements http://rosettacode.org/wiki/Fibonacci_sequence
#[cfg(not(test))]
fn main() {
    let fns=vec![
        (fib_recursive, "recursive implementation"),
        (fib_tail_recursive, "tail recursive implementation"),
        (fib_iterative, "iterative implementation")];

for &(f, desc) in fns.iter() {
    println!("{}", desc);
    for i in range(0u64, 10) {
        print!("{} ", f(i));
    }
    println!("\n");
    }
}

// Fibonacci "classic" recursive version
// not tail recursive (it's going to blow the stack
// for n too high)
fn fib_recursive(n: u64) -> u64 {
    match n {
        n if n<2 => n,
        m => fib_recursive(m - 1) + fib_recursive(m - 2)
    }
}

// tail recursive version
fn fib_tail_recursive(n: u64) -> u64 {
        fn in_fib(n : u64, current : u64, next : u64) -> u64 {
            match n {
                0 => current,
                _ => in_fib(n - 1, next, current + next)
            }
        }
        in_fib(n, 0, 1)
}

// iterative version
fn fib_iterative(n: u64) -> u64 {
    let (mut fib1, mut fib2) = (0u64, 1u64);
    let mut fib = n;

    for _ in range(1u64,n) {
        fib = fib1 + fib2;
        fib1 = fib2;
        fib2 = fib;
    }
    fib
}

// helper function to test that all versions of the fib function
// return the expected values.
#[cfg(test)]
fn tester(f: fn(u64)->u64) {
    let exp = [0u64,1,1,2,3,5,8,13,21,34];
    for i in range(0u64, 10) {
        let ret=f(i);
        assert_eq!(ret, exp[i as uint]);
    }
}

#[test]
fn fib_values() {
    let fns=vec![fib_recursive, fib_tail_recursive, fib_iterative];
    fns.iter()
        .advance(|&f| {tester(f); true});
}<|endoftext|>