<commit_before>// Copyright (c) 2018, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::convert::TryInto;
use std::ffi::CString;
use std::ptr;

use log::error;
use uninit::out_ref::Out;

use crate::{DefaultKeyring, KeyPermissions, KeyringSerial, TimeoutSeconds};

/// Reexport of `Errno` as `Error`.
type Error = errno::Errno;
/// Simpler `Result` type with the error already set.
type Result<T> = std::result::Result<T, Error>;

fn check_syscall(res: libc::c_long) -> Result<libc::c_long> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(res)
    }
}

static THE_KERNEL_LIED: &str = concat!(
    "It appears as though the kernel made a 64-bit key ID. Please report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);
static ZERO_KEY_ID_FOUND: &str = concat!(
    "It appears as though a key ID of zero was found. This is novel and should not happen. Please \
     report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);
static BUFFER_OVERFLOW: &str = concat!(
    "The kernel returned a size that could not be represented as a `usize`. This should not be \
     possible. Please report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);

fn cstring(s: &str) -> CString {
    CString::new(s.as_bytes()).unwrap()
}

fn opt_cstring(opt: Option<&str>) -> Option<CString> {
    opt.map(cstring)
}

fn opt_cstring_ptr(opt: &Option<CString>) -> *const libc::c_char {
    opt.as_ref().map_or(ptr::null(), |cs| cs.as_ptr())
}

fn opt_key_serial(opt: Option<KeyringSerial>) -> i32 {
    opt.map(KeyringSerial::get).unwrap_or(0)
}

fn keyring_serial(res: libc::c_long) -> KeyringSerial {
    KeyringSerial::new(res.try_into().expect(THE_KERNEL_LIED)).expect(ZERO_KEY_ID_FOUND)
}

fn default_keyring(res: libc::c_long) -> Result<DefaultKeyring> {
    res.try_into().map_err(|err: crate::UnknownDefault| {
        error!(
            concat!(
                "The kernel has returned an unexpected default keyring ID: {}. Please report a \
                 bug.\n\n",
                env!("CARGO_PKG_REPOSITORY"),
            ),
            err.0,
        );
        errno::Errno(libc::EINVAL)
    })
}

fn size(res: libc::c_long) -> usize {
    res.try_into().expect(BUFFER_OVERFLOW)
}

fn ignore(res: libc::c_long) {
    assert_eq!(res, 0);
}

macro_rules! syscall {
    ( $( $arg:expr, )* ) => {
        check_syscall(libc::syscall($( $arg, )*))
    };
}

macro_rules! keyctl {
    ( $( $arg:expr, )* ) => {
        syscall!(libc::SYS_keyctl, $( $arg, )*)
    };
}

pub fn add_key(
    type_: &str,
    description: &str,
    payload: &[u8],
    keyring: KeyringSerial,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    unsafe {
        syscall!(
            libc::SYS_add_key,
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
            keyring.get(),
        )
    }
    .map(keyring_serial)
}

pub fn request_key(
    type_: &str,
    description: &str,
    callout_info: Option<&str>,
    keyring: Option<KeyringSerial>,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    let callout_cstr = opt_cstring(callout_info);
    let callout_ptr = opt_cstring_ptr(&callout_cstr);

    unsafe {
        syscall!(
            libc::SYS_request_key,
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            callout_ptr,
            opt_key_serial(keyring),
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_get_keyring_id(id: KeyringSerial, create: bool) -> Result<KeyringSerial> {
    unsafe {
        keyctl!(
            libc::KEYCTL_GET_KEYRING_ID,
            id.get(),
            if create { 1 } else { 0 },
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_join_session_keyring(name: Option<&str>) -> Result<KeyringSerial> {
    let name_cstr = opt_cstring(name);
    let name_ptr = opt_cstring_ptr(&name_cstr);

    unsafe { keyctl!(libc::KEYCTL_JOIN_SESSION_KEYRING, name_ptr,) }.map(keyring_serial)
}

pub fn keyctl_update(id: KeyringSerial, payload: &[u8]) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_UPDATE,
            id.get(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
        )
    }
    .map(ignore)
}

pub fn keyctl_revoke(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_REVOKE, id.get(),) }.map(ignore)
}

pub fn keyctl_chown(
    id: KeyringSerial,
    uid: Option<libc::uid_t>,
    gid: Option<libc::gid_t>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_CHOWN,
            id.get(),
            uid.unwrap_or(!0),
            gid.unwrap_or(!0),
        )
    }
    .map(ignore)
}

pub fn keyctl_setperm(id: KeyringSerial, perm: KeyPermissions) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SETPERM, id.get(), perm,) }.map(ignore)
}

pub fn keyctl_describe(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_DESCRIBE,
            id.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_clear(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_CLEAR, id.get(),) }.map(ignore)
}

pub fn keyctl_link(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_LINK, id.get(), ringid.get(),) }.map(ignore)
}

pub fn keyctl_unlink(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_UNLINK, id.get(), ringid.get(),) }.map(ignore)
}

pub fn keyctl_search(
    ringid: KeyringSerial,
    type_: &str,
    description: &str,
    destringid: Option<KeyringSerial>,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    unsafe {
        keyctl!(
            libc::KEYCTL_SEARCH,
            ringid.get(),
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            opt_key_serial(destringid),
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_read(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_READ,
            id.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_instantiate(
    id: KeyringSerial,
    payload: &[u8],
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_INSTANTIATE,
            id.get(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_negate(
    id: KeyringSerial,
    timeout: TimeoutSeconds,
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_NEGATE,
            id.get(),
            timeout,
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_set_reqkey_keyring(reqkey_defl: DefaultKeyring) -> Result<DefaultKeyring> {
    unsafe { keyctl!(libc::KEYCTL_SET_REQKEY_KEYRING, reqkey_defl,) }.and_then(default_keyring)
}

pub fn keyctl_set_timeout(key: KeyringSerial, timeout: TimeoutSeconds) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SET_TIMEOUT, key.get(), timeout,) }.map(ignore)
}

pub fn keyctl_assume_authority(key: Option<KeyringSerial>) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_ASSUME_AUTHORITY, opt_key_serial(key),) }.map(ignore)
}

pub fn keyctl_get_security(key: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_GET_SECURITY,
            key.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_reject(
    id: KeyringSerial,
    timeout: TimeoutSeconds,
    error: errno::Errno,
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_REJECT,
            id.get(),
            timeout,
            error,
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_invalidate(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_INVALIDATE, id.get(),) }.map(ignore)
}

pub fn keyctl_get_persistent(uid: libc::uid_t, id: KeyringSerial) -> Result<KeyringSerial> {
    unsafe { keyctl!(libc::KEYCTL_GET_PERSISTENT, uid, id.get(),) }.map(keyring_serial)
}

pub fn keyctl_session_to_parent() -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SESSION_TO_PARENT,) }.map(ignore)
}

pub fn keyctl_dh_compute(
    private: KeyringSerial,
    prime: KeyringSerial,
    base: KeyringSerial,
    mut buffer: Option<Out<[u8]>>,
) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_DH_COMPUTE,
            private.get(),
            prime.get(),
            base.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub enum Restriction<'a> {
    AllLinks,
    ByType {
        type_: &'a str,
        restriction: &'a str,
    },
}

pub fn keyctl_restrict_keyring(keyring: KeyringSerial, restriction: Restriction) -> Result<()> {
    let type_cstr;
    let restriction_cstr;

    let (type_ptr, restriction_ptr) = match restriction {
        Restriction::AllLinks => (ptr::null(), ptr::null()),
        Restriction::ByType {
            type_,
            restriction,
        } => {
            type_cstr = cstring(type_);
            restriction_cstr = cstring(restriction);

            (type_cstr.as_ptr(), restriction_cstr.as_ptr())
        },
    };
    unsafe {
        keyctl!(
            libc::KEYCTL_RESTRICT_KEYRING,
            keyring.get(),
            type_ptr,
            restriction_ptr,
        )
    }
    .map(ignore)
}
<commit_msg>keyctl_dh_compute: use the syscall API<commit_after>// Copyright (c) 2018, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::convert::TryInto;
use std::ffi::CString;
use std::ptr;

use log::error;
use uninit::out_ref::Out;

use crate::{DefaultKeyring, KeyPermissions, KeyringSerial, TimeoutSeconds};

/// Reexport of `Errno` as `Error`.
type Error = errno::Errno;
/// Simpler `Result` type with the error already set.
type Result<T> = std::result::Result<T, Error>;

fn check_syscall(res: libc::c_long) -> Result<libc::c_long> {
    if res == -1 {
        Err(errno::errno())
    } else {
        Ok(res)
    }
}

