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

use std::marker::PhantomData;
use std::mem;
use std::ptr::NonNull;

/// A trait that can be used to ensure that users of the boringssl module can't
/// implement a trait.
///
/// See the [API Guidelines] for details.
///
/// [API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub trait Sealed {}

macro_rules! sealed {
    ($name:ident) => {
        impl ::boringssl::wrapper::Sealed for ::boringssl::raw::ffi::$name {}
    };
}

macro_rules! impl_traits {
    (@inner $name:ident, CNew => $fn:tt) => {
        c_new!($name, $fn);
    };
    (@inner $name:ident, CUpRef => $fn:tt) => {
        c_up_ref!($name, $fn);
    };
    (@inner $name:ident, CFree => $fn:tt) => {
        c_free!($name, $fn);
    };
    (@inner $name:ident, CInit => $fn:tt) => {
        c_init!($name, $fn);
    };
    (@inner $name:ident, CDestruct => $fn:tt) => {
        c_destruct!($name, $fn);
    };
    (@inner $name:ident, $trait:ident => $fn:tt) => {
        compile_error!(concat!("unrecognized trait ", stringify!($trait)));
    };
    ($name:ident, $($trait:ident => $fn:tt),*) => {
        sealed!($name);
        $(impl_traits!(@inner $name, $trait => $fn);)*
    };
}

/// A C object from the BoringSSL API which can be allocated and constructed.
pub unsafe trait CNew: Sealed {
    /// Returns a new, constructed, heap-allocated object, or NULL on failure.
    ///
    /// This should not be called directly; instead, use `new`.
    #[deprecated = "do not call new_raw directly; instead, call new"]
    unsafe fn new_raw() -> *mut Self;

    /// Returns a new, constructed, heap-allocated object, or `None` on failure.
    #[must_use]
    unsafe fn new() -> Option<NonNull<Self>> {
        #[allow(deprecated)]
        NonNull::new(Self::new_raw())
    }
}

macro_rules! c_new {
    ($name:ident, $new:ident) => {
        unsafe impl ::boringssl::wrapper::CNew for ::boringssl::raw::ffi::$name {
            unsafe fn new_raw() -> *mut Self {
                ::boringssl::raw::ffi::$new()
            }
        }
    };
}

/// A C object from the BoringSSL API which has a reference count that can be
/// increased.
pub unsafe trait CUpRef: Sealed {
    /// Increases an object's reference count.
    unsafe fn up_ref(slf: *mut Self);
}

macro_rules! c_up_ref {
    ($name:ident, $up_ref:ident) => {
        unsafe impl ::boringssl::wrapper::CUpRef for ::boringssl::raw::ffi::$name {
            unsafe fn up_ref(slf: *mut Self) {
                use boringssl::abort::UnwrapAbort;
                ::boringssl::raw::one_or_err(
                    stringify!($up_ref),
                    ::boringssl::raw::ffi::$up_ref(slf),
                )
                .unwrap_abort()
            }
        }
    };
}

/// A C object from the BoringSSL API which can be freed.
pub unsafe trait CFree: Sealed {
    /// Frees a heap-allocated object.
    ///
    /// If this is a reference-counted object, `free` decrements the reference
    /// count, and frees the object if it reaches zero. Otherwise, if this is
    /// not a reference-counted object, it frees it.
    unsafe fn free(slf: *mut Self);
}

macro_rules! c_free {
    ($name:ident, $free:ident) => {
        unsafe impl ::boringssl::wrapper::CFree for ::boringssl::raw::ffi::$name {
            unsafe fn free(slf: *mut Self) {
                ::boringssl::raw::ffi::$free(slf)
            }
        }
    };
}

/// A C object from the BoringSSL API which can be initialized.
pub unsafe trait CInit: Sealed {
    /// Initializes an uninitialized object.
    ///
    /// # Safety
    ///
    /// `init` must not be called on an initialized object.
    unsafe fn init(slf: *mut Self);
}

#[allow(unused)] // TODO: Remove once it's used in the 'raw' module
macro_rules! c_init {
    ($name:ident, $init:ident) => {
        unsafe impl ::boringssl::wrapper::CInit for ::boringssl::raw::ffi::$name {
            unsafe fn init(slf: *mut Self) {
                ::boringssl::raw::ffi::$init(slf)
            }
        }
    };
}

/// A C object from the BoringSSL API which can be destructed.
pub unsafe trait CDestruct: Sealed {
    /// Destructs an initialized object.
    ///
    /// # Safety
    ///
    /// `slf` must be an initialized object. After a call to `destruct`, `slf`
    /// is uninitialized.
    unsafe fn destruct(slf: *mut Self);
}

macro_rules! c_destruct {
    ($name:ident, _) => {
        unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
            unsafe fn destruct(_slf: *mut Self) {}
        }
    };
    ($name:ident, $destruct:tt) => {
        unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
            unsafe fn destruct(slf: *mut Self) {
                ::boringssl::raw::ffi::$destruct(slf)
            }
        }
    };
}

/// A wrapper around a pointer to a heap-allocated, constructed C object from
/// the BoringSSL API.
///
/// `CHeapWrapper` maintains the invariant that the object it references is
/// always allocated and constructed. This means that:
/// - If the object can be reference counted, `CHeapWrapper` implements `Clone`
///   by incrementing the reference count, and decrementing on `Drop`.
/// - If the object cannot be reference counted, `CHeapWrapper` does not
///   implement `Clone`, but will still free the object on `Drop`.
///
/// `CHeapWrapper`s are not thread-safe; they do not implement `Send` or `Sync`.
pub struct CHeapWrapper<C: CFree> {
    // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
    // is changed, make sure it's still !Send + !Sync.
    obj: NonNull<C>,
}

impl<C: CFree> CHeapWrapper<C> {
    /// Takes ownership of a constructed object.
    ///
    /// # Safety
    ///
    /// `obj` must point to an allocated, constructed object. The caller must
    /// ensure that, when the returned `CHeapWrapper` is dropped, it is safe to
    /// call `C::free` on `obj`. In most cases, this means that the caller
    /// should not free `obj`, and instead consider ownership of `obj` to have
    /// transferred to the new `CHeapWrapper`.
    ///
    /// The caller must also ensure that no pointers to the object will ever be
    /// used by other threads so long as this `CHeapWrapper` exists.
    #[must_use]
    pub unsafe fn new_from(obj: NonNull<C>) -> CHeapWrapper<C> {
        CHeapWrapper { obj }
    }

    #[must_use]
    pub fn as_mut(&mut self) -> *mut C {
        self.obj.as_ptr()
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        self.obj.as_ptr()
    }

    /// Consumes this `CHeapWrapper` and return the underlying pointer.
    ///
    /// The object will not be freed. Instead, the caller takes logical
    /// ownership of the object.
    #[must_use]
    pub fn into_mut(self) -> *mut C {
        // NOTE: This method safe for the same reason that mem::forget is safe:
        // it's equivalent to sending it to a thread that goes to sleep forever
        // or creating a Rc cycle or some other silly-but-safe behavior.
        let ptr = self.obj.as_ptr();
        mem::forget(self);
        ptr
    }
}

