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.

2554 lines
64 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2002 Microsoft Corporation
  3. Module Name :
  4. cmgr.cpp
  5. Abstract :
  6. implements CJobManager
  7. ***********************************************************************/
  8. #include "stdafx.h"
  9. #include <dbt.h>
  10. #include <ioevent.h>
  11. #include <malloc.h>
  12. #include "cmanager.tmh"
  13. //
  14. // If DownloadCurrentFile fails with TRANSIENT_ERROR, the downloader will sleep for this many seconds.
  15. //
  16. const DWORD DELAY_AFTER_TRANSIENT_ERROR = 60;
  17. //
  18. // After a network-alive notification, we wait this long before attempting a download.
  19. //
  20. const UINT64 NETWORK_INIT_TOLERANCE_SECS = 60; // in seconds
  21. //
  22. // This is the windows message ID that is sent when a user logs on or off.
  23. // wParam == true for logon, false for logoff
  24. // lParam == session ID
  25. //
  26. #define WM_SESSION_CHANGE (WM_USER+1)
  27. //------------------------------------------------------------------------
  28. CJobManager * g_Manager;
  29. extern SERVICE_STATUS_HANDLE ghServiceHandle;
  30. extern DWORD g_dwBackgroundThreadId;
  31. //------------------------------------------------------------------------
  32. void GetGuidString( GUID Guid, wchar_t pStr[] )
  33. {
  34. if (!StringFromGUID2( Guid, pStr, MAX_GUID_CHARS ))
  35. {
  36. wcsncpy( pStr, L"(can't convert)", MAX_GUID_CHARS );
  37. }
  38. }
  39. long g_cCalls = 0;
  40. //
  41. // COM uses this to determine when a DLL can unload safely.
  42. //
  43. long g_cLocks = 0;
  44. HRESULT GlobalLockServer(BOOL fLock)
  45. {
  46. if (WPP_LEVEL_ENABLED(LogFlagRefCount))
  47. {
  48. LogInfo("%d", fLock );
  49. }
  50. if (fLock)
  51. InterlockedIncrement(&g_cLocks);
  52. else
  53. InterlockedDecrement(&g_cLocks);
  54. return S_OK;
  55. }
  56. //
  57. // The job manager.
  58. //
  59. MANAGER_STATE g_ServiceState = MANAGER_INACTIVE;
  60. long g_ServiceInstance = 0;
  61. //
  62. // Static data used by the backup writer.
  63. //
  64. VSS_ID s_WRITERID = { /* 4969d978-be47-48b0-b100-f328f07ac1e0 */
  65. 0x4969d978,
  66. 0xbe47,
  67. 0x48b0,
  68. {0xb1, 0x00, 0xf3, 0x28, 0xf0, 0x7a, 0xc1, 0xe0}
  69. };
  70. static LPCWSTR s_WRITERNAME = L"BITS Writer";
  71. #define COMPONENTNAME L"BITS_temporary_files"
  72. //------------------------------------------------------------------------
  73. CJobManager::CJobManager() :
  74. m_ComId_0_5( 0 ),
  75. m_ComId_1_0( 0 ),
  76. m_ComId_1_5( 0 ),
  77. m_hWininet(NULL),
  78. m_pPD( NULL ),
  79. m_CurrentJob( NULL ),
  80. m_Users( m_TaskScheduler ),
  81. m_ExternalInterface( new CJobManagerExternal ),
  82. m_OldQmgrInterface( new COldQmgrInterface ),
  83. m_BackupWriter(NULL),
  84. m_hVssapi_dll(NULL),
  85. m_hQuantumTimer(NULL)
  86. {
  87. try
  88. {
  89. QMErrInfo ErrInfo;
  90. // use manual reset to insure that we are reseting it when
  91. // the downloader task is reinserted.
  92. m_hQuantumTimer = CreateWaitableTimer( NULL, TRUE, NULL );
  93. if ( !m_hQuantumTimer )
  94. {
  95. throw ComError( HRESULT_FROM_WIN32( GetLastError()));
  96. }
  97. THROW_HRESULT( m_NetworkMonitor.Listen( NetworkChangeCallback, this ));
  98. //
  99. // Create the HTTP downloader.
  100. //
  101. THROW_HRESULT( CreateHttpDownloader( &m_pPD, &ErrInfo ));
  102. //
  103. // This object is fully constructed now.
  104. //
  105. GetExternalInterface()->SetInterfaceClass( this );
  106. GetOldExternalInterface()->SetInterfaceClass( this );
  107. g_ServiceState = MANAGER_STARTING;
  108. }
  109. catch( ComError Error )
  110. {
  111. LogError("exception %x at line %d", Error.m_error, Error.m_line );
  112. Cleanup();
  113. throw;
  114. }
  115. }
  116. HRESULT
  117. CJobManager::RegisterClassObjects()
  118. {
  119. try
  120. {
  121. g_ServiceState = MANAGER_ACTIVE;
  122. THROW_HRESULT( CreateBackupWriter() );
  123. THROW_HRESULT(
  124. CoRegisterClassObject(CLSID_BackgroundCopyManager1_5,
  125. (LPUNKNOWN) static_cast<IClassFactory *>(GetExternalInterface() ),
  126. CLSCTX_LOCAL_SERVER,
  127. REGCLS_MULTIPLEUSE,
  128. &m_ComId_1_5 ) );
  129. THROW_HRESULT(
  130. CoRegisterClassObject(CLSID_BackgroundCopyManager,
  131. (LPUNKNOWN) static_cast<IClassFactory *>(GetExternalInterface() ),
  132. CLSCTX_LOCAL_SERVER,
  133. REGCLS_MULTIPLEUSE,
  134. &m_ComId_1_0 ) );
  135. THROW_HRESULT(
  136. CoRegisterClassObject(CLSID_BackgroundCopyQMgr,
  137. (LPUNKNOWN) static_cast<IClassFactory *>(GetOldExternalInterface() ),
  138. CLSCTX_LOCAL_SERVER,
  139. REGCLS_MULTIPLEUSE,
  140. &m_ComId_0_5 ) );
  141. return S_OK;
  142. }
  143. catch ( ComError error )
  144. {
  145. RevokeClassObjects();
  146. return error.Error();
  147. }
  148. }
  149. void
  150. CJobManager::RevokeClassObjects()
  151. {
  152. DeleteBackupWriter();
  153. if (m_ComId_1_5)
  154. {
  155. CoRevokeClassObject( m_ComId_1_5 );
  156. m_ComId_1_5 = 0;
  157. }
  158. if (m_ComId_1_0)
  159. {
  160. CoRevokeClassObject( m_ComId_1_0 );
  161. m_ComId_1_0 = 0;
  162. }
  163. if (m_ComId_0_5)
  164. {
  165. CoRevokeClassObject( m_ComId_0_5 );
  166. m_ComId_0_5 = 0;
  167. }
  168. }
  169. void CJobManager::Cleanup()
  170. {
  171. RevokeClassObjects();
  172. if (m_pPD)
  173. {
  174. DeleteHttpDownloader( m_pPD );
  175. m_pPD = NULL;
  176. }
  177. if (m_hWininet)
  178. {
  179. FreeLibrary(m_hWininet);
  180. m_hWininet = NULL;
  181. }
  182. if ( m_hQuantumTimer )
  183. {
  184. CloseHandle( m_hQuantumTimer );
  185. m_hQuantumTimer = NULL;
  186. }
  187. LogInfo( "cleanup: marking manager inactive" );
  188. g_ServiceState = MANAGER_INACTIVE;
  189. }
  190. CJobManager::~CJobManager()
  191. {
  192. Cleanup();
  193. }
  194. HRESULT CJobManager::CreateBackupWriter()
  195. /*
  196. This function creates the backup writer object.
  197. Note: the vssapi.dll library is not available on Windows 2000. Also, even though
  198. it is present on Windows XP, it is not binary compatable with the version on
  199. Windows Server. So, vssapi.dll is set to delay load and the APIs are only
  200. called if this is Windows Server (v 5.2) or greater.
  201. */
  202. {
  203. // Only initialize the vss writer if this is an OS whose version is later than
  204. // Windows Server (5.2)
  205. if ( (g_PlatformMajorVersion > 5)
  206. ||((g_PlatformMajorVersion == 5)&&(g_PlatformMinorVersion >= 2)) )
  207. {
  208. try
  209. {
  210. //
  211. // system support for the backup writer exists. Initialize it.
  212. //
  213. m_BackupWriter = new CBitsVssWriter;
  214. //
  215. // Initialize the backup writer, if the infrastructure is present.
  216. //
  217. THROW_HRESULT( m_BackupWriter->Initialize( s_WRITERID,
  218. s_WRITERNAME,
  219. VSS_UT_SYSTEMSERVICE,
  220. VSS_ST_OTHER
  221. ));
  222. THROW_HRESULT( m_BackupWriter->Subscribe());
  223. }
  224. catch ( ComError err )
  225. {
  226. LogWarning("unable to init backup writer, hr %x at line %d", err.m_error, err.m_line );
  227. delete m_BackupWriter; m_BackupWriter = NULL;
  228. // ignore the lack of writer
  229. }
  230. }
  231. return S_OK;
  232. }
  233. void CJobManager::DeleteBackupWriter()
  234. {
  235. if (m_BackupWriter)
  236. {
  237. m_BackupWriter->Unsubscribe();
  238. delete m_BackupWriter; m_BackupWriter = NULL;
  239. }
  240. if (m_hVssapi_dll)
  241. {
  242. FreeLibrary( m_hVssapi_dll );
  243. m_hVssapi_dll = NULL;
  244. }
  245. }
  246. void
  247. CJobManager::Shutdown()
  248. {
  249. g_ServiceState = MANAGER_TERMINATING;
  250. // 1. Block creation of new manager proxies.
  251. LogInfo( "shutdown: revoking class objects" );
  252. RevokeClassObjects();
  253. m_TaskScheduler.KillBackgroundTasks();
  254. // 1.5 halt network-change notification
  255. m_NetworkMonitor.CancelListen();
  256. // 5. Wait for calls in progress to finish.
  257. // releasing the reference for the hook thread, added during the constructor.
  258. //
  259. LogInfo("release: internal usage");
  260. NotifyInternalDelete();
  261. while (ActiveCallCount() > 0)
  262. {
  263. Sleep(50);
  264. }
  265. LogInfo( "shutdown: finished" );
  266. }
  267. void
  268. CJobManager::TaskThread()
  269. {
  270. HANDLE hWorkItemAvailable = m_TaskScheduler.GetWaitableObject();
  271. while (1)
  272. {
  273. DWORD dwRet = MsgWaitForMultipleObjectsEx( 1,
  274. &hWorkItemAvailable,
  275. INFINITE,
  276. QS_ALLINPUT,
  277. MWMO_ALERTABLE
  278. );
  279. switch ( dwRet )
  280. {
  281. case WAIT_OBJECT_0:
  282. m_TaskScheduler.DispatchWorkItem();
  283. break;
  284. case WAIT_OBJECT_0 + 1:
  285. // There is one or more window message available. Dispatch them
  286. MSG msg;
  287. while (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
  288. {
  289. if ( msg.message == WM_QUIT )
  290. return;
  291. if (msg.message == WM_SESSION_CHANGE)
  292. {
  293. DWORD Session = (DWORD) msg.lParam;
  294. if (msg.wParam)
  295. {
  296. m_Users.LogonSession( Session );
  297. }
  298. else
  299. {
  300. m_Users.LogoffSession( Session );
  301. }
  302. }
  303. TranslateMessage(&msg);
  304. DispatchMessage(&msg);
  305. if (WaitForSingleObject(hWorkItemAvailable, 0) == WAIT_OBJECT_0)
  306. m_TaskScheduler.DispatchWorkItem();
  307. }
  308. break;
  309. case WAIT_IO_COMPLETION:
  310. //
  311. // an APC fired.
  312. //
  313. break;
  314. default:
  315. Sleep( 20 * 1000 );
  316. break;
  317. }
  318. }
  319. }
  320. //------------------------------------------------------------------------
  321. HRESULT
  322. CJobManager::CreateJob(
  323. LPCWSTR DisplayName,
  324. BG_JOB_TYPE Type,
  325. GUID Id,
  326. SidHandle sid,
  327. CJob ** ppJob,
  328. bool OldStyleJob
  329. )
  330. {
  331. HRESULT Hr = S_OK;
  332. *ppJob = NULL;
  333. //
  334. // create the job
  335. //
  336. try
  337. {
  338. if (Type != BG_JOB_TYPE_DOWNLOAD
  339. #if !defined(BITS_V12)
  340. && Type != BG_JOB_TYPE_UPLOAD
  341. && Type != BG_JOB_TYPE_UPLOAD_REPLY
  342. #endif
  343. )
  344. {
  345. throw ComError( E_NOTIMPL );
  346. }
  347. // Do not allow duplicate guids
  348. if ( m_OnlineJobs.Find( Id ) ||
  349. m_OfflineJobs.Find( Id ) )
  350. throw ComError( E_INVALIDARG );
  351. auto_ptr<WCHAR> TempDisplayName(NULL);
  352. DisplayName = TruncateString( DisplayName, MAX_DISPLAYNAME, TempDisplayName );
  353. ExtendMetadata();
  354. if (Type == BG_JOB_TYPE_DOWNLOAD)
  355. {
  356. *ppJob = new CJob( DisplayName, Type, Id, sid );
  357. if ( OldStyleJob )
  358. {
  359. COldGroupInterface *pOldGroup = new COldGroupInterface( *ppJob );
  360. (*ppJob)->SetOldExternalGroupInterface( pOldGroup );
  361. }
  362. }
  363. else
  364. {
  365. *ppJob = new CUploadJob( DisplayName, Type, Id, sid );
  366. }
  367. m_OnlineJobs.Add( *ppJob );
  368. m_TaskScheduler.InsertDelayedWorkItem( static_cast<CJobInactivityTimeout *>(*ppJob),
  369. g_GlobalInfo->m_JobInactivityTimeout
  370. );
  371. Serialize();
  372. }
  373. catch( ComError exception )
  374. {
  375. Hr = exception.Error();
  376. if (*ppJob)
  377. {
  378. (*ppJob)->UnlinkFromExternalInterfaces();
  379. delete *ppJob;
  380. *ppJob = NULL;
  381. }
  382. ShrinkMetadata();
  383. }
  384. return Hr;
  385. }
  386. HRESULT
  387. CJobManager::GetJob(
  388. REFGUID id,
  389. CJob ** ppJob
  390. )
  391. {
  392. *ppJob = NULL;
  393. CJob * job = m_OnlineJobs.Find( id );
  394. if (job != NULL)
  395. {
  396. if (S_OK != job->IsVisible())
  397. {
  398. return E_ACCESSDENIED;
  399. }
  400. job->UpdateLastAccessTime();
  401. *ppJob = job;
  402. return S_OK;
  403. }
  404. job = m_OfflineJobs.Find( id );
  405. if (job != NULL)
  406. {
  407. if (S_OK != job->IsVisible())
  408. {
  409. return E_ACCESSDENIED;
  410. }
  411. job->UpdateLastAccessTime();
  412. *ppJob = job;
  413. return S_OK;
  414. }
  415. return BG_E_NOT_FOUND;
  416. }
  417. HRESULT
  418. CJobManager::SuspendJob(
  419. CJob * job
  420. )
  421. {
  422. BG_JOB_STATE state = job->_GetState();
  423. switch (state)
  424. {
  425. case BG_JOB_STATE_SUSPENDED:
  426. {
  427. return S_OK;
  428. }
  429. case BG_JOB_STATE_CONNECTING:
  430. case BG_JOB_STATE_TRANSFERRING:
  431. InterruptDownload();
  432. // OK to fall through here
  433. case BG_JOB_STATE_TRANSFERRED:
  434. m_TaskScheduler.CancelWorkItem( static_cast<CJobCallbackItem *>(job) );
  435. // fall through
  436. case BG_JOB_STATE_QUEUED:
  437. case BG_JOB_STATE_TRANSIENT_ERROR:
  438. case BG_JOB_STATE_ERROR:
  439. m_TaskScheduler.CancelWorkItem( static_cast<CJobNoProgressItem *>(job) );
  440. job->SetState( BG_JOB_STATE_SUSPENDED );
  441. job->UpdateModificationTime();
  442. ScheduleAnotherGroup();
  443. return S_OK;
  444. case BG_JOB_STATE_CANCELLED:
  445. case BG_JOB_STATE_ACKNOWLEDGED:
  446. {
  447. return BG_E_INVALID_STATE;
  448. }
  449. default:
  450. {
  451. ASSERT( 0 );
  452. return S_OK;
  453. }
  454. }
  455. ASSERT( 0 );
  456. return S_OK;
  457. }
  458. bool
  459. CJobManager::IsUserLoggedOn( SidHandle sid )
  460. {
  461. CUser * user = m_Users.FindUser( sid, ANY_SESSION );
  462. if (!user)
  463. {
  464. return false;
  465. }
  466. user->DecrementRefCount();
  467. return true;
  468. }
  469. HRESULT
  470. CJobManager::CloneUserToken(
  471. SidHandle sid,
  472. DWORD session,
  473. HANDLE * pToken
  474. )
  475. {
  476. CUser * user = m_Users.FindUser( sid, session );
  477. if (!user)
  478. {
  479. return HRESULT_FROM_WIN32( ERROR_NOT_LOGGED_ON );
  480. }
  481. HRESULT hr = user->CopyToken( pToken );
  482. user->DecrementRefCount();
  483. return hr;
  484. }
  485. void CJobManager::TransferCurrentJob()
  486. {
  487. LogDl("***********START********************");
  488. if (m_TaskScheduler.LockWriter() )
  489. {
  490. m_TaskScheduler.AcknowledgeWorkItemCancel();
  491. return;
  492. }
  493. if (NULL == m_CurrentJob)
  494. {
  495. LogDl( "no more items" );
  496. }
  497. else
  498. {
  499. LogDl("transferring job %p", m_CurrentJob);
  500. m_CurrentJob->Transfer();
  501. }
  502. // It's OK if the item has already been completed.
  503. //
  504. m_TaskScheduler.CompleteWorkItem();
  505. ScheduleAnotherGroup();
  506. m_TaskScheduler.UnlockWriter();
  507. LogDl("************END*********************");
  508. }
  509. void CJobManager::MoveJobOffline(
  510. CJob * job
  511. )
  512. {
  513. m_OnlineJobs.Remove( job );
  514. m_OfflineJobs.Add( job );
  515. }
  516. void CJobManager::AppendOnline(
  517. CJob * job
  518. )
  519. //
  520. // moves a job to the end of the active list.
  521. //
  522. {
  523. if (!m_OnlineJobs.Remove( job ))
  524. {
  525. m_OfflineJobs.Remove( job );
  526. }
  527. m_OnlineJobs.Add( job );
  528. }
  529. void
  530. CJobManager::UpdateRemoteSizes(
  531. CUnknownFileSizeList *pUnknownFileSizeList,
  532. HANDLE hToken,
  533. QMErrInfo *pErrorInfo,
  534. const PROXY_SETTINGS *ProxySettings,
  535. const CCredentialsContainer * Credentials
  536. )
  537. {
  538. bool bTimeout = false;
  539. for(CUnknownFileSizeList::iterator iter = pUnknownFileSizeList->begin();
  540. iter != pUnknownFileSizeList->end() && !bTimeout; iter++ )
  541. {
  542. CFile *pFile = iter->m_file;
  543. const WCHAR *pURL = (const WCHAR*)iter->m_url;
  544. pErrorInfo->Clear();
  545. LogDl( "Retrieving remote infomation for %ls", pURL );
  546. UINT64 FileSize;
  547. FILETIME FileTime;
  548. m_pPD->GetRemoteFileInformation(
  549. hToken,
  550. pURL,
  551. &FileSize,
  552. &FileTime,
  553. pErrorInfo,
  554. ProxySettings,
  555. Credentials
  556. );
  557. // If we can't get the size for one file, skip that file
  558. // and move to other files in the file.
  559. if (pErrorInfo->result != QM_FILE_DONE )
  560. {
  561. LogWarning( "Unable to retrieve remote information for %ls", pURL );
  562. continue;
  563. }
  564. // Update size in file.
  565. if ( m_TaskScheduler.LockWriter() )
  566. {
  567. m_TaskScheduler.AcknowledgeWorkItemCancel();
  568. pErrorInfo->result = QM_FILE_ABORTED;
  569. return;
  570. }
  571. pFile->SetBytesTotal( FileSize );
  572. //
  573. // A zero-length file will not be downloading any info, so it skips the normal path
  574. // for setting the correct creation time. Set it here.
  575. //
  576. if (FileSize == 0 &&
  577. (FileTime.dwLowDateTime != 0 || FileTime.dwHighDateTime != 0))
  578. {
  579. DWORD err = pFile->SetLocalFileTime( FileTime );
  580. if (err)
  581. {
  582. pErrorInfo->result = QM_FILE_TRANSIENT_ERROR;
  583. pErrorInfo->Set( SOURCE_QMGR_FILE, ERROR_STYLE_WIN32, err, NULL );
  584. }
  585. }
  586. if (CheckForQuantumTimeout())
  587. {
  588. bTimeout = true;
  589. }
  590. m_TaskScheduler.UnlockWriter();
  591. }
  592. }
  593. void
  594. CJobManager::InterruptDownload()
  595. {
  596. LogInfo( "Interrupting download...\n");
  597. // Cancel the downloader workitem. CancelWorkItem
  598. // should ignore the request if the a download isn't running or pending.
  599. // Writer lock required!!!!!
  600. m_TaskScheduler.CancelWorkItem( this );
  601. // Now you must call ScheduleAnotherGroup, in order for the downloader to download anything.
  602. }
  603. void
  604. CJobManager::ScheduleAnotherGroup(
  605. bool fInsertNetworkDelay
  606. )
  607. /*++
  608. Description:
  609. Called by any thread to make sure that the highest-priority available job is being downloaded.
  610. It finds the highest-priority job that is in the QUEUED or RUNNING state.
  611. If this job is different from the current download job, the current job is cancelled and
  612. the new job is started. If no job is currently running, the new job is started.
  613. Starting the new job requires interrupting the download thread.
  614. At entry:
  615. m_CurrentJob is the job being downloaded
  616. If the downloader thread is active, then the manager's work item is in the scheduler.
  617. Otherwise it is not.
  618. At exit:
  619. the best available job to download is in m_CurrentJob, or NULL if none available
  620. If a job is available, the work item is in the task scheduler.
  621. --*/
  622. {
  623. CJob *pOldCurrentJob = m_CurrentJob;
  624. if (IsServiceShuttingDown())
  625. {
  626. LogInfo("no job scheduled; service is shutting down.");
  627. m_CurrentJob = NULL;
  628. }
  629. else
  630. {
  631. //
  632. // Choose the best candidate, which may be the old current job.
  633. //
  634. ChooseCurrentJob();
  635. #if DBG
  636. // do some validation checking on the queue
  637. ULONG RunningJobs = 0;
  638. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  639. {
  640. if ( iter->IsRunning() )
  641. RunningJobs++;
  642. }
  643. if (m_CurrentJob == NULL)
  644. {
  645. ASSERT( RunningJobs == 0 );
  646. }
  647. else
  648. {
  649. // zero if the download item is queued, one if running
  650. //
  651. ASSERT( RunningJobs == 0 || RunningJobs == 1 );
  652. }
  653. #endif
  654. }
  655. if (m_CurrentJob)
  656. {
  657. if ( m_CurrentJob != pOldCurrentJob )
  658. m_TaskScheduler.CancelWorkItem( this );
  659. if (!m_TaskScheduler.IsWorkItemInScheduler( this ))
  660. {
  661. if (fInsertNetworkDelay)
  662. {
  663. m_TaskScheduler.InsertDelayedWorkItem( this,
  664. NETWORK_INIT_TOLERANCE_SECS * NanoSec100PerSec );
  665. }
  666. else
  667. {
  668. m_TaskScheduler.InsertWorkItem( this );
  669. }
  670. }
  671. }
  672. else
  673. {
  674. m_TaskScheduler.CancelWorkItem( this );
  675. }
  676. }
  677. void
  678. CJobManager::ChooseCurrentJob()
  679. {
  680. CJob * NewJob = NULL;
  681. if (m_NetworkMonitor.GetAddressCount() == 0)
  682. {
  683. NewJob = NULL;
  684. }
  685. else
  686. {
  687. // Look at all the jobs and choose the best one
  688. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  689. {
  690. if (iter->IsRunnable())
  691. {
  692. BG_JOB_PRIORITY priority = iter->_GetPriority();
  693. if ( !NewJob || ( priority < NewJob->_GetPriority() ) )
  694. {
  695. NewJob = &(*iter);
  696. }
  697. }
  698. }
  699. }
  700. LogInfo( "scheduler: current=%p new=%p", m_CurrentJob, NewJob );
  701. if (m_CurrentJob == NewJob)
  702. {
  703. return;
  704. }
  705. if ( m_CurrentJob )
  706. {
  707. LogInfo( "scheduler: current priority %u", m_CurrentJob->_GetPriority() );
  708. //
  709. // an inactive job goes to QUEUED state if we have network connectivity,
  710. // TRANSIENT_ERROR state otherwise.
  711. //
  712. if ( m_CurrentJob->IsRunning() )
  713. {
  714. m_CurrentJob->RecalcTransientError();
  715. }
  716. }
  717. if ( NewJob )
  718. {
  719. LogInfo( "scheduler: new priority %u", NewJob->_GetPriority() );
  720. SetQuantumTimeout();
  721. }
  722. m_CurrentJob = NewJob;
  723. }
  724. void
  725. CJobManager::RetaskJob( CJob *pJob )
  726. {
  727. if ( pJob->IsRunning() )
  728. {
  729. InterruptDownload();
  730. }
  731. ScheduleAnotherGroup();
  732. }
  733. void CALLBACK
  734. CJobManager::NetworkChangeCallback(
  735. PVOID arg
  736. )
  737. {
  738. reinterpret_cast<CJobManager *>(arg)->OnNetworkChange();
  739. }
  740. void
  741. CJobManager::OnNetworkChange()
  742. {
  743. if (g_ServiceState == MANAGER_TERMINATING)
  744. {
  745. LogInfo("network change: manager terminating");
  746. return;
  747. }
  748. LogInfo("network adapters changed: now %d active", m_NetworkMonitor.GetAddressCount());
  749. if (m_NetworkMonitor.GetAddressCount() > 0)
  750. {
  751. ReactivateTransientErrorJobs();
  752. }
  753. else
  754. {
  755. MarkJobsWithNetDisconnected();
  756. }
  757. {
  758. HoldWriterLock lock( &m_TaskScheduler );
  759. //
  760. // The previous proxy data is incorrect if we have switched from a corporate net to roaming or vice-versa..
  761. //
  762. g_ProxyCache->Invalidate();
  763. ScheduleAnotherGroup();
  764. }
  765. for (int i=1; i <= 3; ++i)
  766. {
  767. HRESULT hr;
  768. hr = m_NetworkMonitor.Listen( NetworkChangeCallback, this );
  769. if (SUCCEEDED(hr))
  770. {
  771. return;
  772. }
  773. LogError( "re-listen failed %x", hr);
  774. Sleep( 1000 );
  775. }
  776. }
  777. void
  778. CJobManager::MarkJobsWithNetDisconnected()
  779. {
  780. HoldWriterLock lock( &m_TaskScheduler );
  781. for (CJobList::iterator iter=m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  782. {
  783. iter->OnNetworkDisconnect();
  784. }
  785. for (CJobList::iterator iter=m_OfflineJobs.begin(); iter != m_OfflineJobs.end(); ++iter)
  786. {
  787. iter->OnNetworkDisconnect();
  788. }
  789. ScheduleAnotherGroup();
  790. }
  791. void
  792. CJobManager::ReactivateTransientErrorJobs()
  793. {
  794. HoldWriterLock lock( &m_TaskScheduler );
  795. for (CJobList::iterator iter=m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  796. {
  797. iter->OnNetworkConnect();
  798. }
  799. for (CJobList::iterator iter=m_OfflineJobs.begin(); iter != m_OfflineJobs.end(); ++iter)
  800. {
  801. iter->OnNetworkConnect();
  802. }
  803. ScheduleAnotherGroup( true );
  804. }
  805. void
  806. CJobManager::UserLoggedOn(
  807. SidHandle sid
  808. )
  809. {
  810. HoldWriterLock LockHolder( &m_TaskScheduler );
  811. CJobList::iterator iter = m_OfflineJobs.begin();
  812. while (iter != m_OfflineJobs.end())
  813. {
  814. if (false == iter->IsOwner( sid ))
  815. {
  816. ++iter;
  817. continue;
  818. }
  819. LogInfo("manager : moving job %p to online list", &(*iter) );
  820. //
  821. // move the job to the online list.
  822. //
  823. CJobList::iterator next = iter;
  824. ++next;
  825. m_OfflineJobs.erase( iter );
  826. m_OnlineJobs.push_back( *iter );
  827. iter = next;
  828. }
  829. //
  830. // Make sure a group is running.
  831. //
  832. ScheduleAnotherGroup();
  833. }
  834. void
  835. CJobManager::UserLoggedOff(
  836. SidHandle sid
  837. )
  838. {
  839. bool fReschedule = false;
  840. HoldWriterLock LockHolder( &m_TaskScheduler );
  841. //
  842. // If a job is in progress and the user owns it, cancel it.
  843. //
  844. if (m_CurrentJob &&
  845. m_CurrentJob->IsOwner( sid ))
  846. {
  847. InterruptDownload();
  848. fReschedule = true;
  849. }
  850. //
  851. // Move all the user's jobs into the offline list.
  852. //
  853. CJobList::iterator iter = m_OnlineJobs.begin();
  854. while (iter != m_OnlineJobs.end())
  855. {
  856. //
  857. // Skip over other users' job.
  858. // Also skip over the currently downloading job, which will be handled by the download thread.
  859. //
  860. if (false == iter->IsOwner( sid ) ||
  861. &(*iter) == m_CurrentJob)
  862. {
  863. ++iter;
  864. continue;
  865. }
  866. LogInfo("manager : moving job %p to offline list", &(*iter) );
  867. /*
  868. this should't ever be true, since we skip over m_CurrentJob.
  869. */ ASSERT( false == iter->IsRunning() );
  870. //
  871. // move the job to the online list.
  872. //
  873. CJobList::iterator next = iter;
  874. ++next;
  875. m_OnlineJobs.erase( iter );
  876. m_OfflineJobs.push_back( *iter );
  877. iter = next;
  878. }
  879. if (fReschedule)
  880. {
  881. ScheduleAnotherGroup();
  882. }
  883. }
  884. void
  885. CJobManager::ResetOnlineStatus(
  886. CJob *pJob,
  887. SidHandle sid
  888. )
  889. //
  890. // Called when a job owner changes. This fn checks whether the job needs to be moved
  891. // from the offline list to the online list. (If the job needs to move from the online
  892. // list to the offline list, the downloader thread will take care of it when the job
  893. // becomes the current job.)
  894. //
  895. {
  896. if ( IsUserLoggedOn( sid ) &&
  897. m_OfflineJobs.Remove( pJob ) )
  898. {
  899. m_OnlineJobs.Add( pJob );
  900. }
  901. }
  902. size_t
  903. CJobManager::MoveActiveJobToListEnd(
  904. CJob *pJob
  905. )
  906. {
  907. if (m_NetworkMonitor.GetAddressCount() == 0)
  908. {
  909. // if the net is disconnected, no need to rearrange jobs.
  910. //
  911. return 1;
  912. }
  913. ASSERT( m_TaskScheduler.IsWriter() );
  914. // Returns the number of queue or running jobs with a higher or same priority as ourself.
  915. ASSERT( pJob->IsRunnable() );
  916. size_t PossibleActiveJobs = 0;
  917. CJobList::iterator jobpos = m_OnlineJobs.end();
  918. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  919. {
  920. if ( iter->IsRunnable() &&
  921. iter->_GetPriority() <= pJob->_GetPriority() )
  922. {
  923. PossibleActiveJobs++;
  924. }
  925. if ( &(*iter) == pJob )
  926. {
  927. jobpos = iter;
  928. }
  929. }
  930. //
  931. // If the job is online, and another job can be pushed to the front,
  932. // push our job to the back.
  933. //
  934. if ( PossibleActiveJobs > 1 && jobpos != m_OnlineJobs.end())
  935. {
  936. // move job to the end of the list.
  937. m_OnlineJobs.erase( jobpos );
  938. m_OnlineJobs.push_back( *pJob );
  939. }
  940. else if (jobpos == m_OnlineJobs.end())
  941. {
  942. LogWarning("resuming an offline job");
  943. }
  944. return PossibleActiveJobs;
  945. }
  946. void
  947. CJobManager::SetQuantumTimeout()
  948. {
  949. LARGE_INTEGER QuantumTime;
  950. QuantumTime.QuadPart = -g_GlobalInfo->m_TimeQuantaLength;
  951. BOOL bResult =
  952. SetWaitableTimer(
  953. m_hQuantumTimer,
  954. &QuantumTime,
  955. 0,
  956. NULL,
  957. NULL,
  958. FALSE );
  959. ASSERT( bResult );
  960. }
  961. bool
  962. CJobManager::CheckForQuantumTimeout()
  963. {
  964. DWORD dwResult =
  965. WaitForSingleObject( m_hQuantumTimer, 0 );
  966. if ( WAIT_OBJECT_0 != dwResult)
  967. {
  968. // The timer didn't expire, so we have nothing to do.
  969. return false;
  970. }
  971. // The timeout fired so we need to move the current job
  972. // to the end of the list and signal the downloader to abort.
  973. // Do not cancel the current work item and do not change the current
  974. // job. Just let the downloader exit and let it call ScheduleAnotherGroup
  975. // to switch jobs if needed.
  976. // Special case. If only one RUNNING or QUEUED job exists in the list with
  977. // a priority >= our own, then we have no reason to switch tasks.
  978. // Just reset the timer and continue.
  979. bool fTookWriter = false;
  980. if (!m_TaskScheduler.IsWriter())
  981. {
  982. if (m_TaskScheduler.LockWriter() )
  983. {
  984. // cancelled; can't tell whether there is more than one job - assume the worst.
  985. return true;
  986. }
  987. fTookWriter = true;
  988. }
  989. ASSERT( m_CurrentJob );
  990. size_t PossibleActiveJobs = MoveActiveJobToListEnd( m_CurrentJob );
  991. if (fTookWriter)
  992. {
  993. m_TaskScheduler.UnlockWriter();
  994. }
  995. if ( 1 == PossibleActiveJobs )
  996. {
  997. LogInfo( "Time quantum fired, but nothing else can run. Ignoring and resetting timer.");
  998. SetQuantumTimeout();
  999. return false;
  1000. }
  1001. LogInfo( "Time quantum fired, moving job to the end of the queue.");
  1002. return true; // signal downloader to abort
  1003. }
  1004. extern HMODULE g_hInstance;
  1005. HRESULT
  1006. CJobManager::GetErrorDescription(
  1007. HRESULT hResult,
  1008. DWORD LanguageId,
  1009. LPWSTR *pErrorDescription )
  1010. {
  1011. // Do not allow 0 for now, untill propagation of thread error is investigated more.
  1012. if (!LanguageId)
  1013. {
  1014. return E_INVALIDARG;
  1015. }
  1016. TCHAR *pBuffer = NULL;
  1017. //
  1018. // Use the following search path to find the message.
  1019. //
  1020. // 1. This DLL
  1021. // 2. wininet.dll
  1022. // 3. the system
  1023. DWORD dwSize =
  1024. FormatMessage(
  1025. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
  1026. g_hInstance,
  1027. (DWORD)hResult,
  1028. LanguageId,
  1029. (LPTSTR)&pBuffer,
  1030. 0,
  1031. NULL );
  1032. if ( !dwSize )
  1033. {
  1034. if ( GetLastError() == ERROR_OUTOFMEMORY )
  1035. {
  1036. return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  1037. }
  1038. {
  1039. if (!m_hWininet)
  1040. {
  1041. m_hWininet =
  1042. LoadLibraryEx( _T("winhttp.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE );
  1043. }
  1044. if ( m_hWininet )
  1045. {
  1046. dwSize =
  1047. FormatMessage(
  1048. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
  1049. m_hWininet,
  1050. (DWORD)(0x0000FFFF & (hResult)),
  1051. LanguageId,
  1052. (LPTSTR)&pBuffer,
  1053. 0,
  1054. NULL );
  1055. if ( !dwSize && ( GetLastError() == ERROR_OUTOFMEMORY ) )
  1056. {
  1057. return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
  1058. }
  1059. }
  1060. }
  1061. if ( !dwSize )
  1062. {
  1063. dwSize =
  1064. FormatMessage(
  1065. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1066. NULL,
  1067. (DWORD)hResult,
  1068. LanguageId,
  1069. (LPTSTR)&pBuffer,
  1070. 0,
  1071. NULL );
  1072. if (!dwSize)
  1073. {
  1074. return HRESULT_FROM_WIN32( GetLastError() );
  1075. }
  1076. }
  1077. }
  1078. ++dwSize; // needs to include trailing NULL
  1079. ASSERT( pBuffer );
  1080. #if !defined(_UNICODE)
  1081. #error need to add ASCII to unicode conversion here
  1082. #else
  1083. *pErrorDescription = MidlCopyString( pBuffer );
  1084. LocalFree( pBuffer );
  1085. return (*pErrorDescription) ? S_OK : E_OUTOFMEMORY;
  1086. #endif
  1087. }
  1088. HRESULT
  1089. CJobManager::Serialize()
  1090. {
  1091. //
  1092. // If this function changes, be sure that the metadata extension
  1093. // constants are adequate.
  1094. //
  1095. HRESULT hr;
  1096. try
  1097. {
  1098. //
  1099. // Serialization requires the thread to run in local-system context.
  1100. // If the thread is impersonating a COM client, it must revert.
  1101. //
  1102. CSaveThreadToken tok;
  1103. RevertToSelf();
  1104. // The service should automatically start if any groups
  1105. // are in the waiting/Running state or a logged off user has groups.
  1106. bool bAutomaticStart;
  1107. bAutomaticStart = (m_OnlineJobs.size() > 0) || (m_OfflineJobs.size() > 0);
  1108. LogSerial("Need to set service to %s start", bAutomaticStart ? "auto" : "manual" );
  1109. if ( bAutomaticStart )
  1110. {
  1111. // If we can't set the service to autostart, it's a fatal error.
  1112. // Fail the serialize at this point.
  1113. THROW_HRESULT( SetServiceStartup( bAutomaticStart ) );
  1114. }
  1115. CQmgrWriteStateFile StateFile( *this );
  1116. HANDLE hFile = StateFile.GetHandle();
  1117. SafeWriteBlockBegin( hFile, PriorityQueuesStorageGUID );
  1118. m_OnlineJobs.Serialize( hFile );
  1119. m_OfflineJobs.Serialize( hFile );
  1120. SafeWriteBlockEnd( hFile, PriorityQueuesStorageGUID );
  1121. StateFile.CommitFile();
  1122. if ( !bAutomaticStart )
  1123. {
  1124. // If we can't set the service to manual, its not a big deal. The worst
  1125. // that should happen is we start when we really don't need to.
  1126. hr = SetServiceStartup( bAutomaticStart );
  1127. if ( !SUCCEEDED( hr ) )
  1128. {
  1129. LogWarning("Couldn't set service startup to manual, ignoring. Hr 0x%8.8X", hr );
  1130. }
  1131. }
  1132. LogSerial( "finished");
  1133. hr = S_OK;
  1134. }
  1135. catch( ComError Error )
  1136. {
  1137. LogWarning("Error %u writing metadata\n", Error.Error() );
  1138. hr = Error.Error();
  1139. }
  1140. return hr;
  1141. }
  1142. HRESULT
  1143. CJobManager::Unserialize()
  1144. {
  1145. HRESULT hr;
  1146. try
  1147. {
  1148. BOOL fIncludeLogoffList;
  1149. CQmgrReadStateFile StateFile( *this );
  1150. HANDLE hFile = StateFile.GetHandle();
  1151. SafeReadBlockBegin( hFile, PriorityQueuesStorageGUID );
  1152. //
  1153. // In the Serialize() code, the first is online jobs and the second is offline.
  1154. // When unserializing, the set of logged-in users is likely to be different, so
  1155. // we pull them all in and then lazily move them to the offline list.
  1156. //
  1157. m_OnlineJobs.Unserialize( hFile );
  1158. m_OnlineJobs.Unserialize( hFile );
  1159. SafeReadBlockEnd( hFile, PriorityQueuesStorageGUID );
  1160. StateFile.ValidateEndOfFile();
  1161. hr = S_OK;
  1162. }
  1163. catch( ComError err )
  1164. {
  1165. //
  1166. // File corruption is reason to delete the group data and start fresh.
  1167. // Other errors, like out-of-memory, are not.
  1168. //
  1169. LogError( "Error %u reading metadata", err.Error() );
  1170. hr = err.Error();
  1171. if (hr == E_INVALIDARG ||
  1172. hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ))
  1173. {
  1174. LogSerial("clearing job list");
  1175. if (hr == E_INVALIDARG )
  1176. {
  1177. g_EventLogger->ReportStateFileCleared();
  1178. }
  1179. m_OnlineJobs.Clear();
  1180. m_OfflineJobs.Clear();
  1181. hr = S_OK;
  1182. }
  1183. }
  1184. return hr;
  1185. }
  1186. void
  1187. CJobManager::OnDeviceLock(
  1188. const WCHAR *CanonicalVolume )
  1189. {
  1190. bool fChanged = false;
  1191. // Look at all the jobs and move the ones for this drive to the
  1192. // transient error state.
  1193. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1194. {
  1195. fChanged |= iter->OnDeviceLock( CanonicalVolume );
  1196. }
  1197. if (fChanged)
  1198. {
  1199. ScheduleAnotherGroup();
  1200. Serialize();
  1201. }
  1202. }
  1203. void
  1204. CJobManager::OnDeviceUnlock(
  1205. const WCHAR *CanonicalVolume )
  1206. {
  1207. bool fChanged = false;
  1208. // Look at all the jobs and retry the ones that are in the transient error state
  1209. // do to this drive being locked.
  1210. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1211. {
  1212. fChanged |= iter->OnDeviceUnlock( CanonicalVolume );
  1213. }
  1214. if (fChanged)
  1215. {
  1216. ScheduleAnotherGroup();
  1217. Serialize();
  1218. }
  1219. }
  1220. void
  1221. CJobManager::OnDiskChange(
  1222. const WCHAR *CanonicalVolume,
  1223. DWORD VolumeSerialNumber )
  1224. {
  1225. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1226. {
  1227. iter->OnDiskChange( CanonicalVolume, VolumeSerialNumber );
  1228. }
  1229. ScheduleAnotherGroup();
  1230. Serialize();
  1231. }
  1232. void
  1233. CJobManager::OnDismount(
  1234. const WCHAR *CanonicalVolume )
  1235. {
  1236. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1237. {
  1238. iter->OnDismount( CanonicalVolume );
  1239. }
  1240. ScheduleAnotherGroup();
  1241. Serialize();
  1242. }
  1243. BOOL
  1244. CJobList::Add(
  1245. CJob * job
  1246. )
  1247. //
  1248. // adds a single group to the list.
  1249. //
  1250. {
  1251. push_back( *job );
  1252. return TRUE;
  1253. }
  1254. CJob *
  1255. CJobList::Find(
  1256. REFGUID id
  1257. )
  1258. {
  1259. iterator iter;
  1260. for (iter=begin(); iter != end(); ++iter)
  1261. {
  1262. GUID jobid = iter->GetId();
  1263. if (id == jobid)
  1264. {
  1265. return &(*iter);
  1266. }
  1267. }
  1268. return NULL;
  1269. }
  1270. BOOL
  1271. CJobList::Remove(
  1272. CJob * job
  1273. )
  1274. //
  1275. // removes a single group to the list. Quite inefficient for large lists.
  1276. //
  1277. {
  1278. iterator iter;
  1279. for (iter=begin(); iter != end(); ++iter)
  1280. {
  1281. if (job == &(*iter))
  1282. {
  1283. erase( iter );
  1284. return TRUE;
  1285. }
  1286. }
  1287. return FALSE;
  1288. }
  1289. void
  1290. CJobList::Clear()
  1291. {
  1292. iterator iter;
  1293. while ((iter=begin()) != end())
  1294. {
  1295. CJob * job = &(*iter);
  1296. LogInfo("clearing %p", job);
  1297. erase( iter );
  1298. job->Release();
  1299. }
  1300. }
  1301. void
  1302. CJobList::Serialize( HANDLE hFile )
  1303. {
  1304. DWORD dwNumberOfGroups = 0;
  1305. dwNumberOfGroups = size();
  1306. SafeWriteBlockBegin( hFile, GroupListStorageGUID );
  1307. SafeWriteFile( hFile, dwNumberOfGroups );
  1308. iterator iter;
  1309. for (iter=begin(); iter != end(); ++iter)
  1310. {
  1311. iter->Serialize(hFile);
  1312. }
  1313. SafeWriteBlockEnd( hFile, GroupListStorageGUID );
  1314. }
  1315. void
  1316. CJobList::Unserialize(
  1317. HANDLE hFile
  1318. )
  1319. {
  1320. SafeReadBlockBegin( hFile, GroupListStorageGUID );
  1321. DWORD dwNumberOfGroups;
  1322. SafeReadFile( hFile, &dwNumberOfGroups );
  1323. for (int i = 0; i < dwNumberOfGroups; i++)
  1324. {
  1325. CJob * job = NULL;
  1326. try
  1327. {
  1328. job = CJob::UnserializeJob( hFile );
  1329. push_back( *job );
  1330. LogSerial( "added job %p to queue %p, priority %d",
  1331. job, this, job->_GetPriority() );
  1332. }
  1333. catch ( ComError err )
  1334. {
  1335. LogError( "error in joblist unserialize 0x%x", err.Error() );
  1336. throw;
  1337. }
  1338. }
  1339. SafeReadBlockEnd( hFile, GroupListStorageGUID );
  1340. }
  1341. CJobList::~CJobList()
  1342. {
  1343. ASSERT( g_ServiceState != MANAGER_ACTIVE );
  1344. iterator iter;
  1345. while ( (iter=begin()) != end() )
  1346. {
  1347. CJob * job = &(*iter);
  1348. LogInfo("deleting job %p", job );
  1349. iter.excise();
  1350. job->UnlinkFromExternalInterfaces();
  1351. delete job;
  1352. }
  1353. }
  1354. CJobManagerExternal::CJobManagerExternal() :
  1355. m_ServiceInstance( g_ServiceInstance ),
  1356. m_refs(1),
  1357. m_pJobManager( NULL )
  1358. {
  1359. }
  1360. STDMETHODIMP
  1361. CJobManagerExternal::QueryInterface(
  1362. REFIID iid,
  1363. void** ppvObject
  1364. )
  1365. {
  1366. BEGIN_EXTERNAL_FUNC
  1367. HRESULT Hr = S_OK;
  1368. *ppvObject = NULL;
  1369. if (iid == IID_IUnknown)
  1370. {
  1371. *ppvObject = static_cast<IBackgroundCopyManager *>(this);
  1372. LogInfo("mgr: QI for IUnknown");
  1373. ((IUnknown *)(*ppvObject))->AddRef();
  1374. }
  1375. else if (iid == IID_IBackgroundCopyManager)
  1376. {
  1377. *ppvObject = static_cast<IBackgroundCopyManager *>(this);
  1378. LogInfo("mgr: QI for IManager");
  1379. ((IUnknown *)(*ppvObject))->AddRef();
  1380. }
  1381. else if (iid == IID_IClassFactory)
  1382. {
  1383. *ppvObject = static_cast<IClassFactory *>(this);
  1384. LogInfo("mgr: QI for IFactory");
  1385. ((IUnknown *)(*ppvObject))->AddRef();
  1386. }
  1387. else if (iid == __uuidof(IBitsTest1))
  1388. {
  1389. *ppvObject = static_cast<IBitsTest1 *>(this);
  1390. LogInfo("mgr: QI for IFactory");
  1391. ((IUnknown *)(*ppvObject))->AddRef();
  1392. }
  1393. else
  1394. {
  1395. Hr = E_NOINTERFACE;
  1396. }
  1397. LogRef( "iid %!guid!, Hr %x", &iid, Hr );
  1398. return Hr;
  1399. END_EXTERNAL_FUNC
  1400. }
  1401. ULONG
  1402. CJobManagerExternal::AddRef()
  1403. {
  1404. BEGIN_EXTERNAL_FUNC;
  1405. ULONG newrefs = InterlockedIncrement(&m_refs);
  1406. LogRef( "new refs = %d", newrefs );
  1407. return newrefs;
  1408. END_EXTERNAL_FUNC;
  1409. }
  1410. ULONG
  1411. CJobManagerExternal::Release()
  1412. {
  1413. BEGIN_EXTERNAL_FUNC;
  1414. ULONG newrefs = InterlockedDecrement(&m_refs);
  1415. LogRef( "new refs = %d", newrefs );
  1416. if (newrefs == 0)
  1417. {
  1418. delete this;
  1419. }
  1420. return newrefs;
  1421. END_EXTERNAL_FUNC;
  1422. }
  1423. /************************************************************************************
  1424. IClassFactory Implementation
  1425. ************************************************************************************/
  1426. HRESULT CJobManagerExternal::CreateInstance(IUnknown* pUnkOuter, REFIID iid, void** ppvObject)
  1427. {
  1428. BEGIN_EXTERNAL_FUNC
  1429. HRESULT hr = S_OK;
  1430. if (pUnkOuter != NULL)
  1431. {
  1432. hr = CLASS_E_NOAGGREGATION;
  1433. }
  1434. else
  1435. {
  1436. if ((iid == IID_IBackgroundCopyManager) || (iid == IID_IUnknown))
  1437. {
  1438. hr = QueryInterface(iid, ppvObject);
  1439. }
  1440. else
  1441. {
  1442. hr = E_NOTIMPL;
  1443. }
  1444. }
  1445. LogRef( "iid %!guid!, Hr %x, object at %p", &iid, hr, *ppvObject );
  1446. return hr;
  1447. END_EXTERNAL_FUNC
  1448. }
  1449. HRESULT CJobManagerExternal::LockServer(BOOL fLock)
  1450. {
  1451. BEGIN_EXTERNAL_FUNC
  1452. LogRef( "LockServer(%d)", fLock);
  1453. return GlobalLockServer( fLock );
  1454. END_EXTERNAL_FUNC
  1455. }
  1456. /************************************************************************************
  1457. IBackgroundCopyManager Implementation
  1458. ************************************************************************************/
  1459. HRESULT STDMETHODCALLTYPE
  1460. CJobManagerExternal::CreateJobInternal (
  1461. /* [in] */ LPCWSTR DisplayName,
  1462. /* [in] */ BG_JOB_TYPE Type,
  1463. /* [out] */ GUID *pJobId,
  1464. /* [out] */ IBackgroundCopyJob **ppJob)
  1465. {
  1466. CLockedJobManagerWritePointer LockedJobManager(m_pJobManager );
  1467. LogPublicApiBegin( "DisplayName %S, Type %u", DisplayName, Type );
  1468. HRESULT Hr = S_OK;
  1469. CJob * job = NULL;
  1470. //
  1471. // create the job
  1472. //
  1473. try
  1474. {
  1475. THROW_HRESULT( LockedJobManager.ValidateAccess());
  1476. //
  1477. // validate parameters
  1478. //
  1479. if (DisplayName == NULL ||
  1480. pJobId == NULL ||
  1481. ppJob == NULL)
  1482. {
  1483. throw ComError( E_INVALIDARG );
  1484. }
  1485. *ppJob = NULL;
  1486. GUID Id;
  1487. if (0 !=UuidCreate( &Id ))
  1488. {
  1489. throw ComError( HRESULT_FROM_WIN32( GetLastError() ));
  1490. }
  1491. THROW_HRESULT( LockedJobManager->CreateJob( DisplayName, Type, Id, GetThreadClientSid(), &job ));
  1492. *ppJob = job->GetExternalInterface();
  1493. (*ppJob)->AddRef();
  1494. *pJobId = Id;
  1495. Hr = S_OK;
  1496. }
  1497. catch( ComError exception )
  1498. {
  1499. Hr = exception.Error();
  1500. memset(pJobId, 0, sizeof(*pJobId) );
  1501. }
  1502. LogPublicApiEnd( "pJobId %p(%!guid!), ppJob %p(%p)",
  1503. pJobId, pJobId, ppJob, *ppJob );
  1504. return Hr;
  1505. }
  1506. HRESULT STDMETHODCALLTYPE
  1507. CJobManagerExternal::GetJobInternal(
  1508. /* [in] */ REFGUID jobID,
  1509. /* [out] */ IBackgroundCopyJob **ppJob)
  1510. {
  1511. CLockedJobManagerReadPointer LockedJobManager(m_pJobManager);
  1512. LogPublicApiBegin( "jobID %!guid!", &jobID );
  1513. HRESULT Hr = LockedJobManager.ValidateAccess();
  1514. if (SUCCEEDED( Hr ) )
  1515. {
  1516. Hr = BG_E_NOT_FOUND;
  1517. *ppJob = NULL;
  1518. CJob *pJob = NULL;
  1519. Hr = LockedJobManager->GetJob( jobID, &pJob );
  1520. if (SUCCEEDED(Hr))
  1521. {
  1522. *ppJob = pJob->GetExternalInterface();
  1523. (*ppJob)->AddRef();
  1524. Hr = S_OK;
  1525. }
  1526. }
  1527. LogPublicApiEnd( "jobID %!guid!, pJob %p", &jobID, *ppJob );
  1528. return Hr;
  1529. }
  1530. HRESULT STDMETHODCALLTYPE
  1531. CJobManagerExternal::EnumJobsInternal(
  1532. /* [in] */ DWORD dwFlags,
  1533. /* [out] */ IEnumBackgroundCopyJobs **ppEnum)
  1534. {
  1535. HRESULT Hr = S_OK;
  1536. CLockedJobManagerReadPointer LockedJobManager(m_pJobManager );
  1537. LogPublicApiBegin( "dwFlags %u, ppEnum %p", dwFlags, ppEnum );
  1538. *ppEnum = NULL;
  1539. CEnumJobs *pEnum = NULL;
  1540. try
  1541. {
  1542. THROW_HRESULT( LockedJobManager.ValidateAccess() );
  1543. if ( dwFlags & ~(BG_JOB_ENUM_ALL_USERS) )
  1544. {
  1545. throw ComError(E_NOTIMPL);
  1546. }
  1547. bool bHideJobs = !( dwFlags & BG_JOB_ENUM_ALL_USERS );
  1548. if (!bHideJobs)
  1549. THROW_HRESULT( DenyNonAdminAccess() );
  1550. SidHandle sid;
  1551. if (bHideJobs)
  1552. {
  1553. sid = GetThreadClientSid();
  1554. }
  1555. pEnum = new CEnumJobs;
  1556. for (CJobList::iterator iter = LockedJobManager->m_OnlineJobs.begin();
  1557. iter != LockedJobManager->m_OnlineJobs.end();
  1558. ++iter)
  1559. {
  1560. if ( bHideJobs )
  1561. {
  1562. if (!iter->IsOwner( sid ))
  1563. {
  1564. continue;
  1565. }
  1566. }
  1567. pEnum->Add( iter->GetExternalInterface() );
  1568. }
  1569. for (CJobList::iterator iter = LockedJobManager->m_OfflineJobs.begin();
  1570. iter != LockedJobManager->m_OfflineJobs.end();
  1571. ++iter)
  1572. {
  1573. if ( bHideJobs )
  1574. {
  1575. if (!iter->IsOwner( sid ))
  1576. {
  1577. continue;
  1578. }
  1579. }
  1580. pEnum->Add( iter->GetExternalInterface() );
  1581. }
  1582. *ppEnum = pEnum;
  1583. }
  1584. catch( ComError exception )
  1585. {
  1586. Hr = exception.Error();
  1587. SafeRelease( pEnum );
  1588. }
  1589. LogPublicApiEnd( "dwFlags %u, ppEnum %p(%p)", dwFlags, ppEnum, *ppEnum );
  1590. return Hr;
  1591. }
  1592. STDMETHODIMP
  1593. CJobManagerExternal::GetErrorDescriptionInternal(
  1594. HRESULT hResult,
  1595. DWORD LanguageId,
  1596. LPWSTR *pErrorDescription
  1597. )
  1598. {
  1599. HRESULT Hr = S_OK;
  1600. LogPublicApiBegin( "hResult %!winerr!, LanguageId %u, pErrorDescription %p", hResult, LanguageId, pErrorDescription );
  1601. *pErrorDescription = NULL;
  1602. Hr = g_Manager->CheckClientAccess();
  1603. if (SUCCEEDED(Hr))
  1604. {
  1605. Hr = g_Manager->GetErrorDescription( hResult, LanguageId, pErrorDescription );
  1606. }
  1607. LogPublicApiEnd( "hResult %!winerr!, LanguageId %u, pErrorDescription %p(%S)", hResult, LanguageId, pErrorDescription,
  1608. (*pErrorDescription ? *pErrorDescription : L"NULL") );
  1609. return Hr;
  1610. }
  1611. STDMETHODIMP
  1612. CJobManagerExternal::GetBitsDllPath(
  1613. LPWSTR *pVal
  1614. )
  1615. {
  1616. HRESULT Hr = S_OK;
  1617. *pVal = NULL;
  1618. Hr = g_Manager->CheckClientAccess();
  1619. if (SUCCEEDED(Hr))
  1620. {
  1621. *pVal = (LPWSTR) CoTaskMemAlloc((1+MAX_PATH)*sizeof(wchar_t));
  1622. if (*pVal == NULL)
  1623. {
  1624. Hr = E_OUTOFMEMORY;
  1625. }
  1626. else
  1627. {
  1628. if (!GetModuleFileName( g_hInstance, *pVal, 1+MAX_PATH))
  1629. {
  1630. Hr = HRESULT_FROM_WIN32( GetLastError() );
  1631. CoTaskMemFree( *pVal );
  1632. }
  1633. }
  1634. }
  1635. LogPublicApiEnd( "hResult %!winerr!, path (%S)", Hr, (*pVal ? *pVal : L"NULL") );
  1636. return Hr;
  1637. }
  1638. HRESULT
  1639. CJobManager::CheckClientAccess()
  1640. {
  1641. try
  1642. {
  1643. CNestedImpersonation imp;
  1644. if (imp.CopySid() == g_GlobalInfo->m_AnonymousSid)
  1645. {
  1646. throw ComError( E_ACCESSDENIED );
  1647. }
  1648. HRESULT hr = IsRemoteUser();
  1649. if (FAILED(hr) )
  1650. throw ComError( hr );
  1651. if ( S_OK == hr )
  1652. throw ComError( BG_E_REMOTE_NOT_SUPPORTED );
  1653. if (IsTokenRestricted( imp.QueryToken()))
  1654. {
  1655. throw ComError( E_ACCESSDENIED );
  1656. }
  1657. return S_OK;
  1658. }
  1659. catch ( ComError err )
  1660. {
  1661. return err.Error();
  1662. }
  1663. }
  1664. CDeviceNotificationController::~CDeviceNotificationController()
  1665. {
  1666. for( CHandleToNotify::iterator iter = m_HandleToNotify.begin(); iter != m_HandleToNotify.end(); iter++ )
  1667. {
  1668. UnregisterDeviceNotification( iter->second->m_hDeviceNotify );
  1669. delete iter->second;
  1670. }
  1671. }
  1672. void
  1673. CDeviceNotificationController::DeleteNotify(
  1674. CDriveNotify *pNotify
  1675. )
  1676. {
  1677. RTL_VERIFY( m_HandleToNotify.erase( pNotify->m_hDeviceNotify ) );
  1678. RTL_VERIFY( m_CanonicalVolumeToNotify.erase( pNotify->m_CanonicalName ) );
  1679. UnregisterDeviceNotification( pNotify->m_hDeviceNotify );
  1680. ASSERT( NULL != pNotify );
  1681. delete pNotify;
  1682. }
  1683. DWORD
  1684. CDeviceNotificationController::OnDeviceEvent(
  1685. DWORD dwEventType,
  1686. LPVOID lpEventData )
  1687. {
  1688. switch( dwEventType )
  1689. {
  1690. case DBT_CUSTOMEVENT:
  1691. {
  1692. PDEV_BROADCAST_HANDLE pdev = (PDEV_BROADCAST_HANDLE)lpEventData;
  1693. LogInfo( "Received DBT_CUSTOMEVENT(%!guid!) event for handle %p",
  1694. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1695. CHandleToNotify::iterator iter = m_HandleToNotify.find( pdev->dbch_hdevnotify );
  1696. if ( m_HandleToNotify.end() == iter )
  1697. {
  1698. LogWarning("DBT_CUSTOMEVENT(%!guid!) received for unknown notify handle %p",
  1699. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1700. return NO_ERROR;
  1701. }
  1702. CDriveNotify *pNotify = iter->second;
  1703. ASSERT( pNotify );
  1704. if ( ( GUID_IO_VOLUME_LOCK == pdev->dbch_eventguid ) ||
  1705. ( GUID_IO_VOLUME_DISMOUNT == pdev->dbch_eventguid ) )
  1706. {
  1707. ++pNotify->m_LockCount;
  1708. LogInfo( "GUID_IO_VOLUME_LOCK or _VOLUME_DISMOUNT received for drive %ls, new locks %d / %d",
  1709. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_RemoveCount, pNotify->m_LockCount );
  1710. if ((0 == pNotify->m_RemoveCount) && (1 == pNotify->m_LockCount))
  1711. OnDeviceLock( pNotify->m_CanonicalName );
  1712. return NO_ERROR;
  1713. }
  1714. else if ( ( GUID_IO_VOLUME_UNLOCK == pdev->dbch_eventguid ) ||
  1715. ( GUID_IO_VOLUME_LOCK_FAILED == pdev->dbch_eventguid ) ||
  1716. ( GUID_IO_VOLUME_MOUNT == pdev->dbch_eventguid ) ||
  1717. ( GUID_IO_VOLUME_DISMOUNT_FAILED == pdev->dbch_eventguid ) )
  1718. {
  1719. --pNotify->m_LockCount;
  1720. LogInfo( "GUID_IO_VOLUME_UNLOCK, _LOCK_FAILED or _DISMOUNT_FAILED received for drive %ls, new locks %d / %d",
  1721. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_RemoveCount, pNotify->m_LockCount);
  1722. if ((0 == pNotify->m_RemoveCount) && (0 == pNotify->m_LockCount))
  1723. OnDeviceUnlock( pNotify->m_CanonicalName );
  1724. return NO_ERROR;
  1725. }
  1726. else
  1727. {
  1728. LogWarning("Received unknown DBT_CUSTOMEVENT(%!guid!) event for handle %p",
  1729. &pdev->dbch_eventguid, pdev->dbch_hdevnotify );
  1730. return NO_ERROR;
  1731. }
  1732. }
  1733. case DBT_DEVICEQUERYREMOVE:
  1734. case DBT_DEVICEQUERYREMOVEFAILED:
  1735. case DBT_DEVICEREMOVEPENDING:
  1736. case DBT_DEVICEREMOVECOMPLETE:
  1737. {
  1738. PDEV_BROADCAST_HANDLE pdev = (PDEV_BROADCAST_HANDLE)lpEventData;
  1739. LogInfo( "Received devicechange event %u received for handle %p", dwEventType, pdev->dbch_hdevnotify );
  1740. CHandleToNotify::iterator iter = m_HandleToNotify.find( pdev->dbch_hdevnotify );
  1741. if ( m_HandleToNotify.end() == iter )
  1742. {
  1743. LogWarning("device change event received for unknown notify handle %p", pdev->dbch_hdevnotify );
  1744. return NO_ERROR;
  1745. }
  1746. CDriveNotify *pNotify = iter->second;
  1747. ASSERT( pNotify );
  1748. switch( dwEventType )
  1749. {
  1750. case DBT_DEVICEQUERYREMOVE:
  1751. ++pNotify->m_RemoveCount;
  1752. LogInfo( "DBT_DEVICEQUERYREMOVE received for drive %ls, new locks %d / %d",
  1753. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_RemoveCount, pNotify->m_LockCount );
  1754. if ((1 == pNotify->m_RemoveCount) && (0 == pNotify->m_LockCount))
  1755. OnDeviceLock( pNotify->m_CanonicalName );
  1756. return NO_ERROR;
  1757. case DBT_DEVICEQUERYREMOVEFAILED:
  1758. if (pNotify->m_RemoveCount > 0)
  1759. {
  1760. --pNotify->m_RemoveCount;
  1761. LogInfo( "DBT_DEVICEQUERYREMOVEFAILED received for drive %ls, new locks %d / %d",
  1762. (const WCHAR*)pNotify->m_CanonicalName, pNotify->m_RemoveCount, pNotify->m_LockCount );
  1763. if ((0 == pNotify->m_RemoveCount) && (0 == pNotify->m_LockCount))
  1764. OnDeviceUnlock( pNotify->m_CanonicalName );
  1765. }
  1766. else
  1767. {
  1768. LogWarning("DBT_DEVICEQUERYREMOVEFAILED received for drive %ls, duplicate notification; ignoring",
  1769. (const WCHAR*)pNotify->m_CanonicalName );
  1770. }
  1771. return NO_ERROR;
  1772. case DBT_DEVICEREMOVECOMPLETE:
  1773. case DBT_DEVICEREMOVEPENDING:
  1774. LogInfo( "DBT_DEVICEREMOVECOMPLETE or DBT_DEVICEREMOVEPENDING received for drive %ls, failing jobs",
  1775. ( const WCHAR*) pNotify->m_CanonicalName );
  1776. OnDismount( pNotify->m_CanonicalName );
  1777. DeleteNotify( pNotify );
  1778. return NO_ERROR;
  1779. default:
  1780. ASSERT(0);
  1781. return NO_ERROR;
  1782. }
  1783. }
  1784. default:
  1785. LogInfo( "Unknown device event %u", dwEventType );
  1786. return NO_ERROR;
  1787. }
  1788. }
  1789. HRESULT
  1790. CDeviceNotificationController::IsVolumeLocked(
  1791. const WCHAR *CanonicalVolume
  1792. )
  1793. {
  1794. HRESULT Hr = S_OK;
  1795. try
  1796. {
  1797. CCanonicalVolumeToNotify::iterator iter = m_CanonicalVolumeToNotify.find( CanonicalVolume );
  1798. if ( m_CanonicalVolumeToNotify.end() == iter )
  1799. {
  1800. LogInfo( "Canonical volume %ls has not been registered, register now\n", CanonicalVolume );
  1801. //
  1802. // Register for device-lock notification. If it fails, it is of small consequence:
  1803. // if CHKDSK and BITS try to access a file simultanteously, the job would go into
  1804. // ERROR state instead of TRANSIENT_ERROR state.
  1805. //
  1806. Hr = RegisterNotification( CanonicalVolume );
  1807. if (FAILED(Hr))
  1808. {
  1809. LogWarning("unable to register: 0x%x", Hr);
  1810. }
  1811. Hr = S_OK;
  1812. }
  1813. else
  1814. {
  1815. CDriveNotify *pNotify = iter->second;
  1816. if ((pNotify->m_LockCount > 0) || (pNotify->m_RemoveCount > 0))
  1817. throw ComError( BG_E_DESTINATION_LOCKED );
  1818. }
  1819. }
  1820. catch(ComError Error)
  1821. {
  1822. Hr = Error.Error();
  1823. }
  1824. return Hr;
  1825. }
  1826. HRESULT
  1827. CDeviceNotificationController::RegisterNotification(
  1828. const WCHAR *CanonicalVolume
  1829. )
  1830. {
  1831. HRESULT Hr = S_OK;
  1832. HANDLE hDriveHandle = INVALID_HANDLE_VALUE;
  1833. HDEVNOTIFY hNotify = NULL;
  1834. CDriveNotify *pNotify = NULL;
  1835. StringHandle wCanonicalVolume;
  1836. try
  1837. {
  1838. wCanonicalVolume = CanonicalVolume;
  1839. CCanonicalVolumeToNotify::iterator iter = m_CanonicalVolumeToNotify.find( wCanonicalVolume );
  1840. if ( m_CanonicalVolumeToNotify.end() != iter )
  1841. {
  1842. LogInfo( "Canonical volume %ls has already been registered, nothing to do.", CanonicalVolume );
  1843. return S_OK;
  1844. }
  1845. // Need to remove the trailing / from the volume name.
  1846. ASSERTMSG( "Canonical name has an unexpected size", wCanonicalVolume.Size() );
  1847. CAutoString TempVolumePath = CAutoString( CopyString( wCanonicalVolume ));
  1848. ASSERT( wCanonicalVolume.Size() > 0 );
  1849. TempVolumePath.get()[ wCanonicalVolume.Size() - 1 ] = L'\0';
  1850. hDriveHandle =
  1851. CreateFile( TempVolumePath.get(),
  1852. GENERIC_READ,
  1853. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1854. NULL,
  1855. OPEN_EXISTING,
  1856. 0,
  1857. NULL );
  1858. if ( INVALID_HANDLE_VALUE == hDriveHandle )
  1859. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  1860. DEV_BROADCAST_HANDLE DbtHandle;
  1861. memset( &DbtHandle, 0, sizeof(DbtHandle) );
  1862. DbtHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  1863. DbtHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  1864. DbtHandle.dbch_handle = hDriveHandle;
  1865. hNotify =
  1866. RegisterDeviceNotification( (HANDLE) ghServiceHandle,
  1867. &DbtHandle,
  1868. DEVICE_NOTIFY_SERVICE_HANDLE );
  1869. if ( !hNotify )
  1870. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  1871. CloseHandle( hDriveHandle );
  1872. hDriveHandle = NULL;
  1873. pNotify = new CDriveNotify( hNotify, CanonicalVolume );
  1874. if ( !pNotify )
  1875. throw ComError( E_OUTOFMEMORY );
  1876. RTL_VERIFY( m_CanonicalVolumeToNotify.insert( CCanonicalVolumeToNotify::value_type( wCanonicalVolume, pNotify ) ).second );
  1877. RTL_VERIFY( m_HandleToNotify.insert( CHandleToNotify::value_type( hNotify, pNotify ) ).second );
  1878. }
  1879. catch(ComError Error)
  1880. {
  1881. Hr = Error.Error();
  1882. }
  1883. if ( FAILED(Hr) )
  1884. {
  1885. if ( hNotify )
  1886. UnregisterDeviceNotification( hNotify );
  1887. if ( hDriveHandle != INVALID_HANDLE_VALUE )
  1888. CloseHandle( hDriveHandle );
  1889. if ( pNotify )
  1890. {
  1891. m_CanonicalVolumeToNotify.erase( wCanonicalVolume );
  1892. m_HandleToNotify.erase( hNotify );
  1893. delete pNotify;
  1894. }
  1895. }
  1896. return Hr;
  1897. }
  1898. HRESULT
  1899. SessionLogonCallback(
  1900. DWORD SessionId
  1901. )
  1902. {
  1903. if (!PostThreadMessage(g_dwBackgroundThreadId, WM_SESSION_CHANGE, true, SessionId))
  1904. {
  1905. return E_FAIL;
  1906. }
  1907. return S_OK;
  1908. }
  1909. HRESULT
  1910. SessionLogoffCallback(
  1911. DWORD SessionId
  1912. )
  1913. {
  1914. if (!PostThreadMessage(g_dwBackgroundThreadId, WM_SESSION_CHANGE, false, SessionId))
  1915. {
  1916. return E_FAIL;
  1917. }
  1918. return S_OK;
  1919. }
  1920. DWORD
  1921. DeviceEventCallback(
  1922. DWORD dwEventType,
  1923. LPVOID lpEventData
  1924. )
  1925. {
  1926. return g_Manager->OnDeviceEvent( dwEventType, lpEventData );
  1927. }
  1928. bool
  1929. CJobManager::OnIdentify(
  1930. IN IVssCreateWriterMetadata *pMetadata
  1931. )
  1932. /*
  1933. This is called by CBitsVssWriter::OnIdentify, used by backup programs.
  1934. Our implementation simply excludes the metadata and job temporary files
  1935. from the backup set.
  1936. */
  1937. {
  1938. // Exclude the BITS metadata files.
  1939. //
  1940. THROW_HRESULT( pMetadata->AddExcludeFiles( g_GlobalInfo->m_QmgrDirectory, L"*", FALSE ));
  1941. // Enumerate and exclude every temp file created by BITS.
  1942. //
  1943. for (CJobList::iterator iter = m_OnlineJobs.begin(); iter != m_OnlineJobs.end(); ++iter)
  1944. {
  1945. THROW_HRESULT( iter->ExcludeFilesFromBackup( pMetadata ));
  1946. }
  1947. for (CJobList::iterator iter = m_OfflineJobs.begin(); iter != m_OfflineJobs.end(); ++iter)
  1948. {
  1949. THROW_HRESULT( iter->ExcludeFilesFromBackup( pMetadata ));
  1950. }
  1951. return TRUE;
  1952. }
  1953. bool STDMETHODCALLTYPE
  1954. CBitsVssWriter::OnIdentify(
  1955. IN IVssCreateWriterMetadata *pMetadata
  1956. )
  1957. {
  1958. LogInfo("called");
  1959. ASSERT( g_Manager );
  1960. try
  1961. {
  1962. // This increments the global call count, preventing the service from exiting
  1963. // until the call completes.
  1964. //
  1965. DispatchedCall c;
  1966. HoldReaderLock lock ( g_Manager->m_TaskScheduler );
  1967. if (g_ServiceState != MANAGER_ACTIVE)
  1968. {
  1969. // Since we are shutting down or starting up, a second attempt will probably succeed.
  1970. //
  1971. SetWriterFailure( VSS_E_WRITERERROR_RETRYABLE );
  1972. return false;
  1973. }
  1974. return g_Manager->OnIdentify( pMetadata );
  1975. }
  1976. catch ( ComError err )
  1977. {
  1978. LogError("exception 0x%x raised at line %d", err.m_error, err.m_line);
  1979. SetWriterFailure( VSS_E_WRITERERROR_OUTOFRESOURCES );
  1980. return false;
  1981. }
  1982. }
  1983. void
  1984. AddExcludeFile(
  1985. IN IVssCreateWriterMetadata *pMetadata,
  1986. LPCWSTR FileName
  1987. )
  1988. /*
  1989. Adds a file to the backup exclusion list. <FileName> must be a complete path.
  1990. */
  1991. {
  1992. //
  1993. // Convert the filename into its long-name equivalent.
  1994. //
  1995. #define LONG_NAME_BUFFER_CHARS (1+MAX_PATH)
  1996. CAutoStringW LongName(new WCHAR[LONG_NAME_BUFFER_CHARS]);
  1997. DWORD s;
  1998. s = GetLongPathName( FileName, LongName.get(), LONG_NAME_BUFFER_CHARS );
  1999. if (s == 0)
  2000. {
  2001. ThrowLastError();
  2002. }
  2003. if (s > LONG_NAME_BUFFER_CHARS)
  2004. {
  2005. THROW_HRESULT( HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ));
  2006. }
  2007. // Break the file into path and file components, as required by the snapshot API.
  2008. //
  2009. StringHandle PathSpec;
  2010. StringHandle FileSpec;
  2011. PathSpec = BITSCrackFileName( LongName.get(), FileSpec );
  2012. // Actually exclude the file.
  2013. //
  2014. HRESULT hr;
  2015. if (FAILED(hr=pMetadata->AddExcludeFiles( PathSpec,
  2016. FileSpec,
  2017. FALSE
  2018. )))
  2019. {
  2020. LogError("unable to exclude file '%S', hr=%x", FileName, hr);
  2021. THROW_HRESULT( hr );
  2022. }
  2023. }