<commit_before>extern crate libc;

use mesos;
use native::ProtobufObj;
use protobuf::error::ProtobufError;
use protobuf::Message;
use protobuf::stream::CodedInputStream;

use std::ops::Deref;
use std::path::BytesContainer;
use std::raw::{Repr, Slice};
use std::vec::Vec;

/// Returns a wrapped represtation of the supplied vector as protobuf
/// data.
fn slice_to_pb(buffer: &[u8]) -> ProtobufObj {
    let repr = buffer.repr();
    ProtobufObj {
        data: repr.data as *mut libc::c_void,
        size: repr.len as u64
    }
}

/// Returns an unwrapped represtation of the supplied protobuf data
/// as a vector of bytes.
fn pb_to_slice(pb: &ProtobufObj) -> &[u8] {
    unsafe {
        // std::raw::Slice is internal representation of a slice,
        // so we just construct it manually and reinterpret it as an &[u8]
        ::std::mem::transmute(
            Slice {
                data: pb.data as *const u8,
                len: pb.size as uint
            }
        )
    }
}

/// Returns an input stream wrapping the supplied vector.
fn slice_to_stream(buffer: &[u8]) -> CodedInputStream {
    CodedInputStream::from_bytes(buffer)
}

/// Returns the result of serializing the supplied protobuf message
pub fn serialize(proto: &Message) -> Result<ProtobufObj, ProtobufError> {
    proto.write_to_bytes().map(|buffer: Vec<u8>| {
        slice_to_pb(buffer.as_slice())
    })
}

/*
/// Returns...
pub fn deserialize<T: Message>(
    obj: &ProtobufObj,
    proto: &T) -> Result<T, ProtobufError> {
    let slice = pb_to_slice(obj);
    let stream = slice_to_stream(slice);
    proto.merge_from(stream).map(|_| proto)
}
*/
<commit_msg>Completed and simplified pb utils.<commit_after>#![allow(dead_code)]

extern crate libc;

use native::ProtobufObj;
use protobuf::error::ProtobufError;
use protobuf::Message;

use std::raw::{Repr, Slice};
use std::vec::Vec;

/// Returns a wrapped represtation of the supplied vector as protobuf
/// data.
fn slice_to_pb(buffer: &[u8]) -> ProtobufObj {
    let repr = buffer.repr();
    ProtobufObj {
        data: repr.data as *mut libc::c_void,
        size: repr.len as u64
    }
}

/// Returns an unwrapped represtation of the supplied protobuf data
/// as a vector of bytes.
fn pb_to_slice(pb: &ProtobufObj) -> &[u8] {
    unsafe {
        // std::raw::Slice is internal representation of a slice,
        // so we just construct it manually and reinterpret it as an &[u8]
        ::std::mem::transmute(
            Slice {
                data: pb.data as *const u8,
                len: pb.size as uint
            }
        )
    }
}

/// Returns the result of serializing the supplied protobuf message
pub fn serialize(proto: &Message) -> Result<ProtobufObj, ProtobufError> {
    proto.write_to_bytes().map(|buffer: Vec<u8>| {
        slice_to_pb(buffer.as_slice())
    })
}

/// Returns the result of deserializing the supplied protobuf data
/// into the supplied message.
pub fn deserialize<'a, T: Message>(
    obj: &ProtobufObj,
    proto: &'a mut T) -> Result<&'a mut T, ProtobufError> {
    let slice = pb_to_slice(obj);
    try!(proto.merge_from_bytes(slice));
    Ok(proto)
}

<|endoftext|>


<commit_before>// Originally from https://github.com/rust-lang/crates.io/blob/master/src/s3/lib.rs
//#![deny(warnings)]

use std::ascii::AsciiExt;
use std::fmt;
use std::io::prelude::*;
use std::io;

use simples3::credential::*;
use crypto::digest::Digest;
use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::sha1::Sha1;
use hyper::{self,header};
use rustc_serialize::base64::{ToBase64, STANDARD};
use time;

#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
/// Whether or not to use SSL.
pub enum Ssl {
    /// Use SSL.
    Yes,
    /// Do not use SSL.
    No,
}

fn base_url(bucket_name: &str, ssl: Ssl, region: Option<&str>) -> String {
    format!("{}://{}.s3{}.amazonaws.com/",
            match ssl {
                Ssl::Yes => "https",
                Ssl::No => "http",
            },
            bucket_name,
            match region {
                Some(ref r) => format!("-{}", r),
                None => String::new(),
            })
}

fn hmac<D: Digest>(d: D, key: &[u8], data: &[u8]) -> Vec<u8> {
    let mut hmac = Hmac::new(d, key);
    hmac.input(data);
    hmac.result().code().iter().map(|b| *b).collect::<Vec<u8>>()
}

fn signature(string_to_sign: &str, signing_key: &str) -> String {
    hmac(Sha1::new(), signing_key.as_bytes(), string_to_sign.as_bytes()).to_base64(STANDARD)
}

/// An S3 bucket.
pub struct Bucket {
    name: String,
    base_url: String,
    client: hyper::Client,
}

impl fmt::Display for Bucket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Bucket(name={}, base_url={})", self.name, self.base_url)
    }
}

/// Errors from making S3 requests.
#[derive(Debug)]
pub enum S3Error {
    HyperError(hyper::Error),
    IOError(io::Error),
    BadHTTPStatus(hyper::status::StatusCode),
}

impl Bucket {
    pub fn new(name: &str, ssl: Ssl) -> Bucket {
        let base_url = base_url(&name, ssl, None);
        Bucket {
            name: name.to_owned(),
            base_url: base_url,
            client: hyper::Client::new(),
        }
    }

    pub fn get(&self, key: &str) -> Result<Vec<u8>, S3Error> {
        let url = format!("{}{}", self.base_url, key);
        match self.client.get(&url).send() {
            Err(e) => Err(S3Error::HyperError(e)),
            Ok(mut res) => {
                if res.status.class() == hyper::status::StatusClass::Success {
                    let mut body = vec!();
                    res.read_to_end(&mut body)
                        .or_else(|e| Err(S3Error::IOError(e)))
                        .map(|_| body)
                } else {
                    Err(S3Error::BadHTTPStatus(res.status))
                }
            }
        }
    }

    pub fn put(&self, key: &str, content: &[u8],
               creds: &AwsCredentials)
               -> Result<(), S3Error> {
        let content_type = "application/octet-stream";
        let date = time::now().rfc822z().to_string();
        let mut headers = header::Headers::new();
        let mut canonical_headers = String::new();
        // Keep the list of header values sorted!
        for (header, value) in vec![
            ("x-amz-acl", "public-read"),
            ("x-amz-storage-class", "REDUCED_REDUNDANCY"),
            ] {
            headers.set_raw(header, vec!(value.as_bytes().to_vec()));
            canonical_headers.push_str(format!("{}:{}\n", header.to_ascii_lowercase(), value).as_ref());
        }
        let auth = self.auth("PUT", &date, key, "", &canonical_headers, content_type, creds);
        headers.set_raw("Date", vec!(date.into_bytes()));
        headers.set(header::ContentType(content_type.parse().unwrap()));
        headers.set(header::CacheControl(vec![
            // Two weeks
            header::CacheDirective::MaxAge(1296000)
                ]));
        headers.set_raw("Authorization", vec!(auth.into_bytes()));
        let url = format!("{}{}", self.base_url, key);
        println!("PUT {}", url);

        match self.client.put(&url)
            .body(content)
            .headers(headers)
            .send() {
                Err(e) => Err(S3Error::HyperError(e)),
                Ok(res) => {
                    if res.status.class() == hyper::status::StatusClass::Success {
                        Ok(())
                    } else {
                        Err(S3Error::BadHTTPStatus(res.status))
                    }
                }
            }
    }