static THE_KERNEL_LIED: &str = concat!(
    "It appears as though the kernel made a 64-bit key ID. Please report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);
static ZERO_KEY_ID_FOUND: &str = concat!(
    "It appears as though a key ID of zero was found. This is novel and should not happen. Please \
     report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);
static BUFFER_OVERFLOW: &str = concat!(
    "The kernel returned a size that could not be represented as a `usize`. This should not be \
     possible. Please report a bug.\n\n",
    env!("CARGO_PKG_REPOSITORY"),
);

fn cstring(s: &str) -> CString {
    CString::new(s.as_bytes()).unwrap()
}

fn opt_cstring(opt: Option<&str>) -> Option<CString> {
    opt.map(cstring)
}

fn opt_cstring_ptr(opt: &Option<CString>) -> *const libc::c_char {
    opt.as_ref().map_or(ptr::null(), |cs| cs.as_ptr())
}

fn opt_key_serial(opt: Option<KeyringSerial>) -> i32 {
    opt.map(KeyringSerial::get).unwrap_or(0)
}

fn keyring_serial(res: libc::c_long) -> KeyringSerial {
    KeyringSerial::new(res.try_into().expect(THE_KERNEL_LIED)).expect(ZERO_KEY_ID_FOUND)
}

fn default_keyring(res: libc::c_long) -> Result<DefaultKeyring> {
    res.try_into().map_err(|err: crate::UnknownDefault| {
        error!(
            concat!(
                "The kernel has returned an unexpected default keyring ID: {}. Please report a \
                 bug.\n\n",
                env!("CARGO_PKG_REPOSITORY"),
            ),
            err.0,
        );
        errno::Errno(libc::EINVAL)
    })
}

fn size(res: libc::c_long) -> usize {
    res.try_into().expect(BUFFER_OVERFLOW)
}

fn ignore(res: libc::c_long) {
    assert_eq!(res, 0);
}

macro_rules! syscall {
    ( $( $arg:expr, )* ) => {
        check_syscall(libc::syscall($( $arg, )*))
    };
}

macro_rules! keyctl {
    ( $( $arg:expr, )* ) => {
        syscall!(libc::SYS_keyctl, $( $arg, )*)
    };
}

pub fn add_key(
    type_: &str,
    description: &str,
    payload: &[u8],
    keyring: KeyringSerial,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    unsafe {
        syscall!(
            libc::SYS_add_key,
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
            keyring.get(),
        )
    }
    .map(keyring_serial)
}

pub fn request_key(
    type_: &str,
    description: &str,
    callout_info: Option<&str>,
    keyring: Option<KeyringSerial>,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    let callout_cstr = opt_cstring(callout_info);
    let callout_ptr = opt_cstring_ptr(&callout_cstr);

    unsafe {
        syscall!(
            libc::SYS_request_key,
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            callout_ptr,
            opt_key_serial(keyring),
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_get_keyring_id(id: KeyringSerial, create: bool) -> Result<KeyringSerial> {
    unsafe {
        keyctl!(
            libc::KEYCTL_GET_KEYRING_ID,
            id.get(),
            if create { 1 } else { 0 },
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_join_session_keyring(name: Option<&str>) -> Result<KeyringSerial> {
    let name_cstr = opt_cstring(name);
    let name_ptr = opt_cstring_ptr(&name_cstr);

    unsafe { keyctl!(libc::KEYCTL_JOIN_SESSION_KEYRING, name_ptr,) }.map(keyring_serial)
}

pub fn keyctl_update(id: KeyringSerial, payload: &[u8]) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_UPDATE,
            id.get(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
        )
    }
    .map(ignore)
}

pub fn keyctl_revoke(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_REVOKE, id.get(),) }.map(ignore)
}

pub fn keyctl_chown(
    id: KeyringSerial,
    uid: Option<libc::uid_t>,
    gid: Option<libc::gid_t>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_CHOWN,
            id.get(),
            uid.unwrap_or(!0),
            gid.unwrap_or(!0),
        )
    }
    .map(ignore)
}

pub fn keyctl_setperm(id: KeyringSerial, perm: KeyPermissions) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SETPERM, id.get(), perm,) }.map(ignore)
}

pub fn keyctl_describe(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_DESCRIBE,
            id.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_clear(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_CLEAR, id.get(),) }.map(ignore)
}

pub fn keyctl_link(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_LINK, id.get(), ringid.get(),) }.map(ignore)
}

pub fn keyctl_unlink(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_UNLINK, id.get(), ringid.get(),) }.map(ignore)
}

pub fn keyctl_search(
    ringid: KeyringSerial,
    type_: &str,
    description: &str,
    destringid: Option<KeyringSerial>,
) -> Result<KeyringSerial> {
    let type_cstr = cstring(type_);
    let desc_cstr = cstring(description);
    unsafe {
        keyctl!(
            libc::KEYCTL_SEARCH,
            ringid.get(),
            type_cstr.as_ptr(),
            desc_cstr.as_ptr(),
            opt_key_serial(destringid),
        )
    }
    .map(keyring_serial)
}

pub fn keyctl_read(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_READ,
            id.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_instantiate(
    id: KeyringSerial,
    payload: &[u8],
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_INSTANTIATE,
            id.get(),
            payload.as_ptr() as *const libc::c_void,
            payload.len(),
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_negate(
    id: KeyringSerial,
    timeout: TimeoutSeconds,
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_NEGATE,
            id.get(),
            timeout,
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_set_reqkey_keyring(reqkey_defl: DefaultKeyring) -> Result<DefaultKeyring> {
    unsafe { keyctl!(libc::KEYCTL_SET_REQKEY_KEYRING, reqkey_defl,) }.and_then(default_keyring)
}

pub fn keyctl_set_timeout(key: KeyringSerial, timeout: TimeoutSeconds) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SET_TIMEOUT, key.get(), timeout,) }.map(ignore)
}

pub fn keyctl_assume_authority(key: Option<KeyringSerial>) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_ASSUME_AUTHORITY, opt_key_serial(key),) }.map(ignore)
}

pub fn keyctl_get_security(key: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> {
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_GET_SECURITY,
            key.get(),
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
        )
    }
    .map(size)
}

pub fn keyctl_reject(
    id: KeyringSerial,
    timeout: TimeoutSeconds,
    error: errno::Errno,
    ringid: Option<KeyringSerial>,
) -> Result<()> {
    unsafe {
        keyctl!(
            libc::KEYCTL_REJECT,
            id.get(),
            timeout,
            error,
            opt_key_serial(ringid),
        )
    }
    .map(ignore)
}

pub fn keyctl_invalidate(id: KeyringSerial) -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_INVALIDATE, id.get(),) }.map(ignore)
}

pub fn keyctl_get_persistent(uid: libc::uid_t, id: KeyringSerial) -> Result<KeyringSerial> {
    unsafe { keyctl!(libc::KEYCTL_GET_PERSISTENT, uid, id.get(),) }.map(keyring_serial)
}

pub fn keyctl_session_to_parent() -> Result<()> {
    unsafe { keyctl!(libc::KEYCTL_SESSION_TO_PARENT,) }.map(ignore)
}

#[repr(C)]
struct DhComputeParamsKernel {
    priv_: i32,
    prime: i32,
    base: i32,
}

pub fn keyctl_dh_compute(
    private: KeyringSerial,
    prime: KeyringSerial,
    base: KeyringSerial,
    mut buffer: Option<Out<[u8]>>,
) -> Result<usize> {
    let params = DhComputeParamsKernel {
        priv_: private.get(),
        prime: prime.get(),
        base: base.get(),
    };
    let capacity = buffer.as_mut().map_or(0, |b| b.len());
    unsafe {
        keyctl!(
            libc::KEYCTL_DH_COMPUTE,
            &params as *const DhComputeParamsKernel,
            buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()),
            capacity,
            0,
        )
    }
    .map(size)
}

pub enum Restriction<'a> {
    AllLinks,
    ByType {
        type_: &'a str,
        restriction: &'a str,
    },
}

pub fn keyctl_restrict_keyring(keyring: KeyringSerial, restriction: Restriction) -> Result<()> {
    let type_cstr;
    let restriction_cstr;

    let (type_ptr, restriction_ptr) = match restriction {
        Restriction::AllLinks => (ptr::null(), ptr::null()),
        Restriction::ByType {
            type_,
            restriction,
        } => {
            type_cstr = cstring(type_);
            restriction_cstr = cstring(restriction);

            (type_cstr.as_ptr(), restriction_cstr.as_ptr())
        },
    };
    unsafe {
        keyctl!(
            libc::KEYCTL_RESTRICT_KEYRING,
            keyring.get(),
            type_ptr,
            restriction_ptr,
        )
    }
    .map(ignore)
}
<|endoftext|>


<commit_before>#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
#[cfg(any(
    target_os = "linux",
    target_os = "fuchsia",
    target_os = "redox",
    target_os = "emscripten"
))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::mem;
    use crate::sys_common::thread_local::register_dtor_fallback;

    extern "C" {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern "C" fn(
            dtor: unsafe extern "C" fn(*mut u8),
            arg: *mut u8,
            dso_handle: *mut u8,
        ) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
            dtor,
            t,
            &__dso_handle as *const _ as *mut _,
        );
        return;
    }
    register_dtor_fallback(t, dtor);
}

