<commit_before>use std::fs::File;
use std::io::{BufRead, BufReader};
use std::net::IpAddr;
use std::os::unix::io::AsRawFd;

use blake2::{self, Digest};
use failure::{Error, ResultExt};
use ipnetwork::IpNetwork;
use nix::sched::{setns, CLONE_NEWNET};
use serde_json::to_vec;
use unshare;

use lithos::sandbox_config::{SandboxConfig, BridgedNetwork};
use lithos::child_config::ChildConfig;
use lithos::container_config::InstantiatedConfig;

pub fn setup(sandbox: &SandboxConfig, child: &ChildConfig,
    container: &InstantiatedConfig)
    -> Result<(), String>
{
    _setup(sandbox, child, container)
    .map_err(|e| e.to_string())
}

fn interface_name(network: &BridgedNetwork, ip: &IpAddr) -> String {
    #[derive(Serialize)]
    struct HashSource<'a> {
        bridge: &'a str,
        ip: &'a IpAddr,
    }
    let (ip1, ip2) = match *ip {
        IpAddr::V4(ip) => (ip.octets()[2], ip.octets()[3]),
        IpAddr::V6(ip) => (ip.octets()[14], ip.octets()[15]),
    };
    let name = format!("li_{:.6}_{:02x}{:02x}",
        // double formatting because of a bug in generic array
        format!("{:06x}", blake2::Blake2b::digest(&to_vec(&HashSource {
            bridge: &network.bridge,
            ip: ip,
        }).expect("can always serialize"))),
        ip1, ip2);
    assert!(name.len() <= 15);
    return name;
}

fn get_real_pids() -> Result<(u32, u32), Error> {
    let mut pid = None::<u32>;
    let mut ppid = None::<u32>;
    let f = BufReader::new(File::open("/proc/self/status")
        .context("can open /proc/self/status")?);
    for line in f.lines() {
        let line = line.context("can open /proc/self/status")?;
        if line.starts_with("Pid:") {
            pid = Some(line[5..].trim().parse::<u32>()
                .context("can parse pid in /proc/self/status")?);
        } else if line.starts_with("PPid:") {
            ppid = Some(line[5..].trim().parse::<u32>()
                .context("can parse pid in /proc/self/status")?);
        } else {
            continue;
        }
        match (pid, ppid) {
            (Some(pid), Some(ppid)) => return Ok((pid, ppid)),
            _ => continue,
        }
    }
    bail!("can't find ppid");
}

fn _setup(sandbox: &SandboxConfig, child: &ChildConfig,
    _container: &InstantiatedConfig)
    -> Result<(), Error>
{
    let net = sandbox.bridged_network.as_ref().expect("bridged network");
    let ip = child.ip_addresses.get(0).expect("ip address");
    let (pid, ppid) = get_real_pids()?;
    let my_ns = File::open("/proc/self/ns/net")
        .context("can't open namespace")?;
    let parent_ns = File::open(&format!("/proc/{}/ns/net", ppid))
        .context("can't open parent namespace")?;
    setns(parent_ns.as_raw_fd(), CLONE_NEWNET)?;
    let interface = interface_name(net, ip);
    let iinterface = interface.replace("_", "-");
    assert!(iinterface != interface);

    let mut cmd = unshare::Command::new("/bin/ip");
    cmd.arg("link").arg("add");
    cmd.arg(&interface);
    cmd.arg("type").arg("veth");
    cmd.arg("peer").arg("name").arg(&iinterface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg("dev").arg(&iinterface);
    cmd.arg("netns").arg(&format!("/proc/{}/fd/{}", pid, my_ns.as_raw_fd()));
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/brctl");
    cmd.arg("addif").arg(&net.bridge).arg(&interface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("brctl failed: {}", s),
        Err(e) => bail!("brctl failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg(&interface);
    cmd.arg("up");
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    setns(my_ns.as_raw_fd(), CLONE_NEWNET)?;

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("addr").arg("add");
    cmd.arg(&format!("{}",
        IpNetwork::new(*ip, net.network.prefix())
        .expect("network asways valid")));
    cmd.arg("dev").arg(&iinterface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg(&iinterface);
    cmd.arg("up");
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    if let Some(gw) = net.default_gateway {
        let mut cmd = unshare::Command::new("/sbin/ip");
        cmd.arg("route").arg("add");
        cmd.arg("default");
        cmd.arg("via").arg(&format!("{}", gw));
        match cmd.status() {
            Ok(s) if s.success() => {}
            Ok(s) => bail!("ip route failed: {}", s),
            Err(e) => bail!("ip route failed: {}", e),
        }
    }
    Ok(())
}
<commit_msg>Create veth interface in child namespace and move outside rather than vice versa<commit_after>use std::fs::File;
use std::io::{BufRead, BufReader};
use std::net::IpAddr;
use std::os::unix::io::AsRawFd;

use blake2::{self, Digest};
use failure::{Error, ResultExt};
use ipnetwork::IpNetwork;
use nix::sched::{setns, CLONE_NEWNET};
use serde_json::to_vec;
use unshare;

use lithos::sandbox_config::{SandboxConfig, BridgedNetwork};
use lithos::child_config::ChildConfig;
use lithos::container_config::InstantiatedConfig;

pub fn setup(sandbox: &SandboxConfig, child: &ChildConfig,
    container: &InstantiatedConfig)
    -> Result<(), String>
{
    _setup(sandbox, child, container)
    .map_err(|e| e.to_string())
}

fn interface_name(network: &BridgedNetwork, ip: &IpAddr) -> String {
    #[derive(Serialize)]
    struct HashSource<'a> {
        bridge: &'a str,
        ip: &'a IpAddr,
    }
    let (ip1, ip2) = match *ip {
        IpAddr::V4(ip) => (ip.octets()[2], ip.octets()[3]),
        IpAddr::V6(ip) => (ip.octets()[14], ip.octets()[15]),
    };
    let name = format!("li_{:.6}_{:02x}{:02x}",
        // double formatting because of a bug in generic array
        format!("{:06x}", blake2::Blake2b::digest(&to_vec(&HashSource {
            bridge: &network.bridge,
            ip: ip,
        }).expect("can always serialize"))),
        ip1, ip2);
    assert!(name.len() <= 15);
    return name;
}

fn get_real_pids() -> Result<(u32, u32), Error> {
    let mut pid = None::<u32>;
    let mut ppid = None::<u32>;
    let f = BufReader::new(File::open("/proc/self/status")
        .context("can open /proc/self/status")?);
    for line in f.lines() {
        let line = line.context("can open /proc/self/status")?;
        if line.starts_with("Pid:") {
            pid = Some(line[5..].trim().parse::<u32>()
                .context("can parse pid in /proc/self/status")?);
        } else if line.starts_with("PPid:") {
            ppid = Some(line[5..].trim().parse::<u32>()
                .context("can parse pid in /proc/self/status")?);
        } else {
            continue;
        }
        match (pid, ppid) {
            (Some(pid), Some(ppid)) => return Ok((pid, ppid)),
            _ => continue,
        }
    }
    bail!("can't find ppid");
}

fn _setup(sandbox: &SandboxConfig, child: &ChildConfig,
    _container: &InstantiatedConfig)
    -> Result<(), Error>
{
    let net = sandbox.bridged_network.as_ref().expect("bridged network");
    let ip = child.ip_addresses.get(0).expect("ip address");
    let (pid, ppid) = get_real_pids()?;
    let my_ns = File::open("/proc/self/ns/net")
        .context("can't open namespace")?;
    let parent_ns = File::open(&format!("/proc/{}/ns/net", ppid))
        .context("can't open parent namespace")?;
    let interface = interface_name(net, ip);
    let iinterface = interface.replace("_", "-");
    assert!(iinterface != interface);

    // Create interface in the child namespace
    // This helps to keep parent namespace clean if this process crashes for
    // some reason.
    let mut cmd = unshare::Command::new("/bin/ip");
    cmd.arg("link").arg("add");
    cmd.arg(&interface);
    cmd.arg("type").arg("veth");
    cmd.arg("peer").arg("name").arg(&iinterface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    // The move just external part of the interface to the parent namespace
    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg("dev").arg(&interface);
    cmd.arg("netns").arg(&format!("/proc/{}/fd/{}",
        pid, parent_ns.as_raw_fd()));
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    // jump into parent namespace to add to bridge and up the interface
    setns(parent_ns.as_raw_fd(), CLONE_NEWNET)?;

    let mut cmd = unshare::Command::new("/sbin/brctl");
    cmd.arg("addif").arg(&net.bridge).arg(&interface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("brctl failed: {}", s),
        Err(e) => bail!("brctl failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg(&interface);
    cmd.arg("up");
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    // and again to the child to setup internal part and routing
    setns(my_ns.as_raw_fd(), CLONE_NEWNET)?;

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("addr").arg("add");
    cmd.arg(&format!("{}",
        IpNetwork::new(*ip, net.network.prefix())
        .expect("network asways valid")));
    cmd.arg("dev").arg(&iinterface);
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    let mut cmd = unshare::Command::new("/sbin/ip");
    cmd.arg("link").arg("set");
    cmd.arg(&iinterface);
    cmd.arg("up");
    match cmd.status() {
        Ok(s) if s.success() => {}
        Ok(s) => bail!("ip link failed: {}", s),
        Err(e) => bail!("ip link failed: {}", e),
    }

    if let Some(gw) = net.default_gateway {
        let mut cmd = unshare::Command::new("/sbin/ip");
        cmd.arg("route").arg("add");
        cmd.arg("default");
        cmd.arg("via").arg(&format!("{}", gw));
        match cmd.status() {
            Ok(s) if s.success() => {}
            Ok(s) => bail!("ip route failed: {}", s),
            Err(e) => bail!("ip route failed: {}", e),
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::io::SeekFrom;
use std::io::prelude::*;
use std::path::Path;

use curl::easy::Easy;
use git2;
use rustc_serialize::json;
use rustc_serialize::hex::ToHex;

use core::{PackageId, SourceId};
use ops;
use sources::git;
use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
use util::network;
use util::paths;
use util::{FileLock, Filesystem};
use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl};

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    handle: Option<Easy>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str)
               -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config: config,
            handle: None,
        }
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn config(&self) -> CargoResult<Option<RegistryConfig>> {
        let lock = try!(self.index_path.open_ro(Path::new(INDEX_LOCK),
                                                self.config,
                                                "the registry index"));
        let path = lock.path().parent().unwrap();
        let contents = try!(paths::read(&path.join("config.json")));
        let config = try!(json::decode(&contents));
        Ok(Some(config))
    }

    fn update_index(&mut self) -> CargoResult<()> {
        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        try!(ops::http_handle(self.config));

        // Then we actually update the index
        try!(self.index_path.create_dir());
        let lock = try!(self.index_path.open_rw(Path::new(INDEX_LOCK),
                                                self.config,
                                                "the registry index"));
        let path = lock.path().parent().unwrap();

        try!(self.config.shell().status("Updating",
             format!("registry `{}`", self.source_id.url())));
        let repo = try!(git2::Repository::open(path).or_else(|_| {
            let _ = lock.remove_siblings();
            git2::Repository::init(path)
        }));

        // git fetch origin
        let url = self.source_id.url().to_string();
        let refspec = "refs/heads/*:refs/remotes/origin/*";

        try!(git::fetch(&repo, &url, refspec, &self.config).chain_error(|| {
            human(format!("failed to fetch `{}`", url))
        }));

        // git reset --hard origin/master
        let reference = "refs/remotes/origin/master";
        let oid = try!(repo.refname_to_id(reference));
        trace!("[{}] updating to rev {}", self.source_id, oid);
        let object = try!(repo.find_object(oid, None));
        try!(repo.reset(&object, git2::ResetType::Hard, None));
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str)
                -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);
        let mut dst = try!(self.cache_path.open_rw(path, self.config, &filename));
        let meta = try!(dst.file().metadata());
        if meta.len() > 0 {
            return Ok(dst)
        }
        try!(self.config.shell().status("Downloading", pkg));

        let config = try!(self.config()).unwrap();
        let mut url = try!(config.dl.to_url());
        url.path_segments_mut().unwrap()
            .push(pkg.name())
            .push(&pkg.version().to_string())
            .push("download");

        let handle = match self.handle {
            Some(ref mut handle) => handle,
            None => {
                self.handle = Some(try!(ops::http_handle(self.config)));
                self.handle.as_mut().unwrap()
            }
        };
        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        try!(handle.get(true));
        try!(handle.url(&url.to_string()));
        try!(handle.follow_location(true));
        let mut state = Sha256::new();
        let mut body = Vec::new();
        {
            let mut handle = handle.transfer();
            try!(handle.write_function(|buf| {
                state.update(buf);
                body.extend_from_slice(buf);
                Ok(buf.len())
            }));
            try!(network::with_retry(self.config, || {
                handle.perform()
            }))
        }
        let code = try!(handle.response_code());
        if code != 200 && code != 0 {
            bail!("failed to get 200 response from `{}`, got {}", url, code)
        }

        // Verify what we just downloaded
        if state.finish().to_hex() != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        try!(dst.write_all(&body));
        try!(dst.seek(SeekFrom::Start(0)));
        Ok(dst)
    }
}
<commit_msg>Auto merge of #2974 - alexcrichton:etag, r=brson<commit_after>use std::io::SeekFrom;
use std::io::prelude::*;
use std::path::Path;

use curl::easy::{Easy, List};
use git2;
use rustc_serialize::hex::ToHex;
use rustc_serialize::json;
use url::Url;

use core::{PackageId, SourceId};
use ops;
use sources::git;
use sources::registry::{RegistryData, RegistryConfig, INDEX_LOCK};
use util::network;
use util::paths;
use util::{FileLock, Filesystem};
use util::{Config, CargoResult, ChainError, human, Sha256, ToUrl};

pub struct RemoteRegistry<'cfg> {
    index_path: Filesystem,
    cache_path: Filesystem,
    source_id: SourceId,
    config: &'cfg Config,
    handle: Option<Easy>,
}

impl<'cfg> RemoteRegistry<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config, name: &str)
               -> RemoteRegistry<'cfg> {
        RemoteRegistry {
            index_path: config.registry_index_path().join(name),
            cache_path: config.registry_cache_path().join(name),
            source_id: source_id.clone(),
            config: config,
            handle: None,
        }
    }
}

impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
    fn index_path(&self) -> &Filesystem {
        &self.index_path
    }

    fn config(&self) -> CargoResult<Option<RegistryConfig>> {
        let lock = try!(self.index_path.open_ro(Path::new(INDEX_LOCK),
                                                self.config,
                                                "the registry index"));
        let path = lock.path().parent().unwrap();
        let contents = try!(paths::read(&path.join("config.json")));
        let config = try!(json::decode(&contents));
        Ok(Some(config))
    }

    fn update_index(&mut self) -> CargoResult<()> {
        // Ensure that we'll actually be able to acquire an HTTP handle later on
        // once we start trying to download crates. This will weed out any
        // problems with `.cargo/config` configuration related to HTTP.
        //
        // This way if there's a problem the error gets printed before we even
        // hit the index, which may not actually read this configuration.
        try!(ops::http_handle(self.config));

        // Then we actually update the index
        try!(self.index_path.create_dir());
        let lock = try!(self.index_path.open_rw(Path::new(INDEX_LOCK),
                                                self.config,
                                                "the registry index"));
        let path = lock.path().parent().unwrap();

        try!(self.config.shell().status("Updating",
             format!("registry `{}`", self.source_id.url())));

        let repo = try!(git2::Repository::open(path).or_else(|_| {
            let _ = lock.remove_siblings();
            git2::Repository::init(path)
        }));

        if self.source_id.url().host_str() == Some("github.com") {
            if let Ok(oid) = repo.refname_to_id("refs/heads/master") {
                let handle = match self.handle {
                    Some(ref mut handle) => handle,
                    None => {
                        self.handle = Some(try!(ops::http_handle(self.config)));
                        self.handle.as_mut().unwrap()
                    }
                };
                debug!("attempting github fast path for {}",
                       self.source_id.url());
                if github_up_to_date(handle, &self.source_id.url(), &oid) {
                    return Ok(())
                }
                debug!("fast path failed, falling back to a git fetch");
            }
        }

        // git fetch origin
        let url = self.source_id.url().to_string();
        let refspec = "refs/heads/*:refs/remotes/origin/*";

        try!(git::fetch(&repo, &url, refspec, &self.config).chain_error(|| {
            human(format!("failed to fetch `{}`", url))
        }));

        // git reset --hard origin/master
        let reference = "refs/remotes/origin/master";
        let oid = try!(repo.refname_to_id(reference));
        trace!("[{}] updating to rev {}", self.source_id, oid);
        let object = try!(repo.find_object(oid, None));
        try!(repo.reset(&object, git2::ResetType::Hard, None));
        Ok(())
    }

    fn download(&mut self, pkg: &PackageId, checksum: &str)
                -> CargoResult<FileLock> {
        let filename = format!("{}-{}.crate", pkg.name(), pkg.version());
        let path = Path::new(&filename);
        let mut dst = try!(self.cache_path.open_rw(path, self.config, &filename));
        let meta = try!(dst.file().metadata());
        if meta.len() > 0 {
            return Ok(dst)
        }
        try!(self.config.shell().status("Downloading", pkg));

        let config = try!(self.config()).unwrap();
        let mut url = try!(config.dl.to_url());
        url.path_segments_mut().unwrap()
            .push(pkg.name())
            .push(&pkg.version().to_string())
            .push("download");

        let handle = match self.handle {
            Some(ref mut handle) => handle,
            None => {
                self.handle = Some(try!(ops::http_handle(self.config)));
                self.handle.as_mut().unwrap()
            }
        };
        // TODO: don't download into memory, but ensure that if we ctrl-c a
        //       download we should resume either from the start or the middle
        //       on the next time
        try!(handle.get(true));
        try!(handle.url(&url.to_string()));
        try!(handle.follow_location(true));
        let mut state = Sha256::new();
        let mut body = Vec::new();
        {
            let mut handle = handle.transfer();
            try!(handle.write_function(|buf| {
                state.update(buf);
                body.extend_from_slice(buf);
                Ok(buf.len())
            }));
            try!(network::with_retry(self.config, || {
                handle.perform()
            }))
        }
        let code = try!(handle.response_code());
        if code != 200 && code != 0 {
            bail!("failed to get 200 response from `{}`, got {}", url, code)
        }

        // Verify what we just downloaded
        if state.finish().to_hex() != checksum {
            bail!("failed to verify the checksum of `{}`", pkg)
        }

        try!(dst.write_all(&body));
        try!(dst.seek(SeekFrom::Start(0)));
        Ok(dst)
    }
}