    // http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
    fn auth(&self, verb: &str, date: &str, path: &str,
            md5: &str, headers: &str, content_type: &str, creds: &AwsCredentials) -> String {
        let string = format!("{verb}\n{md5}\n{ty}\n{date}\n{headers}{resource}",
                             verb = verb,
                             md5 = md5,
                             ty = content_type,
                             date = date,
                             headers = headers,
                             resource = format!("/{}/{}", self.name, path));
        let signature = signature(&string, creds.aws_secret_access_key());
        format!("AWS {}:{}", creds.aws_access_key_id(), signature)
    }
}
<commit_msg>send security token with PUT requests if present<commit_after>// Originally from https://github.com/rust-lang/crates.io/blob/master/src/s3/lib.rs
//#![deny(warnings)]

use std::ascii::AsciiExt;
use std::fmt;
use std::io::prelude::*;
use std::io;

use simples3::credential::*;
use crypto::digest::Digest;
use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::sha1::Sha1;
use hyper::{self,header};
use rustc_serialize::base64::{ToBase64, STANDARD};
use time;

#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
/// Whether or not to use SSL.
pub enum Ssl {
    /// Use SSL.
    Yes,
    /// Do not use SSL.
    No,
}

fn base_url(bucket_name: &str, ssl: Ssl, region: Option<&str>) -> String {
    format!("{}://{}.s3{}.amazonaws.com/",
            match ssl {
                Ssl::Yes => "https",
                Ssl::No => "http",
            },
            bucket_name,
            match region {
                Some(ref r) => format!("-{}", r),
                None => String::new(),
            })
}

fn hmac<D: Digest>(d: D, key: &[u8], data: &[u8]) -> Vec<u8> {
    let mut hmac = Hmac::new(d, key);
    hmac.input(data);
    hmac.result().code().iter().map(|b| *b).collect::<Vec<u8>>()
}

fn signature(string_to_sign: &str, signing_key: &str) -> String {
    hmac(Sha1::new(), signing_key.as_bytes(), string_to_sign.as_bytes()).to_base64(STANDARD)
}

/// An S3 bucket.
pub struct Bucket {
    name: String,
    base_url: String,
    client: hyper::Client,
}

impl fmt::Display for Bucket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Bucket(name={}, base_url={})", self.name, self.base_url)
    }
}

/// Errors from making S3 requests.
#[derive(Debug)]
pub enum S3Error {
    HyperError(hyper::Error),
    IOError(io::Error),
    BadHTTPStatus(hyper::status::StatusCode),
}

impl Bucket {
    pub fn new(name: &str, ssl: Ssl) -> Bucket {
        let base_url = base_url(&name, ssl, None);
        Bucket {
            name: name.to_owned(),
            base_url: base_url,
            client: hyper::Client::new(),
        }
    }

    pub fn get(&self, key: &str) -> Result<Vec<u8>, S3Error> {
        let url = format!("{}{}", self.base_url, key);
        match self.client.get(&url).send() {
            Err(e) => Err(S3Error::HyperError(e)),
            Ok(mut res) => {
                if res.status.class() == hyper::status::StatusClass::Success {
                    let mut body = vec!();
                    res.read_to_end(&mut body)
                        .or_else(|e| Err(S3Error::IOError(e)))
                        .map(|_| body)
                } else {
                    Err(S3Error::BadHTTPStatus(res.status))
                }
            }
        }
    }

    pub fn put(&self, key: &str, content: &[u8],
               creds: &AwsCredentials)
               -> Result<(), S3Error> {
        let content_type = "application/octet-stream";
        let date = time::now().rfc822z().to_string();
        let mut headers = header::Headers::new();
        let mut canonical_headers = String::new();
        let token = creds.token().as_ref().map(|s| s.as_str());
        // Keep the list of header values sorted!
        for (header, maybe_value) in vec![
            ("x-amz-acl", Some("public-read")),
            ("x-amz-security-token", token),
            ("x-amz-storage-class", Some("REDUCED_REDUNDANCY")),
            ] {
            if let Some(ref value) = maybe_value {
                headers.set_raw(header, vec!(value.as_bytes().to_vec()));
                canonical_headers.push_str(format!("{}:{}\n", header.to_ascii_lowercase(), value).as_ref());
            }
        }
        let auth = self.auth("PUT", &date, key, "", &canonical_headers, content_type, creds);
        headers.set_raw("Date", vec!(date.into_bytes()));
        headers.set(header::ContentType(content_type.parse().unwrap()));
        headers.set(header::CacheControl(vec![
            // Two weeks
            header::CacheDirective::MaxAge(1296000)
                ]));
        headers.set_raw("Authorization", vec!(auth.into_bytes()));
        let url = format!("{}{}", self.base_url, key);
        println!("PUT {}", url);

        match self.client.put(&url)
            .body(content)
            .headers(headers)
            .send() {
                Err(e) => Err(S3Error::HyperError(e)),
                Ok(res) => {
                    if res.status.class() == hyper::status::StatusClass::Success {
                        Ok(())
                    } else {
                        Err(S3Error::BadHTTPStatus(res.status))
                    }
                }
            }
    }

    // http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
    fn auth(&self, verb: &str, date: &str, path: &str,
            md5: &str, headers: &str, content_type: &str, creds: &AwsCredentials) -> String {
        let string = format!("{verb}\n{md5}\n{ty}\n{date}\n{headers}{resource}",
                             verb = verb,
                             md5 = md5,
                             ty = content_type,
                             date = date,
                             headers = headers,
                             resource = format!("/{}/{}", self.name, path));
        let signature = signature(&string, creds.aws_secret_access_key());
        format!("AWS {}:{}", creds.aws_access_key_id(), signature)
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_chgrp"]

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown};
pub use uucore::entries;
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::io::prelude::*;
use std::io::Result as IOResult;
use std::io::Error as IOError;

use std::fs;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::path::Path;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &'static str = "chgrp [OPTION]... GROUP FILE...\n or :  chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "Change the group of each FILE to GROUP.";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    if matches.free.len() < 1 {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let dest_gid: gid_t;
    let mut files;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = meta.gid();
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
        files = matches.free;
    } else {
        match entries::grp2gid(&matches.free[0]) {
            Ok(g) => {
                dest_gid = g;
            }
            _ => {
                show_info!("invalid group: {}", matches.free[0].as_str());
                return 1;
            }
        }
        files = matches.free;
        files.remove(0);
    }

    let executor = Chgrper {
        bit_flag: bit_flag,
        dest_gid: dest_gid,
        verbosity: verbosity,
        recursive: recursive,
        dereference: derefer != 0,
        preserve_root: preserve_root,
        files: files,
    };
    executor.exec()
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

