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.

1362 lines
31 KiB

  1. /*++
  2. 1998 Seagate Software, Inc. All rights reserved
  3. Module Name:
  4. RmsDrive.cpp
  5. Abstract:
  6. Implementation of CRmsDrive
  7. Author:
  8. Brian Dodd [brian] 15-Nov-1996
  9. Revision History:
  10. --*/
  11. #include "stdafx.h"
  12. #include "RmsDrive.h"
  13. #include "RmsServr.h"
  14. int CRmsDrive::s_InstanceCount = 0;
  15. #define RMS_CRITICAL_SECTION 1
  16. ////////////////////////////////////////////////////////////////////////////////
  17. //
  18. STDMETHODIMP
  19. CRmsDrive::CompareTo(
  20. IN IUnknown *pCollectable,
  21. OUT SHORT *pResult
  22. )
  23. /*++
  24. Implements:
  25. IWsbCollectable::CompareTo
  26. --*/
  27. {
  28. HRESULT hr = E_FAIL;
  29. SHORT result = 1;
  30. WsbTraceIn( OLESTR("CRmsDrive::CompareTo"), OLESTR("") );
  31. try {
  32. // Validate arguments - Okay if pResult is NULL
  33. WsbAssertPointer( pCollectable );
  34. // !!!!!
  35. //
  36. // IMPORTANT: The collectable coming in may not be a CRmsDrive if the collection
  37. // is the unconfigured device list.
  38. //
  39. // !!!!!
  40. CComQIPtr<IRmsComObject, &IID_IRmsComObject> pObject = pCollectable;
  41. // Every collectable should be an CRmsComObject
  42. WsbAssertPointer( pObject );
  43. switch ( m_findBy ) {
  44. case RmsFindByDeviceInfo:
  45. case RmsFindByDeviceAddress:
  46. case RmsFindByDeviceName:
  47. case RmsFindByDeviceType:
  48. // Do CompareTo for device
  49. hr = CRmsDevice::CompareTo( pCollectable, &result );
  50. break;
  51. case RmsFindByElementNumber:
  52. case RmsFindByMediaSupported:
  53. // Do CompareTo for changer element
  54. hr = CRmsChangerElement::CompareTo( pCollectable, &result );
  55. break;
  56. case RmsFindByObjectId:
  57. default:
  58. // Do CompareTo for object
  59. hr = CRmsComObject::CompareTo( pCollectable, &result );
  60. break;
  61. }
  62. }
  63. WsbCatch( hr );
  64. if ( SUCCEEDED(hr) && (0 != pResult) ){
  65. *pResult = result;
  66. }
  67. WsbTraceOut( OLESTR("CRmsDrive::CompareTo"),
  68. OLESTR("hr = <%ls>, result = <%ls>"),
  69. WsbHrAsString( hr ), WsbPtrToShortAsString( pResult ) );
  70. return hr;
  71. }
  72. STDMETHODIMP
  73. CRmsDrive::FinalConstruct(
  74. void
  75. )
  76. /*++
  77. Implements:
  78. CComObjectRoot::FinalConstruct
  79. --*/
  80. {
  81. HRESULT hr = S_OK;
  82. WsbTraceIn(OLESTR("CRmsDrive::FinalConstruct"), OLESTR(""));
  83. try {
  84. WsbAssertHr(CWsbObject::FinalConstruct());
  85. // Initialize values
  86. m_MountReference = 0;
  87. m_UnloadNowTime.dwHighDateTime = 0;
  88. m_UnloadNowTime.dwLowDateTime = 0;
  89. m_UnloadThreadHandle = NULL;
  90. try {
  91. // May raise STATUS_NO_MEMORY exception
  92. InitializeCriticalSection(&m_CriticalSection);
  93. } catch(DWORD status) {
  94. WsbLogEvent(status, 0, NULL, NULL);
  95. switch (status) {
  96. case STATUS_NO_MEMORY:
  97. WsbThrow(E_OUTOFMEMORY);
  98. break;
  99. default:
  100. WsbThrow(E_UNEXPECTED);
  101. break;
  102. }
  103. }
  104. m_UnloadNowEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  105. m_UnloadedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  106. } WsbCatch(hr);
  107. s_InstanceCount++;
  108. WsbTraceAlways(OLESTR("CRmsDrive::s_InstanceCount += %d\n"), s_InstanceCount);
  109. WsbTraceOut(OLESTR("CRmsDrive::FinalConstruct"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  110. return(hr);
  111. }
  112. STDMETHODIMP
  113. CRmsDrive::FinalRelease(void)
  114. {
  115. HRESULT hr = S_OK;
  116. WsbTraceIn(OLESTR("CRmsDrive::FinalRelease"), OLESTR(""));
  117. try {
  118. try {
  119. // InitializeCriticalSection raises an exception. Delete may too.
  120. DeleteCriticalSection(&m_CriticalSection);
  121. } catch(DWORD status) {
  122. WsbLogEvent(status, 0, NULL, NULL);
  123. }
  124. CloseHandle(m_UnloadNowEvent);
  125. CloseHandle(m_UnloadedEvent);
  126. CWsbObject::FinalRelease();
  127. } WsbCatch(hr);
  128. s_InstanceCount--;
  129. WsbTraceAlways(OLESTR("CRmsDrive::s_InstanceCount -= %d\n"), s_InstanceCount);
  130. WsbTraceOut(OLESTR("CRmsDrive::FinalRelease"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  131. return(hr);
  132. }
  133. STDMETHODIMP
  134. CRmsDrive::GetClassID(
  135. OUT CLSID* pClsid
  136. )
  137. /*++
  138. Implements:
  139. IPersist::GetClassId
  140. --*/
  141. {
  142. HRESULT hr = S_OK;
  143. WsbTraceIn(OLESTR("CRmsDrive::GetClassID"), OLESTR(""));
  144. try {
  145. WsbAssert(0 != pClsid, E_POINTER);
  146. *pClsid = CLSID_CRmsDrive;
  147. } WsbCatch(hr);
  148. WsbTraceOut(OLESTR("CRmsDrive::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));
  149. return(hr);
  150. }
  151. STDMETHODIMP
  152. CRmsDrive::GetSizeMax(
  153. OUT ULARGE_INTEGER* pcbSize
  154. )
  155. /*++
  156. Implements:
  157. IPersistStream::GetSizeMax
  158. --*/
  159. {
  160. HRESULT hr = E_NOTIMPL;
  161. WsbTraceIn(OLESTR("CRmsDrive::GetSizeMax"), OLESTR(""));
  162. // try {
  163. // WsbAssert(0 != pcbSize, E_POINTER);
  164. // // Set up max size
  165. // pcbSize->QuadPart = WsbPersistSizeOf(LONG); // m_MountReference
  166. // } WsbCatch(hr);
  167. WsbTraceOut(OLESTR("CRmsDrive::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pcbSize));
  168. return(hr);
  169. }
  170. STDMETHODIMP
  171. CRmsDrive::Load(
  172. IN IStream* pStream
  173. )
  174. /*++
  175. Implements:
  176. IPersistStream::Load
  177. --*/
  178. {
  179. HRESULT hr = S_OK;
  180. ULONG ulBytes = 0;
  181. WsbTraceIn(OLESTR("CRmsDrive::Load"), OLESTR(""));
  182. try {
  183. WsbAssert(0 != pStream, E_POINTER);
  184. WsbAffirmHr(CRmsDevice::Load(pStream));
  185. // Read value
  186. WsbAffirmHr(WsbLoadFromStream(pStream, &m_MountReference));
  187. // We just reset to zero, one day we could try to reconnect to
  188. // the process that issued the mount...
  189. m_MountReference = 0;
  190. }
  191. WsbCatch(hr);
  192. WsbTraceOut(OLESTR("CRmsDrive::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  193. return(hr);
  194. }
  195. STDMETHODIMP
  196. CRmsDrive::Save(
  197. IN IStream* pStream,
  198. IN BOOL clearDirty
  199. )
  200. /*++
  201. Implements:
  202. IPersistStream::Save
  203. --*/
  204. {
  205. HRESULT hr = S_OK;
  206. ULONG ulBytes = 0;
  207. WsbTraceIn(OLESTR("CRmsDrive::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty));
  208. try {
  209. WsbAssert(0 != pStream, E_POINTER);
  210. WsbAffirmHr(CRmsDevice::Save(pStream, clearDirty));
  211. // Write value
  212. WsbAffirmHr(WsbSaveToStream(pStream, m_MountReference));
  213. // Do we need to clear the dirty bit?
  214. if (clearDirty) {
  215. m_isDirty = FALSE;
  216. }
  217. } WsbCatch(hr);
  218. WsbTraceOut(OLESTR("CRmsDrive::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  219. return(hr);
  220. }
  221. STDMETHODIMP
  222. CRmsDrive::Test(
  223. OUT USHORT *pPassed,
  224. OUT USHORT *pFailed
  225. )
  226. /*++
  227. Implements:
  228. IWsbTestable::Test
  229. --*/
  230. {
  231. HRESULT hr = S_OK;
  232. CComPtr<IRmsDrive> pDrive1;
  233. CComPtr<IRmsDrive> pDrive2;
  234. CComPtr<IPersistFile> pFile1;
  235. CComPtr<IPersistFile> pFile2;
  236. LONG i;
  237. LONG longWork1;
  238. WsbTraceIn(OLESTR("CRmsDrive::Test"), OLESTR(""));
  239. try {
  240. // Get the Drive interface.
  241. hr = S_OK;
  242. try {
  243. WsbAssertHr(((IUnknown*) (IRmsDrive*) this)->QueryInterface(IID_IRmsDrive, (void**) &pDrive1));
  244. // Test All of MountReference Functions
  245. ResetMountReference();
  246. GetMountReference(&longWork1);
  247. if(longWork1 == 0) {
  248. (*pPassed)++;
  249. } else {
  250. (*pFailed)++;
  251. }
  252. for(i = 1; i < 20; i++){
  253. AddMountReference();
  254. GetMountReference(&longWork1);
  255. if(longWork1 == i){
  256. (*pPassed)++;
  257. } else {
  258. (*pFailed)++;
  259. }
  260. }
  261. for(i = 19; i == 0; i--){
  262. ReleaseMountReference();
  263. GetMountReference(&longWork1);
  264. if(longWork1 == i){
  265. (*pPassed)++;
  266. } else {
  267. (*pFailed)++;
  268. }
  269. }
  270. } WsbCatch(hr);
  271. // Tally up the results
  272. hr = S_OK;
  273. if (*pFailed) {
  274. hr = S_FALSE;
  275. }
  276. } WsbCatch(hr);
  277. WsbTraceOut(OLESTR("CRmsDrive::Test"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  278. return(hr);
  279. }
  280. STDMETHODIMP
  281. CRmsDrive::GetMountReference(
  282. OUT LONG *pRefs
  283. )
  284. /*++
  285. Implements:
  286. IRmsDrive::GetMountReference
  287. --*/
  288. {
  289. HRESULT hr = S_OK;
  290. WsbTraceIn(OLESTR("CRmsDrive::GetMountReference"), OLESTR(""));
  291. LONG refs = -999;
  292. try {
  293. WsbAssertPointer(pRefs);
  294. refs = m_MountReference;
  295. *pRefs = refs;
  296. } WsbCatch(hr)
  297. WsbTraceOut(OLESTR("CRmsDrive::GetMountReference"), OLESTR("hr = <%ls>, refs = %d"),
  298. WsbHrAsString(hr), refs);
  299. return hr;
  300. }
  301. STDMETHODIMP
  302. CRmsDrive::ResetMountReference(
  303. void
  304. )
  305. /*++
  306. Implements:
  307. IRmsDrive::ResetMountReference
  308. --*/
  309. {
  310. HRESULT hr = S_OK;
  311. WsbTraceIn(OLESTR("CRmsDrive::ResetMountReference"), OLESTR(""));
  312. #if RMS_CRITICAL_SECTION
  313. try {
  314. // <<<<< ENTER SINGLE THREADED SECTION
  315. WsbAffirmHr(Lock());
  316. m_MountReference = 0;
  317. m_isDirty = TRUE;
  318. WsbAffirmHr(Unlock());
  319. // >>>>> LEAVE SINGLE THREADED SECTION
  320. } WsbCatch(hr)
  321. #else
  322. InterlockedExchange( &m_MountReference, 0);
  323. m_isDirty = TRUE;
  324. #endif
  325. WsbTraceOut(OLESTR("CRmsDrive::ResetMountReference"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  326. return hr;
  327. }
  328. STDMETHODIMP
  329. CRmsDrive::AddMountReference(
  330. void
  331. )
  332. /*++
  333. Implements:
  334. IRmsDrive::AddMountReference
  335. --*/
  336. {
  337. HRESULT hr = S_OK;
  338. WsbTraceIn(OLESTR("CRmsDrive::AddMountReference"), OLESTR(""));
  339. LONG refs = -999;
  340. #if RMS_CRITICAL_SECTION
  341. try {
  342. // <<<<< ENTER SINGLE THREADED SECTION
  343. WsbAffirmHr(Lock());
  344. m_MountReference++;
  345. m_isDirty = TRUE;
  346. refs = m_MountReference;
  347. WsbAffirmStatus(ResetEvent(m_UnloadedEvent));
  348. WsbAffirmHr(Unlock());
  349. // >>>>> LEAVE SINGLE THREADED SECTION
  350. } WsbCatch(hr)
  351. #else
  352. refs = InterlockedIncrement( &m_MountReference );
  353. m_isDirty = TRUE;
  354. #endif
  355. WsbTraceOut(OLESTR("CRmsDrive::AddMountReference"), OLESTR("hr = <%ls>, refs = %d"),
  356. WsbHrAsString(hr), refs);
  357. return hr;
  358. }
  359. STDMETHODIMP
  360. CRmsDrive::ReleaseMountReference(
  361. IN DWORD dwOptions
  362. )
  363. /*++
  364. Implements:
  365. IRmsDrive::ReleaseMountReference
  366. --*/
  367. {
  368. HRESULT hr S_OK;
  369. WsbTraceIn(OLESTR("CRmsDrive::ReleaseMountReference"), OLESTR("<%ld>"), dwOptions);
  370. // We need to be sure this object doesn't go away until we're done.
  371. // This happens when we dismount a NTMS managed cartridge.
  372. CComPtr<IRmsDrive> thisDrive = this;
  373. LONG refs = -999;
  374. BOOL bUnloadNow =
  375. ( (dwOptions & RMS_DISMOUNT_IMMEDIATE) || (dwOptions & RMS_DISMOUNT_DEFERRED_ONLY) ) ? TRUE : FALSE;
  376. try {
  377. #if RMS_CRITICAL_SECTION
  378. // <<<<< ENTER SINGLE THREADED SECTION
  379. WsbAffirmHr(Lock());
  380. m_MountReference--;
  381. m_isDirty = TRUE;
  382. refs = m_MountReference;
  383. #else
  384. refs = InterlockedDecrement( &m_MountReference );
  385. m_isDirty = TRUE;
  386. #endif
  387. // Note:
  388. // Even if the caller requests immediate dismount, if the ref count is > 0,
  389. // the media is not dismounted (only the ref count is decreased).
  390. // This is necessary because positive ref count means that some other component
  391. // is also using the media (possible for Optical). The media must be dismounted
  392. // only when this other component is done as well.
  393. if (refs < 0) {
  394. //
  395. // This shouldn't happen under normal conditions... if it does,
  396. // we quiety reset the the reference count and try to recover.
  397. //
  398. WsbLogEvent(E_UNEXPECTED, sizeof(refs), &refs, NULL);
  399. InterlockedExchange( &m_MountReference, 0);
  400. refs = 0;
  401. // If we don't have a cartridge in the drive, there's no point
  402. // in continueing.
  403. WsbAffirm(S_OK == IsOccupied(), E_ABORT);
  404. }
  405. if (0 == refs) {
  406. //
  407. // Deferred Dismount Logic: We wait the specified time before
  408. // dismounting the media. Each dismount request resets the dismount
  409. // now time. As long as the media is actively used, it will not be
  410. // dismounted.
  411. //
  412. // Retrieve the DeferredDismountWaitTime parameter
  413. DWORD size;
  414. OLECHAR tmpString[256];
  415. DWORD waitTime = RMS_DEFAULT_DISMOUNT_WAIT_TIME;
  416. if (SUCCEEDED(WsbGetRegistryValueString(NULL, RMS_REGISTRY_STRING, RMS_PARAMETER_DISMOUNT_WAIT_TIME, tmpString, 256, &size))) {
  417. waitTime = wcstol(tmpString, NULL, 10);
  418. WsbTrace(OLESTR("DismountWaitTime is %d milliseconds.\n"), waitTime);
  419. }
  420. if (waitTime > 0 && !bUnloadNow) {
  421. // convert waitTime to 100-nanosecond units
  422. waitTime *= 10000;
  423. FILETIME now;
  424. GetSystemTimeAsFileTime(&now);
  425. ULARGE_INTEGER time;
  426. time.LowPart = now.dwLowDateTime;
  427. time.HighPart = now.dwHighDateTime;
  428. time.QuadPart += waitTime;
  429. m_UnloadNowTime.dwLowDateTime = time.LowPart;
  430. m_UnloadNowTime.dwHighDateTime = time.HighPart;
  431. WsbTrace(OLESTR("Target Unload Time = <%ls>\n"),
  432. WsbQuickString(WsbFiletimeAsString(FALSE, m_UnloadNowTime)));
  433. // If we already have an active unload thread we skip this step.
  434. if (!m_UnloadThreadHandle) {
  435. //
  436. // Create a thread to wait for dismount
  437. //
  438. WsbTrace(OLESTR("Starting thread for deferred dismount.\n"));
  439. DWORD threadId;
  440. WsbAffirmHandle(m_UnloadThreadHandle = CreateThread(NULL, 1024, CRmsDrive::StartUnloadThread, this, 0, &threadId));
  441. CloseHandle(m_UnloadThreadHandle);
  442. }
  443. }
  444. else {
  445. // Dismount the media now
  446. // Double check that we still have something to dismount
  447. if (S_OK == IsOccupied()) {
  448. // Best effort - home
  449. // Fixed drives are always occupied and we shouldn't call Home for their cartridge
  450. FlushBuffers();
  451. if (RmsDeviceFixedDisk != m_deviceType) {
  452. if (S_OK == m_pCartridge->Home(dwOptions)) {
  453. SetIsOccupied(FALSE);
  454. }
  455. }
  456. }
  457. // set event that blocks immediate unload
  458. SetEvent(m_UnloadedEvent);
  459. }
  460. }
  461. #if RMS_CRITICAL_SECTION
  462. WsbAffirmHr(Unlock());
  463. // >>>>> LEAVE SINGLE THREADED SECTION
  464. } WsbCatchAndDo(hr,
  465. WsbAffirmHr(Unlock());
  466. // >>>>> LEAVE SINGLE THREADED SECTION
  467. );
  468. #else
  469. } WsbCatch(hr)
  470. #endif
  471. WsbTraceOut(OLESTR("CRmsDrive::ReleaseMountReference"), OLESTR("hr = <%ls>, refs = %d"),
  472. WsbHrAsString(hr), refs);
  473. return hr;
  474. }
  475. STDMETHODIMP
  476. CRmsDrive::SelectForMount(
  477. void
  478. )
  479. /*++
  480. Implements:
  481. IRmsDrive::SelectForMount
  482. --*/
  483. {
  484. HRESULT hr = S_OK;
  485. WsbTraceIn(OLESTR("CRmsDrive::SelectForMount"), OLESTR(""));
  486. #if RMS_CRITICAL_SECTION
  487. try {
  488. // <<<<< ENTER SINGLE THREADED SECTION
  489. WsbAffirmHr(Lock());
  490. if (!m_MountReference) {
  491. m_MountReference++;
  492. m_isDirty = TRUE;
  493. } else {
  494. hr = RMS_E_DRIVE_BUSY;
  495. }
  496. WsbAffirmHr(Unlock());
  497. // >>>>> LEAVE SINGLE THREADED SECTION
  498. } WsbCatch(hr)
  499. #else
  500. LONG one = 1;
  501. LONG zero = 0;
  502. LONG flag = InterlockedCompareExchange( &m_MountReference, one, zero );
  503. hr = ( flag > 0 ) ? RMS_E_DRIVE_BUSY : S_OK;
  504. #endif
  505. WsbTraceOut(OLESTR("CRmsDrive::SelectForMount"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  506. return hr;
  507. }
  508. STDMETHODIMP
  509. CRmsDrive::CreateDataMover(
  510. IDataMover **ptr)
  511. /*++
  512. Implements:
  513. IRmsDrive::CreateDataMover
  514. --*/
  515. {
  516. HRESULT hr = S_OK;
  517. WsbTraceIn(OLESTR("CRmsDrive::CreateDataMover"), OLESTR(""));
  518. try {
  519. WsbAssertPointer(ptr);
  520. if (m_isOccupied) {
  521. switch (m_mediaSupported) {
  522. case RmsMedia8mm:
  523. case RmsMedia4mm:
  524. case RmsMediaDLT:
  525. case RmsMediaTape:
  526. {
  527. //
  528. // Create a tape style data mover to the drive
  529. //
  530. WsbAssertHr(CoCreateInstance(CLSID_CNtTapeIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)ptr));
  531. }
  532. break;
  533. case RmsMediaWORM:
  534. break;
  535. case RmsMediaOptical:
  536. case RmsMediaMO35:
  537. case RmsMediaCDR:
  538. case RmsMediaDVD:
  539. case RmsMediaDisk:
  540. case RmsMediaFixed:
  541. {
  542. //
  543. // Create a file style data mover to the drive
  544. //
  545. WsbAssertHr(CoCreateInstance(CLSID_CNtFileIo, 0, CLSCTX_SERVER, IID_IDataMover, (void **)ptr));
  546. }
  547. break;
  548. default:
  549. WsbThrow(E_UNEXPECTED);
  550. break;
  551. }
  552. }
  553. else {
  554. WsbThrow(RMS_E_RESOURCE_UNAVAILABLE);
  555. }
  556. // Initialize the data mover
  557. WsbAffirmHr((*ptr)->SetDeviceName(m_deviceName));
  558. WsbAffirmHr((*ptr)->SetCartridge(m_pCartridge));
  559. // Update stroage info for this cartridge.
  560. //
  561. // IMPORTANT NOTE: This also needs to touch the physical device
  562. // to make sure the device is ready for I/O.
  563. // If we get device errors here, we must fail the
  564. // mount.
  565. CComQIPtr<IRmsStorageInfo, &IID_IRmsStorageInfo> pInfo = m_pCartridge;
  566. // marking the FreeSpace to -1 gaurantees it's stale for the
  567. // following GetLargestFreeSpace() call.
  568. WsbAffirmHr(pInfo->SetFreeSpace(-1));
  569. hr = (*ptr)->GetLargestFreeSpace(NULL, NULL);
  570. if (MVR_E_UNRECOGNIZED_VOLUME == hr) {
  571. // This is expected if this is an unformatted optical media
  572. hr = S_OK;
  573. }
  574. WsbAffirmHr(hr);
  575. WsbAssertHrOk(hr);
  576. /*
  577. Tracking DataMovers is only partially implemented.
  578. CComQIPtr<IRmsServer, &IID_IRmsServer> pServer = g_pServer;
  579. CComPtr<IWsbIndexedCollection> pDataMovers;
  580. WsbAffirmHr(pServer->GetDataMovers(&pDataMovers));
  581. WsbAffirmHr(pDataMovers->Add((IDataMover *)(*ptr)));
  582. */
  583. } WsbCatch(hr);
  584. WsbTraceOut(OLESTR("CRmsDrive::CreateDataMover"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  585. return hr;
  586. }
  587. STDMETHODIMP
  588. CRmsDrive::ReleaseDataMover(
  589. IN IDataMover *ptr)
  590. /*++
  591. Implements:
  592. IRmsDrive::ReleaseDataMover
  593. --*/
  594. {
  595. HRESULT hr = S_OK;
  596. WsbTraceIn(OLESTR("CRmsDrive::ReleaseDataMover"), OLESTR(""));
  597. try {
  598. WsbAssertPointer(ptr);
  599. WsbThrow(E_NOTIMPL);
  600. /*
  601. Tracking DataMovers is only partially implemented.
  602. CComQIPtr<IRmsServer, &IID_IRmsServer> pServer = g_pServer;
  603. CComPtr<IWsbIndexedCollection> pDataMovers;
  604. WsbAffirmHr(pServer->GetDataMovers(&pDataMovers));
  605. WsbAffirmHr(pDataMovers->RemoveAndRelease((IDataMover *)ptr));
  606. ULONG activeDataMovers;
  607. WsbAffirmHr(pDataMovers->GetEntries( &activeDataMovers));
  608. WsbTrace(OLESTR("activeDataMovers = <%u>\n"), activeDataMovers);
  609. */
  610. } WsbCatch(hr);
  611. WsbTraceOut(OLESTR("CRmsDrive::ReleaseDataMover"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  612. return hr;
  613. }
  614. STDMETHODIMP
  615. CRmsDrive::Eject(
  616. void
  617. )
  618. /*++
  619. Implements:
  620. IRmsDrive::Eject
  621. --*/
  622. {
  623. HRESULT hr = E_FAIL;
  624. WsbTraceIn(OLESTR("CRmsDrive::Eject"), OLESTR(""));
  625. HANDLE hDrive = INVALID_HANDLE_VALUE;
  626. try {
  627. CWsbBstrPtr drive = "";
  628. switch ( m_mediaSupported ) {
  629. case RmsMedia8mm:
  630. case RmsMedia4mm:
  631. case RmsMediaDLT:
  632. case RmsMediaTape:
  633. drive = m_deviceName;
  634. break;
  635. case RmsMediaWORM:
  636. break;
  637. case RmsMediaOptical:
  638. case RmsMediaMO35:
  639. case RmsMediaCDR:
  640. case RmsMediaDVD:
  641. case RmsMediaDisk:
  642. case RmsMediaFixed:
  643. // TODO: permanently remove trailing \ from device name ????
  644. WsbAffirmHr(drive.Realloc(2));
  645. wcsncpy(drive, m_deviceName, 2);
  646. drive.Prepend( OLESTR( "\\\\.\\" ) );
  647. break;
  648. }
  649. int retry = 0;
  650. do {
  651. hDrive = CreateFile( drive,
  652. GENERIC_READ | GENERIC_WRITE,
  653. 0,
  654. 0,
  655. OPEN_EXISTING,
  656. 0,
  657. NULL
  658. );
  659. if ( INVALID_HANDLE_VALUE == hDrive )
  660. Sleep(2000);
  661. else
  662. break;
  663. } while ( retry++ < 10 );
  664. WsbAssertHandle( hDrive );
  665. DWORD dwReturn;
  666. WsbAffirmHr(PrepareTape(hDrive, TAPE_UNLOAD, FALSE));
  667. WsbAffirmHr(PrepareTape(hDrive, TAPE_UNLOCK, FALSE));
  668. WsbAssertStatus( DeviceIoControl( hDrive,
  669. IOCTL_STORAGE_EJECT_MEDIA,
  670. NULL,
  671. 0,
  672. NULL,
  673. 0,
  674. &dwReturn,
  675. NULL ));
  676. WsbAssertStatus( CloseHandle( hDrive ) );
  677. hr = S_OK;
  678. }
  679. WsbCatchAndDo( hr,
  680. if ( INVALID_HANDLE_VALUE != hDrive )
  681. CloseHandle( hDrive );
  682. );
  683. WsbTraceOut(OLESTR("CRmsDrive::Eject"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  684. return hr;
  685. }
  686. STDMETHODIMP
  687. CRmsDrive::GetLargestFreeSpace(
  688. LONGLONG *pFreeSpace,
  689. LONGLONG *pCapacity
  690. )
  691. /*++
  692. Implements:
  693. IRmsDrive::GetLargestFreeSpace
  694. --*/
  695. {
  696. HRESULT hr = S_OK;
  697. WsbTraceIn(OLESTR("CRmsDrive::GetLargestFreeSpace"), OLESTR(""));
  698. try {
  699. CComPtr<IDataMover> pDataMover;
  700. WsbAffirmHr(CreateDataMover(&pDataMover));
  701. WsbAffirmHr(pDataMover->GetLargestFreeSpace(pFreeSpace, pCapacity));
  702. } WsbCatch(hr);
  703. WsbTraceOut(OLESTR("CRmsDrive::GetLargestFreeSpace"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  704. return hr;
  705. }
  706. HRESULT
  707. CRmsDrive::UnloadNow(void)
  708. {
  709. HRESULT hr = S_OK;
  710. WsbTraceIn(OLESTR("CRmsDrive::UnloadNow"), OLESTR(""));
  711. try {
  712. WsbAffirmHr(Lock());
  713. WsbAffirmStatus(SetEvent(m_UnloadNowEvent));
  714. WsbAffirmHr(Unlock());
  715. switch(WaitForSingleObject(m_UnloadedEvent, INFINITE)) {
  716. case WAIT_FAILED:
  717. hr = HRESULT_FROM_WIN32(GetLastError());
  718. WsbTrace(OLESTR("CRmsDrive::UnloadNow - Wait for Single Object returned error: %ls\n"),
  719. WsbHrAsString(hr));
  720. WsbAffirmHr(hr);
  721. break;
  722. case WAIT_TIMEOUT:
  723. WsbTrace(OLESTR("CRmsDrive::UnloadNow - Awakened by timeout.\n"));
  724. break;
  725. default:
  726. WsbTrace(OLESTR("CRmsDrive::UnloadNow - Awakened by external signal.\n"));
  727. GetSystemTimeAsFileTime(&m_UnloadNowTime);
  728. break;
  729. }
  730. } WsbCatch(hr);
  731. WsbTraceOut(OLESTR("CRmsDrive::UnloadNow"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  732. return hr;
  733. }
  734. DWORD WINAPI
  735. CRmsDrive::StartUnloadThread(
  736. IN LPVOID pv)
  737. {
  738. return(((CRmsDrive*) pv)->Unload());
  739. }
  740. HRESULT
  741. CRmsDrive::Unload(void)
  742. {
  743. HRESULT hr = S_OK;
  744. WsbTraceIn(OLESTR("CRmsDrive::Unload"), OLESTR(""));
  745. // We need to be sure this object doesn't go away until we're done.
  746. // This happens when we dismount a NTMS managed cartridge.
  747. CComPtr<IRmsDrive> thisDrive = this;
  748. try {
  749. BOOL waiting = TRUE;
  750. LARGE_INTEGER delta = {0,0};
  751. while (waiting) {
  752. //
  753. // !!!!! VERY IMPORTANT !!!!
  754. //
  755. // no 'break' in this loop, we're entering
  756. // a critical section!
  757. //
  758. #if RMS_CRITICAL_SECTION
  759. // <<<<< ENTER SINGLE THREADED SECTION
  760. WsbAffirmHr(Lock());
  761. #endif
  762. WsbTrace(OLESTR("Refs = %d\n"), m_MountReference);
  763. if (0 == m_MountReference) {
  764. FILETIME now;
  765. GetSystemTimeAsFileTime(&now);
  766. ULARGE_INTEGER time0;
  767. ULARGE_INTEGER time1;
  768. time0.LowPart = m_UnloadNowTime.dwLowDateTime;
  769. time0.HighPart = m_UnloadNowTime.dwHighDateTime;
  770. time1.LowPart = now.dwLowDateTime;
  771. time1.HighPart = now.dwHighDateTime;
  772. // time0 is the target time for dismount.
  773. // When delta goes negative, we've expired our
  774. // wait time.
  775. delta.QuadPart = time0.QuadPart-time1.QuadPart;
  776. // convert delta to 100-ns to milliseconds
  777. delta.QuadPart /= 10000;
  778. WsbTrace(OLESTR("Time = <%ls>; Unload Time = <%ls>; delta = %I64d (ms)\n"),
  779. WsbQuickString(WsbFiletimeAsString(FALSE, now)),
  780. WsbQuickString(WsbFiletimeAsString(FALSE, m_UnloadNowTime)),
  781. delta.QuadPart);
  782. if (delta.QuadPart <= 0) {
  783. // Dismount wait time has expired
  784. // Double check that we still have something to dismount
  785. if (S_OK == IsOccupied()) {
  786. // Best effort home
  787. // Fixed drives are always occupied and we shouldn't call Home for their cartridge
  788. FlushBuffers();
  789. if (RmsDeviceFixedDisk != m_deviceType) {
  790. if (S_OK == m_pCartridge->Home()) {
  791. SetIsOccupied(FALSE);
  792. }
  793. }
  794. }
  795. m_UnloadThreadHandle = NULL;
  796. waiting = FALSE;
  797. SetEvent(m_UnloadedEvent);
  798. }
  799. }
  800. else {
  801. hr = S_FALSE;
  802. m_UnloadThreadHandle = NULL;
  803. waiting = FALSE;
  804. }
  805. #if RMS_CRITICAL_SECTION
  806. WsbAffirmHr(Unlock());
  807. // >>>>> LEAVE SINGLE THREADED SECTION
  808. #endif
  809. if ( waiting ) {
  810. switch(WaitForSingleObject(m_UnloadNowEvent, delta.LowPart)) {
  811. case WAIT_FAILED:
  812. WsbTrace(OLESTR("CRmsDrive::Unload - Wait for Single Object returned error: %ls\n"),
  813. WsbHrAsString(HRESULT_FROM_WIN32(GetLastError())));
  814. break;
  815. case WAIT_TIMEOUT:
  816. WsbTrace(OLESTR("CRmsDrive::Unload - Awakened by timeout.\n"));
  817. break;
  818. default:
  819. WsbTrace(OLESTR("CRmsDrive::Unload - Awakened by external signal.\n"));
  820. GetSystemTimeAsFileTime(&m_UnloadNowTime);
  821. break;
  822. }
  823. }
  824. } // waiting
  825. } WsbCatch(hr);
  826. WsbTraceOut(OLESTR("CRmsDrive::Unload"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  827. return hr;
  828. }
  829. HRESULT
  830. CRmsDrive::FlushBuffers( void )
  831. /*++
  832. Implements:
  833. IRmsDrive::FlushBuffers
  834. --*/
  835. {
  836. HRESULT hr S_OK;
  837. WsbTraceIn(OLESTR("CRmsDrive::FlushBuffers"), OLESTR("Device=<%ls>"), (WCHAR *) m_deviceName);
  838. HANDLE hDrive = INVALID_HANDLE_VALUE;
  839. try {
  840. // First flush system buffers
  841. switch (m_mediaSupported) {
  842. case RmsMedia8mm:
  843. case RmsMedia4mm:
  844. case RmsMediaDLT:
  845. case RmsMediaTape:
  846. case RmsMediaWORM:
  847. break;
  848. case RmsMediaOptical:
  849. case RmsMediaMO35:
  850. case RmsMediaCDR:
  851. case RmsMediaDVD:
  852. case RmsMediaDisk:
  853. // No need to flush for Optical media - RSM should flush the system buffers before dismounting
  854. break;
  855. case RmsMediaFixed:
  856. {
  857. // This is special code to flush the file system buffers.
  858. // Create an exclusive handle
  859. CWsbStringPtr drive;
  860. WsbAffirmHr(drive.Alloc(10));
  861. wcsncat( drive, m_deviceName, 2 );
  862. drive.Prepend( OLESTR( "\\\\.\\" ) );
  863. hDrive = CreateFile( drive,
  864. GENERIC_READ | GENERIC_WRITE,
  865. FILE_SHARE_READ | FILE_SHARE_WRITE,
  866. 0,
  867. OPEN_EXISTING,
  868. 0,
  869. NULL
  870. );
  871. WsbAffirmHandle(hDrive);
  872. // Flush buffers
  873. WsbAffirmStatus(FlushFileBuffers(hDrive));
  874. CloseHandle(hDrive);
  875. hDrive = INVALID_HANDLE_VALUE;
  876. }
  877. break;
  878. }
  879. } WsbCatchAndDo(hr,
  880. if (INVALID_HANDLE_VALUE != hDrive) {
  881. CloseHandle(hDrive);
  882. }
  883. );
  884. WsbTraceOut(OLESTR("CRmsDrive::FlushBuffers"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  885. return hr;
  886. }
  887. HRESULT
  888. CRmsDrive::Lock( void )
  889. /*++
  890. Implements:
  891. IRmsDrive::Lock
  892. --*/
  893. {
  894. HRESULT hr S_OK;
  895. WsbTraceIn(OLESTR("CRmsDrive::Lock"), OLESTR(""));
  896. try {
  897. try {
  898. // InitializeCriticalSection raises an exception. Enter/Leave may too.
  899. EnterCriticalSection(&m_CriticalSection);
  900. } catch(DWORD status) {
  901. WsbLogEvent(status, 0, NULL, NULL);
  902. WsbThrow(E_UNEXPECTED);
  903. }
  904. } WsbCatch(hr)
  905. WsbTraceOut(OLESTR("CRmsDrive::Lock"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  906. return hr;
  907. }
  908. HRESULT
  909. CRmsDrive::Unlock( void )
  910. /*++
  911. Implements:
  912. IRmsDrive::Unlock
  913. --*/
  914. {
  915. HRESULT hr S_OK;
  916. WsbTraceIn(OLESTR("CRmsDrive::Unlock"), OLESTR(""));
  917. try {
  918. try {
  919. // InitializeCriticalSection raises an exception. Enter/Leave may too.
  920. LeaveCriticalSection(&m_CriticalSection);
  921. } catch(DWORD status) {
  922. WsbLogEvent(status, 0, NULL, NULL);
  923. WsbThrow(E_UNEXPECTED);
  924. }
  925. } WsbCatch(hr)
  926. WsbTraceOut(OLESTR("CRmsDrive::Unlock"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
  927. return hr;
  928. }