impl<C: CNew + CFree> Default for CHeapWrapper<C> {
    fn default() -> CHeapWrapper<C> {
        // TODO(joshlf): In order for this to be safe, CNew must provide the
        // safety guarantee that it's always safe to call CNew::new and then
        // later to call CFree::free on that object (e.g., see the safety
        // comment on CStackWrapper::new).
        unsafe {
            use boringssl::abort::UnwrapAbort;
            let obj = C::new().expect_abort("could not allocate object");
            CHeapWrapper { obj }
        }
    }
}

impl<C: CUpRef + CFree> Clone for CHeapWrapper<C> {
    fn clone(&self) -> CHeapWrapper<C> {
        unsafe { C::up_ref(self.obj.as_ptr()) };
        CHeapWrapper { obj: self.obj }
    }
}

impl<C: CFree> Drop for CHeapWrapper<C> {
    fn drop(&mut self) {
        unsafe { C::free(self.obj.as_ptr()) };
    }
}

/// A wrapper around a pointer to a C object from the BoringSSL API.
///
/// Unlike `CHeapWrapper` or `CStackWrapper`, `CRef` does not own the pointed-to
/// object, but merely borrows it like a normal Rust reference. The only reason
/// to use `CRef<C>` instead of a `&C` is to make it so that access to the `C`
/// is unsafe, as `CRef` only exposes a raw pointer accessor for its object.
///
/// `CRef` maintains the invariant that the object it references is always
/// allocated and constructed, and that mutable access to the object is disabled
/// for the lifetime of the `CRef`.
pub struct CRef<'a, C> {
    // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
    // is changed, make sure it's still !Send + !Sync.
    obj: NonNull<C>,
    // Make sure CRef has the lifetime 'a.
    _lifetime: PhantomData<&'a ()>,
}

impl<'a, C> CRef<'a, C> {
    /// Creates a new `CRef` from a raw pointer.
    ///
    /// # Safety
    ///
    /// `obj` must point to an allocated, constructed object. The caller must
    /// ensure that, for the lifetime, `'a`, `obj` will continue to point to the
    /// same allocated, constructed object, and that mutable access to the
    /// object will be disallowed.
    ///
    /// The caller must also ensure that no other pointers to the object will
    /// ever be sent to other threads so long as this `CRef` exists.
    #[must_use]
    pub unsafe fn new(obj: NonNull<C>) -> CRef<'a, C> {
        CRef {
            obj,
            _lifetime: PhantomData,
        }
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        self.obj.as_ptr()
    }
}

/// A wrapper around a constructed C object from the BoringSSL API.
///
/// `CStackWrapper` maintains the invariant that the object it contains is
/// always constructed. The object is destructed on `Drop`.
///
/// `CStackWrapper`s are not thread-safe; they do not implement `Send` or
/// `Sync`.
pub struct CStackWrapper<C: CDestruct> {
    obj: C,
    // Make sure CStackWrapper doesn't implement Send or Sync regardless of C.
    _no_sync: PhantomData<*mut ()>,
}

impl<C: CDestruct> CStackWrapper<C> {
    /// Constructs a new `CStackWrapper`.
    ///
    /// # Safety
    ///
    /// `obj` must be constructed, and it must be safe for `C::destruct` to be
    /// called on `obj` when this `CStackWrapper` is dropped.
    #[must_use]
    pub unsafe fn new(obj: C) -> CStackWrapper<C> {
        CStackWrapper {
            obj,
            _no_sync: PhantomData,
        }
    }

    #[must_use]
    pub fn as_mut(&mut self) -> *mut C {
        &mut self.obj
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        &self.obj
    }
}

impl<C: CInit + CDestruct> Default for CStackWrapper<C> {
    // TODO(joshlf): In order for this to be safe, CInit must provide the safety
    // guarantee that it's always safe to call CInit::init and then later to
    // call CDestruct::destruct on that object (e.g., see the safety comment on
    // CStackWrapper::new).
    fn default() -> CStackWrapper<C> {
        unsafe {
            let mut obj: C = mem::uninitialized();
            C::init(&mut obj);
            CStackWrapper {
                obj,
                _no_sync: PhantomData,
            }
        }
    }
}

