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

1180 lines
32 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 2000
  6. //
  7. // File: cscpin.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <stdio.h>
  13. #include "cscpin.h"
  14. #include "console.h"
  15. #include "error.h"
  16. #include "exitcode.h"
  17. #include "listfile.h"
  18. #include "print.h"
  19. #include "strings.h"
  20. //-----------------------------------------------------------------------------
  21. // CCscPinItem
  22. //
  23. // This class represents a single item being pinned or unpinned.
  24. // It contains all of the knowledge of how to pin and unpin a file. The
  25. // CCscPin class coordinates the pinning and unpinning of the entire set
  26. // of files.
  27. //-----------------------------------------------------------------------------
  28. class CCscPinItem
  29. {
  30. public:
  31. CCscPinItem(LPCWSTR pszFile,
  32. const WIN32_FIND_DATAW *pfd,
  33. const CPrint& pr);
  34. DWORD Pin(DWORD *pdwCscResult = NULL);
  35. DWORD Unpin(DWORD *pdwCscResult = NULL);
  36. DWORD DeleteIfUnused(void);
  37. private:
  38. WCHAR m_szFile[MAX_PATH];
  39. SHFILEINFOW m_sfi;
  40. WIN32_FIND_DATAW m_fd;
  41. BOOL m_bIsValidUnc; // Is m_szFile a valid UNC?
  42. BOOL m_bIsValidFindData; // Is m_fd valid?
  43. const CPrint& m_pr; // For console/log output.
  44. bool _Skip(void) const;
  45. DWORD _PinFile(LPCWSTR pszFile, WIN32_FIND_DATAW *pfd, DWORD *pdwCscResult);
  46. DWORD _PinOrUnpinLinkTarget(LPCWSTR pszFile, BOOL bPin, DWORD *pdwCscResult);
  47. DWORD _PinLinkTarget(LPCWSTR pszFile, DWORD *pdwCscResult)
  48. { return _PinOrUnpinLinkTarget(pszFile, TRUE, pdwCscResult); }
  49. DWORD _UnpinLinkTarget(LPCWSTR pszFile, DWORD *pdwCscResult)
  50. { return _PinOrUnpinLinkTarget(pszFile, FALSE, pdwCscResult); }
  51. DWORD _UnpinFile(LPCWSTR pszFile, WIN32_FIND_DATAW *pfd, DWORD *pdwCscResult);
  52. DWORD _GetDesiredPinCount(LPCWSTR pszFile);
  53. void _DecrementPinCountForFile(LPCWSTR pszFile, DWORD dwCurrentPinCount);
  54. BOOL _IsSpecialRedirectedFile(LPCWSTR pszFile);
  55. WIN32_FIND_DATAW *_FindDataPtrOrNull(void)
  56. { return m_bIsValidFindData ? &m_fd : NULL; }
  57. //
  58. // Prevent copy.
  59. //
  60. CCscPinItem(const CCscPinItem& rhs); // not implemented.
  61. CCscPinItem& operator = (const CCscPinItem& rhs); // not implemented.
  62. };
  63. CCscPinItem::CCscPinItem(
  64. LPCWSTR pszFile,
  65. const WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
  66. const CPrint& pr
  67. ) : m_bIsValidUnc(FALSE),
  68. m_bIsValidFindData(FALSE),
  69. m_pr(pr)
  70. {
  71. TraceAssert(NULL != pszFile);
  72. TraceAssert(NULL == pfd || !IsBadReadPtr(pfd, sizeof(*pfd)));
  73. lstrcpynW(m_szFile, pszFile, ARRAYSIZE(m_szFile));
  74. if (NULL != pfd)
  75. {
  76. m_fd = *pfd;
  77. m_bIsValidFindData = TRUE;
  78. }
  79. ZeroMemory(&m_sfi, sizeof(m_sfi));
  80. m_sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK;
  81. if (PathIsUNCW(m_szFile) &&
  82. SHGetFileInfoW(m_szFile, 0, &m_sfi, sizeof(m_sfi), SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED))
  83. {
  84. m_bIsValidUnc = true;
  85. }
  86. }
  87. //
  88. // Pins the item's file. If the item is a link, the link target
  89. // is also pinned.
  90. // Returns one of the CSCPROC_RETURN_XXXXX codes.
  91. // Optionally returns the result of CSCPinFile.
  92. //
  93. DWORD
  94. CCscPinItem::Pin(
  95. DWORD *pdwCscResult // Optional. Default is NULL.
  96. )
  97. {
  98. TraceEnter(TRACE_ADMINPIN, "CCscPinItem::Pin");
  99. DWORD dwCscResult = ERROR_SUCCESS;
  100. DWORD dwResult = CSCPROC_RETURN_SKIP;
  101. if (!_Skip())
  102. {
  103. if (SFGAO_LINK & m_sfi.dwAttributes)
  104. {
  105. //
  106. // Ignore result from pinning the link target.
  107. //
  108. DWORD dwCscResultIgnored;
  109. _PinLinkTarget(m_szFile, &dwCscResultIgnored);
  110. }
  111. dwResult = _PinFile(m_szFile, _FindDataPtrOrNull(), &dwCscResult);
  112. }
  113. if (NULL != pdwCscResult)
  114. {
  115. *pdwCscResult = dwCscResult;
  116. }
  117. TraceLeaveValue(dwResult);
  118. }
  119. //
  120. // Unpins the item's file.
  121. // Returns one of the CSCPROC_RETURN_XXXXX codes.
  122. // Optionally returns the result of CSCUnpinFile.
  123. //
  124. DWORD
  125. CCscPinItem::Unpin(
  126. DWORD *pdwCscResult // Optional. Default is NULL.
  127. )
  128. {
  129. TraceEnter(TRACE_ADMINPIN, "CCscPinItem::Unpin");
  130. DWORD dwCscResult = ERROR_SUCCESS;
  131. DWORD dwResult = CSCPROC_RETURN_SKIP;
  132. if (!_Skip())
  133. {
  134. if (SFGAO_LINK & m_sfi.dwAttributes)
  135. {
  136. //
  137. // Ignore result from unpinning the link target.
  138. //
  139. DWORD dwCscResultIgnored;
  140. _UnpinLinkTarget(m_szFile, &dwCscResultIgnored);
  141. }
  142. dwResult = _UnpinFile(m_szFile, _FindDataPtrOrNull(), &dwCscResult);
  143. }
  144. if (NULL != pdwCscResult)
  145. {
  146. *pdwCscResult = dwCscResult;
  147. }
  148. TraceLeaveResult(dwResult);
  149. }
  150. //
  151. // Delete an item if it is no longer used.
  152. //
  153. DWORD
  154. CCscPinItem::DeleteIfUnused(
  155. void
  156. )
  157. {
  158. TraceEnter(TRACE_ADMINPIN, "CCscPin::DeleteIfUnused");
  159. DWORD dwStatus = 0;
  160. DWORD dwPinCount = 0;
  161. DWORD dwHintFlags = 0;
  162. DWORD dwResult = ERROR_SUCCESS;
  163. if (CSCQueryFileStatusW(m_szFile, &dwStatus, &dwPinCount, &dwHintFlags) &&
  164. 0 == dwPinCount &&
  165. 0 == dwHintFlags &&
  166. !(dwStatus & FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY))
  167. {
  168. dwResult = CscDelete(m_szFile);
  169. if (ERROR_SUCCESS == dwResult)
  170. {
  171. m_pr.PrintVerbose(L"Deleted \"%s\" from cache.\n", m_szFile);
  172. ShellChangeNotify(m_szFile, _FindDataPtrOrNull(), FALSE);
  173. }
  174. else
  175. {
  176. if (ERROR_DIR_NOT_EMPTY == dwResult)
  177. {
  178. dwResult = ERROR_SUCCESS;
  179. }
  180. if (ERROR_SUCCESS != dwResult)
  181. {
  182. m_pr.PrintAlways(L"Error deleting \"%s\" from cache. %s\n",
  183. m_szFile, CWinError(dwResult).Text());
  184. }
  185. }
  186. }
  187. TraceLeaveValue(dwResult);
  188. }
  189. //
  190. // Internal function for pinning a file. This is a common
  191. // function called by both Pin() and _PinOrUnpinLinkTarget().
  192. //
  193. DWORD
  194. CCscPinItem::_PinFile(
  195. LPCWSTR pszFile, // UNC path of file to pin.
  196. WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
  197. DWORD *pdwCscResult // Result of CSCPinFile.
  198. )
  199. {
  200. TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_PinFile");
  201. TraceAssert(NULL != pszFile);
  202. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  203. TraceAssert(NULL != pdwCscResult);
  204. *pdwCscResult = ERROR_SUCCESS;
  205. //
  206. // Collect cache information for the item.
  207. // This may fail, for example if the file is not in the cache
  208. //
  209. DWORD dwPinCount = 0;
  210. DWORD dwHintFlags = 0;
  211. CSCQueryFileStatusW(pszFile, NULL, &dwPinCount, &dwHintFlags);
  212. //
  213. // Is the admin flag already turned on?
  214. //
  215. const BOOL bNewItem = !(dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN);
  216. if (bNewItem)
  217. {
  218. //
  219. // Turn on the admin flag
  220. //
  221. dwHintFlags |= FLAG_CSC_HINT_PIN_ADMIN;
  222. if (CSCPinFileW(pszFile,
  223. dwHintFlags,
  224. NULL,
  225. &dwPinCount,
  226. &dwHintFlags))
  227. {
  228. m_pr.PrintVerbose(L"Pin \"%s\"\n", pszFile);
  229. ShellChangeNotify(pszFile, pfd, FALSE);
  230. }
  231. else
  232. {
  233. const DWORD dwErr = GetLastError();
  234. if (ERROR_INVALID_NAME == dwErr)
  235. {
  236. //
  237. // This is the error we get from CSC when trying to
  238. // pin a file in the exclusion list. Display a unique
  239. // error message for this particular situation.
  240. //
  241. m_pr.PrintAlways(L"Pinning file \"%s\" is not allowed.\n", pszFile);
  242. }
  243. else
  244. {
  245. m_pr.PrintAlways(L"Error pinning \"%s\". %s\n",
  246. pszFile,
  247. CWinError(dwErr).Text());
  248. }
  249. *pdwCscResult = dwErr;
  250. }
  251. }
  252. else
  253. {
  254. m_pr.PrintVerbose(L"\"%s\" already pinned.\n", pszFile);
  255. }
  256. TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
  257. }
  258. //
  259. //.Get the target of a link and pin it.
  260. //
  261. DWORD
  262. CCscPinItem::_PinOrUnpinLinkTarget(
  263. LPCWSTR pszFile, // UNC of link file.
  264. BOOL bPin,
  265. DWORD *pdwCscResult // Result of CSCPinFile on target.
  266. )
  267. {
  268. TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_PinOrUnpinLinkTarget");
  269. TraceAssert(NULL != pszFile);
  270. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  271. TraceAssert(NULL != pdwCscResult);
  272. *pdwCscResult = ERROR_SUCCESS;
  273. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  274. LPWSTR pszTarget = NULL;
  275. //
  276. // We only want to pin a link target if it's a file (not a directory).
  277. // GetLinkTarget does this check and only returns files.
  278. //
  279. GetLinkTarget(pszFile, NULL, &pszTarget);
  280. if (NULL != pszTarget)
  281. {
  282. WIN32_FIND_DATAW fd = {0};
  283. LPCWSTR pszT = PathFindFileName(pszTarget);
  284. fd.dwFileAttributes = 0;
  285. lstrcpynW(fd.cFileName, pszT ? pszT : pszTarget, ARRAYSIZE(fd.cFileName));
  286. //
  287. // Pin the target
  288. //
  289. if (bPin)
  290. {
  291. dwResult = _PinFile(pszTarget, &fd, pdwCscResult);
  292. }
  293. else
  294. {
  295. dwResult = _UnpinFile(pszTarget, &fd, pdwCscResult);
  296. }
  297. LocalFree(pszTarget);
  298. }
  299. TraceLeaveValue(dwResult);
  300. }
  301. DWORD
  302. CCscPinItem::_UnpinFile(
  303. LPCWSTR pszFile, // UNC of file to unpin.
  304. WIN32_FIND_DATAW *pfd, // Optional. May be NULL.
  305. DWORD *pdwCscResult // Result of CSCUnpinFile
  306. )
  307. {
  308. TraceEnter(TRACE_ADMINPIN, "CCscPinItem::_UnpinFile");
  309. TraceAssert(NULL != pszFile);
  310. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  311. TraceAssert(NULL != pdwCscResult);
  312. *pdwCscResult = ERROR_SUCCESS;
  313. //
  314. // Collect cache information for the item.
  315. // This may fail, for example if the file is not in the cache
  316. //
  317. DWORD dwPinCount = 0;
  318. DWORD dwHintFlags = 0;
  319. DWORD dwStatus = 0;
  320. CSCQueryFileStatusW(pszFile, &dwStatus, &dwPinCount, &dwHintFlags);
  321. if (dwHintFlags & FLAG_CSC_HINT_PIN_ADMIN)
  322. {
  323. DWORD dwStatus = 0;
  324. DWORD dwHintFlags = 0;
  325. //
  326. // Decrement pin count. Amount decremented depends on the file.
  327. // Win2000 deployment code increments the pin count of some special
  328. // folders as well as for the desktop.ini file in those special
  329. // folders. In those cases, we want to leave the pin count at
  330. // 1. For all other files, the pin count can drop to zero.
  331. //
  332. _DecrementPinCountForFile(pszFile, dwPinCount);
  333. //
  334. // Clear system-pin flag (aka admin-pin flag).
  335. //
  336. dwHintFlags |= FLAG_CSC_HINT_PIN_ADMIN;
  337. if (CSCUnpinFileW(pszFile,
  338. dwHintFlags,
  339. &dwStatus,
  340. &dwPinCount,
  341. &dwHintFlags))
  342. {
  343. m_pr.PrintVerbose(L"Unpin \"%s\"\n", pszFile);
  344. if (FLAG_CSC_COPY_STATUS_IS_FILE & dwStatus)
  345. {
  346. //
  347. // Delete a file here. Directories are deleted
  348. // on the backside of the post-order traversal
  349. // in CscPin::_FolderCallback.
  350. //
  351. DeleteIfUnused();
  352. }
  353. ShellChangeNotify(pszFile, pfd, FALSE);
  354. }
  355. else
  356. {
  357. *pdwCscResult = GetLastError();
  358. m_pr.PrintAlways(L"Error unpinning \"%s\". %s\n",
  359. pszFile,
  360. CWinError(*pdwCscResult).Text());
  361. }
  362. }
  363. TraceLeaveValue(CSCPROC_RETURN_CONTINUE);
  364. }
  365. //
  366. // As part of the unpin operation, we decrement the pin count
  367. // to either 0 or 1. Folder redirection (contact RahulTh) increments
  368. // the pin count of redirected special folders and the desktop.ini file
  369. // within those folders. In those cases, we want to leave the
  370. // pin count at 1 so that we don't upset the behavior of redirected
  371. // folders. For all other files we drop the pin count to 0.
  372. //
  373. void
  374. CCscPinItem::_DecrementPinCountForFile(
  375. LPCWSTR pszFile,
  376. DWORD dwCurrentPinCount
  377. )
  378. {
  379. DWORD dwStatus = 0;
  380. DWORD dwPinCount = 0;
  381. DWORD dwHintFlags = 0;
  382. const DWORD dwDesiredPinCount = _GetDesiredPinCount(pszFile);
  383. while(dwCurrentPinCount-- > dwDesiredPinCount)
  384. {
  385. dwHintFlags = FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT;
  386. CSCUnpinFileW(pszFile,
  387. dwHintFlags,
  388. &dwStatus,
  389. &dwPinCount,
  390. &dwHintFlags);
  391. }
  392. }
  393. //
  394. // This function returns the desired pin count (0 or 1) for a
  395. // given file. Returns 1 for any redirected special folders
  396. // and the desktop.ini file within those folders. Returns 0
  397. // for all other files.
  398. //
  399. DWORD
  400. CCscPinItem::_GetDesiredPinCount(
  401. LPCWSTR pszFile
  402. )
  403. {
  404. TraceAssert(NULL != pszFile);
  405. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  406. DWORD dwPinCount = 0; // Default for most files.
  407. if (_IsSpecialRedirectedFile(pszFile))
  408. {
  409. dwPinCount = 1;
  410. }
  411. return dwPinCount;
  412. }
  413. //
  414. // Determines if a path is a "special" file pinned by the folder
  415. // redirection code.
  416. //
  417. BOOL
  418. CCscPinItem::_IsSpecialRedirectedFile(
  419. LPCWSTR pszPath
  420. )
  421. {
  422. TraceAssert(NULL != pszPath);
  423. TraceAssert(!IsBadStringPtr(pszPath, MAX_PATH));
  424. //
  425. // This list of special folder IDs provided by RahulTh (08/30/00).
  426. // These are the paths that may be pinned by folder redirection.
  427. //
  428. static struct
  429. {
  430. int csidl;
  431. WCHAR szPath[MAX_PATH];
  432. int cchPath;
  433. } rgFolderPaths[] = {
  434. { CSIDL_PERSONAL, 0, 0 },
  435. { CSIDL_MYPICTURES, 0, 0 },
  436. { CSIDL_DESKTOPDIRECTORY, 0, 0 },
  437. { CSIDL_STARTMENU, 0, 0 },
  438. { CSIDL_PROGRAMS, 0, 0 },
  439. { CSIDL_STARTUP, 0, 0 },
  440. { CSIDL_APPDATA, 0, 0 }
  441. };
  442. int i;
  443. if (L'\0' == rgFolderPaths[0].szPath[0])
  444. {
  445. //
  446. // Initialize the special folder path data.
  447. // One-time only initialization.
  448. //
  449. for (i = 0; i < ARRAYSIZE(rgFolderPaths); i++)
  450. {
  451. if (!SHGetSpecialFolderPath(NULL,
  452. rgFolderPaths[i].szPath,
  453. rgFolderPaths[i].csidl | CSIDL_FLAG_DONT_VERIFY,
  454. FALSE))
  455. {
  456. m_pr.PrintAlways(L"Error getting path for shell special folder %d. %s\n",
  457. rgFolderPaths[i].csidl,
  458. CWinError(GetLastError()).Text());
  459. }
  460. else
  461. {
  462. //
  463. // Calculate and cache the length.
  464. //
  465. rgFolderPaths[i].cchPath = lstrlen(rgFolderPaths[i].szPath);
  466. }
  467. }
  468. }
  469. const int cchPath = lstrlen(pszPath);
  470. for (i = 0; i < ARRAYSIZE(rgFolderPaths); i++)
  471. {
  472. int cchThis = rgFolderPaths[i].cchPath;
  473. LPCWSTR pszThis = rgFolderPaths[i].szPath;
  474. if (cchPath >= cchThis)
  475. {
  476. //
  477. // Path being examined is the same length or longer than
  478. // current path from the table. Possible match.
  479. //
  480. if (0 == StrCmpNIW(pszPath, pszThis, cchThis))
  481. {
  482. //
  483. // Path being examined is either the same as,
  484. // or a child of, the current path from the table.
  485. //
  486. if (L'\0' == *(pszPath + cchThis))
  487. {
  488. //
  489. // Path is same as this path from the table.
  490. //
  491. return TRUE;
  492. }
  493. else if (0 == lstrcmpiW(pszPath + cchThis + 1, L"desktop.ini"))
  494. {
  495. //
  496. // Path is for a desktop.ini file that exists in the
  497. // root of one of our special folders.
  498. //
  499. return TRUE;
  500. }
  501. }
  502. }
  503. }
  504. return FALSE;
  505. }
  506. //
  507. // Determines if the item should be skipped or not.
  508. //
  509. bool
  510. CCscPinItem::_Skip(
  511. void
  512. ) const
  513. {
  514. return !m_bIsValidUnc || (0 == (SFGAO_FILESYSTEM & m_sfi.dwAttributes));
  515. }
  516. //-----------------------------------------------------------------------------
  517. // CCscPin
  518. //-----------------------------------------------------------------------------
  519. CCscPin::CCscPin(
  520. const CSCPIN_INFO& info
  521. ) : m_bUseListFile(info.bUseListFile),
  522. m_bPin(info.bPin),
  523. m_bPinDefaultSet(info.bPinDefaultSet),
  524. m_bBreakDetected(FALSE),
  525. m_cFilesPinned(0),
  526. m_cCscErrors(0),
  527. m_pr(info.bVerbose, info.pszLogFile)
  528. {
  529. TraceAssert(NULL != info.pszFile);
  530. lstrcpynW(m_szFile, info.pszFile, ARRAYSIZE(m_szFile));
  531. }
  532. CCscPin::~CCscPin(
  533. void
  534. )
  535. {
  536. }
  537. //
  538. // The only public method on the CCscPin object.
  539. // Just create an object and tell it to Run.
  540. //
  541. HRESULT
  542. CCscPin::Run(
  543. void
  544. )
  545. {
  546. HRESULT hr = E_FAIL;
  547. m_cCscErrors = 0;
  548. m_cFilesPinned = 0;
  549. if (!IsCSCEnabled())
  550. {
  551. m_pr.PrintAlways(L"Offline Files is not enabled.\n");
  552. SetExitCode(CSCPIN_EXIT_CSC_NOT_ENABLED);
  553. }
  554. else if (_IsAdminPinPolicyActive())
  555. {
  556. m_pr.PrintAlways(L"The Offline Files 'admin-pin' policy is active.\n");
  557. SetExitCode(CSCPIN_EXIT_POLICY_ACTIVE);
  558. }
  559. else
  560. {
  561. if (m_bUseListFile)
  562. {
  563. //
  564. // Process files listed in m_szFile.
  565. //
  566. hr = _ProcessPathsInFile(m_szFile);
  567. }
  568. else
  569. {
  570. //
  571. // Process the one file provided on the cmd line.
  572. // Do a quick existence check first.
  573. //
  574. if (DWORD(-1) != GetFileAttributesW(m_szFile))
  575. {
  576. hr = _ProcessThisPath(m_szFile, m_bPin);
  577. }
  578. else
  579. {
  580. m_pr.PrintAlways(L"File \"%s\" not found.\n", m_szFile);
  581. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  582. SetExitCode(CSCPIN_EXIT_FILE_NOT_FOUND);
  583. }
  584. }
  585. //
  586. // Flush all change notifications.
  587. //
  588. ShellChangeNotify(NULL, TRUE);
  589. if (0 < m_cFilesPinned && !_DetectConsoleBreak())
  590. {
  591. //
  592. // If we pinned some files, fill all sparse
  593. // files in the cache.
  594. //
  595. _FillSparseFiles();
  596. }
  597. if (0 < m_cCscErrors)
  598. {
  599. SetExitCode(CSCPIN_EXIT_CSC_ERRORS);
  600. }
  601. }
  602. return hr;
  603. }
  604. //
  605. // Callback parameter block passed to _FolderCallback.
  606. //
  607. struct
  608. CSCPIN_FOLDER_CBK_PARAMS
  609. {
  610. CCscPin *pCscPin; // Reference to the CCscPin object.
  611. BOOL bPin; // TRUE == Pin files, FALSE == Unpin.
  612. };
  613. //
  614. // Callback used for enumerating the filesystem. This function
  615. // is called for each file processed.
  616. //
  617. DWORD WINAPI
  618. CCscPin::_FolderCallback(
  619. LPCWSTR pszItem,
  620. ENUM_REASON eReason,
  621. WIN32_FIND_DATAW *pFind32,
  622. LPARAM pContext // Ptr to CSCPIN_FOLDER_CBK_PARAMS.
  623. )
  624. {
  625. TraceEnter(TRACE_ADMINPIN, "CCscPin::_PinFolderCallback");
  626. TraceAssert(NULL != pszItem);
  627. TraceAssert(NULL != pContext);
  628. CSCPIN_FOLDER_CBK_PARAMS *pfcp = (CSCPIN_FOLDER_CBK_PARAMS *)pContext;
  629. CCscPin *pThis = pfcp->pCscPin;
  630. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  631. if (pThis->_DetectConsoleBreak())
  632. {
  633. TraceLeaveValue(CSCPROC_RETURN_ABORT);
  634. }
  635. if (!pszItem || !*pszItem)
  636. {
  637. TraceLeaveValue(CSCPROC_RETURN_SKIP);
  638. }
  639. if (ENUM_REASON_FILE == eReason || ENUM_REASON_FOLDER_BEGIN == eReason)
  640. {
  641. TraceAssert(NULL != pFind32);
  642. CCscPinItem item(pszItem, pFind32, pThis->m_pr);
  643. DWORD dwCscResult = ERROR_SUCCESS;
  644. if (pfcp->bPin)
  645. {
  646. dwResult = item.Pin(&dwCscResult);
  647. if (ERROR_SUCCESS == dwCscResult)
  648. {
  649. pThis->m_cFilesPinned++;
  650. }
  651. }
  652. else
  653. {
  654. dwResult = item.Unpin(&dwCscResult);
  655. }
  656. if (ERROR_SUCCESS != dwCscResult)
  657. {
  658. pThis->m_cCscErrors++;
  659. }
  660. }
  661. else if (ENUM_REASON_FOLDER_END == eReason && !pfcp->bPin)
  662. {
  663. //
  664. // This code is executed for each folder item after all children
  665. // have been visited in the post-order traversal of the
  666. // CSC filesystem. We use it to remove any empty folder entries
  667. // from the cache.
  668. //
  669. CCscPinItem item(pszItem, pFind32, pThis->m_pr);
  670. item.DeleteIfUnused();
  671. }
  672. TraceLeaveValue(dwResult);
  673. }
  674. //
  675. // Pin or unpin one path string. If it's a folder, all it's children
  676. // are also pinned or unpinned according to the bPin argument.
  677. //
  678. HRESULT
  679. CCscPin::_ProcessThisPath(
  680. LPCWSTR pszFile,
  681. BOOL bPin
  682. )
  683. {
  684. TraceEnter(TRACE_ADMINPIN, "CCscPin::_ProcessThisPath");
  685. TraceAssert(NULL != pszFile);
  686. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  687. LPCWSTR pszPath = pszFile;
  688. LPWSTR pszUncPath = NULL;
  689. if (!PathIsUNC(pszPath))
  690. {
  691. GetRemotePath(pszPath, &pszUncPath);
  692. pszPath = (LPCWSTR)pszUncPath;
  693. }
  694. if (NULL != pszPath)
  695. {
  696. CSCPIN_FOLDER_CBK_PARAMS CbkParams = { this, bPin };
  697. //
  698. // Process this item
  699. //
  700. DWORD dwResult = _FolderCallback(pszPath, ENUM_REASON_FILE, NULL, (LPARAM)&CbkParams);
  701. //
  702. // Process everything under this folder (if it's a folder)
  703. //
  704. //
  705. // ISSUE-2000/08/28-BrianAu Should we provide the capability to
  706. // limit recursive pinning and unpinning? Maybe in the future
  707. // but not now.
  708. //
  709. if (CSCPROC_RETURN_CONTINUE == dwResult && PathIsUNC(pszPath))
  710. {
  711. _Win32EnumFolder(pszPath, TRUE, _FolderCallback, (LPARAM)&CbkParams);
  712. }
  713. //
  714. // Finally, once we're all done, delete the top level item if it's
  715. // unused.
  716. //
  717. CCscPinItem item(pszPath, NULL, m_pr);
  718. item.DeleteIfUnused();
  719. }
  720. LocalFreeString(&pszUncPath);
  721. TraceLeaveResult(S_OK);
  722. }
  723. //
  724. // Reads paths in the [Pin], [Unpin] and [Default] sections of an INI file.
  725. // For each, call the _ProcessThisPath function.
  726. //
  727. HRESULT
  728. CCscPin::_ProcessPathsInFile(
  729. LPCWSTR pszFile
  730. )
  731. {
  732. TraceEnter(TRACE_ADMINPIN, "CCscPin::_ProcessPathsInFile");
  733. TraceAssert(NULL != pszFile);
  734. TraceAssert(!IsBadStringPtr(pszFile, MAX_PATH));
  735. HRESULT hr = S_OK;
  736. //
  737. // Need a full path name. Otherwise, the PrivateProfile APIs
  738. // used by the CListFile object will assume the file is in
  739. // one of the "system" directories.
  740. //
  741. WCHAR szFile[MAX_PATH];
  742. LPWSTR pszNamePart;
  743. if (0 == GetFullPathNameW(pszFile,
  744. ARRAYSIZE(szFile),
  745. szFile,
  746. &pszNamePart))
  747. {
  748. const DWORD dwErr = GetLastError();
  749. hr = HRESULT_FROM_WIN32(dwErr);
  750. SetExitCode(CSCPIN_EXIT_LISTFILE_NO_OPEN);
  751. m_pr.PrintAlways(L"Error expanding path \"%s\". %s\n",
  752. pszFile,
  753. CWinError(hr).Text());
  754. }
  755. else
  756. {
  757. //
  758. // Before we go any further, verify the file really exists.
  759. //
  760. if (DWORD(-1) == GetFileAttributesW(szFile))
  761. {
  762. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  763. SetExitCode(CSCPIN_EXIT_LISTFILE_NO_OPEN);
  764. m_pr.PrintAlways(L"Error opening input file \"%s\". %s\n",
  765. szFile, CWinError(hr).Text());
  766. }
  767. else
  768. {
  769. //
  770. // Read and process the information in the file.
  771. // Note that the listfile object MUST remain alive while the
  772. // iterator is being used.
  773. //
  774. CListFile listfile(szFile);
  775. CDblNulStrIter iter;
  776. typedef HRESULT (CListFile::*PFN)(CDblNulStrIter *);
  777. //
  778. // This table describes the sections read from the listfile,
  779. // the order they are read in and if the files read should
  780. // be 'pinned' or 'unpinned'.
  781. //
  782. static const struct
  783. {
  784. PFN pfn; // Function called to read file contents.
  785. BOOL bQuery; // Query input file for these items?
  786. BOOL bPin; // Action to perform on contents read.
  787. } rgReadFuncs[] = {
  788. { &CListFile::GetFilesToUnpin, TRUE, FALSE }, // Reads [Unpin] section.
  789. { &CListFile::GetFilesToPin, TRUE, TRUE }, // Reads [Pin] section.
  790. { &CListFile::GetFilesDefault, m_bPinDefaultSet, m_bPin }, // Reads [Default] section.
  791. };
  792. for (int i = 0; i < ARRAYSIZE(rgReadFuncs) && !_DetectConsoleBreak(); i++)
  793. {
  794. if (rgReadFuncs[i].bQuery)
  795. {
  796. PFN pfn = rgReadFuncs[i].pfn;
  797. BOOL bPin = rgReadFuncs[i].bPin;
  798. //
  799. // Read the info from the listfile using the appropriate
  800. // function. The returned iterator will iterate over all
  801. // of the files read.
  802. //
  803. hr = (listfile.*pfn)(&iter);
  804. if (SUCCEEDED(hr))
  805. {
  806. //
  807. // Process the entries.
  808. //
  809. LPCWSTR pszPath;
  810. while(iter.Next(&pszPath))
  811. {
  812. //
  813. // Paths in the listfile can contain embedded environment
  814. // strings.
  815. //
  816. TCHAR szPathExpanded[MAX_PATH];
  817. if (0 == ExpandEnvironmentStrings(pszPath, szPathExpanded, ARRAYSIZE(szPathExpanded)))
  818. {
  819. m_pr.PrintAlways(L"Error expanding \"%s\". %s\n",
  820. pszPath,
  821. CWinError(GetLastError()));
  822. lstrcpynW(szPathExpanded, pszPath, ARRAYSIZE(szPathExpanded));
  823. }
  824. hr = _ProcessThisPath(szPathExpanded, bPin);
  825. }
  826. }
  827. }
  828. }
  829. }
  830. }
  831. return hr;
  832. }
  833. //
  834. // Enumerates each share in the cache and attempts to fill all sparse
  835. // files in that share.
  836. //
  837. HRESULT
  838. CCscPin::_FillSparseFiles(
  839. void
  840. )
  841. {
  842. HRESULT hr = S_OK;
  843. m_pr.PrintAlways(L"Copying pinned files into cache...\n");
  844. DWORD dwStatus;
  845. DWORD dwPinCount;
  846. DWORD dwHintFlags;
  847. WIN32_FIND_DATA fd;
  848. FILETIME ft;
  849. CCscFindHandle hFind;
  850. hFind = CacheFindFirst(NULL, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft);
  851. if (hFind.IsValid())
  852. {
  853. do
  854. {
  855. const BOOL bFullSync = FALSE;
  856. CSCFillSparseFilesW(fd.cFileName,
  857. bFullSync,
  858. _FillSparseFilesCallback,
  859. (DWORD_PTR)this);
  860. }
  861. while(CacheFindNext(hFind, &fd, &dwStatus, &dwPinCount, &dwHintFlags, &ft));
  862. }
  863. return hr;
  864. }
  865. //
  866. // Called by CSC for each file processed by CSCFillSparseFiles.
  867. //
  868. DWORD WINAPI
  869. CCscPin::_FillSparseFilesCallback(
  870. LPCWSTR pszName,
  871. DWORD dwStatus,
  872. DWORD dwHintFlags,
  873. DWORD dwPinCount,
  874. WIN32_FIND_DATAW *pfd,
  875. DWORD dwReason,
  876. DWORD dwParam1,
  877. DWORD dwParam2,
  878. DWORD_PTR dwContext
  879. )
  880. {
  881. TraceAssert(NULL != dwContext);
  882. CCscPin *pThis = (CCscPin *)dwContext;
  883. DWORD dwResult = CSCPROC_RETURN_CONTINUE;
  884. if (pThis->_DetectConsoleBreak())
  885. {
  886. dwResult = CSCPROC_RETURN_ABORT;
  887. }
  888. else
  889. {
  890. switch(dwReason)
  891. {
  892. case CSCPROC_REASON_BEGIN:
  893. pThis->m_pr.PrintVerbose(L"Filling file \"%s\"\n", pszName);
  894. break;
  895. case CSCPROC_REASON_END:
  896. dwParam2 = pThis->_TranslateFillResult(dwParam2, dwStatus, &dwResult);
  897. if (ERROR_SUCCESS != dwParam2)
  898. {
  899. pThis->m_cCscErrors++;
  900. pThis->m_pr.PrintAlways(L"Error filling \"%s\" %s\n",
  901. pszName,
  902. CWinError(dwParam2).Text());
  903. }
  904. break;
  905. default:
  906. break;
  907. }
  908. }
  909. TraceLeaveValue(dwResult);
  910. }
  911. //
  912. // Translates the error code and status provided by CSC from CSCFillSparseFiles
  913. // into the correct error code and CSCPROC_RETURN_XXXXXX value. Some errors
  914. // require translation before presentation to the user.
  915. //
  916. DWORD
  917. CCscPin::_TranslateFillResult(
  918. DWORD dwError,
  919. DWORD dwStatus,
  920. DWORD *pdwCscAction
  921. )
  922. {
  923. DWORD dwResult = dwError;
  924. DWORD dwAction = CSCPROC_RETURN_CONTINUE;
  925. if (ERROR_SUCCESS != dwError)
  926. {
  927. if (3000 <= dwError && dwError <= 3200)
  928. {
  929. //
  930. // Special internal CSC error codes.
  931. //
  932. dwResult = ERROR_SUCCESS;
  933. }
  934. else
  935. {
  936. switch(dwError)
  937. {
  938. case ERROR_OPERATION_ABORTED:
  939. dwResult = ERROR_SUCCESS;
  940. dwAction = CSCPROC_RETURN_ABORT;
  941. break;
  942. case ERROR_GEN_FAILURE:
  943. if (FLAG_CSC_COPY_STATUS_FILE_IN_USE & dwStatus)
  944. {
  945. dwResult = ERROR_OPEN_FILES;
  946. }
  947. break;
  948. case ERROR_DISK_FULL:
  949. dwAction = CSCPROC_RETURN_ABORT;
  950. break;
  951. default:
  952. break;
  953. }
  954. }
  955. }
  956. if (NULL != pdwCscAction)
  957. {
  958. *pdwCscAction = dwAction;
  959. }
  960. return dwResult;
  961. }
  962. //
  963. // Determine if the admin-pin policy is active on the current
  964. // computer.
  965. //
  966. BOOL
  967. CCscPin::_IsAdminPinPolicyActive(
  968. void
  969. )
  970. {
  971. const HKEY rghkeyRoots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
  972. BOOL bIsActive = FALSE;
  973. for (int i = 0; !bIsActive && i < ARRAYSIZE(rghkeyRoots); i++)
  974. {
  975. HKEY hkey;
  976. if (ERROR_SUCCESS == RegOpenKey(rghkeyRoots[i], c_szRegKeyAPF, &hkey))
  977. {
  978. WCHAR szName[MAX_PATH];
  979. DWORD cchName = ARRAYSIZE(szName);
  980. if (ERROR_SUCCESS == RegEnumValue(hkey,
  981. 0,
  982. szName,
  983. &cchName,
  984. NULL,
  985. NULL,
  986. NULL,
  987. NULL))
  988. {
  989. bIsActive = TRUE;
  990. }
  991. RegCloseKey(hkey);
  992. }
  993. }
  994. return bIsActive;
  995. }
  996. //
  997. // Determine if one of the following system events has occured.
  998. //
  999. // 1. User pressed Ctrl-C.
  1000. // 2. User pressed Ctrl-Break.
  1001. // 3. Console window was closed.
  1002. // 4. User logged off.
  1003. //
  1004. // If one of these events has occured, an output message is generated
  1005. // and TRUE is returned.
  1006. // Otherwise, FALSE is returned.
  1007. // Note that the output message is generated only once.
  1008. //
  1009. BOOL
  1010. CCscPin::_DetectConsoleBreak(
  1011. void
  1012. )
  1013. {
  1014. if (!m_bBreakDetected)
  1015. {
  1016. DWORD dwCtrlEvent;
  1017. m_bBreakDetected = ConsoleHasCtrlEventOccured(&dwCtrlEvent);
  1018. if (m_bBreakDetected)
  1019. {
  1020. m_pr.PrintAlways(L"Program aborted. ");
  1021. switch(dwCtrlEvent)
  1022. {
  1023. case CTRL_C_EVENT:
  1024. m_pr.PrintAlways(L"User pressed Ctrl-C\n");
  1025. break;
  1026. case CTRL_BREAK_EVENT:
  1027. m_pr.PrintAlways(L"User pressed Ctrl-Break\n");
  1028. break;
  1029. case CTRL_CLOSE_EVENT:
  1030. m_pr.PrintAlways(L"Application forceably closed.\n");
  1031. break;
  1032. case CTRL_LOGOFF_EVENT:
  1033. m_pr.PrintAlways(L"User logged off.\n");
  1034. break;
  1035. default:
  1036. m_pr.PrintAlways(L"Unknown console break event %d\n", dwCtrlEvent);
  1037. break;
  1038. }
  1039. }
  1040. }
  1041. return m_bBreakDetected;
  1042. }