krata: restructure packages for cleanliness

This commit is contained in:
Alex Zenla 2024-03-30 06:17:30 +00:00
parent da9e6cac14
commit bdb91a6cb3
No known key found for this signature in database
GPG Key ID: 067B238899B51269
85 changed files with 35 additions and 1345 deletions

View File

@ -1,14 +1,12 @@
[workspace] [workspace]
members = [ members = [
"crates/krata", "crates/krata",
"crates/krataoci", "crates/oci",
"crates/krataguest", "crates/guest",
"crates/kratart", "crates/runtime",
"crates/kratad", "crates/daemon",
"crates/kratanet", "crates/network",
"crates/kratactl", "crates/ctl",
"crates/vendor/advmac",
"crates/vendor/loopdev",
"crates/xen/xencall", "crates/xen/xencall",
"crates/xen/xenclient", "crates/xen/xenclient",
"crates/xen/xenevtchn", "crates/xen/xenevtchn",
@ -21,6 +19,7 @@ resolver = "2"
version = "0.0.1" version = "0.0.1"
[workspace.dependencies] [workspace.dependencies]
advmac = "1.0.3"
anyhow = "1.0" anyhow = "1.0"
arrayvec = "0.7.4" arrayvec = "0.7.4"
async-compression = "0.4.6" async-compression = "0.4.6"
@ -40,6 +39,7 @@ futures = "0.3.30"
ipnetwork = "0.20.0" ipnetwork = "0.20.0"
libc = "0.2" libc = "0.2"
log = "0.4.20" log = "0.4.20"
loopdev-3 = "0.5.1"
memchr = "2" memchr = "2"
nix = "0.28.0" nix = "0.28.0"
oci-spec = "0.6.4" oci-spec = "0.6.4"

8
DEV.md
View File

@ -6,10 +6,10 @@ krata is composed of four major executables:
| Executable | Runs On | User Interaction | Dev Runner | Code Path | | Executable | Runs On | User Interaction | Dev Runner | Code Path |
| ---------- | ------- | ---------------- | ------------------------ | ----------------- | | ---------- | ------- | ---------------- | ------------------------ | ----------------- |
| kratad | host | backend daemon | ./hack/debug/kratad.sh | crates/kratad | | kratad | host | backend daemon | ./hack/debug/kratad.sh | crates/daemon |
| kratanet | host | backend daemon | ./hack/debug/kratanet.sh | crates/kratanet | | kratanet | host | backend daemon | ./hack/debug/kratanet.sh | crates/network |
| kratactl | host | CLI tool | ./hack/debug/kratactl.sh | crates/kratactl | | kratactl | host | CLI tool | ./hack/debug/kratactl.sh | crates/ctl |
| krataguest | guest | none, guest init | N/A | crates/krataguest | | krataguest | guest | none, guest init | N/A | crates/guest |
You will find the code to each executable available in the bin/ and src/ directories inside You will find the code to each executable available in the bin/ and src/ directories inside
it's corresponding code path from the above table. it's corresponding code path from the above table.

View File

@ -1,14 +0,0 @@
# Licensing Guide
## Krata License
This repository is licensed under Apache License 2.0, except where noted in this document.
## Licensing Exceptions
As noted in the table below, some code in this repository is licensed under different terms:
| Vendored Path | Source Repository | License |
| --------------------- | -------------------------------------------- | -------- |
| crates/vendor/advmac | https://github.com/GamePad64/advmac | MIT |
| crates/vendor/loopdev | https://github.com/stratis-storage/loopdev-3 | MIT |

View File

@ -1,5 +1,5 @@
[package] [package]
name = "kratactl" name = "krata-ctl"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "kratad" name = "krata-daemon"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
@ -13,7 +13,7 @@ clap = { workspace = true }
env_logger = { workspace = true } env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
krata = { path = "../krata" } krata = { path = "../krata" }
kratart = { path = "../kratart" } krata-runtime = { path = "../runtime" }
log = { workspace = true } log = { workspace = true }
prost = { workspace = true } prost = { workspace = true }
redb = { workspace = true } redb = { workspace = true }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "krataguest" name = "krata-guest"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
@ -10,6 +10,7 @@ env_logger = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata" } krata = { path = "../krata" }
krata-xenstore = { path = "../xen/xenstore" }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
nix = { workspace = true, features = ["process"] } nix = { workspace = true, features = ["process"] }
@ -21,7 +22,6 @@ serde_json = { workspace = true }
sys-mount = { workspace = true } sys-mount = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
walkdir = { workspace = true } walkdir = { workspace = true }
xenstore = { path = "../xen/xenstore" }
[lib] [lib]
name = "krataguest" name = "krataguest"

View File

@ -1,11 +1,11 @@
[package] [package]
name = "kratanet" name = "krata-network"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
[dependencies] [dependencies]
advmac = { path = "../vendor/advmac" } advmac = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
bytes = { workspace = true } bytes = { workspace = true }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "krataoci" name = "krata-oci"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -1,25 +1,25 @@
[package] [package]
name = "kratart" name = "krata-runtime"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
[dependencies] [dependencies]
advmac = { path = "../vendor/advmac" } advmac = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
backhand = { workspace = true } backhand = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
krata = { path = "../krata" } krata = { path = "../krata" }
krataoci = { path = "../krataoci" } krata-oci = { path = "../oci" }
log = { workspace = true } log = { workspace = true }
loopdev = { path = "../vendor/loopdev" } loopdev-3 = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
xenclient = { path = "../xen/xenclient" } krata-xenclient = { path = "../xen/xenclient" }
xenevtchn = { path = "../xen/xenevtchn" } krata-xenevtchn = { path = "../xen/xenevtchn" }
xengnt = { path = "../xen/xengnt" } krata-xengnt = { path = "../xen/xengnt" }
xenstore = { path = "../xen/xenstore" } krata-xenstore = { path = "../xen/xenstore" }
[lib] [lib]
name = "kratart" name = "kratart"

View File

@ -1,15 +0,0 @@
# This package is from https://github.com/GamePad64/advmac
# Edera maintains an in-tree version because of dependencies being out of date.
[package]
name = "advmac"
version.workspace = true
license = "MIT"
edition = "2021"
[dependencies]
arrayvec = { workspace = true, features = ["serde"] }
rand = { workspace = true }
serde = { workspace = true }
[lib]
name = "advmac"

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Alexander Shishenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,474 +0,0 @@
mod parser;
use arrayvec::ArrayString;
use core::fmt::{self, Debug, Display, Formatter};
use core::str::FromStr;
use rand::Rng;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum ParseError {
InvalidMac,
InvalidLength { length: usize },
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidMac => write!(f, "invalid MAC address"),
Self::InvalidLength { length } => write!(f, "invalid string length: {}", length),
}
}
}
impl std::error::Error for ParseError {}
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum IpError {
NotLinkLocal,
NotMulticast,
}
impl Display for IpError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::NotLinkLocal => write!(f, "not link-local address"),
Self::NotMulticast => write!(f, "not multicast address"),
}
}
}
impl std::error::Error for IpError {}
/// Maximum formatted size.
///
/// It is useful for creating a stack-allocated buffer `[u8; MAC_MAX_SIZE]`
/// and formatting address into it using [MacAddr6::format_write] or [MacAddr8::format_write].
pub const MAC_MAX_SIZE: usize = 23;
/// Size of formatted MAC using [MacAddr6::format_string] and [MacAddrFormat::Canonical].
pub const MAC_CANONICAL_SIZE6: usize = 17;
/// Size of formatted MAC using [MacAddr8::format_string] and [MacAddrFormat::Canonical].
pub const MAC_CANONICAL_SIZE8: usize = 23;
/// Size of formatted MAC using [MacAddr6::format_string] and [MacAddrFormat::ColonNotation].
pub const MAC_COLON_NOTATION_SIZE6: usize = 17;
/// Size of formatted MAC using [MacAddr8::format_string] and [MacAddrFormat::ColonNotation].
pub const MAC_COLON_NOTATION_SIZE8: usize = 23;
/// Size of formatted MAC using [MacAddr6::format_string] and [MacAddrFormat::DotNotation].
pub const MAC_DOT_NOTATION_SIZE6: usize = 14;
/// Size of formatted MAC using [MacAddr8::format_string] and [MacAddrFormat::DotNotation].
pub const MAC_DOT_NOTATION_SIZE8: usize = 19;
/// Size of formatted MAC using [MacAddr6::format_string] and [MacAddrFormat::Hexadecimal].
pub const MAC_HEXADECIMAL_SIZE6: usize = 12;
/// Size of formatted MAC using [MacAddr8::format_string] and [MacAddrFormat::Hexadecimal].
pub const MAC_HEXADECIMAL_SIZE8: usize = 16;
/// Size of formatted MAC using [MacAddr6::format_string] and [MacAddrFormat::Hexadecimal0x].
pub const MAC_HEXADECIMAL0X_SIZE6: usize = 14;
/// Size of formatted MAC using [MacAddr8::format_string] and [MacAddrFormat::Hexadecimal0x].
pub const MAC_HEXADECIMAL0X_SIZE8: usize = 18;
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MacAddrFormat {
/// `AA-BB-CC-DD-EE-FF` (17 bytes) or `AA-BB-CC-DD-EE-FF-GG-HH` (23 bytes)
Canonical,
/// `AA:BB:CC:DD:EE:FF` (17 bytes) or `AA:BB:CC:DD:EE:FF:GG:HH` (23 bytes)
ColonNotation,
/// `AABB.CCDD.EEFF` (14 bytes) or `AABB.CCDD.EEFF.GGHH` (19 bytes)
DotNotation,
/// `AABBCCDDEEFF` (12 bytes) or `AABBCCDDEEFFGGHH` (16 bytes)
Hexadecimal,
/// `0xAABBCCDDEEFF` (14 bytes) or `0xAABBCCDDEEFFGGHH` (18 bytes)
Hexadecimal0x,
}
macro_rules! mac_impl {
($nm:ident, $sz:literal, $hex_sz:literal) => {
impl $nm {
pub const fn new(eui: [u8; $sz]) -> Self {
Self(eui)
}
pub fn random() -> Self {
let mut result = Self::default();
rand::rngs::OsRng.fill(result.as_mut_slice());
result
}
pub const fn broadcast() -> Self {
Self([0xFF; $sz])
}
pub const fn nil() -> Self {
Self([0; $sz])
}
/// Sets *locally administered* flag
pub fn set_local(&mut self, v: bool) {
if v {
self.0[0] |= 0b0000_0010;
} else {
self.0[0] &= !0b0000_0010;
}
}
/// Returns the state of *locally administered* flag
pub const fn is_local(&self) -> bool {
(self.0[0] & 0b0000_0010) != 0
}
/// Sets *multicast* flag
pub fn set_multicast(&mut self, v: bool) {
if v {
self.0[0] |= 0b0000_0001;
} else {
self.0[0] &= !0b0000_0001;
}
}
/// Returns the state of *multicast* flag
pub const fn is_multicast(&self) -> bool {
(self.0[0] & 0b0000_0001) != 0
}
/// Returns [organizationally unique identifier (OUI)](https://en.wikipedia.org/wiki/Organizationally_unique_identifier) of this MAC address
pub const fn oui(&self) -> [u8; 3] {
[self.0[0], self.0[1], self.0[2]]
}
/// Sets [organizationally unique identifier (OUI)](https://en.wikipedia.org/wiki/Organizationally_unique_identifier) for this MAC address
pub fn set_oui(&mut self, oui: [u8; 3]) {
self.0[..3].copy_from_slice(&oui);
}
/// Returns internal array representation for this MAC address, consuming it
pub const fn to_array(self) -> [u8; $sz] {
self.0
}
/// Returns internal array representation for this MAC address as [u8] slice
pub const fn as_slice(&self) -> &[u8] {
&self.0
}
/// Returns internal array representation for this MAC address as mutable [u8] slice
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.0
}
/// Returns internal array representation for this MAC address as [core::ffi::c_char] slice.
/// This can be useful in parsing `ifr_hwaddr`, for example.
pub const fn as_c_slice(&self) -> &[core::ffi::c_char] {
unsafe { &*(self.as_slice() as *const _ as *const [core::ffi::c_char]) }
}
/// Parse MAC address from string and return it as `MacAddr`.
/// This function can be used in const context, so MAC address can be parsed in compile-time.
pub const fn parse_str(s: &str) -> Result<Self, ParseError> {
match parser::MacParser::<$sz, $hex_sz>::parse(s) {
Ok(v) => Ok(Self(v)),
Err(e) => Err(e),
}
}
/// Write MAC address to `impl core::fmt::Write`, which can be used in `no_std` environments.
///
/// It can be used like this with [arrayvec::ArrayString] without allocations:
/// ```
/// use arrayvec::ArrayString;
/// use advmac::{MacAddr6, MacAddrFormat, MAC_CANONICAL_SIZE6};
///
/// let mac = MacAddr6::parse_str("AA:BB:CC:DD:EE:FF").unwrap();
///
/// let mut buf = ArrayString::<MAC_CANONICAL_SIZE6>::new();
/// mac.format_write(&mut buf, MacAddrFormat::Canonical).unwrap();
/// # assert_eq!(buf.as_str(), "AA-BB-CC-DD-EE-FF")
/// ```
pub fn format_write<T: fmt::Write>(
&self,
f: &mut T,
format: MacAddrFormat,
) -> fmt::Result {
match format {
MacAddrFormat::Canonical => self.write_internal(f, "", "-", "-"),
MacAddrFormat::ColonNotation => self.write_internal(f, "", ":", ":"),
MacAddrFormat::DotNotation => self.write_internal(f, "", "", "."),
MacAddrFormat::Hexadecimal => self.write_internal(f, "", "", ""),
MacAddrFormat::Hexadecimal0x => self.write_internal(f, "0x", "", ""),
}
}
/// Write MAC address to [String]. This function uses [Self::format_write] internally and
/// produces the same result, but in string form, which can be convenient in non-constrainted
/// environments.
pub fn format_string(&self, format: MacAddrFormat) -> String {
let mut buf = String::new();
self.format_write(&mut buf, format).unwrap();
buf
}
}
impl Display for $nm {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.format_write(f, MacAddrFormat::Canonical)
}
}
impl Debug for $nm {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.format_write(f, MacAddrFormat::Canonical)
}
}
impl From<[u8; $sz]> for $nm {
fn from(arr: [u8; $sz]) -> Self {
Self(arr)
}
}
impl TryFrom<&[u8]> for $nm {
type Error = ParseError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into().map_err(|_| ParseError::InvalidMac)?))
}
}
#[cfg(not(target_arch = "aarch64"))]
impl TryFrom<&[core::ffi::c_char]> for $nm {
type Error = ParseError;
fn try_from(value: &[core::ffi::c_char]) -> Result<Self, Self::Error> {
Self::try_from(unsafe { &*(value as *const _ as *const [u8]) })
}
}
impl TryFrom<&str> for $nm {
type Error = ParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::parse_str(value)
}
}
impl TryFrom<String> for $nm {
type Error = ParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::parse_str(&value)
}
}
impl FromStr for $nm {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse_str(s)
}
}
impl Serialize for $nm {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut buf = ArrayString::<MAC_MAX_SIZE>::new();
self.format_write(&mut buf, MacAddrFormat::Canonical)
.unwrap();
s.serialize_str(buf.as_ref())
}
}
impl<'de> Deserialize<'de> for $nm {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Self::from_str(ArrayString::<MAC_MAX_SIZE>::deserialize(d)?.as_ref())
.map_err(serde::de::Error::custom)
}
}
};
}
/// MAC address, represented as EUI-48
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct MacAddr6([u8; 6]);
/// MAC address, represented as EUI-64
#[repr(transparent)]
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct MacAddr8([u8; 8]);
mac_impl!(MacAddr6, 6, 12);
mac_impl!(MacAddr8, 8, 16);
impl MacAddr6 {
pub const fn to_modified_eui64(self) -> MacAddr8 {
let b = self.to_array();
MacAddr8([b[0] ^ 0b00000010, b[1], b[2], 0xFF, 0xFE, b[3], b[4], b[5]])
}
pub const fn try_from_modified_eui64(eui64: MacAddr8) -> Result<Self, IpError> {
let b = eui64.to_array();
if (b[3] == 0xFF) | (b[4] == 0xFE) {
Ok(Self([b[0] ^ 0b00000010, b[1], b[2], b[5], b[6], b[7]]))
} else {
Err(IpError::NotLinkLocal)
}
}
pub const fn to_link_local_ipv6(self) -> Ipv6Addr {
let mac64 = self.to_modified_eui64().to_array();
Ipv6Addr::new(
0xFE80,
0x0000,
0x0000,
0x0000,
((mac64[0] as u16) << 8) + mac64[1] as u16,
((mac64[2] as u16) << 8) + mac64[3] as u16,
((mac64[4] as u16) << 8) + mac64[5] as u16,
((mac64[6] as u16) << 8) + mac64[7] as u16,
)
}
pub const fn try_from_link_local_ipv6(ip: Ipv6Addr) -> Result<Self, IpError> {
let octets = ip.octets();
if (octets[0] != 0xFE)
| (octets[1] != 0x80)
| (octets[2] != 0x00)
| (octets[3] != 0x00)
| (octets[4] != 0x00)
| (octets[5] != 0x00)
| (octets[6] != 0x00)
| (octets[7] != 0x00)
| (octets[11] != 0xFF)
| (octets[12] != 0xFE)
{
return Err(IpError::NotLinkLocal);
}
Ok(Self([
octets[8] ^ 0b00000010,
octets[9],
octets[10],
octets[13],
octets[14],
octets[15],
]))
}
pub const fn try_from_multicast_ipv4(ip: Ipv4Addr) -> Result<Self, IpError> {
if !ip.is_multicast() {
return Err(IpError::NotMulticast);
}
let b = ip.octets();
Ok(Self::new([0x01, 0x00, 0x5E, b[1] & 0x7F, b[2], b[3]]))
}
pub const fn try_from_multicast_ipv6(ip: Ipv6Addr) -> Result<Self, IpError> {
if !ip.is_multicast() {
return Err(IpError::NotMulticast);
}
let b = ip.octets();
Ok(Self::new([0x33, 0x33, b[12], b[13], b[14], b[15]]))
}
pub const fn try_from_multicast_ip(ip: IpAddr) -> Result<Self, IpError> {
match ip {
IpAddr::V4(ip) => Self::try_from_multicast_ipv4(ip),
IpAddr::V6(ip) => Self::try_from_multicast_ipv6(ip),
}
}
}
impl MacAddr6 {
// String representations
fn write_internal<T: fmt::Write>(
&self,
f: &mut T,
pre: &str,
sep: &str,
sep2: &str,
) -> fmt::Result {
write!(
f,
"{pre}{:02X}{sep}{:02X}{sep2}{:02X}{sep}{:02X}{sep2}{:02X}{sep}{:02X}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
)
}
}
impl MacAddr8 {
// String representations
fn write_internal<T: fmt::Write>(
&self,
f: &mut T,
pre: &str,
sep: &str,
sep2: &str,
) -> fmt::Result {
write!(
f,
"{pre}{:02X}{sep}{:02X}{sep2}{:02X}{sep}{:02X}{sep2}{:02X}{sep}{:02X}{sep2}{:02X}{sep}{:02X}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7]
)
}
}
/// Convenience macro for creating [MacAddr6] in compile-time.
///
/// Example:
/// ```
/// use advmac::{mac6, MacAddr6};
/// const MAC6: MacAddr6 = mac6!("11:22:33:44:55:66");
/// # assert_eq!(MAC6.to_array(), [0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
/// ```
#[macro_export]
macro_rules! mac6 {
($s:expr) => {
match $crate::MacAddr6::parse_str($s) {
Ok(mac) => mac,
Err(_) => panic!("Invalid MAC address"),
}
};
}
/// Convenience macro for creating [MacAddr8] in compile-time.
///
/// Example:
/// ```
/// use advmac::{mac8, MacAddr8};
/// const MAC8: MacAddr8 = mac8!("11:22:33:44:55:66:77:88");
/// # assert_eq!(MAC8.to_array(), [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]);
/// ```
#[macro_export]
macro_rules! mac8 {
($s:expr) => {
match $crate::MacAddr8::parse_str($s) {
Ok(mac) => mac,
Err(_) => panic!("Invalid MAC address"),
}
};
}
#[cfg(test)]
mod test {
#[test]
fn test_flags_roundtrip() {
let mut addr = mac6!("50:74:f2:b1:a8:7f");
assert!(!addr.is_local());
assert!(!addr.is_multicast());
addr.set_multicast(true);
assert!(!addr.is_local());
assert!(addr.is_multicast());
addr.set_local(true);
assert!(addr.is_local());
assert!(addr.is_multicast());
addr.set_multicast(false);
assert!(addr.is_local());
assert!(!addr.is_multicast());
addr.set_local(false);
assert!(!addr.is_local());
assert!(!addr.is_multicast());
}
}