struct Chgrper {
    dest_gid: gid_t,
    bit_flag: u8,
    verbosity: Verbosity,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chgrper {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chgrp<P: AsRef<Path>>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            } else {
                lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(IOError::last_os_error())
        }
    }

    #[cfg(windows)]
    fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
        false
    }

    #[cfg(unix)]
    fn is_bind_root<P: AsRef<Path>>(&self, path: P) -> bool {
        use std::os::unix::fs::MetadataExt;

        // FIXME: remove unwrap here
        let given = std::fs::metadata(path).unwrap();
        let root = std::fs::metadata("/").unwrap();
        given.dev() == root.dev() && given.ino() == root.ino()
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() || self.is_bind_root(p) {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = self.wrap_chgrp(path, &meta, follow_arg);

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            ret = self.wrap_chgrp(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chgrp<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_gid = self.dest_gid;
        let path = path.as_ref();
        if let Err(e) = self.chgrp(path, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing group of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!("failed to change group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!("changed group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!("group of {} retained as {}",
                         path.display(),
                         entries::gid2grp(dest_gid).unwrap());
            }
        }
        ret
    }
}
<commit_msg>chgrp: remove unwrap() in is_bind_root()<commit_after>#![crate_name = "uu_chgrp"]

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate uucore;
use uucore::libc::{self, gid_t, lchown};
pub use uucore::entries;
use uucore::fs::resolve_relative_path;

extern crate walkdir;
use walkdir::WalkDir;

use std::io::prelude::*;
use std::io::Result as IOResult;
use std::io::Error as IOError;

use std::fs;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::path::Path;

use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;

static SYNTAX: &'static str = "chgrp [OPTION]... GROUP FILE...\n or :  chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "Change the group of each FILE to GROUP.";

const FTS_COMFOLLOW: u8 = 1;
const FTS_PHYSICAL: u8 = 1 << 1;
const FTS_LOGICAL: u8 = 1 << 2;

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
    opts.optflag("c",
                 "changes",
                 "like verbose but report only when a change is made")
        .optflag("f", "silent", "")
        .optflag("", "quiet", "suppress most error messages")
        .optflag("v",
                 "verbose",
                 "output a diagnostic for every file processed")
        .optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
        .optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
        .optflag("",
                 "no-preserve-root",
                 "do not treat '/' specially (the default)")
        .optflag("", "preserve-root", "fail to operate recursively on '/'")
        .optopt("",
                "reference",
                "use RFILE's owner and group rather than specifying OWNER:GROUP values",
                "RFILE")
        .optflag("R",
                 "recursive",
                 "operate on files and directories recursively")
        .optflag("H",
                 "",
                 "if a command line argument is a symbolic link to a directory, traverse it")
        .optflag("L",
                 "",
                 "traverse every symbolic link to a directory encountered")
        .optflag("P", "", "do not traverse any symbolic links (default)");

    let mut bit_flag = FTS_PHYSICAL;
    let mut preserve_root = false;
    let mut derefer = -1;
    let flags: &[char] = &['H', 'L', 'P'];
    for opt in &args {
        match opt.as_str() {
            // If more than one is specified, only the final one takes effect.
            s if s.contains(flags) => {
                if let Some(idx) = s.rfind(flags) {
                    match s.chars().nth(idx).unwrap() {
                        'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
                        'L' => bit_flag = FTS_LOGICAL,
                        'P' => bit_flag = FTS_PHYSICAL,
                        _ => (),
                    }
                }
            }
            "--no-preserve-root" => preserve_root = false,
            "--preserve-root" => preserve_root = true,
            "--dereference" => derefer = 1,
            "--no-dereference" => derefer = 0,
            _ => (),
        }
    }

    let matches = opts.parse(args);
    let recursive = matches.opt_present("recursive");
    if recursive {
        if bit_flag == FTS_PHYSICAL {
            if derefer == 1 {
                show_info!("-R --dereference requires -H or -L");
                return 1;
            }
            derefer = 0;
        }
    } else {
        bit_flag = FTS_PHYSICAL;
    }

    let verbosity = if matches.opt_present("changes") {
        Verbosity::Changes
    } else if matches.opt_present("silent") || matches.opt_present("quiet") {
        Verbosity::Silent
    } else if matches.opt_present("verbose") {
        Verbosity::Verbose
    } else {
        Verbosity::Normal
    };

    if matches.free.len() < 1 {
        disp_err!("missing operand");
        return 1;
    } else if matches.free.len() < 2 && !matches.opt_present("reference") {
        disp_err!("missing operand after ‘{}’", matches.free[0]);
        return 1;
    }

    let dest_gid: gid_t;
    let mut files;
    if let Some(file) = matches.opt_str("reference") {
        match fs::metadata(&file) {
            Ok(meta) => {
                dest_gid = meta.gid();
            }
            Err(e) => {
                show_info!("failed to get attributes of '{}': {}", file, e);
                return 1;
            }
        }
        files = matches.free;
    } else {
        match entries::grp2gid(&matches.free[0]) {
            Ok(g) => {
                dest_gid = g;
            }
            _ => {
                show_info!("invalid group: {}", matches.free[0].as_str());
                return 1;
            }
        }
        files = matches.free;
        files.remove(0);
    }

    let executor = Chgrper {
        bit_flag: bit_flag,
        dest_gid: dest_gid,
        verbosity: verbosity,
        recursive: recursive,
        dereference: derefer != 0,
        preserve_root: preserve_root,
        files: files,
    };
    executor.exec()
}

#[derive(PartialEq, Debug)]
enum Verbosity {
    Silent,
    Changes,
    Verbose,
    Normal,
}

struct Chgrper {
    dest_gid: gid_t,
    bit_flag: u8,
    verbosity: Verbosity,
    files: Vec<String>,
    recursive: bool,
    preserve_root: bool,
    dereference: bool,
}

macro_rules! unwrap {
    ($m:expr, $e:ident, $err:block) => (
        match $m {
            Ok(meta) => meta,
            Err($e) => $err,
        }
    )
}

impl Chgrper {
    fn exec(&self) -> i32 {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        ret
    }

    fn chgrp<P: AsRef<Path>>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> {
        let path = path.as_ref();
        let s = CString::new(path.as_os_str().as_bytes()).unwrap();
        let ret = unsafe {
            if follow {
                libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            } else {
                lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid)
            }
        };
        if ret == 0 {
            Ok(())
        } else {
            Err(IOError::last_os_error())
        }
    }

    #[cfg(windows)]
    fn is_bind_root<P: AsRef<Path>>(&self, root: P) -> bool {
        // TODO: is there an equivalent on Windows?
        false
    }

    #[cfg(unix)]
    fn is_bind_root<P: AsRef<Path>>(&self, path: P) -> bool {
        if let (Ok(given), Ok(root)) = (fs::metadata(path), fs::metadata("/")) {
            given.dev() == root.dev() && given.ino() == root.ino()
        } else {
            // FIXME: not totally sure if it's okay to just ignore an error here
            false
        }
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, follow_arg) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if follow_arg {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() || self.is_bind_root(p) {
                    show_info!("it is dangerous to operate recursively on '/'");
                    show_info!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = self.wrap_chgrp(path, &meta, follow_arg);

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let mut ret = 0;
        let root = root.as_ref();
        let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
        for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
            let entry = unwrap!(entry, e, {
                ret = 1;
                show_info!("{}", e);
                continue;
            });
            let path = entry.path();
            let meta = match self.obtain_meta(path, follow) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    continue;
                }
            };

            ret = self.wrap_chgrp(path, &meta, follow);
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        use self::Verbosity::*;
        let path = path.as_ref();
        let meta = if follow {
            unwrap!(path.metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot access '{}': {}", path.display(), e),
                }
                return None;
            })
        } else {
            unwrap!(path.symlink_metadata(), e, {
                match self.verbosity {
                    Silent => (),
                    _ => show_info!("cannot dereference '{}': {}", path.display(), e),
                }
                return None;
            })
        };
        Some(meta)
    }

    fn wrap_chgrp<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
        use self::Verbosity::*;
        let mut ret = 0;
        let dest_gid = self.dest_gid;
        let path = path.as_ref();
        if let Err(e) = self.chgrp(path, dest_gid, follow) {
            match self.verbosity {
                Silent => (),
                _ => {
                    show_info!("changing group of '{}': {}", path.display(), e);
                    if self.verbosity == Verbose {
                        println!("failed to change group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    };
                }
            }
            ret = 1;
        } else {
            let changed = dest_gid != meta.gid();
            if changed {
                match self.verbosity {
                    Changes | Verbose => {
                        println!("changed group of {} from {} to {}",
                                 path.display(),
                                 entries::gid2grp(meta.gid()).unwrap(),
                                 entries::gid2grp(dest_gid).unwrap());
                    }
                    _ => (),
                };
            } else if self.verbosity == Verbose {
                println!("group of {} retained as {}",
                         path.display(),
                         entries::gid2grp(dest_gid).unwrap());
            }
        }
        ret
    }
}
<|endoftext|>


