CommitDlg: Update empty file list message
[TortoiseGit.git] / src / TortoiseShell / RemoteCacheLink.cpp
bloba5c4e89c03bcd7a00a389d7734dd4be24250e154
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013 - TortoiseGit
4 // Copyright (C) 2003-2014 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "RemoteCacheLink.h"
22 #include "ShellExt.h"
23 #include "../TGitCache/CacheInterface.h"
24 #include "TGitPath.h"
25 #include "PathUtils.h"
26 #include "CreateProcessHelper.h"
28 CRemoteCacheLink::CRemoteCacheLink(void)
30 SecureZeroMemory(&m_dummyStatus, sizeof(m_dummyStatus));
31 SecureZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
32 // m_dummyStatus.node_status = git_wc_status_none;
33 m_dummyStatus.text_status = git_wc_status_none;
34 m_dummyStatus.prop_status = git_wc_status_none;
35 // m_dummyStatus.repos_text_status = git_wc_status_none;
36 // m_dummyStatus.repos_prop_status = git_wc_status_none;
37 // m_dummyStatus.repos_node_status = git_wc_status_none;
38 m_lastTimeout = 0;
39 m_critSec.Init();
42 CRemoteCacheLink::~CRemoteCacheLink(void)
44 ClosePipe();
45 CloseCommandPipe();
46 m_critSec.Term();
49 bool CRemoteCacheLink::InternalEnsurePipeOpen ( CAutoFile& hPipe
50 , const CString& pipeName) const
52 if (hPipe)
53 return true;
55 int tryleft = 2;
57 while (!hPipe && tryleft--)
60 hPipe = CreateFile(
61 pipeName, // pipe name
62 GENERIC_READ | // read and write access
63 GENERIC_WRITE,
64 0, // no sharing
65 NULL, // default security attributes
66 OPEN_EXISTING, // opens existing pipe
67 FILE_FLAG_OVERLAPPED, // default attributes
68 NULL); // no template file
69 if ((!hPipe) && (GetLastError() == ERROR_PIPE_BUSY))
71 // TGitCache is running but is busy connecting a different client.
72 // Do not give up immediately but wait for a few milliseconds until
73 // the server has created the next pipe instance
74 if (!WaitNamedPipe (pipeName, 50))
76 continue;
81 if (hPipe)
83 // The pipe connected; change to message-read mode.
84 DWORD dwMode;
86 dwMode = PIPE_READMODE_MESSAGE;
87 if(!SetNamedPipeHandleState(
88 hPipe, // pipe handle
89 &dwMode, // new pipe mode
90 NULL, // don't set maximum bytes
91 NULL)) // don't set maximum time
93 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": SetNamedPipeHandleState failed");
94 hPipe.CloseHandle();
98 return hPipe;
101 bool CRemoteCacheLink::EnsurePipeOpen()
103 AutoLocker lock(m_critSec);
105 if (InternalEnsurePipeOpen (m_hPipe, GetCachePipeName()))
107 // create an unnamed (=local) manual reset event for use in the overlapped structure
108 if (m_hEvent)
109 return true;
111 m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
112 if (m_hEvent)
113 return true;
115 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": CreateEvent failed");
116 ClosePipe();
119 return false;
122 bool CRemoteCacheLink::EnsureCommandPipeOpen()
124 AutoLocker lock(m_critSec);
125 return InternalEnsurePipeOpen (m_hCommandPipe, GetCacheCommandPipeName());
128 void CRemoteCacheLink::ClosePipe()
130 AutoLocker lock(m_critSec);
132 m_hPipe.CloseHandle();
133 m_hEvent.CloseHandle();
136 void CRemoteCacheLink::CloseCommandPipe()
138 AutoLocker lock(m_critSec);
140 if(m_hCommandPipe)
142 // now tell the cache we don't need it's command thread anymore
143 DWORD cbWritten;
144 TGITCacheCommand cmd;
145 SecureZeroMemory(&cmd, sizeof(TGITCacheCommand));
146 cmd.command = TGITCACHECOMMAND_END;
147 WriteFile(
148 m_hCommandPipe, // handle to pipe
149 &cmd, // buffer to write from
150 sizeof(cmd), // number of bytes to write
151 &cbWritten, // number of bytes written
152 NULL); // not overlapped I/O
153 DisconnectNamedPipe(m_hCommandPipe);
154 m_hCommandPipe.CloseHandle();
158 bool CRemoteCacheLink::GetStatusFromRemoteCache(const CTGitPath& Path, TGITCacheResponse* pReturnedStatus, bool bRecursive)
160 if(!EnsurePipeOpen())
162 // We've failed to open the pipe - try and start the cache
163 // but only if the last try to start the cache was a certain time
164 // ago. If we just try over and over again without a small pause
165 // in between, the explorer is rendered unusable!
166 // Failing to start the cache can have different reasons: missing exe,
167 // missing registry key, corrupt exe, ...
168 if (((long)GetTickCount() - m_lastTimeout) < 0)
169 return false;
170 // if we're in protected mode, don't try to start the cache: since we're
171 // here, we know we can't access it anyway and starting a new process will
172 // trigger a warning dialog in IE7+ on Vista - we don't want that.
173 if (GetProcessIntegrityLevel() < SECURITY_MANDATORY_MEDIUM_RID)
174 return false;
176 if (!RunTGitCacheProcess())
177 return false;
179 // Wait for the cache to open
180 long endTime = (long)GetTickCount()+1000;
181 while(!EnsurePipeOpen())
183 if(((long)GetTickCount() - endTime) > 0)
185 m_lastTimeout = (long)GetTickCount()+10000;
186 return false;
189 m_lastTimeout = (long)GetTickCount()+10000;
192 AutoLocker lock(m_critSec);
194 DWORD nBytesRead;
195 TGITCacheRequest request;
196 request.flags = TGITCACHE_FLAGS_NONOTIFICATIONS;
197 if(bRecursive)
199 request.flags |= TGITCACHE_FLAGS_RECUSIVE_STATUS;
201 wcsncpy_s(request.path, Path.GetWinPath(), _countof(request.path) - 1);
202 SecureZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
203 m_Overlapped.hEvent = m_hEvent;
204 // Do the transaction in overlapped mode.
205 // That way, if anything happens which might block this call
206 // we still can get out of it. We NEVER MUST BLOCK THE SHELL!
207 // A blocked shell is a very bad user impression, because users
208 // who don't know why it's blocked might find the only solution
209 // to such a problem is a reboot and therefore they might loose
210 // valuable data.
211 // One particular situation where the shell could hang is when
212 // the cache crashes and our crash report dialog comes up.
213 // Sure, it would be better to have no situations where the shell
214 // even can get blocked, but the timeout of 10 seconds is long enough
215 // so that users still recognize that something might be wrong and
216 // report back to us so we can investigate further.
218 BOOL fSuccess = TransactNamedPipe(m_hPipe,
219 &request, sizeof(request),
220 pReturnedStatus, sizeof(*pReturnedStatus),
221 &nBytesRead, &m_Overlapped);
223 if (!fSuccess)
225 if (GetLastError()!=ERROR_IO_PENDING)
227 //OutputDebugStringA("TortoiseShell: TransactNamedPipe failed\n");
228 ClosePipe();
229 return false;
232 // TransactNamedPipe is working in an overlapped operation.
233 // Wait for it to finish
234 DWORD dwWait = WaitForSingleObject(m_hEvent, 10000);
235 if (dwWait == WAIT_OBJECT_0)
237 fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE);
239 else
241 // the cache didn't respond!
242 fSuccess = FALSE;
246 if (fSuccess)
248 return true;
250 ClosePipe();
251 return false;
254 bool CRemoteCacheLink::ReleaseLockForPath(const CTGitPath& path)
256 EnsureCommandPipeOpen();
257 if (m_hCommandPipe)
259 DWORD cbWritten;
260 TGITCacheCommand cmd;
261 SecureZeroMemory(&cmd, sizeof(TGITCacheCommand));
262 cmd.command = TGITCACHECOMMAND_RELEASE;
263 wcsncpy_s(cmd.path, path.GetDirectory().GetWinPath(), _countof(cmd.path) - 1);
264 BOOL fSuccess = WriteFile(
265 m_hCommandPipe, // handle to pipe
266 &cmd, // buffer to write from
267 sizeof(cmd), // number of bytes to write
268 &cbWritten, // number of bytes written
269 NULL); // not overlapped I/O
270 if (! fSuccess || sizeof(cmd) != cbWritten)
272 CloseCommandPipe();
273 return false;
275 return true;
277 return false;
280 DWORD CRemoteCacheLink::GetProcessIntegrityLevel() const
282 DWORD dwIntegrityLevel = SECURITY_MANDATORY_MEDIUM_RID;
284 CAutoGeneralHandle hProcess = GetCurrentProcess();
285 CAutoGeneralHandle hToken;
286 if (OpenProcessToken(hProcess, TOKEN_QUERY |
287 TOKEN_QUERY_SOURCE, hToken.GetPointer()))
289 // Get the Integrity level.
290 DWORD dwLengthNeeded;
291 if (!GetTokenInformation(hToken, TokenIntegrityLevel,
292 NULL, 0, &dwLengthNeeded))
294 DWORD dwError = GetLastError();
295 if (dwError == ERROR_INSUFFICIENT_BUFFER)
297 PTOKEN_MANDATORY_LABEL pTIL =
298 (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, dwLengthNeeded);
299 if (pTIL != NULL)
301 if (GetTokenInformation(hToken, TokenIntegrityLevel,
302 pTIL, dwLengthNeeded, &dwLengthNeeded))
304 dwIntegrityLevel = *GetSidSubAuthority(pTIL->Label.Sid,
305 (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid)-1));
307 LocalFree(pTIL);
313 return dwIntegrityLevel;
316 bool CRemoteCacheLink::RunTGitCacheProcess()
318 const CString sCachePath = GetTGitCachePath();
319 if (!CCreateProcessHelper::CreateProcessDetached(sCachePath, NULL))
321 // It's not appropriate to do a message box here, because there may be hundreds of calls
322 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Failed to start cache\n");
323 return false;
325 return true;
328 CString CRemoteCacheLink::GetTGitCachePath() const
330 CString sCachePath = CPathUtils::GetAppDirectory(g_hmodThisDll) + _T("TGitCache.exe");
331 return sCachePath;