impl<C: CDestruct> Drop for CStackWrapper<C> {
    fn drop(&mut self) {
        unsafe { C::destruct(&mut self.obj) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use boringssl::EC_KEY;

    #[test]
    fn test_heap_wrapper_into_mut() {
        // Test that CHeapWrapper::into_mut doesn't free the pointer. If it
        // does, then EC_KEY::free is likely (though not guaranteed) to abort
        // when it finds the refcount at 0.
        let key = CHeapWrapper::<EC_KEY>::default();
        unsafe { EC_KEY::free(key.into_mut()) };
    }
}
<commit_msg>Fix improper syntax in 'deprecated' attribute<commit_after>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

use std::marker::PhantomData;
use std::mem;
use std::ptr::NonNull;

/// A trait that can be used to ensure that users of the boringssl module can't
/// implement a trait.
///
/// See the [API Guidelines] for details.
///
/// [API Guidelines]: https://rust-lang-nursery.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub trait Sealed {}

macro_rules! sealed {
    ($name:ident) => {
        impl ::boringssl::wrapper::Sealed for ::boringssl::raw::ffi::$name {}
    };
}

macro_rules! impl_traits {
    (@inner $name:ident, CNew => $fn:tt) => {
        c_new!($name, $fn);
    };
    (@inner $name:ident, CUpRef => $fn:tt) => {
        c_up_ref!($name, $fn);
    };
    (@inner $name:ident, CFree => $fn:tt) => {
        c_free!($name, $fn);
    };
    (@inner $name:ident, CInit => $fn:tt) => {
        c_init!($name, $fn);
    };
    (@inner $name:ident, CDestruct => $fn:tt) => {
        c_destruct!($name, $fn);
    };
    (@inner $name:ident, $trait:ident => $fn:tt) => {
        compile_error!(concat!("unrecognized trait ", stringify!($trait)));
    };
    ($name:ident, $($trait:ident => $fn:tt),*) => {
        sealed!($name);
        $(impl_traits!(@inner $name, $trait => $fn);)*
    };
}

/// A C object from the BoringSSL API which can be allocated and constructed.
pub unsafe trait CNew: Sealed {
    /// Returns a new, constructed, heap-allocated object, or NULL on failure.
    ///
    /// This should not be called directly; instead, use `new`.
    #[deprecated(note = "do not call new_raw directly; instead, call new")]
    unsafe fn new_raw() -> *mut Self;

    /// Returns a new, constructed, heap-allocated object, or `None` on failure.
    #[must_use]
    unsafe fn new() -> Option<NonNull<Self>> {
        #[allow(deprecated)]
        NonNull::new(Self::new_raw())
    }
}

macro_rules! c_new {
    ($name:ident, $new:ident) => {
        unsafe impl ::boringssl::wrapper::CNew for ::boringssl::raw::ffi::$name {
            unsafe fn new_raw() -> *mut Self {
                ::boringssl::raw::ffi::$new()
            }
        }
    };
}

/// A C object from the BoringSSL API which has a reference count that can be
/// increased.
pub unsafe trait CUpRef: Sealed {
    /// Increases an object's reference count.
    unsafe fn up_ref(slf: *mut Self);
}

macro_rules! c_up_ref {
    ($name:ident, $up_ref:ident) => {
        unsafe impl ::boringssl::wrapper::CUpRef for ::boringssl::raw::ffi::$name {
            unsafe fn up_ref(slf: *mut Self) {
                use boringssl::abort::UnwrapAbort;
                ::boringssl::raw::one_or_err(
                    stringify!($up_ref),
                    ::boringssl::raw::ffi::$up_ref(slf),
                )
                .unwrap_abort()
            }
        }
    };
}

/// A C object from the BoringSSL API which can be freed.
pub unsafe trait CFree: Sealed {
    /// Frees a heap-allocated object.
    ///
    /// If this is a reference-counted object, `free` decrements the reference
    /// count, and frees the object if it reaches zero. Otherwise, if this is
    /// not a reference-counted object, it frees it.
    unsafe fn free(slf: *mut Self);
}

macro_rules! c_free {
    ($name:ident, $free:ident) => {
        unsafe impl ::boringssl::wrapper::CFree for ::boringssl::raw::ffi::$name {
            unsafe fn free(slf: *mut Self) {
                ::boringssl::raw::ffi::$free(slf)
            }
        }
    };
}

/// A C object from the BoringSSL API which can be initialized.
pub unsafe trait CInit: Sealed {
    /// Initializes an uninitialized object.
    ///
    /// # Safety
    ///
    /// `init` must not be called on an initialized object.
    unsafe fn init(slf: *mut Self);
}

#[allow(unused)] // TODO: Remove once it's used in the 'raw' module
macro_rules! c_init {
    ($name:ident, $init:ident) => {
        unsafe impl ::boringssl::wrapper::CInit for ::boringssl::raw::ffi::$name {
            unsafe fn init(slf: *mut Self) {
                ::boringssl::raw::ffi::$init(slf)
            }
        }
    };
}

/// A C object from the BoringSSL API which can be destructed.
pub unsafe trait CDestruct: Sealed {
    /// Destructs an initialized object.
    ///
    /// # Safety
    ///
    /// `slf` must be an initialized object. After a call to `destruct`, `slf`
    /// is uninitialized.
    unsafe fn destruct(slf: *mut Self);
}

macro_rules! c_destruct {
    ($name:ident, _) => {
        unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
            unsafe fn destruct(_slf: *mut Self) {}
        }
    };
    ($name:ident, $destruct:tt) => {
        unsafe impl ::boringssl::wrapper::CDestruct for ::boringssl::raw::ffi::$name {
            unsafe fn destruct(slf: *mut Self) {
                ::boringssl::raw::ffi::$destruct(slf)
            }
        }
    };
}

/// A wrapper around a pointer to a heap-allocated, constructed C object from
/// the BoringSSL API.
///
/// `CHeapWrapper` maintains the invariant that the object it references is
/// always allocated and constructed. This means that:
/// - If the object can be reference counted, `CHeapWrapper` implements `Clone`
///   by incrementing the reference count, and decrementing on `Drop`.
/// - If the object cannot be reference counted, `CHeapWrapper` does not
///   implement `Clone`, but will still free the object on `Drop`.
///
/// `CHeapWrapper`s are not thread-safe; they do not implement `Send` or `Sync`.
pub struct CHeapWrapper<C: CFree> {
    // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
    // is changed, make sure it's still !Send + !Sync.
    obj: NonNull<C>,
}

impl<C: CFree> CHeapWrapper<C> {
    /// Takes ownership of a constructed object.
    ///
    /// # Safety
    ///
    /// `obj` must point to an allocated, constructed object. The caller must
    /// ensure that, when the returned `CHeapWrapper` is dropped, it is safe to
    /// call `C::free` on `obj`. In most cases, this means that the caller
    /// should not free `obj`, and instead consider ownership of `obj` to have
    /// transferred to the new `CHeapWrapper`.
    ///
    /// The caller must also ensure that no pointers to the object will ever be
    /// used by other threads so long as this `CHeapWrapper` exists.
    #[must_use]
    pub unsafe fn new_from(obj: NonNull<C>) -> CHeapWrapper<C> {
        CHeapWrapper { obj }
    }

    #[must_use]
    pub fn as_mut(&mut self) -> *mut C {
        self.obj.as_ptr()
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        self.obj.as_ptr()
    }

    /// Consumes this `CHeapWrapper` and return the underlying pointer.
    ///
    /// The object will not be freed. Instead, the caller takes logical
    /// ownership of the object.
    #[must_use]
    pub fn into_mut(self) -> *mut C {
        // NOTE: This method safe for the same reason that mem::forget is safe:
        // it's equivalent to sending it to a thread that goes to sleep forever
        // or creating a Rc cycle or some other silly-but-safe behavior.
        let ptr = self.obj.as_ptr();
        mem::forget(self);
        ptr
    }
}

impl<C: CNew + CFree> Default for CHeapWrapper<C> {
    fn default() -> CHeapWrapper<C> {
        // TODO(joshlf): In order for this to be safe, CNew must provide the
        // safety guarantee that it's always safe to call CNew::new and then
        // later to call CFree::free on that object (e.g., see the safety
        // comment on CStackWrapper::new).
        unsafe {
            use boringssl::abort::UnwrapAbort;
            let obj = C::new().expect_abort("could not allocate object");
            CHeapWrapper { obj }
        }
    }
}

impl<C: CUpRef + CFree> Clone for CHeapWrapper<C> {
    fn clone(&self) -> CHeapWrapper<C> {
        unsafe { C::up_ref(self.obj.as_ptr()) };
        CHeapWrapper { obj: self.obj }
    }
}

impl<C: CFree> Drop for CHeapWrapper<C> {
    fn drop(&mut self) {
        unsafe { C::free(self.obj.as_ptr()) };
    }
}

/// A wrapper around a pointer to a C object from the BoringSSL API.
///
/// Unlike `CHeapWrapper` or `CStackWrapper`, `CRef` does not own the pointed-to
/// object, but merely borrows it like a normal Rust reference. The only reason
/// to use `CRef<C>` instead of a `&C` is to make it so that access to the `C`
/// is unsafe, as `CRef` only exposes a raw pointer accessor for its object.
///
/// `CRef` maintains the invariant that the object it references is always
/// allocated and constructed, and that mutable access to the object is disabled
/// for the lifetime of the `CRef`.
pub struct CRef<'a, C> {
    // NOTE: NonNull ensures that CHeapWrapper is !Send + !Sync. If this struct
    // is changed, make sure it's still !Send + !Sync.
    obj: NonNull<C>,
    // Make sure CRef has the lifetime 'a.
    _lifetime: PhantomData<&'a ()>,
}