View File

@ -1,113 +0,0 @@
use crate::ParseError;
// This whole thing is written this way to be const.
// If you want normal hex handling, just use hex crate
pub struct MacParser<const N: usize, const N2: usize>;
impl<const N: usize, const N2: usize> MacParser<N, N2> {
const CANONICAL_COLON_SIZE: usize = 3 * N - 1;
const DOT_NOTATION_SIZE: usize = (2 * N) + (N / 2 - 1);
const HEXADECIMAL_SIZE: usize = 2 * N;
const HEXADECIMAL0X_SIZE: usize = 2 * N + 2;
#[inline]
const fn nibble(v: u8) -> Result<u8, ParseError> {
match v {
b'A'..=b'F' => Ok(10 + (v - b'A')),
b'a'..=b'f' => Ok(10 + (v - b'a')),
b'0'..=b'9' => Ok(v - b'0'),
_ => Err(ParseError::InvalidMac),
}
}
#[inline]
const fn byte(b1: u8, b2: u8) -> Result<u8, ParseError> {
// ? is not available in const
match (Self::nibble(b1), Self::nibble(b2)) {
(Ok(v1), Ok(v2)) => Ok((v1 << 4) + v2),
(Err(e), _) | (_, Err(e)) => Err(e),
}
}
const fn from_hex(s: &[u8]) -> Result<[u8; N], ParseError> {
if s.len() != Self::HEXADECIMAL_SIZE {
return Err(ParseError::InvalidLength { length: s.len() });
}
let mut result = [0u8; N];
// for-loops and iterators are unavailable in const
let mut i = 0;
while i < N {
result[i] = match Self::byte(s[2 * i], s[2 * i + 1]) {
Ok(v) => v,
Err(e) => return Err(e),
};
i += 1;
}
Ok(result)
}
const fn check_separator(s: &[u8], sep: u8, group_len: usize) -> bool {
let mut i = group_len;
while i < s.len() {
if s[i] != sep {
return false;
}
i += group_len + 1;
}
true
}
const fn parse_separated(s: &[u8], sep: u8, group_len: usize) -> Result<[u8; N], ParseError> {
let expected_len = (2 * N) + ((2 * N) / group_len) - 1;
if s.len() != expected_len {
return Err(ParseError::InvalidLength { length: s.len() });
}
if !Self::check_separator(s, sep, group_len) {
return Err(ParseError::InvalidMac);
}
let mut hex_buf = [0u8; N2];
let (mut in_i, mut out_i) = (0, 0);
while in_i < s.len() {
if (in_i + 1) % (group_len + 1) != 0 {
hex_buf[out_i] = s[in_i];
out_i += 1;
}
in_i += 1;
}
Self::from_hex(&hex_buf)
}
pub const fn parse(s: &str) -> Result<[u8; N], ParseError> {
let s = s.as_bytes();
if s.len() == Self::HEXADECIMAL_SIZE {
Self::from_hex(s)
} else if (s.len() == Self::HEXADECIMAL0X_SIZE) && (s[0] == b'0') && (s[1] == b'x') {
// unsafe is the only way I know to make it const
Self::from_hex(unsafe {
core::slice::from_raw_parts(s.as_ptr().offset(2), s.len() - 2)
})
} else if s.len() == Self::CANONICAL_COLON_SIZE {
let sep = s[2];
match sep {
b'-' | b':' => Self::parse_separated(s, sep, 2),
_ => Err(ParseError::InvalidMac),
}
} else if s.len() == Self::DOT_NOTATION_SIZE {
let sep = s[4];
match sep {
b'.' => Self::parse_separated(s, sep, 4),
_ => Err(ParseError::InvalidMac),
}
} else {
Err(ParseError::InvalidLength { length: s.len() })
}
}
}

