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.

1496 lines
37 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. cjob.cpp
  5. Abstract :
  6. Main code file for handling jobs and files.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #if !defined(BITS_V12_ON_NT4)
  12. #include "cfile.tmh"
  13. #endif
  14. //------------------------------------------------------------------------
  15. StringHandle
  16. BITSGetVolumePathName(
  17. const WCHAR * FileName )
  18. {
  19. #if defined( BITS_V12_ON_NT4 )
  20. WCHAR VolumePath[4];
  21. if ( !(
  22. ( FileName[0] >= L'A' && FileName[0] <= L'Z') ||
  23. ( FileName[0] >= L'a' && FileName[0] <= L'z')
  24. ) ||
  25. ( FileName[1] != L':' ) ||
  26. ( FileName[2] != L'\\' ) )
  27. {
  28. LogError( "%s doesn't appear to start with a drive letter", FileName );
  29. throw ComError( HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ) );
  30. }
  31. VolumePath[0] = FileName[0];
  32. VolumePath[1] = FileName[1];
  33. VolumePath[2] = FileName[2];
  34. VolumePath[3] = '\0';
  35. return StringHandle( VolumePath );
  36. #else
  37. // Call get full path name to get the
  38. // required buffer size
  39. DWORD dwBufferLength =
  40. GetFullPathName(
  41. FileName,
  42. 0,
  43. NULL,
  44. NULL );
  45. if ( !dwBufferLength )
  46. {
  47. DWORD dwError = GetLastError();
  48. LogError( "GetFullPathName failed, error %!winerr!", dwError );
  49. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  50. }
  51. auto_ptr<WCHAR> VolumePathName( new WCHAR[ dwBufferLength + 1 ] );
  52. BOOL bResult =
  53. GetVolumePathName(
  54. FileName,
  55. VolumePathName.get(),
  56. dwBufferLength + 1 );
  57. if ( !bResult )
  58. {
  59. DWORD dwError = GetLastError();
  60. LogError( "GetVolumePathName failed, error %!winerr!", dwError );
  61. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  62. }
  63. return StringHandle( VolumePathName.get() );
  64. #endif
  65. }
  66. StringHandle
  67. BITSGetVolumeNameForVolumeMountPoint(
  68. const WCHAR * VolumeMountPoint )
  69. {
  70. #if defined( BITS_V12_ON_NT4 )
  71. return BITSGetVolumePathName( VolumeMountPoint );
  72. #else
  73. WCHAR VolumeName[50]; // 50 is listed in MSDN
  74. BOOL bResult =
  75. GetVolumeNameForVolumeMountPoint(
  76. VolumeMountPoint,
  77. VolumeName,
  78. 50 );
  79. if ( !bResult )
  80. {
  81. DWORD dwError = GetLastError();
  82. LogError( "GetVolumeNameForVolumeMountPoint failed, error %!winerr!", dwError );
  83. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  84. }
  85. return StringHandle( VolumeName );
  86. #endif
  87. }
  88. DWORD
  89. BITSGetVolumeSerialNumber(
  90. const WCHAR * VolumePath )
  91. {
  92. DWORD VolumeSerialNumber;
  93. BOOL bResult =
  94. GetVolumeInformation(
  95. VolumePath, // root directory
  96. NULL, // volume name buffer
  97. 0, // length of name buffer
  98. &VolumeSerialNumber, // volume serial number
  99. NULL, // maximum file name length
  100. NULL, // file system options
  101. NULL, // file system name buffer
  102. 0 // length of file system name buffer
  103. );
  104. if ( !bResult )
  105. {
  106. DWORD dwError = GetLastError();
  107. LogError( "GetVolumeInformation failed, error %!winerr!", dwError );
  108. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  109. }
  110. return VolumeSerialNumber;
  111. }
  112. StringHandle
  113. BITSGetTempFileName(
  114. const WCHAR *PathName,
  115. const WCHAR *Prefix,
  116. UINT Unique )
  117. {
  118. auto_ptr<WCHAR> TempBuffer( new WCHAR[MAX_PATH+1] );
  119. UINT Result =
  120. GetTempFileName(
  121. PathName,
  122. Prefix,
  123. Unique,
  124. TempBuffer.get() );
  125. if ( !Result )
  126. {
  127. DWORD dwError = GetLastError();
  128. LogError( "GetTempFileName( '%S', '%S' ) failed, error %!winerr!", PathName, Prefix, dwError );
  129. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  130. }
  131. StringHandle ReturnVal;
  132. try
  133. {
  134. ReturnVal = TempBuffer.get();
  135. }
  136. catch( ComError Error )
  137. {
  138. LogError( "Out of memory returning temp filename, deleting temp file" );
  139. DeleteFile( TempBuffer.get() );
  140. throw;
  141. }
  142. return ReturnVal;
  143. }
  144. StringHandle
  145. BITSCrackFileName(
  146. const WCHAR * RawFileName,
  147. StringHandle & ReturnFileName )
  148. {
  149. DWORD FullPathSize =
  150. GetFullPathName( RawFileName,
  151. 0,
  152. NULL,
  153. NULL );
  154. DWORD dwAllocSize = FullPathSize;
  155. if ( !FullPathSize )
  156. {
  157. DWORD dwError = GetLastError();
  158. LogError( "GetFullPathName failed, error %!winerr!", dwError );
  159. throw ComError( HRESULT_FROM_WIN32( dwError ) );
  160. }
  161. if ( FullPathSize > MAX_PATH )
  162. {
  163. // Fail large paths until the code can be cleanup up
  164. LogError( "Path larger then MAX_PATH, failing" );
  165. throw ComError( E_INVALIDARG );
  166. }
  167. auto_ptr<WCHAR> FullPath( new WCHAR[ dwAllocSize ] );
  168. WCHAR *FilePointer = NULL;
  169. FullPathSize =
  170. GetFullPathName( RawFileName,
  171. dwAllocSize,
  172. FullPath.get(),
  173. &FilePointer
  174. );
  175. if (FullPathSize == 0 ||
  176. FullPathSize > dwAllocSize)
  177. {
  178. LogError( "GetFullPathName failed " );
  179. throw ComError( E_INVALIDARG );
  180. }
  181. if ( !FilePointer ||
  182. (*FilePointer == L'\0') ||
  183. (FilePointer == FullPath.get()))
  184. {
  185. throw ComError( E_INVALIDARG );
  186. }
  187. auto_ptr<WCHAR> DirectoryName( new WCHAR[ dwAllocSize ] );
  188. auto_ptr<WCHAR> FileName( new WCHAR[ dwAllocSize ] );
  189. memcpy( DirectoryName.get(), FullPath.get(), (char*)FilePointer - (char*)FullPath.get() );
  190. (DirectoryName.get())[ ((char*)FilePointer - (char*)FullPath.get()) / sizeof(WCHAR) ] = L'\0';
  191. THROW_HRESULT( StringCchCopy( FileName.get(), dwAllocSize, FilePointer ));
  192. FilePointer = NULL;
  193. ReturnFileName = FileName.get();
  194. return StringHandle( DirectoryName.get() );
  195. }
  196. StringHandle
  197. BITSCreateTempFile(
  198. StringHandle Directory
  199. )
  200. {
  201. StringHandle TempFileName;
  202. TempFileName = BITSGetTempFileName( Directory, L"BITS", 0 ); //throw ComError
  203. //
  204. // Make sure the client can create the temp file.
  205. //
  206. HANDLE hFile;
  207. hFile = CreateFile( TempFileName,
  208. GENERIC_WRITE,
  209. 0, // no file sharing
  210. NULL, // generic security descriptor
  211. CREATE_ALWAYS,
  212. FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_HIDDEN,
  213. NULL // no template file
  214. );
  215. if (hFile == INVALID_HANDLE_VALUE)
  216. {
  217. ThrowLastError();
  218. }
  219. CloseHandle( hFile );
  220. return TempFileName;
  221. }
  222. HRESULT
  223. BITSCheckFileWritability(
  224. LPCWSTR name
  225. )
  226. {
  227. HANDLE hFile;
  228. hFile = CreateFile( name,
  229. GENERIC_WRITE,
  230. 0, // no file sharing
  231. NULL, // generic security descriptor
  232. OPEN_EXISTING,
  233. 0,
  234. NULL // no template file
  235. );
  236. if (hFile == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND)
  237. {
  238. return HRESULT_FROM_WIN32( GetLastError() );
  239. }
  240. if (hFile != INVALID_HANDLE_VALUE)
  241. {
  242. if (GetFileType( hFile ) != FILE_TYPE_DISK)
  243. {
  244. CloseHandle( hFile );
  245. return E_INVALIDARG;
  246. }
  247. CloseHandle( hFile );
  248. }
  249. return S_OK;
  250. }
  251. HRESULT
  252. BITSCheckFileReadability(
  253. LPCWSTR name
  254. )
  255. {
  256. HANDLE hFile;
  257. hFile = CreateFile( name,
  258. GENERIC_READ,
  259. FILE_SHARE_READ, // no file sharing
  260. NULL, // generic security descriptor
  261. OPEN_EXISTING,
  262. 0,
  263. NULL // no template file
  264. );
  265. if (hFile == INVALID_HANDLE_VALUE)
  266. {
  267. return HRESULT_FROM_WIN32( GetLastError() );
  268. }
  269. if (GetFileType( hFile ) != FILE_TYPE_DISK)
  270. {
  271. CloseHandle( hFile );
  272. return E_INVALIDARG;
  273. }
  274. CloseHandle( hFile );
  275. return S_OK;
  276. }
  277. CFile::CFile(
  278. CJob* Job,
  279. BG_JOB_TYPE FileType,
  280. StringHandle RemoteName,
  281. StringHandle LocalName
  282. )
  283. {
  284. m_Job = Job;
  285. m_RemoteName = RemoteName;
  286. m_LocalName = LocalName;
  287. m_BytesTransferred = 0;
  288. m_BytesTotal = BG_SIZE_UNKNOWN;
  289. m_Completed = false;
  290. if (!VerifyRemoteName( RemoteName ))
  291. {
  292. throw ComError( E_INVALIDARG );
  293. }
  294. HRESULT hr = VerifyLocalName( LocalName, FileType );
  295. if (FAILED(hr))
  296. {
  297. throw ComError( hr );
  298. }
  299. }
  300. // private constructor used during unserialization
  301. // It initializes only the transient data.
  302. CFile::CFile(
  303. CJob* Job
  304. )
  305. {
  306. m_Job = Job;
  307. m_BytesTotal = 0;
  308. m_BytesTransferred = 0;
  309. m_Completed = false;
  310. }
  311. CFile::~CFile()
  312. {
  313. }
  314. //----------------------------------------------
  315. CFileExternal *
  316. CFile::CreateExternalInterface()
  317. {
  318. return new CFileExternal( this, m_Job->GetExternalInterface() );
  319. }
  320. HRESULT
  321. CFile::GetLocalName(
  322. LPWSTR *pVal
  323. ) const
  324. {
  325. *pVal = MidlCopyString( m_LocalName );
  326. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  327. }
  328. HRESULT
  329. CFile::GetRemoteName(
  330. LPWSTR *pVal
  331. ) const
  332. {
  333. *pVal = MidlCopyString( m_RemoteName );
  334. return (*pVal) ? S_OK : E_OUTOFMEMORY;
  335. }
  336. void
  337. CFile::GetProgress(
  338. BG_FILE_PROGRESS * s
  339. ) const
  340. {
  341. s->BytesTransferred = m_BytesTransferred;
  342. s->BytesTotal = m_BytesTotal;
  343. s->Completed = m_Completed;
  344. }
  345. HRESULT
  346. CFile::Serialize(
  347. HANDLE hFile
  348. )
  349. {
  350. //
  351. // If this function changes, be sure that the metadata extension
  352. // constants are adequate.
  353. //
  354. // not needed for download jobs, and serializing it would be incompatible
  355. // with BITS 1.0.
  356. //
  357. if (m_Job->GetType() != BG_JOB_TYPE_DOWNLOAD)
  358. {
  359. SafeWriteFile( hFile, m_LocalFileTime );
  360. }
  361. SafeWriteStringHandle( hFile, m_LocalName );
  362. SafeWriteStringHandle( hFile, m_RemoteName );
  363. SafeWriteStringHandle( hFile, m_TemporaryName );
  364. SafeWriteFile( hFile, m_BytesTransferred );
  365. SafeWriteFile( hFile, m_BytesTotal );
  366. SafeWriteFile( hFile, m_Completed );
  367. // Drive info
  368. SafeWriteStringHandle( hFile, m_VolumePath );
  369. SafeWriteStringHandle( hFile, m_CanonicalVolumePath );
  370. SafeWriteFile( hFile, m_DriveType );
  371. SafeWriteFile( hFile, m_VolumeSerialNumber );
  372. return S_OK;
  373. }
  374. CFile *
  375. CFile::Unserialize(
  376. HANDLE hFile,
  377. CJob* Job
  378. )
  379. {
  380. CFile * file = NULL;
  381. try
  382. {
  383. file = new CFile(Job);
  384. if (Job->GetType() != BG_JOB_TYPE_DOWNLOAD)
  385. {
  386. SafeReadFile( hFile, &file->m_LocalFileTime );
  387. }
  388. file->m_LocalName = SafeReadStringHandle( hFile );
  389. file->m_RemoteName = SafeReadStringHandle( hFile );
  390. file->m_TemporaryName = SafeReadStringHandle( hFile );
  391. SafeReadFile( hFile, &file->m_BytesTransferred );
  392. SafeReadFile( hFile, &file->m_BytesTotal );
  393. SafeReadFile( hFile, &file->m_Completed );
  394. file->m_VolumePath = SafeReadStringHandle( hFile );
  395. file->m_CanonicalVolumePath = SafeReadStringHandle( hFile );
  396. SafeReadFile( hFile, &file->m_DriveType );
  397. SafeReadFile( hFile, &file->m_VolumeSerialNumber );
  398. }
  399. catch ( ComError Error )
  400. {
  401. delete file;
  402. throw;
  403. }
  404. return file;
  405. }
  406. inline HRESULT
  407. CFile::CheckClientAccess(
  408. DWORD RequestedAccess
  409. ) const
  410. {
  411. return m_Job->CheckClientAccess( RequestedAccess );
  412. }
  413. HRESULT
  414. CFile::VerifyLocalFileName(
  415. LPCWSTR name,
  416. BG_JOB_TYPE FileType
  417. )
  418. {
  419. if (name == NULL)
  420. {
  421. return E_INVALIDARG;
  422. }
  423. //
  424. // Make sure the client can create a file there.
  425. //
  426. HRESULT Hr = S_OK;
  427. try
  428. {
  429. DWORD s;
  430. BOOL bResult;
  431. if (!IsAbsolutePath( name ))
  432. throw ComError( E_INVALIDARG );
  433. if ( wcsncmp( name, L"\\\\?\\", 4 ) == 0 )
  434. throw ComError( E_INVALIDARG );
  435. if (FileType == BG_JOB_TYPE_DOWNLOAD)
  436. {
  437. HRESULT hr;
  438. hr = BITSCheckFileWritability( name );
  439. if (FAILED(hr))
  440. {
  441. throw ComError( hr );
  442. }
  443. }
  444. else
  445. {
  446. //
  447. // See if the client can read the destination file.
  448. //
  449. auto_HANDLE<NULL> hFile;
  450. hFile = CreateFile( name,
  451. GENERIC_READ,
  452. FILE_SHARE_READ,
  453. NULL, // gneeric security descriptor
  454. OPEN_EXISTING,
  455. 0,
  456. NULL // no template file
  457. );
  458. if (hFile.get() == INVALID_HANDLE_VALUE)
  459. {
  460. ThrowLastError();
  461. }
  462. if (GetFileType( hFile.get() ) != FILE_TYPE_DISK)
  463. {
  464. throw ComError( E_INVALIDARG );
  465. }
  466. }
  467. //
  468. // Success.
  469. //
  470. Hr = S_OK;
  471. }
  472. catch ( ComError exception )
  473. {
  474. Hr = exception.Error();
  475. }
  476. return Hr;
  477. }
  478. HRESULT
  479. CFile::VerifyLocalName(
  480. LPCWSTR name,
  481. BG_JOB_TYPE FileType
  482. )
  483. {
  484. if (name == NULL)
  485. {
  486. return E_INVALIDARG;
  487. }
  488. //
  489. // Make sure the client can create a file there.
  490. //
  491. HRESULT Hr = S_OK;
  492. try
  493. {
  494. DWORD s;
  495. BOOL bResult;
  496. if (!IsAbsolutePath( name ))
  497. throw ComError( E_INVALIDARG );
  498. if ( wcsncmp( name, L"\\\\?\\", 4 ) == 0 )
  499. throw ComError( E_INVALIDARG );
  500. StringHandle FileName;
  501. StringHandle DirectoryName =
  502. BITSCrackFileName(
  503. name,
  504. FileName ); // throws ComError
  505. StringHandle VolumePath =
  506. BITSGetVolumePathName(
  507. DirectoryName ); // throws ComError
  508. UINT DriveType = GetDriveType( VolumePath );
  509. BOOL bIsRemote = IsDriveTypeRemote( DriveType );
  510. StringHandle CanonicalPath;
  511. DWORD VolumeSerialNumber = 0;
  512. if ( !bIsRemote )
  513. {
  514. CanonicalPath =
  515. BITSGetVolumeNameForVolumeMountPoint(
  516. VolumePath ); // throw ComError
  517. VolumeSerialNumber =
  518. BITSGetVolumeSerialNumber( CanonicalPath ); //throws ComError
  519. }
  520. m_VolumePath = VolumePath;
  521. m_CanonicalVolumePath = CanonicalPath;
  522. m_DriveType = DriveType;
  523. m_VolumeSerialNumber = VolumeSerialNumber;
  524. if (FileType == BG_JOB_TYPE_DOWNLOAD)
  525. {
  526. HRESULT hr;
  527. m_TemporaryName = BITSCreateTempFile( DirectoryName );
  528. hr = BITSCheckFileWritability( name );
  529. if (FAILED(hr))
  530. {
  531. DeleteFile( m_TemporaryName );
  532. throw ComError( hr );
  533. }
  534. }
  535. else
  536. {
  537. //
  538. // See if the client can read the destination file.
  539. //
  540. auto_HANDLE<NULL> hFile;
  541. hFile = CreateFile( name,
  542. GENERIC_READ,
  543. FILE_SHARE_READ, // no file sharing
  544. NULL, // generic security descriptor
  545. OPEN_EXISTING,
  546. 0,
  547. NULL // no template file
  548. );
  549. if (hFile.get() == INVALID_HANDLE_VALUE)
  550. {
  551. ThrowLastError();
  552. }
  553. LARGE_INTEGER size;
  554. if (!GetFileSizeEx( hFile.get(), &size ))
  555. {
  556. ThrowLastError();
  557. }
  558. m_BytesTotal = size.QuadPart;
  559. BY_HANDLE_FILE_INFORMATION info;
  560. if (!GetFileInformationByHandle( hFile.get(), &info ))
  561. {
  562. ThrowLastError();
  563. }
  564. m_LocalFileTime = info.ftLastWriteTime;
  565. }
  566. //
  567. // Success.
  568. //
  569. Hr = S_OK;
  570. }
  571. catch ( ComError exception )
  572. {
  573. Hr = exception.Error();
  574. }
  575. return Hr;
  576. }
  577. HRESULT
  578. CFile::ValidateAccessForUser(
  579. SidHandle sid,
  580. bool fWrite
  581. )
  582. {
  583. try
  584. {
  585. StringHandle CanonicalPath;
  586. DWORD VolumeSerialNumber = 0;
  587. UINT DriveType = 0;
  588. CNestedImpersonation imp( sid );
  589. StringHandle VolumePath =
  590. BITSGetVolumePathName( m_LocalName );
  591. DriveType = GetDriveType( VolumePath );
  592. bool bIsRemote = IsDriveTypeRemote( DriveType );
  593. if ( !bIsRemote )
  594. {
  595. CanonicalPath =
  596. BITSGetVolumeNameForVolumeMountPoint( VolumePath );
  597. // Need to stop impersonating at this point since registration
  598. // for notifications doesn't seem to tolerate impersonating callers
  599. imp.Revert();
  600. #if !defined( BITS_V12_ON_NT4 )
  601. THROW_HRESULT( g_Manager->IsVolumeLocked( CanonicalPath ));
  602. #endif
  603. VolumeSerialNumber =
  604. BITSGetVolumeSerialNumber( CanonicalPath );
  605. }
  606. bool bValid =
  607. ( _wcsicmp( VolumePath, m_VolumePath ) == 0 ) &&
  608. ( _wcsicmp( CanonicalPath, m_CanonicalVolumePath ) == 0 ) &&
  609. ( m_DriveType == DriveType ) &&
  610. ( m_VolumeSerialNumber == VolumeSerialNumber );
  611. if ( !bValid )
  612. return BG_E_NEW_OWNER_DIFF_MAPPING;
  613. // Revalidate access to the file. There are three cases:
  614. //
  615. // 1.file is not renamed: test the temporary file and local file.
  616. // 2. Mars job, file is renamed: test the local file
  617. // 3. new job, file is renamed: no test; the app owns the local file
  618. HANDLE hFile;
  619. HRESULT hr;
  620. imp.Impersonate();
  621. if (IsCompleted())
  622. {
  623. if (m_Job->GetOldExternalGroupInterface())
  624. {
  625. //
  626. // case 2
  627. //
  628. hr = BITSCheckFileWritability( m_LocalName );
  629. if (hr == E_ACCESSDENIED)
  630. {
  631. hr = BG_E_NEW_OWNER_NO_FILE_ACCESS;
  632. }
  633. THROW_HRESULT( hr );
  634. }
  635. else
  636. {
  637. //
  638. // case 3
  639. //
  640. }
  641. }
  642. else
  643. {
  644. //
  645. // case 1
  646. //
  647. if (fWrite)
  648. {
  649. hr = BITSCheckFileWritability( m_TemporaryName );
  650. if (hr == E_ACCESSDENIED)
  651. {
  652. hr = BG_E_NEW_OWNER_NO_FILE_ACCESS;
  653. }
  654. THROW_HRESULT( hr );
  655. hr = BITSCheckFileWritability( m_LocalName );
  656. if (hr == E_ACCESSDENIED)
  657. {
  658. hr = BG_E_NEW_OWNER_NO_FILE_ACCESS;
  659. }
  660. THROW_HRESULT( hr );
  661. }
  662. else
  663. {
  664. hr = BITSCheckFileReadability( m_LocalName );
  665. if (hr == E_ACCESSDENIED)
  666. {
  667. hr = BG_E_NEW_OWNER_NO_FILE_ACCESS;
  668. }
  669. THROW_HRESULT( hr );
  670. }
  671. }
  672. return S_OK;
  673. }
  674. catch( ComError Error )
  675. {
  676. return Error.Error();
  677. }
  678. }
  679. bool
  680. CFile::ValidateDriveInfo(
  681. HANDLE hToken,
  682. QMErrInfo & ErrInfo
  683. )
  684. {
  685. try
  686. {
  687. StringHandle CanonicalPath;
  688. DWORD VolumeSerialNumber = 0;
  689. UINT DriveType = 0;
  690. CNestedImpersonation imp( hToken );
  691. StringHandle VolumePath =
  692. BITSGetVolumePathName( m_LocalName ); // throws ComError
  693. DriveType = GetDriveType( VolumePath );
  694. bool bIsRemote = IsDriveTypeRemote( DriveType );
  695. if ( !bIsRemote )
  696. {
  697. CanonicalPath =
  698. BITSGetVolumeNameForVolumeMountPoint(
  699. VolumePath ); // throws ComError
  700. // Need to stop impersonating at this point since registration
  701. // for notifications doesn't seem to tolerate impersonating callers
  702. imp.Revert();
  703. #if !defined( BITS_V12_ON_NT4 )
  704. THROW_HRESULT( g_Manager->IsVolumeLocked( CanonicalPath ));
  705. #endif
  706. VolumeSerialNumber =
  707. BITSGetVolumeSerialNumber( CanonicalPath ); //throws ComError
  708. }
  709. bool bValid =
  710. ( _wcsicmp( VolumePath, m_VolumePath ) == 0 ) &&
  711. ( _wcsicmp( CanonicalPath, m_CanonicalVolumePath ) == 0 ) &&
  712. ( m_DriveType == DriveType ) &&
  713. ( m_VolumeSerialNumber == VolumeSerialNumber );
  714. if ( !bValid )
  715. {
  716. imp.Revert();
  717. g_Manager->OnDiskChange( m_CanonicalVolumePath, m_VolumeSerialNumber );
  718. ErrInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, BG_E_VOLUME_CHANGED, "Volume has changed");
  719. ErrInfo.result = QM_FILE_FATAL_ERROR;
  720. return false;
  721. }
  722. }
  723. catch( ComError Error )
  724. {
  725. HRESULT Hr = Error.Error();
  726. LogWarning( "Transient error while reading volume info for %ls, hr %!winerr!",
  727. (const WCHAR*)m_LocalName, Hr );
  728. if ( Hr == HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ) )
  729. {
  730. LogWarning( "Volume info APIs returned access denied, assume locked volume." );
  731. Hr = BG_E_DESTINATION_LOCKED;
  732. }
  733. ErrInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, Hr, "Volume has changed");
  734. ErrInfo.result = QM_FILE_TRANSIENT_ERROR;
  735. return false;
  736. }
  737. return true;
  738. }
  739. bool
  740. CFile::OnDiskChange(
  741. const WCHAR *CanonicalVolume,
  742. DWORD VolumeSerialNumber )
  743. {
  744. bool bFail =
  745. ( _wcsicmp( m_CanonicalVolumePath, CanonicalVolume ) == 0 ) &&
  746. ( VolumeSerialNumber == m_VolumeSerialNumber );
  747. if (!bFail)
  748. return true;
  749. LogWarning( "Failing job %p, to do disk change affecting file %ls",
  750. m_Job, (const WCHAR*)m_LocalName );
  751. QMErrInfo ErrInfo;
  752. ErrInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, BG_E_VOLUME_CHANGED, "Volume has changed");
  753. m_Job->FileFatalError( &ErrInfo );
  754. return false;
  755. }
  756. bool
  757. CFile::OnDismount(
  758. const WCHAR *CanonicalVolume )
  759. {
  760. bool bFail =
  761. ( _wcsicmp( m_CanonicalVolumePath, CanonicalVolume ) == 0 );
  762. if (!bFail)
  763. return true;
  764. LogWarning( "Failing job %p, to do dismount affecting file %ls",
  765. m_Job, (const WCHAR*)m_LocalName );
  766. QMErrInfo ErrInfo;
  767. ErrInfo.Set( SOURCE_QMGR_FILE, ERROR_STYLE_HRESULT, BG_E_VOLUME_CHANGED, "Volume has changed");
  768. m_Job->FileFatalError( &ErrInfo );
  769. return false;
  770. }
  771. bool CFile::Transfer(
  772. HANDLE hToken,
  773. BG_JOB_PRIORITY priority,
  774. const PROXY_SETTINGS & ProxySettings,
  775. const CCredentialsContainer * Credentials,
  776. QMErrInfo & ErrInfo
  777. )
  778. {
  779. // Check if the destination is locked or changed.
  780. if (!ValidateDriveInfo( hToken, ErrInfo ))
  781. {
  782. return true;
  783. }
  784. if (m_BytesTransferred == m_BytesTotal)
  785. {
  786. ErrInfo.result = QM_FILE_DONE;
  787. return true;
  788. }
  789. //
  790. // Release the global lock while the download is in progress.
  791. //
  792. g_Manager->m_TaskScheduler.UnlockWriter();
  793. LogDl( "Download starting." );
  794. g_Manager->m_pPD->Download( m_RemoteName,
  795. m_TemporaryName,
  796. m_BytesTransferred,
  797. this,
  798. &ErrInfo,
  799. hToken,
  800. priority,
  801. &ProxySettings,
  802. Credentials,
  803. m_Job->GetHostId()
  804. );
  805. LogDl( "Download Ended." );
  806. ErrInfo.Log();
  807. switch (ErrInfo.result)
  808. {
  809. case QM_FILE_ABORTED:
  810. //
  811. // If the abort was due to quantum timeout, don't poke the workitem.
  812. //
  813. if (g_Manager->m_TaskScheduler.PollAbort())
  814. {
  815. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  816. }
  817. break;
  818. case QM_IN_PROGRESS: ASSERT( ErrInfo.result != QM_IN_PROGRESS ); break;
  819. case QM_SERVER_FILE_CHANGED: ChangedOnServer(); break;
  820. case QM_FILE_TRANSIENT_ERROR:
  821. #if !defined( BITS_V12_ON_NT4 )
  822. //
  823. // Map any connection failure to BG_E_NETWORK_DISCONNECTED, if no nets are active.
  824. //
  825. if (g_Manager->m_NetworkMonitor.GetAddressCount() == 0)
  826. {
  827. ErrInfo.Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_NETWORK_DISCONNECTED, NULL );
  828. }
  829. #else
  830. break;
  831. #endif
  832. }
  833. //
  834. // Take the writer lock, since the caller expects it to be taken
  835. // upon return.
  836. //
  837. while (g_Manager->m_TaskScheduler.LockWriter() )
  838. {
  839. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  840. ErrInfo.result = QM_FILE_ABORTED;
  841. }
  842. if (ErrInfo.result == QM_FILE_ABORTED)
  843. {
  844. return false;
  845. }
  846. return true;
  847. }
  848. bool
  849. CFile::UploaderProgress(
  850. UINT64 BytesTransferred
  851. )
  852. {
  853. ASSERT( g_Manager->m_TaskScheduler.IsWriter() );
  854. //
  855. // Skip the progress update if the server is rewinding the file. Thus the no-progress
  856. // timer is cleared only after we actually see progress.
  857. //
  858. if (BytesTransferred > m_BytesTransferred)
  859. {
  860. m_BytesTransferred = BytesTransferred;
  861. m_Job->UpdateProgress( BytesTransferred, m_BytesTotal );
  862. }
  863. else
  864. {
  865. m_BytesTransferred = BytesTransferred;
  866. }
  867. bool bAbortQuantumExpired = g_Manager->CheckForQuantumTimeout();
  868. return bAbortQuantumExpired;
  869. }
  870. bool
  871. CFile::DownloaderProgress(
  872. UINT64 BytesTransferred,
  873. UINT64 BytesTotal
  874. )
  875. {
  876. if ( g_Manager->m_TaskScheduler.LockWriter() )
  877. {
  878. // Cancel was requested, notify downloader.
  879. return true;
  880. }
  881. m_BytesTransferred = BytesTransferred;
  882. m_BytesTotal = BytesTotal;
  883. m_Job->UpdateProgress( BytesTransferred, BytesTotal );
  884. bool bAbortQuantumExpired = g_Manager->CheckForQuantumTimeout();
  885. g_Manager->m_TaskScheduler.UnlockWriter();
  886. return bAbortQuantumExpired;
  887. }
  888. bool
  889. CFile::PollAbort()
  890. {
  891. if (g_Manager->m_TaskScheduler.PollAbort() ||
  892. g_Manager->CheckForQuantumTimeout())
  893. {
  894. return true;
  895. }
  896. return false;
  897. }
  898. BOOL
  899. CFile::VerifyRemoteName(
  900. LPCWSTR name
  901. )
  902. {
  903. if (name == NULL)
  904. {
  905. return FALSE;
  906. }
  907. if ( ( 0 != wcsncmp(name, L"http://", 7)) &&
  908. ( 0 != wcsncmp(name, L"https://", 8)) )
  909. {
  910. return FALSE;
  911. }
  912. if (( wcslen(name) > INTERNET_MAX_URL_LENGTH))
  913. return FALSE;
  914. return TRUE;
  915. }
  916. HRESULT
  917. CFile::MoveTempFile()
  918. {
  919. LogInfo( "commit: moving '%S' to '%S'", (const WCHAR*)m_TemporaryName, (const WCHAR*)m_LocalName );
  920. ASSERT( !m_Completed );
  921. DWORD dwFileAttributes =
  922. GetFileAttributes( (const WCHAR*)m_TemporaryName );
  923. if ( (DWORD)-1 == dwFileAttributes )
  924. {
  925. DWORD dwError = GetLastError();
  926. LogError( "GetFileAttributes error %!winerr!%", dwError );
  927. return HRESULT_FROM_WIN32( dwError );
  928. }
  929. if (!SetFileAttributes( m_TemporaryName, FILE_ATTRIBUTE_NORMAL ))
  930. {
  931. DWORD dwError = GetLastError();
  932. LogError( "SetFileAttributes error %d", dwError );
  933. return HRESULT_FROM_WIN32( dwError );
  934. }
  935. const DWORD dwMaxRetries = 10;
  936. DWORD dwError;
  937. DWORD dwSleepMSec = 50;
  938. for (int i=0; i<dwMaxRetries; i++)
  939. {
  940. if ( MoveFileEx( m_TemporaryName, m_LocalName, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED ) )
  941. {
  942. LogInfo( "file moved ok.");
  943. m_Completed = true;
  944. return S_OK;
  945. }
  946. dwError = GetLastError();
  947. LogError( "Unable to move file '%S' to '%S' due to %!winerr!, sleeping",
  948. (const WCHAR*) m_TemporaryName, (const WCHAR*)m_LocalName, dwError );
  949. // Only try three times if this isn't a file sharing violation.
  950. if ((i>3) && (dwError != ERROR_SHARING_VIOLATION))
  951. {
  952. break;
  953. }
  954. if (i<dwMaxRetries-1) // Don't hang the thread for the last sleep...
  955. {
  956. Sleep( dwSleepMSec );
  957. }
  958. dwSleepMSec *= 2;
  959. }
  960. LogError( "Timed out renaming temp file" );
  961. // Attemp to reset the attributes on the file.
  962. SetFileAttributes( (const WCHAR*)m_TemporaryName, dwFileAttributes );
  963. return HRESULT_FROM_WIN32( dwError );
  964. }
  965. HRESULT
  966. CFile::DeleteTempFile()
  967. {
  968. if (!DeleteFile( m_TemporaryName ))
  969. {
  970. DWORD s = GetLastError();
  971. LogWarning("error %!winerr! deleting %S", s, LPCWSTR( m_TemporaryName ));
  972. return HRESULT_FROM_WIN32( s );
  973. }
  974. return S_OK;
  975. }
  976. void
  977. CFile::ChangedOnServer()
  978. {
  979. LogError( "deleting '%S' since it was changed on the server", (const WCHAR*)m_TemporaryName );
  980. DeleteTempFile();
  981. m_BytesTransferred = 0;
  982. m_Completed = false;
  983. m_BytesTotal = BG_SIZE_UNKNOWN;
  984. }
  985. void
  986. CFile::DiscoverBytesTotal(
  987. HANDLE Token,
  988. const PROXY_SETTINGS & ProxySettings,
  989. const CCredentialsContainer * Credentials,
  990. QMErrInfo & ErrorInfo
  991. )
  992. {
  993. UINT64 FileSize;
  994. FILETIME FileTime;
  995. LogDl( "Retrieving remote infomation for %ls", m_RemoteName );
  996. g_Manager->m_TaskScheduler.UnlockWriter();
  997. g_Manager->m_pPD->GetRemoteFileInformation(
  998. Token,
  999. m_RemoteName,
  1000. &FileSize,
  1001. &FileTime,
  1002. &ErrorInfo,
  1003. &ProxySettings,
  1004. Credentials,
  1005. m_Job->GetHostId()
  1006. );
  1007. {
  1008. bool fCancelled = false;
  1009. while (g_Manager->m_TaskScheduler.LockWriter() )
  1010. {
  1011. g_Manager->m_TaskScheduler.AcknowledgeWorkItemCancel();
  1012. fCancelled = true;
  1013. }
  1014. if (fCancelled)
  1015. {
  1016. ErrorInfo.result = QM_FILE_ABORTED;
  1017. }
  1018. }
  1019. LogDl("result was %d", ErrorInfo.result );
  1020. switch (ErrorInfo.result)
  1021. {
  1022. case QM_FILE_DONE: m_BytesTotal = FileSize; break;
  1023. case QM_IN_PROGRESS: ASSERT( 0 ); break;
  1024. case QM_SERVER_FILE_CHANGED: ChangedOnServer(); break;
  1025. }
  1026. }
  1027. HANDLE
  1028. CFile::OpenLocalFileForUpload()
  1029. {
  1030. auto_HANDLE<NULL> hFile = CreateFile( m_LocalName,
  1031. GENERIC_READ,
  1032. FILE_SHARE_READ,
  1033. NULL,
  1034. OPEN_EXISTING,
  1035. 0,
  1036. NULL );
  1037. if ( hFile.get() == INVALID_HANDLE_VALUE )
  1038. {
  1039. THROW_HRESULT( HRESULT_FROM_WIN32( GetLastError() ));
  1040. }
  1041. BY_HANDLE_FILE_INFORMATION info;
  1042. if (!GetFileInformationByHandle( hFile.get(), &info ))
  1043. {
  1044. THROW_HRESULT( HRESULT_FROM_WIN32( GetLastError() ));
  1045. }
  1046. if (m_LocalFileTime != info.ftLastWriteTime)
  1047. {
  1048. THROW_HRESULT( BG_E_LOCAL_FILE_CHANGED );
  1049. }
  1050. LARGE_INTEGER Offset;
  1051. Offset.QuadPart = m_BytesTransferred;
  1052. if (!SetFilePointerEx( hFile.get(), Offset, NULL, FILE_BEGIN ))
  1053. {
  1054. THROW_HRESULT( HRESULT_FROM_WIN32( GetLastError() ));
  1055. }
  1056. return hFile.release();
  1057. }
  1058. HRESULT
  1059. CFile::SetLocalFileTime( FILETIME Time )
  1060. /*
  1061. This is used as a special case to set the file time of a zero-length file, since
  1062. the normal download path is skipped.
  1063. */
  1064. {
  1065. try
  1066. {
  1067. auto_HANDLE<NULL> hFile = CreateFile( m_TemporaryName,
  1068. GENERIC_READ | GENERIC_WRITE,
  1069. 0,
  1070. NULL,
  1071. OPEN_ALWAYS,
  1072. FILE_ATTRIBUTE_HIDDEN,
  1073. NULL );
  1074. if ( hFile.get() == INVALID_HANDLE_VALUE )
  1075. {
  1076. ThrowLastError();
  1077. }
  1078. if (!SetFileTime( hFile.get(), &Time, &Time, &Time ) )
  1079. {
  1080. ThrowLastError();
  1081. }
  1082. return S_OK;
  1083. }
  1084. catch ( ComError err )
  1085. {
  1086. LogError( "error %x setting creation time", err.Error() );
  1087. return err.Error();
  1088. }
  1089. }
  1090. //------------------------------------------------------------------------
  1091. typedef CLockedReadPointer<CFile, BG_JOB_READ> CLockedFileReadPointer;
  1092. typedef CLockedWritePointer<CFile, BG_JOB_WRITE> CLockedFileWritePointer;
  1093. CFileExternal::CFileExternal(
  1094. CFile * file,
  1095. CJobExternal * JobExternal
  1096. )
  1097. : m_ServiceInstance( g_ServiceInstance ),
  1098. m_refs(1),
  1099. m_file( file ),
  1100. m_JobExternal( JobExternal )
  1101. {
  1102. m_JobExternal->AddRef();
  1103. }
  1104. CFileExternal::~CFileExternal()
  1105. {
  1106. SafeRelease( m_JobExternal );
  1107. }
  1108. STDMETHODIMP
  1109. CFileExternal::QueryInterface(
  1110. REFIID iid,
  1111. void** ppvObject
  1112. )
  1113. {
  1114. BEGIN_EXTERNAL_FUNC
  1115. HRESULT Hr = S_OK;
  1116. *ppvObject = NULL;
  1117. if ((iid == IID_IUnknown) || (iid == IID_IBackgroundCopyFile))
  1118. {
  1119. *ppvObject = (IBackgroundCopyFile *)this;
  1120. AddRef();
  1121. }
  1122. else
  1123. {
  1124. Hr = E_NOINTERFACE;
  1125. }
  1126. LogRef( "iid %!guid!, hr %x", &iid, Hr );
  1127. return Hr;
  1128. END_EXTERNAL_FUNC
  1129. }
  1130. ULONG
  1131. CFileExternal::AddRef()
  1132. {
  1133. BEGIN_EXTERNAL_FUNC
  1134. ULONG newrefs = InterlockedIncrement(&m_refs);
  1135. LogRef("refs %d", newrefs);
  1136. return newrefs;
  1137. END_EXTERNAL_FUNC
  1138. }
  1139. ULONG
  1140. CFileExternal::Release()
  1141. {
  1142. BEGIN_EXTERNAL_FUNC
  1143. ULONG newrefs = InterlockedDecrement(&m_refs);
  1144. LogRef("refs %d", newrefs);
  1145. if (0 == newrefs)
  1146. {
  1147. delete this;
  1148. }
  1149. return newrefs;
  1150. END_EXTERNAL_FUNC
  1151. }
  1152. STDMETHODIMP
  1153. CFileExternal::GetRemoteNameInternal(
  1154. /* [out] */ LPWSTR *pVal
  1155. )
  1156. {
  1157. CLockedFileReadPointer LockedPointer(m_file);
  1158. LogPublicApiBegin( "pVal %p", pVal );
  1159. HRESULT Hr = LockedPointer.ValidateAccess();
  1160. if (SUCCEEDED(Hr))
  1161. {
  1162. Hr = LockedPointer->GetRemoteName( pVal );
  1163. }
  1164. LogPublicApiEnd( "pVal %p(%S) ", pVal, SUCCEEDED(Hr) ? *pVal : L"?" );
  1165. return Hr;
  1166. }
  1167. STDMETHODIMP
  1168. CFileExternal::GetLocalNameInternal(
  1169. /* [out] */ LPWSTR *pVal
  1170. )
  1171. {
  1172. CLockedFileReadPointer LockedPointer(m_file);
  1173. LogPublicApiBegin( "pVal %p", pVal );
  1174. HRESULT Hr = LockedPointer.ValidateAccess();
  1175. if (SUCCEEDED(Hr))
  1176. {
  1177. Hr = LockedPointer->GetLocalName( pVal );
  1178. }
  1179. LogPublicApiEnd( "pVal %p(%S) ", pVal, SUCCEEDED(Hr) ? *pVal : L"?" );
  1180. return Hr;
  1181. }
  1182. STDMETHODIMP
  1183. CFileExternal::GetProgressInternal(
  1184. /* [out] */ BG_FILE_PROGRESS *pVal
  1185. )
  1186. {
  1187. CLockedFileReadPointer LockedPointer(m_file);
  1188. LogPublicApiBegin( "pVal %p", pVal );
  1189. HRESULT Hr = LockedPointer.ValidateAccess();
  1190. if (SUCCEEDED(Hr))
  1191. {
  1192. LockedPointer->GetProgress( pVal );
  1193. }
  1194. LogPublicApiEnd( "pVal %p", pVal );
  1195. return Hr;
  1196. }