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.

1721 lines
50 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+============================================================================
  3. //
  4. // File: logfile.cxx
  5. //
  6. // This file contains the definition of the CLogFile class.
  7. //
  8. // Purpose: This class represents the file which contains the
  9. // Tracking/Workstation move notification log. Clients
  10. // of this class may request one entry or header at a time,
  11. // using based on a log entry index.
  12. //
  13. // Entries in the log file are joined by a linked-list,
  14. // so this class includes methods that clients use to
  15. // advance their log entry index (i.e., traverse the list).
  16. //
  17. // Notes: CLogFile reads/writes a sector at a time for reliability.
  18. // When a client modifies a log entry in one sector, then
  19. // attempts to access another sector, CLogFile automatically
  20. // flushes the changes. This is dependent, however, on the
  21. // client properly calling the SetDirty method whenever it
  22. // changes a log entry or header.
  23. //
  24. //+============================================================================
  25. #include "pch.cxx"
  26. #pragma hdrstop
  27. #include "trkwks.hxx"
  28. const GUID s_guidLogSignature = { /* 6643a7ec-effe-11d1-b2ae-00c04fb9386d */
  29. 0x6643a7ec, 0xeffe, 0x11d1,
  30. {0xb2, 0xae, 0x00, 0xc0, 0x4f, 0xb9, 0x38, 0x6d} };
  31. NTSTATUS
  32. CSecureFile::CreateSecureDirectory( const TCHAR *ptszDirectory )
  33. {
  34. HANDLE hDir = NULL;
  35. NTSTATUS status = STATUS_SUCCESS;
  36. CSystemSD ssd;
  37. SECURITY_ATTRIBUTES security_attributes;
  38. LONG cLocks = Lock();
  39. __try
  40. {
  41. memset( &security_attributes, 0, sizeof(security_attributes) );
  42. ssd.Initialize( CSystemSD::SYSTEM_ONLY ); // No Administrator access
  43. security_attributes.nLength = sizeof(security_attributes);
  44. security_attributes.bInheritHandle = FALSE;
  45. security_attributes.lpSecurityDescriptor = static_cast<const PSECURITY_DESCRIPTOR>(ssd);
  46. // Create the directory. Make it system|hidden so that the NT5 shell
  47. // won't ever display it.
  48. status = TrkCreateFile( ptszDirectory,
  49. FILE_LIST_DIRECTORY,
  50. FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
  51. FILE_SHARE_READ | FILE_SHARE_WRITE,
  52. FILE_OPEN_IF,
  53. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  54. &security_attributes,
  55. &hDir );
  56. if( !NT_SUCCESS(status) )
  57. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create secure directory(status=%08x)"), status ));
  58. else
  59. NtClose(hDir);
  60. }
  61. __finally
  62. {
  63. ssd.UnInitialize();
  64. #if DBG
  65. TrkVerify( Unlock() == cLocks );
  66. #else
  67. Unlock();
  68. #endif
  69. }
  70. return( status );
  71. }
  72. // Assuming that tszFile is a file under a directory that is under a root
  73. // directory. Try to create tszFile first. If not successful, create its
  74. // parent directory. And then try create the file again.
  75. NTSTATUS
  76. CSecureFile::CreateAlwaysSecureFile(const TCHAR * ptszFile)
  77. {
  78. NTSTATUS status = STATUS_SUCCESS;
  79. CSystemSD ssd;
  80. SECURITY_ATTRIBUTES security_attributes;
  81. memset( &security_attributes, 0, sizeof(security_attributes) );
  82. LONG cLocks = Lock();
  83. __try
  84. {
  85. TrkAssert(!IsOpen());
  86. ssd.Initialize();
  87. security_attributes.nLength = sizeof(security_attributes);
  88. security_attributes.bInheritHandle = FALSE;
  89. security_attributes.lpSecurityDescriptor = static_cast<const PSECURITY_DESCRIPTOR>(ssd);
  90. for(int cTries = 0; cTries < 2; cTries++)
  91. {
  92. // Create the file, deleting an existing file if there is one.
  93. // We make it system|hidden so that it's "super-hidden"; the NT5
  94. // shell won't ever display it.
  95. status = TrkCreateFile( ptszFile,
  96. FILE_GENERIC_READ|FILE_GENERIC_WRITE, // Access
  97. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, // Attributes
  98. 0, // Share
  99. FILE_SUPERSEDE, // Creation/distribution
  100. 0, // Options
  101. &security_attributes, // Security
  102. &_hFile );
  103. if(NT_SUCCESS(status))
  104. break;
  105. _hFile = NULL;
  106. if( STATUS_OBJECT_PATH_NOT_FOUND == status )
  107. {
  108. // The create failed because the directory ("System Volume Information")
  109. // didn't exist.
  110. CDirectoryName dirname;
  111. TrkVerify( dirname.SetFromFileName( ptszFile ));
  112. status = CreateSecureDirectory( dirname );
  113. if( !NT_SUCCESS(status) )
  114. {
  115. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create directory %s"),
  116. static_cast<const TCHAR*>(dirname) ));
  117. break;
  118. }
  119. } // path not found
  120. else
  121. {
  122. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create file(status=%08x)"), status ));
  123. _hFile = NULL;
  124. break;
  125. }
  126. } // for
  127. }
  128. __finally
  129. {
  130. ssd.UnInitialize();
  131. #if DBG
  132. TrkVerify( Unlock() == cLocks );
  133. #else
  134. Unlock();
  135. #endif
  136. }
  137. return( status );
  138. }
  139. NTSTATUS
  140. CSecureFile::OpenExistingSecureFile( const TCHAR * ptszFile, BOOL fReadOnly )
  141. {
  142. NTSTATUS status = STATUS_SUCCESS;
  143. LONG cLocks = Lock();
  144. __try
  145. {
  146. TrkAssert(!IsOpen());
  147. // From the point of view of CSecureFile, we might as well open share_read,
  148. // since the file's still protected by the ACLs. From the point of view
  149. // of the CLogFile derivation, we want to open share_read in order to
  150. // allow the dltadmin tool to read the log. We add DELETE access so
  151. // that the file can be renamed (necessary for migrating pre-nt5beta3 files).
  152. // Note - this is an async open
  153. status = TrkCreateFile( ptszFile,
  154. fReadOnly // Access
  155. ? FILE_GENERIC_READ
  156. : FILE_GENERIC_READ|FILE_GENERIC_WRITE|DELETE,
  157. FILE_ATTRIBUTE_NORMAL, // Attributes
  158. FILE_SHARE_READ, // Share
  159. FILE_OPEN, // Creation/distribution
  160. 0, // Options (async)
  161. NULL, // Security
  162. &_hFile );
  163. if( !NT_SUCCESS(status) )
  164. _hFile = NULL;
  165. }
  166. __finally
  167. {
  168. #if DBG
  169. TrkVerify( Unlock() == cLocks );
  170. #else
  171. Unlock();
  172. #endif
  173. }
  174. return( status );
  175. }
  176. NTSTATUS
  177. CSecureFile::RenameSecureFile( const TCHAR *ptszFile )
  178. {
  179. NTSTATUS status = STATUS_SUCCESS;
  180. IO_STATUS_BLOCK Iosb;
  181. FILE_RENAME_INFORMATION *pfile_rename_information = NULL;
  182. UNICODE_STRING uPath;
  183. PVOID pFreeBuffer = NULL;
  184. ULONG cbSize = 0;
  185. LONG cLocks = Lock();
  186. __try
  187. {
  188. // Convert the Win32 path name to an NT name
  189. if( !RtlDosPathNameToNtPathName_U( ptszFile, &uPath, NULL, NULL ))
  190. {
  191. status = STATUS_OBJECT_NAME_INVALID;
  192. __leave;
  193. }
  194. pFreeBuffer = uPath.Buffer;
  195. // Fill in the rename information
  196. cbSize = sizeof(*pfile_rename_information) + uPath.Length;
  197. pfile_rename_information = reinterpret_cast<FILE_RENAME_INFORMATION*>
  198. (new BYTE[ cbSize ]);
  199. if( NULL == pfile_rename_information )
  200. {
  201. status = STATUS_NO_MEMORY;
  202. __leave;
  203. }
  204. pfile_rename_information->ReplaceIfExists = TRUE;
  205. pfile_rename_information->RootDirectory = NULL;
  206. pfile_rename_information->FileNameLength = uPath.Length;
  207. memcpy( pfile_rename_information->FileName, uPath.Buffer, uPath.Length );
  208. // Rename the file
  209. status = NtSetInformationFile( _hFile, &Iosb,
  210. pfile_rename_information, cbSize,
  211. FileRenameInformation );
  212. if( !NT_SUCCESS(status) )
  213. __leave;
  214. }
  215. __finally
  216. {
  217. #if DBG
  218. TrkVerify( Unlock() == cLocks );
  219. #else
  220. Unlock();
  221. #endif
  222. }
  223. if( NULL != pfile_rename_information )
  224. delete [] pfile_rename_information;
  225. if( NULL != pFreeBuffer )
  226. RtlFreeHeap( RtlProcessHeap(), 0, pFreeBuffer );
  227. return( status );
  228. }
  229. //+----------------------------------------------------------------------------
  230. //
  231. // Method: Initialize
  232. //
  233. // Purpose: Initialize a CLogFile object.
  234. //
  235. // Arguments: [hFile] (in)
  236. // The file in which the log should be stored.
  237. // [dwCreationDistribution] (in)
  238. // Either CREATE_ALWAYS or OPEN_EXISTING.
  239. // [pcTrkWksConfiguration] (in)
  240. // Configuration parameters for the log.
  241. //
  242. // Returns: None.
  243. //
  244. //+----------------------------------------------------------------------------
  245. void
  246. CLogFile::Initialize( const TCHAR *ptszVolumeDeviceName,
  247. const CTrkWksConfiguration *pcTrkWksConfiguration,
  248. PLogFileNotify *pLogFileNotify,
  249. TCHAR tcVolume
  250. )
  251. {
  252. LogHeader logheader;
  253. TCHAR tszRootPathName[ MAX_PATH + 1];
  254. DWORD dwSectorsPerCluster, dwNumberOfFreeClusters, dwTotalNumberOfClusters;
  255. HANDLE hFile = NULL;
  256. FILE_FS_SIZE_INFORMATION FileSizeInformation;
  257. IO_STATUS_BLOCK IoStatusBlock;
  258. NTSTATUS status = STATUS_SUCCESS;
  259. CSecureFile::Initialize();
  260. // Save caller-provided values
  261. TrkAssert( NULL != pcTrkWksConfiguration || NULL != _pcTrkWksConfiguration );
  262. if( NULL != pcTrkWksConfiguration )
  263. _pcTrkWksConfiguration = pcTrkWksConfiguration;
  264. TrkAssert( 0 < _pcTrkWksConfiguration->GetLogFileOpenTime() );
  265. TrkAssert( NULL != ptszVolumeDeviceName || NULL != _ptszVolumeDeviceName );
  266. if( NULL != ptszVolumeDeviceName )
  267. _ptszVolumeDeviceName = ptszVolumeDeviceName;
  268. TrkAssert( NULL != pLogFileNotify || NULL != _pLogFileNotify );
  269. if( NULL != pLogFileNotify )
  270. _pLogFileNotify = pLogFileNotify;
  271. _tcVolume = tcVolume;
  272. // Calculate the bytes/sector of the log file's volume.
  273. //
  274. // It would be easier to use the Win32 GetDiskFreeSpace here, but that
  275. // API requires a root path name of the form "A:\\", which we don't have.
  276. // That requirement only exists, though, for some historic reason.
  277. // Postpend a whack to get a root path.
  278. TCHAR tszVolumeName[ MAX_PATH + 1 ] = { TEXT('\0') };
  279. TrkAssert( NULL != _ptszVolumeDeviceName );
  280. _tcscpy( tszVolumeName, _ptszVolumeDeviceName );
  281. _tcscat( tszVolumeName, TEXT("\\") );
  282. // Open the root
  283. status = TrkCreateFile( tszVolumeName, FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
  284. FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
  285. FILE_OPEN,
  286. FILE_OPEN_FOR_FREE_SPACE_QUERY | FILE_SYNCHRONOUS_IO_NONALERT,
  287. NULL,
  288. &hFile );
  289. if( !NT_SUCCESS(status) )
  290. {
  291. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open volume root to query free space") ));
  292. TrkRaiseNtStatus(status);
  293. }
  294. // Query the volume for the free space and unconditionally close the handle.
  295. status = NtQueryVolumeInformationFile( hFile, &IoStatusBlock, &FileSizeInformation,
  296. sizeof(FileSizeInformation), FileFsSizeInformation );
  297. NtClose( hFile );
  298. if( !NT_SUCCESS(status) )
  299. {
  300. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't query volume information from log file") ));
  301. TrkRaiseNtStatus(status);
  302. }
  303. // Save the free space away
  304. _cbLogSector = FileSizeInformation.BytesPerSector;
  305. if( MIN_LOG_SECTOR_SIZE > _cbLogSector )
  306. {
  307. TrkLog(( TRKDBG_ERROR, TEXT("Volume sector sizes too small") ));
  308. TrkRaiseNtStatus( STATUS_INVALID_BUFFER_SIZE );
  309. }
  310. // Initialize members
  311. _header.Initialize( _cbLogSector );
  312. _sector.Initialize( _header.NumSectors(), _cbLogSector );
  313. if( INVALID_HANDLE_VALUE == _heventOplock )
  314. {
  315. _heventOplock = CreateEvent( NULL, // Security Attributes.
  316. FALSE, // Manual Reset Flag.
  317. FALSE, // Inital State = Signaled, Flag.
  318. NULL ); // Name
  319. if( INVALID_HANDLE_VALUE == _heventOplock )
  320. {
  321. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create event for logfile") ));
  322. TrkRaiseLastError();
  323. }
  324. }
  325. // Open the log file here. We open the log file at initialization time and
  326. // keeps it open unless the handle is broken or we are notified to give up
  327. // the volume.
  328. ActivateLogFile();
  329. } // CLogFile::Initialize()
  330. //+----------------------------------------------------------------------------
  331. //
  332. // Method: UnInitialize
  333. //
  334. // Purpose: Free resources
  335. //
  336. // Arguments: None
  337. //
  338. // Returns: None
  339. //
  340. //+----------------------------------------------------------------------------
  341. void
  342. CLogFile::UnInitialize( )
  343. {
  344. __try
  345. {
  346. CloseLog( );
  347. if( INVALID_HANDLE_VALUE != _heventOplock )
  348. {
  349. CloseHandle( _heventOplock );
  350. _heventOplock = INVALID_HANDLE_VALUE;
  351. }
  352. }
  353. __finally
  354. {
  355. _header.UnInitialize();
  356. _sector.UnInitialize();
  357. }
  358. } // CLogFile::UnInitialize()
  359. //+----------------------------------------------------------------------------
  360. //
  361. // PRobustlyCreateableFile::RobustlyCreateFile
  362. //
  363. // Open the specified file on the specified volume. If the file is corrupt,
  364. // delete it. If the file doesn't exist (or was deleted), create a new
  365. // one.
  366. //
  367. //+----------------------------------------------------------------------------
  368. void
  369. PRobustlyCreateableFile::RobustlyCreateFile( const TCHAR * ptszFile, TCHAR tcVolumeDriveLetter )
  370. {
  371. // Attempt to open the file
  372. RCF_RESULT r = OpenExistingFile( ptszFile );
  373. #if DBG
  374. if( r == OK )
  375. TrkLog(( TRKDBG_LOG, TEXT("Opened log file %s"), ptszFile ));
  376. #endif
  377. if( r == CORRUPT )
  378. {
  379. #if DBG
  380. TCHAR tszFileBak[ MAX_PATH + 1 ];
  381. // Generate a backup name for the existing log file.
  382. _tcscpy( tszFileBak, ptszFile );
  383. _tcscat( tszFileBak, TEXT(".bak") );
  384. // Rename the existing logfile to the backup location.
  385. if (!MoveFileEx(ptszFile, tszFileBak, MOVEFILE_REPLACE_EXISTING))
  386. {
  387. TrkLog((TRKDBG_ERROR, TEXT("Couldn't make backup of corrupted file name\n (\"%s\" to \"%s\")"),
  388. ptszFile, tszFileBak ));
  389. TrkRaiseLastError( );
  390. }
  391. #else
  392. // Delete the file.
  393. RobustlyDeleteFile( ptszFile );
  394. #endif // #if DBG
  395. TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE,
  396. static_cast<const TCHAR*>( CStringize(tcVolumeDriveLetter) ),
  397. NULL );
  398. // Go into the file-not-found mode.
  399. r = NOT_FOUND;
  400. }
  401. if( r == NOT_FOUND )
  402. {
  403. TCHAR tszLogFileTmp[MAX_PATH+1];
  404. // Create a temporary file name. We'll do everything here, and switch it
  405. // to the real name when everything's set up.
  406. _tcscpy( tszLogFileTmp, ptszFile );
  407. _tcscat( tszLogFileTmp, TEXT(".tmp") );
  408. TrkLog(( TRKDBG_LOG, TEXT("Creating new file %s"), ptszFile ));
  409. CreateAlwaysFile( tszLogFileTmp );
  410. // Move the log file into its final name
  411. if (!( MoveFile(tszLogFileTmp, ptszFile) ))
  412. {
  413. TrkLog((TRKDBG_ERROR, TEXT("Couldn't rename file (\"%s\" to \"%s\")"), tszLogFileTmp, ptszFile ));
  414. TrkRaiseLastError( );
  415. }
  416. SetLastError(0);
  417. r = OpenExistingFile( ptszFile );
  418. }
  419. if( r != OK )
  420. {
  421. TrkLog((TRKDBG_ERROR, TEXT("Couldn't create/open file (%s)"), ptszFile ));
  422. TrkRaiseLastError();
  423. }
  424. }
  425. void
  426. PRobustlyCreateableFile::RobustlyDeleteFile( const TCHAR * ptszFile )
  427. {
  428. TCHAR tszFileBak[ MAX_PATH + 1 ];
  429. BOOL fDeleted = FALSE;
  430. // Generate a backup name for the existing log file.
  431. _tcscpy( tszFileBak, ptszFile );
  432. _tcscat( tszFileBak, TEXT(".bak") );
  433. // Delete the file.
  434. // First rename it, though, so we don't get held up by the case where
  435. // e.g. backup has the file open.
  436. if (!MoveFileEx(ptszFile, tszFileBak, MOVEFILE_REPLACE_EXISTING))
  437. {
  438. if( ERROR_PATH_NOT_FOUND == GetLastError()
  439. ||
  440. ERROR_FILE_NOT_FOUND == GetLastError() )
  441. {
  442. fDeleted = TRUE;
  443. }
  444. else
  445. {
  446. TrkLog((TRKDBG_ERROR, TEXT("Couldn't rename existing log file (%lu, \"%s\")"),
  447. GetLastError(), ptszFile ));
  448. TrkRaiseLastError( );
  449. }
  450. }
  451. if( !fDeleted && !DeleteFile( tszFileBak ))
  452. {
  453. TrkLog(( TRKDBG_WARNING, TEXT("Couldn't delete backup log file\n (%lu, \"%s\")"),
  454. GetLastError(), tszFileBak ));
  455. }
  456. }
  457. void
  458. CLogFile::CloseLog() // doesn't raise
  459. {
  460. if( IsOpen() )
  461. {
  462. _header.OnClose();
  463. _sector.OnClose();
  464. UnregisterOplockFromThreadPool();
  465. CloseFile();
  466. TrkLog(( TRKDBG_LOG, TEXT("Log file closed on volume %c"), _tcVolume ));
  467. }
  468. }
  469. //+----------------------------------------------------------------------------
  470. //
  471. // Method: InitializeLogEntries (private)
  472. //
  473. // Synopsis: Given a contiguous set of log entries in the log file,
  474. // initialize all of the fields. The entries are initialized
  475. // to link to their neighbors in a circular queue.
  476. //
  477. // Arguments: [ilogFirst] (in)
  478. // The first log entry in the list.
  479. // [ilogLast] (in)
  480. // The last log entry in the list.
  481. //
  482. // Returns: None
  483. //
  484. //+----------------------------------------------------------------------------
  485. void
  486. CLogFile::InitializeLogEntries( LogIndex ilogFirst, LogIndex ilogLast )
  487. {
  488. LogEntry *ple = NULL;
  489. LogIndex ilogEntry;
  490. ULONG cEntries = ilogLast - ilogFirst + 1;
  491. TrkAssert( cEntries > 0 );
  492. // Initialize the first log entry.
  493. ple = _sector.GetLogEntry( ilogFirst );
  494. _sector.SetDirty( TRUE );
  495. _sector.InitSectorHeader();
  496. memset( ple, 0, sizeof(*ple) );
  497. ple->ilogNext = ilogFirst + 1;
  498. ple->ilogPrevious = ilogLast;
  499. ple->move.type = LE_TYPE_EMPTY;
  500. // Initialize all log entries except for the first and last.
  501. for( ilogEntry = ilogFirst + 1; ilogEntry < ilogLast; ilogEntry++ )
  502. {
  503. ple = _sector.GetLogEntry( ilogEntry );
  504. _sector.InitSectorHeader();
  505. memset( ple, 0, sizeof(*ple) );
  506. ple->ilogNext = ilogEntry + 1;
  507. ple->ilogPrevious = ilogEntry - 1;
  508. ple->move.type = LE_TYPE_EMPTY;
  509. }
  510. // Initialize the last log entry.
  511. ple = _sector.GetLogEntry( ilogLast );
  512. _sector.InitSectorHeader();
  513. memset( ple, 0, sizeof(*ple) );
  514. ple->ilogNext = ilogFirst;
  515. ple->ilogPrevious = ilogLast - 1;
  516. ple->move.type = LE_TYPE_EMPTY;
  517. Flush( );
  518. return;
  519. } // CLogFile::InitializeLogEntries()
  520. //+----------------------------------------------------------------------------
  521. //
  522. // Method: Validate (private)
  523. //
  524. // Synopsis: Validate the log file by verifying the linked-list pointers,
  525. // the log file expansion data,
  526. // and by verifying the Signature and Format IDs in the headers.
  527. //
  528. // Arguments: None
  529. //
  530. // Returns: None (raises on error)
  531. //
  532. //+----------------------------------------------------------------------------
  533. BOOL
  534. CLogFile::Validate( )
  535. {
  536. BOOL fValid = FALSE;
  537. BOOL fFirstPass;
  538. LogIndex ilog;
  539. ULONG cEntries;
  540. ULONG cEntriesSeen;
  541. // ---------------
  542. // Simple Checking
  543. // ---------------
  544. // Do some quick checking, and only continue if this exposes a problem.
  545. // Check the signature & format in the first sector's header.
  546. if( s_guidLogSignature != _header.GetSignature()
  547. ||
  548. CLOG_MAJOR_FORMAT != _header.GetMajorFormat()
  549. )
  550. {
  551. TrkLog(( TRKDBG_ERROR, TEXT("Corrupted log file on volume %c (Signature=%s, Format=0x%X)"),
  552. _tcVolume,
  553. (const TCHAR*)CDebugString(_header.GetSignature()),
  554. _header.GetFormat() ));
  555. goto Exit;
  556. }
  557. // If this log has an uplevel minor version format, then set a bit
  558. // to show that the log has been touched by someone that doesn't fully
  559. // understand it. This won't actually make the header dirty, but if
  560. // some other change takes place that does make it dirty, this bit
  561. // will be included in the flush.
  562. if( CLOG_MINOR_FORMAT < _header.GetMinorFormat() )
  563. {
  564. TrkLog(( TRKDBG_VOLUME, TEXT("Setting downlevel-dirtied (0x%x, 0x%x)"),
  565. CLOG_MINOR_FORMAT, _header.GetMinorFormat() ));
  566. _header.SetDownlevelDirtied();
  567. }
  568. // Check for proper shutdown. The shutdown flag is always stored in the
  569. // first sector's header, since the log may not be in a state where we can
  570. // read for the most recent sector's header. If the header wasn't shutdown,
  571. // we go into Recovering mode. If it was shut down, we don't clear the
  572. // recovery flag, since the there may have been a shutdown during a previous recovery.
  573. // I.e., CLogFile's responsibility is to set the recovery flag automatically,
  574. // but to clear it only on request.
  575. if( _header.IsShutdown() )
  576. {
  577. if(_header.IsExpansionDataClear())
  578. {
  579. fValid = TRUE;
  580. }
  581. else
  582. goto Exit;
  583. // On debug builds, go ahead and run the validation code
  584. #if DBG
  585. fValid = FALSE; // Fall through
  586. #else
  587. goto Exit;
  588. #endif
  589. }
  590. else
  591. TrkLog(( TRKDBG_ERROR, TEXT("Log was not properly shut down on volume %c:"), _tcVolume ));
  592. // -----------------------------------------
  593. // Recover after a crash during an expansion
  594. // -----------------------------------------
  595. if( 0 != _header.ExpansionInProgress() )
  596. {
  597. TrkLog(( TRKDBG_ERROR, TEXT("Recovering from a previous log expansion attempt on volume %c"), _tcVolume ));
  598. TrkAssert( _header.GetPreExpansionStart() != _header.GetPreExpansionEnd() );
  599. TrkAssert( _header.GetPreExpansionSize() <= _cbLogFile );
  600. // Validate the expansion header. We'll just ensure that the size is reasonable;
  601. // the linked-list pointers will be validated below.
  602. if( _header.GetPreExpansionSize() > _cbLogFile ) {
  603. TrkLog(( TRKDBG_ERROR, TEXT("Pre-expansion size is corrupted") ));
  604. }
  605. // Throw away the extra, uninitialized portion at the end of the file.
  606. if( !SetSize( _header.GetPreExpansionSize() ))
  607. {
  608. TrkLog(( TRKDBG_ERROR, TEXT("Failed SetSize during validation of log on %c:"), _tcVolume ));
  609. TrkRaiseLastError();
  610. }
  611. // Ensure that the linked list is still circular.
  612. _sector.GetLogEntry( _header.GetPreExpansionEnd() )->ilogNext
  613. = _header.GetPreExpansionStart();
  614. _sector.GetLogEntry( _header.GetPreExpansionStart() )->ilogPrevious
  615. = _header.GetPreExpansionEnd();
  616. // Now that we're back in a good state, we can throw away the expansion
  617. // information.
  618. _sector.Flush( );
  619. _header.ClearExpansionData(); // flush through cache
  620. } // if( 0 != _header.ExpansionInProgress() )
  621. // ----------------------
  622. // Check forward pointers
  623. // ----------------------
  624. TrkAssert( 0 == _header.GetPreExpansionSize() );
  625. TrkAssert( 0 == _header.GetPreExpansionStart() );
  626. TrkAssert( 0 == _header.GetPreExpansionEnd() );
  627. // Walk through the next pointers, and verify each of the headers.
  628. cEntries = _cEntriesInFile;
  629. fFirstPass = TRUE;
  630. for( ilog = 0, cEntriesSeen = 0;
  631. cEntriesSeen < cEntries;
  632. ilog = _sector.ReadLogEntry(ilog)->ilogNext, cEntriesSeen++ )
  633. {
  634. // Ensure that the index is within range
  635. if( ilog >= cEntries )
  636. {
  637. TrkLog(( TRKDBG_ERROR, TEXT("Invalid index in log on volume %c (%d)"), _tcVolume, ilog ));
  638. goto Exit;
  639. }
  640. // We should never see index zero after the first pass through this
  641. // for loop. If we do, then we have a cycle.
  642. if( fFirstPass )
  643. fFirstPass = FALSE;
  644. else if( 0 == ilog )
  645. {
  646. // We have a cycle
  647. TrkLog(( TRKDBG_ERROR, TEXT( "Forward cycle in log file on volume %c (%d of %d entries)"),
  648. _tcVolume, ilog - 1, cEntries ));
  649. goto Exit;
  650. }
  651. } // for( ilog = 0; ilog < cEntries; ilog = GetLogEntry(ilog)->ilogNext )
  652. // If the forward pointers are valid, we should have arrived back where
  653. // we started ... at index 0.
  654. if( 0 != ilog )
  655. {
  656. TrkLog(( TRKDBG_ERROR, TEXT( "Forward cycle in log file on volume %c"), _tcVolume ));
  657. goto Exit;
  658. }
  659. // -----------------------
  660. // Check backward pointers
  661. // -----------------------
  662. // Walk through the prev pointers. This time, we needn't check
  663. // the headers.
  664. fFirstPass = TRUE;
  665. for( ilog = 0, cEntriesSeen = 0;
  666. cEntriesSeen < cEntries;
  667. ilog = _sector.ReadLogEntry(ilog)->ilogPrevious, cEntriesSeen++ )
  668. {
  669. // Again, we should never see index zero after the first pass
  670. if( fFirstPass )
  671. fFirstPass = FALSE;
  672. else if( 0 == ilog )
  673. {
  674. TrkLog(( TRKDBG_ERROR, TEXT( "Backward cycle in log file on volume %c (%d of %d entries)"),
  675. _tcVolume, ilog - 1, cEntries ));
  676. goto Exit;
  677. }
  678. } // for( ilog = 0; ilog < cEntries; ilog = GetLogEntry(ilog)->ilogPrevious )
  679. // Ensure that we got back to where we started.
  680. if( 0 != ilog )
  681. {
  682. TrkLog(( TRKDBG_ERROR, TEXT( "Backward cycle in log file on volume %c"), _tcVolume ));
  683. goto Exit;
  684. }
  685. // If we reach this point, the log was valid.
  686. fValid = TRUE;
  687. // ----
  688. // Exit
  689. // ----
  690. Exit:
  691. return( fValid );
  692. } // CLogFile::Validate()
  693. //+----------------------------------------------------------------------------
  694. //
  695. // Method: CreateNewLog (private)
  696. //
  697. // Synopsis: Create and initialize a new log file. The file is created
  698. // so that only Administrators and the system can access it.
  699. //
  700. // Arguments: [ptszLogFile] (in)
  701. // The name of the log file.
  702. //
  703. // Returns: None
  704. //
  705. //+----------------------------------------------------------------------------
  706. void
  707. CLogFile::CreateAlwaysFile( const TCHAR *ptszTempName )
  708. {
  709. NTSTATUS status = STATUS_SUCCESS;
  710. // --------------
  711. // Initialization
  712. // --------------
  713. HRESULT hr = S_OK;
  714. DWORD dw = 0;
  715. DWORD dwWritten = 0;
  716. ULONG iLogEntry = 0;
  717. LogEntry *ple;
  718. // --------------------------------------
  719. // Create and Initialize the New Log File
  720. // --------------------------------------
  721. status = CreateAlwaysSecureFile(ptszTempName);
  722. if( !NT_SUCCESS(status) )
  723. TrkRaiseException( status );
  724. // ------------------------------
  725. // Initialize the CLogFile object
  726. // ------------------------------
  727. // Initialize the file. Sets _cEntriesInFile.
  728. if( !SetSize( _pcTrkWksConfiguration->GetMinLogKB() * 1024 ))
  729. {
  730. TrkLog(( TRKDBG_ERROR, TEXT("Failed SetSize of %s"), ptszTempName ));
  731. TrkRaiseLastError();
  732. }
  733. // We must always have at least 2 entries; an entry to hold data, and a margin
  734. // entry.
  735. if( 2 >= _cEntriesInFile )
  736. {
  737. TrkLog((TRKDBG_ERROR, TEXT("Log file is too small (%d entries)"), _cEntriesInFile));
  738. TrkRaiseException( E_FAIL );
  739. }
  740. // Initialize the log header and sector
  741. _header.OnCreate( _hFile );
  742. _sector.OnCreate( _hFile );
  743. InitializeLogEntries( 0, _cEntriesInFile - 1 );
  744. // Since all the clients of the logfile have been notified, we can set the
  745. // shutdown flag. This causes the first flush. If we crash prior to this point,
  746. // a subsequent open attempt will find a corrupt file and re-create it.
  747. SetShutdown( TRUE );
  748. // Close the file
  749. CloseLog();
  750. } // CLogFile::CreateNewLog
  751. //+----------------------------------------------------------------------------
  752. //
  753. // Method: OpenExistingFile (derived from PRobustlyCreateableFile)
  754. //
  755. // Synopsis: Open the log file and use it to initialize our state
  756. // (such as indices).
  757. //
  758. // Arguments: [ptszFile] (in)
  759. // The name of the log file.
  760. //
  761. // Returns: [RCF_RESULT]
  762. // OK - file now open
  763. // CORRUPT - file corrupt (and closed)
  764. // NOT_FOUND - file not found
  765. //
  766. // All other conditions result in an exception..
  767. //
  768. //+----------------------------------------------------------------------------
  769. RCF_RESULT
  770. CLogFile::OpenExistingFile( const TCHAR * ptszFile )
  771. {
  772. // --------------
  773. // Initialization
  774. // --------------
  775. NTSTATUS status = STATUS_SUCCESS;
  776. RCF_RESULT r = OK;
  777. ULONG cEntries = 0;
  778. ULONG cbRead = 0;
  779. SequenceNumber seqMin;
  780. SequenceNumber seqMax;
  781. BOOL fLogEmpty = TRUE;
  782. BOOL fWriteProtected;
  783. LogIndex ilogMin;
  784. LogIndex ilogMax;
  785. LogIndex ilogEntry;
  786. LogEntryHeader entryheader;
  787. const LogMoveNotification *plmn = NULL;
  788. // --------------------------
  789. // Open and Validate the File
  790. // --------------------------
  791. __try
  792. {
  793. // See if the volume is read-only
  794. status = CheckVolumeWriteProtection( _ptszVolumeDeviceName, &fWriteProtected );
  795. if( !NT_SUCCESS(status) )
  796. {
  797. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't check if volume was write-protected for %s"),
  798. ptszFile ));
  799. TrkRaiseNtStatus(status);
  800. }
  801. _fWriteProtected = fWriteProtected;
  802. TrkLog(( TRKDBG_VOLUME, TEXT("Volume is%s write-protected"),
  803. _fWriteProtected ? TEXT("") : TEXT(" not") ));
  804. // Open the file
  805. status = OpenExistingSecureFile( ptszFile, _fWriteProtected );
  806. if( !NT_SUCCESS(status))
  807. {
  808. if (status != STATUS_OBJECT_NAME_NOT_FOUND && status != STATUS_OBJECT_PATH_NOT_FOUND)
  809. {
  810. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open %s"), ptszFile ));
  811. TrkRaiseNtStatus(status);
  812. }
  813. r = NOT_FOUND;
  814. }
  815. // If we didn't find it, see if the pre-beta3 file exists.
  816. if( NOT_FOUND == r )
  817. {
  818. TCHAR tszOldLogAbsoluteName[ MAX_PATH + 1 ];
  819. _tcscpy( tszOldLogAbsoluteName, _ptszVolumeDeviceName );
  820. _tcscat( tszOldLogAbsoluteName, s_tszOldLogFileName );
  821. TrkAssert( _tcslen(tszOldLogAbsoluteName) <= MAX_PATH );
  822. // Try to open the old log file.
  823. status = OpenExistingSecureFile( tszOldLogAbsoluteName, _fWriteProtected );
  824. if( !NT_SUCCESS(status))
  825. __leave; // r == NOT_FOUND
  826. // Move that old file from its current location ("\~secure.nt") to
  827. // the modern location ("\System Volume Information").
  828. TrkLog(( TRKDBG_VOLUME, TEXT("Found pre-beta3 log file, renaming") ));
  829. CDirectoryName dirnameOld, dirnameNew;
  830. TrkVerify( dirnameNew.SetFromFileName( ptszFile ) );
  831. TrkVerify( dirnameOld.SetFromFileName( tszOldLogAbsoluteName ) );
  832. // Create or open the new directory.
  833. status = CreateSecureDirectory( dirnameNew );
  834. if( !NT_SUCCESS(status) )
  835. {
  836. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create (%08x) %s"),
  837. status, static_cast<const TCHAR*>(dirnameNew) ));
  838. TrkRaiseNtStatus(status);
  839. }
  840. // Rename the old logfile into the new directory
  841. status = RenameSecureFile( ptszFile ); // To the new directory
  842. if( !NT_SUCCESS(status) )
  843. {
  844. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't rename (%08x) old log file to %s"),
  845. status, ptszFile ));
  846. TrkRaiseNtStatus(status);
  847. }
  848. // If there was a .bak logfile in the old directory, remove it.
  849. _tcscat( tszOldLogAbsoluteName, TEXT(".bak") );
  850. DeleteFile( tszOldLogAbsoluteName );
  851. // Delete the old directory.
  852. RemoveDirectory( dirnameOld );
  853. r = OK;
  854. }
  855. // Oplock the file
  856. TrkAssert( INVALID_HANDLE_VALUE != _heventOplock );
  857. SetOplock();
  858. _header.OnOpen( _hFile );
  859. _sector.OnOpen( _hFile );
  860. // Determine the size of the file.
  861. GetSize();
  862. // Validate the log file. If it's corrupt, the _fRecovering
  863. // flag will be set in the Validate method.
  864. if( !Validate() )
  865. {
  866. r = CORRUPT;
  867. }
  868. }
  869. __finally
  870. {
  871. if( AbnormalTermination() || r == CORRUPT )
  872. {
  873. CloseLog( );
  874. TrkAssert(!IsOpen());
  875. }
  876. }
  877. // ----
  878. // Exit
  879. // ----
  880. return( r );
  881. } // CLog::OpenExistingFile()
  882. //+----------------------------------------------------------------------------
  883. //
  884. // Method: ReadMoveNotification
  885. //
  886. // Synopsis: Return a move notification entry from the log.
  887. //
  888. // Arguments: [ilogEntry] (in)
  889. // The desired entry's index in the physical file.
  890. //
  891. // Returns: [LogMoveNotificatoin]
  892. //
  893. //+----------------------------------------------------------------------------
  894. void
  895. CLogFile::ReadMoveNotification( LogIndex ilogEntry, LogMoveNotification *plmn )
  896. {
  897. const LogEntry *ple = NULL;
  898. // Load the sector which contains this log entry, if necessary
  899. // (also if necessary, this will flush the currently loaded
  900. // sector).
  901. *plmn = _sector.ReadLogEntry( ilogEntry )->move;
  902. return;
  903. } // CLogFile::ReadMoveNotification()
  904. //+----------------------------------------------------------------------------
  905. //
  906. // Method: WriteMoveNotification
  907. //
  908. // Synopsis: Write a move notification entry to the log.
  909. //
  910. // Arguments: [ilogEntry] (in)
  911. // The desired entry's index in the physical file.
  912. // [lmn] (in)
  913. // The move notification data.
  914. // [entryheader] (in)
  915. // The new LogEntryHeader
  916. //
  917. // Returns: None
  918. //
  919. //+----------------------------------------------------------------------------
  920. void
  921. CLogFile::WriteMoveNotification( LogIndex ilogEntry,
  922. const LogMoveNotification &lmn,
  923. const LogEntryHeader &entryheader )
  924. {
  925. // Load the sector from the file, put the new data into it,
  926. // and flush it.
  927. _sector.WriteMoveNotification( ilogEntry, lmn );
  928. _sector.WriteEntryHeader( ilogEntry, entryheader );
  929. _sector.Flush( );
  930. } // CLogFile::WriteMoveNotification()
  931. //+----------------------------------------------------------------------------
  932. //
  933. // Method: ReadEntryHeader
  934. //
  935. // Purpose: Get the log header, which is stored in the sector of a given
  936. // log entry.
  937. //
  938. // Arguments: [ilogEntry] (in)
  939. // The entry whose sector's header is to be loaded.
  940. //
  941. // Returns: [LogHeader]
  942. //
  943. //+----------------------------------------------------------------------------
  944. LogEntryHeader
  945. CLogFile::ReadEntryHeader( LogIndex ilogEntry )
  946. {
  947. return _sector.ReadEntryHeader( ilogEntry );
  948. } // CLogFile::ReadEntryHeader()
  949. //+----------------------------------------------------------------------------
  950. //
  951. // Method: WriteEntryHeader
  952. //
  953. // Purpose: Write the log header, which is stored in the sector of a given
  954. // log entry.
  955. //
  956. // Arguments: [ilogEntry] (in)
  957. // The entry whose sector's header is to be loaded.
  958. // [logheader]
  959. // The new header.
  960. //
  961. // Returns: None.
  962. //
  963. //+----------------------------------------------------------------------------
  964. void
  965. CLogFile::WriteEntryHeader( LogIndex ilogEntry, const LogEntryHeader &EntryHeader )
  966. {
  967. _sector.WriteEntryHeader( ilogEntry, EntryHeader );
  968. } // CLogFile::WriteEntryHeader()
  969. //+----------------------------------------------------------------------------
  970. //
  971. // Method: ReadExtendedHeader
  972. //
  973. // Purpose: Get a portion of the extended log header. The offset is
  974. // relative to the extended portion of the header, not to the
  975. // start of the sector.
  976. //
  977. // Arguments: [iOffset] (in)
  978. // The offset into the header.
  979. // [pv] (out)
  980. // The buffer to which to read.
  981. // [cb] (in)
  982. // The amount to read.
  983. //
  984. // Returns: None
  985. //
  986. //+----------------------------------------------------------------------------
  987. void
  988. CLogFile::ReadExtendedHeader( ULONG iOffset, void *pv, ULONG cb )
  989. {
  990. __try
  991. {
  992. ULONG cbRead;
  993. _header.ReadExtended( iOffset, pv, cb );
  994. }
  995. __finally
  996. {
  997. if( AbnormalTermination() )
  998. memset( pv, 0, cb );
  999. }
  1000. } // CLogFile::ReadEntryHeader()
  1001. //+----------------------------------------------------------------------------
  1002. //
  1003. // Method: WriteExtendedHeader
  1004. //
  1005. // Purpose: Write to the extended portion of the log header.
  1006. // log entry. The offset is
  1007. // relative to the extended portion of the header, not to the
  1008. // start of the sector.
  1009. //
  1010. // Arguments: [iOffset] (in)
  1011. // The entry whose sector's header is to be loaded.
  1012. // [pv] (in)
  1013. // The data to be written
  1014. // [cb] (in)
  1015. // The size of the buffer to be written
  1016. //
  1017. // Returns: None.
  1018. //
  1019. //+----------------------------------------------------------------------------
  1020. void
  1021. CLogFile::WriteExtendedHeader( ULONG iOffset, const void *pv, ULONG cb )
  1022. {
  1023. ULONG cbWritten;
  1024. _header.WriteExtended( iOffset, pv, cb );
  1025. } // CLogFile::WriteExtendedHeader()
  1026. //+----------------------------------------------------------------------------
  1027. //
  1028. // Method: AdjustLogIndex
  1029. //
  1030. // Synopsis: Moves a log entry index (requiring a linked-list
  1031. // traversal). We adjust by the number of entries specified
  1032. // by the caller, or until we reach an optionally-provided limiting index,
  1033. // whichever comes first.
  1034. //
  1035. // Arguments: [pilog] (in/out)
  1036. // A pointer to the index to be adjusted.
  1037. // [iDelta] (in)
  1038. // The amount to adjust.
  1039. // [adjustLimitEnum] (in)
  1040. // If set, then abide by ilogLimit.
  1041. // [ilogLimit] (in)
  1042. // Stop if we reach this index.
  1043. //
  1044. // Returns: None.
  1045. //
  1046. //+----------------------------------------------------------------------------
  1047. void
  1048. CLogFile::AdjustLogIndex( LogIndex *pilog, LONG iDelta,
  1049. AdjustLimitEnum adjustLimitEnum, LogIndex ilogLimit )
  1050. {
  1051. LogIndex ilogEntry = *pilog;
  1052. LogIndex ilogEntryNew;
  1053. while( iDelta != 0
  1054. &&
  1055. ( ilogEntry != ilogLimit || ADJUST_WITHOUT_LIMIT == adjustLimitEnum ) )
  1056. {
  1057. if( iDelta > 0 )
  1058. {
  1059. ilogEntryNew = _sector.ReadLogEntry( ilogEntry )->ilogNext;
  1060. iDelta--;
  1061. }
  1062. else
  1063. {
  1064. ilogEntryNew = _sector.ReadLogEntry( ilogEntry )->ilogPrevious;
  1065. iDelta++;
  1066. }
  1067. if( ilogEntryNew >= _cEntriesInFile || ilogEntryNew == ilogEntry )
  1068. {
  1069. TrkLog(( TRKDBG_ERROR, TEXT("Invalid Next index in log file (%d, %d)"), ilogEntry, ilogEntryNew ));
  1070. TrkRaiseException( TRK_E_CORRUPT_LOG );
  1071. }
  1072. ilogEntry = ilogEntryNew;
  1073. }
  1074. *pilog = ilogEntry;
  1075. } // CLogFile::AdjustLogIndex()
  1076. //+----------------------------------------------------------------------------
  1077. //
  1078. // Method: SetSize (private)
  1079. //
  1080. // Synopsis: Sets the size of the log file.
  1081. //
  1082. // Arguments: [cbLogFile] (in)
  1083. // The new file size.
  1084. //
  1085. // Returns: TRUE iff successfully. Sets GetLastError otherwise.
  1086. //
  1087. //+----------------------------------------------------------------------------
  1088. BOOL
  1089. CLogFile::SetSize( DWORD cbLogFile )
  1090. {
  1091. TrkAssert( cbLogFile >= 2 * _cbLogSector );
  1092. if ( 0xFFFFFFFF == SetFilePointer( cbLogFile, NULL, FILE_BEGIN )
  1093. ||
  1094. !SetEndOfFile() )
  1095. {
  1096. TrkLog((TRKDBG_ERROR, TEXT("Couldn't reset log file size to %lu (%08x)"),
  1097. cbLogFile, HRESULT_FROM_WIN32(GetLastError()) ));
  1098. return( FALSE );
  1099. }
  1100. _cbLogFile = cbLogFile;
  1101. CalcNumEntriesInFile(); // Sets _cEntriesInFile
  1102. return( TRUE );
  1103. } // CLogFile::SetSize()
  1104. //+----------------------------------------------------------------------------
  1105. //
  1106. // CLogFile::DoWork
  1107. //
  1108. // Called when the log file's oplock breaks. This calls up to the
  1109. // host CVolume, which in turn calls and closes all handles.
  1110. //
  1111. //+----------------------------------------------------------------------------
  1112. void
  1113. CLogFile::DoWork()
  1114. {
  1115. NTSTATUS status;
  1116. TrkLog(( TRKDBG_VOLUME,
  1117. TEXT("Logfile oplock broken for %c:\n")
  1118. TEXT(" (info=0x%08x, status=0x%08x, _hreg=%x, this=%p)"),
  1119. _tcVolume,
  1120. _iosbOplock.Status,
  1121. _iosbOplock.Information,
  1122. _hRegisterWaitForSingleObjectEx,
  1123. this ));
  1124. // This is a register-once registration, so we must unregister it now.
  1125. // Unregister with no completion event, since this would cause us to hang
  1126. // (we're executing in the wait thread upon which it would wait).
  1127. UnregisterOplockFromThreadPool( NULL );
  1128. if( STATUS_CANCELLED == _iosbOplock.Status )
  1129. {
  1130. // The thread on which the oplock was created is gone.
  1131. // We should be running on an IO thread now, so reset
  1132. // the oplock.
  1133. SetOplock();
  1134. }
  1135. else if( !NT_SUCCESS(_iosbOplock.Status) )
  1136. {
  1137. TrkLog(( TRKDBG_WARNING, TEXT("Oplock failed (0x%08x)"), _iosbOplock.Status ));
  1138. }
  1139. else
  1140. {
  1141. // The oplock broke because someone tried to open the
  1142. // log file.
  1143. _pLogFileNotify->OnHandlesMustClose();
  1144. }
  1145. }
  1146. void
  1147. CLogFile::SetOplock()
  1148. {
  1149. NTSTATUS status;
  1150. TrkAssert( INVALID_HANDLE_VALUE != _heventOplock );
  1151. TrkAssert( NULL == _hRegisterWaitForSingleObjectEx );
  1152. RegisterOplockWithThreadPool();
  1153. status = NtFsControlFile(
  1154. _hFile,
  1155. _heventOplock,
  1156. NULL, NULL,
  1157. &_iosbOplock,
  1158. FSCTL_REQUEST_BATCH_OPLOCK,
  1159. NULL, 0, NULL, 0 );
  1160. if( STATUS_PENDING != status )
  1161. {
  1162. TrkLog(( TRKDBG_WARNING, TEXT("Couldn't oplock logfile (%08x)"), status ));
  1163. }
  1164. else
  1165. TrkLog(( TRKDBG_VOLUME, TEXT("Log file oplocked") ));
  1166. }
  1167. //+----------------------------------------------------------------------------
  1168. //
  1169. // Method: GetSize (private)
  1170. //
  1171. // Synopsis: Determine the size of the log file.
  1172. //
  1173. // Arguments: None
  1174. //
  1175. // Returns: None
  1176. //
  1177. //+----------------------------------------------------------------------------
  1178. void
  1179. CLogFile::GetSize()
  1180. {
  1181. ULONG cbFile = 0;
  1182. cbFile = GetFileSize( );
  1183. if( 0xffffffff == cbFile )
  1184. {
  1185. TrkLog((TRKDBG_ERROR, TEXT("Couldn't get log file size")));
  1186. TrkRaiseLastError( );
  1187. }
  1188. _cbLogFile = cbFile;
  1189. CalcNumEntriesInFile(); // Sets _cEntriesInFile
  1190. } // CLogFile::GetSize()
  1191. //+----------------------------------------------------------------------------
  1192. //
  1193. // CLogFile::RegisterOplockWithThreadPool
  1194. //
  1195. // Register the logfile oplock with the thread pool. When this event fires,
  1196. // we need to close the log.
  1197. //
  1198. //+----------------------------------------------------------------------------
  1199. void
  1200. CLogFile::RegisterOplockWithThreadPool()
  1201. {
  1202. if( NULL == _hRegisterWaitForSingleObjectEx )
  1203. {
  1204. // Between the time the event was previously unregistered and the
  1205. // time the oplocked handle was closed, the event may have signaled.
  1206. // Clear this irrelevant state now.
  1207. ResetEvent( _heventOplock );
  1208. // Register the event with the thread pool.
  1209. _hRegisterWaitForSingleObjectEx
  1210. = TrkRegisterWaitForSingleObjectEx( _heventOplock, ThreadPoolCallbackFunction,
  1211. static_cast<PWorkItem*>(this), INFINITE,
  1212. WT_EXECUTEONLYONCE | WT_EXECUTEINIOTHREAD );
  1213. if( NULL == _hRegisterWaitForSingleObjectEx )
  1214. {
  1215. TrkLog(( TRKDBG_ERROR, TEXT("Failed to register log oplock with thread pool (%lu) for %c:"),
  1216. GetLastError(), _tcVolume ));
  1217. TrkRaiseLastError();
  1218. }
  1219. else
  1220. {
  1221. TrkLog(( TRKDBG_VOLUME, TEXT("Registered log oplock event (h=%p, this=%p)"),
  1222. _hRegisterWaitForSingleObjectEx, this ));
  1223. }
  1224. }
  1225. }
  1226. //+----------------------------------------------------------------------------
  1227. //
  1228. // CLogFile::UnregisterOplockFromThreadPool
  1229. //
  1230. // Unregister the log file oplock from the thread pool. This should be done
  1231. // before closing the log file, so that we don't get an oplock break
  1232. // notification during the close itself.
  1233. //
  1234. //+----------------------------------------------------------------------------
  1235. void
  1236. CLogFile::UnregisterOplockFromThreadPool( HANDLE hCompletionEvent )
  1237. {
  1238. HANDLE hToUnregister1 = NULL;
  1239. HANDLE hToUnregister2 = NULL;
  1240. if( NULL == _hRegisterWaitForSingleObjectEx )
  1241. {
  1242. TrkLog(( TRKDBG_VOLUME, TEXT("No oplock wait to unregister") ));
  1243. return;
  1244. }
  1245. // We don't want to use any kind of locking mechanism, in order to
  1246. // avoid the risk of blocking during the oplock break (while we're
  1247. // in this code, someone on the system is indefinitely blocked).
  1248. // Get the current value of the handle.
  1249. hToUnregister1 = _hRegisterWaitForSingleObjectEx;
  1250. // If the handle hasn't changed between the previous line and the
  1251. // following call, set it to null.
  1252. hToUnregister2 = InterlockedCompareExchangePointer( &_hRegisterWaitForSingleObjectEx,
  1253. NULL,
  1254. hToUnregister1 );
  1255. // If _hRegisterWaitForSingleObjectEx was unchanged as of the previous
  1256. // call, we've got a local copy of it now, and we can safely unregister it.
  1257. if( hToUnregister1 == hToUnregister2 )
  1258. {
  1259. TrkLog(( TRKDBG_VOLUME, TEXT("Unregistering oplock wait (%x, %p)"), hToUnregister2, this ));
  1260. if( !TrkUnregisterWait( hToUnregister2, hCompletionEvent ))
  1261. {
  1262. TrkLog(( TRKDBG_ERROR, TEXT("Failed UnregisterWait for log oplock event (%lu)"),
  1263. GetLastError() ));
  1264. }
  1265. else
  1266. TrkLog(( TRKDBG_VOLUME, TEXT("Unregistered wait for log oplock (%p)"), hToUnregister2 ));
  1267. }
  1268. #if DBG
  1269. else
  1270. {
  1271. TrkLog(( TRKDBG_VOLUME, TEXT("No need to unregister wait for log oplock") ));
  1272. }
  1273. #endif
  1274. }
  1275. //+----------------------------------------------------------------------------
  1276. //
  1277. // Method: Expand
  1278. //
  1279. // Synopsis: Grows the underlying log file, initializes the linked-list
  1280. // entries in this new area, and links the new sub-list into
  1281. // the existing list. The previous end of the list points
  1282. // to the new sub-list, and the last entry in the new
  1283. // sub-list becomes the new end of the total list.
  1284. //
  1285. // Arguments: [cbDelta] (in)
  1286. // The amount by which to grow (rounded down to a sector
  1287. // boundary).
  1288. // [ilogStart] (in)
  1289. //
  1290. //
  1291. // Returns: None
  1292. //
  1293. //+----------------------------------------------------------------------------
  1294. void
  1295. CLogFile::Expand( LogIndex ilogStart )
  1296. {
  1297. ULONG cbLogFileNew;
  1298. ULONG cEntriesOld, cEntriesNew;
  1299. LogIndex ilogEnd = 0, ilogEntry = 0;
  1300. LogHeader logheader;
  1301. // -------------
  1302. // Grow the file
  1303. // -------------
  1304. // Find the end index
  1305. ilogEnd = ilogStart;
  1306. AdjustLogIndex( &ilogEnd, -1 );
  1307. // We'll grow the log file by cbDelta, with a GetLogMaxKB ceiling, and
  1308. // rounded down to an integral number of sectors.
  1309. cbLogFileNew = _cbLogFile + _pcTrkWksConfiguration->GetLogDeltaKB() * 1024;
  1310. cbLogFileNew = min( cbLogFileNew, _pcTrkWksConfiguration->GetMaxLogKB() * 1024 );
  1311. cbLogFileNew = (cbLogFileNew / _cbLogSector) * _cbLogSector;
  1312. TrkAssert( cbLogFileNew > _cbLogFile );
  1313. // Put our current state in the log header, so that we can
  1314. // recover if there is a crash during the remaining code.
  1315. _header.SetExpansionData( _cbLogFile, ilogStart, ilogEnd ); // flush through cache
  1316. // Grow the log file, keeping track of the old and new entry count.
  1317. TrkLog(( TRKDBG_LOG, TEXT("Expanded log on volume %c (%d to %d bytes)"),
  1318. _tcVolume, _cbLogFile, cbLogFileNew ));
  1319. cEntriesOld = _cEntriesInFile;
  1320. if( !SetSize( cbLogFileNew )) // Updates _cEntriesInFile
  1321. {
  1322. LONG lError = GetLastError();
  1323. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't expand log on %c:"), _tcVolume ));
  1324. _header.ClearExpansionData();
  1325. TrkRaiseWin32Error( lError );
  1326. }
  1327. cEntriesNew = _cEntriesInFile;
  1328. TrkAssert( cEntriesNew - cEntriesOld >= 1 );
  1329. // -----------------------------------------------------
  1330. // Initialize the new entries and link into current list
  1331. // -----------------------------------------------------
  1332. // Initialize the new entries
  1333. InitializeLogEntries( cEntriesOld, cEntriesNew - 1 );
  1334. // Link the last new entry and the overall start entry.
  1335. _sector.GetLogEntry( cEntriesNew - 1 )->ilogNext = ilogStart;
  1336. _sector.GetLogEntry( ilogStart )->ilogPrevious = cEntriesNew - 1;
  1337. // Link the end to the first new entry.
  1338. _sector.GetLogEntry( ilogEnd )->ilogNext = cEntriesOld;
  1339. _sector.GetLogEntry( cEntriesOld )->ilogPrevious = ilogEnd;
  1340. // Show that we finished the expansion without crashing.
  1341. _sector.Flush( );
  1342. _header.ClearExpansionData(); // flush through cache
  1343. } // CLogFile::Expand()