/// Updating the index is done pretty regularly so we want it to be as fast as
/// possible. For registries hosted on github (like the crates.io index) there's
/// a fast path available to use [1] to tell us that there's no updates to be
/// made.
///
/// This function will attempt to hit that fast path and verify that the `oid`
/// is actually the current `master` branch of the repository. If `true` is
/// returned then no update needs to be performed, but if `false` is returned
/// then the standard update logic still needs to happen.
///
/// [1]: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
///
/// Note that this function should never cause an actual failure because it's
/// just a fast path. As a result all errors are ignored in this function and we
/// just return a `bool`. Any real errors will be reported through the normal
/// update path above.
fn github_up_to_date(handle: &mut Easy, url: &Url, oid: &git2::Oid) -> bool {
    macro_rules! try {
        ($e:expr) => (match $e {
            Some(e) => e,
            None => return false,
        })
    }

    // This expects github urls in the form `github.com/user/repo` and nothing
    // else
    let mut pieces = try!(url.path_segments());
    let username = try!(pieces.next());
    let repo = try!(pieces.next());
    if pieces.next().is_some() {
        return false
    }

    let url = format!("https://api.github.com/repos/{}/{}/commits/master",
                      username, repo);
    try!(handle.get(true).ok());
    try!(handle.url(&url).ok());
    try!(handle.useragent("cargo").ok());
    let mut headers = List::new();
    try!(headers.append("Accept: application/vnd.github.3.sha").ok());
    try!(headers.append(&format!("If-None-Match: \"{}\"", oid)).ok());
    try!(handle.http_headers(headers).ok());
    try!(handle.perform().ok());

    try!(handle.response_code().ok()) == 304
}
<|endoftext|>


<commit_before>// =================================================================
//
//                           * WARNING *
//
//                    This file is generated!
//
//  Changes made to this file will be overwritten. If changes are
//  required to the generated code, the service_crategen project
//  must be updated to generate the changes.
//
// =================================================================

use std::error::Error;
use std::fmt;
use std::io;

#[allow(warnings)]
use futures::future;
use futures::Future;
use rusoto_core::region;
use rusoto_core::request::{BufferedHttpResponse, DispatchSignedRequest};
use rusoto_core::{Client, RusotoFuture};

use rusoto_core::credential::{CredentialsError, ProvideAwsCredentials};
use rusoto_core::request::HttpDispatchError;

use rusoto_core::signature::SignedRequest;
use serde_json;
use serde_json::from_slice;
use serde_json::Value as SerdeJsonValue;
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct InvokeEndpointInput {
    ///
The desired MIME type of the inference in the response.

    #[serde(rename = "Accept")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub accept: Option<String>,
    ///
Provides input data, in the format specified in the ContentType request header. Amazon SageMaker passes all of the data in the body to the model.

    #[serde(rename = "Body")]
    #[serde(
        deserialize_with = "::rusoto_core::serialization::SerdeBlob::deserialize_blob",
        serialize_with = "::rusoto_core::serialization::SerdeBlob::serialize_blob",
        default
    )]
    pub body: Vec<u8>,
    ///
The MIME type of the input data in the request body.

    #[serde(rename = "ContentType")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_type: Option<String>,
    ///
The name of the endpoint that you specified when you created the endpoint using the CreateEndpoint API.

    #[serde(rename = "EndpointName")]
    pub endpoint_name: String,
}

#[derive(Default, Debug, Clone, PartialEq)]
pub struct InvokeEndpointOutput {
    ///
Includes the inference provided by the model.

    pub body: Vec<u8>,
    ///
The MIME type of the inference returned in the response body.

    pub content_type: Option<String>,
    ///
Identifies the production variant that was invoked.

    pub invoked_production_variant: Option<String>,
}

/// Errors returned by InvokeEndpoint
#[derive(Debug, PartialEq)]
pub enum InvokeEndpointError {
    ///
 Internal failure occurred.

    InternalFailure(String),
    ///
 Model (owned by the customer in the container) returned an error 500.

    ModelError(String),
    ///
 Service is unavailable. Try your call again.

    ServiceUnavailable(String),
    ///
 Inspect your request and try again.

    ValidationError(String),
    /// An error occurred dispatching the HTTP request
    HttpDispatch(HttpDispatchError),
    /// An error was encountered with AWS credentials.
    Credentials(CredentialsError),
    /// A validation error occurred.  Details from AWS are provided.
    Validation(String),
    /// An error occurred parsing the response payload.
    ParseError(String),
    /// An unknown error occurred.  The raw HTTP response is provided.
    Unknown(BufferedHttpResponse),
}

impl InvokeEndpointError {
    // see boto RestJSONParser impl for parsing errors
    // https://github.com/boto/botocore/blob/4dff78c840403d1d17db9b3f800b20d3bd9fbf9f/botocore/parsers.py#L838-L850
    pub fn from_response(res: BufferedHttpResponse) -> InvokeEndpointError {
        if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
            let error_type = match res.headers.get("x-amzn-errortype") {
                Some(raw_error_type) => raw_error_type
                    .split(':')
                    .next()
                    .unwrap_or_else(|| "Unknown"),
                _ => json
                    .get("code")
                    .or_else(|| json.get("Code"))
                    .and_then(|c| c.as_str())
                    .unwrap_or_else(|| "Unknown"),
            };

            // message can come in either "message" or "Message"
            // see boto BaseJSONParser impl for parsing message
            // https://github.com/boto/botocore/blob/4dff78c840403d1d17db9b3f800b20d3bd9fbf9f/botocore/parsers.py#L595-L598
            let error_message = json
                .get("message")
                .or_else(|| json.get("Message"))
                .and_then(|m| m.as_str())
                .unwrap_or("");

            match error_type {
                "InternalFailure" => {
                    return InvokeEndpointError::InternalFailure(String::from(error_message))
                }
                "ModelError" => return InvokeEndpointError::ModelError(String::from(error_message)),
                "ServiceUnavailable" => {
                    return InvokeEndpointError::ServiceUnavailable(String::from(error_message))
                }
                "ValidationError" => {
                    return InvokeEndpointError::ValidationError(String::from(error_message))
                }
                "ValidationException" => {
                    return InvokeEndpointError::Validation(error_message.to_string())
                }
                _ => {}
            }
        }
        return InvokeEndpointError::Unknown(res);
    }
}

impl From<serde_json::error::Error> for InvokeEndpointError {
    fn from(err: serde_json::error::Error) -> InvokeEndpointError {
        InvokeEndpointError::ParseError(err.description().to_string())
    }
}
impl From<CredentialsError> for InvokeEndpointError {
    fn from(err: CredentialsError) -> InvokeEndpointError {
        InvokeEndpointError::Credentials(err)
    }
}
impl From<HttpDispatchError> for InvokeEndpointError {
    fn from(err: HttpDispatchError) -> InvokeEndpointError {
        InvokeEndpointError::HttpDispatch(err)
    }
}
impl From<io::Error> for InvokeEndpointError {
    fn from(err: io::Error) -> InvokeEndpointError {
        InvokeEndpointError::HttpDispatch(HttpDispatchError::from(err))
    }
}
impl fmt::Display for InvokeEndpointError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}
impl Error for InvokeEndpointError {
    fn description(&self) -> &str {
        match *self {
            InvokeEndpointError::InternalFailure(ref cause) => cause,
            InvokeEndpointError::ModelError(ref cause) => cause,
            InvokeEndpointError::ServiceUnavailable(ref cause) => cause,
            InvokeEndpointError::ValidationError(ref cause) => cause,
            InvokeEndpointError::Validation(ref cause) => cause,
            InvokeEndpointError::Credentials(ref err) => err.description(),
            InvokeEndpointError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
            InvokeEndpointError::ParseError(ref cause) => cause,
            InvokeEndpointError::Unknown(_) => "unknown error",
        }
    }
}
/// Trait representing the capabilities of the Amazon SageMaker Runtime API. Amazon SageMaker Runtime clients implement this trait.
pub trait SageMakerRuntime {
    ///
After you deploy a model into production using Amazon SageMaker hosting services, your client applications use this API to get inferences from the model hosted at the specified endpoint.

For an overview of Amazon SageMaker, see How It Works