impl<'a, C> CRef<'a, C> {
    /// Creates a new `CRef` from a raw pointer.
    ///
    /// # Safety
    ///
    /// `obj` must point to an allocated, constructed object. The caller must
    /// ensure that, for the lifetime, `'a`, `obj` will continue to point to the
    /// same allocated, constructed object, and that mutable access to the
    /// object will be disallowed.
    ///
    /// The caller must also ensure that no other pointers to the object will
    /// ever be sent to other threads so long as this `CRef` exists.
    #[must_use]
    pub unsafe fn new(obj: NonNull<C>) -> CRef<'a, C> {
        CRef {
            obj,
            _lifetime: PhantomData,
        }
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        self.obj.as_ptr()
    }
}

/// A wrapper around a constructed C object from the BoringSSL API.
///
/// `CStackWrapper` maintains the invariant that the object it contains is
/// always constructed. The object is destructed on `Drop`.
///
/// `CStackWrapper`s are not thread-safe; they do not implement `Send` or
/// `Sync`.
pub struct CStackWrapper<C: CDestruct> {
    obj: C,
    // Make sure CStackWrapper doesn't implement Send or Sync regardless of C.
    _no_sync: PhantomData<*mut ()>,
}

impl<C: CDestruct> CStackWrapper<C> {
    /// Constructs a new `CStackWrapper`.
    ///
    /// # Safety
    ///
    /// `obj` must be constructed, and it must be safe for `C::destruct` to be
    /// called on `obj` when this `CStackWrapper` is dropped.
    #[must_use]
    pub unsafe fn new(obj: C) -> CStackWrapper<C> {
        CStackWrapper {
            obj,
            _no_sync: PhantomData,
        }
    }

    #[must_use]
    pub fn as_mut(&mut self) -> *mut C {
        &mut self.obj
    }

    #[must_use]
    pub fn as_const(&self) -> *const C {
        &self.obj
    }
}

impl<C: CInit + CDestruct> Default for CStackWrapper<C> {
    // TODO(joshlf): In order for this to be safe, CInit must provide the safety
    // guarantee that it's always safe to call CInit::init and then later to
    // call CDestruct::destruct on that object (e.g., see the safety comment on
    // CStackWrapper::new).
    fn default() -> CStackWrapper<C> {
        unsafe {
            let mut obj: C = mem::uninitialized();
            C::init(&mut obj);
            CStackWrapper {
                obj,
                _no_sync: PhantomData,
            }
        }
    }
}

impl<C: CDestruct> Drop for CStackWrapper<C> {
    fn drop(&mut self) {
        unsafe { C::destruct(&mut self.obj) }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use boringssl::EC_KEY;

    #[test]
    fn test_heap_wrapper_into_mut() {
        // Test that CHeapWrapper::into_mut doesn't free the pointer. If it
        // does, then EC_KEY::free is likely (though not guaranteed) to abort
        // when it finds the refcount at 0.
        let key = CHeapWrapper::<EC_KEY>::default();
        unsafe { EC_KEY::free(key.into_mut()) };
    }
}
<|endoftext|>


<commit_before>use super::{
    status::{FAILURE, SUCCESS},
    variables::{Value, Variables},
};
use crate::sys::env as sys_env;
use std::{
    borrow::Cow,
    collections::VecDeque,
    env::{self, set_current_dir},
    path::{Component, Path, PathBuf},
};

fn set_current_dir_ion(dir: &Path) -> Result<(), Cow<'static, str>> {
    set_current_dir(dir).map_err(|why| Cow::Owned(format!("{}", why)))?;

    env::set_var(
        "OLDPWD",
        env::var("PWD")
            .ok()
            .and_then(|pwd| if pwd.is_empty() { None } else { Some(pwd) })
            .unwrap_or_else(|| "?".into()),
    );

    env::set_var("PWD", dir.to_str().unwrap_or("?"));
    Ok(())
}

