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.

1468 lines
38 KiB

  1. //+============================================================================
  2. //
  3. // File: nffmstm.cxx
  4. //
  5. // This file provides the NFF (NTFS Flat File) IMappedStream implementation.
  6. //
  7. // History:
  8. // 5/6/98 MikeHill
  9. // - Misc dbg cleanup.
  10. //
  11. //+============================================================================
  12. #include <pch.cxx>
  13. CNFFMappedStream::~CNFFMappedStream()
  14. {
  15. HRESULT hr = S_OK;
  16. // If the update stream has the latest data, rename it over the original
  17. // stream. Ordinarily this replace call will create a new update stream.
  18. // But since we're going away, tell it not to bother.
  19. // Errors are ignored here because there's no way to return them.
  20. // If the caller wishes to avoid this, they should call Flush first.
  21. if( NULL != _pstmUpdate )
  22. {
  23. ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
  24. DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
  25. }
  26. // Just to be safe, free the mapping buffer (it should have
  27. // already been freed).
  28. DfpAssert( NULL == _pbMappedStream );
  29. CoTaskMemFree( _pbMappedStream );
  30. // If we've got the global reserved buffer locked,
  31. // free it now.
  32. if (_fLowMem)
  33. g_ReservedMemory.UnlockMemory();
  34. }
  35. HRESULT
  36. CNFFMappedStream::QueryInterface( REFIID riid, void**ppvObject )
  37. {
  38. return( _pnffstm->QueryInterface( riid, ppvObject ));
  39. }
  40. ULONG
  41. CNFFMappedStream::AddRef()
  42. {
  43. return( _pnffstm->AddRef() );
  44. }
  45. ULONG
  46. CNFFMappedStream::Release()
  47. {
  48. return( _pnffstm->Release() );
  49. }
  50. //+----------------------------------------------------------------------------
  51. //
  52. // Method: CNFFMappedStream::Open (IMappedStream)
  53. //
  54. //+----------------------------------------------------------------------------
  55. VOID
  56. CNFFMappedStream::Open( IN VOID *powner, OUT LONG *phr )
  57. {
  58. nffITrace( "CNFFMappedStream::Open" );
  59. VOID *pv = NULL;
  60. HRESULT sc=S_OK;
  61. BOOL fUsingLatestStream = FALSE;
  62. DfpAssert(!_fLowMem);
  63. _pnffstm->Lock( INFINITE );
  64. nffChk( _pnffstm->CheckReverted() );
  65. // If the previous open crashed during a flush, roll forward to the
  66. // updated copy. If we're only open for read access, then this will
  67. // just set _fUpdateStreamHasLatest so that we'll know to process
  68. // reads from that stream.
  69. nffChk( RollForwardIfNecessary() );
  70. BeginUsingLatestStream();
  71. fUsingLatestStream = TRUE;
  72. // If given a pointer to the owner of this mapped stream,
  73. // save it. This could be NULL (i.e., when called from
  74. // ReOpen).
  75. if( NULL != powner )
  76. _pMappedStreamOwner = powner;
  77. // If we haven't already read the stream, read it now.
  78. if( NULL == _pbMappedStream )
  79. {
  80. BY_HANDLE_FILE_INFORMATION fileinfo;
  81. DfpAssert( INVALID_HANDLE_VALUE != _pnffstm->GetFileHandle() );
  82. DfpAssert( 0 == _cbMappedStream );
  83. DfpAssert( 0 == _cbMappedStreamActual);
  84. // Get and validate the size of the file
  85. if( !GetFileInformationByHandle( _pnffstm->GetFileHandle(), &fileinfo ))
  86. {
  87. nffErr( EH_Err, LAST_SCODE );
  88. }
  89. else if( 0 != fileinfo.nFileSizeHigh
  90. || CBMAXPROPSETSTREAM < fileinfo.nFileSizeLow )
  91. {
  92. nffErr( EH_Err, STG_E_INVALIDHEADER );
  93. }
  94. _cbMappedStream = _cbMappedStreamActual = fileinfo.nFileSizeLow;
  95. // Allocate a buffer to hold the Stream. If there isn't sufficient
  96. // memory in the system, lock and get the reserved buffer. In the
  97. // end, 'pv' points to the appropriate buffer.
  98. #if DBG
  99. pv = _fSimulateLowMem ? NULL : CoTaskMemAlloc( _cbMappedStreamActual );
  100. #else
  101. pv = CoTaskMemAlloc( _cbMappedStreamActual );
  102. #endif
  103. if( NULL == pv )
  104. {
  105. // could block until previous property call completes
  106. pv = g_ReservedMemory.LockMemory();
  107. if( NULL == pv )
  108. nffErr( EH_Err, E_OUTOFMEMORY );
  109. _fLowMem = TRUE;
  110. }
  111. _pbMappedStream = (BYTE*) pv;
  112. // Read in the file.
  113. if( 0 != _cbMappedStreamActual )
  114. {
  115. ULARGE_INTEGER ulOffset;
  116. ulOffset.QuadPart = 0;
  117. if( FAILED(_pnffstm->SyncReadAtFile( ulOffset, _pbMappedStream,
  118. _cbMappedStreamActual, &_cbMappedStream)))
  119. {
  120. nffErr( EH_Err, LAST_SCODE );
  121. }
  122. // Ensure that we got all the bytes we requested.
  123. if( _cbMappedStream != _cbMappedStreamActual )
  124. {
  125. propDbg((DEBTRACE_ERROR,
  126. "CMappedStreamOnHFile(%08X)::Open bytes-read (%lu) doesn't match bytes-requested (%lu)\n",
  127. this, _cbMappedStream, _cbMappedStreamActual ));
  128. nffErr( EH_Err, STG_E_INVALIDHEADER );
  129. }
  130. }
  131. #if BIGENDIAN==1
  132. // Notify our owner that we've read in new data.
  133. if( _pMappedStreamOwner != NULL && 0 != _cbMappedStream )
  134. {
  135. nffChk( PrOnMappedStreamEvent( _pMappedStreamOwner, _pbMappedStream, _cbMappedStream ) );
  136. }
  137. #endif
  138. } // if( NULL == _pbMappedStream )
  139. // ----
  140. // Exit
  141. // ----
  142. EH_Err:
  143. if( fUsingLatestStream )
  144. EndUsingLatestStream();
  145. // If there was an error, free any memory we have.
  146. if( FAILED(sc) )
  147. {
  148. propDbg((DEB_ERROR, "IMappedStream::CNtfsStream(%08X)::Open exception returns %08X\n", this, *phr));
  149. if (_fLowMem)
  150. g_ReservedMemory.UnlockMemory();
  151. else
  152. CoTaskMemFree(pv);
  153. _pbMappedStream = NULL;
  154. _cbMappedStream = _cbMappedStreamActual = 0;
  155. _fLowMem = FALSE;
  156. }
  157. _pnffstm->Unlock();
  158. *phr = sc;
  159. return;
  160. }
  161. //+-------------------------------------------------------------------
  162. //
  163. // Member: CNFFMappedStream::Flush (IMappedStream)
  164. //
  165. //--------------------------------------------------------------------
  166. VOID CNFFMappedStream::Flush(OUT LONG *phr)
  167. {
  168. nffITrace( "CNFFMappedStream::Flush" );
  169. HRESULT sc=S_OK;
  170. BOOL fUsingLatestStream = FALSE;
  171. _pnffstm->Lock( INFINITE );;
  172. BeginUsingLatestStream();
  173. fUsingLatestStream = TRUE;
  174. nffChk( _pnffstm->CheckReverted() );
  175. if( !IsWriteable() )
  176. nffErr( EH_Err, STG_E_ACCESSDENIED );
  177. // If the IMappedStream is being used, write it out to the
  178. // underlying file.
  179. if( NULL != _pbMappedStream )
  180. nffChk( WriteMappedStream() );
  181. // Commit the Stream.
  182. if( !FlushFileBuffers( _pnffstm->GetFileHandle() ))
  183. nffErr( EH_Err, LAST_SCODE );
  184. EndUsingLatestStream();
  185. fUsingLatestStream = FALSE;
  186. nffChk( ReplaceOriginalWithUpdate( CREATE_NEW_UPDATE_STREAM ));
  187. sc = S_OK;
  188. EH_Err:
  189. if( fUsingLatestStream )
  190. EndUsingLatestStream();
  191. _pnffstm->Unlock();
  192. *phr = sc;
  193. return;
  194. }
  195. //+-------------------------------------------------------------------
  196. //
  197. // Member: IMappedStream::Close
  198. //
  199. // Synopsis: Close the mapped stream by writing out
  200. // the mapping buffer and then freeing it.
  201. // Errors are ignored, so if the caller wants an
  202. // opportunity to recover from an error, they should
  203. // call Flush before calling Close.
  204. //
  205. // Arguments: [LONG*] phr
  206. // An HRESULT error code.
  207. //
  208. // Returns: None.
  209. //
  210. //--------------------------------------------------------------------
  211. VOID CNFFMappedStream::Close(OUT LONG *phr)
  212. {
  213. nffITrace( "CNFFMappedStream::Close" );
  214. HRESULT sc=S_OK;
  215. _pnffstm->Lock( INFINITE );
  216. // So watch out for multiple closes.
  217. sc = _pnffstm->CheckReverted();
  218. // If we are already closed then return immediatly (but don't error)
  219. if( STG_E_REVERTED == sc )
  220. {
  221. sc = S_OK;
  222. goto EH_Err;
  223. }
  224. // Report any real errors.
  225. if( FAILED( sc ) )
  226. nffErr( EH_Err, sc );
  227. // Write the changes. We don't need to Commit them,
  228. // they will be implicitely committed when the
  229. // Stream is Released.
  230. sc = WriteMappedStream();
  231. // Even if we fail the write, we must free the memory.
  232. // (PrClosePropertySet deletes everything whether or not
  233. // there was an error here, so we must free the memory.
  234. // There's no danger of this happenning due to out-of-
  235. // disk-space conditions, because the propset code
  236. // pre-allocates).
  237. CoTaskMemFree( _pbMappedStream );
  238. _pbMappedStream = NULL;
  239. // Re-zero the member data.
  240. InitMappedStreamMembers();
  241. sc = S_OK;
  242. EH_Err:
  243. _pnffstm->Unlock();
  244. *phr = sc;
  245. return;
  246. }
  247. //+-------------------------------------------------------------------
  248. //
  249. // Member: CNFFMappedStream::ReOpen (IMappedStream)
  250. //
  251. //--------------------------------------------------------------------
  252. VOID
  253. CNFFMappedStream::ReOpen(IN OUT VOID **ppv, OUT LONG *phr)
  254. {
  255. nffITrace( "CNFFMappedStream::ReOpen" );
  256. HRESULT sc=S_OK;
  257. *ppv = NULL;
  258. _pnffstm->Lock( INFINITE );;
  259. nffChk( _pnffstm->CheckReverted() );
  260. this->Open(NULL, &sc);
  261. nffChk(sc);
  262. *ppv = _pbMappedStream;
  263. EH_Err:
  264. _pnffstm->Unlock();
  265. *phr = sc;
  266. return;
  267. }
  268. //+-------------------------------------------------------------------
  269. //
  270. // Member: CNFFMappedStream::Quiesce (IMappedStream)
  271. //
  272. //--------------------------------------------------------------------
  273. VOID CNFFMappedStream::Quiesce(VOID)
  274. {
  275. nffITrace( "CNFFMappedStream::Quiesce" );
  276. // Not necessary for this implemented
  277. }
  278. //+-------------------------------------------------------------------
  279. //
  280. // Member: CNFFMappedStream::Map (IMappedStream)
  281. //
  282. //--------------------------------------------------------------------
  283. VOID
  284. CNFFMappedStream::Map(IN BOOLEAN fCreate, OUT VOID **ppv)
  285. {
  286. nffITrace( "CNFFMappedStream::Map" );
  287. HRESULT sc;
  288. _pnffstm->Lock( INFINITE );;
  289. nffChk( _pnffstm->CheckReverted() );
  290. DfpAssert(_pbMappedStream != NULL);
  291. *ppv = _pbMappedStream;
  292. EH_Err:
  293. _pnffstm->Unlock();
  294. }
  295. //+-------------------------------------------------------------------
  296. //
  297. // Member: CNFFMappedStream::Unmap (IMappedStream)
  298. //
  299. //--------------------------------------------------------------------
  300. VOID
  301. CNFFMappedStream::Unmap(BOOLEAN fFlush, VOID **ppv)
  302. {
  303. nffITrace( "CNFFMappedStream::Unmap" );
  304. *ppv = NULL;
  305. }
  306. //+-------------------------------------------------------------------
  307. //
  308. // Member: CNFFMappedStream::WriteMappedStream (internal support for IMappedStream)
  309. //
  310. // Returns: S_OK if successful, S_FALSE if there was nothing to write.
  311. //
  312. //--------------------------------------------------------------------
  313. #define STACK_BYTES 16
  314. HRESULT
  315. CNFFMappedStream::WriteMappedStream()
  316. {
  317. nffITrace( "CNFFMappedStream::WriteMappedStream" );
  318. HRESULT sc = S_OK;
  319. ULONG cbWritten;
  320. BOOL fOwnerSignaled = FALSE;
  321. BOOL fUsingUpdateStream = FALSE;
  322. // We can return right away if there's nothing to write.
  323. // (_pbMappedStream may be NULL in the error path of our
  324. // caller).
  325. if (!IsModified() || NULL == _pbMappedStream )
  326. {
  327. propDbg((DEB_TRACE, "IMappedStream::CNtfsStream(%08X)::Flush returns with not-dirty\n", this));
  328. return S_FALSE;
  329. }
  330. // Put the update stream's handle into _pnffstm, so that we write out to it.
  331. BeginUsingUpdateStream();
  332. fUsingUpdateStream = TRUE;
  333. DfpAssert( INVALID_HANDLE_VALUE != _pnffstm->GetFileHandle() );
  334. #if BIGENDIAN==1
  335. // Notify our owner that we're about to perform a Write.
  336. nffChk( PrOnMappedStreamEvent( _powner, _pbMappedStream, _cbMappedStream ) );
  337. fOwnerSignaled = TRUE;
  338. #endif
  339. // Write out the mapping buffer (to the update stream).
  340. ULARGE_INTEGER ulOffset;
  341. ulOffset.QuadPart = 0;
  342. nffChk( _pnffstm->SyncWriteAtFile( ulOffset, _pbMappedStream,
  343. _cbMappedStream, &cbWritten ));
  344. if( cbWritten != _cbMappedStream )
  345. {
  346. propDbg((DEB_ERROR,
  347. "CMappedStreamOnHFile(%08X)::Write bytes-written (%lu) doesn't match bytes-requested (%lu)\n",
  348. this, cbWritten, _cbMappedStream ));
  349. sc = STG_E_INVALIDHEADER;
  350. goto EH_Err;
  351. }
  352. // If the buffer is shrinking, this is a good time to shrink the file.
  353. if (_cbMappedStream < _cbMappedStreamActual)
  354. {
  355. nffChk( _pnffstm->SetSize( static_cast<CULargeInteger>(_cbMappedStream) ) );
  356. _cbMappedStreamActual = _cbMappedStream;
  357. }
  358. if( _fStreamRenameSupported )
  359. {
  360. // We wrote the data to the update stream. So flag that it now
  361. // has the latest data.
  362. _fUpdateStreamHasLatest = TRUE;
  363. DfpAssert( NULL != _pstmUpdate && INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle() );
  364. }
  365. // ----
  366. // Exit
  367. // ----
  368. EH_Err:
  369. #if BIGENDIAN==1
  370. // Notify our owner that we're done with the Write. We do this
  371. // whether or not there was an error, because _pbMappedStream is
  372. // not modified, and therefore intact even in the error path.
  373. if( fOwnerSignaled )
  374. {
  375. DfpVerify( PrOnMappedStreamEvent( _powner,
  376. _pbMappedStream, _cbMappedStream ) );
  377. }
  378. #endif
  379. if( fUsingUpdateStream )
  380. EndUsingUpdateStream();
  381. if (sc == S_OK || sc == STG_E_REVERTED)
  382. {
  383. _fMappedStreamDirty = FALSE;
  384. }
  385. propDbg(( DbgFlag(sc,DEB_ITRACE), "CNtfsStream(%08X)::Write %s returns hr=%08X\n",
  386. this, sc != S_OK ? "exception" : "", sc));
  387. return sc;
  388. }
  389. //+-------------------------------------------------------------------
  390. //
  391. // Member: CNFFMappedStream::GetSize (IMappedStream)
  392. //
  393. //--------------------------------------------------------------------
  394. ULONG CNFFMappedStream::GetSize(OUT LONG *phr)
  395. {
  396. nffITrace( "CNFFMappedStream::GetSize" );
  397. HRESULT sc=S_OK;
  398. _pnffstm->Lock( INFINITE );;
  399. nffChk( _pnffstm->CheckReverted() );
  400. // If necessary, open the Stream.
  401. if( NULL == _pbMappedStream )
  402. {
  403. this->Open(NULL, &sc);
  404. }
  405. if( SUCCEEDED(sc) )
  406. {
  407. DfpAssert( NULL != _pbMappedStream );
  408. }
  409. // Return the size of the mapped stream. If there was an
  410. // Open error, it will be zero, and *phr will be set.
  411. EH_Err:
  412. _pnffstm->Unlock();
  413. *phr = sc;
  414. return _cbMappedStream;
  415. }
  416. //+-------------------------------------------------------------------
  417. //
  418. // Member: CNFFMappedStream::InitMappedStreamMembers
  419. //
  420. //--------------------------------------------------------------------
  421. void
  422. CNFFMappedStream::InitMappedStreamMembers()
  423. {
  424. nffITrace( "CNFFMappedStream::InitMappedStreamMembers" );
  425. _pbMappedStream = NULL;
  426. _cbMappedStream = 0;
  427. _cbMappedStreamActual = 0;
  428. _pMappedStreamOwner = NULL;
  429. _fLowMem = FALSE;
  430. _fMappedStreamDirty = FALSE;
  431. _fCheckedForRollForward = FALSE;
  432. _fStreamRenameSupported = FALSE;
  433. _cUpdateStreamInUse = _cLatestStreamInUse = 0;
  434. }
  435. //+-------------------------------------------------------------------
  436. //
  437. // Member: CNFFMappedStream::SetSize (IMappedStream)
  438. //
  439. //--------------------------------------------------------------------
  440. VOID
  441. CNFFMappedStream::SetSize(IN ULONG cb,
  442. IN BOOLEAN fPersistent,
  443. IN OUT VOID **ppv, OUT LONG *phr)
  444. {
  445. nffITrace( "CNFFMappedStream::SetSize" );
  446. BYTE *pv;
  447. HRESULT &sc = *phr;
  448. BOOL fUsingUpdateStream = FALSE, fUsingLatestStream = FALSE;
  449. DfpAssert(cb != 0);
  450. sc = S_OK;
  451. _pnffstm->Lock( INFINITE );;
  452. nffChk( _pnffstm->CheckReverted() );
  453. if( CBMAXPROPSETSTREAM < cb )
  454. nffErr( EH_Err, STG_E_MEDIUMFULL );
  455. if( fPersistent )
  456. {
  457. nffChk( CreateUpdateStreamIfNecessary() );
  458. BeginUsingUpdateStream();
  459. fUsingUpdateStream = TRUE;
  460. }
  461. else
  462. {
  463. BeginUsingLatestStream();
  464. fUsingLatestStream = TRUE;
  465. }
  466. // if we are growing the data, we should grow the file
  467. if( fPersistent && cb > _cbMappedStreamActual )
  468. {
  469. nffChk( _pnffstm->SetFileSize( CULargeInteger(cb) ) );
  470. _cbMappedStreamActual = cb;
  471. }
  472. // We only get here if we either (1) didn't want to grow the
  473. // underlying stream, or (2) we successfully grew the underlying stream.
  474. // Re-size the buffer to the size specified in cb.
  475. if( _fLowMem )
  476. {
  477. // If we want to grow the buffer In low-memory conditions,
  478. // no realloc is necessary, because
  479. // _pbMappedStream is already large enough for the largest
  480. // property set.
  481. if( NULL != ppv )
  482. *ppv = _pbMappedStream;
  483. }
  484. else if ( cb != _cbMappedStream )
  485. {
  486. // We must re-alloc the buffer.
  487. #if DBG
  488. pv = _fSimulateLowMem ? NULL : (PBYTE) CoTaskMemRealloc( _pbMappedStream, cb );
  489. #else
  490. pv = (PBYTE)CoTaskMemRealloc( _pbMappedStream, cb );
  491. #endif
  492. if ((pv == NULL) )
  493. {
  494. // allocation failed: we need to try using a backup mechanism for
  495. // more memory.
  496. // copy the data to the global reserved chunk... we will wait until
  497. // someone else has released it. it will be released on the way out
  498. // of the property code.
  499. pv = g_ReservedMemory.LockMemory();
  500. if( NULL == pv )
  501. nffErr( EH_Err, E_OUTOFMEMORY );
  502. _fLowMem = TRUE;
  503. if( NULL != _pbMappedStream )
  504. {
  505. memcpy( pv, _pbMappedStream, _cbMappedStream );
  506. }
  507. CoTaskMemFree( _pbMappedStream );
  508. }
  509. _pbMappedStream = pv;
  510. if( NULL != ppv )
  511. *ppv = pv;
  512. }
  513. _cbMappedStream = cb;
  514. // ----
  515. // Exit
  516. // ----
  517. EH_Err:
  518. if( fUsingUpdateStream )
  519. {
  520. DfpAssert( !fUsingLatestStream );
  521. EndUsingUpdateStream();
  522. }
  523. else if( fUsingLatestStream )
  524. {
  525. EndUsingLatestStream();
  526. }
  527. _pnffstm->Unlock();
  528. if( FAILED(*phr) )
  529. {
  530. propDbg((DbgFlag(*phr,DEB_ITRACE), "IMappedStream::CNtfsStream(%08X)::SetSize %s returns hr=%08X\n",
  531. this, *phr != S_OK ? "exception" : "", *phr));
  532. }
  533. }
  534. //+-------------------------------------------------------------------
  535. //
  536. // Member: CNFFMappedStream::Lock (IMappedStream)
  537. //
  538. //--------------------------------------------------------------------
  539. NTSTATUS
  540. CNFFMappedStream::Lock(IN BOOLEAN fExclusive)
  541. {
  542. // Don't trace at this level. The noice is too great!
  543. //nffXTrace( "CNFFMappedStream::Lock");
  544. UNREFERENCED_PARM(fExclusive);
  545. _pnffstm->Lock( INFINITE );
  546. return(STATUS_SUCCESS);
  547. }
  548. //+-------------------------------------------------------------------
  549. //
  550. // Member: CNFFMappedStream::Unlock (IMappedStream)
  551. //
  552. //--------------------------------------------------------------------
  553. // Should we unlock even if there's an error?
  554. NTSTATUS
  555. CNFFMappedStream::Unlock(VOID)
  556. {
  557. // Don't trace at this level. The noice is too great!
  558. //nffXTrace( "CNFFMappedStream::Unlock");
  559. // if at the end of the properties set/get call we have the low
  560. // memory region locked, we flush to disk.
  561. HRESULT sc = S_OK;
  562. if (_fLowMem)
  563. {
  564. Flush(&sc);
  565. g_ReservedMemory.UnlockMemory();
  566. _pbMappedStream = NULL;
  567. _cbMappedStream = _cbMappedStreamActual = 0;
  568. _fLowMem = FALSE;
  569. propDbg((DEB_PROP_INFO, "CMappedStreamOnHFile(%08X):Unlock low-mem returns NTSTATUS=%08X\n",
  570. this, sc));
  571. }
  572. _pnffstm->Unlock();
  573. return(sc);
  574. }
  575. //+-------------------------------------------------------------------
  576. //
  577. // Member: CNFFMappedStream::QueryTimeStamps (IMappedStream)
  578. //
  579. //--------------------------------------------------------------------
  580. VOID
  581. CNFFMappedStream::QueryTimeStamps(OUT STATPROPSETSTG *pspss, BOOLEAN fNonSimple) const
  582. {
  583. nffITrace( "CNFFMappedStream::QueryTimeStamps" );
  584. }
  585. //+-------------------------------------------------------------------
  586. //
  587. // Member: CNFFMappedStream::QueryModifyTime (IMappedStream)
  588. //
  589. //--------------------------------------------------------------------
  590. BOOLEAN
  591. CNFFMappedStream::QueryModifyTime(OUT LONGLONG *pll) const
  592. {
  593. nffITrace( "CNFFMappedStream::QueryModifyTime" );
  594. return(FALSE);
  595. }
  596. //+-------------------------------------------------------------------
  597. //
  598. // Member: Unused methods by this IMappedStream implementation:
  599. // QuerySecurity, IsWritable, GetHandle
  600. //
  601. //--------------------------------------------------------------------
  602. BOOLEAN
  603. CNFFMappedStream::QuerySecurity(OUT ULONG *pul) const
  604. {
  605. nffITrace( "CNFFMappedStream::QuerySecurity" );
  606. return(FALSE);
  607. }
  608. BOOLEAN
  609. CNFFMappedStream::IsWriteable() const
  610. {
  611. nffITrace( "CNFFMappedStream::IsWriteable" );
  612. return( (BOOLEAN) _pnffstm->IsWriteable() );
  613. }
  614. HANDLE
  615. CNFFMappedStream::GetHandle(VOID) const
  616. {
  617. nffITrace( "CNFFMappedStream::GetHandle" );
  618. return(INVALID_HANDLE_VALUE);
  619. }
  620. //+-------------------------------------------------------------------
  621. //
  622. // Member: CNFFMappedStream::SetModified/IsModified (IMappedStream)
  623. //
  624. //--------------------------------------------------------------------
  625. VOID
  626. CNFFMappedStream::SetModified(OUT LONG *phr)
  627. {
  628. nffITrace( "CNFFMappedStream::SetModified" );
  629. HRESULT &sc = *phr;
  630. _pnffstm->Lock( INFINITE );;
  631. nffChk( _pnffstm->CheckReverted() );
  632. nffChk( CreateUpdateStreamIfNecessary() );
  633. _fMappedStreamDirty = TRUE;
  634. sc = S_OK;
  635. EH_Err:
  636. _pnffstm->Unlock();
  637. }
  638. BOOLEAN
  639. CNFFMappedStream::IsModified(VOID) const
  640. {
  641. nffITrace( "CNFFMappedStream::IsModified" );
  642. return _fMappedStreamDirty;
  643. }
  644. //+-------------------------------------------------------------------
  645. //
  646. // Member: ImappedStream::IsNtMappedStream/SetChangePending
  647. //
  648. // Synopsis: Debug routines.
  649. //
  650. //--------------------------------------------------------------------
  651. #if DBGPROP
  652. BOOLEAN
  653. CNFFMappedStream::IsNtMappedStream(VOID) const
  654. {
  655. nffITrace( "CNFFMappedStream::IsNtMappedStream" );
  656. return(TRUE);
  657. }
  658. #endif
  659. #if DBGPROP
  660. BOOLEAN
  661. CNFFMappedStream::SetChangePending(BOOLEAN f)
  662. {
  663. nffITrace( "CNFFMappedStream::SetChangePending" );
  664. return(f);
  665. }
  666. #endif
  667. //
  668. // CNFFMappedStream::BeginUsingLatestStream/EndUsingLatestStream
  669. //
  670. // These routines are similar to Begin/EndUsing*Update*Stream,
  671. // except that they honor the _fUpdateStreamHasLatest flag.
  672. // Thus, if the original stream has the latest data, then this
  673. // routine will do nothing.
  674. //
  675. void
  676. CNFFMappedStream::BeginUsingLatestStream()
  677. {
  678. if( _fUpdateStreamHasLatest )
  679. {
  680. if( 0 == _cLatestStreamInUse++ )
  681. BeginUsingUpdateStream();
  682. }
  683. }
  684. void
  685. CNFFMappedStream::EndUsingLatestStream()
  686. {
  687. if( 0 != _cLatestStreamInUse )
  688. {
  689. EndUsingUpdateStream();
  690. _cLatestStreamInUse--;
  691. }
  692. DfpAssert( static_cast<USHORT>(-1) != _cLatestStreamInUse );
  693. }
  694. //
  695. // CNFFMappedStream::BeginUsingUpdateStream
  696. //
  697. // This is called when the update stream is to be used. It
  698. // does nothing, though, if we don't have an update stream
  699. // (e.g. if the file system doesn't support stream renames).
  700. // We increment the _cUpdateStreamInUse count, so that we can determine in
  701. // EndUsingUpdateStream when to swap the handles back.
  702. void
  703. CNFFMappedStream::BeginUsingUpdateStream()
  704. {
  705. if( NULL != _pstmUpdate
  706. &&
  707. INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle()
  708. &&
  709. 0 == _cUpdateStreamInUse++ )
  710. {
  711. HANDLE hTemp = _pnffstm->_hFile;
  712. _pnffstm->_hFile = _pstmUpdate->_hFile;
  713. _pstmUpdate->_hFile = hTemp;
  714. }
  715. }
  716. //
  717. // CNFFMappedStream::EndUsingUpdateStream
  718. //
  719. // Decrement the _cUpdateStreamInUse count. And, if that puts
  720. // the count down to zero, swap the handles back.
  721. //
  722. void
  723. CNFFMappedStream::EndUsingUpdateStream()
  724. {
  725. if( 0 != _cUpdateStreamInUse
  726. &&
  727. 0 == --_cUpdateStreamInUse )
  728. {
  729. DfpAssert( NULL != _pstmUpdate && INVALID_HANDLE_VALUE != _pstmUpdate->GetFileHandle() );
  730. HANDLE hTemp = _pnffstm->_hFile;
  731. _pnffstm->_hFile = _pstmUpdate->_hFile;
  732. _pstmUpdate->_hFile = hTemp;
  733. }
  734. DfpAssert( static_cast<USHORT>(-1) != _cUpdateStreamInUse );
  735. }
  736. inline HRESULT
  737. CNFFMappedStream::CreateUpdateStreamIfNecessary()
  738. {
  739. if( _fStreamRenameSupported
  740. &&
  741. ( NULL == _pstmUpdate
  742. ||
  743. INVALID_HANDLE_VALUE == _pstmUpdate->GetFileHandle()
  744. )
  745. )
  746. {
  747. return( OpenUpdateStream( TRUE ));
  748. }
  749. else
  750. return( S_OK );
  751. }
  752. //+----------------------------------------------------------------------------
  753. //
  754. // Method: CNFFMAppedStream::RollForwardIfNecessary (non-interface method)
  755. //
  756. // In the open path, we look to see if there's a leftover update stream for
  757. // a previous open of the stream, which must have crashed during a write.
  758. // If we're opening for write, we fix the problem. Otherwise, we
  759. // just remember that we'll have to read out of the update stream.
  760. //
  761. // See the CNtfsStreamForPropStg class declaration for a description of
  762. // this transactioning.
  763. //
  764. //+----------------------------------------------------------------------------
  765. HRESULT
  766. CNFFMappedStream::RollForwardIfNecessary()
  767. {
  768. HRESULT hr = S_OK;
  769. BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
  770. // If we've already checked for this, then we needn't check again.
  771. if( _fCheckedForRollForward )
  772. goto Exit;
  773. // We also needn't do anything if we're creating, since that overwrites
  774. // any existing data anyway.
  775. if( !(STGM_CREATE & _pnffstm->_grfMode) )
  776. {
  777. // Get the size of the current stream.
  778. if( !GetFileInformationByHandle( _pnffstm->_hFile, &ByHandleFileInformation ))
  779. {
  780. hr = HRESULT_FROM_WIN32( GetLastError() );
  781. goto Exit;
  782. }
  783. // If the size is zero, then there might be an update
  784. // stream with the real data.
  785. if( 0 == ByHandleFileInformation.nFileSizeLow
  786. &&
  787. 0 == ByHandleFileInformation.nFileSizeHigh )
  788. {
  789. // See if there's an update stream
  790. hr = OpenUpdateStream( FALSE );
  791. if( SUCCEEDED(hr) )
  792. {
  793. // We have a zero-length main stream and an update stream,
  794. // so there must have been a crash in ReplaceOriginalWithUpdate,
  795. // after the truncation but before the NtSetInformationFile
  796. // (FileRenameInformation).
  797. // If this is a writable stream, rename the update stream
  798. // over the zero-length one. Otherwise, we'll just read from
  799. // the update stream.
  800. _fUpdateStreamHasLatest = TRUE;
  801. if( IsWriteable() )
  802. {
  803. hr = ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
  804. if( FAILED(hr) ) goto Exit;
  805. }
  806. }
  807. else if( STG_E_FILENOTFOUND == hr )
  808. // Ignore the case where there's no update stream. This happens
  809. // when the stream is created without STGM_CREATE set.
  810. hr = S_OK;
  811. else
  812. goto Exit;
  813. }
  814. } // if( !(STGM_CREATE & _grfMode) )
  815. // We don't need to check for this again.
  816. _fCheckedForRollForward = TRUE;
  817. Exit:
  818. return( hr );
  819. } // CNtfsStreamForPropStg::RollForwardIfNecessary
  820. //+----------------------------------------------------------------------------
  821. //
  822. // Method: CNtfsStreamForPropStg::ReplaceOriginalWithUpdate (internal method)
  823. //
  824. // This method renames the update stream over the original stream, then
  825. // creates a new update stream (with no data but properly sized). If, however,
  826. // the update stream doesn't have the latest data anyway, then this routine
  827. // noops.
  828. //
  829. // See the CNtfsStreamForPropStg class declaration for a description of
  830. // this transactioning.
  831. //
  832. //+----------------------------------------------------------------------------
  833. HRESULT
  834. CNFFMappedStream::ReplaceOriginalWithUpdate( enumCREATE_NEW_UPDATE_STREAM CreateNewUpdateStream )
  835. {
  836. HRESULT hr = S_OK;
  837. NTSTATUS status;
  838. FILE_END_OF_FILE_INFORMATION file_end_of_file_information;
  839. IO_STATUS_BLOCK io_status_block;
  840. // If the original stream already has the latest data, then
  841. // there's nothing to do.
  842. if( !_fUpdateStreamHasLatest )
  843. goto Exit;
  844. DfpAssert( NULL != _pstmUpdate );
  845. DfpAssert( 0 == _cUpdateStreamInUse );
  846. // We must write the update data all the way to disk.
  847. hr = _pstmUpdate->Flush();
  848. if( FAILED(hr) ) goto Exit;
  849. // Truncate the original stream so that it can be overwritten.
  850. // After this atomic operation, the update stream is considered
  851. // *the* stream (which is why we had to flush it above).
  852. file_end_of_file_information.EndOfFile = CLargeInteger(0);
  853. status = NtSetInformationFile( _pnffstm->_hFile, &io_status_block,
  854. &file_end_of_file_information,
  855. sizeof(file_end_of_file_information),
  856. FileEndOfFileInformation );
  857. if( !NT_SUCCESS(status) )
  858. {
  859. hr = NtStatusToScode(status);
  860. goto Exit;
  861. }
  862. NtClose( _pnffstm->_hFile );
  863. _pnffstm->_hFile = INVALID_HANDLE_VALUE;
  864. // Rename the updated stream over the original (now empty) stream.
  865. // This is atomic.
  866. hr = _pstmUpdate->Rename( _pnffstm->_pwcsName, TRUE );
  867. if( FAILED(hr) )
  868. {
  869. // Go into the reverted state
  870. NtClose( _pstmUpdate->_hFile );
  871. _pstmUpdate->_hFile = INVALID_HANDLE_VALUE;
  872. goto Exit;
  873. }
  874. // Make the updated stream the master
  875. _pnffstm->_hFile = _pstmUpdate->_hFile;
  876. _pstmUpdate->_hFile = INVALID_HANDLE_VALUE;
  877. _fUpdateStreamHasLatest = FALSE;
  878. // Optionally create a new update stream.
  879. if( CREATE_NEW_UPDATE_STREAM == CreateNewUpdateStream )
  880. {
  881. // return an error if we cannot create the update stream
  882. hr = OpenUpdateStream( TRUE );
  883. if( FAILED(hr) ) goto Exit;
  884. }
  885. else
  886. DfpAssert( DONT_CREATE_NEW_UPDATE_STREAM == CreateNewUpdateStream );
  887. Exit:
  888. return( hr );
  889. } // CNFFMappedStream::ReplaceOriginalWithUpdate()
  890. //+----------------------------------------------------------------------------
  891. //
  892. // Method: CNFFMappedStream::OpenUpdateStream
  893. //
  894. // This method opens the update stream, to which stream updates are written.
  895. // This is necessary to provide a minimal level of transactioning.
  896. //
  897. // See the CNtfsStreamForPropStg class declaration for a description of
  898. // this transactioning.
  899. //
  900. //+----------------------------------------------------------------------------
  901. HRESULT
  902. CNFFMappedStream::OpenUpdateStream( BOOL fCreate )
  903. {
  904. HRESULT hr = S_OK;
  905. HANDLE hStream = INVALID_HANDLE_VALUE;
  906. CNtfsUpdateStreamName UpdateStreamName = _pnffstm->_pwcsName;
  907. // Open the NTFS stream
  908. hr = _pnffstm->_pnffstg->GetStreamHandle( &hStream,
  909. UpdateStreamName,
  910. _pnffstm->_grfMode | (fCreate ? STGM_CREATE : 0),
  911. fCreate );
  912. if( FAILED(hr) ) goto Exit;
  913. // If necessary, instantiate a CNtfsUpdateStreamForPropStg
  914. if( NULL == _pstmUpdate )
  915. {
  916. _pstmUpdate = new CNtfsUpdateStreamForPropStg( _pnffstm->_pnffstg, _pnffstm->_pBlockingLock );
  917. if( NULL == _pstmUpdate )
  918. {
  919. hr = E_OUTOFMEMORY;
  920. goto Exit;
  921. }
  922. }
  923. // Put the NTFS stream handle into the CNtfsUpdateStreamForPropStg
  924. hr = _pnffstm->_pnffstg->InitCNtfsStream( _pstmUpdate, hStream,
  925. _pnffstm->_grfMode | (fCreate ? STGM_CREATE : 0),
  926. UpdateStreamName );
  927. hStream = INVALID_HANDLE_VALUE; // ownership of the handle has changed
  928. if( FAILED(hr) ) goto Exit;
  929. // If we're creating the update stream, size it to match the size
  930. // of the original stream.
  931. if( fCreate )
  932. {
  933. ULONG ulSize = GetSize(&hr);
  934. if( FAILED(hr) ) goto Exit;
  935. hr = _pstmUpdate->SetSize( CULargeInteger(ulSize) );
  936. if( FAILED(hr) ) goto Exit;
  937. }
  938. Exit:
  939. if( INVALID_HANDLE_VALUE != hStream )
  940. NtClose( hStream );
  941. if( FAILED(hr) )
  942. {
  943. // If we were attempting a create but failed, then ensure the
  944. // update stream is gone.
  945. if( NULL != _pstmUpdate && fCreate )
  946. _pstmUpdate->Delete();
  947. DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
  948. }
  949. return( hr );
  950. } // CNFFMappedStream::OpenUpdateStream()
  951. //+----------------------------------------------------------------------------
  952. //
  953. // Method: CNFFMappedStream::Init (override from CNtfsStream)
  954. //
  955. // This method initializes the CNtfsStream, and checks the file system to
  956. // determine if we can support the update stream (for robustness). The
  957. // necessary file system support is stream renaming, which we use to provide
  958. // a minimal level of transactioning.
  959. //
  960. // See the CNtfsStreamForPropStg class declaration for a description of
  961. // this transactioning.
  962. //
  963. //+----------------------------------------------------------------------------
  964. HRESULT
  965. CNFFMappedStream::Init( HANDLE hFile )
  966. {
  967. HRESULT hr = S_OK;
  968. NTSTATUS status = STATUS_SUCCESS;
  969. FILE_FS_ATTRIBUTE_INFORMATION file_fs_attribute_information;
  970. IO_STATUS_BLOCK io_status_block;
  971. // Check to see if we'll be able to support stream renaming.
  972. if( NULL != _pnffstm->_pnffstg )
  973. {
  974. // We can at least see an IStorage for the file, so stream renaming
  975. // could potentially work, but we also need to query the file system
  976. // attributes to see if it actually supports it.
  977. status = NtQueryVolumeInformationFile( hFile, &io_status_block,
  978. &file_fs_attribute_information,
  979. sizeof(file_fs_attribute_information),
  980. FileFsAttributeInformation );
  981. // We should always get a buffer-overflow error here, because we don't
  982. // provide enough buffer for the file system name, but that's OK because
  983. // we don't need it (status_buffer_overflow is just a warning, so the rest
  984. // of the data is good).
  985. if( !NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status)
  986. {
  987. hr = NtStatusToScode(status);
  988. goto Exit;
  989. }
  990. // There's no attribute bit which says "supports stream rename". The best
  991. // we can do is look for another NTFS5 feature and make an inferrence.
  992. if( FILE_SUPPORTS_OBJECT_IDS & file_fs_attribute_information.FileSystemAttributes )
  993. _fStreamRenameSupported = TRUE;
  994. }
  995. Exit:
  996. return( hr );
  997. } // CNFFMappedStream::Init()
  998. HRESULT
  999. CNFFMappedStream::ShutDown()
  1000. { // mikehill step
  1001. HRESULT hr = S_OK;
  1002. _pnffstm->Lock( INFINITE );
  1003. // Close the mapped stream
  1004. Close( &hr );
  1005. if( FAILED(hr) && STG_E_REVERTED != hr )
  1006. propDbg(( DEB_ERROR, "CNFFMappedStream(0x%x)::ShutDown failed call to CNtfsStream::Close (%08x)\n",
  1007. this, hr ));
  1008. // Overwrite the original stream with the update (if necessary),
  1009. // but don't bother to create a new update stream afterwards.
  1010. if( NULL != _pstmUpdate )
  1011. {
  1012. hr = ReplaceOriginalWithUpdate( DONT_CREATE_NEW_UPDATE_STREAM );
  1013. if( FAILED(hr) )
  1014. propDbg(( DEB_ERROR, "CNFFMappedStream(0x%x)::ShutDown failed call to ReplaceOriginalWithUpdate (%08x)\n",
  1015. this, hr ));
  1016. }
  1017. // Release the update stream.
  1018. if( NULL != _pstmUpdate )
  1019. DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
  1020. propDbg(( DbgFlag(hr,DEB_ITRACE), "CNFFMappedStream(0x%x)::ShutDown() returns %08x\n", this, hr ));
  1021. _pnffstm->Unlock();
  1022. return( hr );
  1023. } // CNFFMappedStream::ShutDown
  1024. void
  1025. CNFFMappedStream::Read( void *pv, ULONG ulOffset, ULONG *pcbCopy )
  1026. {
  1027. if( *pcbCopy > _cbMappedStream )
  1028. *pcbCopy = 0;
  1029. else if( *pcbCopy > _cbMappedStream - ulOffset )
  1030. *pcbCopy = _cbMappedStream - ulOffset;
  1031. memcpy( pv, &_pbMappedStream[ ulOffset ], *pcbCopy );
  1032. return;
  1033. }
  1034. void
  1035. CNFFMappedStream::Write( const void *pv, ULONG ulOffset, ULONG *pcbCopy )
  1036. {
  1037. if( *pcbCopy > _cbMappedStream )
  1038. *pcbCopy = 0;
  1039. else if( *pcbCopy + ulOffset > _cbMappedStream )
  1040. *pcbCopy = _cbMappedStream - ulOffset;
  1041. memcpy( &_pbMappedStream[ulOffset], pv, *pcbCopy );
  1042. return;
  1043. }
  1044. //+----------------------------------------------------------------------------
  1045. //
  1046. // Method: IStorageTest::UseNTFS4Streams (DBG only)
  1047. //
  1048. // This method can be used to disable the stream-renaming necessary for
  1049. // robust property sets. This emulates an NTFS4 volume.
  1050. //
  1051. //+----------------------------------------------------------------------------
  1052. #if DBG
  1053. HRESULT STDMETHODCALLTYPE
  1054. CNFFMappedStream::UseNTFS4Streams( BOOL fUseNTFS4Streams )
  1055. {
  1056. HRESULT hr = S_OK;
  1057. if( _fUpdateStreamHasLatest )
  1058. {
  1059. hr = STG_E_INVALIDPARAMETER;
  1060. propDbg(( DEB_ERROR, "CNtfsStreamForPropStg(0x%x)::UseNTFS4Streams(%s)"
  1061. "was called while an update stream was already in use\n",
  1062. this, fUseNTFS4Streams ? "True" : "False" ));
  1063. }
  1064. else if( fUseNTFS4Streams )
  1065. {
  1066. DfpVerify( 0 == RELEASE_INTERFACE(_pstmUpdate) );
  1067. _fStreamRenameSupported = FALSE;
  1068. }
  1069. else
  1070. {
  1071. // Shutting NTFS4streams off isn't implemented
  1072. hr = E_NOTIMPL;
  1073. }
  1074. return( hr );
  1075. } // CNFFMAppedStream::UseNTFS4Streams
  1076. #endif // #if DBG
  1077. #if DBG
  1078. HRESULT
  1079. CNFFMappedStream::GetFormatVersion( WORD *pw )
  1080. {
  1081. return( E_NOTIMPL );
  1082. }
  1083. #endif // #if DBG
  1084. #if DBG
  1085. HRESULT
  1086. CNFFMappedStream::SimulateLowMemory( BOOL fSimulate )
  1087. {
  1088. _fSimulateLowMem = fSimulate;
  1089. return( S_OK );
  1090. }
  1091. #endif // #if DBG
  1092. #if DBG
  1093. LONG
  1094. CNFFMappedStream::GetLockCount()
  1095. {
  1096. return( _pnffstm->GetLockCount() );
  1097. }
  1098. #endif // #if DBG
  1099. #if DBG
  1100. HRESULT
  1101. CNFFMappedStream::IsDirty()
  1102. {
  1103. return( _fMappedStreamDirty ? S_OK : S_FALSE );
  1104. }
  1105. #endif // #if DBG
  1106. //+----------------------------------------------------------------------------
  1107. //
  1108. // Method: CNtfsUpdateStreamForPropStg::ShutDown (overrides CNtfsStream)
  1109. //
  1110. // Override so that we can remove this stream from the linked-list, but
  1111. // not do a flush. See the CNtfsStreamForPropStg class declaration for
  1112. // more information on this class.
  1113. //
  1114. //+----------------------------------------------------------------------------
  1115. HRESULT
  1116. CNtfsUpdateStreamForPropStg::ShutDown()
  1117. {
  1118. RemoveSelfFromList();
  1119. return( S_OK );
  1120. } // CNtfsUpdateStreamForPropStg::ShutDown