// This implementation is very similar to register_dtor_fallback in
// sys_common/thread_local.rs. The main difference is that we want to hook into
// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
// registered dtors before any TLS slots get freed, and when the main thread
// exits.
//
// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
// thread. thread_local dtors are pushed to the DTOR list without calling
// _tlv_atexit.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::cell::Cell;
    use crate::ptr;

    #[thread_local]
    static REGISTERED: Cell<bool> = Cell::new(false);
    if !REGISTERED.get() {
        _tlv_atexit(run_dtors, ptr::null_mut());
        REGISTERED.set(true);
    }

    type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;

    #[thread_local]
    static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
    if DTORS.get().is_null() {
        let v: Box<List> = box Vec::new();
        DTORS.set(Box::into_raw(v));
    }

    extern "C" {
        fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
    }

    let list: &mut List = &mut *DTORS.get();
    list.push((t, dtor));

    unsafe extern "C" fn run_dtors(_: *mut u8) {
        let mut ptr = DTORS.replace(ptr::null_mut());
        while !ptr.is_null() {
            let list = Box::from_raw(ptr);
            for (ptr, dtor) in list.into_iter() {
                dtor(ptr);
            }
            ptr = DTORS.replace(ptr::null_mut());
        }
    }
}
<commit_msg>Remove irelevant comment on `register_dtor`<commit_after>#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]

// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
#[cfg(any(
    target_os = "linux",
    target_os = "fuchsia",
    target_os = "redox",
    target_os = "emscripten"
))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::mem;
    use crate::sys_common::thread_local::register_dtor_fallback;

    extern "C" {
        #[linkage = "extern_weak"]
        static __dso_handle: *mut u8;
        #[linkage = "extern_weak"]
        static __cxa_thread_atexit_impl: *const libc::c_void;
    }
    if !__cxa_thread_atexit_impl.is_null() {
        type F = unsafe extern "C" fn(
            dtor: unsafe extern "C" fn(*mut u8),
            arg: *mut u8,
            dso_handle: *mut u8,
        ) -> libc::c_int;
        mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
            dtor,
            t,
            &__dso_handle as *const _ as *mut _,
        );
        return;
    }
    register_dtor_fallback(t, dtor);
}

// This implementation is very similar to register_dtor_fallback in
// sys_common/thread_local.rs. The main difference is that we want to hook into
// macOS's analog of the above linux function, _tlv_atexit. OSX will run the
// registered dtors before any TLS slots get freed, and when the main thread
// exits.
//
// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
// thread. thread_local dtors are pushed to the DTOR list without calling
// _tlv_atexit.
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
    use crate::cell::Cell;
    use crate::ptr;

    #[thread_local]
    static REGISTERED: Cell<bool> = Cell::new(false);
    if !REGISTERED.get() {
        _tlv_atexit(run_dtors, ptr::null_mut());
        REGISTERED.set(true);
    }

    type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;

    #[thread_local]
    static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
    if DTORS.get().is_null() {
        let v: Box<List> = box Vec::new();
        DTORS.set(Box::into_raw(v));
    }

    extern "C" {
        fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
    }

    let list: &mut List = &mut *DTORS.get();
    list.push((t, dtor));

    unsafe extern "C" fn run_dtors(_: *mut u8) {
        let mut ptr = DTORS.replace(ptr::null_mut());
        while !ptr.is_null() {
            let list = Box::from_raw(ptr);
            for (ptr, dtor) in list.into_iter() {
                dtor(ptr);
            }
            ptr = DTORS.replace(ptr::null_mut());
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;
use std::path::Path;
use cpu::CpuState;
use memory::Memory;
use cpu::AddressMode;
use std::fmt::UpperHex;

use std::vec;
use cpu::OpcodeDebugInfo;

//collections::vec::Vec<cpu::common_defs::OpcodeDebugInfo>

pub struct NesTest {
    f:File,
    op_info: Vec<OpcodeDebugInfo>,
}

impl NesTest {
    pub fn new<P:AsRef<Path>>(file_path: P, opcode_info:Vec<OpcodeDebugInfo>) -> NesTest {
        NesTest {
            f : File::create(&file_path).unwrap(),
            op_info: opcode_info,
        }
    }

    pub fn log_after_fetch(self: &mut NesTest, cpu_state: &CpuState, mem: &Memory) {
        let pc = cpu_state.pc-1;
        let dr = &cpu_state.decode_register;
        let len = cpu_state.decode_register.info.len;
        let opcode = cpu_state.instruction_register;
        let info = &self.op_info[opcode as usize];

        let mut s:String = format!("{:X}  ",pc).to_owned();

        match len {
            1 => { s.push_str(&format!("{:X}           ",cpu_state.instruction_register)) },
            2 => { s.push_str(&format!("{:X} {:X}      ",cpu_state.instruction_register,mem.read8(pc+1).unwrap())) },
            3 => { s.push_str(&format!("{:X} {:X} {:X} ",cpu_state.instruction_register,mem.read8(pc+1).unwrap(),mem.read8(pc+2).unwrap())) },
            _ => panic!("instructions should have a length of 1, 2, or 3.")
        }

        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { panic!("Accumulator addressing mode is unimplemented"); },
            AddressMode::Implied      => { panic!("Implied addressing mode is unimplemented"); },

            //explicit addresses from here on out
            AddressMode::Absolute =>        {
                s.push_str(&format!("{} ${:X}",info.name,mem.read16(pc+1).unwrap()));
            },
            AddressMode::AbsoluteX       => { self.f.write(s.as_bytes()); panic!("AbsoluteX addressing mode is unimplemented"); },
            AddressMode::AbsoluteY       => { self.f.write(s.as_bytes()); panic!("AbsoluteY addressing mode is unimplemented"); },
            AddressMode::Immediate       => { self.f.write(s.as_bytes()); panic!("Immediate addressing mode is unimplemented"); },
            AddressMode::Indirect        => { self.f.write(s.as_bytes()); panic!("Indirect addressing mode is unimplemented"); },
            AddressMode::IndexedIndirect => { self.f.write(s.as_bytes()); panic!("IndexedIndirect addressing mode is unimplemented"); },
            AddressMode::IndirectIndexed => { self.f.write(s.as_bytes()); panic!("IndirectIndexed addressing mode is unimplemented"); },
            AddressMode::Relative        => { self.f.write(s.as_bytes()); panic!("Relative addressing mode is unimplemented"); },
            AddressMode::ZeroPage        => { self.f.write(s.as_bytes()); panic!("ZeroPage addressing mode is unimplemented"); },
            AddressMode::ZeroPageX       => { self.f.write(s.as_bytes()); panic!("ZeroPageX addressing mode is unimplemented"); },
            AddressMode::ZeroPageY       => { self.f.write(s.as_bytes()); panic!("ZeroPageY addressing mode is unimplemented"); },
            _ => { self.f.write(s.as_bytes()); panic!("unrecognized addressing mode") }
        }
        let len = s.len();
        s.push_str(&format!("{output:>0$}",48-len,output="")); // spacing
        s.push_str(&format!("A:{:0>2X}",cpu_state.a));
        s.push_str(&format!(" X:{:0>2X}",cpu_state.x));
        s.push_str(&format!(" Y:{:0>2X}",cpu_state.y));
        s.push_str(&format!(" P:{:0>2X}",cpu_state.unpack_flags()));
        s.push_str(&format!(" SP:{:0>2X}\n",cpu_state.sp));

        self.f.write(s.as_bytes());
    }
}

<commit_msg>fixed new warnings<commit_after>use std::fs::File;
use std::io::Write;
use std::path::Path;
use cpu::CpuState;
use memory::Memory;
use cpu::AddressMode;
use std::fmt::UpperHex;

use std::vec;
use cpu::OpcodeDebugInfo;

//collections::vec::Vec<cpu::common_defs::OpcodeDebugInfo>

pub struct NesTest {
    f:File,
    op_info: Vec<OpcodeDebugInfo>,
}

impl NesTest {
    pub fn new<P:AsRef<Path>>(file_path: P, opcode_info:Vec<OpcodeDebugInfo>) -> NesTest {
        NesTest {
            f : File::create(&file_path).unwrap(),
            op_info: opcode_info,
        }
    }

    pub fn log_after_fetch(self: &mut NesTest, cpu_state: &CpuState, mem: &Memory) {
        let pc = cpu_state.pc-1;
        let dr = &cpu_state.decode_register;
        let len = cpu_state.decode_register.info.len;
        let opcode = cpu_state.instruction_register;
        let info = &self.op_info[opcode as usize];

        let mut s:String = format!("{:X}  ",pc).to_owned();

        match len {
            1 => { s.push_str(&format!("{:X}           ",cpu_state.instruction_register)) },
            2 => { s.push_str(&format!("{:X} {:X}      ",cpu_state.instruction_register,mem.read8(pc+1).unwrap())) },
            3 => { s.push_str(&format!("{:X} {:X} {:X} ",cpu_state.instruction_register,mem.read8(pc+1).unwrap(),mem.read8(pc+2).unwrap())) },
            _ => panic!("instructions should have a length of 1, 2, or 3.")
        }

        match dr.info.address_mode {
            // no explicit addresses for the following modes
            AddressMode::Accumulator  => { let _ = self.f.write(s.as_bytes()); panic!("Accumulator addressing mode is unimplemented"); },
            AddressMode::Implied      => { let _ = self.f.write(s.as_bytes()); panic!("Implied addressing mode is unimplemented"); },

            //explicit addresses from here on out
            AddressMode::Absolute =>        {
                s.push_str(&format!("{} ${:X}",info.name,mem.read16(pc+1).unwrap()));
            },
            AddressMode::AbsoluteX       => { let _ = self.f.write(s.as_bytes()); panic!("AbsoluteX addressing mode is unimplemented"); },
            AddressMode::AbsoluteY       => { let _ = self.f.write(s.as_bytes()); panic!("AbsoluteY addressing mode is unimplemented"); },
            AddressMode::Immediate       => { let _ = self.f.write(s.as_bytes()); panic!("Immediate addressing mode is unimplemented"); },
            AddressMode::Indirect        => { let _ = self.f.write(s.as_bytes()); panic!("Indirect addressing mode is unimplemented"); },
            AddressMode::IndexedIndirect => { let _ = self.f.write(s.as_bytes()); panic!("IndexedIndirect addressing mode is unimplemented"); },
            AddressMode::IndirectIndexed => { let _ = self.f.write(s.as_bytes()); panic!("IndirectIndexed addressing mode is unimplemented"); },
            AddressMode::Relative        => { let _ = self.f.write(s.as_bytes()); panic!("Relative addressing mode is unimplemented"); },
            AddressMode::ZeroPage        => { let _ = self.f.write(s.as_bytes()); panic!("ZeroPage addressing mode is unimplemented"); },
            AddressMode::ZeroPageX       => { let _ = self.f.write(s.as_bytes()); panic!("ZeroPageX addressing mode is unimplemented"); },
            AddressMode::ZeroPageY       => { let _ = self.f.write(s.as_bytes()); panic!("ZeroPageY addressing mode is unimplemented"); },
            _ => { let _ = self.f.write(s.as_bytes()); panic!("unrecognized addressing mode") }
        }
        let len = s.len();
        s.push_str(&format!("{output:>0$}",48-len,output="")); // spacing
        s.push_str(&format!("A:{:0>2X}",cpu_state.a));
        s.push_str(&format!(" X:{:0>2X}",cpu_state.x));
        s.push_str(&format!(" Y:{:0>2X}",cpu_state.y));
        s.push_str(&format!(" P:{:0>2X}",cpu_state.unpack_flags()));
        s.push_str(&format!(" SP:{:0>2X}\n",cpu_state.sp));

        self.f.write(s.as_bytes());
    }
}

<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::layout::{Size, Align};
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use rustc_data_structures::indexed_vec::Idx;

use error::EvalResult;
use eval_context::{EvalContext};
use memory::Pointer;
use value::{PrimVal, Value};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Lvalue<'tcx> {
    /// An lvalue referring to a value allocated in the `Memory` system.
    Ptr {
        ptr: Pointer,
        extra: LvalueExtra,
    },

    /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with
    /// a Mir local index.
    Local {
        frame: usize,
        local: mir::Local,
    },

    /// An lvalue referring to a global
    Global(GlobalId<'tcx>),
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum LvalueExtra {
    None,
    Length(u64),
    Vtable(Pointer),
    DowncastVariant(usize),
}

/// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalId<'tcx> {
    /// For a constant or static, the `DefId` of the item itself.
    /// For a promoted global, the `DefId` of the function they belong to.
    pub(super) def_id: DefId,

    /// For statics and constants this is `Substs::empty()`, so only promoteds and associated
    /// constants actually have something useful here. We could special case statics and constants,
    /// but that would only require more branching when working with constants, and not bring any
    /// real benefits.
    pub(super) substs: &'tcx Substs<'tcx>,

    /// The index for promoted globals within their function's `Mir`.
    pub(super) promoted: Option<mir::Promoted>,
}

#[derive(Copy, Clone, Debug)]
pub struct Global<'tcx> {
    pub(super) value: Value,
    /// Only used in `force_allocation` to ensure we don't mark the memory
    /// before the static is initialized. It is possible to convert a
    /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets
    /// lifted to an allocation before the static is fully initialized
    pub(super) initialized: bool,
    pub(super) mutable: bool,
    pub(super) ty: Ty<'tcx>,
}

