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.

817 lines
17 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. sfc.c
  5. Abstract:
  6. code file for system file checker utilty program
  7. Revision History:
  8. Andrew Ritz (andrewr) 2-Jul-1999 : Added comments
  9. --*/
  10. #include <nt.h>
  11. #include <ntrtl.h>
  12. #include <nturtl.h>
  13. #include <windows.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <tchar.h>
  17. #include <sfcapip.h>
  18. #include <sfcapi.h>
  19. #include <locale.h>
  20. #include "msg.h"
  21. BOOL
  22. IsUserAdmin(
  23. VOID
  24. );
  25. int __cdecl
  26. My_wprintf(
  27. const wchar_t *format,
  28. ...
  29. );
  30. int __cdecl
  31. My_fwprintf(
  32. FILE *str,
  33. const wchar_t *format,
  34. ...
  35. );
  36. int __cdecl
  37. My_vfwprintf(
  38. FILE *str,
  39. const wchar_t *format,
  40. va_list argptr
  41. );
  42. #define DLLCACHE_DIR_DEFAULT L"%SystemRoot%\\system32\\DllCache"
  43. #define SFC_REGISTRY_KEY L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
  44. #define SFC_DLLCACHE_VALUE L"SFCDllCacheDir"
  45. #define SFC_SCAN_VALUE L"SFCScan"
  46. #define SFC_DISABLE_VALUE L"SFCDisable"
  47. #define SFC_QUOTA_VALUE L"SFCQuota"
  48. typedef enum _SFCACTION {
  49. ScanOnce,
  50. ScanNow,
  51. ScanBoot,
  52. RevertScan,
  53. PurgeCacheNow,
  54. SetCache
  55. } SFCACTION;
  56. DWORD
  57. SfcQueryRegDword(
  58. LPWSTR KeyName,
  59. LPWSTR ValueName
  60. )
  61. /*++
  62. Routine Description:
  63. Registry wrapper function. Retreives the DWORD value at the specified key.
  64. Only handles values under HKLM.
  65. Arguments:
  66. KeyName - Keyname that the specified value lives under.
  67. ValueName - ValueName we want to query.
  68. Return Value:
  69. The DWORD value at the specifed location or 0 on failure.
  70. If the call fails, GetLastError() returns something other than
  71. ERROR_SUCESS.
  72. --*/
  73. {
  74. HKEY hKey;
  75. DWORD val;
  76. DWORD sz = sizeof(DWORD);
  77. long rslt;
  78. rslt = RegOpenKey( HKEY_LOCAL_MACHINE, KeyName, &hKey );
  79. if (rslt != ERROR_SUCCESS) {
  80. val = 0;
  81. goto e0;
  82. }
  83. rslt = RegQueryValueEx( hKey, ValueName, NULL, NULL, (LPBYTE)&val, &sz );
  84. if (rslt != ERROR_SUCCESS) {
  85. val = 0;
  86. }
  87. RegCloseKey( hKey );
  88. e0:
  89. SetLastError( rslt );
  90. return(val);
  91. }
  92. PWSTR
  93. SfcQueryRegString(
  94. LPWSTR KeyName,
  95. LPWSTR ValueName
  96. )
  97. /*++
  98. Routine Description:
  99. Registry wrapper function. Retreives the string value at the specified key.
  100. Only handles values under HKLM.
  101. Arguments:
  102. KeyName - Keyname that the specified value lives under.
  103. ValueName - ValueName we want to query.
  104. Return Value:
  105. The string value at the specifed location or NULL on failure. If the
  106. function fails, call GetLastError() to get the extended error code.
  107. --*/
  108. {
  109. HKEY hKey;
  110. DWORD size = 0;
  111. PWSTR val;
  112. long rslt;
  113. rslt = RegOpenKey( HKEY_LOCAL_MACHINE, KeyName, &hKey );
  114. if (rslt != ERROR_SUCCESS) {
  115. val = NULL;
  116. goto e0;
  117. }
  118. rslt = RegQueryValueEx( hKey, ValueName, NULL, NULL, NULL, &size );
  119. if (rslt != ERROR_SUCCESS) {
  120. val = NULL;
  121. goto e1;
  122. }
  123. val = malloc( size+ sizeof(WCHAR) );
  124. if (val == NULL) {
  125. rslt = ERROR_NOT_ENOUGH_MEMORY;
  126. goto e1;
  127. }
  128. rslt = RegQueryValueEx( hKey, ValueName, NULL, NULL, (LPBYTE)val, &size );
  129. if (rslt != ERROR_SUCCESS) {
  130. free( val );
  131. val = NULL;
  132. }
  133. e1:
  134. RegCloseKey( hKey );
  135. e0:
  136. SetLastError( rslt );
  137. return val;
  138. }
  139. DWORD
  140. SfcWriteRegDword(
  141. LPWSTR KeyName,
  142. LPWSTR ValueName,
  143. ULONG Value
  144. )
  145. /*++
  146. Routine Description:
  147. Registry wrapper function. Writes the DWORD value at the specified key.
  148. Only handles values under HKLM.
  149. Arguments:
  150. KeyName - Keyname that the specified value lives under.
  151. ValueName - ValueName we want to query.
  152. Value - value to be set
  153. Return Value:
  154. WIN32 error code indicating outcome (ERROR_SUCCESS on success).
  155. --*/
  156. {
  157. HKEY hKey;
  158. DWORD retval;
  159. long rslt;
  160. rslt = RegOpenKey( HKEY_LOCAL_MACHINE, KeyName, &hKey );
  161. if (rslt != ERROR_SUCCESS) {
  162. retval = rslt;
  163. goto e0;
  164. }
  165. rslt = RegSetValueEx(
  166. hKey,
  167. ValueName,
  168. 0,
  169. REG_DWORD,
  170. (LPBYTE)&Value,
  171. sizeof(DWORD) );
  172. retval = rslt;
  173. RegCloseKey( hKey );
  174. e0:
  175. SetLastError( rslt );
  176. return( retval );
  177. }
  178. DWORD
  179. SfcWriteRegString(
  180. LPWSTR KeyName,
  181. LPWSTR ValueName,
  182. PWSTR Value
  183. )
  184. /*++
  185. Routine Description:
  186. Registry wrapper function. Writes the string value at the specified key.
  187. Only handles values under HKLM.
  188. Arguments:
  189. KeyName - Keyname that the specified value lives under.
  190. ValueName - ValueName we want to query.
  191. Value - value to be set
  192. Return Value:
  193. WIN32 error code indicating outcome (ERROR_SUCCESS on success).
  194. --*/
  195. {
  196. HKEY hKey;
  197. DWORD retval;
  198. long rslt;
  199. rslt = RegOpenKey( HKEY_LOCAL_MACHINE, KeyName, &hKey );
  200. if (rslt != ERROR_SUCCESS) {
  201. retval = rslt;
  202. goto e0;
  203. }
  204. rslt = RegSetValueEx(
  205. hKey,
  206. ValueName,
  207. 0,
  208. REG_SZ,
  209. (LPBYTE)Value,
  210. (wcslen(Value)+1)*sizeof(WCHAR) );
  211. retval = rslt;
  212. RegCloseKey( hKey );
  213. e0:
  214. SetLastError( rslt );
  215. return( retval );
  216. }
  217. /***
  218. * My_wprintf(format) - print formatted data
  219. *
  220. * Prints Unicode formatted string to console window using WriteConsoleW.
  221. * Note: This My_wprintf() is used to workaround the problem in c-runtime
  222. * which looks up LC_CTYPE even for Unicode string.
  223. *
  224. */
  225. int __cdecl
  226. My_wprintf(
  227. const wchar_t *format,
  228. ...
  229. )
  230. {
  231. DWORD cchWChar;
  232. va_list args;
  233. va_start( args, format );
  234. cchWChar = My_vfwprintf(stdout, format, args);
  235. va_end(args);
  236. return cchWChar;
  237. }
  238. /***
  239. * My_fwprintf(stream, format) - print formatted data
  240. *
  241. * Prints Unicode formatted string to console window using WriteConsoleW.
  242. * Note: This My_fwprintf() is used to workaround the problem in c-runtime
  243. * which looks up LC_CTYPE even for Unicode string.
  244. *
  245. */
  246. int __cdecl
  247. My_fwprintf(
  248. FILE *str,
  249. const wchar_t *format,
  250. ...
  251. )
  252. {
  253. DWORD cchWChar;
  254. va_list args;
  255. va_start( args, format );
  256. cchWChar = My_vfwprintf(str, format, args);
  257. va_end(args);
  258. return cchWChar;
  259. }
  260. int __cdecl
  261. My_vfwprintf(
  262. FILE *str,
  263. const wchar_t *format,
  264. va_list argptr
  265. )
  266. {
  267. HANDLE hOut;
  268. if (str == stderr) {
  269. hOut = GetStdHandle(STD_ERROR_HANDLE);
  270. }
  271. else {
  272. hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  273. }
  274. if ((GetFileType(hOut) & ~FILE_TYPE_REMOTE) == FILE_TYPE_CHAR) {
  275. DWORD cchWChar;
  276. WCHAR szBufferMessage[1024];
  277. vswprintf( szBufferMessage, format, argptr );
  278. cchWChar = wcslen(szBufferMessage);
  279. WriteConsoleW(hOut, szBufferMessage, cchWChar, &cchWChar, NULL);
  280. return cchWChar;
  281. }
  282. return vfwprintf(str, format, argptr);
  283. }
  284. void
  285. PrintMessage(
  286. DWORD MsgId,
  287. DWORD LastError,
  288. PCWSTR FileName,
  289. BOOL bStdOut
  290. )
  291. /*++
  292. Routine Description:
  293. Output the specified message id to specified output.
  294. Arguments:
  295. MsgId - resource message id of message to be output
  296. LastError - error code
  297. FileName - filename to be logged, if specified
  298. bStdOut - TRUE indicates the message goes to stdout, else stderr
  299. Return Value:
  300. None.
  301. --*/
  302. {
  303. WCHAR buf[2048];
  304. WCHAR LastErrorText[200];
  305. PVOID ErrText;
  306. PVOID Array[3];
  307. FormatMessage(
  308. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  309. FORMAT_MESSAGE_FROM_SYSTEM |
  310. FORMAT_MESSAGE_IGNORE_INSERTS,
  311. NULL,
  312. LastError,
  313. 0,
  314. (PWSTR) &ErrText,
  315. 0,
  316. NULL );
  317. if (ErrText) {
  318. wsprintf(LastErrorText,L"0x%08x [%ws]",LastError,ErrText);
  319. LocalFree( ErrText );
  320. } else {
  321. wsprintf(LastErrorText,L"0x%08x",LastError);
  322. }
  323. Array[0] = (PVOID)LastErrorText;
  324. Array[1] = (PVOID)FileName;
  325. Array[2] = NULL;
  326. FormatMessage(
  327. FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
  328. NULL,
  329. MsgId,
  330. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  331. buf,
  332. sizeof(buf) / sizeof(WCHAR),
  333. (va_list *)Array );
  334. My_fwprintf(
  335. bStdOut
  336. ? stdout
  337. : stderr,
  338. L"%ws",
  339. buf );
  340. }
  341. void
  342. Usage(
  343. void
  344. )
  345. /*++
  346. Routine Description:
  347. Display's usage for program to stdout.
  348. Arguments:
  349. None.
  350. Return Value:
  351. None.
  352. --*/
  353. {
  354. PrintMessage( MSG_USAGE, 0, NULL, FALSE );
  355. }
  356. BOOL
  357. DoAction(
  358. SFCACTION SfcAction,
  359. DWORD CacheSize
  360. )
  361. /*++
  362. Routine Description:
  363. take the specified action based on input parameter
  364. Arguments:
  365. SfcAction - enumerated type illustrating action
  366. CacheSize - only used for SetCache action, specifies cache size
  367. Return Value:
  368. TRUE on success, FALSE on failure.
  369. --*/
  370. {
  371. HANDLE RpcHandle;
  372. DWORD errcode;
  373. BOOL retval;
  374. switch( SfcAction ) {
  375. case ScanOnce:
  376. //
  377. // connect to SFC RPC server and telling it to scan once.
  378. //
  379. RpcHandle = SfcConnectToServer( NULL );
  380. if (RpcHandle) {
  381. errcode = SfcInitiateScan( RpcHandle, SFC_SCAN_ONCE );
  382. retval = (errcode == ERROR_SUCCESS);
  383. SfcClose( RpcHandle );
  384. } else {
  385. retval = FALSE;
  386. errcode = GetLastError();
  387. }
  388. if (errcode != ERROR_SUCCESS) {
  389. PrintMessage( MSG_SET_FAIL, errcode, NULL, FALSE );
  390. retval = FALSE;
  391. } else {
  392. //
  393. // requires a reboot
  394. //
  395. PrintMessage( MSG_REBOOT, 0, NULL, TRUE );
  396. retval = TRUE;
  397. }
  398. break;
  399. case ScanBoot:
  400. //
  401. // connect to SFC RPC server and telling it to scan every boot.
  402. //
  403. RpcHandle = SfcConnectToServer( NULL );
  404. if (RpcHandle) {
  405. errcode = SfcInitiateScan( RpcHandle, SFC_SCAN_ALWAYS );
  406. retval = (errcode == ERROR_SUCCESS);
  407. SfcClose( RpcHandle );
  408. } else {
  409. retval = FALSE;
  410. errcode = GetLastError();
  411. }
  412. if (errcode != ERROR_SUCCESS) {
  413. PrintMessage( MSG_SET_FAIL, errcode, NULL, FALSE );
  414. retval = FALSE;
  415. } else {
  416. //
  417. // requires a reboot
  418. //
  419. PrintMessage( MSG_REBOOT, 0, NULL, TRUE );
  420. retval = TRUE;
  421. }
  422. break;
  423. case ScanNow:
  424. //
  425. // scan immediately by connecting to SFC RPC server
  426. // and telling it to scan now.
  427. //
  428. RpcHandle = SfcConnectToServer( NULL );
  429. if (RpcHandle) {
  430. //
  431. // scanwhen argument is ignored.
  432. //
  433. errcode = SfcInitiateScan( RpcHandle, SFC_SCAN_IMMEDIATE );
  434. retval = (errcode == ERROR_SUCCESS);
  435. SfcClose( RpcHandle );
  436. } else {
  437. retval = FALSE;
  438. errcode = GetLastError();
  439. }
  440. if (!retval) {
  441. PrintMessage(MSG_SCAN_FAIL, errcode, NULL, FALSE);
  442. }
  443. break;
  444. case RevertScan:
  445. //
  446. // connect to SFC RPC server and telling it to scan normally.
  447. //
  448. RpcHandle = SfcConnectToServer( NULL );
  449. if (RpcHandle) {
  450. errcode = SfcInitiateScan( RpcHandle, SFC_SCAN_NORMAL);
  451. retval = (errcode == ERROR_SUCCESS);
  452. SfcClose( RpcHandle );
  453. } else {
  454. retval = FALSE;
  455. errcode = GetLastError();
  456. }
  457. if (errcode != ERROR_SUCCESS) {
  458. PrintMessage( MSG_SET_FAIL, errcode, NULL, FALSE );
  459. retval = FALSE;
  460. } else {
  461. //
  462. // requires a reboot
  463. //
  464. PrintMessage( MSG_REBOOT, 0, NULL, TRUE );
  465. retval = TRUE;
  466. }
  467. break;
  468. case SetCache:
  469. //
  470. // connect to SFC RPC server and tell it to set the cache size.
  471. //
  472. RpcHandle = SfcConnectToServer( NULL );
  473. if (RpcHandle) {
  474. errcode = SfcCli_SetCacheSize( RpcHandle, CacheSize );
  475. retval = (errcode == ERROR_SUCCESS);
  476. SfcClose( RpcHandle );
  477. } else {
  478. retval = FALSE;
  479. errcode = GetLastError();
  480. }
  481. if (errcode != ERROR_SUCCESS) {
  482. PrintMessage( MSG_SET_FAIL, errcode, NULL, FALSE );
  483. retval = FALSE;
  484. } else {
  485. //
  486. // print success message
  487. //
  488. PrintMessage( MSG_SUCCESS, 0, NULL, TRUE );
  489. retval = TRUE;
  490. }
  491. break;
  492. case PurgeCacheNow:
  493. //
  494. // remove all files from the cache
  495. //
  496. //
  497. // connect to SFC RPC server and tell it to purge the cache
  498. //
  499. RpcHandle = SfcConnectToServer( NULL );
  500. if (RpcHandle) {
  501. errcode = SfcCli_PurgeCache( RpcHandle );
  502. retval = (errcode == ERROR_SUCCESS);
  503. SfcClose( RpcHandle );
  504. } else {
  505. retval = FALSE;
  506. errcode = GetLastError();
  507. }
  508. if (!retval) {
  509. PrintMessage(MSG_PURGE_FAIL, errcode, NULL, FALSE);
  510. } else {
  511. PrintMessage(MSG_SUCCESS, 0, NULL, FALSE);
  512. }
  513. break;
  514. default:
  515. //
  516. // should never get here!
  517. //
  518. ASSERT(FALSE);
  519. retval = FALSE;
  520. }
  521. return(retval);
  522. }
  523. int
  524. __cdecl wmain(
  525. int argc,
  526. WCHAR *argv[]
  527. )
  528. /*++
  529. Routine Description:
  530. program entrypoint
  531. Arguments:
  532. argc - number of arguments
  533. argv - pointer to argument array
  534. Return Value:
  535. 0 indicates success, 1 failure.
  536. --*/
  537. {
  538. int i;
  539. DWORD val;
  540. PWSTR s;
  541. SFCACTION SfcAction;
  542. setlocale(LC_ALL, ".OCP");
  543. //
  544. // only an administrator logged on to session 0 (console) is allowed to run this app
  545. //
  546. if (!IsUserAdmin() || !ProcessIdToSessionId(GetCurrentProcessId(), &val) || val != 0) {
  547. PrintMessage( MSG_ADMIN, 0, NULL, FALSE );
  548. return 1;
  549. }
  550. //
  551. // parse args
  552. //
  553. if (argc == 1) {
  554. Usage();
  555. return 1;
  556. }
  557. val = 0;
  558. for (i=1; i<argc; i++) {
  559. s = argv[i];
  560. //
  561. // support '-' and '/' as synonyms
  562. //
  563. if (*s != L'-' && *s != L'/') {
  564. Usage();
  565. return 1;
  566. }
  567. s += 1;
  568. if (_wcsicmp( s, L"SCANONCE" ) == 0) {
  569. SfcAction = ScanOnce;
  570. } else if (_wcsicmp( s, L"SCANBOOT" ) == 0) {
  571. SfcAction = ScanBoot;
  572. } else if (_wcsicmp( s, L"SCANNOW" ) == 0) {
  573. SfcAction = ScanNow;
  574. } else if (_wcsicmp( s, L"REVERT" ) == 0) {
  575. SfcAction = RevertScan;
  576. } else if (_wcsnicmp( s, L"CACHESIZE=", 10 ) == 0) {
  577. SfcAction = SetCache;
  578. val = wcstoul( s+10, NULL, 0 );
  579. } else if (_wcsicmp( s, L"PURGECACHE" ) == 0) {
  580. SfcAction = PurgeCacheNow;
  581. } else {
  582. Usage();
  583. return 1;
  584. }
  585. }
  586. //
  587. // do the specified action
  588. //
  589. if (DoAction(SfcAction,val)) {
  590. return 0;
  591. }
  592. return 1;
  593. }
  594. BOOL
  595. IsUserAdmin(
  596. VOID
  597. )
  598. /*++
  599. Routine Description:
  600. This routine returns TRUE if the caller's process is a
  601. member of the Administrators local group.
  602. Caller is NOT expected to be impersonating anyone and IS
  603. expected to be able to open their own process and process
  604. token.
  605. Arguments:
  606. None.
  607. Return Value:
  608. TRUE - Caller has Administrators local group.
  609. FALSE - Caller does not have Administrators local group.
  610. --*/
  611. {
  612. BOOL b;
  613. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  614. PSID AdministratorsGroup;
  615. b = AllocateAndInitializeSid(
  616. &NtAuthority,
  617. 2,
  618. SECURITY_BUILTIN_DOMAIN_RID,
  619. DOMAIN_ALIAS_RID_ADMINS,
  620. 0, 0, 0, 0, 0, 0,
  621. &AdministratorsGroup
  622. );
  623. if(b) {
  624. if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) {
  625. b = FALSE;
  626. }
  627. FreeSid(AdministratorsGroup);
  628. }
  629. return(b);
  630. }