1111#include  "../config.h" 
1212#include  "../string-list.h" 
1313
14+ #ifdef  DEBUG_FILE_LOCKS 
15+ 
16+ #include  "libbacktrace/backtrace.h" 
17+ #include  "../hashmap.h" 
18+ 
19+ struct  file_lock_backtrace 
20+ {
21+ 	struct  hashmap_entry  entry ;
22+ 	int  fd , count ;
23+ 	uintptr_t  * pcs ;
24+ 	const  char  * filename ;
25+ };
26+ 
27+ static  CRITICAL_SECTION  backtrace_mutex ;
28+ static  struct  hashmap  file_lock_map ;
29+ #define  FILE_LOCK_MAX_FD  256
30+ static  struct  file_lock_backtrace  * file_lock_by_fd [FILE_LOCK_MAX_FD ];
31+ 
32+ static  int  my_backtrace_cb (void  * data , uintptr_t  pc , const  char  * filename ,
33+ 			   int  lineno , const  char  * function )
34+ {
35+ 	struct  strbuf  * buf  =  data ;
36+ 
37+ 	if  (!function  ||  !strcmp ("__tmainCRTStartup" , function ))
38+ 		return  -1 ;
39+ 
40+ 	strbuf_addf (buf , "%s:%d:\n\t%s\n" , filename , lineno , function );
41+ 
42+ 	return  0 ;
43+ }
44+ 
45+ static  void  my_error_cb (void  * data , const  char  * msg , int  errnum )
46+ {
47+ 	struct  strbuf  * buf  =  data ;
48+ 
49+ 	strbuf_addf (buf , "error %s (%d)\n" , msg , errnum );
50+ }
51+ 
52+ static  void  file_lock_backtrace (struct  file_lock_backtrace  * data ,
53+ 				struct  strbuf  * buf )
54+ {
55+ 	static  struct  backtrace_state  * state ;
56+ 	static  int  initialized ;
57+ 	int  i ;
58+ 
59+ 	if  (!initialized ) {
60+ 		EnterCriticalSection (& backtrace_mutex );
61+ 		if  (!initialized ) {
62+ 			state  =  backtrace_create_state (NULL , 1 , my_error_cb ,
63+ 						       NULL );
64+ 			initialized  =  1 ;
65+ 		}
66+ 		LeaveCriticalSection (& backtrace_mutex );
67+ 	}
68+ 
69+ 	if  (data -> fd  >= 0 )
70+ 		strbuf_addf (buf , "file '%s' (fd %d) was opened here:\n" ,
71+ 			    data -> filename , data -> fd );
72+ 	for  (i  =  0 ; i  <  data -> count ; i ++ )
73+ 		if  (backtrace_pcinfo (state , data -> pcs [i ], my_backtrace_cb ,
74+ 				     my_error_cb , buf ) <  0 )
75+ 			break ;
76+ }
77+ 
78+ static  struct  file_lock_backtrace  * alloc_file_lock_backtrace (int  fd ,
79+ 		const  char  * filename )
80+ {
81+ 	DECLARE_PROC_ADDR (kernel32 .dll , USHORT , RtlCaptureStackBackTrace ,
82+ 			  ULONG , ULONG , PVOID * , PULONG );
83+ 	struct  file_lock_backtrace  * result ;
84+ 	uintptr_t  pcs [62 ];
85+ 	int  count  =  0 ;
86+ 	size_t  pcs_size  =  0 , size ;
87+ 
88+ 	if  ((fd  <  0  ||  fd  >= FILE_LOCK_MAX_FD ) &&  fd  !=  -123 )
89+ 		BUG ("Called with fd = %d\n" , fd );
90+ 
91+ 	if  (INIT_PROC_ADDR (RtlCaptureStackBackTrace )) {
92+ 		count  =  RtlCaptureStackBackTrace (1 , ARRAY_SIZE (pcs ),
93+ 						 (void  * * )pcs , NULL );
94+ 		pcs_size  =  sizeof (uintptr_t ) *  count ;
95+ 	}
96+ 	size  =  sizeof (* result ) +  pcs_size  +  strlen (filename ) +  1 ;
97+ 
98+ 	result  =  xmalloc (size );
99+ 	result -> fd  =  fd ;
100+ 	result -> count  =  count ;
101+ 	if  (!count )
102+ 		result -> pcs  =  NULL ;
103+ 	else  {
104+ 		result -> pcs  =  (uintptr_t  * )((char  * )result  +  sizeof (* result ));
105+ 		memcpy (result -> pcs , pcs , pcs_size );
106+ 	}
107+ 
108+ 	result -> filename  =  ((char  * )result  +  sizeof (* result ) +  pcs_size );
109+ 	strcpy ((char  * )result -> filename , filename );
110+ 
111+ 	if  (fd  <  0 )
112+ 		return  result ;
113+ 
114+ 	EnterCriticalSection (& backtrace_mutex );
115+ 	if  (file_lock_by_fd [fd ]) {
116+ 		struct  strbuf  buf  =  STRBUF_INIT ;
117+ 		strbuf_addf (& buf , "Bogus file_lock (%d). First trace:\n" , fd );
118+ 		file_lock_backtrace (file_lock_by_fd [fd ], & buf );
119+ 		strbuf_addf (& buf , "\nSecond trace:\n" );
120+ 		file_lock_backtrace (result , & buf );
121+ 		BUG (buf .buf );
122+ 	}
123+ 	file_lock_by_fd [fd ] =  result ;
124+ 	hashmap_entry_init (& result -> entry , strihash (filename ));
125+ 	hashmap_add (& file_lock_map , result );
126+ 	LeaveCriticalSection (& backtrace_mutex );
127+ 
128+ 	return  result ;
129+ }
130+ 
131+ static  void  current_backtrace (struct  strbuf  * buf )
132+ {
133+ 	struct  file_lock_backtrace  * p  =  alloc_file_lock_backtrace (-123 , "" );
134+ 	file_lock_backtrace (p , buf );
135+ 	free (p );
136+ }
137+ 
138+ static  void  remove_file_lock_backtrace (int  fd )
139+ {
140+ 	if  (fd  <  0  ||  fd  >= FILE_LOCK_MAX_FD )
141+ 		BUG ("Called with fd = %d\n" , fd );
142+ 
143+ 	EnterCriticalSection (& backtrace_mutex );
144+ 	if  (!file_lock_by_fd [fd ])
145+ 		BUG ("trying to release non-existing lock for fd %d" , fd );
146+ 
147+ 	hashmap_remove (& file_lock_map , file_lock_by_fd [fd ], NULL );
148+ 	free (file_lock_by_fd [fd ]);
149+ 	file_lock_by_fd [fd ] =  NULL ;
150+ 	LeaveCriticalSection (& backtrace_mutex );
151+ }
152+ 
153+ static  int  file_lock_backtrace_cmp (const  void  * dummy ,
154+ 		const  struct  file_lock_backtrace  * a ,
155+ 		const  struct  file_lock_backtrace  * b ,
156+ 		const  void  * keydata )
157+ {
158+ 	return  strcasecmp (a -> filename ,
159+ 			  keydata  ? (const  char  * )keydata  : b -> filename );
160+ }
161+ 
162+ static  struct  file_lock_backtrace  * file_lock_lookup (const  char  * filename )
163+ {
164+ 	struct  hashmap_entry  entry ;
165+ 	struct  file_lock_backtrace  * result ;
166+ 
167+ 	hashmap_entry_init (& entry , strihash (filename ));
168+ 	EnterCriticalSection (& backtrace_mutex );
169+ 	result  =  hashmap_get (& file_lock_map , & entry , filename );
170+ 	LeaveCriticalSection (& backtrace_mutex );
171+ 
172+ 	return  result ;
173+ }
174+ 
175+ static  void  initialize_file_lock_map (void )
176+ {
177+ 	InitializeCriticalSection (& backtrace_mutex );
178+ 	hashmap_init (& file_lock_map , (hashmap_cmp_fn )file_lock_backtrace_cmp ,
179+ 		     NULL , 0 );
180+ }
181+ #endif 
182+ 
14183#define  HCAST (type , handle ) ((type)(intptr_t)handle)
15184
16185int  err_win_to_posix (DWORD  winerr )
@@ -405,6 +574,21 @@ int mingw_unlink(const char *pathname)
405574		 */ 
406575		if  (!_wrmdir (wpathname ))
407576			return  0 ;
577+ #ifdef  DEBUG_FILE_LOCKS 
578+ 		{
579+ 			struct  file_lock_backtrace  * p  = 
580+ 				file_lock_lookup (pathname );
581+ 			if  (p ) {
582+ 				struct  strbuf  buf  =  STRBUF_INIT ;
583+ 				strbuf_addf (& buf , "the file '%s' wants " 
584+ 					    "to be deleted here:\n" , pathname );
585+ 				current_backtrace (& buf );
586+ 				strbuf_addf (& buf , "\nBut it is still open:\n" );
587+ 				file_lock_backtrace (p , & buf );
588+ 				die ("%s\n" , buf .buf );
589+ 			}
590+ 		}
591+ #endif 
408592	} while  (retry_ask_yes_no (& tries , "Unlink of file '%s' failed. " 
409593			"Should I try again?" , pathname ));
410594	return  -1 ;
@@ -553,6 +737,10 @@ int mingw_open (const char *filename, int oflags, ...)
553737		if  (fd  >= 0  &&  set_hidden_flag (wfilename , 1 ))
554738			warning ("could not mark '%s' as hidden." , filename );
555739	}
740+ #ifdef  DEBUG_FILE_LOCKS 
741+ 	if  (fd  >= 0 )
742+ 		alloc_file_lock_backtrace (fd , filename );
743+ #endif 
556744	return  fd ;
557745}
558746
@@ -601,6 +789,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
601789		errno  =  ENOENT ;
602790	if  (file  &&  hide  &&  set_hidden_flag (wfilename , 1 ))
603791		warning ("could not mark '%s' as hidden." , filename );
792+ #ifdef  DEBUG_FILE_LOCKS 
793+ 	if  (file )
794+ 		alloc_file_lock_backtrace (fileno (file ), filename );
795+ #endif 
604796	return  file ;
605797}
606798
@@ -609,6 +801,9 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
609801	int  hide  =  needs_hiding (filename );
610802	FILE  * file ;
611803	wchar_t  wfilename [MAX_LONG_PATH ], wotype [4 ];
804+ #ifdef  DEBUG_FILE_LOCKS 
805+ 	int  oldfd  =  fileno (stream );
806+ #endif 
612807	if  (filename  &&  !strcmp (filename , "/dev/null" ))
613808		filename  =  "nul" ;
614809	if  (xutftowcs_long_path (wfilename , filename ) <  0  || 
@@ -621,9 +816,37 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
621816	file  =  _wfreopen (wfilename , wotype , stream );
622817	if  (file  &&  hide  &&  set_hidden_flag (wfilename , 1 ))
623818		warning ("could not mark '%s' as hidden." , filename );
819+ #ifdef  DEBUG_FILE_LOCKS 
820+ 	if  (file ) {
821+ 		remove_file_lock_backtrace (oldfd );
822+ 		alloc_file_lock_backtrace (fileno (file ), filename );
823+ 	}
824+ #endif 
624825	return  file ;
625826}
626827
828+ #ifdef  DEBUG_FILE_LOCKS 
829+ #undef  close
830+ int  mingw_close (int  fd )
831+ {
832+ 	int  ret  =  close (fd );
833+ 	if  (!ret )
834+ 		remove_file_lock_backtrace (fd );
835+ 	return  ret ;
836+ }
837+ #define  close  mingw_close
838+ 
839+ #undef  fclose
840+ int  mingw_fclose (FILE  * stream )
841+ {
842+ 	int  fd  =  fileno (stream ), ret  =  fclose (stream );
843+ 	if  (!ret )
844+ 		remove_file_lock_backtrace (fd );
845+ 	return  ret ;
846+ }
847+ #define  fclose  mingw_fclose
848+ #endif 
849+ 
627850#undef  fflush
628851int  mingw_fflush (FILE  * stream )
629852{
@@ -2438,6 +2661,26 @@ int mingw_rename(const char *pold, const char *pnew)
24382661		    SetFileAttributesW (wpnew , attrs  &  ~FILE_ATTRIBUTE_READONLY ))
24392662			goto repeat ;
24402663	}
2664+ #ifdef  DEBUG_FILE_LOCKS 
2665+ 	{
2666+ 		struct  file_lock_backtrace  * p  =  file_lock_lookup (pnew );
2667+ 		const  char  * which  =  "target" ;
2668+ 		if  (!p ) {
2669+ 			p  =  file_lock_lookup (pold );
2670+ 			which  =  "source" ;
2671+ 		}
2672+ 		if  (p ) {
2673+ 			struct  strbuf  buf  =  STRBUF_INIT ;
2674+ 			strbuf_addf (& buf , "the file '%s' wants to be " 
2675+ 				    "renamed to '%s' here:\n" , pold , pnew );
2676+ 			current_backtrace (& buf );
2677+ 			strbuf_addf (& buf , "\nBut the %s is still open:\n" ,
2678+ 				    which );
2679+ 			file_lock_backtrace (p , & buf );
2680+ 			die ("%s\n" , buf .buf );
2681+ 		}
2682+ 	}
2683+ #endif 
24412684	if  (retry_ask_yes_no (& tries , "Rename from '%s' to '%s' failed. " 
24422685		       "Should I try again?" , pold , pnew ))
24432686		goto repeat ;
@@ -3313,6 +3556,9 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
33133556	fsync_object_files  =  1 ;
33143557	maybe_redirect_std_handles ();
33153558	adjust_symlink_flags ();
3559+ #ifdef  DEBUG_FILE_LOCKS 
3560+ 	initialize_file_lock_map ();
3561+ #endif 
33163562
33173563	/* determine size of argv conversion buffer */ 
33183564	maxlen  =  wcslen (_wpgmptr );
@@ -3381,6 +3627,9 @@ void mingw_startup(void)
33813627	fsync_object_files  =  1 ;
33823628	maybe_redirect_std_handles ();
33833629	adjust_symlink_flags ();
3630+ #ifdef  DEBUG_FILE_LOCKS 
3631+ 	initialize_file_lock_map ();
3632+ #endif 
33843633
33853634	/* get wide char arguments and environment */ 
33863635	si .newmode  =  0 ;
0 commit comments