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.

975 lines
29 KiB

  1. #include "common.h"
  2. #include <emptyvc.h>
  3. #ifndef COMPCLEN_H
  4. #include "compclen.h"
  5. #endif
  6. #include <regstr.h>
  7. #include <olectl.h>
  8. #include <tlhelp32.h>
  9. #ifndef RESOURCE_H
  10. #include "resource.h"
  11. #endif
  12. #ifdef DEBUG
  13. #include <stdio.h>
  14. #endif // DEBUG
  15. BOOL g_bSettingsChange = FALSE;
  16. const LPCTSTR g_NoCompressFiles[] =
  17. {
  18. TEXT("NTLDR"),
  19. TEXT("OSLOADER.EXE"),
  20. TEXT("PAGEFILE.SYS"),
  21. TEXT("NTDETECT.COM"),
  22. TEXT("EXPLORER.EXE"),
  23. };
  24. LPCTSTR g_NoCompressExts[] =
  25. {
  26. TEXT(".PAL")
  27. };
  28. extern HINSTANCE g_hDllModule;
  29. extern UINT incDllObjectCount(void);
  30. extern UINT decDllObjectCount(void);
  31. CCompCleanerClassFactory::CCompCleanerClassFactory() : m_cRef(1)
  32. {
  33. incDllObjectCount();
  34. }
  35. CCompCleanerClassFactory::~CCompCleanerClassFactory()
  36. {
  37. decDllObjectCount();
  38. }
  39. STDMETHODIMP CCompCleanerClassFactory::QueryInterface(REFIID riid, void **ppv)
  40. {
  41. *ppv = NULL;
  42. if (IsEqualIID(riid, IID_IUnknown) ||
  43. IsEqualIID(riid, IID_IClassFactory))
  44. {
  45. *ppv = (IClassFactory *)this;
  46. AddRef();
  47. return S_OK;
  48. }
  49. return E_NOINTERFACE;
  50. }
  51. STDMETHODIMP_(ULONG) CCompCleanerClassFactory::AddRef()
  52. {
  53. return ++m_cRef;
  54. }
  55. STDMETHODIMP_(ULONG) CCompCleanerClassFactory::Release()
  56. {
  57. if (--m_cRef)
  58. return m_cRef;
  59. delete this;
  60. return 0;
  61. }
  62. STDMETHODIMP CCompCleanerClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, void **ppvObj)
  63. {
  64. *ppvObj = NULL;
  65. if (pUnkOuter)
  66. {
  67. return CLASS_E_NOAGGREGATION;
  68. }
  69. HRESULT hr;
  70. CCompCleaner *pCompCleaner = new CCompCleaner();
  71. if (pCompCleaner)
  72. {
  73. hr = pCompCleaner->QueryInterface (riid, ppvObj);
  74. pCompCleaner->Release();
  75. }
  76. else
  77. {
  78. hr = E_OUTOFMEMORY;
  79. }
  80. return hr;
  81. }
  82. STDMETHODIMP CCompCleanerClassFactory::LockServer(BOOL fLock)
  83. {
  84. if (fLock)
  85. incDllObjectCount();
  86. else
  87. decDllObjectCount();
  88. return S_OK;
  89. }
  90. CCompCleaner::CCompCleaner() : m_cRef(1)
  91. {
  92. cbSpaceUsed.QuadPart = 0;
  93. cbSpaceFreed.QuadPart = 0;
  94. szVolume[0] = 0;
  95. szFolder[0] = 0;
  96. incDllObjectCount();
  97. }
  98. CCompCleaner::~CCompCleaner()
  99. {
  100. // Free the list of directories
  101. FreeList(head);
  102. head = NULL;
  103. decDllObjectCount();
  104. }
  105. STDMETHODIMP CCompCleaner::QueryInterface(REFIID riid, void **ppv)
  106. {
  107. *ppv = NULL;
  108. if (IsEqualIID(riid, IID_IUnknown) ||
  109. IsEqualIID(riid, IID_IEmptyVolumeCache) ||
  110. IsEqualIID(riid, IID_IEmptyVolumeCache2))
  111. {
  112. *ppv = (IEmptyVolumeCache2*) this;
  113. AddRef();
  114. return S_OK;
  115. }
  116. return E_NOINTERFACE;
  117. }
  118. STDMETHODIMP_(ULONG) CCompCleaner::AddRef()
  119. {
  120. return ++m_cRef;
  121. }
  122. STDMETHODIMP_(ULONG) CCompCleaner::Release()
  123. {
  124. if (--m_cRef)
  125. return m_cRef;
  126. delete this;
  127. return 0;
  128. }
  129. // Initializes the Compression Cleaner and returns flags to the cache manager
  130. STDMETHODIMP CCompCleaner::Initialize(HKEY hRegKey,
  131. LPCWSTR pszVolume,
  132. LPWSTR *ppwszDisplayName,
  133. LPWSTR *ppwszDescription,
  134. DWORD *pdwFlags)
  135. {
  136. TCHAR szFileSystemName[MAX_PATH];
  137. DWORD fFileSystemFlags;
  138. bPurged = FALSE;
  139. //
  140. // Allocate memory for the DisplayName string and load the string.
  141. // If the allocation fails, then we will return NULL which will cause
  142. // cleanmgr.exe to read the name from the registry.
  143. //
  144. if (*ppwszDisplayName = (LPWSTR)CoTaskMemAlloc(DISPLAYNAME_LENGTH * sizeof(WCHAR)))
  145. {
  146. LoadString(g_hDllModule, IDS_COMPCLEANER_DISP, *ppwszDisplayName, DISPLAYNAME_LENGTH);
  147. }
  148. //
  149. // Allocate memory for the Description string and load the string.
  150. // If the allocation fails, then we will return NULL which will cause
  151. // cleanmgr.exe to read the description from the registry.
  152. //
  153. if (*ppwszDescription = (LPWSTR)CoTaskMemAlloc(DESCRIPTION_LENGTH * sizeof(WCHAR)))
  154. {
  155. LoadString(g_hDllModule, IDS_COMPCLEANER_DESC, *ppwszDescription, DESCRIPTION_LENGTH);
  156. }
  157. //
  158. //If you want your cleaner to run only when the machine is dangerously low on
  159. //disk space then return S_FALSE unless the EVCF_OUTOFDISKSPACE flag is set.
  160. //
  161. //if (!(*pdwFlags & EVCF_OUTOFDISKSPACE))
  162. //{
  163. // return S_FALSE;
  164. //}
  165. if (*pdwFlags & EVCF_SETTINGSMODE)
  166. {
  167. bSettingsMode = TRUE;
  168. }
  169. else
  170. {
  171. bSettingsMode = FALSE;
  172. }
  173. //Tell the cache manager to disable this item by default
  174. *pdwFlags = 0;
  175. //Tell the Disk Cleanup Manager that we have a Settings button
  176. *pdwFlags |= EVCF_HASSETTINGS;
  177. // If we're in Settings mode no need to do all this other work
  178. //
  179. if (bSettingsMode)
  180. return S_OK;
  181. ftMinLastAccessTime.dwLowDateTime = 0;
  182. ftMinLastAccessTime.dwHighDateTime = 0;
  183. if (GetVolumeInformation(pszVolume, NULL, 0, NULL, NULL, &fFileSystemFlags, szFileSystemName, MAX_PATH) &&
  184. (0 == lstrcmp(szFileSystemName, TEXT("NTFS"))) &&
  185. (fFileSystemFlags & FS_FILE_COMPRESSION))
  186. {
  187. StringCchCopy(szFolder, ARRAYSIZE(szFolder), pszVolume);
  188. // Calculate the last access date filetime
  189. CalcLADFileTime();
  190. return S_OK;
  191. }
  192. return S_FALSE;
  193. }
  194. STDMETHODIMP CCompCleaner::InitializeEx(HKEY hRegKey, LPCWSTR pcwszVolume, LPCWSTR pcwszKeyName,
  195. LPWSTR *ppwszDisplayName, LPWSTR *ppwszDescription,
  196. LPWSTR *ppwszBtnText, DWORD *pdwFlags)
  197. {
  198. // Allocate memory for the ButtonText string and load the string.
  199. // If we can't allocate the memory, leave the pointer NULL.
  200. if (*ppwszBtnText = (LPWSTR)CoTaskMemAlloc(BUTTONTEXT_LENGTH * sizeof(WCHAR)))
  201. {
  202. LoadString(g_hDllModule, IDS_COMPCLEANER_BUTTON, *ppwszBtnText, BUTTONTEXT_LENGTH);
  203. }
  204. //
  205. // Now let the IEmptyVolumeCache version 1 Init function do the rest
  206. //
  207. return Initialize(hRegKey, pcwszVolume, ppwszDisplayName, ppwszDescription, pdwFlags);
  208. }
  209. // Returns the total amount of space that the compression cleaner can free.
  210. STDMETHODIMP CCompCleaner::GetSpaceUsed(DWORDLONG *pdwSpaceUsed, IEmptyVolumeCacheCallBack *picb)
  211. {
  212. cbSpaceUsed.QuadPart = 0;
  213. WalkFileSystem(picb, FALSE);
  214. picb->ScanProgress(cbSpaceUsed.QuadPart, EVCCBF_LASTNOTIFICATION, NULL);
  215. *pdwSpaceUsed = cbSpaceUsed.QuadPart;
  216. return S_OK;
  217. }
  218. // does the compression
  219. STDMETHODIMP CCompCleaner::Purge(DWORDLONG dwSpaceToFree, IEmptyVolumeCacheCallBack *picb)
  220. {
  221. bPurged = TRUE;
  222. // Compress the files
  223. WalkFileSystem(picb, TRUE);
  224. // Send the last notification to the cleanup manager
  225. picb->PurgeProgress(cbSpaceFreed.QuadPart, (cbSpaceUsed.QuadPart - cbSpaceFreed.QuadPart), EVCCBF_LASTNOTIFICATION, NULL);
  226. return S_OK;
  227. }
  228. /*
  229. ** Dialog box that displays all of the files that will be compressed by this cleaner.
  230. **
  231. ** NOTE: Per the specification for the Compression Cleaner we are not
  232. ** providing the view files functionality. However, I will leave
  233. ** the framework in place just in case we want to use it.
  234. */
  235. INT_PTR CALLBACK ViewFilesDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
  236. {
  237. HWND hwndList;
  238. LV_ITEM lviItem;
  239. CLEANFILESTRUCT *pCleanFile;
  240. switch (Msg)
  241. {
  242. case WM_INITDIALOG:
  243. hwndList = GetDlgItem(hDlg, IDC_COMP_LIST);
  244. pCleanFile = (CLEANFILESTRUCT *)lParam;
  245. ListView_DeleteAllItems(hwndList);
  246. while (pCleanFile)
  247. {
  248. lviItem.mask = LVIF_TEXT | LVIF_IMAGE;
  249. lviItem.iSubItem = 0;
  250. lviItem.iItem = 0;
  251. //
  252. //Only show files
  253. //
  254. if (!pCleanFile->bDirectory)
  255. {
  256. lviItem.pszText = pCleanFile->file;
  257. ListView_InsertItem(hwndList, &lviItem);
  258. }
  259. pCleanFile = pCleanFile->pNext;
  260. lviItem.iItem++;
  261. }
  262. break;
  263. case WM_COMMAND:
  264. switch (LOWORD(wParam))
  265. {
  266. case IDOK:
  267. case IDCANCEL:
  268. EndDialog(hDlg, 0);
  269. break;
  270. }
  271. break;
  272. default:
  273. return FALSE;
  274. }
  275. return TRUE;
  276. }
  277. // Dialog box that displays the settings for this cleaner.
  278. INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
  279. {
  280. HKEY hCompClenReg; // Handle to our registry path
  281. DWORD dwDisposition; // stuff for the reg calls
  282. DWORD dwByteCount; // Ditto
  283. DWORD dwNumDays = DEFAULT_DAYS; // Number of days setting from registry
  284. static UINT DaysIn; // Number of days initial setting
  285. UINT DaysOut; // Number of days final setting
  286. #ifdef DEBUG
  287. static CLEANFILESTRUCT *pCleanFile; // Pointer to our file list
  288. #endif // DEBUG
  289. switch(Msg) {
  290. case WM_INITDIALOG:
  291. #ifdef DEBUG
  292. pCleanFile = (CLEANFILESTRUCT *)lParam;
  293. #endif // DEBUG
  294. //
  295. // Set the range for the Days spin control (1 to 500)
  296. //
  297. SendDlgItemMessage(hDlg, IDC_COMP_SPIN, UDM_SETRANGE, 0, (LPARAM) MAKELONG ((short) MAX_DAYS, (short) MIN_DAYS));
  298. //
  299. // Get the current user setting for # days and init
  300. // the spin control edit box
  301. //
  302. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, COMPCLN_REGPATH,
  303. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL,
  304. &hCompClenReg, &dwDisposition))
  305. {
  306. dwByteCount = sizeof(dwNumDays);
  307. if (ERROR_SUCCESS == RegQueryValueEx(hCompClenReg,
  308. TEXT("Days"), NULL, NULL, (LPBYTE) &dwNumDays, &dwByteCount))
  309. {
  310. //
  311. // Got day count from registry, make sure it's
  312. // not too big or too small.
  313. //
  314. if (dwNumDays > MAX_DAYS) dwNumDays = MAX_DAYS;
  315. if (dwNumDays < MIN_DAYS) dwNumDays = MIN_DAYS;
  316. SetDlgItemInt(hDlg, IDC_COMP_EDIT, dwNumDays, FALSE);
  317. }
  318. else
  319. {
  320. //
  321. // Failed to get the day count from the registry
  322. // so just use the default.
  323. //
  324. SetDlgItemInt(hDlg, IDC_COMP_EDIT, DEFAULT_DAYS, FALSE);
  325. }
  326. }
  327. else
  328. {
  329. //
  330. // Failed to get the day count from the registry
  331. // so just use the default.
  332. //
  333. SetDlgItemInt(hDlg, IDC_COMP_EDIT, DEFAULT_DAYS, FALSE);
  334. }
  335. RegCloseKey(hCompClenReg);
  336. // Track the initial setting so we can figure out
  337. // if the user has changed the setting on the way
  338. // out.
  339. DaysIn = GetDlgItemInt(hDlg, IDC_COMP_EDIT, NULL, FALSE);
  340. break;
  341. case WM_COMMAND:
  342. switch (LOWORD(wParam))
  343. {
  344. #ifdef DEBUG
  345. case IDC_VIEW:
  346. DialogBoxParam(g_hDllModule, MAKEINTRESOURCE(IDD_COMP_VIEW), hDlg, ViewFilesDlgProc, (LPARAM)pCleanFile);
  347. break;
  348. #endif // DEBUG
  349. case IDOK:
  350. //
  351. // Get the current spin control value and write the
  352. // setting to the registry.
  353. //
  354. DaysOut = GetDlgItemInt(hDlg, IDC_COMP_EDIT, NULL, FALSE);
  355. if (DaysOut > MAX_DAYS) DaysOut = MAX_DAYS;
  356. if (DaysOut < MIN_DAYS) DaysOut = MIN_DAYS;
  357. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  358. COMPCLN_REGPATH,
  359. 0,
  360. NULL,
  361. REG_OPTION_NON_VOLATILE,
  362. KEY_WRITE,
  363. NULL,
  364. &hCompClenReg,
  365. &dwDisposition))
  366. {
  367. dwNumDays = (DWORD)DaysOut;
  368. RegSetValueEx(hCompClenReg,
  369. TEXT("Days"),
  370. 0,
  371. REG_DWORD,
  372. (LPBYTE) &dwNumDays,
  373. sizeof(dwNumDays));
  374. RegCloseKey(hCompClenReg);
  375. }
  376. // Don't care if this failed -- what can we
  377. // do about it anyway...
  378. // If the user has changed the setting we need
  379. // to recalculate the list of files.
  380. if (DaysIn != DaysOut)
  381. {
  382. g_bSettingsChange = TRUE;
  383. }
  384. // Fall thru to IDCANCEL
  385. case IDCANCEL:
  386. EndDialog(hDlg, 0);
  387. break;
  388. }
  389. break;
  390. default:
  391. return FALSE;
  392. }
  393. return TRUE;
  394. }
  395. STDMETHODIMP CCompCleaner::ShowProperties(HWND hwnd)
  396. {
  397. g_bSettingsChange = FALSE;
  398. DialogBoxParam(g_hDllModule, MAKEINTRESOURCE(IDD_COMP_SETTINGS), hwnd, SettingsDlgProc, (LPARAM)head);
  399. //
  400. // If the settings have changed we need to recalculate the
  401. // LAD Filetime.
  402. //
  403. if (g_bSettingsChange)
  404. {
  405. CalcLADFileTime();
  406. return S_OK; // Tell CleanMgr that settings have changed.
  407. }
  408. else
  409. {
  410. return S_FALSE; // Tell CleanMgr no settings changed.
  411. }
  412. return S_OK; // Shouldn't hit this but just in case.
  413. }
  414. // Deactivates the cleaner...this basically does nothing.
  415. STDMETHODIMP CCompCleaner::Deactivate(DWORD *pdwFlags)
  416. {
  417. *pdwFlags = 0;
  418. return S_OK;
  419. }
  420. /*
  421. ** checks if the file is a specified number of days
  422. ** old. If the file has not been accessed in the
  423. ** specified number of days then it can safely be
  424. ** deleted. If the file has been accessed in that
  425. ** number of days then the file will not be deleted.
  426. **
  427. ** Notes;
  428. ** Mod Log: Created by Jason Cobb (7/97)
  429. ** Adapted for Compression Cleaner by DSchott (6/98)
  430. */
  431. BOOL CCompCleaner::LastAccessisOK(FILETIME ftFileLastAccess)
  432. {
  433. //Is the last access FILETIME for this file less than the current
  434. //FILETIME minus the number of specified days?
  435. return (CompareFileTime(&ftFileLastAccess, &ftMinLastAccessTime) == -1);
  436. }
  437. // This function checks if the file is in the g_NoCompressFiles file list.
  438. // If it is, returns TRUE, else FALSE.
  439. BOOL IsDontCompressFile(LPCTSTR lpFullPath)
  440. {
  441. LPCTSTR lpFile = PathFindFileName(lpFullPath);
  442. if (lpFile)
  443. {
  444. for (int i = 0; i < ARRAYSIZE(g_NoCompressFiles); i++)
  445. {
  446. if (!lstrcmpi(lpFile, g_NoCompressFiles[i]))
  447. {
  448. MiDebugMsg((0, TEXT("File is in No Compress list: %s"), lpFile));
  449. return TRUE;
  450. }
  451. }
  452. LPCTSTR lpExt = PathFindExtension(lpFile);
  453. if (lpExt)
  454. {
  455. for (int i = 0; i < ARRAYSIZE(g_NoCompressExts); i++)
  456. {
  457. if (!lstrcmpi(lpExt, g_NoCompressExts[i]))
  458. {
  459. MiDebugMsg((0, TEXT("File has No Compress extension: %s"), lpFile));
  460. return TRUE;
  461. }
  462. }
  463. }
  464. }
  465. return FALSE; // If we made it here the file must be OK to compress.
  466. }
  467. /*
  468. ** checks if a file is open by doing a CreateFile
  469. ** with fdwShareMode of 0. If GetLastError() retuns
  470. ** ERROR_SHARING_VIOLATION then this function retuns TRUE because
  471. ** someone has the file open. Otherwise this function retuns false.
  472. **
  473. ** Notes;
  474. ** Mod Log: Created by Jason Cobb (7/97)
  475. ** Adapted for Compression Cleaner by DSchott (6/98)
  476. **------------------------------------------------------------------------------
  477. */
  478. BOOL IsFileOpen(LPTSTR lpFile, DWORD dwAttributes, FILETIME *lpftFileLastAccess)
  479. {
  480. BOOL bRet = FALSE;
  481. #if 0
  482. // Need to see if we can open file with WRITE access -- if we
  483. // can't we can't compress it. Of course if the file has R/O
  484. // attribute then we won't be able to open for WRITE. So,
  485. // we need to remove the R/O attribute long enough to try
  486. // opening the file then restore the original attributes.
  487. if (dwAttributes & FILE_ATTRIBUTE_READONLY)
  488. {
  489. SetFileAttributes(lpFile, FILE_ATTRIBUTE_NORMAL);
  490. }
  491. SetLastError(0);
  492. HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
  493. FILE_ATTRIBUTE_NORMAL, NULL);
  494. if (INVALID_HANDLE_VALUE == hFile)
  495. {
  496. DWORD dwResult = GetLastError();
  497. if ((ERROR_SHARING_VIOLATION == dwResult) || (ERROR_ACCESS_DENIED == dwResult))
  498. {
  499. bRet = TRUE;
  500. }
  501. }
  502. else
  503. {
  504. SetFileTime(hFile, NULL, lpftFileLastAccess, NULL);
  505. CloseHandle(hFile);
  506. }
  507. if (dwAttributes & FILE_ATTRIBUTE_READONLY)
  508. SetFileAttributes(lpFile, dwAttributes);
  509. #endif
  510. return bRet;
  511. }
  512. /*
  513. **
  514. ** Purpose: This function provides a common entry point for
  515. ** searching for files to compress and then compressing them
  516. **
  517. ** Notes;
  518. ** Mod Log: Created by Bret Anderson (1/01)
  519. **
  520. **------------------------------------------------------------------------------
  521. */
  522. void CCompCleaner::WalkFileSystem(IEmptyVolumeCacheCallBack *picb, BOOL bCompress)
  523. {
  524. MiDebugMsg((0, TEXT("CCompCleaner::WalkFileSystem")));
  525. cbSpaceUsed.QuadPart = 0;
  526. if (!bCompress)
  527. {
  528. //
  529. //Walk all of the folders in the folders list scanning for disk space.
  530. //
  531. for (LPTSTR lpSingleFolder = szFolder; *lpSingleFolder; lpSingleFolder += lstrlen(lpSingleFolder) + 1)
  532. WalkForUsedSpace(lpSingleFolder, picb, bCompress, 0);
  533. }
  534. else
  535. {
  536. //
  537. // Walk through the linked list of directories compressing the necessary files
  538. //
  539. CLEANFILESTRUCT *pCompDir = head;
  540. while (pCompDir)
  541. {
  542. WalkForUsedSpace(pCompDir->file, picb, bCompress, 0);
  543. pCompDir = pCompDir->pNext;
  544. }
  545. }
  546. return;
  547. }
  548. /*
  549. ** Purpose: This function gets the current last access days
  550. ** setting from the registry and calculates the magic
  551. ** filetime we're looking for when searching for files
  552. ** to compress.
  553. **
  554. ** Notes;
  555. ** Mod Log: Created by David Schott (7/98)
  556. */
  557. void CCompCleaner::CalcLADFileTime()
  558. {
  559. HKEY hCompClenReg = NULL; // Handle to our registry path
  560. DWORD dwDisposition; // stuff for the reg calls
  561. DWORD dwByteCount; // Ditto
  562. DWORD dwDaysLastAccessed = 0; // Day count from the registry setting
  563. MiDebugMsg((0, TEXT("CCompCleaner::CalcLADFileTime")));
  564. //
  565. // Get the DaysLastAccessed value from the registry.
  566. //
  567. dwDaysLastAccessed = DEFAULT_DAYS;
  568. if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_LOCAL_MACHINE, COMPCLN_REGPATH,
  569. 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ,
  570. NULL, &hCompClenReg, &dwDisposition))
  571. {
  572. dwByteCount = sizeof(dwDaysLastAccessed);
  573. RegQueryValueEx(hCompClenReg,
  574. TEXT("Days"),
  575. NULL,
  576. NULL,
  577. (LPBYTE) &dwDaysLastAccessed,
  578. &dwByteCount);
  579. RegCloseKey(hCompClenReg);
  580. }
  581. //
  582. // Verify LD setting is within range
  583. //
  584. if (dwDaysLastAccessed > MAX_DAYS)
  585. dwDaysLastAccessed = MAX_DAYS;
  586. if (dwDaysLastAccessed < MIN_DAYS)
  587. dwDaysLastAccessed = MIN_DAYS;
  588. //
  589. //Determine the LastAccessedTime
  590. //
  591. if (dwDaysLastAccessed != 0)
  592. {
  593. ULARGE_INTEGER ulTemp, ulLastAccessTime;
  594. FILETIME ft;
  595. //Determine the number of days in 100ns units
  596. ulTemp.LowPart = FILETIME_HOUR_LOW;
  597. ulTemp.HighPart = FILETIME_HOUR_HIGH;
  598. ulTemp.QuadPart *= dwDaysLastAccessed;
  599. //Get the current FILETIME
  600. GetSystemTimeAsFileTime(&ft);
  601. ulLastAccessTime.LowPart = ft.dwLowDateTime;
  602. ulLastAccessTime.HighPart = ft.dwHighDateTime;
  603. //Subtract the Last Access number of days (in 100ns units) from
  604. //the current system time.
  605. ulLastAccessTime.QuadPart -= ulTemp.QuadPart;
  606. //Save this minimal Last Access time in the FILETIME member variable
  607. //ftMinLastAccessTime.
  608. ftMinLastAccessTime.dwLowDateTime = ulLastAccessTime.LowPart;
  609. ftMinLastAccessTime.dwHighDateTime = ulLastAccessTime.HighPart;
  610. }
  611. }
  612. /*
  613. ** Purpose: This function will walk the specified directory and increment
  614. ** the member variable to indicate how much disk space these files
  615. ** are taking or it will perform the action of compressing the files
  616. ** if the bCompress variable is set.
  617. ** It will look at the dwFlags member variable to determine if it
  618. ** needs to recursively walk the tree or not.
  619. ** We no longer want to store a linked list of all files to compress
  620. ** due to extreme memory usage on large filesystems. This means
  621. ** we will walk through all the files on the system twice.
  622. ** Notes;
  623. ** Mod Log: Created by Jason Cobb (2/97)
  624. ** Adapted for Compression Cleaner by DSchott (6/98)
  625. */
  626. BOOL CCompCleaner::WalkForUsedSpace(LPCTSTR lpPath, IEmptyVolumeCacheCallBack *picb, BOOL bCompress, int depth)
  627. {
  628. BOOL bRet = TRUE;
  629. BOOL bFind = TRUE;
  630. WIN32_FIND_DATA wd;
  631. ULARGE_INTEGER dwFileSize;
  632. static DWORD dwCount = 0;
  633. TCHAR szFindPath[MAX_PATH], szAddFile[MAX_PATH];
  634. if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
  635. {
  636. BOOL bFolderFound = FALSE;
  637. bFind = TRUE;
  638. HANDLE hFind = FindFirstFile(szFindPath, &wd);
  639. while (hFind != INVALID_HANDLE_VALUE && bFind)
  640. {
  641. if (!PathCombine(szAddFile, lpPath, wd.cFileName))
  642. {
  643. // Failure here means the file name is too long, just ignore that file
  644. continue;
  645. }
  646. if (wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  647. {
  648. dwFileSize.HighPart = 0;
  649. dwFileSize.LowPart = 0;
  650. bFolderFound = TRUE;
  651. }
  652. else if ((IsFileOpen(szAddFile, wd.dwFileAttributes, &wd.ftLastAccessTime) == FALSE) &&
  653. (!(wd.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)) &&
  654. (!(wd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) &&
  655. (!(wd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE)) &&
  656. (LastAccessisOK(wd.ftLastAccessTime)) &&
  657. (!IsDontCompressFile(szAddFile)))
  658. {
  659. dwFileSize.HighPart = wd.nFileSizeHigh;
  660. dwFileSize.LowPart = wd.nFileSizeLow;
  661. if (bCompress)
  662. {
  663. if (!CompressFile(picb, szAddFile, dwFileSize))
  664. {
  665. bRet = FALSE;
  666. bFind = FALSE;
  667. break;
  668. }
  669. }
  670. else
  671. {
  672. cbSpaceUsed.QuadPart += (dwFileSize.QuadPart * 4 / 10);
  673. }
  674. }
  675. // CallBack the cleanup Manager to update the UI
  676. if ((dwCount++ % 10) == 0 && !bCompress)
  677. {
  678. if (picb && picb->ScanProgress(cbSpaceUsed.QuadPart, 0, NULL) == E_ABORT)
  679. {
  680. //
  681. //User aborted
  682. //
  683. bFind = FALSE;
  684. bRet = FALSE;
  685. break;
  686. }
  687. }
  688. bFind = FindNextFile(hFind, &wd);
  689. }
  690. FindClose(hFind);
  691. if (bRet && bFolderFound)
  692. {
  693. //
  694. //Recurse through all of the directories
  695. //
  696. if (PathCombine(szFindPath, lpPath, TEXT("*.*")))
  697. {
  698. bFind = TRUE;
  699. HANDLE hFind = FindFirstFile(szFindPath, &wd);
  700. while (hFind != INVALID_HANDLE_VALUE && bFind)
  701. {
  702. if ((wd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  703. (lstrcmp(wd.cFileName, TEXT(".")) != 0) &&
  704. (lstrcmp(wd.cFileName, TEXT("..")) != 0))
  705. {
  706. ULARGE_INTEGER cbSpaceBefore;
  707. cbSpaceBefore.QuadPart = cbSpaceUsed.QuadPart;
  708. PathCombine(szAddFile, lpPath, wd.cFileName);
  709. if (WalkForUsedSpace(szAddFile, picb, bCompress, depth + 1) == FALSE)
  710. {
  711. // User canceled
  712. bFind = FALSE;
  713. bRet = FALSE;
  714. break;
  715. }
  716. // Tag this directory for compression
  717. // We only want to tag directories that are in the root
  718. // otherwise we'll end up with a very large data structure
  719. if (cbSpaceBefore.QuadPart != cbSpaceUsed.QuadPart &&
  720. depth == 0 && !bCompress)
  721. {
  722. AddDirToList(szAddFile);
  723. }
  724. }
  725. bFind = FindNextFile(hFind, &wd);
  726. }
  727. FindClose(hFind);
  728. }
  729. }
  730. }
  731. return bRet;
  732. }
  733. // Adds a directory to the linked list of directories.
  734. BOOL CCompCleaner::AddDirToList(LPCTSTR lpFile)
  735. {
  736. BOOL bRet = TRUE;
  737. CLEANFILESTRUCT *pNew = (CLEANFILESTRUCT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pNew));
  738. if (pNew == NULL)
  739. {
  740. MiDebugMsg((0, TEXT("CCompCleaner::AddDirToList -> ERROR HeapAlloc() failed with error %d"), GetLastError()));
  741. return FALSE;
  742. }
  743. StringCchCopy(pNew->file, ARRAYSIZE(pNew->file), lpFile);
  744. if (head)
  745. pNew->pNext = head;
  746. else
  747. pNew->pNext = NULL;
  748. head = pNew;
  749. return bRet;
  750. }
  751. void CCompCleaner::FreeList(CLEANFILESTRUCT *pCleanFile)
  752. {
  753. if (pCleanFile == NULL)
  754. return;
  755. if (pCleanFile->pNext)
  756. FreeList(pCleanFile->pNext);
  757. HeapFree(GetProcessHeap(), 0, pCleanFile);
  758. }
  759. // Compresses the specified file
  760. BOOL CCompCleaner::CompressFile(IEmptyVolumeCacheCallBack *picb, LPCTSTR lpFile, ULARGE_INTEGER filesize)
  761. {
  762. ULARGE_INTEGER ulCompressedSize;
  763. ulCompressedSize.QuadPart = filesize.QuadPart;
  764. // If the file is read only, we need to remove the
  765. // R/O attribute long enough to compress the file.
  766. BOOL bFileWasRO = FALSE;
  767. DWORD dwAttributes = GetFileAttributes(lpFile);
  768. if ((0xFFFFFFFF != dwAttributes) && (dwAttributes & FILE_ATTRIBUTE_READONLY))
  769. {
  770. bFileWasRO = TRUE;
  771. SetFileAttributes(lpFile, FILE_ATTRIBUTE_NORMAL);
  772. }
  773. HANDLE hFile = CreateFile(lpFile, GENERIC_READ | GENERIC_WRITE,
  774. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
  775. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  776. if (INVALID_HANDLE_VALUE != hFile)
  777. {
  778. USHORT InBuffer = COMPRESSION_FORMAT_DEFAULT;
  779. DWORD dwBytesReturned = 0;
  780. if (DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, &InBuffer, sizeof(InBuffer),
  781. NULL, 0, &dwBytesReturned, NULL))
  782. {
  783. // Get the compressed file size so we can figure out
  784. // how much space we gained by compressing.
  785. ulCompressedSize.LowPart = GetCompressedFileSize(lpFile, &ulCompressedSize.HighPart);
  786. }
  787. CloseHandle(hFile);
  788. }
  789. // Restore the file attributes if needed
  790. if (bFileWasRO)
  791. SetFileAttributes(lpFile, dwAttributes);
  792. // Adjust the cbSpaceFreed
  793. cbSpaceFreed.QuadPart = cbSpaceFreed.QuadPart + (filesize.QuadPart - ulCompressedSize.QuadPart);
  794. // Call back the cleanup manager to update the progress bar
  795. if (picb->PurgeProgress(cbSpaceFreed.QuadPart, (cbSpaceUsed.QuadPart - cbSpaceFreed.QuadPart), 0, NULL) == E_ABORT)
  796. {
  797. // User aborted so stop compressing files
  798. MiDebugMsg((0, TEXT("CCompCleaner::PurgeFiles User abort")));
  799. return FALSE;
  800. }
  801. return TRUE;
  802. }