Fixed blame crash at XP system
[TortoiseGit.git] / src / TortoiseShell / RemoteCacheLink.cpp
blob2e21500ab1435be81c4fdd465fed408900026541
1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "StdAfx.h"
20 #include "Remotecachelink.h"
21 #include "ShellExt.h"
22 #include "..\TGitCache\CacheInterface.h"
23 #include "TGitPath.h"
25 CRemoteCacheLink::CRemoteCacheLink(void)
26 : m_hPipe(INVALID_HANDLE_VALUE)
27 , m_hCommandPipe(INVALID_HANDLE_VALUE)
29 SecureZeroMemory(&m_dummyStatus, sizeof(m_dummyStatus));
30 m_dummyStatus.text_status = git_wc_status_none;
31 m_dummyStatus.prop_status = git_wc_status_none;
32 // m_dummyStatus.repos_text_status = git_wc_status_none;
33 // m_dummyStatus.repos_prop_status = git_wc_status_none;
34 m_lastTimeout = 0;
35 m_critSec.Init();
38 CRemoteCacheLink::~CRemoteCacheLink(void)
40 ClosePipe();
41 CloseCommandPipe();
42 m_critSec.Term();
45 bool CRemoteCacheLink::EnsurePipeOpen()
47 AutoLocker lock(m_critSec);
48 if(m_hPipe != INVALID_HANDLE_VALUE)
50 return true;
53 m_hPipe = CreateFile(
54 GetCachePipeName(), // pipe name
55 GENERIC_READ | // read and write access
56 GENERIC_WRITE,
57 0, // no sharing
58 NULL, // default security attributes
59 OPEN_EXISTING, // opens existing pipe
60 FILE_FLAG_OVERLAPPED, // default attributes
61 NULL); // no template file
63 if (m_hPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY)
65 // TSVNCache is running but is busy connecting a different client.
66 // Do not give up immediately but wait for a few milliseconds until
67 // the server has created the next pipe instance
68 if (WaitNamedPipe(GetCachePipeName(), 50))
70 m_hPipe = CreateFile(
71 GetCachePipeName(), // pipe name
72 GENERIC_READ | // read and write access
73 GENERIC_WRITE,
74 0, // no sharing
75 NULL, // default security attributes
76 OPEN_EXISTING, // opens existing pipe
77 FILE_FLAG_OVERLAPPED, // default attributes
78 NULL); // no template file
83 if (m_hPipe != INVALID_HANDLE_VALUE)
85 // The pipe connected; change to message-read mode.
86 DWORD dwMode;
88 dwMode = PIPE_READMODE_MESSAGE;
89 if(!SetNamedPipeHandleState(
90 m_hPipe, // pipe handle
91 &dwMode, // new pipe mode
92 NULL, // don't set maximum bytes
93 NULL)) // don't set maximum time
95 ATLTRACE("SetNamedPipeHandleState failed");
96 CloseHandle(m_hPipe);
97 m_hPipe = INVALID_HANDLE_VALUE;
98 return false;
100 // create an unnamed (=local) manual reset event for use in the overlapped structure
101 m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
102 if (m_hEvent)
103 return true;
104 ATLTRACE("CreateEvent failed");
105 ClosePipe();
106 return false;
109 return false;
112 bool CRemoteCacheLink::EnsureCommandPipeOpen()
114 AutoLocker lock(m_critSec);
115 if(m_hCommandPipe != INVALID_HANDLE_VALUE)
117 return true;
120 m_hCommandPipe = CreateFile(
121 GetCacheCommandPipeName(), // pipe name
122 GENERIC_READ | // read and write access
123 GENERIC_WRITE,
124 0, // no sharing
125 NULL, // default security attributes
126 OPEN_EXISTING, // opens existing pipe
127 FILE_FLAG_OVERLAPPED, // default attributes
128 NULL); // no template file
130 if (m_hCommandPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY)
132 // TSVNCache is running but is busy connecting a different client.
133 // Do not give up immediately but wait for a few milliseconds until
134 // the server has created the next pipe instance
135 if (WaitNamedPipe(GetCacheCommandPipeName(), 50))
137 m_hCommandPipe = CreateFile(
138 GetCacheCommandPipeName(), // pipe name
139 GENERIC_READ | // read and write access
140 GENERIC_WRITE,
141 0, // no sharing
142 NULL, // default security attributes
143 OPEN_EXISTING, // opens existing pipe
144 FILE_FLAG_OVERLAPPED, // default attributes
145 NULL); // no template file
150 if (m_hCommandPipe != INVALID_HANDLE_VALUE)
152 // The pipe connected; change to message-read mode.
153 DWORD dwMode;
155 dwMode = PIPE_READMODE_MESSAGE;
156 if(!SetNamedPipeHandleState(
157 m_hCommandPipe, // pipe handle
158 &dwMode, // new pipe mode
159 NULL, // don't set maximum bytes
160 NULL)) // don't set maximum time
162 ATLTRACE("SetNamedPipeHandleState failed");
163 CloseHandle(m_hCommandPipe);
164 m_hCommandPipe = INVALID_HANDLE_VALUE;
165 return false;
167 return true;
170 return false;
173 void CRemoteCacheLink::ClosePipe()
175 AutoLocker lock(m_critSec);
177 if(m_hPipe != INVALID_HANDLE_VALUE)
179 CloseHandle(m_hPipe);
180 CloseHandle(m_hEvent);
181 m_hPipe = INVALID_HANDLE_VALUE;
182 m_hEvent = INVALID_HANDLE_VALUE;
186 void CRemoteCacheLink::CloseCommandPipe()
188 AutoLocker lock(m_critSec);
190 if(m_hCommandPipe != INVALID_HANDLE_VALUE)
192 // now tell the cache we don't need it's command thread anymore
193 DWORD cbWritten;
194 TSVNCacheCommand cmd;
195 SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand));
196 cmd.command = TSVNCACHECOMMAND_END;
197 WriteFile(
198 m_hCommandPipe, // handle to pipe
199 &cmd, // buffer to write from
200 sizeof(cmd), // number of bytes to write
201 &cbWritten, // number of bytes written
202 NULL); // not overlapped I/O
203 DisconnectNamedPipe(m_hCommandPipe);
204 CloseHandle(m_hCommandPipe);
205 m_hCommandPipe = INVALID_HANDLE_VALUE;
209 bool CRemoteCacheLink::GetStatusFromRemoteCache(const CTGitPath& Path, TSVNCacheResponse* pReturnedStatus, bool bRecursive)
211 if(!EnsurePipeOpen())
213 // We've failed to open the pipe - try and start the cache
214 // but only if the last try to start the cache was a certain time
215 // ago. If we just try over and over again without a small pause
216 // in between, the explorer is rendered unusable!
217 // Failing to start the cache can have different reasons: missing exe,
218 // missing registry key, corrupt exe, ...
219 if (((long)GetTickCount() - m_lastTimeout) < 0)
220 return false;
221 STARTUPINFO startup;
222 PROCESS_INFORMATION process;
223 memset(&startup, 0, sizeof(startup));
224 startup.cb = sizeof(startup);
225 memset(&process, 0, sizeof(process));
227 CRegString cachePath(_T("Software\\TortoiseGit\\CachePath"), _T("TGitCache.exe"), false, HKEY_LOCAL_MACHINE);
228 CString sCachePath = cachePath;
229 if (CreateProcess(sCachePath.GetBuffer(sCachePath.GetLength()+1), _T(""), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)
231 // It's not appropriate to do a message box here, because there may be hundreds of calls
232 sCachePath.ReleaseBuffer();
233 ATLTRACE("Failed to start cache\n");
234 return false;
236 CloseHandle(process.hThread);
237 CloseHandle(process.hProcess);
238 sCachePath.ReleaseBuffer();
240 // Wait for the cache to open
241 long endTime = (long)GetTickCount()+1000;
242 while(!EnsurePipeOpen())
244 if(((long)GetTickCount() - endTime) > 0)
246 m_lastTimeout = (long)GetTickCount()+10000;
247 return false;
252 AutoLocker lock(m_critSec);
254 DWORD nBytesRead;
255 TSVNCacheRequest request;
256 request.flags = TSVNCACHE_FLAGS_NONOTIFICATIONS;
257 if(bRecursive)
259 request.flags |= TSVNCACHE_FLAGS_RECUSIVE_STATUS;
261 wcsncpy_s(request.path, MAX_PATH+1, Path.GetWinPath(), MAX_PATH);
262 SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
263 m_Overlapped.hEvent = m_hEvent;
264 // Do the transaction in overlapped mode.
265 // That way, if anything happens which might block this call
266 // we still can get out of it. We NEVER MUST BLOCK THE SHELL!
267 // A blocked shell is a very bad user impression, because users
268 // who don't know why it's blocked might find the only solution
269 // to such a problem is a reboot and therefore they might loose
270 // valuable data.
271 // One particular situation where the shell could hang is when
272 // the cache crashes and our crash report dialog comes up.
273 // Sure, it would be better to have no situations where the shell
274 // even can get blocked, but the timeout of 10 seconds is long enough
275 // so that users still recognize that something might be wrong and
276 // report back to us so we can investigate further.
278 BOOL fSuccess = TransactNamedPipe(m_hPipe,
279 &request, sizeof(request),
280 pReturnedStatus, sizeof(*pReturnedStatus),
281 &nBytesRead, &m_Overlapped);
283 if (!fSuccess)
285 if (GetLastError()!=ERROR_IO_PENDING)
287 //OutputDebugStringA("TortoiseShell: TransactNamedPipe failed\n");
288 ClosePipe();
289 return false;
292 // TransactNamedPipe is working in an overlapped operation.
293 // Wait for it to finish
294 DWORD dwWait = WaitForSingleObject(m_hEvent, 10000);
295 if (dwWait == WAIT_OBJECT_0)
297 fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE);
299 else
301 // the cache didn't respond!
302 fSuccess = FALSE;
306 if (fSuccess)
308 if(nBytesRead == sizeof(TSVNCacheResponse))
310 // This is a full response - we need to fix-up some pointers
311 // pReturnedStatus->m_status.entry = &pReturnedStatus->m_entry;
312 // pReturnedStatus->m_entry.url = pReturnedStatus->m_url;
314 else
316 // pReturnedStatus->m_status.entry = NULL;
319 return true;
321 ClosePipe();
322 return false;
325 bool CRemoteCacheLink::ReleaseLockForPath(const CTGitPath& path)
327 EnsureCommandPipeOpen();
328 if (m_hCommandPipe != INVALID_HANDLE_VALUE)
330 DWORD cbWritten;
331 TSVNCacheCommand cmd;
332 SecureZeroMemory(&cmd, sizeof(TSVNCacheCommand));
333 cmd.command = TSVNCACHECOMMAND_RELEASE;
334 wcsncpy_s(cmd.path, MAX_PATH+1, path.GetDirectory().GetWinPath(), MAX_PATH);
335 BOOL fSuccess = WriteFile(
336 m_hCommandPipe, // handle to pipe
337 &cmd, // buffer to write from
338 sizeof(cmd), // number of bytes to write
339 &cbWritten, // number of bytes written
340 NULL); // not overlapped I/O
341 if (! fSuccess || sizeof(cmd) != cbWritten)
343 CloseCommandPipe();
344 return false;
346 return true;
348 return false;