1 /* 2 * PROJECT: ReactOS EventLog File Library 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: sdk/lib/evtlib/evtlib.c 5 * PURPOSE: Provides functionality for reading and writing 6 * EventLog files in the NT <= 5.2 (.evt) format. 7 * PROGRAMMERS: Copyright 2005 Saveliy Tretiakov 8 * Michael Martin 9 * Hermes Belusca-Maito 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include "evtlib.h" 15 16 #define NDEBUG 17 #include <debug.h> 18 19 #define EVTLTRACE(...) DPRINT("EvtLib: " __VA_ARGS__) 20 // Once things become stabilized enough, replace all the EVTLTRACE1 by EVTLTRACE 21 #define EVTLTRACE1(...) DPRINT1("EvtLib: " __VA_ARGS__) 22 23 24 /* GLOBALS *******************************************************************/ 25 26 static const EVENTLOGEOF EOFRecord = 27 { 28 sizeof(EOFRecord), 29 0x11111111, 0x22222222, 0x33333333, 0x44444444, 30 0, 0, 0, 0, 31 sizeof(EOFRecord) 32 }; 33 34 /* HELPER FUNCTIONS **********************************************************/ 35 36 static NTSTATUS 37 ReadLogBuffer( 38 IN PEVTLOGFILE LogFile, 39 OUT PVOID Buffer, 40 IN SIZE_T Length, 41 OUT PSIZE_T ReadLength OPTIONAL, 42 IN PLARGE_INTEGER ByteOffset, 43 OUT PLARGE_INTEGER NextOffset OPTIONAL) 44 { 45 NTSTATUS Status; 46 LARGE_INTEGER FileOffset; 47 SIZE_T BufSize; 48 SIZE_T ReadBufLength = 0, OldReadBufLength; 49 50 ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); 51 ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); 52 53 if (ReadLength) 54 *ReadLength = 0; 55 56 if (NextOffset) 57 NextOffset->QuadPart = 0LL; 58 59 /* Read the first part of the buffer */ 60 FileOffset = *ByteOffset; 61 BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); 62 63 Status = LogFile->FileRead(LogFile, 64 &FileOffset, 65 Buffer, 66 BufSize, 67 &ReadBufLength); 68 if (!NT_SUCCESS(Status)) 69 { 70 EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status); 71 return Status; 72 } 73 74 if (Length > BufSize) 75 { 76 OldReadBufLength = ReadBufLength; 77 78 /* 79 * The buffer was splitted in two, its second part 80 * is to be read at the beginning of the log. 81 */ 82 Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); 83 BufSize = Length - BufSize; 84 FileOffset.QuadPart = sizeof(EVENTLOGHEADER); 85 86 Status = LogFile->FileRead(LogFile, 87 &FileOffset, 88 Buffer, 89 BufSize, 90 &ReadBufLength); 91 if (!NT_SUCCESS(Status)) 92 { 93 EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status); 94 return Status; 95 } 96 /* Add the read number of bytes from the first read */ 97 ReadBufLength += OldReadBufLength; 98 } 99 100 if (ReadLength) 101 *ReadLength = ReadBufLength; 102 103 /* We return the offset just after the end of the read buffer */ 104 if (NextOffset) 105 NextOffset->QuadPart = FileOffset.QuadPart + BufSize; 106 107 return Status; 108 } 109 110 static NTSTATUS 111 WriteLogBuffer( 112 IN PEVTLOGFILE LogFile, 113 IN PVOID Buffer, 114 IN SIZE_T Length, 115 OUT PSIZE_T WrittenLength OPTIONAL, 116 IN PLARGE_INTEGER ByteOffset, 117 OUT PLARGE_INTEGER NextOffset OPTIONAL) 118 { 119 NTSTATUS Status; 120 LARGE_INTEGER FileOffset; 121 SIZE_T BufSize; 122 SIZE_T WrittenBufLength = 0, OldWrittenBufLength; 123 124 /* We must have write access to the log file */ 125 ASSERT(!LogFile->ReadOnly); 126 127 /* 128 * It is expected that the log file is already correctly expanded 129 * before we can write in it. Therefore the following assertions hold. 130 */ 131 ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); 132 ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); 133 134 if (WrittenLength) 135 *WrittenLength = 0; 136 137 if (NextOffset) 138 NextOffset->QuadPart = 0LL; 139 140 /* Write the first part of the buffer */ 141 FileOffset = *ByteOffset; 142 BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); 143 144 Status = LogFile->FileWrite(LogFile, 145 &FileOffset, 146 Buffer, 147 BufSize, 148 &WrittenBufLength); 149 if (!NT_SUCCESS(Status)) 150 { 151 EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status); 152 return Status; 153 } 154 155 if (Length > BufSize) 156 { 157 OldWrittenBufLength = WrittenBufLength; 158 159 /* 160 * The buffer was splitted in two, its second part 161 * is written at the beginning of the log. 162 */ 163 Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); 164 BufSize = Length - BufSize; 165 FileOffset.QuadPart = sizeof(EVENTLOGHEADER); 166 167 Status = LogFile->FileWrite(LogFile, 168 &FileOffset, 169 Buffer, 170 BufSize, 171 &WrittenBufLength); 172 if (!NT_SUCCESS(Status)) 173 { 174 EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status); 175 return Status; 176 } 177 /* Add the written number of bytes from the first write */ 178 WrittenBufLength += OldWrittenBufLength; 179 180 /* The log wraps */ 181 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; 182 } 183 184 if (WrittenLength) 185 *WrittenLength = WrittenBufLength; 186 187 /* We return the offset just after the end of the written buffer */ 188 if (NextOffset) 189 NextOffset->QuadPart = FileOffset.QuadPart + BufSize; 190 191 return Status; 192 } 193 194 195 /* Returns 0 if nothing is found */ 196 static ULONG 197 ElfpOffsetByNumber( 198 IN PEVTLOGFILE LogFile, 199 IN ULONG RecordNumber) 200 { 201 UINT i; 202 203 for (i = 0; i < LogFile->OffsetInfoNext; i++) 204 { 205 if (LogFile->OffsetInfo[i].EventNumber == RecordNumber) 206 return LogFile->OffsetInfo[i].EventOffset; 207 } 208 return 0; 209 } 210 211 #define OFFSET_INFO_INCREMENT 64 212 213 static BOOL 214 ElfpAddOffsetInformation( 215 IN PEVTLOGFILE LogFile, 216 IN ULONG ulNumber, 217 IN ULONG ulOffset) 218 { 219 PVOID NewOffsetInfo; 220 221 if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize) 222 { 223 /* Allocate a new offset table */ 224 NewOffsetInfo = LogFile->Allocate((LogFile->OffsetInfoSize + OFFSET_INFO_INCREMENT) * 225 sizeof(EVENT_OFFSET_INFO), 226 HEAP_ZERO_MEMORY, 227 TAG_ELF); 228 if (!NewOffsetInfo) 229 { 230 EVTLTRACE1("Cannot reallocate heap.\n"); 231 return FALSE; 232 } 233 234 /* Free the old offset table and use the new one */ 235 if (LogFile->OffsetInfo) 236 { 237 /* Copy the handles from the old table to the new one */ 238 RtlCopyMemory(NewOffsetInfo, 239 LogFile->OffsetInfo, 240 LogFile->OffsetInfoSize * sizeof(EVENT_OFFSET_INFO)); 241 LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF); 242 } 243 LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo; 244 LogFile->OffsetInfoSize += OFFSET_INFO_INCREMENT; 245 } 246 247 LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber; 248 LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset; 249 LogFile->OffsetInfoNext++; 250 251 return TRUE; 252 } 253 254 static BOOL 255 ElfpDeleteOffsetInformation( 256 IN PEVTLOGFILE LogFile, 257 IN ULONG ulNumberMin, 258 IN ULONG ulNumberMax) 259 { 260 UINT i; 261 262 if (ulNumberMin > ulNumberMax) 263 return FALSE; 264 265 /* Remove records ulNumberMin to ulNumberMax inclusive */ 266 while (ulNumberMin <= ulNumberMax) 267 { 268 /* 269 * As the offset information is listed in increasing order, and we want 270 * to keep the list without holes, we demand that ulNumberMin is the first 271 * element in the list. 272 */ 273 if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber) 274 return FALSE; 275 276 /* 277 * RtlMoveMemory(&LogFile->OffsetInfo[0], 278 * &LogFile->OffsetInfo[1], 279 * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1)); 280 */ 281 for (i = 0; i < LogFile->OffsetInfoNext - 1; i++) 282 { 283 LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber; 284 LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset; 285 } 286 LogFile->OffsetInfoNext--; 287 288 /* Go to the next offset information */ 289 ulNumberMin++; 290 } 291 292 return TRUE; 293 } 294 295 296 static NTSTATUS 297 ElfpInitNewFile( 298 IN PEVTLOGFILE LogFile, 299 IN ULONG FileSize, 300 IN ULONG MaxSize, 301 IN ULONG Retention) 302 { 303 NTSTATUS Status; 304 LARGE_INTEGER FileOffset; 305 SIZE_T WrittenLength; 306 EVENTLOGEOF EofRec; 307 308 /* Initialize the event log header */ 309 RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER)); 310 311 LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER); 312 LogFile->Header.Signature = LOGFILE_SIGNATURE; 313 LogFile->Header.MajorVersion = MAJORVER; 314 LogFile->Header.MinorVersion = MINORVER; 315 316 /* Set the offset to the oldest record */ 317 LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); 318 /* Set the offset to the ELF_EOF_RECORD */ 319 LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER); 320 /* Set the number of the next record that will be added */ 321 LogFile->Header.CurrentRecordNumber = 1; 322 /* The event log is empty, there is no record so far */ 323 LogFile->Header.OldestRecordNumber = 0; 324 325 // FIXME: Windows' EventLog log file sizes are always multiple of 64kB 326 // but that does not mean the real log size is == file size. 327 328 /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */ 329 LogFile->Header.MaxSize = ROUND_UP(MaxSize, sizeof(ULONG)); 330 LogFile->CurrentSize = LogFile->Header.MaxSize; // or: FileSize ?? 331 LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); 332 333 LogFile->Header.Flags = 0; 334 LogFile->Header.Retention = Retention; 335 LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER); 336 337 /* Write the header */ 338 FileOffset.QuadPart = 0LL; 339 Status = LogFile->FileWrite(LogFile, 340 &FileOffset, 341 &LogFile->Header, 342 sizeof(EVENTLOGHEADER), 343 &WrittenLength); 344 if (!NT_SUCCESS(Status)) 345 { 346 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 347 return Status; 348 } 349 350 /* Initialize the ELF_EOF_RECORD and write it */ 351 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); 352 EofRec.BeginRecord = LogFile->Header.StartOffset; 353 EofRec.EndRecord = LogFile->Header.EndOffset; 354 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; 355 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; 356 357 Status = LogFile->FileWrite(LogFile, 358 NULL, 359 &EofRec, 360 sizeof(EofRec), 361 &WrittenLength); 362 if (!NT_SUCCESS(Status)) 363 { 364 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 365 return Status; 366 } 367 368 Status = LogFile->FileFlush(LogFile, NULL, 0); 369 if (!NT_SUCCESS(Status)) 370 { 371 EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status); 372 return Status; 373 } 374 375 return STATUS_SUCCESS; 376 } 377 378 static NTSTATUS 379 ElfpInitExistingFile( 380 IN PEVTLOGFILE LogFile, 381 IN ULONG FileSize, 382 // IN ULONG MaxSize, 383 IN ULONG Retention) 384 { 385 NTSTATUS Status; 386 LARGE_INTEGER FileOffset, NextOffset; 387 SIZE_T ReadLength; 388 ULONG RecordNumber = 0; 389 ULONG RecOffset; 390 PULONG pRecSize2; 391 EVENTLOGEOF EofRec; 392 EVENTLOGRECORD RecBuf; 393 PEVENTLOGRECORD pRecBuf; 394 BOOLEAN Wrapping = FALSE; 395 BOOLEAN IsLogDirty = FALSE; 396 397 /* Read the log header */ 398 FileOffset.QuadPart = 0LL; 399 Status = LogFile->FileRead(LogFile, 400 &FileOffset, 401 &LogFile->Header, 402 sizeof(EVENTLOGHEADER), 403 &ReadLength); 404 if (!NT_SUCCESS(Status)) 405 { 406 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); 407 return STATUS_EVENTLOG_FILE_CORRUPT; // return Status; 408 } 409 if (ReadLength != sizeof(EVENTLOGHEADER)) 410 { 411 EVTLTRACE("Invalid file `%wZ'.\n", &LogFile->FileName); 412 return STATUS_EVENTLOG_FILE_CORRUPT; 413 } 414 415 /* Header validity checks */ 416 417 if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) || 418 LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER)) 419 { 420 EVTLTRACE("Invalid header size in `%wZ'.\n", &LogFile->FileName); 421 return STATUS_EVENTLOG_FILE_CORRUPT; 422 } 423 424 if (LogFile->Header.Signature != LOGFILE_SIGNATURE) 425 { 426 EVTLTRACE("Invalid signature %x in `%wZ'.\n", 427 LogFile->Header.Signature, &LogFile->FileName); 428 return STATUS_EVENTLOG_FILE_CORRUPT; 429 } 430 431 IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY); 432 433 /* If the log is read-only (e.g. a backup log) and is dirty, then it is corrupted */ 434 if (LogFile->ReadOnly && IsLogDirty) 435 { 436 EVTLTRACE("Read-only log `%wZ' is dirty.\n", &LogFile->FileName); 437 return STATUS_EVENTLOG_FILE_CORRUPT; 438 } 439 440 LogFile->CurrentSize = FileSize; 441 // FIXME!! What to do? And what to do if the MaxSize from the registry 442 // is strictly less than the CurrentSize?? Should we "reduce" the log size 443 // by clearing it completely?? 444 // --> ANSWER: Save the new MaxSize somewhere, and only when the log is 445 // being cleared, use the new MaxSize to resize (ie. shrink) it. 446 // LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); 447 448 /* Adjust the log maximum size if needed */ 449 if (LogFile->CurrentSize > LogFile->Header.MaxSize) 450 LogFile->Header.MaxSize = LogFile->CurrentSize; 451 452 /* 453 * Reset the log retention value. The value stored 454 * in the log file is just for information purposes. 455 */ 456 LogFile->Header.Retention = Retention; 457 458 /* 459 * For a non-read-only dirty log, the most up-to-date information about 460 * the Start/End offsets and the Oldest and Current event record numbers 461 * are found in the EOF record. We need to locate the EOF record without 462 * relying on the log header's EndOffset, then patch the log header with 463 * the values from the EOF record. 464 */ 465 if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) && 466 (LogFile->Header.EndOffset < LogFile->CurrentSize) && 467 (LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0 468 { 469 /* The header EOF offset may be valid, try to start with it */ 470 RecOffset = LogFile->Header.EndOffset; 471 } 472 else 473 { 474 /* The header EOF offset could not be valid, so start from the beginning */ 475 RecOffset = sizeof(EVENTLOGHEADER); 476 } 477 478 FileOffset.QuadPart = RecOffset; 479 Wrapping = FALSE; 480 481 for (;;) 482 { 483 if (Wrapping && FileOffset.QuadPart >= RecOffset) 484 { 485 EVTLTRACE1("EOF record not found!\n"); 486 return STATUS_EVENTLOG_FILE_CORRUPT; 487 } 488 489 /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */ 490 Status = ReadLogBuffer(LogFile, 491 &EofRec, 492 EVENTLOGEOF_SIZE_FIXED, 493 &ReadLength, 494 &FileOffset, 495 NULL); 496 if (!NT_SUCCESS(Status)) 497 { 498 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); 499 return STATUS_EVENTLOG_FILE_CORRUPT; 500 } 501 if (ReadLength != EVENTLOGEOF_SIZE_FIXED) 502 { 503 EVTLTRACE1("Cannot read at most an EOF record!\n"); 504 return STATUS_EVENTLOG_FILE_CORRUPT; 505 } 506 507 /* Is it an EVENTLOGEOF record? */ 508 if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED) 509 { 510 DPRINT("Found EOF record at %llx\n", FileOffset.QuadPart); 511 512 /* Got it! Break the loop and continue */ 513 break; 514 } 515 516 /* No, continue looping */ 517 if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord)) 518 FileOffset.QuadPart += sizeof(ULONG); 519 else 520 if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) 521 FileOffset.QuadPart += 2*sizeof(ULONG); 522 else 523 if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) 524 FileOffset.QuadPart += 3*sizeof(ULONG); 525 else 526 if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) 527 FileOffset.QuadPart += 4*sizeof(ULONG); 528 else 529 FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED 530 531 if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */) 532 { 533 /* Wrap the offset */ 534 FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER); 535 Wrapping = TRUE; 536 } 537 } 538 /* 539 * The only way to be there is to have found a valid EOF record. 540 * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT 541 * was returned. 542 */ 543 544 /* Read the full EVENTLOGEOF (may wrap) and validate it */ 545 Status = ReadLogBuffer(LogFile, 546 &EofRec, 547 sizeof(EofRec), 548 &ReadLength, 549 &FileOffset, 550 NULL); 551 if (!NT_SUCCESS(Status)) 552 { 553 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); 554 return STATUS_EVENTLOG_FILE_CORRUPT; 555 } 556 if (ReadLength != sizeof(EofRec)) 557 { 558 EVTLTRACE1("Cannot read the full EOF record!\n"); 559 return STATUS_EVENTLOG_FILE_CORRUPT; 560 } 561 562 /* Complete validity checks */ 563 if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) || 564 (EofRec.EndRecord != FileOffset.QuadPart)) 565 { 566 DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected 0x%x 0x%x!\n", 567 FileOffset.QuadPart, 568 EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning, 569 EofRec.EndRecord, FileOffset.QuadPart, 570 EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning); 571 DPRINT1("RecordSizeEnd = 0x%x\n", EofRec.RecordSizeEnd); 572 DPRINT1("RecordSizeBeginning = 0x%x\n", EofRec.RecordSizeBeginning); 573 DPRINT1("EndRecord = 0x%x\n", EofRec.EndRecord); 574 return STATUS_EVENTLOG_FILE_CORRUPT; 575 } 576 577 /* The EOF record is valid, break the loop and continue */ 578 579 /* If the log is not dirty, the header values should correspond to the EOF ones */ 580 if (!IsLogDirty) 581 { 582 if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) || 583 (LogFile->Header.EndOffset != EofRec.EndRecord) || 584 (LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) || 585 (LogFile->Header.OldestRecordNumber != EofRec.OldestRecordNumber) ) 586 { 587 DPRINT1("\n" 588 "Log header or EOF record is corrupted:\n" 589 " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n" 590 " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n", 591 LogFile->Header.StartOffset, EofRec.BeginRecord, 592 LogFile->Header.EndOffset , EofRec.EndRecord, 593 LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber, 594 LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber); 595 596 return STATUS_EVENTLOG_FILE_CORRUPT; 597 } 598 } 599 600 /* If the log is dirty, patch the log header with the values from the EOF record */ 601 if (!LogFile->ReadOnly && IsLogDirty) 602 { 603 LogFile->Header.StartOffset = EofRec.BeginRecord; 604 LogFile->Header.EndOffset = EofRec.EndRecord; 605 LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber; 606 LogFile->Header.OldestRecordNumber = EofRec.OldestRecordNumber; 607 } 608 609 /* 610 * FIXME! During operations the EOF record is the one that is the most 611 * updated (its Oldest & Current record numbers are always up-to 612 * date) while the ones from the header may be unsync. When closing 613 * (or flushing?) the event log, the header's record numbers get 614 * updated with the same values as the ones stored in the EOF record. 615 */ 616 617 /* Verify Start/End offsets boundaries */ 618 619 if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) || 620 (LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0 621 { 622 EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n", 623 LogFile->Header.StartOffset, &LogFile->FileName); 624 return STATUS_EVENTLOG_FILE_CORRUPT; 625 } 626 if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) || 627 (LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0 628 { 629 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", 630 LogFile->Header.EndOffset, &LogFile->FileName); 631 return STATUS_EVENTLOG_FILE_CORRUPT; 632 } 633 634 if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) && 635 (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) 636 { 637 /* 638 * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF, 639 * it should point to a non-splitted EVENTLOGRECORD. 640 */ 641 EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n", 642 LogFile->Header.StartOffset, &LogFile->FileName); 643 return STATUS_EVENTLOG_FILE_CORRUPT; 644 } 645 646 if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) && 647 (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) 648 { 649 /* 650 * In non-wrapping case, there must be enough space between StartOffset 651 * and EndOffset to contain at least a full EVENTLOGRECORD. 652 */ 653 EVTLTRACE("Invalid start offset 0x%x or end offset 0x%x in `%wZ'.\n", 654 LogFile->Header.StartOffset, LogFile->Header.EndOffset, &LogFile->FileName); 655 return STATUS_EVENTLOG_FILE_CORRUPT; 656 } 657 658 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) 659 { 660 /* 661 * Non-wrapping case: the (wrapping) free space starting at EndOffset 662 * must be able to contain an EVENTLOGEOF. 663 */ 664 if (LogFile->Header.MaxSize - LogFile->Header.EndOffset + 665 LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF)) 666 { 667 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", 668 LogFile->Header.EndOffset, &LogFile->FileName); 669 return STATUS_EVENTLOG_FILE_CORRUPT; 670 } 671 } 672 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) 673 { 674 /* 675 * Wrapping case: the free space between EndOffset and StartOffset 676 * must be able to contain an EVENTLOGEOF. 677 */ 678 if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF)) 679 { 680 EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", 681 LogFile->Header.EndOffset, &LogFile->FileName); 682 return STATUS_EVENTLOG_FILE_CORRUPT; 683 } 684 } 685 686 /* Start enumerating the event records from the beginning */ 687 RecOffset = LogFile->Header.StartOffset; 688 FileOffset.QuadPart = RecOffset; 689 Wrapping = FALSE; 690 691 // // FIXME! FIXME! 692 // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP)) 693 // { 694 // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n"); 695 // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; 696 // } 697 698 DPRINT("StartOffset = 0x%x, EndOffset = 0x%x\n", 699 LogFile->Header.StartOffset, LogFile->Header.EndOffset); 700 701 /* 702 * For non-read-only logs of size < MaxSize, reorganize the events 703 * such that they do not wrap as soon as we write new ones. 704 */ 705 #if 0 706 if (!LogFile->ReadOnly) 707 { 708 pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); 709 if (pRecBuf == NULL) 710 { 711 DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n"); 712 goto Continue; 713 } 714 715 // TODO: Do the job! 716 } 717 718 Continue: 719 720 DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n", 721 LogFile->Header.StartOffset, LogFile->Header.EndOffset); 722 #endif 723 724 while (FileOffset.QuadPart != LogFile->Header.EndOffset) 725 { 726 if (Wrapping && FileOffset.QuadPart >= RecOffset) 727 { 728 /* We have finished enumerating all the event records */ 729 break; 730 } 731 732 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ 733 Status = LogFile->FileRead(LogFile, 734 &FileOffset, 735 &RecBuf, 736 sizeof(RecBuf), 737 &ReadLength); 738 if (!NT_SUCCESS(Status)) 739 { 740 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); 741 return STATUS_EVENTLOG_FILE_CORRUPT; 742 } 743 if (ReadLength != sizeof(RecBuf)) 744 { 745 DPRINT1("Length != sizeof(RecBuf)\n"); 746 break; 747 } 748 749 if (RecBuf.Reserved != LOGFILE_SIGNATURE || 750 RecBuf.Length < sizeof(EVENTLOGRECORD)) 751 { 752 DPRINT1("RecBuf problem\n"); 753 break; 754 } 755 756 /* Allocate a full EVENTLOGRECORD (header + data) */ 757 pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); 758 if (pRecBuf == NULL) 759 { 760 EVTLTRACE1("Cannot allocate heap!\n"); 761 return STATUS_NO_MEMORY; 762 } 763 764 /* Attempt to read the full EVENTLOGRECORD (can wrap) */ 765 Status = ReadLogBuffer(LogFile, 766 pRecBuf, 767 RecBuf.Length, 768 &ReadLength, 769 &FileOffset, 770 &NextOffset); 771 if (!NT_SUCCESS(Status)) 772 { 773 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); 774 LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 775 return STATUS_EVENTLOG_FILE_CORRUPT; 776 } 777 if (ReadLength != RecBuf.Length) 778 { 779 DPRINT1("Oh oh!!\n"); 780 LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 781 break; 782 } 783 784 // /* If OverWrittenRecords is TRUE and this record has already been read */ 785 // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber)) 786 // { 787 // LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 788 // break; 789 // } 790 791 pRecSize2 = (PULONG)((ULONG_PTR)pRecBuf + RecBuf.Length - 4); 792 793 if (*pRecSize2 != RecBuf.Length) 794 { 795 EVTLTRACE1("Invalid RecordSizeEnd of record %d (0x%x) in `%wZ'\n", 796 RecordNumber, *pRecSize2, &LogFile->FileName); 797 LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 798 break; 799 } 800 801 EVTLTRACE("Add new record %d @ offset 0x%x\n", pRecBuf->RecordNumber, FileOffset.QuadPart); 802 803 RecordNumber++; 804 805 if (!ElfpAddOffsetInformation(LogFile, 806 pRecBuf->RecordNumber, 807 FileOffset.QuadPart)) 808 { 809 EVTLTRACE1("ElfpAddOffsetInformation() failed!\n"); 810 LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 811 return STATUS_EVENTLOG_FILE_CORRUPT; 812 } 813 814 LogFile->Free(pRecBuf, 0, TAG_ELF_BUF); 815 816 if (NextOffset.QuadPart == LogFile->Header.EndOffset) 817 { 818 /* We have finished enumerating all the event records */ 819 DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n"); 820 break; 821 } 822 823 /* 824 * If this was the last event record before the end of the log file, 825 * the next one should start at the beginning of the log and the space 826 * between the last event record and the end of the file is padded. 827 */ 828 if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD)) 829 { 830 /* Wrap to the beginning of the log */ 831 DPRINT("Wrap!\n"); 832 NextOffset.QuadPart = sizeof(EVENTLOGHEADER); 833 } 834 835 /* 836 * If the next offset to read is below the current offset, 837 * this means we are wrapping. 838 */ 839 if (FileOffset.QuadPart > NextOffset.QuadPart) 840 { 841 DPRINT("Wrapping = TRUE;\n"); 842 Wrapping = TRUE; 843 } 844 845 /* Move the current offset */ 846 FileOffset = NextOffset; 847 } 848 849 /* If the event log was empty, it will now contain one record */ 850 if (RecordNumber != 0 && LogFile->Header.OldestRecordNumber == 0) 851 LogFile->Header.OldestRecordNumber = 1; 852 853 LogFile->Header.CurrentRecordNumber = RecordNumber + LogFile->Header.OldestRecordNumber; 854 if (LogFile->Header.CurrentRecordNumber == 0) 855 LogFile->Header.CurrentRecordNumber = 1; 856 857 /* Flush the log if it is not read-only */ 858 if (!LogFile->ReadOnly) 859 { 860 Status = ElfFlushFile(LogFile); 861 if (!NT_SUCCESS(Status)) 862 { 863 EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status); 864 return STATUS_EVENTLOG_FILE_CORRUPT; // Status; 865 } 866 } 867 868 return STATUS_SUCCESS; 869 } 870 871 872 /* FUNCTIONS *****************************************************************/ 873 874 NTSTATUS 875 NTAPI 876 ElfCreateFile( 877 IN OUT PEVTLOGFILE LogFile, 878 IN PUNICODE_STRING FileName OPTIONAL, 879 IN ULONG FileSize, 880 IN ULONG MaxSize, 881 IN ULONG Retention, 882 IN BOOLEAN CreateNew, 883 IN BOOLEAN ReadOnly, 884 IN PELF_ALLOCATE_ROUTINE Allocate, 885 IN PELF_FREE_ROUTINE Free, 886 IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize, 887 IN PELF_FILE_WRITE_ROUTINE FileWrite, 888 IN PELF_FILE_READ_ROUTINE FileRead, 889 IN PELF_FILE_FLUSH_ROUTINE FileFlush) // What about Seek ?? 890 { 891 NTSTATUS Status = STATUS_SUCCESS; 892 893 ASSERT(LogFile); 894 895 /* Creating a new log file with the 'ReadOnly' flag set is incompatible */ 896 if (CreateNew && ReadOnly) 897 return STATUS_INVALID_PARAMETER; 898 899 RtlZeroMemory(LogFile, sizeof(*LogFile)); 900 901 LogFile->Allocate = Allocate; 902 LogFile->Free = Free; 903 LogFile->FileSetSize = FileSetSize; 904 LogFile->FileWrite = FileWrite; 905 LogFile->FileRead = FileRead; 906 LogFile->FileFlush = FileFlush; 907 908 /* Copy the log file name if provided (optional) */ 909 RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0); 910 if (FileName && FileName->Buffer && FileName->Length && 911 (FileName->Length <= FileName->MaximumLength)) 912 { 913 LogFile->FileName.Buffer = LogFile->Allocate(FileName->Length, 914 HEAP_ZERO_MEMORY, 915 TAG_ELF); 916 if (LogFile->FileName.Buffer) 917 { 918 LogFile->FileName.MaximumLength = FileName->Length; 919 RtlCopyUnicodeString(&LogFile->FileName, FileName); 920 } 921 } 922 923 LogFile->OffsetInfo = LogFile->Allocate(OFFSET_INFO_INCREMENT * sizeof(EVENT_OFFSET_INFO), 924 HEAP_ZERO_MEMORY, 925 TAG_ELF); 926 if (LogFile->OffsetInfo == NULL) 927 { 928 EVTLTRACE1("Cannot allocate heap\n"); 929 Status = STATUS_NO_MEMORY; 930 goto Quit; 931 } 932 LogFile->OffsetInfoSize = OFFSET_INFO_INCREMENT; 933 LogFile->OffsetInfoNext = 0; 934 935 // FIXME: Always use the regitry values for MaxSize, 936 // even for existing logs! 937 938 // FIXME: On Windows, EventLog uses the MaxSize setting 939 // from the registry itself; the MaxSize from the header 940 // is just for information purposes. 941 942 EVTLTRACE("Initializing log file `%wZ'\n", &LogFile->FileName); 943 944 LogFile->ReadOnly = ReadOnly; // !CreateNew && ReadOnly; 945 946 if (CreateNew) 947 Status = ElfpInitNewFile(LogFile, FileSize, MaxSize, Retention); 948 else 949 Status = ElfpInitExistingFile(LogFile, FileSize, /* MaxSize, */ Retention); 950 951 Quit: 952 if (!NT_SUCCESS(Status)) 953 { 954 if (LogFile->OffsetInfo) 955 LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF); 956 957 if (LogFile->FileName.Buffer) 958 LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF); 959 } 960 961 return Status; 962 } 963 964 NTSTATUS 965 NTAPI 966 ElfReCreateFile( 967 IN PEVTLOGFILE LogFile) 968 { 969 ASSERT(LogFile); 970 971 return ElfpInitNewFile(LogFile, 972 LogFile->CurrentSize, 973 LogFile->Header.MaxSize, 974 LogFile->Header.Retention); 975 } 976 977 NTSTATUS 978 NTAPI 979 ElfBackupFile( 980 IN PEVTLOGFILE LogFile, 981 IN PEVTLOGFILE BackupLogFile) 982 { 983 NTSTATUS Status; 984 985 LARGE_INTEGER FileOffset; 986 SIZE_T ReadLength, WrittenLength; 987 PEVENTLOGHEADER Header; 988 EVENTLOGRECORD RecBuf; 989 EVENTLOGEOF EofRec; 990 ULONG i; 991 ULONG RecOffset; 992 PVOID Buffer = NULL; 993 994 ASSERT(LogFile); 995 996 RtlZeroMemory(BackupLogFile, sizeof(*BackupLogFile)); 997 998 BackupLogFile->FileSetSize = LogFile->FileSetSize; 999 BackupLogFile->FileWrite = LogFile->FileWrite; 1000 BackupLogFile->FileFlush = LogFile->FileFlush; 1001 1002 // BackupLogFile->CurrentSize = LogFile->CurrentSize; 1003 1004 BackupLogFile->ReadOnly = FALSE; 1005 1006 /* Initialize the (dirty) log file header */ 1007 Header = &BackupLogFile->Header; 1008 Header->HeaderSize = sizeof(EVENTLOGHEADER); 1009 Header->Signature = LOGFILE_SIGNATURE; 1010 Header->MajorVersion = MAJORVER; 1011 Header->MinorVersion = MINORVER; 1012 Header->StartOffset = sizeof(EVENTLOGHEADER); 1013 Header->EndOffset = sizeof(EVENTLOGHEADER); 1014 Header->CurrentRecordNumber = 1; 1015 Header->OldestRecordNumber = 0; 1016 Header->MaxSize = LogFile->Header.MaxSize; 1017 Header->Flags = ELF_LOGFILE_HEADER_DIRTY; 1018 Header->Retention = LogFile->Header.Retention; 1019 Header->EndHeaderSize = sizeof(EVENTLOGHEADER); 1020 1021 /* Write the (dirty) log file header */ 1022 FileOffset.QuadPart = 0LL; 1023 Status = BackupLogFile->FileWrite(BackupLogFile, 1024 &FileOffset, 1025 Header, 1026 sizeof(EVENTLOGHEADER), 1027 &WrittenLength); 1028 if (!NT_SUCCESS(Status)) 1029 { 1030 EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status); 1031 goto Quit; 1032 } 1033 1034 for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++) 1035 { 1036 RecOffset = ElfpOffsetByNumber(LogFile, i); 1037 if (RecOffset == 0) 1038 break; 1039 1040 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ 1041 FileOffset.QuadPart = RecOffset; 1042 Status = LogFile->FileRead(LogFile, 1043 &FileOffset, 1044 &RecBuf, 1045 sizeof(RecBuf), 1046 &ReadLength); 1047 if (!NT_SUCCESS(Status)) 1048 { 1049 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); 1050 goto Quit; 1051 } 1052 1053 // if (ReadLength != sizeof(RecBuf)) 1054 // break; 1055 1056 Buffer = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); 1057 if (Buffer == NULL) 1058 { 1059 EVTLTRACE1("Allocate() failed!\n"); 1060 goto Quit; 1061 } 1062 1063 /* Read the full EVENTLOGRECORD (header + data) with wrapping */ 1064 Status = ReadLogBuffer(LogFile, 1065 Buffer, 1066 RecBuf.Length, 1067 &ReadLength, 1068 &FileOffset, 1069 NULL); 1070 if (!NT_SUCCESS(Status)) 1071 { 1072 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); 1073 LogFile->Free(Buffer, 0, TAG_ELF_BUF); 1074 // Status = STATUS_EVENTLOG_FILE_CORRUPT; 1075 goto Quit; 1076 } 1077 1078 /* Write the event record (no wrap for the backup log) */ 1079 Status = BackupLogFile->FileWrite(BackupLogFile, 1080 NULL, 1081 Buffer, 1082 RecBuf.Length, 1083 &WrittenLength); 1084 if (!NT_SUCCESS(Status)) 1085 { 1086 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 1087 LogFile->Free(Buffer, 0, TAG_ELF_BUF); 1088 goto Quit; 1089 } 1090 1091 /* Update the header information */ 1092 Header->EndOffset += RecBuf.Length; 1093 1094 /* Free the buffer */ 1095 LogFile->Free(Buffer, 0, TAG_ELF_BUF); 1096 Buffer = NULL; 1097 } 1098 1099 // Quit: 1100 1101 /* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */ 1102 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); 1103 EofRec.BeginRecord = Header->StartOffset; 1104 EofRec.EndRecord = Header->EndOffset; 1105 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; 1106 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; 1107 1108 Status = BackupLogFile->FileWrite(BackupLogFile, 1109 NULL, 1110 &EofRec, 1111 sizeof(EofRec), 1112 &WrittenLength); 1113 if (!NT_SUCCESS(Status)) 1114 { 1115 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 1116 goto Quit; 1117 } 1118 1119 /* Update the header information */ 1120 Header->CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; 1121 Header->OldestRecordNumber = LogFile->Header.OldestRecordNumber; 1122 Header->MaxSize = ROUND_UP(Header->EndOffset + sizeof(EofRec), sizeof(ULONG)); 1123 Header->Flags = 0; // FIXME? 1124 1125 /* Flush the log file - Write the (clean) log file header */ 1126 Status = ElfFlushFile(BackupLogFile); 1127 1128 Quit: 1129 return Status; 1130 } 1131 1132 NTSTATUS 1133 NTAPI 1134 ElfFlushFile( 1135 IN PEVTLOGFILE LogFile) 1136 { 1137 NTSTATUS Status; 1138 LARGE_INTEGER FileOffset; 1139 SIZE_T WrittenLength; 1140 1141 ASSERT(LogFile); 1142 1143 if (LogFile->ReadOnly) 1144 return STATUS_SUCCESS; // STATUS_ACCESS_DENIED; 1145 1146 /* 1147 * NOTE that both the EOF record *AND* the log file header 1148 * are supposed to be already updated! 1149 * We just remove the dirty log bit. 1150 */ 1151 LogFile->Header.Flags &= ~ELF_LOGFILE_HEADER_DIRTY; 1152 1153 /* Update the log file header */ 1154 FileOffset.QuadPart = 0LL; 1155 Status = LogFile->FileWrite(LogFile, 1156 &FileOffset, 1157 &LogFile->Header, 1158 sizeof(EVENTLOGHEADER), 1159 &WrittenLength); 1160 if (!NT_SUCCESS(Status)) 1161 { 1162 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 1163 return Status; 1164 } 1165 1166 /* Flush the log file */ 1167 Status = LogFile->FileFlush(LogFile, NULL, 0); 1168 if (!NT_SUCCESS(Status)) 1169 { 1170 EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status); 1171 return Status; 1172 } 1173 1174 return STATUS_SUCCESS; 1175 } 1176 1177 VOID 1178 NTAPI 1179 ElfCloseFile( // ElfFree 1180 IN PEVTLOGFILE LogFile) 1181 { 1182 ASSERT(LogFile); 1183 1184 /* Flush the log file */ 1185 ElfFlushFile(LogFile); 1186 1187 /* Free the data */ 1188 LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF); 1189 1190 if (LogFile->FileName.Buffer) 1191 LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF); 1192 RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0); 1193 } 1194 1195 NTSTATUS 1196 NTAPI 1197 ElfReadRecord( 1198 IN PEVTLOGFILE LogFile, 1199 IN ULONG RecordNumber, 1200 OUT PEVENTLOGRECORD Record, 1201 IN SIZE_T BufSize, // Length 1202 OUT PSIZE_T BytesRead OPTIONAL, 1203 OUT PSIZE_T BytesNeeded OPTIONAL) 1204 { 1205 NTSTATUS Status; 1206 LARGE_INTEGER FileOffset; 1207 ULONG RecOffset; 1208 SIZE_T RecSize; 1209 SIZE_T ReadLength; 1210 1211 ASSERT(LogFile); 1212 1213 if (BytesRead) 1214 *BytesRead = 0; 1215 1216 if (BytesNeeded) 1217 *BytesNeeded = 0; 1218 1219 /* Retrieve the offset of the event record */ 1220 RecOffset = ElfpOffsetByNumber(LogFile, RecordNumber); 1221 if (RecOffset == 0) 1222 return STATUS_NOT_FOUND; 1223 1224 /* Retrieve its full size */ 1225 FileOffset.QuadPart = RecOffset; 1226 Status = LogFile->FileRead(LogFile, 1227 &FileOffset, 1228 &RecSize, 1229 sizeof(RecSize), 1230 &ReadLength); 1231 if (!NT_SUCCESS(Status)) 1232 { 1233 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); 1234 // Status = STATUS_EVENTLOG_FILE_CORRUPT; 1235 return Status; 1236 } 1237 1238 /* Check whether the buffer is big enough to hold the event record */ 1239 if (BufSize < RecSize) 1240 { 1241 if (BytesNeeded) 1242 *BytesNeeded = RecSize; 1243 1244 return STATUS_BUFFER_TOO_SMALL; 1245 } 1246 1247 /* Read the event record into the buffer */ 1248 FileOffset.QuadPart = RecOffset; 1249 Status = ReadLogBuffer(LogFile, 1250 Record, 1251 RecSize, 1252 &ReadLength, 1253 &FileOffset, 1254 NULL); 1255 if (!NT_SUCCESS(Status)) 1256 { 1257 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); 1258 // Status = STATUS_EVENTLOG_FILE_CORRUPT; 1259 } 1260 1261 if (BytesRead) 1262 *BytesRead = ReadLength; 1263 1264 return Status; 1265 } 1266 1267 NTSTATUS 1268 NTAPI 1269 ElfWriteRecord( 1270 IN PEVTLOGFILE LogFile, 1271 IN PEVENTLOGRECORD Record, 1272 IN SIZE_T BufSize) 1273 { 1274 NTSTATUS Status; 1275 LARGE_INTEGER FileOffset, NextOffset; 1276 SIZE_T ReadLength, WrittenLength; 1277 EVENTLOGEOF EofRec; 1278 EVENTLOGRECORD RecBuf; 1279 ULONG FreeSpace = 0; 1280 ULONG UpperBound; 1281 ULONG RecOffset, WriteOffset; 1282 1283 ASSERT(LogFile); 1284 1285 if (LogFile->ReadOnly) 1286 return STATUS_ACCESS_DENIED; 1287 1288 // ASSERT(sizeof(*Record) == sizeof(RecBuf)); 1289 1290 if (!Record || BufSize < sizeof(*Record)) 1291 return STATUS_INVALID_PARAMETER; 1292 1293 Record->RecordNumber = LogFile->Header.CurrentRecordNumber; 1294 1295 /* Compute the available log free space */ 1296 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) 1297 FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER); 1298 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) 1299 FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset; 1300 1301 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY; 1302 1303 /* If the event log was empty, it will now contain one record */ 1304 if (LogFile->Header.OldestRecordNumber == 0) 1305 LogFile->Header.OldestRecordNumber = 1; 1306 1307 /* By default we append the new record at the old EOF record offset */ 1308 WriteOffset = LogFile->Header.EndOffset; 1309 1310 /* 1311 * Check whether the log is going to wrap (the events being overwritten). 1312 */ 1313 1314 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) 1315 UpperBound = LogFile->Header.MaxSize; 1316 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) 1317 UpperBound = LogFile->Header.StartOffset; 1318 1319 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec)) 1320 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) 1321 { 1322 EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n" 1323 "UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n", 1324 LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize); 1325 /* This will be done later */ 1326 } 1327 1328 if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) && 1329 (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf)) 1330 { 1331 // ASSERT(UpperBound == LogFile->Header.MaxSize); 1332 // ASSERT(WriteOffset == LogFile->Header.EndOffset); 1333 1334 /* 1335 * We cannot fit the EVENTLOGRECORD header of the buffer before 1336 * the end of the file. We need to pad the end of the log with 1337 * 0x00000027, normally we will need to pad at most 0x37 bytes 1338 * (corresponding to sizeof(EVENTLOGRECORD) - 1). 1339 */ 1340 1341 /* Rewind to the beginning of the log, just after the header */ 1342 WriteOffset = sizeof(EVENTLOGHEADER); 1343 /**/UpperBound = LogFile->Header.StartOffset;/**/ 1344 1345 FreeSpace = LogFile->Header.StartOffset - WriteOffset; 1346 1347 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; 1348 } 1349 /* 1350 * Otherwise, we can fit the header and only part 1351 * of the data will overwrite the oldest records. 1352 * 1353 * It might be possible that all the event record can fit in one piece, 1354 * but that the EOF record needs to be split. This is not a problem, 1355 * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be. 1356 */ 1357 1358 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) 1359 { 1360 ULONG OrgOldestRecordNumber, OldestRecordNumber; 1361 1362 // DPRINT("EventLogFile has reached maximum size, wrapping...\n"); 1363 1364 OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber; 1365 1366 // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record??? 1367 // NOTE: It should be, by construction (and this should have been checked when 1368 // initializing a new, or existing log). 1369 1370 /* 1371 * Determine how many old records need to be overwritten. 1372 * Check the size of the record as the record added may be larger. 1373 * Need to take into account that we append the EOF record. 1374 */ 1375 while (FreeSpace < BufSize + sizeof(EofRec)) 1376 { 1377 /* Get the oldest record data */ 1378 RecOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber); 1379 if (RecOffset == 0) 1380 { 1381 EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber); 1382 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN; 1383 return STATUS_LOG_FILE_FULL; 1384 } 1385 1386 RtlZeroMemory(&RecBuf, sizeof(RecBuf)); 1387 1388 FileOffset.QuadPart = RecOffset; 1389 Status = LogFile->FileRead(LogFile, 1390 &FileOffset, 1391 &RecBuf, 1392 sizeof(RecBuf), 1393 &ReadLength); 1394 if (!NT_SUCCESS(Status)) 1395 { 1396 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); 1397 // Status = STATUS_EVENTLOG_FILE_CORRUPT; 1398 return Status; 1399 } 1400 1401 if (RecBuf.Reserved != LOGFILE_SIGNATURE) 1402 { 1403 EVTLTRACE1("The event log file is corrupted!\n"); 1404 return STATUS_EVENTLOG_FILE_CORRUPT; 1405 } 1406 1407 /* 1408 * Check whether this event can be overwritten by comparing its 1409 * written timestamp with the log's retention value. This value 1410 * is the time interval, in seconds, that events records are 1411 * protected from being overwritten. 1412 * 1413 * If the retention value is zero the events are always overwritten. 1414 * 1415 * If the retention value is non-zero, when the age of an event, 1416 * in seconds, reaches or exceeds this value, it can be overwritten. 1417 * Also if the events are in the future, we do not overwrite them. 1418 */ 1419 if (LogFile->Header.Retention != 0 && 1420 (Record->TimeWritten < RecBuf.TimeWritten || 1421 (Record->TimeWritten >= RecBuf.TimeWritten && 1422 Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention))) 1423 { 1424 EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n"); 1425 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN; 1426 return STATUS_LOG_FILE_FULL; 1427 } 1428 1429 /* 1430 * Advance the oldest record number, add the event record length 1431 * (as long as it is valid...) then take account for the possible 1432 * paddind after the record, in case this is the last one at the 1433 * end of the file. 1434 */ 1435 OldestRecordNumber++; 1436 RecOffset += RecBuf.Length; 1437 FreeSpace += RecBuf.Length; 1438 1439 /* 1440 * If this was the last event record before the end of the log file, 1441 * the next one should start at the beginning of the log and the space 1442 * between the last event record and the end of the file is padded. 1443 */ 1444 if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD)) 1445 { 1446 /* Add the padding size */ 1447 FreeSpace += LogFile->Header.MaxSize - RecOffset; 1448 } 1449 } 1450 1451 EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize); 1452 1453 /* The log records are wrapping */ 1454 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; 1455 1456 1457 // FIXME: May lead to corruption if the other subsequent calls fail... 1458 1459 /* 1460 * We have validated all the region of events to be discarded, 1461 * now we can perform their deletion. 1462 */ 1463 ElfpDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1); 1464 LogFile->Header.OldestRecordNumber = OldestRecordNumber; 1465 LogFile->Header.StartOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber); 1466 if (LogFile->Header.StartOffset == 0) 1467 { 1468 /* 1469 * We have deleted all the existing event records to make place 1470 * for the new one. We can put it at the start of the event log. 1471 */ 1472 LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); 1473 WriteOffset = LogFile->Header.StartOffset; 1474 LogFile->Header.EndOffset = WriteOffset; 1475 } 1476 1477 EVTLTRACE("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n" 1478 "OldestRecordNumber = %d\n", 1479 LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize, 1480 OldestRecordNumber); 1481 } 1482 1483 /* 1484 * Expand the log file if needed. 1485 * NOTE: It may be needed to perform this task a bit sooner if we need 1486 * such a thing for performing read operations, in the future... 1487 * Or if this operation needs to modify 'FreeSpace'... 1488 */ 1489 if (LogFile->CurrentSize < LogFile->Header.MaxSize) 1490 { 1491 EVTLTRACE1("Expanding the log file from %lu to %lu\n", 1492 LogFile->CurrentSize, LogFile->Header.MaxSize); 1493 1494 LogFile->CurrentSize = LogFile->Header.MaxSize; 1495 LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); 1496 } 1497 1498 /* Since we can write events in the log, clear the log full flag */ 1499 LogFile->Header.Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN; 1500 1501 /* Pad the end of the log */ 1502 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize) 1503 if (WriteOffset < LogFile->Header.EndOffset) 1504 { 1505 /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */ 1506 WrittenLength = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG)); 1507 RtlFillMemoryUlong(&RecBuf, WrittenLength, 0x00000027); 1508 1509 FileOffset.QuadPart = LogFile->Header.EndOffset; 1510 Status = LogFile->FileWrite(LogFile, 1511 &FileOffset, 1512 &RecBuf, 1513 WrittenLength, 1514 &WrittenLength); 1515 if (!NT_SUCCESS(Status)) 1516 { 1517 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); 1518 // return Status; 1519 } 1520 } 1521 1522 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */ 1523 FileOffset.QuadPart = WriteOffset; 1524 Status = WriteLogBuffer(LogFile, 1525 Record, 1526 BufSize, 1527 &WrittenLength, 1528 &FileOffset, 1529 &NextOffset); 1530 if (!NT_SUCCESS(Status)) 1531 { 1532 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); 1533 return Status; 1534 } 1535 /* FileOffset now contains the offset just after the end of the record buffer */ 1536 FileOffset = NextOffset; 1537 1538 if (!ElfpAddOffsetInformation(LogFile, 1539 Record->RecordNumber, 1540 WriteOffset)) 1541 { 1542 return STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT; 1543 } 1544 1545 LogFile->Header.CurrentRecordNumber++; 1546 if (LogFile->Header.CurrentRecordNumber == 0) 1547 LogFile->Header.CurrentRecordNumber = 1; 1548 1549 /* 1550 * Write the new EOF record offset just after the event record. 1551 * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF) 1552 * bytes remains between the end of the record and the end of the log file. 1553 */ 1554 LogFile->Header.EndOffset = FileOffset.QuadPart; 1555 1556 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); 1557 EofRec.BeginRecord = LogFile->Header.StartOffset; 1558 EofRec.EndRecord = LogFile->Header.EndOffset; 1559 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; 1560 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; 1561 1562 // FileOffset.QuadPart = LogFile->Header.EndOffset; 1563 Status = WriteLogBuffer(LogFile, 1564 &EofRec, 1565 sizeof(EofRec), 1566 &WrittenLength, 1567 &FileOffset, 1568 &NextOffset); 1569 if (!NT_SUCCESS(Status)) 1570 { 1571 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); 1572 return Status; 1573 } 1574 FileOffset = NextOffset; 1575 1576 /* Flush the log file */ 1577 Status = ElfFlushFile(LogFile); 1578 if (!NT_SUCCESS(Status)) 1579 { 1580 EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status); 1581 return STATUS_EVENTLOG_FILE_CORRUPT; // Status; 1582 } 1583 1584 return Status; 1585 } 1586 1587 ULONG 1588 NTAPI 1589 ElfGetOldestRecord( 1590 IN PEVTLOGFILE LogFile) 1591 { 1592 ASSERT(LogFile); 1593 return LogFile->Header.OldestRecordNumber; 1594 } 1595 1596 ULONG 1597 NTAPI 1598 ElfGetCurrentRecord( 1599 IN PEVTLOGFILE LogFile) 1600 { 1601 ASSERT(LogFile); 1602 return LogFile->Header.CurrentRecordNumber; 1603 } 1604 1605 ULONG 1606 NTAPI 1607 ElfGetFlags( 1608 IN PEVTLOGFILE LogFile) 1609 { 1610 ASSERT(LogFile); 1611 return LogFile->Header.Flags; 1612 } 1613 1614 #if DBG 1615 VOID PRINT_HEADER(PEVENTLOGHEADER Header) 1616 { 1617 ULONG Flags = Header->Flags; 1618 1619 EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header); 1620 1621 DbgPrint("HeaderSize = %lu\n" , Header->HeaderSize); 1622 DbgPrint("Signature = 0x%x\n", Header->Signature); 1623 DbgPrint("MajorVersion = %lu\n" , Header->MajorVersion); 1624 DbgPrint("MinorVersion = %lu\n" , Header->MinorVersion); 1625 DbgPrint("StartOffset = 0x%x\n", Header->StartOffset); 1626 DbgPrint("EndOffset = 0x%x\n", Header->EndOffset); 1627 DbgPrint("CurrentRecordNumber = %lu\n", Header->CurrentRecordNumber); 1628 DbgPrint("OldestRecordNumber = %lu\n", Header->OldestRecordNumber); 1629 DbgPrint("MaxSize = 0x%x\n", Header->MaxSize); 1630 DbgPrint("Retention = 0x%x\n", Header->Retention); 1631 DbgPrint("EndHeaderSize = %lu\n" , Header->EndHeaderSize); 1632 DbgPrint("Flags: "); 1633 if (Flags & ELF_LOGFILE_HEADER_DIRTY) 1634 { 1635 DbgPrint("ELF_LOGFILE_HEADER_DIRTY"); 1636 Flags &= ~ELF_LOGFILE_HEADER_DIRTY; 1637 } 1638 if (Flags) DbgPrint(" | "); 1639 if (Flags & ELF_LOGFILE_HEADER_WRAP) 1640 { 1641 DbgPrint("ELF_LOGFILE_HEADER_WRAP"); 1642 Flags &= ~ELF_LOGFILE_HEADER_WRAP; 1643 } 1644 if (Flags) DbgPrint(" | "); 1645 if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN) 1646 { 1647 DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN"); 1648 Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN; 1649 } 1650 if (Flags) DbgPrint(" | "); 1651 if (Flags & ELF_LOGFILE_ARCHIVE_SET) 1652 { 1653 DbgPrint("ELF_LOGFILE_ARCHIVE_SET"); 1654 Flags &= ~ELF_LOGFILE_ARCHIVE_SET; 1655 } 1656 if (Flags) DbgPrint(" | 0x%x", Flags); 1657 DbgPrint("\n"); 1658 } 1659 #endif 1660