 Amazon SageMaker strips all POST headers except those supported by the API. Amazon SageMaker might add additional headers. You should not rely on the behavior of headers outside those enumerated in the request syntax.

    fn invoke_endpoint(
        &self,
        input: InvokeEndpointInput,
    ) -> RusotoFuture<InvokeEndpointOutput, InvokeEndpointError>;
}
/// A client for the Amazon SageMaker Runtime API.
pub struct SageMakerRuntimeClient {
    client: Client,
    region: region::Region,
}

impl SageMakerRuntimeClient {
    /// Creates a client backed by the default tokio event loop.
    ///
    /// The client will use the default credentials provider and tls client.
    pub fn new(region: region::Region) -> SageMakerRuntimeClient {
        SageMakerRuntimeClient {
            client: Client::shared(),
            region: region,
        }
    }

    pub fn new_with
(
        request_dispatcher: D,
        credentials_provider: P,
        region: region::Region,
    ) -> SageMakerRuntimeClient
    where
        P: ProvideAwsCredentials + Send + Sync + 'static,
        P::Future: Send,
        D: DispatchSignedRequest + Send + Sync + 'static,
        D::Future: Send,
    {
        SageMakerRuntimeClient {
            client: Client::new_with(credentials_provider, request_dispatcher),
            region: region,
        }
    }
}

impl SageMakerRuntime for SageMakerRuntimeClient {
    ///
After you deploy a model into production using Amazon SageMaker hosting services, your client applications use this API to get inferences from the model hosted at the specified endpoint.

For an overview of Amazon SageMaker, see How It Works

 Amazon SageMaker strips all POST headers except those supported by the API. Amazon SageMaker might add additional headers. You should not rely on the behavior of headers outside those enumerated in the request syntax.

    fn invoke_endpoint(
        &self,
        input: InvokeEndpointInput,
    ) -> RusotoFuture<InvokeEndpointOutput, InvokeEndpointError> {
        let request_uri = format!(
            "/endpoints/{endpoint_name}/invocations",
            endpoint_name = input.endpoint_name
        );

        let mut request = SignedRequest::new("POST", "sagemaker", &self.region, &request_uri);
        request.set_content_type("application/x-amz-json-1.1".to_owned());

        request.set_endpoint_prefix("runtime.sagemaker".to_string());
        let encoded = Some(input.body.to_owned());
        request.set_payload(encoded);

        if let Some(ref accept) = input.accept {
            request.add_header("Accept", &accept.to_string());
        }

        if let Some(ref content_type) = input.content_type {
            request.add_header("Content-Type", &content_type.to_string());
        }

        self.client.sign_and_dispatch(request, |response| {
            if response.status.is_success() {
                Box::new(response.buffer().from_err().map(|response| {
                    let mut result = InvokeEndpointOutput::default();
                    result.body = response.body;

                    if let Some(content_type) = response.headers.get("Content-Type") {
                        let value = content_type.to_owned();
                        result.content_type = Some(value)
                    };
                    if let Some(invoked_production_variant) =
                        response.headers.get("x-Amzn-Invoked-Production-Variant")
                    {
                        let value = invoked_production_variant.to_owned();
                        result.invoked_production_variant = Some(value)
                    };

                    result
                }))
            } else {
                Box::new(
                    response
                        .buffer()
                        .from_err()
                        .and_then(|response| Err(InvokeEndpointError::from_response(response))),
                )
            }
        })
    }
}

#[cfg(test)]
mod protocol_tests {}
<commit_msg>Catch services that needed regeneration.<commit_after>// =================================================================
//
//                           * WARNING *
//
//                    This file is generated!
//
//  Changes made to this file will be overwritten. If changes are
//  required to the generated code, the service_crategen project
//  must be updated to generate the changes.
//
// =================================================================

use std::error::Error;
use std::fmt;
use std::io;

#[allow(warnings)]
use futures::future;
use futures::Future;
use rusoto_core::region;
use rusoto_core::request::{BufferedHttpResponse, DispatchSignedRequest};
use rusoto_core::{Client, RusotoFuture};

use rusoto_core::credential::{CredentialsError, ProvideAwsCredentials};
use rusoto_core::request::HttpDispatchError;

use rusoto_core::signature::SignedRequest;
use serde_json;
use serde_json::from_slice;
use serde_json::Value as SerdeJsonValue;
#[derive(Default, Debug, Clone, PartialEq, Serialize)]
pub struct InvokeEndpointInput {
    ///
The desired MIME type of the inference in the response.

    #[serde(rename = "Accept")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub accept: Option<String>,
    ///
Provides input data, in the format specified in the ContentType request header. Amazon SageMaker passes all of the data in the body to the model.

    #[serde(rename = "Body")]
    #[serde(
        deserialize_with = "::rusoto_core::serialization::SerdeBlob::deserialize_blob",
        serialize_with = "::rusoto_core::serialization::SerdeBlob::serialize_blob",
        default,
    )]
    pub body: Vec<u8>,
    ///
The MIME type of the input data in the request body.

    #[serde(rename = "ContentType")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content_type: Option<String>,
    ///
The name of the endpoint that you specified when you created the endpoint using the CreateEndpoint API.

    #[serde(rename = "EndpointName")]
    pub endpoint_name: String,
}

#[derive(Default, Debug, Clone, PartialEq)]
pub struct InvokeEndpointOutput {
    ///
Includes the inference provided by the model.

    pub body: Vec<u8>,
    ///
The MIME type of the inference returned in the response body.

    pub content_type: Option<String>,
    ///
Identifies the production variant that was invoked.

    pub invoked_production_variant: Option<String>,
}

/// Errors returned by InvokeEndpoint
#[derive(Debug, PartialEq)]
pub enum InvokeEndpointError {
    ///
 Internal failure occurred.

    InternalFailure(String),
    ///
 Model (owned by the customer in the container) returned an error 500.

    ModelError(String),
    ///
 Service is unavailable. Try your call again.

    ServiceUnavailable(String),
    ///
 Inspect your request and try again.

    ValidationError(String),
    /// An error occurred dispatching the HTTP request
    HttpDispatch(HttpDispatchError),
    /// An error was encountered with AWS credentials.
    Credentials(CredentialsError),
    /// A validation error occurred.  Details from AWS are provided.
    Validation(String),
    /// An error occurred parsing the response payload.
    ParseError(String),
    /// An unknown error occurred.  The raw HTTP response is provided.
    Unknown(BufferedHttpResponse),
}

impl InvokeEndpointError {
    // see boto RestJSONParser impl for parsing errors
    // https://github.com/boto/botocore/blob/4dff78c840403d1d17db9b3f800b20d3bd9fbf9f/botocore/parsers.py#L838-L850
    pub fn from_response(res: BufferedHttpResponse) -> InvokeEndpointError {
        if let Ok(json) = from_slice::<SerdeJsonValue>(&res.body) {
            let error_type = match res.headers.get("x-amzn-errortype") {
                Some(raw_error_type) => raw_error_type
                    .split(':')
                    .next()
                    .unwrap_or_else(|| "Unknown"),
                _ => json
                    .get("code")
                    .or_else(|| json.get("Code"))
                    .and_then(|c| c.as_str())
                    .unwrap_or_else(|| "Unknown"),
            };

            // message can come in either "message" or "Message"
            // see boto BaseJSONParser impl for parsing message
            // https://github.com/boto/botocore/blob/4dff78c840403d1d17db9b3f800b20d3bd9fbf9f/botocore/parsers.py#L595-L598
            let error_message = json
                .get("message")
                .or_else(|| json.get("Message"))
                .and_then(|m| m.as_str())
                .unwrap_or("");

            match error_type {
                "InternalFailure" => {
                    return InvokeEndpointError::InternalFailure(String::from(error_message))
                }
                "ModelError" => return InvokeEndpointError::ModelError(String::from(error_message)),
                "ServiceUnavailable" => {
                    return InvokeEndpointError::ServiceUnavailable(String::from(error_message))
                }
                "ValidationError" => {
                    return InvokeEndpointError::ValidationError(String::from(error_message))
                }
                "ValidationException" => {
                    return InvokeEndpointError::Validation(error_message.to_string())
                }
                _ => {}
            }
        }
        return InvokeEndpointError::Unknown(res);
    }
}