View File

@ -1,15 +0,0 @@
# This package is from https://github.com/stratis-storage/loopdev-3
# Edera maintains an in-tree version because the goals of krata mean that
# there is as little binding generation as possible, especially bindings which
# prevent development from macOS, like the original library.
[package]
name = "loopdev"
version.workspace = true
license = "MIT"
edition = "2021"
[dependencies]
libc = { workspace = true }
[lib]
name = "loopdev"

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2023 Anne Mulhern
Copyright (c) 2016 Michael Daffin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,147 +0,0 @@
/* originally generated by rust-bindgen */
/* modified to remove unused content by Edera */
#![allow(non_camel_case_types)]
pub const __BITS_PER_LONG: u32 = 64;
pub const __FD_SETSIZE: u32 = 1024;
pub const LOOP_SET_FD: u32 = 19456;
pub const LOOP_CLR_FD: u32 = 19457;
pub const LOOP_SET_STATUS64: u32 = 19460;
pub const LOOP_SET_CAPACITY: u32 = 19463;
pub const LOOP_CTL_ADD: u32 = 19584;
pub const LOOP_CTL_GET_FREE: u32 = 19586;
pub const LO_FLAGS_READ_ONLY: _bindgen_ty_1 = 1;
pub const LO_FLAGS_AUTOCLEAR: _bindgen_ty_1 = 4;
pub const LO_FLAGS_PARTSCAN: _bindgen_ty_1 = 8;
pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
pub type __kernel_long_t = ::std::os::raw::c_long;
pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
pub type __kernel_ino_t = __kernel_ulong_t;
pub type __kernel_mode_t = ::std::os::raw::c_uint;
pub type __kernel_pid_t = ::std::os::raw::c_int;
pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
pub type __kernel_uid_t = ::std::os::raw::c_uint;
pub type __kernel_gid_t = ::std::os::raw::c_uint;
pub type __kernel_suseconds_t = __kernel_long_t;
pub type __kernel_daddr_t = ::std::os::raw::c_int;
pub type __kernel_uid32_t = ::std::os::raw::c_uint;
pub type __kernel_gid32_t = ::std::os::raw::c_uint;
pub type __kernel_size_t = __kernel_ulong_t;
pub type __kernel_ssize_t = __kernel_long_t;
pub type __kernel_ptrdiff_t = __kernel_long_t;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct __kernel_fsid_t {
pub val: [::std::os::raw::c_int; 2usize],
}
pub type __kernel_off_t = __kernel_long_t;
pub type __kernel_loff_t = ::std::os::raw::c_longlong;
pub type __kernel_old_time_t = __kernel_long_t;
pub type __kernel_time_t = __kernel_long_t;
pub type __kernel_time64_t = ::std::os::raw::c_longlong;
pub type __kernel_clock_t = __kernel_long_t;
pub type __kernel_timer_t = ::std::os::raw::c_int;
pub type __kernel_clockid_t = ::std::os::raw::c_int;
pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
pub type __s8 = ::std::os::raw::c_schar;
pub type __u8 = ::std::os::raw::c_uchar;
pub type __s16 = ::std::os::raw::c_short;
pub type __u16 = ::std::os::raw::c_ushort;
pub type __s32 = ::std::os::raw::c_int;
pub type __u32 = ::std::os::raw::c_uint;
pub type __s64 = ::std::os::raw::c_longlong;
pub type __u64 = ::std::os::raw::c_ulonglong;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct __kernel_fd_set {
pub fds_bits: [::std::os::raw::c_ulong; 16usize],
}
pub type __kernel_sighandler_t = Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
pub type __kernel_key_t = ::std::os::raw::c_int;
pub type __kernel_mqd_t = ::std::os::raw::c_int;
pub type __le16 = __u16;
pub type __be16 = __u16;
pub type __le32 = __u32;
pub type __be32 = __u32;
pub type __le64 = __u64;
pub type __be64 = __u64;
pub type __sum16 = __u16;
pub type __wsum = __u32;
pub type __poll_t = ::std::os::raw::c_uint;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct loop_info {
pub lo_number: ::std::os::raw::c_int,
pub lo_device: __kernel_old_dev_t,
pub lo_inode: ::std::os::raw::c_ulong,
pub lo_rdevice: __kernel_old_dev_t,
pub lo_offset: ::std::os::raw::c_int,
pub lo_encrypt_type: ::std::os::raw::c_int,
pub lo_encrypt_key_size: ::std::os::raw::c_int,
pub lo_flags: ::std::os::raw::c_int,
pub lo_name: [::std::os::raw::c_char; 64usize],
pub lo_encrypt_key: [::std::os::raw::c_uchar; 32usize],
pub lo_init: [::std::os::raw::c_ulong; 2usize],
pub reserved: [::std::os::raw::c_char; 4usize],
}
impl Default for loop_info {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct loop_info64 {
pub lo_device: __u64,
pub lo_inode: __u64,
pub lo_rdevice: __u64,
pub lo_offset: __u64,
pub lo_sizelimit: __u64,
pub lo_number: __u32,
pub lo_encrypt_type: __u32,
pub lo_encrypt_key_size: __u32,
pub lo_flags: __u32,
pub lo_file_name: [__u8; 64usize],
pub lo_crypt_name: [__u8; 64usize],
pub lo_encrypt_key: [__u8; 32usize],
pub lo_init: [__u64; 2usize],
}
impl Default for loop_info64 {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct loop_config {
pub fd: __u32,
pub block_size: __u32,
pub info: loop_info64,
pub __reserved: [__u64; 8usize],
}
impl Default for loop_config {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}

View File

@ -1,475 +0,0 @@
// Taken from https://github.com/stratis-storage/loopdev-3/blob/master/src/lib.rs
// Licensed under MIT.
//! Setup and control loop devices.
//!
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
//!
//! # Examples
//!
//! Default options:
//!
//! ```no_run
//! use loopdev::LoopControl;
//! let lc = LoopControl::open().unwrap();
//! let ld = lc.next_free().unwrap();
//!
//! println!("{}", ld.path().unwrap().display());
//!
//! ld.attach_file("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```
//!
//! Custom options:
//!
//! ```no_run
//! # use loopdev::LoopControl;
//! # let lc = LoopControl::open().unwrap();
//! # let ld = lc.next_free().unwrap();
//! #
//! ld.with()
//! .part_scan(true)
//! .offset(512 * 1024 * 1024) // 512 MiB
//! .size_limit(1024 * 1024 * 1024) // 1GiB
//! .attach("disk.img").unwrap();
//! // ...
//! ld.detach().unwrap();
//! ```
mod bindings;
mod linux;
use crate::bindings::{
loop_info64, LOOP_CLR_FD, LOOP_CTL_ADD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD,
LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY,
};
use libc::ioctl;
use std::ffi::c_int;
use std::{
default::Default,
fs::{File, OpenOptions},
io,
os::unix::prelude::*,
path::{Path, PathBuf},
};
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
type IoctlRequest = std::ffi::c_ulong;
#[cfg(any(target_os = "android", target_env = "musl"))]
type IoctlRequest = c_int;
const LOOP_CONTROL: &str = "/dev/loop-control";
#[cfg(not(target_os = "android"))]
const LOOP_PREFIX: &str = "/dev/loop";
#[cfg(target_os = "android")]
const LOOP_PREFIX: &str = "/dev/block/loop";
/// Interface to the loop control device: `/dev/loop-control`.
#[derive(Debug)]
pub struct LoopControl {
dev_file: File,
}
impl LoopControl {
/// Opens the loop control device.
///
/// # Errors
///
/// This function will return an error for various reasons when opening
/// the loop control file `/dev/loop-control`. See
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
/// for further details.
pub fn open() -> io::Result<Self> {
Ok(Self {
dev_file: OpenOptions::new()
.read(true)
.write(true)
.open(LOOP_CONTROL)?,
})
}
/// Finds and opens the next available loop device.
///
/// # Examples
///
/// ```no_run
/// use loopdev::LoopControl;
/// let lc = LoopControl::open().unwrap();
/// let ld = lc.next_free().unwrap();
/// println!("{}", ld.path().unwrap().display());
/// ```
///
/// # Errors
///
/// This function will return an error for various reasons when opening
/// the loop device file `/dev/loopX`. See
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
/// for further details.
pub fn next_free(&self) -> io::Result<LoopDevice> {
let dev_num = ioctl_to_error(unsafe {
ioctl(
self.dev_file.as_raw_fd() as c_int,
LOOP_CTL_GET_FREE as IoctlRequest,
)
})?;
LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
}
/// Add and opens a new loop device.
///
/// # Examples
///
/// ```no_run
/// use loopdev::LoopControl;
/// let lc = LoopControl::open().unwrap();
/// let ld = lc.add(1).unwrap();
/// println!("{}", ld.path().unwrap().display());
/// ```
///
/// # Errors
///
/// This funcitons will return an error when a loop device with the passed
/// number exists or opening the newly created device fails.
pub fn add(&self, n: u32) -> io::Result<LoopDevice> {
let dev_num = ioctl_to_error(unsafe {
ioctl(
self.dev_file.as_raw_fd() as c_int,
LOOP_CTL_ADD as IoctlRequest,
n as c_int,
)
})?;
LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
}
}
impl AsRawFd for LoopControl {
fn as_raw_fd(&self) -> RawFd {
self.dev_file.as_raw_fd()
}
}
impl IntoRawFd for LoopControl {
fn into_raw_fd(self) -> RawFd {
self.dev_file.into_raw_fd()
}
}
/// Interface to a loop device ie `/dev/loop0`.
#[derive(Debug)]
pub struct LoopDevice {
device: File,
}
impl AsRawFd for LoopDevice {
fn as_raw_fd(&self) -> RawFd {
self.device.as_raw_fd()
}
}
impl IntoRawFd for LoopDevice {
fn into_raw_fd(self) -> RawFd {
self.device.into_raw_fd()
}
}
impl LoopDevice {
/// Opens a loop device.
///
/// # Errors
///
/// This function will return an error for various reasons when opening
/// the given loop device file. See
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
/// for further details.
pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
// TODO create dev if it does not exist and begins with LOOP_PREFIX
Ok(Self {
device: OpenOptions::new().read(true).write(true).open(dev)?,
})
}
/// Attach the loop device to a file with given options.
///
/// # Examples
///
/// Attach the device to a file.
///
/// ```no_run
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
/// ld.with().part_scan(true).attach("disk.img").unwrap();
/// # ld.detach().unwrap();
/// ```
pub fn with(&self) -> AttachOptions<'_> {
AttachOptions {
device: self,
info: loop_info64::default(),
}
}
/// Attach the loop device to a file that maps to the whole file.
///
/// # Examples
///
/// Attach the device to a file.
///
/// ```no_run
/// use loopdev::LoopDevice;
/// let ld = LoopDevice::open("/dev/loop0").unwrap();
/// ld.attach_file("disk.img").unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// # Errors
///
/// This function will return an error for various reasons. Either when
/// opening the backing file (see
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
/// for further details) or when calling the ioctl to attach the backing
/// file to the device.
pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
let info = loop_info64 {
..Default::default()
};
Self::attach_with_loop_info(self, backing_file, info)
}
/// Attach the loop device to a file with `loop_info64`.
fn attach_with_loop_info(
&self, // TODO should be mut? - but changing it is a breaking change
backing_file: impl AsRef<Path>,
info: loop_info64,
) -> io::Result<()> {
let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;
let bf = OpenOptions::new()
.read(true)
.write(write_access)
.open(backing_file)?;
self.attach_fd_with_loop_info(bf, info)
}
/// Attach the loop device to a fd with `loop_info`.
fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
// Attach the file
ioctl_to_error(unsafe {
ioctl(
self.device.as_raw_fd() as c_int,
LOOP_SET_FD as IoctlRequest,
bf.as_raw_fd() as c_int,
)
})?;
let result = unsafe {
ioctl(
self.device.as_raw_fd() as c_int,
LOOP_SET_STATUS64 as IoctlRequest,
&info,
)
};
match ioctl_to_error(result) {
Err(err) => {
// Ignore the error to preserve the original error
let _detach_err = self.detach();
Err(err)
}
Ok(_) => Ok(()),
}
}
/// Get the path of the loop device.
pub fn path(&self) -> Option<PathBuf> {
let mut p = PathBuf::from("/proc/self/fd");
p.push(self.device.as_raw_fd().to_string());
std::fs::read_link(&p).ok()
}
/// Get the device major number
///
/// # Errors
///
/// This function needs to stat the backing file and can fail if there is
/// an IO error.
#[allow(clippy::unnecessary_cast)]
pub fn major(&self) -> io::Result<u32> {
self.device
.metadata()
.map(|m| linux::major(m.rdev()))
.map(|m| m as u32)
}
/// Get the device major number
///
/// # Errors
///
/// This function needs to stat the backing file and can fail if there is
/// an IO error.
#[allow(clippy::unnecessary_cast)]
pub fn minor(&self) -> io::Result<u32> {
self.device
.metadata()
.map(|m| linux::minor(m.rdev()))
.map(|m| m as u32)
}
/// Detach a loop device from its backing file.
///
/// Note that the device won't fully detach until a short delay after the underling device file
/// gets closed. This happens when `LoopDev` goes out of scope so you should ensure the `LoopDev`
/// lives for a short a time as possible.
///
/// # Examples
///
/// ```no_run
/// use loopdev::LoopDevice;
/// let ld = LoopDevice::open("/dev/loop0").unwrap();
/// # ld.attach_file("disk.img").unwrap();
/// ld.detach().unwrap();
/// ```
///
/// # Errors
///
/// This function will return an error for various reasons when calling the
/// ioctl to detach the backing file from the device.
pub fn detach(&self) -> io::Result<()> {
ioctl_to_error(unsafe {
ioctl(
self.device.as_raw_fd() as c_int,
LOOP_CLR_FD as IoctlRequest,
0,
)
})?;
Ok(())
}
/// Resize a live loop device. If the size of the backing file changes this can be called to
/// inform the loop driver about the new size.
///
/// # Errors
///
/// This function will return an error for various reasons when calling the
/// ioctl to set the capacity of the device.
pub fn set_capacity(&self) -> io::Result<()> {
ioctl_to_error(unsafe {
ioctl(
self.device.as_raw_fd() as c_int,
LOOP_SET_CAPACITY as IoctlRequest,
0,
)
})?;
Ok(())
}
}
/// Used to set options when attaching a device. Created with [`LoopDevice::with`()].
///
/// # Examples
///
/// Enable partition scanning on attach:
///
/// ```no_run
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
/// ld.with()
/// .part_scan(true)
/// .attach("disk.img")
/// .unwrap();
/// # ld.detach().unwrap();
/// ```
///
/// A 1MiB slice of the file located at 1KiB into the file.
///
/// ```no_run
/// use loopdev::LoopDevice;
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
/// ld.with()
/// .offset(1024*1024)
/// .size_limit(1024*1024*1024)
/// .attach("disk.img")
/// .unwrap();
/// # ld.detach().unwrap();
/// ```
#[must_use]
pub struct AttachOptions<'d> {
device: &'d LoopDevice,
info: loop_info64,
}
impl AttachOptions<'_> {
/// Offset in bytes from the start of the backing file the data will start at.
pub fn offset(mut self, offset: u64) -> Self {
self.info.lo_offset = offset;
self
}
/// Maximum size of the data in bytes.
pub fn size_limit(mut self, size_limit: u64) -> Self {
self.info.lo_sizelimit = size_limit;
self
}
/// Set read only flag
pub fn read_only(mut self, read_only: bool) -> Self {
if read_only {
self.info.lo_flags |= LO_FLAGS_READ_ONLY;
} else {
self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
}
self
}
/// Set autoclear flag
pub fn autoclear(mut self, autoclear: bool) -> Self {
if autoclear {
self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
} else {
self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
}
self
}
/// Force the kernel to scan the partition table on a newly created loop device. Note that the
/// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
pub fn part_scan(mut self, enable: bool) -> Self {
if enable {
self.info.lo_flags |= LO_FLAGS_PARTSCAN;
} else {
self.info.lo_flags &= !LO_FLAGS_PARTSCAN;
}
self
}
/// Attach the loop device to a file with the set options.
///
/// # Errors
///
/// This function will return an error for various reasons. Either when
/// opening the backing file (see
/// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
/// for further details) or when calling the ioctl to attach the backing
/// file to the device.
pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
self.device.attach_with_loop_info(backing_file, self.info)?;
Ok(())
}
/// Attach the loop device to an fd
///
/// # Errors
///
/// This function will return an error for various reasons when calling the
/// ioctl to attach the backing file to the device.
pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
self.device
.attach_fd_with_loop_info(backing_file_fd, self.info)?;
Ok(())
}
}
fn ioctl_to_error(ret: i32) -> io::Result<i32> {
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(ret)
}
}