#[derive(Debug)]
pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    fn normalize_path(&mut self, dir: &str) -> PathBuf {
        // Create a clone of the current directory.
        let mut new_dir = match self.dirs.front() {
            Some(cur_dir) => cur_dir.clone(),
            None => PathBuf::new(),
        };

        // Iterate through components of the specified directory
        // and calculate the new path based on them.
        for component in Path::new(dir).components() {
            match component {
                Component::CurDir => {}
                Component::ParentDir => {
                    new_dir.pop();
                }
                _ => {
                    new_dir.push(component);
                }
            };
        }

        new_dir
    }

    // pushd -<num>
    fn rotate_right(&mut self, num: usize) {
        let len = self.dirs.len();
        self.rotate_left(len - (num % len));
    }

    // pushd +<num>
    fn rotate_left(&mut self, num: usize) {
        let cloned = self.dirs.clone();
        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
            *dest = src.clone();
        }
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(
        &self,
        index: usize,
        caller: &str,
    ) -> Result<(), Cow<'static, str>> {
        let dir = self.dirs.get(index).ok_or_else(|| {
            Cow::Owned(format!("ion: {}: {}: directory stack out of range", caller, index))
        })?;

        set_current_dir_ion(dir)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_start());
    }

    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }

    pub(crate) fn dirs<'a, I: Iterator<Item = &'a T>, T: 'a + AsRef<str>>(
        &mut self,
        args: I,
    ) -> i32 {
        let mut clear = false; // -c
        let mut abs_pathnames = false; // -l
        let mut multiline = false; // -p | -v
        let mut index = false; // -v

        let mut num_arg = None;

        for arg in args.skip(1).map(AsRef::as_ref) {
            match arg {
                "-c" => clear = true,
                "-l" => abs_pathnames = true,
                "-p" => multiline = true,
                "-v" => {
                    index = true;
                    multiline = true;
                }
                arg => num_arg = Some(arg),
            }
        }

        if clear {
            self.dirs.truncate(1);
        }

        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (abs_pathnames, index) {
            // ABS, INDEX
            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
            (true, false) => |(_, x)| try_abs_path(x),
            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
            (false, false) => |(_, x)| x.to_string_lossy(),
        };

        let mut iter = self.dirs.iter().enumerate().map(mapper);

        if let Some(arg) = num_arg {
            let num = match parse_numeric_arg(arg) {
                Some((true, num)) => num,
                Some((false, num)) if self.dirs.len() > num => self.dirs.len() - num - 1,
                _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
                                      * argument\n", arg))) */
            };
            match iter.nth(num) {
                Some(x) => {
                    println!("{}", x);
                    SUCCESS
                }
                None => FAILURE,
            }
        } else {
            let folder: fn(String, Cow<str>) -> String =
                if multiline { |x, y| x + "\n" + &y } else { |x, y| x + " " + &y };

            if let Some(x) = iter.next() {
                println!("{}", iter.fold(x.to_string(), folder));
            }
            SUCCESS
        }
    }

    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
        self.dirs.insert(index, path);
        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub(crate) fn change_and_push_dir(
        &mut self,
        dir: &str,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        let new_dir = self.normalize_path(dir);
        match set_current_dir_ion(&new_dir) {
            Ok(()) => {
                self.push_dir(new_dir, variables);
                Ok(())
            }
            Err(err) => Err(Cow::Owned(format!(
                "ion: failed to set current dir to {}: {}",
                new_dir.to_string_lossy(),
                err
            ))),
        }
    }

    fn get_previous_dir(&self) -> Option<String> {
        env::var("OLDPWD").ok().filter(|pwd| !pwd.is_empty() && pwd != "?")
    }

    fn switch_to_previous_directory(
        &mut self,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir()
            .ok_or(Cow::Borrowed("ion: no previous directory to switch to"))
            .and_then(|prev| {
                self.dirs.remove(0);
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            })
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        sys_env::home_dir().map_or(
            Err(Cow::Borrowed("ion: failed to get home directory")),
            |home| {
                home.to_str().map_or(
                    Err(Cow::Borrowed("ion: failed to convert home directory to str")),
                    |home| self.change_and_push_dir(home, variables),
                )
            },
        )
    }

    pub(crate) fn cd(
        &mut self,
        args: &[small::String],
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        match args.get(1) {
            Some(dir) => {
                if let Some(Value::Array(cdpath)) = variables.get_ref("CDPATH") {
                    let dir = dir.as_ref();
                    if dir == "-" {
                        self.switch_to_previous_directory(variables)
                    } else {
                        let check_cdpath_first = cdpath
                            .iter()
                            .map(|path| {
                                let path_dir = [path, "/", dir].concat();
                                self.change_and_push_dir(&path_dir, variables)
                            })
                            .find(Result::is_ok)
                            .unwrap_or_else(|| self.change_and_push_dir(dir, variables));
                        self.dirs.remove(1);
                        check_cdpath_first
                    }
                } else {
                    unreachable!();
                }
            }
            None => self.switch_to_home_directory(variables),
        }
    }

    pub(crate) fn pushd<T>(
        &mut self,
        args: &[T],
        variables: &mut Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        T: AsRef<str>,
    {
        enum Action {
            Switch,          // <no arguments>
            RotLeft(usize),  // +[num]
            RotRight(usize), // -[num]
            Push(PathBuf),   // [dir]
        }

        let mut keep_front = false; // whether the -n option is present
        let mut action = Action::Switch;

        for arg in args.iter().skip(1).map(AsRef::as_ref) {
            if arg == "-n" {
                keep_front = true;
            } else if let Action::Switch = action {
                // if action is not yet defined
                action = match parse_numeric_arg(arg) {
                    Some((true, num)) => Action::RotLeft(num),
                    Some((false, num)) => Action::RotRight(num),
                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
                };
            } else {
                return Err(Cow::Borrowed("ion: pushd: too many arguments"));
            }
        }

        let len = self.dirs.len();
        match action {
            Action::Switch => {
                if len < 2 {
                    return Err(Cow::Borrowed("ion: pushd: no other directory"));
                }
                if !keep_front {
                    self.set_current_dir_by_index(1, "pushd")?;
                    self.dirs.swap(0, 1);
                }
            }
            Action::RotLeft(num) => {
                if !keep_front {
                    self.set_current_dir_by_index(num, "pushd")?;
                    self.rotate_left(num);
                }
            }
            Action::RotRight(num) => {
                if !keep_front {
                    self.set_current_dir_by_index(len - (num % len), "pushd")?;
                    self.rotate_right(num);
                }
            }
            Action::Push(dir) => {
                let index = if keep_front { 1 } else { 0 };
                let new_dir = self.normalize_path(dir.to_str().unwrap());
                self.insert_dir(index, new_dir, variables);
                self.set_current_dir_by_index(index, "pushd")?;
            }
        };

        self.print_dirs();
        Ok(())
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub(crate) fn popd<T: AsRef<str>>(&mut self, args: &[T]) -> Result<(), Cow<'static, str>> {
        let len = self.dirs.len();
        if len <= 1 {
            return Err(Cow::Borrowed("ion: popd: directory stack empty"));
        }

        let mut keep_front = false; // whether the -n option is present
        let mut index: usize = 0;

        for arg in args.iter().skip(1).map(AsRef::as_ref) {
            if arg == "-n" {
                keep_front = true;
            } else {
                let (count_from_front, num) = parse_numeric_arg(arg)
                    .ok_or_else(|| Cow::Owned(format!("ion: popd: {}: invalid argument", arg)))?;

                index = if count_from_front {
                    // <=> input number is positive
                    num
                } else {
                    (len - 1).checked_sub(num).ok_or_else(|| {
                        Cow::Owned(
                            "ion: popd: negative directory stack index out of range".to_owned(),
                        )
                    })?
                };
            }
        }

        // apply -n
        if index == 0 && keep_front {
            index = 1;
        } else if index == 0 {
            // change to new directory, return if not possible
            self.set_current_dir_by_index(1, "popd")?;
        }

        // pop element
        if self.dirs.remove(index).is_some() {
            self.print_dirs();
            Ok(())
        } else {
            Err(Cow::Owned(format!("ion: popd: {}: directory stack index out of range", index)))
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of
    /// the
    /// directory stack size variable. If it succeeds, it will return the value of that
    /// variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables.get_str_or_empty("DIRECTORY_STACK_SIZE").parse::<usize>().unwrap_or(1000)
    }

    /// Create a new `DirectoryStack` containing the current working directory,
    /// if available.
    pub(crate) fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match env::current_dir() {
            Ok(curr_dir) => {
                env::set_var("PWD", curr_dir.to_str().unwrap_or_else(|| "?"));
                dirs.push_front(curr_dir);
            }
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                env::set_var("PWD", "?");
            }
        }
        DirectoryStack { dirs }
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
    match arg.chars().nth(0) {
        Some('+') => Some(true),
        Some('-') => Some(false),
        _ => None,
    }
    .and_then(|b| arg[1..].parse::<usize>().ok().map(|num| (b, num)))
}

// converts pbuf to an absolute path if possible
fn try_abs_path(pbuf: &PathBuf) -> Cow<str> {
    Cow::Owned(pbuf.canonicalize().unwrap_or_else(|_| pbuf.clone()).to_string_lossy().to_string())
}
<commit_msg>Don't expect CDPATH, it can be absent in functions<commit_after>use super::{
    status::{FAILURE, SUCCESS},
    variables::{Value, Variables},
};
use crate::sys::env as sys_env;
use std::{
    borrow::Cow,
    collections::VecDeque,
    env::{self, set_current_dir},
    path::{Component, Path, PathBuf},
};

fn set_current_dir_ion(dir: &Path) -> Result<(), Cow<'static, str>> {
    set_current_dir(dir).map_err(|why| Cow::Owned(format!("{}", why)))?;

    env::set_var(
        "OLDPWD",
        env::var("PWD")
            .ok()
            .and_then(|pwd| if pwd.is_empty() { None } else { Some(pwd) })
            .unwrap_or_else(|| "?".into()),
    );

    env::set_var("PWD", dir.to_str().unwrap_or("?"));
    Ok(())
}

