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.

579 lines
14 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. cleanup.cpp
  5. Abstract:
  6. This file implements the BITS server extensions cleanup worker
  7. --*/
  8. #include "precomp.h"
  9. const UINT64 NanoSec100PerSec = 10000000; //no of 100 nanosecs per second
  10. inline UINT64 FILETIMEToUINT64( const FILETIME & FileTime )
  11. {
  12. ULARGE_INTEGER LargeInteger;
  13. LargeInteger.HighPart = FileTime.dwHighDateTime;
  14. LargeInteger.LowPart = FileTime.dwLowDateTime;
  15. return LargeInteger.QuadPart;
  16. };
  17. class CleanupWorker
  18. {
  19. public:
  20. CleanupWorker( HWND hwnd, const WCHAR* Path, const WCHAR *WorkItemName,
  21. const WCHAR *GuidString );
  22. ~CleanupWorker();
  23. void DoIt();
  24. private:
  25. HWND m_hwnd;
  26. const WCHAR * m_Path;
  27. const WCHAR * m_WorkItemName;
  28. const WCHAR * m_GuidString;
  29. const WCHAR * m_ADSIPath;
  30. IADs * m_VDir;
  31. BSTR m_VDirPath;
  32. BSTR m_SessionDirectory;
  33. BSTR m_UNCUsername;
  34. BSTR m_UNCPassword;
  35. UINT64 m_CleanupThreshold;
  36. VARIANT m_vt;
  37. HANDLE m_FindHandle;
  38. HANDLE m_UserToken;
  39. WCHAR * BuildPath( const WCHAR * Dir, const WCHAR *Sub );
  40. BSTR GetBSTRProp( BSTR PropName );
  41. void LogonIfRequired();
  42. void PollKill();
  43. void RemoveConnectionsFromTree(
  44. const WCHAR * DirectoryPath,
  45. bool IsConnectionDirectory );
  46. void RemoveConnection( const WCHAR * ConnectionDirectory );
  47. };
  48. CleanupWorker::CleanupWorker(
  49. HWND hwnd,
  50. const WCHAR* Path,
  51. const WCHAR* WorkItemName,
  52. const WCHAR* GuidString ) :
  53. m_hwnd( hwnd ),
  54. m_Path( Path ),
  55. m_WorkItemName( WorkItemName ),
  56. m_GuidString( GuidString ),
  57. m_ADSIPath( NULL ),
  58. m_VDir( NULL ),
  59. m_VDirPath( NULL ),
  60. m_SessionDirectory( NULL ),
  61. m_CleanupThreshold( 0 ),
  62. m_UNCUsername( NULL ),
  63. m_UNCPassword( NULL ),
  64. m_UserToken( NULL )
  65. {
  66. VariantInit( &m_vt );
  67. }
  68. CleanupWorker::~CleanupWorker()
  69. {
  70. if ( m_UserToken )
  71. {
  72. SetThreadToken( NULL, NULL );
  73. CloseHandle( m_UserToken );
  74. }
  75. delete m_ADSIPath;
  76. SysFreeString( m_VDirPath );
  77. SysFreeString( m_SessionDirectory );
  78. SysFreeString( m_UNCUsername );
  79. SysFreeString( m_UNCPassword );
  80. }
  81. void
  82. CleanupWorker::RemoveConnection( const WCHAR * ConnectionDirectory )
  83. {
  84. UINT64 LatestTime = 0;
  85. HANDLE FindHandle = INVALID_HANDLE_VALUE;
  86. WCHAR *SearchPath = NULL;
  87. WCHAR *FileName = NULL;
  88. try
  89. {
  90. SearchPath = BuildPath( ConnectionDirectory, L"*" );
  91. WIN32_FIND_DATA FindData;
  92. FindHandle =
  93. FindFirstFile(
  94. SearchPath,
  95. &FindData
  96. );
  97. if ( INVALID_HANDLE_VALUE == FindHandle )
  98. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  99. bool FoundFile = false;
  100. do
  101. {
  102. if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  103. continue;
  104. FoundFile = true;
  105. UINT64 CreationTime = FILETIMEToUINT64( FindData.ftCreationTime );
  106. UINT64 LastWriteTime = FILETIMEToUINT64( FindData.ftLastWriteTime );
  107. LatestTime = max( LatestTime, max( CreationTime, LastWriteTime ) );
  108. }
  109. while ( FindNextFile( FindHandle, &FindData ) );
  110. FindClose( FindHandle );
  111. FindHandle = INVALID_HANDLE_VALUE;
  112. FILETIME ftCurrentTime;
  113. GetSystemTimeAsFileTime( &ftCurrentTime );
  114. UINT64 CurrentTime = FILETIMEToUINT64( ftCurrentTime );
  115. if ( FoundFile &&
  116. ( 0xFFFFFFFF - LatestTime > m_CleanupThreshold ) &&
  117. ( LatestTime + m_CleanupThreshold < CurrentTime ) )
  118. {
  119. FindHandle =
  120. FindFirstFile(
  121. SearchPath,
  122. &FindData
  123. );
  124. if ( INVALID_HANDLE_VALUE == FindHandle )
  125. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  126. do
  127. {
  128. if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  129. continue;
  130. FileName = BuildPath( ConnectionDirectory, FindData.cFileName );
  131. DeleteFile( FileName );
  132. delete FileName;
  133. FileName = NULL;
  134. }
  135. while ( FindNextFile( FindHandle, &FindData ) );
  136. FindClose( FindHandle );
  137. FindHandle = INVALID_HANDLE_VALUE;
  138. }
  139. RemoveDirectory( ConnectionDirectory );
  140. }
  141. catch( ComError Error )
  142. {
  143. if ( INVALID_HANDLE_VALUE != FindHandle )
  144. FindClose( FindHandle );
  145. delete SearchPath;
  146. delete FileName;
  147. throw;
  148. }
  149. if ( INVALID_HANDLE_VALUE != FindHandle )
  150. FindClose( FindHandle );
  151. delete SearchPath;
  152. delete FileName;
  153. }
  154. void
  155. CleanupWorker::RemoveConnectionsFromTree(
  156. const WCHAR * DirectoryPath,
  157. bool IsConnectionDirectory )
  158. {
  159. WCHAR *ConnectionDir = NULL;
  160. HANDLE FindHandle = INVALID_HANDLE_VALUE;
  161. WCHAR *SearchString = NULL;
  162. WCHAR *NextSearchPath = NULL;
  163. try
  164. {
  165. // Look for BITS-Sessions directory in connection tree
  166. SearchString = BuildPath( DirectoryPath, L"*" );
  167. WIN32_FIND_DATA FindData;
  168. FindHandle =
  169. FindFirstFile(
  170. SearchString,
  171. &FindData );
  172. if ( INVALID_HANDLE_VALUE == FindHandle )
  173. return;
  174. do
  175. {
  176. PollKill();
  177. if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
  178. continue;
  179. if ( _wcsicmp( L".", FindData.cFileName ) == 0 )
  180. continue;
  181. if ( _wcsicmp( L"..", FindData.cFileName ) == 0 )
  182. continue;
  183. if ( IsConnectionDirectory )
  184. {
  185. GUID Guid;
  186. if (SUCCEEDED( IIDFromString( FindData.cFileName, &Guid ) ) )
  187. {
  188. NextSearchPath = BuildPath( DirectoryPath, FindData.cFileName );
  189. RemoveConnection( NextSearchPath );
  190. delete NextSearchPath;
  191. NextSearchPath = NULL;
  192. }
  193. }
  194. else
  195. {
  196. if ( _wcsicmp( m_SessionDirectory, FindData.cFileName ) == 0 )
  197. {
  198. NextSearchPath = BuildPath( DirectoryPath, FindData.cFileName );
  199. RemoveConnectionsFromTree( NextSearchPath, true );
  200. // Mark this as the connection directory so it
  201. // will be closed after the search handles are closed.
  202. ConnectionDir = NextSearchPath;
  203. NextSearchPath = NULL;
  204. }
  205. else
  206. {
  207. // just another directory to recurse into
  208. NextSearchPath = BuildPath( DirectoryPath, FindData.cFileName );
  209. RemoveConnectionsFromTree( NextSearchPath, false );
  210. delete NextSearchPath;
  211. NextSearchPath = NULL;
  212. }
  213. }
  214. }
  215. while( FindNextFile( FindHandle, &FindData ) );
  216. if ( INVALID_HANDLE_VALUE != FindHandle )
  217. FindClose( FindHandle );
  218. delete SearchString;
  219. delete NextSearchPath;
  220. if ( ConnectionDir )
  221. {
  222. // The attempt to remove the directory will fail if
  223. // the directory still has valid connections
  224. RemoveDirectory( ConnectionDir );
  225. delete ConnectionDir;
  226. }
  227. }
  228. catch( ComError Error )
  229. {
  230. if ( INVALID_HANDLE_VALUE != FindHandle )
  231. FindClose( FindHandle );
  232. delete SearchString;
  233. delete NextSearchPath;
  234. delete ConnectionDir;
  235. throw;
  236. }
  237. }
  238. void
  239. CleanupWorker::PollKill()
  240. {
  241. MSG msg;
  242. while( PeekMessage(
  243. &msg,
  244. m_hwnd,
  245. 0,
  246. 0,
  247. PM_REMOVE ) )
  248. {
  249. if ( WM_QUIT == msg.message )
  250. throw ComError( (HRESULT)msg.wParam );
  251. TranslateMessage( &msg );
  252. DispatchMessage( &msg );
  253. }
  254. }
  255. WCHAR *
  256. CleanupWorker::BuildPath(
  257. const WCHAR *Dir,
  258. const WCHAR *Sub )
  259. {
  260. SIZE_T DirLen = wcslen( Dir );
  261. SIZE_T SubLen = wcslen( Sub );
  262. SIZE_T MaxStringSize = DirLen + SubLen + 2; // one slash, one terminator
  263. WCHAR *RetString = new WCHAR[ MaxStringSize ];
  264. if ( !RetString )
  265. throw ComError( E_OUTOFMEMORY );
  266. memcpy( RetString, Dir, sizeof(WCHAR) * (DirLen + 1) );
  267. WCHAR *p = RetString + DirLen;
  268. if ( p != RetString && *(p - 1) != L'\\' && *(p - 1) != L'/' )
  269. *p++ = L'\\';
  270. memcpy( p, Sub, sizeof(WCHAR) * ( SubLen + 1 ) );
  271. return RetString;
  272. }
  273. BSTR
  274. CleanupWorker::GetBSTRProp( BSTR PropName )
  275. {
  276. BSTR Retval;
  277. THROW_COMERROR( m_VDir->Get( PropName, &m_vt ) );
  278. THROW_COMERROR( VariantChangeType( &m_vt, &m_vt, 0, VT_BSTR ) );
  279. Retval = m_vt.bstrVal;
  280. m_vt.bstrVal = NULL;
  281. VariantClear( &m_vt );
  282. return Retval;
  283. }
  284. void
  285. CleanupWorker::LogonIfRequired()
  286. {
  287. // Don't logon if the path isn't a UNC path
  288. // or the user name is blank
  289. if ( ((WCHAR*)m_VDirPath)[0] != L'\\' ||
  290. ((WCHAR*)m_VDirPath)[1] != L'\\' ||
  291. *(WCHAR*)m_UNCUsername == L'\0' )
  292. return; // no logon required
  293. // crack the user name into a user and domain
  294. WCHAR *UserName = (WCHAR*)m_UNCUsername;
  295. WCHAR *DomainName = NULL;
  296. WCHAR *p = UserName;
  297. while(*p != L'\0')
  298. {
  299. if(*p == L'\\')
  300. {
  301. *p = L'\0';
  302. p++;
  303. //
  304. // first part is domain
  305. // second is user.
  306. //
  307. DomainName = UserName;
  308. UserName = p;
  309. break;
  310. }
  311. p++;
  312. }
  313. if ( !LogonUser(
  314. UserName,
  315. DomainName,
  316. (WCHAR*)m_UNCPassword,
  317. LOGON32_LOGON_BATCH,
  318. LOGON32_PROVIDER_DEFAULT,
  319. &m_UserToken ) )
  320. {
  321. if ( GetLastError() == ERROR_LOGON_TYPE_NOT_GRANTED )
  322. {
  323. if ( !LogonUser(
  324. UserName,
  325. DomainName,
  326. (WCHAR*)m_UNCPassword,
  327. LOGON32_LOGON_INTERACTIVE,
  328. LOGON32_PROVIDER_DEFAULT,
  329. &m_UserToken ) )
  330. {
  331. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  332. }
  333. }
  334. }
  335. if ( !ImpersonateLoggedOnUser( m_UserToken ) )
  336. throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );
  337. }
  338. void
  339. CleanupWorker::DoIt()
  340. {
  341. m_ADSIPath = ConvertObjectPathToADSI( m_Path );
  342. try
  343. {
  344. THROW_COMERROR( ADsGetObject( m_ADSIPath, __uuidof(*m_VDir), (void**)&m_VDir ) );
  345. if ( m_GuidString )
  346. {
  347. BSTR BSTRGuid = GetBSTRProp( (BSTR)L"BITSCleanupWorkItemKey" );
  348. int Result = wcscmp( (LPWSTR)BSTRGuid, m_GuidString );
  349. SysFreeString( BSTRGuid );
  350. if ( Result != 0 )
  351. throw ComError( E_ADS_UNKNOWN_OBJECT );
  352. }
  353. }
  354. catch( ComError Error )
  355. {
  356. if ( ( Error.m_Hr == E_ADS_UNKNOWN_OBJECT ) ||
  357. ( Error.m_Hr == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) ) ||
  358. ( Error.m_Hr == E_ADS_PROPERTY_NOT_FOUND ) )
  359. {
  360. // Somehow the virtual directory was deleted, but the
  361. // task scheduler work item wasn't. Try to delete it now.
  362. ITaskScheduler *TaskScheduler;
  363. if ( SUCCEEDED( ConnectToTaskScheduler( NULL, &TaskScheduler ) ) )
  364. {
  365. TaskScheduler->Delete( m_WorkItemName );
  366. TaskScheduler->Release();
  367. }
  368. }
  369. throw;
  370. }
  371. THROW_COMERROR( m_VDir->Get( (BSTR)L"BITSUploadEnabled", &m_vt ) );
  372. THROW_COMERROR( VariantChangeType( &m_vt, &m_vt, 0, VT_BOOL ) );
  373. if ( !m_vt.boolVal ) // Uploads arn't enabled on this directory
  374. return;
  375. THROW_COMERROR( m_VDir->Get( (BSTR)L"BITSSessionTimeout", &m_vt ) );
  376. THROW_COMERROR( VariantChangeType( &m_vt, &m_vt, 0, VT_BSTR ) );
  377. if ( L'-' == *m_vt.bstrVal )
  378. return; // do not run cleanup in this directory since cleanup has been disabled
  379. UINT64 CleanupSeconds;
  380. if ( 1 != swscanf( (WCHAR*)m_vt.bstrVal, L"%I64u", &CleanupSeconds ) )
  381. return;
  382. if ( CleanupSeconds > ( 0xFFFFFFFFFFFFFFFF / NanoSec100PerSec ) )
  383. m_CleanupThreshold = 0xFFFFFFFFFFFFFFFF; // overflow case
  384. else
  385. m_CleanupThreshold = CleanupSeconds * NanoSec100PerSec;
  386. m_VDirPath = GetBSTRProp( (BSTR)L"Path" );
  387. m_SessionDirectory = GetBSTRProp( (BSTR)L"BITSSessionDirectory" );
  388. m_UNCUsername = GetBSTRProp( (BSTR)L"UNCUserName" );
  389. m_UNCPassword = GetBSTRProp( (BSTR)L"UNCPassword" );
  390. LogonIfRequired();
  391. RemoveConnectionsFromTree( (WCHAR*)m_VDirPath, false );
  392. }
  393. void Cleanup_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpszCmdLine, int nCmdShow )
  394. {
  395. int NumArgs;
  396. LPWSTR * CommandArgs =
  397. CommandLineToArgvW(
  398. lpszCmdLine,
  399. &NumArgs );
  400. if ( !CommandArgs )
  401. return;
  402. if ( FAILED( CoInitializeEx( NULL, COINIT_MULTITHREADED ) ) )
  403. return;
  404. if ( NumArgs != 2 && NumArgs != 3 )
  405. return;
  406. LPWSTR Path = CommandArgs[0];
  407. LPWSTR WorkItemName = CommandArgs[1];
  408. LPWSTR GuidString = NumArgs == 3 ? CommandArgs[2] : NULL;
  409. try
  410. {
  411. CleanupWorker Worker( hwndStub, Path, WorkItemName, GuidString );
  412. Worker.DoIt();
  413. }
  414. catch( ComError Error )
  415. {
  416. }
  417. CoUninitialize( );
  418. GlobalFree( CommandArgs );
  419. }