Compare commits

...

2 Commits

Author SHA1 Message Date
3ef3a8b3aa Use Bitflags trait and Bit enum in which applet;
Move `Bit` and `Who` enums out of `mode` module into submodules
2023-01-23 09:39:02 -05:00
6c0b7cb787 Use Bitflags trait in mode::Parser 2023-01-23 09:10:08 -05:00
5 changed files with 174 additions and 145 deletions

View File

@ -1,3 +1,4 @@
use crate::{bitflags::BitFlags, mode::Bit};
use super::Cmd; use super::Cmd;
use clap::{Arg, Command}; use clap::{Arg, Command};
use std::{env, fs::File, io, os::unix::prelude::MetadataExt, path::PathBuf, process}; use std::{env, fs::File, io, os::unix::prelude::MetadataExt, path::PathBuf, process};
@ -60,10 +61,10 @@ fn which(command: &str, path: &[&str]) -> Option<String> {
let myuid = unsafe { libc::geteuid() }; let myuid = unsafe { libc::geteuid() };
let mygroups = crate::pw::get_gids(); let mygroups = crate::pw::get_gids();
// we own the file and it has u+x // we own the file and it has u+x
if myuid == meta.uid() && mode & 0o100 != 0 { if myuid == meta.uid() && mode.contains(Bit::UExec) {
return Some(format!("{}", file.display())); return Some(format!("{}", file.display()));
// file has ug+x // file has ug+x
} else if mode & 0o110 != 0 { } else if mode.contains(Bit::UExec | Bit::GExec) {
if let Ok(groups) = mygroups { if let Ok(groups) = mygroups {
// one of our groups owns the file // one of our groups owns the file
if groups.contains(&meta.gid()) { if groups.contains(&meta.gid()) {

99
src/mode/bit.rs Normal file
View File

@ -0,0 +1,99 @@
use crate::bitflags::BitFlags;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
/// Unix permission bit flags
#[derive(Clone, Copy, PartialEq)]
pub enum Bit {
Suid = 0o4000,
Sgid = 0o2000,
Sticky = 0o1000,
URead = 0o400,
UWrite = 0o200,
UExec = 0o100,
GRead = 0o40,
GWrite = 0o20,
GExec = 0o10,
ORead = 0o4,
OWrite = 0o2,
OExec = 0o1,
}
impl BitAnd<u32> for Bit {
type Output = u32;
fn bitand(self, rhs: u32) -> Self::Output {
self as u32 & rhs
}
}
impl BitAnd<Bit> for u32 {
type Output = u32;
fn bitand(self, rhs: Bit) -> Self::Output {
self & rhs as u32
}
}
impl BitAnd for Bit {
type Output = u32;
fn bitand(self, rhs: Self) -> Self::Output {
self as u32 & rhs as u32
}
}
impl BitAndAssign<Bit> for u32 {
fn bitand_assign(&mut self, rhs: Bit) {
*self = *self & rhs;
}
}
impl BitOr<u32> for Bit {
type Output = u32;
fn bitor(self, rhs: u32) -> Self::Output {
self as u32 | rhs
}
}
impl BitOr<Bit> for u32 {
type Output = u32;
fn bitor(self, rhs: Bit) -> Self::Output {
self | rhs as u32
}
}
impl BitOr for Bit {
type Output = u32;
fn bitor(self, rhs: Self) -> Self::Output {
self as u32 | rhs as u32
}
}
impl BitOrAssign<Bit> for u32 {
fn bitor_assign(&mut self, rhs: Bit) {
*self = *self | rhs;
}
}
impl Bit {
pub fn as_char(&self, mode: u32) -> char {
if mode & *self != 0 {
match self {
Self::Suid | Self::Sgid => 's',
Self::Sticky => 't',
Self::URead | Self::GRead | Self::ORead => 'r',
Self::UWrite | Self::GWrite | Self::OWrite => 'w',
Self::UExec if mode.contains(Self::Suid) => 's',
Self::GExec if mode.contains(Self::Sgid) => 's',
Self::OExec if mode.contains(Self::Sticky) => 't',
Self::UExec | Self::GExec | Self::OExec => 'x',
}
} else {
'-'
}
}
}

View File

@ -1,11 +1,10 @@
//! Functions for parsing and managing permissions //! Functions for parsing and managing permissions
mod bit;
mod parser; mod parser;
use std::{ mod who;
fmt::{self, Write}, use std::fmt::{self, Write};
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
};
pub use parser::{ParseError, Parser}; pub use {bit::Bit, parser::{ParseError, Parser}, who::Who};
/// Gets the umask for the current user /// Gets the umask for the current user
#[must_use] #[must_use]
@ -15,42 +14,6 @@ pub fn get_umask() -> u32 {
mask mask
} }
/// Unix permission bit flags
#[derive(Clone, Copy, PartialEq)]
pub enum Bit {
Suid = 0o4000,
Sgid = 0o2000,
Sticky = 0o1000,
URead = 0o400,
UWrite = 0o200,
UExec = 0o100,
GRead = 0o40,
GWrite = 0o20,
GExec = 0o10,
ORead = 0o4,
OWrite = 0o2,
OExec = 0o1,
}
impl Bit {
fn as_char(&self, mode: u32) -> char {
if mode & *self != 0 {
match self {
Self::Suid | Self::Sgid => 's',
Self::Sticky => 't',
Self::URead | Self::GRead | Self::ORead => 'r',
Self::UWrite | Self::GWrite | Self::OWrite => 'w',
Self::UExec if mode & Self::Suid != 0 => 's',
Self::GExec if mode & Self::Sgid != 0 => 's',
Self::OExec if mode & Self::Sticky != 0 => 't',
Self::UExec | Self::GExec | Self::OExec => 'x',
}
} else {
'-'
}
}
}
/// Functions for extracting information about Unix modes /// Functions for extracting information about Unix modes
pub trait Mode { pub trait Mode {
/// Returns a string representing permissions in symbolic format /// Returns a string representing permissions in symbolic format
@ -110,50 +73,6 @@ impl Mode for u32 {
} }
} }
impl BitAnd<u32> for Bit {
type Output = u32;
fn bitand(self, rhs: u32) -> Self::Output {
self as u32 & rhs
}
}
impl BitAnd<Bit> for u32 {
type Output = u32;
fn bitand(self, rhs: Bit) -> Self::Output {
self & rhs as u32
}
}
impl BitAndAssign<Bit> for u32 {
fn bitand_assign(&mut self, rhs: Bit) {
*self = *self & rhs;
}
}
impl BitOr<u32> for Bit {
type Output = u32;
fn bitor(self, rhs: u32) -> Self::Output {
self as u32 | rhs
}
}
impl BitOr<Bit> for u32 {
type Output = u32;
fn bitor(self, rhs: Bit) -> Self::Output {
self | rhs as u32
}
}
impl BitOrAssign<Bit> for u32 {
fn bitor_assign(&mut self, rhs: Bit) {
*self = *self | rhs;
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -1,9 +1,9 @@
use super::{get_umask, Bit}; use crate::bitflags::BitFlags;
use super::{get_umask, Bit, Who};
use std::{ use std::{
error, error,
fmt::Display, fmt::Display,
num::ParseIntError, num::ParseIntError,
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
}; };
/// Errors which might occur when parsing Unix permissions from a string /// Errors which might occur when parsing Unix permissions from a string
@ -46,45 +46,6 @@ enum Op {
Equals, Equals,
} }
#[derive(PartialEq)]
/// The granularity of the given permissions
enum Who {
/// applies for the current user
User = 0o100,
/// applies for the current group
Group = 0o10,
/// applies for everyone else
Other = 0o1,
}
impl BitAnd<Who> for u32 {
type Output = u32;
fn bitand(self, rhs: Who) -> Self::Output {
self & rhs as u32
}
}
impl BitAndAssign<Who> for u32 {
fn bitand_assign(&mut self, rhs: Who) {
*self = *self & rhs;
}
}
impl BitOr<Who> for u32 {
type Output = u32;
fn bitor(self, rhs: Who) -> Self::Output {
self | rhs as u32
}
}
impl BitOrAssign<Who> for u32 {
fn bitor_assign(&mut self, rhs: Who) {
*self = *self | rhs;
}
}
/// A parser for octal and symbolic permissions. `Parser::default` creates an /// A parser for octal and symbolic permissions. `Parser::default` creates an
/// instance which applies the given operations to the default setting for the /// instance which applies the given operations to the default setting for the
/// current user's umask. `Parser::new` creates a parser which applies the given /// current user's umask. `Parser::new` creates a parser which applies the given
@ -157,13 +118,13 @@ impl Parser {
if self.op.is_none() { if self.op.is_none() {
Err(ParseError::NoOpSet) Err(ParseError::NoOpSet)
} else { } else {
if self.who & 0o100 != 0 { if self.who.contains(Who::User) {
self.bits |= Bit::URead; self.bits |= Bit::URead;
} }
if self.who & 0o10 != 0 { if self.who.contains(Who::Group) {
self.bits |= Bit::GRead; self.bits |= Bit::GRead;
} }
if self.who & 0o1 != 0 { if self.who.contains(Who::Other) {
self.bits |= Bit::ORead; self.bits |= Bit::ORead;
} }
Ok(()) Ok(())
@ -174,13 +135,13 @@ impl Parser {
if self.op.is_none() { if self.op.is_none() {
Err(ParseError::NoOpSet) Err(ParseError::NoOpSet)
} else { } else {
if self.who & 0o100 != 0 { if self.who.contains(Who::User) {
self.bits |= Bit::UWrite; self.bits |= Bit::UWrite;
} }
if self.who & 0o10 != 0 { if self.who.contains(Who::Group) {
self.bits |= Bit::GWrite; self.bits |= Bit::GWrite;
} }
if self.who & 0o1 != 0 { if self.who.contains(Who::Other) {
self.bits |= Bit::OWrite; self.bits |= Bit::OWrite;
} }
Ok(()) Ok(())
@ -191,13 +152,13 @@ impl Parser {
if self.op.is_none() { if self.op.is_none() {
Err(ParseError::NoOpSet) Err(ParseError::NoOpSet)
} else { } else {
if self.who & 0o100 != 0 { if self.who.contains(Who::User) {
self.bits |= Bit::UExec; self.bits |= Bit::UExec;
} }
if self.who & 0o10 != 0 { if self.who.contains(Who::Group) {
self.bits |= Bit::GExec; self.bits |= Bit::GExec;
} }
if self.who & 0o1 != 0 { if self.who.contains(Who::Other) {
self.bits |= Bit::OExec; self.bits |= Bit::OExec;
} }
Ok(()) Ok(())
@ -205,27 +166,27 @@ impl Parser {
} }
fn push_suid_sgid(&mut self) -> Result<(), ParseError> { fn push_suid_sgid(&mut self) -> Result<(), ParseError> {
if self.who == 0 || self.who & 0o1 != 0 { if self.who == 0 || self.who.contains(Who::Other) {
return Err(ParseError::InvalidBit); return Err(ParseError::InvalidBit);
} else if self.op.is_none() { } else if self.op.is_none() {
return Err(ParseError::NoOpSet); return Err(ParseError::NoOpSet);
} }
if self.who & 0o100 != 0 { if self.who.contains(Who::User) {
self.bits |= Bit::Suid; self.bits |= Bit::Suid;
} }
if self.who & 0o10 != 0 { if self.who.contains(Who::Group) {
self.bits |= Bit::Sgid; self.bits |= Bit::Sgid;
} }
Ok(()) Ok(())
} }
fn push_sticky(&mut self) -> Result<(), ParseError> { fn push_sticky(&mut self) -> Result<(), ParseError> {
if self.who == 0 || self.who & 0o100 != 0 || self.who & 0o10 != 0 { if self.who == 0 || self.who.contains(Who::User) || self.who.contains(Who::Group) {
return Err(ParseError::InvalidBit); return Err(ParseError::InvalidBit);
} else if self.op.is_none() { } else if self.op.is_none() {
return Err(ParseError::NoOpSet); return Err(ParseError::NoOpSet);
} }
if self.who & 0o1 != 0 { if self.who.contains(Who::Other) {
self.bits |= Bit::Sticky; self.bits |= Bit::Sticky;
} }
Ok(()) Ok(())
@ -244,13 +205,13 @@ impl Parser {
Some(Op::Add) => self.add_bits(), Some(Op::Add) => self.add_bits(),
Some(Op::Remove) => self.remove_bits(), Some(Op::Remove) => self.remove_bits(),
Some(Op::Equals) => { Some(Op::Equals) => {
if self.who & 0o100 != 0 { if self.who.contains(Who::User) {
self.mode &= !(0o4700); self.mode &= !(0o4700);
} }
if self.who & 0o10 != 0 { if self.who.contains(Who::Group) {
self.mode &= !(0o2070); self.mode &= !(0o2070);
} }
if self.who & 0o1 != 0 { if self.who.contains(Who::Other) {
self.mode &= !(0o1007); self.mode &= !(0o1007);
} }
self.add_bits(); self.add_bits();

49
src/mode/who.rs Normal file
View File

@ -0,0 +1,49 @@
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign};
#[derive(PartialEq)]
/// The granularity of the given permissions
pub enum Who {
/// applies for the current user
User = 0o100,
/// applies for the current group
Group = 0o10,
/// applies for everyone else
Other = 0o1,
}
impl BitAnd<Who> for u32 {
type Output = u32;
fn bitand(self, rhs: Who) -> Self::Output {
self & rhs as u32
}
}
impl BitAnd<u32> for Who {
type Output = u32;
fn bitand(self, rhs: u32) -> Self::Output {
self as u32 & rhs
}
}
impl BitAndAssign<Who> for u32 {
fn bitand_assign(&mut self, rhs: Who) {
*self = *self & rhs;
}
}
impl BitOr<Who> for u32 {
type Output = u32;
fn bitor(self, rhs: Who) -> Self::Output {
self | rhs as u32
}
}
impl BitOrAssign<Who> for u32 {
fn bitor_assign(&mut self, rhs: Who) {
*self = *self | rhs;
}
}