<commit_before>//! Implementation of panics via stack unwinding
//!
//! This crate is an implementation of panics in Rust using "most native" stack
//! unwinding mechanism of the platform this is being compiled for. This
//! essentially gets categorized into three buckets currently:
//!
//! 1. MSVC targets use SEH in the `seh.rs` file.
//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file.
//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file.
//!
//! More documentation about each implementation can be found in the respective
//! module.

#![no_std]
#![unstable(feature = "panic_unwind", issue = "32837")]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]

#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_unwind)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(unwind_attributes)]

#![panic_runtime]
#![feature(panic_runtime)]

use alloc::boxed::Box;
use core::intrinsics;
use core::mem;
use core::raw;
use core::panic::BoxMeUp;

cfg_if::cfg_if! {
    if #[cfg(miri)] {
        #[path = "miri.rs"]
        mod imp;
    } else if #[cfg(target_os = "emscripten")] {
        #[path = "emcc.rs"]
        mod imp;
    } else if #[cfg(target_arch = "wasm32")] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_os = "hermit")] {
        #[path = "hermit.rs"]
        mod imp;
    } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_env = "msvc")] {
        #[path = "seh.rs"]
        mod imp;
    } else {
        // Rust runtime's startup objects depend on these symbols, so make them public.
        #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
        pub use imp::eh_frame_registry::*;
        #[path = "gcc.rs"]
        mod imp;
    }
}

mod dwarf;

// Entry point for catching an exception, implemented using the `try` intrinsic
// in the compiler.
//
// The interaction between the `payload` function and the compiler is pretty
// hairy and tightly coupled, for more information see the compiler's
// implementation of this.
#[no_mangle]
#[allow(improper_ctypes)]
pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
                                                  data: *mut u8,
                                                  data_ptr: *mut usize,
                                                  vtable_ptr: *mut usize)
                                                  -> u32 {
    let mut payload = imp::payload();
    if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
        0
    } else {
        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
        *data_ptr = obj.data as usize;
        *vtable_ptr = obj.vtable as usize;
        1
    }
}

// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[no_mangle]
#[unwind(allowed)]
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
    let payload = payload as *mut &mut dyn BoxMeUp;
    imp::panic(Box::from_raw((*payload).box_me_up()))
}

#[cfg(miri)]
pub fn miri_panic_trampoline() {}
<commit_msg>Add comment<commit_after>//! Implementation of panics via stack unwinding
//!
//! This crate is an implementation of panics in Rust using "most native" stack
//! unwinding mechanism of the platform this is being compiled for. This
//! essentially gets categorized into three buckets currently:
//!
//! 1. MSVC targets use SEH in the `seh.rs` file.
//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file.
//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file.
//!
//! More documentation about each implementation can be found in the respective
//! module.

#![no_std]
#![unstable(feature = "panic_unwind", issue = "32837")]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]

#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_unwind)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(unwind_attributes)]

#![panic_runtime]
#![feature(panic_runtime)]

use alloc::boxed::Box;
use core::intrinsics;
use core::mem;
use core::raw;
use core::panic::BoxMeUp;

cfg_if::cfg_if! {
    if #[cfg(miri)] {
        #[path = "miri.rs"]
        mod imp;
    } else if #[cfg(target_os = "emscripten")] {
        #[path = "emcc.rs"]
        mod imp;
    } else if #[cfg(target_arch = "wasm32")] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_os = "hermit")] {
        #[path = "hermit.rs"]
        mod imp;
    } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_env = "msvc")] {
        #[path = "seh.rs"]
        mod imp;
    } else {
        // Rust runtime's startup objects depend on these symbols, so make them public.
        #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
        pub use imp::eh_frame_registry::*;
        #[path = "gcc.rs"]
        mod imp;
    }
}

mod dwarf;

// Entry point for catching an exception, implemented using the `try` intrinsic
// in the compiler.
//
// The interaction between the `payload` function and the compiler is pretty
// hairy and tightly coupled, for more information see the compiler's
// implementation of this.
#[no_mangle]
#[allow(improper_ctypes)]
pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
                                                  data: *mut u8,
                                                  data_ptr: *mut usize,
                                                  vtable_ptr: *mut usize)
                                                  -> u32 {
    let mut payload = imp::payload();
    if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
        0
    } else {
        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
        *data_ptr = obj.data as usize;
        *vtable_ptr = obj.vtable as usize;
        1
    }
}

// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[no_mangle]
#[unwind(allowed)]
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
    let payload = payload as *mut &mut dyn BoxMeUp;
    imp::panic(Box::from_raw((*payload).box_me_up()))
}

// A dummy helper function for Miri.
// Used to push an empty stack frame when we start unwinding
#[cfg(miri)]
pub fn miri_panic_trampoline() {}
<|endoftext|>


<commit_before>// Copyright 2015, Paul Osborne <osbpau@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option.  This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

use nix::sys::ioctl;
use std::mem;
use std::io;
use std::os::unix::prelude::*;
use super::SpiModeFlags;

const SPI_IOC_MAGIC: u8 = 'k' as u8;

const SPI_IOC_NR_TRANSFER: u8 = 0;
const SPI_IOC_NR_MODE: u8 = 1;
const SPI_IOC_NR_LSB_FIRST: u8 = 2;
const SPI_IOC_NR_BITS_PER_WORD: u8 = 3;
const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4;
const SPI_IOC_NR_MODE32: u8 = 5;

fn from_nix_error(err: ::nix::Error) -> io::Error {
    io::Error::from_raw_os_error(err.errno() as i32)
}

fn from_nix_result<T>(res: ::nix::Result<T>) -> io::Result<T> {
    match res {
        Ok(r) => Ok(r),
        Err(err) => Err(from_nix_error(err)),
    }
}

/// Structure that is used when performing communication
/// with the kernel.
///
/// From the kernel documentation:
///
/// ```
/// struct spi_ioc_transfer - describes a single SPI transfer
/// @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
///   If no data is provided, zeroes are shifted out.
/// @rx_buf: Holds pointer to userspace buffer for receive data, or null.
/// @len: Length of tx and rx buffers, in bytes.
/// @speed_hz: Temporary override of the device's bitrate.
/// @bits_per_word: Temporary override of the device's wordsize.
/// @delay_usecs: If nonzero, how long to delay after the last bit transfer
///      before optionally deselecting the device before the next transfer.
/// @cs_change: True to deselect device before starting the next transfer.
///
/// This structure is mapped directly to the kernel spi_transfer structure;
/// the fields have the same meanings, except of course that the pointers
/// are in a different address space (and may be of different sizes in some
/// cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
/// Zero-initialize the structure, including currently unused fields, to
/// accomodate potential future updates.
///
/// SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
/// Pass it an array of related transfers, they'll execute together.
/// Each transfer may be half duplex (either direction) or full duplex.
///
///      struct spi_ioc_transfer mesg[4];
///      ...
///      status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
///
/// So for example one transfer might send a nine bit command (right aligned
/// in a 16-bit word), the next could read a block of 8-bit data before
/// terminating that command by temporarily deselecting the chip; the next
/// could send a different nine bit command (re-selecting the chip), and the
/// last transfer might write some register values.
/// ```
#[derive(Debug)]
struct spi_ioc_transfer {
    pub tx_buf: u64,
    pub rx_buf: u64,
    pub len: u32,

    // optional overrides
    pub speed_hz: u32,
    pub delay_usecs: u16,
    pub bits_per_word: u8,
    pub cs_change: u8,
    pub pad: u32,
}

