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.

1524 lines
39 KiB

  1. //+============================================================================
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 1998.
  5. //
  6. // File: hntfsstg.cxx
  7. //
  8. // This file provides the NFF (NTFS Flat File) IStream implementation.
  9. //
  10. // History:
  11. // 5/6/98 MikeHill
  12. // - Use CoTaskMem rather than new/delete.
  13. //
  14. //+============================================================================
  15. #include <pch.cxx>
  16. #include <tstr.h>
  17. #include "cli.hxx"
  18. #include "expparam.hxx"
  19. #ifdef _MAC_NODOC
  20. ASSERTDATA // File-specific data for FnAssert
  21. #endif
  22. DECLARE_INFOLEVEL(nff)
  23. #ifndef DEB_INFO
  24. #define DEB_INFO DEB_USER1
  25. #endif
  26. //+----------------------------------------------------------------------------
  27. //
  28. // Method: CNtfsStream::AddRef (IUnknown)
  29. //
  30. //+----------------------------------------------------------------------------
  31. ULONG
  32. CNtfsStream::AddRef()
  33. {
  34. LONG cRefs;
  35. cRefs = InterlockedIncrement( &_cRefs );
  36. nffDebug((DEB_REFCOUNT, "CNtfsStream::AddRef(this=%x) == %d\n",
  37. this, cRefs));
  38. return cRefs;
  39. }
  40. //+----------------------------------------------------------------------------
  41. //
  42. // Method: CNtfsStream::Release (IUnknown)
  43. //
  44. //+----------------------------------------------------------------------------
  45. ULONG
  46. CNtfsStream::Release()
  47. {
  48. ULONG ulRet = InterlockedDecrement( &_cRefs );
  49. if( 0 == ulRet )
  50. {
  51. RemoveSelfFromList();
  52. delete this;
  53. }
  54. nffDebug((DEB_REFCOUNT, "CNtfsStream::Release(this=%x) == %d\n",
  55. this, ulRet));
  56. return( ulRet );
  57. }
  58. //+----------------------------------------------------------------------------
  59. //
  60. // Method: CNtfsStream::AddRef (IUnknown)
  61. //
  62. //+----------------------------------------------------------------------------
  63. HRESULT
  64. CNtfsStream::QueryInterface(
  65. REFIID riid,
  66. void** ppv )
  67. {
  68. HRESULT sc=S_OK;
  69. #if DBG == 1
  70. WCHAR strIID[64];
  71. StringFromGUID2(riid, strIID, 64);
  72. nffDebug(( DEB_TRACE | DEB_REFCOUNT,
  73. "CNtfsStream::QueryInterface( %ws )\n", strIID ));
  74. #endif
  75. NFF_VALIDATE( QueryInterface( riid, ppv ) );
  76. nffChk( CheckReverted() );
  77. if( IsEqualIID( riid, IID_IUnknown )
  78. ||
  79. IsEqualIID( riid, IID_IStream )
  80. ||
  81. IsEqualIID( riid, IID_ISequentialStream ) )
  82. {
  83. *ppv = static_cast<IStream*>(this);
  84. AddRef();
  85. return( S_OK );
  86. }
  87. else if( IsEqualIID( riid, IID_IMappedStream ))
  88. {
  89. *ppv = static_cast<IMappedStream*>(&_nffMappedStream);
  90. AddRef();
  91. return( S_OK );
  92. }
  93. else if( IsEqualIID( riid, IID_ILockBytes ))
  94. {
  95. *ppv = static_cast<ILockBytes*>(this);
  96. AddRef();
  97. return( S_OK );
  98. }
  99. #if DBG == 1
  100. else if( IsEqualIID( riid, IID_IStorageTest ))
  101. {
  102. *ppv = static_cast<IStorageTest*>(this);
  103. AddRef();
  104. return( S_OK );
  105. }
  106. #endif // #if DBG
  107. else
  108. {
  109. nffDebug(( DEB_TRACE | DEB_REFCOUNT,
  110. "CNtfsStream::QueryInterface() Failed E_NOINTERFACE\n" ));
  111. return( E_NOINTERFACE );
  112. }
  113. EH_Err:
  114. return sc;
  115. }
  116. //+----------------------------------------------------------------------------
  117. //
  118. // Method: CNtfsStream::Seek (IStream)
  119. //
  120. //+----------------------------------------------------------------------------
  121. HRESULT
  122. CNtfsStream::Seek(
  123. LARGE_INTEGER dlibMove,
  124. DWORD dwOrigin,
  125. ULARGE_INTEGER *puliNewPos)
  126. {
  127. HRESULT sc = S_OK;
  128. LARGE_INTEGER liFileSize;
  129. LARGE_INTEGER liNewPos = { 0, 0 };
  130. nffDebug(( DEB_TRACE, "CNtfsStream::Seek( %x:%08x, %d, %p );\n",
  131. dlibMove.HighPart, dlibMove.LowPart,
  132. dwOrigin, puliNewPos ));
  133. NFF_VALIDATE( Seek( dlibMove, dwOrigin, puliNewPos ) );
  134. Lock( INFINITE );
  135. nffChk( CheckReverted() );
  136. switch( dwOrigin )
  137. {
  138. case STREAM_SEEK_SET:
  139. liNewPos.QuadPart = dlibMove.QuadPart;
  140. break;
  141. case STREAM_SEEK_CUR:
  142. liNewPos.QuadPart = _liCurrentSeekPosition.QuadPart + dlibMove.QuadPart;
  143. break;
  144. case STREAM_SEEK_END:
  145. liFileSize.LowPart = GetFileSize( _hFile,
  146. (ULONG*)(&liFileSize.HighPart) );
  147. if( 0xFFFFFFFF == liFileSize.LowPart && NO_ERROR != GetLastError() )
  148. {
  149. nffChk( HRESULT_FROM_WIN32( GetLastError() ) );
  150. }
  151. liNewPos.QuadPart = liFileSize.QuadPart + dlibMove.QuadPart;
  152. break;
  153. default:
  154. nffChk(STG_E_INVALIDPARAMETER);
  155. break;
  156. }
  157. // Compatibility with Docfile. Seeking < 0 fails.
  158. if( liNewPos.QuadPart < 0 )
  159. nffErr( EH_Err, STG_E_INVALIDFUNCTION );
  160. _liCurrentSeekPosition = liNewPos;
  161. // If desired, give the caller the now-current seek position.
  162. if( NULL != puliNewPos )
  163. *puliNewPos = _liCurrentSeekPosition;
  164. EH_Err:
  165. Unlock();
  166. return( sc );
  167. }
  168. //+----------------------------------------------------------------------------
  169. //
  170. // Method: CNtfsStream::SetSize (IStream)
  171. //
  172. //+----------------------------------------------------------------------------
  173. HRESULT
  174. CNtfsStream::SetSize(
  175. ULARGE_INTEGER uliNewSize)
  176. {
  177. HRESULT sc = S_OK;
  178. CLargeInteger liEOF;
  179. if ( uliNewSize.HighPart != 0 )
  180. nffErr(EH_Err, STG_E_INVALIDFUNCTION);
  181. nffDebug(( DEB_ITRACE | DEB_INFO | DEB_WRITE,
  182. "CNtfsStream::SetSize(%x:%x) hdl=%x, stream='%ws'\n",
  183. uliNewSize.QuadPart,
  184. _hFile,
  185. _pwcsName ));
  186. NFF_VALIDATE( SetSize( uliNewSize ) );
  187. Lock( INFINITE );
  188. nffChk( CheckReverted() );
  189. // If this stream is mapped, set the size accordingly
  190. if( _nffMappedStream.IsMapped() )
  191. {
  192. _nffMappedStream.SetSize( uliNewSize.LowPart, TRUE, NULL, &sc );
  193. }
  194. else
  195. {
  196. sc = SetFileSize( CULargeInteger(uliNewSize) );
  197. }
  198. if( !FAILED(sc) )
  199. sc = S_OK;
  200. EH_Err:
  201. Unlock();
  202. return( sc);
  203. }
  204. //+----------------------------------------------------------------------------
  205. //
  206. // Method: CNtfsStream::CopyTo (IStream)
  207. //
  208. // There's no way of knowing what the IStream is to which we're copying, so
  209. // we have to assume that we might be copying to ourself. And given that
  210. // assumption, we have to deal with the case that this is an overlapping
  211. // copy (e.g., "copy 10 bytes from offset 0 to offset 5").
  212. //
  213. //+----------------------------------------------------------------------------
  214. HRESULT
  215. CNtfsStream::CopyTo(
  216. IStream *pstm,
  217. ULARGE_INTEGER cb,
  218. ULARGE_INTEGER *pcbRead,
  219. ULARGE_INTEGER *pcbWritten)
  220. {
  221. return E_NOTIMPL;
  222. // Commenting out this code, because it isn't currently used and is
  223. // not fully tested.
  224. /*
  225. nffXTrace( "CNtfsStream::CopyTo" );
  226. HRESULT sc = S_OK;
  227. PVOID pv = NULL;
  228. ULONG cbRead = 0, cbWritten = 0;
  229. CULargeInteger cbReadTotal = 0, cbWrittenTotal = 0;
  230. CLargeInteger liZero = 0;
  231. CULargeInteger uliOriginalSourcePosition, uliOriginalDestPosition;
  232. CULargeInteger cbSourceSize, cbDestSize;
  233. ULONG cbPerCopy = 0;
  234. STATSTG statstg;
  235. CULargeInteger cbRequested = cb;
  236. BOOL fCopyForward;
  237. NFF_VALIDATE( CopyTo( pstm, cb, pcbRead, pcbWritten ) );
  238. Lock( INFINITE );
  239. nffChk( CheckReverted() );
  240. if( NULL == pstm)
  241. nffErr( EH_Err, STG_E_INVALIDPARAMETER );
  242. // Determine how much we'll copy at a time.
  243. // As of this writing, STREAMBUFFERSIZE is 8192 bytes
  244. if( cbRequested > STREAMBUFFERSIZE )
  245. cbPerCopy = STREAMBUFFERSIZE;
  246. else
  247. cbPerCopy = cbRequested.LowPart;
  248. // ------------------------------------------------------------------
  249. // Get the current stream sizes/positions, and adjust the destination
  250. // size if necessary
  251. // ------------------------------------------------------------------
  252. nffChk( this->Seek( liZero, STREAM_SEEK_CUR, &uliOriginalSourcePosition ) );
  253. nffChk( pstm->Seek( liZero, STREAM_SEEK_CUR, &uliOriginalDestPosition ) );
  254. nffChk( this->Stat( &statstg, STATFLAG_NONAME ) );
  255. cbSourceSize = statstg.cbSize;
  256. nffChk( pstm->Stat( &statstg, STATFLAG_NONAME ) );
  257. cbDestSize = statstg.cbSize;
  258. // Ensure the sizes are valid (we can't handle anything with the high bit
  259. // set, because Seek takes a signed offset).
  260. if( static_cast<CLargeInteger>(cbSourceSize) < 0
  261. ||
  262. static_cast<CLargeInteger>(cbDestSize) < 0 )
  263. {
  264. nffErr( EH_Err, STG_E_INVALIDHEADER );
  265. }
  266. // Don't copy more than the source stream has available
  267. if( cbRequested > cbSourceSize - uliOriginalSourcePosition )
  268. cbRequested = cbSourceSize - uliOriginalSourcePosition;
  269. // If necessary, grow the destination stream.
  270. if( cbSourceSize - uliOriginalSourcePosition > cbDestSize - uliOriginalDestPosition )
  271. {
  272. cbDestSize = cbSourceSize - uliOriginalSourcePosition + uliOriginalDestPosition;
  273. nffChk( pstm->SetSize( cbDestSize ) );
  274. }
  275. // ----------------------
  276. // Allocate a copy buffer
  277. // ----------------------
  278. nffMem( pv = CoTaskMemAlloc( cbPerCopy ) );
  279. // -----------------------------------------------------------------------------
  280. // Determine if we're copying forwards (high seek position to low) or backwards.
  281. // -----------------------------------------------------------------------------
  282. fCopyForward = TRUE;
  283. if( uliOriginalSourcePosition < uliOriginalDestPosition )
  284. {
  285. // E.g., say we're copying 15 bytes from offset 0 to offset 5,
  286. // and we're only able to copy 10 bytes at a time.
  287. // If we copy bytes 0-9 to offset 5, we'll end up overwriting
  288. // bytes 10-14, and be unable to complete the copy.
  289. // So instead, we'll copy bytes 5-14 to offset 10, and finish
  290. // up by copying bytes 0-4 to offset 5.
  291. fCopyForward = FALSE;
  292. // To do this kind of backwards copy, we need to start by seeking
  293. // towards the end of the stream.
  294. CULargeInteger uliNewSourcePosition, uliNewDestPosition;
  295. uliNewSourcePosition = cbSourceSize - cbPerCopy;
  296. nffChk( this->Seek( uliNewSourcePosition, STREAM_SEEK_SET, NULL ) );
  297. uliNewDestPosition = cbDestSize - cbPerCopy;
  298. nffChk( pstm->Seek( uliNewDestPosition, STREAM_SEEK_SET, NULL ) );
  299. }
  300. // --------------
  301. // Copy in chunks
  302. // --------------
  303. cbPerCopy = cbRequested > cbPerCopy ? cbPerCopy : cbRequested.LowPart;
  304. while( cbRequested > 0 )
  305. {
  306. // Read from the source
  307. nffChk( this->Read( pv, cbPerCopy, &cbRead ) );
  308. if( cbRead != cbPerCopy )
  309. nffErr(EH_Err, STG_E_READFAULT);
  310. cbReadTotal += cbRead;
  311. // Write to the dest
  312. nffChk( pstm->Write( pv, cbPerCopy, &cbWritten ) );
  313. if( cbWritten != cbPerCopy )
  314. nffErr( EH_Err, STG_E_WRITEFAULT );
  315. cbWrittenTotal += cbWritten;
  316. // Adjust the amount remaining to be copied
  317. cbRequested -= cbPerCopy;
  318. // Determine how much to copy in the next iteration (this will
  319. // always be cbPerCopy until the last iteration). If copying
  320. // backwards, we need to manually adjust the seek pointer.
  321. cbPerCopy = (cbRequested > cbPerCopy) ? cbPerCopy : cbRequested.LowPart;
  322. if( !fCopyForward )
  323. {
  324. nffChk( this->Seek( -static_cast<CLargeInteger>(cbPerCopy),
  325. STREAM_SEEK_CUR, NULL ) );
  326. nffChk( pstm->Seek( -static_cast<CLargeInteger>(cbPerCopy),
  327. STREAM_SEEK_CUR, NULL ) );
  328. }
  329. }
  330. // If we were backward-copying, adjust the seek pointers
  331. // as if we had forward-copied
  332. if( !fCopyForward )
  333. {
  334. uliOriginalSourcePosition += cbReadTotal;
  335. nffChk( this->Seek( uliOriginalSourcePosition, STREAM_SEEK_SET, NULL ) );
  336. uliOriginalDestPosition += cbWrittenTotal;
  337. nffChk( pstm->Seek( uliOriginalDestPosition, STREAM_SEEK_SET, NULL ) );
  338. }
  339. // ----
  340. // Exit
  341. // ----
  342. if( NULL != pcbRead )
  343. *pcbRead = cbReadTotal;
  344. if( NULL != pcbWritten )
  345. *pcbWritten = cbWrittenTotal;
  346. EH_Err:
  347. if( NULL != pv )
  348. CoTaskMemFree(pv);
  349. Unlock();
  350. return(sc);
  351. */
  352. }
  353. //+----------------------------------------------------------------------------
  354. //
  355. // Method: CNtfsStream::Commit (IStream)
  356. //
  357. //+----------------------------------------------------------------------------
  358. HRESULT
  359. CNtfsStream::Commit( DWORD grfCommitFlags )
  360. {
  361. nffXTrace( "CNtfsStream::Commit" );
  362. HRESULT sc = S_OK;
  363. NFF_VALIDATE( Commit( grfCommitFlags ) );
  364. if(~(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE|STGC_DEFAULT) & grfCommitFlags)
  365. nffErr( EH_Err, STG_E_INVALIDFLAG );
  366. Lock( INFINITE );
  367. nffChkTo ( EH_Unlock, CheckReverted() );
  368. if( !(STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE & grfCommitFlags) )
  369. {
  370. if( !FlushFileBuffers( _hFile ))
  371. sc = LAST_SCODE;
  372. }
  373. EH_Unlock:
  374. Unlock();
  375. EH_Err:
  376. return sc;
  377. }
  378. //+----------------------------------------------------------------------------
  379. //
  380. // Method: CNtfsStream::Revert (IStream)
  381. //
  382. //+----------------------------------------------------------------------------
  383. HRESULT
  384. CNtfsStream::Revert(void)
  385. {
  386. nffXTrace( "CNtfsStream::Revert" );
  387. // We only support direct-mode.
  388. return CheckReverted();
  389. }
  390. //+----------------------------------------------------------------------------
  391. //
  392. // Method: CNtfsStream::LockRegion (IStream)
  393. //
  394. //+----------------------------------------------------------------------------
  395. HRESULT
  396. CNtfsStream::LockRegion(
  397. ULARGE_INTEGER libOffset,
  398. ULARGE_INTEGER cb,
  399. DWORD dwLockType)
  400. {
  401. nffXTrace( "CNtfsStream::LockRegion" );
  402. HRESULT sc = S_OK;
  403. NFF_VALIDATE( LockRegion( libOffset, cb, dwLockType ) );
  404. Lock( INFINITE );
  405. nffChk( CheckReverted() );
  406. if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE)
  407. nffErr( EH_Err, STG_E_INVALIDFUNCTION );
  408. if( !LockFile( _hFile, libOffset.LowPart, libOffset.HighPart,
  409. cb.LowPart, cb.HighPart))
  410. {
  411. nffErr( EH_Err, LAST_SCODE );
  412. }
  413. EH_Err:
  414. Unlock();
  415. return( sc );
  416. }
  417. //+----------------------------------------------------------------------------
  418. //
  419. // Method: CNtfsStream::UnlockRegion (ILockBytes)
  420. //
  421. //+----------------------------------------------------------------------------
  422. HRESULT
  423. CNtfsStream::UnlockRegion(
  424. ULARGE_INTEGER libOffset,
  425. ULARGE_INTEGER cb,
  426. DWORD dwLockType)
  427. {
  428. nffXTrace( "CNtfsStream::UnlockRegion" );
  429. HRESULT sc = S_OK;
  430. NFF_VALIDATE( UnlockRegion( libOffset, cb, dwLockType ) );
  431. Lock( INFINITE );
  432. nffChk( CheckReverted() );
  433. if (dwLockType != LOCK_EXCLUSIVE && dwLockType != LOCK_ONLYONCE)
  434. {
  435. nffErr( EH_Err, STG_E_INVALIDFUNCTION );
  436. }
  437. if( !UnlockFile(_hFile, libOffset.LowPart, libOffset.HighPart,
  438. cb.LowPart, cb.HighPart))
  439. {
  440. nffErr( EH_Err, LAST_SCODE );
  441. }
  442. EH_Err:
  443. Unlock();
  444. return( sc );
  445. }
  446. //+----------------------------------------------------------------------------
  447. //
  448. // Method: CNtfsStream::Stat (IStream)
  449. //
  450. //+----------------------------------------------------------------------------
  451. HRESULT
  452. CNtfsStream::Stat(
  453. STATSTG *pstatstg,
  454. DWORD grfStatFlag)
  455. {
  456. nffXTrace( "CNtfsStream::Stat" );
  457. STATSTG statstg;
  458. HRESULT sc = S_OK;
  459. NTSTATUS status = STATUS_SUCCESS;
  460. FILE_ACCESS_INFORMATION file_access_information;
  461. IO_STATUS_BLOCK IoStatusBlock;
  462. BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
  463. NFF_VALIDATE( Stat( pstatstg, grfStatFlag ) );
  464. Lock( INFINITE );
  465. nffChkTo ( EH_Lock, CheckReverted() );
  466. ZeroMemory((void*)&statstg, sizeof(STATSTG));
  467. // Get the name, if desired
  468. if( (STATFLAG_NONAME & grfStatFlag) )
  469. statstg.pwcsName = NULL;
  470. else
  471. {
  472. nffAssert( NULL != _pwcsName );
  473. ULONG cbName = sizeof(WCHAR)*((ULONG)wcslen(_pwcsName) + 1);
  474. nffMem( statstg.pwcsName = reinterpret_cast<WCHAR*>
  475. ( CoTaskMemAlloc( cbName )));
  476. StringCbCopy( statstg.pwcsName, cbName, _pwcsName );
  477. }
  478. // Get the type
  479. statstg.type = STGTY_STREAM;
  480. statstg.grfLocksSupported = LOCK_EXCLUSIVE | LOCK_ONLYONCE;
  481. // Get the size & times.
  482. if( !GetFileInformationByHandle( _hFile, &ByHandleFileInformation ))
  483. nffErr( EH_Err, LAST_SCODE );
  484. statstg.cbSize.LowPart = ByHandleFileInformation.nFileSizeLow;
  485. statstg.cbSize.HighPart = ByHandleFileInformation.nFileSizeHigh;
  486. // We get a time back in ByHandleFileInformation, but it's the file's times,
  487. // not the streams times. So really the stream times are not supported, and
  488. // we'll just set them to zero.
  489. statstg.mtime = statstg.atime = statstg.ctime = CFILETIME(0);
  490. // Get the STGM modes
  491. statstg.grfMode = _grfMode & ~STGM_CREATE;
  492. *pstatstg = statstg;
  493. EH_Err:
  494. if( FAILED(sc) && NULL != statstg.pwcsName )
  495. CoTaskMemFree( statstg.pwcsName );
  496. EH_Lock:
  497. Unlock();
  498. return( sc );
  499. }
  500. //+----------------------------------------------------------------------------
  501. //
  502. // Method: CNtfsStream::Clone (IStream)
  503. //
  504. //+----------------------------------------------------------------------------
  505. HRESULT
  506. CNtfsStream::Clone(
  507. IStream** ppstm)
  508. {
  509. nffXTrace( "CNtfsStream::Clone" );
  510. return( E_NOTIMPL );
  511. }
  512. //+----------------------------------------------------------------------------
  513. //
  514. // Method: CNtfsStream::Read (IStream)
  515. //
  516. //+----------------------------------------------------------------------------
  517. HRESULT
  518. CNtfsStream::Read(
  519. void* pv,
  520. ULONG cb,
  521. ULONG* pcbRead)
  522. {
  523. nffXTrace( "CNtfsStream::Read" );
  524. HRESULT sc = S_OK;
  525. ULONG cbRead = 0;
  526. nffDebug(( DEB_ITRACE, "Read( pv=0x%x, cb=0x%x );\n", pv, cb ));
  527. NFF_VALIDATE( Read( pv, cb, pcbRead ) );
  528. Lock( INFINITE );
  529. nffChk( CheckReverted() );
  530. nffChk( ReadAt( _liCurrentSeekPosition, pv, cb, &cbRead ) );
  531. _liCurrentSeekPosition += cbRead;
  532. nffDebug(( DEB_ITRACE, "Read() read %x bytes.\n", cbRead ));
  533. if( NULL != pcbRead )
  534. *pcbRead = cbRead;
  535. EH_Err:
  536. Unlock();
  537. return( sc );
  538. }
  539. //+----------------------------------------------------------------------------
  540. //
  541. // Method: CNtfsStream::ReadAt (ILockBytes)
  542. //
  543. //+----------------------------------------------------------------------------
  544. HRESULT
  545. CNtfsStream::ReadAt(
  546. ULARGE_INTEGER ulOffset,
  547. void* pv,
  548. ULONG cb,
  549. ULONG* pcbRead)
  550. {
  551. nffXTrace( "CNtfsStream::ReadAt" );
  552. HRESULT sc = S_OK;
  553. nffDebug(( DEB_ITRACE, "ReadAt( off=%x:%x, pv=0x%x, cb=0x%x );\n",
  554. ulOffset, pv, cb ));
  555. NFF_VALIDATE( ReadAt( ulOffset, pv, cb, pcbRead ) );
  556. if( static_cast<LONG>(ulOffset.HighPart) < 0 )
  557. return TYPE_E_SIZETOOBIG;
  558. Lock( INFINITE );
  559. nffChk( CheckReverted() );
  560. // Is this stream mapped?
  561. if( _nffMappedStream.IsMapped() )
  562. {
  563. // This stream is mapped. We'll read directly from the mapping buffer.
  564. _nffMappedStream.Read( pv, ulOffset.LowPart, &cb );
  565. if( NULL != pcbRead )
  566. *pcbRead = cb;
  567. }
  568. else
  569. {
  570. // No, just read from the file.
  571. nffChk( SyncReadAtFile( ulOffset, pv, cb, pcbRead ) );
  572. }
  573. nffDebug(( DEB_ITRACE, "ReadAt() read %x bytes.\n",
  574. NULL == pcbRead ? -1 : *pcbRead ));
  575. EH_Err:
  576. Unlock();
  577. return( sc );
  578. }
  579. //+----------------------------------------------------------------------------
  580. //
  581. // Method: CNtfsStream::Write (IStream)
  582. //
  583. //+----------------------------------------------------------------------------
  584. HRESULT
  585. CNtfsStream::Write(
  586. const void* pv,
  587. ULONG cb,
  588. ULONG* pcbWritten)
  589. {
  590. nffXTrace( "CNtfsStream::Write" );
  591. HRESULT sc = S_OK;
  592. ULONG cbWritten = 0;
  593. nffDebug(( DEB_ITRACE, "Write( pv=0x%x, cb=0x%x );\n", pv, cb ));
  594. NFF_VALIDATE( Write( pv, cb, pcbWritten ) );
  595. Lock( INFINITE );
  596. nffChk( CheckReverted() );
  597. nffChk(WriteAt( _liCurrentSeekPosition, pv, cb, &cbWritten ));
  598. _liCurrentSeekPosition += cbWritten;
  599. nffDebug(( DEB_ITRACE, "Write() wrote %x bytes.\n", cbWritten ));
  600. if( NULL != pcbWritten )
  601. *pcbWritten = cbWritten;
  602. EH_Err:
  603. Unlock();
  604. return( sc );
  605. }
  606. //+----------------------------------------------------------------------------
  607. //
  608. // Method: CNtfsStream::WriteAt (ILockBytes)
  609. //
  610. //+----------------------------------------------------------------------------
  611. HRESULT
  612. CNtfsStream::WriteAt(
  613. ULARGE_INTEGER ulOffset,
  614. const void* pv,
  615. ULONG cb,
  616. ULONG* pcbWritten)
  617. {
  618. nffXTrace( "CNtfsStream::WriteAt" );
  619. HRESULT sc = S_OK;
  620. nffDebug(( DEB_ITRACE, "WriteAt( off=%x:%x, pv=0x%x, cb=0x%x );\n",
  621. ulOffset, pv, cb ));
  622. NFF_VALIDATE( WriteAt( ulOffset, pv, cb, pcbWritten ) );
  623. if( ((LONG)(ulOffset.HighPart)) < 0 )
  624. return( TYPE_E_SIZETOOBIG );
  625. Lock( INFINITE );
  626. nffChk( CheckReverted() );
  627. // Is this stream mapped?
  628. if( _nffMappedStream.IsMapped() )
  629. {
  630. // This stream is mapped, we'll take the Write to the mapping.
  631. ULONG iPosition = _nffMappedStream.SizeOfMapping() - _liCurrentSeekPosition.LowPart;
  632. if( cb > iPosition )
  633. {
  634. _nffMappedStream.SetSize( iPosition + cb, TRUE, NULL, &sc );
  635. nffChk(sc);
  636. }
  637. _nffMappedStream.Write( pv, ulOffset.LowPart, &cb );
  638. if( NULL != pcbWritten )
  639. *pcbWritten = cb;
  640. }
  641. else
  642. {
  643. // No, just write to the file.
  644. nffChk( SyncWriteAtFile( ulOffset, pv, cb, pcbWritten ) );
  645. }
  646. nffDebug(( DEB_ITRACE, "WriteAt() wrote %x bytes.\n",
  647. NULL == pcbWritten ? -1 : *pcbWritten ));
  648. EH_Err:
  649. Unlock();
  650. return( sc );
  651. }
  652. //+----------------------------------------------------------------------------
  653. //
  654. // Method: CNtfsStream::Flush
  655. //
  656. //+----------------------------------------------------------------------------
  657. HRESULT
  658. CNtfsStream::Flush()
  659. {
  660. HRESULT sc = S_OK;
  661. Lock( INFINITE );
  662. nffChk( CheckReverted() );
  663. _nffMappedStream.Flush(&sc);
  664. EH_Err:
  665. Unlock();
  666. return(sc);
  667. }
  668. //+-------------------------------------------------------------------
  669. //
  670. // Member: CNtfsStream Constructor
  671. //
  672. //--------------------------------------------------------------------
  673. CNtfsStream::CNtfsStream( CNtfsStorage *pnffstg,
  674. IBlockingLock *pBlockingLock )
  675. #pragma warning(disable: 4355)
  676. : _nffMappedStream( this ),
  677. #pragma warning(default: 4355)
  678. _pnffstg( pnffstg )
  679. {
  680. nffXTrace( "CNtfsStream::CNtfsStream" );
  681. nffDebug(( DEB_REFCOUNT, "new CNtfsStream Constructed with cRefs=1\n" ));
  682. _sig = NTFSSTREAM_SIG;
  683. _cRefs = 1;
  684. _grfMode = 0;
  685. _hFile = INVALID_HANDLE_VALUE;
  686. _liCurrentSeekPosition = 0;
  687. _pnffstmNext = NULL;
  688. _pnffstmPrev = NULL;
  689. _pwcsName = NULL;
  690. nffAssert( NULL != pBlockingLock );
  691. _pBlockingLock = pBlockingLock;
  692. _pBlockingLock->AddRef();
  693. _ovlp.Internal = _ovlp.InternalHigh = 0;
  694. _ovlp.Offset = _ovlp.OffsetHigh = 0;
  695. _ovlp.hEvent = NULL;
  696. }
  697. //+-------------------------------------------------------------------
  698. //
  699. // Member: CNtfsStream Destructor
  700. //
  701. //--------------------------------------------------------------------
  702. CNtfsStream::~CNtfsStream()
  703. {
  704. nffXTrace( "CNtfsStream::~CNtfsStream" );
  705. nffDebug(( DEB_INFO, "CNtfsStream: Close 0x%x.\n", _hFile ));
  706. // Shut down the mapped stream
  707. _nffMappedStream.ShutDown();
  708. // Close the file
  709. if( INVALID_HANDLE_VALUE != _hFile )
  710. NtClose( _hFile );
  711. if( NULL != _ovlp.hEvent )
  712. CloseHandle( _ovlp.hEvent );
  713. if( NULL != _pwcsName )
  714. CoTaskMemFree( _pwcsName );
  715. // Release the object that provides access to the tree lock.
  716. nffAssert( NULL != _pBlockingLock );
  717. _pBlockingLock->Release();
  718. _sig = NTFSSTREAM_SIGDEL;
  719. }
  720. //+-------------------------------------------------------------------
  721. //
  722. // Member: CNtfsStream::Init
  723. //
  724. //--------------------------------------------------------------------
  725. HRESULT
  726. CNtfsStream::Init(
  727. HANDLE hFile, // File handle of this NTFS Stream.
  728. DWORD grfMode, // Open Modes
  729. const OLECHAR * pwcsName, // Name of the Stream
  730. CNtfsStream *pnffstm) // Previously Open NTFS Stream (list)
  731. {
  732. HRESULT sc=S_OK;
  733. HANDLE ev;
  734. nffITrace( "CNtfsStream::Init" );
  735. // We now own this file handle, and are responsible for closing it.
  736. _hFile = hFile;
  737. // Save the STGM_ flags so we can return them in a Stat call.
  738. _grfMode = grfMode;
  739. // Save the stream name
  740. if( NULL != _pwcsName )
  741. {
  742. CoTaskMemFree( _pwcsName );
  743. _pwcsName = NULL;
  744. }
  745. if( NULL != pwcsName )
  746. {
  747. ULONG cbName = sizeof(WCHAR)*((ULONG)wcslen(pwcsName) + 1);
  748. nffMem( _pwcsName = reinterpret_cast<WCHAR*>
  749. ( CoTaskMemAlloc( cbName )));
  750. StringCbCopy( _pwcsName, cbName, pwcsName );
  751. }
  752. // All the streams live on a list held by the root Storage.
  753. if(NULL != pnffstm)
  754. InsertSelfIntoList(pnffstm);
  755. nffChk( _nffMappedStream.Init( _hFile ));
  756. EH_Err:
  757. return sc;
  758. }
  759. //+----------------------------------------------------------------------------
  760. //
  761. // CNtfsStream Non-Interface::ShutDown
  762. //
  763. // Flush data, Close File handle and mark the object as reverted.
  764. // This is called when the Storage is released and when the Oplock Breaks.
  765. //
  766. //+----------------------------------------------------------------------------
  767. HRESULT
  768. CNtfsStream::ShutDown()
  769. {
  770. nffITrace( "CNtfsStream::ShutDown" );
  771. HRESULT sc=S_OK;
  772. if( INVALID_HANDLE_VALUE == _hFile )
  773. return S_OK;
  774. nffDebug(( DEB_INFO, "CNtfsStream::ShutDown(%x)\n", _hFile ));
  775. //
  776. // Shut down the mapped stream
  777. //
  778. _nffMappedStream.ShutDown();
  779. //
  780. // Close the file/stream handle and mark the IStream object as
  781. // Reverted by giving the file handle an invalid value.
  782. //
  783. CloseHandle(_hFile);
  784. _hFile = INVALID_HANDLE_VALUE;
  785. // We don't need the parent CNtfsStorage any longer, and more importantly it could
  786. // be going away.
  787. _pnffstg = NULL;
  788. //
  789. // Remove IStream object from the Storage's list
  790. //
  791. RemoveSelfFromList();
  792. return S_OK;
  793. }
  794. //+----------------------------------------------------------------------------
  795. //
  796. // CNtfsStream Non-Interface::Rename
  797. //
  798. //+----------------------------------------------------------------------------
  799. HRESULT
  800. CNtfsStream::Rename(
  801. const WCHAR *pwcsName,
  802. BOOL fOverWrite )
  803. {
  804. IO_STATUS_BLOCK io_status_block;
  805. PFILE_RENAME_INFORMATION pFileRenInfo=NULL; // _alloca()'ed
  806. NTSTATUS status;
  807. CNtfsStreamName nsnName = pwcsName; // Convert to ":name:$DATA"
  808. LONG cbBufSize=0;
  809. HRESULT sc=S_OK;
  810. nffDebug(( DEB_INFO | DEB_WRITE, "CNtfsStream::Rename(%ws -> %ws)\n",
  811. _pwcsName, pwcsName ));
  812. Lock( INFINITE );
  813. nffChk( CheckReverted() );
  814. // Size and allocate a FILE_RENAME_INFOMATION buffer. The size argument
  815. // to NtSetInformationFile must be correct, so subtract 1 WCHAR for the
  816. // FileName[1] in the struct we are not using.
  817. //
  818. cbBufSize = sizeof(FILE_RENAME_INFORMATION) - sizeof(WCHAR);
  819. cbBufSize += (ULONG)nsnName.Count() * sizeof(WCHAR);
  820. __try
  821. {
  822. pFileRenInfo = (PFILE_RENAME_INFORMATION) _alloca( cbBufSize );
  823. }
  824. __except(EXCEPTION_EXECUTE_HANDLER)
  825. {
  826. nffErr(EH_Err, STG_E_INSUFFICIENTMEMORY);
  827. }
  828. // Load the FILE_RENAME_INFORMATION structure
  829. pFileRenInfo->ReplaceIfExists = (BOOLEAN) fOverWrite;
  830. pFileRenInfo->RootDirectory = NULL;
  831. pFileRenInfo->FileNameLength = (ULONG)nsnName.Count() * sizeof(WCHAR);
  832. memcpy( pFileRenInfo->FileName,
  833. (const WCHAR*)nsnName,
  834. pFileRenInfo->FileNameLength );
  835. // Rename the stream
  836. status = NtSetInformationFile( _hFile,
  837. &io_status_block,
  838. pFileRenInfo,
  839. cbBufSize,
  840. FileRenameInformation );
  841. if( !NT_SUCCESS(status) )
  842. {
  843. nffChk( sc = NtStatusToScode(status) );
  844. }
  845. // Discard the old name and Save the new name
  846. CoTaskMemFree( _pwcsName );
  847. // reuse cbBufSize
  848. cbBufSize = sizeof(WCHAR) * ((ULONG)wcslen(pwcsName)+1);
  849. nffMem( _pwcsName = (WCHAR*) CoTaskMemAlloc( cbBufSize ));
  850. StringCbCopy( _pwcsName, cbBufSize, pwcsName );
  851. sc = S_OK;
  852. EH_Err:
  853. Unlock();
  854. return( sc );
  855. }
  856. //+----------------------------------------------------------------------------
  857. //
  858. // CNtfsStream Non-Interface::Delete
  859. //
  860. //+----------------------------------------------------------------------------
  861. HRESULT
  862. CNtfsStream::Delete()
  863. {
  864. HRESULT sc=S_OK;
  865. nffDebug(( DEB_INFO | DEB_WRITE, "Delete(stm=%ws)\n", _pwcsName ));
  866. nffChk( CheckReverted() );
  867. if( IsWriteable() )
  868. nffChk( DeleteStream( &_hFile ));
  869. EH_Err:
  870. return( sc );
  871. }
  872. //+----------------------------------------------------------------------------
  873. //
  874. // CNtfsStream Non-Interface::DeleteStream
  875. //
  876. // This method is static so that it can be called globally.
  877. //
  878. //+----------------------------------------------------------------------------
  879. HRESULT // static
  880. CNtfsStream::DeleteStream( HANDLE *phStream )
  881. {
  882. HRESULT sc=S_OK;
  883. NTSTATUS status = STATUS_SUCCESS;
  884. FILE_DISPOSITION_INFORMATION Disposition;
  885. IO_STATUS_BLOCK IoStatusBlock;
  886. // Execute the following statement:
  887. // Disposition.DeleteFile = TRUE;
  888. // We can't actually write the code that way, because "DeleteFile" is #defined to
  889. // "DeleteFileW".
  890. nffDebug(( DEB_INFO | DEB_WRITE, "DeleteStream(hdl=%x)\n", *phStream ));
  891. *reinterpret_cast<BOOLEAN*>(&Disposition) = TRUE;
  892. DfpAssert( sizeof(Disposition) == sizeof(BOOLEAN) );
  893. // Mark the file for delete on close
  894. // Note that if this is the Contents stream we can delete it successfully,
  895. // but NTFS actually just truncates it to zero length.
  896. status = NtSetInformationFile(
  897. *phStream,
  898. &IoStatusBlock,
  899. &Disposition,
  900. sizeof(Disposition),
  901. FileDispositionInformation
  902. );
  903. if( !NT_SUCCESS(status) )
  904. {
  905. nffErr( EH_Err, NtStatusToScode(status) );
  906. }
  907. NtClose( *phStream );
  908. *phStream = INVALID_HANDLE_VALUE;
  909. EH_Err:
  910. return( sc );
  911. }
  912. //+----------------------------------------------------------------------------
  913. //
  914. // CNtfsStream::SetFileSize (private, non-interface method)
  915. //
  916. // Set the size of the _hFile. This is used by the IStream & IMappedStream
  917. // SetSize methods
  918. //
  919. //+----------------------------------------------------------------------------
  920. HRESULT // private
  921. CNtfsStream::SetFileSize( const CULargeInteger &uliNewSize )
  922. {
  923. nffITrace( "CNtfsStream::SetFileSize" );
  924. HRESULT sc = S_OK;
  925. CLargeInteger liEOF;
  926. // We have to convert uliNewSize into a LARGE_INTEGER, so ensure that it can
  927. // be cast without loss of data.
  928. liEOF = static_cast<CLargeInteger>(uliNewSize);
  929. if( liEOF < 0 )
  930. nffErr( EH_Err, STG_E_INVALIDPARAMETER );
  931. // Move to what will be the new end-of-file position.
  932. liEOF.LowPart = SetFilePointer( _hFile, liEOF.LowPart,
  933. &liEOF.HighPart, FILE_BEGIN );
  934. if( 0xFFFFFFFF == liEOF.LowPart && NO_ERROR != GetLastError() )
  935. nffErr( EH_Err, LAST_SCODE );
  936. // Set this as the new eof
  937. if( !SetEndOfFile( _hFile ))
  938. nffErr( EH_Err, LAST_SCODE );
  939. EH_Err:
  940. return( sc );
  941. }
  942. //+-------------------------------------------------------------------
  943. //
  944. // Member: CNtfsStream::InsertSelfIntoList/RemoveSelfFromList
  945. //
  946. //--------------------------------------------------------------------
  947. // We are passed a "current" element of a doubly linked list.
  948. // Insert "this" right after the given List element.
  949. //
  950. VOID
  951. CNtfsStream::InsertSelfIntoList(CNtfsStream *pnffstmCurrent)
  952. {
  953. nffDebug(( DEB_ITRACE, "CNtfsStream::InsertSelfIntoList this=%x\n", this ));
  954. // If we're already in the list, or there is no list, then we're done.
  955. if( NULL != _pnffstmNext || NULL == pnffstmCurrent )
  956. return;
  957. // Note that this lock takes the tree mutex - it locks the whole
  958. // storage
  959. Lock( INFINITE );
  960. // "this" point back to the current element
  961. // and points forward to current's next element
  962. _pnffstmPrev = pnffstmCurrent;
  963. _pnffstmNext = pnffstmCurrent->_pnffstmNext;
  964. // the current element now points forward to "this"
  965. // and if the next is not NULL it points back at this.
  966. pnffstmCurrent->_pnffstmNext = this;
  967. if(NULL != _pnffstmNext)
  968. _pnffstmNext->_pnffstmPrev = this;
  969. Unlock();
  970. }
  971. VOID
  972. CNtfsStream::RemoveSelfFromList()
  973. {
  974. nffDebug(( DEB_ITRACE, "CNtfsStream::RemoveSelfFromList this=%x\n", this ));
  975. // Note that this lock takes the tree mutex - it locks the whole
  976. // storage
  977. Lock( INFINITE );
  978. // My next element's previous pointer is given my previous pointer.
  979. if(NULL != _pnffstmNext)
  980. _pnffstmNext->_pnffstmPrev = _pnffstmPrev;
  981. // My previous element's next pointer is given my next pointer.
  982. if(NULL != _pnffstmPrev)
  983. _pnffstmPrev->_pnffstmNext = _pnffstmNext;
  984. _pnffstmNext = NULL;
  985. _pnffstmPrev = NULL;
  986. Unlock();
  987. }
  988. //+-------------------------------------------------------------------
  989. //
  990. // CNtfsStream Non-Interface::SyncReadAtFile
  991. //
  992. // Synopsis: Provide synchronous IO for a file handle open in
  993. // asynchronous mode.
  994. //
  995. //--------------------------------------------------------------------
  996. HRESULT
  997. CNtfsStream::SyncReadAtFile(
  998. ULARGE_INTEGER ulOffset,
  999. PVOID pv,
  1000. ULONG cb,
  1001. PULONG pcbRead)
  1002. {
  1003. HRESULT sc=S_OK;
  1004. LONG err=ERROR_SUCCESS;
  1005. //
  1006. // We use the single OVERLAPPED structure in the object.
  1007. // This saves us from creating an Event everytime. We
  1008. // require the TreeMutex will keep us single threaded.
  1009. //
  1010. _ovlp.Offset = ulOffset.LowPart;
  1011. _ovlp.OffsetHigh = ulOffset.HighPart;
  1012. nffDebug(( DEB_ITRACE | DEB_INFO | DEB_READ,
  1013. "SyncReadAtFile(_hFile=0x%x off=%x:%x, pv=0x%x, cb=0x%x )"
  1014. " stream='%ws'\n",
  1015. _hFile, ulOffset.HighPart, ulOffset.LowPart,
  1016. pv, cb, _pwcsName ));
  1017. if( !ReadFile( _hFile, pv, cb, pcbRead, &_ovlp ) )
  1018. {
  1019. err = GetLastError();
  1020. if(ERROR_SUCCESS != err && ERROR_HANDLE_EOF != err)
  1021. nffChk( HRESULT_FROM_WIN32( err ) );
  1022. }
  1023. nffDebug(( DEB_INFO, "SyncReadAtFile() read 0x%x bytes.\n", *pcbRead ));
  1024. EH_Err:
  1025. return sc;
  1026. }
  1027. //+-------------------------------------------------------------------
  1028. //
  1029. // CNtfsStream Non-Interface::SyncWriteAtFile
  1030. //
  1031. // Synopsis: Provide synchronous IO for a file handle open in
  1032. // asynchronous mode.
  1033. //
  1034. //--------------------------------------------------------------------
  1035. HRESULT
  1036. CNtfsStream::SyncWriteAtFile(
  1037. ULARGE_INTEGER ulOffset,
  1038. const void *pv,
  1039. ULONG cb,
  1040. PULONG pcbWritten)
  1041. {
  1042. HRESULT sc=S_OK;
  1043. //
  1044. // We use the single OVERLAPPED structure in the object.
  1045. // We require the TreeMutex will keep us single threaded.
  1046. //
  1047. _ovlp.Offset = ulOffset.LowPart;
  1048. _ovlp.OffsetHigh = ulOffset.HighPart;
  1049. nffDebug(( DEB_ITRACE | DEB_INFO | DEB_WRITE,
  1050. "SyncWriteAtFile(_hFile=0x%x, off=%x:%x, pv=0x%x, cb=0x%x );"
  1051. " stream='%ws'\n",
  1052. _hFile, ulOffset.HighPart, ulOffset.LowPart,
  1053. pv, cb, _pwcsName ));
  1054. //
  1055. // We expect either OK or FALSE/ERROR_IO_PENDING.
  1056. //
  1057. if( !WriteFile( _hFile, pv, cb, pcbWritten, &_ovlp ) )
  1058. {
  1059. nffChk( HRESULT_FROM_WIN32( GetLastError() ) );
  1060. }
  1061. nffDebug(( DEB_ITRACE | DEB_INFO,
  1062. "SyncWriteAtFile() wrote 0x%x bytes.\n", *pcbWritten ));
  1063. EH_Err:
  1064. return sc;
  1065. }
  1066. //+----------------------------------------------------------------------------
  1067. //
  1068. // CNtfsStream Non-Interface::SetStreamTime
  1069. //
  1070. //+----------------------------------------------------------------------------
  1071. HRESULT
  1072. CNtfsStream::SetStreamTime( const FILETIME* pctime,
  1073. const FILETIME* patime,
  1074. const FILETIME* pmtime)
  1075. {
  1076. nffDebug((DEB_INFO | DEB_STATCTRL | DEB_ITRACE,
  1077. "SetStreamTime() hdl=%x, stream='%ws'\n",
  1078. _hFile, _pwcsName ));
  1079. return SetFileHandleTime( _hFile, pctime, patime, pmtime );
  1080. }
  1081. //+----------------------------------------------------------------------------
  1082. //
  1083. // CNtfsStream Static Non-Interface::SetFileHandleTime
  1084. //
  1085. //+----------------------------------------------------------------------------
  1086. HRESULT
  1087. CNtfsStream::SetFileHandleTime( HANDLE hFile,
  1088. const FILETIME* pctime,
  1089. const FILETIME* patime,
  1090. const FILETIME* pmtime)
  1091. {
  1092. HRESULT sc=S_OK;
  1093. if( INVALID_HANDLE_VALUE == hFile )
  1094. {
  1095. nffDebug(( DEB_IWARN, "CNtfsStream::SetFileHandleTime on hFile == -1\n" ));
  1096. return S_OK;
  1097. }
  1098. nffDebug(( DEB_STATCTRL | DEB_ITRACE,
  1099. "SetFileHandleTime(%p %p %p) hdl=%x\n",
  1100. pctime, patime, pmtime, hFile ));
  1101. nffBool( ::SetFileTime( hFile, pctime, patime, pmtime ) );
  1102. EH_Err:
  1103. return sc;
  1104. }
  1105. #if DBG
  1106. HRESULT STDMETHODCALLTYPE
  1107. CNtfsStream::UseNTFS4Streams( BOOL fUseNTFS4Streams )
  1108. {
  1109. return( _nffMappedStream.UseNTFS4Streams( fUseNTFS4Streams ));
  1110. }
  1111. #endif // #if DBG
  1112. #if DBG
  1113. HRESULT STDMETHODCALLTYPE
  1114. CNtfsStream::GetFormatVersion(WORD *pw)
  1115. {
  1116. return( E_NOTIMPL );
  1117. }
  1118. #endif // #if DBG
  1119. #if DBG
  1120. HRESULT STDMETHODCALLTYPE
  1121. CNtfsStream::SimulateLowMemory( BOOL fSimulate )
  1122. {
  1123. return( _nffMappedStream.SimulateLowMemory( fSimulate ));
  1124. }
  1125. #endif // #if DBG
  1126. #if DBG
  1127. HRESULT STDMETHODCALLTYPE
  1128. CNtfsStream::GetLockCount()
  1129. {
  1130. return( NULL == _pnffstg ? 0 : _pnffstg->GetLockCount() );
  1131. }
  1132. #endif // #if DBG
  1133. #if DBG
  1134. HRESULT STDMETHODCALLTYPE
  1135. CNtfsStream::IsDirty()
  1136. {
  1137. return( E_NOTIMPL );
  1138. }
  1139. #endif // #if DBG