Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1780 lines
37 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. util.c
  5. Abstract:
  6. Implementation of general utility functions.
  7. Author:
  8. Wesley Witt (wesw) 18-Dec-1998
  9. Revision History:
  10. Andrew Ritz (andrewr) 6-Jul-1999 : added comments
  11. --*/
  12. #include "sfcp.h"
  13. #pragma hdrstop
  14. //
  15. // use this define to force path redirections
  16. //
  17. //#define SFC_REDIRECTOR_TEST
  18. #define CONST_UNICODE_STRING(sz) { sizeof(sz) - 1, sizeof(sz), sz }
  19. #ifndef _WIN64
  20. typedef struct _SFC_EXPAND_TRANSLATION_ENTRY
  21. {
  22. LPCWSTR Src; // full path to translate from (does not end in \\)
  23. LPCWSTR Dest; // full path to translate to
  24. ULONG ExceptionCount; // count of elements in Exceptions
  25. const UNICODE_STRING* Exceptions; // array of excepted paths, relative to Src (begins with \\ but does not end in \\)
  26. }
  27. SFC_EXPAND_TRANSLATION_ENTRY;
  28. typedef struct _SFC_TRANSLATION_ENTRY
  29. {
  30. UNICODE_STRING Src;
  31. UNICODE_STRING Dest;
  32. }
  33. SFC_TRANSLATION_ENTRY;
  34. //
  35. // exception lists; relative paths with no environment variables
  36. //
  37. static const UNICODE_STRING SfcSystem32Exceptions[] =
  38. {
  39. CONST_UNICODE_STRING(L"\\drivers\\etc"),
  40. CONST_UNICODE_STRING(L"\\spool"),
  41. CONST_UNICODE_STRING(L"\\catroot"),
  42. CONST_UNICODE_STRING(L"\\catroot2")
  43. };
  44. //
  45. // translation table that is expanded into SfcTranslations
  46. //
  47. static const SFC_EXPAND_TRANSLATION_ENTRY SfcExpandTranslations[] =
  48. {
  49. { L"%windir%\\system32", L"%windir%\\syswow64", ARRAY_LENGTH(SfcSystem32Exceptions), SfcSystem32Exceptions },
  50. { L"%windir%\\ime", L"%windir%\\ime (x86)", 0, NULL },
  51. { L"%windir%\\regedit.exe", L"%windir%\\syswow64\\regedit.exe", 0, NULL }
  52. };
  53. //
  54. // translation table with expanded strings
  55. //
  56. static SFC_TRANSLATION_ENTRY* SfcTranslations = NULL;
  57. //
  58. // this guards the initialization of SfcTranslations
  59. //
  60. static RTL_CRITICAL_SECTION SfcTranslatorCs;
  61. static BOOL SfcNeedTranslation = FALSE;
  62. static BOOL SfcIsTranslatorInitialized = FALSE;
  63. #endif // _WIN64
  64. PVOID
  65. SfcGetProcAddress(
  66. HMODULE hModule,
  67. LPSTR ProcName
  68. )
  69. /*++
  70. Routine Description:
  71. Gets the address of the specified function.
  72. Arguments:
  73. hModule - Module handle returned from LdrLoadDll.
  74. ProcName - Procedure name
  75. Return Value:
  76. NULL if the function does not exist.
  77. Valid address is the function is found.
  78. --*/
  79. {
  80. NTSTATUS Status;
  81. PVOID ProcedureAddress;
  82. STRING ProcedureName;
  83. ASSERT((hModule != NULL) && (ProcName != NULL));
  84. RtlInitString(&ProcedureName,ProcName);
  85. Status = LdrGetProcedureAddress(
  86. hModule,
  87. &ProcedureName,
  88. 0,
  89. &ProcedureAddress
  90. );
  91. if (!NT_SUCCESS(Status)) {
  92. DebugPrint2( LVL_MINIMAL, L"GetProcAddress failed for %S, ec=%lx", ProcName, Status );
  93. return NULL;
  94. }
  95. return ProcedureAddress;
  96. }
  97. HMODULE
  98. SfcLoadLibrary(
  99. IN PCWSTR LibFileName
  100. )
  101. /*++
  102. Routine Description:
  103. Loads the specified DLL into session manager's
  104. address space and return the loaded DLL's address.
  105. Arguments:
  106. LibFileName - Name of the desired DLL
  107. Return Value:
  108. NULL if the DLL cannot be loaded.
  109. Valid address is the DLL is loaded.
  110. --*/
  111. {
  112. NTSTATUS Status;
  113. HMODULE hModule;
  114. UNICODE_STRING DllName_U;
  115. ASSERT(LibFileName);
  116. RtlInitUnicodeString( &DllName_U, LibFileName );
  117. Status = LdrLoadDll(
  118. NULL,
  119. NULL,
  120. &DllName_U,
  121. (PVOID *)&hModule
  122. );
  123. if (!NT_SUCCESS( Status )) {
  124. DebugPrint2( LVL_MINIMAL, L"LoadDll failed for %ws, ec=%lx", LibFileName, Status );
  125. return NULL;
  126. }
  127. return hModule;
  128. }
  129. PVOID
  130. MemAlloc(
  131. SIZE_T AllocSize
  132. )
  133. /*++
  134. Routine Description:
  135. Allocates the specified number of bytes using the default process heap.
  136. Arguments:
  137. AllocSize - size in bytes of memory to be allocated
  138. Return Value:
  139. pointer to allocated memory or NULL for failure.
  140. --*/
  141. {
  142. return RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, AllocSize );
  143. }
  144. PVOID
  145. MemReAlloc(
  146. SIZE_T AllocSize,
  147. PVOID OrigPtr
  148. )
  149. /*++
  150. Routine Description:
  151. ReAllocates the specified number of bytes using the default process heap.
  152. Arguments:
  153. AllocSize - size in bytes of memory to be reallocated
  154. OrigPtr - original heap memory pointer
  155. Return Value:
  156. pointer to allocated memory or NULL for failure.
  157. --*/
  158. {
  159. PVOID ptr;
  160. ptr = RtlReAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, OrigPtr, AllocSize );
  161. if (!ptr) {
  162. DebugPrint1( LVL_MINIMAL, L"MemReAlloc [%d bytes] failed", AllocSize );
  163. }
  164. return(ptr);
  165. }
  166. VOID
  167. MemFree(
  168. PVOID MemPtr
  169. )
  170. /*++
  171. Routine Description:
  172. Free's the memory at the specified location from the default process heap.
  173. Arguments:
  174. MemPtr - pointer to memory to be freed. If NULL, no action is taken.
  175. Return Value:
  176. none.
  177. --*/
  178. {
  179. if (MemPtr) {
  180. RtlFreeHeap( RtlProcessHeap(), 0, MemPtr );
  181. }
  182. }
  183. void
  184. SfcWriteDebugLog(
  185. IN LPCSTR String,
  186. IN ULONG Length OPTIONAL
  187. )
  188. {
  189. NTSTATUS Status;
  190. OBJECT_ATTRIBUTES Attrs;
  191. UNICODE_STRING FileName;
  192. IO_STATUS_BLOCK iosb;
  193. HANDLE hFile;
  194. ASSERT(String != NULL);
  195. if(RtlDosPathNameToNtPathName_U(g_szLogFile, &FileName, NULL, NULL))
  196. {
  197. InitializeObjectAttributes(&Attrs, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
  198. Status = NtCreateFile(
  199. &hFile,
  200. FILE_APPEND_DATA | SYNCHRONIZE,
  201. &Attrs,
  202. &iosb,
  203. NULL,
  204. FILE_ATTRIBUTE_NORMAL,
  205. FILE_SHARE_READ,
  206. FILE_OPEN_IF,
  207. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
  208. NULL,
  209. 0
  210. );
  211. MemFree(FileName.Buffer);
  212. if(!NT_SUCCESS(Status))
  213. {
  214. #if DBG
  215. DbgPrint("Could not open the log file.\r\n");
  216. #endif
  217. return;
  218. }
  219. if(0 == Length)
  220. Length = strlen(String);
  221. Status = NtWriteFile(hFile, NULL, NULL, NULL, &iosb, (PVOID) String, Length, NULL, NULL);
  222. NtClose(hFile);
  223. #if DBG
  224. if(!NT_SUCCESS(Status))
  225. DbgPrint("Could not write the log file.\r\n");
  226. #endif
  227. }
  228. }
  229. #define CHECK_DEBUG_LEVEL(var, l) ((var) != LVL_SILENT && (l) <= (var))
  230. void
  231. dprintf(
  232. IN ULONG Level,
  233. IN PCWSTR FileName,
  234. IN ULONG LineNumber,
  235. IN PCWSTR FormatStr,
  236. IN ...
  237. )
  238. /*++
  239. Routine Description:
  240. Main debugger output routine. Callers should use the DebugPrintX macro,
  241. which is compiled out in the retail version of the product
  242. Arguments:
  243. Level - indicates a LVL_ severity level so the amount of output can be
  244. controlled.
  245. FileName - string indicating the filename the debug comes from
  246. LineNumber - indicates the line number of the debug output.
  247. FormatStr - indicates the data to be output
  248. Return Value:
  249. none.
  250. --*/
  251. {
  252. static WCHAR buf[4096];
  253. static CHAR str[4096];
  254. va_list arg_ptr;
  255. ULONG Bytes;
  256. PWSTR p;
  257. SYSTEMTIME CurrTime;
  258. #if DBG
  259. if(!CHECK_DEBUG_LEVEL(SFCDebugDump, Level) && !CHECK_DEBUG_LEVEL(SFCDebugLog, Level))
  260. return;
  261. #else
  262. if(!CHECK_DEBUG_LEVEL(SFCDebugLog, Level))
  263. return;
  264. #endif
  265. GetLocalTime( &CurrTime );
  266. try {
  267. p = buf + swprintf( buf, L"SFC: %02d:%02d:%02d.%03d ",
  268. CurrTime.wHour,
  269. CurrTime.wMinute,
  270. CurrTime.wSecond,
  271. CurrTime.wMilliseconds
  272. );
  273. #if DBG
  274. if (FileName && LineNumber) {
  275. PWSTR s;
  276. s = wcsrchr( FileName, L'\\' );
  277. if (s) {
  278. p += swprintf( p, L"%12s @ %4d ", s+1, LineNumber );
  279. }
  280. }
  281. #else
  282. //
  283. // put only the line number in output
  284. //
  285. p += swprintf(p, L"@ %4d ", LineNumber);
  286. #endif
  287. va_start( arg_ptr, FormatStr );
  288. p += _vsnwprintf( p, 2048, FormatStr, arg_ptr );
  289. va_end( arg_ptr );
  290. wcscpy( p, L"\r\n" );
  291. p += wcslen(p);
  292. } except(EXCEPTION_EXECUTE_HANDLER) {
  293. return;
  294. }
  295. Bytes = (ULONG)(p - buf);
  296. WideCharToMultiByte(
  297. CP_ACP,
  298. 0,
  299. buf,
  300. Bytes + 1, // include the null
  301. str,
  302. sizeof(str),
  303. NULL,
  304. NULL
  305. );
  306. #if DBG
  307. if(CHECK_DEBUG_LEVEL(SFCDebugDump, Level))
  308. DbgPrint( str );
  309. if(CHECK_DEBUG_LEVEL(SFCDebugLog, Level))
  310. SfcWriteDebugLog(str, Bytes);
  311. #else
  312. SfcWriteDebugLog(str, Bytes);
  313. #endif
  314. }
  315. #if DBG
  316. UCHAR HandleBuffer[1024*64];
  317. VOID
  318. PrintHandleCount(
  319. PCWSTR str
  320. )
  321. /*++
  322. Routine Description:
  323. Outputs the handle count for the current process to the debugger. Use
  324. this call before and after a function to look for handle leaks (the input
  325. string can help you identify where you are checking the handle count.)
  326. Arguments:
  327. str - null terminated unicode string that prefaces the debug spew
  328. Return Value:
  329. none. Debug routine only.
  330. --*/
  331. {
  332. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  333. NTSTATUS Status;
  334. ULONG TotalOffset;
  335. //
  336. // get the system process information
  337. //
  338. Status = NtQuerySystemInformation(
  339. SystemProcessInformation,
  340. HandleBuffer,
  341. sizeof(HandleBuffer),
  342. NULL
  343. );
  344. if (NT_SUCCESS(Status)) {
  345. //
  346. // find our process and spew the handle count
  347. //
  348. TotalOffset = 0;
  349. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)HandleBuffer;
  350. while(1) {
  351. if ((DWORD_PTR)ProcessInfo->UniqueProcessId == (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess) {
  352. DebugPrint2( LVL_MINIMAL, L"%ws: handle count = %d", str, ProcessInfo->HandleCount );
  353. break;
  354. }
  355. if (ProcessInfo->NextEntryOffset == 0) {
  356. break;
  357. }
  358. TotalOffset += ProcessInfo->NextEntryOffset;
  359. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&HandleBuffer[TotalOffset];
  360. }
  361. }
  362. }
  363. #endif
  364. DWORD
  365. MyMessageBox(
  366. HWND hwndParent, OPTIONAL
  367. DWORD ResId,
  368. DWORD MsgBoxType,
  369. ...
  370. )
  371. /*++
  372. Routine Description:
  373. Messagebox wrapper that retreives a string table resource id and creates a
  374. popup for the given message.
  375. Arguments:
  376. hwndParent - handle to parent window
  377. ResId - resource id of string to be loaded
  378. MsgBoxType - MB_* constant
  379. Return Value:
  380. Win32 error code indicating outcome
  381. --*/
  382. {
  383. static WCHAR Title[128] = { L"\0" };
  384. WCHAR Tmp1[MAX_PATH*2];
  385. WCHAR Tmp2[MAX_PATH*2];
  386. PWSTR Text = NULL;
  387. PWSTR s;
  388. va_list arg_ptr;
  389. int Size;
  390. //
  391. // SFCNoPopUps is a policy setting that can be set
  392. //
  393. if (SFCNoPopUps) {
  394. return(0);
  395. }
  396. //
  397. // load the title string
  398. //
  399. if (!Title[0]) {
  400. Size = LoadString(
  401. SfcInstanceHandle,
  402. IDS_TITLE,
  403. Title,
  404. UnicodeChars(Title)
  405. );
  406. if (Size == 0) {
  407. return(0);
  408. }
  409. }
  410. //
  411. // load the message string
  412. //
  413. Size = LoadString(
  414. SfcInstanceHandle,
  415. ResId,
  416. Tmp1,
  417. UnicodeChars(Tmp1)
  418. );
  419. if (Size == 0) {
  420. return(0);
  421. }
  422. //
  423. // inplace substitution can occur here
  424. //
  425. s = wcschr( Tmp1, L'%' );
  426. if (s) {
  427. va_start( arg_ptr, MsgBoxType );
  428. _vsnwprintf( Tmp2, sizeof(Tmp2)/sizeof(WCHAR), Tmp1, arg_ptr );
  429. va_end( arg_ptr );
  430. Text = Tmp2;
  431. } else {
  432. Text = Tmp1;
  433. }
  434. //
  435. // actually call messagebox now
  436. //
  437. return MessageBox(
  438. hwndParent,
  439. Text,
  440. Title,
  441. (MsgBoxType | MB_TOPMOST) & ~MB_DEFAULT_DESKTOP_ONLY
  442. );
  443. }
  444. BOOL
  445. EnablePrivilege(
  446. IN PCTSTR PrivilegeName,
  447. IN BOOL Enable
  448. )
  449. /*++
  450. Routine Description:
  451. Enable or disable a given named privilege.
  452. Arguments:
  453. PrivilegeName - supplies the name of a system privilege.
  454. Enable - flag indicating whether to enable or disable the privilege.
  455. Return Value:
  456. Boolean value indicating whether the operation was successful.
  457. --*/
  458. {
  459. HANDLE Token;
  460. BOOL b;
  461. TOKEN_PRIVILEGES NewPrivileges;
  462. LUID Luid;
  463. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
  464. return(FALSE);
  465. }
  466. if(!LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) {
  467. CloseHandle(Token);
  468. return(FALSE);
  469. }
  470. NewPrivileges.PrivilegeCount = 1;
  471. NewPrivileges.Privileges[0].Luid = Luid;
  472. NewPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
  473. b = AdjustTokenPrivileges(
  474. Token,
  475. FALSE,
  476. &NewPrivileges,
  477. 0,
  478. NULL,
  479. NULL
  480. );
  481. CloseHandle(Token);
  482. return(b);
  483. }
  484. void
  485. MyLowerString(
  486. IN PWSTR String,
  487. IN ULONG StringLength // in characters
  488. )
  489. /*++
  490. Routine Description:
  491. lowercase the specified string.
  492. Arguments:
  493. String - supplies string to lowercase
  494. StringLength - length in chars of string to be lowercased
  495. Return Value:
  496. none.
  497. --*/
  498. {
  499. ULONG i;
  500. ASSERT(String != NULL);
  501. for (i=0; i<StringLength; i++) {
  502. String[i] = towlower(String[i]);
  503. }
  504. }
  505. #ifdef SFCLOGFILE
  506. void
  507. SfcLogFileWrite(
  508. IN DWORD StrId,
  509. IN ...
  510. )
  511. /*++
  512. Routine Description:
  513. Output the string table resource id as specified to the sfc logfile.
  514. This logfile is used to record files that have been restored on the system.
  515. This way, an installer can know if packages are attempting to install
  516. system components.
  517. The logfile is a unicode text file of the format:
  518. <TIME> <FILENAME>
  519. We need to record the full path of the file plus the date for this to
  520. be more useful.
  521. Arguments:
  522. StrId - supplies resource id to load.
  523. Return Value:
  524. none.
  525. --*/
  526. {
  527. static WCHAR buf[4096];
  528. static HANDLE hFile = INVALID_HANDLE_VALUE;
  529. WCHAR str[128];
  530. va_list arg_ptr;
  531. ULONG Bytes;
  532. PWSTR p;
  533. SYSTEMTIME CurrTime;
  534. GetSystemTime( &CurrTime );
  535. if (hFile == INVALID_HANDLE_VALUE) {
  536. ExpandEnvironmentStrings( L"%systemroot%\\sfclog.txt", buf, UnicodeChars(buf) );
  537. hFile = CreateFile(
  538. buf,
  539. GENERIC_READ | GENERIC_WRITE,
  540. FILE_SHARE_READ,
  541. NULL,
  542. OPEN_ALWAYS,
  543. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
  544. NULL
  545. );
  546. if (hFile != INVALID_HANDLE_VALUE) {
  547. //
  548. // If the file is empty, write out a unicode tag to the front of
  549. // the file.
  550. //
  551. if (GetFileSize( hFile, NULL ) == 0) {
  552. buf[0] = 0xff;
  553. buf[1] = 0xfe;
  554. WriteFile( hFile, buf, 2, &Bytes, NULL );
  555. }
  556. }
  557. }
  558. if (hFile == INVALID_HANDLE_VALUE) {
  559. return;
  560. }
  561. try {
  562. p = buf;
  563. *p = 0;
  564. swprintf( p, L"%02d:%02d:%02d.%03d ",
  565. CurrTime.wHour,
  566. CurrTime.wMinute,
  567. CurrTime.wSecond,
  568. CurrTime.wMilliseconds
  569. );
  570. p += wcslen(p);
  571. LoadString( SfcInstanceHandle, StrId, str, UnicodeChars(str) );
  572. va_start( arg_ptr, StrId );
  573. _vsnwprintf( p, 2048, str, arg_ptr );
  574. va_end( arg_ptr );
  575. p += wcslen(p);
  576. wcscat( p, L"\r\n" );
  577. } except(EXCEPTION_EXECUTE_HANDLER) {
  578. buf[0] = 0;
  579. }
  580. if (buf[0] == 0) {
  581. return;
  582. }
  583. //
  584. // set file pointer to end of file so we don't overwrite data
  585. //
  586. SetFilePointer(hFile,0,0,FILE_END);
  587. WriteFile( hFile, buf, UnicodeLen(buf), &Bytes, NULL );
  588. return;
  589. }
  590. #endif
  591. int
  592. MyDialogBoxParam(
  593. IN DWORD RcId,
  594. IN DLGPROC lpDialogFunc, // pointer to dialog box procedure
  595. IN LPARAM dwInitParam // initialization value
  596. )
  597. /*++
  598. Routine Description:
  599. creates a dialog box on the user's desktop.
  600. Arguments:
  601. RcId - resource id of dialog to be created.
  602. lpDialogFunc - dialog proc for dialog
  603. dwInitParam - initial parameter that WM_INITDIALOG receives in dialogproc
  604. Return Value:
  605. 0 or -1 for failure, else the value from EndDialog.
  606. --*/
  607. {
  608. #if 0
  609. HDESK hDesk = OpenInputDesktop( 0, FALSE, MAXIMUM_ALLOWED );
  610. if ( hDesk ) {
  611. SetThreadDesktop( hDesk );
  612. CloseDesktop( hDesk );
  613. }
  614. #else
  615. SetThreadDesktop( hUserDesktop );
  616. #endif
  617. return (int) DialogBoxParam(
  618. SfcInstanceHandle,
  619. MAKEINTRESOURCE(RcId),
  620. NULL,
  621. lpDialogFunc,
  622. dwInitParam
  623. );
  624. }
  625. void
  626. CenterDialog(
  627. HWND hwnd
  628. )
  629. /*++
  630. Routine Description:
  631. centers the specified window around the middle of the screen..
  632. Arguments:
  633. HWND - handle to window.
  634. Return Value:
  635. none.
  636. --*/
  637. {
  638. RECT rcWindow;
  639. LONG x,y,w,h;
  640. LONG sx = GetSystemMetrics(SM_CXSCREEN),
  641. sy = GetSystemMetrics(SM_CYSCREEN);
  642. ASSERT(IsWindow(hwnd));
  643. GetWindowRect(hwnd,&rcWindow);
  644. w = rcWindow.right - rcWindow.left + 1;
  645. h = rcWindow.bottom - rcWindow.top + 1;
  646. x = (sx - w)/2;
  647. y = (sy - h)/2;
  648. MoveWindow(hwnd,x,y,w,h,FALSE);
  649. }
  650. BOOL
  651. MakeDirectory(
  652. PCWSTR Dir
  653. )
  654. /*++
  655. Routine Description:
  656. Attempt to create all of the directories in the given path.
  657. Arguments:
  658. Dir - Directory path to create
  659. Return Value:
  660. TRUE for success, FALSE on error
  661. --*/
  662. {
  663. LPTSTR p, NewDir;
  664. BOOL retval;
  665. NewDir = p = MemAlloc( (wcslen(Dir) + 1) *sizeof(WCHAR) );
  666. if (p) {
  667. wcscpy(p, Dir);
  668. } else {
  669. return(FALSE);
  670. }
  671. if (*p != '\\') p += 2;
  672. while( *++p ) {
  673. while(*p && *p != TEXT('\\')) p++;
  674. if (!*p) {
  675. retval = CreateDirectory( NewDir, NULL );
  676. retval = retval
  677. ? retval
  678. : (GetLastError() == ERROR_ALREADY_EXISTS) ;
  679. MemFree(NewDir);
  680. return(retval);
  681. }
  682. *p = 0;
  683. retval = CreateDirectory( NewDir, NULL );
  684. if (!retval && GetLastError() != ERROR_ALREADY_EXISTS) {
  685. MemFree(NewDir);
  686. return(retval);
  687. }
  688. *p = TEXT('\\');
  689. }
  690. MemFree( NewDir );
  691. return(TRUE);
  692. }
  693. BOOL
  694. BuildPathForFile(
  695. IN PCWSTR SourceRootPath,
  696. IN PCWSTR SubDirectoryPath, OPTIONAL
  697. IN PCWSTR FileName,
  698. IN BOOL IncludeSubDirectory,
  699. IN BOOL IncludeArchitectureSpecificSubDirectory,
  700. OUT PWSTR PathBuffer,
  701. IN DWORD PathBufferSize
  702. )
  703. /*++
  704. Routine Description:
  705. Builds the specified path into a buffer
  706. Arguments:
  707. SourceRootPath - Specifies the root path to look for.
  708. SubDirectoryPath - Specifies an optional subdirectory under the root
  709. path where the file is located
  710. FileName - Specifies the filename to look for.
  711. IncludeSubDirectory - If TRUE, specifies that the subdirectory
  712. specification should be used.
  713. IncludeArchitectureSpecificSubDirectory - If TRUE, specifies that the
  714. architecture specif subdir should be used.
  715. If FALSE, specifies that the architecture
  716. specific subdir should be filtered out.
  717. If IncludeSubDirectory is FALSE, this parameter
  718. is ignored
  719. PathBuffer - Specifies a buffer to receive the path
  720. PathBufferSize - Specifies the size of the buffer to receive the
  721. path, in characters
  722. Return Value:
  723. TRUE for success, FALSE on error
  724. --*/
  725. {
  726. WCHAR InternalBuffer[MAX_PATH];
  727. WCHAR InternalSubDirBuffer[MAX_PATH];
  728. PWSTR p;
  729. ASSERT( SourceRootPath != NULL );
  730. ASSERT( FileName != NULL );
  731. ASSERT( PathBuffer != NULL );
  732. wcscpy( InternalBuffer, SourceRootPath );
  733. if (IncludeSubDirectory) {
  734. if (SubDirectoryPath) {
  735. wcscpy( InternalSubDirBuffer, SubDirectoryPath );
  736. if (IncludeArchitectureSpecificSubDirectory) {
  737. p = InternalSubDirBuffer;
  738. } else {
  739. p = wcsstr( InternalSubDirBuffer, PLATFORM_NAME );
  740. if (p) {
  741. p += wcslen(PLATFORM_NAME) + 1;
  742. if (p > InternalSubDirBuffer + wcslen(InternalSubDirBuffer)) {
  743. p = NULL;
  744. }
  745. }
  746. }
  747. } else {
  748. p = NULL;
  749. }
  750. if (p) {
  751. pSetupConcatenatePaths( InternalBuffer, p, UnicodeChars(InternalBuffer), NULL );
  752. }
  753. }
  754. pSetupConcatenatePaths( InternalBuffer, FileName, UnicodeChars(InternalBuffer), NULL );
  755. if (wcslen(InternalBuffer) + 1 <= PathBufferSize) {
  756. wcscpy( PathBuffer, InternalBuffer );
  757. return(TRUE);
  758. }
  759. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  760. return(FALSE);
  761. }
  762. PWSTR
  763. SfcGetSourcePath(
  764. IN BOOL bServicePackSourcePath,
  765. IN OUT PWSTR Path
  766. )
  767. /*++
  768. Routine Description:
  769. Retreives the os source path or servicepack source path, taking into
  770. account group policy.
  771. Arguments:
  772. bServicePackSourcePath - if TRUE, indicates that the servicepack source
  773. path should be retreived.
  774. Path - Specifies the buffer to receive the path.
  775. Assume the buffer is at least
  776. MAX_PATH*sizeof(WCHAR) large.
  777. path where the file is located
  778. Return Value:
  779. if successful, returns back a pointer to Path, else NULL
  780. --*/
  781. {
  782. PWSTR p;
  783. MYASSERT(Path != NULL);
  784. // If running under setup then we need to use the path
  785. // to $WINNT$.~LS or whatever passed in by GUI-setup.
  786. if (SFCDisable == SFC_DISABLE_SETUP) {
  787. MYASSERT(ServicePackSourcePath != NULL && ServicePackSourcePath[0] != 0);
  788. MYASSERT(OsSourcePath != NULL && OsSourcePath[0] != 0);
  789. if(bServicePackSourcePath) {
  790. wcsncpy( Path, ServicePackSourcePath, MAX_PATH );
  791. } else {
  792. wcsncpy( Path, OsSourcePath, MAX_PATH );
  793. }
  794. return(Path);
  795. }
  796. p = SfcQueryRegStringWithAlternate(
  797. REGKEY_POLICY_SETUP,
  798. REGKEY_SETUP_FULL,
  799. bServicePackSourcePath
  800. ? REGVAL_SERVICEPACKSOURCEPATH
  801. : REGVAL_SOURCEPATH );
  802. if(p) {
  803. wcsncpy( Path, p, MAX_PATH );
  804. MemFree( p );
  805. }
  806. return((p != NULL)
  807. ? Path
  808. : NULL );
  809. }
  810. DWORD
  811. SfcRpcPriviledgeCheck(
  812. IN HANDLE RpcHandle
  813. )
  814. /*++
  815. Routine Description:
  816. Check if the user has sufficient privilege to perform the requested action.
  817. Currently only administrators have privilege to do this.
  818. Arguments:
  819. RpcHandle - Rpc binding handle used for impersonating the client.
  820. Return Value:
  821. Win32 error code indicating outcome -- RPC_S_OK (ERROR_SUCCESS) indicates
  822. success.
  823. --*/
  824. {
  825. RPC_STATUS ec;
  826. //
  827. // impersonate the calling client
  828. //
  829. ec = RpcImpersonateClient(RpcHandle);
  830. if (ec != RPC_S_OK) {
  831. DebugPrint1( LVL_MINIMAL, L"RpcImpersonateClient failed, ec = %d",ec );
  832. goto exit;
  833. }
  834. //
  835. // make sure the user has sufficient privilege
  836. //
  837. if (!pSetupIsUserAdmin()) {
  838. RpcRevertToSelf();
  839. ec = ERROR_ACCESS_DENIED;
  840. goto exit;
  841. }
  842. //
  843. // revert back to original context. if this fails, we must return failure.
  844. //
  845. ec = RpcRevertToSelf();
  846. if (ec != RPC_S_OK) {
  847. DebugPrint1( LVL_MINIMAL, L"RpcRevertToSelf failed, ec = 0x%08x", ec );
  848. goto exit;
  849. }
  850. exit:
  851. return((DWORD)ec);
  852. }
  853. PSFC_GET_FILES
  854. SfcLoadSfcFiles(
  855. BOOL bLoad
  856. )
  857. /*++
  858. Routine Description:
  859. Loads or unloads sfcfiles.dll and gets the address of SfcGetFiles function
  860. Arguments:
  861. bLoad: TRUE to load sfcfiles.dll, false otherwise.
  862. Return Value:
  863. If bLoad is TRUE and the function is successful, it returns the address of SfcGetFiles function, otherwise NULL
  864. --*/
  865. {
  866. static HMODULE h = NULL;
  867. PSFC_GET_FILES pfGetFiles = NULL;
  868. if(bLoad)
  869. {
  870. if(NULL == h)
  871. {
  872. h = SfcLoadLibrary(L"sfcfiles.dll");
  873. }
  874. if(h != NULL)
  875. {
  876. pfGetFiles = (PSFC_GET_FILES) GetProcAddress(h, "SfcGetFiles");
  877. }
  878. }
  879. if(NULL == pfGetFiles && h != NULL)
  880. {
  881. LdrUnloadDll(h);
  882. h = NULL;
  883. }
  884. return pfGetFiles;
  885. }
  886. #if DBG
  887. DWORD GetProcessOwner(PTOKEN_OWNER* ppto)
  888. {
  889. HANDLE hToken = NULL;
  890. DWORD dwSize = 100;
  891. DWORD dwError;
  892. ASSERT(ppto != NULL);
  893. *ppto = (PTOKEN_OWNER) MemAlloc(dwSize);
  894. if(NULL == *ppto)
  895. {
  896. dwError = ERROR_NOT_ENOUGH_MEMORY;
  897. goto lExit;
  898. }
  899. if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
  900. {
  901. dwError = GetLastError();
  902. goto lExit;
  903. }
  904. if(!GetTokenInformation(hToken, TokenOwner, *ppto, dwSize, &dwSize))
  905. {
  906. PTOKEN_OWNER p;
  907. dwError = GetLastError();
  908. if(dwError != ERROR_INSUFFICIENT_BUFFER)
  909. goto lExit;
  910. p = (PTOKEN_OWNER) MemReAlloc(dwSize, *ppto);
  911. if(NULL == p)
  912. {
  913. dwError = ERROR_NOT_ENOUGH_MEMORY;
  914. goto lExit;
  915. }
  916. *ppto = p;
  917. if(!GetTokenInformation(hToken, TokenOwner, *ppto, dwSize, &dwSize))
  918. {
  919. dwError = GetLastError();
  920. goto lExit;
  921. }
  922. }
  923. dwError = ERROR_SUCCESS;
  924. lExit:
  925. if(dwError != ERROR_SUCCESS && *ppto != NULL)
  926. {
  927. MemFree(*ppto);
  928. *ppto = NULL;
  929. }
  930. if(hToken != NULL)
  931. CloseHandle(hToken);
  932. return dwError;
  933. }
  934. DWORD CreateSd(PSECURITY_DESCRIPTOR* ppsd)
  935. {
  936. enum
  937. {
  938. AuthenticatedUsers,
  939. MaxSids
  940. };
  941. PTOKEN_OWNER pto = NULL;
  942. PSID psids[MaxSids] = { NULL };
  943. const DWORD cdwAllowedAceLength = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD);
  944. SID_IDENTIFIER_AUTHORITY sidNtAuthority = SECURITY_NT_AUTHORITY;
  945. PACL pacl;
  946. DWORD dwAclSize;
  947. DWORD dwError;
  948. DWORD i;
  949. ASSERT(ppsd != NULL);
  950. *ppsd = NULL;
  951. dwError = GetProcessOwner(&pto);
  952. if(dwError != ERROR_SUCCESS)
  953. goto lExit;
  954. if(!AllocateAndInitializeSid(
  955. &sidNtAuthority,
  956. 1,
  957. SECURITY_AUTHENTICATED_USER_RID,
  958. 0,
  959. 0,
  960. 0,
  961. 0,
  962. 0,
  963. 0,
  964. 0,
  965. psids + AuthenticatedUsers
  966. ))
  967. {
  968. dwError = GetLastError();
  969. goto lExit;
  970. }
  971. //
  972. // compute the size of ACL
  973. //
  974. dwAclSize = sizeof(ACL) + cdwAllowedAceLength + GetLengthSid(pto->Owner);
  975. for(i = 0; i < MaxSids; i++)
  976. dwAclSize += cdwAllowedAceLength + GetLengthSid(psids[i]);
  977. *ppsd = (PSECURITY_DESCRIPTOR) MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH + dwAclSize);
  978. if(NULL == *ppsd)
  979. {
  980. dwError = ERROR_NOT_ENOUGH_MEMORY;
  981. goto lExit;
  982. }
  983. pacl = (PACL) ((LPBYTE) (*ppsd) + SECURITY_DESCRIPTOR_MIN_LENGTH);
  984. if(
  985. !InitializeAcl(pacl, dwAclSize, ACL_REVISION) ||
  986. !AddAccessAllowedAce(pacl, ACL_REVISION, EVENT_ALL_ACCESS, pto->Owner) ||
  987. !AddAccessAllowedAce(pacl, ACL_REVISION, EVENT_MODIFY_STATE, psids[AuthenticatedUsers]) ||
  988. !InitializeSecurityDescriptor(*ppsd, SECURITY_DESCRIPTOR_REVISION) ||
  989. !SetSecurityDescriptorDacl(*ppsd, TRUE, pacl, FALSE)
  990. )
  991. {
  992. dwError = GetLastError();
  993. goto lExit;
  994. }
  995. dwError = ERROR_SUCCESS;
  996. lExit:
  997. if(dwError != ERROR_SUCCESS && *ppsd != NULL)
  998. {
  999. MemFree(*ppsd);
  1000. *ppsd = NULL;
  1001. }
  1002. if(pto != NULL)
  1003. MemFree(pto);
  1004. for(i = 0; i < MaxSids; i++)
  1005. {
  1006. if(psids[i] != NULL)
  1007. FreeSid(psids[i]);
  1008. }
  1009. return dwError;
  1010. }
  1011. #endif
  1012. LRESULT CALLBACK DlgParentWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1013. /*++
  1014. Routine Description:
  1015. This is the window proc of the parent window for network authentication dialog
  1016. Arguments:
  1017. See Platform SDK docs
  1018. Return Value:
  1019. See Platform SDK docs
  1020. --*/
  1021. {
  1022. static PSFC_WINDOW_DATA pWndData = NULL;
  1023. switch(uMsg)
  1024. {
  1025. case WM_CREATE:
  1026. pWndData = pSfcCreateWindowDataEntry(hwnd);
  1027. if(NULL == pWndData)
  1028. {
  1029. return -1;
  1030. }
  1031. break;
  1032. case WM_WFPENDDIALOG:
  1033. //
  1034. // don't try to delete pWndData from the list when this message is sent because we'll deadlock;
  1035. // the entry will be removed by the thread that sent it
  1036. //
  1037. pWndData = NULL;
  1038. DestroyWindow(hwnd);
  1039. break;
  1040. case WM_DESTROY:
  1041. if(pWndData != NULL)
  1042. {
  1043. //
  1044. // delete pWndData from the list since this is not a consequence of receiving WM_WFPENDDIALOG
  1045. //
  1046. pSfcRemoveWindowDataEntry(pWndData);
  1047. }
  1048. break;
  1049. default:
  1050. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1051. }
  1052. return 0;
  1053. }
  1054. DWORD
  1055. CreateDialogParent(
  1056. OUT HWND* phwnd
  1057. )
  1058. /*++
  1059. Routine Description:
  1060. Creates the parent window for network authentication dialog
  1061. Arguments:
  1062. phwnd: receives the handle of the newly-created window
  1063. Return Value:
  1064. Win32 error code
  1065. --*/
  1066. {
  1067. DWORD dwError = ERROR_SUCCESS;
  1068. WNDCLASSW wc;
  1069. ASSERT(phwnd != NULL);
  1070. RtlZeroMemory(&wc, sizeof(wc));
  1071. wc.lpszClassName = PARENT_WND_CLASS;
  1072. wc.hInstance = SfcInstanceHandle;
  1073. wc.lpfnWndProc = DlgParentWndProc;
  1074. //
  1075. // if the class is already registered, there will be no problems;
  1076. // if there's an error registering the class for the first time, it will show up in CreateWindow
  1077. //
  1078. RegisterClassW(&wc);
  1079. *phwnd = CreateWindowW(
  1080. wc.lpszClassName,
  1081. L"",
  1082. WS_OVERLAPPED,
  1083. 0,
  1084. 0,
  1085. 0,
  1086. 0,
  1087. NULL,
  1088. NULL,
  1089. wc.hInstance,
  1090. NULL
  1091. );
  1092. if(NULL == *phwnd)
  1093. {
  1094. dwError = GetLastError();
  1095. DebugPrint1(LVL_VERBOSE, L"CreateDialogParent failed with the code %x", dwError);
  1096. }
  1097. return dwError;
  1098. }
  1099. NTSTATUS
  1100. SfcAllocUnicodeStringFromPath(
  1101. IN PCWSTR szPath,
  1102. OUT PUNICODE_STRING pString
  1103. )
  1104. /*++
  1105. Routine Description:
  1106. Expands the environment variables in the input path. Allocates the output buffer.
  1107. Arguments:
  1108. szPath - a path that can contain environment variables
  1109. pString - receives the expanded path
  1110. Return value:
  1111. The error code.
  1112. --*/
  1113. {
  1114. USHORT usLength;
  1115. pString->Length = pString->MaximumLength = 0;
  1116. pString->Buffer = NULL;
  1117. usLength = (USHORT) ExpandEnvironmentStringsW(szPath, NULL, 0);
  1118. if(0 == usLength)
  1119. {
  1120. return STATUS_INVALID_PARAMETER;
  1121. }
  1122. pString->Buffer = (PWSTR) MemAlloc(usLength * sizeof(WCHAR));
  1123. if(NULL == pString->Buffer)
  1124. {
  1125. return STATUS_NO_MEMORY;
  1126. }
  1127. ExpandEnvironmentStringsW(szPath, pString->Buffer, usLength);
  1128. pString->MaximumLength = usLength * sizeof(WCHAR);
  1129. pString->Length = pString->MaximumLength - sizeof(WCHAR);
  1130. return STATUS_SUCCESS;
  1131. }
  1132. #ifndef _WIN64
  1133. VOID
  1134. SfcCleanupPathTranslator(
  1135. IN BOOL FinalCleanup
  1136. )
  1137. /*++
  1138. Routine Description:
  1139. Frees the memory used in the translation table and optionally the critical section used to access it.
  1140. Arguments:
  1141. FinalCleanup - if TRUE, the critical section is also deleted
  1142. Return value:
  1143. none
  1144. --*/
  1145. {
  1146. if(SfcNeedTranslation)
  1147. {
  1148. if(SfcTranslations != NULL)
  1149. {
  1150. ULONG i;
  1151. for(i = 0; i < ARRAY_LENGTH(SfcExpandTranslations); ++i)
  1152. {
  1153. MemFree(SfcTranslations[i].Src.Buffer);
  1154. MemFree(SfcTranslations[i].Dest.Buffer);
  1155. }
  1156. MemFree(SfcTranslations);
  1157. SfcTranslations = NULL;
  1158. }
  1159. if(FinalCleanup)
  1160. {
  1161. RtlDeleteCriticalSection(&SfcTranslatorCs);
  1162. }
  1163. }
  1164. }
  1165. VOID
  1166. SfcInitPathTranslator(
  1167. VOID
  1168. )
  1169. /*++
  1170. Routine Description:
  1171. Initializes the path translator. Does not expand the table paths.
  1172. Arguments:
  1173. none
  1174. Return value:
  1175. none
  1176. --*/
  1177. {
  1178. #ifdef SFC_REDIRECTOR_TEST
  1179. SfcNeedTranslation = TRUE;
  1180. #else
  1181. SfcNeedTranslation = (GetSystemWow64DirectoryW(NULL, 0) != 0);
  1182. #endif
  1183. if(SfcNeedTranslation) {
  1184. RtlInitializeCriticalSection(&SfcTranslatorCs);
  1185. }
  1186. }
  1187. NTSTATUS
  1188. SfcExpandPathTranslator(
  1189. VOID
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. Expands the translation table paths.
  1194. Arguments:
  1195. none
  1196. Return value:
  1197. The error code.
  1198. --*/
  1199. {
  1200. NTSTATUS Status = STATUS_SUCCESS;
  1201. ASSERT(SfcNeedTranslation);
  1202. RtlEnterCriticalSection(&SfcTranslatorCs);
  1203. if(!SfcIsTranslatorInitialized)
  1204. {
  1205. ULONG ulCount = ARRAY_LENGTH(SfcExpandTranslations);
  1206. ULONG i;
  1207. ASSERT(NULL == SfcTranslations);
  1208. SfcTranslations = (SFC_TRANSLATION_ENTRY*) MemAlloc(ulCount * sizeof(SFC_TRANSLATION_ENTRY));
  1209. if(NULL == SfcTranslations)
  1210. {
  1211. Status = STATUS_NO_MEMORY;
  1212. goto cleanup;
  1213. }
  1214. for(i = 0; i < ulCount; ++i)
  1215. {
  1216. Status = SfcAllocUnicodeStringFromPath(SfcExpandTranslations[i].Src, &SfcTranslations[i].Src);
  1217. if(!NT_SUCCESS(Status))
  1218. {
  1219. goto cleanup;
  1220. }
  1221. Status = SfcAllocUnicodeStringFromPath(SfcExpandTranslations[i].Dest, &SfcTranslations[i].Dest);
  1222. if(!NT_SUCCESS(Status))
  1223. {
  1224. goto cleanup;
  1225. }
  1226. }
  1227. //
  1228. // set this to TRUE only on success; in case of failure, the init will be tried later
  1229. //
  1230. SfcIsTranslatorInitialized = TRUE;
  1231. cleanup:
  1232. if(!NT_SUCCESS(Status))
  1233. {
  1234. SfcCleanupPathTranslator(FALSE);
  1235. DebugPrint(LVL_MINIMAL, L"Could not initialize the path translator");
  1236. }
  1237. }
  1238. RtlLeaveCriticalSection(&SfcTranslatorCs);
  1239. return Status;
  1240. }
  1241. NTSTATUS
  1242. SfcRedirectPath(
  1243. IN PCWSTR szPath,
  1244. OUT PUNICODE_STRING pPath
  1245. )
  1246. /*++
  1247. Routine Description:
  1248. Expands environment variables and translates a path from win32 to wow64. Allocates the output buffer.
  1249. Arguments:
  1250. szPath - the path to expand/translate
  1251. pPath - receives the processed (and possibly changed) path
  1252. Return value:
  1253. The error code.
  1254. --*/
  1255. {
  1256. NTSTATUS Status = STATUS_SUCCESS;
  1257. UNICODE_STRING Path = { 0 };
  1258. ULONG i;
  1259. ASSERT(szPath != NULL);
  1260. ASSERT(pPath != NULL);
  1261. RtlZeroMemory(pPath, sizeof(*pPath));
  1262. //
  1263. // first of all, expand environment strings
  1264. //
  1265. Status = SfcAllocUnicodeStringFromPath(szPath, &Path);
  1266. if(!NT_SUCCESS(Status))
  1267. {
  1268. goto exit;
  1269. }
  1270. if(!SfcNeedTranslation)
  1271. {
  1272. goto no_translation;
  1273. }
  1274. Status = SfcExpandPathTranslator();
  1275. if(!NT_SUCCESS(Status))
  1276. {
  1277. goto exit;
  1278. }
  1279. for(i = 0; i < ARRAY_LENGTH(SfcExpandTranslations); ++i)
  1280. {
  1281. PUNICODE_STRING pSrc = &SfcTranslations[i].Src;
  1282. PUNICODE_STRING pDest = &SfcTranslations[i].Dest;
  1283. if(Path.Length >= pSrc->Length && 0 == _wcsnicmp(Path.Buffer, pSrc->Buffer, pSrc->Length / sizeof(WCHAR)))
  1284. {
  1285. const UNICODE_STRING* pExcep = SfcExpandTranslations[i].Exceptions;
  1286. //
  1287. // test if this is an excluded path
  1288. //
  1289. for(i = SfcExpandTranslations[i].ExceptionCount; i--; ++pExcep)
  1290. {
  1291. if(Path.Length >= pSrc->Length + pExcep->Length &&
  1292. 0 == _wcsnicmp((PWCHAR) ((PCHAR) Path.Buffer + pSrc->Length), pExcep->Buffer, pExcep->Length / sizeof(WCHAR)))
  1293. {
  1294. goto no_translation;
  1295. }
  1296. }
  1297. DebugPrint1(LVL_VERBOSE, L"Redirecting \"%s\"", Path.Buffer);
  1298. //
  1299. // compute the new length, including the terminator
  1300. //
  1301. pPath->MaximumLength = Path.Length - pSrc->Length + pDest->Length + sizeof(WCHAR);
  1302. pPath->Buffer = (PWSTR) MemAlloc(pPath->MaximumLength);
  1303. if(NULL == pPath->Buffer)
  1304. {
  1305. Status = STATUS_NO_MEMORY;
  1306. goto exit;
  1307. }
  1308. RtlCopyMemory(pPath->Buffer, pDest->Buffer, pDest->Length);
  1309. //
  1310. // copy the reminder of the path (including terminator)
  1311. //
  1312. RtlCopyMemory((PCHAR) pPath->Buffer + pDest->Length, (PCHAR) Path.Buffer + pSrc->Length, Path.Length - pSrc->Length + sizeof(WCHAR));
  1313. pPath->Length = pPath->MaximumLength - sizeof(WCHAR);
  1314. DebugPrint1(LVL_VERBOSE, L"Path redirected to \"%s\"", pPath->Buffer);
  1315. goto exit;
  1316. }
  1317. }
  1318. no_translation:
  1319. DebugPrint1(LVL_VERBOSE, L"No translation required for \"%s\"", Path.Buffer);
  1320. //
  1321. // output the expanded string
  1322. //
  1323. *pPath = Path;
  1324. Path.Buffer = NULL;
  1325. exit:
  1326. MemFree(Path.Buffer);
  1327. if(!NT_SUCCESS(Status))
  1328. {
  1329. MemFree(pPath->Buffer);
  1330. RtlZeroMemory(pPath, sizeof(*pPath));
  1331. }
  1332. return Status;
  1333. }
  1334. #endif // _WIN64