Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4404 lines
144 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: update.cpp
  8. //
  9. // Authors;
  10. // Jeff Saathoff (jeffreys)
  11. //
  12. // Notes;
  13. // SyncMgr integration
  14. //--------------------------------------------------------------------------
  15. #include "pch.h"
  16. #include "msgbox.h" // CscWin32Message
  17. #include "folder.h"
  18. #include <openfile.h> // OpenOfflineFile
  19. #include "cscst.h" // PostToSystray
  20. #include "uihooks.h" // Self-host notifications
  21. #include "fopendlg.h" // OpenFilesWarningDialog
  22. #include "nopin.h"
  23. #include "statdlg.h" // ReconnectServers
  24. #include "security.h"
  25. #include "strings.h"
  26. #define RAS_CONNECT_DELAY (10 * 1000)
  27. // Maximum length of username
  28. #define MAX_USERNAME_CHARS 64
  29. // SYNCTHREADDATA.dwSyncStatus flags
  30. #define SDS_SYNC_OUT 0x00000001 // CSCMergeShare
  31. #define SDS_SYNC_IN_QUICK 0x00000002 // CSCFillSparseFiles(FALSE)
  32. #define SDS_SYNC_IN_FULL 0x00000004 // CSCFillSparseFiles(TRUE)
  33. #define SDS_SYNC_FORCE_INWARD 0x00000008
  34. #define SDS_SYNC_RAS_CONNECTED 0x00000010
  35. #define SDS_SYNC_RESTART_MERGE 0x00000020
  36. #define SDS_SYNC_DELETE_DELETE 0x00000040
  37. #define SDS_SYNC_DELETE_RESTORE 0x00000080
  38. #define SDS_SYNC_AUTOCACHE 0x00000100
  39. #define SDS_SYNC_CONFLICT_KEEPLOCAL 0x00000200
  40. #define SDS_SYNC_CONFLICT_KEEPNET 0x00000400
  41. #define SDS_SYNC_CONFLICT_KEEPBOTH 0x00000800
  42. #define SDS_SYNC_STARTED 0x00010000
  43. #define SDS_SYNC_ERROR 0x00020000
  44. #define SDS_SYNC_CANCELLED 0x00040000
  45. #define SDS_SYNC_FILE_SKIPPED 0x00080000
  46. #define SDS_SYNC_DELETE_CONFLICT_MASK (SDS_SYNC_DELETE_DELETE | SDS_SYNC_DELETE_RESTORE)
  47. #define SDS_SYNC_FILE_CONFLICT_MASK (SDS_SYNC_CONFLICT_KEEPLOCAL | SDS_SYNC_CONFLICT_KEEPNET | SDS_SYNC_CONFLICT_KEEPBOTH)
  48. // Sync Flags used internally by CCscUpdate
  49. #define CSC_SYNC_OUT 0x00000001L
  50. #define CSC_SYNC_IN_QUICK 0x00000002L
  51. #define CSC_SYNC_IN_FULL 0x00000004L
  52. #define CSC_SYNC_SETTINGS 0x00000008L
  53. #define CSC_SYNC_MAYBOTHERUSER 0x00000010L
  54. #define CSC_SYNC_NOTIFY_SYSTRAY 0x00000020L
  55. #define CSC_SYNC_LOGOFF 0x00000040L
  56. #define CSC_SYNC_LOGON 0x00000080L
  57. #define CSC_SYNC_IDLE 0x00000100L
  58. #define CSC_SYNC_NONET 0x00000200L
  59. #define CSC_SYNC_PINFILES 0x00000400L
  60. #define CSC_SYNC_PIN_RECURSE 0x00000800L
  61. #define CSC_SYNC_OFWARNINGDONE 0x00001000L
  62. #define CSC_SYNC_CANCELLED 0x00002000L
  63. #define CSC_SYNC_SHOWUI_ALWAYS 0x00004000L
  64. #define CSC_SYNC_IGNORE_ACCESS 0x00008000L
  65. #define CSC_SYNC_EFS_PIN_NONE 0x00010000L
  66. #define CSC_SYNC_EFS_PIN_ALL 0x00020000L
  67. #define CSC_SYNC_RECONNECT 0x00040000L
  68. #define CSC_LOCALLY_MODIFIED (FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED \
  69. | FLAG_CSC_COPY_STATUS_LOCALLY_DELETED \
  70. | FLAG_CSC_COPY_STATUS_LOCALLY_CREATED)
  71. HICON g_hCscIcon = NULL;
  72. // Used for marshalling data into the SyncMgr process
  73. typedef struct _CSC_UPDATE_DATA
  74. {
  75. DWORD dwUpdateFlags;
  76. DWORD dwFileBufferOffset;
  77. } CSC_UPDATE_DATA, *PCSC_UPDATE_DATA;
  78. LPTSTR GetErrorText(DWORD dwErr)
  79. {
  80. UINT idString = (UINT)-1;
  81. LPTSTR pszError = NULL;
  82. switch (dwErr)
  83. {
  84. case ERROR_INVALID_NAME:
  85. // "Files of this type cannot be made available offline."
  86. idString = IDS_CACHING_DISALLOWED;
  87. break;
  88. }
  89. if ((UINT)-1 != idString)
  90. {
  91. LoadStringAlloc(&pszError, g_hInstance, idString);
  92. }
  93. else if (NOERROR != dwErr)
  94. {
  95. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  96. NULL,
  97. dwErr,
  98. 0,
  99. (LPTSTR)&pszError,
  100. 1,
  101. NULL);
  102. }
  103. return pszError;
  104. }
  105. //*************************************************************
  106. //
  107. // CscUpdateCache
  108. //
  109. // Purpose: Invoke SyncMgr to update the CSC cache
  110. //
  111. // Parameters: pNamelist - list of files passed to the CSC SyncMgr handler
  112. //
  113. //
  114. // Return: HRESULT
  115. //
  116. //*************************************************************
  117. HRESULT
  118. CscUpdateCache(DWORD dwUpdateFlags, CscFilenameList *pfnl)
  119. {
  120. HRESULT hr;
  121. HRESULT hrComInit = E_FAIL;
  122. ISyncMgrSynchronizeInvoke *pSyncInvoke = NULL;
  123. DWORD dwSyncMgrFlags = 0;
  124. ULONG cbDataLength = sizeof(CSC_UPDATE_DATA);
  125. PCSC_UPDATE_DATA pUpdateData = NULL;
  126. PCSC_NAMELIST_HDR pNamelist = NULL;
  127. TraceEnter(TRACE_UPDATE, "CscUpdateCache");
  128. hrComInit = CoInitialize(NULL);
  129. hr = CoCreateInstance(CLSID_SyncMgr,
  130. NULL,
  131. CLSCTX_SERVER,
  132. IID_ISyncMgrSynchronizeInvoke,
  133. (LPVOID*)&pSyncInvoke);
  134. FailGracefully(hr, "Unable to create SyncMgr object");
  135. if (dwUpdateFlags & CSC_UPDATE_SELECTION)
  136. {
  137. if (NULL == pfnl || (0 == (CSC_UPDATE_SHOWUI_ALWAYS & dwUpdateFlags) && 0 == pfnl->GetShareCount()))
  138. ExitGracefully(hr, E_INVALIDARG, "CSC_UPDATE_SELECTION with no selection");
  139. pNamelist = pfnl->CreateListBuffer();
  140. if (!pNamelist)
  141. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create namelist buffer");
  142. cbDataLength += pNamelist->cbSize;
  143. }
  144. //
  145. // Alloc a buffer for the cookie data
  146. //
  147. pUpdateData = (PCSC_UPDATE_DATA)LocalAlloc(LPTR, cbDataLength);
  148. if (!pUpdateData)
  149. ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  150. pUpdateData->dwUpdateFlags = dwUpdateFlags;
  151. if (pNamelist)
  152. {
  153. pUpdateData->dwFileBufferOffset = sizeof(CSC_UPDATE_DATA);
  154. CopyMemory(ByteOffset(pUpdateData, pUpdateData->dwFileBufferOffset),
  155. pNamelist,
  156. pNamelist->cbSize);
  157. }
  158. if (dwUpdateFlags & CSC_UPDATE_STARTNOW)
  159. dwSyncMgrFlags |= SYNCMGRINVOKE_STARTSYNC;
  160. //
  161. // Start SyncMgr
  162. //
  163. hr = pSyncInvoke->UpdateItems(dwSyncMgrFlags,
  164. CLSID_CscUpdateHandler,
  165. cbDataLength,
  166. (LPBYTE)pUpdateData);
  167. exit_gracefully:
  168. if (pNamelist)
  169. CscFilenameList::FreeListBuffer(pNamelist);
  170. if (pUpdateData)
  171. LocalFree(pUpdateData);
  172. DoRelease(pSyncInvoke);
  173. if (SUCCEEDED(hrComInit))
  174. CoUninitialize();
  175. TraceLeaveResult(hr);
  176. }
  177. //*************************************************************
  178. //
  179. // GetNewVersionName
  180. //
  181. // Purpose: Create unique names for copies of a file
  182. //
  183. // Parameters: LPTSTR pszUNCPath - fully qualified UNC name of file
  184. // LPTSTR pszShare - \\server\share that file lives on
  185. // LPTSTR pszDrive - drive mapping to use for net operations
  186. // LPTSTR *ppszNewName - filename for new version returned here (must free)
  187. //
  188. // Return: Win32 error code
  189. //
  190. //*************************************************************
  191. DWORD
  192. GetNewVersionName(LPCTSTR pszUNCPath,
  193. LPCTSTR pszShare,
  194. LPCTSTR pszDrive,
  195. LPTSTR *ppszNewName)
  196. {
  197. DWORD dwErr = NOERROR;
  198. LPTSTR pszDriveLetterPath = NULL;
  199. LPTSTR pszPath = NULL;
  200. LPTSTR pszFile = NULL;
  201. LPTSTR pszExt = NULL;
  202. LPTSTR pszWildCardName = NULL;
  203. TCHAR szUserName[MAX_USERNAME_CHARS];
  204. ULONG nLength;
  205. ULONG nMaxVersion = 0;
  206. ULONG cOlderVersions = 0;
  207. HANDLE hFind = INVALID_HANDLE_VALUE;
  208. WIN32_FIND_DATA fd;
  209. LPTSTR pszT;
  210. TraceEnter(TRACE_UPDATE, "GetNewVersionName");
  211. TraceAssert(pszUNCPath != NULL);
  212. TraceAssert(ppszNewName != NULL);
  213. *ppszNewName = NULL;
  214. // 1. Split the path into components.
  215. // 2. Build wildcard name "X:\dir\foo (johndoe v*).txt"
  216. // 3. Do a findfirst/findnext loop to get the min & max version #
  217. // and count the number of old versions.
  218. // 4. Increment the max version # and build the new filename as:
  219. // "foo (johndoe v<max+1>).txt"
  220. // Assume that the UNC name contains more than the share
  221. TraceAssert(!StrCmpNI(pszUNCPath, pszShare, lstrlen(pszShare)));
  222. TraceAssert(lstrlen(pszUNCPath) > lstrlen(pszShare));
  223. // Copy the path (without \\server\share)
  224. if (!LocalAllocString(&pszPath, pszUNCPath + lstrlen(pszShare)))
  225. ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAllocString failed");
  226. // Find the file part of the name
  227. pszT = PathFindFileName(pszPath);
  228. if (!pszT)
  229. ExitGracefully(dwErr, ERROR_INVALID_PARAMETER, "Incomplete path");
  230. // Copy the filename
  231. if (!LocalAllocString(&pszFile, pszT))
  232. ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAllocString failed");
  233. // Look for the file extension
  234. pszT = PathFindExtension(pszFile);
  235. if (pszT)
  236. {
  237. // Copy the extension and truncate the file root at this point
  238. LocalAllocString(&pszExt, pszT);
  239. *pszT = TEXT('\0');
  240. }
  241. // Truncate the path
  242. PathRemoveFileSpec(pszPath);
  243. // Get the user name
  244. nLength = ARRAYSIZE(szUserName);
  245. if (!GetUserName(szUserName, &nLength))
  246. LoadString(g_hInstance, IDS_UNKNOWN_USER, szUserName, ARRAYSIZE(szUserName));
  247. // Build the wildcard path "X:\dir\foo (johndoe v*).txt"
  248. nLength = FormatStringID(&pszWildCardName, g_hInstance, IDS_VERSION_FORMAT, pszFile, szUserName, c_szStar, pszExt);
  249. if (!nLength)
  250. ExitGracefully(dwErr, GetLastError(), "Unable to format string");
  251. nLength += lstrlen(pszUNCPath) + lstrlen(szUserName);
  252. pszDriveLetterPath = (LPTSTR)LocalAlloc(LPTR, MAX(nLength, ULONG(MAX_PATH)) * sizeof(TCHAR));
  253. if (!pszDriveLetterPath)
  254. ExitGracefully(dwErr, ERROR_OUTOFMEMORY, "LocalAlloc failed");
  255. PathCombine(pszDriveLetterPath, pszDrive, pszPath);
  256. PathAppend(pszDriveLetterPath, pszWildCardName);
  257. nLength = (ULONG)(StrStr(pszWildCardName, c_szStar) - pszWildCardName); // remember where the '*' is
  258. // Search for existing versions of the file with this username
  259. hFind = FindFirstFile(pszDriveLetterPath, &fd);
  260. if (hFind != INVALID_HANDLE_VALUE)
  261. {
  262. ULONG nVersion;
  263. do
  264. {
  265. nVersion = StrToLong(&fd.cFileName[nLength]);
  266. if (nVersion > nMaxVersion)
  267. {
  268. nMaxVersion = nVersion;
  269. }
  270. cOlderVersions++;
  271. }
  272. while (FindNextFile(hFind, &fd));
  273. FindClose(hFind);
  274. }
  275. // Build the new file name to return to the caller.
  276. // This one is version nMaxVersion+1.
  277. ULongToString(nMaxVersion+1, pszDriveLetterPath, lstrlen(pszDriveLetterPath));
  278. nLength = FormatStringID(ppszNewName, g_hInstance, IDS_VERSION_FORMAT, pszFile, szUserName, pszDriveLetterPath, pszExt);
  279. if (!nLength)
  280. ExitGracefully(dwErr, GetLastError(), "Unable to format string");
  281. exit_gracefully:
  282. LocalFreeString(&pszDriveLetterPath);
  283. LocalFreeString(&pszPath);
  284. LocalFreeString(&pszFile);
  285. LocalFreeString(&pszExt);
  286. LocalFreeString(&pszWildCardName);
  287. if (NOERROR != dwErr)
  288. {
  289. LocalFreeString(ppszNewName);
  290. }
  291. TraceLeaveValue(dwErr);
  292. }
  293. //*************************************************************
  294. //
  295. // ConflictDlgCallback
  296. //
  297. // Purpose: Display local or remote file from conflict dialog
  298. //
  299. // Parameters: hWnd - conflict dialog handle (used as parent for UI)
  300. // uMsg - one of RFCCM_*
  301. // wParam - depends on uMsg (unused)
  302. // lParam - pointer to context data (RFCDLGPARAM)
  303. //
  304. //
  305. // Return: TRUE on success, FALSE otherwise
  306. //
  307. //*************************************************************
  308. typedef struct _CONFLICT_DATA
  309. {
  310. LPCTSTR pszShare;
  311. LPCTSTR pszDrive;
  312. } CONFLICT_DATA;
  313. BOOL
  314. ConflictDlgCallback(HWND hWnd, UINT uMsg, WPARAM /*wParam*/, LPARAM lParam)
  315. {
  316. RFCDLGPARAM *pdlgParam = (RFCDLGPARAM*)lParam;
  317. CONFLICT_DATA cd = {0};
  318. LPTSTR pszTmpName = NULL;
  319. ULONG cchShare = 0;
  320. LPTSTR szFile;
  321. DWORD dwErr = NOERROR;
  322. TraceEnter(TRACE_UPDATE, "ConflictDlgCallback");
  323. if (NULL == pdlgParam)
  324. {
  325. TraceAssert(FALSE);
  326. TraceLeaveValue(FALSE);
  327. }
  328. szFile = (LPTSTR)LocalAlloc(LMEM_FIXED,
  329. MAX(StringByteSize(pdlgParam->pszLocation)
  330. + StringByteSize(pdlgParam->pszFilename), MAX_PATH_BYTES));
  331. if (!szFile)
  332. TraceLeaveValue(FALSE);
  333. if (pdlgParam->lCallerData)
  334. cd = *(CONFLICT_DATA*)pdlgParam->lCallerData;
  335. if (cd.pszShare)
  336. cchShare = lstrlen(cd.pszShare);
  337. switch (uMsg)
  338. {
  339. case RFCCM_VIEWLOCAL:
  340. // Build UNC path and view what's in the cache
  341. PathCombine(szFile, pdlgParam->pszLocation, pdlgParam->pszFilename);
  342. dwErr = OpenOfflineFile(szFile);
  343. break;
  344. case RFCCM_VIEWNETWORK:
  345. // Build drive letter (non-UNC) path and ShellExecute it
  346. PathCombine(szFile, cd.pszDrive, pdlgParam->pszLocation + cchShare);
  347. PathAppend(szFile, pdlgParam->pszFilename);
  348. {
  349. SHELLEXECUTEINFO si = {0};
  350. si.cbSize = sizeof(si);
  351. si.fMask = SEE_MASK_FLAG_NO_UI;
  352. si.hwnd = hWnd;
  353. si.lpFile = szFile;
  354. si.nShow = SW_NORMAL;
  355. Trace((TEXT("ShellExecuting \"%s\""), szFile));
  356. if (!ShellExecuteEx(&si))
  357. dwErr = GetLastError();
  358. }
  359. break;
  360. }
  361. if (NOERROR != dwErr)
  362. CscWin32Message(hWnd, dwErr, CSCUI::SEV_ERROR);
  363. LocalFree(szFile);
  364. TraceLeaveValue(TRUE);
  365. }
  366. //*************************************************************
  367. //
  368. // ShowConflictDialog
  369. //
  370. // Purpose: Invoke the conflict resolution dialog
  371. //
  372. // Parameters: hWndParent - dialog parent window
  373. // pszUNCPath - full UNC of file that conflicts
  374. // pszNewName - filespec to use for new copy of file (e.g. "foo (johndoe v1).txt"
  375. // pszShare - "\\server\share"
  376. // pszDrive - "X:" drive mapping of remote connection
  377. // pfdLocal - Information about local file
  378. // pfdRemote - Information about remote file
  379. //
  380. //
  381. // Return: HRESULT
  382. //
  383. //*************************************************************
  384. typedef int (WINAPI *PFNSYNCMGRRESOLVECONFLICT)(HWND hWndParent, RFCDLGPARAM *pdlgParam);
  385. TCHAR const c_szSyncMgrDll[] = TEXT("mobsync.dll");
  386. #ifdef UNICODE
  387. CHAR const c_szResolveConflict[] = "SyncMgrResolveConflictW";
  388. #else
  389. CHAR const c_szResolveConflict[] = "SyncMgrResolveConflictA";
  390. #endif
  391. BOOL FileHasAssociation(LPCTSTR pszFile)
  392. {
  393. HRESULT hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
  394. if (pszFile)
  395. {
  396. pszFile = PathFindExtension(pszFile);
  397. if (pszFile && *pszFile)
  398. {
  399. IQueryAssociations *pAssoc = NULL;
  400. hr = AssocCreate(CLSID_QueryAssociations,
  401. IID_IQueryAssociations,
  402. (LPVOID*)&pAssoc);
  403. if (SUCCEEDED(hr))
  404. {
  405. hr = pAssoc->Init(ASSOCF_IGNOREBASECLASS, pszFile, NULL, NULL);
  406. pAssoc->Release();
  407. }
  408. }
  409. }
  410. return SUCCEEDED(hr);
  411. }
  412. int
  413. ShowConflictDialog(HWND hWndParent,
  414. LPCTSTR pszUNCPath,
  415. LPCTSTR pszNewName,
  416. LPCTSTR pszShare,
  417. LPCTSTR pszDrive,
  418. LPWIN32_FIND_DATA pfdLocal,
  419. LPWIN32_FIND_DATA pfdRemote)
  420. {
  421. int nResult = 0;
  422. TCHAR szUser[MAX_USERNAME_CHARS];
  423. LPTSTR pszPath = NULL;
  424. LPTSTR pszFile = NULL;
  425. TCHAR szRemoteDate[MAX_PATH];
  426. TCHAR szLocalDate[MAX_PATH];
  427. ULONG nLength;
  428. SYSTEMTIME st;
  429. RFCDLGPARAM dp = {0};
  430. CONFLICT_DATA cd;
  431. BOOL bLocalIsDir = FALSE;
  432. BOOL bRemoteIsDir = FALSE;
  433. static PFNSYNCMGRRESOLVECONFLICT pfnResolveConflict = NULL;
  434. TraceEnter(TRACE_UPDATE, "ShowConflictDialog");
  435. TraceAssert(pszUNCPath);
  436. if (NULL == pfnResolveConflict)
  437. {
  438. // The CSC Update handler is loaded by SyncMgr, so assume the SyncMgr
  439. // dll is already loaded. We don't want to link to the LIB to keep
  440. // SyncMgr from loading every time our context menu or icon overlay
  441. // handler is loaded (for example).
  442. HMODULE hSyncMgrDll = GetModuleHandle(c_szSyncMgrDll);
  443. if (NULL != hSyncMgrDll)
  444. pfnResolveConflict = (PFNSYNCMGRRESOLVECONFLICT)GetProcAddress(hSyncMgrDll,
  445. c_szResolveConflict);
  446. if (NULL == pfnResolveConflict)
  447. return 0;
  448. }
  449. TraceAssert(NULL != pfnResolveConflict);
  450. szUser[0] = TEXT('\0');
  451. nLength = ARRAYSIZE(szUser);
  452. GetUserName(szUser, &nLength);
  453. szRemoteDate[0] = TEXT('\0');
  454. if (NULL != pfdRemote)
  455. {
  456. DWORD dwFlags = FDTF_DEFAULT;
  457. SHFormatDateTime(&pfdRemote->ftLastWriteTime, &dwFlags, szRemoteDate, ARRAYSIZE(szRemoteDate));
  458. if (pfdRemote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  459. bRemoteIsDir = TRUE;
  460. }
  461. szLocalDate[0] = TEXT('\0');
  462. if (NULL != pfdLocal)
  463. {
  464. DWORD dwFlags = FDTF_DEFAULT;
  465. SHFormatDateTime(&pfdLocal->ftLastWriteTime, &dwFlags, szLocalDate, ARRAYSIZE(szLocalDate));
  466. if (pfdLocal->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  467. bLocalIsDir = TRUE;
  468. }
  469. if (!LocalAllocString(&pszPath, pszUNCPath))
  470. ExitGracefully(nResult, 0, "LocalAllocString failed");
  471. pszFile = PathFindFileName(pszUNCPath);
  472. PathRemoveFileSpec(pszPath);
  473. dp.dwFlags = RFCF_APPLY_ALL;
  474. dp.pszFilename = pszFile;
  475. dp.pszLocation = pszPath;
  476. dp.pszNewName = pszNewName;
  477. dp.pszNetworkModifiedBy = NULL;
  478. dp.pszLocalModifiedBy = szUser;
  479. dp.pszNetworkModifiedOn = szRemoteDate;
  480. dp.pszLocalModifiedOn = szLocalDate;
  481. dp.pfnCallBack = NULL;
  482. dp.lCallerData = 0;
  483. // Only turn on the View buttons (set a callback) if we're
  484. // dealing with files that have associations.
  485. if (!(bLocalIsDir || bRemoteIsDir) && FileHasAssociation(pszFile))
  486. {
  487. // Save both the share name and drive letter for building paths to view files
  488. cd.pszShare = pszShare;
  489. cd.pszDrive = pszDrive;
  490. dp.pfnCallBack = ConflictDlgCallback;
  491. dp.lCallerData = (LPARAM)&cd;
  492. }
  493. nResult = (*pfnResolveConflict)(hWndParent, &dp);
  494. exit_gracefully:
  495. LocalFreeString(&pszPath);
  496. // No need to free pszFile
  497. TraceLeaveValue(nResult);
  498. }
  499. ///////////////////////////////////////////////////////////////////////////////
  500. // //
  501. // SyncMgr integration implementation //
  502. // //
  503. ///////////////////////////////////////////////////////////////////////////////
  504. CCscUpdate::CCscUpdate() : m_cRef(1), m_ShareLog(HKEY_CURRENT_USER, c_szCSCShareKey),
  505. m_pSyncMgrCB(NULL), m_hSyncThreads(NULL),
  506. m_pFileList(NULL), m_hSyncItems(NULL), m_hwndDlgParent(NULL),
  507. m_hSyncInProgMutex(NULL), m_pConflictPinList(NULL),
  508. m_pSilentFolderList(NULL), m_pSpecialFolderList(NULL),
  509. m_bCacheIsEncrypted(IsCacheEncrypted(NULL))
  510. {
  511. DllAddRef();
  512. InitializeCriticalSection(&m_csThreadList);
  513. if (!g_hCscIcon)
  514. g_hCscIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_CSCUI_ICON));
  515. m_hSyncMutex = CreateMutex(NULL, FALSE, c_szSyncMutex);
  516. }
  517. CCscUpdate::~CCscUpdate()
  518. {
  519. TraceEnter(TRACE_UPDATE, "CCscUpdate::~CCscUpdate");
  520. SyncCompleted();
  521. TraceAssert(NULL == m_hSyncInProgMutex);
  522. // We should never get here while a sync thread is still running
  523. TraceAssert(NULL == m_hSyncThreads || 0 == DPA_GetPtrCount(m_hSyncThreads));
  524. if (NULL != m_hSyncThreads)
  525. DPA_Destroy(m_hSyncThreads);
  526. DeleteCriticalSection(&m_csThreadList);
  527. if (NULL != m_hSyncItems)
  528. DSA_Destroy(m_hSyncItems);
  529. DoRelease(m_pSyncMgrCB);
  530. delete m_pFileList;
  531. delete m_pConflictPinList;
  532. delete m_pSilentFolderList;
  533. delete m_pSpecialFolderList;
  534. if (NULL != m_hSyncMutex)
  535. CloseHandle(m_hSyncMutex);
  536. DllRelease();
  537. TraceLeaveVoid();
  538. }
  539. HRESULT WINAPI
  540. CCscUpdate::CreateInstance(REFIID riid, LPVOID *ppv)
  541. {
  542. HRESULT hr;
  543. CCscUpdate *pThis;
  544. TraceEnter(TRACE_UPDATE, "CCscUpdate::CreateInstance");
  545. TraceAssert(IsCSCEnabled());
  546. pThis = new CCscUpdate;
  547. if (pThis)
  548. {
  549. hr = pThis->QueryInterface(riid, ppv);
  550. pThis->Release();
  551. }
  552. else
  553. hr = E_OUTOFMEMORY;
  554. TraceLeaveResult(hr);
  555. }
  556. ///////////////////////////////////////////////////////////////////////////////
  557. // //
  558. // SyncMgr integration implementation (IUnknown) //
  559. // //
  560. ///////////////////////////////////////////////////////////////////////////////
  561. STDMETHODIMP CCscUpdate::QueryInterface(REFIID riid, void **ppv)
  562. {
  563. static const QITAB qit[] =
  564. {
  565. QITABENT(CCscUpdate, ISyncMgrSynchronize),
  566. { 0 },
  567. };
  568. return QISearch(this, qit, riid, ppv);
  569. }
  570. STDMETHODIMP_(ULONG) CCscUpdate::AddRef()
  571. {
  572. return InterlockedIncrement(&m_cRef);
  573. }
  574. STDMETHODIMP_(ULONG) CCscUpdate::Release()
  575. {
  576. if (InterlockedDecrement(&m_cRef))
  577. return m_cRef;
  578. delete this;
  579. return 0;
  580. }
  581. ///////////////////////////////////////////////////////////////////////////////
  582. // //
  583. // Sync Manager integration implementation (ISyncMgrSynchronize) //
  584. // //
  585. ///////////////////////////////////////////////////////////////////////////////
  586. STDMETHODIMP
  587. CCscUpdate::Initialize(DWORD /*dwReserved*/,
  588. DWORD dwSyncFlags,
  589. DWORD cbCookie,
  590. const BYTE *pCookie)
  591. {
  592. HRESULT hr = S_OK;
  593. HKEY hkCSC;
  594. BOOL bNoNet = TRUE;
  595. TraceEnter(TRACE_UPDATE, "CCscUpdate::Initialize");
  596. TraceAssert(IsCSCEnabled());
  597. if (!(SYNCMGRFLAG_SETTINGS & dwSyncFlags) && ::IsSyncInProgress())
  598. {
  599. //
  600. // We need to guard against running multiple syncs at the
  601. // same time. User notification in the UI is handled where
  602. // the UI code calls CscUpdate(). This is so that the UI
  603. // message contains the proper context with respect to what
  604. // the user is doing.
  605. //
  606. TraceLeaveResult(E_FAIL);
  607. }
  608. m_dwSyncFlags = 0;
  609. delete m_pFileList;
  610. m_pFileList = NULL;
  611. delete m_pConflictPinList;
  612. m_pConflictPinList = NULL;
  613. // We used to get the tray status to check for NoNet, but
  614. // there's a timing problem at logon (the tray window may not
  615. // be created yet). So ask RDR instead. If this call fails,
  616. // then RDR must be dead, so bNoNet defaults to TRUE.
  617. CSCIsServerOffline(NULL, &bNoNet);
  618. switch (dwSyncFlags & SYNCMGRFLAG_EVENTMASK)
  619. {
  620. case SYNCMGRFLAG_CONNECT: // Logon
  621. if (bNoNet)
  622. ExitGracefully(hr, E_FAIL, "No Logon sync when no net");
  623. m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_LOGON | CSC_SYNC_NOTIFY_SYSTRAY; // | CSC_SYNC_RECONNECT;
  624. if (CConfig::eSyncFull == CConfig::GetSingleton().SyncAtLogon())
  625. {
  626. m_dwSyncFlags |= CSC_SYNC_IN_FULL;
  627. }
  628. break;
  629. case SYNCMGRFLAG_PENDINGDISCONNECT: // Logoff
  630. if (bNoNet)
  631. ExitGracefully(hr, E_FAIL, "No Logoff sync when no net");
  632. m_dwSyncFlags = CSC_SYNC_LOGOFF;
  633. if (CConfig::eSyncFull == CConfig::GetSingleton().SyncAtLogoff())
  634. m_dwSyncFlags |= CSC_SYNC_OUT | CSC_SYNC_IN_FULL;
  635. else
  636. m_dwSyncFlags |= CSC_SYNC_IN_QUICK;
  637. break;
  638. case SYNCMGRFLAG_INVOKE: // CscUpdateCache
  639. if (pCookie != NULL && cbCookie > 0)
  640. {
  641. PCSC_UPDATE_DATA pUpdateData = (PCSC_UPDATE_DATA)pCookie;
  642. TraceAssert(cbCookie >= sizeof(CSC_UPDATE_DATA));
  643. DWORD dwUpdateFlags = pUpdateData->dwUpdateFlags;
  644. if (dwUpdateFlags & CSC_UPDATE_SELECTION)
  645. {
  646. TraceAssert(cbCookie > sizeof(CSC_UPDATE_DATA));
  647. // Create the filelist from the selection provided
  648. m_pFileList = new CscFilenameList((PCSC_NAMELIST_HDR)ByteOffset(pUpdateData, pUpdateData->dwFileBufferOffset),
  649. true);
  650. if (!m_pFileList)
  651. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create CscFilenameList object");
  652. if (!m_pFileList->IsValid())
  653. ExitGracefully(hr, E_FAIL, "Unable to initialize CscFilenameList object");
  654. if (CSC_UPDATE_SHOWUI_ALWAYS & dwUpdateFlags)
  655. {
  656. m_dwSyncFlags |= CSC_SYNC_SHOWUI_ALWAYS;
  657. }
  658. else if (0 == m_pFileList->GetShareCount())
  659. ExitGracefully(hr, E_UNEXPECTED, "CSC_UPDATE_SELECTION with no selection");
  660. }
  661. if (dwUpdateFlags & CSC_UPDATE_RECONNECT)
  662. {
  663. m_dwSyncFlags |= CSC_SYNC_RECONNECT;
  664. }
  665. if (dwUpdateFlags & CSC_UPDATE_UNATTENDED)
  666. {
  667. dwSyncFlags &= ~SYNCMGRFLAG_MAYBOTHERUSER;
  668. }
  669. if (dwUpdateFlags & CSC_UPDATE_NOTIFY_DONE)
  670. {
  671. //
  672. // Caller of CscUpdateCache want's systray notification
  673. // when sync is complete.
  674. //
  675. m_dwSyncFlags |= CSC_SYNC_NOTIFY_SYSTRAY;
  676. }
  677. if (dwUpdateFlags & CSC_UPDATE_FILL_ALL)
  678. m_dwSyncFlags |= CSC_SYNC_IN_FULL;
  679. else if (dwUpdateFlags & CSC_UPDATE_FILL_QUICK)
  680. m_dwSyncFlags |= CSC_SYNC_IN_QUICK;
  681. if (dwUpdateFlags & CSC_UPDATE_REINT)
  682. m_dwSyncFlags |= CSC_SYNC_OUT;
  683. if (dwUpdateFlags & CSC_UPDATE_PIN_RECURSE)
  684. m_dwSyncFlags |= CSC_SYNC_PINFILES | CSC_SYNC_PIN_RECURSE | CSC_SYNC_IN_QUICK;
  685. else if (dwUpdateFlags & CSC_UPDATE_PINFILES)
  686. m_dwSyncFlags |= CSC_SYNC_PINFILES | CSC_SYNC_IN_QUICK;
  687. if (dwUpdateFlags & CSC_UPDATE_IGNORE_ACCESS)
  688. m_dwSyncFlags |= CSC_SYNC_IGNORE_ACCESS;
  689. }
  690. break;
  691. case SYNCMGRFLAG_IDLE: // Auto-sync at idle time
  692. if (bNoNet)
  693. ExitGracefully(hr, E_FAIL, "No idle sync when no net");
  694. m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_QUICK | CSC_SYNC_IDLE | CSC_SYNC_NOTIFY_SYSTRAY;
  695. break;
  696. case SYNCMGRFLAG_MANUAL: // Run "mobsync.exe"
  697. m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_FULL | CSC_SYNC_NOTIFY_SYSTRAY | CSC_SYNC_RECONNECT;
  698. break;
  699. case SYNCMGRFLAG_SCHEDULED: // User scheduled sync
  700. m_dwSyncFlags = CSC_SYNC_OUT | CSC_SYNC_IN_FULL | CSC_SYNC_NOTIFY_SYSTRAY;
  701. break;
  702. }
  703. if (!(m_dwSyncFlags & CSC_SYNC_PINFILES))
  704. m_dwSyncFlags |= CSC_SYNC_EFS_PIN_NONE; // skip EFS if not pinning
  705. if (dwSyncFlags & SYNCMGRFLAG_SETTINGS)
  706. m_dwSyncFlags |= CSC_SYNC_SETTINGS;
  707. if (!m_dwSyncFlags)
  708. ExitGracefully(hr, E_UNEXPECTED, "Nothing to do");
  709. if (dwSyncFlags & SYNCMGRFLAG_MAYBOTHERUSER)
  710. m_dwSyncFlags |= CSC_SYNC_MAYBOTHERUSER;
  711. if (bNoNet)
  712. m_dwSyncFlags |= CSC_SYNC_NONET;
  713. hr = GetSilentFolderList();
  714. if (FAILED(hr))
  715. {
  716. m_dwSyncFlags = 0;
  717. }
  718. exit_gracefully:
  719. TraceLeaveResult(hr);
  720. }
  721. STDMETHODIMP
  722. CCscUpdate::GetHandlerInfo(LPSYNCMGRHANDLERINFO *ppSyncMgrHandlerInfo)
  723. {
  724. HRESULT hr = S_OK;
  725. LPSYNCMGRHANDLERINFO pHandlerInfo;
  726. TraceEnter(TRACE_UPDATE, "CCscUpdate::GetHandlerInfo");
  727. if (NULL == ppSyncMgrHandlerInfo)
  728. TraceLeaveResult(E_INVALIDARG);
  729. *ppSyncMgrHandlerInfo = NULL;
  730. pHandlerInfo = (LPSYNCMGRHANDLERINFO)CoTaskMemAlloc(sizeof(SYNCMGRHANDLERINFO));
  731. if (NULL == pHandlerInfo)
  732. ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  733. pHandlerInfo->cbSize = sizeof(SYNCMGRHANDLERINFO);
  734. pHandlerInfo->hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_CSCUI_ICON));
  735. pHandlerInfo->SyncMgrHandlerFlags = (m_dwSyncFlags & CSC_SYNC_LOGOFF) ? 0 :
  736. (SYNCMGRHANDLER_HASPROPERTIES | SYNCMGRHANDLER_MAYESTABLISHCONNECTION);
  737. LoadStringW(g_hInstance,
  738. IDS_APPLICATION,
  739. pHandlerInfo->wszHandlerName,
  740. ARRAYSIZE(pHandlerInfo->wszHandlerName));
  741. *ppSyncMgrHandlerInfo = pHandlerInfo;
  742. exit_gracefully:
  743. TraceLeaveResult(hr);
  744. }
  745. STDMETHODIMP
  746. CCscUpdate::EnumSyncMgrItems(LPSYNCMGRENUMITEMS *ppenum)
  747. {
  748. HRESULT hr;
  749. PUPDATEENUM pNewEnum;
  750. TraceEnter(TRACE_UPDATE, "CCscUpdate::EnumSyncMgrItems");
  751. *ppenum = NULL;
  752. pNewEnum = new CUpdateEnumerator(this);
  753. if (pNewEnum)
  754. {
  755. hr = pNewEnum->QueryInterface(IID_ISyncMgrEnumItems, (LPVOID*)ppenum);
  756. pNewEnum->Release();
  757. }
  758. else
  759. hr = E_OUTOFMEMORY;
  760. TraceLeaveResult(hr);
  761. }
  762. STDMETHODIMP
  763. CCscUpdate::GetItemObject(REFSYNCMGRITEMID /*rItemID*/, REFIID /*riid*/, LPVOID * /*ppv*/)
  764. {
  765. return E_NOTIMPL;
  766. }
  767. STDMETHODIMP
  768. CCscUpdate::ShowProperties(HWND hWndParent, REFSYNCMGRITEMID rItemID)
  769. {
  770. CSCEntry *pShareEntry;
  771. LPCTSTR pszShareName = TEXT("");
  772. pShareEntry = m_ShareLog.Get(rItemID);
  773. // We don't enumerate shares to SyncMgr unless a share entry
  774. // exists in the registry, so m_ShareLog.Get should never fail here.
  775. if (pShareEntry)
  776. pszShareName = pShareEntry->Name();
  777. COfflineFilesFolder::Open();
  778. // Notify SyncMgr that the ShowProperties is done.
  779. if (NULL != m_pSyncMgrCB)
  780. m_pSyncMgrCB->ShowPropertiesCompleted(S_OK);
  781. return S_OK;
  782. }
  783. STDMETHODIMP
  784. CCscUpdate::SetProgressCallback(LPSYNCMGRSYNCHRONIZECALLBACK pCallback)
  785. {
  786. TraceEnter(TRACE_UPDATE, "CCscUpdate::SetProgressCallback");
  787. DoRelease(m_pSyncMgrCB);
  788. m_pSyncMgrCB = pCallback;
  789. if (m_pSyncMgrCB)
  790. m_pSyncMgrCB->AddRef();
  791. TraceLeaveResult(S_OK);
  792. }
  793. STDMETHODIMP
  794. CCscUpdate::PrepareForSync(ULONG cNumItems,
  795. SYNCMGRITEMID *pItemID,
  796. HWND /*hWndParent*/,
  797. DWORD /*dwReserved*/)
  798. {
  799. HRESULT hr = S_OK;
  800. TraceEnter(TRACE_UPDATE, "CCscUpdate::PrepareForSync");
  801. TraceAssert(0 != cNumItems);
  802. TraceAssert(NULL != pItemID);
  803. //
  804. // Copy the list of item ID's
  805. //
  806. if (NULL == m_hSyncItems)
  807. {
  808. m_hSyncItems = DSA_Create(sizeof(SYNCMGRITEMID), 4);
  809. if (NULL == m_hSyncItems)
  810. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DSA for SYNCMGRITEMID list");
  811. }
  812. else
  813. DSA_DeleteAllItems(m_hSyncItems);
  814. while (cNumItems--)
  815. DSA_AppendItem(m_hSyncItems, pItemID++);
  816. exit_gracefully:
  817. // ISyncMgrSynchronize::PrepareForSync is now an asynchronous call
  818. // so we could create another thread to do the work and return from
  819. // this call immediately. However, since all we do is copy the list
  820. // of Item IDs, let's do it here and call
  821. // m_pSyncMgrCB->PrepareForSyncCompleted before returning.
  822. if (NULL != m_pSyncMgrCB)
  823. m_pSyncMgrCB->PrepareForSyncCompleted(hr);
  824. TraceLeaveResult(hr);
  825. }
  826. STDMETHODIMP
  827. CCscUpdate::Synchronize(HWND hWndParent)
  828. {
  829. HRESULT hr = E_FAIL;
  830. ULONG cItems = 0;
  831. BOOL bConnectionEstablished = FALSE;
  832. TraceEnter(TRACE_UPDATE, "CCscUpdate::Synchronize");
  833. if (NULL != m_hSyncItems)
  834. cItems = DSA_GetItemCount(m_hSyncItems);
  835. //
  836. // Don't want systray UI updates while syncing.
  837. // Whenever the systray UI is updated, the code checks first
  838. // for this global mutex object. If it's non-signaled, the
  839. // systray knows there's a sync in progress and the UI isn't
  840. // updated.
  841. //
  842. TraceAssert(NULL == m_hSyncInProgMutex);
  843. m_hSyncInProgMutex = CreateMutex(NULL, TRUE, c_szSyncInProgMutex);
  844. if (0 == cItems)
  845. {
  846. ExitGracefully(hr, E_UNEXPECTED, "Nothing to synchronize");
  847. }
  848. else if (1 == cItems)
  849. {
  850. SYNCMGRITEMID *pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, 0);
  851. if (NULL != pItemID && IsEqualGUID(GUID_CscNullSyncItem, *pItemID))
  852. {
  853. //
  854. // A single item in the DSA and it's our "null sync" GUID.
  855. // This means we really have nothing to sync but the invoker
  856. // of the sync wants to see some SyncMgr progress UI. In
  857. // this scenario the update item enumerator already enumerated
  858. // the "null sync" item. Here we set this single item's progress
  859. // UI info to 100% complete and skip any sync activity.
  860. //
  861. SYNCMGRPROGRESSITEM spi = {0};
  862. spi.mask = SYNCMGRPROGRESSITEM_STATUSTYPE |
  863. SYNCMGRPROGRESSITEM_STATUSTEXT |
  864. SYNCMGRPROGRESSITEM_PROGVALUE |
  865. SYNCMGRPROGRESSITEM_MAXVALUE;
  866. spi.cbSize = sizeof(spi);
  867. spi.dwStatusType = SYNCMGRSTATUS_SUCCEEDED;
  868. spi.lpcStatusText = L" ";
  869. spi.iProgValue = 1;
  870. spi.iMaxValue = 1;
  871. m_pSyncMgrCB->Progress(GUID_CscNullSyncItem, &spi);
  872. m_pSyncMgrCB->SynchronizeCompleted(S_OK);
  873. if (CSC_SYNC_RECONNECT & m_dwSyncFlags)
  874. {
  875. //
  876. // We have nothing to sync but one or more servers
  877. // may still be offline. The user's expectation is that the
  878. // sync will transition these to online regardless of link
  879. // speed. Add them to the "reconnect" list.
  880. //
  881. _BuildOfflineShareList(&m_ReconnectList);
  882. }
  883. ExitGracefully(hr, NOERROR, "Nothing to sync. Progress UI displayed");
  884. }
  885. }
  886. m_hwndDlgParent = hWndParent;
  887. // We can pin autocached files without a net (no sync required);
  888. // otherwise we need to establish a RAS connection to do anything.
  889. if ((m_dwSyncFlags & CSC_SYNC_NONET) && !(m_dwSyncFlags & CSC_SYNC_PINFILES))
  890. {
  891. hr = m_pSyncMgrCB->EstablishConnection(NULL, 0);
  892. FailGracefully(hr, "Unable to establish RAS connection");
  893. bConnectionEstablished = TRUE;
  894. }
  895. // For each share, kick off a thread to do the work
  896. while (cItems > 0)
  897. {
  898. SYNCMGRITEMID *pItemID;
  899. CSCEntry *pShareEntry;
  900. --cItems;
  901. pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, cItems);
  902. pShareEntry = m_ShareLog.Get(*pItemID);
  903. // We don't enumerate shares to SyncMgr unless a share entry
  904. // exists in the registry, so m_ShareLog.Get should never fail here.
  905. if (NULL == pShareEntry)
  906. ExitGracefully(hr, E_UNEXPECTED, "No share entry");
  907. hr = SynchronizeShare(pItemID, pShareEntry->Name(), bConnectionEstablished);
  908. DSA_DeleteItem(m_hSyncItems, cItems);
  909. FailGracefully(hr, "Unable to create sync thread");
  910. }
  911. TraceAssert(0 == DSA_GetItemCount(m_hSyncItems));
  912. exit_gracefully:
  913. if (FAILED(hr))
  914. SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  915. TraceLeaveResult(hr);
  916. }
  917. //
  918. // Try to reconnect any server that is currently offline.
  919. //
  920. void
  921. CCscUpdate::_BuildOfflineShareList(
  922. CscFilenameList *pfnl
  923. )
  924. {
  925. WIN32_FIND_DATA fd;
  926. DWORD dwStatus = 0;
  927. CCscFindHandle hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL);
  928. if (hFind.IsValid())
  929. {
  930. do
  931. {
  932. if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwStatus)
  933. {
  934. CscFilenameList::HSHARE hShare;
  935. pfnl->AddShare(fd.cFileName, &hShare);
  936. }
  937. }
  938. while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL));
  939. }
  940. }
  941. STDMETHODIMP
  942. CCscUpdate::SetItemStatus(REFSYNCMGRITEMID rItemID,
  943. DWORD dwSyncMgrStatus)
  944. {
  945. HRESULT hr = E_FAIL;
  946. ULONG cItems;
  947. BOOL bAllItems;
  948. TraceEnter(TRACE_UPDATE, "CCscUpdate::SetItemStatus");
  949. if (SYNCMGRSTATUS_SKIPPED != dwSyncMgrStatus && SYNCMGRSTATUS_STOPPED != dwSyncMgrStatus)
  950. TraceLeaveResult(E_NOTIMPL);
  951. bAllItems = FALSE;
  952. if (SYNCMGRSTATUS_STOPPED == dwSyncMgrStatus)
  953. {
  954. bAllItems = TRUE;
  955. m_dwSyncFlags |= CSC_SYNC_CANCELLED;
  956. }
  957. // SetItemStatus can be called between PrepareForSync and Synchronize, in
  958. // in which case the correct thing to do is remove the item from m_hSyncItems.
  959. if (NULL != m_hSyncItems)
  960. {
  961. cItems = DSA_GetItemCount(m_hSyncItems);
  962. while (cItems > 0)
  963. {
  964. SYNCMGRITEMID *pItemID;
  965. --cItems;
  966. pItemID = (SYNCMGRITEMID*)DSA_GetItemPtr(m_hSyncItems, cItems);
  967. if (bAllItems || (NULL != pItemID && IsEqualGUID(rItemID, *pItemID)))
  968. {
  969. // Remove the item from the list of items to sync
  970. DSA_DeleteItem(m_hSyncItems, cItems);
  971. if (!bAllItems)
  972. ExitGracefully(hr, S_OK, "Skipping item");
  973. }
  974. }
  975. }
  976. // Lookup the thread for the item ID and set its status
  977. // to cause it to terminate.
  978. hr = SetSyncThreadStatus(SyncStop, bAllItems ? GUID_NULL : rItemID);
  979. exit_gracefully:
  980. TraceLeaveResult(hr);
  981. }
  982. STDMETHODIMP
  983. CCscUpdate::ShowError(HWND /*hWndParent*/ , REFSYNCMGRERRORID /*ErrorID*/)
  984. {
  985. return E_NOTIMPL;
  986. }
  987. HRESULT
  988. CCscUpdate::SynchronizeShare(SYNCMGRITEMID *pItemID, LPCTSTR pszShareName, BOOL bRasConnected)
  989. {
  990. HRESULT hr = S_OK;
  991. DWORD dwThreadID;
  992. PSYNCTHREADDATA pThreadData;
  993. ULONG cbShareName = 0;
  994. TraceEnter(TRACE_UPDATE, "CCscUpdate::SynchronizeShare");
  995. TraceAssert(NULL != pItemID);
  996. TraceAssert(NULL != pszShareName);
  997. TraceAssert(*pszShareName);
  998. EnterCriticalSection(&m_csThreadList);
  999. if (NULL == m_hSyncThreads)
  1000. m_hSyncThreads = DPA_Create(4);
  1001. LeaveCriticalSection(&m_csThreadList);
  1002. if (NULL == m_hSyncThreads)
  1003. ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create DPA for threads");
  1004. cbShareName = StringByteSize(pszShareName);
  1005. pThreadData = (PSYNCTHREADDATA)LocalAlloc(LPTR, sizeof(SYNCTHREADDATA) + cbShareName);
  1006. if (!pThreadData)
  1007. ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed");
  1008. pThreadData->pThis = this;
  1009. pThreadData->ItemID = *pItemID;
  1010. pThreadData->pszShareName = (LPTSTR)(pThreadData + 1);
  1011. CopyMemory(pThreadData->pszShareName, pszShareName, cbShareName);
  1012. //
  1013. // If we established a RAS connection, then it will go away
  1014. // right after the sync completes, so there's no point trying
  1015. // to reconnect. That is, only check CSC_SYNC_RECONNECT and
  1016. // add the share to the reconnect list if we aren't doing RAS.
  1017. //
  1018. if (bRasConnected)
  1019. {
  1020. pThreadData->dwSyncStatus |= SDS_SYNC_RAS_CONNECTED;
  1021. }
  1022. else if (m_dwSyncFlags & CSC_SYNC_RECONNECT)
  1023. {
  1024. CscFilenameList::HSHARE hShare;
  1025. m_ReconnectList.AddShare(pszShareName, &hShare);
  1026. }
  1027. // Give the thread a reference to this object and the DLL
  1028. AddRef();
  1029. LoadLibrary(c_szDllName);
  1030. pThreadData->hThread = CreateThread(NULL,
  1031. 0,
  1032. _SyncThread,
  1033. pThreadData,
  1034. CREATE_SUSPENDED,
  1035. &dwThreadID);
  1036. if (NULL != pThreadData->hThread)
  1037. {
  1038. EnterCriticalSection(&m_csThreadList);
  1039. DPA_AppendPtr(m_hSyncThreads, pThreadData);
  1040. LeaveCriticalSection(&m_csThreadList);
  1041. ResumeThread(pThreadData->hThread);
  1042. }
  1043. else
  1044. {
  1045. DWORD dwErr = GetLastError();
  1046. LocalFree(pThreadData);
  1047. LPTSTR pszErr = GetErrorText(GetLastError());
  1048. LogError(*pItemID,
  1049. SYNCMGRLOGLEVEL_ERROR,
  1050. IDS_FILL_SPARSE_FILES_ERROR,
  1051. pszShareName,
  1052. pszErr);
  1053. LocalFreeString(&pszErr);
  1054. hr = HRESULT_FROM_WIN32(dwErr);
  1055. Release();
  1056. FreeLibrary(g_hInstance);
  1057. }
  1058. exit_gracefully:
  1059. TraceLeaveResult(hr);
  1060. }
  1061. void
  1062. CCscUpdate::SetLastSyncTime(LPCTSTR pszShareName)
  1063. {
  1064. HKEY hKey = NULL;
  1065. hKey = m_ShareLog.OpenKey(pszShareName, KEY_SET_VALUE);
  1066. if (hKey)
  1067. {
  1068. FILETIME ft = {0};
  1069. GetSystemTimeAsFileTime(&ft);
  1070. RegSetValueEx(hKey, c_szLastSync, 0, REG_BINARY, (LPBYTE)&ft, sizeof(ft));
  1071. RegCloseKey(hKey);
  1072. }
  1073. }
  1074. DWORD
  1075. CCscUpdate::GetLastSyncTime(LPCTSTR pszShareName, LPFILETIME pft)
  1076. {
  1077. DWORD dwResult = ERROR_PATH_NOT_FOUND;
  1078. HKEY hKey = NULL;
  1079. hKey = m_ShareLog.OpenKey(pszShareName, KEY_QUERY_VALUE);
  1080. if (hKey)
  1081. {
  1082. DWORD dwSize = sizeof(*pft);
  1083. dwResult = RegQueryValueEx(hKey, c_szLastSync, NULL, NULL, (LPBYTE)pft, &dwSize);
  1084. RegCloseKey(hKey);
  1085. }
  1086. return dwResult;
  1087. }
  1088. void
  1089. CCscUpdate::SyncThreadCompleted(PSYNCTHREADDATA pSyncData)
  1090. {
  1091. int iThread;
  1092. TraceEnter(TRACE_UPDATE, "CCscUpdate::SyncThreadCompleted");
  1093. TraceAssert(NULL != pSyncData);
  1094. TraceAssert(NULL != m_hSyncThreads);
  1095. EnterCriticalSection(&m_csThreadList);
  1096. iThread = DPA_GetPtrIndex(m_hSyncThreads, pSyncData);
  1097. TraceAssert(-1 != iThread);
  1098. DPA_DeletePtr(m_hSyncThreads, iThread);
  1099. CloseHandle(pSyncData->hThread);
  1100. pSyncData->hThread = NULL;
  1101. iThread = DPA_GetPtrCount(m_hSyncThreads);
  1102. LeaveCriticalSection(&m_csThreadList);
  1103. if (0 == iThread)
  1104. {
  1105. SyncCompleted();
  1106. }
  1107. TraceLeaveVoid();
  1108. }
  1109. void
  1110. CCscUpdate::SyncCompleted(void)
  1111. {
  1112. if ((m_dwSyncFlags & CSC_SYNC_RECONNECT) &&
  1113. !(m_dwSyncFlags & CSC_SYNC_CANCELLED))
  1114. {
  1115. m_dwSyncFlags &= ~CSC_SYNC_RECONNECT;
  1116. ReconnectServers(&m_ReconnectList, FALSE, FALSE);
  1117. }
  1118. if (NULL != m_hSyncInProgMutex)
  1119. {
  1120. // We're not syncing so reset the global event
  1121. ReleaseMutex(m_hSyncInProgMutex);
  1122. CloseHandle(m_hSyncInProgMutex);
  1123. m_hSyncInProgMutex = NULL;
  1124. }
  1125. if (m_dwSyncFlags & CSC_SYNC_NOTIFY_SYSTRAY)
  1126. {
  1127. // Notify systray that we're done
  1128. PostToSystray(CSCWM_DONESYNCING, 0, 0);
  1129. m_dwSyncFlags &= ~CSC_SYNC_NOTIFY_SYSTRAY;
  1130. }
  1131. // Notify SyncMgr that the sync is done
  1132. if (NULL != m_pSyncMgrCB)
  1133. {
  1134. m_pSyncMgrCB->SynchronizeCompleted(S_OK);
  1135. }
  1136. HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, c_szSyncCompleteEvent);
  1137. if (NULL != hEvent)
  1138. {
  1139. //
  1140. // Let anyone interested know that the sync is complete.
  1141. //
  1142. SetEvent(hEvent);
  1143. CloseHandle(hEvent);
  1144. }
  1145. }
  1146. UINT
  1147. GetErrorFormat(DWORD dwErr, BOOL bMerging = FALSE)
  1148. {
  1149. UINT idString = 0;
  1150. // These are all just initial guesses. Not sure
  1151. // which error codes we'll get from CSC.
  1152. switch (dwErr)
  1153. {
  1154. case ERROR_DISK_FULL:
  1155. // "The server disk is full."
  1156. // "The local disk is full."
  1157. idString = bMerging ? IDS_SERVER_FULL_ERROR : IDS_LOCAL_DISK_FULL_ERROR;
  1158. break;
  1159. case ERROR_LOCK_VIOLATION:
  1160. case ERROR_SHARING_VIOLATION:
  1161. case ERROR_OPEN_FILES:
  1162. case ERROR_ACTIVE_CONNECTIONS:
  1163. case ERROR_DEVICE_IN_USE:
  1164. // "'%1' is in use on %2"
  1165. idString = IDS_FILE_OPEN_ERROR;
  1166. break;
  1167. case ERROR_BAD_NETPATH:
  1168. case ERROR_DEV_NOT_EXIST:
  1169. case ERROR_NETNAME_DELETED:
  1170. case ERROR_BAD_NET_NAME:
  1171. case ERROR_SHARING_PAUSED:
  1172. case ERROR_REQ_NOT_ACCEP:
  1173. case ERROR_REDIR_PAUSED:
  1174. case ERROR_BAD_DEVICE:
  1175. case ERROR_CONNECTION_UNAVAIL:
  1176. case ERROR_NO_NET_OR_BAD_PATH:
  1177. case ERROR_NO_NETWORK:
  1178. case ERROR_CONNECTION_REFUSED:
  1179. case ERROR_GRACEFUL_DISCONNECT:
  1180. case ERROR_NETWORK_UNREACHABLE:
  1181. case ERROR_HOST_UNREACHABLE:
  1182. case ERROR_PROTOCOL_UNREACHABLE:
  1183. case ERROR_PORT_UNREACHABLE:
  1184. case ERROR_LOGON_FAILURE:
  1185. // "Unable to connect to '%1.' %2"
  1186. idString = IDS_SHARE_CONNECT_ERROR;
  1187. break;
  1188. case ERROR_OPEN_FAILED:
  1189. case ERROR_UNEXP_NET_ERR:
  1190. case ERROR_NETWORK_BUSY:
  1191. case ERROR_BAD_NET_RESP:
  1192. // "Unable to access '%1' on %2. %3"
  1193. idString = IDS_NET_ERROR;
  1194. break;
  1195. case ERROR_ACCESS_DENIED:
  1196. case ERROR_NETWORK_ACCESS_DENIED:
  1197. // "Access to '%1' is denied on %2"
  1198. idString = IDS_ACCESS_ERROR;
  1199. break;
  1200. case ERROR_BAD_FORMAT:
  1201. // "The Offline Files cache is corrupt. Restart the computer to correct the cache."
  1202. idString = IDS_CACHE_CORRUPT;
  1203. break;
  1204. default:
  1205. // "Error accessing '%1' on %2. %3"
  1206. idString = IDS_UNKNOWN_SYNC_ERROR;
  1207. break;
  1208. }
  1209. return idString;
  1210. }
  1211. HRESULT
  1212. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1213. LPCTSTR pszText,
  1214. DWORD dwLogLevel,
  1215. REFSYNCMGRERRORID ErrorID)
  1216. {
  1217. HRESULT hr;
  1218. SYNCMGRLOGERRORINFO slei;
  1219. USES_CONVERSION;
  1220. TraceEnter(TRACE_UPDATE, "CCscUpdate::LogError");
  1221. if (NULL == m_pSyncMgrCB)
  1222. TraceLeaveResult(E_UNEXPECTED);
  1223. slei.cbSize = sizeof(slei);
  1224. slei.mask = SYNCMGRLOGERROR_ITEMID | SYNCMGRLOGERROR_ERRORID;
  1225. slei.ItemID = rItemID;
  1226. slei.ErrorID = ErrorID;
  1227. // if we have a jumptext associated with this item then
  1228. // set the enable jumptext flag
  1229. if (ErrorID != GUID_NULL)
  1230. {
  1231. slei.mask |= SYNCMGRLOGERROR_ERRORFLAGS;
  1232. slei.dwSyncMgrErrorFlags = SYNCMGRERRORFLAG_ENABLEJUMPTEXT;
  1233. }
  1234. Trace((pszText));
  1235. hr = m_pSyncMgrCB->LogError(dwLogLevel, T2CW(pszText), &slei);
  1236. TraceLeaveResult(hr);
  1237. }
  1238. DWORD
  1239. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1240. DWORD dwLogLevel,
  1241. UINT nFormatID,
  1242. ...)
  1243. {
  1244. LPTSTR pszError = NULL;
  1245. va_list args;
  1246. va_start(args, nFormatID);
  1247. if (vFormatStringID(&pszError, g_hInstance, nFormatID, &args))
  1248. {
  1249. LogError(rItemID, pszError, dwLogLevel);
  1250. LocalFree(pszError);
  1251. }
  1252. va_end(args);
  1253. return 0;
  1254. }
  1255. DWORD
  1256. CCscUpdate::LogError(REFSYNCMGRITEMID rItemID,
  1257. UINT nFormatID,
  1258. LPCTSTR pszName,
  1259. DWORD dwErr,
  1260. DWORD dwLogLevel)
  1261. {
  1262. //
  1263. // Break the filename into "file" and "path" components
  1264. //
  1265. TCHAR szPath[MAX_PATH] = TEXT("\\");
  1266. LPCTSTR pszFile = NULL;
  1267. if (pszName)
  1268. {
  1269. pszFile = PathFindFileName(pszName);
  1270. lstrcpyn(szPath, pszName, (int)min(ARRAYSIZE(szPath),(int)(pszFile-pszName)));
  1271. }
  1272. //
  1273. // Get the system error text and format the error
  1274. //
  1275. LPTSTR pszErr = GetErrorText(dwErr);
  1276. LogError(rItemID,
  1277. dwLogLevel,
  1278. nFormatID,
  1279. pszFile,
  1280. szPath,
  1281. pszErr);
  1282. LocalFreeString(&pszErr);
  1283. return 0;
  1284. }
  1285. BOOL
  1286. MakeDriveLetterPath(LPCTSTR pszUNC,
  1287. LPCTSTR pszShare,
  1288. LPCTSTR pszDrive,
  1289. LPTSTR *ppszResult)
  1290. {
  1291. BOOL bResult = FALSE;
  1292. ULONG cchShare;
  1293. if (!pszUNC || !pszShare || !ppszResult)
  1294. return FALSE;
  1295. *ppszResult = NULL;
  1296. cchShare = lstrlen(pszShare);
  1297. // If the path is on the share, use the drive letter instead
  1298. if (pszDrive && *pszDrive &&
  1299. CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT,
  1300. NORM_IGNORECASE,
  1301. pszUNC,
  1302. cchShare,
  1303. pszShare,
  1304. cchShare))
  1305. {
  1306. *ppszResult = (LPTSTR)LocalAlloc(LPTR, MAX(StringByteSize(pszUNC), MAX_PATH_BYTES));
  1307. if (*ppszResult)
  1308. {
  1309. PathCombine(*ppszResult, pszDrive, pszUNC + cchShare);
  1310. bResult = TRUE;
  1311. }
  1312. }
  1313. return bResult;
  1314. }
  1315. DWORD
  1316. CCscUpdate::CopyLocalFileWithDriveMapping(LPCTSTR pszSrc,
  1317. LPCTSTR pszDst,
  1318. LPCTSTR pszShare,
  1319. LPCTSTR pszDrive,
  1320. BOOL bDirectory)
  1321. {
  1322. DWORD dwErr = NOERROR;
  1323. LPTSTR szDst = NULL;
  1324. if (!pszSrc || !pszDst || !pszShare)
  1325. return ERROR_INVALID_PARAMETER;
  1326. // If the destination is on the share, use the drive letter instead
  1327. if (MakeDriveLetterPath(pszDst, pszShare, pszDrive, &szDst))
  1328. pszDst = szDst;
  1329. if (bDirectory)
  1330. {
  1331. // We don't need to copy the directory contents here, just create
  1332. // the tree structure on the server.
  1333. if (!CreateDirectory(pszDst, NULL))
  1334. {
  1335. dwErr = GetLastError();
  1336. if (ERROR_ALREADY_EXISTS == dwErr)
  1337. dwErr = NOERROR;
  1338. }
  1339. }
  1340. else
  1341. {
  1342. LPTSTR pszTmpName = NULL;
  1343. if (!CSCCopyReplica(pszSrc, &pszTmpName) ||
  1344. !MoveFileEx(pszTmpName,
  1345. pszDst,
  1346. MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
  1347. {
  1348. dwErr = GetLastError();
  1349. }
  1350. if (NULL != pszTmpName)
  1351. {
  1352. DeleteFile(pszTmpName);
  1353. LocalFree(pszTmpName);
  1354. }
  1355. }
  1356. if (ERROR_PATH_NOT_FOUND == dwErr)
  1357. {
  1358. // The parent directory doesn't exist, create it now.
  1359. TCHAR szParent[MAX_PATH];
  1360. lstrcpyn(szParent, pszDst, ARRAYSIZE(szParent));
  1361. PathRemoveFileSpec(szParent);
  1362. dwErr = CopyLocalFileWithDriveMapping(pszSrc, szParent, pszShare, NULL, TRUE);
  1363. // If that worked, retry the original operation.
  1364. if (NOERROR == dwErr)
  1365. dwErr = CopyLocalFileWithDriveMapping(pszSrc, pszDst, pszShare, NULL, bDirectory);
  1366. }
  1367. LocalFreeString(&szDst);
  1368. return dwErr;
  1369. }
  1370. BOOL
  1371. HandleConflictLocally(PSYNCTHREADDATA pSyncData,
  1372. LPCTSTR pszPath,
  1373. DWORD dwCscStatus,
  1374. DWORD dwLocalAttr,
  1375. DWORD dwRemoteAttr = 0)
  1376. {
  1377. BOOL bResult = FALSE;
  1378. LPTSTR szParent = NULL;
  1379. // If it's super-hidden or not modified locally, we can always
  1380. // handle the conflict locally.
  1381. if (!(dwCscStatus & CSC_LOCALLY_MODIFIED) || IsHiddenSystem(dwLocalAttr) || IsHiddenSystem(dwRemoteAttr))
  1382. return TRUE;
  1383. // If we're dealing with 2 folders, the worst that happens is that the
  1384. // underlying files/folders get merged.
  1385. if ((FILE_ATTRIBUTE_DIRECTORY & dwLocalAttr) && (FILE_ATTRIBUTE_DIRECTORY & dwRemoteAttr))
  1386. return TRUE;
  1387. //
  1388. // Next, check whether the parent path is super-hidden.
  1389. //
  1390. // For example, recycle bin makes super-hidden folders and puts
  1391. // metadata files in them.
  1392. //
  1393. // Do this on the server, since CSC has exclusive access to the database
  1394. // while merging, causing GetFileAttributes to fail with Access Denied.
  1395. //
  1396. //
  1397. // Do this on the server, since CSC has exclusive access to the database
  1398. // while merging, causing GetFileAttributes to fail with Access Denied.
  1399. //
  1400. if (MakeDriveLetterPath(pszPath, pSyncData->pszShareName, pSyncData->szDrive, &szParent))
  1401. {
  1402. //
  1403. // Don't check attributes at the root, just stop.
  1404. // WinSE 16781.
  1405. //
  1406. for(PathRemoveFileSpec(szParent); !PathIsRoot(szParent); PathRemoveFileSpec(szParent))
  1407. {
  1408. dwRemoteAttr = GetFileAttributes(szParent);
  1409. if ((DWORD)-1 == dwRemoteAttr)
  1410. {
  1411. // Path doesn't exist, access denied, etc.
  1412. break;
  1413. }
  1414. if (IsHiddenSystem(dwRemoteAttr))
  1415. {
  1416. bResult = TRUE;
  1417. break;
  1418. }
  1419. }
  1420. }
  1421. LocalFreeString(&szParent);
  1422. return bResult;
  1423. }
  1424. DWORD
  1425. CCscUpdate::HandleFileConflict(PSYNCTHREADDATA pSyncData,
  1426. LPCTSTR pszName,
  1427. DWORD dwStatus,
  1428. DWORD dwHintFlags,
  1429. LPWIN32_FIND_DATA pFind32)
  1430. {
  1431. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1432. DWORD dwErr = NOERROR;
  1433. int nErrorResolution = RFC_KEEPBOTH;
  1434. LPTSTR pszNewName = NULL;
  1435. LPTSTR szFullPath = NULL;
  1436. BOOL bApplyToAll = FALSE;
  1437. TraceEnter(TRACE_UPDATE, "CCscUpdate::HandleFileConflict");
  1438. Trace((TEXT("File conflict: %s"), pszName));
  1439. TraceAssert(pSyncData->dwSyncStatus & SDS_SYNC_OUT);
  1440. szFullPath = (LPTSTR)LocalAlloc(LPTR, StringByteSize(pszName) + MAX_PATH*sizeof(TCHAR));
  1441. if (!szFullPath)
  1442. {
  1443. dwErr = ERROR_OUTOFMEMORY;
  1444. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "LocalAlloc failed");
  1445. }
  1446. HANDLE hFind;
  1447. WIN32_FIND_DATA fdRemote;
  1448. PathCombine(szFullPath, pSyncData->szDrive, pszName + lstrlen(pSyncData->pszShareName));
  1449. hFind = FindFirstFile(szFullPath, &fdRemote);
  1450. // Does the net version still exist?
  1451. if (hFind == INVALID_HANDLE_VALUE)
  1452. ExitGracefully(dwResult, HandleDeleteConflict(pSyncData, pszName, dwStatus, dwHintFlags, pFind32), "Net file deleted");
  1453. // Still exists, continue
  1454. FindClose(hFind);
  1455. // If only the attributes or file times were modified locally,
  1456. // or if the file is hidden+system, keep the server copy and
  1457. // don't bother the user. (e.g. desktop.ini)
  1458. if (HandleConflictLocally(pSyncData, pszName, dwStatus, pFind32->dwFileAttributes, fdRemote.dwFileAttributes))
  1459. {
  1460. ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_INWARD, "Ignoring conflict");
  1461. }
  1462. else if (IsSilentFolder(pszName))
  1463. {
  1464. // It's in a per-user shell special folder. Last writer wins.
  1465. if (CompareFileTime(&pFind32->ftLastWriteTime, &fdRemote.ftLastWriteTime) < 0)
  1466. {
  1467. ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_INWARD, "Handling special folder conflict - server copy wins");
  1468. }
  1469. else
  1470. {
  1471. ExitGracefully(dwResult, CSCPROC_RETURN_FORCE_OUTWARD, "Handling special folder conflict - local copy wins");
  1472. }
  1473. }
  1474. dwErr = GetNewVersionName(pszName,
  1475. pSyncData->pszShareName,
  1476. pSyncData->szDrive,
  1477. &pszNewName);
  1478. if (NOERROR != dwErr)
  1479. {
  1480. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "GetNewVersionName failed");
  1481. }
  1482. switch (SDS_SYNC_FILE_CONFLICT_MASK & pSyncData->dwSyncStatus)
  1483. {
  1484. case 0:
  1485. if (CSC_SYNC_MAYBOTHERUSER & m_dwSyncFlags)
  1486. {
  1487. nErrorResolution = ShowConflictDialog(m_hwndDlgParent,
  1488. pszName,
  1489. pszNewName,
  1490. pSyncData->pszShareName,
  1491. pSyncData->szDrive,
  1492. pFind32,
  1493. &fdRemote);
  1494. if (RFC_APPLY_TO_ALL & nErrorResolution)
  1495. {
  1496. bApplyToAll = TRUE;
  1497. nErrorResolution &= ~RFC_APPLY_TO_ALL;
  1498. }
  1499. }
  1500. break;
  1501. case SDS_SYNC_CONFLICT_KEEPLOCAL:
  1502. nErrorResolution = RFC_KEEPLOCAL;
  1503. break;
  1504. case SDS_SYNC_CONFLICT_KEEPNET:
  1505. nErrorResolution = RFC_KEEPNETWORK;
  1506. break;
  1507. case SDS_SYNC_CONFLICT_KEEPBOTH:
  1508. nErrorResolution = RFC_KEEPBOTH;
  1509. break;
  1510. }
  1511. // Self-host notification callback
  1512. CSCUI_NOTIFYHOOK((CSCH_UpdateConflict, TEXT("Update conflict: %1, resolution %2!d!"), pszName, nErrorResolution));
  1513. switch (nErrorResolution)
  1514. {
  1515. default:
  1516. case RFC_KEEPBOTH:
  1517. if (bApplyToAll)
  1518. pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPBOTH;
  1519. lstrcpy(szFullPath, pszName);
  1520. PathRemoveFileSpec(szFullPath);
  1521. if (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes)
  1522. {
  1523. // Rename the local version in the cache and merge again.
  1524. lstrcpyn(pFind32->cFileName, pszNewName, ARRAYSIZE(pFind32->cFileName));
  1525. if (!CSCDoLocalRenameEx(pszName, szFullPath, pFind32, TRUE, TRUE))
  1526. {
  1527. dwErr = GetLastError();
  1528. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "CSCDoLocalRenameEx failed");
  1529. }
  1530. // Because CSCDoLocalRenameEx and CSCMergeShare are separate operations,
  1531. // we have to abort the current merge operation and start over.
  1532. // Otherwise, the current merge operation fails due to the "left
  1533. // hand not knowing what the right hande is doing".
  1534. Trace((TEXT("Restarting merge on: %s"), pSyncData->pszShareName));
  1535. pSyncData->dwSyncStatus |= SDS_SYNC_RESTART_MERGE;
  1536. dwResult = CSCPROC_RETURN_ABORT;
  1537. }
  1538. else
  1539. {
  1540. // Note that CSCDoLocalRenameEx would work for files also, but we
  1541. // prefer to avoid restarting CSCMergeShare so do these ourselves.
  1542. PathAppend(szFullPath, pszNewName);
  1543. dwErr = CopyLocalFileWithDriveMapping(pszName,
  1544. szFullPath,
  1545. pSyncData->pszShareName,
  1546. pSyncData->szDrive);
  1547. if (NOERROR != dwErr)
  1548. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "CopyLocalFileWithDriveMapping failed");
  1549. // If the original file was pinned, we want to pin the copy also.
  1550. // Unfortunately, we can't reliably pin during a merge, so we have
  1551. // to remember these in a list and pin them later.
  1552. if (dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN))
  1553. {
  1554. if (!m_pConflictPinList)
  1555. m_pConflictPinList = new CscFilenameList;
  1556. if (m_pConflictPinList)
  1557. {
  1558. m_pConflictPinList->AddFile(szFullPath,
  1559. !!(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
  1560. }
  1561. }
  1562. // Tell CSCMergeShare to copy the server copy to the cache
  1563. // (with the old name). This clears the dirty cache.
  1564. dwResult = CSCPROC_RETURN_FORCE_INWARD;
  1565. }
  1566. break;
  1567. case RFC_KEEPNETWORK:
  1568. // Tell CSCMergeShare to copy the server copy to the cache
  1569. dwResult = CSCPROC_RETURN_FORCE_INWARD;
  1570. if (bApplyToAll)
  1571. pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPNET;
  1572. break;
  1573. case RFC_KEEPLOCAL:
  1574. // Tell CSCMergeShare to push the local copy to the server
  1575. dwResult = CSCPROC_RETURN_FORCE_OUTWARD;
  1576. if (bApplyToAll)
  1577. pSyncData->dwSyncStatus |= SDS_SYNC_CONFLICT_KEEPLOCAL;
  1578. break;
  1579. case RFC_CANCEL:
  1580. TraceMsg("HandleFileConflict: Cancelling sync - user bailed");
  1581. SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  1582. dwResult = CSCPROC_RETURN_ABORT;
  1583. break;
  1584. }
  1585. exit_gracefully:
  1586. if (CSCPROC_RETURN_FORCE_INWARD == dwResult)
  1587. {
  1588. // CSCMergeShare truncates (makes sparse) the
  1589. // file if we return this. We'd like to fill
  1590. // it during this sync.
  1591. pSyncData->cFilesToSync++;
  1592. pSyncData->dwSyncStatus |= SDS_SYNC_FORCE_INWARD;
  1593. }
  1594. if (NOERROR != dwErr)
  1595. {
  1596. pszName += lstrlen(pSyncData->pszShareName);
  1597. if (*pszName == TEXT('\\'))
  1598. pszName++;
  1599. LogError(pSyncData->ItemID,
  1600. IDS_NAME_CONFLICT_ERROR,
  1601. pszName,
  1602. dwErr);
  1603. pSyncData->dwSyncStatus |= SDS_SYNC_ERROR;
  1604. }
  1605. LocalFreeString(&szFullPath);
  1606. LocalFreeString(&pszNewName);
  1607. TraceLeaveResult(dwResult);
  1608. }
  1609. // Returns values for the Resolve Delete Conflict dialog
  1610. #define RDC_CANCEL 0x00
  1611. #define RDC_DELETE 0x01
  1612. #define RDC_RESTORE 0x02
  1613. #define RDC_APPLY_ALL 0x04
  1614. #define RDC_DELETE_ALL (RDC_APPLY_ALL | RDC_DELETE)
  1615. #define RDC_RESTORE_ALL (RDC_APPLY_ALL | RDC_RESTORE)
  1616. TCHAR const c_szDeleteSelection[] = TEXT("DeleteConflictSelection");
  1617. INT_PTR CALLBACK
  1618. DeleteConflictProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1619. {
  1620. int nResult;
  1621. switch (uMsg)
  1622. {
  1623. case WM_INITDIALOG:
  1624. {
  1625. TCHAR szShare[MAX_PATH];
  1626. LPCTSTR pszPath = (LPCTSTR)lParam;
  1627. LPTSTR pszT = NULL;
  1628. szShare[0] = TEXT('\0');
  1629. lstrcpyn(szShare, pszPath, ARRAYSIZE(szShare));
  1630. PathStripToRoot(szShare);
  1631. // Format the file name
  1632. PathSetDlgItemPath(hDlg, IDC_FILENAME, pszPath);
  1633. // Build the "Do this for all on <this share>" string
  1634. FormatStringID(&pszT, g_hInstance, IDS_FMT_DELETE_APPLY_ALL, szShare);
  1635. if (pszT)
  1636. {
  1637. SetDlgItemText(hDlg, IDC_APPLY_TO_ALL, pszT);
  1638. LocalFreeString(&pszT);
  1639. }
  1640. // else default text is OK (no share name)
  1641. // Select whatever the user chose last time, default is "restore"
  1642. DWORD dwPrevSelection = RDC_RESTORE;
  1643. DWORD dwType;
  1644. DWORD cbData = sizeof(dwPrevSelection);
  1645. SHGetValue(HKEY_CURRENT_USER,
  1646. c_szCSCKey,
  1647. c_szDeleteSelection,
  1648. &dwType,
  1649. &dwPrevSelection,
  1650. &cbData);
  1651. dwPrevSelection = (RDC_DELETE == dwPrevSelection ? IDC_DELETE_LOCAL : IDC_KEEP_LOCAL);
  1652. CheckRadioButton(hDlg, IDC_KEEP_LOCAL, IDC_DELETE_LOCAL, dwPrevSelection);
  1653. // Get the file-type icon
  1654. pszT = PathFindExtension(pszPath);
  1655. if (pszT)
  1656. {
  1657. SHFILEINFO sfi = {0};
  1658. SHGetFileInfo(pszT, 0, &sfi, sizeof(sfi), SHGFI_ICON);
  1659. if (sfi.hIcon)
  1660. {
  1661. SendDlgItemMessage(hDlg,
  1662. IDC_DLGTYPEICON,
  1663. STM_SETICON,
  1664. (WPARAM)sfi.hIcon,
  1665. 0L);
  1666. }
  1667. }
  1668. }
  1669. return TRUE;
  1670. case WM_COMMAND:
  1671. nResult = -1;
  1672. switch (LOWORD(wParam))
  1673. {
  1674. case IDCANCEL:
  1675. nResult = RDC_CANCEL;
  1676. break;
  1677. case IDOK:
  1678. if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_DELETE_LOCAL))
  1679. nResult = RDC_DELETE;
  1680. else
  1681. nResult = RDC_RESTORE;
  1682. // Remember the selection for next time
  1683. SHSetValue(HKEY_CURRENT_USER,
  1684. c_szCSCKey,
  1685. c_szDeleteSelection,
  1686. REG_DWORD,
  1687. &nResult,
  1688. sizeof(nResult));
  1689. if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_APPLY_TO_ALL))
  1690. nResult |= RDC_APPLY_ALL;
  1691. break;
  1692. }
  1693. if (-1 != nResult)
  1694. {
  1695. EndDialog(hDlg, nResult);
  1696. return TRUE;
  1697. }
  1698. break;
  1699. }
  1700. return FALSE;
  1701. }
  1702. BOOL CALLBACK
  1703. ConflictPurgeCallback(LPCWSTR /*pszFile*/, LPARAM lParam)
  1704. {
  1705. PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)lParam;
  1706. return !(SDS_SYNC_CANCELLED & pSyncData->dwSyncStatus);
  1707. }
  1708. DWORD
  1709. CCscUpdate::HandleDeleteConflict(PSYNCTHREADDATA pSyncData,
  1710. LPCTSTR pszName,
  1711. DWORD dwStatus,
  1712. DWORD dwHintFlags,
  1713. LPWIN32_FIND_DATA pFind32)
  1714. {
  1715. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1716. int nErrorResolution = RDC_DELETE; // default action
  1717. BOOL bDirectory = (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes);
  1718. TraceEnter(TRACE_UPDATE, "CCscUpdate::HandleDeleteConflict");
  1719. Trace((TEXT("Net file deleted: %s"), pszName));
  1720. //
  1721. // We already know that the net file was deleted, or HandleDeleteConflict
  1722. // wouldn't be called. If the local copy was also deleted, then there
  1723. // isn't really a conflict and we can continue without prompting.
  1724. //
  1725. // Handle the conflict silently if only attributes changed or it's super-hidden.
  1726. //
  1727. // Finally, if the file lives in certain special folder locations,
  1728. // such as AppData, handle the conflict silently.
  1729. //
  1730. // If we get past all that, ask the user what to do, but only bother
  1731. // the user as a last resort.
  1732. //
  1733. if ( !(dwStatus & FLAG_CSC_COPY_STATUS_LOCALLY_DELETED)
  1734. && !HandleConflictLocally(pSyncData, pszName, dwStatus, pFind32->dwFileAttributes)
  1735. && !IsSilentFolder(pszName)
  1736. )
  1737. {
  1738. // The file is either pinned or modified locally, so
  1739. // default action is now "restore".
  1740. nErrorResolution = RDC_RESTORE;
  1741. switch (SDS_SYNC_DELETE_CONFLICT_MASK & pSyncData->dwSyncStatus)
  1742. {
  1743. case 0:
  1744. if (CSC_SYNC_MAYBOTHERUSER & m_dwSyncFlags)
  1745. {
  1746. int idDialog = (bDirectory ? IDD_FOLDER_CONFLICT_DELETE : IDD_FILE_CONFLICT_DELETE);
  1747. nErrorResolution = (int)DialogBoxParam(g_hInstance,
  1748. MAKEINTRESOURCE(idDialog),
  1749. m_hwndDlgParent,
  1750. DeleteConflictProc,
  1751. (LPARAM)pszName);
  1752. if (RDC_DELETE_ALL == nErrorResolution)
  1753. {
  1754. pSyncData->dwSyncStatus |= SDS_SYNC_DELETE_DELETE;
  1755. nErrorResolution = RDC_DELETE;
  1756. }
  1757. else if (RDC_RESTORE_ALL == nErrorResolution)
  1758. {
  1759. pSyncData->dwSyncStatus |= SDS_SYNC_DELETE_RESTORE;
  1760. nErrorResolution = RDC_RESTORE;
  1761. }
  1762. }
  1763. break;
  1764. case SDS_SYNC_DELETE_DELETE:
  1765. nErrorResolution = RDC_DELETE;
  1766. break;
  1767. case SDS_SYNC_DELETE_RESTORE:
  1768. nErrorResolution = RDC_RESTORE;
  1769. break;
  1770. }
  1771. // Self-host notification callback
  1772. CSCUI_NOTIFYHOOK((CSCH_DeleteConflict, TEXT("Delete conflict: %1, resolution %2!d!"), pszName, nErrorResolution));
  1773. }
  1774. switch (nErrorResolution)
  1775. {
  1776. default:
  1777. case RDC_RESTORE:
  1778. Trace((TEXT("HandleDeleteConflict: restoring %s"), pszName));
  1779. // Tell CSCMergeShare to push the local copy to the server
  1780. dwResult = CSCPROC_RETURN_FORCE_OUTWARD;
  1781. break;
  1782. case RDC_DELETE:
  1783. Trace((TEXT("HandleDeleteConflict: deleting %s"), pszName));
  1784. if (bDirectory)
  1785. {
  1786. // Deep delete
  1787. CSCUIRemoveFolderFromCache(pszName, 0, ConflictPurgeCallback, (LPARAM)pSyncData);
  1788. }
  1789. else
  1790. {
  1791. if (ERROR_SUCCESS == CscDelete(pszName))
  1792. {
  1793. ShellChangeNotify(pszName, pFind32, TRUE, SHCNE_DELETE);
  1794. }
  1795. }
  1796. dwResult = CSCPROC_RETURN_SKIP;
  1797. break;
  1798. case RDC_CANCEL:
  1799. TraceMsg("HandleDeleteConflict: Cancelling sync - user bailed");
  1800. SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  1801. dwResult = CSCPROC_RETURN_ABORT;
  1802. break;
  1803. }
  1804. TraceLeaveResult(dwResult);
  1805. }
  1806. DWORD
  1807. CCscUpdate::CscCallback(PSYNCTHREADDATA pSyncData,
  1808. LPCTSTR pszName,
  1809. DWORD dwStatus,
  1810. DWORD dwHintFlags,
  1811. DWORD dwPinCount,
  1812. LPWIN32_FIND_DATA pFind32,
  1813. DWORD dwReason,
  1814. DWORD dwParam1,
  1815. DWORD dwParam2)
  1816. {
  1817. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  1818. SYNCMGRPROGRESSITEM spi = { sizeof(spi), 0 };
  1819. TraceEnter(TRACE_UPDATE, "CCscUpdate::CscCallback");
  1820. TraceAssert(pSyncData != NULL);
  1821. TraceAssert(pSyncData->pThis == this);
  1822. // Check for Cancel
  1823. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  1824. {
  1825. TraceMsg("Cancelling sync operation");
  1826. TraceLeaveValue(CSCPROC_RETURN_ABORT);
  1827. }
  1828. switch (dwReason)
  1829. {
  1830. case CSCPROC_REASON_BEGIN:
  1831. // First thing to do is determine if this is for the entire share
  1832. // or an individual file in the share.
  1833. if (!(pSyncData->dwSyncStatus & SDS_SYNC_STARTED))
  1834. {
  1835. // SHARE BEGIN
  1836. pSyncData->dwSyncStatus |= SDS_SYNC_STARTED;
  1837. TraceAssert(!lstrcmpi(pszName, pSyncData->pszShareName));
  1838. Trace((TEXT("Share begin: %s"), pszName));
  1839. if (pSyncData->dwSyncStatus & SDS_SYNC_OUT)
  1840. {
  1841. // Save the drive letter to use for net operations
  1842. Trace((TEXT("Drive %s"), pFind32->cFileName));
  1843. lstrcpyn(pSyncData->szDrive, pFind32->cFileName, ARRAYSIZE(pSyncData->szDrive));
  1844. }
  1845. else
  1846. {
  1847. pSyncData->szDrive[0] = TEXT('\0');
  1848. }
  1849. // Remember whether it's an autocache share or not
  1850. switch (dwStatus & FLAG_CSC_SHARE_STATUS_CACHING_MASK)
  1851. {
  1852. case FLAG_CSC_SHARE_STATUS_AUTO_REINT:
  1853. case FLAG_CSC_SHARE_STATUS_VDO:
  1854. pSyncData->dwSyncStatus |= SDS_SYNC_AUTOCACHE;
  1855. break;
  1856. }
  1857. }
  1858. else
  1859. {
  1860. // FILE BEGIN
  1861. BOOL bSkipFile = FALSE;
  1862. TraceAssert(lstrlen(pszName) > lstrlen(pSyncData->pszShareName));
  1863. if (!(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1864. {
  1865. // If we're updating a file selection and this file
  1866. // isn't part of the selection, skip it.
  1867. if (m_pFileList && !m_pFileList->FileExists(pszName, false))
  1868. {
  1869. bSkipFile = TRUE;
  1870. }
  1871. else if (!(pSyncData->dwSyncStatus & (SDS_SYNC_AUTOCACHE | SDS_SYNC_OUT)) &&
  1872. !(dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN)) &&
  1873. !IsSpecialFolder(pszName))
  1874. {
  1875. // Skip autocached files when filling on a
  1876. // non-autocache share. Raid #341786
  1877. bSkipFile = TRUE;
  1878. }
  1879. else if (!(pSyncData->dwSyncStatus & CSC_SYNC_IGNORE_ACCESS))
  1880. {
  1881. // dwReserved0 is the current user's access mask
  1882. // dwReserved1 is the Guest access mask
  1883. DWORD dwCurrentAccess = pFind32->dwReserved0 | pFind32->dwReserved1;
  1884. if (pSyncData->dwSyncStatus & SDS_SYNC_OUT)
  1885. {
  1886. //
  1887. // If the current user doesn't have sufficient access
  1888. // to merge offline changes, then don't bother trying.
  1889. // (It must be some other user's file.)
  1890. //
  1891. // Have the attributes changed offline?
  1892. if (FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED & dwStatus)
  1893. {
  1894. // Yes. Continue if the current user has
  1895. // write-attribute access.
  1896. bSkipFile = !(dwCurrentAccess & FILE_WRITE_ATTRIBUTES);
  1897. }
  1898. // Have the contents changed offline?
  1899. if (!bSkipFile &&
  1900. ((FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED
  1901. | FLAG_CSC_COPY_STATUS_LOCALLY_CREATED
  1902. | FLAG_CSC_COPY_STATUS_LOCALLY_DELETED) & dwStatus))
  1903. {
  1904. // Yes. Continue if the current user has
  1905. // write-data access.
  1906. bSkipFile = !(dwCurrentAccess & FILE_WRITE_DATA);
  1907. }
  1908. }
  1909. else
  1910. {
  1911. //
  1912. // We're filling. Continue if the current user has
  1913. // read-data access, otherwise skip.
  1914. //
  1915. bSkipFile = !(dwCurrentAccess & FILE_READ_DATA);
  1916. }
  1917. }
  1918. }
  1919. else if (!(pSyncData->dwSyncStatus & SDS_SYNC_OUT))
  1920. {
  1921. // It's a directory and we're in CSCFillSparseFiles.
  1922. //
  1923. // Note that we never skip directories when merging (we may be
  1924. // interested in a file further down the tree) although we
  1925. // can skip directories when filling.
  1926. // If it's not in the file selection, skip it.
  1927. if (m_pFileList && !m_pFileList->FileExists(pszName, false))
  1928. {
  1929. bSkipFile = TRUE;
  1930. }
  1931. }
  1932. if (bSkipFile)
  1933. {
  1934. Trace((TEXT("Skipping: %s"), pszName));
  1935. dwResult = CSCPROC_RETURN_SKIP;
  1936. pSyncData->dwSyncStatus |= SDS_SYNC_FILE_SKIPPED;
  1937. break;
  1938. }
  1939. Trace((TEXT("File begin: %s"), pszName));
  1940. //
  1941. // Since we sometimes don't skip directories, even when it turns
  1942. // out they have nothing that the current user is interested in,
  1943. // don't display directory names in SyncMgr.
  1944. //
  1945. // If we sync a file farther down the tree, we will display the
  1946. // filename and the intervening directory names will be visible
  1947. // at that time.
  1948. //
  1949. if (!(pFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1950. {
  1951. USES_CONVERSION;
  1952. // Tell SyncMgr what we're doing
  1953. spi.mask = SYNCMGRPROGRESSITEM_STATUSTEXT;
  1954. spi.lpcStatusText = T2CW(pszName + lstrlen(pSyncData->pszShareName) + 1);
  1955. NotifySyncMgr(pSyncData, &spi);
  1956. }
  1957. // dwParam1 is non-zero when there is a conflict, i.e. both the
  1958. // local and remote versions of the file have been modified.
  1959. if (dwParam1)
  1960. {
  1961. if (dwParam2) // indicates server file deleted
  1962. {
  1963. Trace((TEXT("Delete conflict: %d"), dwParam2));
  1964. dwResult = HandleDeleteConflict(pSyncData, pszName, dwStatus, dwHintFlags, pFind32);
  1965. }
  1966. else
  1967. {
  1968. Trace((TEXT("Update conflict: %d"), dwParam1));
  1969. dwResult = HandleFileConflict(pSyncData, pszName, dwStatus, dwHintFlags, pFind32);
  1970. }
  1971. }
  1972. }
  1973. break;
  1974. case CSCPROC_REASON_END:
  1975. // dwParam2 == error code (winerror.h)
  1976. if (3000 <= dwParam2 && dwParam2 <= 3200)
  1977. {
  1978. // Private error codes used in cscdll
  1979. Trace((TEXT("CSC error: %d"), dwParam2));
  1980. dwParam2 = NOERROR;
  1981. }
  1982. else if (ERROR_OPERATION_ABORTED == dwParam2)
  1983. {
  1984. // We returned CSCPROC_RETURN_ABORT for some reason.
  1985. // Whatever it was, we already reported it.
  1986. dwParam2 = NOERROR;
  1987. dwResult = CSCPROC_RETURN_ABORT;
  1988. }
  1989. if (lstrlen(pszName) == lstrlen(pSyncData->pszShareName))
  1990. {
  1991. // SHARE END
  1992. TraceAssert(!lstrcmpi(pszName, pSyncData->pszShareName));
  1993. Trace((TEXT("Share end: %s"), pszName));
  1994. pSyncData->dwSyncStatus &= ~SDS_SYNC_STARTED;
  1995. }
  1996. else
  1997. {
  1998. BOOL bUpdateProgress = FALSE;
  1999. // FILE END
  2000. if (!(pSyncData->dwSyncStatus & SDS_SYNC_FILE_SKIPPED))
  2001. {
  2002. Trace((TEXT("File end: %s"), pszName));
  2003. bUpdateProgress = TRUE;
  2004. // Special case errors
  2005. switch (dwParam2)
  2006. {
  2007. case ERROR_ACCESS_DENIED:
  2008. if (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes)
  2009. {
  2010. // 317751 directories are not per-user, so if a
  2011. // different user syncs, we can hit this. Don't want
  2012. // to show an error message unless we are ignoring
  2013. // access (when the user explicitly selected something
  2014. // to pin/sync).
  2015. //
  2016. // 394362 BrianV hit this running as an admin, so don't
  2017. // show this error for admins either.
  2018. //
  2019. if (!(pSyncData->dwSyncStatus & CSC_SYNC_IGNORE_ACCESS))
  2020. {
  2021. TraceMsg("Suppressing ERROR_ACCESS_DENIED on folder");
  2022. dwParam2 = NOERROR;
  2023. }
  2024. }
  2025. break;
  2026. case ERROR_GEN_FAILURE:
  2027. TraceMsg("Received ERROR_GEN_FAILURE from cscdll");
  2028. if (dwStatus & FLAG_CSC_COPY_STATUS_FILE_IN_USE)
  2029. dwParam2 = ERROR_OPEN_FILES;
  2030. break;
  2031. case ERROR_FILE_NOT_FOUND:
  2032. case ERROR_PATH_NOT_FOUND:
  2033. // We either handle the error here or the user is
  2034. // prompted, so no need for another error message.
  2035. dwParam2 = NOERROR;
  2036. // If this is an autocache file and has not been modified
  2037. // offline, nuke it now. Otherwise, prompt for action.
  2038. if (CSCPROC_RETURN_FORCE_OUTWARD == HandleDeleteConflict(pSyncData,
  2039. pszName,
  2040. dwStatus,
  2041. dwHintFlags,
  2042. pFind32))
  2043. {
  2044. dwParam2 = CopyLocalFileWithDriveMapping(pszName,
  2045. pszName,
  2046. pSyncData->pszShareName,
  2047. pSyncData->szDrive,
  2048. (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes));
  2049. }
  2050. break;
  2051. case ERROR_DISK_FULL:
  2052. // There's no point continuing
  2053. dwResult = CSCPROC_RETURN_ABORT;
  2054. break;
  2055. default:
  2056. // nothing
  2057. break;
  2058. }
  2059. }
  2060. else
  2061. {
  2062. pSyncData->dwSyncStatus &= ~SDS_SYNC_FILE_SKIPPED;
  2063. dwParam2 = NOERROR;
  2064. // If doing full sync, then we count progress for skipped
  2065. // files as well. Not true for quick fill or merge.
  2066. if (pSyncData->dwSyncStatus & SDS_SYNC_IN_FULL)
  2067. bUpdateProgress = TRUE;
  2068. }
  2069. // Update progress in SyncMgr
  2070. if (bUpdateProgress)
  2071. {
  2072. pSyncData->cFilesDone++;
  2073. spi.mask = SYNCMGRPROGRESSITEM_PROGVALUE;
  2074. spi.iProgValue = min(pSyncData->cFilesDone, pSyncData->cFilesToSync - 1);
  2075. Trace((TEXT("%d of %d files done"), spi.iProgValue, pSyncData->cFilesToSync));
  2076. NotifySyncMgr(pSyncData, &spi);
  2077. }
  2078. }
  2079. if (dwParam2 != NOERROR)
  2080. {
  2081. UINT idsError = GetErrorFormat(dwParam2, boolify(pSyncData->dwSyncStatus & SDS_SYNC_OUT));
  2082. if (IDS_SHARE_CONNECT_ERROR == idsError)
  2083. {
  2084. LPTSTR pszErr = GetErrorText(dwParam2);
  2085. //
  2086. // Special-case the "can't connect to share" error.
  2087. // Display only the share name in the error message
  2088. // and abort the synchronization of this share.
  2089. //
  2090. LogError(pSyncData->ItemID,
  2091. SYNCMGRLOGLEVEL_ERROR,
  2092. idsError,
  2093. pSyncData->pszShareName,
  2094. pszErr ? pszErr : TEXT(""));
  2095. LocalFreeString(&pszErr);
  2096. dwResult = CSCPROC_RETURN_ABORT;
  2097. }
  2098. else
  2099. {
  2100. LogError(pSyncData->ItemID,
  2101. idsError,
  2102. pszName,
  2103. dwParam2);
  2104. }
  2105. pSyncData->dwSyncStatus |= SDS_SYNC_ERROR;
  2106. }
  2107. break;
  2108. }
  2109. // Check for Cancel
  2110. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2111. {
  2112. TraceMsg("Cancelling sync operation");
  2113. dwResult = CSCPROC_RETURN_ABORT;
  2114. }
  2115. TraceLeaveValue(dwResult);
  2116. }
  2117. void
  2118. CCscUpdate::NotifySyncMgr(PSYNCTHREADDATA pSyncData, LPSYNCMGRPROGRESSITEM pspi)
  2119. {
  2120. LPSYNCMGRSYNCHRONIZECALLBACK pSyncMgr = pSyncData->pThis->m_pSyncMgrCB;
  2121. if (pSyncMgr)
  2122. {
  2123. HRESULT hr = pSyncMgr->Progress(pSyncData->ItemID, pspi);
  2124. if (hr == S_SYNCMGR_CANCELITEM || hr == S_SYNCMGR_CANCELALL)
  2125. pSyncData->dwSyncStatus |= SDS_SYNC_CANCELLED;
  2126. }
  2127. }
  2128. DWORD WINAPI
  2129. CCscUpdate::_CscCallback(LPCTSTR pszName,
  2130. DWORD dwStatus,
  2131. DWORD dwHintFlags,
  2132. DWORD dwPinCount,
  2133. LPWIN32_FIND_DATA pFind32,
  2134. DWORD dwReason,
  2135. DWORD dwParam1,
  2136. DWORD dwParam2,
  2137. DWORD_PTR dwContext)
  2138. {
  2139. DWORD dwResult = CSCPROC_RETURN_ABORT;
  2140. PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)dwContext;
  2141. if (pSyncData != NULL && pSyncData->pThis != NULL)
  2142. dwResult = pSyncData->pThis->CscCallback(pSyncData,
  2143. pszName,
  2144. dwStatus,
  2145. dwHintFlags,
  2146. dwPinCount,
  2147. pFind32,
  2148. dwReason,
  2149. dwParam1,
  2150. dwParam2);
  2151. return dwResult;
  2152. }
  2153. BOOL
  2154. CCscUpdate::PinLinkTarget(LPCTSTR pszName, PSYNCTHREADDATA pSyncData)
  2155. {
  2156. BOOL bResult = FALSE;
  2157. LPTSTR pszTarget = NULL;
  2158. TraceEnter(TRACE_SHELLEX, "PinLinkTarget");
  2159. GetLinkTarget(pszName, &pszTarget);
  2160. if (pszTarget)
  2161. {
  2162. DWORD dwAttr = GetFileAttributes(pszTarget);
  2163. if ((DWORD)-1 == dwAttr)
  2164. ExitGracefully(bResult, FALSE, "Link target not found");
  2165. TraceAssert(!(dwAttr & FILE_ATTRIBUTE_DIRECTORY));
  2166. // Check for EFS
  2167. if ((FILE_ATTRIBUTE_ENCRYPTED & dwAttr) && SkipEFSPin(pSyncData, pszTarget))
  2168. ExitGracefully(bResult, FALSE, "Skipping EFS link target");
  2169. if (!(pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED))
  2170. {
  2171. HRESULT hr = m_NoPinList.IsPinAllowed(pszTarget);
  2172. if (S_OK == hr)
  2173. {
  2174. if (CSCPinFile(pszTarget, pSyncData->dwPinHints, NULL, NULL, NULL))
  2175. {
  2176. WIN32_FIND_DATA fd = {0};
  2177. LPCTSTR pszT = PathFindFileName(pszTarget);
  2178. fd.dwFileAttributes = dwAttr;
  2179. lstrcpyn(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName));
  2180. ShellChangeNotify(pszTarget, &fd, FALSE);
  2181. bResult = TRUE;
  2182. if ((FILE_ATTRIBUTE_ENCRYPTED & dwAttr) && !m_bCacheIsEncrypted)
  2183. {
  2184. LogError(pSyncData->ItemID,
  2185. IDS_PIN_ENCRYPT_WARNING,
  2186. pszTarget,
  2187. NOERROR,
  2188. SYNCMGRLOGLEVEL_WARNING);
  2189. }
  2190. }
  2191. }
  2192. else if (S_FALSE == hr)
  2193. {
  2194. if (FILE_ATTRIBUTE_DIRECTORY & dwAttr)
  2195. {
  2196. LogError(pSyncData->ItemID,
  2197. SYNCMGRLOGLEVEL_WARNING,
  2198. IDS_PIN_NOPINFOLDER_POLICY_WARNING,
  2199. pszTarget);
  2200. }
  2201. else
  2202. {
  2203. LogError(pSyncData->ItemID,
  2204. IDS_PIN_NOPINFILE_POLICY_WARNING,
  2205. pszTarget,
  2206. NOERROR,
  2207. SYNCMGRLOGLEVEL_WARNING);
  2208. }
  2209. }
  2210. }
  2211. }
  2212. exit_gracefully:
  2213. LocalFreeString(&pszTarget);
  2214. TraceLeaveValue(bResult);
  2215. }
  2216. BOOL
  2217. CCscUpdate::ShouldPinRecurse(LPCTSTR pszName)
  2218. {
  2219. //
  2220. // NTRAID#NTBUG9-508029-2001/12/18-jeffreys
  2221. //
  2222. // If CSC_SYNC_PIN_RECURSE is set, the answer is always TRUE. Otherwise,
  2223. // if we're not pinning files (typically running the FrankAr code), we
  2224. // automatically recurse on special folders.
  2225. //
  2226. return ((m_dwSyncFlags & CSC_SYNC_PIN_RECURSE) ||
  2227. (!(m_dwSyncFlags & CSC_SYNC_PINFILES) && !CConfig::GetSingleton().NoAdminPinSpecialFolders() && IsSpecialFolder(pszName)));
  2228. }
  2229. DWORD WINAPI
  2230. CCscUpdate::_PinNewFilesW32Callback(LPCTSTR pszName,
  2231. ENUM_REASON eReason,
  2232. LPWIN32_FIND_DATA pFind32,
  2233. LPARAM lpContext)
  2234. {
  2235. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  2236. PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)lpContext;
  2237. DWORD dwHintFlags = 0;
  2238. DWORD dwErr = NOERROR;
  2239. LPTSTR pszConnectionName = NULL;
  2240. // This callback is used when enumerating a pinned folder looking
  2241. // for new files on the server. Since the parent folder is pinned,
  2242. // any files in it that aren't pinned get pinned here.
  2243. TraceEnter(TRACE_UPDATE, "CCscUpdate::_PinNewFilesW32Callback");
  2244. TraceAssert(pSyncData != NULL);
  2245. // Check for Cancel
  2246. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2247. {
  2248. TraceMsg("Cancelling sync operation");
  2249. TraceLeaveValue(CSCPROC_RETURN_ABORT);
  2250. }
  2251. // Always ignore folder_end and ignore folder_begin if we
  2252. // aren't doing a recursive pin operation.
  2253. if (eReason == ENUM_REASON_FOLDER_END ||
  2254. (eReason == ENUM_REASON_FOLDER_BEGIN && !pSyncData->pThis->ShouldPinRecurse(pszName)))
  2255. {
  2256. TraceLeaveValue(CSCPROC_RETURN_SKIP);
  2257. }
  2258. if (eReason == ENUM_REASON_FOLDER_BEGIN)
  2259. {
  2260. DWORD dwShareStatus = 0;
  2261. // Folders may be DFS junctions, so make sure it's cacheable.
  2262. if (!ShareIsCacheable(pszName, FALSE, &pszConnectionName, &dwShareStatus))
  2263. {
  2264. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "Skipping no-cache folder");
  2265. }
  2266. }
  2267. if (S_FALSE == pSyncData->pThis->m_NoPinList.IsPinAllowed(pszName))
  2268. {
  2269. if (FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes)
  2270. {
  2271. pSyncData->pThis->LogError(pSyncData->ItemID,
  2272. SYNCMGRLOGLEVEL_WARNING,
  2273. IDS_PIN_NOPINFOLDER_POLICY_WARNING,
  2274. pszName);
  2275. }
  2276. else
  2277. {
  2278. pSyncData->pThis->LogError(pSyncData->ItemID,
  2279. IDS_PIN_NOPINFILE_POLICY_WARNING,
  2280. pszName,
  2281. NOERROR,
  2282. SYNCMGRLOGLEVEL_WARNING);
  2283. }
  2284. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "Skipping per no-pin policy");
  2285. }
  2286. // At this point, we either have 1) a file or 2) folder_begin + recurse,
  2287. // so pin anything that isn't pinned.
  2288. // Is this file already pinned?
  2289. if (!CSCQueryFileStatus(pszName, NULL, NULL, &dwHintFlags))
  2290. dwErr = GetLastError();
  2291. if (ERROR_FILE_NOT_FOUND == dwErr ||
  2292. (NOERROR == dwErr && !(dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN))))
  2293. {
  2294. // Check for EFS
  2295. BOOL bIsEFSFile = (FILE_ATTRIBUTE_ENCRYPTED & pFind32->dwFileAttributes) &&
  2296. !(FILE_ATTRIBUTE_DIRECTORY & pFind32->dwFileAttributes);
  2297. if (bIsEFSFile && pSyncData->pThis->SkipEFSPin(pSyncData, pszName))
  2298. ExitGracefully(dwResult, CSCPROC_RETURN_SKIP, "Skipping EFS file");
  2299. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2300. ExitGracefully(dwResult, CSCPROC_RETURN_ABORT, "Sync cancelled");
  2301. // Pin it now.
  2302. if (CSCPinFile(pszName, pSyncData->dwPinHints, NULL, NULL, NULL))
  2303. {
  2304. pSyncData->cFilesToSync++;
  2305. ShellChangeNotify(pszName, pFind32, FALSE);
  2306. if (bIsEFSFile && !pSyncData->pThis->m_bCacheIsEncrypted)
  2307. {
  2308. pSyncData->pThis->LogError(pSyncData->ItemID,
  2309. IDS_PIN_ENCRYPT_WARNING,
  2310. pszName,
  2311. NOERROR,
  2312. SYNCMGRLOGLEVEL_WARNING);
  2313. }
  2314. // If this is a link file, pin the target (if appropriate)
  2315. LPTSTR pszExtn = PathFindExtension(pszName);
  2316. if (pszExtn && !lstrcmpi(pszExtn, c_szLNK))
  2317. {
  2318. if (pSyncData->pThis->PinLinkTarget(pszName, pSyncData))
  2319. pSyncData->cFilesToSync++;
  2320. }
  2321. }
  2322. else
  2323. {
  2324. DWORD dwError = GetLastError();
  2325. UINT idsError = GetErrorFormat(dwError);
  2326. if (IDS_SHARE_CONNECT_ERROR == idsError)
  2327. {
  2328. LPTSTR pszErr = GetErrorText(dwError);
  2329. //
  2330. // Special-case the "can't connect to share" error.
  2331. // Display only the share name in the error message
  2332. // and abort the pinning of this share.
  2333. //
  2334. pSyncData->pThis->LogError(pSyncData->ItemID,
  2335. SYNCMGRLOGLEVEL_ERROR,
  2336. idsError,
  2337. pSyncData->pszShareName,
  2338. pszErr ? pszErr : TEXT(""));
  2339. LocalFreeString(&pszErr);
  2340. pSyncData->dwSyncStatus |= SDS_SYNC_CANCELLED;
  2341. }
  2342. else
  2343. {
  2344. DWORD dwSyncMgrLogLevel = SYNCMGRLOGLEVEL_ERROR;
  2345. if (ERROR_INVALID_NAME == dwError)
  2346. {
  2347. //
  2348. // File type is in the exclusion list.
  2349. // This is a warning, not an error.
  2350. //
  2351. dwSyncMgrLogLevel = SYNCMGRLOGLEVEL_WARNING;
  2352. }
  2353. pSyncData->pThis->LogError(pSyncData->ItemID,
  2354. IDS_PIN_FILE_ERROR,
  2355. pszName,
  2356. dwError,
  2357. dwSyncMgrLogLevel);
  2358. }
  2359. pSyncData->dwSyncStatus |= SDS_SYNC_ERROR;
  2360. }
  2361. LPTSTR pszScanMsg = NULL;
  2362. SYNCMGRPROGRESSITEM spi;
  2363. spi.cbSize = sizeof(spi);
  2364. spi.mask = SYNCMGRPROGRESSITEM_STATUSTEXT;
  2365. spi.lpcStatusText = L" ";
  2366. // Skip the share name
  2367. TraceAssert(PathIsPrefix(pSyncData->pszShareName, pszName));
  2368. pszName += lstrlen(pSyncData->pszShareName);
  2369. if (*pszName == TEXT('\\'))
  2370. pszName++;
  2371. LPCTSTR pszFile = PathFindFileName(pszName);
  2372. TCHAR szPath[MAX_PATH] = TEXT("\\");
  2373. if (*pszName)
  2374. lstrcpyn(szPath, pszName, (int)min(ARRAYSIZE(szPath),(int)(pszFile-pszName)));
  2375. // If we still have a name, build a string like
  2376. // "scanning: dir\foo.txt" to display in SyncMgr
  2377. if (FormatStringID(&pszScanMsg, g_hInstance, IDS_NEW_SCAN, pszFile, szPath))
  2378. {
  2379. USES_CONVERSION;
  2380. spi.lpcStatusText = T2CW(pszScanMsg);
  2381. }
  2382. NotifySyncMgr(pSyncData, &spi);
  2383. LocalFreeString(&pszScanMsg);
  2384. }
  2385. else if ((dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN)) &&
  2386. (pSyncData->pThis->m_dwSyncFlags & CSC_SYNC_PINFILES))
  2387. {
  2388. // FLAG_CSC_HINT_PIN_USER being set implies that CSCQueryFileStatus
  2389. // succeeded above.
  2390. // The item was already pinned. Save it in the undo exclusion list.
  2391. if (!pSyncData->pUndoExclusionList)
  2392. pSyncData->pUndoExclusionList = new CscFilenameList;
  2393. if (pSyncData->pUndoExclusionList)
  2394. pSyncData->pUndoExclusionList->AddFile(pszName);
  2395. }
  2396. exit_gracefully:
  2397. if (pszConnectionName)
  2398. {
  2399. WNetCancelConnection2(pszConnectionName, 0, FALSE);
  2400. LocalFreeString(&pszConnectionName);
  2401. }
  2402. TraceLeaveValue(dwResult);
  2403. }
  2404. DWORD WINAPI
  2405. CCscUpdate::_PinNewFilesCSCCallback(LPCTSTR pszName,
  2406. ENUM_REASON eReason,
  2407. DWORD /*dwStatus*/,
  2408. DWORD dwHintFlags,
  2409. DWORD /*dwPinCount*/,
  2410. LPWIN32_FIND_DATA /*pFind32*/,
  2411. LPARAM lpContext)
  2412. {
  2413. PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)lpContext;
  2414. PCSCUPDATE pThis;
  2415. // This callback is used when enumerating the CSC database looking
  2416. // for pinned folders, with the intention of pinning new files
  2417. // in those folders on the server.
  2418. TraceEnter(TRACE_UPDATE, "CCscUpdate::_PinNewFilesCSCCallback");
  2419. TraceAssert(pSyncData != NULL);
  2420. TraceAssert(pSyncData->pThis != NULL);
  2421. pThis = pSyncData->pThis;
  2422. // Check for Cancel
  2423. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2424. {
  2425. TraceMsg("Cancelling sync operation");
  2426. TraceLeaveValue(CSCPROC_RETURN_ABORT);
  2427. }
  2428. // If this isn't a directory with the user hint flag, keep looking.
  2429. if (eReason != ENUM_REASON_FOLDER_BEGIN ||
  2430. !(dwHintFlags & (FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN)))
  2431. {
  2432. TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
  2433. }
  2434. // If we have a file list and this directory isn't in the list,
  2435. // continue without doing anything here.
  2436. if (pSyncData->pThis->m_pFileList &&
  2437. !pSyncData->pThis->m_pFileList->FileExists(pszName, false))
  2438. {
  2439. TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
  2440. }
  2441. // Ok, we've found a directory with the user hint flag set. Walk
  2442. // this directory on the server, pinning any files that aren't pinned.
  2443. pSyncData->dwPinHints = dwHintFlags;
  2444. _Win32EnumFolder(pszName,
  2445. FALSE,
  2446. _PinNewFilesW32Callback,
  2447. (LPARAM)pSyncData);
  2448. TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
  2449. }
  2450. DWORD WINAPI
  2451. CCscUpdate::_SyncThread(LPVOID pThreadData)
  2452. {
  2453. PSYNCTHREADDATA pSyncData = (PSYNCTHREADDATA)pThreadData;
  2454. PCSCUPDATE pThis;
  2455. HRESULT hrComInit = E_FAIL;
  2456. SYNCMGRPROGRESSITEM spi = {0};
  2457. DWORD dwErr = NOERROR;
  2458. CSCSHARESTATS shareStats;
  2459. CSCGETSTATSINFO si = { SSEF_NONE,
  2460. SSUF_NONE,
  2461. false, // No access info reqd (faster).
  2462. false };
  2463. ULONG cDirtyFiles = 0;
  2464. ULONG cStaleFiles = 0;
  2465. DWORD dwShareStatus = 0;
  2466. BOOL bShareOnline = FALSE;
  2467. DWORD dwConnectionSpeed = 0;
  2468. TraceEnter(TRACE_UPDATE, "CCscUpdate::_SyncThread");
  2469. TraceAssert(pSyncData);
  2470. TraceAssert(pSyncData->pThis);
  2471. TraceAssert(pSyncData->pszShareName && *pSyncData->pszShareName);
  2472. pThis = pSyncData->pThis;
  2473. spi.cbSize = sizeof(spi);
  2474. hrComInit = CoInitialize(NULL);
  2475. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2476. ExitGracefully(dwErr, NOERROR, "Cancelling sync operation");
  2477. // Figure out how many files need updating
  2478. pSyncData->cFilesDone = 0;
  2479. pSyncData->cFilesToSync = 0;
  2480. _GetShareStatisticsForUser(pSyncData->pszShareName, &si, &shareStats);
  2481. // Get share status
  2482. CSCQueryFileStatus(pSyncData->pszShareName, &dwShareStatus, NULL, NULL);
  2483. // The root of a special folder is pinned with a pin count, but
  2484. // not the user-hint flag, so _GetShareStats doesn't count it.
  2485. // We need to count this for some of the checks below.
  2486. // (If the share is manual-cache, these look exactly like the
  2487. // Siemens scenario in 341786, but we want to sync them.)
  2488. if (shareStats.cTotal && pThis->IsSpecialFolderShare(pSyncData->pszShareName))
  2489. {
  2490. shareStats.cPinned++;
  2491. //
  2492. // At logoff, we want to run the FrankAr code on all
  2493. // 'special' folder shares.
  2494. // Customers expect folder redirection of special folders
  2495. // to ensure all contents are cached.
  2496. //
  2497. if (pThis->m_dwSyncFlags & CSC_SYNC_LOGOFF)
  2498. {
  2499. pSyncData->dwSyncStatus |= SDS_SYNC_FORCE_INWARD;
  2500. }
  2501. }
  2502. if (pThis->m_dwSyncFlags & CSC_SYNC_OUT)
  2503. {
  2504. cDirtyFiles = shareStats.cModified;
  2505. //
  2506. // Force the merge code if there are open files, so we are
  2507. // sure to do the open file warning. The danger here is that we
  2508. // don't warn because the share with open files has nothing dirty,
  2509. // but we merge changes on another share and then transition online.
  2510. // We don't want to transition online without warning about open files.
  2511. //
  2512. if (0 == cDirtyFiles)
  2513. {
  2514. if ((FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus) &&
  2515. (FLAG_CSC_SHARE_STATUS_FILES_OPEN & dwShareStatus))
  2516. {
  2517. cDirtyFiles++;
  2518. }
  2519. }
  2520. }
  2521. if (pThis->m_dwSyncFlags & CSC_SYNC_IN_FULL)
  2522. {
  2523. // For full inward sync, always set cStaleFiles to at least 1 to force
  2524. // a call to CSCFillSparseFiles.
  2525. // Also, we get callbacks for each file and folder, even if they
  2526. // are not sparse or stale, so go with cTotal here to make
  2527. // the progress bar look right.
  2528. cStaleFiles = max(shareStats.cTotal, 1);
  2529. }
  2530. else if (pThis->m_dwSyncFlags & CSC_SYNC_IN_QUICK)
  2531. {
  2532. cStaleFiles = shareStats.cSparse;
  2533. // If we're pinning, then it's possible that nothing is sparse yet,
  2534. // but we'll need to call CSCFillSparseFiles.
  2535. if (pThis->m_dwSyncFlags & CSC_SYNC_PINFILES)
  2536. pSyncData->dwSyncStatus |= SDS_SYNC_FORCE_INWARD;
  2537. }
  2538. // Self-host notification callback
  2539. CSCUI_NOTIFYHOOK((CSCH_SyncShare, TEXT("Sync: %1, Dirty: %2!d!, Stale: %3!d!"), pSyncData->pszShareName, cDirtyFiles, cStaleFiles));
  2540. if (dwShareStatus & FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP)
  2541. {
  2542. // Can't call CSCFillSparseFiles when disconnected (it just fails)
  2543. cStaleFiles = 0;
  2544. }
  2545. else if ((dwShareStatus & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_MANUAL_REINT
  2546. && 0 == shareStats.cPinned
  2547. && !(pThis->m_dwSyncFlags & CSC_SYNC_PINFILES))
  2548. {
  2549. // On a manual share, if nothing is pinned (and we aren't pinning)
  2550. // then we prefer not to call CSCFillSparseFiles on the share.
  2551. // Raid #341786
  2552. Trace((TEXT("Manual cache share '%s' has only autocached files"), pSyncData->pszShareName));
  2553. cStaleFiles = 0;
  2554. }
  2555. pSyncData->cFilesToSync = cDirtyFiles + cStaleFiles;
  2556. //
  2557. // At this point, if pSyncData->cFilesToSync is nonzero, then we are doing
  2558. // a sync, and will be calling CSCBeginSynchronization to connect to the
  2559. // share (with prompt for credentials if necessary).
  2560. //
  2561. // If SDS_SYNC_FORCE_INWARD is on, then we are pinning files. We will only
  2562. // call CSCFillSparseFiles is the server is in connected mode, and we will
  2563. // only call CSCBeginSynchronization if pSyncData->cFilesToSync is nonzero
  2564. // (to pin something, you must already have a connection to the share).
  2565. //
  2566. if (0 == pSyncData->cFilesToSync && !(pSyncData->dwSyncStatus & SDS_SYNC_FORCE_INWARD))
  2567. ExitGracefully(dwErr, NOERROR, "Nothing to synchronize");
  2568. // Tell SyncMgr how many files we're updating
  2569. spi.mask = SYNCMGRPROGRESSITEM_STATUSTYPE
  2570. | SYNCMGRPROGRESSITEM_PROGVALUE | SYNCMGRPROGRESSITEM_MAXVALUE;
  2571. spi.dwStatusType = SYNCMGRSTATUS_UPDATING;
  2572. spi.iProgValue = 0;
  2573. spi.iMaxValue = pSyncData->cFilesToSync;
  2574. Trace((TEXT("%d files to sync on %s"), spi.iMaxValue, pSyncData->pszShareName));
  2575. NotifySyncMgr(pSyncData, &spi);
  2576. #if 0
  2577. // RAS isn't notifying RDR before returning success, so
  2578. // do this bogus delay. Remove this when RAS is fixed.
  2579. //
  2580. // This was fixed a few weeks before Windows 2000 RTM.
  2581. // [jeffreys - 1/24/2000]
  2582. //
  2583. if (pSyncData->dwSyncStatus & SDS_SYNC_RAS_CONNECTED)
  2584. {
  2585. //
  2586. // We've just established a RAS connection, but there's a timing
  2587. // problem with RDR. Wait until we can connect to the share, but
  2588. // don't wait longer than RAS_CONNECT_DELAY.
  2589. //
  2590. Trace((TEXT("RAS delay for %s"), pSyncData->pszShareName));
  2591. DWORD dwDelayTime = GetTickCount() + RAS_CONNECT_DELAY;
  2592. #ifdef DEBUG
  2593. int cAttempts = 0;
  2594. #endif
  2595. while (!bShareOnline &&
  2596. GetTickCount() < dwDelayTime &&
  2597. !(pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED))
  2598. {
  2599. WaitForSingleObject(pThis->m_hSyncMutex, INFINITE);
  2600. #ifdef DEBUG
  2601. cAttempts++;
  2602. #endif
  2603. bShareOnline = CSCBeginSynchronization(pSyncData->pszShareName,
  2604. &dwConnectionSpeed,
  2605. &pSyncData->dwCscContext);
  2606. ReleaseMutex(pThis->m_hSyncMutex);
  2607. }
  2608. Trace((TEXT("%s %s after %d attempts (%dms)"),
  2609. pSyncData->pszShareName,
  2610. (bShareOnline ? TEXT("connected") : TEXT("not reachable")),
  2611. cAttempts,
  2612. GetTickCount() - (dwDelayTime - RAS_CONNECT_DELAY)));
  2613. }
  2614. else
  2615. #endif // RAS delay
  2616. if (pSyncData->cFilesToSync)
  2617. {
  2618. //
  2619. // CSCBeginSynchronization makes a net connection to the share
  2620. // using the "interactive" flag. This causes a credential popup
  2621. // if the current user doesn't have access to the share.
  2622. // Use the sync mutex to avoid multiple concurrent popups.
  2623. //
  2624. WaitForSingleObject(pThis->m_hSyncMutex, INFINITE);
  2625. bShareOnline = CSCBeginSynchronization(pSyncData->pszShareName,
  2626. &dwConnectionSpeed,
  2627. &pSyncData->dwCscContext);
  2628. ReleaseMutex(pThis->m_hSyncMutex);
  2629. }
  2630. if (pSyncData->cFilesToSync && !bShareOnline)
  2631. {
  2632. // The share isn't reachable, so there's no point in continuing.
  2633. dwErr = GetLastError();
  2634. if (ERROR_CANCELLED == dwErr)
  2635. {
  2636. // The user cancelled the credential popup
  2637. pSyncData->dwSyncStatus |= SDS_SYNC_CANCELLED;
  2638. ExitGracefully(dwErr, NOERROR, "User cancelled sync");
  2639. }
  2640. LPTSTR pszErr = GetErrorText(dwErr);
  2641. pThis->LogError(pSyncData->ItemID,
  2642. SYNCMGRLOGLEVEL_ERROR,
  2643. IDS_SHARE_CONNECT_ERROR,
  2644. pSyncData->pszShareName,
  2645. pszErr);
  2646. LocalFreeString(&pszErr);
  2647. ExitGracefully(dwErr, dwErr, "Share not reachable");
  2648. }
  2649. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2650. ExitGracefully(dwErr, NOERROR, "Cancelling sync operation");
  2651. // Note the time of this sync
  2652. pThis->SetLastSyncTime(pSyncData->pszShareName);
  2653. // Merge
  2654. if (0 != cDirtyFiles)
  2655. {
  2656. dwErr = pThis->MergeShare(pSyncData);
  2657. if (NOERROR != dwErr)
  2658. {
  2659. LPTSTR pszErr = GetErrorText(dwErr);
  2660. pThis->LogError(pSyncData->ItemID,
  2661. SYNCMGRLOGLEVEL_ERROR,
  2662. IDS_MERGE_SHARE_ERROR,
  2663. pSyncData->pszShareName,
  2664. pszErr);
  2665. LocalFreeString(&pszErr);
  2666. ExitGracefully(dwErr, dwErr, "Aborting due to merge error");
  2667. }
  2668. }
  2669. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2670. ExitGracefully(dwErr, NOERROR, "Cancelling sync operation");
  2671. // Fill
  2672. if (0 != cStaleFiles || (pSyncData->dwSyncStatus & SDS_SYNC_FORCE_INWARD))
  2673. {
  2674. dwErr = pThis->FillShare(pSyncData, shareStats.cPinned, dwConnectionSpeed);
  2675. }
  2676. exit_gracefully:
  2677. // If we called CSCBeginSynchronization and it succeeded,
  2678. // we need to call CSCEndSynchronization.
  2679. if (bShareOnline)
  2680. CSCEndSynchronization(pSyncData->pszShareName, pSyncData->dwCscContext);
  2681. // Tell SyncMgr that we're done (succeeded, failed, or stopped)
  2682. spi.mask = SYNCMGRPROGRESSITEM_STATUSTYPE | SYNCMGRPROGRESSITEM_STATUSTEXT
  2683. | SYNCMGRPROGRESSITEM_PROGVALUE | SYNCMGRPROGRESSITEM_MAXVALUE;
  2684. spi.dwStatusType = SYNCMGRSTATUS_SUCCEEDED; // Assume success
  2685. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2686. spi.dwStatusType = SYNCMGRSTATUS_STOPPED;
  2687. if (NOERROR != dwErr || (pSyncData->dwSyncStatus & SDS_SYNC_ERROR))
  2688. spi.dwStatusType = SYNCMGRSTATUS_FAILED;
  2689. spi.lpcStatusText = L" ";
  2690. spi.iProgValue = spi.iMaxValue = pSyncData->cFilesToSync; // This tells syncmgr that the item is done
  2691. NotifySyncMgr(pSyncData, &spi);
  2692. if ((pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2693. && (pThis->m_dwSyncFlags & CSC_SYNC_PINFILES))
  2694. {
  2695. // We cancelled a pin operation, roll back to the previous state
  2696. CscUnpinFileList(pThis->m_pFileList,
  2697. (pThis->m_dwSyncFlags & CSC_SYNC_PIN_RECURSE),
  2698. (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus),
  2699. pSyncData->pszShareName,
  2700. _UndoProgress,
  2701. (LPARAM)pSyncData);
  2702. }
  2703. // Tell the Update Handler that this thread is exiting
  2704. // This may use OLE to notify SyncMgr that the sync is done,
  2705. // so do this before CoUninitialize.
  2706. Trace((TEXT("%s finished"), pSyncData->pszShareName));
  2707. pThis->SyncThreadCompleted(pSyncData);
  2708. if (SUCCEEDED(hrComInit))
  2709. CoUninitialize();
  2710. delete pSyncData->pUndoExclusionList;
  2711. LocalFree(pSyncData);
  2712. // Release our ref on the object
  2713. // (also release our ref on the DLL)
  2714. pThis->Release();
  2715. TraceLeave();
  2716. FreeLibraryAndExitThread(g_hInstance, dwErr);
  2717. return 0;
  2718. }
  2719. DWORD
  2720. CCscUpdate::MergeShare(PSYNCTHREADDATA pSyncData)
  2721. {
  2722. DWORD dwErr = NOERROR;
  2723. BOOL bMergeResult = TRUE;
  2724. TraceEnter(TRACE_UPDATE, "CCscUpdate::MergeShare");
  2725. // CSCMergeShare fails if another thread (or process) is
  2726. // currently merging. This is because CSCMergeShare uses
  2727. // a drive letter connection to the share to bypass CSC,
  2728. // and we don't want to use up all of the drive letters.
  2729. // So let's protect the call to CSCMergeShare with a mutex
  2730. // (rather than dealing with failure and retrying, etc.)
  2731. WaitForSingleObject(m_hSyncMutex, INFINITE);
  2732. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2733. ExitGracefully(dwErr, NOERROR, "Merge cancelled");
  2734. //
  2735. // It would be nice to skip the open file warning if we knew
  2736. // that the open files were on a "silent folder". The best
  2737. // we can do, though, is detect that the open files are on
  2738. // the same share as a silent folder. There's no guarantee
  2739. // that the open files are not from a different folder on
  2740. // the same share, so we have to show the warning.
  2741. //
  2742. //if (!IsSilentShare(pSyncData->pszShareName))
  2743. {
  2744. DWORD dwShareStatus = 0;
  2745. CSCQueryFileStatus(pSyncData->pszShareName, &dwShareStatus, NULL, NULL);
  2746. if (FLAG_CSC_SHARE_STATUS_FILES_OPEN & dwShareStatus)
  2747. {
  2748. if (CSC_SYNC_MAYBOTHERUSER & m_dwSyncFlags)
  2749. {
  2750. // Only show this warning once per sync (not per thread)
  2751. if (!(CSC_SYNC_OFWARNINGDONE & m_dwSyncFlags))
  2752. {
  2753. m_dwSyncFlags |= CSC_SYNC_OFWARNINGDONE;
  2754. //
  2755. // This dialog we're going to display can use one of
  2756. // two templates.
  2757. //
  2758. // 1. Single user logged on. Tell user to close all files.
  2759. // Dialog provides [OK] and [Cancel] options. User can
  2760. // choose to continue or cancel.
  2761. //
  2762. // 2. Multiple users logged on. Tell user that sync cannot
  2763. // be performed with multiple users logged on. Dialog
  2764. // presents only an [OK] button. However, it's ID is
  2765. // IDCANCEL so pressing it will cause us to stop the
  2766. // merge.
  2767. //
  2768. if (IDOK != OpenFilesWarningDialog())
  2769. {
  2770. TraceMsg("Cancelling sync - user bailed at open file warning");
  2771. SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  2772. }
  2773. }
  2774. // else we already put up the warning on another thread. If the
  2775. // user cancelled, SDS_SYNC_CANCELLED will be set.
  2776. }
  2777. else
  2778. {
  2779. // Don't merge, but continue otherwise.
  2780. LogError(pSyncData->ItemID,
  2781. SYNCMGRLOGLEVEL_WARNING,
  2782. IDS_OPENFILE_MERGE_WARNING,
  2783. pSyncData->pszShareName);
  2784. ExitGracefully(dwErr, NOERROR, "Skipping merge due to open files");
  2785. }
  2786. }
  2787. }
  2788. //
  2789. // Conflict resolution may require stopping and restarting CSCMergeShare,
  2790. // so do this in a loop
  2791. //
  2792. while (!(pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED))
  2793. {
  2794. Trace((TEXT("Calling CSCMergeShare(%s)"), pSyncData->pszShareName));
  2795. pSyncData->dwSyncStatus = SDS_SYNC_OUT;
  2796. bMergeResult = CSCMergeShare(pSyncData->pszShareName,
  2797. CCscUpdate::_CscCallback,
  2798. (DWORD_PTR)pSyncData);
  2799. Trace((TEXT("CSCMergeShare(%s) returned"), pSyncData->pszShareName));
  2800. // Do we need to merge again?
  2801. if (!(SDS_SYNC_RESTART_MERGE & pSyncData->dwSyncStatus))
  2802. break;
  2803. }
  2804. if (!(pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED) && !bMergeResult)
  2805. {
  2806. dwErr = GetLastError();
  2807. if (ERROR_OPERATION_ABORTED == dwErr)
  2808. dwErr = NOERROR;
  2809. }
  2810. exit_gracefully:
  2811. ReleaseMutex(m_hSyncMutex);
  2812. TraceLeaveValue(dwErr);
  2813. }
  2814. DWORD
  2815. CCscUpdate::FillShare(PSYNCTHREADDATA pSyncData, int cPinned, DWORD dwConnectionSpeed)
  2816. {
  2817. DWORD dwErr = NOERROR;
  2818. DWORD dwShareStatus = 0;
  2819. DWORD dwShareHints = 0;
  2820. TraceEnter(TRACE_UPDATE, "CCscUpdate::FillShare");
  2821. CSCQueryFileStatus(pSyncData->pszShareName, &dwShareStatus, NULL, &dwShareHints);
  2822. //
  2823. // At logoff, we want to run the FrankAr code on all
  2824. // 'special' folder shares.
  2825. // Customers expect folder redirection of special folders
  2826. // to ensure all contents are cached.
  2827. //
  2828. if ((m_dwSyncFlags & CSC_SYNC_IN_FULL) ||
  2829. ((m_dwSyncFlags & CSC_SYNC_LOGOFF) && IsSpecialFolderShare(pSyncData->pszShareName)))
  2830. {
  2831. pSyncData->dwSyncStatus = SDS_SYNC_IN_FULL;
  2832. Trace((TEXT("Full sync at %d00 bps"), dwConnectionSpeed));
  2833. //
  2834. // Check the server for new files that should be pinned.
  2835. //
  2836. // We can't do this when disconnected. Also, this is
  2837. // time consuming, so don't do it on a slow connection.
  2838. //
  2839. if (!(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus)
  2840. && cPinned && !_PathIsSlow(dwConnectionSpeed))
  2841. {
  2842. //
  2843. // Look for pinned folders on this share by enumerating
  2844. // in the CSC database. Go out to the server only if/when
  2845. // we find a pinned folder.
  2846. //
  2847. TraceMsg("Running FrankAr code");
  2848. //
  2849. if (CConfig::GetSingleton().AlwaysPinSubFolders())
  2850. {
  2851. //
  2852. // If the "AlwaysPinSubFolders" policy is set, we
  2853. // do a recursive pin. This will cause any content
  2854. // (including folders) of a pinned folder to become pinned.
  2855. //
  2856. pSyncData->pThis->m_dwSyncFlags |= CSC_SYNC_PIN_RECURSE;
  2857. }
  2858. // First check the root folder
  2859. if (_PinNewFilesCSCCallback(pSyncData->pszShareName,
  2860. ENUM_REASON_FOLDER_BEGIN,
  2861. 0,
  2862. dwShareHints,
  2863. 0,
  2864. NULL,
  2865. (LPARAM)pSyncData) == CSCPROC_RETURN_CONTINUE)
  2866. {
  2867. _CSCEnumDatabase(pSyncData->pszShareName,
  2868. TRUE,
  2869. _PinNewFilesCSCCallback,
  2870. (LPARAM)pSyncData);
  2871. }
  2872. TraceMsg("FrankAr code complete");
  2873. }
  2874. }
  2875. else
  2876. {
  2877. pSyncData->dwSyncStatus = SDS_SYNC_IN_QUICK;
  2878. if (m_dwSyncFlags & CSC_SYNC_PINFILES)
  2879. {
  2880. //
  2881. // Enumerate the file list and pin everything, checking with
  2882. // SyncMgr periodically.
  2883. //
  2884. PinFiles(pSyncData);
  2885. }
  2886. }
  2887. if (m_pConflictPinList)
  2888. {
  2889. // Make sure that any files we created because of merge
  2890. // conflicts are pinned.
  2891. PinFiles(pSyncData, TRUE);
  2892. }
  2893. // Can't fill when disconnected
  2894. if (!(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus))
  2895. {
  2896. // Clear the status text and update the max count in case we
  2897. // pinned somthing above
  2898. SYNCMGRPROGRESSITEM spi;
  2899. spi.cbSize = sizeof(spi);
  2900. spi.mask = SYNCMGRPROGRESSITEM_STATUSTEXT | SYNCMGRPROGRESSITEM_MAXVALUE;
  2901. spi.lpcStatusText = L" ";
  2902. spi.iMaxValue = pSyncData->cFilesToSync;
  2903. Trace((TEXT("%d files to sync on %s"), spi.iMaxValue, pSyncData->pszShareName));
  2904. NotifySyncMgr(pSyncData, &spi);
  2905. if (!(pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED))
  2906. {
  2907. Trace((TEXT("Calling CSCFillSparseFiles(%s, %s)"), pSyncData->pszShareName, (pSyncData->dwSyncStatus & SDS_SYNC_IN_FULL) ? TEXT("full") : TEXT("quick")));
  2908. if (!CSCFillSparseFiles(pSyncData->pszShareName,
  2909. !!(pSyncData->dwSyncStatus & SDS_SYNC_IN_FULL),
  2910. CCscUpdate::_CscCallback,
  2911. (DWORD_PTR)pSyncData))
  2912. {
  2913. dwErr = GetLastError();
  2914. if (ERROR_OPERATION_ABORTED == dwErr)
  2915. dwErr = NOERROR;
  2916. }
  2917. Trace((TEXT("CSCFillSparseFiles(%s) complete"), pSyncData->pszShareName));
  2918. }
  2919. }
  2920. else
  2921. {
  2922. Trace((TEXT("Skipping CSCFillSparseFiles(%s) - server is offline"), pSyncData->pszShareName));
  2923. }
  2924. TraceLeaveValue(dwErr);
  2925. }
  2926. void
  2927. CCscUpdate::PinFiles(PSYNCTHREADDATA pSyncData, BOOL bConflictPinList)
  2928. {
  2929. CscFilenameList *pfnl;
  2930. CscFilenameList::HSHARE hShare;
  2931. LPCTSTR pszFile;
  2932. TraceEnter(TRACE_UPDATE, "CCscUpdate::PinFiles");
  2933. TraceAssert((m_dwSyncFlags & CSC_SYNC_PINFILES) || bConflictPinList);
  2934. pfnl = m_pFileList;
  2935. if (bConflictPinList)
  2936. {
  2937. pfnl = m_pConflictPinList;
  2938. }
  2939. if (!pfnl ||
  2940. !pfnl->GetShareHandle(pSyncData->pszShareName, &hShare))
  2941. {
  2942. TraceLeaveVoid();
  2943. }
  2944. CscFilenameList::FileIter fi = pfnl->CreateFileIterator(hShare);
  2945. // Iterate over the filenames associated with the share.
  2946. while (pszFile = fi.Next())
  2947. {
  2948. TCHAR szFullPath[MAX_PATH];
  2949. TCHAR szRelativePath[MAX_PATH];
  2950. WIN32_FIND_DATA fd;
  2951. ULONG cchFile = lstrlen(pszFile) + 1; // include NULL
  2952. // Check for Cancel
  2953. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  2954. break;
  2955. ZeroMemory(&fd, sizeof(fd));
  2956. // Directories have a trailing "\*"
  2957. if (StrChr(pszFile, TEXT('*')))
  2958. {
  2959. // It's a directory. Trim off the "\*"
  2960. cchFile -= 2;
  2961. // When pinning at the share level, pszFile points to "*"
  2962. // and cchFile is now zero.
  2963. }
  2964. szRelativePath[0] = TEXT('\0');
  2965. lstrcpyn(szRelativePath, pszFile, (int)min(cchFile, ARRAYSIZE(szRelativePath)));
  2966. // Build the full path
  2967. PathCombine(szFullPath, pSyncData->pszShareName, szRelativePath);
  2968. // Get attributes and test for existence
  2969. fd.dwFileAttributes = GetFileAttributes(szFullPath);
  2970. if ((DWORD)-1 == fd.dwFileAttributes)
  2971. continue;
  2972. if (S_FALSE == m_NoPinList.IsPinAllowed(szFullPath))
  2973. {
  2974. //
  2975. // Policy says don't pin this file/folder.
  2976. //
  2977. if (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes)
  2978. {
  2979. LogError(pSyncData->ItemID,
  2980. SYNCMGRLOGLEVEL_WARNING,
  2981. IDS_PIN_NOPINFOLDER_POLICY_WARNING,
  2982. szFullPath);
  2983. }
  2984. else
  2985. {
  2986. LogError(pSyncData->ItemID,
  2987. IDS_PIN_NOPINFILE_POLICY_WARNING,
  2988. szFullPath,
  2989. NOERROR,
  2990. SYNCMGRLOGLEVEL_WARNING);
  2991. }
  2992. continue;
  2993. }
  2994. pszFile = PathFindFileName(szFullPath);
  2995. lstrcpyn(fd.cFileName, pszFile ? pszFile : szFullPath, ARRAYSIZE(fd.cFileName));
  2996. // Check for EFS
  2997. BOOL bIsEFSFile;
  2998. bIsEFSFile = (FILE_ATTRIBUTE_ENCRYPTED & fd.dwFileAttributes) &&
  2999. !(FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes);
  3000. if (bIsEFSFile && SkipEFSPin(pSyncData, szFullPath))
  3001. continue;
  3002. if (pSyncData->dwSyncStatus & SDS_SYNC_CANCELLED)
  3003. break;
  3004. // Pin it
  3005. pSyncData->dwPinHints = FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER;
  3006. if (CSCPinFile(szFullPath, pSyncData->dwPinHints, NULL, NULL, NULL))
  3007. {
  3008. if (bConflictPinList && m_pFileList)
  3009. m_pFileList->AddFile(szFullPath, !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
  3010. pSyncData->cFilesToSync++;
  3011. ShellChangeNotify(szFullPath, &fd, FALSE);
  3012. if (bIsEFSFile && !m_bCacheIsEncrypted)
  3013. {
  3014. LogError(pSyncData->ItemID,
  3015. IDS_PIN_ENCRYPT_WARNING,
  3016. szFullPath,
  3017. NOERROR,
  3018. SYNCMGRLOGLEVEL_WARNING);
  3019. }
  3020. }
  3021. else
  3022. {
  3023. DWORD dwError = GetLastError();
  3024. UINT idsError = GetErrorFormat(dwError);
  3025. if (IDS_SHARE_CONNECT_ERROR == idsError)
  3026. {
  3027. LPTSTR pszErr = GetErrorText(dwError);
  3028. //
  3029. // Special-case the "can't connect to share" error.
  3030. // Display only the share name in the error message
  3031. // and abort the pinning of this share.
  3032. //
  3033. LogError(pSyncData->ItemID,
  3034. SYNCMGRLOGLEVEL_ERROR,
  3035. idsError,
  3036. pSyncData->pszShareName,
  3037. pszErr ? pszErr : TEXT(""));
  3038. LocalFreeString(&pszErr);
  3039. pSyncData->dwSyncStatus |= SDS_SYNC_CANCELLED;
  3040. }
  3041. else
  3042. {
  3043. DWORD dwSyncMgrLogLevel = SYNCMGRLOGLEVEL_ERROR;
  3044. if (ERROR_INVALID_NAME == dwError)
  3045. {
  3046. //
  3047. // File type is in the exclusion list.
  3048. // This is a warning, not an error.
  3049. //
  3050. dwSyncMgrLogLevel = SYNCMGRLOGLEVEL_WARNING;
  3051. }
  3052. LogError(pSyncData->ItemID,
  3053. IDS_PIN_FILE_ERROR,
  3054. szFullPath,
  3055. dwError,
  3056. dwSyncMgrLogLevel);
  3057. }
  3058. pSyncData->dwSyncStatus |= SDS_SYNC_ERROR;
  3059. }
  3060. // If it's a directory, pin its contents
  3061. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  3062. {
  3063. _Win32EnumFolder(szFullPath,
  3064. !bConflictPinList && ShouldPinRecurse(szFullPath),
  3065. CCscUpdate::_PinNewFilesW32Callback,
  3066. (LPARAM)pSyncData);
  3067. }
  3068. }
  3069. // Flush the shell notify queue
  3070. ShellChangeNotify(NULL, TRUE);
  3071. TraceLeaveVoid();
  3072. }
  3073. void
  3074. CCscUpdate::NotifyUndo(PSYNCTHREADDATA pSyncData, LPCTSTR pszName)
  3075. {
  3076. LPTSTR pszMsg;
  3077. SYNCMGRPROGRESSITEM spi;
  3078. spi.cbSize = sizeof(spi);
  3079. spi.mask = SYNCMGRPROGRESSITEM_STATUSTEXT;
  3080. spi.lpcStatusText = L" ";
  3081. // Skip the share name
  3082. if (PathIsPrefix(pSyncData->pszShareName, pszName))
  3083. {
  3084. pszName += lstrlen(pSyncData->pszShareName);
  3085. if (*pszName == TEXT('\\'))
  3086. pszName++;
  3087. }
  3088. LPCTSTR pszFile = PathFindFileName(pszName);
  3089. TCHAR szPath[MAX_PATH] = TEXT("\\");
  3090. if (*pszName)
  3091. lstrcpyn(szPath, pszName, (int)min(ARRAYSIZE(szPath),(int)(pszFile-pszName)));
  3092. // If we still have a name, build a string like
  3093. // "undo: dir\foo.txt" to display in SyncMgr
  3094. if (FormatStringID(&pszMsg, g_hInstance, IDS_UNDO_SCAN, pszFile, szPath))
  3095. {
  3096. USES_CONVERSION;
  3097. spi.lpcStatusText = T2CW(pszMsg);
  3098. }
  3099. NotifySyncMgr(pSyncData, &spi);
  3100. LocalFreeString(&pszMsg);
  3101. }
  3102. DWORD WINAPI
  3103. CCscUpdate::_UndoProgress(LPCTSTR pszItem, LPARAM lpContext)
  3104. {
  3105. PSYNCTHREADDATA pSyncData = reinterpret_cast<PSYNCTHREADDATA>(lpContext);
  3106. if (pSyncData->pUndoExclusionList &&
  3107. pSyncData->pUndoExclusionList->FileExists(pszItem))
  3108. {
  3109. return CSCPROC_RETURN_SKIP;
  3110. }
  3111. // Update SyncMgr
  3112. pSyncData->pThis->NotifyUndo(pSyncData, pszItem);
  3113. return CSCPROC_RETURN_CONTINUE;
  3114. }
  3115. //
  3116. // The user's response to the "confirm pin encrypted" dialog is encoded
  3117. // to fit into the return value from EndDialog. The PINEFS_XXXXX macros
  3118. // describe this encoding.
  3119. //
  3120. // Bits 0 and 1 represent the user's [Yes][No][Cancel] choice.
  3121. //
  3122. #define PINEFS_YES 0x00000001
  3123. #define PINEFS_NO 0x00000002
  3124. #define PINEFS_CANCEL 0x00000003
  3125. #define PINEFS_YNC_MASK 0x00000003
  3126. //
  3127. // Bit 31 indicates if the user checked the "Apply to all" checkbox.
  3128. //
  3129. #define PINEFS_APPLYTOALL 0x80000000
  3130. //
  3131. // Convenience macros for indicating yes-to-all and no-to-all.
  3132. //
  3133. #define PINEFS_NO_TOALL (PINEFS_NO | PINEFS_APPLYTOALL)
  3134. #define PINEFS_YES_TOALL (PINEFS_YES | PINEFS_APPLYTOALL)
  3135. //
  3136. // Returns (by way of EndDialog) one of the PINEFS_XXXXXX codes.
  3137. //
  3138. // PINEFS_YES - Pin this file but ask again on the next.
  3139. // PINEFS_YES_TOALL - Pin this file and all encrypted files encountered.
  3140. // PINEFS_NO - Don't pin this file but ask again on the next.
  3141. // PINEFS_NO_TOALL - Don't pin this file nor any other encrypted files.
  3142. // PINEFS_CANCEL - Don't pin this file. Cancel entire operation.
  3143. //
  3144. INT_PTR CALLBACK
  3145. ConfirmEFSPinProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3146. {
  3147. INT_PTR nResult = 0;
  3148. switch (uMsg)
  3149. {
  3150. case WM_INITDIALOG:
  3151. {
  3152. LPTSTR pszMsg = NULL;
  3153. LPCTSTR pszFile = PathFindFileName((LPCTSTR)lParam);
  3154. FormatStringID(&pszMsg, g_hInstance, IDS_FMT_PIN_EFS_MSG, pszFile);
  3155. if (pszMsg)
  3156. {
  3157. SetDlgItemText(hDlg, IDC_EFS_MSG, pszMsg);
  3158. LocalFree(pszMsg);
  3159. }
  3160. else
  3161. {
  3162. //
  3163. // Let's be safe. On failure we won't pin an encrypted file
  3164. // to a non-encrypted cache.
  3165. //
  3166. EndDialog(hDlg, PINEFS_NO_TOALL);
  3167. }
  3168. }
  3169. nResult = TRUE;
  3170. break;
  3171. case WM_COMMAND:
  3172. switch (LOWORD(wParam))
  3173. {
  3174. case IDYES:
  3175. EndDialog(hDlg, BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_PINEFS_APPLYTOALL) ? PINEFS_YES_TOALL : PINEFS_YES);
  3176. nResult = TRUE;
  3177. break;
  3178. case IDNO:
  3179. EndDialog(hDlg, BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_PINEFS_APPLYTOALL) ? PINEFS_NO_TOALL : PINEFS_NO);
  3180. nResult = TRUE;
  3181. break;
  3182. case IDCANCEL:
  3183. EndDialog(hDlg, PINEFS_CANCEL);
  3184. nResult = TRUE;
  3185. break;
  3186. default:
  3187. break;
  3188. }
  3189. break;
  3190. }
  3191. return nResult;
  3192. }
  3193. BOOL
  3194. CCscUpdate::SkipEFSPin(PSYNCTHREADDATA pSyncData, LPCTSTR pszItem)
  3195. {
  3196. BOOL bSkip = FALSE;
  3197. if (!m_bCacheIsEncrypted)
  3198. {
  3199. int iResult;
  3200. EnterCriticalSection(&m_csThreadList);
  3201. if ((CSC_SYNC_EFS_PIN_NONE & m_dwSyncFlags) ||
  3202. !(CSC_SYNC_MAYBOTHERUSER & m_dwSyncFlags))
  3203. {
  3204. iResult = PINEFS_NO;
  3205. }
  3206. else if (CSC_SYNC_EFS_PIN_ALL & m_dwSyncFlags)
  3207. {
  3208. iResult = PINEFS_YES;
  3209. }
  3210. else
  3211. {
  3212. // Suspend the other sync threads
  3213. SetSyncThreadStatus(SyncPause, pSyncData->ItemID);
  3214. iResult = (int)DialogBoxParam(g_hInstance,
  3215. MAKEINTRESOURCE(IDD_CONFIRM_PIN_EFS),
  3216. m_hwndDlgParent,
  3217. ConfirmEFSPinProc,
  3218. (LPARAM)pszItem);
  3219. if (PINEFS_APPLYTOALL & iResult)
  3220. {
  3221. //
  3222. // User checked the "apply to all" checkbox.
  3223. // Persist a [Yes][No] button selection.
  3224. //
  3225. if (PINEFS_NO == (PINEFS_YNC_MASK & iResult))
  3226. {
  3227. m_dwSyncFlags |= CSC_SYNC_EFS_PIN_NONE;
  3228. }
  3229. else if (PINEFS_YES == (PINEFS_YNC_MASK & iResult))
  3230. {
  3231. m_dwSyncFlags |= CSC_SYNC_EFS_PIN_ALL;
  3232. }
  3233. }
  3234. // Resume syncing
  3235. SetSyncThreadStatus(SyncResume, pSyncData->ItemID);
  3236. }
  3237. LeaveCriticalSection(&m_csThreadList);
  3238. switch (PINEFS_YNC_MASK & iResult)
  3239. {
  3240. default:
  3241. case PINEFS_NO:
  3242. bSkip = TRUE;
  3243. break;
  3244. case PINEFS_YES:
  3245. // continue
  3246. break;
  3247. case PINEFS_CANCEL:
  3248. // stop all threads
  3249. SetItemStatus(GUID_NULL, SYNCMGRSTATUS_STOPPED);
  3250. break;
  3251. }
  3252. }
  3253. return bSkip;
  3254. }
  3255. HRESULT
  3256. CCscUpdate::SetSyncThreadStatus(eSetSyncStatus status, REFGUID rItemID)
  3257. {
  3258. // Assume success here. If we don't find the thread,
  3259. // it's probably already finished.
  3260. HRESULT hr = S_OK;
  3261. BOOL bOneItem;
  3262. TraceEnter(TRACE_UPDATE, "CCscUpdate::SetSyncThreadStatus");
  3263. bOneItem = (SyncStop == status && !IsEqualGUID(rItemID, GUID_NULL));
  3264. EnterCriticalSection(&m_csThreadList);
  3265. if (NULL != m_hSyncThreads)
  3266. {
  3267. int cItems = DPA_GetPtrCount(m_hSyncThreads);
  3268. SYNCMGRPROGRESSITEM spi = {0};
  3269. DWORD (WINAPI *pfnStartStop)(HANDLE);
  3270. pfnStartStop = ResumeThread;
  3271. spi.cbSize = sizeof(spi);
  3272. spi.mask = SYNCMGRPROGRESSITEM_STATUSTYPE | SYNCMGRPROGRESSITEM_STATUSTEXT;
  3273. spi.lpcStatusText = L" ";
  3274. spi.dwStatusType = SYNCMGRSTATUS_UPDATING;
  3275. if (SyncPause == status)
  3276. {
  3277. spi.dwStatusType = SYNCMGRSTATUS_PAUSED;
  3278. pfnStartStop = SuspendThread;
  3279. }
  3280. while (cItems > 0)
  3281. {
  3282. PSYNCTHREADDATA pSyncData;
  3283. --cItems;
  3284. pSyncData = (PSYNCTHREADDATA)DPA_FastGetPtr(m_hSyncThreads, cItems);
  3285. TraceAssert(NULL != pSyncData);
  3286. if (SyncStop == status)
  3287. {
  3288. // Tell the thread to abort
  3289. if (!bOneItem || IsEqualGUID(rItemID, pSyncData->ItemID))
  3290. {
  3291. pSyncData->dwSyncStatus |= SDS_SYNC_CANCELLED;
  3292. if (bOneItem)
  3293. break;
  3294. }
  3295. }
  3296. else
  3297. {
  3298. // Suspend or resume the thread if it's not the current thread
  3299. if (!IsEqualGUID(rItemID, pSyncData->ItemID))
  3300. (*pfnStartStop)(pSyncData->hThread);
  3301. m_pSyncMgrCB->Progress(pSyncData->ItemID, &spi);
  3302. }
  3303. }
  3304. }
  3305. LeaveCriticalSection(&m_csThreadList);
  3306. TraceLeaveResult(hr);
  3307. }
  3308. HRESULT
  3309. CCscUpdate::GetSilentFolderList(void)
  3310. {
  3311. HRESULT hr = S_OK;
  3312. delete m_pSilentFolderList;
  3313. m_pSilentFolderList = new CscFilenameList;
  3314. delete m_pSpecialFolderList;
  3315. m_pSpecialFolderList = new CscFilenameList;
  3316. if (NULL == m_pSilentFolderList || NULL == m_pSpecialFolderList)
  3317. {
  3318. delete m_pSilentFolderList;
  3319. m_pSilentFolderList = NULL;
  3320. delete m_pSpecialFolderList;
  3321. m_pSpecialFolderList = NULL;
  3322. hr = E_OUTOFMEMORY;
  3323. }
  3324. else
  3325. {
  3326. BuildSilentFolderList(m_pSilentFolderList, m_pSpecialFolderList);
  3327. if (0 == m_pSilentFolderList->GetShareCount())
  3328. {
  3329. delete m_pSilentFolderList;
  3330. m_pSilentFolderList = NULL;
  3331. }
  3332. if (0 == m_pSpecialFolderList->GetShareCount())
  3333. {
  3334. delete m_pSpecialFolderList;
  3335. m_pSpecialFolderList = NULL;
  3336. }
  3337. }
  3338. return hr;
  3339. }
  3340. void
  3341. BuildSilentFolderList(CscFilenameList *pfnlSilentFolders,
  3342. CscFilenameList *pfnlSpecialFolders)
  3343. {
  3344. //
  3345. // We will silently handle sync conflicts in any of the folders
  3346. // below that have a '1' after them.
  3347. //
  3348. // If we get complaints about conflicts in folders that we
  3349. // think we can handle silently and safely, add them.
  3350. //
  3351. // Note that CSIDL_PERSONAL (MyDocs) and CSIDL_MYPICTURES
  3352. // should probably never be silent, since the user
  3353. // interacts with them directly.
  3354. //
  3355. // This list corresponds to the list of shell folders that may
  3356. // be redirected. See also
  3357. // HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
  3358. //
  3359. static const int s_csidlFolders[][2] =
  3360. {
  3361. { CSIDL_PROGRAMS, 0 },
  3362. { CSIDL_PERSONAL, 0 },
  3363. { CSIDL_FAVORITES, 0 },
  3364. { CSIDL_STARTUP, 0 },
  3365. { CSIDL_RECENT, 1 },
  3366. { CSIDL_SENDTO, 0 },
  3367. { CSIDL_STARTMENU, 1 },
  3368. { CSIDL_DESKTOPDIRECTORY, 0 },
  3369. { CSIDL_NETHOOD, 0 },
  3370. { CSIDL_TEMPLATES, 0 },
  3371. { CSIDL_APPDATA, 1 },
  3372. { CSIDL_PRINTHOOD, 0 },
  3373. { CSIDL_MYPICTURES, 0 },
  3374. { CSIDL_PROFILE, 1 },
  3375. { CSIDL_ADMINTOOLS, 0 },
  3376. };
  3377. TCHAR szPath[MAX_PATH];
  3378. for (int i = 0; i < ARRAYSIZE(s_csidlFolders); i++)
  3379. {
  3380. if (SHGetSpecialFolderPath(NULL,
  3381. szPath,
  3382. s_csidlFolders[i][0] | CSIDL_FLAG_DONT_VERIFY,
  3383. FALSE))
  3384. {
  3385. // We only want UNC net paths
  3386. LPTSTR pszUNC = NULL;
  3387. GetRemotePath(szPath, &pszUNC);
  3388. if (!pszUNC)
  3389. continue;
  3390. if (s_csidlFolders[i][1])
  3391. {
  3392. if (pfnlSilentFolders)
  3393. pfnlSilentFolders->AddFile(pszUNC, true);
  3394. }
  3395. else
  3396. {
  3397. if (pfnlSpecialFolders)
  3398. pfnlSpecialFolders->AddFile(pszUNC, true);
  3399. }
  3400. LocalFreeString(&pszUNC);
  3401. }
  3402. }
  3403. }
  3404. ///////////////////////////////////////////////////////////////////////////////
  3405. // //
  3406. // SyncMgr integration (ISyncMgrEnumItems) //
  3407. // //
  3408. ///////////////////////////////////////////////////////////////////////////////
  3409. CUpdateEnumerator::CUpdateEnumerator(PCSCUPDATE pUpdate)
  3410. : m_cRef(1),
  3411. m_pUpdate(pUpdate),
  3412. m_hFind(INVALID_HANDLE_VALUE),
  3413. m_bEnumFileSelection(FALSE),
  3414. m_cCheckedItemsEnumerated(0)
  3415. {
  3416. DllAddRef();
  3417. if (m_pUpdate)
  3418. {
  3419. m_pUpdate->AddRef();
  3420. if (m_pUpdate->m_pFileList)
  3421. {
  3422. m_bEnumFileSelection = TRUE;
  3423. m_SelectionIterator = m_pUpdate->m_pFileList->CreateShareIterator();
  3424. }
  3425. }
  3426. }
  3427. CUpdateEnumerator::~CUpdateEnumerator()
  3428. {
  3429. if (m_hFind != INVALID_HANDLE_VALUE)
  3430. CSCFindClose(m_hFind);
  3431. DoRelease(m_pUpdate);
  3432. DllRelease();
  3433. }
  3434. STDMETHODIMP CUpdateEnumerator::QueryInterface(REFIID riid, void **ppv)
  3435. {
  3436. static const QITAB qit[] =
  3437. {
  3438. QITABENT(CUpdateEnumerator, ISyncMgrEnumItems),
  3439. { 0 },
  3440. };
  3441. return QISearch(this, qit, riid, ppv);
  3442. }
  3443. STDMETHODIMP_(ULONG) CUpdateEnumerator::AddRef()
  3444. {
  3445. return InterlockedIncrement(&m_cRef);
  3446. }
  3447. STDMETHODIMP_(ULONG) CUpdateEnumerator::Release()
  3448. {
  3449. if (InterlockedDecrement(&m_cRef))
  3450. return m_cRef;
  3451. delete this;
  3452. return 0;
  3453. }
  3454. STDMETHODIMP
  3455. CUpdateEnumerator::Next(ULONG celt, LPSYNCMGRITEM rgelt, PULONG pceltFetched)
  3456. {
  3457. HRESULT hr = S_OK;
  3458. ULONG cFetched = 0;
  3459. LPSYNCMGRITEM pItem = rgelt;
  3460. WIN32_FIND_DATA fd = {0};
  3461. DWORD dwShareStatus = 0;
  3462. DWORD dwSyncFlags;
  3463. CscFilenameList::HSHARE hShare;
  3464. LPCTSTR pszShareName = NULL;
  3465. TraceEnter(TRACE_UPDATE, "CUpdateEnumerator::Next");
  3466. TraceAssert(m_pUpdate != NULL);
  3467. if (NULL == rgelt)
  3468. TraceLeaveResult(E_INVALIDARG);
  3469. dwSyncFlags = m_pUpdate->m_dwSyncFlags;
  3470. while (cFetched < celt)
  3471. {
  3472. CSCEntry *pShareEntry;
  3473. CSCSHARESTATS shareStats;
  3474. CSCGETSTATSINFO si = { SSEF_NONE,
  3475. SSUF_TOTAL | SSUF_PINNED | SSUF_MODIFIED | SSUF_SPARSE | SSUF_DIRS,
  3476. false,
  3477. false };
  3478. if (m_bEnumFileSelection)
  3479. {
  3480. if (!m_SelectionIterator.Next(&hShare))
  3481. break;
  3482. pszShareName = m_pUpdate->m_pFileList->GetShareName(hShare);
  3483. CSCQueryFileStatus(pszShareName, &dwShareStatus, NULL, NULL);
  3484. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  3485. lstrcpyn(fd.cFileName, pszShareName, ARRAYSIZE(fd.cFileName));
  3486. }
  3487. else
  3488. {
  3489. if (m_hFind == INVALID_HANDLE_VALUE)
  3490. {
  3491. m_hFind = CacheFindFirst(NULL, &fd, &dwShareStatus, NULL, NULL, NULL);
  3492. if (m_hFind == INVALID_HANDLE_VALUE)
  3493. {
  3494. // The database is empty, so there's nothing to enumerate..
  3495. break;
  3496. }
  3497. pszShareName = fd.cFileName;
  3498. }
  3499. else if (CacheFindNext(m_hFind, &fd, &dwShareStatus, NULL, NULL, NULL))
  3500. {
  3501. pszShareName = fd.cFileName;
  3502. }
  3503. else
  3504. break;
  3505. }
  3506. TraceAssert(pszShareName);
  3507. //
  3508. // This was proposed as a fix for part of 383011. However,
  3509. // that bug only applies in a multi-user scenario, and if there
  3510. // are more than 3 users using the machine, this would cause a
  3511. // user whose SID had been expelled from the CSC database to not
  3512. // be able to sync a share where they do indeed have access.
  3513. //
  3514. // // If the current user has no access to the share, don't enumerate it.
  3515. // if (!(CscAccessUser(dwShareStatus) || CscAccessGuest(dwShareStatus)))
  3516. // continue;
  3517. // Count the # of pinned files, sparse files, etc.
  3518. _GetShareStatisticsForUser(pszShareName, &si, &shareStats);
  3519. // The root of a special folder is pinned with a pin count, but
  3520. // not the user-hint flag, so _GetShareStats doesn't count it.
  3521. // We need to count this for some of the checks below.
  3522. // (If the share is manual-cache, these look exactly like the
  3523. // Siemens scenario in 341786, but we want to sync them.)
  3524. if (shareStats.cTotal && m_pUpdate->IsSpecialFolderShare(pszShareName))
  3525. {
  3526. shareStats.cPinned++;
  3527. if (dwSyncFlags & CSC_SYNC_LOGOFF)
  3528. {
  3529. //
  3530. // At logoff, we want to run the FrankAr code on all
  3531. // 'special' folder shares.
  3532. // Customers expect folder redirection of special folders
  3533. // to ensure all contents are cached.
  3534. //
  3535. dwSyncFlags |= CSC_SYNC_IN_FULL;
  3536. }
  3537. }
  3538. // If we're pinning, then even if nothing is sparse now,
  3539. // there will be sparse files after we pin them.
  3540. if (dwSyncFlags & CSC_SYNC_PINFILES)
  3541. {
  3542. shareStats.cSparse++;
  3543. shareStats.cTotal++;
  3544. }
  3545. // If there's nothing cached on this share, then don't even
  3546. // enumerate it to SyncMgr. This avoids listing extra junk
  3547. // in SyncMgr.
  3548. if ((0 == shareStats.cTotal) ||
  3549. (shareStats.cTotal == shareStats.cDirs && 0 == shareStats.cPinned))
  3550. {
  3551. // Either there is nothing cached for this share, or the only
  3552. // things found were unpinned dirs (no files, no pinned dirs).
  3553. // The second case can happen if you delete files from the viewer,
  3554. // in which case you think you deleted everything but the viewer
  3555. // doesn't show directories, so they weren't deleted.
  3556. Trace((TEXT("Nothing cached on %s, not enumerating"), pszShareName));
  3557. continue;
  3558. }
  3559. if ((dwShareStatus & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_NO_CACHING)
  3560. {
  3561. //
  3562. // Don't enumerate "no-cache" shares if there's nothing to merge.
  3563. //
  3564. // These can exist in the cache if the share was previously
  3565. // cacheable, but has since been changed to "no caching".
  3566. //
  3567. // If there's something to merge, we should still sync it to
  3568. // get everything squared away.
  3569. //
  3570. if (!((dwSyncFlags & CSC_SYNC_OUT) && (shareStats.cModified)))
  3571. {
  3572. Trace((TEXT("Not enumerating no-cache share %s"), pszShareName));
  3573. continue;
  3574. }
  3575. Trace((TEXT("Enumerating no-cache share %s with offline changes."), pszShareName));
  3576. }
  3577. // Explorer has shut down by the time we do a logoff sync. Hide the
  3578. // Properties button at logoff so we don't end up restarting Explorer.
  3579. pItem->dwFlags = (dwSyncFlags & CSC_SYNC_LOGOFF) ? 0 : SYNCMGRITEM_HASPROPERTIES;
  3580. if ((dwShareStatus & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_MANUAL_REINT
  3581. && 0 == shareStats.cPinned
  3582. && !(dwSyncFlags & CSC_SYNC_PINFILES))
  3583. {
  3584. // On a manual share, if nothing is pinned (and we aren't pinning)
  3585. // then we don't want to call CSCFillSparseFiles on the share.
  3586. // Raid #341786
  3587. Trace((TEXT("Manual cache share '%s' has only autocached files"), pszShareName));
  3588. // However, if there is something to merge, then we need to sync.
  3589. if (!((dwSyncFlags & CSC_SYNC_OUT) && shareStats.cModified))
  3590. {
  3591. Trace((TEXT("Not enumerating manual-cache share %s"), pszShareName));
  3592. continue;
  3593. }
  3594. // There is something to merge, so enumerate the share but
  3595. // tell SyncMgr that it's temporary so it doesn't save state
  3596. // for this share.
  3597. pItem->dwFlags |= SYNCMGRITEM_TEMPORARY;
  3598. }
  3599. //
  3600. // In some circumstances, we may want to merge even if there
  3601. // are no modified files, in order to show the open files warning.
  3602. //
  3603. // See comments in CCscUpdate::_SyncThread
  3604. //
  3605. if (0 == shareStats.cModified)
  3606. {
  3607. if ((FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & dwShareStatus) &&
  3608. (FLAG_CSC_SHARE_STATUS_FILES_OPEN & dwShareStatus))
  3609. {
  3610. shareStats.cModified++;
  3611. }
  3612. }
  3613. // Enumerate this share
  3614. cFetched++;
  3615. // Get existing share entry or create a new one
  3616. pShareEntry = m_pUpdate->m_ShareLog.Add(pszShareName);
  3617. if (!pShareEntry)
  3618. TraceLeaveResult(E_OUTOFMEMORY);
  3619. pItem->cbSize = sizeof(SYNCMGRITEM);
  3620. pItem->ItemID = pShareEntry->Guid();
  3621. pItem->hIcon = g_hCscIcon;
  3622. // SYNCMGRITEM_TEMPORARY causes items to not show up in
  3623. // SyncMgr's logon/logoff settings page. Raid #237288
  3624. //if (0 == shareStats.cPinned)
  3625. // pItem->dwFlags |= SYNCMGRITEM_TEMPORARY;
  3626. if (ERROR_SUCCESS == m_pUpdate->GetLastSyncTime(pszShareName, &pItem->ftLastUpdate))
  3627. pItem->dwFlags |= SYNCMGRITEM_LASTUPDATETIME;
  3628. //
  3629. // Determine whether this share needs syncing.
  3630. //
  3631. // At settings time, assume everything needs syncing (check everything)
  3632. //
  3633. // If outbound, shares with modified files are checked
  3634. // If inbound (full), shares with sparse or pinned files are checked
  3635. // If inbound (quick), shares with sparse files are checked
  3636. //
  3637. // Anything else doesn't need to be sync'ed at this time (unchecked)
  3638. //
  3639. pItem->dwItemState = SYNCMGRITEMSTATE_CHECKED;
  3640. if (!(dwSyncFlags & CSC_SYNC_SETTINGS) &&
  3641. !((dwSyncFlags & CSC_SYNC_OUT) && shareStats.cModified) &&
  3642. !((dwSyncFlags & CSC_SYNC_IN_FULL) && shareStats.cTotal ) &&
  3643. !((dwSyncFlags & CSC_SYNC_IN_QUICK) && shareStats.cSparse ))
  3644. {
  3645. pItem->dwItemState = SYNCMGRITEMSTATE_UNCHECKED;
  3646. }
  3647. // Get friendly share name here
  3648. LPITEMIDLIST pidl = NULL;
  3649. SHFILEINFO sfi = {0};
  3650. if (SUCCEEDED(SHSimpleIDListFromFindData(pszShareName, &fd, &pidl)))
  3651. {
  3652. SHGetFileInfo((LPCTSTR)pidl,
  3653. 0,
  3654. &sfi,
  3655. sizeof(sfi),
  3656. SHGFI_PIDL | SHGFI_DISPLAYNAME);
  3657. SHFree(pidl);
  3658. }
  3659. if (TEXT('\0') != sfi.szDisplayName[0])
  3660. pszShareName = sfi.szDisplayName;
  3661. SHTCharToUnicode((LPTSTR)pszShareName, pItem->wszItemName, ARRAYSIZE(pItem->wszItemName));
  3662. if (SYNCMGRITEMSTATE_CHECKED == pItem->dwItemState)
  3663. {
  3664. m_cCheckedItemsEnumerated++;
  3665. Trace((TEXT("Enumerating %s, checked"), pszShareName));
  3666. }
  3667. else
  3668. {
  3669. Trace((TEXT("Enumerating %s, unchecked"), pszShareName));
  3670. }
  3671. pItem++;
  3672. }
  3673. if (pceltFetched)
  3674. *pceltFetched = cFetched;
  3675. if (cFetched != celt)
  3676. hr = S_FALSE;
  3677. if ((S_FALSE == hr) &&
  3678. 0 == m_cCheckedItemsEnumerated &&
  3679. (CSC_SYNC_SHOWUI_ALWAYS & dwSyncFlags))
  3680. {
  3681. //
  3682. // Special-case where we're synching nothing but still
  3683. // want to display SyncMgr progress UI. We enumerate a
  3684. // special string rather than a share name for display in
  3685. // the status UI. Force hr == S_OK so the caller will accept
  3686. // this "dummy" item. Next() will be called once more but
  3687. // m_cCheckedItemsEnumerated will be 1 so this block won't be
  3688. // entered and we'll return S_FALSE indicating the end of the
  3689. // enumeration.
  3690. //
  3691. pItem->cbSize = sizeof(SYNCMGRITEM);
  3692. pItem->hIcon = g_hCscIcon;
  3693. pItem->dwFlags = 0;
  3694. pItem->dwItemState = SYNCMGRITEMSTATE_CHECKED;
  3695. pItem->ItemID = GUID_CscNullSyncItem;
  3696. UINT idString = IDS_NULLSYNC_ITEMNAME;
  3697. if ((CSC_SYNC_OUT & dwSyncFlags) &&
  3698. !((CSC_SYNC_IN_QUICK | CSC_SYNC_IN_FULL) & dwSyncFlags))
  3699. {
  3700. // Use different text if we are only merging
  3701. idString = IDS_NULLMERGE_ITEMNAME;
  3702. }
  3703. LoadStringW(g_hInstance,
  3704. idString,
  3705. pItem->wszItemName,
  3706. ARRAYSIZE(pItem->wszItemName));
  3707. m_cCheckedItemsEnumerated = 1;
  3708. TraceMsg("Enumerating NULL item");
  3709. hr = S_OK;
  3710. }
  3711. TraceLeaveResult(hr);
  3712. }
  3713. STDMETHODIMP
  3714. CUpdateEnumerator::Skip(ULONG celt)
  3715. {
  3716. return Next(celt, NULL, NULL);
  3717. }
  3718. STDMETHODIMP
  3719. CUpdateEnumerator::Reset()
  3720. {
  3721. m_cCheckedItemsEnumerated = 0;
  3722. if (m_bEnumFileSelection)
  3723. {
  3724. m_SelectionIterator.Reset();
  3725. }
  3726. else if (m_hFind != INVALID_HANDLE_VALUE)
  3727. {
  3728. CSCFindClose(m_hFind);
  3729. m_hFind = INVALID_HANDLE_VALUE;
  3730. }
  3731. return S_OK;
  3732. }
  3733. STDMETHODIMP
  3734. CUpdateEnumerator::Clone(LPSYNCMGRENUMITEMS *ppenum)
  3735. {
  3736. return E_NOTIMPL;
  3737. }