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.

1767 lines
42 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. SsInit.c
  5. Abstract:
  6. This module contains initialization routines for the NT server
  7. service.
  8. Author:
  9. David Treadwell (davidtr) 6-Mar-1991
  10. Revision History:
  11. ChuckC 20-May-93 Load share remarks from messagefile so it
  12. can be internationalized.
  13. --*/
  14. #include "srvsvcp.h"
  15. #include "srvconfg.h"
  16. #include "ssreg.h"
  17. #include <netevent.h>
  18. #include <lmapibuf.h> // NetApiBufferFree().
  19. #include <lmconfig.h>
  20. #include <netlib.h>
  21. #include <apperr2.h>
  22. #include <debugfmt.h>
  23. #include <tstr.h>
  24. #define SERVICE_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
  25. #define SERVER_DRIVER_NAME L"Srv"
  26. #define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
  27. #define MAX(a,b) ( ((a) < (b)) ? (b) : (a) )
  28. #define MINIMIZE(_param,_max) _param = MIN( _param, _max );
  29. //
  30. // Internationalizable share remarks.
  31. //
  32. #define NETMSG_DLL TEXT("NETMSG.DLL")
  33. LPWSTR SsDefaultRemark = TEXT("") ; // if all else fails
  34. LPWSTR SsAdminShareRemark = NULL ;
  35. LPWSTR SsIPCShareRemark = NULL ;
  36. LPWSTR SsDiskAdminShareRemark = NULL ;
  37. //
  38. // Lock to protect the ShareDeleteCommit list
  39. //
  40. extern CRITICAL_SECTION ShareDelContextMutex;
  41. extern PSHARE_DEL_CONTEXT SrvShareDelContextHead;
  42. //
  43. // Forward declarations.
  44. //
  45. NET_API_STATUS
  46. CreateDefaultShares (
  47. VOID
  48. );
  49. VOID
  50. InitializeDefaultData(
  51. VOID
  52. );
  53. VOID
  54. InitializeStrings(
  55. VOID
  56. );
  57. VOID
  58. FreeStrings(
  59. VOID
  60. );
  61. NET_API_STATUS
  62. InitializeServer (
  63. VOID
  64. );
  65. NET_API_STATUS
  66. LoadServer (
  67. VOID
  68. );
  69. VOID
  70. SetServerName (
  71. VOID
  72. );
  73. DWORD
  74. DiscoverDrives (
  75. VOID
  76. );
  77. NET_API_STATUS
  78. TerminateServer (
  79. VOID
  80. );
  81. VOID
  82. UnloadServer (
  83. VOID
  84. );
  85. #define IsEmbedded() IsSuiteVersion(VER_SUITE_EMBEDDEDNT)
  86. BOOL IsSuiteVersion(USHORT SuiteMask);
  87. VOID
  88. SAMWaitAnnounce (
  89. LPVOID event
  90. )
  91. {
  92. ULONG i;
  93. //
  94. // Announce ourselves and then wait for awhile.
  95. // If the event gets signaled, terminate the loop and this thread.
  96. // But don't do this forever, since SAM may actually get stuck
  97. //
  98. //
  99. // Do it for 30 minutes
  100. //
  101. for( i=0; i < 120; i++ ) {
  102. AnnounceServiceStatus( 1 );
  103. if( WaitForSingleObject( (HANDLE)event, 15*1000 ) != WAIT_TIMEOUT ) {
  104. break;
  105. }
  106. }
  107. if( i == 120 ) {
  108. DbgPrint( "SRVSVC: SAM has hung on startup. \"Srv\" will be reported as failed to start.\n" );
  109. }
  110. }
  111. NET_API_STATUS
  112. SsInitialize (
  113. IN DWORD argc,
  114. IN LPWSTR argv[]
  115. )
  116. /*++
  117. Routine Description:
  118. This routine controls initialization of the server service and
  119. server driver. It sets up server data stored in the server
  120. service, parses the command line parameters in case any data needs
  121. to be changed, and then starts the file server.
  122. Arguments:
  123. argc - the count of command-line arguments.
  124. argv - an array of pointers to the command line arguments.
  125. Return Value:
  126. NET_API_STATUS - results of operation.
  127. --*/
  128. {
  129. NET_API_STATUS error;
  130. HANDLE threadHandle = NULL;
  131. HANDLE event = NULL;
  132. //
  133. // Initialize the resource that protects access to global server
  134. // information.
  135. //
  136. SS_ASSERT( !SsData.SsServerInfoResourceInitialized );
  137. try {
  138. RtlInitializeResource( &SsData.SsServerInfoResource );
  139. } except( EXCEPTION_EXECUTE_HANDLER ) {
  140. return RtlNtStatusToDosError( GetExceptionCode() );
  141. }
  142. // Initialize the lock that protects the ShareDelCommit list
  143. InitializeCriticalSection( &ShareDelContextMutex );
  144. SrvShareDelContextHead = NULL;
  145. //
  146. // We hold this resource while we are doing announcements, and when
  147. // we communicate with the FSD. These ought to be quick operations,
  148. // but it's really unpredictable under load. Indicate to the RTL
  149. // that we really don't know how long it'll take.
  150. //
  151. SsData.SsServerInfoResource.Flags |= RTL_RESOURCE_FLAG_LONG_TERM;
  152. SsData.SsServerInfoResourceInitialized = TRUE;
  153. //
  154. // Get the internationalizable special share remarks
  155. //
  156. InitializeStrings( );
  157. //
  158. // Initialize the server name list bits list.
  159. //
  160. SsData.SsServerNameList = NULL;
  161. IF_DEBUG(INITIALIZATION) {
  162. SS_PRINT(( "SsInitialize: resource initialized.\n" ));
  163. }
  164. //
  165. // Create the event used for termination synchronization.
  166. //
  167. SS_ASSERT( SsData.SsTerminationEvent == NULL );
  168. SsData.SsTerminationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  169. if ( SsData.SsTerminationEvent == NULL ) {
  170. error = GetLastError( );
  171. SS_PRINT(( "SsInitialize: CreateEvent failed: %ld\n", error ));
  172. return error;
  173. }
  174. //
  175. // Initialize the server data to its default values stored in
  176. // srvconfg.h.
  177. //
  178. InitializeDefaultData( );
  179. IF_DEBUG(INITIALIZATION) {
  180. SS_PRINT(( "SsInitialize: default data initialized.\n" ));
  181. }
  182. //
  183. // Sometimes the LSA misbehaves and doesn't process the computer name and
  184. // domain name fetching calls. So we need to defeat the service controller
  185. // hang detection mechanism. Sigh.
  186. //
  187. event = CreateEvent( NULL, TRUE, FALSE, NULL );
  188. if( event != NULL ) {
  189. DWORD threadId;
  190. threadHandle = CreateThread(
  191. NULL,
  192. 0,
  193. (LPTHREAD_START_ROUTINE)SAMWaitAnnounce,
  194. (LPVOID)event,
  195. 0,
  196. &threadId
  197. );
  198. if( threadHandle == NULL ) {
  199. CloseHandle( event );
  200. event = NULL;
  201. }
  202. }
  203. //
  204. // Get the computer name.
  205. //
  206. SetServerName( );
  207. //
  208. // Get the primary domain for this computer into the startup parameters.
  209. //
  210. SsSetDomainName( );
  211. if( event != NULL ) {
  212. //
  213. // We created an announcement thread, set the event telling it to terminate
  214. //
  215. SetEvent( event );
  216. //
  217. // Wait for the thread to terminate
  218. //
  219. (VOID)WaitForSingleObject( threadHandle, INFINITE );
  220. //
  221. // Close the handles
  222. //
  223. CloseHandle( event );
  224. CloseHandle( threadHandle );
  225. }
  226. //
  227. // See if we are the top of a DFS tree
  228. //
  229. SsSetDfsRoot();
  230. //
  231. // Verify that the various registry keys under the main server
  232. // service key exist.
  233. //
  234. error = SsCheckRegistry( );
  235. if ( error != NO_ERROR ) {
  236. IF_DEBUG(INITIALIZATION_ERRORS) {
  237. SS_PRINT(( "SsInitialize: SsCheckRegistry failed: %ld\n", error ));
  238. }
  239. return error;
  240. }
  241. IF_DEBUG(INITIALIZATION) {
  242. SS_PRINT(( "SsInitialize: registry keys verified.\n" ));
  243. }
  244. //
  245. // Load server configuration data from the registry.
  246. //
  247. error = SsLoadConfigurationParameters( );
  248. if ( error != NO_ERROR ) {
  249. IF_DEBUG(INITIALIZATION_ERRORS) {
  250. SS_PRINT(( "SsInitialize: SsLoadConfigurationParameters failed: "
  251. "%ld\n", error ));
  252. }
  253. return error;
  254. }
  255. IF_DEBUG(INITIALIZATION) {
  256. SS_PRINT(( "SsInitialize: configuration parameters loaded.\n" ));
  257. }
  258. //
  259. // Parse the command line. This will change server data values as
  260. // specified.
  261. //
  262. error = SsParseCommandLine( argc, argv, TRUE );
  263. if ( error != NO_ERROR ) {
  264. IF_DEBUG(INITIALIZATION_ERRORS) {
  265. SS_PRINT(( "SsInitialize: SsParseCommandLine failed: %ld\n",
  266. error ));
  267. }
  268. return error;
  269. }
  270. IF_DEBUG(INITIALIZATION) {
  271. SS_PRINT(( "SsInitialize: command line parsed.\n" ));
  272. }
  273. //
  274. // Set up the security objects that will be used to validate access
  275. // for APIs.
  276. //
  277. error = SsCreateSecurityObjects( );
  278. if ( error != NO_ERROR ) {
  279. return error;
  280. }
  281. IF_DEBUG(INITIALIZATION) {
  282. SS_PRINT(( "SsInitialize: security initialized.\n" ));
  283. }
  284. //
  285. // Start the file server driver.
  286. //
  287. error = InitializeServer( );
  288. if ( error != NO_ERROR ) {
  289. return error;
  290. }
  291. IF_DEBUG(INITIALIZATION) {
  292. SS_PRINT(( "SsInitialize: server FSP initialized.\n" ));
  293. }
  294. //
  295. // Watch for Domain Name changes, and automatically pick them up
  296. //
  297. error = NetRegisterDomainNameChangeNotification( &SsData.SsDomainNameChangeEvent );
  298. IF_DEBUG(INITIALIZATION_ERRORS) {
  299. if( error != NO_ERROR ) {
  300. SS_PRINT(( "SsInitialize: NetRegisterDomainNameChangeNotification failed: "
  301. "%ld\n", error ));
  302. }
  303. }
  304. //
  305. // Start XACTSRV, if requested.
  306. //
  307. // *** This must be done AFTER the server driver is started, but
  308. // BEFORE sticky shares are recreated, otherwise downlevel print
  309. // shares are lost.
  310. //
  311. if ( SsData.ServerInfo599.sv599_acceptdownlevelapis ) {
  312. error = XsStartXactsrv( );
  313. if ( error != NO_ERROR ) {
  314. return error;
  315. }
  316. }
  317. //
  318. // Re-register the server's domain so we pick up the DNS name
  319. //
  320. SsSetDomainName();
  321. //
  322. // Create the default shares needed by the server.
  323. //
  324. error = CreateDefaultShares( );
  325. if ( error != NO_ERROR ) {
  326. return error;
  327. }
  328. IF_DEBUG(INITIALIZATION) {
  329. SS_PRINT(( "SsInitialize: default shares created.\n" ));
  330. }
  331. //
  332. // Complete loading the configuration--sticky shares and transports.
  333. // These must be done after the server FSP has started.
  334. //
  335. error = SsRecreateStickyShares( );
  336. if ( error != NO_ERROR ) {
  337. return error;
  338. }
  339. IF_DEBUG(INITIALIZATION) {
  340. SS_PRINT(( "SsInitialize: sticky shares reloaded.\n" ));
  341. }
  342. //
  343. // Set information used in server announcements.
  344. //
  345. SsSetExportedServerType( NULL, FALSE, FALSE );
  346. //
  347. // Server initialization was successful.
  348. //
  349. return NO_ERROR;
  350. } // SsInitialize
  351. NET_API_STATUS
  352. SsTerminate (
  353. VOID
  354. )
  355. /*++
  356. Routine Description:
  357. This routine sends the FSCTL_SRV_SHUTDOWN control code to the server
  358. FSD to tell it to terminate its FSP.
  359. Arguments:
  360. None.
  361. Return Value:
  362. None.
  363. --*/
  364. {
  365. NET_API_STATUS error;
  366. PNAME_LIST_ENTRY Service;
  367. PTRANSPORT_LIST_ENTRY Transport;
  368. //
  369. // Shut the server FSD/FSP down.
  370. //
  371. error = TerminateServer( );
  372. //
  373. // Shut down XACTSRV.
  374. //
  375. XsStopXactsrv( );
  376. //
  377. // Stop waiting for domain name changes
  378. //
  379. if( SsData.SsDomainNameChangeEvent ) {
  380. NetUnregisterDomainNameChangeNotification( SsData.SsDomainNameChangeEvent );
  381. SsData.SsDomainNameChangeEvent = NULL;
  382. }
  383. //
  384. // Delete security objects.
  385. //
  386. SsDeleteSecurityObjects( );
  387. //
  388. // Close the network announcement event.
  389. //
  390. if (SsData.SsAnnouncementEvent != NULL) {
  391. CloseHandle( SsData.SsAnnouncementEvent );
  392. SsData.SsAnnouncementEvent = NULL;
  393. }
  394. //
  395. // Close the local announcement event.
  396. //
  397. if (SsData.SsStatusChangedEvent != NULL) {
  398. CloseHandle( SsData.SsStatusChangedEvent );
  399. SsData.SsStatusChangedEvent = NULL;
  400. }
  401. //
  402. // Close the termination event.
  403. //
  404. if ( SsData.SsTerminationEvent != NULL ) {
  405. CloseHandle( SsData.SsTerminationEvent );
  406. SsData.SsTerminationEvent = NULL;
  407. }
  408. //
  409. // Free up the transport service list.
  410. //
  411. while( SsData.SsServerNameList != NULL ) {
  412. PNAME_LIST_ENTRY Service = SsData.SsServerNameList;
  413. while( Service->Transports != NULL ) {
  414. PTRANSPORT_LIST_ENTRY Next = Service->Transports->Next;
  415. MIDL_user_free( Service->Transports );
  416. Service->Transports = Next;
  417. }
  418. SsData.SsServerNameList = Service->Next;
  419. MIDL_user_free( Service );
  420. }
  421. //
  422. // Delete the server info resource.
  423. //
  424. if ( SsData.SsServerInfoResourceInitialized ) {
  425. RtlDeleteResource( &SsData.SsServerInfoResource );
  426. SsData.SsServerInfoResourceInitialized = FALSE;
  427. // Free any orphaned delete context's (caused by penetration style attacks)
  428. while( SrvShareDelContextHead != NULL )
  429. {
  430. PSHARE_DEL_CONTEXT pDelete = SrvShareDelContextHead;
  431. SrvShareDelContextHead = pDelete->Next;
  432. MIDL_user_free( pDelete );
  433. }
  434. DeleteCriticalSection( &ShareDelContextMutex );
  435. }
  436. //
  437. // Free up any string relate memory
  438. //
  439. FreeStrings() ;
  440. return error;
  441. } // SsTerminate
  442. NET_API_STATUS
  443. CreateDefaultShares (
  444. VOID
  445. )
  446. /*++
  447. Routine Description:
  448. This routine sends the NetShareAdd API to the server to add the
  449. default server shares using the data above.
  450. Arguments:
  451. None.
  452. Return Value:
  453. NET_API_STATUS - results of operation.
  454. --*/
  455. {
  456. NET_API_STATUS error;
  457. SHARE_INFO_2 shareInfo;
  458. SHARE_INFO shInfo;
  459. WCHAR diskShareName[3];
  460. WCHAR diskSharePath[4];
  461. ULONG i;
  462. DWORD diskMask;
  463. DWORD diskconfiguration;
  464. //
  465. // Create IPC$.
  466. //
  467. // !!! Need to verify the remarks for these default shares.
  468. //
  469. shareInfo.shi2_netname = IPC_SHARE_NAME;
  470. shareInfo.shi2_type = STYPE_IPC;
  471. shareInfo.shi2_remark = NULL;
  472. shareInfo.shi2_permissions = 0;
  473. shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
  474. shareInfo.shi2_current_uses = 0;
  475. shareInfo.shi2_path = NULL;
  476. shareInfo.shi2_passwd = NULL;
  477. shInfo.ShareInfo2 = &shareInfo;
  478. error = NetrShareAdd( NULL, 2, &shInfo, NULL );
  479. if ( error != NO_ERROR ) {
  480. IF_DEBUG(INITIALIZATION_ERRORS) {
  481. SS_PRINT(( "CreateDefaultShares: failed to add " FORMAT_LPWSTR
  482. ": %X\n", shareInfo.shi2_netname, error ));
  483. }
  484. } else {
  485. IF_DEBUG(INITIALIZATION) {
  486. SS_PRINT(( "CreateDefaultShares: added default share "
  487. FORMAT_LPWSTR "\n", shareInfo.shi2_netname, error ));
  488. }
  489. }
  490. //
  491. // If this is a workstation, and the AutoShareWks key is set to TRUE then
  492. // automatically create the admin$ and drive$ shares.
  493. //
  494. //
  495. // If this is a server, and the AutoShareServer key is set to TRUE then
  496. // automatically create the admin$ and drive$ shares.
  497. //
  498. if( (SsData.ServerInfo598.sv598_producttype == NtProductWinNt &&
  499. SsData.ServerInfo598.sv598_autosharewks) ||
  500. (SsData.ServerInfo598.sv598_producttype != NtProductWinNt &&
  501. SsData.ServerInfo598.sv598_autoshareserver ) ) {
  502. //
  503. // Create ADMIN$.
  504. //
  505. shareInfo.shi2_netname = ADMIN_SHARE_NAME;
  506. shareInfo.shi2_type = STYPE_DISKTREE;
  507. shareInfo.shi2_remark = NULL;
  508. shareInfo.shi2_permissions = 1;
  509. shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
  510. shareInfo.shi2_current_uses = 0;
  511. shareInfo.shi2_path = NULL;
  512. shareInfo.shi2_passwd = NULL;
  513. error = NetrShareAdd( NULL, 2, &shInfo, NULL );
  514. if ( error != NO_ERROR ) {
  515. IF_DEBUG(INITIALIZATION_ERRORS) {
  516. SS_PRINT(( "CreateDefaultShares: failed to add " FORMAT_LPWSTR
  517. ": %X\n", shareInfo.shi2_netname, error ));
  518. }
  519. } else {
  520. IF_DEBUG(INITIALIZATION) {
  521. SS_PRINT(( "CreateDefaultShares: added default share "
  522. FORMAT_LPWSTR "\n", shareInfo.shi2_netname, error ));
  523. }
  524. }
  525. //
  526. // Loop through available drives, creating an admin share for each
  527. // one. Note that we allow "holes" in the drive letter space.
  528. //
  529. diskShareName[0] = 'A';
  530. diskShareName[1] = '$';
  531. diskShareName[2] = '\0';
  532. diskSharePath[0] = diskShareName[0];
  533. diskSharePath[1] = ':';
  534. diskSharePath[2] = '\\';
  535. diskSharePath[3] = '\0';
  536. shareInfo.shi2_netname = diskShareName;
  537. shareInfo.shi2_type = STYPE_DISKTREE;
  538. shareInfo.shi2_remark = SsDiskAdminShareRemark;
  539. shareInfo.shi2_permissions = 1;
  540. shareInfo.shi2_max_uses = SHI_USES_UNLIMITED;
  541. shareInfo.shi2_current_uses = 0;
  542. shareInfo.shi2_path = diskSharePath;
  543. shareInfo.shi2_passwd = NULL;
  544. diskconfiguration = DiscoverDrives();
  545. for ( i = 0, diskMask = 0x80000000;
  546. (i < SRVSVC_MAX_NUMBER_OF_DISKS) && (diskShareName[0] <= 'Z');
  547. i++, diskShareName[0]++, diskSharePath[0]++, diskMask >>= 1 ) {
  548. if ( (diskconfiguration & diskMask) != 0) {
  549. error = NetrShareAdd( NULL, 2, &shInfo, NULL );
  550. if ( error != NO_ERROR ) {
  551. IF_DEBUG(INITIALIZATION_ERRORS) {
  552. SS_PRINT(( "CreateDefaultShares: failed to add "
  553. FORMAT_LPWSTR ": %X\n",
  554. shareInfo.shi2_netname, error ));
  555. }
  556. } else {
  557. IF_DEBUG(INITIALIZATION) {
  558. SS_PRINT(( "CreateDefaultShares: added default share "
  559. FORMAT_LPWSTR "\n",
  560. shareInfo.shi2_netname, error ));
  561. }
  562. }
  563. }
  564. }
  565. }
  566. return NO_ERROR;
  567. } // CreateDefaultShares
  568. DWORD
  569. DiscoverDrives (
  570. VOID
  571. )
  572. /*++
  573. Routine Description:
  574. This routine returns a bit mask representing the local drives present
  575. on the system.
  576. Arguments:
  577. None.
  578. Return Value:
  579. DrivesAvailable - A 32 bit field representing the available drives on
  580. the system. The MSB represents drive A, the next represents drive
  581. B, etc. The extra 6 bits are currently unsed.
  582. --*/
  583. {
  584. WCHAR rootDirPath[4];
  585. WCHAR driveLetter;
  586. DWORD drivesAvailable = 0;
  587. DWORD driveMask = 0x80000000;
  588. UINT driveType;
  589. rootDirPath[1] = ':';
  590. rootDirPath[2] = '\\';
  591. rootDirPath[3] = '\0';
  592. for ( driveLetter = 'A';
  593. driveLetter <= 'Z';
  594. driveLetter++ ) {
  595. rootDirPath[0] = driveLetter;
  596. driveType = SsGetDriveType( rootDirPath );
  597. //
  598. // We only put fixed disk drives into the mask. We used to put
  599. // removables, CD-ROMs, and RAM disks into the list. But this
  600. // list is used for two purposes: creation of X$ shares (for
  601. // backup purposes), and free disk space checking (for admin
  602. // purposes). Neither of these uses applies very well to these
  603. // devices.
  604. //
  605. if ( driveType == DRIVE_FIXED
  606. //|| driveType == DRIVE_REMOVABLE
  607. //|| driveType == DRIVE_CDROM
  608. //|| driveType == DRIVE_RAMDISK
  609. ) {
  610. //
  611. // This is a valid drive letter
  612. //
  613. drivesAvailable |= driveMask;
  614. }
  615. //
  616. // Update drive mask for the next drive
  617. //
  618. driveMask /= 2;
  619. }
  620. return drivesAvailable;
  621. }
  622. VOID
  623. InitializeDefaultData(
  624. VOID
  625. )
  626. /*++
  627. Routine Description:
  628. This routine sets up the default data in the server service by using
  629. the values in srvconfg.h.
  630. Arguments:
  631. None.
  632. Return Value:
  633. None.
  634. --*/
  635. {
  636. NET_API_STATUS error;
  637. USHORT i;
  638. OSVERSIONINFOEX VersionInformation;
  639. NT_PRODUCT_TYPE ProductType;
  640. SYSTEM_INFO SystemInfo;
  641. WCHAR szNumber[ sizeof( SsData.szVersionNumber ) / sizeof( WCHAR ) ], *p;
  642. // Query the system info
  643. GetSystemInfo( &SystemInfo );
  644. //
  645. // Loop through all the defined fields, setting them as we go.
  646. //
  647. for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
  648. error = SsSetField(
  649. &SsServerInfoFields[i],
  650. &SsServerInfoFields[i].DefaultValue,
  651. FALSE,
  652. NULL
  653. );
  654. SS_ASSERT( error == NO_ERROR );
  655. }
  656. SsData.NumberOfPrintShares = 0;
  657. //
  658. // Get the system version and product name
  659. //
  660. VersionInformation.dwOSVersionInfoSize = sizeof( VersionInformation );
  661. i = (USHORT)GetVersionEx( (LPOSVERSIONINFO)&VersionInformation );
  662. SS_ASSERT( i == TRUE );
  663. SsData.ServerInfo102.sv102_version_major = VersionInformation.dwMajorVersion;
  664. SsData.ServerInfo102.sv102_version_minor = VersionInformation.dwMinorVersion;
  665. wcscpy( SsData.ServerProductName, SERVER_PRODUCT_NAME );
  666. //
  667. // Convert the version number to a version number string...
  668. //
  669. szNumber[ sizeof( szNumber ) / sizeof( szNumber[0] ) - 1 ] = L'\0';
  670. for( p = &szNumber[ sizeof( szNumber ) / sizeof( szNumber[0] ) - 2 ]; p > &szNumber[0]; p-- ) {
  671. *p = L"0123456789"[ VersionInformation.dwMinorVersion % 10 ];
  672. VersionInformation.dwMinorVersion /= 10;
  673. if( VersionInformation.dwMinorVersion == 0 )
  674. break;
  675. }
  676. *(--p) = L'.';
  677. do {
  678. *(--p) = L"0123456789"[ VersionInformation.dwMajorVersion % 10 ];
  679. VersionInformation.dwMajorVersion /= 10;
  680. } while( VersionInformation.dwMajorVersion && p > &szNumber[0] );
  681. if( VersionInformation.wSuiteMask & VER_SUITE_PERSONAL )
  682. {
  683. // Turn off auto-shares by default on Personal
  684. SsData.ServerInfo598.sv598_autoshareserver = FALSE;
  685. SsData.ServerInfo598.sv598_autosharewks = FALSE;
  686. }
  687. //
  688. // ... and store it in SsData
  689. //
  690. wcscpy( SsData.szVersionNumber, p );
  691. //
  692. // Change certain defaults for Workstations
  693. if( RtlGetNtProductType( &ProductType ) )
  694. {
  695. if( ProductType == NtProductWinNt )
  696. {
  697. SsData.ServerInfo599.sv599_diskspacethreshold = 0;
  698. SsData.ServerInfo598.sv598_disabledos = TRUE;
  699. }
  700. }
  701. // Change defaults for Embedded
  702. if( IsEmbedded() )
  703. {
  704. MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_EMBEDDED );
  705. }
  706. //
  707. // ADS+ scaling changes
  708. //
  709. if( SystemInfo.dwNumberOfProcessors >= 4 )
  710. {
  711. // Set the "other queue affinity" to 12 to prevent us from leaving our preffered queue
  712. // unless necessary...
  713. SsData.ServerInfo598.sv598_otherqueueaffinity = DEF_ADS_OTHERQUEUEAFFINITY;
  714. }
  715. } // InitializeDefaultData
  716. NET_API_STATUS
  717. InitializeServer (
  718. VOID
  719. )
  720. /*++
  721. Routine Description:
  722. This routine sends the FSCTL_SRV_STARTUP control code to the server
  723. FSD to tell it to start and initialize its FSP.
  724. Arguments:
  725. None.
  726. Return Value:
  727. NET_API_STATUS - results of operation.
  728. --*/
  729. {
  730. NET_API_STATUS error;
  731. PSERVER_REQUEST_PACKET srp;
  732. SS_ASSERT( !SsData.SsServerFspStarted );
  733. //
  734. // Load the server driver.
  735. //
  736. error = LoadServer( );
  737. if ( error != NO_ERROR ) {
  738. return error;
  739. }
  740. //
  741. // Get an SRP and set it up with the appropriate level.
  742. //
  743. srp = SsAllocateSrp( );
  744. if ( srp == NULL ) {
  745. UnloadServer();
  746. return ERROR_NOT_ENOUGH_MEMORY;
  747. }
  748. srp->Level = (ULONG)SS_STARTUP_LEVEL;
  749. //
  750. // Pass domain name to the server.
  751. //
  752. RtlInitUnicodeString( &srp->Name1, SsData.DomainNameBuffer );
  753. //
  754. // Pass server name to the server.
  755. //
  756. RtlInitUnicodeString( &srp->Name2, SsData.ServerNameBuffer );
  757. //
  758. // Send the request on to the server.
  759. //
  760. error = SsServerFsControl(
  761. FSCTL_SRV_STARTUP,
  762. srp,
  763. &SsData.ServerInfo102,
  764. sizeof(SERVER_INFO_102) + sizeof(SERVER_INFO_599) +
  765. sizeof(SERVER_INFO_598)
  766. );
  767. if ( error == NO_ERROR ) {
  768. SsData.SsServerFspStarted = TRUE;
  769. } else {
  770. UnloadServer();
  771. }
  772. //
  773. // Free the SRP and return.
  774. //
  775. SsFreeSrp( srp );
  776. return error;
  777. } // InitializeServer
  778. NET_API_STATUS
  779. StartPnpNotifications (
  780. VOID
  781. )
  782. /*++
  783. Routine Description:
  784. This routine sends the FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS control code to the server
  785. FSD to tell it to start monitoring transport PNP notifications
  786. Arguments:
  787. None.
  788. Return Value:
  789. NET_API_STATUS - results of operation.
  790. --*/
  791. {
  792. NET_API_STATUS error;
  793. //
  794. // Send the request on to the server.
  795. //
  796. error = SsServerFsControl(
  797. FSCTL_SRV_BEGIN_PNP_NOTIFICATIONS,
  798. NULL,
  799. NULL,
  800. 0
  801. );
  802. IF_DEBUG(INITIALIZATION) {
  803. if( error != NO_ERROR ) {
  804. SS_PRINT(( "StartPnpNotifications: error %X\n", error ));
  805. }
  806. }
  807. return error;
  808. } // StartPnpNotifications
  809. NET_API_STATUS
  810. LoadServer (
  811. VOID
  812. )
  813. {
  814. NTSTATUS status;
  815. NET_API_STATUS error;
  816. LPWSTR registryPathBuffer;
  817. UNICODE_STRING registryPath;
  818. ULONG privileges[1];
  819. LPWSTR subString[1];
  820. IF_DEBUG(INITIALIZATION) {
  821. SS_PRINT(( "LoadServer: entered\n" ));
  822. }
  823. registryPathBuffer = (LPWSTR)MIDL_user_allocate(
  824. sizeof(SERVICE_REGISTRY_KEY) +
  825. sizeof(SERVER_DRIVER_NAME) +
  826. sizeof(WCHAR) // for null
  827. );
  828. if ( registryPathBuffer == NULL ) {
  829. IF_DEBUG(INITIALIZATION_ERRORS) {
  830. SS_PRINT(( "LoadServer: Unable to allocate memory\n" ));
  831. }
  832. return ERROR_NOT_ENOUGH_MEMORY;
  833. }
  834. privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
  835. error = NetpGetPrivilege( 1, privileges );
  836. if ( error != NO_ERROR ) {
  837. IF_DEBUG(INITIALIZATION_ERRORS) {
  838. SS_PRINT(( "LoadServer: Unable to enable privilege: %ld\n",
  839. error ));
  840. }
  841. MIDL_user_free( registryPathBuffer );
  842. return error;
  843. }
  844. wcscpy( registryPathBuffer, SERVICE_REGISTRY_KEY );
  845. wcscat( registryPathBuffer, SERVER_DRIVER_NAME );
  846. RtlInitUnicodeString( &registryPath, registryPathBuffer );
  847. status = NtLoadDriver( &registryPath );
  848. MIDL_user_free( registryPathBuffer );
  849. if ( status == STATUS_IMAGE_ALREADY_LOADED ) {
  850. status = STATUS_SUCCESS;
  851. }
  852. if ( !NT_SUCCESS(status) ) {
  853. IF_DEBUG(INITIALIZATION_ERRORS) {
  854. SS_PRINT(( "LoadServer: Unable to load driver: %lx\n",
  855. status ));
  856. }
  857. subString[0] = SERVER_DRIVER_NAME;
  858. SsLogEvent(
  859. EVENT_SRV_CANT_LOAD_DRIVER,
  860. 1,
  861. subString,
  862. status
  863. );
  864. error = RtlNtStatusToDosError(status);
  865. } else {
  866. //
  867. // Get a handle to the server.
  868. //
  869. error = SsOpenServer();
  870. if ( error != NO_ERROR ) {
  871. UnloadServer();
  872. }
  873. }
  874. IF_DEBUG(INITIALIZATION) {
  875. SS_PRINT(( "LoadServer: returning\n" ));
  876. }
  877. NetpReleasePrivilege( );
  878. return error;
  879. } // LoadServer
  880. NET_API_STATUS
  881. ConvertStringToTransportAddress (
  882. IN PUNICODE_STRING InputName,
  883. OUT CHAR TransportAddress[ MAX_PATH ],
  884. OUT PULONG TransportAddressLength
  885. )
  886. {
  887. OEM_STRING computerName;
  888. if( InputName == NULL || InputName->Length == 0 ) {
  889. RtlCopyMemory( TransportAddress,
  890. SsData.SsServerTransportAddress,
  891. SsData.SsServerTransportAddressLength );
  892. *TransportAddressLength = SsData.SsServerTransportAddressLength;
  893. return NO_ERROR;
  894. }
  895. if( InputName->Length > (MAX_PATH - 1 ) * sizeof( WCHAR ) ) {
  896. return ERROR_INVALID_PARAMETER;
  897. }
  898. //
  899. // Write directly to the output buffer
  900. //
  901. computerName.Buffer = TransportAddress;
  902. computerName.MaximumLength = MAX_PATH;
  903. //
  904. // Convert To Oem Name
  905. //
  906. (VOID) RtlUpcaseUnicodeStringToOemString(
  907. &computerName,
  908. InputName,
  909. FALSE
  910. );
  911. //
  912. // Make sure it is exactly NETBIOS_NAME_LEN characters
  913. //
  914. if( computerName.Length < NETBIOS_NAME_LEN ) {
  915. RtlCopyMemory( TransportAddress + computerName.Length,
  916. " ",
  917. NETBIOS_NAME_LEN - computerName.Length
  918. );
  919. *TransportAddressLength = NETBIOS_NAME_LEN;
  920. } else {
  921. *TransportAddressLength = NETBIOS_NAME_LEN;
  922. }
  923. return NO_ERROR;
  924. } // ConvertStringToTransportAddress
  925. VOID
  926. SsSetDomainName (
  927. VOID
  928. )
  929. /*++
  930. Routine Description:
  931. Calls NetpGetDomainName to determine the domain name the server
  932. should use. Ensure this domain name is reflected throughout the server service and
  933. srv.sys
  934. Arguments:
  935. None.
  936. Return Value:
  937. None.
  938. --*/
  939. {
  940. NET_API_STATUS error;
  941. LPWSTR domainName = NULL;
  942. LPWSTR dnsDomainName = NULL;
  943. LPWSTR fullDnsDomainName = NULL;
  944. PNAME_LIST_ENTRY service;
  945. BOOLEAN IsWorkgroup;
  946. DWORD dwError;
  947. dwError = NetpGetDomainNameExEx( &domainName, &fullDnsDomainName, &IsWorkgroup );
  948. if( dwError != NO_ERROR )
  949. {
  950. LPWSTR subString[2];
  951. subString[0] = SsData.DomainNameBuffer;
  952. subString[1] = L"????";
  953. SsLogEvent(
  954. EVENT_SRV_CANT_CHANGE_DOMAIN_NAME,
  955. 2,
  956. subString,
  957. dwError
  958. );
  959. return;
  960. }
  961. if( fullDnsDomainName )
  962. {
  963. // Strip the name domain.foo.com down do just "domain"
  964. dnsDomainName = wcstok( fullDnsDomainName, L"." );
  965. }
  966. RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE );
  967. if( SsData.SsServerFspStarted && SsData.DomainNameBuffer[0] ) {
  968. SERVER_REQUEST_PACKET srp;
  969. NTSTATUS status;
  970. //
  971. // If we used to have a domain name, change the service list to the
  972. // new domain name
  973. //
  974. for( service = SsData.SsServerNameList; service != NULL; service = service->Next ) {
  975. if( !STRCMPI( service->DomainName, SsData.DomainNameBuffer ) ) {
  976. STRCPY( service->DomainName, domainName );
  977. }
  978. }
  979. srp.Name1.Length = wcslen( SsData.DomainNameBuffer ) * sizeof( WCHAR );
  980. srp.Name1.MaximumLength = srp.Name1.Length;
  981. srp.Name1.Buffer = SsData.DomainNameBuffer;
  982. srp.Name2.Length = wcslen( domainName ) * sizeof( WCHAR );
  983. srp.Name2.MaximumLength = srp.Name2.Length;
  984. srp.Name2.Buffer = domainName;
  985. //
  986. // Tell the SMB server about this change
  987. //
  988. status = SsServerFsControl( FSCTL_SRV_CHANGE_DOMAIN_NAME, &srp, NULL, 0 );
  989. //
  990. // If we are unable to change the domain name, log an error
  991. //
  992. if( !NT_SUCCESS( status ) ) {
  993. LPWSTR subString[2];
  994. subString[0] = SsData.DomainNameBuffer;
  995. subString[1] = domainName;
  996. SsLogEvent(
  997. EVENT_SRV_CANT_CHANGE_DOMAIN_NAME,
  998. 2,
  999. subString,
  1000. status
  1001. );
  1002. }
  1003. if( fullDnsDomainName && !IsWorkgroup )
  1004. {
  1005. //
  1006. // Tell the SMB server about the DNS Domain Name change too
  1007. //
  1008. srp.Name2.Length = wcslen( dnsDomainName ) * sizeof( WCHAR );
  1009. srp.Name2.MaximumLength = srp.Name2.Length;
  1010. srp.Name2.Buffer = dnsDomainName;
  1011. srp.Name1.Length = wcslen( domainName ) * sizeof( WCHAR );
  1012. srp.Name1.MaximumLength = srp.Name1.Length;
  1013. srp.Name1.Buffer = domainName;
  1014. //
  1015. // Tell the SMB server about this change
  1016. //
  1017. status = SsServerFsControl( FSCTL_SRV_CHANGE_DNS_DOMAIN_NAME, &srp, NULL, 0 );
  1018. //
  1019. // If we are unable to change the domain name, log an error
  1020. //
  1021. if( !NT_SUCCESS( status ) ) {
  1022. LPWSTR subString[2];
  1023. subString[0] = SsData.DomainNameBuffer;
  1024. subString[1] = domainName;
  1025. SsLogEvent(
  1026. EVENT_SRV_CANT_CHANGE_DOMAIN_NAME,
  1027. 2,
  1028. subString,
  1029. status
  1030. );
  1031. }
  1032. }
  1033. }
  1034. //
  1035. // Copy the name into our name buffer.
  1036. //
  1037. STRNCPY( SsData.DomainNameBuffer, domainName, MAX_PATH);
  1038. RtlReleaseResource( &SsData.SsServerInfoResource );
  1039. //
  1040. // Free the storage allocated by NetpGetComputerName.
  1041. //
  1042. (VOID)NetApiBufferFree( domainName );
  1043. if( fullDnsDomainName )
  1044. {
  1045. (VOID)NetApiBufferFree( fullDnsDomainName );
  1046. }
  1047. IF_DEBUG(INITIALIZATION) {
  1048. SS_PRINT(( "SsSetDomainName: domain name set to " FORMAT_LPWSTR
  1049. "(could be overridden later!)\n",
  1050. SsData.DomainNameBuffer ));
  1051. }
  1052. } // SsSetDomainName
  1053. VOID
  1054. SetServerName (
  1055. VOID
  1056. )
  1057. /*++
  1058. Routine Description:
  1059. Calls NetpGetComputerName to determine the name the server should use
  1060. to register itself on the network.
  1061. Arguments:
  1062. None.
  1063. Return Value:
  1064. None.
  1065. --*/
  1066. {
  1067. NET_API_STATUS error;
  1068. LPWSTR computerName;
  1069. //
  1070. // Get the computer name.
  1071. //
  1072. error = NetpGetComputerName( &computerName );
  1073. SS_ASSERT( error == NO_ERROR );
  1074. //
  1075. // Copy the name into our name buffer. This name is returned to
  1076. // our apis.
  1077. //
  1078. STRCPY( SsData.ServerNameBuffer, computerName );
  1079. //
  1080. // Free the storage allocated by NetpGetComputerName.
  1081. //
  1082. (void) NetApiBufferFree( computerName );
  1083. //
  1084. // Uppercase the server name. This name is used for announcements.
  1085. //
  1086. {
  1087. UNICODE_STRING serverName;
  1088. SsData.ServerAnnounceName.Length =
  1089. serverName.Length =
  1090. (USHORT) (STRLEN( SsData.ServerNameBuffer ) * sizeof(WCHAR));
  1091. SsData.ServerAnnounceName.MaximumLength =
  1092. serverName.MaximumLength =
  1093. (USHORT) (serverName.Length + sizeof(WCHAR));
  1094. serverName.Buffer = SsData.ServerNameBuffer;
  1095. SsData.ServerAnnounceName.Buffer = SsData.AnnounceNameBuffer;
  1096. (VOID)RtlUpcaseUnicodeString(
  1097. &SsData.ServerAnnounceName,
  1098. &serverName,
  1099. FALSE
  1100. );
  1101. //
  1102. // Make the server name in Netbios format.
  1103. //
  1104. error = ConvertStringToTransportAddress(
  1105. &serverName,
  1106. SsData.SsServerTransportAddress,
  1107. &SsData.SsServerTransportAddressLength
  1108. );
  1109. SS_ASSERT( error == NO_ERROR );
  1110. }
  1111. IF_DEBUG(INITIALIZATION) {
  1112. SS_PRINT(( "SetServerName: server name set to " FORMAT_LPWSTR
  1113. " (could be overridden later!)\n",
  1114. SsData.ServerNameBuffer ));
  1115. }
  1116. return;
  1117. } // SetServerName
  1118. NET_API_STATUS
  1119. TerminateServer (
  1120. VOID
  1121. )
  1122. /*++
  1123. Routine Description:
  1124. This routine sends the FSCTL_SRV_SHUTDOWN control code to the server
  1125. FSD to tell it to shutdown operations.
  1126. Arguments:
  1127. None.
  1128. Return Value:
  1129. None.
  1130. --*/
  1131. {
  1132. NET_API_STATUS error = NO_ERROR;
  1133. if ( SsData.SsServerFspStarted ) {
  1134. SsData.SsServerFspStarted = FALSE;
  1135. //
  1136. // Send the request on to the server.
  1137. //
  1138. error = SsServerFsControl(
  1139. FSCTL_SRV_SHUTDOWN,
  1140. NULL,
  1141. NULL,
  1142. 0
  1143. );
  1144. if ( (error != NO_ERROR) &&
  1145. (error != ERROR_SERVER_HAS_OPEN_HANDLES) ) {
  1146. IF_DEBUG(TERMINATION_ERRORS) {
  1147. SS_PRINT(( "TerminateServer: FSCTL_SRV_SHUTDOWN failed: %ld\n",
  1148. error ));
  1149. }
  1150. }
  1151. //
  1152. // Unload the server driver, unless there are other open handles
  1153. // to the server. We don't unload the driver in this case
  1154. // because the driver won't actually go away until the
  1155. // additional handles are closed, so the driver will not be
  1156. // fully unloaded. This would cause a subsequent server startup
  1157. // to fail.
  1158. //
  1159. if ( error != ERROR_SERVER_HAS_OPEN_HANDLES ) {
  1160. IF_DEBUG(TERMINATION) {
  1161. SS_PRINT(( "TerminateServer: Unloading server\n" ));
  1162. }
  1163. UnloadServer( );
  1164. }
  1165. }
  1166. //
  1167. // Close the handle to the server.
  1168. //
  1169. SsCloseServer( );
  1170. return error;
  1171. } // TerminateServer
  1172. VOID
  1173. UnloadServer (
  1174. VOID
  1175. )
  1176. {
  1177. NTSTATUS status;
  1178. NET_API_STATUS error;
  1179. LPWSTR registryPathBuffer;
  1180. UNICODE_STRING registryPath;
  1181. ULONG privileges[1];
  1182. LPWSTR subString[1];
  1183. registryPathBuffer = (LPWSTR)MIDL_user_allocate(
  1184. sizeof(SERVICE_REGISTRY_KEY) +
  1185. sizeof(SERVER_DRIVER_NAME) +
  1186. sizeof(WCHAR) // for null
  1187. );
  1188. if ( registryPathBuffer == NULL ) {
  1189. IF_DEBUG(TERMINATION_ERRORS) {
  1190. SS_PRINT(( "UnloadServer: Unable to allocate memory\n" ));
  1191. }
  1192. return;
  1193. }
  1194. privileges[0] = SE_LOAD_DRIVER_PRIVILEGE;
  1195. error = NetpGetPrivilege( 1, privileges );
  1196. if ( error != NO_ERROR ) {
  1197. IF_DEBUG(TERMINATION_ERRORS) {
  1198. SS_PRINT(( "UnloadServer: Unable to enable privilege: %ld\n",
  1199. error ));
  1200. }
  1201. MIDL_user_free( registryPathBuffer );
  1202. return;
  1203. }
  1204. wcscpy( registryPathBuffer, SERVICE_REGISTRY_KEY );
  1205. wcscat( registryPathBuffer, SERVER_DRIVER_NAME );
  1206. RtlInitUnicodeString( &registryPath, registryPathBuffer );
  1207. status = NtUnloadDriver( &registryPath );
  1208. MIDL_user_free( registryPathBuffer );
  1209. NetpReleasePrivilege( );
  1210. if ( !NT_SUCCESS(status) ) {
  1211. IF_DEBUG(TERMINATION_ERRORS) {
  1212. SS_PRINT(( "UnloadServer: Unable to unload driver: %lx\n",
  1213. status ));
  1214. }
  1215. subString[0] = SERVER_DRIVER_NAME;
  1216. SsLogEvent(
  1217. EVENT_SRV_CANT_UNLOAD_DRIVER,
  1218. 1,
  1219. subString,
  1220. status
  1221. );
  1222. }
  1223. return;
  1224. } // UnloadServer
  1225. VOID
  1226. InitializeStrings(
  1227. VOID
  1228. )
  1229. /*++
  1230. Routine Description:
  1231. Retrieve internationalizable strings from NETMSG.DLL. They
  1232. are used for share comments for IPC$, ADMIN$, C$, etc.
  1233. Routine does not report any errors. If there are problems,
  1234. the strings will be empty ones.
  1235. FreeStrings should be called to free the memory allocated
  1236. by format message and the
  1237. Arguments:
  1238. None.
  1239. Return Value:
  1240. None.
  1241. --*/
  1242. {
  1243. DWORD dwRet, dwFlags ;
  1244. HMODULE hModule ;
  1245. //
  1246. // init the strings to the default empty remark.
  1247. //
  1248. SsAdminShareRemark = SsDefaultRemark ;
  1249. SsIPCShareRemark = SsDefaultRemark ;
  1250. SsDiskAdminShareRemark = SsDefaultRemark ;
  1251. //
  1252. // load NETMSG.DLL - if we cannot, just return.
  1253. //
  1254. hModule = LoadLibrary(NETMSG_DLL) ;
  1255. if(!hModule)
  1256. return ;
  1257. //
  1258. // hit FormatMessage 3 times for the real thing...
  1259. //
  1260. dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE ;
  1261. dwRet = FormatMessage(dwFlags,
  1262. hModule,
  1263. APE2_SERVER_IPC_SHARE_REMARK,
  1264. 0,
  1265. (LPWSTR) &SsIPCShareRemark,
  1266. 1,
  1267. NULL) ;
  1268. if (dwRet == 0)
  1269. SsIPCShareRemark = SsDefaultRemark ;
  1270. dwRet = FormatMessage(dwFlags,
  1271. hModule,
  1272. APE2_SERVER_ADMIN_SHARE_REMARK,
  1273. 0,
  1274. (LPWSTR) &SsAdminShareRemark,
  1275. 1,
  1276. NULL) ;
  1277. if (dwRet == 0)
  1278. SsAdminShareRemark = SsDefaultRemark ;
  1279. dwRet = FormatMessage(dwFlags,
  1280. hModule,
  1281. APE2_SERVER_DISK_ADMIN_SHARE_REMARK,
  1282. 0,
  1283. (LPWSTR) &SsDiskAdminShareRemark,
  1284. 1,
  1285. NULL) ;
  1286. if (dwRet == 0)
  1287. SsDiskAdminShareRemark = SsDefaultRemark ;
  1288. FreeLibrary(hModule) ;
  1289. }
  1290. VOID
  1291. FreeStrings(
  1292. VOID
  1293. )
  1294. /*++
  1295. Routine Description:
  1296. Free the memory used by server comment strings (allocated by
  1297. FormatMessage).
  1298. Arguments:
  1299. None.
  1300. Return Value:
  1301. None.
  1302. --*/
  1303. {
  1304. //
  1305. // as long as the strings do not point to the default (static data),
  1306. // free them.
  1307. //
  1308. if (SsAdminShareRemark && SsAdminShareRemark != SsDefaultRemark)
  1309. LocalFree(SsAdminShareRemark) ;
  1310. SsAdminShareRemark = SsDefaultRemark ;
  1311. if (SsIPCShareRemark && SsIPCShareRemark != SsDefaultRemark)
  1312. LocalFree(SsIPCShareRemark) ;
  1313. SsIPCShareRemark = SsDefaultRemark ;
  1314. if (SsDiskAdminShareRemark && SsDiskAdminShareRemark != SsDefaultRemark)
  1315. LocalFree(SsDiskAdminShareRemark) ;
  1316. SsDiskAdminShareRemark = SsDefaultRemark ;
  1317. }