windows_wfp/transaction.rs
1//! WFP Transaction wrapper with RAII rollback
2//!
3//! Provides safe transaction management for WFP operations with automatic rollback on error.
4
5use crate::engine::WfpEngine;
6use crate::errors::{WfpError, WfpResult};
7use windows::Win32::Foundation::ERROR_SUCCESS;
8use windows::Win32::NetworkManagement::WindowsFilteringPlatform::{
9 FwpmTransactionAbort0, FwpmTransactionBegin0, FwpmTransactionCommit0,
10};
11
12/// WFP Transaction with RAII rollback support
13///
14/// Automatically begins a transaction on creation and rolls back on drop
15/// unless explicitly committed.
16///
17/// # Examples
18///
19/// ```no_run
20/// use windows_wfp::{WfpEngine, WfpTransaction};
21///
22/// let engine = WfpEngine::new()?;
23/// let mut txn = WfpTransaction::begin(&engine)?;
24///
25/// // Perform filter operations...
26/// // If any operation fails, transaction will be rolled back automatically
27///
28/// txn.commit()?; // Explicitly commit if all succeeded
29/// # Ok::<(), windows_wfp::WfpError>(())
30/// ```
31pub struct WfpTransaction<'a> {
32 /// Reference to the WFP engine
33 engine: &'a WfpEngine,
34 /// Whether the transaction has been committed
35 committed: bool,
36}
37
38impl<'a> WfpTransaction<'a> {
39 /// Begin a new WFP transaction
40 ///
41 /// The transaction will automatically rollback if dropped without calling `commit()`.
42 ///
43 /// # Errors
44 ///
45 /// Returns `WfpError::TransactionBeginFailed` if:
46 /// - Another transaction is already active on this session
47 /// - WFP engine session is invalid
48 ///
49 /// # Examples
50 ///
51 /// ```no_run
52 /// use windows_wfp::{WfpEngine, WfpTransaction};
53 ///
54 /// let engine = WfpEngine::new()?;
55 /// let mut txn = WfpTransaction::begin(&engine)?;
56 /// // Transaction active...
57 /// txn.commit()?;
58 /// # Ok::<(), windows_wfp::WfpError>(())
59 /// ```
60 pub fn begin(engine: &'a WfpEngine) -> WfpResult<Self> {
61 unsafe {
62 let result = FwpmTransactionBegin0(engine.handle(), 0);
63
64 if result != ERROR_SUCCESS.0 {
65 return Err(WfpError::TransactionBeginFailed);
66 }
67 }
68
69 Ok(Self {
70 engine,
71 committed: false,
72 })
73 }
74
75 /// Commit the transaction
76 ///
77 /// Makes all changes permanent. If not called, the transaction will
78 /// automatically rollback when dropped.
79 ///
80 /// # Errors
81 ///
82 /// Returns `WfpError::TransactionCommitFailed` if the commit operation fails.
83 ///
84 /// # Examples
85 ///
86 /// ```no_run
87 /// use windows_wfp::{WfpEngine, WfpTransaction};
88 ///
89 /// let engine = WfpEngine::new()?;
90 /// let mut txn = WfpTransaction::begin(&engine)?;
91 /// // Perform operations...
92 /// txn.commit()?; // Make changes permanent
93 /// # Ok::<(), windows_wfp::WfpError>(())
94 /// ```
95 pub fn commit(mut self) -> WfpResult<()> {
96 unsafe {
97 let result = FwpmTransactionCommit0(self.engine.handle());
98
99 if result != ERROR_SUCCESS.0 {
100 return Err(WfpError::TransactionCommitFailed);
101 }
102 }
103
104 self.committed = true;
105 Ok(())
106 }
107
108 /// Explicitly rollback the transaction
109 ///
110 /// This is optional - the transaction will rollback automatically on drop
111 /// if not committed. Use this for explicit error handling.
112 ///
113 /// # Errors
114 ///
115 /// Returns `WfpError::TransactionAbortFailed` if the abort operation fails.
116 pub fn rollback(mut self) -> WfpResult<()> {
117 unsafe {
118 let result = FwpmTransactionAbort0(self.engine.handle());
119
120 if result != ERROR_SUCCESS.0 {
121 return Err(WfpError::TransactionAbortFailed);
122 }
123 }
124
125 self.committed = true; // Prevent double-abort in drop
126 Ok(())
127 }
128
129 /// Check if transaction has been committed
130 pub fn is_committed(&self) -> bool {
131 self.committed
132 }
133}
134
135impl<'a> Drop for WfpTransaction<'a> {
136 /// Automatically rollback transaction if not committed
137 fn drop(&mut self) {
138 if !self.committed {
139 unsafe {
140 // Best effort rollback - ignore errors in drop
141 let _ = FwpmTransactionAbort0(self.engine.handle());
142 }
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 #[ignore] // Requires admin privileges
153 fn test_transaction_begin_commit() {
154 let engine = WfpEngine::new().expect("Failed to create engine");
155 let txn = WfpTransaction::begin(&engine).expect("Failed to begin transaction");
156
157 assert!(!txn.is_committed());
158
159 txn.commit().expect("Failed to commit transaction");
160 }
161
162 #[test]
163 #[ignore] // Requires admin privileges
164 fn test_transaction_auto_rollback() {
165 let engine = WfpEngine::new().expect("Failed to create engine");
166
167 {
168 let _txn = WfpTransaction::begin(&engine).expect("Failed to begin transaction");
169 // Transaction should rollback automatically when dropped
170 }
171
172 // Should be able to begin a new transaction after rollback
173 let _txn2 = WfpTransaction::begin(&engine).expect("Failed to begin second transaction");
174 }
175
176 #[test]
177 #[ignore] // Requires admin privileges
178 fn test_transaction_explicit_rollback() {
179 let engine = WfpEngine::new().expect("Failed to create engine");
180 let txn = WfpTransaction::begin(&engine).expect("Failed to begin transaction");
181
182 txn.rollback().expect("Failed to rollback transaction");
183
184 // Should be able to begin a new transaction after rollback
185 let _txn2 = WfpTransaction::begin(&engine).expect("Failed to begin second transaction");
186 }
187}