use std::ops::{BitAnd, BitOr, BitOrAssign};
macro_rules! impl_flags {
($name:ident, $inner:ty) => {
impl $name {
#[inline]
pub const fn new(raw: $inner) -> Self {
Self(raw)
}
#[inline]
pub const fn bits(&self) -> $inner {
self.0
}
#[inline]
pub const fn contains(&self, flag: $inner) -> bool {
self.0 & flag == flag
}
#[inline]
pub fn set(&mut self, flag: $inner) {
self.0 |= flag;
}
#[inline]
pub fn clear(&mut self, flag: $inner) {
self.0 &= !flag;
}
}
impl BitOr for $name {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitAnd for $name {
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl BitOrAssign for $name {
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct HeaderFlags(pub u32);
impl HeaderFlags {
pub const SERVER_TO_REDIR: u32 = 0x0000_0001;
pub const ASYNC_COMMAND: u32 = 0x0000_0002;
pub const RELATED_OPERATIONS: u32 = 0x0000_0004;
pub const SIGNED: u32 = 0x0000_0008;
pub const PRIORITY_MASK: u32 = 0x0000_0070;
pub const DFS_OPERATIONS: u32 = 0x1000_0000;
pub const REPLAY_OPERATION: u32 = 0x2000_0000;
#[inline]
pub fn is_response(&self) -> bool {
self.contains(Self::SERVER_TO_REDIR)
}
#[inline]
pub fn is_async(&self) -> bool {
self.contains(Self::ASYNC_COMMAND)
}
#[inline]
pub fn is_related(&self) -> bool {
self.contains(Self::RELATED_OPERATIONS)
}
#[inline]
pub fn is_signed(&self) -> bool {
self.contains(Self::SIGNED)
}
#[inline]
pub fn set_response(&mut self) {
self.set(Self::SERVER_TO_REDIR);
}
#[inline]
pub fn set_async(&mut self) {
self.set(Self::ASYNC_COMMAND);
}
#[inline]
pub fn set_related(&mut self) {
self.set(Self::RELATED_OPERATIONS);
}
#[inline]
pub fn set_signed(&mut self) {
self.set(Self::SIGNED);
}
}
impl_flags!(HeaderFlags, u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SecurityMode(pub u16);
impl SecurityMode {
pub const SIGNING_ENABLED: u16 = 0x0001;
pub const SIGNING_REQUIRED: u16 = 0x0002;
#[inline]
pub fn signing_enabled(&self) -> bool {
self.contains(Self::SIGNING_ENABLED)
}
#[inline]
pub fn signing_required(&self) -> bool {
self.contains(Self::SIGNING_REQUIRED)
}
}
impl_flags!(SecurityMode, u16);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Capabilities(pub u32);
impl Capabilities {
pub const DFS: u32 = 0x0000_0001;
pub const LEASING: u32 = 0x0000_0002;
pub const LARGE_MTU: u32 = 0x0000_0004;
pub const MULTI_CHANNEL: u32 = 0x0000_0008;
pub const PERSISTENT_HANDLES: u32 = 0x0000_0010;
pub const DIRECTORY_LEASING: u32 = 0x0000_0020;
pub const ENCRYPTION: u32 = 0x0000_0040;
}
impl_flags!(Capabilities, u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ShareFlags(pub u32);
impl ShareFlags {
pub const DFS: u32 = 0x0000_0001;
pub const DFS_ROOT: u32 = 0x0000_0002;
pub const MANUAL_CACHING: u32 = 0x0000_0000;
pub const AUTO_CACHING: u32 = 0x0000_0010;
pub const VDO_CACHING: u32 = 0x0000_0020;
pub const NO_CACHING: u32 = 0x0000_0030;
pub const RESTRICT_EXCLUSIVE_OPENS: u32 = 0x0000_0100;
pub const FORCE_SHARED_DELETE: u32 = 0x0000_0200;
pub const ALLOW_NAMESPACE_CACHING: u32 = 0x0000_0400;
pub const ACCESS_BASED_DIRECTORY_ENUM: u32 = 0x0000_0800;
pub const FORCE_LEVELII_OPLOCK: u32 = 0x0000_1000;
pub const ENABLE_HASH_V1: u32 = 0x0000_2000;
pub const ENABLE_HASH_V2: u32 = 0x0000_4000;
pub const ENCRYPT_DATA: u32 = 0x0000_8000;
pub const IDENTITY_REMOTING: u32 = 0x0004_0000;
pub const COMPRESS_DATA: u32 = 0x0010_0000;
pub const ISOLATED_TRANSPORT: u32 = 0x0020_0000;
}
impl_flags!(ShareFlags, u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ShareCapabilities(pub u32);
impl ShareCapabilities {
pub const DFS: u32 = 0x0000_0008;
pub const CONTINUOUS_AVAILABILITY: u32 = 0x0000_0010;
pub const SCALEOUT: u32 = 0x0000_0020;
pub const CLUSTER: u32 = 0x0000_0040;
pub const ASYMMETRIC: u32 = 0x0000_0080;
pub const REDIRECT_TO_OWNER: u32 = 0x0000_0100;
}
impl_flags!(ShareCapabilities, u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FileAccessMask(pub u32);
impl FileAccessMask {
pub const FILE_READ_DATA: u32 = 0x0000_0001;
pub const FILE_WRITE_DATA: u32 = 0x0000_0002;
pub const FILE_APPEND_DATA: u32 = 0x0000_0004;
pub const FILE_READ_EA: u32 = 0x0000_0008;
pub const FILE_WRITE_EA: u32 = 0x0000_0010;
pub const FILE_EXECUTE: u32 = 0x0000_0020;
pub const FILE_READ_ATTRIBUTES: u32 = 0x0000_0080;
pub const FILE_WRITE_ATTRIBUTES: u32 = 0x0000_0100;
pub const DELETE: u32 = 0x0001_0000;
pub const READ_CONTROL: u32 = 0x0002_0000;
pub const WRITE_DAC: u32 = 0x0004_0000;
pub const WRITE_OWNER: u32 = 0x0008_0000;
pub const SYNCHRONIZE: u32 = 0x0010_0000;
pub const MAXIMUM_ALLOWED: u32 = 0x0200_0000;
pub const GENERIC_ALL: u32 = 0x1000_0000;
pub const GENERIC_EXECUTE: u32 = 0x2000_0000;
pub const GENERIC_WRITE: u32 = 0x4000_0000;
pub const GENERIC_READ: u32 = 0x8000_0000;
}
impl_flags!(FileAccessMask, u32);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_flags_default_is_zero() {
let f = HeaderFlags::default();
assert_eq!(f.bits(), 0);
assert!(!f.is_response());
assert!(!f.is_async());
assert!(!f.is_related());
assert!(!f.is_signed());
}
#[test]
fn header_flags_set_and_check() {
let mut f = HeaderFlags::default();
f.set_response();
assert!(f.is_response());
assert!(!f.is_async());
f.set_signed();
assert!(f.is_signed());
assert!(f.is_response());
}
#[test]
fn header_flags_clear() {
let mut f = HeaderFlags::new(0xFFFF_FFFF);
assert!(f.is_response());
f.clear(HeaderFlags::SERVER_TO_REDIR);
assert!(!f.is_response());
assert!(f.is_async()); }
#[test]
fn header_flags_contains() {
let f = HeaderFlags::new(HeaderFlags::SIGNED | HeaderFlags::ASYNC_COMMAND);
assert!(f.contains(HeaderFlags::SIGNED));
assert!(f.contains(HeaderFlags::ASYNC_COMMAND));
assert!(!f.contains(HeaderFlags::SERVER_TO_REDIR));
}
#[test]
fn header_flags_bitor() {
let a = HeaderFlags::new(HeaderFlags::SERVER_TO_REDIR);
let b = HeaderFlags::new(HeaderFlags::SIGNED);
let c = a | b;
assert!(c.is_response());
assert!(c.is_signed());
}
#[test]
fn header_flags_bitand() {
let a = HeaderFlags::new(HeaderFlags::SERVER_TO_REDIR | HeaderFlags::SIGNED);
let b = HeaderFlags::new(HeaderFlags::SIGNED);
let c = a & b;
assert!(!c.is_response());
assert!(c.is_signed());
}
#[test]
fn header_flags_bitor_assign() {
let mut a = HeaderFlags::new(HeaderFlags::SERVER_TO_REDIR);
a |= HeaderFlags::new(HeaderFlags::ASYNC_COMMAND);
assert!(a.is_response());
assert!(a.is_async());
}
#[test]
fn security_mode_signing_enabled() {
let m = SecurityMode::new(SecurityMode::SIGNING_ENABLED);
assert!(m.signing_enabled());
assert!(!m.signing_required());
}
#[test]
fn security_mode_signing_required() {
let m = SecurityMode::new(SecurityMode::SIGNING_ENABLED | SecurityMode::SIGNING_REQUIRED);
assert!(m.signing_enabled());
assert!(m.signing_required());
}
#[test]
fn capabilities_combine_with_bitor() {
let a = Capabilities::new(Capabilities::DFS);
let b = Capabilities::new(Capabilities::ENCRYPTION);
let c = a | b;
assert!(c.contains(Capabilities::DFS));
assert!(c.contains(Capabilities::ENCRYPTION));
assert!(!c.contains(Capabilities::LEASING));
}
#[test]
fn capabilities_set_and_clear() {
let mut c = Capabilities::default();
c.set(Capabilities::LARGE_MTU);
assert!(c.contains(Capabilities::LARGE_MTU));
c.clear(Capabilities::LARGE_MTU);
assert!(!c.contains(Capabilities::LARGE_MTU));
}
#[test]
fn share_flags_encrypt_data() {
let f = ShareFlags::new(ShareFlags::ENCRYPT_DATA | ShareFlags::DFS);
assert!(f.contains(ShareFlags::ENCRYPT_DATA));
assert!(f.contains(ShareFlags::DFS));
assert!(!f.contains(ShareFlags::COMPRESS_DATA));
}
#[test]
fn share_capabilities_dfs() {
let c = ShareCapabilities::new(ShareCapabilities::DFS);
assert!(c.contains(ShareCapabilities::DFS));
assert!(!c.contains(ShareCapabilities::CLUSTER));
}
#[test]
fn file_access_mask_generic_read() {
let m = FileAccessMask::new(FileAccessMask::GENERIC_READ);
assert!(m.contains(FileAccessMask::GENERIC_READ));
assert!(!m.contains(FileAccessMask::GENERIC_WRITE));
}
#[test]
fn file_access_mask_combine() {
let m =
FileAccessMask::new(FileAccessMask::FILE_READ_DATA | FileAccessMask::FILE_WRITE_DATA);
assert!(m.contains(FileAccessMask::FILE_READ_DATA));
assert!(m.contains(FileAccessMask::FILE_WRITE_DATA));
assert!(!m.contains(FileAccessMask::DELETE));
}
}