@@ -2,8 +2,8 @@ pub mod convert;
22pub mod raw_exec;
33
44use std:: {
5- ffi:: OsStr , fmt:: Debug , num:: NonZeroUsize , os:: unix:: ffi:: OsStrExt as _, path :: Path ,
6- sync:: OnceLock ,
5+ cell :: Cell , ffi:: OsStr , fmt:: Debug , num:: NonZeroUsize , os:: unix:: ffi:: OsStrExt as _,
6+ path :: Path , sync:: OnceLock ,
77} ;
88
99use convert:: { ToAbsolutePath , ToAccessMode } ;
@@ -125,15 +125,37 @@ impl Client {
125125
126126static CLIENT : OnceLock < Client > = OnceLock :: new ( ) ;
127127
128+ // Resolving and reporting a file access can call another interposed function.
129+ // Suppress same-thread re-entry to prevent recursive access handling while
130+ // still recording accesses from other threads.
131+ thread_local ! {
132+ static HANDLING_OPEN : Cell <bool > = const { Cell :: new( false ) } ;
133+ }
134+
135+ struct ResetHandling < ' a > ( & ' a Cell < bool > ) ;
136+ impl Drop for ResetHandling < ' _ > {
137+ fn drop ( & mut self ) {
138+ self . 0 . set ( false ) ;
139+ }
140+ }
141+
128142pub fn global_client ( ) -> Option < & ' static Client > {
129143 CLIENT . get ( )
130144}
131145
132146pub unsafe fn handle_open ( path : impl ToAbsolutePath , mode : impl ToAccessMode ) {
133- if let Some ( client) = global_client ( ) {
134- // SAFETY: path and mode contain valid pointers/values forwarded from the interposed function's caller
135- unsafe { client. try_handle_open ( path, mode) } . unwrap ( ) ;
136- }
147+ HANDLING_OPEN . with ( |handling| {
148+ if handling. replace ( true ) {
149+ return ;
150+ }
151+
152+ let _reset = ResetHandling ( handling) ;
153+
154+ if let Some ( client) = global_client ( ) {
155+ // SAFETY: path and mode contain valid pointers/values forwarded from the interposed function's caller
156+ unsafe { client. try_handle_open ( path, mode) } . unwrap ( ) ;
157+ }
158+ } ) ;
137159}
138160
139161#[ cfg( not( test) ) ]
0 commit comments