/// Representation of a spidev transfer that is shared
/// with external users
#[derive(Default)]
pub struct SpidevTransfer {
    pub tx_buf: Option<Box<[u8]>>,
    pub rx_buf: Option<Box<[u8]>>,
    len: u32,
    speed_hz: u32,
    delay_usecs: u16,
    bits_per_word: u8,
    cs_change: u8,
    pad: u32,
}

impl SpidevTransfer {
    pub fn read(length: usize) -> SpidevTransfer {
        SpidevTransfer {
            tx_buf: None,
            rx_buf: Some(vec![0u8; length].into_boxed_slice()),
            len: length as u32,
            ..Default::default()
        }
    }

    pub fn write(tx_buf: &[u8]) -> SpidevTransfer {
        let len = tx_buf.len();
        let rx_buf_vec: Vec<u8> = vec![0; len];
        let mut tx_buf_vec: Vec<u8> = Vec::with_capacity(len);
        tx_buf_vec.clone_from_slice(tx_buf);
        SpidevTransfer {
            tx_buf: Some(tx_buf_vec.into_boxed_slice()),
            rx_buf: Some(rx_buf_vec.into_boxed_slice()),
            len: tx_buf.len() as u32,
            ..Default::default()
        }
    }

    fn as_spi_ioc_transfer(&self) -> spi_ioc_transfer {
        spi_ioc_transfer {
            tx_buf: match self.tx_buf {
                Some(ref bufbox) => bufbox.as_ptr() as u64,
                None => 0,
            },
            rx_buf: match self.rx_buf {
                Some(ref bufbox) => bufbox.as_ptr() as u64,
                None => 0,
            },
            len: self.len,
            speed_hz: self.speed_hz,
            delay_usecs: self.delay_usecs,
            bits_per_word: self.bits_per_word,
            cs_change: self.cs_change,
            pad: self.pad,
        }
    }
}

fn spidev_ioc_read<T>(fd: RawFd, nr: u8) -> io::Result<T> {
    let size = mem::size_of::<T>();
    let op = ioctl::op_read(SPI_IOC_MAGIC, nr, size);
    from_nix_result(unsafe { ioctl::read(fd, op) })
}

fn spidev_ioc_write<T>(fd: RawFd, nr: u8, data: &T) -> io::Result<()> {
    let size = mem::size_of::<T>();
    let op = ioctl::op_write(SPI_IOC_MAGIC, nr, size);
    try!(from_nix_result(unsafe { ioctl::write(fd, op, data) }));
    Ok(())
}

pub fn get_mode(fd: RawFd) -> io::Result<u8> {
    // #define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
    spidev_ioc_read::<u8>(fd, SPI_IOC_NR_MODE)
}

pub fn set_mode(fd: RawFd, mode: SpiModeFlags) -> io::Result<()> {
    // #define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
    spidev_ioc_write(fd, SPI_IOC_NR_MODE, &mode.bits)
}

pub fn get_lsb_first(fd: RawFd) -> io::Result<bool> {
    // #define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8)
    Ok(try!(spidev_ioc_read::<u8>(fd, SPI_IOC_NR_LSB_FIRST)) != 0)
}

pub fn set_lsb_first(fd: RawFd, lsb_first: bool) -> io::Result<()> {
    // #define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8)
    let lsb_first_value: u8 = if lsb_first { 1 } else { 0 };
    spidev_ioc_write(fd, SPI_IOC_NR_LSB_FIRST, &lsb_first_value)
}

pub fn get_bits_per_word(fd: RawFd) -> io::Result<u8> {
    // #define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8)
    spidev_ioc_read::<u8>(fd, SPI_IOC_NR_BITS_PER_WORD)
}

pub fn set_bits_per_word(fd: RawFd, bits_per_word: u8) -> io::Result<()> {
    // #define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8)
    spidev_ioc_write(fd, SPI_IOC_NR_BITS_PER_WORD, &bits_per_word)
}

pub fn get_max_speed_hz(fd: RawFd) -> io::Result<u32> {
    // #define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
    spidev_ioc_read::<u32>(fd, SPI_IOC_NR_MAX_SPEED_HZ)
}

pub fn set_max_speed_hz(fd: RawFd, max_speed_hz: u32) -> io::Result<()> {
    // #define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
    spidev_ioc_write(fd, SPI_IOC_NR_MAX_SPEED_HZ, &max_speed_hz)
}

pub fn transfer(fd: RawFd, transfer: &mut SpidevTransfer) -> io::Result<()> {
    let mut raw_transfer = transfer.as_spi_ioc_transfer();
    let op = ioctl::op_write(
            SPI_IOC_MAGIC,
            SPI_IOC_NR_TRANSFER,
            1 * mem::size_of::<spi_ioc_transfer>());

    // The kernel will directly modify the rx_buf of the SpidevTransfer
    // rx_buf if present, so there is no need to do any additional work
    try!(from_nix_result(unsafe {
        ioctl::read_into(fd, op, &mut raw_transfer)
    }));
    Ok(())
}

pub fn transfer_multiple(fd: RawFd, transfers: &Vec<SpidevTransfer>) -> io::Result<()> {
    // create a boxed slice containg several spi_ioc_transfers
    let mut raw_transfers_vec: Vec<spi_ioc_transfer> = Vec::with_capacity(transfers.len());
    for transfer in transfers.iter() {
        raw_transfers_vec.push(transfer.as_spi_ioc_transfer());
    }
    let mut raw_transfers = raw_transfers_vec.into_boxed_slice();

    let tot_size = raw_transfers.len() * mem::size_of::<spi_ioc_transfer>();
    let op = ioctl::op_write(
        SPI_IOC_MAGIC,
        SPI_IOC_NR_TRANSFER,
        tot_size);

    // The kernel will directly modify the rx_buf of each transfer
    // so no additional work is required here
    try!(from_nix_result(unsafe {
        ioctl::read_into_ptr(fd, op, raw_transfers.as_mut_ptr())
    }));
    Ok(())
}
<commit_msg>spidev: fix bug preventing bytes from being transmitted<commit_after>// Copyright 2015, Paul Osborne <osbpau@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option.  This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

use nix::sys::ioctl;
use std::mem;
use std::io;
use std::os::unix::prelude::*;
use super::SpiModeFlags;

const SPI_IOC_MAGIC: u8 = 'k' as u8;

const SPI_IOC_NR_TRANSFER: u8 = 0;
const SPI_IOC_NR_MODE: u8 = 1;
const SPI_IOC_NR_LSB_FIRST: u8 = 2;
const SPI_IOC_NR_BITS_PER_WORD: u8 = 3;
const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4;
const SPI_IOC_NR_MODE32: u8 = 5;

fn from_nix_error(err: ::nix::Error) -> io::Error {
    io::Error::from_raw_os_error(err.errno() as i32)
}

fn from_nix_result<T>(res: ::nix::Result<T>) -> io::Result<T> {
    match res {
        Ok(r) => Ok(r),
        Err(err) => Err(from_nix_error(err)),
    }
}

/// Structure that is used when performing communication
/// with the kernel.
///
/// From the kernel documentation:
///
/// ```
/// struct spi_ioc_transfer - describes a single SPI transfer
/// @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
///   If no data is provided, zeroes are shifted out.
/// @rx_buf: Holds pointer to userspace buffer for receive data, or null.
/// @len: Length of tx and rx buffers, in bytes.
/// @speed_hz: Temporary override of the device's bitrate.
/// @bits_per_word: Temporary override of the device's wordsize.
/// @delay_usecs: If nonzero, how long to delay after the last bit transfer
///      before optionally deselecting the device before the next transfer.
/// @cs_change: True to deselect device before starting the next transfer.
///
/// This structure is mapped directly to the kernel spi_transfer structure;
/// the fields have the same meanings, except of course that the pointers
/// are in a different address space (and may be of different sizes in some
/// cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
/// Zero-initialize the structure, including currently unused fields, to
/// accomodate potential future updates.
///
/// SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
/// Pass it an array of related transfers, they'll execute together.
/// Each transfer may be half duplex (either direction) or full duplex.
///
///      struct spi_ioc_transfer mesg[4];
///      ...
///      status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
///
/// So for example one transfer might send a nine bit command (right aligned
/// in a 16-bit word), the next could read a block of 8-bit data before
/// terminating that command by temporarily deselecting the chip; the next
/// could send a different nine bit command (re-selecting the chip), and the
/// last transfer might write some register values.
/// ```
#[derive(Debug)]
struct spi_ioc_transfer {
    pub tx_buf: u64,
    pub rx_buf: u64,
    pub len: u32,