impl From<serde_json::error::Error> for InvokeEndpointError {
    fn from(err: serde_json::error::Error) -> InvokeEndpointError {
        InvokeEndpointError::ParseError(err.description().to_string())
    }
}
impl From<CredentialsError> for InvokeEndpointError {
    fn from(err: CredentialsError) -> InvokeEndpointError {
        InvokeEndpointError::Credentials(err)
    }
}
impl From<HttpDispatchError> for InvokeEndpointError {
    fn from(err: HttpDispatchError) -> InvokeEndpointError {
        InvokeEndpointError::HttpDispatch(err)
    }
}
impl From<io::Error> for InvokeEndpointError {
    fn from(err: io::Error) -> InvokeEndpointError {
        InvokeEndpointError::HttpDispatch(HttpDispatchError::from(err))
    }
}
impl fmt::Display for InvokeEndpointError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}
impl Error for InvokeEndpointError {
    fn description(&self) -> &str {
        match *self {
            InvokeEndpointError::InternalFailure(ref cause) => cause,
            InvokeEndpointError::ModelError(ref cause) => cause,
            InvokeEndpointError::ServiceUnavailable(ref cause) => cause,
            InvokeEndpointError::ValidationError(ref cause) => cause,
            InvokeEndpointError::Validation(ref cause) => cause,
            InvokeEndpointError::Credentials(ref err) => err.description(),
            InvokeEndpointError::HttpDispatch(ref dispatch_error) => dispatch_error.description(),
            InvokeEndpointError::ParseError(ref cause) => cause,
            InvokeEndpointError::Unknown(_) => "unknown error",
        }
    }
}
/// Trait representing the capabilities of the Amazon SageMaker Runtime API. Amazon SageMaker Runtime clients implement this trait.
pub trait SageMakerRuntime {
    ///
After you deploy a model into production using Amazon SageMaker hosting services, your client applications use this API to get inferences from the model hosted at the specified endpoint.

For an overview of Amazon SageMaker, see How It Works

 Amazon SageMaker strips all POST headers except those supported by the API. Amazon SageMaker might add additional headers. You should not rely on the behavior of headers outside those enumerated in the request syntax.

    fn invoke_endpoint(
        &self,
        input: InvokeEndpointInput,
    ) -> RusotoFuture<InvokeEndpointOutput, InvokeEndpointError>;
}
/// A client for the Amazon SageMaker Runtime API.
pub struct SageMakerRuntimeClient {
    client: Client,
    region: region::Region,
}

impl SageMakerRuntimeClient {
    /// Creates a client backed by the default tokio event loop.
    ///
    /// The client will use the default credentials provider and tls client.
    pub fn new(region: region::Region) -> SageMakerRuntimeClient {
        SageMakerRuntimeClient {
            client: Client::shared(),
            region: region,
        }
    }

    pub fn new_with
(
        request_dispatcher: D,
        credentials_provider: P,
        region: region::Region,
    ) -> SageMakerRuntimeClient
    where
        P: ProvideAwsCredentials + Send + Sync + 'static,
        P::Future: Send,
        D: DispatchSignedRequest + Send + Sync + 'static,
        D::Future: Send,
    {
        SageMakerRuntimeClient {
            client: Client::new_with(credentials_provider, request_dispatcher),
            region: region,
        }
    }
}

impl SageMakerRuntime for SageMakerRuntimeClient {
    ///
After you deploy a model into production using Amazon SageMaker hosting services, your client applications use this API to get inferences from the model hosted at the specified endpoint.

For an overview of Amazon SageMaker, see How It Works

 Amazon SageMaker strips all POST headers except those supported by the API. Amazon SageMaker might add additional headers. You should not rely on the behavior of headers outside those enumerated in the request syntax.

    fn invoke_endpoint(
        &self,
        input: InvokeEndpointInput,
    ) -> RusotoFuture<InvokeEndpointOutput, InvokeEndpointError> {
        let request_uri = format!(
            "/endpoints/{endpoint_name}/invocations",
            endpoint_name = input.endpoint_name
        );

        let mut request = SignedRequest::new("POST", "sagemaker", &self.region, &request_uri);
        request.set_content_type("application/x-amz-json-1.1".to_owned());

        request.set_endpoint_prefix("runtime.sagemaker".to_string());
        let encoded = Some(input.body.to_owned());
        request.set_payload(encoded);

        if let Some(ref accept) = input.accept {
            request.add_header("Accept", &accept.to_string());
        }

        if let Some(ref content_type) = input.content_type {
            request.add_header("Content-Type", &content_type.to_string());
        }

        self.client.sign_and_dispatch(request, |response| {
            if response.status.is_success() {
                Box::new(response.buffer().from_err().map(|response| {
                    let mut result = InvokeEndpointOutput::default();
                    result.body = response.body;

                    if let Some(content_type) = response.headers.get("Content-Type") {
                        let value = content_type.to_owned();
                        result.content_type = Some(value)
                    };
                    if let Some(invoked_production_variant) =
                        response.headers.get("x-Amzn-Invoked-Production-Variant")
                    {
                        let value = invoked_production_variant.to_owned();
                        result.invoked_production_variant = Some(value)
                    };

                    result
                }))
            } else {
                Box::new(
                    response
                        .buffer()
                        .from_err()
                        .and_then(|response| Err(InvokeEndpointError::from_response(response))),
                )
            }
        })
    }
}

#[cfg(test)]
mod protocol_tests {}
<|endoftext|>


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

//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
use std::path::Path;

pub use self::imp::Lock;

#[cfg(unix)]
mod imp {
    use std::ffi::{CString, OsStr};
    use std::os::unix::prelude::*;
    use std::path::Path;
    use std::io;
    use libc;

    #[cfg(target_os = "linux")]
    mod os {
        use libc;

        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 0;
        pub const F_WRLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_SETLK: libc::c_int = 6;
        pub const F_SETLKW: libc::c_int = 7;
    }

    #[cfg(target_os = "freebsd")]
    mod os {
        use libc;

        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 12;
        pub const F_SETLKW: libc::c_int = 13;
    }

    #[cfg(any(target_os = "dragonfly",
              target_os = "bitrig",
              target_os = "netbsd",
              target_os = "openbsd"))]
    mod os {
        use libc;

        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 8;
        pub const F_SETLKW: libc::c_int = 9;
    }

    #[cfg(target_os = "haiku")]
    mod os {
        use libc;

        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_UNLCK: libc::c_short = 0x0200;
        pub const F_WRLCK: libc::c_short = 0x0400;
        pub const F_SETLK: libc::c_int = 0x0080;
        pub const F_SETLKW: libc::c_int = 0x0100;
    }

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    mod os {
        use libc;

        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 8;
        pub const F_SETLKW: libc::c_int = 9;
    }

    #[cfg(target_os = "solaris")]
    mod os {
        use libc;

        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_sysid: libc::c_int,
            pub l_pid: libc::pid_t,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_WRLCK: libc::c_short = 2;
        pub const F_UNLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 6;
        pub const F_SETLKW: libc::c_int = 7;
    }

    #[derive(Debug)]
    pub struct Lock {
        fd: libc::c_int,
    }

    impl Lock {
        pub fn new(p: &Path,
                   wait: bool,
                   create: bool,
                   exclusive: bool)
                   -> io::Result<Lock> {
            let os: &OsStr = p.as_ref();
            let buf = CString::new(os.as_bytes()).unwrap();
            let open_flags = if create {
                libc::O_RDWR | libc::O_CREAT
            } else {
                libc::O_RDWR
            };

            let fd = unsafe {
                libc::open(buf.as_ptr(), open_flags,
                           libc::S_IRWXU as libc::c_int)
            };

            if fd < 0 {
                return Err(io::Error::last_os_error());
            }

            let lock_type = if exclusive {
                os::F_WRLCK
            } else {
                os::F_RDLCK
            };

            let flock = os::flock {
                l_start: 0,
                l_len: 0,
                l_pid: 0,
                l_whence: libc::SEEK_SET as libc::c_short,
                l_type: lock_type,
                l_sysid: 0,
            };
            let cmd = if wait { os::F_SETLKW } else { os::F_SETLK };
            let ret = unsafe {
                libc::fcntl(fd, cmd, &flock)
            };
            if ret == -1 {
                let err = io::Error::last_os_error();
                unsafe { libc::close(fd); }
                Err(err)
            } else {
                Ok(Lock { fd: fd })
            }
        }
    }

    impl Drop for Lock {
        fn drop(&mut self) {
            let flock = os::flock {
                l_start: 0,
                l_len: 0,
                l_pid: 0,
                l_whence: libc::SEEK_SET as libc::c_short,
                l_type: os::F_UNLCK,
                l_sysid: 0,
            };
            unsafe {
                libc::fcntl(self.fd, os::F_SETLK, &flock);
                libc::close(self.fd);
            }
        }
    }
}

