@@ -8,7 +8,7 @@ use std::{
88
99use fspy_detours_sys:: { DetourCopyPayloadToProcess , DetourUpdateProcessWithDll } ;
1010use fspy_shared:: {
11- ipc:: { PathAccess , channel:: channel} ,
11+ ipc:: { AccessMode , PathAccess , channel:: channel} ,
1212 windows:: { PAYLOAD_ID , Payload } ,
1313} ;
1414use futures_util:: FutureExt ;
@@ -22,6 +22,7 @@ use winsafe::co::{CP, WC};
2222
2323use crate :: {
2424 ChildTermination , TrackedChild ,
25+ callback:: windows:: WindowsCallbackServer ,
2526 command:: Command ,
2627 error:: SpawnError ,
2728 ipc:: { OwnedReceiverLockGuard , SHM_CAPACITY } ,
@@ -39,11 +40,6 @@ impl PathAccessIterable {
3940 }
4041}
4142
42- // pub struct TracedProcess {
43- // pub child: Child,
44- // pub path_access_stream: PathAccessIter,
45- // }
46-
4743#[ derive( Debug , Clone ) ]
4844pub struct SpyImpl {
4945 ansi_dll_path_with_nul : Arc < CStr > ,
@@ -73,6 +69,7 @@ impl SpyImpl {
7369 cancellation_token : CancellationToken ,
7470 ) -> Result < TrackedChild , SpawnError > {
7571 let ansi_dll_path_with_nul = Arc :: clone ( & self . ansi_dll_path_with_nul ) ;
72+ let file_callback = command. file_callback . take ( ) ;
7673 command. env ( "FSPY" , "1" ) ;
7774 let mut command = command. into_tokio_command ( ) ;
7875
@@ -81,8 +78,18 @@ impl SpyImpl {
8178 let ( channel_conf, receiver) =
8279 channel ( SHM_CAPACITY ) . map_err ( SpawnError :: ChannelCreation ) ?;
8380
81+ // Supervisor side of the optional blocking open/close callback.
82+ let callback_server = file_callback
83+ . as_ref ( )
84+ . map ( |callback| WindowsCallbackServer :: new ( Arc :: clone ( & callback. callback ) ) )
85+ . transpose ( )
86+ . map_err ( SpawnError :: CallbackChannelCreation ) ?;
87+ let callback_mask =
88+ file_callback. as_ref ( ) . map_or_else ( AccessMode :: empty, |callback| callback. mask ) ;
89+
8490 let mut spawn_success = false ;
8591 let spawn_success = & mut spawn_success;
92+ let callback_server_ref = callback_server. as_ref ( ) ;
8693 let mut child = command
8794 . spawn_with ( |std_command| {
8895 let std_child = std_command. spawn ( ) ?;
@@ -101,6 +108,9 @@ impl SpyImpl {
101108 let payload = Payload {
102109 channel_conf : channel_conf. clone ( ) ,
103110 ansi_dll_path_with_nul : ansi_dll_path_with_nul. to_bytes ( ) ,
111+ callback_pipe_name : callback_server_ref
112+ . map_or ( & [ ] [ ..] , WindowsCallbackServer :: pipe_name_bytes) ,
113+ callback_mask,
104114 } ;
105115 let payload_bytes = wincode:: serialize ( & payload) . unwrap ( ) ;
106116 // SAFETY: process_handle is valid, PAYLOAD_ID is a static GUID,
@@ -117,6 +127,18 @@ impl SpyImpl {
117127 return Err ( io:: Error :: last_os_error ( ) ) ;
118128 }
119129
130+ // Hand the child's process handle to the callback server
131+ // before resuming it, so handle duplication works as soon as
132+ // the child starts issuing callbacks.
133+ if let Some ( server) = callback_server_ref {
134+ use std:: os:: windows:: io:: BorrowedHandle ;
135+ // SAFETY: the child was just spawned; its raw handle is valid here.
136+ let borrowed = unsafe { BorrowedHandle :: borrow_raw ( std_child. as_raw_handle ( ) ) } ;
137+ if let Ok ( owned) = borrowed. try_clone_to_owned ( ) {
138+ server. set_process_handle ( owned) ;
139+ }
140+ }
141+
120142 let main_thread_handle = std_child. main_thread_handle ( ) ;
121143 // SAFETY: main_thread_handle is a valid thread handle from the spawned child
122144 let resume_thread_ret =
@@ -159,6 +181,10 @@ impl SpyImpl {
159181 child. wait( ) . await ?
160182 }
161183 } ;
184+ // Drain in-flight blocking callbacks before locking the channel.
185+ if let Some ( callback_server) = callback_server {
186+ callback_server. shutdown ( ) . await ;
187+ }
162188 // Lock the ipc channel after the child has exited.
163189 // We are not interested in path accesses from descendants after the main child has exited.
164190 let ipc_receiver_lock_guard = OwnedReceiverLockGuard :: lock_async ( receiver) . await ?;
0 commit comments