commit
7741b4054a
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
*~
|
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "readable-perms"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Avril <flanchan@cumallover.me>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "1.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.2"
|
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
extern crate rustc_version;
|
||||||
|
use rustc_version::{version, version_meta, Channel};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Assert we haven't travelled back in time
|
||||||
|
assert!(version().unwrap().major >= 1);
|
||||||
|
|
||||||
|
// Set cfg flags depending on release channel
|
||||||
|
match version_meta().unwrap().channel {
|
||||||
|
Channel::Stable => {
|
||||||
|
println!("cargo:rustc-cfg=stable");
|
||||||
|
}
|
||||||
|
Channel::Beta => {
|
||||||
|
println!("cargo:rustc-cfg=beta");
|
||||||
|
}
|
||||||
|
Channel::Nightly => {
|
||||||
|
println!("cargo:rustc-cfg=nightly");
|
||||||
|
}
|
||||||
|
Channel::Dev => {
|
||||||
|
println!("cargo:rustc-cfg=dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,377 @@
|
|||||||
|
//! Unix permissions `mode_t` in readable format.
|
||||||
|
//!
|
||||||
|
//No I don't care about Microshaft wangblows
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
enum Class
|
||||||
|
{
|
||||||
|
Owner(Bit),
|
||||||
|
Group(Bit),
|
||||||
|
Other(Bit),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Class
|
||||||
|
{
|
||||||
|
/// Lmfao. Fuck bit masks and unix permissiong, `const fn` so idc
|
||||||
|
#[cfg(nightly)]
|
||||||
|
const fn mode(&self) -> u32
|
||||||
|
{
|
||||||
|
macro_rules! map {
|
||||||
|
($bit:expr, $bt:path, $constant:ident) => {
|
||||||
|
if $bit.contains($bt) {
|
||||||
|
$constant
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Owner(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_USER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_USER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_USER_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Group(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_GROUP_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_GROUP_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Other(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_OTHER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_OTHER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(nightly))]
|
||||||
|
fn mode(&self) -> u32 //rip stable
|
||||||
|
{
|
||||||
|
macro_rules! map {
|
||||||
|
($bit:expr, $bt:path, $constant:ident) => {
|
||||||
|
if $bit.contains($bt) {
|
||||||
|
$constant
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::Owner(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_USER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_USER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_USER_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Group(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_GROUP_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_GROUP_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Other(bit) => {
|
||||||
|
0u32 |
|
||||||
|
map!(bit, Bit::Read, MODE_OTHER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_OTHER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(nightly)]
|
||||||
|
const fn mask_mode(&self, bit: u32) -> Bit
|
||||||
|
{
|
||||||
|
macro_rules! map {
|
||||||
|
($bit:expr, $bt:path, $constant:ident) => {
|
||||||
|
if ($bit & $constant) == $constant {$bt.bits()} else {0u32}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bit::from_bits_truncate(match self {
|
||||||
|
Self::Owner(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_USER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_USER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_USER_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Group(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_GROUP_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_GROUP_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Other(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_OTHER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_OTHER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(nightly))]
|
||||||
|
fn mask_mode(&self, bit: u32) -> Bit
|
||||||
|
{
|
||||||
|
macro_rules! map {
|
||||||
|
($bit:expr, $bt:path, $constant:ident) => {
|
||||||
|
if ($bit & $constant) == $constant {$bt.bits()} else {0u32}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bit::from_bits_truncate(match self {
|
||||||
|
Self::Owner(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_USER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_USER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_USER_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Group(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_GROUP_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_GROUP_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_GROUP_EXECUTE)
|
||||||
|
},
|
||||||
|
Self::Other(_) => {
|
||||||
|
map!(bit, Bit::Read, MODE_OTHER_READ) |
|
||||||
|
map!(bit, Bit::Write, MODE_OTHER_WRITE) |
|
||||||
|
map!(bit, Bit::Execute, MODE_OTHER_EXECUTE)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MODE_USER: u32 = 0o700;
|
||||||
|
const MODE_USER_READ: u32 = 0o400;
|
||||||
|
const MODE_USER_WRITE: u32 = 0o200;
|
||||||
|
const MODE_USER_EXECUTE: u32 = 0o100;
|
||||||
|
|
||||||
|
const MODE_GROUP: u32 = 0o70;
|
||||||
|
const MODE_GROUP_READ: u32 = 0o40;
|
||||||
|
const MODE_GROUP_WRITE: u32 = 0o20;
|
||||||
|
const MODE_GROUP_EXECUTE: u32 = 0o10;
|
||||||
|
|
||||||
|
const MODE_OTHER: u32 = 0o7;
|
||||||
|
const MODE_OTHER_READ: u32 = 0o4;
|
||||||
|
const MODE_OTHER_WRITE: u32 = 0o2;
|
||||||
|
const MODE_OTHER_EXECUTE: u32 = 0o1;
|
||||||
|
|
||||||
|
const MODE: u32 = 0o777;
|
||||||
|
|
||||||
|
bitflags!{
|
||||||
|
/// The mode mask for each individual class of `User`.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// No this does not map directly to `mode_t`. Use `User.mode(bits)` and `User.from_mode(mode)`.
|
||||||
|
pub struct Bit: u32 {
|
||||||
|
const None = 0;
|
||||||
|
const Read = 1;
|
||||||
|
const Write = 2;
|
||||||
|
const Execute = 4;
|
||||||
|
const Mask = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags!{
|
||||||
|
// not supported, don't care
|
||||||
|
struct StickyBit: u32{
|
||||||
|
const None = 0;
|
||||||
|
const Setuid = 0o40000;
|
||||||
|
const Setgid = 0o20000;
|
||||||
|
const SaveSwap = 0o10000;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Permissions struct in readable format
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Ord, PartialOrd)]
|
||||||
|
pub struct Permissions
|
||||||
|
{
|
||||||
|
|
||||||
|
pub u_owner: Bit,
|
||||||
|
pub u_group: Bit,
|
||||||
|
pub u_other: Bit,
|
||||||
|
|
||||||
|
_u_sticky: StickyBit, //idc about this either
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||||
|
pub enum User
|
||||||
|
{
|
||||||
|
Owner,
|
||||||
|
Group,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl User
|
||||||
|
{
|
||||||
|
/// Convert this class's representation of `Bit` into the corresponding `mode_t`.
|
||||||
|
pub const fn mode(self, mask: Bit) -> u32
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
User::Owner => Class::Owner(mask).mode(),
|
||||||
|
User::Group => Class::Group(mask).mode(),
|
||||||
|
User::Other => Class::Other(mask).mode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a `mode_t` `u32` into `Bit` with this class' user.
|
||||||
|
pub const fn from_mode(self, mask: u32) -> Bit
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
User::Owner => Class::Owner(Bit::Mask).mask_mode(mask),
|
||||||
|
User::Group => Class::Group(Bit::Mask).mask_mode(mask),
|
||||||
|
User::Other => Class::Other(Bit::Mask).mask_mode(mask),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Permissions
|
||||||
|
{
|
||||||
|
#[inline] fn default() -> Self
|
||||||
|
{
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Permissions
|
||||||
|
{
|
||||||
|
/// Create a new empty `Permissions` struct
|
||||||
|
pub const fn new() -> Self
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
u_owner: Bit::None,
|
||||||
|
u_group: Bit::None,
|
||||||
|
u_other: Bit::None,
|
||||||
|
_u_sticky: StickyBit::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask impl
|
||||||
|
impl Permissions
|
||||||
|
{
|
||||||
|
#[inline] const fn c_owner(&self) -> Class
|
||||||
|
{
|
||||||
|
Class::Owner(self.u_owner)
|
||||||
|
}
|
||||||
|
#[inline] const fn c_group(&self) -> Class
|
||||||
|
{
|
||||||
|
Class::Group(self.u_group)
|
||||||
|
}
|
||||||
|
#[inline] const fn c_other(&self) -> Class
|
||||||
|
{
|
||||||
|
Class::Other(self.u_other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert into `mode_t` repr.
|
||||||
|
#[inline] pub const fn mask(&self) -> u32
|
||||||
|
{
|
||||||
|
self.c_owner().mode() |
|
||||||
|
self.c_group().mode() |
|
||||||
|
self.c_other().mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert from `mode_t` repr.
|
||||||
|
#[inline] pub const fn from_mask(bit: u32) -> Self
|
||||||
|
{
|
||||||
|
Self{
|
||||||
|
u_owner: Class::Owner(Bit::Mask).mask_mode(bit),
|
||||||
|
u_group: Class::Group(Bit::Mask).mask_mode(bit),
|
||||||
|
u_other: Class::Other(Bit::Mask).mask_mode(bit),
|
||||||
|
|
||||||
|
_u_sticky: StickyBit::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume and add a mask.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// For builder pattern
|
||||||
|
#[inline] pub const fn add_mask(self, class: User, bit: Bit) -> Self
|
||||||
|
{
|
||||||
|
match class {
|
||||||
|
User::Owner => {
|
||||||
|
Self {
|
||||||
|
u_owner: Bit::from_bits_truncate(self.u_owner.bits() | bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
User::Group => {
|
||||||
|
Self {
|
||||||
|
u_group: Bit::from_bits_truncate(self.u_group.bits() | bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
User::Other => {
|
||||||
|
Self {
|
||||||
|
u_other: Bit::from_bits_truncate(self.u_other.bits() | bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Consume and remove a mask.
|
||||||
|
///
|
||||||
|
/// # Usage
|
||||||
|
/// For builder pattern
|
||||||
|
#[inline] pub const fn remove_mask(self, class: User, bit: Bit) -> Self
|
||||||
|
{
|
||||||
|
match class {
|
||||||
|
User::Owner => {
|
||||||
|
Self {
|
||||||
|
u_owner: Bit::from_bits_truncate(self.u_owner.bits() & !bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
User::Group => {
|
||||||
|
Self {
|
||||||
|
u_group: Bit::from_bits_truncate(self.u_group.bits() & !bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
User::Other => {
|
||||||
|
Self {
|
||||||
|
u_other: Bit::from_bits_truncate(self.u_other.bits() & !bit.bits()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the specified mode mask is all present
|
||||||
|
#[inline] pub const fn has_mask(&self, class: User, bit: Bit) -> bool
|
||||||
|
{
|
||||||
|
match class {
|
||||||
|
User::Owner => {
|
||||||
|
(self.u_owner.bits() & bit.bits()) == bit.bits()
|
||||||
|
},
|
||||||
|
User::Group => {
|
||||||
|
(self.u_group.bits() & bit.bits()) == bit.bits()
|
||||||
|
},
|
||||||
|
User::Other => {
|
||||||
|
(self.u_other.bits() & bit.bits()) == bit.bits()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Permissions> for u32
|
||||||
|
{
|
||||||
|
#[inline] fn from(from: Permissions) -> Self
|
||||||
|
{
|
||||||
|
from.mask()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Permissions
|
||||||
|
{
|
||||||
|
#[inline] fn from(from: u32) -> Self
|
||||||
|
{
|
||||||
|
Self::from_mask(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test;
|
@ -0,0 +1,24 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mask()
|
||||||
|
{
|
||||||
|
assert_eq!(Permissions::new()
|
||||||
|
.add_mask(User::Owner, Bit::Mask)
|
||||||
|
.add_mask(User::Group, Bit::Read | Bit::Execute)
|
||||||
|
.add_mask(User::Other, Bit::Read | Bit::Write)
|
||||||
|
.remove_mask(User::Other, Bit::Write)
|
||||||
|
.mask(),
|
||||||
|
0o754);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_mask()
|
||||||
|
{
|
||||||
|
let mask = Permissions::from_mask(0o754);
|
||||||
|
assert!(mask.has_mask(User::Owner, Bit::Mask));
|
||||||
|
assert!(mask.has_mask(User::Group, Bit::Read | Bit::Execute));
|
||||||
|
assert!(mask.has_mask(User::Other, Bit::Read));
|
||||||
|
}
|
Loading…
Reference in new issue