#[cfg(windows)]
#[allow(bad_style)]
mod imp {
    use std::io;
    use std::mem;
    use std::os::windows::prelude::*;
    use std::os::windows::raw::HANDLE;
    use std::path::Path;
    use std::fs::{File, OpenOptions};
    use std::os::raw::{c_ulong, c_ulonglong, c_int};

    type DWORD = c_ulong;
    type BOOL = c_int;
    type ULONG_PTR = c_ulonglong;

    type LPOVERLAPPED = *mut OVERLAPPED;
    const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x00000002;
    const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x00000001;

    const FILE_SHARE_DELETE: DWORD = 0x4;
    const FILE_SHARE_READ: DWORD = 0x1;
    const FILE_SHARE_WRITE: DWORD = 0x2;

    #[repr(C)]
    struct OVERLAPPED {
        Internal: ULONG_PTR,
        InternalHigh: ULONG_PTR,
        Offset: DWORD,
        OffsetHigh: DWORD,
        hEvent: HANDLE,
    }

    extern "system" {
        fn LockFileEx(hFile: HANDLE,
                      dwFlags: DWORD,
                      dwReserved: DWORD,
                      nNumberOfBytesToLockLow: DWORD,
                      nNumberOfBytesToLockHigh: DWORD,
                      lpOverlapped: LPOVERLAPPED) -> BOOL;
    }

    #[derive(Debug)]
    pub struct Lock {
        _file: File,
    }

    impl Lock {
        pub fn new(p: &Path,
                   wait: bool,
                   create: bool,
                   exclusive: bool)
                   -> io::Result<Lock> {
            assert!(p.parent().unwrap().exists(),
                "Parent directory of lock-file must exist: {}",
                p.display());

            let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

            let mut open_options = OpenOptions::new();
            open_options.read(true)
                        .share_mode(share_mode);

            if create {
                open_options.create(true)
                            .write(true);
            }

            debug!("Attempting to open lock file `{}`", p.display());
            let file = match open_options.open(p) {
                Ok(file) => {
                    debug!("Lock file opened successfully");
                    file
                }
                Err(err) => {
                    debug!("Error opening lock file: {}", err);
                    return Err(err)
                }
            };

            let ret = unsafe {
                let mut overlapped: OVERLAPPED = mem::zeroed();

                let mut dwFlags = 0;
                if !wait {
                    dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                }

                if exclusive {
                    dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                }

                debug!("Attempting to acquire lock on lock file `{}`",
                       p.display());
                LockFileEx(file.as_raw_handle(),
                           dwFlags,
                           0,
                           0xFFFF_FFFF,
                           0xFFFF_FFFF,
                           &mut overlapped)
            };
            if ret == 0 {
                let err = io::Error::last_os_error();
                debug!("Failed acquiring file lock: {}", err);
                Err(err)
            } else {
                debug!("Successfully acquired lock.");
                Ok(Lock { _file: file })
            }
        }
    }

    // Note that we don't need a Drop impl on the Windows: The file is unlocked
    // automatically when it's closed.
}

impl imp::Lock {
    pub fn panicking_new(p: &Path,
                         wait: bool,
                         create: bool,
                         exclusive: bool)
                         -> Lock {
        Lock::new(p, wait, create, exclusive).unwrap_or_else(|err| {
            panic!("could not lock `{}`: {}", p.display(), err);
        })
    }
}
<commit_msg>flock needs repr(C)<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Simple file-locking apis for each OS.
//!
//! This is not meant to be in the standard library, it does nothing with
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.

#![allow(non_camel_case_types)]
use std::path::Path;

pub use self::imp::Lock;

#[cfg(unix)]
mod imp {
    use std::ffi::{CString, OsStr};
    use std::os::unix::prelude::*;
    use std::path::Path;
    use std::io;
    use libc;

    #[cfg(target_os = "linux")]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 0;
        pub const F_WRLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_SETLK: libc::c_int = 6;
        pub const F_SETLKW: libc::c_int = 7;
    }

    #[cfg(target_os = "freebsd")]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 12;
        pub const F_SETLKW: libc::c_int = 13;
    }

    #[cfg(any(target_os = "dragonfly",
              target_os = "bitrig",
              target_os = "netbsd",
              target_os = "openbsd"))]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 8;
        pub const F_SETLKW: libc::c_int = 9;
    }

    #[cfg(target_os = "haiku")]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_UNLCK: libc::c_short = 0x0200;
        pub const F_WRLCK: libc::c_short = 0x0400;
        pub const F_SETLK: libc::c_int = 0x0080;
        pub const F_SETLKW: libc::c_int = 0x0100;
    }

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_pid: libc::pid_t,
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,

            // not actually here, but brings in line with freebsd
            pub l_sysid: libc::c_int,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_UNLCK: libc::c_short = 2;
        pub const F_WRLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 8;
        pub const F_SETLKW: libc::c_int = 9;
    }

    #[cfg(target_os = "solaris")]
    mod os {
        use libc;

        #[repr(C)]
        pub struct flock {
            pub l_type: libc::c_short,
            pub l_whence: libc::c_short,
            pub l_start: libc::off_t,
            pub l_len: libc::off_t,
            pub l_sysid: libc::c_int,
            pub l_pid: libc::pid_t,
        }

        pub const F_RDLCK: libc::c_short = 1;
        pub const F_WRLCK: libc::c_short = 2;
        pub const F_UNLCK: libc::c_short = 3;
        pub const F_SETLK: libc::c_int = 6;
        pub const F_SETLKW: libc::c_int = 7;
    }

    #[derive(Debug)]
    pub struct Lock {
        fd: libc::c_int,
    }

    impl Lock {
        pub fn new(p: &Path,
                   wait: bool,
                   create: bool,
                   exclusive: bool)
                   -> io::Result<Lock> {
            let os: &OsStr = p.as_ref();
            let buf = CString::new(os.as_bytes()).unwrap();
            let open_flags = if create {
                libc::O_RDWR | libc::O_CREAT
            } else {
                libc::O_RDWR
            };

            let fd = unsafe {
                libc::open(buf.as_ptr(), open_flags,
                           libc::S_IRWXU as libc::c_int)
            };

            if fd < 0 {
                return Err(io::Error::last_os_error());
            }

            let lock_type = if exclusive {
                os::F_WRLCK
            } else {
                os::F_RDLCK
            };

            let flock = os::flock {
                l_start: 0,
                l_len: 0,
                l_pid: 0,
                l_whence: libc::SEEK_SET as libc::c_short,
                l_type: lock_type,
                l_sysid: 0,
            };
            let cmd = if wait { os::F_SETLKW } else { os::F_SETLK };
            let ret = unsafe {
                libc::fcntl(fd, cmd, &flock)
            };
            if ret == -1 {
                let err = io::Error::last_os_error();
                unsafe { libc::close(fd); }
                Err(err)
            } else {
                Ok(Lock { fd: fd })
            }
        }
    }

    impl Drop for Lock {
        fn drop(&mut self) {
            let flock = os::flock {
                l_start: 0,
                l_len: 0,
                l_pid: 0,
                l_whence: libc::SEEK_SET as libc::c_short,
                l_type: os::F_UNLCK,
                l_sysid: 0,
            };
            unsafe {
                libc::fcntl(self.fd, os::F_SETLK, &flock);
                libc::close(self.fd);
            }
        }
    }
}

#[cfg(windows)]
#[allow(bad_style)]
mod imp {
    use std::io;
    use std::mem;
    use std::os::windows::prelude::*;
    use std::os::windows::raw::HANDLE;
    use std::path::Path;
    use std::fs::{File, OpenOptions};
    use std::os::raw::{c_ulong, c_ulonglong, c_int};

    type DWORD = c_ulong;
    type BOOL = c_int;
    type ULONG_PTR = c_ulonglong;

    type LPOVERLAPPED = *mut OVERLAPPED;
    const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x00000002;
    const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x00000001;

    const FILE_SHARE_DELETE: DWORD = 0x4;
    const FILE_SHARE_READ: DWORD = 0x1;
    const FILE_SHARE_WRITE: DWORD = 0x2;

    #[repr(C)]
    struct OVERLAPPED {
        Internal: ULONG_PTR,
        InternalHigh: ULONG_PTR,
        Offset: DWORD,
        OffsetHigh: DWORD,
        hEvent: HANDLE,
    }

    extern "system" {
        fn LockFileEx(hFile: HANDLE,
                      dwFlags: DWORD,
                      dwReserved: DWORD,
                      nNumberOfBytesToLockLow: DWORD,
                      nNumberOfBytesToLockHigh: DWORD,
                      lpOverlapped: LPOVERLAPPED) -> BOOL;
    }

    #[derive(Debug)]
    pub struct Lock {
        _file: File,
    }