#[derive(Debug)]
pub struct DirectoryStack {
    dirs: VecDeque<PathBuf>, // The top is always the current directory
}

impl DirectoryStack {
    fn normalize_path(&mut self, dir: &str) -> PathBuf {
        // Create a clone of the current directory.
        let mut new_dir = match self.dirs.front() {
            Some(cur_dir) => cur_dir.clone(),
            None => PathBuf::new(),
        };

        // Iterate through components of the specified directory
        // and calculate the new path based on them.
        for component in Path::new(dir).components() {
            match component {
                Component::CurDir => {}
                Component::ParentDir => {
                    new_dir.pop();
                }
                _ => {
                    new_dir.push(component);
                }
            };
        }

        new_dir
    }

    // pushd -<num>
    fn rotate_right(&mut self, num: usize) {
        let len = self.dirs.len();
        self.rotate_left(len - (num % len));
    }

    // pushd +<num>
    fn rotate_left(&mut self, num: usize) {
        let cloned = self.dirs.clone();
        for (dest, src) in self.dirs.iter_mut().zip(cloned.iter().cycle().skip(num)) {
            *dest = src.clone();
        }
    }

    // sets current_dir to the element referred by index
    fn set_current_dir_by_index(
        &self,
        index: usize,
        caller: &str,
    ) -> Result<(), Cow<'static, str>> {
        let dir = self.dirs.get(index).ok_or_else(|| {
            Cow::Owned(format!("ion: {}: {}: directory stack out of range", caller, index))
        })?;

        set_current_dir_ion(dir)
    }

    fn print_dirs(&self) {
        let dir = self.dirs.iter().fold(String::new(), |acc, dir| {
            acc + " " + dir.to_str().unwrap_or("ion: no directory found")
        });
        println!("{}", dir.trim_start());
    }

    pub(crate) fn dir_from_bottom(&self, num: usize) -> Option<&PathBuf> {
        self.dirs.iter().rev().nth(num)
    }

    pub(crate) fn dir_from_top(&self, num: usize) -> Option<&PathBuf> { self.dirs.get(num) }

    pub(crate) fn dirs<'a, I: Iterator<Item = &'a T>, T: 'a + AsRef<str>>(
        &mut self,
        args: I,
    ) -> i32 {
        let mut clear = false; // -c
        let mut abs_pathnames = false; // -l
        let mut multiline = false; // -p | -v
        let mut index = false; // -v

        let mut num_arg = None;

        for arg in args.skip(1).map(AsRef::as_ref) {
            match arg {
                "-c" => clear = true,
                "-l" => abs_pathnames = true,
                "-p" => multiline = true,
                "-v" => {
                    index = true;
                    multiline = true;
                }
                arg => num_arg = Some(arg),
            }
        }

        if clear {
            self.dirs.truncate(1);
        }

        let mapper: fn((usize, &PathBuf)) -> Cow<str> = match (abs_pathnames, index) {
            // ABS, INDEX
            (true, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, try_abs_path(x))),
            (true, false) => |(_, x)| try_abs_path(x),
            (false, true) => |(num, x)| Cow::Owned(format!(" {}  {}", num, x.to_string_lossy())),
            (false, false) => |(_, x)| x.to_string_lossy(),
        };

        let mut iter = self.dirs.iter().enumerate().map(mapper);

        if let Some(arg) = num_arg {
            let num = match parse_numeric_arg(arg) {
                Some((true, num)) => num,
                Some((false, num)) if self.dirs.len() > num => self.dirs.len() - num - 1,
                _ => return FAILURE, /* Err(Cow::Owned(format!("ion: dirs: {}: invalid
                                      * argument\n", arg))) */
            };
            match iter.nth(num) {
                Some(x) => {
                    println!("{}", x);
                    SUCCESS
                }
                None => FAILURE,
            }
        } else {
            let folder: fn(String, Cow<str>) -> String =
                if multiline { |x, y| x + "\n" + &y } else { |x, y| x + " " + &y };

            if let Some(x) = iter.next() {
                println!("{}", iter.fold(x.to_string(), folder));
            }
            SUCCESS
        }
    }

    fn insert_dir(&mut self, index: usize, path: PathBuf, variables: &Variables) {
        self.dirs.insert(index, path);
        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    fn push_dir(&mut self, path: PathBuf, variables: &Variables) {
        self.dirs.push_front(path);

        self.dirs.truncate(DirectoryStack::get_size(variables));
    }

    pub(crate) fn change_and_push_dir(
        &mut self,
        dir: &str,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        let new_dir = self.normalize_path(dir);
        match set_current_dir_ion(&new_dir) {
            Ok(()) => {
                self.push_dir(new_dir, variables);
                Ok(())
            }
            Err(err) => Err(Cow::Owned(format!(
                "ion: failed to set current dir to {}: {}",
                new_dir.to_string_lossy(),
                err
            ))),
        }
    }

    fn get_previous_dir(&self) -> Option<String> {
        env::var("OLDPWD").ok().filter(|pwd| !pwd.is_empty() && pwd != "?")
    }

    fn switch_to_previous_directory(
        &mut self,
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        self.get_previous_dir()
            .ok_or(Cow::Borrowed("ion: no previous directory to switch to"))
            .and_then(|prev| {
                self.dirs.remove(0);
                println!("{}", prev);
                self.change_and_push_dir(&prev, variables)
            })
    }

    fn switch_to_home_directory(&mut self, variables: &Variables) -> Result<(), Cow<'static, str>> {
        sys_env::home_dir().map_or(
            Err(Cow::Borrowed("ion: failed to get home directory")),
            |home| {
                home.to_str().map_or(
                    Err(Cow::Borrowed("ion: failed to convert home directory to str")),
                    |home| self.change_and_push_dir(home, variables),
                )
            },
        )
    }

    pub(crate) fn cd(
        &mut self,
        args: &[small::String],
        variables: &Variables,
    ) -> Result<(), Cow<'static, str>> {
        match args.get(1) {
            Some(dir) => {
                if let Some(Value::Array(cdpath)) = variables.get_ref("CDPATH") {
                    let dir = dir.as_ref();
                    if dir == "-" {
                        self.switch_to_previous_directory(variables)
                    } else {
                        let check_cdpath_first = cdpath
                            .iter()
                            .map(|path| {
                                let path_dir = [path, "/", dir].concat();
                                self.change_and_push_dir(&path_dir, variables)
                            })
                            .find(Result::is_ok)
                            .unwrap_or_else(|| self.change_and_push_dir(dir, variables));
                        self.dirs.remove(1);
                        check_cdpath_first
                    }
                } else {
                    self.change_and_push_dir(dir, variables)
                }
            }
            None => self.switch_to_home_directory(variables),
        }
    }

    pub(crate) fn pushd<T>(
        &mut self,
        args: &[T],
        variables: &mut Variables,
    ) -> Result<(), Cow<'static, str>>
    where
        T: AsRef<str>,
    {
        enum Action {
            Switch,          // <no arguments>
            RotLeft(usize),  // +[num]
            RotRight(usize), // -[num]
            Push(PathBuf),   // [dir]
        }

        let mut keep_front = false; // whether the -n option is present
        let mut action = Action::Switch;

        for arg in args.iter().skip(1).map(AsRef::as_ref) {
            if arg == "-n" {
                keep_front = true;
            } else if let Action::Switch = action {
                // if action is not yet defined
                action = match parse_numeric_arg(arg) {
                    Some((true, num)) => Action::RotLeft(num),
                    Some((false, num)) => Action::RotRight(num),
                    None => Action::Push(PathBuf::from(arg)), // no numeric arg => `dir`-parameter
                };
            } else {
                return Err(Cow::Borrowed("ion: pushd: too many arguments"));
            }
        }

        let len = self.dirs.len();
        match action {
            Action::Switch => {
                if len < 2 {
                    return Err(Cow::Borrowed("ion: pushd: no other directory"));
                }
                if !keep_front {
                    self.set_current_dir_by_index(1, "pushd")?;
                    self.dirs.swap(0, 1);
                }
            }
            Action::RotLeft(num) => {
                if !keep_front {
                    self.set_current_dir_by_index(num, "pushd")?;
                    self.rotate_left(num);
                }
            }
            Action::RotRight(num) => {
                if !keep_front {
                    self.set_current_dir_by_index(len - (num % len), "pushd")?;
                    self.rotate_right(num);
                }
            }
            Action::Push(dir) => {
                let index = if keep_front { 1 } else { 0 };
                let new_dir = self.normalize_path(dir.to_str().unwrap());
                self.insert_dir(index, new_dir, variables);
                self.set_current_dir_by_index(index, "pushd")?;
            }
        };

        self.print_dirs();
        Ok(())
    }

    /// Attempts to set the current directory to the directory stack's previous directory,
    /// and then removes the front directory from the stack.
    pub(crate) fn popd<T: AsRef<str>>(&mut self, args: &[T]) -> Result<(), Cow<'static, str>> {
        let len = self.dirs.len();
        if len <= 1 {
            return Err(Cow::Borrowed("ion: popd: directory stack empty"));
        }

        let mut keep_front = false; // whether the -n option is present
        let mut index: usize = 0;

        for arg in args.iter().skip(1).map(AsRef::as_ref) {
            if arg == "-n" {
                keep_front = true;
            } else {
                let (count_from_front, num) = parse_numeric_arg(arg)
                    .ok_or_else(|| Cow::Owned(format!("ion: popd: {}: invalid argument", arg)))?;

                index = if count_from_front {
                    // <=> input number is positive
                    num
                } else {
                    (len - 1).checked_sub(num).ok_or_else(|| {
                        Cow::Owned(
                            "ion: popd: negative directory stack index out of range".to_owned(),
                        )
                    })?
                };
            }
        }

        // apply -n
        if index == 0 && keep_front {
            index = 1;
        } else if index == 0 {
            // change to new directory, return if not possible
            self.set_current_dir_by_index(1, "popd")?;
        }

        // pop element
        if self.dirs.remove(index).is_some() {
            self.print_dirs();
            Ok(())
        } else {
            Err(Cow::Owned(format!("ion: popd: {}: directory stack index out of range", index)))
        }
    }

    /// This function will take a map of variables as input and attempt to parse the value of
    /// the
    /// directory stack size variable. If it succeeds, it will return the value of that
    /// variable,
    /// else it will return a default value of 1000.
    fn get_size(variables: &Variables) -> usize {
        variables.get_str_or_empty("DIRECTORY_STACK_SIZE").parse::<usize>().unwrap_or(1000)
    }

    /// Create a new `DirectoryStack` containing the current working directory,
    /// if available.
    pub(crate) fn new() -> DirectoryStack {
        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
        match env::current_dir() {
            Ok(curr_dir) => {
                env::set_var("PWD", curr_dir.to_str().unwrap_or_else(|| "?"));
                dirs.push_front(curr_dir);
            }
            Err(_) => {
                eprintln!("ion: failed to get current directory when building directory stack");
                env::set_var("PWD", "?");
            }
        }
        DirectoryStack { dirs }
    }
}