impl<'tcx> Lvalue<'tcx> {
    pub fn from_ptr(ptr: Pointer) -> Self {
        Lvalue::Ptr { ptr, extra: LvalueExtra::None }
    }

    pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) {
        match self {
            Lvalue::Ptr { ptr, extra } => (ptr, extra),
            _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self),

        }
    }

    pub(super) fn to_ptr(self) -> Pointer {
        let (ptr, extra) = self.to_ptr_and_extra();
        assert_eq!(extra, LvalueExtra::None);
        ptr
    }

    pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
        match ty.sty {
            ty::TyArray(elem, n) => (elem, n as u64),

            ty::TySlice(elem) => {
                match self {
                    Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len),
                    _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self),
                }
            }

            _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
        }
    }
}

impl<'tcx> Global<'tcx> {
    pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self {
        Global {
            value: Value::ByVal(PrimVal::Undef),
            mutable: true,
            ty,
            initialized: false,
        }
    }
}

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
        if let mir::Lvalue::Projection(ref proj) = *lvalue {
            if let mir::Lvalue::Local(index) = proj.base {
                if let Value::ByValPair(a, b) = self.frame().get_local(index) {
                    if let mir::ProjectionElem::Field(ref field, _) = proj.elem {
                        let val = [a, b][field.index()];
                        return Ok(Value::ByVal(val));
                    }
                }
            }
        }
        let lvalue = self.eval_lvalue(lvalue)?;
        Ok(self.read_lvalue(lvalue))
    }

    pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> Value {
        match lvalue {
            Lvalue::Ptr { ptr, extra } => {
                assert_eq!(extra, LvalueExtra::None);
                Value::ByRef(ptr)
            }
            Lvalue::Local { frame, local } => self.stack[frame].get_local(local),
            Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value,
        }
    }

    pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
        use rustc::mir::Lvalue::*;
        let lvalue = match *mir_lvalue {
            Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
            Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local },

            Static(def_id) => {
                let substs = self.tcx.intern_substs(&[]);
                Lvalue::Global(GlobalId { def_id, substs, promoted: None })
            }

            Projection(ref proj) => return self.eval_lvalue_projection(proj),
        };

        if log_enabled!(::log::LogLevel::Trace) {
            self.dump_local(lvalue);
        }

        Ok(lvalue)
    }

    fn eval_lvalue_projection(
        &mut self,
        proj: &mir::LvalueProjection<'tcx>,
    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
        use rustc::mir::ProjectionElem::*;
        let (ptr, extra) = match proj.elem {
            Field(field, field_ty) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                let base_layout = self.type_layout(base_ty)?;
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, base_extra) = base.to_ptr_and_extra();

                let field_ty = self.monomorphize(field_ty, self.substs());
                let field = field.index();

                use rustc::ty::layout::Layout::*;
                let (offset, packed) = match *base_layout {
                    Univariant { ref variant, .. } => {
                        (variant.offsets[field], variant.packed)
                    },

                    General { ref variants, .. } => {
                        if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
                            // +1 for the discriminant, which is field 0
                            (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed)
                        } else {
                            bug!("field access on enum had no variant index");
                        }
                    }

                    RawNullablePointer { .. } => {
                        assert_eq!(field, 0);
                        return Ok(base);
                    }

                    StructWrappedNullablePointer { ref nonnull, .. } => {
                        (nonnull.offsets[field], nonnull.packed)
                    }

                    UntaggedUnion { .. } => return Ok(base),

                    Vector { element, count } => {
                        let field = field as u64;
                        assert!(field < count);
                        let elem_size = element.size(&self.tcx.data_layout).bytes();
                        (Size::from_bytes(field * elem_size), false)
                    }

                    _ => bug!("field access on non-product type: {:?}", base_layout),
                };

                let offset = match base_extra {
                    LvalueExtra::Vtable(tab) => {
                        let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?;
                        offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
                    }
                    _ => offset.bytes(),
                };

                let ptr = base_ptr.offset(offset);

                if packed {
                    let size = self.type_size(field_ty)?.expect("packed struct must be sized");
                    self.memory.mark_packed(ptr, size);
                }

                let extra = if self.type_is_sized(field_ty) {
                    LvalueExtra::None
                } else {
                    match base_extra {
                        LvalueExtra::None => bug!("expected fat pointer"),
                        LvalueExtra::DowncastVariant(..) =>
                            bug!("Rust doesn't support unsized fields in enum variants"),
                        LvalueExtra::Vtable(_) |
                        LvalueExtra::Length(_) => {},
                    }
                    base_extra
                };

                (ptr, extra)
            }

            Downcast(_, variant) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                let base_layout = self.type_layout(base_ty)?;
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, base_extra) = base.to_ptr_and_extra();

                use rustc::ty::layout::Layout::*;
                let extra = match *base_layout {
                    General { .. } => LvalueExtra::DowncastVariant(variant),
                    RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
                    _ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
                };
                (base_ptr, extra)
            }

            Deref => {
                let base_ty = self.lvalue_ty(&proj.base);
                let val = self.eval_and_read_lvalue(&proj.base)?;

                let pointee_type = match base_ty.sty {
                    ty::TyRawPtr(ref tam) |
                    ty::TyRef(_, ref tam) => tam.ty,
                    ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(),
                    _ => bug!("can only deref pointer types"),
                };

                trace!("deref to {} on {:?}", pointee_type, val);

                match self.tcx.struct_tail(pointee_type).sty {
                    ty::TyDynamic(..) => {
                        let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?;
                        (ptr, LvalueExtra::Vtable(vtable))
                    },
                    ty::TyStr | ty::TySlice(_) => {
                        let (ptr, len) = val.expect_slice(&self.memory)?;
                        (ptr, LvalueExtra::Length(len))
                    },
                    _ => (val.read_ptr(&self.memory)?, LvalueExtra::None),
                }
            }

            Index(ref operand) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, len) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
                let n_ptr = self.eval_operand(operand)?;
                let usize = self.tcx.types.usize;
                let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
                assert!(n < len);
                let ptr = base_ptr.offset(n * elem_size);
                (ptr, LvalueExtra::None)
            }

            ConstantIndex { offset, min_length, from_end } => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, n) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
                assert!(n >= min_length as u64);

                let index = if from_end {
                    n - u64::from(offset)
                } else {
                    u64::from(offset)
                };

                let ptr = base_ptr.offset(index * elem_size);
                (ptr, LvalueExtra::None)
            }

            Subslice { from, to } => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, n) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
                assert!(u64::from(from) <= n - u64::from(to));
                let ptr = base_ptr.offset(u64::from(from) * elem_size);
                let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
                (ptr, extra)
            }
        };

        Ok(Lvalue::Ptr { ptr, extra })
    }

    pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
        self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs())
    }
}
<commit_msg>factor out lvalue field access into its own function<commit_after>use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::layout::{Size, Align};
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use rustc_data_structures::indexed_vec::Idx;