    impl Lock {
        pub fn new(p: &Path,
                   wait: bool,
                   create: bool,
                   exclusive: bool)
                   -> io::Result<Lock> {
            assert!(p.parent().unwrap().exists(),
                "Parent directory of lock-file must exist: {}",
                p.display());

            let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;

            let mut open_options = OpenOptions::new();
            open_options.read(true)
                        .share_mode(share_mode);

            if create {
                open_options.create(true)
                            .write(true);
            }

            debug!("Attempting to open lock file `{}`", p.display());
            let file = match open_options.open(p) {
                Ok(file) => {
                    debug!("Lock file opened successfully");
                    file
                }
                Err(err) => {
                    debug!("Error opening lock file: {}", err);
                    return Err(err)
                }
            };

            let ret = unsafe {
                let mut overlapped: OVERLAPPED = mem::zeroed();

                let mut dwFlags = 0;
                if !wait {
                    dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
                }

                if exclusive {
                    dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
                }

                debug!("Attempting to acquire lock on lock file `{}`",
                       p.display());
                LockFileEx(file.as_raw_handle(),
                           dwFlags,
                           0,
                           0xFFFF_FFFF,
                           0xFFFF_FFFF,
                           &mut overlapped)
            };
            if ret == 0 {
                let err = io::Error::last_os_error();
                debug!("Failed acquiring file lock: {}", err);
                Err(err)
            } else {
                debug!("Successfully acquired lock.");
                Ok(Lock { _file: file })
            }
        }
    }

    // Note that we don't need a Drop impl on the Windows: The file is unlocked
    // automatically when it's closed.
}

impl imp::Lock {
    pub fn panicking_new(p: &Path,
                         wait: bool,
                         create: bool,
                         exclusive: bool)
                         -> Lock {
        Lock::new(p, wait, create, exclusive).unwrap_or_else(|err| {
            panic!("could not lock `{}`: {}", p.display(), err);
        })
    }
}
<|endoftext|>


<commit_before>//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
//! types until we arrive at the leaves, with custom handling for primitive types.

use std::fmt;

use rustc::ty::layout::{self, TyLayout};
use rustc::ty;
use rustc::mir::interpret::{
    EvalResult,
};

use super::{
    Machine, EvalContext, MPlaceTy, OpTy,
};

// A thing that we can project into, and that has a layout.
// This wouldn't have to depend on `Machine` but with the current type inference,
// that's just more convenient to work with (avoids repeading all the `Machine` bounds).
pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
{
    // Get this value's layout.
    fn layout(&self) -> TyLayout<'tcx>;

    // Get the underlying `MPlaceTy`, or panic if there is no such thing.
    fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag>;

    // Create this from an `MPlaceTy`
    fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;

    // Project to the n-th field
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self>;
}

// Operands and places are both values
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
    for OpTy<'tcx, M::PointerTag>
{
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.layout
    }

    #[inline(always)]
    fn to_mem_place(self) -> MPlaceTy<'tcx, M::PointerTag> {
        self.to_mem_place()
    }

    #[inline(always)]
    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
        mplace.into()
    }

    #[inline(always)]
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self> {
        ectx.operand_field(self, field)
    }
}

// How to traverse a value and what to do when we are at the leaves.
pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
    type V: Value<'a, 'mir, 'tcx, M>;

    // There's a value in here.
    fn value(&self) -> &Self::V;

    // The value's layout (not meant to be overwritten).
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.value().layout()
    }

    // Replace the value by `val`, which must be the `field`th field of `self`,
    // then call `f` and then un-do everything that might have happened to the visitor state.
    // The point of this is that some visitors keep a stack of fields that we projected below,
    // and this lets us avoid copying that stack; instead they will pop the stack after
    // executing `f`.
    fn with_field(
        &mut self,
        val: Self::V,
        field: usize,
        f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
    ) -> EvalResult<'tcx>;

    // This is an enum, downcast it to whatever the current variant is.
    // (We do this here and not in `Value` to keep error handling
    // under control of th visitor.)
    fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;

    // A chance for the visitor to do special (different or more efficient) handling for some
    // array types.  Return `true` if the value was handled and we should return.
    #[inline]
    fn handle_array(&mut self, _ectx: &EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx, bool>
    {
        Ok(false)
    }

    // Actions on the leaves.
    fn visit_uninhabited(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;
    fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
        -> EvalResult<'tcx>;
    fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
    pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(
        &mut self,
        v: &mut V,
    ) -> EvalResult<'tcx> {
        trace!("visit_value: {:?}", v);

        // If this is a multi-variant layout, we have find the right one and proceed with that.
        // (No benefit from making this recursion, but it is equivalent to that.)
        match v.layout().variants {
            layout::Variants::NicheFilling { .. } |
            layout::Variants::Tagged { .. } => {
                v.downcast_enum(self)?;
                trace!("variant layout: {:#?}", v.layout());
            }
            layout::Variants::Single { .. } => {}
        }

        // Even for single variants, we might be able to get a more refined type:
        // If it is a trait object, switch to the actual type that was used to create it.
        match v.layout().ty.sty {
            ty::Dynamic(..) => {
                let dest = v.value().to_mem_place(); // immediate trait objects are not a thing
                let inner = self.unpack_dyn_trait(dest)?.1;
                // recurse with the inner type
                return v.with_field(Value::from_mem_place(inner), 0, |v| self.visit_value(v));
            },
            _ => {},
        };

        // If this is a scalar, visit it as such.
        // Things can be aggregates and have scalar layout at the same time, and that
        // is very relevant for `NonNull` and similar structs: We need to visit them
        // at their scalar layout *before* descending into their fields.
        // FIXME: We could avoid some redundant checks here. For newtypes wrapping
        // scalars, we do the same check on every "level" (e.g. first we check
        // MyNewtype and then the scalar in there).
        match v.layout().abi {
            layout::Abi::Uninhabited => {
                v.visit_uninhabited(self)?;
            }
            layout::Abi::Scalar(ref layout) => {
                v.visit_scalar(self, layout)?;
            }
            // FIXME: Should we do something for ScalarPair? Vector?
            _ => {}
        }

        // Check primitive types.  We do this after checking the scalar layout,
        // just to have that done as well.  Primitives can have varying layout,
        // so we check them separately and before aggregate handling.
        // It is CRITICAL that we get this check right, or we might be
        // validating the wrong thing!
        let primitive = match v.layout().fields {
            // Primitives appear as Union with 0 fields -- except for Boxes and fat pointers.
            layout::FieldPlacement::Union(0) => true,
            _ => v.layout().ty.builtin_deref(true).is_some(),
        };
        if primitive {
            return v.visit_primitive(self);
        }

        // Proceed into the fields.
        match v.layout().fields {
            layout::FieldPlacement::Union(fields) => {
                // Empty unions are not accepted by rustc. That's great, it means we can
                // use that as an unambiguous signal for detecting primitives.  Make sure
                // we did not miss any primitive.
                debug_assert!(fields > 0);
                // We can't traverse unions, their bits are allowed to be anything.
                // The fields don't need to correspond to any bit pattern of the union's fields.
                // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
            },
            layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
                for i in 0..offsets.len() {
                    let val = v.value().project_field(self, i as u64)?;
                    v.with_field(val, i, |v| self.visit_value(v))?;
                }
            },
            layout::FieldPlacement::Array { .. } => {
                if !v.handle_array(self)? {
                    // We still have to work!
                    // Let's get an mplace first.
                    let mplace = if v.layout().is_zst() {
                        // it's a ZST, the memory content cannot matter
                        MPlaceTy::dangling(v.layout(), self)
                    } else {
                        // non-ZST array/slice/str cannot be immediate
                        v.value().to_mem_place()
                    };
                    // Now iterate over it.
                    for (i, field) in self.mplace_array_fields(mplace)?.enumerate() {
                        v.with_field(Value::from_mem_place(field?), i, |v| self.visit_value(v))?;
                    }
                }
            }
        }
        Ok(())
    }
}
<commit_msg>also allow visiting places and mplaces<commit_after>//! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound
//! types until we arrive at the leaves, with custom handling for primitive types.

use std::fmt;

use rustc::ty::layout::{self, TyLayout};
use rustc::ty;
use rustc::mir::interpret::{
    EvalResult,
};

use super::{
    Machine, EvalContext, MPlaceTy, PlaceTy, OpTy,
};

// A thing that we can project into, and that has a layout.
// This wouldn't have to depend on `Machine` but with the current type inference,
// that's just more convenient to work with (avoids repeading all the `Machine` bounds).
pub trait Value<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: Copy
{
    // Get this value's layout.
    fn layout(&self) -> TyLayout<'tcx>;

