Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2929 lines
82 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. cntxtlog.c
  5. Abstract:
  6. This module implements more logging for setupapi
  7. Author:
  8. Gabe Schaffer (t-gabes) 25-Jun-1998
  9. Revision History:
  10. Jamie Hunter (jamiehun) Apr 11 2000 - added #xnnnn identifiers
  11. Jamie Hunter (jamiehun) Feb 2 2000 - cleanup
  12. Jamie Hunter (jamiehun) Aug 31 1998
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. //
  17. // global data used by logging
  18. //
  19. struct _GlobalLogData {
  20. CRITICAL_SECTION CritSec;
  21. BOOL DoneInitCritSec;
  22. LONG UID;
  23. ULONG Flags;
  24. PTSTR FileName;
  25. } GlobalLogData;
  26. #define LogLock() EnterCriticalSection(&GlobalLogData.CritSec)
  27. #define LogUnlock() LeaveCriticalSection(&GlobalLogData.CritSec)
  28. // process-wide log counter
  29. //
  30. // C = critical
  31. // E = error
  32. // W = warning
  33. // I = information
  34. // V = verbose
  35. // T = timing
  36. // * = currently undefined
  37. //
  38. static const TCHAR LogLevelShort[17] = TEXT("CEWIVTTV********");
  39. #define LOGLEVELSHORT_MASK (0x0f)
  40. #define LOGLEVELSHORT_INIT (0x100)
  41. #define LOGLEVELSHORT_SHIFT (4)
  42. __inline // we want to always optimize this out
  43. BOOL
  44. _WouldNeverLog(
  45. IN DWORD Level
  46. )
  47. /*++
  48. Routine Description:
  49. Determines if at the current logging level and the required level, we would never log
  50. inline'd for optimization (used only in this file)
  51. Arguments:
  52. Level - only required to check for special case of 0.
  53. Return Value:
  54. TRUE if we know we would never log based on passed information
  55. --*/
  56. {
  57. if (Level == 0) {
  58. //
  59. // don't-log level
  60. //
  61. return TRUE;
  62. }
  63. if (((GlobalLogData.Flags & SETUP_LOG_LEVELMASK) <= SETUP_LOG_NOLOG)
  64. &&((GlobalLogData.Flags & DRIVER_LOG_LEVELMASK) <= DRIVER_LOG_NOLOG)) {
  65. //
  66. // Global flags indicate do no logging at all
  67. //
  68. return TRUE;
  69. }
  70. return FALSE;
  71. }
  72. __inline // we want to always optimize this out
  73. BOOL
  74. _WouldLog(
  75. IN DWORD Level
  76. )
  77. /*++
  78. Routine Description:
  79. Determines if at the current logging level and the required level, we would log
  80. inline'd for optimization (used only in this file)
  81. Note that if _WouldNeverLog is TRUE, _WouldLog is always FALSE
  82. if _WouldLog is TRUE, _WouldNeverLog is always FALSE
  83. if both are FALSE, then we are on "maybe"
  84. Arguments:
  85. Level - bitmask indicating logging flags. See SETUP_LOG_* and DRIVER_LOG_*
  86. at the beginning of cntxtlog.h for details. It may also be a slot
  87. returned by AllocLogInfoSlot, or 0 (no logging)
  88. Return Value:
  89. TRUE if we know we would log
  90. --*/
  91. {
  92. if (_WouldNeverLog(Level)) {
  93. //
  94. // some simple tests (LogLevel==NULL is a not sure case)
  95. //
  96. return FALSE;
  97. }
  98. if ((Level & SETUP_LOG_IS_CONTEXT)!=0) {
  99. //
  100. // context logging - ignored here (a not sure case)
  101. //
  102. return FALSE;
  103. }
  104. //
  105. // determine logability
  106. //
  107. if ((Level & SETUP_LOG_LEVELMASK) > 0 && (Level & SETUP_LOG_LEVELMASK) <= (GlobalLogData.Flags & SETUP_LOG_LEVELMASK)) {
  108. //
  109. // we're interested in logging - raw error level
  110. //
  111. return TRUE;
  112. }
  113. if ((Level & DRIVER_LOG_LEVELMASK) > 0 && (Level & DRIVER_LOG_LEVELMASK) <= (GlobalLogData.Flags & DRIVER_LOG_LEVELMASK)) {
  114. //
  115. // we're interested in logging - driver error level
  116. //
  117. return TRUE;
  118. }
  119. return FALSE;
  120. }
  121. VOID
  122. UnMapLogFile(
  123. IN PSTR baseaddr,
  124. IN HANDLE hLogfile,
  125. IN HANDLE hMapping,
  126. IN BOOL seteof
  127. )
  128. /*++
  129. Routine Description:
  130. Unmap, possibly unlock, maybe set the EOF, and close a file. Note, setting
  131. EOF must occur after unmapping.
  132. Arguments:
  133. baseaddr - this is the address where the file is mapped. It must be what
  134. was returned by MapLogFile.
  135. hLogfile - this is the Win32 handle for the log file.
  136. hMapping - this is the Win32 handle to the mapping object.
  137. seteof - Boolean value indicating whether the EOF should be set to the
  138. current file pointer. If the EOF is set and the file pointer has not
  139. been moved, the EOF will be set at byte 0, thus truncating the file
  140. to 0 bytes.
  141. Return Value:
  142. NONE.
  143. --*/
  144. {
  145. DWORD success;
  146. //
  147. // we brute-force try to close everything up
  148. //
  149. try {
  150. if (baseaddr != NULL) {
  151. success = UnmapViewOfFile(baseaddr);
  152. }
  153. } except(EXCEPTION_EXECUTE_HANDLER) {
  154. //
  155. // do nothing
  156. //
  157. }
  158. try {
  159. if (hMapping != NULL) {
  160. //
  161. // hMapping uses NULL to indicate a problem
  162. //
  163. success = CloseHandle(hMapping);
  164. }
  165. } except(EXCEPTION_EXECUTE_HANDLER) {
  166. //
  167. // do nothing
  168. //
  169. }
  170. try {
  171. if (hLogfile != INVALID_HANDLE_VALUE && seteof) {
  172. success = SetEndOfFile(hLogfile);
  173. }
  174. } except(EXCEPTION_EXECUTE_HANDLER) {
  175. //
  176. // do nothing
  177. //
  178. }
  179. try {
  180. if (hLogfile != INVALID_HANDLE_VALUE) {
  181. if (!(GlobalLogData.Flags & SETUP_LOG_NOFLUSH)) {
  182. FlushFileBuffers(hLogfile);
  183. }
  184. success = CloseHandle(hLogfile);
  185. }
  186. } except(EXCEPTION_EXECUTE_HANDLER) {
  187. //
  188. // do nothing
  189. //
  190. }
  191. //
  192. // Win9x provides no way to wait for a file to become unlocked, so we
  193. // have to poll. Putting this Sleep(0) allows others to have a chance
  194. // at the file.
  195. //
  196. Sleep(0);
  197. }
  198. VOID
  199. WriteLogFileHeader(
  200. IN HANDLE hLogFile
  201. )
  202. /*++
  203. Routine Description:
  204. Write general information at start of log file
  205. [SetupAPI Log]
  206. OS Version = %1!u!.%2!u!.%3!u! %4!s!
  207. Platform ID = %5!u!
  208. Service Pack = %6!u!.%7!u!
  209. Suite = 0x%8!04x!
  210. Product Type = %9!u!
  211. Arguments:
  212. hLogfile - file to write header to
  213. Return Value:
  214. NONE
  215. --*/
  216. {
  217. #ifdef UNICODE
  218. OSVERSIONINFOEX VersionInfo;
  219. #else
  220. OSVERSIONINFO VersionInfo;
  221. #endif
  222. DWORD count;
  223. DWORD written;
  224. PTSTR buffer;
  225. PSTR ansibuffer;
  226. ULONG_PTR args[14];
  227. DWORD MessageId = MSG_LOGFILE_HEADER_OTHER;
  228. ZeroMemory(&VersionInfo,sizeof(VersionInfo));
  229. #ifdef UNICODE
  230. VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  231. if(!GetVersionEx((POSVERSIONINFO)&VersionInfo)) {
  232. VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  233. if(!GetVersionEx((POSVERSIONINFO)&VersionInfo)) {
  234. return;
  235. }
  236. }
  237. #else
  238. VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  239. if(!GetVersionEx((POSVERSIONINFO)&VersionInfo)) {
  240. return;
  241. }
  242. #endif
  243. args[1] = (ULONG_PTR)VersionInfo.dwMajorVersion;
  244. args[2] = (ULONG_PTR)VersionInfo.dwMinorVersion;
  245. args[4] = (ULONG_PTR)VersionInfo.szCSDVersion; // string
  246. args[5] = (ULONG_PTR)VersionInfo.dwPlatformId;
  247. if(VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
  248. args[3] = (ULONG_PTR)VersionInfo.dwBuildNumber;
  249. #ifdef UNICODE
  250. MessageId = MSG_LOGFILE_HEADER_NT;
  251. #endif
  252. } else {
  253. args[3] = (ULONG_PTR)LOWORD(VersionInfo.dwBuildNumber); // Win9x re-uses high word
  254. }
  255. #ifdef UNICODE
  256. args[6] = (ULONG_PTR)VersionInfo.wServicePackMajor;
  257. args[7] = (ULONG_PTR)VersionInfo.wServicePackMinor;
  258. args[8] = (ULONG_PTR)VersionInfo.wSuiteMask;
  259. args[9] = (ULONG_PTR)VersionInfo.wProductType;
  260. args[10] = (ULONG_PTR)pszPlatformName;
  261. #endif
  262. count = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_ARGUMENT_ARRAY|FORMAT_MESSAGE_FROM_HMODULE,
  263. MyDllModuleHandle,
  264. MessageId,
  265. 0,
  266. (LPTSTR) &buffer,
  267. 0,
  268. (va_list*)(args+1));
  269. if (count && buffer) {
  270. #ifdef UNICODE
  271. ansibuffer = pSetupUnicodeToMultiByte(buffer,CP_ACP);
  272. if (ansibuffer) {
  273. WriteFile(hLogFile,ansibuffer,strlen(ansibuffer),&written,NULL);
  274. MyFree(ansibuffer);
  275. }
  276. #else
  277. WriteFile(hLogFile,buffer,strlen(buffer),&written,NULL);
  278. #endif
  279. LocalFree(buffer);
  280. }
  281. }
  282. DWORD
  283. MapLogFile(
  284. IN PCTSTR FileName,
  285. OUT PHANDLE hLogfile,
  286. OUT PHANDLE hMapping,
  287. OUT PDWORD dwFilesize,
  288. OUT PSTR *mapaddr,
  289. IN DWORD extrabytes
  290. )
  291. /*++
  292. Routine Description:
  293. Open the log file for writing and memory map it. On NT the file is locked,
  294. but Win9x doesn't allow memory mapped access to locked files, so the file
  295. is opened without FILE_SHARE_WRITE access. Since CreateFile won't block
  296. like LockFileEx, we have to poll once per second on Win9x until the file
  297. opens.
  298. Arguments:
  299. FileName - supplies path name to the log file.
  300. hLogfile - receives the Win32 file handle for the log file.
  301. hMapping - receives the Win32 handle to the mapping object.
  302. dwFileSize - receives the size of the file before it is mapped, because
  303. mapping increases the size of the file by extrabytes.
  304. mapaddr - receives the address of where the log file is mapped.
  305. extrabytes - supplies the number of extra bytes (beyond the size of the
  306. file) to add to the size of the mapping object to allow for appending
  307. the new log line and possibly a section header.
  308. Return Value:
  309. NO_ERROR if the file is successfully opened and mapped. The caller must
  310. call UnMapLogFile when finished with the file.
  311. Win32 error code if the file is not open.
  312. --*/
  313. {
  314. HANDLE logfile = INVALID_HANDLE_VALUE;
  315. HANDLE mapping = NULL;
  316. DWORD filesize = 0;
  317. DWORD lockretrywait = 1;
  318. DWORD wait_total = 0;
  319. PSTR baseaddr = NULL;
  320. DWORD retval = ERROR_INVALID_PARAMETER;
  321. //
  322. // wrap it all up in a nice big try/except, because you just never know
  323. //
  324. try {
  325. //
  326. // give initial "failed" values
  327. // this also validates the pointers
  328. //
  329. *hLogfile = logfile;
  330. *hMapping = mapping;
  331. *dwFilesize = filesize;
  332. *mapaddr = baseaddr;
  333. do {
  334. //
  335. // retry here, in case lock fails
  336. //
  337. logfile = CreateFile(
  338. FileName,
  339. GENERIC_READ | GENERIC_WRITE, // access mode
  340. FILE_SHARE_READ,
  341. NULL, // security
  342. OPEN_ALWAYS, // open, or create if not already there
  343. //FILE_FLAG_WRITE_THROUGH, // flags - ensures that if machine crashes in the next operation, we are still logged
  344. 0,
  345. NULL); // template
  346. if (logfile == INVALID_HANDLE_VALUE) {
  347. retval = GetLastError();
  348. if (retval != ERROR_SHARING_VIOLATION) {
  349. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Could not create file %s. Error %d\n"), FileName, retval));
  350. leave;
  351. }
  352. if(wait_total >= MAX_LOG_WAIT) {
  353. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Given up waiting for log file %s.\n"), FileName));
  354. leave;
  355. }
  356. //
  357. // don't want to wait more than a second at a time
  358. //
  359. if (lockretrywait < MAX_LOG_INTERVAL) {
  360. lockretrywait *= 2;
  361. }
  362. MYTRACE((DPFLTR_WARNING_LEVEL, TEXT("Setup: Could not open file. Error %d; waiting %ums\n"), GetLastError(), lockretrywait));
  363. Sleep(lockretrywait);
  364. wait_total += lockretrywait;
  365. }
  366. } while (logfile == INVALID_HANDLE_VALUE);
  367. //
  368. // this will NOT work with files >= 4GB, but it's not supposed to
  369. //
  370. filesize = GetFileSize(logfile,NULL);
  371. if (filesize == 0) {
  372. //
  373. // fill some OS information into file
  374. //
  375. WriteLogFileHeader(logfile);
  376. filesize = GetFileSize(logfile,NULL);
  377. }
  378. //
  379. // make the mapping object with extra space to accomodate the new log entry
  380. //
  381. mapping = CreateFileMapping(
  382. logfile, // file to map
  383. NULL, // security
  384. PAGE_READWRITE, // protection
  385. 0, // maximum size high
  386. filesize + extrabytes, // maximum size low
  387. NULL); // name
  388. if (mapping != NULL) {
  389. //
  390. // NULL isn't a bug, CreateFileMapping returns this
  391. // to indicate error, instead of INVALID_HANDLE_VALUE
  392. //
  393. //
  394. // now we have a section object, so attach it to the log file
  395. //
  396. baseaddr = (PSTR) MapViewOfFile(
  397. mapping, // file mapping object
  398. FILE_MAP_ALL_ACCESS, // desired access
  399. 0, // file offset high
  400. 0, // file offset low
  401. 0); // number of bytes to map (0 = whole file)
  402. }
  403. else {
  404. retval = GetLastError();
  405. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Could not create mapping. Error %d\n"), retval));
  406. leave;
  407. }
  408. if (baseaddr == NULL) {
  409. //
  410. // either the mapping object couldn't be created or
  411. // the file couldn't be mapped
  412. //
  413. retval = GetLastError();
  414. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Could not map file. Error %d\n"), retval));
  415. leave;
  416. }
  417. //
  418. // now put everything where the caller can see it, but make sure we clean
  419. // up first
  420. //
  421. *hLogfile = logfile;
  422. *hMapping = mapping;
  423. *dwFilesize = filesize;
  424. *mapaddr = baseaddr;
  425. retval = NO_ERROR;
  426. } except(EXCEPTION_EXECUTE_HANDLER) {
  427. //
  428. // something bad happened, probably an AV, so just dump everything
  429. // and return an error meaning "Attempt to access invalid address."
  430. //
  431. }
  432. if (retval != NO_ERROR) {
  433. //
  434. // an error occurred, cleanup what we need to
  435. //
  436. UnMapLogFile(baseaddr, logfile, mapping, FALSE);
  437. }
  438. return retval;
  439. }
  440. BOOL
  441. IsSectionHeader(
  442. IN PCSTR Header,
  443. IN DWORD Size,
  444. IN PCSTR Beginning
  445. )
  446. /*++
  447. Routine Description:
  448. Determines whether a given string starts with a section header. This is
  449. the routine that essentially defines what a valid section header is.
  450. Arguments:
  451. Header - supplies a pointer to what may be the first character in a header.
  452. Size - supplies the length of the string passed in, which is NOT the size
  453. of the header.
  454. Beginning - supplies a pointer to the beginning of the file.
  455. Return Value:
  456. BOOL indicating if Header points to a valid section header.
  457. --*/
  458. {
  459. //
  460. // assume a header looks like [foobar]\r\n
  461. //
  462. DWORD i;
  463. //
  464. // state holds the value we're looking for
  465. UINT state = '[';
  466. //
  467. // a section header must always be either at the start of a line or at
  468. // the beginning of a file
  469. //
  470. if (Header != Beginning && Header[-1] != '\n')
  471. return FALSE;
  472. for (i = 0; i < Size; i++) {
  473. switch (state) {
  474. case '[':
  475. if (Header[i] == '[') {
  476. state = ']';
  477. } else {
  478. return FALSE;
  479. }
  480. break;
  481. case ']':
  482. if (Header[i] == ']') {
  483. state = '\r';
  484. }
  485. break;
  486. case '\r':
  487. if (Header[i] == '\r') {
  488. state = '\n';
  489. //
  490. // allow for the case where a line has a linefeed, but no CR
  491. //
  492. } else if (Header[i] == '\n') {
  493. return TRUE;
  494. } else {
  495. return FALSE;
  496. }
  497. break;
  498. case '\n':
  499. if (Header[i] == '\n') {
  500. return TRUE;
  501. } else {
  502. return FALSE;
  503. }
  504. //
  505. // break; -- commented out to avoid unreachable code error
  506. //
  507. default:
  508. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Invalid state! (%d)\n"), state));
  509. MYASSERT(0);
  510. }
  511. }
  512. return FALSE;
  513. }
  514. BOOL
  515. IsEqualSection(
  516. IN PCSTR Section1,
  517. IN DWORD Len1,
  518. IN PCSTR Section2,
  519. IN DWORD Len2
  520. )
  521. /*++
  522. Routine Description:
  523. Says whether two ANSI strings both start with the same section header. One of
  524. the strings must be just a section header, while the other one may be
  525. anything, such as the entire log file.
  526. Arguments:
  527. Section1 - supplies the address of the first string.
  528. Len1 - supplies the length of the first string.
  529. Section2 - supplies the address of the second string.
  530. Len2 - supplies the length of the second string.
  531. Return Value:
  532. BOOL indicating if the longer string starts with the shorter string.
  533. --*/
  534. {
  535. //
  536. // maxlen is the maximum length that both strings could be, and still be
  537. // the same section name
  538. //
  539. DWORD maxlen = Len2;
  540. if (Len1 < Len2) {
  541. maxlen = Len1;
  542. }
  543. if (_strnicmp(Section1, Section2, maxlen) == 0) {
  544. //
  545. // they're the same (ignoring case)
  546. //
  547. return TRUE;
  548. }
  549. return FALSE;
  550. }
  551. DWORD
  552. AppendLogEntryToSection(
  553. IN PCTSTR FileName,
  554. IN PCSTR Section,
  555. IN PCSTR Entry,
  556. IN BOOL SimpleAppend
  557. )
  558. /*++
  559. Routine Description:
  560. Opens the log file, finds the appropriate section, moves it to the end of
  561. the file, appends the new entry, and closes the file.
  562. Arguments:
  563. FileName - supplies the path name of the log file.
  564. Section - supplies the ANSI name of the section to be logged to.
  565. Entry - supplies the ANSI string to be logged.
  566. SimpleAppend - specifies whether entries will simply be appended to the log
  567. file or appended to the section where they belong.
  568. Return Value:
  569. NO_ERROR if the entry gets written to the log file.
  570. Win32 error or exception code if anything went wrong.
  571. --*/
  572. {
  573. DWORD retval = NO_ERROR;
  574. DWORD fpoff;
  575. HANDLE hLogfile = INVALID_HANDLE_VALUE;
  576. HANDLE hMapping = NULL;
  577. DWORD filesize = 0;
  578. PSTR baseaddr = NULL;
  579. DWORD sectlen = lstrlenA(Section);
  580. DWORD entrylen = lstrlenA(Entry);
  581. DWORD error;
  582. BOOL seteof = FALSE;
  583. BOOL mapped = FALSE;
  584. PSTR eof;
  585. PSTR curptr;
  586. PSTR lastsect = NULL;
  587. try {
  588. MYASSERT(Section != NULL && Entry != NULL);
  589. sectlen = lstrlenA(Section);
  590. entrylen = lstrlenA(Entry);
  591. if (sectlen == 0 || entrylen == 0) {
  592. //
  593. // not an error as such, but not useful either
  594. //
  595. retval = NO_ERROR;
  596. leave;
  597. }
  598. error = MapLogFile(
  599. FileName,
  600. &hLogfile,
  601. &hMapping,
  602. &filesize,
  603. &baseaddr,
  604. sectlen + entrylen + 8);// add some extra space to the mapping
  605. // to take into account the log entry
  606. // +2 to terminate unterminated last line
  607. // +2 to append CRLF or ": " after section
  608. // +2 to append CRLF after entrylen if req
  609. // +2 for good measure
  610. if (error != NO_ERROR) {
  611. //
  612. // could not map file
  613. //
  614. retval = error;
  615. leave;
  616. }
  617. mapped = TRUE;
  618. eof = baseaddr + filesize; // end of file, as of now
  619. curptr = eof;
  620. while (curptr > baseaddr && (curptr[-1]==0 || curptr[-1]==0x1A)) {
  621. //
  622. // eat up trailing Nul's or ^Z's
  623. // the former is a side-effect of mapping
  624. // the latter could be introduced by an editor
  625. //
  626. curptr --;
  627. eof = curptr;
  628. }
  629. if (eof > baseaddr && eof[-1] != '\n') {
  630. //
  631. // make sure file already ends in LF
  632. // if it doesn't, append a CRLF
  633. //
  634. memcpy(eof, "\r\n", 2);
  635. eof += 2;
  636. }
  637. if (SimpleAppend) {
  638. //
  639. // instead of having a regular section header, the section name is
  640. // placed at the beginning of each log line followed by a colon.
  641. // this is particularly only of interest when debugging the logging functions
  642. //
  643. memcpy(eof, Section, sectlen);
  644. eof += sectlen;
  645. memcpy(eof, ": ", 2);
  646. eof += 2;
  647. } else {
  648. //
  649. // the entry must be appended to the correct section in the log,
  650. // which requires finding the section and moving it to the end of
  651. // the file if required.
  652. //
  653. // search backwards in the file, looking for the section header
  654. //
  655. if (eof == baseaddr) {
  656. //
  657. // truncated (empty) file
  658. //
  659. curptr = NULL;
  660. } else {
  661. curptr = eof - 1;
  662. while(curptr > baseaddr) {
  663. //
  664. // scan for section header a line at a time
  665. // going backwards, since our section should be near end
  666. //
  667. if (curptr[-1] == '\n') {
  668. //
  669. // speed optimization: only bother checking if we think we're at the beginning of a new line
  670. // this may find a '\n' that is part of a MBCS char,
  671. // but should be eliminated by IsSectionHeader check
  672. //
  673. if (IsSectionHeader(curptr, (DWORD)(eof - curptr), baseaddr)) {
  674. //
  675. // looks like a section header, now see if it's the one we want
  676. //
  677. if (IsEqualSection(curptr, (DWORD)(eof - curptr), Section, sectlen)) {
  678. //
  679. // yep - done
  680. //
  681. break;
  682. } else {
  683. //
  684. // will eventually be the section after the one of interest
  685. //
  686. lastsect = curptr;
  687. }
  688. }
  689. }
  690. curptr --;
  691. }
  692. if (curptr == baseaddr) {
  693. //
  694. // final check if we got to the beginning of the file (no find)
  695. //
  696. if (IsSectionHeader(curptr, (DWORD)(eof - curptr), baseaddr)) {
  697. //
  698. // the first line should always be a section header
  699. //
  700. if (!IsEqualSection(curptr, (DWORD)(eof - curptr), Section, sectlen)) {
  701. //
  702. // first section isn't the one of interest
  703. // so therefore we couldn't find it
  704. //
  705. curptr = NULL;
  706. }
  707. }
  708. }
  709. }
  710. if (curptr == NULL) {
  711. //
  712. // no matching section found (or file was empty)
  713. // copy the section header to the end of the file
  714. // eof is known to be actual end of file
  715. //
  716. memcpy(eof, Section, sectlen);
  717. eof += sectlen;
  718. memcpy(eof, "\r\n", 2);
  719. eof += 2;
  720. } else if (lastsect != NULL) {
  721. //
  722. // we have to rearrange the sections, as we have a case as follows:
  723. //
  724. // ....
  725. // ....
  726. // (curptr) [section A] = section of interest
  727. // ....
  728. // ....
  729. // (lastsect) [section B] = section after section of interest
  730. // ....
  731. // ....
  732. //
  733. // we want to move the text between curptr and lastsect to end of file
  734. //
  735. PSTR buffer = MyMalloc((DWORD)(lastsect - curptr));
  736. if (buffer) {
  737. // first copy the important section to the buffer
  738. //
  739. memcpy(buffer, curptr, (size_t)(lastsect - curptr));
  740. //
  741. // now move the rest of the thing back
  742. //
  743. memcpy(curptr, lastsect, (size_t)(eof - lastsect));
  744. //
  745. // put the important section at the end where it belongs
  746. //
  747. memcpy(curptr - lastsect + eof, buffer, (size_t)(lastsect - curptr));
  748. MyFree(buffer);
  749. } else {
  750. //
  751. // For some reason, we cannot allocate enough memory.
  752. //
  753. // There are 4 options here:
  754. // 1. Do nothing; this will cause the entry to be appended to
  755. // the file, but as part of the wrong section.
  756. // 2. Bail; this will cause the log entry to get lost.
  757. // 3. Create a second file to contain a temporary copy of the
  758. // section; this will require creating another file, and
  759. // then deleting it.
  760. // 4. Extend the mapping of the current file to be big enough
  761. // to hold another copy of the section; this will cause the
  762. // file to have a lot of 0s or possibly another copy of the
  763. // section, should the machine crash during the processing.
  764. //
  765. // we do option 2 - BAIL!
  766. //
  767. retval = ERROR_NOT_ENOUGH_MEMORY;
  768. leave;
  769. }
  770. }
  771. }
  772. //
  773. // now append the log entry
  774. //
  775. memcpy(eof, Entry, entrylen);
  776. eof += entrylen;
  777. if (eof[-1] != '\n') {
  778. //
  779. // entry did not supply en end of line, so we will
  780. //
  781. memcpy(eof, "\r\n", 2);
  782. eof += 2;
  783. }
  784. //
  785. // because of the memory mapping, the file size will not be correct,
  786. // so set the pointer to where we think the end of file is, and then
  787. // the real EOF will be set after unmapping, but before closing
  788. //
  789. fpoff = SetFilePointer(
  790. hLogfile, // handle of file
  791. (LONG)(eof - baseaddr), // number of bytes to move file pointer
  792. NULL, // pointer to high-order DWORD of
  793. // distance to move
  794. FILE_BEGIN); // how to move
  795. if (fpoff == (DWORD)(-1) && (error = GetLastError()) != NO_ERROR) {
  796. MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: SFP returned %u; eof = %u\n"), error, (eof - baseaddr)));
  797. retval = error;
  798. leave;
  799. }
  800. seteof = TRUE;
  801. retval = NO_ERROR;
  802. } except (EXCEPTION_EXECUTE_HANDLER) {
  803. //
  804. // invalid data
  805. //
  806. retval = ERROR_INVALID_DATA;
  807. }
  808. //
  809. // unmap
  810. //
  811. if (mapped) {
  812. UnMapLogFile(baseaddr, hLogfile, hMapping, seteof);
  813. }
  814. return retval;
  815. }
  816. VOID
  817. WriteLogSectionEntry(
  818. IN PCTSTR FileName,
  819. IN PCTSTR Section,
  820. IN PCTSTR Entry,
  821. IN BOOL SimpleAppend
  822. )
  823. /*++
  824. Routine Description:
  825. Convert parameters to ANSI, then append an entry to a given section of the
  826. log file.
  827. Arguments:
  828. FileName - supplies the path name of the log file.
  829. Section - supplies the name of section.
  830. Entry - supplies the string to append to section.
  831. SimpleAppend - specifies whether entries will simply be appended to the log
  832. file or appended to the section where they belong.
  833. Return Value:
  834. NONE.
  835. --*/
  836. {
  837. PCSTR ansiSection = NULL;
  838. PCSTR ansiEntry = NULL;
  839. try {
  840. MYASSERT(Section != NULL && Entry != NULL);
  841. #ifdef UNICODE
  842. ansiSection = pSetupUnicodeToMultiByte(Section, CP_ACP);
  843. ansiEntry = pSetupUnicodeToMultiByte(Entry, CP_ACP);
  844. if(!ansiSection || !ansiEntry) {
  845. leave;
  846. }
  847. #else
  848. ansiSection = Section;
  849. ansiEntry = Entry;
  850. #endif
  851. AppendLogEntryToSection(
  852. FileName,
  853. ansiSection,
  854. ansiEntry,
  855. SimpleAppend);
  856. } except (EXCEPTION_EXECUTE_HANDLER) {
  857. //
  858. // invalid data
  859. //
  860. }
  861. #ifdef UNICODE
  862. if (ansiSection != NULL) {
  863. MyFree(ansiSection);
  864. }
  865. if (ansiEntry != NULL) {
  866. MyFree(ansiEntry);
  867. }
  868. #endif
  869. }
  870. DWORD
  871. MakeUniqueName(
  872. IN PCTSTR Component, OPTIONAL
  873. OUT PTSTR * UniqueString
  874. )
  875. /*++
  876. Routine Description:
  877. Create a section name that's unique by using a timestamp.
  878. If Component is supplied, append that to the timestamp.
  879. Arguments:
  880. Component - supplies a string to be included in the unique name.
  881. UniqueString - supplies a pointer to be set with return string
  882. Return Value:
  883. Error status
  884. --*/
  885. {
  886. SYSTEMTIME now;
  887. LPTSTR buffer = NULL;
  888. DWORD status = ERROR_INVALID_DATA;
  889. ULONG sz;
  890. LONG UID;
  891. try {
  892. if (UniqueString == NULL) {
  893. //
  894. // invalid param
  895. //
  896. status = ERROR_INVALID_PARAMETER;
  897. leave;
  898. }
  899. *UniqueString = NULL;
  900. if (Component == NULL) {
  901. //
  902. // treat as empty string
  903. //
  904. Component = TEXT("");
  905. }
  906. UID = InterlockedIncrement(&(GlobalLogData.UID)); // returns a new ID value whenever called, ensures uniqueness per process
  907. //
  908. // calculate how big string is going to be, be generous (see wsprintf below)
  909. //
  910. sz = /*[] and padding*/ 4 /*date*/ +5+3+3 /*time*/ +3+3+3 /*PID*/ +12 /*UID*/ +12 /*Component*/ +1+lstrlen(Component);
  911. buffer = MyTaggedMalloc(sz * sizeof(TCHAR),MEMTAG_LCSECTION);
  912. if (buffer == NULL) {
  913. status = ERROR_NOT_ENOUGH_MEMORY;
  914. leave;
  915. }
  916. GetLocalTime(&now);
  917. wsprintf(buffer, TEXT("[%04d/%02d/%02d %02d:%02d:%02d %u.%u%s%s]"),
  918. now.wYear, now.wMonth, now.wDay,
  919. now.wHour, now.wMinute, now.wSecond,
  920. (UINT)GetCurrentProcessId(),
  921. (UINT)UID,
  922. (Component[0] ? TEXT(" ") : TEXT("")),
  923. Component);
  924. *UniqueString = buffer;
  925. buffer = NULL;
  926. status = NO_ERROR;
  927. } except (EXCEPTION_EXECUTE_HANDLER) {
  928. //
  929. // status remains ERROR_INVALID_DATA
  930. //
  931. }
  932. if (buffer != NULL) {
  933. MyTaggedFree(buffer,MEMTAG_LCSECTION);
  934. }
  935. return status;
  936. }
  937. DWORD
  938. CreateLogContext(
  939. IN PCTSTR SectionName, OPTIONAL
  940. IN BOOL UseDefault,
  941. OUT PSETUP_LOG_CONTEXT *LogContext
  942. )
  943. /*++
  944. Routine Description:
  945. Creates and initializes a SETUP_LOG_CONTEXT struct.
  946. Arguments:
  947. SectionName - supplies an initial string to be used as part of the
  948. section name.
  949. LogContext - supplies a pointer to where the pointer to the allocated
  950. SETUP_LOG_CONTEXT should be stored.
  951. Return Value:
  952. NO_ERROR in case of successful structure creation.
  953. Win32 error code in case of error.
  954. --*/
  955. {
  956. PSETUP_LOG_CONTEXT lc = NULL;
  957. DWORD status = ERROR_INVALID_DATA;
  958. DWORD rc;
  959. try {
  960. if (LogContext == NULL) {
  961. status = ERROR_INVALID_PARAMETER;
  962. leave;
  963. }
  964. *LogContext = NULL;
  965. if (UseDefault) {
  966. lc = GetThreadLogContext();
  967. RefLogContext(lc);
  968. }
  969. if (!lc) {
  970. lc = (PSETUP_LOG_CONTEXT) MyTaggedMalloc(sizeof(SETUP_LOG_CONTEXT),MEMTAG_LOGCONTEXT);
  971. if (lc == NULL) {
  972. status = ERROR_NOT_ENOUGH_MEMORY;
  973. leave;
  974. }
  975. //
  976. // all fields start out at 0
  977. //
  978. ZeroMemory(lc, sizeof(SETUP_LOG_CONTEXT));
  979. lc->RefCount = 1;
  980. lc->ContextInfo = NULL;
  981. lc->ContextIndexes = NULL;
  982. lc->ContextBufferSize = 0;
  983. lc->ContextLastUnused = -1;
  984. lc->ContextFirstUsed = -1;
  985. lc->ContextFirstAuto = -1;
  986. rc = MakeUniqueName(SectionName,&(lc->SectionName));
  987. if (rc != NO_ERROR) {
  988. status = rc;
  989. leave;
  990. }
  991. }
  992. *LogContext = lc;
  993. status = NO_ERROR;
  994. } except (EXCEPTION_EXECUTE_HANDLER) {
  995. //
  996. // status remains ERROR_INVALID_DATA
  997. //
  998. }
  999. if (status != NO_ERROR) {
  1000. if (lc != NULL) {
  1001. DeleteLogContext(lc);
  1002. lc = NULL;
  1003. }
  1004. }
  1005. return status;
  1006. }
  1007. DWORD
  1008. AllocLogInfoSlotOrLevel(
  1009. IN PSETUP_LOG_CONTEXT LogContext,
  1010. IN DWORD Level,
  1011. IN BOOL AutoRelease
  1012. )
  1013. /*++
  1014. Routine Description:
  1015. Obtain a new context stack entry for a context string only if current logging level is less verbose than specified
  1016. Eg, if we specified DRIVER_LOG_VERBOSE, we will either return DRIVER_LOG_VERBOSE (if we would log it) or a slot
  1017. if we would not normally log it.
  1018. Arguments:
  1019. LogContext - supplies a pointer to the SETUP_LOG_CONTEXT to use
  1020. Level - logging level we want to always log the information at
  1021. AutoRelease - if set, will release the context when dumped
  1022. Return Value:
  1023. Slot value to pass to logging functions, or a copy of Level
  1024. note that if there is an error, 0 is returned
  1025. return value can always be passed to ReleaseLogInfoSlot
  1026. --*/
  1027. {
  1028. if((LogContext == NULL) || _WouldNeverLog(Level)) {
  1029. //
  1030. // when 0 get's passed to logging functions, it will exit out very quickly
  1031. //
  1032. return 0;
  1033. }
  1034. if(_WouldLog(Level)) {
  1035. //
  1036. // Level specifies a verbosity level that would cause logging
  1037. //
  1038. return Level;
  1039. } else {
  1040. //
  1041. // interestingly enough, we will also get here if Level is a slot
  1042. // this is what we want
  1043. //
  1044. return AllocLogInfoSlot(LogContext,AutoRelease);
  1045. }
  1046. }
  1047. DWORD
  1048. AllocLogInfoSlot(
  1049. IN PSETUP_LOG_CONTEXT LogContext,
  1050. IN BOOL AutoRelease
  1051. )
  1052. /*++
  1053. Routine Description:
  1054. Obtain a new context stack entry for a context string
  1055. Arguments:
  1056. LogContext - supplies a pointer to the SETUP_LOG_CONTEXT to use
  1057. AutoRelease - if set, will release the context when dumped
  1058. Return Value:
  1059. Slot value to pass to logging functions
  1060. note that if there is an error, 0 is returned
  1061. which may be safely used (means don't log)
  1062. --*/
  1063. {
  1064. DWORD retval = 0;
  1065. LPVOID newbuffer;
  1066. int newsize;
  1067. int newitem;
  1068. BOOL locked = FALSE;
  1069. if (LogContext == NULL) {
  1070. //
  1071. // if they pass no LogContext - duh!
  1072. //
  1073. return 0;
  1074. }
  1075. if (((GlobalLogData.Flags & SETUP_LOG_LEVELMASK) <= SETUP_LOG_NOLOG)
  1076. &&((GlobalLogData.Flags & DRIVER_LOG_LEVELMASK) <= DRIVER_LOG_NOLOG)) {
  1077. //
  1078. // no logging, period! Don't waste time in locked code
  1079. //
  1080. return 0;
  1081. }
  1082. try {
  1083. LogLock();
  1084. locked = TRUE;
  1085. if (LogContext->ContextLastUnused < 0) {
  1086. //
  1087. // need to allocate more
  1088. //
  1089. if (LogContext->ContextBufferSize >= SETUP_LOG_CONTEXTMASK) {
  1090. //
  1091. // too many contexts
  1092. //
  1093. leave;
  1094. }
  1095. //
  1096. // need to (re)alloc buffer
  1097. //
  1098. newsize = LogContext->ContextBufferSize+10;
  1099. if (LogContext->ContextInfo) {
  1100. newbuffer = MyTaggedRealloc(LogContext->ContextInfo,sizeof(PTSTR)*(newsize),MEMTAG_LCINFO);
  1101. } else {
  1102. newbuffer = MyTaggedMalloc(sizeof(PTSTR)*(newsize),MEMTAG_LCINFO);
  1103. }
  1104. if (newbuffer == NULL) {
  1105. leave;
  1106. }
  1107. LogContext->ContextInfo = (PTSTR*)newbuffer;
  1108. if (LogContext->ContextIndexes) {
  1109. newbuffer = MyTaggedRealloc(LogContext->ContextIndexes,sizeof(UINT)*(newsize),MEMTAG_LCINDEXES);
  1110. } else {
  1111. newbuffer = MyTaggedMalloc(sizeof(UINT)*(newsize),MEMTAG_LCINDEXES);
  1112. }
  1113. if (newbuffer == NULL) {
  1114. leave;
  1115. }
  1116. LogContext->ContextIndexes = (UINT*)newbuffer;
  1117. LogContext->ContextLastUnused = LogContext->ContextBufferSize;
  1118. LogContext->ContextBufferSize ++;
  1119. while(LogContext->ContextBufferSize < newsize) {
  1120. LogContext->ContextIndexes[LogContext->ContextBufferSize-1] = LogContext->ContextBufferSize;
  1121. LogContext->ContextBufferSize ++;
  1122. }
  1123. LogContext->ContextIndexes[LogContext->ContextBufferSize-1] = -1;
  1124. }
  1125. newitem = LogContext->ContextLastUnused;
  1126. LogContext->ContextLastUnused = LogContext->ContextIndexes[newitem];
  1127. if(AutoRelease) {
  1128. if (LogContext->ContextFirstAuto<0) {
  1129. //
  1130. // first auto-release context item
  1131. //
  1132. LogContext->ContextFirstAuto = newitem;
  1133. } else {
  1134. int lastitem = LogContext->ContextFirstAuto;
  1135. while (LogContext->ContextIndexes[lastitem]>=0) {
  1136. lastitem = LogContext->ContextIndexes[lastitem];
  1137. }
  1138. LogContext->ContextIndexes[lastitem] = newitem;
  1139. }
  1140. } else {
  1141. if (LogContext->ContextFirstUsed<0) {
  1142. //
  1143. // first context item
  1144. //
  1145. LogContext->ContextFirstUsed = newitem;
  1146. } else {
  1147. int lastitem = LogContext->ContextFirstUsed;
  1148. while (LogContext->ContextIndexes[lastitem]>=0) {
  1149. lastitem = LogContext->ContextIndexes[lastitem];
  1150. }
  1151. LogContext->ContextIndexes[lastitem] = newitem;
  1152. }
  1153. }
  1154. LogContext->ContextIndexes[newitem] = -1; // init
  1155. LogContext->ContextInfo[newitem] = NULL;
  1156. retval = (DWORD)(newitem) | SETUP_LOG_IS_CONTEXT;
  1157. } except (EXCEPTION_EXECUTE_HANDLER) {
  1158. //
  1159. // do nothing special; this just allows us to catch errors
  1160. //
  1161. retval = 0;
  1162. }
  1163. if(locked) {
  1164. LogUnlock();
  1165. }
  1166. //
  1167. // returns a logging flag (SETUP_LOG_IS_CONTEXT | n) or 0
  1168. //
  1169. return retval;
  1170. }
  1171. VOID
  1172. ReleaseLogInfoSlot(
  1173. IN PSETUP_LOG_CONTEXT LogContext,
  1174. DWORD Slot
  1175. )
  1176. /*++
  1177. Routine Description:
  1178. Releases (non auto-release) slot previously obtained
  1179. Arguments:
  1180. LogContext - supplies a pointer to the SETUP_LOG_CONTEXT to use
  1181. Slot - supplies Slot value returned by AllocLogInfoSlot
  1182. Return Value:
  1183. none
  1184. --*/
  1185. {
  1186. int item;
  1187. int lastitem;
  1188. BOOL locked = FALSE;
  1189. if ((Slot & SETUP_LOG_IS_CONTEXT) == 0) {
  1190. //
  1191. // GetLogContextMark had failed, value wasn't set, or not a context log
  1192. //
  1193. return;
  1194. }
  1195. MYASSERT(LogContext != NULL);
  1196. try {
  1197. LogLock();
  1198. locked = TRUE;
  1199. //
  1200. // log context must have been supplied
  1201. //
  1202. item = (int)(Slot & SETUP_LOG_CONTEXTMASK);
  1203. MYASSERT(item >= 0);
  1204. MYASSERT(item < LogContext->ContextBufferSize);
  1205. MYASSERT(LogContext->ContextFirstUsed >= 0);
  1206. //
  1207. // remove item out of linked list
  1208. //
  1209. if (item == LogContext->ContextFirstUsed) {
  1210. //
  1211. // removing first in list
  1212. //
  1213. LogContext->ContextFirstUsed = LogContext->ContextIndexes[item];
  1214. } else {
  1215. lastitem = LogContext->ContextFirstUsed;
  1216. while (lastitem >= 0) {
  1217. if (LogContext->ContextIndexes[lastitem] == item) {
  1218. LogContext->ContextIndexes[lastitem] = LogContext->ContextIndexes[item];
  1219. break;
  1220. }
  1221. lastitem = LogContext->ContextIndexes[lastitem];
  1222. }
  1223. }
  1224. //
  1225. // drop a string that hasn't been output
  1226. //
  1227. if (LogContext->ContextInfo[item] != NULL) {
  1228. MyTaggedFree(LogContext->ContextInfo[item],MEMTAG_LCBUFFER);
  1229. LogContext->ContextInfo[item] = NULL;
  1230. }
  1231. //
  1232. // add item into free list
  1233. //
  1234. LogContext->ContextIndexes[item] = LogContext->ContextLastUnused;
  1235. LogContext->ContextLastUnused = item;
  1236. } except (EXCEPTION_EXECUTE_HANDLER) {
  1237. //
  1238. // do nothing special; this just allows us to catch errors
  1239. //
  1240. }
  1241. if(locked) {
  1242. LogUnlock();
  1243. }
  1244. }
  1245. VOID
  1246. ReleaseLogInfoList(
  1247. IN PSETUP_LOG_CONTEXT LogContext,
  1248. IN OUT PINT ListStart
  1249. )
  1250. /*++
  1251. Routine Description:
  1252. Releases whole list of slots
  1253. Helper function. Caller must have exclusive access to LogContext
  1254. Arguments:
  1255. LogContext - supplies a pointer to the SETUP_LOG_CONTEXT to use
  1256. ListStart - pointer to list index
  1257. Return Value:
  1258. none
  1259. --*/
  1260. {
  1261. int item;
  1262. MYASSERT(ListStart);
  1263. try {
  1264. if (*ListStart < 0) {
  1265. //
  1266. // list is empty
  1267. //
  1268. leave;
  1269. }
  1270. //
  1271. // log context must have been supplied
  1272. //
  1273. MYASSERT(LogContext != NULL);
  1274. while (*ListStart >= 0) {
  1275. item = *ListStart; // item we're about to release
  1276. MYASSERT(item < LogContext->ContextBufferSize);
  1277. *ListStart = LogContext->ContextIndexes[item]; // next item on list (we're going to trash this index)
  1278. if (LogContext->ContextInfo[item] != NULL) {
  1279. MyTaggedFree(LogContext->ContextInfo[item],MEMTAG_LCBUFFER); // release string if still allocated
  1280. LogContext->ContextInfo[item] = NULL;
  1281. }
  1282. //
  1283. // add to free list
  1284. //
  1285. LogContext->ContextIndexes[item] = LogContext->ContextLastUnused;
  1286. LogContext->ContextLastUnused = item;
  1287. }
  1288. } except (EXCEPTION_EXECUTE_HANDLER) {
  1289. //
  1290. // do nothing special; this just allows us to catch errors
  1291. //
  1292. }
  1293. }
  1294. VOID
  1295. DeleteLogContext(
  1296. IN PSETUP_LOG_CONTEXT LogContext
  1297. )
  1298. /*++
  1299. Routine Description:
  1300. Decrement ref count of LogContext, and delete if zero.
  1301. Arguments:
  1302. LogContext - supplies a pointer to the SETUP_LOG_CONTEXT to be deleted.
  1303. Return Value:
  1304. NONE.
  1305. --*/
  1306. {
  1307. BOOL locked = FALSE;
  1308. if (!LogContext) {
  1309. return;
  1310. }
  1311. try {
  1312. LogLock();
  1313. locked = TRUE;
  1314. //
  1315. // check ref count
  1316. //
  1317. MYASSERT(LogContext->RefCount > 0);
  1318. if (--LogContext->RefCount) {
  1319. leave;
  1320. }
  1321. //
  1322. // we can unlock now, since we have exclusive access to this context (it is unowned)
  1323. // and we don't want to hold global lock longer than needed
  1324. //
  1325. LogUnlock();
  1326. locked = FALSE;
  1327. ReleaseLogInfoList(LogContext,&LogContext->ContextFirstAuto);
  1328. ReleaseLogInfoList(LogContext,&LogContext->ContextFirstUsed);
  1329. if (LogContext->SectionName) {
  1330. MyTaggedFree(LogContext->SectionName,MEMTAG_LCSECTION);
  1331. }
  1332. if (LogContext->Buffer) {
  1333. MyTaggedFree(LogContext->Buffer,MEMTAG_LCBUFFER);
  1334. }
  1335. if (LogContext->ContextInfo) {
  1336. MyTaggedFree(LogContext->ContextInfo,MEMTAG_LCINFO);
  1337. }
  1338. if (LogContext->ContextIndexes) {
  1339. MyTaggedFree(LogContext->ContextIndexes,MEMTAG_LCINDEXES);
  1340. }
  1341. //
  1342. // now deallocate the struct
  1343. //
  1344. MyTaggedFree(LogContext,MEMTAG_LOGCONTEXT);
  1345. } except (EXCEPTION_EXECUTE_HANDLER) {
  1346. //
  1347. // cleanup below
  1348. //
  1349. }
  1350. //
  1351. // if we have not yet released global lock, release it now
  1352. //
  1353. if(locked) {
  1354. LogUnlock();
  1355. }
  1356. return;
  1357. }
  1358. DWORD
  1359. RefLogContext( // increment reference count
  1360. IN PSETUP_LOG_CONTEXT LogContext
  1361. )
  1362. /*++
  1363. Routine Description:
  1364. Increment the reference count on a SETUP_LOG_CONTEXT object.
  1365. Arguments:
  1366. LogContext - supplies a pointer to a valid SETUP_LOG_CONTEXT object. If
  1367. NULL, this is a NOP.
  1368. Return Value:
  1369. DWORD containing old reference count.
  1370. --*/
  1371. {
  1372. DWORD ref = 0;
  1373. BOOL locked = FALSE;
  1374. if (LogContext == NULL) {
  1375. return 0;
  1376. }
  1377. try {
  1378. LogLock();
  1379. locked = TRUE;
  1380. ref = LogContext->RefCount++;
  1381. MYASSERT(LogContext->RefCount);
  1382. } except (EXCEPTION_EXECUTE_HANDLER) {
  1383. //
  1384. // do nothing; this just allows us to catch errors
  1385. //
  1386. }
  1387. if(locked) {
  1388. LogUnlock();
  1389. }
  1390. return ref;
  1391. }
  1392. VOID
  1393. SendLogString(
  1394. IN PSETUP_LOG_CONTEXT LogContext,
  1395. IN PCTSTR Buffer
  1396. )
  1397. /*++
  1398. Routine Description:
  1399. Send a string to the logfile and/or debugger based on settings.
  1400. It's expected that LogLock has been called prior to calling this function
  1401. LogLock causes per-process thread synchronisation
  1402. Arguments:
  1403. LogContext - supplies a pointer to a valid SETUP_LOG_CONTEXT object.
  1404. Buffer - supplies the buffer to be sent to the logfile/debugger.
  1405. Return Value:
  1406. NONE.
  1407. --*/
  1408. {
  1409. int len;
  1410. try {
  1411. MYASSERT(LogContext);
  1412. MYASSERT(Buffer);
  1413. if (Buffer[0] == 0) {
  1414. //
  1415. // useless call
  1416. //
  1417. leave;
  1418. }
  1419. if (GlobalLogData.FileName) {
  1420. WriteLogSectionEntry(
  1421. GlobalLogData.FileName,
  1422. LogContext->SectionName,
  1423. Buffer,
  1424. (GlobalLogData.Flags & SETUP_LOG_SIMPLE) ? TRUE : FALSE);
  1425. }
  1426. //
  1427. // do debugger output here
  1428. //
  1429. if (GlobalLogData.Flags & SETUP_LOG_DEBUGOUT) {
  1430. DebugPrintEx(DPFLTR_ERROR_LEVEL,
  1431. TEXT("SetupAPI: %s: %s"),
  1432. LogContext->SectionName,
  1433. Buffer);
  1434. len = lstrlen(Buffer);
  1435. if (Buffer[len-1] != TEXT('\n')) {
  1436. DebugPrintEx(DPFLTR_ERROR_LEVEL, TEXT("\r\n"));
  1437. }
  1438. }
  1439. } except (EXCEPTION_EXECUTE_HANDLER) {
  1440. //
  1441. // do nothing; this just allows us to catch errors
  1442. //
  1443. }
  1444. }
  1445. DWORD
  1446. pSetupWriteLogEntry(
  1447. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  1448. IN DWORD Level,
  1449. IN DWORD MessageId,
  1450. IN PCTSTR MessageStr, OPTIONAL
  1451. ... OPTIONAL
  1452. )
  1453. /*++
  1454. Routine Description:
  1455. Write a log entry to a file or debugger. If MessageId is 0 and MessageStr
  1456. is NULL, the LogContext's buffer will be flushed.
  1457. Arguments:
  1458. LogContext - optionally supplies a pointer to the SETUP_LOG_CONTEXT to be
  1459. used for logging. If not supplied, a temporary one is created just for
  1460. a single use.
  1461. Level - bitmask indicating logging flags. See SETUP_LOG_* and DRIVER_LOG_*
  1462. at the beginning of cntxtlog.h for details. It may also be a slot
  1463. returned by AllocLogInfoSlot, or 0 (no logging)
  1464. MessageId - ID of string from string table. Ignored if MessageStr is
  1465. supplied. The string may contain formatting codes for FormatMessage.
  1466. MessageStr - optionally supplies string to be formatted with FormatMessage.
  1467. If not supplied, MessageId is used instead.
  1468. ... - supply optional parameters based on string to be formatted.
  1469. Return Value:
  1470. Win32 error code.
  1471. --*/
  1472. {
  1473. PSETUP_LOG_CONTEXT lc = NULL;
  1474. DWORD retval = NO_ERROR;
  1475. DWORD error;
  1476. DWORD flags;
  1477. DWORD context = 0;
  1478. DWORD logmask;
  1479. DWORD count;
  1480. LPVOID source = NULL;
  1481. PTSTR buffer = NULL;
  1482. PTSTR locbuffer = NULL;
  1483. PTSTR buffer2 = NULL;
  1484. va_list arglist;
  1485. BOOL logit = FALSE;
  1486. BOOL timestamp = FALSE;
  1487. BOOL endsync = FALSE;
  1488. SYSTEMTIME now;
  1489. TCHAR scratch[1024];
  1490. int logindex;
  1491. int thisindex;
  1492. int numeric=0;
  1493. try {
  1494. //
  1495. // return immediately if we know we'll never log
  1496. //
  1497. if (_WouldNeverLog(Level)) {
  1498. retval = NO_ERROR;
  1499. leave;
  1500. }
  1501. if ((Level & SETUP_LOG_IS_CONTEXT)!=0) {
  1502. //
  1503. // write to context slot
  1504. //
  1505. if(Level & ~SETUP_LOG_VALIDCONTEXTBITS) {
  1506. MYASSERT((Level & ~SETUP_LOG_VALIDCONTEXTBITS)==0);
  1507. retval = ERROR_INVALID_PARAMETER;
  1508. leave;
  1509. }
  1510. if ((GlobalLogData.Flags & SETUP_LOG_ALL_CONTEXT)!=0) {
  1511. //
  1512. // don't treat as context - log it anyway
  1513. //
  1514. Level = 0;
  1515. logit = TRUE;
  1516. } else if (LogContext) {
  1517. //
  1518. // determine which slot
  1519. //
  1520. context = Level & SETUP_LOG_CONTEXTMASK;
  1521. Level = SETUP_LOG_IS_CONTEXT; // effective log level, we've stripped out log context
  1522. logit = TRUE;
  1523. } else {
  1524. //
  1525. // can't write something to slot if there's no LogContext
  1526. //
  1527. leave;
  1528. }
  1529. }
  1530. if(!logit) {
  1531. //
  1532. // we're still not sure if we'll end up logging this, let's see if we should log this based on level rules
  1533. //
  1534. logit = _WouldLog(Level);
  1535. if (!logit) {
  1536. leave;
  1537. }
  1538. }
  1539. if (LogContext == NULL) {
  1540. //
  1541. // if they pass no LogContext and they want buffering, this call's a nop
  1542. //
  1543. if (Level & SETUP_LOG_BUFFER) {
  1544. retval = NO_ERROR;
  1545. leave;
  1546. }
  1547. //
  1548. // now make a temporary context
  1549. //
  1550. error = CreateLogContext(NULL, TRUE, &lc);
  1551. if (error != NO_ERROR) {
  1552. lc = NULL;
  1553. retval = error;
  1554. leave;
  1555. }
  1556. LogContext = lc;
  1557. }
  1558. //
  1559. // after this point, we know we're going to log something, and we know we have a LogContext
  1560. // note that going down this path is a perf hit.
  1561. // anything we can do in reducing number of times we go down here for "context" information is good
  1562. //
  1563. // hold the lock through to cleanup. It is needed for ReleaseLogInfoList,
  1564. // LogContext modifications and will reduce conflicts when actually writing to the log file
  1565. //
  1566. LogLock();
  1567. endsync = TRUE; // indicate we need to release later
  1568. timestamp = (GlobalLogData.Flags & SETUP_LOG_TIMESTAMP)
  1569. || ((Level & DRIVER_LOG_LEVELMASK) >= DRIVER_LOG_TIME)
  1570. || ((Level & SETUP_LOG_LEVELMASK) >= SETUP_LOG_TIME)
  1571. || (((Level & SETUP_LOG_LEVELMASK) > 0) && (SETUP_LOG_TIMEALL <= (GlobalLogData.Flags & SETUP_LOG_LEVELMASK)))
  1572. || (((Level & DRIVER_LOG_LEVELMASK) > 0) && (DRIVER_LOG_TIMEALL <= (GlobalLogData.Flags & DRIVER_LOG_LEVELMASK)));
  1573. if ((Level & SETUP_LOG_IS_CONTEXT) == FALSE) {
  1574. //
  1575. // only do this if we're about to do REAL logging
  1576. //
  1577. // if this is the first log output in the section, we will give the
  1578. // command line and module to help the user see what's going on
  1579. //
  1580. if (LogContext->LoggedEntries==0) {
  1581. //
  1582. // recursively call ourselves to log what the command line is
  1583. // note that some apps (eg rundll32) will go and trash command line
  1584. // if this is the case, try and do the right thing
  1585. // we're willing to spend a little extra time in this case, since we know we're going to
  1586. // log something to the section, and we'll only do this once per section
  1587. //
  1588. PTSTR CmdLine = GetCommandLine();
  1589. LogContext->LoggedEntries++; // stop calling this code when we do the pSetupWriteLogEntry's below
  1590. if (CmdLine[0] == TEXT('\"')) {
  1591. CmdLine++;
  1592. }
  1593. if(_tcsnicmp(ProcessFileName,CmdLine,_tcslen(ProcessFileName))==0) {
  1594. //
  1595. // commandline is prefixed with process file name
  1596. // chance is it's good
  1597. //
  1598. pSetupWriteLogEntry(
  1599. LogContext,
  1600. AllocLogInfoSlot(LogContext,TRUE), // delayed slot
  1601. MSG_LOG_COMMAND_LINE,
  1602. NULL,
  1603. GetCommandLine());
  1604. } else {
  1605. //
  1606. // it appears that the command line has been modified somewhat
  1607. // so show what we have
  1608. //
  1609. pSetupWriteLogEntry(
  1610. LogContext,
  1611. AllocLogInfoSlot(LogContext,TRUE), // delayed slot
  1612. MSG_LOG_BAD_COMMAND_LINE,
  1613. NULL,
  1614. ProcessFileName,
  1615. GetCommandLine());
  1616. #ifdef UNICODE
  1617. {
  1618. //
  1619. // UNICODE only
  1620. //
  1621. // now see if we can get something more useful by looking at the ANSI command line buffer
  1622. //
  1623. PSTR AnsiProcessFileName = pSetupUnicodeToMultiByte(ProcessFileName,CP_ACP);
  1624. PSTR AnsiCmdLine = GetCommandLineA();
  1625. if (AnsiCmdLine[0] == '\"') {
  1626. AnsiCmdLine++;
  1627. }
  1628. if(AnsiProcessFileName && _mbsnicmp(AnsiProcessFileName,AnsiCmdLine,_mbslen(AnsiProcessFileName))==0) {
  1629. //
  1630. // well, the Ansi version appears ok, let's use that
  1631. //
  1632. pSetupWriteLogEntry(
  1633. LogContext,
  1634. AllocLogInfoSlot(LogContext,TRUE), // delayed slot
  1635. MSG_LOG_COMMAND_LINE_ANSI,
  1636. NULL,
  1637. GetCommandLineA());
  1638. } else {
  1639. //
  1640. // appears that both Unicode and Ansi might be bad
  1641. //
  1642. AnsiCmdLine = pSetupUnicodeToMultiByte(GetCommandLine(),CP_ACP);
  1643. if (AnsiCmdLine && _mbsicmp(AnsiCmdLine,GetCommandLineA())!=0) {
  1644. //
  1645. // also log ansi as reference, since it's different
  1646. //
  1647. pSetupWriteLogEntry(
  1648. LogContext,
  1649. AllocLogInfoSlot(LogContext,TRUE), // delayed slot
  1650. MSG_LOG_BAD_COMMAND_LINE_ANSI,
  1651. NULL,
  1652. GetCommandLineA());
  1653. }
  1654. if (AnsiCmdLine) {
  1655. MyFree(AnsiCmdLine);
  1656. }
  1657. }
  1658. if (AnsiProcessFileName) {
  1659. MyFree(AnsiProcessFileName);
  1660. }
  1661. }
  1662. #endif // UNICODE
  1663. }
  1664. #ifdef UNICODE
  1665. #ifndef _WIN64
  1666. //
  1667. // we're running 32-bit setupapi
  1668. //
  1669. if (IsWow64) {
  1670. //
  1671. // we're running it under WOW64
  1672. //
  1673. pSetupWriteLogEntry(
  1674. LogContext,
  1675. AllocLogInfoSlot(LogContext,TRUE), // delayed slot
  1676. MSG_LOG_WOW64,
  1677. NULL,
  1678. GetCommandLine());
  1679. }
  1680. #endif
  1681. #endif // UNICODE
  1682. }
  1683. }
  1684. flags = FORMAT_MESSAGE_ALLOCATE_BUFFER;
  1685. //
  1686. // if MessageStr is supplied, we use that; otherwise use a
  1687. // string from a string table
  1688. //
  1689. if (MessageStr) {
  1690. flags |= FORMAT_MESSAGE_FROM_STRING;
  1691. source = (PTSTR) MessageStr; // cast away const
  1692. } else if (MessageId) {
  1693. //
  1694. // the message ID may be an HRESULT error code
  1695. //
  1696. if (MessageId & 0xC0000000) {
  1697. flags |= FORMAT_MESSAGE_FROM_SYSTEM;
  1698. //
  1699. // Some system messages contain inserts, but whomever is calling
  1700. // will not supply them, so this flag prevents us from
  1701. // tripping over those cases.
  1702. //
  1703. flags |= FORMAT_MESSAGE_IGNORE_INSERTS;
  1704. } else {
  1705. flags |= FORMAT_MESSAGE_FROM_HMODULE;
  1706. source = MyDllModuleHandle;
  1707. numeric = (int)(MessageId-MSG_LOG_FIRST);
  1708. }
  1709. }
  1710. if (MessageStr || MessageId) {
  1711. va_start(arglist, MessageStr);
  1712. count = FormatMessage(
  1713. flags,
  1714. source,
  1715. MessageId,
  1716. 0, // LANGID
  1717. (LPTSTR) &locbuffer,
  1718. 0, // minimum size of buffer
  1719. &arglist);
  1720. } else {
  1721. //
  1722. // There is no string to format, so we are probably just
  1723. // flushing the buffer.
  1724. //
  1725. count = 1;
  1726. }
  1727. if (count > 0) {
  1728. //
  1729. // no error; prefix string with a code and place into a MyMalloc allocated buffer
  1730. // we don't want to prefix string with a code if we're appending to an existing message
  1731. //
  1732. if (locbuffer) {
  1733. if ((numeric > 0) && (LogContext->Buffer==NULL)) {
  1734. //
  1735. // determine level code, which indicates severity for why we logged this
  1736. // and machine readable ID
  1737. //
  1738. if (Level & SETUP_LOG_IS_CONTEXT) {
  1739. //
  1740. // if this is context information, use #-xxxx
  1741. //
  1742. _stprintf(scratch,TEXT("#-%03d "),numeric);
  1743. } else {
  1744. logindex = LOGLEVELSHORT_INIT; // maps to 0. after >>4&0x0f.
  1745. if ((Level & SETUP_LOG_LEVELMASK) > 0 && (Level & SETUP_LOG_LEVELMASK) <= (GlobalLogData.Flags & SETUP_LOG_LEVELMASK)) {
  1746. thisindex = (Level & SETUP_LOG_LEVELMASK) >> SETUP_LOG_SHIFT;
  1747. if (thisindex < logindex) {
  1748. logindex = thisindex;
  1749. }
  1750. }
  1751. if ((Level & DRIVER_LOG_LEVELMASK) > 0 && (Level & DRIVER_LOG_LEVELMASK) <= (GlobalLogData.Flags & DRIVER_LOG_LEVELMASK)) {
  1752. thisindex = (Level & DRIVER_LOG_LEVELMASK) >> DRIVER_LOG_SHIFT;
  1753. if (thisindex < logindex) {
  1754. logindex = thisindex;
  1755. }
  1756. }
  1757. //
  1758. // #Cxxxx #Vxxxx etc
  1759. //
  1760. _stprintf(scratch,TEXT("#%c%03d "),LogLevelShort[(logindex>>LOGLEVELSHORT_SHIFT)&LOGLEVELSHORT_MASK],numeric);
  1761. }
  1762. } else {
  1763. scratch[0] = TEXT('\0');
  1764. }
  1765. buffer = (PTSTR)MyTaggedMalloc((lstrlen(scratch)+lstrlen(locbuffer)+1)*sizeof(TCHAR),MEMTAG_LCBUFFER);
  1766. if (buffer) {
  1767. lstrcpy(buffer,scratch);
  1768. lstrcat(buffer,locbuffer);
  1769. }
  1770. LocalFree(locbuffer);
  1771. } else {
  1772. buffer = NULL;
  1773. }
  1774. //
  1775. // Check to see if the buffer has anything in it. If so, the newest
  1776. // string needs to be appended to it.
  1777. //
  1778. if (LogContext->Buffer) {
  1779. //
  1780. // in case of a flush, buffer == NULL
  1781. //
  1782. if (buffer!=NULL) {
  1783. int blen = lstrlen(LogContext->Buffer);
  1784. int pad = 0;
  1785. TCHAR lastchr = *CharPrev(LogContext->Buffer,LogContext->Buffer+blen);
  1786. if (lastchr != TEXT(' ')) {
  1787. //
  1788. // silently correct any errors in the message text (which should end in a space)
  1789. //
  1790. while((lastchr == TEXT('\t')) ||
  1791. (lastchr == TEXT('\r')) ||
  1792. (lastchr == TEXT('\n'))) {
  1793. blen--; // these characters are always sizeof(TCHAR)
  1794. lastchr = *CharPrev(LogContext->Buffer,LogContext->Buffer+blen);
  1795. }
  1796. LogContext->Buffer[blen] = TEXT('\0');
  1797. if (lastchr != TEXT(' ')) {
  1798. //
  1799. // we want to insert a space padding
  1800. //
  1801. pad++;
  1802. }
  1803. }
  1804. buffer2 = MyTaggedRealloc(LogContext->Buffer,
  1805. (blen + pad + lstrlen(buffer) + 1) * sizeof(TCHAR),
  1806. MEMTAG_LCBUFFER
  1807. );
  1808. //
  1809. // if the realloc was successful, add the new data, otherwise
  1810. // just drop it on the floor
  1811. //
  1812. if (buffer2) {
  1813. if (pad) {
  1814. lstrcat(buffer2,TEXT(" "));
  1815. }
  1816. lstrcat(buffer2, buffer);
  1817. LogContext->Buffer = buffer2;
  1818. buffer2 = NULL;
  1819. }
  1820. MyTaggedFree(buffer,MEMTAG_LCBUFFER);
  1821. buffer = NULL;
  1822. }
  1823. buffer = LogContext->Buffer;
  1824. LogContext->Buffer = NULL;
  1825. }
  1826. if (Level & SETUP_LOG_BUFFER) {
  1827. LogContext->Buffer = buffer;
  1828. buffer = NULL;
  1829. } else if (Level & SETUP_LOG_IS_CONTEXT) {
  1830. PTSTR TempDupeString;
  1831. //
  1832. // replace the string indicated
  1833. //
  1834. if(buffer) {
  1835. if (LogContext->ContextInfo[context]) {
  1836. MyTaggedFree(LogContext->ContextInfo[context],MEMTAG_LCBUFFER);
  1837. }
  1838. LogContext->ContextInfo[context] = buffer;
  1839. buffer = NULL;
  1840. }
  1841. } else {
  1842. int item;
  1843. //
  1844. // actually do some logging
  1845. //
  1846. LogContext->LoggedEntries++;
  1847. if (!LogContext->SectionName) {
  1848. error = MakeUniqueName(NULL,&(LogContext->SectionName));
  1849. }
  1850. //
  1851. // first dump the auto-release context info
  1852. //
  1853. item = LogContext->ContextFirstAuto;
  1854. while (item >= 0) {
  1855. if (LogContext->ContextInfo[item]) {
  1856. //
  1857. // dump this string
  1858. //
  1859. SendLogString(LogContext, LogContext->ContextInfo[item]);
  1860. MyTaggedFree (LogContext->ContextInfo[item],MEMTAG_LCBUFFER);
  1861. LogContext->ContextInfo[item] = NULL;
  1862. }
  1863. item = LogContext->ContextIndexes[item];
  1864. }
  1865. ReleaseLogInfoList(LogContext,&LogContext->ContextFirstAuto);
  1866. //
  1867. // now dump any strings set in currently allocated slots
  1868. //
  1869. item = LogContext->ContextFirstUsed;
  1870. while (item >= 0) {
  1871. if (LogContext->ContextInfo[item]) {
  1872. //
  1873. // dump this string
  1874. //
  1875. SendLogString(LogContext, LogContext->ContextInfo[item]);
  1876. MyTaggedFree (LogContext->ContextInfo[item],MEMTAG_LCBUFFER);
  1877. LogContext->ContextInfo[item] = NULL;
  1878. }
  1879. item = LogContext->ContextIndexes[item];
  1880. }
  1881. //
  1882. // we have built up a line to send
  1883. //
  1884. if (buffer != NULL) {
  1885. if(timestamp) {
  1886. //
  1887. // this is the point we're interested in prefixing with timestamp
  1888. // this allows us to build up a string, then emit it prefixed with stamp
  1889. //
  1890. GetLocalTime(&now);
  1891. _stprintf(scratch, TEXT("@ %02d:%02d:%02d.%03d "),
  1892. now.wHour, now.wMinute, now.wSecond, now.wMilliseconds);
  1893. buffer2 = MyTaggedMalloc((lstrlen(scratch)+lstrlen(buffer)+1)*sizeof(TCHAR),MEMTAG_LCBUFFER);
  1894. if (buffer2) {
  1895. lstrcpy(buffer2,scratch);
  1896. lstrcat(buffer2,buffer);
  1897. MyTaggedFree(buffer,MEMTAG_LCBUFFER);
  1898. buffer = buffer2;
  1899. buffer2 = NULL;
  1900. }
  1901. }
  1902. SendLogString(LogContext,buffer);
  1903. }
  1904. }
  1905. } else {
  1906. //
  1907. // the FormatMessage failed
  1908. //
  1909. retval = GetLastError();
  1910. if(retval == NO_ERROR) {
  1911. retval = ERROR_INVALID_DATA;
  1912. }
  1913. }
  1914. } except (EXCEPTION_EXECUTE_HANDLER) {
  1915. //
  1916. // do nothing special; this just allows us to catch errors
  1917. //
  1918. retval = ERROR_INVALID_DATA;
  1919. }
  1920. //
  1921. // cleanup
  1922. //
  1923. if (endsync) {
  1924. LogUnlock();
  1925. }
  1926. if (buffer) {
  1927. MyTaggedFree(buffer,MEMTAG_LCBUFFER);
  1928. }
  1929. if (lc) {
  1930. DeleteLogContext(lc);
  1931. }
  1932. return retval;
  1933. }
  1934. VOID
  1935. SetLogSectionName(
  1936. IN PSETUP_LOG_CONTEXT LogContext,
  1937. IN PCTSTR SectionName
  1938. )
  1939. /*++
  1940. Routine Description:
  1941. Sets the section name for the log context if it hasn't been used.
  1942. Arguments:
  1943. LogContext - supplies pointer to SETUP_LOG_CONTEXT.
  1944. SectionName - supplies a pointer to a string to be included in the
  1945. section name.
  1946. Return Value:
  1947. NONE.
  1948. --*/
  1949. {
  1950. DWORD rc;
  1951. PTSTR NewSectionName = NULL;
  1952. BOOL locked = FALSE;
  1953. MYASSERT(LogContext);
  1954. MYASSERT(SectionName);
  1955. try {
  1956. LogLock();
  1957. locked = TRUE;
  1958. //
  1959. // make sure the entry has never been used before
  1960. //
  1961. if (LogContext->LoggedEntries==0 || LogContext->SectionName==NULL) {
  1962. //
  1963. // get rid of any previous name
  1964. //
  1965. rc = MakeUniqueName(SectionName,&NewSectionName);
  1966. if (rc == NO_ERROR) {
  1967. if (LogContext->SectionName) {
  1968. MyTaggedFree(LogContext->SectionName,MEMTAG_LCSECTION);
  1969. }
  1970. LogContext->SectionName = NewSectionName;
  1971. }
  1972. }
  1973. } except (EXCEPTION_EXECUTE_HANDLER) {
  1974. }
  1975. if(locked) {
  1976. LogUnlock();
  1977. }
  1978. }
  1979. #if MEM_DBG
  1980. #undef InheritLogContext // defined again below
  1981. #endif
  1982. DWORD
  1983. InheritLogContext(
  1984. IN TRACK_ARG_DECLARE TRACK_ARG_COMMA
  1985. IN PSETUP_LOG_CONTEXT Source,
  1986. OUT PSETUP_LOG_CONTEXT *Dest
  1987. )
  1988. /*++
  1989. Routine Description:
  1990. Copies a log context from one structure to another, deleting the one that
  1991. gets overwritten. If Source and Dest are both NULL, a new log context is
  1992. created for Dest.
  1993. Arguments:
  1994. Source - supplies pointer to source SETUP_LOG_CONTEXT. If NULL, this
  1995. creates a new log context for Dest.
  1996. Dest - supplies the location to receive a pointer to the log context.
  1997. Return Value:
  1998. NONE.
  1999. --*/
  2000. {
  2001. DWORD status = ERROR_INVALID_DATA;
  2002. DWORD rc;
  2003. PSETUP_LOG_CONTEXT Old = NULL;
  2004. TRACK_PUSH
  2005. try {
  2006. MYASSERT(Dest);
  2007. Old = *Dest;
  2008. if (Old == NULL && Source == NULL) {
  2009. //
  2010. // this is a roundabout way of saying we want to create a context
  2011. // used when the source logcontext is optional
  2012. //
  2013. rc = CreateLogContext(NULL, TRUE, Dest);
  2014. if (rc != NO_ERROR) {
  2015. status = rc;
  2016. leave;
  2017. }
  2018. } else if (Source != NULL && (Old == NULL || Old->LoggedEntries == 0)) {
  2019. //
  2020. // We can replace Dest, since it hasn't been used yet
  2021. //
  2022. *Dest = Source;
  2023. RefLogContext(Source);
  2024. if (Old != NULL) {
  2025. //
  2026. // now delete old
  2027. //
  2028. DeleteLogContext(Old);
  2029. }
  2030. }
  2031. status = NO_ERROR;
  2032. } except (EXCEPTION_EXECUTE_HANDLER) {
  2033. //
  2034. // do nothing; this just allows us to catch errors
  2035. //
  2036. }
  2037. TRACK_POP
  2038. return status;
  2039. }
  2040. #if MEM_DBG
  2041. #define InheritLogContext(a,b) InheritLogContext(TRACK_ARG_CALL,a,b)
  2042. #endif
  2043. DWORD
  2044. ShareLogContext(
  2045. IN OUT PSETUP_LOG_CONTEXT *Primary,
  2046. IN OUT PSETUP_LOG_CONTEXT *Secondary
  2047. )
  2048. /*++
  2049. Routine Description:
  2050. Bidirectional inherit
  2051. Arguments:
  2052. Primary - preferred source
  2053. Secondary - preferred target
  2054. Return Value:
  2055. any potential error
  2056. --*/
  2057. {
  2058. DWORD rc = ERROR_INVALID_DATA;
  2059. try {
  2060. MYASSERT(Primary);
  2061. MYASSERT(*Primary);
  2062. MYASSERT(Secondary);
  2063. MYASSERT(*Secondary);
  2064. if((*Secondary)->LoggedEntries) {
  2065. //
  2066. // secondary has already been used, so see if we can update primary
  2067. //
  2068. rc = InheritLogContext(*Secondary,Primary);
  2069. } else {
  2070. //
  2071. // else behave exactly like InheritLogContext
  2072. //
  2073. rc = InheritLogContext(*Primary,Secondary);
  2074. }
  2075. } except (EXCEPTION_EXECUTE_HANDLER) {
  2076. //
  2077. // do nothing; this just allows us to catch errors
  2078. //
  2079. }
  2080. return rc;
  2081. }
  2082. VOID
  2083. pSetupWriteLogError(
  2084. IN PSETUP_LOG_CONTEXT LogContext, OPTIONAL
  2085. IN DWORD Level,
  2086. IN DWORD Error
  2087. )
  2088. /*++
  2089. Routine Description:
  2090. Logs an error code and an error message on the same line.
  2091. Arguments:
  2092. LogContext - supplies a pointer to a valid SETUP_LOG_CONTEXT object. If
  2093. NULL, this is a NOP.
  2094. Level - supplies a log level as defined by pSetupWriteLogEntry.
  2095. Error - supplies the Win32 error, HRESULT, or SETUPAPI error code to log.
  2096. Return Value:
  2097. NONE.
  2098. --*/
  2099. {
  2100. DWORD err;
  2101. if (!LogContext) {
  2102. //
  2103. // error is meaningless without context
  2104. //
  2105. goto final;
  2106. }
  2107. if (Error == NO_ERROR) {
  2108. pSetupWriteLogEntry(
  2109. LogContext,
  2110. Level,
  2111. MSG_LOG_NO_ERROR,
  2112. NULL);
  2113. goto final;
  2114. }
  2115. pSetupWriteLogEntry(
  2116. LogContext,
  2117. Level | SETUP_LOG_BUFFER,
  2118. //
  2119. // print HRESULTs in hex, Win32 errors in decimal
  2120. //
  2121. (Error & 0xC0000000 ? MSG_LOG_HRESULT_ERROR
  2122. : MSG_LOG_WIN32_ERROR),
  2123. NULL,
  2124. Error);
  2125. //
  2126. // If it's a Win32 error, we convert it to an HRESULT, because
  2127. // pSetupWriteLogEntry only knows that it's an error code by the fact
  2128. // that it's an HRESULT. However, we don't want the user to
  2129. // get an HRESULT if we can help it, so just do the conversion
  2130. // after converting to a string. Also, SETUPAPI errors are not
  2131. // in proper HRESULT format without conversion
  2132. //
  2133. Error = HRESULT_FROM_SETUPAPI(Error);
  2134. //
  2135. // writing the error message may fail...
  2136. //
  2137. err = pSetupWriteLogEntry(
  2138. LogContext,
  2139. Level,
  2140. Error,
  2141. NULL);
  2142. if (err != NO_ERROR) {
  2143. pSetupWriteLogEntry(
  2144. LogContext,
  2145. Level,
  2146. MSG_LOG_UNKNOWN_ERROR,
  2147. NULL);
  2148. }
  2149. final:
  2150. SetLastError(Error);
  2151. }
  2152. BOOL
  2153. ContextLoggingTlsInit(
  2154. IN BOOL Init
  2155. )
  2156. /*++
  2157. Routine Description:
  2158. Init = TRUE Initializes per-thread data for logging
  2159. Init = FALSE releases memory on cleanup
  2160. Arguments:
  2161. Init - set to initialize
  2162. Return Value:
  2163. TRUE if initialized ok.
  2164. --*/
  2165. {
  2166. BOOL b = FALSE;
  2167. PSETUP_TLS pTLS;
  2168. PSETUP_LOG_TLS pLogTLS;
  2169. pTLS = SetupGetTlsData();
  2170. MYASSERT(pTLS);
  2171. pLogTLS = &pTLS->SetupLog;
  2172. if (Init) {
  2173. pLogTLS->ThreadLogContext = NULL;
  2174. b = TRUE;
  2175. } else {
  2176. //
  2177. // ISSUE-JamieHun-2001/05/01 ASSERT when thread terminated
  2178. // thread might not have terminated cleanly
  2179. // causing this assert to fire
  2180. //
  2181. // MYASSERT(!pLogTLS->ThreadLogContext);
  2182. b = TRUE;
  2183. }
  2184. return b;
  2185. }
  2186. BOOL
  2187. SetThreadLogContext(
  2188. IN PSETUP_LOG_CONTEXT LogContext,
  2189. OUT PSETUP_LOG_CONTEXT *PrevContext OPTIONAL
  2190. )
  2191. /*++
  2192. Routine Description:
  2193. Modify current thread log context
  2194. Arguments:
  2195. LogContext new log context (expected to be apropriately ref counted)
  2196. PrevContext if set, filled with previous context
  2197. Return Value:
  2198. TRUE if set ok.
  2199. --*/
  2200. {
  2201. PSETUP_TLS pTLS;
  2202. PSETUP_LOG_TLS pLogTLS;
  2203. PSETUP_LOG_CONTEXT Top;
  2204. pTLS = SetupGetTlsData();
  2205. if(!pTLS) {
  2206. return FALSE;
  2207. }
  2208. pLogTLS = &pTLS->SetupLog;
  2209. if (PrevContext) {
  2210. *PrevContext = pLogTLS->ThreadLogContext;
  2211. }
  2212. pLogTLS->ThreadLogContext = LogContext;
  2213. return TRUE;
  2214. }
  2215. PSETUP_LOG_CONTEXT
  2216. GetThreadLogContext(
  2217. )
  2218. /*++
  2219. Routine Description:
  2220. Return thread's default log context
  2221. Arguments:
  2222. NONE
  2223. Return Value:
  2224. Current LogContext or NULL
  2225. --*/
  2226. {
  2227. PSETUP_TLS pTLS;
  2228. pTLS = SetupGetTlsData();
  2229. if(!pTLS) {
  2230. return NULL;
  2231. }
  2232. return pTLS->SetupLog.ThreadLogContext;
  2233. }
  2234. BOOL
  2235. InitializeContextLogging(
  2236. IN BOOL Attach
  2237. )
  2238. /*++
  2239. Routine Description:
  2240. Initializes structures/data for logging or releases allocated memory.
  2241. Arguments:
  2242. Attach - set when called at attach time as opposed to detach time
  2243. Return Value:
  2244. TRUE if initialized ok.
  2245. --*/
  2246. {
  2247. BOOL Successful = FALSE;
  2248. if (Attach) {
  2249. LONG error;
  2250. HKEY key;
  2251. HKEY loglevel;
  2252. DWORD len;
  2253. DWORD level = 0;
  2254. DWORD type;
  2255. PTSTR PathName = NULL;
  2256. TCHAR testchar;
  2257. BOOL isdir = FALSE;
  2258. GlobalLogData.FileName = NULL;
  2259. GlobalLogData.Flags = 0;
  2260. GlobalLogData.UID = 0;
  2261. GlobalLogData.DoneInitCritSec = FALSE;
  2262. try {
  2263. InitializeCriticalSection(&GlobalLogData.CritSec);
  2264. GlobalLogData.DoneInitCritSec = TRUE;
  2265. error = RegOpenKeyEx(
  2266. HKEY_LOCAL_MACHINE,
  2267. REGSTR_PATH_SETUP REGSTR_KEY_SETUP,
  2268. 0, // reserved
  2269. KEY_QUERY_VALUE,
  2270. &key);
  2271. if (error == ERROR_SUCCESS) {
  2272. if(QueryRegistryDwordValue(key,SP_REGKEY_LOGLEVEL,&level) != NO_ERROR) {
  2273. level = 0;
  2274. }
  2275. if(QueryRegistryValue(key,SP_REGKEY_LOGPATH,&PathName,&type,&len) != NO_ERROR) {
  2276. PathName = NULL;
  2277. }
  2278. //
  2279. // Allow a user to override the log level for a particular program
  2280. //
  2281. error = RegOpenKeyEx(
  2282. key,
  2283. SP_REGKEY_APPLOGLEVEL,
  2284. 0, // reserved
  2285. KEY_QUERY_VALUE,
  2286. &loglevel);
  2287. if (error == ERROR_SUCCESS) {
  2288. DWORD override;
  2289. if(QueryRegistryDwordValue(loglevel,pSetupGetFileTitle(ProcessFileName),&override) == NO_ERROR) {
  2290. level = override;
  2291. }
  2292. RegCloseKey(loglevel);
  2293. }
  2294. RegCloseKey(key);
  2295. }
  2296. //
  2297. // if they don't supply a valid name, we use the Windows dir
  2298. //
  2299. if (!(PathName && PathName[0])) {
  2300. if(PathName) {
  2301. MyFree(PathName);
  2302. }
  2303. PathName = DuplicateString(WindowsDirectory);
  2304. if(!PathName) {
  2305. leave;
  2306. }
  2307. isdir = TRUE; // we know this should be a directory
  2308. } else {
  2309. //
  2310. // see if we're pointing at a directory
  2311. //
  2312. testchar = CharPrev(PathName,PathName+lstrlen(PathName))[0];
  2313. if(testchar == TEXT('\\') || testchar == TEXT('/')) {
  2314. //
  2315. // explicit directiory
  2316. //
  2317. isdir = TRUE;
  2318. } else {
  2319. DWORD attr = GetFileAttributes(PathName);
  2320. if (isdir || (attr != (DWORD)(-1) && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 )) {
  2321. //
  2322. // implicit directory
  2323. //
  2324. isdir = TRUE;
  2325. }
  2326. }
  2327. }
  2328. if (isdir) {
  2329. //
  2330. // if they gave a directory, add a filename
  2331. //
  2332. LPTSTR NewPath;
  2333. if(!pSetupAppendPath(PathName,SP_LOG_FILENAME,&NewPath)) {
  2334. MyFree(PathName);
  2335. PathName = NULL;
  2336. leave;
  2337. }
  2338. MyFree(PathName);
  2339. PathName = NewPath;
  2340. }
  2341. pSetupMakeSurePathExists(PathName);
  2342. //
  2343. // validate level flags
  2344. //
  2345. level &= SETUP_LOG_VALIDREGBITS;
  2346. //
  2347. // handle defaults
  2348. //
  2349. if((level & SETUP_LOG_LEVELMASK) == 0) {
  2350. //
  2351. // level not explicitly set
  2352. //
  2353. level |= SETUP_LOG_DEFAULT;
  2354. }
  2355. if((level & DRIVER_LOG_LEVELMASK) == 0) {
  2356. //
  2357. // level not explicitly set
  2358. //
  2359. level |= DRIVER_LOG_DEFAULT;
  2360. }
  2361. GlobalLogData.Flags = level;
  2362. GlobalLogData.FileName = PathName;
  2363. PathName = NULL;
  2364. if (GlobalLogData.FileName == NULL) {
  2365. leave;
  2366. }
  2367. Successful = TRUE;
  2368. } except (EXCEPTION_EXECUTE_HANDLER) {
  2369. //
  2370. // Successful remains FALSE
  2371. //
  2372. }
  2373. } else {
  2374. if (GlobalLogData.FileName) {
  2375. MyFree(GlobalLogData.FileName);
  2376. GlobalLogData.FileName = NULL;
  2377. }
  2378. if(GlobalLogData.DoneInitCritSec) {
  2379. DeleteCriticalSection(&GlobalLogData.CritSec);
  2380. }
  2381. Successful = TRUE;
  2382. }
  2383. return Successful;
  2384. }