use error::EvalResult;
use eval_context::{EvalContext};
use memory::Pointer;
use value::{PrimVal, Value};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Lvalue<'tcx> {
    /// An lvalue referring to a value allocated in the `Memory` system.
    Ptr {
        ptr: Pointer,
        extra: LvalueExtra,
    },

    /// An lvalue referring to a value on the stack. Represented by a stack frame index paired with
    /// a Mir local index.
    Local {
        frame: usize,
        local: mir::Local,
    },

    /// An lvalue referring to a global
    Global(GlobalId<'tcx>),
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum LvalueExtra {
    None,
    Length(u64),
    Vtable(Pointer),
    DowncastVariant(usize),
}

/// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalId<'tcx> {
    /// For a constant or static, the `DefId` of the item itself.
    /// For a promoted global, the `DefId` of the function they belong to.
    pub(super) def_id: DefId,

    /// For statics and constants this is `Substs::empty()`, so only promoteds and associated
    /// constants actually have something useful here. We could special case statics and constants,
    /// but that would only require more branching when working with constants, and not bring any
    /// real benefits.
    pub(super) substs: &'tcx Substs<'tcx>,

    /// The index for promoted globals within their function's `Mir`.
    pub(super) promoted: Option<mir::Promoted>,
}

#[derive(Copy, Clone, Debug)]
pub struct Global<'tcx> {
    pub(super) value: Value,
    /// Only used in `force_allocation` to ensure we don't mark the memory
    /// before the static is initialized. It is possible to convert a
    /// global which initially is `Value::ByVal(PrimVal::Undef)` and gets
    /// lifted to an allocation before the static is fully initialized
    pub(super) initialized: bool,
    pub(super) mutable: bool,
    pub(super) ty: Ty<'tcx>,
}

impl<'tcx> Lvalue<'tcx> {
    pub fn from_ptr(ptr: Pointer) -> Self {
        Lvalue::Ptr { ptr, extra: LvalueExtra::None }
    }

    pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) {
        match self {
            Lvalue::Ptr { ptr, extra } => (ptr, extra),
            _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self),

        }
    }

    pub(super) fn to_ptr(self) -> Pointer {
        let (ptr, extra) = self.to_ptr_and_extra();
        assert_eq!(extra, LvalueExtra::None);
        ptr
    }

    pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
        match ty.sty {
            ty::TyArray(elem, n) => (elem, n as u64),

            ty::TySlice(elem) => {
                match self {
                    Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => (elem, len),
                    _ => bug!("elem_ty_and_len of a TySlice given non-slice lvalue: {:?}", self),
                }
            }

            _ => bug!("elem_ty_and_len expected array or slice, got {:?}", ty),
        }
    }
}