View File

@ -1,15 +0,0 @@
use std::ffi::c_uint;
pub fn major(dev: u64) -> c_uint {
let mut major = 0;
major |= (dev & 0x00000000000fff00) >> 8;
major |= (dev & 0xfffff00000000000) >> 32;
major as c_uint
}
pub fn minor(dev: u64) -> c_uint {
let mut minor = 0;
minor |= dev & 0x00000000000000ff;
minor |= (dev & 0x00000ffffff00000) >> 12;
minor as c_uint
}

View File

@ -1,5 +1,5 @@
[package] [package]
name = "xencall" name = "krata-xencall"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "xenclient" name = "krata-xenclient"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"
@ -9,14 +9,14 @@ elf = { workspace = true }
flate2 = { workspace = true } flate2 = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
krata-xencall = { path = "../xencall" }
krata-xenstore = { path = "../xenstore" }
memchr = { workspace = true } memchr = { workspace = true }
slice-copy = { workspace = true } slice-copy = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
uuid = { workspace = true } uuid = { workspace = true }
xz2 = { workspace = true } xz2 = { workspace = true }
xencall = { path = "../xencall" }
xenstore = { path = "../xenstore" }
[dev-dependencies] [dev-dependencies]
env_logger = { workspace = true } env_logger = { workspace = true }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "xenevtchn" name = "krata-xenevtchn"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "xengnt" name = "krata-xengnt"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "xenstore" name = "krata-xenstore"
version.workspace = true version.workspace = true
edition = "2021" edition = "2021"
resolver = "2" resolver = "2"