1 /* 2 * PROJECT: ReactOS EventLog Service 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/services/eventlog/file.c 5 * PURPOSE: Event log file support wrappers 6 * COPYRIGHT: Copyright 2005 Saveliy Tretiakov 7 * Michael Martin 8 * Hermes Belusca-Maito 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include "eventlog.h" 14 #include <ndk/iofuncs.h> 15 #include <ndk/kefuncs.h> 16 17 #define NDEBUG 18 #include <debug.h> 19 20 /* LOG FILE LIST - GLOBALS ***************************************************/ 21 22 static LIST_ENTRY LogFileListHead; 23 static CRITICAL_SECTION LogFileListCs; 24 25 /* LOG FILE LIST - FUNCTIONS *************************************************/ 26 27 VOID LogfListInitialize(VOID) 28 { 29 InitializeCriticalSection(&LogFileListCs); 30 InitializeListHead(&LogFileListHead); 31 } 32 33 PLOGFILE LogfListItemByName(LPCWSTR Name) 34 { 35 PLIST_ENTRY CurrentEntry; 36 PLOGFILE Item, Result = NULL; 37 38 ASSERT(Name); 39 40 EnterCriticalSection(&LogFileListCs); 41 42 CurrentEntry = LogFileListHead.Flink; 43 while (CurrentEntry != &LogFileListHead) 44 { 45 Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry); 46 47 if (Item->LogName && !_wcsicmp(Item->LogName, Name)) 48 { 49 Result = Item; 50 break; 51 } 52 53 CurrentEntry = CurrentEntry->Flink; 54 } 55 56 LeaveCriticalSection(&LogFileListCs); 57 return Result; 58 } 59 60 #if 0 61 /* Index starting from 1 */ 62 DWORD LogfListItemIndexByName(LPCWSTR Name) 63 { 64 PLIST_ENTRY CurrentEntry; 65 DWORD Result = 0; 66 DWORD i = 1; 67 68 ASSERT(Name); 69 70 EnterCriticalSection(&LogFileListCs); 71 72 CurrentEntry = LogFileListHead.Flink; 73 while (CurrentEntry != &LogFileListHead) 74 { 75 PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry); 76 77 if (Item->LogName && !_wcsicmp(Item->LogName, Name)) 78 { 79 Result = i; 80 break; 81 } 82 83 CurrentEntry = CurrentEntry->Flink; 84 i++; 85 } 86 87 LeaveCriticalSection(&LogFileListCs); 88 return Result; 89 } 90 #endif 91 92 /* Index starting from 1 */ 93 PLOGFILE LogfListItemByIndex(DWORD Index) 94 { 95 PLIST_ENTRY CurrentEntry; 96 PLOGFILE Result = NULL; 97 DWORD i = 1; 98 99 EnterCriticalSection(&LogFileListCs); 100 101 CurrentEntry = LogFileListHead.Flink; 102 while (CurrentEntry != &LogFileListHead) 103 { 104 if (i == Index) 105 { 106 Result = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry); 107 break; 108 } 109 110 CurrentEntry = CurrentEntry->Flink; 111 i++; 112 } 113 114 LeaveCriticalSection(&LogFileListCs); 115 return Result; 116 } 117 118 DWORD LogfListItemCount(VOID) 119 { 120 PLIST_ENTRY CurrentEntry; 121 DWORD i = 0; 122 123 EnterCriticalSection(&LogFileListCs); 124 125 CurrentEntry = LogFileListHead.Flink; 126 while (CurrentEntry != &LogFileListHead) 127 { 128 CurrentEntry = CurrentEntry->Flink; 129 i++; 130 } 131 132 LeaveCriticalSection(&LogFileListCs); 133 return i; 134 } 135 136 static VOID 137 LogfListAddItem(PLOGFILE Item) 138 { 139 EnterCriticalSection(&LogFileListCs); 140 InsertTailList(&LogFileListHead, &Item->ListEntry); 141 LeaveCriticalSection(&LogFileListCs); 142 } 143 144 static VOID 145 LogfListRemoveItem(PLOGFILE Item) 146 { 147 EnterCriticalSection(&LogFileListCs); 148 RemoveEntryList(&Item->ListEntry); 149 LeaveCriticalSection(&LogFileListCs); 150 } 151 152 153 /* FUNCTIONS *****************************************************************/ 154 155 // PELF_ALLOCATE_ROUTINE 156 static 157 PVOID NTAPI 158 LogfpAlloc(IN SIZE_T Size, 159 IN ULONG Flags, 160 IN ULONG Tag) 161 { 162 UNREFERENCED_PARAMETER(Tag); 163 return RtlAllocateHeap(GetProcessHeap(), Flags, Size); 164 } 165 166 // PELF_FREE_ROUTINE 167 static 168 VOID NTAPI 169 LogfpFree(IN PVOID Ptr, 170 IN ULONG Flags, 171 IN ULONG Tag) 172 { 173 UNREFERENCED_PARAMETER(Tag); 174 RtlFreeHeap(GetProcessHeap(), Flags, Ptr); 175 } 176 177 // PELF_FILE_READ_ROUTINE 178 static 179 NTSTATUS NTAPI 180 LogfpReadFile(IN PEVTLOGFILE LogFile, 181 IN PLARGE_INTEGER FileOffset, 182 OUT PVOID Buffer, 183 IN SIZE_T Length, 184 OUT PSIZE_T ReadLength OPTIONAL) 185 { 186 NTSTATUS Status; 187 PLOGFILE pLogFile = (PLOGFILE)LogFile; 188 IO_STATUS_BLOCK IoStatusBlock; 189 190 if (ReadLength) 191 *ReadLength = 0; 192 193 Status = NtReadFile(pLogFile->FileHandle, 194 NULL, 195 NULL, 196 NULL, 197 &IoStatusBlock, 198 Buffer, 199 Length, 200 FileOffset, 201 NULL); 202 203 if (ReadLength) 204 *ReadLength = IoStatusBlock.Information; 205 206 return Status; 207 } 208 209 // PELF_FILE_WRITE_ROUTINE 210 static 211 NTSTATUS NTAPI 212 LogfpWriteFile(IN PEVTLOGFILE LogFile, 213 IN PLARGE_INTEGER FileOffset, 214 IN PVOID Buffer, 215 IN SIZE_T Length, 216 OUT PSIZE_T WrittenLength OPTIONAL) 217 { 218 NTSTATUS Status; 219 PLOGFILE pLogFile = (PLOGFILE)LogFile; 220 IO_STATUS_BLOCK IoStatusBlock; 221 222 if (WrittenLength) 223 *WrittenLength = 0; 224 225 Status = NtWriteFile(pLogFile->FileHandle, 226 NULL, 227 NULL, 228 NULL, 229 &IoStatusBlock, 230 Buffer, 231 Length, 232 FileOffset, 233 NULL); 234 235 if (WrittenLength) 236 *WrittenLength = IoStatusBlock.Information; 237 238 return Status; 239 } 240 241 // PELF_FILE_SET_SIZE_ROUTINE 242 static 243 NTSTATUS NTAPI 244 LogfpSetFileSize(IN PEVTLOGFILE LogFile, 245 IN ULONG FileSize, // SIZE_T 246 IN ULONG OldFileSize) // SIZE_T 247 { 248 NTSTATUS Status; 249 PLOGFILE pLogFile = (PLOGFILE)LogFile; 250 IO_STATUS_BLOCK IoStatusBlock; 251 FILE_END_OF_FILE_INFORMATION FileEofInfo; 252 FILE_ALLOCATION_INFORMATION FileAllocInfo; 253 254 UNREFERENCED_PARAMETER(OldFileSize); 255 256 // FIXME: Should we round up FileSize ?? 257 258 FileEofInfo.EndOfFile.QuadPart = FileSize; 259 Status = NtSetInformationFile(pLogFile->FileHandle, 260 &IoStatusBlock, 261 &FileEofInfo, 262 sizeof(FileEofInfo), 263 FileEndOfFileInformation); 264 if (!NT_SUCCESS(Status)) 265 return Status; 266 267 FileAllocInfo.AllocationSize.QuadPart = FileSize; 268 Status = NtSetInformationFile(pLogFile->FileHandle, 269 &IoStatusBlock, 270 &FileAllocInfo, 271 sizeof(FileAllocInfo), 272 FileAllocationInformation); 273 274 return Status; 275 } 276 277 // PELF_FILE_FLUSH_ROUTINE 278 static 279 NTSTATUS NTAPI 280 LogfpFlushFile(IN PEVTLOGFILE LogFile, 281 IN PLARGE_INTEGER FileOffset, 282 IN ULONG Length) 283 { 284 PLOGFILE pLogFile = (PLOGFILE)LogFile; 285 IO_STATUS_BLOCK IoStatusBlock; 286 287 UNREFERENCED_PARAMETER(FileOffset); 288 UNREFERENCED_PARAMETER(Length); 289 290 return NtFlushBuffersFile(pLogFile->FileHandle, &IoStatusBlock); 291 } 292 293 NTSTATUS 294 LogfCreate(PLOGFILE* LogFile, 295 PCWSTR LogName, 296 PUNICODE_STRING FileName, 297 ULONG MaxSize, 298 ULONG Retention, 299 BOOLEAN Permanent, 300 BOOLEAN Backup) 301 { 302 NTSTATUS Status; 303 OBJECT_ATTRIBUTES ObjectAttributes; 304 IO_STATUS_BLOCK IoStatusBlock; 305 FILE_STANDARD_INFORMATION FileStdInfo; 306 PLOGFILE pLogFile; 307 SIZE_T LogNameLen; 308 BOOLEAN CreateNew; 309 310 pLogFile = LogfpAlloc(sizeof(*pLogFile), HEAP_ZERO_MEMORY, TAG_ELF); 311 if (!pLogFile) 312 { 313 DPRINT1("Cannot allocate heap!\n"); 314 return STATUS_NO_MEMORY; 315 } 316 317 LogNameLen = (LogName ? wcslen(LogName) : 0) + 1; 318 pLogFile->LogName = LogfpAlloc(LogNameLen * sizeof(WCHAR), HEAP_ZERO_MEMORY, 0); 319 if (pLogFile->LogName == NULL) 320 { 321 DPRINT1("Cannot allocate heap\n"); 322 Status = STATUS_NO_MEMORY; 323 goto Quit; 324 } 325 326 if (LogName) 327 StringCchCopyW(pLogFile->LogName, LogNameLen, LogName); 328 329 InitializeObjectAttributes(&ObjectAttributes, 330 FileName, 331 OBJ_CASE_INSENSITIVE, 332 NULL, 333 NULL); 334 335 DPRINT("Going to create or open %wZ\n", FileName); 336 Status = NtCreateFile(&pLogFile->FileHandle, 337 Backup ? (GENERIC_READ | SYNCHRONIZE) 338 : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE), 339 &ObjectAttributes, 340 &IoStatusBlock, 341 NULL, 342 FILE_ATTRIBUTE_NORMAL, 343 FILE_SHARE_READ, 344 Backup ? FILE_OPEN : FILE_OPEN_IF, 345 FILE_SYNCHRONOUS_IO_NONALERT, 346 NULL, 347 0); 348 if (!NT_SUCCESS(Status)) 349 { 350 DPRINT1("Cannot create file `%wZ' (Status 0x%08lx)\n", FileName, Status); 351 goto Quit; 352 } 353 354 CreateNew = (IoStatusBlock.Information == FILE_CREATED); 355 DPRINT("%wZ %s successfully\n", FileName, CreateNew ? "created" : "opened"); 356 357 /* 358 * Retrieve the log file size and check whether the file is not too large; 359 * this log format only supports files of theoretical size < 0xFFFFFFFF . 360 * 361 * As it happens that, on Windows (and ReactOS), retrieving the End-Of-File 362 * information using NtQueryInformationFile with the FileEndOfFileInformation 363 * class is invalid (who knows why...), use instead the FileStandardInformation 364 * class, and the EndOfFile member of the returned FILE_STANDARD_INFORMATION 365 * structure will give the desired information. 366 */ 367 Status = NtQueryInformationFile(pLogFile->FileHandle, 368 &IoStatusBlock, 369 &FileStdInfo, 370 sizeof(FileStdInfo), 371 FileStandardInformation); 372 if (!NT_SUCCESS(Status)) 373 { 374 DPRINT1("EventLog: NtQueryInformationFile failed (Status 0x%08lx)\n", Status); 375 goto Quit; 376 } 377 if (FileStdInfo.EndOfFile.HighPart != 0) 378 { 379 DPRINT1("EventLog: Log `%wZ' is too large.\n", FileName); 380 Status = STATUS_EVENTLOG_FILE_CORRUPT; // STATUS_FILE_TOO_LARGE; 381 goto Quit; 382 } 383 384 DPRINT("Initializing LogFile `%S'\n", pLogFile->LogName); 385 386 Status = ElfCreateFile(&pLogFile->LogFile, 387 FileName, 388 FileStdInfo.EndOfFile.LowPart, 389 MaxSize, 390 Retention, 391 CreateNew, 392 Backup, 393 LogfpAlloc, 394 LogfpFree, 395 LogfpSetFileSize, 396 LogfpWriteFile, 397 LogfpReadFile, 398 LogfpFlushFile); 399 if (!NT_SUCCESS(Status)) 400 goto Quit; 401 402 pLogFile->Permanent = Permanent; 403 404 RtlInitializeResource(&pLogFile->Lock); 405 406 LogfListAddItem(pLogFile); 407 408 Quit: 409 if (!NT_SUCCESS(Status)) 410 { 411 if (pLogFile->FileHandle != NULL) 412 NtClose(pLogFile->FileHandle); 413 414 if (pLogFile->LogName) 415 LogfpFree(pLogFile->LogName, 0, 0); 416 417 LogfpFree(pLogFile, 0, TAG_ELF); 418 } 419 else 420 { 421 *LogFile = pLogFile; 422 } 423 424 return Status; 425 } 426 427 VOID 428 LogfClose(PLOGFILE LogFile, 429 BOOLEAN ForceClose) 430 { 431 if (LogFile == NULL) 432 return; 433 434 if (!ForceClose && LogFile->Permanent) 435 return; 436 437 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); 438 439 LogfListRemoveItem(LogFile); 440 441 ElfCloseFile(&LogFile->LogFile); 442 NtClose(LogFile->FileHandle); 443 LogfpFree(LogFile->LogName, 0, 0); 444 445 RtlDeleteResource(&LogFile->Lock); 446 447 LogfpFree(LogFile, 0, TAG_ELF); 448 449 return; 450 } 451 452 VOID LogfCloseAll(VOID) 453 { 454 EnterCriticalSection(&LogFileListCs); 455 456 while (!IsListEmpty(&LogFileListHead)) 457 { 458 LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE); 459 } 460 461 LeaveCriticalSection(&LogFileListCs); 462 463 DeleteCriticalSection(&LogFileListCs); 464 } 465 466 NTSTATUS 467 LogfClearFile(PLOGFILE LogFile, 468 PUNICODE_STRING BackupFileName) 469 { 470 NTSTATUS Status; 471 472 /* Lock the log file exclusive */ 473 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); 474 475 if (BackupFileName->Length > 0) 476 { 477 /* Write a backup file */ 478 Status = LogfBackupFile(LogFile, BackupFileName); 479 if (!NT_SUCCESS(Status)) 480 { 481 DPRINT1("LogfBackupFile failed (Status 0x%08lx)\n", Status); 482 goto Quit; 483 } 484 } 485 486 Status = ElfReCreateFile(&LogFile->LogFile); 487 if (!NT_SUCCESS(Status)) 488 { 489 DPRINT1("LogfInitializeNew failed (Status 0x%08lx)\n", Status); 490 } 491 492 Quit: 493 /* Unlock the log file */ 494 RtlReleaseResource(&LogFile->Lock); 495 return Status; 496 } 497 498 NTSTATUS 499 LogfBackupFile(PLOGFILE LogFile, 500 PUNICODE_STRING BackupFileName) 501 { 502 NTSTATUS Status; 503 LOGFILE BackupLogFile; 504 OBJECT_ATTRIBUTES ObjectAttributes; 505 IO_STATUS_BLOCK IoStatusBlock; 506 507 DPRINT("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName); 508 509 /* Lock the log file shared */ 510 RtlAcquireResourceShared(&LogFile->Lock, TRUE); 511 512 InitializeObjectAttributes(&ObjectAttributes, 513 BackupFileName, 514 OBJ_CASE_INSENSITIVE, 515 NULL, 516 NULL); 517 518 Status = NtCreateFile(&BackupLogFile.FileHandle, 519 GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, 520 &ObjectAttributes, 521 &IoStatusBlock, 522 NULL, 523 FILE_ATTRIBUTE_NORMAL, 524 FILE_SHARE_READ, 525 FILE_CREATE, 526 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT, 527 NULL, 528 0); 529 if (!NT_SUCCESS(Status)) 530 { 531 DPRINT("Cannot create backup file `%wZ' (Status 0x%08lx)\n", BackupFileName, Status); 532 goto Quit; 533 } 534 535 Status = ElfBackupFile(&LogFile->LogFile, 536 &BackupLogFile.LogFile); 537 538 Quit: 539 /* Close the backup file */ 540 if (BackupLogFile.FileHandle != NULL) 541 NtClose(BackupLogFile.FileHandle); 542 543 /* Unlock the log file */ 544 RtlReleaseResource(&LogFile->Lock); 545 546 return Status; 547 } 548 549 550 static NTSTATUS 551 ReadRecord(IN PEVTLOGFILE LogFile, 552 IN ULONG RecordNumber, 553 OUT PEVENTLOGRECORD Record, 554 IN SIZE_T BufSize, // Length 555 OUT PSIZE_T BytesRead OPTIONAL, 556 OUT PSIZE_T BytesNeeded OPTIONAL, 557 IN BOOLEAN Ansi) 558 { 559 NTSTATUS Status; 560 PEVENTLOGRECORD UnicodeBuffer = NULL; 561 PEVENTLOGRECORD Src, Dst; 562 ANSI_STRING StringA; 563 UNICODE_STRING StringW; 564 PVOID SrcPtr, DstPtr; 565 DWORD i; 566 DWORD dwPadding; 567 DWORD dwRecordLength; 568 PDWORD pLength; 569 570 if (!Ansi) 571 { 572 return ElfReadRecord(LogFile, 573 RecordNumber, 574 Record, 575 BufSize, 576 BytesRead, 577 BytesNeeded); 578 } 579 580 if (BytesRead) 581 *BytesRead = 0; 582 583 if (BytesNeeded) 584 *BytesNeeded = 0; 585 586 UnicodeBuffer = LogfpAlloc(BufSize, HEAP_ZERO_MEMORY, TAG_ELF_BUF); 587 if (UnicodeBuffer == NULL) 588 { 589 DPRINT1("Alloc failed!\n"); 590 return STATUS_NO_MEMORY; 591 } 592 593 Status = ElfReadRecord(LogFile, 594 RecordNumber, 595 UnicodeBuffer, 596 BufSize, 597 BytesRead, 598 BytesNeeded); 599 if (!NT_SUCCESS(Status)) 600 goto Quit; 601 602 Src = UnicodeBuffer; 603 Dst = Record; 604 605 Dst->Reserved = Src->Reserved; 606 Dst->RecordNumber = Src->RecordNumber; 607 Dst->TimeGenerated = Src->TimeGenerated; 608 Dst->TimeWritten = Src->TimeWritten; 609 Dst->EventID = Src->EventID; 610 Dst->EventType = Src->EventType; 611 Dst->EventCategory = Src->EventCategory; 612 Dst->NumStrings = Src->NumStrings; 613 Dst->UserSidLength = Src->UserSidLength; 614 Dst->DataLength = Src->DataLength; 615 616 SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD)); 617 DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD)); 618 619 /* Convert the module name */ 620 RtlInitUnicodeString(&StringW, SrcPtr); 621 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); 622 if (NT_SUCCESS(Status)) 623 { 624 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); 625 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); 626 627 RtlFreeAnsiString(&StringA); 628 } 629 else 630 { 631 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); 632 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); 633 } 634 SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength); 635 636 /* Convert the computer name */ 637 RtlInitUnicodeString(&StringW, SrcPtr); 638 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); 639 if (NT_SUCCESS(Status)) 640 { 641 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); 642 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); 643 644 RtlFreeAnsiString(&StringA); 645 } 646 else 647 { 648 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); 649 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); 650 } 651 652 /* Add the padding and the User SID */ 653 dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG)); 654 RtlZeroMemory(DstPtr, dwPadding); 655 656 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->UserSidOffset); 657 DstPtr = (PVOID)((ULONG_PTR)DstPtr + dwPadding); 658 659 Dst->UserSidOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst); 660 RtlCopyMemory(DstPtr, SrcPtr, Src->UserSidLength); 661 662 /* Convert the strings */ 663 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->StringOffset); 664 DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->UserSidLength); 665 Dst->StringOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst); 666 667 for (i = 0; i < Dst->NumStrings; i++) 668 { 669 RtlInitUnicodeString(&StringW, SrcPtr); 670 Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); 671 if (NT_SUCCESS(Status)) 672 { 673 RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); 674 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); 675 676 RtlFreeAnsiString(&StringA); 677 } 678 else 679 { 680 RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); 681 DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); 682 } 683 SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength); 684 } 685 686 /* Copy the binary data */ 687 SrcPtr = (PVOID)((ULONG_PTR)Src + Src->DataOffset); 688 Dst->DataOffset = (ULONG_PTR)DstPtr - (ULONG_PTR)Dst; 689 RtlCopyMemory(DstPtr, SrcPtr, Src->DataLength); 690 DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength); 691 692 /* Add the padding */ 693 dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG)); 694 RtlZeroMemory(DstPtr, dwPadding); 695 696 /* Set the record length at the beginning and the end of the record */ 697 dwRecordLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst); 698 Dst->Length = dwRecordLength; 699 pLength = (PDWORD)((ULONG_PTR)DstPtr + dwPadding); 700 *pLength = dwRecordLength; 701 702 if (BytesRead) 703 *BytesRead = dwRecordLength; 704 705 Status = STATUS_SUCCESS; 706 707 Quit: 708 LogfpFree(UnicodeBuffer, 0, TAG_ELF_BUF); 709 710 return Status; 711 } 712 713 /* 714 * NOTE: 715 * 'RecordNumber' is a pointer to the record number at which the read operation 716 * should start. If the record number is 0 and the flags given in the 'Flags' 717 * parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is 718 * computed. 719 */ 720 NTSTATUS 721 LogfReadEvents(PLOGFILE LogFile, 722 ULONG Flags, 723 PULONG RecordNumber, 724 ULONG BufSize, 725 PBYTE Buffer, 726 PULONG BytesRead, 727 PULONG BytesNeeded, 728 BOOLEAN Ansi) 729 { 730 NTSTATUS Status; 731 ULONG RecNum; 732 SIZE_T ReadLength, NeededSize; 733 ULONG BufferUsage; 734 735 /* Parameters validation */ 736 737 /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */ 738 if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (Flags & EVENTLOG_SEEK_READ)) 739 return STATUS_INVALID_PARAMETER; 740 741 if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && !(Flags & EVENTLOG_SEEK_READ)) 742 return STATUS_INVALID_PARAMETER; 743 744 /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */ 745 if ((Flags & EVENTLOG_FORWARDS_READ) && (Flags & EVENTLOG_BACKWARDS_READ)) 746 return STATUS_INVALID_PARAMETER; 747 748 if (!(Flags & EVENTLOG_FORWARDS_READ) && !(Flags & EVENTLOG_BACKWARDS_READ)) 749 return STATUS_INVALID_PARAMETER; 750 751 if (!Buffer || !BytesRead || !BytesNeeded) 752 return STATUS_INVALID_PARAMETER; 753 754 /* In seek read mode, a record number of 0 is invalid */ 755 if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0)) 756 return STATUS_INVALID_PARAMETER; 757 758 /* Lock the log file shared */ 759 RtlAcquireResourceShared(&LogFile->Lock, TRUE); 760 761 /* 762 * In sequential read mode, a record number of 0 means we need 763 * to determine where to start the read operation. Otherwise 764 * we just use the provided record number. 765 */ 766 if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0)) 767 { 768 if (Flags & EVENTLOG_FORWARDS_READ) 769 { 770 *RecordNumber = ElfGetOldestRecord(&LogFile->LogFile); 771 } 772 else // if (Flags & EVENTLOG_BACKWARDS_READ) 773 { 774 *RecordNumber = ElfGetCurrentRecord(&LogFile->LogFile) - 1; 775 } 776 } 777 778 RecNum = *RecordNumber; 779 780 *BytesRead = 0; 781 *BytesNeeded = 0; 782 783 BufferUsage = 0; 784 do 785 { 786 Status = ReadRecord(&LogFile->LogFile, 787 RecNum, 788 (PEVENTLOGRECORD)(Buffer + BufferUsage), 789 BufSize - BufferUsage, 790 &ReadLength, 791 &NeededSize, 792 Ansi); 793 if (Status == STATUS_NOT_FOUND) 794 { 795 if (BufferUsage == 0) 796 { 797 Status = STATUS_END_OF_FILE; 798 goto Quit; 799 } 800 else 801 { 802 break; 803 } 804 } 805 else 806 if (Status == STATUS_BUFFER_TOO_SMALL) 807 { 808 if (BufferUsage == 0) 809 { 810 *BytesNeeded = NeededSize; 811 // Status = STATUS_BUFFER_TOO_SMALL; 812 goto Quit; 813 } 814 else 815 { 816 break; 817 } 818 } 819 else 820 if (!NT_SUCCESS(Status)) 821 { 822 DPRINT1("ElfReadRecord failed (Status 0x%08lx)\n", Status); 823 goto Quit; 824 } 825 826 /* Go to the next event record */ 827 /* 828 * NOTE: This implicitly supposes that all the other record numbers 829 * are consecutive (and do not jump than more than one unit); but if 830 * it is not the case, then we would prefer here to call some 831 * "get_next_record_number" function. 832 */ 833 if (Flags & EVENTLOG_FORWARDS_READ) 834 RecNum++; 835 else // if (Flags & EVENTLOG_BACKWARDS_READ) 836 RecNum--; 837 838 BufferUsage += ReadLength; 839 } 840 while (BufferUsage <= BufSize); 841 842 *BytesRead = BufferUsage; 843 *RecordNumber = RecNum; 844 845 Status = STATUS_SUCCESS; 846 847 Quit: 848 /* Unlock the log file */ 849 RtlReleaseResource(&LogFile->Lock); 850 851 if (!NT_SUCCESS(Status)) 852 DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status); 853 854 return Status; 855 } 856 857 NTSTATUS 858 LogfWriteRecord(PLOGFILE LogFile, 859 PEVENTLOGRECORD Record, 860 SIZE_T BufSize) 861 { 862 NTSTATUS Status; 863 LARGE_INTEGER SystemTime; 864 865 // ASSERT(sizeof(*Record) == sizeof(RecBuf)); 866 867 if (!Record || BufSize < sizeof(*Record)) 868 return STATUS_INVALID_PARAMETER; 869 870 /* Lock the log file exclusive */ 871 RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); 872 873 /* 874 * Retrieve the record written time now, that will also be compared 875 * with the existing events timestamps in case the log is wrapping. 876 */ 877 NtQuerySystemTime(&SystemTime); 878 RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten); 879 880 Status = ElfWriteRecord(&LogFile->LogFile, Record, BufSize); 881 if (Status == STATUS_LOG_FILE_FULL) 882 { 883 /* The event log file is full, queue a message box for the user and exit */ 884 // TODO! 885 DPRINT1("Log file `%S' is full!\n", LogFile->LogName); 886 } 887 888 /* Unlock the log file */ 889 RtlReleaseResource(&LogFile->Lock); 890 891 return Status; 892 } 893 894 895 PEVENTLOGRECORD 896 LogfAllocAndBuildNewRecord(PSIZE_T pRecSize, 897 ULONG Time, 898 USHORT wType, 899 USHORT wCategory, 900 ULONG dwEventId, 901 PUNICODE_STRING SourceName, 902 PUNICODE_STRING ComputerName, 903 ULONG dwSidLength, 904 PSID pUserSid, 905 USHORT wNumStrings, 906 PWSTR pStrings, 907 ULONG dwDataSize, 908 PVOID pRawData) 909 { 910 SIZE_T RecSize; 911 SIZE_T SourceNameSize, ComputerNameSize, StringLen; 912 PBYTE Buffer; 913 PEVENTLOGRECORD pRec; 914 PWSTR str; 915 UINT i, pos; 916 917 SourceNameSize = (SourceName && SourceName->Buffer) ? SourceName->Length : 0; 918 ComputerNameSize = (ComputerName && ComputerName->Buffer) ? ComputerName->Length : 0; 919 920 RecSize = sizeof(EVENTLOGRECORD) + /* Add the sizes of the strings, NULL-terminated */ 921 SourceNameSize + ComputerNameSize + 2*sizeof(UNICODE_NULL); 922 923 /* Align on DWORD boundary for the SID */ 924 RecSize = ROUND_UP(RecSize, sizeof(ULONG)); 925 926 RecSize += dwSidLength; 927 928 /* Add the sizes for the strings array */ 929 ASSERT((pStrings == NULL && wNumStrings == 0) || 930 (pStrings != NULL && wNumStrings >= 0)); 931 for (i = 0, str = pStrings; i < wNumStrings; i++) 932 { 933 StringLen = wcslen(str) + 1; // str must be != NULL 934 RecSize += StringLen * sizeof(WCHAR); 935 str += StringLen; 936 } 937 938 /* Add the data size */ 939 RecSize += dwDataSize; 940 941 /* Align on DWORD boundary for the full structure */ 942 RecSize = ROUND_UP(RecSize, sizeof(ULONG)); 943 944 /* Size of the trailing 'Length' member */ 945 RecSize += sizeof(ULONG); 946 947 Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, RecSize); 948 if (!Buffer) 949 { 950 DPRINT1("Cannot allocate heap!\n"); 951 return NULL; 952 } 953 954 pRec = (PEVENTLOGRECORD)Buffer; 955 pRec->Length = RecSize; 956 pRec->Reserved = LOGFILE_SIGNATURE; 957 958 /* 959 * Do not assign here any precomputed record number to the event record. 960 * The true record number will be assigned atomically and sequentially in 961 * LogfWriteRecord, so that all the event records will have consistent and 962 * unique record numbers. 963 */ 964 pRec->RecordNumber = 0; 965 966 /* 967 * Set the generated time, and temporarily set the written time 968 * with the generated time. 969 */ 970 pRec->TimeGenerated = Time; 971 pRec->TimeWritten = Time; 972 973 pRec->EventID = dwEventId; 974 pRec->EventType = wType; 975 pRec->EventCategory = wCategory; 976 977 pos = sizeof(EVENTLOGRECORD); 978 979 /* NOTE: Equivalents of RtlStringCbCopyUnicodeString calls */ 980 if (SourceNameSize) 981 { 982 StringCbCopyNW((PWSTR)(Buffer + pos), SourceNameSize + sizeof(UNICODE_NULL), 983 SourceName->Buffer, SourceNameSize); 984 } 985 pos += SourceNameSize + sizeof(UNICODE_NULL); 986 if (ComputerNameSize) 987 { 988 StringCbCopyNW((PWSTR)(Buffer + pos), ComputerNameSize + sizeof(UNICODE_NULL), 989 ComputerName->Buffer, ComputerNameSize); 990 } 991 pos += ComputerNameSize + sizeof(UNICODE_NULL); 992 993 /* Align on DWORD boundary for the SID */ 994 pos = ROUND_UP(pos, sizeof(ULONG)); 995 996 pRec->UserSidLength = 0; 997 pRec->UserSidOffset = 0; 998 if (dwSidLength) 999 { 1000 RtlCopyMemory(Buffer + pos, pUserSid, dwSidLength); 1001 pRec->UserSidLength = dwSidLength; 1002 pRec->UserSidOffset = pos; 1003 pos += dwSidLength; 1004 } 1005 1006 pRec->StringOffset = pos; 1007 for (i = 0, str = pStrings; i < wNumStrings; i++) 1008 { 1009 StringLen = wcslen(str) + 1; // str must be != NULL 1010 StringCchCopyW((PWSTR)(Buffer + pos), StringLen, str); 1011 str += StringLen; 1012 pos += StringLen * sizeof(WCHAR); 1013 } 1014 pRec->NumStrings = wNumStrings; 1015 1016 pRec->DataLength = 0; 1017 pRec->DataOffset = 0; 1018 if (dwDataSize) 1019 { 1020 RtlCopyMemory(Buffer + pos, pRawData, dwDataSize); 1021 pRec->DataLength = dwDataSize; 1022 pRec->DataOffset = pos; 1023 pos += dwDataSize; 1024 } 1025 1026 /* Align on DWORD boundary for the full structure */ 1027 pos = ROUND_UP(pos, sizeof(ULONG)); 1028 1029 /* Initialize the trailing 'Length' member */ 1030 *((PDWORD)(Buffer + pos)) = RecSize; 1031 1032 *pRecSize = RecSize; 1033 return pRec; 1034 } 1035 1036 VOID 1037 LogfReportEvent(USHORT wType, 1038 USHORT wCategory, 1039 ULONG dwEventId, 1040 USHORT wNumStrings, 1041 PWSTR pStrings, 1042 ULONG dwDataSize, 1043 PVOID pRawData) 1044 { 1045 NTSTATUS Status; 1046 UNICODE_STRING SourceName, ComputerName; 1047 PEVENTLOGRECORD LogBuffer; 1048 LARGE_INTEGER SystemTime; 1049 ULONG Time; 1050 SIZE_T RecSize; 1051 DWORD dwComputerNameLength; 1052 WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; 1053 1054 if (!EventLogSource) 1055 return; 1056 1057 RtlInitUnicodeString(&SourceName, EventLogSource->szName); 1058 1059 dwComputerNameLength = ARRAYSIZE(szComputerName); 1060 if (!GetComputerNameW(szComputerName, &dwComputerNameLength)) 1061 szComputerName[0] = L'\0'; 1062 1063 RtlInitUnicodeString(&ComputerName, szComputerName); 1064 1065 NtQuerySystemTime(&SystemTime); 1066 RtlTimeToSecondsSince1970(&SystemTime, &Time); 1067 1068 LogBuffer = LogfAllocAndBuildNewRecord(&RecSize, 1069 Time, 1070 wType, 1071 wCategory, 1072 dwEventId, 1073 &SourceName, 1074 &ComputerName, 1075 0, 1076 NULL, 1077 wNumStrings, 1078 pStrings, 1079 dwDataSize, 1080 pRawData); 1081 if (LogBuffer == NULL) 1082 { 1083 DPRINT1("LogfAllocAndBuildNewRecord failed!\n"); 1084 return; 1085 } 1086 1087 Status = LogfWriteRecord(EventLogSource->LogFile, LogBuffer, RecSize); 1088 if (!NT_SUCCESS(Status)) 1089 { 1090 DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n", 1091 EventLogSource->LogFile->LogName, Status); 1092 } 1093 1094 LogfFreeRecord(LogBuffer); 1095 } 1096