impl<'tcx> Global<'tcx> {
    pub(super) fn uninitialized(ty: Ty<'tcx>) -> Self {
        Global {
            value: Value::ByVal(PrimVal::Undef),
            mutable: true,
            ty,
            initialized: false,
        }
    }
}

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
        if let mir::Lvalue::Projection(ref proj) = *lvalue {
            if let mir::Lvalue::Local(index) = proj.base {
                if let Value::ByValPair(a, b) = self.frame().get_local(index) {
                    if let mir::ProjectionElem::Field(ref field, _) = proj.elem {
                        let val = [a, b][field.index()];
                        return Ok(Value::ByVal(val));
                    }
                }
            }
        }
        let lvalue = self.eval_lvalue(lvalue)?;
        Ok(self.read_lvalue(lvalue))
    }

    pub fn read_lvalue(&self, lvalue: Lvalue<'tcx>) -> Value {
        match lvalue {
            Lvalue::Ptr { ptr, extra } => {
                assert_eq!(extra, LvalueExtra::None);
                Value::ByRef(ptr)
            }
            Lvalue::Local { frame, local } => self.stack[frame].get_local(local),
            Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value,
        }
    }

    pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
        use rustc::mir::Lvalue::*;
        let lvalue = match *mir_lvalue {
            Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
            Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local },

            Static(def_id) => {
                let substs = self.tcx.intern_substs(&[]);
                Lvalue::Global(GlobalId { def_id, substs, promoted: None })
            }

            Projection(ref proj) => return self.eval_lvalue_projection(proj),
        };

        if log_enabled!(::log::LogLevel::Trace) {
            self.dump_local(lvalue);
        }

        Ok(lvalue)
    }

    fn lvalue_field(
        &mut self,
        base: Lvalue<'tcx>,
        field: usize,
        base_ty: Ty<'tcx>,
        field_ty: Ty<'tcx>,
    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
        let base_layout = self.type_layout(base_ty)?;
        // FIXME(solson)
        let base = self.force_allocation(base)?;
        let (base_ptr, base_extra) = base.to_ptr_and_extra();

        let field_ty = self.monomorphize(field_ty, self.substs());

        use rustc::ty::layout::Layout::*;
        let (offset, packed) = match *base_layout {
            Univariant { ref variant, .. } => {
                (variant.offsets[field], variant.packed)
            },

            General { ref variants, .. } => {
                if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
                    // +1 for the discriminant, which is field 0
                    (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed)
                } else {
                    bug!("field access on enum had no variant index");
                }
            }

            RawNullablePointer { .. } => {
                assert_eq!(field, 0);
                return Ok(base);
            }

            StructWrappedNullablePointer { ref nonnull, .. } => {
                (nonnull.offsets[field], nonnull.packed)
            }

            UntaggedUnion { .. } => return Ok(base),

            Vector { element, count } => {
                let field = field as u64;
                assert!(field < count);
                let elem_size = element.size(&self.tcx.data_layout).bytes();
                (Size::from_bytes(field * elem_size), false)
            }

            _ => bug!("field access on non-product type: {:?}", base_layout),
        };

        let offset = match base_extra {
            LvalueExtra::Vtable(tab) => {
                let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?;
                offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
            }
            _ => offset.bytes(),
        };

        let ptr = base_ptr.offset(offset);

        if packed {
            let size = self.type_size(field_ty)?.expect("packed struct must be sized");
            self.memory.mark_packed(ptr, size);
        }

        let extra = if self.type_is_sized(field_ty) {
            LvalueExtra::None
        } else {
            match base_extra {
                LvalueExtra::None => bug!("expected fat pointer"),
                LvalueExtra::DowncastVariant(..) =>
                    bug!("Rust doesn't support unsized fields in enum variants"),
                LvalueExtra::Vtable(_) |
                LvalueExtra::Length(_) => {},
            }
            base_extra
        };

        Ok(Lvalue::Ptr { ptr, extra })
    }

    fn eval_lvalue_projection(
        &mut self,
        proj: &mir::LvalueProjection<'tcx>,
    ) -> EvalResult<'tcx, Lvalue<'tcx>> {
        use rustc::mir::ProjectionElem::*;
        let (ptr, extra) = match proj.elem {
            Field(field, field_ty) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                return self.lvalue_field(base, field.index(), base_ty, field_ty);
            }

            Downcast(_, variant) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                let base_layout = self.type_layout(base_ty)?;
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, base_extra) = base.to_ptr_and_extra();

                use rustc::ty::layout::Layout::*;
                let extra = match *base_layout {
                    General { .. } => LvalueExtra::DowncastVariant(variant),
                    RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => base_extra,
                    _ => bug!("variant downcast on non-aggregate: {:?}", base_layout),
                };
                (base_ptr, extra)
            }

            Deref => {
                let base_ty = self.lvalue_ty(&proj.base);
                let val = self.eval_and_read_lvalue(&proj.base)?;

                let pointee_type = match base_ty.sty {
                    ty::TyRawPtr(ref tam) |
                    ty::TyRef(_, ref tam) => tam.ty,
                    ty::TyAdt(ref def, _) if def.is_box() => base_ty.boxed_ty(),
                    _ => bug!("can only deref pointer types"),
                };

                trace!("deref to {} on {:?}", pointee_type, val);

                match self.tcx.struct_tail(pointee_type).sty {
                    ty::TyDynamic(..) => {
                        let (ptr, vtable) = val.expect_ptr_vtable_pair(&self.memory)?;
                        (ptr, LvalueExtra::Vtable(vtable))
                    },
                    ty::TyStr | ty::TySlice(_) => {
                        let (ptr, len) = val.expect_slice(&self.memory)?;
                        (ptr, LvalueExtra::Length(len))
                    },
                    _ => (val.read_ptr(&self.memory)?, LvalueExtra::None),
                }
            }

            Index(ref operand) => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, len) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
                let n_ptr = self.eval_operand(operand)?;
                let usize = self.tcx.types.usize;
                let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
                assert!(n < len);
                let ptr = base_ptr.offset(n * elem_size);
                (ptr, LvalueExtra::None)
            }

            ConstantIndex { offset, min_length, from_end } => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, n) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("sequence element must be sized");
                assert!(n >= min_length as u64);

                let index = if from_end {
                    n - u64::from(offset)
                } else {
                    u64::from(offset)
                };

                let ptr = base_ptr.offset(index * elem_size);
                (ptr, LvalueExtra::None)
            }

            Subslice { from, to } => {
                let base = self.eval_lvalue(&proj.base)?;
                let base_ty = self.lvalue_ty(&proj.base);
                // FIXME(solson)
                let base = self.force_allocation(base)?;
                let (base_ptr, _) = base.to_ptr_and_extra();

                let (elem_ty, n) = base.elem_ty_and_len(base_ty);
                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
                assert!(u64::from(from) <= n - u64::from(to));
                let ptr = base_ptr.offset(u64::from(from) * elem_size);
                let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
                (ptr, extra)
            }
        };

        Ok(Lvalue::Ptr { ptr, extra })
    }

    pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
        self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs())
    }
}
<|endoftext|>


<commit_before>/// Gets the offset of a field. Used by container_of!
macro_rules! offset_of(
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
);

/// Gets the parent struct from a pointer.
/// VERY unsafe. The parent struct _must_ be repr(C), and the
/// type passed to this macro _must_ match the type of the parent.
macro_rules! container_of (
    ($ptr: expr, $container: ty, $field: ident) => {
        ($ptr as *mut u8).offset(-(offset_of!($container, $field) as isize)) as *mut $container
    }
);

/// Iterates over a wl_list.
///
/// # Safety
/// It is not safe to delete an element while iterating over the list,
/// so don't do it!
macro_rules! wl_list_for_each {
    ($ptr: expr, $field: ident, ($pos: ident : $container: ty) => $body: block) => {
        let mut $pos: *mut $container;
        $pos = container_of!($ptr.next, $container, $field);
        loop {
            if &(*$pos).$field as *const _ == &$ptr as *const _ {
                break
            }
            { $body }
            $pos = container_of!((*$pos).$field.next, $container, $field);
        }
    }
}

/// Convert a literal string to a C string.
/// Note: Does not check for internal nulls, nor does it do any conversions on
/// the grapheme clustors. Just passes the bytes as is.
/// So probably only works on ASCII.
#[macro_export]
macro_rules! c_str {
    ($s:expr) => {
        concat!($s, "\0").as_ptr() as *const $crate::libc::c_char
    }
}

/// Logs a message using wlroots' logging capability.
///
/// Example:
/// ```rust,no_run,ignore
/// #[macro_use]
/// use wlroots::log::{init_logging, L_DEBUG, L_ERROR};
///
/// // Call this once, at the beginning of your program.
/// init_logging(WLR_DEBUG, None);
///
/// wlr_log!(L_DEBUG, "Hello world");
/// wlr_log!(L_ERROR, "Could not {:#?} the {}", foo, bar);
/// ```
#[macro_export]
macro_rules! wlr_log {
    ($verb: expr, $($msg:tt)*) => {{
        use $crate::wlroots_sys::_wlr_log;
        use $crate::wlroots_sys::wlr_log_importance::*;
        use ::std::ffi::CString;
        unsafe {
            let fmt = CString::new(format!($($msg)*))
                .expect("Could not convert log message to C string");
            let raw = fmt.into_raw();
            _wlr_log($verb, c_str!("[%s:%lu] %s"),
                    c_str!(file!()), line!(), raw);
            // Deallocate string
            CString::from_raw(raw);
        }
    }}
}

/// Defines a new struct that contains a variable number of listeners that
/// will trigger unsafe user-defined callbacks.
///
/// The structure that is defined is repr(C), has one `data` field with the
/// given user type, and a field for each `$listener`.
///
/// Each `$listener` has a getter method that lets you get the pointer to the
/// listener. This method is unsafe, since it returns a raw pointer.
/// To use it correctly, you need to ensure that the data it refers to never
/// moves (e.g keep it in a box). The primary purpose of this method is to pass
/// the listener pointer to other methods to register it for a Wayland event.
/// **A listener can only be registered to one event at a time**.
///
/// Finally, it also takes in a body for each `$listener` that is called
/// every time the event that is later hooked up to it is fired.
/// This method is inherently unsafe, because the user data hasn't been cast
/// from the void pointer yet. It is the user's job to write this safely.
/// To highlight this fact, the body of the function must be prefixed with
/// `unsafe`.
///
/// # Example
/// ```rust,no_run,ignore
/// #[macro_use] extern crate wlroots;
/// extern crate wlroots_sys;
/// #[macro_use] extern crate wayland_sys;
/// extern crate libc;
///
/// use wlroots::InputDevice;
/// use wlroots_sys::wlr_input_device;
///
/// // Handles input addition and removal.
/// pub trait InputManagerHandler {
///     // Callback triggered when an input device is added.
///     fn input_added(&mut self, InputDevice);
/// }
///
/// wayland_listener!(
///     // The name of the structure that will be defined.
///     InputManager,
///     // The type that's stored in the `data` field.
///     // Note that we use a Box here to achieve dynamic dispatch,
///     // it's not required for this type to be in a box.
///     Box<InputManagerHandler>,
///     [
///         // Adds a new listener called `add_listener`.
///         // Adds an unsafe function called `add_notify` that is triggered
///         // whenever add_listener is activated from a Wayland event.
///         add_listener => add_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
///             let ref mut manager = this.data;
///             // Call the method defined above, wrapping it in a safe interface.
///             // It is your job to ensure that the code in here doesn't trigger UB!
///             manager.input_added(InputDevice::from_ptr(data as *mut wlr_input_device))
///         };
///     ]
/// );
/// # fn main() {}
/// ```
///
/// # Unsafety
/// Note that the purpose of this macro is to make it easy to generate unsafe
/// boiler plate for using listeners with Rust data.
///
/// However, there are a few things this macro doesn't protect against.
///
/// First and foremost, the data cannot move. The listeners assume that the
/// structure will never move, so in order to defend against this the generated
/// `new` method returns a Box version. **Do not move out of the box**.
///
/// Second, this macro doesn't protect against the stored data being unsized.
/// Passing a pointer of unsized data to C is UB, don't do it.
macro_rules! wayland_listener {
    ($struct_name: ident, $data: ty, $([
        $($listener: ident => $listener_func: ident :
          |$($func_arg:ident: $func_type:ty,)*| unsafe $body: block;)*])+) => {
        #[repr(C)]
        pub struct $struct_name {
            data: $data,
            $($($listener: $crate::wlroots_sys::wl_listener),*)*
        }

        impl $struct_name {
            pub(crate) fn new(data: $data) -> Box<$struct_name> {
                use $crate::wlroots_sys::server::WAYLAND_SERVER_HANDLE;
                Box::new($struct_name {
                    data,
                    $($($listener: unsafe {
                        // NOTE Rationale for zeroed memory:
                        // * Need to pass a pointer to wl_list_init
                        // * The list is initialized by Wayland, which doesn't "drop"
                        // * The listener is written to without dropping any of the data
                        let mut listener: $crate::wlroots_sys::wl_listener = ::std::mem::zeroed();
                        ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                      wl_list_init,
                                      &mut listener.link as *mut _ as _);
                        ::std::ptr::write(&mut listener.notify, Some($struct_name::$listener_func));
                        listener
                    }),*)*
                })
            }

            $($(pub(crate) unsafe extern "C" fn $listener(&mut self)
                                                   -> *mut $crate::wlroots_sys::wl_listener {
                &mut self.$listener as *mut _
            })*)*

            $($(pub(crate) unsafe extern "C" fn $listener_func(listener:
                                                        *mut $crate::wlroots_sys::wl_listener,
                                                        data: *mut $crate::libc::c_void) {
                let manager: &mut $struct_name = &mut (*container_of!(listener,
                                                                      $struct_name,
                                                                      $listener));
                $crate::utils::handle_unwind(
                    ::std::panic::catch_unwind(
                        ::std::panic::AssertUnwindSafe(|| {
                            (|$($func_arg: $func_type,)*| { $body })(manager, data)
                        })));
            })*)*
        }
    }
}

