use std::ffi::CStr;
use std::io;
use crate::error::io_assert;
use crate::types::Device;
use super::sys::{MountAttr, MountPropagation, StatMountFlags, SuperblockFlags};
use super::sys::{SYS_listmount, SYS_statmount};
use super::{MountId, MountNsId, ReusedMountId};
#[derive(Clone, Copy, Debug)]
#[repr(C)]
struct RawMountIdRequest {
size: u32,
spare: u32,
mount_id: MountId,
param: u64,
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
struct RawMountIdRequestForNs {
req: RawMountIdRequest,
mnt_ns_id: MountNsId,
}
#[derive(Clone, Copy, Debug)]
struct MountIdRequest {
inner: MountIdRequestInner,
}
#[derive(Clone, Copy, Debug)]
enum MountIdRequestInner {
Ver0(RawMountIdRequest),
Ver1(RawMountIdRequestForNs),
}
impl MountIdRequest {
pub const fn new() -> Self {
Self {
inner: MountIdRequestInner::Ver0(RawMountIdRequest {
size: std::mem::size_of::<RawMountIdRequest>() as u32,
spare: 0,
mount_id: MountId::from_raw(0),
param: 0,
}),
}
}
pub const fn mount_id(mut self, id: MountId) -> Self {
self.inner = match self.inner {
MountIdRequestInner::Ver0(mut n) => MountIdRequestInner::Ver0({
n.mount_id = id;
n
}),
MountIdRequestInner::Ver1(mut n) => MountIdRequestInner::Ver1({
n.req.mount_id = id;
n
}),
};
self
}
pub const fn mount_namespace(mut self, mnt_ns_id: MountNsId) -> Self {
self.inner = match self.inner {
MountIdRequestInner::Ver0(mut req) => {
req.size = std::mem::size_of::<RawMountIdRequestForNs>() as u32;
MountIdRequestInner::Ver1(RawMountIdRequestForNs { req, mnt_ns_id })
}
MountIdRequestInner::Ver1(mut n) => MountIdRequestInner::Ver1({
n.mnt_ns_id = mnt_ns_id;
n
}),
};
self
}
fn finalize(&mut self, param: u64) -> *mut u8 {
match &mut self.inner {
MountIdRequestInner::Ver0(n) => {
let size = std::mem::size_of_val::<RawMountIdRequest>(n);
n.param = param;
n.size = u32::try_from(size).unwrap();
&mut *n as *mut RawMountIdRequest as *mut u8
}
MountIdRequestInner::Ver1(n) => {
let size = std::mem::size_of_val::<RawMountIdRequestForNs>(n);
n.req.param = param;
n.req.size = u32::try_from(size).unwrap();
&mut *n as *mut RawMountIdRequestForNs as *mut u8
}
}
}
}
pub struct ListMounts {
request: MountIdRequest,
buf: Box<[MountId; 64]>,
at: usize,
capacity: usize,
mnt_id: Option<MountId>,
done: bool,
}
pub fn list() -> ListMounts {
ListMounts::here()
}
impl ListMounts {
pub fn new(parent: MountId, namespace: Option<MountNsId>) -> Self {
let mut request = MountIdRequest::new().mount_id(parent);
if let Some(namespace) = namespace {
request = request.mount_namespace(namespace);
}
Self {
request,
buf: Box::new([MountId::from_raw(0); 64]),
at: 0,
capacity: 0,
mnt_id: None,
done: false,
}
}
pub fn here() -> Self {
Self::new(MountId::root(), None)
}
fn list_more(&mut self) -> io::Result<()> {
if self.done || self.at < self.capacity {
return Ok(());
}
let req = self
.request
.finalize(self.mnt_id.map_or(0, MountId::as_raw_id));
let rc =
unsafe { libc::syscall(SYS_listmount, req, self.buf.as_mut_ptr(), self.buf.len(), 0) };
io_assert!(rc >= 0);
self.capacity = rc as usize;
self.at = 0;
if self.capacity == 0 {
self.done = true;
} else {
self.mnt_id = Some(self.buf[self.capacity - 1]);
}
Ok(())
}
}
impl Iterator for ListMounts {
type Item = io::Result<MountId>;
fn next(&mut self) -> Option<io::Result<MountId>> {
if let Err(err) = self.list_more() {
return Some(Err(err));
}
if self.done {
return None;
}
let at = self.at;
self.at += 1;
Some(Ok(self.buf[at]))
}
}
impl MountId {
pub fn stat_full(self) -> io::Result<Box<StatMount>> {
StatMount::stat(self)
}
pub fn stat(self, what: StatMountFlags) -> io::Result<Box<StatMount>> {
StatMount::builder()
.set_flags(true, what)
.mount_id(self)
.stat()
}
pub fn stat_ns(self, what: StatMountFlags, namespace: MountNsId) -> io::Result<Box<StatMount>> {
StatMount::builder()
.set_flags(true, what)
.mount_id(self)
.mount_namespace(namespace)
.stat()
}
}
#[derive(Clone, Copy, Debug)]
pub struct StatMountBuilder {
flags: StatMountFlags,
request: MountIdRequest,
}
impl Default for StatMountBuilder {
fn default() -> Self {
Self::new()
}
}
impl StatMountBuilder {
pub const fn new() -> Self {
Self {
flags: StatMountFlags::SB_BASIC,
request: MountIdRequest::new(),
}
}
pub fn mount_id(mut self, id: MountId) -> Self {
self.request = self.request.mount_id(id);
self
}
pub fn mount_namespace(mut self, mnt_ns_id: MountNsId) -> Self {
self.request = self.request.mount_namespace(mnt_ns_id);
self
}
fn set_flags(mut self, on: bool, flags: StatMountFlags) -> Self {
if on {
self.flags |= flags;
} else {
self.flags &= !flags;
}
self
}
pub fn basic_superblock_info(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::SB_BASIC)
}
pub fn basic_mount_info(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::MNT_BASIC)
}
pub fn propagate_from(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::PROPAGATE_FROM)
}
pub fn mount_root(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::MNT_ROOT)
}
pub fn mount_point(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::MNT_POINT)
}
pub fn fs_type(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::FS_TYPE)
}
pub fn mount_ns_id(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::MNT_NS_ID)
}
pub fn mount_options(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::MNT_OPTS)
}
pub fn fs_subtype(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::FS_SUBTYPE)
}
pub fn source(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::SB_SOURCE)
}
pub fn mount_option_array(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::OPT_ARRAY)
}
pub fn mount_security_option_array(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::OPT_SEC_ARRAY)
}
pub fn all(self, on: bool) -> Self {
self.set_flags(on, StatMountFlags::all())
}
pub fn stat(&mut self) -> io::Result<Box<StatMount>> {
StatMount::request(self)
}
}
#[derive(Debug)]
#[repr(C)]
struct StatMountBase {
size: u32,
mnt_opts: u32,
mask: u64,
sb_dev_major: u32,
sb_dev_minor: u32,
sb_magic: u64,
sb_flags: SuperblockFlags,
fs_type: u32,
mnt_id: MountId,
mnt_parent_id: MountId,
mnt_id_old: ReusedMountId,
mnt_parent_id_old: ReusedMountId,
mnt_attr: MountAttr,
mnt_propagation: MountPropagation,
mnt_peer_group: u64,
mnt_master: u64,
propagate_from: u64,
mnt_root: u32,
mnt_point: u32,
mnt_ns_id: MountNsId,
fs_subtype: u32,
sb_source: u32,
opt_num: u32,
opt_array: u32,
opt_sec_num: u32,
opt_sec_array: u32,
_spare2: [u64; 46],
}
#[derive(Debug)]
#[repr(C)]
pub struct StatMount {
base: StatMountBase,
str: [u8],
}
impl StatMount {
pub const fn builder() -> StatMountBuilder {
StatMountBuilder::new()
}
pub fn stat(mount_id: MountId) -> io::Result<Box<Self>> {
Self::builder().all(true).mount_id(mount_id).stat()
}
fn with_capacity(size: usize) -> Box<Self> {
let str_capacity = size - std::mem::size_of::<StatMountBase>();
let layout =
std::alloc::Layout::from_size_align(size, std::mem::align_of::<StatMountBase>())
.expect("bad size for `StatMount::with_capacity()`");
unsafe {
let ptr = std::alloc::alloc(layout);
let intermediate = std::ptr::slice_from_raw_parts_mut(ptr, str_capacity);
Box::from_raw(intermediate as *mut Self)
}
}
fn realloc(self: Box<Self>, size: usize) -> Box<Self> {
let str_capacity = size - std::mem::size_of::<StatMountBase>();
let old_capacity = self.str.len();
let old_size = std::mem::size_of::<StatMountBase>() + old_capacity;
let old_layout =
std::alloc::Layout::from_size_align(old_size, std::mem::align_of::<StatMountBase>())
.expect("bad capacity for `StatMount::realloc()`");
let this = Box::into_raw(self);
unsafe {
let ptr = std::alloc::realloc(this as *mut u8, old_layout, size);
let intermediate = std::ptr::slice_from_raw_parts_mut(ptr, str_capacity);
Box::from_raw(intermediate as *mut Self)
}
}
fn as_mut_raw_ptr(&mut self) -> *mut u8 {
&raw mut self.base as *mut u8
}
pub fn request(req: &mut StatMountBuilder) -> io::Result<Box<Self>> {
let mut capacity = 32768;
let mut this = Self::with_capacity(capacity);
let req_ptr = req.request.finalize(req.flags.bits());
loop {
let rc = unsafe {
libc::syscall(SYS_statmount, req_ptr, this.as_mut_raw_ptr(), capacity, 0)
};
if rc == 0 {
return Ok(this);
}
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(libc::EOVERFLOW) || capacity >= 0x1000_0000 {
return Err(err);
}
capacity <<= 1;
this = Self::realloc(this, capacity);
}
}
fn str(&self) -> &[u8] {
let len =
usize::try_from(self.base.size).unwrap() - std::mem::size_of::<StatMountBuilder>();
&self.str[..len]
}
fn option<T>(&self, flag: StatMountFlags, value: T) -> Option<T> {
(self.base.mask & flag.bits() == flag.bits()).then_some(value)
}
fn str_slice(&self, flag: StatMountFlags, value: u32) -> Option<&[u8]> {
let value = self.option(flag, value)?;
let value = usize::try_from(value).unwrap();
let str = self.str();
if value >= str.len() {
return None;
}
Some(&str[value..])
}
fn c_str(&self, flag: StatMountFlags, value: u32) -> Option<&CStr> {
CStr::from_bytes_until_nul(self.str_slice(flag, value)?).ok()
}
pub fn mount_options(&self) -> Option<&CStr> {
self.c_str(StatMountFlags::MNT_OPTS, self.base.mnt_opts)
}
pub fn device(&self) -> Option<Device> {
self.option(
StatMountFlags::SB_BASIC,
Device {
major: self.base.sb_dev_major,
minor: self.base.sb_dev_minor,
},
)
}
pub fn superblock_magic(&self) -> Option<u64> {
self.option(StatMountFlags::SB_BASIC, self.base.sb_magic)
}
pub fn superblock_flags(&self) -> Option<SuperblockFlags> {
self.option(StatMountFlags::SB_BASIC, self.base.sb_flags)
}
pub fn id(&self) -> Option<MountId> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_id)
}
pub fn parent_id(&self) -> Option<MountId> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_parent_id)
}
pub fn old_id(&self) -> Option<ReusedMountId> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_id_old)
}
pub fn old_parent_id(&self) -> Option<ReusedMountId> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_parent_id_old)
}
pub fn attr(&self) -> Option<MountAttr> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_attr)
}
pub fn propagation(&self) -> Option<MountPropagation> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_propagation)
}
pub fn peer_group_id(&self) -> Option<u64> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_peer_group)
}
pub fn master_group_id(&self) -> Option<u64> {
self.option(StatMountFlags::MNT_BASIC, self.base.mnt_master)
}
pub fn source(&self) -> Option<&CStr> {
self.c_str(StatMountFlags::SB_SOURCE, self.base.sb_source)
}
pub fn propagate_from(&self) -> Option<u64> {
self.option(StatMountFlags::PROPAGATE_FROM, self.base.propagate_from)
}
pub fn mount_root(&self) -> Option<&CStr> {
self.c_str(StatMountFlags::MNT_ROOT, self.base.mnt_root)
}
pub fn mount_point(&self) -> Option<&CStr> {
self.c_str(StatMountFlags::MNT_POINT, self.base.mnt_point)
}
pub fn fs_type(&self) -> Option<u32> {
self.option(StatMountFlags::FS_TYPE, self.base.fs_type)
}
pub fn mount_namespace_id(&self) -> Option<MountNsId> {
self.option(StatMountFlags::MNT_NS_ID, self.base.mnt_ns_id)
}
pub fn fs_subtype(&self) -> Option<&CStr> {
self.c_str(StatMountFlags::FS_SUBTYPE, self.base.fs_subtype)
}
pub fn options(&self) -> Option<OptionIter<'_>> {
Some(OptionIter::new(
usize::try_from(self.base.opt_num).unwrap(),
self.str_slice(StatMountFlags::OPT_ARRAY, self.base.opt_array)?,
))
}
pub fn security_options(&self) -> Option<OptionIter<'_>> {
Some(OptionIter::new(
usize::try_from(self.base.opt_sec_num).unwrap(),
self.str_slice(StatMountFlags::OPT_SEC_ARRAY, self.base.opt_sec_array)?,
))
}
}
pub struct OptionIter<'a> {
remaining: usize,
buf: &'a [u8],
}
impl<'a> OptionIter<'a> {
fn new(count: usize, buf: &'a [u8]) -> Self {
Self {
remaining: count,
buf,
}
}
}
impl<'a> Iterator for OptionIter<'a> {
type Item = &'a CStr;
fn next(&mut self) -> Option<&'a CStr> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
match self.buf.iter().position(|&b| b == 0) {
None | Some(0) => {
self.remaining = 0;
None
}
Some(n) => {
let out = unsafe { CStr::from_bytes_with_nul_unchecked(&self.buf[..=n]) };
self.buf = &self.buf[(n + 1)..];
Some(out)
}
}
}
}