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.

673 lines
17 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. share.c
  5. Abstract:
  6. This module contains routines for adding, deleting, and enumerating
  7. shared resources.
  8. Author:
  9. David Treadwell (davidtr) 15-Nov-1989
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include "share.tmh"
  14. #pragma hdrstop
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text( PAGE, SrvVerifyShare )
  17. #pragma alloc_text( PAGE, SrvFindShare )
  18. #pragma alloc_text( PAGE, SrvRemoveShare )
  19. #pragma alloc_text( PAGE, SrvAddShare )
  20. #pragma alloc_text( PAGE, SrvShareEnumApiHandler )
  21. #endif
  22. PSHARE
  23. SrvVerifyShare (
  24. IN PWORK_CONTEXT WorkContext,
  25. IN PSZ ShareName,
  26. IN PSZ ShareTypeString,
  27. IN BOOLEAN ShareNameIsUnicode,
  28. IN BOOLEAN IsNullSession,
  29. OUT PNTSTATUS Status,
  30. OUT PUNICODE_STRING ServerName OPTIONAL
  31. )
  32. /*++
  33. Routine Description:
  34. Attempts to find a share that matches a given name and share type.
  35. Arguments:
  36. ShareName - name of share to verify, including the server name.
  37. (I.e., of the form "\\server\share", as received in the SMB.)
  38. ShareTypeString - type of the share (A:, LPT1:, COMM, IPC, or ?????).
  39. ShareNameIsUnicode - if TRUE, the share name is Unicode.
  40. IsNullSession - Is this the NULL session?
  41. Status - Reason why this call failed. Not used if a share is returned.
  42. ServerName - The servername part of the requested resource.
  43. Return Value:
  44. A pointer to a share matching the given name and share type, or NULL
  45. if none exists.
  46. --*/
  47. {
  48. PSHARE share;
  49. BOOLEAN anyShareType = FALSE;
  50. SHARE_TYPE shareType;
  51. PWCH nameOnly;
  52. UNICODE_STRING nameOnlyString;
  53. UNICODE_STRING shareName;
  54. PAGED_CODE( );
  55. if( ARGUMENT_PRESENT( ServerName ) ) {
  56. ServerName->Buffer = NULL;
  57. ServerName->MaximumLength = ServerName->Length = 0;
  58. }
  59. //
  60. // If the client passed in a malformed type string, then bail out
  61. //
  62. if( SrvGetStringLength( ShareTypeString,
  63. END_OF_REQUEST_SMB( WorkContext ),
  64. FALSE, TRUE ) == (USHORT)-1 ) {
  65. IF_DEBUG(ERRORS) {
  66. KdPrint(( "SrvVerifyShare: Invalid share type length!\n" ));
  67. }
  68. *Status = STATUS_BAD_DEVICE_TYPE;
  69. return NULL;
  70. }
  71. if( SrvGetStringLength( ShareName,
  72. END_OF_REQUEST_SMB( WorkContext ),
  73. ShareNameIsUnicode, TRUE ) == (USHORT)-1 ) {
  74. IF_DEBUG(ERRORS) {
  75. KdPrint(( "SrvVerifyShare: Invalid share name!\n" ));
  76. }
  77. *Status = STATUS_BAD_NETWORK_NAME;
  78. return NULL;
  79. }
  80. //
  81. // First ensure that the share type string is valid.
  82. //
  83. if ( _stricmp( StrShareTypeNames[ShareTypeDisk], ShareTypeString ) == 0 ) {
  84. shareType = ShareTypeDisk;
  85. } else if ( _stricmp( StrShareTypeNames[ShareTypePipe], ShareTypeString ) == 0 ) {
  86. shareType = ShareTypePipe;
  87. } else if ( _stricmp( StrShareTypeNames[ShareTypePrint], ShareTypeString ) == 0 ) {
  88. shareType = ShareTypePrint;
  89. } else if ( _stricmp( StrShareTypeNames[ShareTypeWild], ShareTypeString ) == 0 ) {
  90. anyShareType = TRUE;
  91. } else {
  92. IF_DEBUG(ERRORS) {
  93. SrvPrint1( "SrvVerifyShare: Invalid share type: %s\n",
  94. ShareTypeString );
  95. }
  96. *Status = STATUS_BAD_DEVICE_TYPE;
  97. return NULL;
  98. }
  99. //
  100. // If the passed-in server\share combination is not Unicode, convert
  101. // it to Unicode.
  102. //
  103. if ( ShareNameIsUnicode ) {
  104. ShareName = ALIGN_SMB_WSTR( ShareName );
  105. }
  106. if ( !NT_SUCCESS(SrvMakeUnicodeString(
  107. ShareNameIsUnicode,
  108. &shareName,
  109. ShareName,
  110. NULL
  111. )) ) {
  112. IF_DEBUG(ERRORS) {
  113. SrvPrint0( "SrvVerifyShare: Unable to allocate heap for Unicode share name string\n" );
  114. }
  115. *Status = STATUS_INSUFF_SERVER_RESOURCES;
  116. return NULL;
  117. }
  118. //
  119. // Skip past the "\\server\" part of the input string. If there is
  120. // no leading "\\", assume that the input string contains the share
  121. // name only. If there is a "\\", but no subsequent "\", assume
  122. // that the input string contains just a server name, and points to
  123. // the end of that name, thus fabricating a null share name.
  124. //
  125. nameOnly = shareName.Buffer;
  126. if ( (*nameOnly == DIRECTORY_SEPARATOR_CHAR) &&
  127. (*(nameOnly+1) == DIRECTORY_SEPARATOR_CHAR) ) {
  128. PWSTR nextSlash;
  129. nameOnly += 2;
  130. nextSlash = wcschr( nameOnly, DIRECTORY_SEPARATOR_CHAR );
  131. if( ShareNameIsUnicode && ARGUMENT_PRESENT( ServerName ) ) {
  132. ServerName->Buffer = nameOnly;
  133. ServerName->MaximumLength = ServerName->Length = (USHORT)((nextSlash - nameOnly) * sizeof( WCHAR ));
  134. }
  135. if ( nextSlash == NULL ) {
  136. nameOnly = NULL;
  137. } else {
  138. nameOnly = nextSlash + 1;
  139. }
  140. }
  141. RtlInitUnicodeString( &nameOnlyString, nameOnly );
  142. //
  143. // Try to match share name against available share names.
  144. //
  145. ACQUIRE_LOCK( &SrvShareLock );
  146. share = SrvFindShare( &nameOnlyString );
  147. if ( share == NULL ) {
  148. RELEASE_LOCK( &SrvShareLock );
  149. //
  150. // Perhaps the client is DFS aware. In this case, see if the DFS
  151. // driver can help us out.
  152. //
  153. if( ( (anyShareType == TRUE) || (shareType == ShareTypeDisk) ) &&
  154. SMB_CONTAINS_DFS_NAME( WorkContext )) {
  155. *Status = DfsFindShareName( &nameOnlyString );
  156. } else {
  157. *Status = STATUS_BAD_NETWORK_NAME;
  158. }
  159. IF_DEBUG(ERRORS) {
  160. SrvPrint1( "SrvVerifyShare: Unknown share name: %wZ\n",
  161. &nameOnlyString );
  162. }
  163. if ( !ShareNameIsUnicode ) {
  164. RtlFreeUnicodeString( &shareName );
  165. }
  166. return NULL;
  167. }
  168. #if SRVNTVERCHK
  169. //
  170. // If we are watching out for old client versions or bad domains, do not allow
  171. // it to connect to this share if it is a disk share
  172. //
  173. if( WorkContext->Connection &&
  174. (share->ShareType == ShareTypeDisk || SrvMinNT5ClientIPCToo) &&
  175. (WorkContext->Connection->PagedConnection->ClientTooOld ||
  176. (WorkContext->Session && WorkContext->Session->ClientBadDomain) )) {
  177. //
  178. // This client may not connect to this share!
  179. //
  180. RELEASE_LOCK( &SrvShareLock );
  181. if ( !ShareNameIsUnicode ) {
  182. RtlFreeUnicodeString( &shareName );
  183. }
  184. *Status = WorkContext->Connection->PagedConnection->ClientTooOld ?
  185. STATUS_REVISION_MISMATCH : STATUS_ACCOUNT_RESTRICTION;
  186. return NULL;
  187. }
  188. #endif
  189. //
  190. // If this is the null session, allow it to connect only to IPC$ or
  191. // to shares specified in the NullSessionShares list.
  192. //
  193. if ( IsNullSession &&
  194. SrvRestrictNullSessionAccess &&
  195. ( share->ShareType != ShareTypePipe ) ) {
  196. BOOLEAN matchFound = FALSE;
  197. ULONG i;
  198. ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
  199. for ( i = 0; SrvNullSessionShares[i] != NULL ; i++ ) {
  200. if ( _wcsicmp(
  201. SrvNullSessionShares[i],
  202. nameOnlyString.Buffer
  203. ) == 0 ) {
  204. matchFound = TRUE;
  205. break;
  206. }
  207. }
  208. RELEASE_LOCK( &SrvConfigurationLock );
  209. if ( !matchFound ) {
  210. RELEASE_LOCK( &SrvShareLock );
  211. IF_DEBUG(ERRORS) {
  212. SrvPrint0( "SrvVerifyShare: Illegal null session access.\n");
  213. }
  214. if ( !ShareNameIsUnicode ) {
  215. RtlFreeUnicodeString( &shareName );
  216. }
  217. *Status = STATUS_ACCESS_DENIED;
  218. return(NULL);
  219. }
  220. }
  221. if ( !ShareNameIsUnicode ) {
  222. RtlFreeUnicodeString( &shareName );
  223. }
  224. if ( anyShareType || (share->ShareType == shareType) ) {
  225. //
  226. // Put share in work context block and reference it.
  227. //
  228. SrvReferenceShare( share );
  229. RELEASE_LOCK( &SrvShareLock );
  230. WorkContext->Share = share;
  231. return share;
  232. } else {
  233. RELEASE_LOCK( &SrvShareLock );
  234. IF_DEBUG(ERRORS) {
  235. SrvPrint1( "SrvVerifyShare: incorrect share type: %s\n",
  236. ShareTypeString );
  237. }
  238. *Status = STATUS_BAD_DEVICE_TYPE;
  239. return NULL;
  240. }
  241. } // SrvVerifyShare
  242. PSHARE
  243. SrvFindShare (
  244. IN PUNICODE_STRING ShareName
  245. )
  246. /*++
  247. Routine Description:
  248. Attempts to find a share that matches a given name.
  249. *** This routine must be called with the share lock (SrvShareLock)
  250. held.
  251. Arguments:
  252. ShareName - name of share to Find.
  253. Return Value:
  254. A pointer to a share matching the given name, or NULL if none exists.
  255. --*/
  256. {
  257. PSHARE share;
  258. PLIST_ENTRY listEntryRoot, listEntry;
  259. ULONG hashValue;
  260. PAGED_CODE( );
  261. //
  262. // Try to match share name against available share names.
  263. //
  264. COMPUTE_STRING_HASH( ShareName, &hashValue );
  265. listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( hashValue ) ];
  266. for( listEntry = listEntryRoot->Flink;
  267. listEntry != listEntryRoot;
  268. listEntry = listEntry->Flink ) {
  269. share = CONTAINING_RECORD( listEntry, SHARE, GlobalShareList );
  270. if( share->ShareNameHashValue == hashValue &&
  271. RtlCompareUnicodeString(
  272. &share->ShareName,
  273. ShareName,
  274. TRUE
  275. ) == 0 ) {
  276. //
  277. // Found a matching share. If it is active return its
  278. // address.
  279. //
  280. if ( GET_BLOCK_STATE( share ) == BlockStateActive ) {
  281. return share;
  282. }
  283. }
  284. }
  285. //
  286. // Couldn't find a matching share that was active.
  287. //
  288. return NULL;
  289. } // SrvFindShare
  290. VOID
  291. SrvRemoveShare(
  292. PSHARE Share
  293. )
  294. {
  295. PAGED_CODE();
  296. RemoveEntryList( &Share->GlobalShareList );
  297. }
  298. VOID
  299. SrvAddShare(
  300. PSHARE Share
  301. )
  302. {
  303. PLIST_ENTRY listEntryRoot, listEntry;
  304. ULONG hashValue;
  305. PAGED_CODE();
  306. COMPUTE_STRING_HASH( &Share->ShareName, &hashValue );
  307. Share->ShareNameHashValue = hashValue;
  308. listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( hashValue ) ];
  309. InsertTailList( listEntryRoot, &Share->GlobalShareList );
  310. }
  311. NTSTATUS
  312. SrvShareEnumApiHandler (
  313. IN PSERVER_REQUEST_PACKET Srp,
  314. IN PVOID OutputBuffer,
  315. IN ULONG BufferLength,
  316. IN PENUM_FILTER_ROUTINE FilterRoutine,
  317. IN PENUM_SIZE_ROUTINE SizeRoutine,
  318. IN PENUM_FILL_ROUTINE FillRoutine
  319. )
  320. /*++
  321. Routine Description:
  322. All share Enum and GetInfo APIs are handled by this routine in the server
  323. FSD. It takes the ResumeHandle in the SRP to find the first
  324. appropriate share, then calls the passed-in filter routine to check
  325. if the share should be filled in. If it should, we call the filter
  326. routine, then try to get another shar. This continues until the
  327. entire list has been walked.
  328. Arguments:
  329. Srp - a pointer to the SRP for the operation.
  330. OutputBuffer - the buffer in which to fill output information.
  331. BufferLength - the length of the buffer.
  332. FilterRoutine - a pointer to a function that will check a share entry
  333. against information in the SRP to determine whether the
  334. information in the share should be placed in the output
  335. buffer.
  336. SizeRoutine - a pointer to a function that will find the total size
  337. a single share will take up in the output buffer. This routine
  338. is used to check whether we should bother to call the fill
  339. routine.
  340. FillRoutine - a pointer to a function that will fill in the output
  341. buffer with information from a share.
  342. Return Value:
  343. NTSTATUS - results of operation.
  344. --*/
  345. {
  346. PSHARE share;
  347. ULONG totalEntries;
  348. ULONG entriesRead;
  349. ULONG bytesRequired;
  350. PCHAR fixedStructurePointer;
  351. PCHAR variableData;
  352. ULONG blockSize;
  353. BOOLEAN bufferOverflow = FALSE;
  354. BOOLEAN entryReturned = FALSE;
  355. PLIST_ENTRY listEntryRoot, listEntry;
  356. ULONG oldSkipCount;
  357. ULONG newResumeKey;
  358. PAGED_CODE( );
  359. //
  360. // Set up local variables.
  361. //
  362. fixedStructurePointer = OutputBuffer;
  363. variableData = fixedStructurePointer + BufferLength;
  364. variableData = (PCHAR)((ULONG_PTR)variableData & ~1);
  365. entriesRead = 0;
  366. totalEntries = 0;
  367. bytesRequired = 0;
  368. listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( Srp->Parameters.Get.ResumeHandle >> 16 ) ];
  369. oldSkipCount = Srp->Parameters.Get.ResumeHandle & 0xff;
  370. ACQUIRE_LOCK_SHARED( &SrvShareLock );
  371. for( ;
  372. listEntryRoot < &SrvShareHashTable[ NSHARE_HASH_TABLE ];
  373. listEntryRoot++, newResumeKey = 0 ) {
  374. newResumeKey = (ULONG)((listEntryRoot - SrvShareHashTable) << 16);
  375. for( listEntry = listEntryRoot->Flink;
  376. listEntry != listEntryRoot;
  377. listEntry = listEntry->Flink, newResumeKey++ ) {
  378. if( oldSkipCount ) {
  379. --oldSkipCount;
  380. ++newResumeKey;
  381. continue;
  382. }
  383. share = CONTAINING_RECORD( listEntry, SHARE, GlobalShareList );
  384. //
  385. // Call the filter routine to determine whether we should
  386. // return this share.
  387. //
  388. if ( FilterRoutine( Srp, share ) ) {
  389. blockSize = SizeRoutine( Srp, share );
  390. totalEntries++;
  391. bytesRequired += blockSize;
  392. //
  393. // If all the information in the share will fit in the
  394. // output buffer, write it. Otherwise, indicate that there
  395. // was an overflow. As soon as an entry doesn't fit, stop
  396. // putting them in the buffer. This ensures that the resume
  397. // mechanism will work--retuning partial entries would make
  398. // it nearly impossible to use the resumability of the APIs,
  399. // since the caller would have to resume from an imcomplete
  400. // entry.
  401. //
  402. if ( (ULONG_PTR)fixedStructurePointer + blockSize <=
  403. (ULONG_PTR)variableData && !bufferOverflow ) {
  404. FillRoutine(
  405. Srp,
  406. share,
  407. (PVOID *)&fixedStructurePointer,
  408. (LPWSTR *)&variableData
  409. );
  410. entriesRead++;
  411. newResumeKey++;
  412. } else {
  413. bufferOverflow = TRUE;
  414. }
  415. }
  416. }
  417. }
  418. RELEASE_LOCK( &SrvShareLock );
  419. //
  420. // Set the information to pass back to the server service.
  421. //
  422. Srp->Parameters.Get.EntriesRead = entriesRead;
  423. Srp->Parameters.Get.TotalEntries = totalEntries;
  424. Srp->Parameters.Get.TotalBytesNeeded = bytesRequired;
  425. //
  426. // Return appropriate status.
  427. //
  428. if ( entriesRead == 0 && totalEntries > 0 ) {
  429. //
  430. // Not even a single entry fit.
  431. //
  432. Srp->ErrorCode = NERR_BufTooSmall;
  433. return STATUS_SUCCESS;
  434. } else if ( bufferOverflow ) {
  435. //
  436. // At least one entry fit, but not all of them.
  437. //
  438. Srp->ErrorCode = ERROR_MORE_DATA;
  439. Srp->Parameters.Get.ResumeHandle = newResumeKey;
  440. return STATUS_SUCCESS;
  441. } else {
  442. //
  443. // All entries fit.
  444. //
  445. Srp->ErrorCode = NO_ERROR;
  446. Srp->Parameters.Get.ResumeHandle = 0;
  447. return STATUS_SUCCESS;
  448. }
  449. } // SrvEnumApiHandler
  450. NTSTATUS
  451. SrvClearDfsOnShares()
  452. /*++
  453. Routine Description:
  454. Clears all DFS marks on all shares. This is called when the DFS service
  455. is stopping
  456. Arguments:
  457. None
  458. Return Value:
  459. NTSTATUS - results of operation.
  460. Notes:
  461. The SrvShareLock MUST be held across this operation
  462. --*/
  463. {
  464. PSHARE share;
  465. PLIST_ENTRY listEntryRoot, listEntry;
  466. ULONG iteration;
  467. PAGED_CODE( );
  468. for( iteration = 0; iteration < NSHARE_HASH_TABLE; iteration ++ )
  469. {
  470. listEntryRoot = &SrvShareHashTable[ iteration ];
  471. for( listEntry = listEntryRoot->Flink;
  472. listEntry != listEntryRoot;
  473. listEntry = listEntry->Flink ) {
  474. share = CONTAINING_RECORD( listEntry, SHARE, GlobalShareList );
  475. share->IsDfs = share->IsDfsRoot = FALSE;
  476. }
  477. }
  478. return STATUS_SUCCESS;
  479. }