Leaked source code of windows server 2003
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.

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