// parses -N or +N patterns
// required for popd, pushd, dirs
fn parse_numeric_arg(arg: &str) -> Option<(bool, usize)> {
    match arg.chars().nth(0) {
        Some('+') => Some(true),
        Some('-') => Some(false),
        _ => None,
    }
    .and_then(|b| arg[1..].parse::<usize>().ok().map(|num| (b, num)))
}

// converts pbuf to an absolute path if possible
fn try_abs_path(pbuf: &PathBuf) -> Cow<str> {
    Cow::Owned(pbuf.canonicalize().unwrap_or_else(|_| pbuf.clone()).to_string_lossy().to_string())
}
<|endoftext|>


<commit_before>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
    }
}

fn get_envp(pidStrRef: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pidStrRef + &"/environ".to_string();
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pidStrRef = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pidStrRef);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pidStrRef + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(9);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        write(1, "About to start shell...\n\0".as_bytes());
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<commit_msg>Remove one unused variable<commit_after>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
    }
}

fn get_envp(pidStrRef: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pidStrRef + &"/environ".to_string();
    match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pidStrRef = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pidStrRef);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pidStrRef + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pidStrRef + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(9);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        write(1, "About to start shell...\n\0".as_bytes());
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<|endoftext|>


<commit_before>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
    }
}

fn get_envp(pid_str_ref: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pid_str_ref + &"/environ".to_string();
    match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pid_str_ref = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pid_str_ref);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pid_str_ref + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(cwd_fd);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        write(1, "About to start shell...\n\0".as_bytes());
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<commit_msg>Only print info about launching shell if it fails<commit_after>// use std::env;
use std::fs::File;
use std::io::Read;
use std::ptr;
use std::env;

#[macro_use]
extern crate syscall;

fn write(fd: usize, buf: &[u8]) {
    unsafe {
        syscall!(WRITE, fd, buf.as_ptr(), buf.len());
    }
}

fn open_as_fd_or_die(filename: &[u8]) -> usize {
    let o_rdonly = 0u64;
    let no_flags = 0u64;
    unsafe {
        let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags);
        sanity_check_fd(return_value);
        return return_value;
    }
}

fn setns(fd: usize, nstype: usize) {
    unsafe {
        syscall!(SETNS, fd, nstype);
    }
}

fn close(fd: usize) {
    unsafe {
        syscall!(CLOSE, fd);
    }
}

fn sanity_check_fd(fd: usize) {
    // For some reason, syscall!(OPEN) returns usize, but I need to check its output against
    // -1. So I'm going to just check if it's >255.
    if fd > 255 {
        panic!("Failed to open a needed file. Bailing.");
    }
}

