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.

2208 lines
72 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // Ntfs.cpp : Implementation of NTFS Store driver classes
  4. //
  5. // Copyright (C) 1998, Microsoft Corporation
  6. //
  7. // File: ntfs.cpp
  8. //
  9. // Contents: Implementation of NTFS Store driver classes.
  10. //
  11. // Classes: CNtfsStoreDriver, CNtfsPropertyStream
  12. //
  13. // Functions:
  14. //
  15. // History: 3/31/98 KeithLau Created
  16. //
  17. //-----------------------------------------------------------------------------
  18. #include "stdafx.h"
  19. #include "filehc.h"
  20. #include "mailmsg.h"
  21. #include "mailmsgi.h"
  22. #include "mailmsgprops.h"
  23. #include "seo.h"
  24. #include "seo_i.c"
  25. #include "Ntfs.h"
  26. #include "smtpmsg.h"
  27. HANDLE g_hTransHeap = NULL;
  28. //
  29. // Instantiate the CLSIDs
  30. //
  31. #ifdef __cplusplus
  32. extern "C"{
  33. #endif
  34. const CLSID CLSID_NtfsStoreDriver = {0x609b7e3a,0xc918,0x11d1,{0xaa,0x5e,0x00,0xc0,0x4f,0xa3,0x5b,0x82}};
  35. const CLSID CLSID_NtfsEnumMessages = {0xbbddbdec,0xc947,0x11d1,{0xaa,0x5e,0x00,0xc0,0x4f,0xa3,0x5b,0x82}};
  36. const CLSID CLSID_NtfsPropertyStream = {0x6d7572ac,0xc939,0x11d1,{0xaa,0x5e,0x00,0xc0,0x4f,0xa3,0x5b,0x82}};
  37. #ifdef __cplusplus
  38. }
  39. #endif
  40. //
  41. // Define the store file prefix and extension
  42. //
  43. #define NTFS_STORE_FILE_PREFIX _T("\\NTFS_")
  44. #define NTFS_STORE_FILE_WILDCARD _T("*")
  45. #define NTFS_STORE_FILE_EXTENSION _T(".EML")
  46. #define NTFS_FAT_STREAM_FILE_EXTENSION_1ST _T(".STM")
  47. #define NTFS_FAT_STREAM_FILE_EXTENSION_LIVE _T(".STL")
  48. #define NTFS_STORE_FILE_PROPERTY_STREAM_1ST _T(":PROPERTIES")
  49. #define NTFS_STORE_FILE_PROPERTY_STREAM_LIVE _T(":PROPERTIES-LIVE")
  50. #define NTFS_STORE_BACKSLASH _T("\\")
  51. #define NTFS_QUEUE_DIRECTORY_SUFFIX _T("\\Queue")
  52. #define NTFS_DROP_DIRECTORY_SUFFIX _T("\\Drop")
  53. #define SMTP_MD_ID_BEGIN_RESERVED 0x00009000
  54. #define MD_MAIL_QUEUE_DIR (SMTP_MD_ID_BEGIN_RESERVED+11 )
  55. #define MD_MAIL_DROP_DIR (SMTP_MD_ID_BEGIN_RESERVED+18 )
  56. /////////////////////////////////////////////////////////////////////////////
  57. // CDriverUtils
  58. //
  59. // Define the registry path location in the registry
  60. //
  61. #define NTFS_STORE_DIRECTORY_REG_PATH _T("Software\\Microsoft\\Exchange\\StoreDriver\\Ntfs\\%u")
  62. #define NTFS_STORE_DIRECTORY_REG_NAME _T("StoreDir")
  63. //
  64. // Instantiate static
  65. //
  66. DWORD CDriverUtils::s_dwCounter = 0;
  67. CEventLogWrapper *CNtfsStoreDriver::g_pEventLog = NULL;
  68. CDriverUtils::CDriverUtils()
  69. {
  70. }
  71. CDriverUtils::~CDriverUtils()
  72. {
  73. }
  74. HRESULT CDriverUtils::LoadStoreDirectory(
  75. DWORD dwInstanceId,
  76. LPTSTR szStoreDirectory,
  77. DWORD *pdwLength
  78. )
  79. {
  80. HKEY hKey = NULL;
  81. DWORD dwRes;
  82. DWORD dwType;
  83. TCHAR szStoreDirPath[MAX_PATH];
  84. HRESULT hrRes = S_OK;
  85. _ASSERT(szStoreDirectory);
  86. _ASSERT(pdwLength);
  87. TraceFunctEnter("CDriverUtils::LoadStoreDirectory");
  88. // Build up the registry path given instance ID
  89. wsprintf(szStoreDirPath, NTFS_STORE_DIRECTORY_REG_PATH, dwInstanceId);
  90. // Open the registry key
  91. dwRes = (DWORD)RegOpenKeyEx(
  92. HKEY_LOCAL_MACHINE,
  93. szStoreDirPath,
  94. 0,
  95. KEY_ALL_ACCESS,
  96. &hKey);
  97. if (dwRes != ERROR_SUCCESS)
  98. {
  99. hrRes = HRESULT_FROM_WIN32(dwRes);
  100. goto Cleanup;
  101. }
  102. // Adjust the buffer size for character type ...
  103. (*pdwLength) *= sizeof(TCHAR);
  104. dwRes = (DWORD)RegQueryValueEx(
  105. hKey,
  106. NTFS_STORE_DIRECTORY_REG_NAME,
  107. NULL,
  108. &dwType,
  109. (LPBYTE)szStoreDirectory,
  110. pdwLength);
  111. if (dwRes != ERROR_SUCCESS)
  112. {
  113. hrRes = HRESULT_FROM_WIN32(dwRes);
  114. ErrorTrace((LPARAM)0, "Failed to load store driver directory %u", dwRes);
  115. }
  116. else
  117. {
  118. hrRes = S_OK;
  119. DebugTrace((LPARAM)0, "Store directory is %s", szStoreDirectory);
  120. }
  121. Cleanup:
  122. if (hKey)
  123. RegCloseKey(hKey);
  124. TraceFunctLeave();
  125. return(hrRes);
  126. }
  127. HRESULT CDriverUtils::GetStoreFileName(
  128. LPTSTR szStoreDirectory,
  129. LPTSTR szStoreFilename,
  130. DWORD *pdwLength
  131. )
  132. {
  133. _ASSERT(szStoreDirectory);
  134. _ASSERT(szStoreFilename);
  135. _ASSERT(pdwLength);
  136. DWORD dwLength = *pdwLength;
  137. DWORD dwStrLen;
  138. FILETIME ftTime;
  139. dwStrLen = lstrlen(szStoreDirectory);
  140. if (dwLength <= dwStrLen)
  141. return(HRESULT_FROM_WIN32(ERROR_MORE_DATA));
  142. lstrcpy(szStoreFilename, szStoreDirectory);
  143. dwLength -= dwStrLen;
  144. szStoreFilename += dwStrLen;
  145. *pdwLength = dwStrLen;
  146. GetSystemTimeAsFileTime(&ftTime);
  147. dwStrLen = lstrlen(NTFS_STORE_FILE_PREFIX) +
  148. lstrlen(NTFS_STORE_FILE_EXTENSION) +
  149. 26;
  150. if (dwLength <= dwStrLen)
  151. return(HRESULT_FROM_WIN32(ERROR_MORE_DATA));
  152. wsprintf(szStoreFilename,
  153. "%s%08x%08x%08x%s",
  154. NTFS_STORE_FILE_PREFIX,
  155. ftTime.dwLowDateTime,
  156. ftTime.dwHighDateTime,
  157. InterlockedIncrement((PLONG)&s_dwCounter),
  158. NTFS_STORE_FILE_EXTENSION);
  159. *pdwLength += (dwStrLen + 1);
  160. return(S_OK);
  161. }
  162. HRESULT CDriverUtils::GetStoreFileFromPath(
  163. LPTSTR szStoreFilename,
  164. IMailMsgPropertyStream **ppStream,
  165. PFIO_CONTEXT *ppFIOContentFile,
  166. BOOL fCreate,
  167. BOOL fIsFAT,
  168. IMailMsgProperties *pMsg,
  169. GUID guidInstance
  170. )
  171. {
  172. // OK, got a file, get the content handle and property stream
  173. HRESULT hrRes = S_OK;
  174. HANDLE hFile = INVALID_HANDLE_VALUE;
  175. HANDLE hStream = INVALID_HANDLE_VALUE;
  176. TCHAR szPropertyStream[MAX_PATH << 1];
  177. BOOL fDeleteOnCleanup = FALSE;
  178. _ASSERT(fCreate || (guidInstance == GUID_NULL));
  179. IMailMsgPropertyStream *pIStream = NULL;
  180. TraceFunctEnter("CDriverUtils::GetStoreFileFromPath");
  181. if (ppFIOContentFile)
  182. {
  183. // Open the content ...
  184. hFile = CreateFile(
  185. szStoreFilename,
  186. GENERIC_READ | GENERIC_WRITE, // Read / Write
  187. FILE_SHARE_READ, // Shared read
  188. NULL, // Default security
  189. (fCreate)?CREATE_NEW:OPEN_EXISTING, // Create new or open existing file
  190. FILE_FLAG_OVERLAPPED | // Overlapped access
  191. FILE_FLAG_SEQUENTIAL_SCAN, // Seq scan
  192. //FILE_FLAG_WRITE_THROUGH, // Write through cache
  193. NULL); // No template
  194. if (hFile == INVALID_HANDLE_VALUE)
  195. goto Cleanup;
  196. }
  197. DebugTrace(0, "--- start ---");
  198. if (ppStream)
  199. {
  200. DebugTrace((LPARAM)0, "Handling stream in %s", fIsFAT?"FAT":"NTFS");
  201. BOOL fTryLiveStream = !fCreate;
  202. BOOL fNoLiveStream = FALSE;
  203. BOOL fLiveWasCorrupt = FALSE;
  204. do {
  205. // Open the alternate file stream
  206. lstrcpy(szPropertyStream, szStoreFilename);
  207. if (fTryLiveStream) {
  208. DebugTrace((LPARAM) 0, "TryingLive");
  209. lstrcat(szPropertyStream,
  210. fIsFAT?NTFS_FAT_STREAM_FILE_EXTENSION_LIVE:
  211. NTFS_STORE_FILE_PROPERTY_STREAM_LIVE);
  212. } else {
  213. DebugTrace((LPARAM) 0, "Trying1st");
  214. lstrcat(szPropertyStream,
  215. fIsFAT?NTFS_FAT_STREAM_FILE_EXTENSION_1ST:
  216. NTFS_STORE_FILE_PROPERTY_STREAM_1ST);
  217. }
  218. DebugTrace((LPARAM) 0, "File: %s", szPropertyStream);
  219. hStream = CreateFile(
  220. szPropertyStream,
  221. GENERIC_READ | GENERIC_WRITE, // Read / Write
  222. FILE_SHARE_READ, // No sharing
  223. NULL, // Default security
  224. (fCreate)?
  225. CREATE_NEW: // Create new or
  226. OPEN_EXISTING, // Open existing file
  227. FILE_FLAG_SEQUENTIAL_SCAN, // Seq scan
  228. //FILE_FLAG_WRITE_THROUGH, // Write through cache
  229. NULL); // No template
  230. if (hStream == INVALID_HANDLE_VALUE) {
  231. DebugTrace((LPARAM) 0, "Got INVALID_HANDLE_VALUE\n");
  232. if (fTryLiveStream && GetLastError() == ERROR_FILE_NOT_FOUND) {
  233. DebugTrace((LPARAM) 0, "livestream and FILE_NOT_FOUND\n");
  234. hrRes = S_INVALIDSTREAM;
  235. fNoLiveStream = TRUE;
  236. } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
  237. DebugTrace((LPARAM) 0, "no primary stream either\n");
  238. hrRes = S_NO_FIRST_COMMIT;
  239. } else {
  240. DebugTrace((LPARAM) 0,
  241. "Returning CreateFile error %lu\n", GetLastError());
  242. hrRes = HRESULT_FROM_WIN32(GetLastError());
  243. goto Cleanup;
  244. }
  245. } else {
  246. hrRes = CoCreateInstance(
  247. CLSID_NtfsPropertyStream,
  248. NULL,
  249. CLSCTX_INPROC_SERVER,
  250. IID_IMailMsgPropertyStream,
  251. (LPVOID *)&pIStream);
  252. if (FAILED(hrRes))
  253. goto Cleanup;
  254. hrRes = ((CNtfsPropertyStream *)pIStream)->SetHandle(hStream,
  255. guidInstance,
  256. fTryLiveStream,
  257. pMsg);
  258. if (FAILED(hrRes)) {
  259. if (fCreate) fDeleteOnCleanup = TRUE;
  260. goto Cleanup;
  261. }
  262. }
  263. if (hrRes == S_INVALIDSTREAM || hrRes == S_NO_FIRST_COMMIT) {
  264. if (hrRes == S_INVALIDSTREAM) {
  265. DebugTrace((LPARAM) 0,
  266. "SetHandle returned S_INVALIDSTREAM\n");
  267. } else {
  268. DebugTrace((LPARAM) 0,
  269. "SetHandle returned S_NO_FIRST_COMMIT\n");
  270. }
  271. if (fTryLiveStream) {
  272. // if we were working with the live stream then retry with
  273. // the 1st commited stream
  274. fTryLiveStream = FALSE;
  275. fLiveWasCorrupt = !fNoLiveStream;
  276. DebugTrace((LPARAM) 0, "Trying regular stream\n");
  277. if (pIStream) {
  278. pIStream->Release();
  279. pIStream = NULL;
  280. }
  281. if (hrRes == S_NO_FIRST_COMMIT) hrRes = S_INVALIDSTREAM;
  282. } else {
  283. // the 1st committed stream was invalid. this can
  284. // only occur when the message was not acked.
  285. //
  286. // if the live stream existed and this one is invalid
  287. // then something is weird, so we go down the eventlog
  288. // path (returning S_OK). S_OK works because currently
  289. // pStream->m_fStreamHasHeader is set to 0. Either
  290. // the mailmsg signature check will fail or mailmsg
  291. // won't be able to read the entire master header.
  292. // either way will cause the message to be ignored and
  293. // eventlog'd
  294. //
  295. // CEnumNtfsMessages::Next will delete the message
  296. // for us
  297. if (hrRes == S_INVALIDSTREAM) {
  298. if (fLiveWasCorrupt && !fNoLiveStream) {
  299. hrRes = S_OK;
  300. DebugTrace((LPARAM) 0, "Returning S_OK because there was no live stream\n");
  301. } else {
  302. hrRes = S_NO_FIRST_COMMIT;
  303. DebugTrace((LPARAM) 0, "Returning S_NO_FIRST_COMMIT\n");
  304. }
  305. } else {
  306. DebugTrace((LPARAM) 0, "Returning S_NO_FIRST_COMMIT\n");
  307. }
  308. }
  309. } else {
  310. DebugTrace((LPARAM) 0, "SetHandle returned other error %x\n", hrRes);
  311. }
  312. _ASSERT(SUCCEEDED(hrRes));
  313. if (FAILED(hrRes)) goto Cleanup;
  314. } while (hrRes == S_INVALIDSTREAM);
  315. }
  316. // Fill in the return values
  317. if (ppStream) {
  318. *ppStream = pIStream;
  319. }
  320. if (ppFIOContentFile) {
  321. *ppFIOContentFile = AssociateFile(hFile);
  322. if (*ppFIOContentFile == NULL) {
  323. goto Cleanup;
  324. }
  325. }
  326. TraceFunctLeave();
  327. return(hrRes);
  328. Cleanup:
  329. if (hrRes == S_OK) hrRes = HRESULT_FROM_WIN32(GetLastError());
  330. if (SUCCEEDED(hrRes)) hrRes = E_FAIL;
  331. if (hStream != INVALID_HANDLE_VALUE) {
  332. CloseHandle(hStream);
  333. }
  334. if (hFile != INVALID_HANDLE_VALUE) {
  335. CloseHandle(hFile);
  336. }
  337. if (fDeleteOnCleanup) {
  338. // this only happens at file creation time. There is no
  339. // live file to worry about. The below code is too simplistic
  340. // if we do have to delete a live stream.
  341. _ASSERT(fCreate);
  342. DeleteFile(szStoreFilename);
  343. DeleteFile(szPropertyStream);
  344. }
  345. TraceFunctLeave();
  346. return(hrRes);
  347. }
  348. HRESULT CDriverUtils::SetMessageContext(
  349. IMailMsgProperties *pMsg,
  350. LPBYTE pbContext,
  351. DWORD dwLength
  352. )
  353. {
  354. HRESULT hrRes = S_OK;
  355. BYTE pbData[(MAX_PATH * 2) + sizeof(CLSID)];
  356. _ASSERT(pMsg);
  357. if (dwLength > (MAX_PATH * 2))
  358. return(E_INVALIDARG);
  359. MoveMemory(pbData, &CLSID_NtfsStoreDriver, sizeof(CLSID));
  360. MoveMemory(pbData + sizeof(CLSID), pbContext, dwLength);
  361. dwLength += sizeof(CLSID);
  362. hrRes = pMsg->PutProperty(
  363. IMMPID_MPV_STORE_DRIVER_HANDLE,
  364. dwLength,
  365. pbData);
  366. // make S_FALSE return S_OK
  367. if (SUCCEEDED(hrRes)) hrRes = S_OK;
  368. return(hrRes);
  369. }
  370. HRESULT CDriverUtils::GetMessageContext(
  371. IMailMsgProperties *pMsg,
  372. LPBYTE pbContext,
  373. DWORD *pdwLength
  374. )
  375. {
  376. HRESULT hrRes = S_OK;
  377. DWORD dwLength;
  378. _ASSERT(pMsg);
  379. _ASSERT(pbContext);
  380. _ASSERT(pdwLength);
  381. dwLength = *pdwLength;
  382. hrRes = pMsg->GetProperty(
  383. IMMPID_MPV_STORE_DRIVER_HANDLE,
  384. dwLength,
  385. pdwLength,
  386. pbContext);
  387. if (SUCCEEDED(hrRes))
  388. {
  389. dwLength = *pdwLength;
  390. // Verify length and CLSID
  391. if ((dwLength < sizeof(CLSID)) ||
  392. (*(CLSID *)pbContext != CLSID_NtfsStoreDriver))
  393. hrRes = NTE_BAD_SIGNATURE;
  394. else
  395. {
  396. // Copy the context info
  397. dwLength -= sizeof(CLSID);
  398. MoveMemory(pbContext, pbContext + sizeof(CLSID), dwLength);
  399. *pdwLength = dwLength;
  400. }
  401. }
  402. return(hrRes);
  403. }
  404. HRESULT CDriverUtils::IsStoreDirectoryFat(
  405. LPTSTR szStoreDirectory,
  406. BOOL *pfIsFAT
  407. )
  408. {
  409. HRESULT hrRes = S_OK;
  410. TCHAR szDisk[MAX_PATH];
  411. TCHAR szFileSystem[MAX_PATH];
  412. DWORD lSerial, lMaxLen, lFlags;
  413. DWORD dwLength;
  414. UINT uiErrorMode;
  415. _ASSERT(szStoreDirectory);
  416. _ASSERT(pfIsFAT);
  417. TraceFunctEnter("CDriverUtils::IsStoreDirectoryFat");
  418. // OK, find the root drive, make sure we handle UNC names
  419. dwLength = lstrlen(szStoreDirectory);
  420. if (dwLength < 2)
  421. return(E_INVALIDARG);
  422. szDisk[0] = szStoreDirectory[0];
  423. szDisk[1] = szStoreDirectory[1];
  424. if ((szDisk[0] == _T('\\')) && (szDisk[1] == _T('\\')))
  425. {
  426. DWORD dwCount = 0;
  427. LPTSTR pTemp = szDisk + 2;
  428. DebugTrace((LPARAM)0, "UNC Name: %s", szStoreDirectory);
  429. // Handle UNC
  430. szStoreDirectory += 2;
  431. while (*szStoreDirectory)
  432. if (*pTemp = *szStoreDirectory++)
  433. if (*pTemp++ == _T('\\'))
  434. {
  435. dwCount++;
  436. if (dwCount == 2)
  437. break;
  438. }
  439. if (dwCount == 2)
  440. *pTemp = _T('\0');
  441. else if (dwCount == 1)
  442. {
  443. *pTemp++ = _T('\\');
  444. *pTemp = _T('\0');
  445. }
  446. else
  447. return(E_INVALIDARG);
  448. }
  449. else
  450. {
  451. DebugTrace((LPARAM)0, "Local drive: %s", szStoreDirectory);
  452. // Local path
  453. if (!_istalpha(szDisk[0]) || (szDisk[1] != _T(':')))
  454. return(E_INVALIDARG);
  455. szDisk[2] = _T('\\');
  456. szDisk[3] = _T('\0');
  457. }
  458. // Call the system to determine what file system we have here,
  459. // we set the error mode here to avoid unsightly pop-ups.
  460. uiErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  461. if (GetVolumeInformation(
  462. szDisk,
  463. NULL, 0,
  464. &lSerial, &lMaxLen, &lFlags,
  465. szFileSystem, MAX_PATH))
  466. {
  467. DebugTrace((LPARAM)0, "File system is: %s", szFileSystem);
  468. if (!lstrcmpi(szFileSystem, _T("NTFS")))
  469. *pfIsFAT = FALSE;
  470. else if (!lstrcmpi(szFileSystem, _T("FAT")))
  471. *pfIsFAT = TRUE;
  472. else if (!lstrcmpi(szFileSystem, _T("FAT32")))
  473. *pfIsFAT = TRUE;
  474. else
  475. hrRes = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  476. }
  477. else
  478. hrRes = HRESULT_FROM_WIN32(GetLastError());
  479. SetErrorMode(uiErrorMode);
  480. TraceFunctLeave();
  481. return(hrRes);
  482. }
  483. /////////////////////////////////////////////////////////////////////////////
  484. // CNtfsStoreDriver
  485. //
  486. //
  487. // Instantiate static
  488. //
  489. DWORD CNtfsStoreDriver::sm_cCurrentInstances = 0;
  490. CRITICAL_SECTION CNtfsStoreDriver::sm_csLockInstList;
  491. LIST_ENTRY CNtfsStoreDriver::sm_ListHead;
  492. CNtfsStoreDriver::CNtfsStoreDriver()
  493. {
  494. m_fInitialized = FALSE;
  495. m_fIsShuttingDown = FALSE;
  496. *m_szQueueDirectory = _T('\0');
  497. m_pSMTPServer = NULL;
  498. m_lRefCount = 0;
  499. m_fIsFAT = TRUE; // Assume we ARE on a fat partition until we discover otherwise
  500. UuidCreate(&m_guidInstance);
  501. m_ppoi = NULL;
  502. m_InstLEntry.Flink = NULL;
  503. m_InstLEntry.Blink = NULL;
  504. }
  505. CNtfsStoreDriver::~CNtfsStoreDriver() {
  506. CNtfsStoreDriver::LockList();
  507. if (m_InstLEntry.Flink != NULL) {
  508. _ASSERT(m_InstLEntry.Blink != NULL);
  509. HRESULT hr = CNtfsStoreDriver::RemoveSinkInstance(
  510. (IUnknown *)(ISMTPStoreDriver *)this);
  511. _ASSERT(SUCCEEDED(hr));
  512. }
  513. _ASSERT(m_InstLEntry.Flink == NULL);
  514. _ASSERT(m_InstLEntry.Blink == NULL);
  515. CNtfsStoreDriver::UnLockList();
  516. }
  517. DECLARE_STD_IUNKNOWN_METHODS(NtfsStoreDriver, IMailMsgStoreDriver)
  518. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::AllocMessage(
  519. IMailMsgProperties *pMsg,
  520. DWORD dwFlags,
  521. IMailMsgPropertyStream **ppStream,
  522. PFIO_CONTEXT *phContentFile,
  523. IMailMsgNotify *pNotify
  524. )
  525. {
  526. HRESULT hrRes = S_OK;
  527. TCHAR szStoreFileName[MAX_PATH << 1];
  528. DWORD dwLength;
  529. _ASSERT(pMsg);
  530. _ASSERT(ppStream);
  531. _ASSERT(phContentFile);
  532. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::AllocMessage");
  533. if (!m_fInitialized)
  534. return(E_FAIL);
  535. if (m_fIsShuttingDown)
  536. {
  537. DebugTrace((LPARAM)this, "Failing because shutting down");
  538. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  539. }
  540. if (!pMsg || !ppStream || !phContentFile)
  541. return(E_POINTER);
  542. do {
  543. // Get a file name
  544. dwLength = sizeof(szStoreFileName);
  545. hrRes = CDriverUtils::GetStoreFileName(
  546. m_szQueueDirectory,
  547. szStoreFileName,
  548. &dwLength);
  549. if (FAILED(hrRes))
  550. return(hrRes);
  551. // Create the file
  552. hrRes = CDriverUtils::GetStoreFileFromPath(
  553. szStoreFileName,
  554. ppStream,
  555. phContentFile,
  556. TRUE,
  557. m_fIsFAT,
  558. pMsg,
  559. m_guidInstance
  560. );
  561. } while (hrRes == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS));
  562. //
  563. // GetStoreFileFromPath can return S_NO_FIRST_COMMIT and no handle
  564. // treat this as an error.
  565. //
  566. if (S_NO_FIRST_COMMIT == hrRes) hrRes = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  567. if (FAILED(hrRes))
  568. return(hrRes);
  569. ((CNtfsPropertyStream *)*ppStream)->SetInfo(this);
  570. // OK, save the file name as a store driver context
  571. hrRes = CDriverUtils::SetMessageContext(
  572. pMsg,
  573. (LPBYTE)szStoreFileName,
  574. dwLength * sizeof(TCHAR));
  575. if (FAILED(hrRes))
  576. {
  577. // Release all file resources
  578. ReleaseContext(*phContentFile);
  579. DecCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  580. _VERIFY((*ppStream)->Release() == 0);
  581. } else {
  582. // Update counters
  583. IncCtr(m_ppoi, NTFSDRV_QUEUE_LENGTH);
  584. IncCtr(m_ppoi, NTFSDRV_NUM_ALLOCS);
  585. IncCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  586. }
  587. TraceFunctLeave();
  588. return(hrRes);
  589. }
  590. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::EnumMessages(
  591. IMailMsgEnumMessages **ppEnum
  592. )
  593. {
  594. HRESULT hrRes = S_OK;
  595. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::EnumMessages");
  596. if (!m_fInitialized)
  597. return(E_FAIL);
  598. if (m_fIsShuttingDown)
  599. {
  600. DebugTrace((LPARAM)this, "Failing because shutting down");
  601. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  602. }
  603. if (!ppEnum)
  604. return E_POINTER;
  605. hrRes = CoCreateInstance(
  606. CLSID_NtfsEnumMessages,
  607. NULL,
  608. CLSCTX_INPROC_SERVER,
  609. IID_IMailMsgEnumMessages,
  610. (LPVOID *)ppEnum);
  611. if (SUCCEEDED(hrRes))
  612. {
  613. ((CNtfsEnumMessages *)(*ppEnum))->SetInfo(this);
  614. hrRes = ((CNtfsEnumMessages *)(*ppEnum))->SetStoreDirectory(
  615. m_szQueueDirectory,
  616. m_fIsFAT);
  617. }
  618. TraceFunctLeave();
  619. return(hrRes);
  620. }
  621. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::ReOpen(
  622. IMailMsgProperties *pMsg,
  623. IMailMsgPropertyStream **ppStream,
  624. PFIO_CONTEXT *phContentFile,
  625. IMailMsgNotify *pNotify
  626. )
  627. {
  628. HRESULT hrRes = S_OK;
  629. TCHAR szStoreFileName[MAX_PATH * 2];
  630. DWORD dwLength = MAX_PATH * 2;
  631. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::ReOpen");
  632. if (!m_fInitialized)
  633. return(E_FAIL);
  634. if (!pMsg)
  635. return E_POINTER;
  636. if (m_fIsShuttingDown)
  637. {
  638. // We allow reopen to occur when we are pending shutdown.
  639. // This gives a chance to reopen the streams and commit any
  640. // unchanged data
  641. DebugTrace((LPARAM)this, "ReOpening while shutting down ...");
  642. }
  643. // Now we have to load the file name from the context
  644. dwLength *= sizeof(TCHAR);
  645. hrRes = CDriverUtils::GetMessageContext(
  646. pMsg,
  647. (LPBYTE)szStoreFileName,
  648. &dwLength);
  649. if (FAILED(hrRes))
  650. return(hrRes);
  651. // Got the file name, just open the files
  652. hrRes = CDriverUtils::GetStoreFileFromPath(
  653. szStoreFileName,
  654. ppStream,
  655. phContentFile,
  656. FALSE,
  657. m_fIsFAT,
  658. pMsg);
  659. //
  660. // GetStoreFileFromPath can return S_NO_FIRST_COMMIT and no handle
  661. // treat this as an error.
  662. //
  663. if (S_NO_FIRST_COMMIT == hrRes) hrRes = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  664. if (SUCCEEDED(hrRes) && ppStream) {
  665. ((CNtfsPropertyStream *)*ppStream)->SetInfo(this);
  666. }
  667. if (SUCCEEDED(hrRes)) {
  668. if (phContentFile) IncCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  669. }
  670. TraceFunctLeave();
  671. return(hrRes);
  672. }
  673. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::ReAllocMessage(
  674. IMailMsgProperties *pOriginalMsg,
  675. IMailMsgProperties *pNewMsg,
  676. IMailMsgPropertyStream **ppStream,
  677. PFIO_CONTEXT *phContentFile,
  678. IMailMsgNotify *pNotify
  679. )
  680. {
  681. HRESULT hrRes = S_OK;
  682. TCHAR szStoreFileName[MAX_PATH * 2];
  683. DWORD dwLength = MAX_PATH * 2;
  684. IMailMsgPropertyStream *pStream;
  685. PFIO_CONTEXT hContentFile;
  686. _ASSERT(pOriginalMsg);
  687. _ASSERT(pNewMsg);
  688. _ASSERT(ppStream);
  689. _ASSERT(phContentFile);
  690. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::ReAllocMessage");
  691. if (!m_fInitialized)
  692. return(E_FAIL);
  693. if (m_fIsShuttingDown)
  694. {
  695. DebugTrace((LPARAM)this, "Failing because shutting down");
  696. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  697. }
  698. // Now we have to load the file name from the context
  699. dwLength *= sizeof(TCHAR);
  700. hrRes = CDriverUtils::GetMessageContext(
  701. pOriginalMsg,
  702. (LPBYTE)szStoreFileName,
  703. &dwLength);
  704. if (FAILED(hrRes))
  705. return(hrRes);
  706. // Allocate a new message
  707. hrRes = AllocMessage(
  708. pNewMsg,
  709. 0,
  710. &pStream,
  711. &hContentFile,
  712. NULL);
  713. if (FAILED(hrRes))
  714. return(hrRes);
  715. // Copy the content from original message to new message
  716. hrRes = pOriginalMsg->CopyContentToFile(
  717. hContentFile,
  718. NULL);
  719. if (SUCCEEDED(hrRes))
  720. {
  721. *ppStream = pStream;
  722. *phContentFile = hContentFile;
  723. }
  724. else
  725. {
  726. HRESULT myRes;
  727. // Delete on failure
  728. pStream->Release();
  729. ReleaseContext(hContentFile);
  730. DecCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  731. myRes = Delete(pNewMsg, NULL);
  732. _ASSERT(myRes);
  733. }
  734. TraceFunctLeave();
  735. return(hrRes);
  736. }
  737. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::Delete(
  738. IMailMsgProperties *pMsg,
  739. IMailMsgNotify *pNotify
  740. )
  741. {
  742. HRESULT hrRes = S_OK;
  743. TCHAR szStoreFileName[MAX_PATH * 2];
  744. TCHAR szStoreFileNameStl[MAX_PATH * 2];
  745. DWORD dwLength = MAX_PATH * 2;
  746. _ASSERT(pMsg);
  747. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::Delete");
  748. if (!m_fInitialized)
  749. return(E_FAIL);
  750. if (!pMsg)
  751. return E_POINTER;
  752. if (m_fIsShuttingDown)
  753. {
  754. // We would allow deletes during shutdown
  755. DebugTrace((LPARAM)this, "Deleteing while shutting down ...");
  756. }
  757. // Now we have to load the file name from the context
  758. dwLength *= sizeof(TCHAR);
  759. hrRes = CDriverUtils::GetMessageContext(
  760. pMsg,
  761. (LPBYTE)szStoreFileName,
  762. &dwLength);
  763. if (FAILED(hrRes))
  764. return(hrRes);
  765. // Got the file name, delete the file
  766. // For FAT, we know we can force delete the stream, but we are not
  767. // so sure about the content file. So we always try to delete the
  768. // content file first, if it succeeds, we delete the stream file.
  769. // If it fails, we will keep the stream intact so we can at least
  770. // use the stream to debug what's going on.
  771. if (!DeleteFile(szStoreFileName)) {
  772. DWORD cRetries = 0;
  773. hrRes = HRESULT_FROM_WIN32(GetLastError());
  774. // in hotmail we've found that delete sometimes fails with
  775. // a sharing violation even though we've closed all handles.
  776. // in this case we try again
  777. for (cRetries = 0;
  778. hrRes == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) && cRetries < 5;
  779. cRetries++)
  780. {
  781. Sleep(0);
  782. if (DeleteFile(szStoreFileName)) {
  783. hrRes = S_OK;
  784. } else {
  785. hrRes = HRESULT_FROM_WIN32(GetLastError());
  786. }
  787. }
  788. _ASSERT(SUCCEEDED(hrRes));
  789. ErrorTrace((LPARAM) this,
  790. "DeleteFile(%s) failed with %lu, cRetries=%lu, hrRes=%x",
  791. szStoreFileName, GetLastError(), cRetries, hrRes);
  792. } else if (m_fIsFAT) {
  793. // Wiped the content, now wipe the stream
  794. DWORD cRetries = 0;
  795. lstrcpy(szStoreFileNameStl, szStoreFileName);
  796. lstrcat(szStoreFileName, NTFS_FAT_STREAM_FILE_EXTENSION_1ST);
  797. if (!DeleteFile(szStoreFileName)) {
  798. hrRes = HRESULT_FROM_WIN32(GetLastError());
  799. for (cRetries = 0;
  800. hrRes == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) && cRetries < 5;
  801. cRetries++)
  802. {
  803. Sleep(0);
  804. if (DeleteFile(szStoreFileName)) {
  805. hrRes = S_OK;
  806. } else {
  807. hrRes = HRESULT_FROM_WIN32(GetLastError());
  808. }
  809. }
  810. _ASSERT(SUCCEEDED(hrRes));
  811. ErrorTrace((LPARAM) this,
  812. "DeleteFile(%s) failed with %lu, cRetries=%lu, hrRes=%x",
  813. szStoreFileName, GetLastError(), cRetries, hrRes);
  814. }
  815. lstrcat(szStoreFileNameStl, NTFS_FAT_STREAM_FILE_EXTENSION_LIVE);
  816. // this can fail, since we don't always have a live stream
  817. DeleteFile(szStoreFileNameStl);
  818. }
  819. if (SUCCEEDED(hrRes)) {
  820. DecCtr(m_ppoi, NTFSDRV_QUEUE_LENGTH);
  821. IncCtr(m_ppoi, NTFSDRV_NUM_DELETES);
  822. }
  823. TraceFunctLeave();
  824. return(hrRes);
  825. }
  826. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::CloseContentFile(
  827. IMailMsgProperties *pMsg,
  828. PFIO_CONTEXT hContentFile
  829. )
  830. {
  831. HRESULT hrRes = S_OK;
  832. _ASSERT(pMsg);
  833. _ASSERT(hContentFile!=NULL);
  834. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::CloseContentFile");
  835. if (!m_fInitialized)
  836. return (E_FAIL);
  837. if (m_fIsShuttingDown)
  838. {
  839. // We would allow content files to be closed during shutdown
  840. DebugTrace((LPARAM)this, "Closing content file while shutting down ...");
  841. }
  842. #ifdef DEBUG
  843. TCHAR szStoreFileName[MAX_PATH * 2];
  844. DWORD dwLength = MAX_PATH * 2;
  845. dwLength *= sizeof(TCHAR);
  846. _ASSERT(SUCCEEDED(CDriverUtils::GetMessageContext(pMsg,(LPBYTE)szStoreFileName,&dwLength)));
  847. #endif
  848. ReleaseContext(hContentFile);
  849. DecCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  850. TraceFunctLeave();
  851. return (hrRes);
  852. }
  853. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::Init(
  854. DWORD dwInstance,
  855. IUnknown *pBinding,
  856. IUnknown *pServer,
  857. DWORD dwReason,
  858. IUnknown **ppStoreDriver
  859. )
  860. {
  861. HRESULT hrRes = S_OK;
  862. DWORD dwLength = sizeof(m_szQueueDirectory);
  863. REFIID iidStoreDriverBinding = GUID_NULL;
  864. IUnknown * pTempStoreDriver = NULL;
  865. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::Init");
  866. // We will treat all dwReasons as equal ...
  867. //NK** : We need to treat binding change differently in order to set the correct
  868. //enumeration status - we do it before returning from here
  869. if (m_fInitialized)
  870. return(HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED));
  871. if (m_fIsShuttingDown)
  872. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  873. // Try to load the store directory
  874. DebugTrace((LPARAM)this, "Initializing instance %u", dwInstance);
  875. //Grab a lock for the duration of this function
  876. //
  877. CNtfsStoreDriver::LockList();
  878. pTempStoreDriver = CNtfsStoreDriver::LookupSinkInstance(dwInstance, iidStoreDriverBinding);
  879. if(pTempStoreDriver)
  880. {
  881. //Found a valid store driver
  882. pTempStoreDriver->AddRef();
  883. *ppStoreDriver = (IUnknown *)(ISMTPStoreDriver *)pTempStoreDriver;
  884. CNtfsStoreDriver::UnLockList();
  885. return S_OK;
  886. }
  887. DWORD BuffSize = sizeof(m_szQueueDirectory);
  888. // Get the SMTP server interface
  889. m_pSMTPServer = NULL;
  890. if (pServer &&
  891. !SUCCEEDED(pServer->QueryInterface(IID_ISMTPServer, (LPVOID *)&m_pSMTPServer)))
  892. m_pSMTPServer = NULL;
  893. // Read the metabase if we have a server, otherwise read from the registry
  894. if(m_pSMTPServer)
  895. {
  896. hrRes = m_pSMTPServer->ReadMetabaseString(MD_MAIL_QUEUE_DIR, (unsigned char *) m_szQueueDirectory, &BuffSize, FALSE);
  897. if (FAILED(hrRes))
  898. {
  899. //retry once, then fall through
  900. ErrorTrace((LPARAM)this, "failed to read queue directory from metabase -%x", hrRes);
  901. BuffSize = sizeof(m_szQueueDirectory);
  902. hrRes = m_pSMTPServer->ReadMetabaseString(MD_MAIL_QUEUE_DIR, (unsigned char *) m_szQueueDirectory, &BuffSize, FALSE);
  903. }
  904. }
  905. else
  906. {
  907. DebugTrace((LPARAM)this, "NTFSDRV Getting config from registry");
  908. hrRes = CDriverUtils::LoadStoreDirectory(
  909. dwInstance,
  910. m_szQueueDirectory,
  911. &dwLength);
  912. if (SUCCEEDED(hrRes))
  913. {
  914. // Deduce the queue directory
  915. lstrcat(m_szQueueDirectory, NTFS_QUEUE_DIRECTORY_SUFFIX);
  916. }
  917. }
  918. // return failure code if we failed to get a queue directory, to avoid message loss.
  919. if (FAILED(hrRes))
  920. {
  921. ErrorTrace((LPARAM)this, "CNtfsStoreDriver::Init failed -%x", hrRes);
  922. CNtfsStoreDriver::UnLockList();
  923. return hrRes;
  924. }
  925. // Detect the file system
  926. hrRes = CDriverUtils::IsStoreDirectoryFat(
  927. m_szQueueDirectory,
  928. &m_fIsFAT);
  929. m_fInitialized = TRUE;
  930. m_fIsShuttingDown = FALSE;
  931. m_dwInstance = dwInstance;
  932. m_lRefCount = 0;
  933. //NK** MAke binding GUID a member and start storing it
  934. DebugTrace((LPARAM)this, "Queue directory: %s", m_szQueueDirectory);
  935. // Return a store driver only if we succeeded initialization
  936. if (ppStoreDriver)
  937. {
  938. *ppStoreDriver = (IUnknown *)(ISMTPStoreDriver *)this;
  939. AddRef();
  940. // if we are the first instance then initialize perfmon
  941. if (IsListEmpty(&sm_ListHead)) {
  942. InitializePerformanceStatistics();
  943. }
  944. CNtfsStoreDriver::InsertSinkInstance(&m_InstLEntry);
  945. }
  946. WCHAR wszPerfInstanceName[MAX_INSTANCE_NAME];
  947. _snwprintf(wszPerfInstanceName, MAX_INSTANCE_NAME, L"SMTP #%u", dwInstance);
  948. wszPerfInstanceName[MAX_INSTANCE_NAME-1] = L'\0';
  949. m_ppoi = CreatePerfObjInstance(wszPerfInstanceName);
  950. TraceFunctLeaveEx((LPARAM)this);
  951. // Always return S_OK
  952. CNtfsStoreDriver::UnLockList();
  953. return(S_OK);
  954. }
  955. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::PrepareForShutdown(
  956. DWORD dwReason
  957. )
  958. {
  959. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::PrepareForShutdown");
  960. m_fIsShuttingDown = TRUE;
  961. TraceFunctLeaveEx((LPARAM)this);
  962. return(S_OK);
  963. }
  964. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::Shutdown(
  965. DWORD dwReason
  966. )
  967. {
  968. DWORD dwWaitTime = 0;
  969. HRESULT hr = S_OK;
  970. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::Shutdown");
  971. m_fIsShuttingDown = TRUE;
  972. _ASSERT(m_lRefCount == 0);
  973. #if 0
  974. // BUG - 80960
  975. // Now wait for all our references to come back
  976. while (m_lRefCount)
  977. {
  978. _ASSERT(m_lRefCount >= 0);
  979. Sleep(100);
  980. dwWaitTime += 100;
  981. DebugTrace((LPARAM)this,
  982. "[%u ms] Waiting for objects to be released (%u outstanding)",
  983. dwWaitTime, m_lRefCount);
  984. }
  985. #endif
  986. if(m_pSMTPServer)
  987. {
  988. m_pSMTPServer->Release();
  989. m_pSMTPServer = NULL;
  990. }
  991. if (m_ppoi) {
  992. delete m_ppoi;
  993. m_ppoi = NULL;
  994. }
  995. CNtfsStoreDriver::LockList();
  996. hr = CNtfsStoreDriver::RemoveSinkInstance((IUnknown *)(ISMTPStoreDriver *)this);
  997. // if we are the last instance then shutdown perfmon
  998. if (IsListEmpty(&sm_ListHead)) {
  999. ShutdownPerformanceStatistics();
  1000. }
  1001. CNtfsStoreDriver::UnLockList();
  1002. if(FAILED(hr))
  1003. {
  1004. //We failed to remove this sink from the global list
  1005. _ASSERT(0);
  1006. }
  1007. m_fInitialized = FALSE;
  1008. m_fIsShuttingDown = FALSE;
  1009. TraceFunctLeaveEx((LPARAM)this);
  1010. return(S_OK);
  1011. }
  1012. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::LocalDelivery(
  1013. IMailMsgProperties *pMsg,
  1014. DWORD dwRecipCount,
  1015. DWORD *pdwRecipIndexes,
  1016. IMailMsgNotify *pNotify
  1017. )
  1018. {
  1019. HRESULT hrRes = S_OK;
  1020. TCHAR szStoreFileName[MAX_PATH * 2];
  1021. TCHAR szCopyFileName[MAX_PATH * 2];
  1022. LPTSTR pszFileName;
  1023. DWORD dwLength = MAX_PATH * 2;
  1024. _ASSERT(pMsg);
  1025. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::LocalDelivery");
  1026. TraceFunctLeaveEx((LPARAM)this);
  1027. return (hrRes);
  1028. }
  1029. static void LogEventCorruptMessage(CEventLogWrapper *pEventLog,
  1030. IMailMsgProperties *pMsg,
  1031. char *pszQueueDirectory,
  1032. HRESULT hrLog)
  1033. {
  1034. HRESULT hr;
  1035. char szMessageFile[MAX_PATH];
  1036. DWORD dwLength = sizeof(szMessageFile);
  1037. const char *rgszSubstrings[] = { szMessageFile, pszQueueDirectory };
  1038. hr = CDriverUtils::GetMessageContext(pMsg, (LPBYTE) szMessageFile, &dwLength);
  1039. if (FAILED(hr)) {
  1040. strcpy(szMessageFile, "<unknown>");
  1041. }
  1042. pEventLog->LogEvent(NTFSDRV_INVALID_FILE_IN_QUEUE,
  1043. 2,
  1044. rgszSubstrings,
  1045. EVENTLOG_WARNING_TYPE,
  1046. hrLog,
  1047. LOGEVENT_DEBUGLEVEL_MEDIUM,
  1048. szMessageFile,
  1049. LOGEVENT_FLAG_ALWAYS);
  1050. }
  1051. static void DeleteNeverAckdMessage(CEventLogWrapper *pEventLog,
  1052. IMailMsgProperties *pMsg,
  1053. char *pszQueueDirectory,
  1054. HRESULT hrLog,
  1055. BOOL fIsFAT)
  1056. {
  1057. HRESULT hr;
  1058. char szMessageFile[MAX_PATH+50];
  1059. char szMessageFileSTL[MAX_PATH+50];
  1060. DWORD dwLength = MAX_PATH;
  1061. TraceFunctEnter("DeleteNeverAckdMessage");
  1062. hr = CDriverUtils::GetMessageContext(pMsg, (LPBYTE) szMessageFile, &dwLength);
  1063. if (FAILED(hr)) {
  1064. _ASSERT(FALSE && "GetMessageContext failed");
  1065. return;
  1066. }
  1067. DebugTrace((LPARAM) 0, "Deleting: %s\n", szMessageFile);
  1068. DeleteFile(szMessageFile);
  1069. if (fIsFAT) {
  1070. // Wiped the content, now wipe the stream
  1071. lstrcpy(szMessageFileSTL, szMessageFile);
  1072. lstrcat(szMessageFile, NTFS_FAT_STREAM_FILE_EXTENSION_1ST);
  1073. DeleteFile(szMessageFile);
  1074. lstrcat(szMessageFileSTL, NTFS_FAT_STREAM_FILE_EXTENSION_LIVE);
  1075. // this can fail, since we don't always have a live stream
  1076. DeleteFile(szMessageFileSTL);
  1077. }
  1078. }
  1079. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::EnumerateAndSubmitMessages(
  1080. IMailMsgNotify *pNotify
  1081. )
  1082. {
  1083. HRESULT hrRes = S_OK;
  1084. IMailMsgEnumMessages *pEnum = NULL;
  1085. TraceFunctEnterEx((LPARAM)this, "CNtfsStoreDriver::EnumerateAndSubmitMessages");
  1086. if (!m_fInitialized)
  1087. return (E_FAIL);
  1088. if (m_fIsShuttingDown)
  1089. goto Shutdown;
  1090. // Assert we got all the pieces ...
  1091. if (!m_pSMTPServer) return S_FALSE;
  1092. // Now, get an enumerator from our peer IMailMsgStoreDriver and
  1093. // start enumerating away ...
  1094. hrRes = EnumMessages(&pEnum);
  1095. if (SUCCEEDED(hrRes))
  1096. {
  1097. IMailMsgProperties *pMsg = NULL;
  1098. IMailMsgPropertyStream *pStream = NULL;
  1099. PFIO_CONTEXT hContentFile = NULL;
  1100. do
  1101. {
  1102. // Check for shut down
  1103. if (m_fIsShuttingDown)
  1104. goto Shutdown;
  1105. // Create an instance of the message object, note
  1106. // we reuse messages from a failed attempt
  1107. if (!pMsg)
  1108. {
  1109. hrRes = CoCreateInstance(
  1110. CLSID_MsgImp,
  1111. NULL,
  1112. CLSCTX_INPROC_SERVER,
  1113. IID_IMailMsgProperties,
  1114. (LPVOID *)&pMsg);
  1115. // Next, check if we are over the inbound cutoff limit. If so, we will release the message
  1116. // and not proceed.
  1117. if (SUCCEEDED(hrRes))
  1118. {
  1119. DWORD dwCreationFlags;
  1120. hrRes = pMsg->GetDWORD(
  1121. IMMPID_MPV_MESSAGE_CREATION_FLAGS,
  1122. &dwCreationFlags);
  1123. if (FAILED(hrRes) ||
  1124. (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED))
  1125. {
  1126. // If we fail to get this property of if the inbound cutoff
  1127. // exceeded flag is set, discard the message and return failure
  1128. if (SUCCEEDED(hrRes))
  1129. {
  1130. DebugTrace((LPARAM)this, "Failing because inbound cutoff reached");
  1131. hrRes = E_OUTOFMEMORY;
  1132. }
  1133. pMsg->Release();
  1134. pMsg = NULL;
  1135. }
  1136. }
  1137. // Now if we are out of memory, we would probably
  1138. // keep failing, so lets just return and get on with
  1139. // delivery
  1140. if (!SUCCEEDED(hrRes))
  1141. {
  1142. break;
  1143. }
  1144. }
  1145. // Get the next message
  1146. hrRes = pEnum->Next(
  1147. pMsg,
  1148. &pStream,
  1149. &hContentFile,
  1150. NULL);
  1151. // Next() cleans up its own mess if it fails
  1152. if (SUCCEEDED(hrRes))
  1153. {
  1154. DWORD dwStreamSize = 0;
  1155. IncCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  1156. DebugTrace((LPARAM) this, "Next returned success\n");
  1157. // We delete streams which are too short to contain
  1158. // a master header
  1159. hrRes = pStream->GetSize(pMsg, &dwStreamSize, NULL);
  1160. DebugTrace((LPARAM) this, "GetSize returned %x, %x\n", dwStreamSize, hrRes);
  1161. if (!SUCCEEDED(hrRes) || dwStreamSize < 1024)
  1162. {
  1163. pStream->Release();
  1164. ReleaseContext(hContentFile);
  1165. DeleteNeverAckdMessage(g_pEventLog,
  1166. pMsg,
  1167. m_szQueueDirectory,
  1168. hrRes,
  1169. m_fIsFAT);
  1170. DecCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  1171. continue;
  1172. }
  1173. DebugTrace((LPARAM) this, "Submitting to mailmsg\n");
  1174. // Submit the message, this call will actually do the
  1175. // bind to the store driver
  1176. if (m_fIsShuttingDown)
  1177. hrRes = E_FAIL;
  1178. else
  1179. {
  1180. IMailMsgBind *pBind = NULL;
  1181. // Bind and submit
  1182. hrRes = pMsg->QueryInterface(
  1183. IID_IMailMsgBind,
  1184. (LPVOID *)&pBind);
  1185. if (SUCCEEDED(hrRes))
  1186. {
  1187. hrRes = pBind->BindToStore(
  1188. pStream,
  1189. (IMailMsgStoreDriver *)this,
  1190. hContentFile);
  1191. pBind->Release();
  1192. if (SUCCEEDED(hrRes))
  1193. {
  1194. // Relinquish the extra refcount added by bind(2 -> 1)
  1195. pStream->Release();
  1196. hrRes = m_pSMTPServer->SubmitMessage(
  1197. pMsg);
  1198. if (!SUCCEEDED(hrRes))
  1199. {
  1200. // Relinquish the usage count added by bind (1 -> 0)
  1201. IMailMsgQueueMgmt *pMgmt = NULL;
  1202. hrRes = pMsg->QueryInterface(
  1203. IID_IMailMsgQueueMgmt,
  1204. (LPVOID *)&pMgmt);
  1205. if (SUCCEEDED(hrRes))
  1206. {
  1207. pMgmt->ReleaseUsage();
  1208. pMgmt->Release();
  1209. }
  1210. else
  1211. {
  1212. _ASSERT(hrRes == S_OK);
  1213. }
  1214. } else {
  1215. // update counter
  1216. IncCtr(m_ppoi, NTFSDRV_QUEUE_LENGTH);
  1217. IncCtr(m_ppoi, NTFSDRV_NUM_ENUMERATED);
  1218. }
  1219. // Whether or not the message is submitted, release our
  1220. // refcount
  1221. pMsg->Release();
  1222. pMsg = NULL;
  1223. }
  1224. }
  1225. else
  1226. {
  1227. _ASSERT(hrRes == S_OK);
  1228. }
  1229. }
  1230. if (!SUCCEEDED(hrRes))
  1231. {
  1232. // Clean up the mess ...
  1233. pStream->Release();
  1234. ReleaseContext(hContentFile);
  1235. DecCtr(m_ppoi, NTFSDRV_MSG_BODIES_OPEN);
  1236. if (m_fIsShuttingDown)
  1237. goto Shutdown;
  1238. //
  1239. // log an event about the message being corrupt
  1240. //
  1241. LogEventCorruptMessage(g_pEventLog,
  1242. pMsg,
  1243. m_szQueueDirectory,
  1244. hrRes);
  1245. // We might want to discard this message and go on
  1246. // with other messages. We will re-use this message
  1247. // object upstream.
  1248. hrRes = S_OK;
  1249. }
  1250. else
  1251. {
  1252. // Make sure we will not accidentally delete or
  1253. // reuse the message
  1254. pMsg = NULL;
  1255. }
  1256. }
  1257. } while (SUCCEEDED(hrRes));
  1258. // We distinguish the successful end of enumeration
  1259. if (hrRes == HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES))
  1260. hrRes = S_OK;
  1261. // Release the enumerator, of course ...
  1262. pEnum->Release();
  1263. // Release any residual messages
  1264. if (pMsg)
  1265. pMsg->Release();
  1266. }
  1267. TraceFunctLeaveEx((LPARAM)this);
  1268. return (S_OK);
  1269. Shutdown:
  1270. // Release the enumerator, of course ...
  1271. if(pEnum)
  1272. pEnum->Release();
  1273. TraceFunctLeaveEx((LPARAM)this);
  1274. return (HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  1275. }
  1276. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::IsCacheable()
  1277. {
  1278. // signal that only one instance of the sink should be created
  1279. return (S_OK);
  1280. }
  1281. HRESULT STDMETHODCALLTYPE CNtfsStoreDriver::ValidateMessageContext(
  1282. BYTE *pbContext,
  1283. DWORD cbContext)
  1284. {
  1285. return E_NOTIMPL;
  1286. }
  1287. /////////////////////////////////////////////////////////////////////////////
  1288. // CMailMsgEnumMessages
  1289. //
  1290. CNtfsEnumMessages::CNtfsEnumMessages()
  1291. {
  1292. *m_szEnumPath = _T('\0');
  1293. m_hEnum = INVALID_HANDLE_VALUE;
  1294. m_pDriver = NULL;
  1295. m_fIsFAT = TRUE; // Assume we are FAT until we discover otherwise
  1296. }
  1297. CNtfsEnumMessages::~CNtfsEnumMessages()
  1298. {
  1299. *m_szEnumPath = _T('\0');
  1300. if (m_hEnum != INVALID_HANDLE_VALUE)
  1301. {
  1302. if (!FindClose(m_hEnum))
  1303. {
  1304. _ASSERT(FALSE);
  1305. }
  1306. m_hEnum = INVALID_HANDLE_VALUE;
  1307. }
  1308. if (m_pDriver)
  1309. m_pDriver->ReleaseUsage();
  1310. }
  1311. HRESULT CNtfsEnumMessages::SetStoreDirectory(
  1312. LPTSTR szStoreDirectory,
  1313. BOOL fIsFAT
  1314. )
  1315. {
  1316. if (!szStoreDirectory)
  1317. return(E_FAIL);
  1318. // Mark the file system
  1319. m_fIsFAT = fIsFAT;
  1320. if (lstrlen(szStoreDirectory) >= MAX_PATH)
  1321. {
  1322. _ASSERT(FALSE);
  1323. return(E_FAIL);
  1324. }
  1325. lstrcpy(m_szEnumPath, szStoreDirectory);
  1326. lstrcat(m_szEnumPath, NTFS_STORE_FILE_PREFIX);
  1327. lstrcat(m_szEnumPath, NTFS_STORE_FILE_WILDCARD);
  1328. lstrcat(m_szEnumPath, NTFS_STORE_FILE_EXTENSION);
  1329. lstrcpy(m_szStorePath, szStoreDirectory);
  1330. lstrcat(m_szStorePath, NTFS_STORE_BACKSLASH);
  1331. return(S_OK);
  1332. }
  1333. DECLARE_STD_IUNKNOWN_METHODS(NtfsEnumMessages, IMailMsgEnumMessages)
  1334. HRESULT STDMETHODCALLTYPE CNtfsEnumMessages::Next(
  1335. IMailMsgProperties *pMsg,
  1336. IMailMsgPropertyStream **ppStream,
  1337. PFIO_CONTEXT *phContentFile,
  1338. IMailMsgNotify *pNotify
  1339. )
  1340. {
  1341. HRESULT hrRes = S_OK;
  1342. TCHAR szFQPN[MAX_PATH * 2];
  1343. if (!pMsg || !ppStream || !phContentFile) return E_POINTER;
  1344. BOOL fFoundFile = FALSE;
  1345. TraceFunctEnter("CNtfsEnumMessages::Next");
  1346. while (!fFoundFile) {
  1347. _ASSERT(m_pDriver);
  1348. if (m_pDriver->IsShuttingDown())
  1349. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  1350. if (m_hEnum == INVALID_HANDLE_VALUE)
  1351. {
  1352. m_hEnum = FindFirstFile(m_szEnumPath, &m_Data);
  1353. if (m_hEnum == INVALID_HANDLE_VALUE)
  1354. {
  1355. return(HRESULT_FROM_WIN32(GetLastError()));
  1356. }
  1357. }
  1358. else
  1359. {
  1360. if (!FindNextFile(m_hEnum, &m_Data))
  1361. {
  1362. return(HRESULT_FROM_WIN32(GetLastError()));
  1363. }
  1364. }
  1365. // Digest the data ...
  1366. while (m_Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1367. {
  1368. // Make sure it's not a directory
  1369. if (!FindNextFile(m_hEnum, &m_Data))
  1370. {
  1371. return(HRESULT_FROM_WIN32(GetLastError()));
  1372. }
  1373. }
  1374. // OK, got a file, get the content handle and property stream
  1375. lstrcpy(szFQPN, m_szStorePath);
  1376. lstrcat(szFQPN, m_Data.cFileName);
  1377. hrRes = CDriverUtils::GetStoreFileFromPath(
  1378. szFQPN,
  1379. ppStream,
  1380. phContentFile,
  1381. FALSE,
  1382. m_fIsFAT,
  1383. pMsg);
  1384. if (hrRes == S_NO_FIRST_COMMIT) {
  1385. DebugTrace((LPARAM) this, "Got no first commit, doing a delete\n");
  1386. // this means that we never ACK'd the message. silently delete it
  1387. if (*ppStream) (*ppStream)->Release();
  1388. ReleaseContext(*phContentFile);
  1389. DeleteFile(szFQPN);
  1390. if (m_fIsFAT) {
  1391. TCHAR szFileName[MAX_PATH * 2];
  1392. lstrcpy(szFileName, szFQPN);
  1393. lstrcat(szFileName, NTFS_FAT_STREAM_FILE_EXTENSION_1ST);
  1394. DeleteFile(szFileName);
  1395. lstrcpy(szFileName, szFQPN);
  1396. lstrcat(szFileName, NTFS_FAT_STREAM_FILE_EXTENSION_LIVE);
  1397. DeleteFile(szFileName);
  1398. }
  1399. } else if (FAILED(hrRes)) {
  1400. // couldn't open the file. try the next one
  1401. DebugTrace((LPARAM) this, "GetStoreFileFromPath returned %x\n", hrRes);
  1402. } else {
  1403. CNtfsPropertyStream *pNtfsStream =
  1404. (CNtfsPropertyStream *) (*ppStream);
  1405. // skip over items made with this instance of the ntfs store driver
  1406. if (pNtfsStream->GetInstanceGuid() ==
  1407. m_pDriver->GetInstanceGuid())
  1408. {
  1409. (*ppStream)->Release();
  1410. ReleaseContext(*phContentFile);
  1411. } else {
  1412. fFoundFile = TRUE;
  1413. }
  1414. }
  1415. }
  1416. // We got the handles successfully opened, now write the filename
  1417. // as the store driver context
  1418. hrRes = CDriverUtils::SetMessageContext(
  1419. pMsg,
  1420. (LPBYTE)szFQPN,
  1421. (lstrlen(szFQPN) + 1) * sizeof(TCHAR));
  1422. if (FAILED(hrRes))
  1423. {
  1424. // Release all file resources
  1425. ReleaseContext(*phContentFile);
  1426. _VERIFY((*ppStream)->Release() == 0);
  1427. }
  1428. else
  1429. {
  1430. ((CNtfsPropertyStream *)(*ppStream))->SetInfo(m_pDriver);
  1431. }
  1432. return(hrRes);
  1433. }
  1434. /////////////////////////////////////////////////////////////////////////////
  1435. // CNtfsPropertyStream
  1436. //
  1437. CNtfsPropertyStream::CNtfsPropertyStream()
  1438. {
  1439. m_hStream = INVALID_HANDLE_VALUE;
  1440. m_pDriver = NULL;
  1441. m_fValidation = FALSE;
  1442. // this will make us fail writeblocks if they don't call startwriteblocks
  1443. // first
  1444. m_hrStartWriteBlocks = E_FAIL;
  1445. }
  1446. CNtfsPropertyStream::~CNtfsPropertyStream()
  1447. {
  1448. if (m_hStream != INVALID_HANDLE_VALUE)
  1449. {
  1450. CloseHandle(m_hStream);
  1451. m_hStream = INVALID_HANDLE_VALUE;
  1452. }
  1453. if (m_pDriver) {
  1454. DecCtr((m_pDriver->m_ppoi), NTFSDRV_MSG_STREAMS_OPEN);
  1455. m_pDriver->ReleaseUsage();
  1456. }
  1457. }
  1458. DECLARE_STD_IUNKNOWN_METHODS(NtfsPropertyStream, IMailMsgPropertyStream)
  1459. //
  1460. // IMailMsgPropertyStream
  1461. //
  1462. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::GetSize(
  1463. IMailMsgProperties *pMsg,
  1464. DWORD *pdwSize,
  1465. IMailMsgNotify *pNotify
  1466. )
  1467. {
  1468. DWORD dwHigh, dwLow;
  1469. DWORD cStreamOffset = m_fStreamHasHeader ? STREAM_OFFSET : 0;
  1470. _ASSERT(m_pDriver || m_fValidation);
  1471. if (!m_fValidation && (!m_pDriver || m_pDriver->IsShuttingDown()))
  1472. return(HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
  1473. if (m_hStream == INVALID_HANDLE_VALUE)
  1474. return(E_FAIL);
  1475. if (!pdwSize) return E_POINTER;
  1476. dwLow = GetFileSize(m_hStream, &dwHigh);
  1477. if (dwHigh)
  1478. return(HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW));
  1479. *pdwSize = dwLow - cStreamOffset;
  1480. return(S_OK);
  1481. }
  1482. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::ReadBlocks(
  1483. IMailMsgProperties *pMsg,
  1484. DWORD dwCount,
  1485. DWORD *pdwOffset,
  1486. DWORD *pdwLength,
  1487. BYTE **ppbBlock,
  1488. IMailMsgNotify *pNotify
  1489. )
  1490. {
  1491. DWORD dwSizeRead;
  1492. DWORD dwStreamSize;
  1493. DWORD dwOffsetToRead;
  1494. DWORD dwLengthToRead;
  1495. HRESULT hrRes = S_OK;
  1496. DWORD cStreamOffset = m_fStreamHasHeader ? STREAM_OFFSET : 0;
  1497. TraceFunctEnterEx((LPARAM)this, "CNtfsPropertyStream::ReadBlocks");
  1498. if (m_hStream == INVALID_HANDLE_VALUE)
  1499. return(E_FAIL);
  1500. if (!pdwOffset || !pdwLength || !ppbBlock) {
  1501. return E_POINTER;
  1502. }
  1503. if (!m_pDriver && !m_fValidation) {
  1504. return E_UNEXPECTED;
  1505. }
  1506. _ASSERT(m_pDriver || m_fValidation);
  1507. if (m_pDriver && m_pDriver->IsShuttingDown())
  1508. {
  1509. DebugTrace((LPARAM)this, "Reading while shutting down ...");
  1510. }
  1511. // Need to get the file size to determine if there is enough bytes
  1512. // to read for each block. Note that WriteBlocks are to be serialzed so
  1513. // ReadBlocks and WriteBlocks should not be overlapped.
  1514. dwStreamSize = GetFileSize(m_hStream, NULL);
  1515. if (dwStreamSize == 0xffffffff)
  1516. {
  1517. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1518. if (hrRes == S_OK)
  1519. hrRes = STG_E_READFAULT;
  1520. ErrorTrace((LPARAM)this, "Failed to get size of stream (%08x)", hrRes);
  1521. return(hrRes);
  1522. }
  1523. for (DWORD i = 0; i < dwCount; i++, pdwOffset++, pdwLength++, ppbBlock++)
  1524. {
  1525. // For each block, check beforehand that we are not reading past
  1526. // the end of the file. Make sure to be weary about overflow cases
  1527. dwOffsetToRead = (*pdwOffset) + cStreamOffset;
  1528. dwLengthToRead = *pdwLength;
  1529. if ((dwOffsetToRead > dwStreamSize) ||
  1530. (dwOffsetToRead > (dwOffsetToRead + dwLengthToRead)) ||
  1531. ((dwOffsetToRead + dwLengthToRead) > dwStreamSize))
  1532. {
  1533. // Insufficient bytes, abort immediately
  1534. ErrorTrace((LPARAM)this, "Insufficient bytes: Read(%u, %u); Size = %u",
  1535. dwOffsetToRead, dwLengthToRead, dwStreamSize);
  1536. hrRes = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  1537. break;
  1538. }
  1539. if (SetFilePointer(
  1540. m_hStream,
  1541. dwOffsetToRead,
  1542. NULL,
  1543. FILE_BEGIN) == 0xffffffff)
  1544. {
  1545. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1546. break;
  1547. }
  1548. if (!ReadFile(
  1549. m_hStream,
  1550. *ppbBlock,
  1551. dwLengthToRead,
  1552. &dwSizeRead,
  1553. NULL))
  1554. {
  1555. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1556. break;
  1557. }
  1558. else if (dwSizeRead != dwLengthToRead)
  1559. {
  1560. hrRes = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
  1561. break;
  1562. }
  1563. }
  1564. TraceFunctLeaveEx((LPARAM)this);
  1565. return(hrRes);
  1566. }
  1567. HRESULT CNtfsPropertyStream::SetHandle(HANDLE hStream,
  1568. GUID guidInstance,
  1569. BOOL fLiveStream,
  1570. IMailMsgProperties *pMsg)
  1571. {
  1572. TraceFunctEnter("CNtfsPropertyStream::SetHandle");
  1573. if (hStream == INVALID_HANDLE_VALUE) return(E_FAIL);
  1574. m_hStream = hStream;
  1575. DWORD dw;
  1576. NTFS_STREAM_HEADER header;
  1577. //
  1578. // if guidInstance is non-NULL then we are dealing with a fresh
  1579. // stream and need to write the header block
  1580. //
  1581. if (guidInstance != GUID_NULL) {
  1582. DebugTrace((LPARAM) this, "writing NTFSDRV header");
  1583. header.dwSignature = STREAM_SIGNATURE_PRECOMMIT;
  1584. header.dwVersion = 1;
  1585. header.guidInstance = guidInstance;
  1586. if (!WriteFile(m_hStream, &header, sizeof(header), &dw, NULL)) {
  1587. return HRESULT_FROM_WIN32(GetLastError());
  1588. }
  1589. m_fStreamHasHeader = TRUE;
  1590. m_guidInstance = guidInstance;
  1591. m_cCommits = 0;
  1592. } else {
  1593. DebugTrace((LPARAM) this, "reading NTFSDRV header, fLiveStream = %lu", fLiveStream);
  1594. // if we are working with :PROPERTIES then we want to set the
  1595. // commit count to 1 so that the next set of writes will go to
  1596. // :PROPERTIES-LIVE
  1597. m_cCommits = (fLiveStream) ? 2 : 1;
  1598. // read the header. if we can't read it, or there aren't enough
  1599. // bytes to read it, then assume that the header wasn't fully
  1600. // written out.
  1601. if (!ReadFile(m_hStream, &header, sizeof(header), &dw, NULL) ||
  1602. dw != sizeof(header))
  1603. {
  1604. header.dwSignature = STREAM_SIGNATURE_PRECOMMIT;
  1605. }
  1606. // act according to what we find in the signature
  1607. switch (header.dwSignature) {
  1608. case STREAM_SIGNATURE: {
  1609. DebugTrace((LPARAM) this, "signature is valid");
  1610. // the signature (and thus the stream) is valid
  1611. m_fStreamHasHeader = TRUE;
  1612. m_guidInstance = header.guidInstance;
  1613. break;
  1614. }
  1615. case STREAM_SIGNATURE_PRECOMMIT: {
  1616. DebugTrace((LPARAM) this, "signature is STREAM_SIGNATURE_PRECOMMIT");
  1617. // a commit was never completed
  1618. return S_NO_FIRST_COMMIT;
  1619. break;
  1620. }
  1621. case STREAM_SIGNATURE_INVALID: {
  1622. DebugTrace((LPARAM) this, "signature is STREAM_SIGNATURE_INVALID");
  1623. // the valid-stream signature was never written
  1624. IMailMsgValidate *pValidate = NULL;
  1625. HRESULT hr;
  1626. // assume that the stream is valid, and go through a full
  1627. // check
  1628. m_fStreamHasHeader = TRUE;
  1629. m_guidInstance = header.guidInstance;
  1630. // this flag allows the read stream operations to take place
  1631. // before the stream is fully setup
  1632. m_fValidation = TRUE;
  1633. // validate stream can only be trusted on the first
  1634. // property stream. this is because it can detect
  1635. // truncated streams, but not streams with corrupted
  1636. // properties. the first stream (:PROPERTIES) can be
  1637. // truncated, but not corrupted.
  1638. //
  1639. // if we see the invalid signature on a :PROPERTIES-LIVE
  1640. // stream then we will always assume that it is corrupted
  1641. // and fall back to the initial stream.
  1642. //
  1643. // call into mailmsg to see if the stream is valid. if
  1644. // it isn't then we won't allow it to be loaded
  1645. DebugTrace((LPARAM) this, "Calling ValidateStream\n");
  1646. if (fLiveStream ||
  1647. FAILED(pMsg->QueryInterface(IID_IMailMsgValidate,
  1648. (void **) &pValidate)) ||
  1649. FAILED(pValidate->ValidateStream(this)))
  1650. {
  1651. DebugTrace((LPARAM) this, "Stream contains invalid data");
  1652. m_fStreamHasHeader = FALSE;
  1653. m_guidInstance = GUID_NULL;
  1654. if (pValidate) pValidate->Release();
  1655. return S_INVALIDSTREAM;
  1656. }
  1657. // we are done with the validation routines
  1658. if (pValidate) pValidate->Release();
  1659. m_fValidation = FALSE;
  1660. DebugTrace((LPARAM) this, "Stream contains valid data");
  1661. break;
  1662. }
  1663. default: {
  1664. // if it is anything else then it could be a file with
  1665. // no header (older builds generated these) or it could be
  1666. // invalid data. mailmsg will figure it out.
  1667. m_fStreamHasHeader = FALSE;
  1668. m_guidInstance = GUID_NULL;
  1669. DebugTrace((LPARAM) this, "Unknown signature %x on stream",
  1670. header.dwSignature);
  1671. }
  1672. }
  1673. }
  1674. return(S_OK);
  1675. }
  1676. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::StartWriteBlocks(
  1677. IMailMsgProperties *pMsg,
  1678. DWORD cBlocksToWrite,
  1679. DWORD cBytesToWrite)
  1680. {
  1681. TraceFunctEnter("CNtfsPropertyStream::StartWriteBlocks");
  1682. NTFS_STREAM_HEADER header;
  1683. DWORD dw;
  1684. m_hrStartWriteBlocks = S_OK;
  1685. // if we have seen one full commit, then fork the stream and start
  1686. // writing to the live stream
  1687. if (m_cCommits == 1) {
  1688. char szLiveStreamFilename[MAX_PATH * 2];
  1689. BOOL fIsFAT = m_pDriver->IsFAT();
  1690. char szFatLiveStreamExtension[] = NTFS_FAT_STREAM_FILE_EXTENSION_LIVE;
  1691. char szNtfsLiveStreamExtension[] = NTFS_STORE_FILE_PROPERTY_STREAM_LIVE;
  1692. const DWORD cCopySize = 64 * 1024;
  1693. // get the filename of the message from the mailmsg object
  1694. //
  1695. // we need to save space in szLiveStreamFilename for the largest
  1696. // extension that we might tack on
  1697. DWORD dwLength = sizeof(char) * ((MAX_PATH * 2) -
  1698. max(sizeof(szNtfsLiveStreamExtension),
  1699. sizeof(szFatLiveStreamExtension)));
  1700. m_hrStartWriteBlocks = CDriverUtils::GetMessageContext(pMsg,
  1701. (LPBYTE) szLiveStreamFilename,
  1702. &dwLength);
  1703. if (FAILED(m_hrStartWriteBlocks)) {
  1704. ErrorTrace((LPARAM) this,
  1705. "GetMessageContext failed with %x",
  1706. m_hrStartWriteBlocks);
  1707. TraceFunctLeave();
  1708. return m_hrStartWriteBlocks;
  1709. }
  1710. // allocate memory up front that will be used for copying the
  1711. // streams
  1712. BYTE *lpb = new BYTE[cCopySize];
  1713. if (lpb == NULL) {
  1714. m_hrStartWriteBlocks = E_OUTOFMEMORY;
  1715. ErrorTrace((LPARAM) this, "pvMalloc failed to allocate 64k");
  1716. TraceFunctLeave();
  1717. return m_hrStartWriteBlocks;
  1718. }
  1719. // we know that we have enough space for the strcats because
  1720. // we saved space for it in the GetMessageContext call above
  1721. strcat(szLiveStreamFilename,
  1722. (m_pDriver->IsFAT()) ? szFatLiveStreamExtension :
  1723. szNtfsLiveStreamExtension);
  1724. // open the new stream
  1725. HANDLE hLiveStream = CreateFile(szLiveStreamFilename,
  1726. GENERIC_READ | GENERIC_WRITE,
  1727. FILE_SHARE_READ,
  1728. NULL,
  1729. CREATE_ALWAYS,
  1730. FILE_FLAG_SEQUENTIAL_SCAN,
  1731. NULL);
  1732. if (hLiveStream == INVALID_HANDLE_VALUE) {
  1733. delete[] (lpb);
  1734. ErrorTrace((LPARAM) this,
  1735. "CreateFile(%s) failed with %lu",
  1736. szLiveStreamFilename,
  1737. GetLastError());
  1738. m_hrStartWriteBlocks = HRESULT_FROM_WIN32(GetLastError());
  1739. TraceFunctLeave();
  1740. return m_hrStartWriteBlocks;
  1741. }
  1742. // copy the data between the two streams
  1743. BOOL fCopyFailed = FALSE;
  1744. DWORD i = 0, cRead = cCopySize, cWritten;
  1745. SetFilePointer(m_hStream, 0, NULL, FILE_BEGIN);
  1746. while (!fCopyFailed && cRead == cCopySize) {
  1747. if (ReadFile(m_hStream,
  1748. lpb,
  1749. cCopySize,
  1750. &cRead,
  1751. NULL))
  1752. {
  1753. // if this is the first block then we will touch the
  1754. // signature to mark it as invalid. it will get
  1755. // rewritten as valid once this commit is complete
  1756. if (i == 0) {
  1757. DWORD *pdwSignature = (DWORD *) lpb;
  1758. if (*pdwSignature == STREAM_SIGNATURE) {
  1759. *pdwSignature = STREAM_SIGNATURE_PRECOMMIT;
  1760. }
  1761. }
  1762. if (WriteFile(hLiveStream,
  1763. lpb,
  1764. cRead,
  1765. &cWritten,
  1766. NULL))
  1767. {
  1768. _ASSERT(cWritten == cRead);
  1769. fCopyFailed = (cWritten != cRead);
  1770. if (fCopyFailed) {
  1771. SetLastError(ERROR_WRITE_FAULT);
  1772. ErrorTrace((LPARAM) this,
  1773. "WriteFile didn't write enough bytes"
  1774. "cWritten = %lu, cRead = %lu",
  1775. cWritten, cRead);
  1776. }
  1777. } else {
  1778. fCopyFailed = TRUE;
  1779. ErrorTrace((LPARAM) this, "WriteFile failed with %lu",
  1780. GetLastError());
  1781. }
  1782. } else {
  1783. ErrorTrace((LPARAM) this, "ReadFile failed with %lu",
  1784. GetLastError());
  1785. fCopyFailed = TRUE;
  1786. }
  1787. i++;
  1788. }
  1789. delete[] (lpb);
  1790. if (fCopyFailed) {
  1791. // there isn't any way to delete the incomplete stream here.
  1792. // however we gave it an invalid signature above, so it won't
  1793. // be loaded during enumeration
  1794. CloseHandle(hLiveStream);
  1795. m_hrStartWriteBlocks = HRESULT_FROM_WIN32(GetLastError());
  1796. TraceFunctLeave();
  1797. return m_hrStartWriteBlocks;
  1798. }
  1799. // close the handle to the current stream and point the stream handle
  1800. // to the new one
  1801. CloseHandle(m_hStream);
  1802. m_hStream = hLiveStream;
  1803. } else {
  1804. header.dwSignature = STREAM_SIGNATURE_INVALID;
  1805. if (m_fStreamHasHeader) {
  1806. if (SetFilePointer(m_hStream, 0, NULL, FILE_BEGIN) == 0) {
  1807. if (!WriteFile(m_hStream, &header, sizeof(header.dwSignature), &dw, NULL)) {
  1808. m_hrStartWriteBlocks = HRESULT_FROM_WIN32(GetLastError());
  1809. }
  1810. } else {
  1811. m_hrStartWriteBlocks = HRESULT_FROM_WIN32(GetLastError());
  1812. }
  1813. }
  1814. }
  1815. TraceFunctLeave();
  1816. return m_hrStartWriteBlocks;
  1817. }
  1818. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::EndWriteBlocks(
  1819. IMailMsgProperties *pMsg)
  1820. {
  1821. HRESULT hr = S_OK;
  1822. DWORD dw;
  1823. NTFS_STREAM_HEADER header;
  1824. _ASSERT(SUCCEEDED(m_hrStartWriteBlocks));
  1825. if (FAILED(m_hrStartWriteBlocks)) {
  1826. return m_hrStartWriteBlocks;
  1827. }
  1828. header.dwSignature = STREAM_SIGNATURE;
  1829. if (m_fStreamHasHeader) {
  1830. if (SetFilePointer(m_hStream, 0, NULL, FILE_BEGIN) == 0) {
  1831. if (!WriteFile(m_hStream, &header, sizeof(header.dwSignature), &dw, NULL)) {
  1832. hr = HRESULT_FROM_WIN32(GetLastError());
  1833. }
  1834. } else {
  1835. hr = HRESULT_FROM_WIN32(GetLastError());
  1836. }
  1837. }
  1838. if (hr == S_OK) m_cCommits++;
  1839. return hr;
  1840. }
  1841. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::CancelWriteBlocks(
  1842. IMailMsgProperties *pMsg)
  1843. {
  1844. return S_OK;
  1845. }
  1846. HRESULT STDMETHODCALLTYPE CNtfsPropertyStream::WriteBlocks(
  1847. IMailMsgProperties *pMsg,
  1848. DWORD dwCount,
  1849. DWORD *pdwOffset,
  1850. DWORD *pdwLength,
  1851. BYTE **ppbBlock,
  1852. IMailMsgNotify *pNotify
  1853. )
  1854. {
  1855. DWORD dwSizeWritten;
  1856. HRESULT hrRes = S_OK;
  1857. DWORD cStreamOffset = m_fStreamHasHeader ? STREAM_OFFSET : 0;
  1858. TraceFunctEnterEx((LPARAM)this, "CNtfsPropertyStream::WriteBlocks");
  1859. if (!pdwOffset || !pdwLength || !ppbBlock) {
  1860. return E_POINTER;
  1861. }
  1862. if (!m_pDriver) {
  1863. return E_UNEXPECTED;
  1864. }
  1865. _ASSERT(m_pDriver);
  1866. if (m_pDriver->IsShuttingDown())
  1867. {
  1868. DebugTrace((LPARAM)this, "Writing while shutting down ...");
  1869. }
  1870. if (m_hStream == INVALID_HANDLE_VALUE)
  1871. return(E_FAIL);
  1872. if (FAILED(m_hrStartWriteBlocks))
  1873. return m_hrStartWriteBlocks;
  1874. for (DWORD i = 0; i < dwCount; i++, pdwOffset++, pdwLength++, ppbBlock++)
  1875. {
  1876. if (SetFilePointer(
  1877. m_hStream,
  1878. (*pdwOffset) + cStreamOffset,
  1879. NULL,
  1880. FILE_BEGIN) == 0xffffffff)
  1881. {
  1882. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1883. break;
  1884. }
  1885. if (!WriteFile(
  1886. m_hStream,
  1887. *ppbBlock,
  1888. *pdwLength,
  1889. &dwSizeWritten,
  1890. NULL) ||
  1891. (dwSizeWritten != *pdwLength))
  1892. {
  1893. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1894. break;
  1895. }
  1896. }
  1897. if (SUCCEEDED(hrRes))
  1898. {
  1899. if (!FlushFileBuffers(m_hStream))
  1900. hrRes = HRESULT_FROM_WIN32(GetLastError());
  1901. }
  1902. TraceFunctLeaveEx((LPARAM)this);
  1903. return(hrRes);
  1904. }