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.

868 lines
28 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: policy.cpp
  3. Description: Handles disk quota policy issues for both a GPE client
  4. extension and a server MMC policy snapin (see snapin.cpp).
  5. ProgressGroupPolicy is called by winlogon to process disk quota policy
  6. on the client machine. ProcessGroupPolicy instantiates a CDiskQuotaPolicy
  7. object to handle the loading and application of disk quota policy.
  8. The CDiskQuotaPolicy object is also instantiated by the MMC
  9. disk quota policy snapin to save quota policy information to the
  10. registry.
  11. A good deal of this module, especially in CDiskQuotaPolicy::Apply( ),
  12. is devoted to reporting errors to the NT event log. This is necessary
  13. because much of this code runs without UI from within winlogon. That's
  14. also why there's a lot of debugger spew.
  15. Revision History:
  16. Date Description Programmer
  17. -------- --------------------------------------------------- ----------
  18. 02/14/98 Initial creation. BrianAu
  19. 11/24/98 Added event logging settings to policy. BrianAu
  20. 11/30/98 Replaced ProcessGPO function with BrianAu
  21. ProcessGroupPolicy to support GPO interface changes.
  22. */
  23. ///////////////////////////////////////////////////////////////////////////////
  24. #include "pch.h"
  25. #pragma hdrstop
  26. #include <userenv.h>
  27. #include <gpedit.h>
  28. #include <dskquota.h>
  29. #include "policy.h"
  30. #include "registry.h"
  31. #include "guidsp.h"
  32. #include "msg.h"
  33. #include "resource.h"
  34. //
  35. // Global NT event log object.
  36. //
  37. CEventLog g_Log;
  38. //
  39. // Name of the disk quota dll.
  40. //
  41. const TCHAR g_szDskquotaDll[] = TEXT("dskquota.dll");
  42. //
  43. // Exported function called by winlogon to update policy on the client machine.
  44. //
  45. // This function is registered as a GPO extension (see selfreg.inf).
  46. //
  47. DWORD
  48. ProcessGroupPolicy(
  49. DWORD dwFlags,
  50. HANDLE hUserToken,
  51. HKEY hkeyRoot,
  52. PGROUP_POLICY_OBJECT pDeletedGPOList,
  53. PGROUP_POLICY_OBJECT pChangedGPOList,
  54. ASYNCCOMPLETIONHANDLE pHandle,
  55. BOOL *pbAbort,
  56. PFNSTATUSMESSAGECALLBACK pStatusCallback
  57. )
  58. {
  59. HRESULT hr = ERROR_SUCCESS;
  60. DBGTRACE((DM_POLICY, DL_HIGH, TEXT("ProcessGroupPolicy")));
  61. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\tdwFlags......: 0x%08X"), dwFlags));
  62. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\thUserToken...: 0x%08X"), hUserToken));
  63. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\thKeyRoot.....: 0x%08X"), hkeyRoot));
  64. //
  65. // FEATURE: Need to add support for pDeletedGPOList
  66. // If pDeletedGPOList is non-null, you should
  67. // reset the disk quotas back to their defaults first
  68. // and then apply the new settings below if appropriate
  69. //
  70. if (pChangedGPOList)
  71. {
  72. hr = g_Log.Initialize(TEXT("DiskQuota"));
  73. if (FAILED(hr))
  74. {
  75. DBGERROR((TEXT("Error 0x%08X initializing NT event log."), hr));
  76. //
  77. // Continue without event log.
  78. //
  79. }
  80. //
  81. // Only process policy info when...
  82. //
  83. // 1. Not deleting policy.
  84. //
  85. try
  86. {
  87. DBGPRINT((DM_POLICY, DL_HIGH, TEXT("Set quota policy - START.")));
  88. autoptr<CDiskQuotaPolicy> ptrPolicy(new CDiskQuotaPolicy(NULL,
  89. hkeyRoot,
  90. 0 != (GPO_INFO_FLAG_VERBOSE & dwFlags),
  91. pbAbort));
  92. DISKQUOTAPOLICYINFO dqpi;
  93. ZeroMemory(&dqpi, sizeof(dqpi));
  94. //
  95. // Load policy info from the registry and apply to local volumes.
  96. //
  97. hr = ptrPolicy->Load(&dqpi);
  98. if (SUCCEEDED(hr))
  99. {
  100. hr = ptrPolicy->Apply(&dqpi);
  101. }
  102. DBGPRINT((DM_POLICY, DL_HIGH, TEXT("Set quota policy - FINISHED.")));
  103. }
  104. catch(CAllocException& e)
  105. {
  106. DBGERROR((TEXT("Insufficient memory in ProcessGroupPolicy")));
  107. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE,
  108. 0,
  109. MSG_E_POLICY_OUTOFMEMORY);
  110. hr = E_OUTOFMEMORY;
  111. }
  112. }
  113. return hr;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // CDiskQuotaPolicy
  117. //-----------------------------------------------------------------------------
  118. //
  119. // Location of disk quota policy information in the registry. The "PolicyData"
  120. // value name is somewhat arbitrary. The policy key name string however must
  121. // coordinate with other system policy locations in the registry. It should
  122. // not change unless you have a good reason to do so and you understand the
  123. // consequences.
  124. //
  125. const TCHAR CDiskQuotaPolicy::m_szRegKeyPolicy[] = REGSTR_KEY_POLICYDATA;
  126. #ifdef POLICY_MMC_SNAPIN
  127. const TCHAR CDiskQuotaPolicy::m_szRegValPolicy[] = REGSTR_VAL_POLICYDATA;
  128. #endif
  129. CDiskQuotaPolicy::CDiskQuotaPolicy(
  130. LPGPEINFORMATION pGPEInfo,
  131. HKEY hkeyRoot,
  132. bool bVerboseEventLog,
  133. BOOL *pbAbort
  134. ) : m_cRef(0),
  135. m_pGPEInfo(pGPEInfo),
  136. m_hkeyRoot(hkeyRoot),
  137. m_pbAbort(pbAbort),
  138. m_bRootKeyOpened(false),
  139. m_bVerboseEventLog(bVerboseEventLog)
  140. {
  141. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::CDiskQuotaPolicy")));
  142. }
  143. CDiskQuotaPolicy::~CDiskQuotaPolicy(
  144. void
  145. )
  146. {
  147. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::~CDiskQuotaPolicy")));
  148. if (NULL != m_hkeyRoot && m_bRootKeyOpened)
  149. RegCloseKey(m_hkeyRoot);
  150. if (NULL != m_pGPEInfo)
  151. m_pGPEInfo->Release();
  152. }
  153. HRESULT
  154. CDiskQuotaPolicy::QueryInterface(
  155. REFIID riid,
  156. LPVOID *ppvOut
  157. )
  158. {
  159. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::~QueryInterface")));
  160. HRESULT hr = E_NOINTERFACE;
  161. *ppvOut = NULL;
  162. if (IID_IUnknown == riid || IID_IDiskQuotaPolicy == riid)
  163. {
  164. *ppvOut = this;
  165. ((LPUNKNOWN)*ppvOut)->AddRef();
  166. hr = NOERROR;
  167. }
  168. return hr;
  169. }
  170. ULONG
  171. CDiskQuotaPolicy::AddRef(
  172. void
  173. )
  174. {
  175. DBGTRACE((DM_POLICY, DL_LOW, TEXT("CDiskQuotaPolicy::AddRef")));
  176. ULONG ulReturn = m_cRef + 1;
  177. InterlockedIncrement(&m_cRef);
  178. return ulReturn;
  179. }
  180. ULONG
  181. CDiskQuotaPolicy::Release(
  182. void
  183. )
  184. {
  185. DBGTRACE((DM_POLICY, DL_LOW, TEXT("CDiskQuotaPolicy::Release")));
  186. ULONG ulReturn = m_cRef - 1;
  187. if (InterlockedDecrement(&m_cRef) == 0)
  188. {
  189. delete this;
  190. ulReturn = 0;
  191. }
  192. return ulReturn;
  193. }
  194. //
  195. // Caller can init with either:
  196. //
  197. // 1. Ptr to IGPEInformation interface. The snap in should initialize this
  198. // way since it has a pointer to an IGPEInformation
  199. // interface (LPGPEINFORMATION).
  200. //
  201. // 2. HKEY retrieved from IGPEInformation interface or from Group Policy
  202. // notification. ProcessGroupPolicy should initialize this way since it is given
  203. // the root key from winlogon.
  204. //
  205. // Can also init with both but pGPEInfo will be ignored if hkeyRoot is provided.
  206. //
  207. HRESULT
  208. CDiskQuotaPolicy::Initialize(
  209. LPGPEINFORMATION pGPEInfo,
  210. HKEY hkeyRoot
  211. )
  212. {
  213. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::Initialize")));
  214. if (NULL != m_pGPEInfo || NULL != m_hkeyRoot)
  215. return S_FALSE; // Already intialized
  216. m_hkeyRoot = hkeyRoot;
  217. m_pGPEInfo = pGPEInfo;
  218. if (NULL != m_pGPEInfo)
  219. m_pGPEInfo->AddRef();
  220. return S_OK;
  221. }
  222. //
  223. // Fill in a DISKQUOTAPOLICYINFO structure with default data.
  224. //
  225. void
  226. CDiskQuotaPolicy::InitPolicyInfo(
  227. LPDISKQUOTAPOLICYINFO pInfo
  228. )
  229. {
  230. pInfo->llDefaultQuotaThreshold = (LONGLONG)-1; // No limit.
  231. pInfo->llDefaultQuotaLimit = (LONGLONG)-1; // No limit.
  232. pInfo->dwQuotaState = 0;
  233. pInfo->dwQuotaLogFlags = 0;
  234. pInfo->bRemovableMedia = 0;
  235. }
  236. //
  237. // Initialize and load policy information into a DISKQUOTAPOLICYINFO structure.
  238. // If reg values don't exist, default values are used.
  239. //
  240. void
  241. CDiskQuotaPolicy::LoadPolicyInfo(
  242. const RegKey& key,
  243. LPDISKQUOTAPOLICYINFO pInfo
  244. )
  245. {
  246. DWORD dwValue = DWORD(-1);
  247. const struct
  248. {
  249. LPCTSTR pszValue; // Name of the "value" reg value.
  250. LPCTSTR pszUnits; // Name of the "units" reg value.
  251. LONGLONG *pValue; // Address of destination for computed limit or threshold.
  252. } rgValUnits[] = {
  253. { REGSTR_VAL_POLICY_LIMIT, REGSTR_VAL_POLICY_LIMITUNITS, &(pInfo->llDefaultQuotaLimit) },
  254. { REGSTR_VAL_POLICY_THRESHOLD, REGSTR_VAL_POLICY_THRESHOLDUNITS, &(pInfo->llDefaultQuotaThreshold) }
  255. };
  256. //
  257. // Initialize with defaults.
  258. //
  259. InitPolicyInfo(pInfo);
  260. //
  261. // Load the limit and threshold values along with their respective "units"
  262. // factor. The factor is a number [1..6] that represents the required
  263. // multiplier to convert the "value" to a byte value.
  264. //
  265. // 1 = KB, 2 = MB, 3 = GB, 4 = TB, 5 = PB, 6 = EB
  266. //
  267. // Bytes = value << (factor * 10).
  268. //
  269. // Given: value = 250
  270. // factor = 2 (MB)
  271. //
  272. // Bytes = 250 << 20
  273. // = 262,144,000
  274. // = 250 MB
  275. //
  276. for (int i = 0; i < ARRAYSIZE(rgValUnits); i++)
  277. {
  278. dwValue = DWORD(-1);
  279. DWORD dwUnits = DWORD(-1);
  280. key.GetValue(rgValUnits[i].pszValue, &dwValue);
  281. key.GetValue(rgValUnits[i].pszUnits, &dwUnits);
  282. *(rgValUnits[i].pValue) = LONGLONG(-1);
  283. // must cast dwValue to "int" since DWORDs are never negative
  284. // Don't need to test dwUnits for negative since negative DWORDs
  285. // turn into huge positive values, so it will fail the "6 >= dwUnits"
  286. // test.
  287. if (0 <= (int)dwValue && 6 >= dwUnits)
  288. {
  289. *(rgValUnits[i].pValue) = LONGLONG(dwValue) << (10 * dwUnits);
  290. }
  291. }
  292. //
  293. // This logic for setting the dwQuotaState member is the same as that
  294. // used in VolumePropPage::QuotaStateFromControls (volprop.cpp).
  295. //
  296. DWORD dwEnable = 0;
  297. DWORD dwEnforce = 0;
  298. key.GetValue(REGSTR_VAL_POLICY_ENABLE, &dwEnable);
  299. key.GetValue(REGSTR_VAL_POLICY_ENFORCE, &dwEnforce);
  300. if (dwEnable)
  301. {
  302. if (dwEnforce)
  303. {
  304. pInfo->dwQuotaState = DISKQUOTA_STATE_ENFORCE;
  305. }
  306. else
  307. {
  308. pInfo->dwQuotaState = DISKQUOTA_STATE_TRACK;
  309. }
  310. }
  311. else
  312. {
  313. pInfo->dwQuotaState = DISKQUOTA_STATE_DISABLED;
  314. }
  315. //
  316. // Get event logging settings.
  317. //
  318. DWORD dwLog = 0;
  319. key.GetValue(REGSTR_VAL_POLICY_LOGLIMIT, &dwLog);
  320. DISKQUOTA_SET_LOG_USER_LIMIT(pInfo->dwQuotaLogFlags, dwLog);
  321. dwLog = 0;
  322. key.GetValue(REGSTR_VAL_POLICY_LOGTHRESHOLD, &dwLog);
  323. DISKQUOTA_SET_LOG_USER_THRESHOLD(pInfo->dwQuotaLogFlags, dwLog);
  324. //
  325. // Determine if policy is to be applied to removable as well as fixed
  326. // media.
  327. //
  328. if (SUCCEEDED(key.GetValue(REGSTR_VAL_POLICY_REMOVABLEMEDIA, &dwValue)))
  329. {
  330. pInfo->bRemovableMedia = boolify(dwValue);
  331. }
  332. }
  333. //
  334. // Load machine policy information from the registry. See comment
  335. // in CDiskQuotaPolicy::Save( ) for registry location information.
  336. //
  337. HRESULT
  338. CDiskQuotaPolicy::Load(
  339. LPDISKQUOTAPOLICYINFO pInfo
  340. )
  341. {
  342. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::Load")));
  343. DBGASSERT((NULL != pInfo));
  344. if (NULL == m_pGPEInfo && NULL == m_hkeyRoot)
  345. {
  346. DBGERROR((TEXT("Policy object not initialized")));
  347. return E_FAIL; // Not initialized.
  348. }
  349. HRESULT hr = E_FAIL;
  350. if (NULL == m_hkeyRoot &&
  351. SUCCEEDED(hr = m_pGPEInfo->GetRegistryKey(GPO_SECTION_MACHINE, &m_hkeyRoot)))
  352. {
  353. m_bRootKeyOpened = true;
  354. }
  355. if (NULL != m_hkeyRoot)
  356. {
  357. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Opening reg key 0x%08X \"%s\""), m_hkeyRoot, m_szRegKeyPolicy));
  358. RegKey key(m_hkeyRoot, m_szRegKeyPolicy);
  359. hr = key.Open(KEY_READ);
  360. if (SUCCEEDED(hr))
  361. {
  362. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Reading disk quota policy information.")));
  363. LoadPolicyInfo(key, pInfo);
  364. if (m_bVerboseEventLog)
  365. {
  366. //
  367. // Report successful information retrieval.
  368. //
  369. g_Log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_POLICY_INFOLOADED);
  370. }
  371. }
  372. else if (ERROR_FILE_NOT_FOUND != HRESULT_CODE(hr)) // Key doesn't always exist.
  373. {
  374. DBGERROR((TEXT("Error 0x%08X opening policy reg key"), hr));
  375. g_Log.Push(hr, CEventLog::eFmtHex);
  376. g_Log.Push(m_szRegKeyPolicy),
  377. g_Log.Push(hr, CEventLog::eFmtSysErr);
  378. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_GPEREGKEYOPEN);
  379. }
  380. }
  381. else
  382. {
  383. DBGERROR((TEXT("m_hkeyRoot is NULL")));
  384. g_Log.Push(hr, CEventLog::eFmtHex);
  385. g_Log.Push(hr, CEventLog::eFmtSysErr);
  386. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_GPEREGKEYROOT);
  387. }
  388. return hr;
  389. }
  390. #ifdef POLICY_MMC_SNAPIN
  391. //
  392. // NOTE: This code has been disabled because we're moving from using an MMC
  393. // snapin to an ADM-file format that fits better into the current
  394. // MMC software policy scheme. This Save() function was required for
  395. // the snapin implementation but is not required when we use an ADM file.
  396. // The function was originally written to store a whole DISKQUOTAPOLICYINFO
  397. // structure in the registry and the original version of Load() was
  398. // written to read a whole DISKQUOTAPOLICYINFO structure as REG_BINARY.
  399. // Since the ADM file works with specific reg values rather than a single
  400. // REG_BINARY, Load() was modified to work with values stored using
  401. // the ADM format. Save() has not been modified.
  402. //
  403. // If we reactivate this function to use in a snapin, we need to
  404. // update it to write out data in a format acceptable to Load().
  405. // [brianau - 6/25/98]
  406. //
  407. // This function saves the policy info to the following registry value on the
  408. // local machine.
  409. //
  410. // HKCU\Software\Microsoft\GPE\{98E1D3C1-9DC1-11D1-8544-0000F8046117}Machine\Software\Policies\Microsoft\Windows NT\DiskQuota\PolicyData"
  411. //
  412. // The call to PolicyChanged( ) flushes the data to the server file:
  413. //
  414. // \\<server>\SysVol\Policies\{98E1D3C1-9DC1-11D1-8544-0000F8046117}\machine\registry.pol
  415. //
  416. // where <server> is the name of the server.
  417. //
  418. //
  419. HRESULT
  420. CDiskQuotaPolicy::Save(
  421. LPCDISKQUOTAPOLICYINFO pInfo
  422. )
  423. {
  424. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::Save")));
  425. DBGASSERT((NULL != pInfo));
  426. if (NULL == m_pGPEInfo && NULL == m_hkeyRoot)
  427. {
  428. DBGERROR((TEXT("Policy object not initialized")));
  429. return E_FAIL; // Not initialized.
  430. }
  431. HRESULT hr = E_FAIL;
  432. if (NULL == m_hkeyRoot &&
  433. SUCCEEDED(hr = m_pGPEInfo->GetRegistryKey(GPO_SECTION_MACHINE, &m_hkeyRoot)))
  434. {
  435. m_bRootKeyOpened = true;
  436. }
  437. if (NULL != m_hkeyRoot)
  438. {
  439. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Creating reg key 0x%08X \"%s\""), m_hkeyRoot, m_szRegKeyPolicy));
  440. RegKey key(m_hkeyRoot, m_szRegKeyPolicy);
  441. hr = key.Open(KEY_WRITE, true);
  442. if (SUCCEEDED(hr))
  443. {
  444. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Setting reg value \"%s\""), m_szRegValPolicy));
  445. hr = key.SetValue(m_szRegValPolicy, (LPBYTE)pInfo, sizeof(*pInfo));
  446. if (SUCCEEDED(hr))
  447. {
  448. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Calling PolicyChanged().")));
  449. if (FAILED(hr = m_pGPEInfo->PolicyChanged(TRUE)))
  450. DBGERROR((TEXT("Error 0x%08X returned by PolicyChanged()"), hr));
  451. }
  452. else
  453. DBGERROR((TEXT("Error 0x%08X setting policy reg value"), hr));
  454. }
  455. else
  456. DBGERROR((TEXT("Error 0x%08X opening policy reg key"), hr));
  457. }
  458. else
  459. DBGERROR((TEXT("m_hkeyRoot is NULL")));
  460. return hr;
  461. }
  462. #endif // POLICY_MMC_SNAPIN
  463. //
  464. // Apply policy information to all local NTFS volumes. Removable media
  465. // are optional per a value in the policy information structure.
  466. //
  467. HRESULT
  468. CDiskQuotaPolicy::Apply(
  469. LPCDISKQUOTAPOLICYINFO pInfo
  470. )
  471. {
  472. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::Apply")));
  473. DBGASSERT((NULL != pInfo));
  474. HRESULT hr = NOERROR;
  475. BOOL bAborted = m_pbAbort ? *m_pbAbort : FALSE;
  476. try
  477. {
  478. if (!bAborted)
  479. {
  480. CString strVolCompleted;
  481. //
  482. // Get list of drives on which to set policy.
  483. //
  484. CArray<CString> rgstrDrives;
  485. hr = GetDriveNames(&rgstrDrives, pInfo->bRemovableMedia);
  486. int cDrives = rgstrDrives.Count();
  487. if (SUCCEEDED(hr) && 0 < cDrives)
  488. {
  489. //
  490. // Get the disk quota class factory. This way we don't
  491. // call CoCreateInstance for each drive. Only call it once then
  492. // call the class factory's CreateInstance for each drive.
  493. // Should be more efficient.
  494. //
  495. com_autoptr<IClassFactory> pcf;
  496. hr = CoCreateInstance(CLSID_DiskQuotaControl,
  497. NULL,
  498. CLSCTX_INPROC_SERVER,
  499. IID_IClassFactory,
  500. reinterpret_cast<void **>(pcf.getaddr()));
  501. if (SUCCEEDED(hr))
  502. {
  503. bAborted = m_pbAbort ? *m_pbAbort : FALSE;
  504. for (int i = 0; i < cDrives && !bAborted; i++)
  505. {
  506. DBGPRINT((DM_POLICY, DL_MID, TEXT("Setting policy for \"%s\""), rgstrDrives[i].Cstr()));
  507. //
  508. // Get a quota control object and initialize it for drive[i].
  509. // Init with read/write access.
  510. //
  511. com_autoptr<IDiskQuotaControl> pdqc;
  512. hr = pcf->CreateInstance(NULL,
  513. IID_IDiskQuotaControl,
  514. reinterpret_cast<void **>(pdqc.getaddr()));
  515. if (SUCCEEDED(hr))
  516. {
  517. hr = pdqc->Initialize(rgstrDrives[i], TRUE);
  518. if (SUCCEEDED(hr))
  519. {
  520. //
  521. // Set the quota information on the volume.
  522. //
  523. if (FAILED(hr = pdqc->SetQuotaLogFlags(pInfo->dwQuotaLogFlags)))
  524. {
  525. DBGERROR((TEXT("Error 0x%08X setting log flags"), hr));
  526. goto setpolerr;
  527. }
  528. if (FAILED(hr = pdqc->SetDefaultQuotaThreshold(pInfo->llDefaultQuotaThreshold)))
  529. {
  530. DBGERROR((TEXT("Error 0x%08X setting default threshold"), hr));
  531. goto setpolerr;
  532. }
  533. if (FAILED(hr = pdqc->SetDefaultQuotaLimit(pInfo->llDefaultQuotaLimit)))
  534. {
  535. DBGERROR((TEXT("Error 0x%08X setting default limit"), hr));
  536. goto setpolerr;
  537. }
  538. //
  539. // Set state last in case we're enabling quotas. That way
  540. // any rebuild activity will come after the other settings have
  541. // been set.
  542. //
  543. if (FAILED(hr = pdqc->SetQuotaState(DISKQUOTA_STATE_MASK & pInfo->dwQuotaState)))
  544. {
  545. DBGERROR((TEXT("Error 0x%08X setting quota state"), hr));
  546. goto setpolerr;
  547. }
  548. goto setpolsuccess;
  549. }
  550. else
  551. DBGERROR((TEXT("Error 0x%08X initializing vol \"%s\""),
  552. hr, rgstrDrives[i].Cstr()));
  553. setpolerr:
  554. //
  555. // Record error for this particular volume.
  556. //
  557. g_Log.Push(hr, CEventLog::eFmtHex);
  558. g_Log.Push(rgstrDrives[i].Cstr());
  559. g_Log.Push(hr, CEventLog::eFmtSysErr);
  560. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_SETQUOTA);
  561. setpolsuccess:
  562. if (m_bVerboseEventLog && SUCCEEDED(hr))
  563. {
  564. //
  565. // Append name to list of drives that have been successful.
  566. //
  567. strVolCompleted += rgstrDrives[i];
  568. strVolCompleted += CString(TEXT(" "));
  569. }
  570. pdqc = NULL; // This releases pdqc.
  571. }
  572. else
  573. {
  574. DBGERROR((TEXT("CreateInstance failed with error 0x%08X"), hr));
  575. g_Log.Push(hr, CEventLog::eFmtHex);
  576. g_Log.Push(hr, CEventLog::eFmtSysErr);
  577. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_CREATEQUOTACONTROL);
  578. }
  579. }
  580. pcf = NULL; // This releases pcf.
  581. }
  582. else
  583. {
  584. DBGERROR((TEXT("CoCreateInstance failed with error 0x%08X"), hr));
  585. g_Log.Push(hr, CEventLog::eFmtHex);
  586. g_Log.Push(hr, CEventLog::eFmtSysErr);
  587. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_CREATECLASSFACTORY);
  588. }
  589. }
  590. else
  591. {
  592. DBGERROR((TEXT("Error 0x%08X getting drive name list"), hr));
  593. g_Log.Push(hr, CEventLog::eFmtHex);
  594. g_Log.Push(hr, CEventLog::eFmtSysErr);
  595. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_GETDRIVELIST);
  596. }
  597. if (m_bVerboseEventLog && 0 < strVolCompleted.Length())
  598. {
  599. //
  600. // Log successful completions by listing volumes
  601. // and applied policy values.
  602. //
  603. CString s;
  604. LONGLONG llValue;
  605. g_Log.Push(strVolCompleted);
  606. g_Log.Push(!DISKQUOTA_IS_DISABLED(pInfo->dwQuotaState));
  607. g_Log.Push(DISKQUOTA_IS_ENFORCED(pInfo->dwQuotaState));
  608. llValue = pInfo->llDefaultQuotaThreshold;
  609. if (LONGLONG(-1) != llValue)
  610. {
  611. XBytes::FormatByteCountForDisplay(llValue, s.GetBuffer(40), 40);
  612. s.ReleaseBuffer();
  613. }
  614. else
  615. {
  616. s.Format(g_hInstDll, IDS_NO_LIMIT);
  617. }
  618. g_Log.Push(s);
  619. llValue = pInfo->llDefaultQuotaLimit;
  620. if (LONGLONG(-1) != llValue)
  621. {
  622. XBytes::FormatByteCountForDisplay(llValue, s.GetBuffer(40), 40);
  623. s.ReleaseBuffer();
  624. }
  625. else
  626. {
  627. s.Format(g_hInstDll, IDS_NO_LIMIT);
  628. }
  629. g_Log.Push(s);
  630. g_Log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_POLICY_FINISHED);
  631. }
  632. }
  633. }
  634. catch(CAllocException& e)
  635. {
  636. DBGERROR((TEXT("Insufficient memory.")));
  637. hr = E_OUTOFMEMORY;
  638. g_Log.ReportEvent(EVENTLOG_ERROR_TYPE, 0, MSG_E_POLICY_OUTOFMEMORY);
  639. }
  640. if (bAborted)
  641. {
  642. g_Log.ReportEvent(EVENTLOG_INFORMATION_TYPE, 0, MSG_I_POLICY_ABORTED);
  643. }
  644. return hr;
  645. }
  646. //
  647. // Build a list of drives to which policy can be applied.
  648. //
  649. HRESULT
  650. CDiskQuotaPolicy::GetDriveNames( // [ static ]
  651. CArray<CString> *prgstrDrives, // Output array of name strings.
  652. bool bRemovableMedia // Include removable media?
  653. )
  654. {
  655. DBGTRACE((DM_POLICY, DL_MID, TEXT("CDiskQuotaPolicy::GetDriveNames")));
  656. DBGASSERT((NULL != prgstrDrives));
  657. HRESULT hr = NOERROR;
  658. //
  659. // Get buffer size required to hold drive name strings.
  660. //
  661. int cch = GetLogicalDriveStrings(0, NULL);
  662. //
  663. // Allocate buffer and get the strings.
  664. //
  665. array_autoptr<TCHAR> ptrDrives(new TCHAR[cch + 1]);
  666. if (0 < GetLogicalDriveStrings(cch, ptrDrives.get()))
  667. {
  668. //
  669. // Iterate over all of the drive name strings. Append to the
  670. // string array each that can accept policy.
  671. //
  672. DblNulTermListIter iter(ptrDrives.get());
  673. LPCTSTR pszDrive;
  674. while(iter.Next(&pszDrive))
  675. {
  676. if (S_OK == OkToApplyPolicy(pszDrive, bRemovableMedia))
  677. {
  678. prgstrDrives->Append(CString(pszDrive));
  679. }
  680. }
  681. }
  682. else
  683. {
  684. DWORD dwErr = GetLastError();
  685. DBGERROR((TEXT("GetLogicalDriveStrings failed with error %d"), dwErr));
  686. hr = HRESULT_FROM_WIN32(dwErr);
  687. }
  688. return hr;
  689. }
  690. //
  691. // Returns: S_OK = OK to set policy on drive.
  692. // S_FALSE = Not OK to set policy
  693. // Other = Error occured. Not OK to set policy.
  694. //
  695. HRESULT
  696. CDiskQuotaPolicy::OkToApplyPolicy( // [ static ]
  697. LPCTSTR pszDrive, // Drive (volume) name string.
  698. bool bRemovableMedia // Include removable media?
  699. )
  700. {
  701. DBGTRACE((DM_POLICY, DL_LOW, TEXT("CDiskQuotaPolicy::OkToApplyPolicy")));
  702. HRESULT hr = S_FALSE;
  703. //
  704. // Primary filter is drive type.
  705. //
  706. UINT uDriveType = GetDriveType(pszDrive);
  707. switch(uDriveType)
  708. {
  709. case DRIVE_UNKNOWN:
  710. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" is UNKNOWN"), pszDrive));
  711. return S_FALSE;
  712. case DRIVE_NO_ROOT_DIR:
  713. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" has no root dir"), pszDrive));
  714. return S_FALSE;
  715. case DRIVE_REMOTE:
  716. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" is REMOTE"), pszDrive));
  717. return S_FALSE;
  718. case DRIVE_CDROM:
  719. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" is CDROM"), pszDrive));
  720. return S_FALSE;
  721. case DRIVE_RAMDISK:
  722. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" is RAMDISK"), pszDrive));
  723. return S_FALSE;
  724. case DRIVE_REMOVABLE:
  725. //
  726. // Removable is allowable if policy says it is. It should be
  727. // disallowed by default since using disk quota on removable media
  728. // doesn't make a lot of sense in most situations.
  729. //
  730. if (!bRemovableMedia)
  731. {
  732. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" is REMOVABLE"), pszDrive));
  733. return S_FALSE;
  734. }
  735. //
  736. // Fall through...
  737. //
  738. case DRIVE_FIXED:
  739. //
  740. // Fixed drives are always acceptable.
  741. //
  742. break;
  743. default:
  744. DBGERROR((TEXT("Unknown drive type %d for \"%s\""), uDriveType, pszDrive));
  745. return S_FALSE;
  746. }
  747. //
  748. // Next filter is support for NTFS quotas. We do the drive-type check first
  749. // because it doesn't require hitting the disks. GetVolumeInformation does
  750. // hit the disk so we only want to do it if necessary.
  751. //
  752. DWORD dwFlags = 0;
  753. if (GetVolumeInformation(pszDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0))
  754. {
  755. if (FILE_VOLUME_QUOTAS & dwFlags)
  756. {
  757. DBGPRINT((DM_POLICY, DL_LOW, TEXT("Ok to set policy on \"%s\""), pszDrive));
  758. hr = S_OK;
  759. }
  760. else
  761. {
  762. DBGPRINT((DM_POLICY, DL_LOW, TEXT("\"%s\" doesn't support NTFS quotas"), pszDrive));
  763. }
  764. }
  765. else
  766. {
  767. DWORD dwErr = GetLastError();
  768. DBGERROR((TEXT("GetVolumeInformation failed with error %d for \"%s\""),
  769. dwErr, pszDrive));
  770. hr = HRESULT_FROM_WIN32(dwErr);
  771. }
  772. return hr;
  773. }