    // optional overrides
    pub speed_hz: u32,
    pub delay_usecs: u16,
    pub bits_per_word: u8,
    pub cs_change: u8,
    pub pad: u32,
}

/// Representation of a spidev transfer that is shared
/// with external users
#[derive(Default)]
pub struct SpidevTransfer {
    pub tx_buf: Option<Box<[u8]>>,
    pub rx_buf: Option<Box<[u8]>>,
    len: u32,
    speed_hz: u32,
    delay_usecs: u16,
    bits_per_word: u8,
    cs_change: u8,
    pad: u32,
}

impl SpidevTransfer {
    pub fn read(length: usize) -> SpidevTransfer {
        SpidevTransfer {
            tx_buf: None,
            rx_buf: Some(vec![0u8; length].into_boxed_slice()),
            len: length as u32,
            ..Default::default()
        }
    }

    pub fn write(tx_buf: &[u8]) -> SpidevTransfer {
        let len = tx_buf.len();
        let rx_buf_vec: Vec<u8> = vec![0; len];
        let mut tx_buf_vec: Vec<u8> = vec![0; len];
        tx_buf_vec.clone_from_slice(tx_buf);
        SpidevTransfer {
            tx_buf: Some(tx_buf_vec.into_boxed_slice()),
            rx_buf: Some(rx_buf_vec.into_boxed_slice()),
            len: tx_buf.len() as u32,
            ..Default::default()
        }
    }

    fn as_spi_ioc_transfer(&self) -> spi_ioc_transfer {
        spi_ioc_transfer {
            tx_buf: match self.tx_buf {
                Some(ref bufbox) => bufbox.as_ptr() as u64,
                None => 0,
            },
            rx_buf: match self.rx_buf {
                Some(ref bufbox) => bufbox.as_ptr() as u64,
                None => 0,
            },
            len: self.len,
            speed_hz: self.speed_hz,
            delay_usecs: self.delay_usecs,
            bits_per_word: self.bits_per_word,
            cs_change: self.cs_change,
            pad: self.pad,
        }
    }
}

fn spidev_ioc_read<T>(fd: RawFd, nr: u8) -> io::Result<T> {
    let size = mem::size_of::<T>();
    let op = ioctl::op_read(SPI_IOC_MAGIC, nr, size);
    from_nix_result(unsafe { ioctl::read(fd, op) })
}

fn spidev_ioc_write<T>(fd: RawFd, nr: u8, data: &T) -> io::Result<()> {
    let size = mem::size_of::<T>();
    let op = ioctl::op_write(SPI_IOC_MAGIC, nr, size);
    try!(from_nix_result(unsafe { ioctl::write(fd, op, data) }));
    Ok(())
}

pub fn get_mode(fd: RawFd) -> io::Result<u8> {
    // #define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
    spidev_ioc_read::<u8>(fd, SPI_IOC_NR_MODE)
}

pub fn set_mode(fd: RawFd, mode: SpiModeFlags) -> io::Result<()> {
    // #define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
    spidev_ioc_write(fd, SPI_IOC_NR_MODE, &mode.bits)
}

pub fn get_lsb_first(fd: RawFd) -> io::Result<bool> {
    // #define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8)
    Ok(try!(spidev_ioc_read::<u8>(fd, SPI_IOC_NR_LSB_FIRST)) != 0)
}

pub fn set_lsb_first(fd: RawFd, lsb_first: bool) -> io::Result<()> {
    // #define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8)
    let lsb_first_value: u8 = if lsb_first { 1 } else { 0 };
    spidev_ioc_write(fd, SPI_IOC_NR_LSB_FIRST, &lsb_first_value)
}

pub fn get_bits_per_word(fd: RawFd) -> io::Result<u8> {
    // #define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8)
    spidev_ioc_read::<u8>(fd, SPI_IOC_NR_BITS_PER_WORD)
}

pub fn set_bits_per_word(fd: RawFd, bits_per_word: u8) -> io::Result<()> {
    // #define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8)
    spidev_ioc_write(fd, SPI_IOC_NR_BITS_PER_WORD, &bits_per_word)
}

pub fn get_max_speed_hz(fd: RawFd) -> io::Result<u32> {
    // #define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
    spidev_ioc_read::<u32>(fd, SPI_IOC_NR_MAX_SPEED_HZ)
}

pub fn set_max_speed_hz(fd: RawFd, max_speed_hz: u32) -> io::Result<()> {
    // #define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
    spidev_ioc_write(fd, SPI_IOC_NR_MAX_SPEED_HZ, &max_speed_hz)
}

pub fn transfer(fd: RawFd, transfer: &mut SpidevTransfer) -> io::Result<()> {
    let mut raw_transfer = transfer.as_spi_ioc_transfer();
    let op = ioctl::op_write(
            SPI_IOC_MAGIC,
            SPI_IOC_NR_TRANSFER,
            1 * mem::size_of::<spi_ioc_transfer>());

    // The kernel will directly modify the rx_buf of the SpidevTransfer
    // rx_buf if present, so there is no need to do any additional work
    try!(from_nix_result(unsafe {
        ioctl::read_into(fd, op, &mut raw_transfer)
    }));
    Ok(())
}

pub fn transfer_multiple(fd: RawFd, transfers: &Vec<SpidevTransfer>) -> io::Result<()> {
    // create a boxed slice containg several spi_ioc_transfers
    let mut raw_transfers_vec: Vec<spi_ioc_transfer> = Vec::with_capacity(transfers.len());
    for transfer in transfers.iter() {
        raw_transfers_vec.push(transfer.as_spi_ioc_transfer());
    }
    let mut raw_transfers = raw_transfers_vec.into_boxed_slice();

    let tot_size = raw_transfers.len() * mem::size_of::<spi_ioc_transfer>();
    let op = ioctl::op_write(
        SPI_IOC_MAGIC,
        SPI_IOC_NR_TRANSFER,
        tot_size);

    // The kernel will directly modify the rx_buf of each transfer
    // so no additional work is required here
    try!(from_nix_result(unsafe {
        ioctl::read_into_ptr(fd, op, raw_transfers.as_mut_ptr())
    }));
    Ok(())
}
<|endoftext|>


<commit_before>//! SBD message storage.
//!
//! We can store messages in one of several backends, provided as submodules here. Storages
//! implement the `Storage` trait.

mod filesystem;
mod memory;

pub use self::filesystem::Storage as FilesystemStorage;
pub use self::memory::Storage as MemoryStorage;

use Result;
use mo::Message;

/// Trait for all backend SBD storages.
pub trait Storage {
    /// Stores message in this storage.
    fn store(&mut self, message: &Message) -> Result<()>;
}
<commit_msg>Add doc test for store<commit_after>//! Squirrel away SBD messages and retrieve them later.

mod filesystem;
mod memory;

pub use self::filesystem::Storage as FilesystemStorage;
pub use self::memory::Storage as MemoryStorage;

use Result;
use mo::Message;