/// Used to indicate what data is global compositor data.
/// It will automatically implement the CompositorData trait for the struct,
/// and also add a method to `Compositor` to unwrap the data from the fat
/// pointer.
#[macro_export]
macro_rules! compositor_data {
    ($struct_name: ty) => {
        impl<'a>::std::convert::From<&'a mut $crate::Compositor> for &'a mut $struct_name {
            fn from(compositor: &'a mut $crate::Compositor) -> &'a mut $struct_name {
                &mut *compositor.data.downcast_mut::<$struct_name>()
                    .unwrap_or_else(|| {
                        wlr_log!(WLR_ERROR, "Could not cast compositor state to {:#?}",
                                 stringify!($struct_name));
                        panic!("Could not cast compositor state to correct value")
                    })
            }
        }
    }
}

/// A convenience macro designed for use with Handle types.
///
/// This allows you to avoid the rightward drift of death that is often found
/// with heavily nested callback systems.
///
/// Any `HandleResult`s are flattened and the first one encountered is immediately
/// returned before any of the `$body` code is executed.
///
/// Order of evaluation is from left to right. It is possible to refer to the previous
/// result, as commonly found in Lisp's `let*` macro.
///
/// An example of simple use:
///
/// ```rust,ignore
/// with_handles!([(compositor: {compositor}),
///    (output: {&mut result.output_handle})] => {
///    ...
/// })
/// ```
///
/// A more complex use:
///
/// ```rust,ignore
/// with_handles!([(shell: {shell_handle}),
///    // Notice how we use the previous result to get the surface.
///    (surface: {shell.surface_handle})] => {
///    ...
/// })
/// ```
///
#[macro_export]
macro_rules! with_handles {
    ([($handle_name: ident: $unhandle_name: block)] => $body: block) => {
        $unhandle_name.run(|$handle_name| {
            $body
        })
    };
    ([($handle_name1: ident: $unhandle_name1: block),
      ($handle_name2: ident: $unhandle_name2: block),
      $($rest: tt)*] => $body: block) => {
        $unhandle_name1.run(|$handle_name1| {
            with_handles!([($handle_name2: $unhandle_name2), $($rest)*] => $body)
        }).and_then(|n: $crate::HandleResult<_>| n)
    };
    ([($handle_name: ident: $unhandle_name: block), $($rest: tt)*] => $body: block) => {
        $unhandle_name.run(|$handle_name| {
            with_handles!([$($rest)*] => $body)
        }).and_then(|n: $crate::HandleResult<_>| n)
    };
}
<commit_msg>I can hide the c_str macro :D<commit_after>/// Gets the offset of a field. Used by container_of!
macro_rules! offset_of(
    ($ty:ty, $field:ident) => {
        &(*(0 as *const $ty)).$field as *const _ as usize
    }
);

/// Gets the parent struct from a pointer.
/// VERY unsafe. The parent struct _must_ be repr(C), and the
/// type passed to this macro _must_ match the type of the parent.
macro_rules! container_of (
    ($ptr: expr, $container: ty, $field: ident) => {
        ($ptr as *mut u8).offset(-(offset_of!($container, $field) as isize)) as *mut $container
    }
);

/// Iterates over a wl_list.
///
/// # Safety
/// It is not safe to delete an element while iterating over the list,
/// so don't do it!
macro_rules! wl_list_for_each {
    ($ptr: expr, $field: ident, ($pos: ident : $container: ty) => $body: block) => {
        let mut $pos: *mut $container;
        $pos = container_of!($ptr.next, $container, $field);
        loop {
            if &(*$pos).$field as *const _ == &$ptr as *const _ {
                break
            }
            { $body }
            $pos = container_of!((*$pos).$field.next, $container, $field);
        }
    }
}

/// Logs a message using wlroots' logging capability.
///
/// Example:
/// ```rust,no_run,ignore
/// #[macro_use]
/// use wlroots::log::{init_logging, L_DEBUG, L_ERROR};
///
/// // Call this once, at the beginning of your program.
/// init_logging(WLR_DEBUG, None);
///
/// wlr_log!(L_DEBUG, "Hello world");
/// wlr_log!(L_ERROR, "Could not {:#?} the {}", foo, bar);
/// ```
#[macro_export]
macro_rules! wlr_log {
    ($verb: expr, $($msg:tt)*) => {{
        /// Convert a literal string to a C string.
        /// Note: Does not check for internal nulls, nor does it do any conversions on
        /// the grapheme clustors. Just passes the bytes as is.
        /// So probably only works on ASCII.
        macro_rules! c_str {
            ($s:expr) => {
                concat!($s, "\0").as_ptr() as *const $crate::libc::c_char
            }
        }
        use $crate::wlroots_sys::_wlr_log;
        use $crate::wlroots_sys::wlr_log_importance::*;
        use ::std::ffi::CString;
        unsafe {
            let fmt = CString::new(format!($($msg)*))
                .expect("Could not convert log message to C string");
            let raw = fmt.into_raw();
            _wlr_log($verb, c_str!("[%s:%lu] %s"),
                    c_str!(file!()), line!(), raw);
            // Deallocate string
            CString::from_raw(raw);
        }
    }}
}