fn setgroups_zero() {
    // Clears auxilary groups. Probably not necessary, but whatever.
    unsafe {
        syscall!(SETGROUPS, 0);
    }
}

fn fchdir(fd: usize) {
    unsafe {
        syscall!(FCHDIR, fd);
    }
}

fn fork() -> usize {
    unsafe {
        syscall!(FORK)
    }
}

fn wait_all_children() {
    let all_children = -1isize;
    let no_options = 0usize;
    let nullptr = 0usize;
    unsafe {
        syscall!(WAIT4, all_children, no_options, nullptr, nullptr);
    }
}

fn setuid_setgid_1000() {
    // These are the right UID & GID because they are what Sandstorm
    // uses inside the user namespace.
    let one_thousand = 1000usize;
    unsafe {
        syscall!(SETUID, one_thousand);
        syscall!(SETGID, one_thousand);
    }
}

fn execve_bash(envp: std::vec::Vec<*const u8>) {
    let bash_path = "/bin/bash\0".as_bytes();
    let nullargv = 0usize;
    unsafe {
        // execve will not return at all in the case of success. In the case of failure it will
        // return -1. I check it against 0, which is the typical UNIX/C idiom.
        let retval = syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr());
        if retval != 0usize {
            panic!("Failed to find and launch bash within the grain. Bailing out now.");
        }
    }
}

fn get_envp(pid_str_ref: &str) -> std::vec::Vec<*const u8> {
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.

    let path = "/proc/".to_string() + pid_str_ref + &"/environ".to_string();
    match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path, why),
        Ok(mut file) => {
            let mut s = String::new();
            match file.read_to_string(&mut s) {
                Err(why) => panic!("couldn't read {}: {}", path, why),
                Ok(_) => {
                    let mut v: Vec<String> = s.split('\0').map(
                        |x| x.to_string() + "\0").collect();
                    v.push("__terminator".to_string());
                    let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| (
                        if x == "__terminator" { ptr::null() }
                        else { x.as_ptr() }
                        )).collect();
                    return all_environ_arguments;
                }
            }
        }
    };
}

fn main() {
    let pid_str_ref = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string();
    // Grab the environ from pid 1048 so that when we execve a shell
    // at the end, we can provide the environment.
    let result = get_envp(pid_str_ref);

    // let filename: &[u8] = b"/usr/bin/sensors\x00";     // <-- Make c strings like this
    // let argv1: &[u8] = b"/usr/bin/sensors\x00";
    // let argv2: &[u8] = b"-h\x00";
    // let argv: &[int] = [                               // <-- store them in this
    // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting
    // ::core::intrinsics::transmute(argv2.as_ptr()),
    // 0                                              // <-- and NULL terminate
    // ];
    // let envp: &[int] = [0];let target_environ
    write(1,
          ("Attaching to process ID ".to_string() + pid_str_ref + &"...\n".to_string()).as_bytes());
    let userns_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/user\0").as_bytes());
    let ipc_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/ipc\0").as_bytes());
    let uts_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/uts\0").as_bytes());
    let net_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/net\0").as_bytes());
    let pid_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/pid\0").as_bytes());
    let mnt_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/ns/mnt\0").as_bytes());
    let cwd_fd = open_as_fd_or_die(
        ("/proc/".to_string() + pid_str_ref + "/cwd\0").as_bytes());
    setgroups_zero();
    setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER
    close(userns_fd);
    setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC
    close(ipc_fd);
    setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS
    close(uts_fd);
    setns(net_fd, 0x40000000usize); // CLONE_NEWNET
    close(net_fd);
    setns(pid_fd, 0x20000000usize); // CLONE_NEWPID
    close(pid_fd);
    setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces
    close(mnt_fd);
    fchdir(cwd_fd);
    close(cwd_fd);
    // fork, and do a handful of things in the child before we execve bash.
    let fork_result = fork();
    if fork_result == 0 {
        // in the child
        setuid_setgid_1000();
        execve_bash(result);
    } else {
        // in the parent
        wait_all_children();
    }
}

// open("/proc/1048/ns/user", O_RDONLY)    = 3
// open("/proc/1048/ns/ipc", O_RDONLY)     = 4
// open("/proc/1048/ns/uts", O_RDONLY)     = 5
// open("/proc/1048/ns/net", O_RDONLY)     = 6
// open("/proc/1048/ns/pid", O_RDONLY)     = 7
// open("/proc/1048/ns/mnt", O_RDONLY)     = 8
// open("/proc/1048/cwd", O_RDONLY)        = 9
// setgroups(0, [])                        = 0
// setns(3, CLONE_NEWUSER)                 = 0
// close(3)                                = 0
// setns(4, CLONE_NEWIPC)                  = 0
// close(4)                                = 0
// setns(5, CLONE_NEWUTS)                  = 0
// close(5)                                = 0
// setns(6, CLONE_NEWNET)                  = 0
// close(6)                                = 0
// setns(7, CLONE_NEWPID)                  = 0
// close(7)                                = 0
// setns(8, CLONE_NEWNS)                   = 0
// close(8)                                = 0
// fchdir(9)                               = 0
// close(9)                                = 0
<|endoftext|>


<commit_before>pub use self::dependency::Dependency;
pub use self::manifest::{Manifest, Target, TargetKind, Profile, LibKind, Profiles};
pub use self::manifest::{EitherManifest, VirtualManifest};
pub use self::package::{Package, PackageSet};
pub use self::package_id::PackageId;
pub use self::package_id_spec::PackageIdSpec;
pub use self::registry::Registry;
pub use self::resolver::Resolve;
pub use self::shell::{Shell, Verbosity};
pub use self::source::{Source, SourceId, SourceMap, GitReference};
pub use self::summary::Summary;
pub use self::workspace::{Workspace, WorkspaceConfig};

pub mod source;
pub mod package;
pub mod package_id;
pub mod dependency;
pub mod manifest;
pub mod resolver;
pub mod summary;
pub mod shell;
pub mod registry;
mod package_id_spec;
mod workspace;
<commit_msg>Auto merge of #4360 - sid0:members, r=matklad<commit_after>pub use self::dependency::Dependency;
pub use self::manifest::{Manifest, Target, TargetKind, Profile, LibKind, Profiles};
pub use self::manifest::{EitherManifest, VirtualManifest};
pub use self::package::{Package, PackageSet};
pub use self::package_id::PackageId;
pub use self::package_id_spec::PackageIdSpec;
pub use self::registry::Registry;
pub use self::resolver::Resolve;
pub use self::shell::{Shell, Verbosity};
pub use self::source::{Source, SourceId, SourceMap, GitReference};
pub use self::summary::Summary;
pub use self::workspace::{Members, Workspace, WorkspaceConfig};

pub mod source;
pub mod package;
pub mod package_id;
pub mod dependency;
pub mod manifest;
pub mod resolver;
pub mod summary;
pub mod shell;
pub mod registry;
mod package_id_spec;
mod workspace;
<|endoftext|>