/// Basic storage operations.
pub trait Storage {
    /// Place a message into this storage.
    ///
    /// # Examples
    ///
    /// ```
    /// # use sbd::mo::Message;
    /// # use sbd::storage::{Storage, MemoryStorage};
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// let mut storage = MemoryStorage::new();
    /// storage.store(&message);
    /// ```
    fn store(&mut self, message: &Message) -> Result<()>;
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File as FSFile;
use std::ops::Deref;
use std::io::Write;
use std::io::Read;

pub mod path;
pub mod file;
pub mod parser;
pub mod json;

use module::Module;
use runtime::Runtime;
use storage::file::File;
use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use storage::parser::{FileHeaderParser, Parser, ParserError};
use storage::file::header::data::FileHeaderData;

type Cache = HashMap<FileID, Rc<RefCell<File>>>;

pub struct Store {
    storepath: String,
    cache : RefCell<Cache>,
}

/**
 * Store object
 *
 * This object is an abstraction layer over FS and an interface to the object store of this
 * software.
 */
impl Store {

    pub fn new(storepath: String) -> Store {
        Store {
            storepath: storepath,
            cache: RefCell::new(HashMap::new()),
        }
    }

    /**
     * Put a file into the cache
     */
    fn put_in_cache(&self, f: File) -> FileID {
        let res = f.id().clone();
        self.cache.borrow_mut().insert(f.id().clone(), Rc::new(RefCell::new(f)));
        res
    }

    /**
     * Load a file by ID into the cache and return it afterwards
     *
     * Returns None if the file could be loaded from the Filesystem
     */
    pub fn load_in_cache<HP>(&self, m: &Module, parser: &Parser<HP>, id: FileID)
        -> Option<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        let idstr : String = id.clone().into();
        let path = format!("{}/{}-{}.imag", self.storepath, m.name(), idstr);
        debug!("Loading path = '{}'", path);
        let mut string = String::new();

        FSFile::open(&path).map(|mut file| {
            file.read_to_string(&mut string)
                .map_err(|e| error!("Failed reading file: '{}'", path));
        });

        parser.read(string).map(|(header, data)| {
            self.new_file_from_parser_result(m, id.clone(), header, data);
        });

        self.load(&id)
    }

