use crate::error::Result;
use crate::pack::{Pack, ReadCursor, Unpack, WriteCursor};
use crate::types::{FileId, OplockLevel};
use crate::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OplockBreak {
pub oplock_level: OplockLevel,
pub file_id: FileId,
}
impl OplockBreak {
pub const STRUCTURE_SIZE: u16 = 24;
}
impl Pack for OplockBreak {
fn pack(&self, cursor: &mut WriteCursor) {
cursor.write_u16_le(Self::STRUCTURE_SIZE);
cursor.write_u8(self.oplock_level as u8);
cursor.write_u8(0);
cursor.write_u32_le(0);
cursor.write_u64_le(self.file_id.persistent);
cursor.write_u64_le(self.file_id.volatile);
}
}
impl Unpack for OplockBreak {
fn unpack(cursor: &mut ReadCursor<'_>) -> Result<Self> {
let structure_size = cursor.read_u16_le()?;
if structure_size != Self::STRUCTURE_SIZE {
return Err(Error::invalid_data(format!(
"invalid OplockBreak structure size: expected {}, got {}",
Self::STRUCTURE_SIZE,
structure_size
)));
}
let oplock_level = OplockLevel::try_from(cursor.read_u8()?)?;
let _reserved = cursor.read_u8()?;
let _reserved2 = cursor.read_u32_le()?;
let persistent = cursor.read_u64_le()?;
let volatile = cursor.read_u64_le()?;
Ok(OplockBreak {
oplock_level,
file_id: FileId {
persistent,
volatile,
},
})
}
}
pub type OplockBreakNotification = OplockBreak;
pub type OplockBreakAcknowledgment = OplockBreak;
pub type OplockBreakResponse = OplockBreak;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oplock_break_notification_roundtrip() {
let original = OplockBreakNotification {
oplock_level: OplockLevel::LevelII,
file_id: FileId {
persistent: 0x1122_3344_5566_7788,
volatile: 0xAABB_CCDD_EEFF_0011,
},
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 24);
let mut r = ReadCursor::new(&bytes);
let decoded = OplockBreakNotification::unpack(&mut r).unwrap();
assert_eq!(decoded.oplock_level, OplockLevel::LevelII);
assert_eq!(decoded.file_id, original.file_id);
}
#[test]
fn oplock_break_notification_exclusive_level() {
let original = OplockBreakNotification {
oplock_level: OplockLevel::Exclusive,
file_id: FileId {
persistent: 0x42,
volatile: 0x99,
},
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
let mut r = ReadCursor::new(&bytes);
let decoded = OplockBreakNotification::unpack(&mut r).unwrap();
assert_eq!(decoded.oplock_level, OplockLevel::Exclusive);
assert_eq!(decoded.file_id.persistent, 0x42);
assert_eq!(decoded.file_id.volatile, 0x99);
}
#[test]
fn oplock_break_acknowledgment_roundtrip() {
let original = OplockBreakAcknowledgment {
oplock_level: OplockLevel::None,
file_id: FileId {
persistent: 0xDEAD,
volatile: 0xBEEF,
},
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 24);
let mut r = ReadCursor::new(&bytes);
let decoded = OplockBreakAcknowledgment::unpack(&mut r).unwrap();
assert_eq!(decoded.oplock_level, OplockLevel::None);
assert_eq!(decoded.file_id, original.file_id);
}
#[test]
fn oplock_break_response_roundtrip() {
let original = OplockBreakResponse {
oplock_level: OplockLevel::Batch,
file_id: FileId {
persistent: 0xCAFE,
volatile: 0xFACE,
},
};
let mut w = WriteCursor::new();
original.pack(&mut w);
let bytes = w.into_inner();
assert_eq!(bytes.len(), 24);
let mut r = ReadCursor::new(&bytes);
let decoded = OplockBreakResponse::unpack(&mut r).unwrap();
assert_eq!(decoded.oplock_level, OplockLevel::Batch);
assert_eq!(decoded.file_id, original.file_id);
}
#[test]
fn oplock_break_wrong_structure_size() {
let mut buf = [0u8; 24];
buf[0..2].copy_from_slice(&99u16.to_le_bytes());
let mut cursor = ReadCursor::new(&buf);
let result = OplockBreak::unpack(&mut cursor);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("structure size"), "error was: {err}");
}
#[test]
fn oplock_break_reserved_fields_ignored() {
let mut buf = [0u8; 24];
buf[0..2].copy_from_slice(&24u16.to_le_bytes());
buf[2] = OplockLevel::LevelII as u8;
buf[3] = 0xFF;
buf[4..8].copy_from_slice(&0xDEAD_BEEFu32.to_le_bytes());
buf[8..16].copy_from_slice(&1u64.to_le_bytes());
buf[16..24].copy_from_slice(&2u64.to_le_bytes());
let mut cursor = ReadCursor::new(&buf);
let decoded = OplockBreak::unpack(&mut cursor).unwrap();
assert_eq!(decoded.oplock_level, OplockLevel::LevelII);
assert_eq!(decoded.file_id.persistent, 1);
assert_eq!(decoded.file_id.volatile, 2);
}
}