/// Defines a new struct that contains a variable number of listeners that
/// will trigger unsafe user-defined callbacks.
///
/// The structure that is defined is repr(C), has one `data` field with the
/// given user type, and a field for each `$listener`.
///
/// Each `$listener` has a getter method that lets you get the pointer to the
/// listener. This method is unsafe, since it returns a raw pointer.
/// To use it correctly, you need to ensure that the data it refers to never
/// moves (e.g keep it in a box). The primary purpose of this method is to pass
/// the listener pointer to other methods to register it for a Wayland event.
/// **A listener can only be registered to one event at a time**.
///
/// Finally, it also takes in a body for each `$listener` that is called
/// every time the event that is later hooked up to it is fired.
/// This method is inherently unsafe, because the user data hasn't been cast
/// from the void pointer yet. It is the user's job to write this safely.
/// To highlight this fact, the body of the function must be prefixed with
/// `unsafe`.
///
/// # Example
/// ```rust,no_run,ignore
/// #[macro_use] extern crate wlroots;
/// extern crate wlroots_sys;
/// #[macro_use] extern crate wayland_sys;
/// extern crate libc;
///
/// use wlroots::InputDevice;
/// use wlroots_sys::wlr_input_device;
///
/// // Handles input addition and removal.
/// pub trait InputManagerHandler {
///     // Callback triggered when an input device is added.
///     fn input_added(&mut self, InputDevice);
/// }
///
/// wayland_listener!(
///     // The name of the structure that will be defined.
///     InputManager,
///     // The type that's stored in the `data` field.
///     // Note that we use a Box here to achieve dynamic dispatch,
///     // it's not required for this type to be in a box.
///     Box<InputManagerHandler>,
///     [
///         // Adds a new listener called `add_listener`.
///         // Adds an unsafe function called `add_notify` that is triggered
///         // whenever add_listener is activated from a Wayland event.
///         add_listener => add_notify: |this: &mut InputManager, data: *mut libc::c_void,| unsafe {
///             let ref mut manager = this.data;
///             // Call the method defined above, wrapping it in a safe interface.
///             // It is your job to ensure that the code in here doesn't trigger UB!
///             manager.input_added(InputDevice::from_ptr(data as *mut wlr_input_device))
///         };
///     ]
/// );
/// # fn main() {}
/// ```
///
/// # Unsafety
/// Note that the purpose of this macro is to make it easy to generate unsafe
/// boiler plate for using listeners with Rust data.
///
/// However, there are a few things this macro doesn't protect against.
///
/// First and foremost, the data cannot move. The listeners assume that the
/// structure will never move, so in order to defend against this the generated
/// `new` method returns a Box version. **Do not move out of the box**.
///
/// Second, this macro doesn't protect against the stored data being unsized.
/// Passing a pointer of unsized data to C is UB, don't do it.
macro_rules! wayland_listener {
    ($struct_name: ident, $data: ty, $([
        $($listener: ident => $listener_func: ident :
          |$($func_arg:ident: $func_type:ty,)*| unsafe $body: block;)*])+) => {
        #[repr(C)]
        pub struct $struct_name {
            data: $data,
            $($($listener: $crate::wlroots_sys::wl_listener),*)*
        }

        impl $struct_name {
            pub(crate) fn new(data: $data) -> Box<$struct_name> {
                use $crate::wlroots_sys::server::WAYLAND_SERVER_HANDLE;
                Box::new($struct_name {
                    data,
                    $($($listener: unsafe {
                        // NOTE Rationale for zeroed memory:
                        // * Need to pass a pointer to wl_list_init
                        // * The list is initialized by Wayland, which doesn't "drop"
                        // * The listener is written to without dropping any of the data
                        let mut listener: $crate::wlroots_sys::wl_listener = ::std::mem::zeroed();
                        ffi_dispatch!(WAYLAND_SERVER_HANDLE,
                                      wl_list_init,
                                      &mut listener.link as *mut _ as _);
                        ::std::ptr::write(&mut listener.notify, Some($struct_name::$listener_func));
                        listener
                    }),*)*
                })
            }

            $($(pub(crate) unsafe extern "C" fn $listener(&mut self)
                                                   -> *mut $crate::wlroots_sys::wl_listener {
                &mut self.$listener as *mut _
            })*)*

            $($(pub(crate) unsafe extern "C" fn $listener_func(listener:
                                                        *mut $crate::wlroots_sys::wl_listener,
                                                        data: *mut $crate::libc::c_void) {
                let manager: &mut $struct_name = &mut (*container_of!(listener,
                                                                      $struct_name,
                                                                      $listener));
                $crate::utils::handle_unwind(
                    ::std::panic::catch_unwind(
                        ::std::panic::AssertUnwindSafe(|| {
                            (|$($func_arg: $func_type,)*| { $body })(manager, data)
                        })));
            })*)*
        }
    }
}

/// Used to indicate what data is global compositor data.
/// It will automatically implement the CompositorData trait for the struct,
/// and also add a method to `Compositor` to unwrap the data from the fat
/// pointer.
#[cfg(feature = "unstable")]
#[macro_export]
macro_rules! compositor_data {
    ($struct_name: ty) => {
        impl<'a>::std::convert::From<&'a mut $crate::Compositor> for &'a mut $struct_name {
            fn from(compositor: &'a mut $crate::Compositor) -> &'a mut $struct_name {
                &mut *compositor.data.downcast_mut::<$struct_name>()
                    .unwrap_or_else(|| {
                        wlr_log!(WLR_ERROR, "Could not cast compositor state to {:#?}",
                                 stringify!($struct_name));
                        panic!("Could not cast compositor state to correct value")
                    })
            }
        }
    }
}

/// A convenience macro designed for use with Handle types.
///
/// This allows you to avoid the rightward drift of death that is often found
/// with heavily nested callback systems.
///
/// Any `HandleResult`s are flattened and the first one encountered is immediately
/// returned before any of the `$body` code is executed.
///
/// Order of evaluation is from left to right. It is possible to refer to the previous
/// result, as commonly found in Lisp's `let*` macro.
///
/// An example of simple use:
///
/// ```rust,ignore
/// with_handles!([(compositor: {compositor}),
///    (output: {&mut result.output_handle})] => {
///    ...
/// })
/// ```
///
/// A more complex use:
///
/// ```rust,ignore
/// with_handles!([(shell: {shell_handle}),
///    // Notice how we use the previous result to get the surface.
///    (surface: {shell.surface_handle})] => {
///    ...
/// })
/// ```
///
#[cfg(feature = "unstable")]
#[macro_export]
macro_rules! with_handles {
    ([($handle_name: ident: $unhandle_name: block)] => $body: block) => {
        $unhandle_name.run(|$handle_name| {
            $body
        })
    };
    ([($handle_name1: ident: $unhandle_name1: block),
      ($handle_name2: ident: $unhandle_name2: block),
      $($rest: tt)*] => $body: block) => {
        $unhandle_name1.run(|$handle_name1| {
            with_handles!([($handle_name2: $unhandle_name2), $($rest)*] => $body)
        }).and_then(|n: $crate::HandleResult<_>| n)
    };
    ([($handle_name: ident: $unhandle_name: block), $($rest: tt)*] => $body: block) => {
        $unhandle_name.run(|$handle_name| {
            with_handles!([$($rest)*] => $body)
        }).and_then(|n: $crate::HandleResult<_>| n)
    };
}
<|endoftext|>


<commit_before>macro_rules! io_error {
    ($fmt:expr) =>
        (
            Err(io::Error::new(io::ErrorKind::Other, $fmt))
        );

    ($fmt:expr, $($arg:tt)*) =>
        (
            Err(io::Error::new(io::ErrorKind::Other, format!($fmt, $($arg)*)))
        );
}
<commit_msg>goblin: remove macros module<commit_after><|endoftext|>


<commit_before>///
/// A basic macro to ease creation and management
/// of Hooks
///
#[macro_export]
macro_rules! hook {
    ($hook_name:expr, $fn_name:ident) => {
        $crate::Hook {
            name: $hook_name.to_string(),
            callback: $fn_name,
        }
    };
    ($hook_name:ident $fn_name:ident) => {
        $crate::Hook {
            name: stringify!($hook_name).to_string(),
            callback: $fn_name,
        }
    };
}

#[cfg(test)]
mod tests{
    use super::super::Hook;
    fn cb() -> Result<(),String> {
        Ok(())
    }
    #[test]
    fn it_makes_a_hook_correctly() {
        let h1 = hook!(test cb);
        let h2 = Hook {
            name: "test".to_string(),
            callback: cb,
        };
        assert_eq!(h1, h2);
    }

    #[test]
    fn it_makes_a_complex_named_hook_correctly() {
        let h1 = hook!("config-changed", cb);
        let h2 = Hook {
            name: "config-changed".to_string(),
            callback: cb,
        };
        assert_eq!(h1, h2);
    }
}<commit_msg>add status_set! macro<commit_after>///
/// A basic macro to ease creation and management
/// of Hooks
///
#[macro_export]
macro_rules! hook {
    ($hook_name:expr, $fn_name:ident) => {
        $crate::Hook {
            name: $hook_name.to_string(),
            callback: $fn_name,
        }
    };
    ($hook_name:ident $fn_name:ident) => {
        $crate::Hook {
            name: stringify!($hook_name).to_string(),
            callback: $fn_name,
        }
    };
}

#[macro_export]
macro_rules! status_set {
    ($status_type:ident $message:expr) => {
        $crate::status_set(
            $crate::Status {
                status_type: $crate::StatusType::$status_type,
                message: $message.to_string()
            }
        )
    }
}

#[cfg(test)]
mod tests{
    #[allow(dead_code)]
    mod status_set {
        fn it_compiles_correctly() {
            let _ = status_set!(Maintenance "Doing stuff");
        }
    }
    use super::super::Hook;
    fn cb() -> Result<(),String> {
        Ok(())
    }
    #[test]
    fn it_makes_a_hook_correctly() {
        let h1 = hook!(test cb);
        let h2 = Hook {
            name: "test".to_string(),
            callback: cb,
        };
        assert_eq!(h1, h2);
    }

    #[test]
    fn it_makes_a_complex_named_hook_correctly() {
        let h1 = hook!("config-changed", cb);
        let h2 = Hook {
            name: "config-changed".to_string(),
            callback: cb,
        };
        assert_eq!(h1, h2);
    }
}<|endoftext|>