    /**
     * Generate a new file for a module.
     *
     * Returns the new FileID object then
     */
    pub fn new_file(&self, module: &Module)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: String::from(""),
            id: self.get_new_file_id(),
        };

        debug!("Create new File object: {:?}", &f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file from a parser result.
     *
     * @deprecated This function shouldn't be needed anymore
     */
    pub fn new_file_from_parser_result(&self,
                                       module: &Module,
                                       id: FileID,
                                       header: FileHeaderData,
                                       data: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some header data
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_header(&self,
                                module: &Module,
                                h: FileHeaderData)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: String::from(""),
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some initial data
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_data(&self, module: &Module, d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some initial data and some header
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_content(&self,
                                 module: &Module,
                                 h: FileHeaderData,
                                 d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Persist a File on the filesystem
     *
     * Returns true if this worked
     */
    pub fn persist<HP>(&self,
                       p: &Parser<HP>,
                       f: Rc<RefCell<File>>) -> bool
        where HP: FileHeaderParser
    {
        let file = f.deref().borrow();
        let text = p.write(file.contents());
        if text.is_err() {
            error!("Error: {}", text.err().unwrap());
            return false;
        }

        let path = {
            let ids : String = file.id().clone().into();
            format!("{}/{}-{}.imag", self.storepath, file.owning_module_name, ids)
        };

        self.ensure_store_path_exists();

        FSFile::create(&path).map(|mut fsfile| {
            fsfile.write_all(&text.unwrap().clone().into_bytes()[..])
        }).map_err(|writeerr|  {
            debug!("Could not create file at '{}'", path);
        }).and(Ok(true)).unwrap()

        // TODO: Is this unwrap() save?
    }

    /**
     * Helper to generate the store path
     *
     * Kills the program if it fails
     */
    fn ensure_store_path_exists(&self) {
        use std::fs::create_dir_all;
        use std::process::exit;

        create_dir_all(&self.storepath).unwrap_or_else(|e| {
            error!("Could not create store: '{}'", self.storepath);
            error!("Error                 : '{}'", e);
            error!("Killing myself now");
            exit(1);
        })
    }

    /**
     * Load a file from the cache by FileID
     *
     * TODO: Semantics: This function should load from FS if the file is not in the cache yet or
     * fail if the file is not available.
     */
    pub fn load(&self, id: &FileID) -> Option<Rc<RefCell<File>>> {
        debug!("Loading '{:?}'", id);
        self.cache.borrow().get(id).cloned()
    }

    /**
     * Load a file from the filesystem/cache by a FileHash
     */
    pub fn load_by_hash<HP>(&self,
                            m: &Module,
                            parser: &Parser<HP>,
                            hash: FileHash)
        -> Option<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        macro_rules! try_some {
            ($expr:expr) => (match $expr {
                ::std::option::Option::Some(val) => val,
                ::std::option::Option::None => return ::std::option::Option::None,
            });

            ($expr:expr => return) => (match $expr {
                ::std::option::Option::Some(val) => val,
                ::std::option::Option::None => return,
            })
        }

        use glob::{glob, Paths, PatternError};

        let hashstr : String = hash.into();
        let globstr = format!("{}/*-{}.imag", self.storepath, hashstr);
        debug!("glob({})", globstr);

        let globs = glob(&globstr[..]);
        if globs.is_err() {
            return None;
        }

        let path = globs.unwrap().last();
        debug!("path = {:?}", path);

        let pathbuf = try_some!(path);
        if pathbuf.is_err() { return None; }

        let pathbuf_un  = pathbuf.unwrap();
        let filename    = pathbuf_un.file_name();
        let s           = try_some!(filename).to_str();
        let string      = String::from(try_some!(s));
        let id          = try_some!(FileID::parse(&string));

        debug!("Loaded ID = '{:?}'", id);

        self.load_in_cache(m, parser, id)
            .map(|file| {
                debug!("Loaded File = '{:?}'", file);
                Some(file)
            }).unwrap_or(None)
    }

    /**
     * Remove a file from the filesystem by FileID
     *
     * Returns true if this works.
     */
    pub fn remove(&self, id: FileID) -> bool {
        use std::fs::remove_file;

        self.cache
            .borrow_mut()
            .remove(&id)
            .map(|file| {
                let idstr : String = id.into();
                let path = format!("{}/{}-{}.imag",
                                   self.storepath,
                                   file.deref().borrow().owner_name(),
                                   idstr);
                debug!("Removing file NOW: '{}'", path);
                remove_file(path).is_ok()
            })
            .unwrap_or(false)
    }

    /**
     * Load all files for a module
     */
    pub fn load_for_module<HP>(&self, m: &Module, parser: &Parser<HP>)
        -> Vec<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        use glob::{glob, Paths, PatternError};

        let globstr = format!("{}/{}-*.imag", self.storepath, m.name());
        let mut res = vec![];

        glob(&globstr[..]).map(|paths| {
            for path in paths {
                if let Ok(pathbuf) = path {
                    let fname = pathbuf.file_name().and_then(|s| s.to_str());
                    fname.map(|s| {
                        FileID::parse(&String::from(s)).map(|id| {
                            self.load_in_cache(m, parser, id).map(|file| {
                                res.push(file);
                            })
                        });
                    });
                }
            }
        });
        res
    }

    /**
     * Helper to generate a new FileID object
     */
    fn get_new_file_id(&self) -> FileID {
        use uuid::Uuid;
        let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
        FileID::new(FileIDType::UUID, hash)
    }

}
<commit_msg>Rewrite Store::load_in_cache() to check cache before loading<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File as FSFile;
use std::ops::Deref;
use std::io::Write;
use std::io::Read;

pub mod path;
pub mod file;
pub mod parser;
pub mod json;

use module::Module;
use runtime::Runtime;
use storage::file::File;
use storage::file::id::FileID;
use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;
use storage::parser::{FileHeaderParser, Parser, ParserError};
use storage::file::header::data::FileHeaderData;

type Cache = HashMap<FileID, Rc<RefCell<File>>>;

pub struct Store {
    storepath: String,
    cache : RefCell<Cache>,
}

/**
 * Store object
 *
 * This object is an abstraction layer over FS and an interface to the object store of this
 * software.
 */
impl Store {

    pub fn new(storepath: String) -> Store {
        Store {
            storepath: storepath,
            cache: RefCell::new(HashMap::new()),
        }
    }

    /**
     * Put a file into the cache
     */
    fn put_in_cache(&self, f: File) -> FileID {
        let res = f.id().clone();
        self.cache.borrow_mut().insert(f.id().clone(), Rc::new(RefCell::new(f)));
        res
    }

    /**
     * Load a file by ID into the cache and return it afterwards
     *
     * Returns None if the file could be loaded from the Filesystem
     */
    pub fn load_in_cache<HP>(&self, m: &Module, parser: &Parser<HP>, id: FileID)
        -> Option<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        self.load(&id).or({
            let idstr : String = id.clone().into();
            let path = format!("{}/{}-{}.imag", self.storepath, m.name(), idstr);
            debug!("Loading path = '{}'", path);
            let mut string = String::new();

            FSFile::open(&path).map(|mut file| {
                file.read_to_string(&mut string)
                    .map_err(|e| error!("Failed reading file: '{}'", path));
            });

            parser.read(string).map(|(header, data)| {
                self.new_file_from_parser_result(m, id.clone(), header, data);
                self.load(&id)
            }).unwrap_or(None)
        })
    }

    /**
     * Generate a new file for a module.
     *
     * Returns the new FileID object then
     */
    pub fn new_file(&self, module: &Module)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: String::from(""),
            id: self.get_new_file_id(),
        };

        debug!("Create new File object: {:?}", &f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file from a parser result.
     *
     * @deprecated This function shouldn't be needed anymore
     */
    pub fn new_file_from_parser_result(&self,
                                       module: &Module,
                                       id: FileID,
                                       header: FileHeaderData,
                                       data: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: header,
            data: data,
            id: id,
        };
        debug!("Create new File object from parser result: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some header data
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_header(&self,
                                module: &Module,
                                h: FileHeaderData)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: String::from(""),
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with header: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some initial data
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_data(&self, module: &Module, d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: FileHeaderData::Null,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with data: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Generate a new file for a module, providing some initial data and some header
     *
     * Returns the new FileID object then
     */
    pub fn new_file_with_content(&self,
                                 module: &Module,
                                 h: FileHeaderData,
                                 d: String)
        -> FileID
    {
        let f = File {
            owning_module_name: module.name(),
            header: h,
            data: d,
            id: self.get_new_file_id(),
        };
        debug!("Create new File object with content: {:?}", f);
        self.put_in_cache(f)
    }

    /**
     * Persist a File on the filesystem
     *
     * Returns true if this worked
     */
    pub fn persist<HP>(&self,
                       p: &Parser<HP>,
                       f: Rc<RefCell<File>>) -> bool
        where HP: FileHeaderParser
    {
        let file = f.deref().borrow();
        let text = p.write(file.contents());
        if text.is_err() {
            error!("Error: {}", text.err().unwrap());
            return false;
        }

        let path = {
            let ids : String = file.id().clone().into();
            format!("{}/{}-{}.imag", self.storepath, file.owning_module_name, ids)
        };

        self.ensure_store_path_exists();

        FSFile::create(&path).map(|mut fsfile| {
            fsfile.write_all(&text.unwrap().clone().into_bytes()[..])
        }).map_err(|writeerr|  {
            debug!("Could not create file at '{}'", path);
        }).and(Ok(true)).unwrap()

        // TODO: Is this unwrap() save?
    }

    /**
     * Helper to generate the store path
     *
     * Kills the program if it fails
     */
    fn ensure_store_path_exists(&self) {
        use std::fs::create_dir_all;
        use std::process::exit;

        create_dir_all(&self.storepath).unwrap_or_else(|e| {
            error!("Could not create store: '{}'", self.storepath);
            error!("Error                 : '{}'", e);
            error!("Killing myself now");
            exit(1);
        })
    }

    /**
     * Load a file from the cache by FileID
     *
     * TODO: Semantics: This function should load from FS if the file is not in the cache yet or
     * fail if the file is not available.
     */
    pub fn load(&self, id: &FileID) -> Option<Rc<RefCell<File>>> {
        debug!("Loading '{:?}'", id);
        self.cache.borrow().get(id).cloned()
    }

    /**
     * Load a file from the filesystem/cache by a FileHash
     */
    pub fn load_by_hash<HP>(&self,
                            m: &Module,
                            parser: &Parser<HP>,
                            hash: FileHash)
        -> Option<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        macro_rules! try_some {
            ($expr:expr) => (match $expr {
                ::std::option::Option::Some(val) => val,
                ::std::option::Option::None => return ::std::option::Option::None,
            });

            ($expr:expr => return) => (match $expr {
                ::std::option::Option::Some(val) => val,
                ::std::option::Option::None => return,
            })
        }

        use glob::{glob, Paths, PatternError};

        let hashstr : String = hash.into();
        let globstr = format!("{}/*-{}.imag", self.storepath, hashstr);
        debug!("glob({})", globstr);

        let globs = glob(&globstr[..]);
        if globs.is_err() {
            return None;
        }

        let path = globs.unwrap().last();
        debug!("path = {:?}", path);

        let pathbuf = try_some!(path);
        if pathbuf.is_err() { return None; }

        let pathbuf_un  = pathbuf.unwrap();
        let filename    = pathbuf_un.file_name();
        let s           = try_some!(filename).to_str();
        let string      = String::from(try_some!(s));
        let id          = try_some!(FileID::parse(&string));

        debug!("Loaded ID = '{:?}'", id);

        self.load_in_cache(m, parser, id)
            .map(|file| {
                debug!("Loaded File = '{:?}'", file);
                Some(file)
            }).unwrap_or(None)
    }

    /**
     * Remove a file from the filesystem by FileID
     *
     * Returns true if this works.
     */
    pub fn remove(&self, id: FileID) -> bool {
        use std::fs::remove_file;

        self.cache
            .borrow_mut()
            .remove(&id)
            .map(|file| {
                let idstr : String = id.into();
                let path = format!("{}/{}-{}.imag",
                                   self.storepath,
                                   file.deref().borrow().owner_name(),
                                   idstr);
                debug!("Removing file NOW: '{}'", path);
                remove_file(path).is_ok()
            })
            .unwrap_or(false)
    }

    /**
     * Load all files for a module
     */
    pub fn load_for_module<HP>(&self, m: &Module, parser: &Parser<HP>)
        -> Vec<Rc<RefCell<File>>>
        where HP: FileHeaderParser
    {
        use glob::{glob, Paths, PatternError};

        let globstr = format!("{}/{}-*.imag", self.storepath, m.name());
        let mut res = vec![];

        glob(&globstr[..]).map(|paths| {
            for path in paths {
                if let Ok(pathbuf) = path {
                    let fname = pathbuf.file_name().and_then(|s| s.to_str());
                    fname.map(|s| {
                        FileID::parse(&String::from(s)).map(|id| {
                            self.load_in_cache(m, parser, id).map(|file| {
                                res.push(file);
                            })
                        });
                    });
                }
            }
        });
        res
    }

    /**
     * Helper to generate a new FileID object
     */
    fn get_new_file_id(&self) -> FileID {
        use uuid::Uuid;
        let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
        FileID::new(FileIDType::UUID, hash)
    }

}
<|endoftext|>