    // Make this a `MPlaceTy`, or panic if that's not possible.
    fn force_allocation(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>>;

    // Create this from an `MPlaceTy`.
    fn from_mem_place(MPlaceTy<'tcx, M::PointerTag>) -> Self;

    // Project to the n-th field.
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self>;
}

// Operands and places are both values
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
    for OpTy<'tcx, M::PointerTag>
{
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.layout
    }

    #[inline(always)]
    fn force_allocation(
        self,
        _ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
        Ok(self.to_mem_place())
    }

    #[inline(always)]
    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
        mplace.into()
    }

    #[inline(always)]
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self> {
        ectx.operand_field(self, field)
    }
}
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
    for MPlaceTy<'tcx, M::PointerTag>
{
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.layout
    }

    #[inline(always)]
    fn force_allocation(
        self,
        _ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
        Ok(self)
    }

    #[inline(always)]
    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
        mplace
    }

    #[inline(always)]
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self> {
        ectx.mplace_field(self, field)
    }
}
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Value<'a, 'mir, 'tcx, M>
    for PlaceTy<'tcx, M::PointerTag>
{
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.layout
    }

    #[inline(always)]
    fn force_allocation(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
        ectx.force_allocation(self)
    }

    #[inline(always)]
    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
        mplace.into()
    }

    #[inline(always)]
    fn project_field(
        self,
        ectx: &mut EvalContext<'a, 'mir, 'tcx, M>,
        field: u64,
    ) -> EvalResult<'tcx, Self> {
        ectx.place_field(self, field)
    }
}

// How to traverse a value and what to do when we are at the leaves.
pub trait ValueVisitor<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>: fmt::Debug {
    type V: Value<'a, 'mir, 'tcx, M>;

    // There's a value in here.
    fn value(&self) -> &Self::V;

    // The value's layout (not meant to be overwritten).
    #[inline(always)]
    fn layout(&self) -> TyLayout<'tcx> {
        self.value().layout()
    }

    // Replace the value by `val`, which must be the `field`th field of `self`,
    // then call `f` and then un-do everything that might have happened to the visitor state.
    // The point of this is that some visitors keep a stack of fields that we projected below,
    // and this lets us avoid copying that stack; instead they will pop the stack after
    // executing `f`.
    fn with_field(
        &mut self,
        val: Self::V,
        field: usize,
        f: impl FnOnce(&mut Self) -> EvalResult<'tcx>,
    ) -> EvalResult<'tcx>;

    // This is an enum, downcast it to whatever the current variant is.
    // (We do this here and not in `Value` to keep error handling
    // under control of th visitor.)
    fn downcast_enum(&mut self, ectx: &EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;

    // A chance for the visitor to do special (different or more efficient) handling for some
    // array types.  Return `true` if the value was handled and we should return.
    #[inline]
    fn handle_array(&mut self, _ectx: &EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx, bool>
    {
        Ok(false)
    }

    // Actions on the leaves.
    fn visit_uninhabited(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;
    fn visit_scalar(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>, layout: &layout::Scalar)
        -> EvalResult<'tcx>;
    fn visit_primitive(&mut self, ectx: &mut EvalContext<'a, 'mir, 'tcx, M>)
        -> EvalResult<'tcx>;
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
    pub fn visit_value<V: ValueVisitor<'a, 'mir, 'tcx, M>>(
        &mut self,
        v: &mut V,
    ) -> EvalResult<'tcx> {
        trace!("visit_value: {:?}", v);

        // If this is a multi-variant layout, we have find the right one and proceed with that.
        // (No benefit from making this recursion, but it is equivalent to that.)
        match v.layout().variants {
            layout::Variants::NicheFilling { .. } |
            layout::Variants::Tagged { .. } => {
                v.downcast_enum(self)?;
                trace!("variant layout: {:#?}", v.layout());
            }
            layout::Variants::Single { .. } => {}
        }

        // Even for single variants, we might be able to get a more refined type:
        // If it is a trait object, switch to the actual type that was used to create it.
        match v.layout().ty.sty {
            ty::Dynamic(..) => {
                // immediate trait objects are not a thing
                let dest = v.value().force_allocation(self)?;
                let inner = self.unpack_dyn_trait(dest)?.1;
                // recurse with the inner type
                return v.with_field(Value::from_mem_place(inner), 0, |v| self.visit_value(v));
            },
            _ => {},
        };

        // If this is a scalar, visit it as such.
        // Things can be aggregates and have scalar layout at the same time, and that
        // is very relevant for `NonNull` and similar structs: We need to visit them
        // at their scalar layout *before* descending into their fields.
        // FIXME: We could avoid some redundant checks here. For newtypes wrapping
        // scalars, we do the same check on every "level" (e.g. first we check
        // MyNewtype and then the scalar in there).
        match v.layout().abi {
            layout::Abi::Uninhabited => {
                v.visit_uninhabited(self)?;
            }
            layout::Abi::Scalar(ref layout) => {
                v.visit_scalar(self, layout)?;
            }
            // FIXME: Should we do something for ScalarPair? Vector?
            _ => {}
        }

        // Check primitive types.  We do this after checking the scalar layout,
        // just to have that done as well.  Primitives can have varying layout,
        // so we check them separately and before aggregate handling.
        // It is CRITICAL that we get this check right, or we might be
        // validating the wrong thing!
        let primitive = match v.layout().fields {
            // Primitives appear as Union with 0 fields -- except for Boxes and fat pointers.
            layout::FieldPlacement::Union(0) => true,
            _ => v.layout().ty.builtin_deref(true).is_some(),
        };
        if primitive {
            return v.visit_primitive(self);
        }

        // Proceed into the fields.
        match v.layout().fields {
            layout::FieldPlacement::Union(fields) => {
                // Empty unions are not accepted by rustc. That's great, it means we can
                // use that as an unambiguous signal for detecting primitives.  Make sure
                // we did not miss any primitive.
                debug_assert!(fields > 0);
                // We can't traverse unions, their bits are allowed to be anything.
                // The fields don't need to correspond to any bit pattern of the union's fields.
                // See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
            },
            layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
                for i in 0..offsets.len() {
                    let val = v.value().project_field(self, i as u64)?;
                    v.with_field(val, i, |v| self.visit_value(v))?;
                }
            },
            layout::FieldPlacement::Array { .. } => {
                if !v.handle_array(self)? {
                    // We still have to work!
                    // Let's get an mplace first.
                    let mplace = if v.layout().is_zst() {
                        // it's a ZST, the memory content cannot matter
                        MPlaceTy::dangling(v.layout(), self)
                    } else {
                        // non-ZST array/slice/str cannot be immediate
                        v.value().force_allocation(self)?
                    };
                    // Now iterate over it.
                    for (i, field) in self.mplace_array_fields(mplace)?.enumerate() {
                        v.with_field(Value::from_mem_place(field?), i, |v| self.visit_value(v))?;
                    }
                }
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>
	macro_rules! syscall_a {
		($id:expr, $( $reg:tt = $val:expr),*) => {{
			let rv;
			asm!("syscall"
				: "={rax}" (rv)
				: "{rax}" ($id) $(, $reg ($val))*
				: "rcx", "r11"
				: "volatile"
				);
			rv
		}};
	}
	// SAVE rdi, rsi, rdx, r10, r8, r9
	pub unsafe fn syscall_0(id: u32) -> u64 {
		syscall_a!(id, )
	}
	pub unsafe fn syscall_1(id: u32, a1: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1)
	}
	pub unsafe fn syscall_2(id: u32, a1: usize, a2: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2)
	}
	pub unsafe fn syscall_3(id: u32, a1: usize, a2: usize, a3: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2, "{rdx}"=a3)
	}
	pub unsafe fn syscall_4(id: u32, a1: usize, a2: usize, a3: usize, a4: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2, "{rdx}"=a3, "{r10}"=a4)
	}
<commit_msg>Usermode - Fix crash caused by invalid data size passed to ASM<commit_after>
	macro_rules! syscall_a {
		($id:expr, $( $reg:tt = $val:expr),*) => {{
			let rv;
			asm!("syscall"
				: "={rax}" (rv)
				: "{rax}" ($id as usize) $(, $reg ($val as usize))*
				: "rcx", "r11"
				: "volatile"
				);
			rv
		}};
	}
	// SAVE rdi, rsi, rdx, r10, r8, r9
	pub unsafe fn syscall_0(id: u32) -> u64 {
		syscall_a!(id, )
	}
	pub unsafe fn syscall_1(id: u32, a1: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1)
	}
	pub unsafe fn syscall_2(id: u32, a1: usize, a2: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2)
	}
	pub unsafe fn syscall_3(id: u32, a1: usize, a2: usize, a3: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2, "{rdx}"=a3)
	}
	pub unsafe fn syscall_4(id: u32, a1: usize, a2: usize, a3: usize, a4: usize) -> u64 {
		syscall_a!(id, "{rdi}"=a1, "{rsi}"=a2, "{rdx}"=a3, "{r10}"=a4)
	}
<|endoftext|>