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.

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