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.

7146 lines
185 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. smbsupp.c
  5. Abstract:
  6. This module contains various support routines for processing SMBs.
  7. Author:
  8. Chuck Lenzmeier (chuckl) 9-Nov-1989
  9. David Treadwell (davidtr)
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include "smbsupp.tmh"
  14. #pragma hdrstop
  15. #define BugCheckFileId SRV_FILE_SMBSUPP
  16. #define CHAR_SP ' '
  17. //
  18. // Mapping is defined in inc\srvfsctl.h
  19. //
  20. STATIC GENERIC_MAPPING SrvFileAccessMapping = GENERIC_SHARE_FILE_ACCESS_MAPPING;
  21. //
  22. // Forward references
  23. //
  24. #ifdef ALLOC_PRAGMA
  25. #pragma alloc_text( PAGE, Srv8dot3ToUnicodeString )
  26. #pragma alloc_text( PAGE, SrvAllocateAndBuildPathName )
  27. #pragma alloc_text( PAGE, SrvCanonicalizePathName )
  28. #pragma alloc_text( PAGE, SrvCanonicalizePathNameWithReparse )
  29. #pragma alloc_text( PAGE, SrvCheckSearchAttributesForHandle )
  30. #pragma alloc_text( PAGE, SrvCheckSearchAttributes )
  31. #pragma alloc_text( PAGE, SrvGetAlertServiceName )
  32. #pragma alloc_text( PAGE, SrvGetBaseFileName )
  33. #pragma alloc_text( PAGE, SrvGetMultiSZList )
  34. #pragma alloc_text( PAGE, SrvGetOsVersionString )
  35. #pragma alloc_text( PAGE, SrvGetString )
  36. #pragma alloc_text( PAGE, SrvGetStringLength )
  37. #pragma alloc_text( PAGE, SrvGetSubdirectoryLength )
  38. #pragma alloc_text( PAGE, SrvIsLegalFatName )
  39. #pragma alloc_text( PAGE, SrvMakeUnicodeString )
  40. //#pragma alloc_text( PAGE, SrvReleaseContext )
  41. #pragma alloc_text( PAGE, SrvSetFileWritethroughMode )
  42. #pragma alloc_text( PAGE, SrvOemStringTo8dot3 )
  43. #pragma alloc_text( PAGE, SrvUnicodeStringTo8dot3 )
  44. #pragma alloc_text( PAGE, SrvVerifySid )
  45. #pragma alloc_text( PAGE, SrvVerifyTid )
  46. #pragma alloc_text( PAGE, SrvVerifyUid )
  47. #pragma alloc_text( PAGE, SrvVerifyUidAndTid )
  48. #pragma alloc_text( PAGE, SrvIoCreateFile )
  49. #pragma alloc_text( PAGE, SrvNtClose )
  50. #pragma alloc_text( PAGE, SrvVerifyDeviceStackSize )
  51. #pragma alloc_text( PAGE, SrvImpersonate )
  52. #pragma alloc_text( PAGE, SrvRevert )
  53. #pragma alloc_text( PAGE, SrvSetLastWriteTime )
  54. #pragma alloc_text( PAGE, SrvCheckShareFileAccess )
  55. #pragma alloc_text( PAGE, SrvReleaseShareRootHandle )
  56. #pragma alloc_text( PAGE, SrvUpdateVcQualityOfService )
  57. #pragma alloc_text( PAGE, SrvIsAllowedOnAdminShare )
  58. #pragma alloc_text( PAGE, SrvRetrieveMaximalAccessRightsForUser )
  59. #pragma alloc_text( PAGE, SrvRetrieveMaximalAccessRights )
  60. #pragma alloc_text( PAGE, SrvRetrieveMaximalShareAccessRights )
  61. #pragma alloc_text( PAGE, SrvUpdateMaximalAccessRightsInResponse )
  62. #pragma alloc_text( PAGE, SrvUpdateMaximalShareAccessRightsInResponse )
  63. //#pragma alloc_text( PAGE, SrvValidateSmb )
  64. #pragma alloc_text( PAGE, SrvWildcardRename )
  65. #pragma alloc_text( PAGE8FIL, SrvCheckForSavedError )
  66. #pragma alloc_text( PAGE, SrvIsDottedQuadAddress )
  67. #endif
  68. #if 0
  69. NOT PAGEABLE -- SrvUpdateStatistics2
  70. NOT PAGEABLE -- SrvVerifyFid2
  71. NOT PAGEABLE -- SrvVerifyFidForRawWrite
  72. NOT PAGEABLE -- SrvReceiveBufferShortage
  73. #endif
  74. VOID
  75. Srv8dot3ToUnicodeString (
  76. IN PSZ Input8dot3,
  77. OUT PUNICODE_STRING OutputString
  78. )
  79. /*++
  80. Routine Description:
  81. Convert FAT 8.3 format into a string.
  82. Arguments:
  83. Input8dot3 - Supplies the input 8.3 name to convert
  84. OutputString - Receives the converted name. The memory must be
  85. supplied by the caller.
  86. Return Value:
  87. None
  88. --*/
  89. {
  90. LONG i;
  91. CLONG lastOutputChar;
  92. UCHAR tempBuffer[8+1+3];
  93. OEM_STRING tempString;
  94. PAGED_CODE( );
  95. //
  96. // If we get "." or "..", just return them. They do not follow
  97. // the usual rules for FAT names.
  98. //
  99. lastOutputChar = 0;
  100. if ( Input8dot3[0] == '.' && Input8dot3[1] == '\0' ) {
  101. tempBuffer[0] = '.';
  102. lastOutputChar = 0;
  103. } else if ( Input8dot3[0] == '.' && Input8dot3[1] == '.' &&
  104. Input8dot3[2] == '\0' ) {
  105. tempBuffer[0] = '.';
  106. tempBuffer[1] = '.';
  107. lastOutputChar = 1;
  108. } else {
  109. //
  110. // Copy over the 8 part of the 8.3 name into the output buffer,
  111. // then back up the index to the first non-space character,
  112. // searching backwards.
  113. //
  114. RtlCopyMemory( tempBuffer, Input8dot3, 8 );
  115. for ( i = 7;
  116. (i >= 0) && (tempBuffer[i] == CHAR_SP);
  117. i -- ) {
  118. ;
  119. }
  120. //
  121. // Add a dot.
  122. //
  123. i++;
  124. tempBuffer[i] = '.';
  125. //
  126. // Copy over the 3 part of the 8.3 name into the output buffer,
  127. // then back up the index to the first non-space character,
  128. // searching backwards.
  129. //
  130. lastOutputChar = i;
  131. for ( i = 8; i < 11; i++ ) {
  132. //
  133. // Copy the byte.
  134. //
  135. // *** This code used to mask off the top bit. This was a
  136. // legacy of very ancient times when the bit may have
  137. // been used as a resume key sequence bit.
  138. //
  139. tempBuffer[++lastOutputChar] = (UCHAR)Input8dot3[i];
  140. }
  141. while ( tempBuffer[lastOutputChar] == CHAR_SP ) {
  142. lastOutputChar--;
  143. }
  144. //
  145. // If the last character is a '.', then we don't have an
  146. // extension, so back up before the dot.
  147. //
  148. if ( tempBuffer[lastOutputChar] == '.') {
  149. lastOutputChar--;
  150. }
  151. }
  152. //
  153. // Convert to Unicode.
  154. //
  155. tempString.Length = (SHORT)(lastOutputChar + 1);
  156. tempString.Buffer = tempBuffer;
  157. OutputString->MaximumLength =
  158. (SHORT)((lastOutputChar + 2) * sizeof(WCHAR));
  159. RtlOemStringToUnicodeString( OutputString, &tempString, FALSE );
  160. return;
  161. } // Srv8dot3ToUnicodeString
  162. VOID
  163. SrvAllocateAndBuildPathName(
  164. IN PUNICODE_STRING Path1,
  165. IN PUNICODE_STRING Path2 OPTIONAL,
  166. IN PUNICODE_STRING Path3 OPTIONAL,
  167. OUT PUNICODE_STRING BuiltPath
  168. )
  169. /*++
  170. Routine Description:
  171. Allocates space and concatenates the paths in the parameter strings.
  172. ALLOCATE_HEAP is used to allocate the memory for the full
  173. pathname. Directory separator characters ('\') are added as
  174. necessary so that a legitimate path is built. If the third
  175. parameter is NULL, then only the first two strings are concatenated,
  176. and if both the second and third parameters are NULL then the first
  177. string is simply copied over to a new location.
  178. Arguments:
  179. Input8dot3 - Supplies the input 8.3 name to convert
  180. OutputString - Receives the converted name, the memory must be supplied
  181. by the caller.
  182. Return Value:
  183. None
  184. --*/
  185. {
  186. UNICODE_STRING path2;
  187. UNICODE_STRING path3;
  188. PWCH nextLocation;
  189. PWSTR pathBuffer;
  190. ULONG allocationLength;
  191. WCHAR nullString = 0;
  192. PAGED_CODE( );
  193. //
  194. // Set up the strings for optional parameters Path2 and Path3. Doing
  195. // this allows later code to be ignorant of whether the strings were
  196. // actually passed.
  197. //
  198. if ( ARGUMENT_PRESENT(Path2) ) {
  199. path2.Buffer = Path2->Buffer;
  200. path2.Length = Path2->Length;
  201. } else {
  202. path2.Buffer = &nullString;
  203. path2.Length = 0;
  204. }
  205. if ( ARGUMENT_PRESENT(Path3) ) {
  206. path3.Buffer = Path3->Buffer;
  207. path3.Length = Path3->Length;
  208. } else {
  209. path3.Buffer = &nullString;
  210. path3.Length = 0;
  211. }
  212. //
  213. // Allocate space in which to put the path name we are building.
  214. // The +3 if to account for as many as two directory separator
  215. // characters being added and the zero terminator at the end. This
  216. // has a small cost in terms of memory usage, but it simplifies this
  217. // code.
  218. //
  219. // The calling routine must be careful to deallocate this space
  220. // when it is done with the path name.
  221. //
  222. allocationLength = Path1->Length + path2.Length + path3.Length +
  223. 3 * sizeof(WCHAR);
  224. pathBuffer = ALLOCATE_HEAP_COLD(
  225. allocationLength,
  226. BlockTypeDataBuffer
  227. );
  228. if ( pathBuffer == NULL ) {
  229. INTERNAL_ERROR(
  230. ERROR_LEVEL_EXPECTED,
  231. "SrvAllocateAndBuildPathName: Unable to allocate %d bytes "
  232. "from heap.",
  233. allocationLength,
  234. NULL
  235. );
  236. BuiltPath->Buffer = NULL;
  237. return;
  238. }
  239. BuiltPath->Buffer = pathBuffer;
  240. BuiltPath->MaximumLength = (USHORT)allocationLength;
  241. ASSERT ( ( allocationLength & 0xffff0000 ) == 0 );
  242. RtlZeroMemory( pathBuffer, allocationLength );
  243. //
  244. // Copy the first path name to the space we have allocated.
  245. //
  246. RtlCopyMemory( pathBuffer, Path1->Buffer, Path1->Length );
  247. nextLocation = (PWCH)((PCHAR)pathBuffer + Path1->Length);
  248. //
  249. // If there was no separator character at the end of the first path
  250. // or at the beginning of the next path, put one in. We don't
  251. // want to put in leading slashes, however, so don't put one in
  252. // if it would be the first character. Also, we don't want to insert
  253. // a slash if a relative stream is being opened (i.e. name begins with ':')
  254. //
  255. if ( nextLocation > pathBuffer &&
  256. *(nextLocation - 1) != DIRECTORY_SEPARATOR_CHAR &&
  257. *path2.Buffer != DIRECTORY_SEPARATOR_CHAR &&
  258. *path2.Buffer != RELATIVE_STREAM_INITIAL_CHAR ) {
  259. *nextLocation++ = DIRECTORY_SEPARATOR_CHAR;
  260. }
  261. //
  262. // Concatenate the second path name with the first.
  263. //
  264. RtlCopyMemory( nextLocation, path2.Buffer, path2.Length );
  265. nextLocation = (PWCH)((PCHAR)nextLocation + path2.Length);
  266. //
  267. // If there was no separator character at the end of the first path
  268. // or at the beginning of the next path, put one in. Again, don't
  269. // put in leading slashes, and watch out for relative stream opens.
  270. //
  271. if ( nextLocation > pathBuffer &&
  272. *(nextLocation - 1) != DIRECTORY_SEPARATOR_CHAR &&
  273. *path3.Buffer != DIRECTORY_SEPARATOR_CHAR &&
  274. *path3.Buffer != RELATIVE_STREAM_INITIAL_CHAR ) {
  275. *nextLocation++ = DIRECTORY_SEPARATOR_CHAR;
  276. }
  277. //
  278. // Concatenate the third path name.
  279. //
  280. RtlCopyMemory( nextLocation, path3.Buffer, path3.Length );
  281. nextLocation = (PWCH)((PCHAR)nextLocation + path3.Length);
  282. //
  283. // The path cannot end in a '\', so if there was one at the end get
  284. // rid of it.
  285. //
  286. if ( nextLocation > pathBuffer &&
  287. *(nextLocation - 1) == DIRECTORY_SEPARATOR_CHAR ) {
  288. *(--nextLocation) = '\0';
  289. }
  290. //
  291. // Find the length of the path we built.
  292. //
  293. BuiltPath->Length = (SHORT)((PCHAR)nextLocation - (PCHAR)pathBuffer);
  294. return;
  295. } // SrvAllocateAndBuildPathName
  296. NTSTATUS
  297. SrvCanonicalizePathName(
  298. IN PWORK_CONTEXT WorkContext,
  299. IN PSHARE Share OPTIONAL,
  300. IN PUNICODE_STRING RelatedPath OPTIONAL,
  301. IN OUT PVOID Name,
  302. IN PCHAR LastValidLocation,
  303. IN BOOLEAN RemoveTrailingDots,
  304. IN BOOLEAN SourceIsUnicode,
  305. OUT PUNICODE_STRING String
  306. )
  307. /*++
  308. Routine Description:
  309. This routine canonicalizes a filename. All ".\" are removed, and
  310. "..\" are evaluated to go up a directory level. A check is also
  311. made to ensure that the pathname does not go to a directory above
  312. the share root directory (i.e., no leading "..\"). Trailing blanks
  313. are always removed, as are trailing dots if RemoveTrailingDots is
  314. TRUE.
  315. If the input string is not Unicode, a Unicode representation of the
  316. input is obtained. This requires that additional space be
  317. allocated, and it is the caller's responsibility to free this space.
  318. If the input string IS Unicode, this routine will align the input
  319. pointer (Name) to the next two-byte boundary before performing the
  320. canonicalization. All Unicode strings in SMBs must be aligned
  321. properly.
  322. This routine operates "in place," meaning that it puts the
  323. canonicalized pathname in the same storage as the uncanonicalized
  324. pathname. This is useful for operating on the Buffer fields of the
  325. request SMBs--simply call this routine and it will fix the pathname.
  326. However, the calling routine must be careful if there are two
  327. pathnames stored in the buffer field--the second won't necessarily
  328. start in the space just after the first '\0'.
  329. The LastValidLocation parameter is used to determine the maximum
  330. possible length of the name. This prevents an access violation if
  331. the client fails to include a zero terminator, or for strings (such
  332. as the file name in NT Create And X) that are not required to be
  333. zero terminated.
  334. If the SMB described by WorkContext is marked as containing Dfs names,
  335. this routine will additionally call the Dfs driver to translate the
  336. Dfs name to a path relative to the Share. Since this call to the Dfs
  337. driver is NOT idempotent, the SMB flag indicating that it contains a
  338. Dfs name is CLEARED after a call to this routine. This posses a problem
  339. for the few SMBs that contain multiple names. The handlers for those
  340. SMBs must make sure that they conditionally call the SMB_MARK_AS_DFS_NAME
  341. macro before calling this routine.
  342. Arguments:
  343. WorkContext - contains information about the negotiated dialect. This
  344. is used for deciding whether to strip trailing spaces and dots.
  345. Share - a pointer to the share entry
  346. Name - a pointer to the filename to canonicalize.
  347. LastValidLocation - a pointer to the last valid location in the
  348. buffer pointed to by Name.
  349. RemoveTrailingDots - if TRUE, trailing dots are removed. Otherwise,
  350. they are left in (this supports special behavior needed by
  351. directory search logic).
  352. SourceIsUnicode - if TRUE, the input is canonicalized in place.
  353. If FALSE, the input is first converted to Unicode, then
  354. canonicalized.
  355. String - a pointer to string descriptor.
  356. Return Value:
  357. BOOLEAN - FALSE if the name was invalid or if storage for the
  358. Unicode string could not be obtained.
  359. --*/
  360. {
  361. PWCH source, destination, lastComponent, name;
  362. BOOLEAN notNtClient;
  363. NTSTATUS status = STATUS_SUCCESS;
  364. DWORD numberOfPathElements = 0;
  365. PAGED_CODE( );
  366. #if DBG
  367. return SrvCanonicalizePathNameWithReparse( WorkContext, Share, RelatedPath, Name, LastValidLocation, RemoveTrailingDots, SourceIsUnicode, String );
  368. #else
  369. if( SMB_IS_UNICODE( WorkContext ) &&
  370. FlagOn( WorkContext->RequestHeader->Flags2, SMB_FLAGS2_REPARSE_PATH ) )
  371. {
  372. return SrvCanonicalizePathNameWithReparse( WorkContext, Share, RelatedPath, Name, LastValidLocation, RemoveTrailingDots, SourceIsUnicode, String );
  373. }
  374. #endif
  375. notNtClient = !IS_NT_DIALECT( WorkContext->Connection->SmbDialect );
  376. if ( SourceIsUnicode ) {
  377. //
  378. // The source string is already Unicode. Align the pointer.
  379. // Save the character at the last location in the buffer, then
  380. // set that location to zero. This prevents any loops from
  381. // going past the end of the buffer.
  382. //
  383. name = ALIGN_SMB_WSTR(Name);
  384. String->Buffer = name;
  385. } else {
  386. OEM_STRING oemString;
  387. PCHAR p;
  388. ULONG length;
  389. //
  390. // The source string is not Unicode. Determine the length of
  391. // the string by finding the zero terminator or the end of the
  392. // input buffer. We need the length in order to convert the
  393. // string to Unicode, and we can't just call RtlInitString, in
  394. // case the string isn't terminated.
  395. //
  396. for ( p = Name, length = 0;
  397. p <= LastValidLocation && *p != 0;
  398. p++, length++ ) {
  399. ;
  400. }
  401. //
  402. // Convert the source string to Unicode.
  403. //
  404. oemString.Buffer = Name;
  405. oemString.Length = (USHORT)length;
  406. oemString.MaximumLength = (USHORT)length;
  407. status = RtlOemStringToUnicodeString(
  408. String,
  409. &oemString,
  410. TRUE
  411. );
  412. if( !NT_SUCCESS( status ) ) {
  413. return status;
  414. }
  415. name = (PWCH)String->Buffer;
  416. LastValidLocation = (PCHAR)String->Buffer + String->Length;
  417. }
  418. //
  419. // Though everything is done in place, separate source and
  420. // destination pointers are maintained. It is necessary that source
  421. // >= destination at all times to avoid writing into space we
  422. // haven't looked at yet. The three main operations performed by
  423. // this routine ( ".\", "..\", and getting rid of trailing "." and "
  424. // ") do not interfere with this goal.
  425. //
  426. destination = name;
  427. source = name;
  428. //
  429. // The lastComponent variable is used as a placeholder when
  430. // backtracking over trailing blanks and dots. It points to the
  431. // first character after the last directory separator or the
  432. // beginning of the pathname.
  433. //
  434. lastComponent = destination;
  435. //
  436. // Get rid of leading directory separators.
  437. //
  438. while ( source <= (PWCH)LastValidLocation &&
  439. (*source == UNICODE_DIR_SEPARATOR_CHAR) && (*source != L'\0') ) {
  440. source++;
  441. }
  442. //
  443. // Walk through the pathname until we reach the zero terminator. At
  444. // the start of this loop, source points to the first charaecter
  445. // after a directory separator or the first character of the
  446. // pathname.
  447. //
  448. while ( (source <= (PWCH)LastValidLocation) && (*source != L'\0') ) {
  449. if ( *source == L'.' ) {
  450. //
  451. // If we see a dot, look at the next character.
  452. //
  453. if ( notNtClient &&
  454. ((source+1) <= (PWCH)LastValidLocation) &&
  455. (*(source+1) == UNICODE_DIR_SEPARATOR_CHAR) ) {
  456. //
  457. // If the next character is a directory separator,
  458. // advance the source pointer to the directory
  459. // separator.
  460. //
  461. source += 1;
  462. } else if ( ((source+1) <= (PWCH)LastValidLocation) &&
  463. (*(source+1) == L'.') &&
  464. ((source+1) == (PWCH)LastValidLocation ||
  465. IS_UNICODE_PATH_SEPARATOR( *(source+2) ))) {
  466. //
  467. // If the following characters are ".\", we have a "..\".
  468. // Advance the source pointer to the "\".
  469. //
  470. source += 2;
  471. //
  472. // Move the destination pointer to the charecter before the
  473. // last directory separator in order to prepare for backing
  474. // up. This may move the pointer before the beginning of
  475. // the name pointer.
  476. //
  477. destination -= 2;
  478. //
  479. // If destination points before the beginning of the name
  480. // pointer, fail because the user is attempting to go
  481. // to a higher directory than the share root. This is
  482. // the equivalent of a leading "..\", but may result from
  483. // a case like "dir\..\..\file".
  484. //
  485. if ( destination <= name ) {
  486. if ( !SourceIsUnicode ) {
  487. RtlFreeUnicodeString( String );
  488. String->Buffer = NULL;
  489. }
  490. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  491. }
  492. //
  493. // Back up the destination pointer to after the last
  494. // directory separator or to the beginning of the pathname.
  495. // Backup to the beginning of the pathname will occur
  496. // in a case like "dir\..\file".
  497. //
  498. while ( destination >= name &&
  499. *destination != UNICODE_DIR_SEPARATOR_CHAR ) {
  500. destination--;
  501. }
  502. //
  503. // destination points to \ or character before name; we
  504. // want it to point to character after last \.
  505. //
  506. destination++;
  507. } else {
  508. //
  509. // The characters after the dot are not "\" or ".\", so
  510. // so just copy source to destination until we reach a
  511. // directory separator character. This will occur in
  512. // a case like ".file" (filename starts with a dot).
  513. //
  514. do {
  515. *destination++ = *source++;
  516. } while ( (source <= (PWCH)LastValidLocation) &&
  517. !IS_UNICODE_PATH_SEPARATOR( *source ) );
  518. numberOfPathElements++;
  519. }
  520. } else { // if ( *source == L'.' )
  521. //
  522. // source does not point to a dot, so copy source to
  523. // destination until we get to a directory separator.
  524. //
  525. while ( (source <= (PWCH)LastValidLocation) &&
  526. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  527. *destination++ = *source++;
  528. }
  529. numberOfPathElements++;
  530. }
  531. //
  532. // Truncate trailing dots and blanks. destination should point
  533. // to the last character before the directory separator, so back
  534. // up over blanks and dots.
  535. //
  536. if ( notNtClient ) {
  537. while ( ( destination > lastComponent ) &&
  538. ( (RemoveTrailingDots && *(destination-1) == '.')
  539. || *(destination-1) == ' ' ) ) {
  540. destination--;
  541. }
  542. }
  543. //
  544. // At this point, source points to a directory separator or to
  545. // a zero terminator. If it is a directory separator, put one
  546. // in the destination.
  547. //
  548. if ( (source <= (PWCH)LastValidLocation) &&
  549. (*source == UNICODE_DIR_SEPARATOR_CHAR) ) {
  550. //
  551. // If we haven't put the directory separator in the path name,
  552. // put it in.
  553. //
  554. if ( destination != name &&
  555. *(destination-1) != UNICODE_DIR_SEPARATOR_CHAR ) {
  556. *destination++ = UNICODE_DIR_SEPARATOR_CHAR;
  557. }
  558. //
  559. // It is legal to have multiple directory separators, so get
  560. // rid of them here. Example: "dir\\\\\\\\file".
  561. //
  562. do {
  563. source++;
  564. } while ( (source <= (PWCH)LastValidLocation) &&
  565. (*source == UNICODE_DIR_SEPARATOR_CHAR) );
  566. //
  567. // Make lastComponent point to the character after the directory
  568. // separator.
  569. //
  570. lastComponent = destination;
  571. }
  572. }
  573. //
  574. // We're just about done. If there was a trailing .. (example:
  575. // "file\.."), trailing . ("file\."), or multiple trailing
  576. // separators ("file\\\\"), then back up one since separators are
  577. // illegal at the end of a pathname.
  578. //
  579. if ( destination > name &&
  580. *(destination-1) == UNICODE_DIR_SEPARATOR_CHAR ) {
  581. destination--;
  582. }
  583. *destination = L'\0';
  584. //
  585. // The length of the destination string is the difference between the
  586. // destination pointer (points to zero terminator at this point)
  587. // and the name pointer (points to the beginning of the destination
  588. // string).
  589. //
  590. String->Length = (SHORT)((PCHAR)destination - (PCHAR)name);
  591. String->MaximumLength = String->Length;
  592. //
  593. // One final thing: Is this SMB referring to a DFS name? If so, ask
  594. // the DFS driver to turn it into a local name.
  595. //
  596. if( ARGUMENT_PRESENT( Share ) &&
  597. Share->IsDfs &&
  598. SMB_CONTAINS_DFS_NAME( WorkContext )) {
  599. BOOLEAN stripLastComponent = FALSE;
  600. //
  601. // We have to special case some SMBs (like TRANS2_FIND_FIRST2)
  602. // because they contain path Dfs path names that could refer to a
  603. // junction point. The SMB handlers for these SMBs are not interested
  604. // in a STATUS_PATH_NOT_COVERED error; instead they want the name
  605. // to be resolved to the the junction point.
  606. //
  607. if (WorkContext->NextCommand == SMB_COM_TRANSACTION2 ) {
  608. PTRANSACTION transaction;
  609. USHORT command;
  610. transaction = WorkContext->Parameters.Transaction;
  611. command = SmbGetUshort( &transaction->InSetup[0] );
  612. if (command == TRANS2_FIND_FIRST2 && numberOfPathElements > 2 )
  613. stripLastComponent = TRUE;
  614. }
  615. status =
  616. DfsNormalizeName(Share, RelatedPath, stripLastComponent, String);
  617. SMB_MARK_AS_DFS_TRANSLATED( WorkContext );
  618. if( !NT_SUCCESS( status ) ) {
  619. if ( !SourceIsUnicode ) {
  620. RtlFreeUnicodeString( String );
  621. String->Buffer = NULL;
  622. }
  623. }
  624. }
  625. return status;
  626. } // SrvCanonicalizePathName
  627. NTSTATUS
  628. SrvCanonicalizePathNameWithReparse(
  629. IN PWORK_CONTEXT WorkContext,
  630. IN PSHARE Share OPTIONAL,
  631. IN PUNICODE_STRING RelatedPath OPTIONAL,
  632. IN OUT PVOID Name,
  633. IN PCHAR LastValidLocation,
  634. IN BOOLEAN RemoveTrailingDots,
  635. IN BOOLEAN SourceIsUnicode,
  636. OUT PUNICODE_STRING String
  637. )
  638. /*++
  639. Routine Description:
  640. This routine is identical to the one above with the exception that it
  641. checks the path for reparse-able names (such as snapshot references) and
  642. handles them accordingly. This allows us to present new features within
  643. the Win32 namespace so old applications can use them.
  644. Arguments:
  645. WorkContext - contains information about the negotiated dialect. This
  646. is used for deciding whether to strip trailing spaces and dots.
  647. Share - a pointer to the share entry
  648. Name - a pointer to the filename to canonicalize.
  649. LastValidLocation - a pointer to the last valid location in the
  650. buffer pointed to by Name.
  651. RemoveTrailingDots - if TRUE, trailing dots are removed. Otherwise,
  652. they are left in (this supports special behavior needed by
  653. directory search logic).
  654. SourceIsUnicode - if TRUE, the input is canonicalized in place.
  655. If FALSE, the input is first converted to Unicode, then
  656. canonicalized.
  657. String - a pointer to string descriptor.
  658. Return Value:
  659. BOOLEAN - FALSE if the name was invalid or if storage for the
  660. Unicode string could not be obtained.
  661. --*/
  662. {
  663. PWCH source, destination, lastComponent, name;
  664. BOOLEAN notNtClient;
  665. NTSTATUS status = STATUS_SUCCESS;
  666. DWORD numberOfPathElements = 0;
  667. PAGED_CODE( );
  668. notNtClient = !IS_NT_DIALECT( WorkContext->Connection->SmbDialect );
  669. if ( SourceIsUnicode ) {
  670. //
  671. // The source string is already Unicode. Align the pointer.
  672. // Save the character at the last location in the buffer, then
  673. // set that location to zero. This prevents any loops from
  674. // going past the end of the buffer.
  675. //
  676. name = ALIGN_SMB_WSTR(Name);
  677. String->Buffer = name;
  678. } else {
  679. OEM_STRING oemString;
  680. PCHAR p;
  681. ULONG length;
  682. //
  683. // The source string is not Unicode. Determine the length of
  684. // the string by finding the zero terminator or the end of the
  685. // input buffer. We need the length in order to convert the
  686. // string to Unicode, and we can't just call RtlInitString, in
  687. // case the string isn't terminated.
  688. //
  689. for ( p = Name, length = 0;
  690. p <= LastValidLocation && *p != 0;
  691. p++, length++ ) {
  692. ;
  693. }
  694. //
  695. // Convert the source string to Unicode.
  696. //
  697. oemString.Buffer = Name;
  698. oemString.Length = (USHORT)length;
  699. oemString.MaximumLength = (USHORT)length;
  700. status = RtlOemStringToUnicodeString(
  701. String,
  702. &oemString,
  703. TRUE
  704. );
  705. if( !NT_SUCCESS( status ) ) {
  706. return status;
  707. }
  708. name = (PWCH)String->Buffer;
  709. LastValidLocation = (PCHAR)String->Buffer + String->Length;
  710. }
  711. //
  712. // Though everything is done in place, separate source and
  713. // destination pointers are maintained. It is necessary that source
  714. // >= destination at all times to avoid writing into space we
  715. // haven't looked at yet. The three main operations performed by
  716. // this routine ( ".\", "..\", and getting rid of trailing "." and "
  717. // ") do not interfere with this goal.
  718. //
  719. destination = name;
  720. source = name;
  721. //
  722. // The lastComponent variable is used as a placeholder when
  723. // backtracking over trailing blanks and dots. It points to the
  724. // first character after the last directory separator or the
  725. // beginning of the pathname.
  726. //
  727. lastComponent = destination;
  728. //
  729. // Get rid of leading directory separators.
  730. //
  731. while ( source <= (PWCH)LastValidLocation &&
  732. (*source == UNICODE_DIR_SEPARATOR_CHAR) && (*source != L'\0') ) {
  733. source++;
  734. }
  735. //
  736. // Walk through the pathname until we reach the zero terminator. At
  737. // the start of this loop, source points to the first charaecter
  738. // after a directory separator or the first character of the
  739. // pathname.
  740. //
  741. while ( (source <= (PWCH)LastValidLocation) && (*source != L'\0') ) {
  742. if ( *source == L'.' ) {
  743. //
  744. // If we see a dot, look at the next character.
  745. //
  746. if ( notNtClient &&
  747. ((source+1) <= (PWCH)LastValidLocation) &&
  748. (*(source+1) == UNICODE_DIR_SEPARATOR_CHAR) ) {
  749. //
  750. // If the next character is a directory separator,
  751. // advance the source pointer to the directory
  752. // separator.
  753. //
  754. source += 1;
  755. } else if ( ((source+1) <= (PWCH)LastValidLocation) &&
  756. (*(source+1) == L'.') &&
  757. ((source+1) == (PWCH)LastValidLocation ||
  758. IS_UNICODE_PATH_SEPARATOR( *(source+2) ))) {
  759. //
  760. // If the following characters are ".\", we have a "..\".
  761. // Advance the source pointer to the "\".
  762. //
  763. source += 2;
  764. //
  765. // Move the destination pointer to the charecter before the
  766. // last directory separator in order to prepare for backing
  767. // up. This may move the pointer before the beginning of
  768. // the name pointer.
  769. //
  770. destination -= 2;
  771. //
  772. // If destination points before the beginning of the name
  773. // pointer, fail because the user is attempting to go
  774. // to a higher directory than the share root. This is
  775. // the equivalent of a leading "..\", but may result from
  776. // a case like "dir\..\..\file".
  777. //
  778. if ( destination <= name ) {
  779. if ( !SourceIsUnicode ) {
  780. RtlFreeUnicodeString( String );
  781. String->Buffer = NULL;
  782. }
  783. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  784. }
  785. //
  786. // Back up the destination pointer to after the last
  787. // directory separator or to the beginning of the pathname.
  788. // Backup to the beginning of the pathname will occur
  789. // in a case like "dir\..\file".
  790. //
  791. while ( destination >= name &&
  792. *destination != UNICODE_DIR_SEPARATOR_CHAR ) {
  793. destination--;
  794. }
  795. //
  796. // destination points to \ or character before name; we
  797. // want it to point to character after last \.
  798. //
  799. destination++;
  800. } else {
  801. //
  802. // The characters after the dot are not "\" or ".\", so
  803. // so just copy source to destination until we reach a
  804. // directory separator character. This will occur in
  805. // a case like ".file" (filename starts with a dot).
  806. //
  807. do {
  808. *destination++ = *source++;
  809. } while ( (source <= (PWCH)LastValidLocation) &&
  810. !IS_UNICODE_PATH_SEPARATOR( *source ) );
  811. numberOfPathElements++;
  812. }
  813. } else { // if ( *source == L'.' )
  814. // Try to parse out a snap token
  815. if( SrvSnapParseToken( source, &WorkContext->SnapShotTime ) )
  816. {
  817. while ( (source <= (PWCH)LastValidLocation) &&
  818. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  819. source++;
  820. }
  821. #if DBG
  822. if( !(WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_REPARSE_PATH) )
  823. {
  824. DbgPrint( "Found token but REPARSE not set!\n" );
  825. DbgBreakPoint();
  826. }
  827. #endif
  828. }
  829. else
  830. {
  831. while ( (source <= (PWCH)LastValidLocation) &&
  832. !IS_UNICODE_PATH_SEPARATOR( *source ) ) {
  833. *destination++ = *source++;
  834. }
  835. }
  836. numberOfPathElements++;
  837. }
  838. //
  839. // Truncate trailing dots and blanks. destination should point
  840. // to the last character before the directory separator, so back
  841. // up over blanks and dots.
  842. //
  843. if ( notNtClient ) {
  844. while ( ( destination > lastComponent ) &&
  845. ( (RemoveTrailingDots && *(destination-1) == '.')
  846. || *(destination-1) == ' ' ) ) {
  847. destination--;
  848. }
  849. }
  850. //
  851. // At this point, source points to a directory separator or to
  852. // a zero terminator. If it is a directory separator, put one
  853. // in the destination.
  854. //
  855. if ( (source <= (PWCH)LastValidLocation) &&
  856. (*source == UNICODE_DIR_SEPARATOR_CHAR) ) {
  857. //
  858. // If we haven't put the directory separator in the path name,
  859. // put it in.
  860. //
  861. if ( destination != name &&
  862. *(destination-1) != UNICODE_DIR_SEPARATOR_CHAR ) {
  863. *destination++ = UNICODE_DIR_SEPARATOR_CHAR;
  864. }
  865. //
  866. // It is legal to have multiple directory separators, so get
  867. // rid of them here. Example: "dir\\\\\\\\file".
  868. //
  869. do {
  870. source++;
  871. } while ( (source <= (PWCH)LastValidLocation) &&
  872. (*source == UNICODE_DIR_SEPARATOR_CHAR) );
  873. //
  874. // Make lastComponent point to the character after the directory
  875. // separator.
  876. //
  877. lastComponent = destination;
  878. }
  879. }
  880. //
  881. // We're just about done. If there was a trailing .. (example:
  882. // "file\.."), trailing . ("file\."), or multiple trailing
  883. // separators ("file\\\\"), then back up one since separators are
  884. // illegal at the end of a pathname.
  885. //
  886. if ( destination > name &&
  887. *(destination-1) == UNICODE_DIR_SEPARATOR_CHAR ) {
  888. destination--;
  889. }
  890. *destination = L'\0';
  891. //
  892. // The length of the destination string is the difference between the
  893. // destination pointer (points to zero terminator at this point)
  894. // and the name pointer (points to the beginning of the destination
  895. // string).
  896. //
  897. String->Length = (SHORT)((PCHAR)destination - (PCHAR)name);
  898. String->MaximumLength = String->Length;
  899. //
  900. // One final thing: Is this SMB referring to a DFS name? If so, ask
  901. // the DFS driver to turn it into a local name.
  902. //
  903. if( ARGUMENT_PRESENT( Share ) &&
  904. Share->IsDfs &&
  905. SMB_CONTAINS_DFS_NAME( WorkContext )) {
  906. BOOLEAN stripLastComponent = FALSE;
  907. //
  908. // We have to special case some SMBs (like TRANS2_FIND_FIRST2)
  909. // because they contain path Dfs path names that could refer to a
  910. // junction point. The SMB handlers for these SMBs are not interested
  911. // in a STATUS_PATH_NOT_COVERED error; instead they want the name
  912. // to be resolved to the the junction point.
  913. //
  914. if (WorkContext->NextCommand == SMB_COM_TRANSACTION2 ) {
  915. PTRANSACTION transaction;
  916. USHORT command;
  917. transaction = WorkContext->Parameters.Transaction;
  918. command = SmbGetUshort( &transaction->InSetup[0] );
  919. if (command == TRANS2_FIND_FIRST2 && numberOfPathElements > 2 )
  920. stripLastComponent = TRUE;
  921. }
  922. status =
  923. DfsNormalizeName(Share, RelatedPath, stripLastComponent, String);
  924. SMB_MARK_AS_DFS_TRANSLATED( WorkContext );
  925. if( !NT_SUCCESS( status ) ) {
  926. if ( !SourceIsUnicode ) {
  927. RtlFreeUnicodeString( String );
  928. String->Buffer = NULL;
  929. }
  930. }
  931. }
  932. #if DBG
  933. if( (WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_REPARSE_PATH) &&
  934. (WorkContext->SnapShotTime.QuadPart == 0) )
  935. {
  936. DbgPrint( "Token not found but REPARSE set!\n" );
  937. DbgBreakPoint();
  938. }
  939. #endif
  940. return status;
  941. } // SrvCanonicalizePathNameWithReparse
  942. NTSTATUS
  943. SrvCheckForSavedError(
  944. IN PWORK_CONTEXT WorkContext,
  945. IN PRFCB Rfcb
  946. )
  947. /*++
  948. Routine Description:
  949. This routine checks to see if there was a saved error.
  950. Arguments:
  951. WorkContext - Pointer to the workcontext block which will be
  952. marked with the error.
  953. Rfcb - Pointer to the rfcb which contains the saved error status.
  954. Return Value:
  955. status of SavedErrorCode.
  956. --*/
  957. {
  958. NTSTATUS savedErrorStatus;
  959. KIRQL oldIrql;
  960. UNLOCKABLE_CODE( 8FIL );
  961. //
  962. // Acquire the spin lock and see if the saved error
  963. // is still there.
  964. //
  965. ACQUIRE_SPIN_LOCK( &Rfcb->Connection->SpinLock, &oldIrql );
  966. savedErrorStatus = Rfcb->SavedError;
  967. if ( !NT_SUCCESS( savedErrorStatus ) ) {
  968. //
  969. // There was a write behind error. Fail this operation
  970. // with the write error.
  971. //
  972. Rfcb->SavedError = STATUS_SUCCESS;
  973. RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, oldIrql );
  974. IF_DEBUG(ERRORS) {
  975. KdPrint(( "SrvCheckForSavedError: Returning write"
  976. "behind error %X\n", savedErrorStatus ));
  977. }
  978. SrvSetSmbError( WorkContext, savedErrorStatus );
  979. } else {
  980. RELEASE_SPIN_LOCK( &Rfcb->Connection->SpinLock, oldIrql );
  981. }
  982. return savedErrorStatus;
  983. } // SrvCheckForSavedError
  984. NTSTATUS SRVFASTCALL
  985. SrvCheckSearchAttributes(
  986. IN USHORT FileAttributes,
  987. IN USHORT SmbSearchAttributes
  988. )
  989. /*++
  990. Routine Description:
  991. Determines whether the FileAttributes has
  992. attributes not specified in SmbSearchAttributes. Only the system
  993. and hidden bits are examined.
  994. Arguments:
  995. FileAttributes - The attributes in question
  996. SmbSearchAttributes - the search attributes passed in an SMB.
  997. Return Value:
  998. STATUS_NO_SUCH_FILE if the attributes do not jive, or STATUS_SUCCESS if
  999. the search attributes encompass the attributes on the file.
  1000. --*/
  1001. {
  1002. PAGED_CODE( );
  1003. //
  1004. // If the search attributes has both the system and hidden bits set,
  1005. // then the file must be OK.
  1006. //
  1007. if ( (SmbSearchAttributes & FILE_ATTRIBUTE_SYSTEM) != 0 &&
  1008. (SmbSearchAttributes & FILE_ATTRIBUTE_HIDDEN) != 0 ) {
  1009. return STATUS_SUCCESS;
  1010. }
  1011. //
  1012. // Mask out everything but the system and hidden bits--they're all
  1013. // we care about.
  1014. //
  1015. FileAttributes &= (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
  1016. //
  1017. // If a bit is set in fileAttributes that was not set in the search
  1018. // attributes, then their bitwise OR will have a bit set that is
  1019. // not set in search attributes.
  1020. //
  1021. if ( (SmbSearchAttributes | FileAttributes) != SmbSearchAttributes ) {
  1022. return STATUS_NO_SUCH_FILE;
  1023. }
  1024. return STATUS_SUCCESS;
  1025. } // SrvCheckSearchAttributes
  1026. NTSTATUS
  1027. SrvCheckSearchAttributesForHandle(
  1028. IN HANDLE FileHandle,
  1029. IN USHORT SmbSearchAttributes
  1030. )
  1031. /*++
  1032. Routine Description:
  1033. Determines whether the file corresponding to FileHandle has
  1034. attributes not specified in SmbSearchAttributes. Only the system
  1035. and hidden bits are examined.
  1036. Arguments:
  1037. FileHandle - handle to the file; must have FILE_READ_ATTRIBUTES access.
  1038. SmbSearchAttributes - the search attributes passed in an SMB.
  1039. Return Value:
  1040. STATUS_NO_SUCH_FILE if the attributes do not jive, some other status
  1041. code if the NtQueryInformationFile fails, or STATUS_SUCCESS if
  1042. the search attributes encompass the attributes on the file.
  1043. --*/
  1044. {
  1045. NTSTATUS status;
  1046. FILE_BASIC_INFORMATION fileBasicInformation;
  1047. PAGED_CODE( );
  1048. //
  1049. // If the search attributes has both the system and hidden bits set,
  1050. // then the file must be OK.
  1051. //
  1052. if ( (SmbSearchAttributes & FILE_ATTRIBUTE_SYSTEM) != 0 &&
  1053. (SmbSearchAttributes & FILE_ATTRIBUTE_HIDDEN) != 0 ) {
  1054. return STATUS_SUCCESS;
  1055. }
  1056. //
  1057. // Get the attributes on the file.
  1058. //
  1059. status = SrvQueryBasicAndStandardInformation(
  1060. FileHandle,
  1061. NULL,
  1062. &fileBasicInformation,
  1063. NULL
  1064. );
  1065. if ( !NT_SUCCESS(status) ) {
  1066. INTERNAL_ERROR(
  1067. ERROR_LEVEL_UNEXPECTED,
  1068. "SrvCheckSearchAttributesForHandle: NtQueryInformationFile (basic "
  1069. "information) returned %X",
  1070. status,
  1071. NULL
  1072. );
  1073. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  1074. return status;
  1075. }
  1076. return SrvCheckSearchAttributes( (USHORT)fileBasicInformation.FileAttributes,
  1077. SmbSearchAttributes );
  1078. } // SrvCheckSearchAttributesForHandle
  1079. VOID
  1080. SrvGetAlertServiceName(
  1081. VOID
  1082. )
  1083. /*++
  1084. Routine Description:
  1085. This routine gets the server display string from the registry.
  1086. Arguments:
  1087. None.
  1088. Return Value:
  1089. none.
  1090. --*/
  1091. {
  1092. UNICODE_STRING unicodeKeyName;
  1093. UNICODE_STRING unicodeRegPath;
  1094. OBJECT_ATTRIBUTES objAttributes;
  1095. HANDLE keyHandle;
  1096. ULONG lengthNeeded;
  1097. NTSTATUS status;
  1098. PWCHAR displayString;
  1099. PWCHAR newString;
  1100. PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
  1101. PAGED_CODE( );
  1102. RtlInitUnicodeString( &unicodeRegPath, StrRegServerPath );
  1103. RtlInitUnicodeString( &unicodeKeyName, StrRegSrvDisplayName );
  1104. InitializeObjectAttributes(
  1105. &objAttributes,
  1106. &unicodeRegPath,
  1107. OBJ_CASE_INSENSITIVE,
  1108. NULL,
  1109. NULL
  1110. );
  1111. status = ZwOpenKey(
  1112. &keyHandle,
  1113. KEY_QUERY_VALUE,
  1114. &objAttributes
  1115. );
  1116. if ( !NT_SUCCESS(status) ) {
  1117. goto use_default;
  1118. }
  1119. status = ZwQueryValueKey(
  1120. keyHandle,
  1121. &unicodeKeyName,
  1122. KeyValueFullInformation,
  1123. NULL,
  1124. 0,
  1125. &lengthNeeded
  1126. );
  1127. if ( status != STATUS_BUFFER_TOO_SMALL ) {
  1128. NtClose( keyHandle );
  1129. goto use_default;
  1130. }
  1131. infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1132. if ( infoBuffer == NULL ) {
  1133. NtClose( keyHandle );
  1134. goto use_default;
  1135. }
  1136. status = ZwQueryValueKey(
  1137. keyHandle,
  1138. &unicodeKeyName,
  1139. KeyValueFullInformation,
  1140. infoBuffer,
  1141. lengthNeeded,
  1142. &lengthNeeded
  1143. );
  1144. NtClose( keyHandle );
  1145. if ( !NT_SUCCESS(status) ) {
  1146. goto use_default;
  1147. }
  1148. //
  1149. // If it's empty, use the default.
  1150. //
  1151. lengthNeeded = infoBuffer->DataLength;
  1152. if ( lengthNeeded <= sizeof(WCHAR) ) {
  1153. goto use_default;
  1154. }
  1155. //
  1156. // Get the display string. If this is the same as the default,
  1157. // exit.
  1158. //
  1159. displayString = (PWCHAR)((PCHAR)infoBuffer + infoBuffer->DataOffset);
  1160. if ( wcscmp( displayString, StrDefaultSrvDisplayName ) == 0 ) {
  1161. goto use_default;
  1162. }
  1163. //
  1164. // allocate memory for the new display string
  1165. //
  1166. newString = (PWCHAR)ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1167. if ( newString == NULL ) {
  1168. goto use_default;
  1169. }
  1170. RtlCopyMemory(
  1171. newString,
  1172. displayString,
  1173. lengthNeeded
  1174. );
  1175. SrvAlertServiceName = newString;
  1176. FREE_HEAP( infoBuffer );
  1177. return;
  1178. use_default:
  1179. if ( infoBuffer != NULL ) {
  1180. FREE_HEAP( infoBuffer );
  1181. }
  1182. SrvAlertServiceName = StrDefaultSrvDisplayName;
  1183. return;
  1184. } // SrvGetAlertServiceName
  1185. VOID
  1186. SrvGetBaseFileName (
  1187. IN PUNICODE_STRING InputName,
  1188. OUT PUNICODE_STRING OutputName
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. This routine finds the part of a path name that is only a file
  1193. name. For example, with a\b\c\filename, it sets the buffer
  1194. field of OutputName to point to "filename" and the length to 8.
  1195. *** This routine should be used AFTER SrvCanonicalizePathName has
  1196. been used on the path name to ensure that the name is good and
  1197. '..' have been removed.
  1198. Arguments:
  1199. InputName - Supplies a pointer to the pathname string.
  1200. OutputName - a pointer to where the base name information should
  1201. be written.
  1202. Return Value:
  1203. None.
  1204. --*/
  1205. {
  1206. PWCH ep = &InputName->Buffer[ InputName->Length / sizeof(WCHAR) ];
  1207. PWCH baseFileName = ep - 1;
  1208. PAGED_CODE( );
  1209. for( ; baseFileName > InputName->Buffer; --baseFileName ) {
  1210. if( *baseFileName == DIRECTORY_SEPARATOR_CHAR ) {
  1211. OutputName->Buffer = baseFileName + 1;
  1212. OutputName->Length = PTR_DIFF_SHORT(ep, OutputName->Buffer);
  1213. OutputName->MaximumLength = OutputName->Length;
  1214. return;
  1215. }
  1216. }
  1217. *OutputName = *InputName;
  1218. return;
  1219. } // SrvGetBaseFileName
  1220. VOID
  1221. SrvGetMultiSZList(
  1222. PWSTR **ListPointer,
  1223. PWSTR BaseKeyName,
  1224. PWSTR ParameterKeyName,
  1225. PWSTR *DefaultList
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. This routine queries a registry value key for its MULTI_SZ values.
  1230. Arguments:
  1231. ListPointer - Pointer to receive the pointer to the null session pipes.
  1232. ParameterKeyValue - Name of the value parameter to query.
  1233. DefaultList - Value to assign to the list pointer in case
  1234. something goes wrong.
  1235. Return Value:
  1236. none.
  1237. --*/
  1238. {
  1239. UNICODE_STRING unicodeKeyName;
  1240. UNICODE_STRING unicodeParamPath;
  1241. OBJECT_ATTRIBUTES objAttributes;
  1242. HANDLE keyHandle;
  1243. ULONG lengthNeeded;
  1244. ULONG i;
  1245. ULONG numberOfEntries;
  1246. ULONG numberOfDefaultEntries = 0;
  1247. NTSTATUS status;
  1248. PWCHAR regEntry;
  1249. PWCHAR dataEntry;
  1250. PWSTR *ptrEntry;
  1251. PCHAR newBuffer;
  1252. PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
  1253. PAGED_CODE( );
  1254. RtlInitUnicodeString( &unicodeParamPath, BaseKeyName );
  1255. RtlInitUnicodeString( &unicodeKeyName, ParameterKeyName );
  1256. InitializeObjectAttributes(
  1257. &objAttributes,
  1258. &unicodeParamPath,
  1259. OBJ_CASE_INSENSITIVE,
  1260. NULL,
  1261. NULL
  1262. );
  1263. status = ZwOpenKey(
  1264. &keyHandle,
  1265. KEY_QUERY_VALUE,
  1266. &objAttributes
  1267. );
  1268. if ( !NT_SUCCESS(status) ) {
  1269. goto use_default;
  1270. }
  1271. status = ZwQueryValueKey(
  1272. keyHandle,
  1273. &unicodeKeyName,
  1274. KeyValueFullInformation,
  1275. NULL,
  1276. 0,
  1277. &lengthNeeded
  1278. );
  1279. if ( status != STATUS_BUFFER_TOO_SMALL ) {
  1280. NtClose( keyHandle );
  1281. goto use_default;
  1282. }
  1283. infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1284. if ( infoBuffer == NULL ) {
  1285. NtClose( keyHandle );
  1286. goto use_default;
  1287. }
  1288. status = ZwQueryValueKey(
  1289. keyHandle,
  1290. &unicodeKeyName,
  1291. KeyValueFullInformation,
  1292. infoBuffer,
  1293. lengthNeeded,
  1294. &lengthNeeded
  1295. );
  1296. NtClose( keyHandle );
  1297. if ( !NT_SUCCESS(status) ) {
  1298. goto use_default;
  1299. }
  1300. //
  1301. // Figure out how many entries there are.
  1302. //
  1303. // numberOfEntries should be total number of entries + 1. The extra
  1304. // one is for the NULL sentinel entry.
  1305. //
  1306. lengthNeeded = infoBuffer->DataLength;
  1307. if ( lengthNeeded <= sizeof(WCHAR) ) {
  1308. //
  1309. // No entries on the list. Use default.
  1310. //
  1311. goto use_default;
  1312. }
  1313. dataEntry = (PWCHAR)((PCHAR)infoBuffer + infoBuffer->DataOffset);
  1314. for ( i = 0, regEntry = dataEntry, numberOfEntries = 0;
  1315. i < lengthNeeded;
  1316. i += sizeof(WCHAR) ) {
  1317. if ( *regEntry++ == L'\0' ) {
  1318. numberOfEntries++;
  1319. }
  1320. }
  1321. //
  1322. // Add the number of entries in the default list.
  1323. //
  1324. if ( DefaultList != NULL ) {
  1325. for ( i = 0; DefaultList[i] != NULL ; i++ ) {
  1326. numberOfDefaultEntries++;
  1327. }
  1328. }
  1329. //
  1330. // Allocate space needed for the array of pointers. This is in addition
  1331. // to the ones in the default list.
  1332. //
  1333. newBuffer = ALLOCATE_HEAP_COLD(
  1334. lengthNeeded +
  1335. (numberOfDefaultEntries + numberOfEntries + 1) *
  1336. sizeof( PWSTR ),
  1337. BlockTypeDataBuffer
  1338. );
  1339. if ( newBuffer == NULL ) {
  1340. goto use_default;
  1341. }
  1342. //
  1343. // Copy the names
  1344. //
  1345. regEntry = (PWCHAR)(newBuffer +
  1346. (numberOfDefaultEntries + numberOfEntries + 1) * sizeof(PWSTR));
  1347. RtlCopyMemory(
  1348. regEntry,
  1349. dataEntry,
  1350. lengthNeeded
  1351. );
  1352. //
  1353. // Free the info buffer
  1354. //
  1355. FREE_HEAP( infoBuffer );
  1356. //
  1357. // Copy the pointers in the default list.
  1358. //
  1359. ptrEntry = (PWSTR *) newBuffer;
  1360. for ( i = 0; i < numberOfDefaultEntries ; i++ ) {
  1361. *ptrEntry++ = DefaultList[i];
  1362. }
  1363. //
  1364. // Build the array of pointers. If numberOfEntries is 1, then
  1365. // it means that the list is empty.
  1366. //
  1367. if ( numberOfEntries > 1 ) {
  1368. *ptrEntry++ = regEntry++;
  1369. //
  1370. // Skip the first WCHAR and the last 2 NULL terminators.
  1371. //
  1372. for ( i = 3*sizeof(WCHAR) ; i < lengthNeeded ; i += sizeof(WCHAR) ) {
  1373. if ( *regEntry++ == L'\0' ) {
  1374. *ptrEntry++ = regEntry;
  1375. }
  1376. }
  1377. }
  1378. *ptrEntry = NULL;
  1379. *ListPointer = (PWSTR *)newBuffer;
  1380. return;
  1381. use_default:
  1382. if ( infoBuffer != NULL ) {
  1383. FREE_HEAP( infoBuffer );
  1384. }
  1385. *ListPointer = DefaultList;
  1386. return;
  1387. } // SrvGetMultiSZList
  1388. VOID
  1389. SrvGetOsVersionString(
  1390. VOID
  1391. )
  1392. {
  1393. ULONG lengthNeeded;
  1394. NTSTATUS status;
  1395. HANDLE keyHandle;
  1396. UNICODE_STRING unicodeParamPath;
  1397. UNICODE_STRING unicodeKeyName;
  1398. OBJECT_ATTRIBUTES objAttributes;
  1399. PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
  1400. PAGED_CODE();
  1401. //
  1402. // Read the version number string
  1403. //
  1404. RtlInitUnicodeString( &unicodeParamPath, StrRegOsVersionPath );
  1405. RtlInitUnicodeString( &unicodeKeyName, StrRegVersionKeyName );
  1406. InitializeObjectAttributes(
  1407. &objAttributes,
  1408. &unicodeParamPath,
  1409. OBJ_CASE_INSENSITIVE,
  1410. NULL,
  1411. NULL
  1412. );
  1413. status = ZwOpenKey(
  1414. &keyHandle,
  1415. KEY_QUERY_VALUE,
  1416. &objAttributes
  1417. );
  1418. if ( !NT_SUCCESS(status) ) {
  1419. goto use_default;
  1420. }
  1421. status = ZwQueryValueKey(
  1422. keyHandle,
  1423. &unicodeKeyName,
  1424. KeyValueFullInformation,
  1425. NULL,
  1426. 0,
  1427. &lengthNeeded
  1428. );
  1429. if ( status != STATUS_BUFFER_TOO_SMALL ) {
  1430. NtClose( keyHandle );
  1431. goto use_default;
  1432. }
  1433. infoBuffer = ALLOCATE_HEAP_COLD( lengthNeeded, BlockTypeDataBuffer );
  1434. if ( infoBuffer == NULL ) {
  1435. NtClose( keyHandle );
  1436. goto use_default;
  1437. }
  1438. status = ZwQueryValueKey(
  1439. keyHandle,
  1440. &unicodeKeyName,
  1441. KeyValueFullInformation,
  1442. infoBuffer,
  1443. lengthNeeded,
  1444. &lengthNeeded
  1445. );
  1446. NtClose( keyHandle );
  1447. //
  1448. // Construct the version (oem and unicode) strings.
  1449. //
  1450. if ( infoBuffer->DataLength != 0 ) {
  1451. ULONG stringLength;
  1452. stringLength = infoBuffer->DataLength +
  1453. STRLEN(StrNativeOsPrefix) * sizeof(WCHAR);
  1454. SrvNativeOS.MaximumLength = (USHORT)stringLength;
  1455. SrvNativeOS.Length = SrvNativeOS.MaximumLength - sizeof(WCHAR);
  1456. SrvNativeOS.Buffer =
  1457. ALLOCATE_HEAP_COLD( SrvNativeOS.MaximumLength, BlockTypeDataBuffer );
  1458. if ( SrvNativeOS.Buffer == NULL) {
  1459. goto use_default;
  1460. }
  1461. RtlCopyMemory(
  1462. SrvNativeOS.Buffer,
  1463. StrNativeOsPrefix,
  1464. STRLEN(StrNativeOsPrefix) * sizeof(WCHAR)
  1465. );
  1466. RtlCopyMemory(
  1467. SrvNativeOS.Buffer + STRLEN(StrNativeOsPrefix),
  1468. (PCHAR)infoBuffer+infoBuffer->DataOffset,
  1469. infoBuffer->DataLength
  1470. );
  1471. FREE_HEAP( infoBuffer );
  1472. infoBuffer = NULL;
  1473. //
  1474. // Now get the oem version
  1475. //
  1476. status = RtlUnicodeStringToOemString(
  1477. &SrvOemNativeOS,
  1478. &SrvNativeOS,
  1479. TRUE
  1480. );
  1481. if ( !NT_SUCCESS(status) ) {
  1482. FREE_HEAP( SrvNativeOS.Buffer );
  1483. goto use_default;
  1484. }
  1485. return;
  1486. }
  1487. use_default:
  1488. if ( infoBuffer != NULL ) {
  1489. FREE_HEAP( infoBuffer );
  1490. }
  1491. RtlInitUnicodeString( &SrvNativeOS, StrDefaultNativeOs );
  1492. RtlInitAnsiString( (PANSI_STRING)&SrvOemNativeOS, StrDefaultNativeOsOem );
  1493. return;
  1494. } // SrvGetOsVersionString
  1495. USHORT
  1496. SrvGetString (
  1497. IN OUT PUNICODE_STRING Destination,
  1498. IN PVOID Source,
  1499. IN PVOID EndOfSourceBuffer,
  1500. IN BOOLEAN SourceIsUnicode
  1501. )
  1502. /*++
  1503. Routine Description:
  1504. Reads a string out of an SMB buffer, and converts it to Unicode,
  1505. if necessary. This function is similar to SrvMakeUnicodeString,
  1506. except
  1507. (1) It always copies data from source to destination
  1508. (2) It assumes storage for destination has been preallocated. Its length
  1509. is Destination->MaximumLength
  1510. Arguments:
  1511. Destination - the resultant Unicode string.
  1512. Source - a zero-terminated input.
  1513. EndOfSourceBuffer - A pointer to the end of the SMB buffer. Used to
  1514. protect the server from accessing beyond the end of the SMB buffer,
  1515. if the format is invalid.
  1516. SourceIsUnicode - TRUE if the source is already Unicode.
  1517. Return Value:
  1518. Length - Length of input buffer (included the NUL terminator).
  1519. -1 if EndOfSourceBuffer is reached before the NUL terminator.
  1520. --*/
  1521. {
  1522. USHORT length;
  1523. PAGED_CODE( );
  1524. if ( SourceIsUnicode ) {
  1525. PWCH currentChar = Source;
  1526. ASSERT( ((ULONG_PTR)Source & 1) == 0 );
  1527. length = 0;
  1528. while ( currentChar < (PWCH)EndOfSourceBuffer &&
  1529. *currentChar != UNICODE_NULL ) {
  1530. currentChar++;
  1531. length += sizeof( WCHAR );
  1532. }
  1533. //
  1534. // If we hit the end of the SMB buffer without finding a NUL, this
  1535. // is a bad string. Return an error.
  1536. //
  1537. if ( currentChar >= (PWCH)EndOfSourceBuffer ) {
  1538. return (USHORT)-1;
  1539. }
  1540. //
  1541. // If we overran our storage buffer, this is a bad string. Return an error
  1542. //
  1543. if( length + sizeof( UNICODE_NULL ) > Destination->MaximumLength ) {
  1544. return (USHORT)-1;
  1545. }
  1546. //
  1547. // Copy the unicode data to the destination, including the NULL. Set length
  1548. // of Destination to the non-null string length.
  1549. //
  1550. Destination->Length = length;
  1551. //
  1552. // We didn't change this to RtlCopyMemory because it is possible that
  1553. // the source and destination can overlap. Copy the NULL as well
  1554. //
  1555. RtlMoveMemory( Destination->Buffer, Source, length );
  1556. } else {
  1557. PCHAR currentChar = Source;
  1558. OEM_STRING sourceString;
  1559. length = 0;
  1560. while ( currentChar <= (PCHAR)EndOfSourceBuffer &&
  1561. *currentChar != '\0' ) {
  1562. currentChar++;
  1563. length++;
  1564. }
  1565. //
  1566. // If we hit the end of the SMB buffer without finding a NUL, this
  1567. // is a bad string. Return an error.
  1568. //
  1569. if ( currentChar > (PCHAR)EndOfSourceBuffer ) {
  1570. return (USHORT)-1;
  1571. }
  1572. //
  1573. // If we overran our storage buffer, this is a bad string. Return an error
  1574. //
  1575. if( (USHORT)(length + 1)*sizeof(WCHAR) > Destination->MaximumLength ) {
  1576. return (USHORT)-1;
  1577. }
  1578. sourceString.Buffer = Source;
  1579. sourceString.Length = length;
  1580. //
  1581. // Convert the data to unicode.
  1582. //
  1583. Destination->Length = 0;
  1584. RtlOemStringToUnicodeString( Destination, &sourceString, FALSE );
  1585. //
  1586. // Increment 'length', to indicate that the NUL has been copied.
  1587. //
  1588. length++;
  1589. }
  1590. //
  1591. // Return the number of bytes copied from the source buffer.
  1592. //
  1593. return length;
  1594. } // SrvGetString
  1595. USHORT
  1596. SrvGetStringLength (
  1597. IN PVOID Source,
  1598. IN PVOID EndOfSourceBuffer,
  1599. IN BOOLEAN SourceIsUnicode,
  1600. IN BOOLEAN IncludeNullTerminator
  1601. )
  1602. /*++
  1603. Routine Description:
  1604. This routine returns the length of a string in an SMB buffer in bytes.
  1605. If the end of the buffer is encountered before the NUL terminator,
  1606. the function returns -1 as the length.
  1607. Arguments:
  1608. Source - a NUL-terminated input.
  1609. EndOfSourceBuffer - A pointer to the end of the SMB buffer. Used to
  1610. protect the server from accessing beyond the end of the SMB buffer,
  1611. if the format is invalid.
  1612. SourceIsUnicode - TRUE if the source is already Unicode.
  1613. IncludeNullTerminator - TRUE if the Length to be returned includes the
  1614. null terminator.
  1615. Return Value:
  1616. Length - Length of input buffer. -1 if EndOfSourceBuffer is reached
  1617. before the NUL terminator.
  1618. --*/
  1619. {
  1620. USHORT length;
  1621. PAGED_CODE( );
  1622. if ( IncludeNullTerminator) {
  1623. length = 1;
  1624. } else {
  1625. length = 0;
  1626. }
  1627. if ( SourceIsUnicode ) {
  1628. PWCH currentChar = (PWCH)Source;
  1629. ASSERT( ((ULONG_PTR)currentChar & 1) == 0 );
  1630. while ( currentChar < (PWCH)EndOfSourceBuffer &&
  1631. *currentChar != UNICODE_NULL ) {
  1632. currentChar++;
  1633. length++;
  1634. }
  1635. //
  1636. // If we hit the end of the SMB buffer without finding a NUL, this
  1637. // is a bad string. Return an error.
  1638. //
  1639. if ( currentChar >= (PWCH)EndOfSourceBuffer ) {
  1640. length = (USHORT)-1;
  1641. } else {
  1642. length = (USHORT)(length * sizeof(WCHAR));
  1643. }
  1644. } else {
  1645. PCHAR currentChar = Source;
  1646. while ( currentChar <= (PCHAR)EndOfSourceBuffer &&
  1647. *currentChar != '\0' ) {
  1648. currentChar++;
  1649. length++;
  1650. }
  1651. //
  1652. // If we hit the end of the SMB buffer without finding a NUL, this
  1653. // is a bad string. Return an error.
  1654. //
  1655. if ( currentChar > (PCHAR)EndOfSourceBuffer ) {
  1656. length = (USHORT)-1;
  1657. }
  1658. }
  1659. //
  1660. // Return the length of the string.
  1661. //
  1662. return length;
  1663. } // SrvGetStringLength
  1664. USHORT
  1665. SrvGetSubdirectoryLength (
  1666. IN PUNICODE_STRING InputName
  1667. )
  1668. /*++
  1669. Routine Description:
  1670. This routine finds the length of "subdirectory" information in a
  1671. path name, that is, the parts of the path name that are not the
  1672. actual name of the file. For example, for a\b\c\filename, it
  1673. returns 5. This allows the calling routine to open the directory
  1674. containing the file or get a full pathname to a file after a search.
  1675. *** This routine should be used AFTER SrvCanonicalizePathName has
  1676. been used on the path name to ensure that the name is good and
  1677. '..' have been removed.
  1678. Arguments:
  1679. InputName - Supplies a pointer to the pathname string.
  1680. Return Value:
  1681. The number of bytes that contain directory information. If the
  1682. input is just a filename, then 0 is returned.
  1683. --*/
  1684. {
  1685. ULONG i;
  1686. PWCH baseFileName = InputName->Buffer;
  1687. PAGED_CODE( );
  1688. for ( i = 0; i < InputName->Length / sizeof(WCHAR); i++ ) {
  1689. //
  1690. // If s points to a directory separator, set fileBaseName to
  1691. // the character after the separator.
  1692. //
  1693. if ( InputName->Buffer[i] == DIRECTORY_SEPARATOR_CHAR ) {
  1694. baseFileName = &InputName->Buffer[i];
  1695. }
  1696. }
  1697. return (USHORT)((baseFileName - InputName->Buffer) * sizeof(WCHAR));
  1698. } // SrvGetSubdirectoryLength
  1699. BOOLEAN SRVFASTCALL
  1700. SrvIsLegalFatName (
  1701. IN PWSTR InputName,
  1702. IN CLONG InputNameLength
  1703. )
  1704. /*++
  1705. Routine Description:
  1706. Determines whether a file name would be legal for FAT. This
  1707. is needed for SrvQueryDirectoryFile because it must filter names
  1708. for clients that do not know about long or non-FAT filenames.
  1709. Arguments:
  1710. InputName - Supplies the string to test
  1711. InputNameLength - Length of the string (excluding the NULL termination)
  1712. to test.
  1713. Return Value:
  1714. TRUE if the name is a legal FAT name, FALSE if the name would be
  1715. rejected by FAT.
  1716. --*/
  1717. {
  1718. UNICODE_STRING original_name;
  1719. UNICODE_STRING upcase_name;
  1720. WCHAR upcase_buffer[ 13 ];
  1721. STRING oem_string;
  1722. CHAR oem_buffer[ 13 ];
  1723. UNICODE_STRING converted_name;
  1724. WCHAR converted_name_buffer[ 13 ];
  1725. BOOLEAN spacesInName, nameValid8Dot3;
  1726. PAGED_CODE();
  1727. //
  1728. // Special case . and .. -- they are legal FAT names
  1729. //
  1730. if( InputName[0] == L'.' ) {
  1731. if( InputNameLength == sizeof(WCHAR) ||
  1732. ((InputNameLength == 2*sizeof(WCHAR)) && InputName[1] == L'.')) {
  1733. return TRUE;
  1734. }
  1735. return FALSE;
  1736. }
  1737. original_name.Buffer = InputName;
  1738. original_name.Length = original_name.MaximumLength = (USHORT)InputNameLength;
  1739. nameValid8Dot3 = RtlIsNameLegalDOS8Dot3( &original_name, NULL, &spacesInName );
  1740. if( !nameValid8Dot3 || spacesInName ) {
  1741. return FALSE;
  1742. }
  1743. if( SrvFilterExtendedCharsInPath == FALSE ) {
  1744. //
  1745. // One final test -- we must be able to convert this name to OEM and back again
  1746. // without any loss of information.
  1747. //
  1748. oem_string.Buffer = oem_buffer;
  1749. upcase_name.Buffer = upcase_buffer;
  1750. converted_name.Buffer = converted_name_buffer;
  1751. oem_string.MaximumLength = sizeof( oem_buffer );
  1752. upcase_name.MaximumLength = sizeof( upcase_buffer );
  1753. converted_name.MaximumLength = sizeof( converted_name_buffer );
  1754. oem_string.Length = 0;
  1755. upcase_name.Length = 0;
  1756. converted_name.Length = 0;
  1757. nameValid8Dot3 = NT_SUCCESS( RtlUpcaseUnicodeString( &upcase_name, &original_name, FALSE )) &&
  1758. NT_SUCCESS( RtlUnicodeStringToOemString( &oem_string, &upcase_name, FALSE )) &&
  1759. FsRtlIsFatDbcsLegal( oem_string, FALSE, FALSE, FALSE ) &&
  1760. NT_SUCCESS( RtlOemStringToUnicodeString( &converted_name, &oem_string, FALSE )) &&
  1761. RtlEqualUnicodeString( &upcase_name, &converted_name, FALSE );
  1762. }
  1763. return nameValid8Dot3;
  1764. } // SrvIsLegalFatName
  1765. NTSTATUS
  1766. SrvMakeUnicodeString (
  1767. IN BOOLEAN SourceIsUnicode,
  1768. OUT PUNICODE_STRING Destination,
  1769. IN PVOID Source,
  1770. IN PUSHORT SourceLength OPTIONAL
  1771. )
  1772. /*++
  1773. Routine Description:
  1774. Makes a unicode string from a zero-terminated input that is either
  1775. ANSI or Unicode.
  1776. Arguments:
  1777. SourceIsUnicode - TRUE if the source is already Unicode. If FALSE,
  1778. RtlOemStringToUnicodeString will allocate space to hold the
  1779. Unicode string; it is the responsibility of the caller to
  1780. free this space.
  1781. Destination - the resultant Unicode string.
  1782. Source - a zero-terminated input.
  1783. Return Value:
  1784. NTSTATUS - result of operation.
  1785. --*/
  1786. {
  1787. OEM_STRING oemString;
  1788. PAGED_CODE( );
  1789. if ( SourceIsUnicode ) {
  1790. ASSERT( ((ULONG_PTR)Source & 1) == 0 );
  1791. if ( ARGUMENT_PRESENT( SourceLength ) ) {
  1792. ASSERT( (*SourceLength) != (USHORT) -1 );
  1793. Destination->Buffer = Source;
  1794. Destination->Length = *SourceLength;
  1795. Destination->MaximumLength = *SourceLength;
  1796. } else {
  1797. RtlInitUnicodeString( Destination, Source );
  1798. }
  1799. return STATUS_SUCCESS;
  1800. }
  1801. if ( ARGUMENT_PRESENT( SourceLength ) ) {
  1802. oemString.Buffer = Source;
  1803. oemString.Length = *SourceLength;
  1804. oemString.MaximumLength = *SourceLength;
  1805. } else {
  1806. RtlInitString( &oemString, Source );
  1807. }
  1808. return RtlOemStringToUnicodeString(
  1809. Destination,
  1810. &oemString,
  1811. TRUE
  1812. );
  1813. } // SrvMakeUnicodeString
  1814. VOID
  1815. SrvReleaseContext (
  1816. IN PWORK_CONTEXT WorkContext
  1817. )
  1818. /*++
  1819. Routine Description:
  1820. This function releases (dereferences) control blocks referenced by a
  1821. Work Context block. It is called when processing of an incoming SMB
  1822. is complete, just before the response SMB (if any) is set.
  1823. The following control blocks are dereferenced: Share, Session,
  1824. TreeConnect, and File. If any of these fields is nonzero in
  1825. WorkContext, the block is dereferenced and the fields is zeroed.
  1826. Note that the Connection block and the Endpoint block are NOT
  1827. dereferenced. This is based on the assumption that a response is
  1828. about to be sent, so the connection must stay referenced. The
  1829. Connection block is dereferenced after the send of the response (if
  1830. any) when SrvRequeueReceiveIrp is called. That function also
  1831. releases the response buffer, if it is different from the request
  1832. buffer.
  1833. Arguments:
  1834. WorkContext - Supplies a pointer to a work context block.
  1835. Return Value:
  1836. None.
  1837. --*/
  1838. {
  1839. PAGED_CODE( );
  1840. //
  1841. // !!! If you change the way this routine works (e.g., you add
  1842. // another block that needs to be dereferenced), make sure you
  1843. // check fsd.c\SrvFsdRestartSmbComplete to see if it needs to be
  1844. // changed too.
  1845. //
  1846. //
  1847. // Dereference the Share block, if any.
  1848. //
  1849. if ( WorkContext->Share != NULL ) {
  1850. SrvDereferenceShare( WorkContext->Share );
  1851. WorkContext->Share = NULL;
  1852. }
  1853. //
  1854. // Dereference the Session block, if any.
  1855. //
  1856. if ( WorkContext->Session != NULL ) {
  1857. SrvDereferenceSession( WorkContext->Session );
  1858. WorkContext->Session= NULL;
  1859. }
  1860. //
  1861. // Dereference the Tree Connect block, if any.
  1862. //
  1863. if ( WorkContext->TreeConnect != NULL ) {
  1864. SrvDereferenceTreeConnect( WorkContext->TreeConnect );
  1865. WorkContext->TreeConnect = NULL;
  1866. }
  1867. //
  1868. // Dereference the RFCB, if any.
  1869. //
  1870. if ( WorkContext->Rfcb != NULL ) {
  1871. SrvDereferenceRfcb( WorkContext->Rfcb );
  1872. WorkContext->OplockOpen = FALSE;
  1873. WorkContext->Rfcb = NULL;
  1874. }
  1875. //
  1876. // Dereference the wait for oplock break, if any
  1877. //
  1878. if ( WorkContext->WaitForOplockBreak != NULL ) {
  1879. SrvDereferenceWaitForOplockBreak( WorkContext->WaitForOplockBreak );
  1880. WorkContext->WaitForOplockBreak = NULL;
  1881. }
  1882. //
  1883. // If this was a blocking operation, update the blocking i/o count.
  1884. //
  1885. if ( WorkContext->BlockingOperation ) {
  1886. InterlockedDecrement( &SrvBlockingOpsInProgress );
  1887. WorkContext->BlockingOperation = FALSE;
  1888. }
  1889. return;
  1890. } // SrvReleaseContext
  1891. BOOLEAN
  1892. SrvSetFileWritethroughMode (
  1893. IN PLFCB Lfcb,
  1894. IN BOOLEAN Writethrough
  1895. )
  1896. /*++
  1897. Routine Description:
  1898. Sets the writethrough mode of a file as specified. Returns the
  1899. original mode of the file.
  1900. Arguments:
  1901. Lfcb - A pointer to the LFCB representing the open file.
  1902. Writethrough - A boolean indicating whether the file is to be placed
  1903. into writethrough mode (TRUE) or writebehind mode (FALSE).
  1904. Return Value:
  1905. BOOLEAN - Returns the original mode of the file.
  1906. --*/
  1907. {
  1908. FILE_MODE_INFORMATION modeInformation;
  1909. IO_STATUS_BLOCK iosb;
  1910. PAGED_CODE( );
  1911. //
  1912. // If the file is already in the correct mode, simply return.
  1913. // Otherwise, set the file to the correct mode.
  1914. //
  1915. if ( Writethrough ) {
  1916. if ( (Lfcb->FileMode & FILE_WRITE_THROUGH) != 0 ) {
  1917. return TRUE;
  1918. } else {
  1919. Lfcb->FileMode |= FILE_WRITE_THROUGH;
  1920. }
  1921. } else {
  1922. if ( (Lfcb->FileMode & FILE_WRITE_THROUGH) == 0 ) {
  1923. return FALSE;
  1924. } else {
  1925. Lfcb->FileMode &= ~FILE_WRITE_THROUGH;
  1926. }
  1927. }
  1928. //
  1929. // Change the file mode.
  1930. //
  1931. // !!! Don't do this by file handle -- build and issue an IRP using
  1932. // the file object pointer directly.
  1933. //
  1934. modeInformation.Mode = Lfcb->FileMode;
  1935. (VOID)NtSetInformationFile(
  1936. Lfcb->FileHandle,
  1937. &iosb,
  1938. &modeInformation,
  1939. sizeof( modeInformation ),
  1940. FileModeInformation
  1941. );
  1942. //
  1943. // Return the original mode of the file, which was the opposite of
  1944. // what was requested.
  1945. //
  1946. return (BOOLEAN)!Writethrough;
  1947. } // SrvSetFileWritethroughMode
  1948. VOID
  1949. SrvOemStringTo8dot3 (
  1950. IN POEM_STRING InputString,
  1951. OUT PSZ Output8dot3
  1952. )
  1953. /*++
  1954. Routine Description:
  1955. Convert a string into FAT 8.3 format. This derived from GaryKi's
  1956. routine FatStringTo8dot3 in fastfat\namesup.c.
  1957. Arguments:
  1958. InputString - Supplies the input string to convert
  1959. Output8dot3 - Receives the converted string. The memory must be
  1960. supplied by the caller.
  1961. Return Value:
  1962. None.
  1963. --*/
  1964. {
  1965. CLONG i, j;
  1966. PCHAR inBuffer = InputString->Buffer;
  1967. ULONG inLength = InputString->Length;
  1968. PAGED_CODE( );
  1969. ASSERT( inLength <= 12 );
  1970. //
  1971. // First make the output name all blanks.
  1972. //
  1973. RtlFillMemory( Output8dot3, 11, CHAR_SP );
  1974. //
  1975. // If we get "." or "..", just return them. They do not follow
  1976. // the usual rules for FAT names.
  1977. //
  1978. if( inBuffer[0] == '.' ) {
  1979. if( inLength == 1 ) {
  1980. Output8dot3[0] = '.';
  1981. return;
  1982. }
  1983. if( inLength == 2 && inBuffer[1] == '.' ) {
  1984. Output8dot3[0] = '.';
  1985. Output8dot3[1] = '.';
  1986. return;
  1987. }
  1988. }
  1989. //
  1990. // Copy over the first part of the file name. Stop when we get to
  1991. // the end of the input string or a dot.
  1992. //
  1993. if (NLS_MB_CODE_PAGE_TAG) {
  1994. for ( i = 0;
  1995. (i < inLength) && (inBuffer[i] != '.') && (inBuffer[i] != '\\');
  1996. i++ ) {
  1997. if (FsRtlIsLeadDbcsCharacter(inBuffer[i])) {
  1998. if (i+1 < inLength) {
  1999. Output8dot3[i] = inBuffer[i];
  2000. i++;
  2001. Output8dot3[i] = inBuffer[i];
  2002. } else {
  2003. break;
  2004. }
  2005. } else {
  2006. Output8dot3[i] = inBuffer[i];
  2007. }
  2008. }
  2009. } else {
  2010. for ( i = 0;
  2011. (i < inLength) && (inBuffer[i] != '.') && (inBuffer[i] != '\\');
  2012. i++ ) {
  2013. Output8dot3[i] = inBuffer[i];
  2014. }
  2015. }
  2016. //
  2017. // See if we need to add an extension.
  2018. //
  2019. if ( i < inLength ) {
  2020. //
  2021. // Skip over the dot.
  2022. //
  2023. ASSERT( (inLength - i) <= 4 );
  2024. ASSERT( inBuffer[i] == '.' );
  2025. i++;
  2026. //
  2027. // Add the extension to the output name
  2028. //
  2029. if (NLS_MB_CODE_PAGE_TAG) {
  2030. for ( j = 8;
  2031. (i < inLength) && (inBuffer[i] != '\\');
  2032. i++, j++ ) {
  2033. if (FsRtlIsLeadDbcsCharacter(inBuffer[i])) {
  2034. if (i+1 < inLength) {
  2035. Output8dot3[j] = inBuffer[i];
  2036. i++; j++;
  2037. Output8dot3[j] = inBuffer[i];
  2038. } else {
  2039. break;
  2040. }
  2041. } else {
  2042. Output8dot3[j] = inBuffer[i];
  2043. }
  2044. }
  2045. } else {
  2046. for ( j = 8;
  2047. (i < inLength) && (inBuffer[i] != '\\');
  2048. i++, j++ ) {
  2049. Output8dot3[j] = inBuffer[i];
  2050. }
  2051. }
  2052. }
  2053. //
  2054. // We're all done with the conversion.
  2055. //
  2056. return;
  2057. } // SrvOemStringTo8dot3
  2058. VOID
  2059. SrvUnicodeStringTo8dot3 (
  2060. IN PUNICODE_STRING InputString,
  2061. OUT PSZ Output8dot3,
  2062. IN BOOLEAN Upcase
  2063. )
  2064. /*++
  2065. Routine Description:
  2066. Convert a string into fat 8.3 format. This derived from GaryKi's
  2067. routine FatStringTo8dot3 in fat\fatname.c.
  2068. Arguments:
  2069. InputString - Supplies the input string to convert
  2070. Output8dot3 - Receives the converted string, the memory must be supplied
  2071. by the caller.
  2072. Upcase - Whether the string is to be uppercased.
  2073. Return Value:
  2074. None.
  2075. --*/
  2076. {
  2077. ULONG oemSize;
  2078. OEM_STRING oemString;
  2079. ULONG index = 0;
  2080. UCHAR aSmallBuffer[ 50 ];
  2081. NTSTATUS status;
  2082. PAGED_CODE( );
  2083. oemSize = RtlUnicodeStringToOemSize( InputString );
  2084. ASSERT( oemSize < MAXUSHORT );
  2085. if( oemSize <= sizeof( aSmallBuffer ) ) {
  2086. oemString.Buffer = aSmallBuffer;
  2087. } else {
  2088. oemString.Buffer = ALLOCATE_HEAP( oemSize, BlockTypeBuffer );
  2089. if( oemString.Buffer == NULL ) {
  2090. *Output8dot3 = '\0';
  2091. return;
  2092. }
  2093. }
  2094. oemString.MaximumLength = (USHORT)oemSize;
  2095. oemString.Length = (USHORT)oemSize - 1;
  2096. if ( Upcase ) {
  2097. status = RtlUpcaseUnicodeToOemN(
  2098. oemString.Buffer,
  2099. oemString.Length,
  2100. &index,
  2101. InputString->Buffer,
  2102. InputString->Length
  2103. );
  2104. ASSERT( NT_SUCCESS( status ) );
  2105. } else {
  2106. status = RtlUnicodeToOemN(
  2107. oemString.Buffer,
  2108. oemString.Length,
  2109. &index,
  2110. InputString->Buffer,
  2111. InputString->Length
  2112. );
  2113. ASSERT( NT_SUCCESS( status ) );
  2114. }
  2115. if( NT_SUCCESS( status ) ) {
  2116. oemString.Buffer[ index ] = '\0';
  2117. SrvOemStringTo8dot3(
  2118. &oemString,
  2119. Output8dot3
  2120. );
  2121. } else {
  2122. *Output8dot3 = '\0';
  2123. }
  2124. if( oemSize > sizeof( aSmallBuffer ) ) {
  2125. FREE_HEAP( oemString.Buffer );
  2126. }
  2127. } // SrvUnicodeStringTo8dot3
  2128. #if SRVDBG_STATS
  2129. VOID SRVFASTCALL
  2130. SrvUpdateStatistics2 (
  2131. PWORK_CONTEXT WorkContext,
  2132. UCHAR SmbCommand
  2133. )
  2134. /*++
  2135. Routine Description:
  2136. Update the server statistics database to reflect the work item
  2137. that is being completed.
  2138. Arguments:
  2139. WorkContext - pointer to the work item containing the statistics
  2140. for this request.
  2141. SmbCommand - The SMB command code of the current operation.
  2142. Return Value:
  2143. None.
  2144. --*/
  2145. {
  2146. if ( WorkContext->StartTime != 0 ) {
  2147. LARGE_INTEGER td;
  2148. td.QuadPart = WorkContext->CurrentWorkQueue->stats.SystemTime - WorkContext->StartTime;
  2149. //
  2150. // Update the SMB-specific statistics fields.
  2151. //
  2152. // !!! doesn't work for original transact smb--SmbCommand is too
  2153. // large.
  2154. //ASSERT( SmbCommand <= MAX_STATISTICS_SMB );
  2155. if ( SmbCommand <= MAX_STATISTICS_SMB ) {
  2156. SrvDbgStatistics.Smb[SmbCommand].SmbCount++;
  2157. SrvDbgStatistics.Smb[SmbCommand].TotalTurnaroundTime.QuadPart +=
  2158. td.QuadPart;
  2159. }
  2160. #if 0 // this code is no longer valid!
  2161. //
  2162. // Update the size-dependent IO fields if necessary. The arrays
  2163. // in SrvStatistics correspond to powers of two sizes for the IO.
  2164. // The correspondence between array location and IO size is:
  2165. //
  2166. // Location IO Size (min)
  2167. // 0 0
  2168. // 1 1
  2169. // 2 2
  2170. // 3 4
  2171. // 4 8
  2172. // 5 16
  2173. // 6 32
  2174. // 7 64
  2175. // 8 128
  2176. // 9 256
  2177. // 10 512
  2178. // 11 1024
  2179. // 12 2048
  2180. // 13 4096
  2181. // 14 8192
  2182. // 15 16384
  2183. // 16 32768
  2184. //
  2185. if ( WorkContext->BytesRead != 0 ) {
  2186. CLONG i;
  2187. for ( i = 0;
  2188. i < 17 && WorkContext->BytesRead != 0;
  2189. i++, WorkContext->BytesRead >>= 1 );
  2190. SrvDbgStatistics.ReadSize[i].SmbCount++;
  2191. SrvDbgStatistics.ReadSize[i].TotalTurnaroundTime.QuadPart +=
  2192. td.QuadPart;
  2193. }
  2194. if ( WorkContext->BytesWritten != 0 ) {
  2195. CLONG i;
  2196. for ( i = 0;
  2197. i < 17 && WorkContext->BytesWritten != 0;
  2198. i++, WorkContext->BytesWritten >>= 1 );
  2199. SrvDbgStatistics.WriteSize[i].SmbCount++;
  2200. SrvDbgStatistics.WriteSize[i].TotalTurnaroundTime.QuadPart +=
  2201. td.QuadPart;
  2202. }
  2203. #endif
  2204. }
  2205. return;
  2206. } // SrvUpdateStatistics2
  2207. #endif // SRVDBG_STATS
  2208. PRFCB
  2209. SrvVerifyFid2 (
  2210. IN PWORK_CONTEXT WorkContext,
  2211. IN USHORT Fid,
  2212. IN BOOLEAN FailOnSavedError,
  2213. IN PRESTART_ROUTINE SerializeWithRawRestartRoutine OPTIONAL,
  2214. OUT PNTSTATUS NtStatus
  2215. )
  2216. /*++
  2217. Routine Description:
  2218. Verifies the FID, TID, and UID in an incoming SMB. If they are
  2219. valid, the address of the RFCB corresponding to the FID is returned,
  2220. and the block is referenced.
  2221. Arguments:
  2222. WorkContext - Supplies a pointer to the work context block for the
  2223. current SMB. In particular, the Connection block pointer is
  2224. used to find the appropriate file table. If the FID is valid,
  2225. the RFCB address is stored in WorkContext->Rfcb.
  2226. Fid - Supplies the FID sent in the request SMB
  2227. FailOnSavedError - If TRUE, return NULL to the caller if there is
  2228. an outstanding write behind error. If FALSE, always attempt
  2229. to return a pointer to the RFCB.
  2230. SerializeWithRawRestartRoutine - If not NULL, is the address of an
  2231. FSP restart routine, and specifies that this operation should be
  2232. queued if a raw write is currently in progress on the file. If
  2233. this is the case, this routine queues the work context block to
  2234. a queue in the RFCB. When the raw write completes, all work
  2235. items on the queue are restarted.
  2236. NtStatus - This field is filled in only when this function returns
  2237. NULL. If there was a write behind error, NtStatus returns the
  2238. write behind error status, otherwise it returns
  2239. STATUS_INVALID_HANDLE.
  2240. Return Value:
  2241. PRFCB - Address of the RFCB, or SRV_INVALID_RFCB_POINTER if the Fid
  2242. was invalid, or if there is a raw write in progress and
  2243. serialization was requested (in which case *NtStatus is set
  2244. to STATUS_SUCCESS).
  2245. --*/
  2246. {
  2247. PCONNECTION connection;
  2248. PTABLE_HEADER tableHeader;
  2249. PRFCB rfcb;
  2250. USHORT index;
  2251. USHORT sequence;
  2252. KIRQL oldIrql;
  2253. #if 0
  2254. // THIS IS NOW DONE IN THE SrvVerifyFid MACRO.
  2255. //
  2256. // If the FID has already been verified, return the RFCB pointer.
  2257. //
  2258. // *** Note that we don't do the saved error checking or the raw
  2259. // write serialization in this case, on the assumption that
  2260. // since we already passed the checks once (in order to get the
  2261. // RFCB pointer in the first place), we don't need to do them
  2262. // again.
  2263. //
  2264. if ( WorkContext->Rfcb != NULL ) {
  2265. return WorkContext->Rfcb;
  2266. }
  2267. #endif
  2268. //
  2269. // Initialize local variables: obtain the connection block address
  2270. // and crack the FID into its components.
  2271. //
  2272. connection = WorkContext->Connection;
  2273. //
  2274. // Acquire the spin lock that guards the connection's file table.
  2275. //
  2276. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  2277. //
  2278. // See if this is the cached rfcb
  2279. //
  2280. if ( connection->CachedFid == (ULONG)Fid ) {
  2281. rfcb = connection->CachedRfcb;
  2282. } else {
  2283. //
  2284. // Verify that the FID is in range, is in use, and has the correct
  2285. // sequence number.
  2286. index = FID_INDEX( Fid );
  2287. sequence = FID_SEQUENCE( Fid );
  2288. tableHeader = &connection->FileTable;
  2289. if ( (index >= tableHeader->TableSize) ||
  2290. (tableHeader->Table[index].Owner == NULL) ||
  2291. (tableHeader->Table[index].SequenceNumber != sequence) ) {
  2292. *NtStatus = STATUS_INVALID_HANDLE;
  2293. goto error_exit;
  2294. }
  2295. rfcb = tableHeader->Table[index].Owner;
  2296. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  2297. *NtStatus = STATUS_INVALID_HANDLE;
  2298. goto error_exit;
  2299. }
  2300. //
  2301. // If the caller wants to fail when there is a write behind
  2302. // error and the error exists, fill in NtStatus and do not
  2303. // return the RFCB pointer.
  2304. //
  2305. if ( !NT_SUCCESS(rfcb->SavedError) && FailOnSavedError ) {
  2306. if ( !NT_SUCCESS(rfcb->SavedError) ) {
  2307. *NtStatus = rfcb->SavedError;
  2308. rfcb->SavedError = STATUS_SUCCESS;
  2309. goto error_exit;
  2310. }
  2311. }
  2312. //
  2313. // Cache the fid.
  2314. //
  2315. connection->CachedRfcb = rfcb;
  2316. connection->CachedFid = (ULONG)Fid;
  2317. }
  2318. //
  2319. // The FID is valid within the context of this connection. Verify
  2320. // that the owning tree connect's TID is correct.
  2321. //
  2322. // Do not verify the UID for clients that do not understand it.
  2323. //
  2324. if ( (rfcb->Tid !=
  2325. SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )) ||
  2326. ((rfcb->Uid !=
  2327. SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )) &&
  2328. DIALECT_HONORS_UID(connection->SmbDialect)) ) {
  2329. *NtStatus = STATUS_INVALID_HANDLE;
  2330. goto error_exit;
  2331. }
  2332. //
  2333. // If raw write serialization was requested, and a raw write
  2334. // is active, queue this work item in the RFCB pending
  2335. // completion of the raw write.
  2336. //
  2337. if ( (rfcb->RawWriteCount != 0) &&
  2338. ARGUMENT_PRESENT(SerializeWithRawRestartRoutine) ) {
  2339. InsertTailList(
  2340. &rfcb->RawWriteSerializationList,
  2341. &WorkContext->ListEntry
  2342. );
  2343. WorkContext->FspRestartRoutine = SerializeWithRawRestartRoutine;
  2344. *NtStatus = STATUS_SUCCESS;
  2345. goto error_exit;
  2346. }
  2347. //
  2348. // The file is active and the TID is valid. Reference the
  2349. // RFCB. Release the spin lock (we don't need it anymore).
  2350. //
  2351. rfcb->BlockHeader.ReferenceCount++;
  2352. UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
  2353. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2354. //
  2355. // Save the RFCB address in the work context block and
  2356. // return the file address.
  2357. //
  2358. WorkContext->Rfcb = rfcb;
  2359. //
  2360. // Mark the rfcb as active
  2361. //
  2362. rfcb->IsActive = TRUE;
  2363. ASSERT( GET_BLOCK_TYPE( rfcb->Mfcb ) == BlockTypeMfcb );
  2364. return rfcb;
  2365. error_exit:
  2366. //
  2367. // Either the FID is invalid for this connection, the file is
  2368. // closing, or the TID doesn't match. Release the lock, clear the
  2369. // file address in the work context block, and return a file address
  2370. // of NULL.
  2371. //
  2372. WorkContext->Rfcb = NULL; // connection spinlock must be held
  2373. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2374. return SRV_INVALID_RFCB_POINTER;
  2375. } // SrvVerifyFid2
  2376. PRFCB
  2377. SrvVerifyFidForRawWrite (
  2378. IN PWORK_CONTEXT WorkContext,
  2379. IN USHORT Fid,
  2380. OUT PNTSTATUS NtStatus
  2381. )
  2382. /*++
  2383. Routine Description:
  2384. Verifies the FID, TID, and UID in an incoming SMB. If they are
  2385. valid, the address of the RFCB corresponding to the FID is returned,
  2386. and the block is referenced. In addition, the RawWriteCount in the
  2387. RFCB is incremented.
  2388. Arguments:
  2389. WorkContext - Supplies a pointer to the work context block for the
  2390. current SMB. In particular, the Connection block pointer is
  2391. used to find the appropriate file table. If the FID is valid,
  2392. the RFCB address is stored in WorkContext->Rfcb.
  2393. Fid - Supplies the FID sent in the request SMB
  2394. NtStatus - This field is filled in only when this function returns
  2395. NULL. If there was a write behind error, NtStatus returns the
  2396. write behind error status, otherwise it returns
  2397. STATUS_INVALID_HANDLE.
  2398. Return Value:
  2399. PRFCB - Address of the RFCB, or SRV_INVALID_RFCB_POINTER if the Fid
  2400. was invalid, or if there is a raw write in progress and
  2401. serialization was requested (in which case *NtStatus is set
  2402. to STATUS_SUCCESS).
  2403. --*/
  2404. {
  2405. PCONNECTION connection;
  2406. PTABLE_HEADER tableHeader;
  2407. PRFCB rfcb;
  2408. USHORT index;
  2409. USHORT sequence;
  2410. KIRQL oldIrql;
  2411. ASSERT( WorkContext->Rfcb == NULL );
  2412. //
  2413. // Initialize local variables: obtain the connection block address
  2414. // and crack the FID into its components.
  2415. //
  2416. connection = WorkContext->Connection;
  2417. //
  2418. // Acquire the spin lock that guards the connection's file table.
  2419. //
  2420. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  2421. //
  2422. // See if this is the cached rfcb
  2423. //
  2424. if ( connection->CachedFid == Fid ) {
  2425. rfcb = connection->CachedRfcb;
  2426. } else {
  2427. //
  2428. // Verify that the FID is in range, is in use, and has the correct
  2429. // sequence number.
  2430. index = FID_INDEX( Fid );
  2431. sequence = FID_SEQUENCE( Fid );
  2432. tableHeader = &connection->FileTable;
  2433. if ( (index >= tableHeader->TableSize) ||
  2434. (tableHeader->Table[index].Owner == NULL) ||
  2435. (tableHeader->Table[index].SequenceNumber != sequence) ) {
  2436. *NtStatus = STATUS_INVALID_HANDLE;
  2437. goto error_exit;
  2438. }
  2439. rfcb = tableHeader->Table[index].Owner;
  2440. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  2441. *NtStatus = STATUS_INVALID_HANDLE;
  2442. goto error_exit;
  2443. }
  2444. //
  2445. // If there is a write behind error, fill in NtStatus and do
  2446. // not return the RFCB pointer.
  2447. //
  2448. if ( !NT_SUCCESS( rfcb->SavedError ) ) {
  2449. if ( !NT_SUCCESS( rfcb->SavedError ) ) {
  2450. *NtStatus = rfcb->SavedError;
  2451. rfcb->SavedError = STATUS_SUCCESS;
  2452. goto error_exit;
  2453. }
  2454. }
  2455. connection->CachedRfcb = rfcb;
  2456. connection->CachedFid = (ULONG)Fid;
  2457. //
  2458. // The FID is valid within the context of this connection. Verify
  2459. // that the owning tree connect's TID is correct.
  2460. //
  2461. // Do not verify the UID for clients that do not understand it.
  2462. //
  2463. if ( (rfcb->Tid !=
  2464. SmbGetAlignedUshort(&WorkContext->RequestHeader->Tid)) ||
  2465. ( (rfcb->Uid !=
  2466. SmbGetAlignedUshort(&WorkContext->RequestHeader->Uid)) &&
  2467. DIALECT_HONORS_UID(connection->SmbDialect) ) ) {
  2468. *NtStatus = STATUS_INVALID_HANDLE;
  2469. goto error_exit;
  2470. }
  2471. }
  2472. //
  2473. // If a raw write is already active, queue this work item in
  2474. // the RFCB pending completion of the raw write.
  2475. //
  2476. if ( rfcb->RawWriteCount != 0 ) {
  2477. InsertTailList(
  2478. &rfcb->RawWriteSerializationList,
  2479. &WorkContext->ListEntry
  2480. );
  2481. WorkContext->FspRestartRoutine = SrvRestartSmbReceived;
  2482. *NtStatus = STATUS_SUCCESS;
  2483. goto error_exit;
  2484. }
  2485. //
  2486. // The file is active and the TID is valid. Reference the
  2487. // RFCB and increment the raw write count. Release the spin
  2488. // lock (we don't need it anymore).
  2489. //
  2490. rfcb->BlockHeader.ReferenceCount++;
  2491. UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
  2492. rfcb->RawWriteCount++;
  2493. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2494. //
  2495. // Save the RFCB address in the work context block and
  2496. // return the file address.
  2497. //
  2498. WorkContext->Rfcb = rfcb;
  2499. ASSERT( GET_BLOCK_TYPE( rfcb->Mfcb ) == BlockTypeMfcb );
  2500. //
  2501. // Mark the rfcb as active
  2502. //
  2503. rfcb->IsActive = TRUE;
  2504. return rfcb;
  2505. error_exit:
  2506. //
  2507. // Either the FID is invalid for this connection, the file is
  2508. // closing, or the TID and UID don't match. Clear the file address
  2509. // in the work context block, and return a file address of NULL.
  2510. //
  2511. WorkContext->Rfcb = NULL; // connection spinlock must be held
  2512. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2513. return SRV_INVALID_RFCB_POINTER;
  2514. } // SrvVerifyFidForRawWrite
  2515. PSEARCH
  2516. SrvVerifySid (
  2517. IN PWORK_CONTEXT WorkContext,
  2518. IN USHORT Index,
  2519. IN USHORT Sequence,
  2520. IN PSRV_DIRECTORY_INFORMATION DirectoryInformation,
  2521. IN CLONG BufferSize
  2522. )
  2523. /*++
  2524. Routine Description:
  2525. Verifies the SID in the resume key of a Search or Find SMB. If the
  2526. SID is valid, the address of the search block corresponding to the
  2527. SID is returned. The appropiate fields in the DirectoryInformation
  2528. structure are filled in so that SrvQueryDirectoryFile may be called.
  2529. Arguments:
  2530. WorkContext - Supplies a pointer to the work context block for the
  2531. current SMB. In particular, the Connection block pointer is
  2532. used to find the appropriate search table.
  2533. ResumeKey - a pointer the the resume key to evaluate.
  2534. Return Value:
  2535. PSEARCH - address of the Search block, or NULL.
  2536. --*/
  2537. {
  2538. PCONNECTION connection;
  2539. PTABLE_HEADER tableHeader;
  2540. PSEARCH search;
  2541. PAGED_CODE( );
  2542. connection = WorkContext->Connection;
  2543. //
  2544. // Acquire the connection's lock.
  2545. //
  2546. ACQUIRE_LOCK( &connection->Lock );
  2547. //
  2548. // Verify that the index is in range, that the search block is in use,
  2549. // and that the resume key has the correct sequence number.
  2550. //
  2551. tableHeader = &connection->PagedConnection->SearchTable;
  2552. if ( (Index < tableHeader->TableSize) &&
  2553. (tableHeader->Table[Index].Owner != NULL) &&
  2554. (tableHeader->Table[Index].SequenceNumber == Sequence) ) {
  2555. search = tableHeader->Table[Index].Owner;
  2556. //
  2557. // The SID is valid. Verify that the search block is still
  2558. // active.
  2559. //
  2560. // !!! Does this really apply for search blocks?
  2561. //
  2562. if ( GET_BLOCK_STATE(search) != BlockStateActive || search->InUse ) {
  2563. //
  2564. // The search block is no longer active or somebody is
  2565. // already using the search block.
  2566. //
  2567. search = NULL;
  2568. } else {
  2569. //
  2570. // We found a legitimate search block, so reference it.
  2571. //
  2572. SrvReferenceSearch( search );
  2573. //
  2574. // Fill in fields of DirectoryInformation.
  2575. //
  2576. DirectoryInformation->DirectoryHandle = search->DirectoryHandle;
  2577. DirectoryInformation->CurrentEntry = NULL;
  2578. DirectoryInformation->BufferLength = BufferSize -
  2579. sizeof(SRV_DIRECTORY_INFORMATION);
  2580. DirectoryInformation->Wildcards = search->Wildcards;
  2581. DirectoryInformation->ErrorOnFileOpen = FALSE;
  2582. //
  2583. // Indicate that the search is being used.
  2584. //
  2585. search->InUse = TRUE;
  2586. }
  2587. } else {
  2588. //
  2589. // The SID is invalid.
  2590. //
  2591. search = NULL;
  2592. }
  2593. //
  2594. // Release the lock and return the search block address (or NULL).
  2595. //
  2596. RELEASE_LOCK( &connection->Lock );
  2597. return search;
  2598. } // SrvVerifySid
  2599. PTREE_CONNECT
  2600. SrvVerifyTid (
  2601. IN PWORK_CONTEXT WorkContext,
  2602. IN USHORT Tid
  2603. )
  2604. /*++
  2605. Routine Description:
  2606. Verifies the TID in an incoming SMB. If the TID is valid, the
  2607. address of the tree connect block corresponding to the TID is
  2608. returned, and the block is referenced.
  2609. Arguments:
  2610. WorkContext - Supplies a pointer to the work context block for the
  2611. current SMB. In particular, the Connection block pointer is
  2612. used to find the appropriate tree table. Also, the tree connect
  2613. block address, if the TID is valid, is stored in
  2614. WorkContext->TreeConnect.
  2615. Tid - Supplies the TID sent in the request SMB
  2616. Return Value:
  2617. PTREE_CONNECT - Address of the tree connect block, or NULL
  2618. --*/
  2619. {
  2620. PCONNECTION connection;
  2621. PTREE_CONNECT treeConnect;
  2622. PTABLE_HEADER tableHeader;
  2623. USHORT index;
  2624. USHORT sequence;
  2625. PAGED_CODE( );
  2626. //
  2627. // If the TID has already been verified, return the tree connect
  2628. // pointer.
  2629. //
  2630. if ( WorkContext->TreeConnect != NULL ) {
  2631. return WorkContext->TreeConnect;
  2632. }
  2633. //
  2634. // Initialize local variables: obtain the connection block address
  2635. // and crack the TID into its components.
  2636. //
  2637. connection = WorkContext->Connection;
  2638. index = TID_INDEX( Tid );
  2639. sequence = TID_SEQUENCE( Tid );
  2640. //
  2641. // Acquire the connection's tree connect lock.
  2642. //
  2643. ACQUIRE_LOCK( &connection->Lock );
  2644. //
  2645. // Verify that the TID is in range, is in use, and has the correct
  2646. // sequence number.
  2647. tableHeader = &connection->PagedConnection->TreeConnectTable;
  2648. if ( (index < tableHeader->TableSize) &&
  2649. (tableHeader->Table[index].Owner != NULL) &&
  2650. (tableHeader->Table[index].SequenceNumber == sequence) ) {
  2651. treeConnect = tableHeader->Table[index].Owner;
  2652. //
  2653. // The TID is valid within the context of this connection.
  2654. // Verify that the tree connect is still active.
  2655. //
  2656. if ( GET_BLOCK_STATE(treeConnect) == BlockStateActive ) {
  2657. //
  2658. // The tree connect is active. Reference it.
  2659. //
  2660. SrvReferenceTreeConnect( treeConnect );
  2661. } else {
  2662. //
  2663. // The tree connect is closing.
  2664. //
  2665. treeConnect = NULL;
  2666. }
  2667. } else {
  2668. //
  2669. // The TID is invalid for this connection.
  2670. //
  2671. treeConnect = NULL;
  2672. }
  2673. //
  2674. // Release the lock, save the tree connect address in the work context
  2675. // block, and return the tree connect address (or NULL).
  2676. //
  2677. RELEASE_LOCK( &connection->Lock );
  2678. WorkContext->TreeConnect = treeConnect;
  2679. return treeConnect;
  2680. } // SrvVerifyTid
  2681. PSESSION
  2682. SrvVerifyUid (
  2683. IN PWORK_CONTEXT WorkContext,
  2684. IN USHORT Uid
  2685. )
  2686. /*++
  2687. Routine Description:
  2688. Verifies the UID in an incoming SMB. If the UID is valid, the
  2689. address of the session block corresponding to the UID is returned,
  2690. and the block is referenced.
  2691. Arguments:
  2692. WorkContext - Supplies a pointer to the work context block for the
  2693. current SMB. In particular, the Connection block pointer is
  2694. used to find the appropriate user table. Also, the session block
  2695. address, if the UID is valid, is stored in WorkContext->Session.
  2696. Uid - Supplies the UID sent in the request SMB
  2697. Return Value:
  2698. PSESSION - Address of the session block, or NULL
  2699. --*/
  2700. {
  2701. PCONNECTION connection;
  2702. PTABLE_HEADER tableHeader;
  2703. PSESSION session;
  2704. USHORT index;
  2705. USHORT sequence;
  2706. PAGED_CODE( );
  2707. //
  2708. // If the UID has already been verified, return the session pointer.
  2709. //
  2710. if ( WorkContext->Session != NULL ) {
  2711. return WorkContext->Session;
  2712. }
  2713. //
  2714. // Initialize local variables: obtain the connection block address
  2715. // and crack the UID into its components.
  2716. //
  2717. connection = WorkContext->Connection;
  2718. index = UID_INDEX( Uid );
  2719. sequence = UID_SEQUENCE( Uid );
  2720. //
  2721. // Acquire the connection's session lock.
  2722. //
  2723. ACQUIRE_LOCK( &connection->Lock );
  2724. //
  2725. // If this is a down-level (LAN Man 1.0 or earlier) client, than
  2726. // we will not receive a UID, and there will only be one session
  2727. // per connection. Reference that session.
  2728. //
  2729. tableHeader = &connection->PagedConnection->SessionTable;
  2730. if (!DIALECT_HONORS_UID(connection->SmbDialect) ) {
  2731. session = tableHeader->Table[0].Owner;
  2732. } else if ( (index < tableHeader->TableSize) &&
  2733. (tableHeader->Table[index].Owner != NULL) &&
  2734. (tableHeader->Table[index].SequenceNumber == sequence) ) {
  2735. //
  2736. // The UID is in range, is in use, and has the correct sequence
  2737. // number.
  2738. //
  2739. session = tableHeader->Table[index].Owner;
  2740. } else {
  2741. //
  2742. // The UID is invalid for this connection.
  2743. //
  2744. IF_DEBUG( ERRORS ) {
  2745. KdPrint(( "SrvVerifyUid: index %d, size %d\n", index, tableHeader->TableSize ));
  2746. if( index < tableHeader->TableSize ) {
  2747. KdPrint((" Owner %p, Table.SequenceNumber %d, seq %d\n",
  2748. tableHeader->Table[index].Owner,
  2749. tableHeader->Table[index].SequenceNumber,
  2750. sequence
  2751. ));
  2752. }
  2753. }
  2754. session = NULL;
  2755. }
  2756. if ( session != NULL ) {
  2757. //
  2758. // The UID is valid within the context of this connection.
  2759. // Verify that the session is still active.
  2760. //
  2761. if ( GET_BLOCK_STATE(session) == BlockStateActive ) {
  2762. LARGE_INTEGER liNow;
  2763. KeQuerySystemTime( &liNow);
  2764. if( session->LogonSequenceInProgress == FALSE &&
  2765. liNow.QuadPart >= session->LogOffTime.QuadPart )
  2766. {
  2767. IF_DEBUG( ERRORS ) {
  2768. KdPrint(( "SrvVerifyUid: LogOffTime has passed %x %x\n",
  2769. session->LogOffTime.HighPart,
  2770. session->LogOffTime.LowPart
  2771. ));
  2772. }
  2773. // Mark the session as expired
  2774. session->IsSessionExpired = TRUE;
  2775. KdPrint(( "Marking session as expired.\n" ));
  2776. }
  2777. //
  2778. // The session is active. Reference it.
  2779. //
  2780. SrvReferenceSession( session );
  2781. //
  2782. // Update the last use time for autologoff.
  2783. //
  2784. session->LastUseTime = liNow;
  2785. } else {
  2786. //
  2787. // The session is closing.
  2788. //
  2789. IF_DEBUG( ERRORS ) {
  2790. KdPrint(( "SrvVerifyUid: Session state %x\n", GET_BLOCK_STATE( session ) ));
  2791. }
  2792. session = NULL;
  2793. }
  2794. }
  2795. //
  2796. // Release the lock, save the session address in the work context
  2797. // block, and return the session address (or NULL).
  2798. //
  2799. RELEASE_LOCK( &connection->Lock );
  2800. WorkContext->Session = session;
  2801. return session;
  2802. } // SrvVerifyUid
  2803. NTSTATUS
  2804. SrvVerifyUidAndTid (
  2805. IN PWORK_CONTEXT WorkContext,
  2806. OUT PSESSION *Session,
  2807. OUT PTREE_CONNECT *TreeConnect,
  2808. IN SHARE_TYPE ShareType
  2809. )
  2810. /*++
  2811. Routine Description:
  2812. Verifies the UID and TID in an incoming SMB. If both the UID and
  2813. the TDI are valid, the addresses of the session/tree connect blocks
  2814. corresponding to the UID/TID are returned, and the blocks are
  2815. referenced.
  2816. Arguments:
  2817. WorkContext - Supplies a pointer to the work context block for the
  2818. current SMB. In particular, the Connection block pointer is
  2819. used to find the appropriate user table. If the UID and TID are
  2820. valid, the session/tree connect block addresses are stored in
  2821. WorkContext->Session and WorkContext->TreeConnect.
  2822. Uid - Supplies the UID sent in the request SMB
  2823. Tid - Supplies the TID sent in the request SMB
  2824. Session - Returns a pointer to the session block
  2825. TreeConnect - Returns a pointer to the tree connect block
  2826. ShareType - the type of share it should be
  2827. Return Value:
  2828. NTSTATUS - STATUS_SUCCESS, STATUS_SMB_BAD_UID, or STATUS_SMB_BAD_TID
  2829. --*/
  2830. {
  2831. PCONNECTION connection;
  2832. PSESSION session;
  2833. PTREE_CONNECT treeConnect;
  2834. PPAGED_CONNECTION pagedConnection;
  2835. PTABLE_HEADER tableHeader;
  2836. USHORT index;
  2837. USHORT Uid;
  2838. USHORT Tid;
  2839. USHORT sequence;
  2840. LARGE_INTEGER liNow;
  2841. PAGED_CODE( );
  2842. //
  2843. // If the UID and TID have already been verified, don't do all this
  2844. // work again.
  2845. //
  2846. if ( (WorkContext->Session != NULL) &&
  2847. (WorkContext->TreeConnect != NULL) ) {
  2848. if( ShareType != ShareTypeWild &&
  2849. WorkContext->TreeConnect->Share->ShareType != ShareType ) {
  2850. return STATUS_ACCESS_DENIED;
  2851. }
  2852. *Session = WorkContext->Session;
  2853. *TreeConnect = WorkContext->TreeConnect;
  2854. return STATUS_SUCCESS;
  2855. }
  2856. //
  2857. // Obtain the connection block address and lock the connection.
  2858. //
  2859. connection = WorkContext->Connection;
  2860. pagedConnection = connection->PagedConnection;
  2861. ACQUIRE_LOCK( &connection->Lock );
  2862. //
  2863. // If we haven't negotiated successfully with this client, then we have
  2864. // a failure
  2865. //
  2866. if( connection->SmbDialect == SmbDialectIllegal) {
  2867. RELEASE_LOCK( &connection->Lock );
  2868. return STATUS_INVALID_SMB;
  2869. }
  2870. //
  2871. // If the UID has already been verified, don't verify it again.
  2872. //
  2873. if ( WorkContext->Session != NULL ) {
  2874. session = WorkContext->Session;
  2875. } else {
  2876. //
  2877. // Crack the UID into its components.
  2878. //
  2879. Uid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ),
  2880. index = UID_INDEX( Uid );
  2881. sequence = UID_SEQUENCE( Uid );
  2882. //
  2883. // If this is a down-level (LAN Man 1.0 or earlier) client, than
  2884. // we will not receive a UID, and there will only be one session
  2885. // per connection. Reference that session.
  2886. //
  2887. // For clients that do send UIDs, verify that the UID is in
  2888. // range, is in use, and has the correct sequence number, and
  2889. // that the session is not closing.
  2890. //
  2891. tableHeader = &pagedConnection->SessionTable;
  2892. if (!DIALECT_HONORS_UID(connection->SmbDialect))
  2893. {
  2894. session = tableHeader->Table[0].Owner;
  2895. }
  2896. else if( (index >= tableHeader->TableSize) ||
  2897. ((session = tableHeader->Table[index].Owner) == NULL) ||
  2898. (tableHeader->Table[index].SequenceNumber != sequence) ||
  2899. (GET_BLOCK_STATE(session) != BlockStateActive) )
  2900. {
  2901. //
  2902. // The UID is invalid for this connection, or the session is
  2903. // closing.
  2904. //
  2905. RELEASE_LOCK( &connection->Lock );
  2906. return STATUS_SMB_BAD_UID;
  2907. }
  2908. //
  2909. // it's valid
  2910. //
  2911. KeQuerySystemTime(&liNow);
  2912. if( session == NULL )
  2913. {
  2914. RELEASE_LOCK( &connection->Lock );
  2915. return STATUS_SMB_BAD_UID;
  2916. }
  2917. if( session->LogonSequenceInProgress == FALSE &&
  2918. liNow.QuadPart >= session->LogOffTime.QuadPart )
  2919. {
  2920. // Mark the session as expired
  2921. session->IsSessionExpired = TRUE;
  2922. }
  2923. }
  2924. //
  2925. // The UID is valid. Check the TID. If the TID has already been
  2926. // verified, don't verify it again.
  2927. //
  2928. if ( WorkContext->TreeConnect != NULL ) {
  2929. treeConnect = WorkContext->TreeConnect;
  2930. } else {
  2931. //
  2932. // Crack the TID into its components.
  2933. //
  2934. Tid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ),
  2935. index = TID_INDEX( Tid );
  2936. sequence = TID_SEQUENCE( Tid );
  2937. //
  2938. // Verify that the TID is in range, is in use, and has the
  2939. // correct sequence number, and that the tree connect is not
  2940. // closing.
  2941. //
  2942. tableHeader = &pagedConnection->TreeConnectTable;
  2943. if ( (index >= tableHeader->TableSize) ||
  2944. ((treeConnect = tableHeader->Table[index].Owner) == NULL) ||
  2945. (tableHeader->Table[index].SequenceNumber != sequence) ||
  2946. (GET_BLOCK_STATE(treeConnect) != BlockStateActive) ) {
  2947. //
  2948. // The TID is invalid for this connection, or the tree
  2949. // connect is closing.
  2950. //
  2951. RELEASE_LOCK( &connection->Lock );
  2952. return STATUS_SMB_BAD_TID;
  2953. }
  2954. //
  2955. // Make sure this is not a Null session trying to sneak in
  2956. // through an established tree connect.
  2957. //
  2958. if ( session->IsNullSession &&
  2959. SrvRestrictNullSessionAccess &&
  2960. ( treeConnect->Share->ShareType != ShareTypePipe ) ) {
  2961. BOOLEAN matchFound = FALSE;
  2962. ULONG i;
  2963. ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
  2964. for ( i = 0; SrvNullSessionShares[i] != NULL ; i++ ) {
  2965. if ( _wcsicmp(
  2966. SrvNullSessionShares[i],
  2967. treeConnect->Share->ShareName.Buffer
  2968. ) == 0 ) {
  2969. matchFound = TRUE;
  2970. break;
  2971. }
  2972. }
  2973. RELEASE_LOCK( &SrvConfigurationLock );
  2974. //
  2975. // The null session is not allowed to access this share - reject.
  2976. //
  2977. if ( !matchFound ) {
  2978. RELEASE_LOCK( &connection->Lock );
  2979. return(STATUS_ACCESS_DENIED);
  2980. }
  2981. }
  2982. }
  2983. //
  2984. // Both the UID and the TID are valid. Reference the session and
  2985. // tree connect blocks.
  2986. //
  2987. if ( WorkContext->Session == NULL ) {
  2988. SrvReferenceSession( session );
  2989. WorkContext->Session = session;
  2990. //
  2991. // Update the last use time for autologoff.
  2992. //
  2993. session->LastUseTime = liNow;
  2994. }
  2995. if ( WorkContext->TreeConnect == NULL ) {
  2996. SrvReferenceTreeConnect( treeConnect );
  2997. WorkContext->TreeConnect = treeConnect;
  2998. }
  2999. //
  3000. // Release the connection lock and return success.
  3001. //
  3002. RELEASE_LOCK( &connection->Lock );
  3003. *Session = session;
  3004. *TreeConnect = treeConnect;
  3005. //
  3006. // Make sure this is the correct type of share
  3007. //
  3008. if( ShareType != ShareTypeWild && (*TreeConnect)->Share->ShareType != ShareType ) {
  3009. return STATUS_ACCESS_DENIED;
  3010. }
  3011. return STATUS_SUCCESS;
  3012. } // SrvVerifyUidAndTid
  3013. BOOLEAN
  3014. SrvReceiveBufferShortage (
  3015. VOID
  3016. )
  3017. /*++
  3018. Routine Description:
  3019. This function calculates if the server is running low on receive
  3020. work items that are not involved in blocking operations.
  3021. Arguments:
  3022. None.
  3023. Return Value:
  3024. TRUE - The server is running short on receive work items.
  3025. FALSE - The server is *not* running short on receive work items.
  3026. --*/
  3027. {
  3028. KIRQL oldIrql;
  3029. BOOLEAN bufferShortage;
  3030. PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
  3031. //
  3032. // Even if we have reached our limit, we will allow this blocking
  3033. // operation if we have enough free work items to allocate. This
  3034. // will allow the resource thread to allocate more work items to
  3035. // service blocking requests.
  3036. //
  3037. if ( (queue->FreeWorkItems < queue->MaximumWorkItems) ||
  3038. ((queue->FreeWorkItems - SrvBlockingOpsInProgress)
  3039. > SrvMinFreeWorkItemsBlockingIo) ) {
  3040. //
  3041. // The caller will start a blocking operation. Increment the
  3042. // blocking operation count.
  3043. //
  3044. InterlockedIncrement( &SrvBlockingOpsInProgress );
  3045. bufferShortage = FALSE;
  3046. } else {
  3047. //
  3048. // The server is running short on uncommitted receive work items.
  3049. //
  3050. bufferShortage = TRUE;
  3051. }
  3052. return bufferShortage;
  3053. } // SrvReceiveBufferShortage
  3054. #if SMBDBG
  3055. //
  3056. // The following functions are defined in smbgtpt.h. When debug mode is
  3057. // disabled (!SMBDBG), these functions are instead defined as macros.
  3058. //
  3059. USHORT
  3060. SmbGetUshort (
  3061. IN PSMB_USHORT SrcAddress
  3062. )
  3063. {
  3064. return (USHORT)(
  3065. ( ( (PUCHAR)(SrcAddress) )[0] ) |
  3066. ( ( (PUCHAR)(SrcAddress) )[1] << 8 )
  3067. );
  3068. }
  3069. USHORT
  3070. SmbGetAlignedUshort (
  3071. IN PUSHORT SrcAddress
  3072. )
  3073. {
  3074. return *(SrcAddress);
  3075. }
  3076. VOID
  3077. SmbPutUshort (
  3078. OUT PSMB_USHORT DestAddress,
  3079. IN USHORT Value
  3080. )
  3081. {
  3082. ( (PUCHAR)(DestAddress) )[0] = BYTE_0(Value);
  3083. ( (PUCHAR)(DestAddress) )[1] = BYTE_1(Value);
  3084. return;
  3085. }
  3086. VOID
  3087. SmbPutAlignedUshort (
  3088. OUT PUSHORT DestAddress,
  3089. IN USHORT Value
  3090. )
  3091. {
  3092. *(DestAddress) = (Value);
  3093. return;
  3094. }
  3095. ULONG
  3096. SmbGetUlong (
  3097. IN PSMB_ULONG SrcAddress
  3098. )
  3099. {
  3100. return (ULONG)(
  3101. ( ( (PUCHAR)(SrcAddress) )[0] ) |
  3102. ( ( (PUCHAR)(SrcAddress) )[1] << 8 ) |
  3103. ( ( (PUCHAR)(SrcAddress) )[2] << 16 ) |
  3104. ( ( (PUCHAR)(SrcAddress) )[3] << 24 )
  3105. );
  3106. }
  3107. ULONG
  3108. SmbGetAlignedUlong (
  3109. IN PULONG SrcAddress
  3110. )
  3111. {
  3112. return *(SrcAddress);
  3113. }
  3114. VOID
  3115. SmbPutUlong (
  3116. OUT PSMB_ULONG DestAddress,
  3117. IN ULONG Value
  3118. )
  3119. {
  3120. ( (PUCHAR)(DestAddress) )[0] = BYTE_0(Value);
  3121. ( (PUCHAR)(DestAddress) )[1] = BYTE_1(Value);
  3122. ( (PUCHAR)(DestAddress) )[2] = BYTE_2(Value);
  3123. ( (PUCHAR)(DestAddress) )[3] = BYTE_3(Value);
  3124. return;
  3125. }
  3126. VOID
  3127. SmbPutAlignedUlong (
  3128. OUT PULONG DestAddress,
  3129. IN ULONG Value
  3130. )
  3131. {
  3132. *(DestAddress) = Value;
  3133. return;
  3134. }
  3135. VOID
  3136. SmbPutDate (
  3137. OUT PSMB_DATE DestAddress,
  3138. IN SMB_DATE Value
  3139. )
  3140. {
  3141. ( (PUCHAR)&(DestAddress)->Ushort )[0] = BYTE_0(Value.Ushort);
  3142. ( (PUCHAR)&(DestAddress)->Ushort )[1] = BYTE_1(Value.Ushort);
  3143. return;
  3144. }
  3145. VOID
  3146. SmbMoveDate (
  3147. OUT PSMB_DATE DestAddress,
  3148. IN PSMB_DATE SrcAddress
  3149. )
  3150. {
  3151. (DestAddress)->Ushort = (USHORT)(
  3152. ( ( (PUCHAR)&(SrcAddress)->Ushort )[0] ) |
  3153. ( ( (PUCHAR)&(SrcAddress)->Ushort )[1] << 8 ) );
  3154. return;
  3155. }
  3156. VOID
  3157. SmbZeroDate (
  3158. IN PSMB_DATE Date
  3159. )
  3160. {
  3161. (Date)->Ushort = 0;
  3162. }
  3163. BOOLEAN
  3164. SmbIsDateZero (
  3165. IN PSMB_DATE Date
  3166. )
  3167. {
  3168. return (BOOLEAN)( (Date)->Ushort == 0 );
  3169. }
  3170. VOID
  3171. SmbPutTime (
  3172. OUT PSMB_TIME DestAddress,
  3173. IN SMB_TIME Value
  3174. )
  3175. {
  3176. ( (PUCHAR)&(DestAddress)->Ushort )[0] = BYTE_0(Value.Ushort);
  3177. ( (PUCHAR)&(DestAddress)->Ushort )[1] = BYTE_1(Value.Ushort);
  3178. return;
  3179. }
  3180. VOID
  3181. SmbMoveTime (
  3182. OUT PSMB_TIME DestAddress,
  3183. IN PSMB_TIME SrcAddress
  3184. )
  3185. {
  3186. (DestAddress)->Ushort = (USHORT)(
  3187. ( ( (PUCHAR)&(SrcAddress)->Ushort )[0] ) |
  3188. ( ( (PUCHAR)&(SrcAddress)->Ushort )[1] << 8 ) );
  3189. return;
  3190. }
  3191. VOID
  3192. SmbZeroTime (
  3193. IN PSMB_TIME Time
  3194. )
  3195. {
  3196. (Time)->Ushort = 0;
  3197. }
  3198. BOOLEAN
  3199. SmbIsTimeZero (
  3200. IN PSMB_TIME Time
  3201. )
  3202. {
  3203. return (BOOLEAN)( (Time)->Ushort == 0 );
  3204. }
  3205. #endif // SMBDBG
  3206. NTSTATUS
  3207. SrvIoCreateFile (
  3208. IN PWORK_CONTEXT WorkContext,
  3209. OUT PHANDLE FileHandle,
  3210. IN ACCESS_MASK DesiredAccess,
  3211. IN POBJECT_ATTRIBUTES ObjectAttributes,
  3212. OUT PIO_STATUS_BLOCK IoStatusBlock,
  3213. IN PLARGE_INTEGER AllocationSize OPTIONAL,
  3214. IN ULONG FileAttributes,
  3215. IN ULONG ShareAccess,
  3216. IN ULONG Disposition,
  3217. IN ULONG CreateOptions,
  3218. IN PVOID EaBuffer OPTIONAL,
  3219. IN ULONG EaLength,
  3220. IN CREATE_FILE_TYPE CreateFileType,
  3221. IN PVOID ExtraCreateParameters OPTIONAL,
  3222. IN ULONG Options,
  3223. IN PSHARE Share OPTIONAL
  3224. )
  3225. {
  3226. PFILE_OBJECT fileObject;
  3227. PDEVICE_OBJECT deviceObject;
  3228. NTSTATUS status;
  3229. NTSTATUS tempStatus;
  3230. BOOLEAN dispositionModified = FALSE;
  3231. ULONG eventToLog = 0;
  3232. ULONG newUsage;
  3233. ULONG requiredSize;
  3234. SHARE_TYPE shareType = ShareTypeWild;
  3235. UNICODE_STRING fileName, *pName;
  3236. #if SRVDBG_STATS
  3237. LARGE_INTEGER timeStamp, currentTime;
  3238. LARGE_INTEGER timeDifference;
  3239. #endif
  3240. PAGED_CODE( );
  3241. IF_DEBUG( CREATE ) {
  3242. KdPrint(("\nSrvIoCreateFile:\n" ));
  3243. KdPrint((" Obja->ObjectName <%wZ>\n", ObjectAttributes->ObjectName ));
  3244. KdPrint((" Obja->Attributes %X,", ObjectAttributes->Attributes ));
  3245. KdPrint((" RootDirectory %p,", ObjectAttributes->RootDirectory ));
  3246. KdPrint((" SecurityDescriptor %p,", ObjectAttributes->SecurityDescriptor ));
  3247. KdPrint((" SecurityQOS %p\n", ObjectAttributes->SecurityQualityOfService ));
  3248. KdPrint((" DesiredAccess %X, FileAttributes %X, ShareAccess %X\n",
  3249. DesiredAccess, FileAttributes, ShareAccess ));
  3250. KdPrint((" Disposition %X, CreateOptions %X, EaLength %X\n",
  3251. Disposition, CreateOptions, EaLength ));
  3252. KdPrint((" CreateFileType %X, ExtraCreateParameters %p, Options %X\n",
  3253. CreateFileType, ExtraCreateParameters, Options ));
  3254. }
  3255. //
  3256. // See if this operation is allowed on this share
  3257. //
  3258. if( ARGUMENT_PRESENT( Share ) ) {
  3259. status = SrvIsAllowedOnAdminShare( WorkContext, Share );
  3260. if( !NT_SUCCESS( status ) ) {
  3261. IF_DEBUG( CREATE ) {
  3262. KdPrint(("Create disallowed on Admin Share: %X\n", status ));
  3263. }
  3264. return status;
  3265. }
  3266. }
  3267. //
  3268. // We do not allow the remote opening of structured storage files
  3269. //
  3270. if( (CreateOptions & FILE_STRUCTURED_STORAGE) == FILE_STRUCTURED_STORAGE ) {
  3271. IF_DEBUG( CREATE ) {
  3272. KdPrint(("Create FILE_STRUCTURED_STORAGE unsupported\n" ));
  3273. }
  3274. return STATUS_NOT_SUPPORTED;
  3275. }
  3276. //
  3277. // We do not allow opening files by ID. It is too easy to escape the share
  3278. //
  3279. if( CreateOptions & FILE_OPEN_BY_FILE_ID ) {
  3280. IF_DEBUG( CREATE ) {
  3281. KdPrint(("Create FILE_OPEN_BY_FILE_ID unsupported\n" ));
  3282. }
  3283. return STATUS_NOT_SUPPORTED;
  3284. }
  3285. //
  3286. // Make sure the client isn't trying to create a file having the name
  3287. // of a DOS device
  3288. //
  3289. SrvGetBaseFileName( ObjectAttributes->ObjectName, &fileName );
  3290. for( pName = SrvDosDevices; pName->Length; pName++ ) {
  3291. if( pName->Length == fileName.Length &&
  3292. RtlCompareUnicodeString( pName, &fileName, TRUE ) == 0 ) {
  3293. //
  3294. // Whoa! We don't want clients trying to create files having a
  3295. // DOS device name
  3296. //
  3297. IF_DEBUG( CREATE ) {
  3298. KdPrint(("Create open %wZ unsupported\n", &fileName ));
  3299. }
  3300. return STATUS_ACCESS_DENIED;
  3301. }
  3302. }
  3303. //
  3304. // If this is from the NULL session, allow it to open only certain
  3305. // pipes.
  3306. //
  3307. if ( CreateFileType != CreateFileTypeMailslot ) {
  3308. shareType = WorkContext->TreeConnect->Share->ShareType;
  3309. if( shareType == ShareTypePipe ) {
  3310. if ( WorkContext->Session->IsNullSession ) {
  3311. if( SrvRestrictNullSessionAccess ) {
  3312. BOOLEAN matchFound = FALSE;
  3313. ULONG i;
  3314. ACQUIRE_LOCK( &SrvConfigurationLock );
  3315. for ( i = 0; SrvNullSessionPipes[i] != NULL ; i++ ) {
  3316. if ( _wcsicmp(
  3317. SrvNullSessionPipes[i],
  3318. ObjectAttributes->ObjectName->Buffer
  3319. ) == 0 ) {
  3320. matchFound = TRUE;
  3321. break;
  3322. }
  3323. }
  3324. RELEASE_LOCK( &SrvConfigurationLock );
  3325. if ( !matchFound ) {
  3326. IF_DEBUG( CREATE ) {
  3327. KdPrint(( "Create via NULL session denied\n" ));
  3328. }
  3329. return(STATUS_ACCESS_DENIED);
  3330. }
  3331. }
  3332. } else if( WorkContext->Session->IsLSNotified == FALSE ) {
  3333. //
  3334. // We have a pipe open request, not a NULL session, and
  3335. // we haven't gotten clearance from the license server yet.
  3336. // If this pipe requires clearance, get a license.
  3337. //
  3338. ULONG i;
  3339. BOOLEAN matchFound = FALSE;
  3340. ACQUIRE_LOCK( &SrvConfigurationLock );
  3341. for ( i = 0; SrvPipesNeedLicense[i] != NULL ; i++ ) {
  3342. if ( _wcsicmp(
  3343. SrvPipesNeedLicense[i],
  3344. ObjectAttributes->ObjectName->Buffer
  3345. ) == 0 ) {
  3346. matchFound = TRUE;
  3347. break;
  3348. }
  3349. }
  3350. RELEASE_LOCK( &SrvConfigurationLock );
  3351. if( matchFound == TRUE ) {
  3352. status = SrvXsLSOperation( WorkContext->Session,
  3353. XACTSRV_MESSAGE_LSREQUEST );
  3354. if( !NT_SUCCESS( status ) ) {
  3355. IF_DEBUG( CREATE ) {
  3356. KdPrint(( "Create failed due to license server: %X\n", status ));
  3357. }
  3358. return status;
  3359. }
  3360. }
  3361. }
  3362. }
  3363. //
  3364. // !!! a hack to handle a bug in the Object system and path-based
  3365. // operations on print shares. to test a fix, try from OS/2:
  3366. //
  3367. // copy config.sys \\server\printshare
  3368. //
  3369. if ( Share == NULL &&
  3370. ObjectAttributes->ObjectName->Length == 0 &&
  3371. ObjectAttributes->RootDirectory == NULL ) {
  3372. IF_DEBUG( CREATE ) {
  3373. KdPrint(("Create failed: ObjectName Len == 0, an ! Root directory\n" ));
  3374. }
  3375. return STATUS_OBJECT_PATH_SYNTAX_BAD;
  3376. }
  3377. //
  3378. // Check desired access against share ACL.
  3379. //
  3380. // This gets a little hairy. Basically, we simply want to check the
  3381. // desired access against the ACL. But this doesn't correctly
  3382. // handle the case where the client only has read access to the
  3383. // share, and wants to (perhaps optionally) create (or overwrite) a
  3384. // file or directory, but only asks for read access to the file. We
  3385. // deal with this problem by, in effect, adding write access to the
  3386. // access requested by the client if the client specifies a
  3387. // dispostion mode that will or may create or overwrite the file.
  3388. //
  3389. // If the client specifies a dispostion that WILL create or
  3390. // overwrite (CREATE, SUPERSEDE, OVERWRITE, or OVERWRITE_IF), we
  3391. // turn on the FILE_WRITE_DATA (aka FILE_ADD_FILE) and
  3392. // FILE_APPEND_DATA (aka FILE_ADD_SUBDIRECTORY) accesses. If the
  3393. // access check fails, we return STATUS_ACCESS_DENIED.
  3394. //
  3395. // If the client specifies optional creation, then we have to be
  3396. // even more tricky. We don't know if the file actually exists, so
  3397. // we can't just reject the request out-of-hand, because if the file
  3398. // does exist, and the client really does have read access to the
  3399. // file, it will look weird if we deny the open. So in this case we
  3400. // turn the OPEN_IF request into an OPEN (fail if doesn't exist)
  3401. // request. If the open fails because the file doesn't exist, we
  3402. // return STATUS_ACCESS_DENIED.
  3403. //
  3404. // Note that this method effectively means that the share ACL cannot
  3405. // distinguish between a user who can write to existing files but
  3406. // who cannot create new files. This is because of the overloading
  3407. // of FILE_WRITE_DATA/FILE_ADD_FILE and
  3408. // FILE_APPEND_DATA/FILE_ADD_SUBDIRECTORY.
  3409. //
  3410. //
  3411. // OK. First, check the access exactly as requested.
  3412. //
  3413. status = SrvCheckShareFileAccess( WorkContext, DesiredAccess );
  3414. if ( !NT_SUCCESS( status )) {
  3415. //
  3416. // Some clients want ACCESS_DENIED to be in the server class
  3417. // instead of the DOS class when it's due to share ACL
  3418. // restrictions. So we need to keep track of why we're
  3419. // returning ACCESS_DENIED.
  3420. //
  3421. IF_DEBUG( CREATE ) {
  3422. KdPrint(("Create failed, SrvCheckShareFileAccess returns %X\n", status ));
  3423. }
  3424. WorkContext->ShareAclFailure = TRUE;
  3425. return STATUS_ACCESS_DENIED;
  3426. }
  3427. } else {
  3428. //
  3429. // Set it to CreateFileTypeNone so no extra checking is done.
  3430. //
  3431. CreateFileType = CreateFileTypeNone;
  3432. }
  3433. //
  3434. // That worked. Now, if the Disposition may or will create or
  3435. // overwrite, do more checking.
  3436. //
  3437. if ( Disposition != FILE_OPEN ) {
  3438. status = SrvCheckShareFileAccess(
  3439. WorkContext,
  3440. DesiredAccess | FILE_WRITE_DATA | FILE_APPEND_DATA
  3441. );
  3442. if ( !NT_SUCCESS( status )) {
  3443. //
  3444. // The client cannot create or overwrite files. Unless
  3445. // they asked for FILE_OPEN_IF, jump out now.
  3446. //
  3447. if ( Disposition != FILE_OPEN_IF ) {
  3448. //
  3449. // Some clients want ACCESS_DENIED to be in the server class
  3450. // instead of the DOS class when it's due to share ACL
  3451. // restrictions. So we need to keep track of why we're
  3452. // returning ACCESS_DENIED.
  3453. //
  3454. IF_DEBUG( CREATE ) {
  3455. KdPrint(("Create failed, SrvCheckShareFileAccess returns ACCESS_DENIED\n"));
  3456. }
  3457. WorkContext->ShareAclFailure = TRUE;
  3458. return STATUS_ACCESS_DENIED;
  3459. }
  3460. //
  3461. // Change OPEN_IF to OPEN, and remember that we did it.
  3462. //
  3463. Disposition = FILE_OPEN;
  3464. dispositionModified = TRUE;
  3465. }
  3466. }
  3467. //
  3468. // If this client is reading from the file, turn off FILE_SEQUENTIAL_ONLY in case
  3469. // caching this file would be beneficial to other clients.
  3470. //
  3471. if( shareType == ShareTypeDisk &&
  3472. !(DesiredAccess & (FILE_WRITE_DATA|FILE_APPEND_DATA)) ) {
  3473. CreateOptions &= ~FILE_SEQUENTIAL_ONLY;
  3474. }
  3475. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  3476. //
  3477. // Make sure that this open will not push the server over its
  3478. // nonpaged and paged quotas.
  3479. //
  3480. newUsage = InterlockedExchangeAdd(
  3481. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  3482. IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  3483. ) + IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE;
  3484. if ( newUsage > SrvMaxNonPagedPoolUsage ) {
  3485. status = STATUS_INSUFF_SERVER_RESOURCES;
  3486. eventToLog = EVENT_SRV_NONPAGED_POOL_LIMIT;
  3487. goto error_exit1;
  3488. }
  3489. if ( SrvStatistics.CurrentNonPagedPoolUsage > SrvStatistics.PeakNonPagedPoolUsage) {
  3490. SrvStatistics.PeakNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
  3491. }
  3492. }
  3493. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  3494. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3495. newUsage = InterlockedExchangeAdd(
  3496. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  3497. IO_FILE_OBJECT_PAGED_POOL_CHARGE
  3498. ) + IO_FILE_OBJECT_PAGED_POOL_CHARGE;
  3499. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3500. if ( newUsage > SrvMaxPagedPoolUsage ) {
  3501. status = STATUS_INSUFF_SERVER_RESOURCES;
  3502. eventToLog = EVENT_SRV_PAGED_POOL_LIMIT;
  3503. goto error_exit;
  3504. }
  3505. if ( SrvStatistics.CurrentPagedPoolUsage > SrvStatistics.PeakPagedPoolUsage) {
  3506. SrvStatistics.PeakPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
  3507. }
  3508. }
  3509. //
  3510. // If Share is specified, we may need to fill up the root share
  3511. // handle of the object attribute.
  3512. //
  3513. if ( ARGUMENT_PRESENT( Share ) && (shareType != ShareTypePrint) ) {
  3514. //
  3515. // Get the Share root handle.
  3516. //
  3517. status = SrvGetShareRootHandle( Share );
  3518. if ( !NT_SUCCESS(status) ) {
  3519. IF_DEBUG(CREATE) {
  3520. KdPrint(( "SrvIoCreateFile: SrvGetShareRootHandle failed: %X\n",
  3521. status ));
  3522. }
  3523. goto error_exit;
  3524. }
  3525. //
  3526. // Fill in the root handle.
  3527. //
  3528. status = SrvSnapGetRootHandle( WorkContext, &ObjectAttributes->RootDirectory );
  3529. if( !NT_SUCCESS( status ) )
  3530. {
  3531. goto error_exit;
  3532. }
  3533. }
  3534. //
  3535. // Impersonate the client. This makes us look like the client for
  3536. // the purpose of checking security. Don't do impersonation if
  3537. // this is a spool file since spool files have admin all access,
  3538. // everybody read access by definition.
  3539. //
  3540. status = STATUS_SUCCESS;
  3541. if ( shareType != ShareTypePrint ) {
  3542. status = IMPERSONATE( WorkContext );
  3543. }
  3544. #if SRVDBG_STATS
  3545. //
  3546. // Get the system time for statistics tracking.
  3547. //
  3548. KeQuerySystemTime( &timeStamp );
  3549. #endif
  3550. //
  3551. // Perform the actual open.
  3552. //
  3553. // *** Do not lose the status returned by IoCreateFile! Even if
  3554. // it's a success code. The caller needs to know if it's
  3555. // STATUS_OPLOCK_BREAK_IN_PROGRESS.
  3556. //
  3557. if( NT_SUCCESS( status ) ) {
  3558. status = IoCreateFile(
  3559. FileHandle,
  3560. DesiredAccess,
  3561. ObjectAttributes,
  3562. IoStatusBlock,
  3563. AllocationSize,
  3564. FileAttributes,
  3565. ShareAccess,
  3566. Disposition,
  3567. CreateOptions,
  3568. EaBuffer,
  3569. EaLength,
  3570. CreateFileType,
  3571. ExtraCreateParameters,
  3572. Options
  3573. );
  3574. //
  3575. // If the volume was dismounted, and we can refresh the share root handle,
  3576. // we should try the operation again.
  3577. //
  3578. if( ARGUMENT_PRESENT( Share ) && SrvRetryDueToDismount( Share, status ) ) {
  3579. status = SrvSnapGetRootHandle( WorkContext, &ObjectAttributes->RootDirectory );
  3580. if( !NT_SUCCESS( status ) )
  3581. {
  3582. goto error_exit;
  3583. }
  3584. status = IoCreateFile(
  3585. FileHandle,
  3586. DesiredAccess,
  3587. ObjectAttributes,
  3588. IoStatusBlock,
  3589. AllocationSize,
  3590. FileAttributes,
  3591. ShareAccess,
  3592. Disposition,
  3593. CreateOptions,
  3594. EaBuffer,
  3595. EaLength,
  3596. CreateFileType,
  3597. ExtraCreateParameters,
  3598. Options
  3599. );
  3600. }
  3601. }
  3602. #if SRVDBG_STATS
  3603. //
  3604. // Grab the time again.
  3605. //
  3606. KeQuerySystemTime( &currentTime );
  3607. #endif
  3608. //
  3609. // Go back to the server's security context.
  3610. //
  3611. if ( shareType != ShareTypePrint ) {
  3612. //
  3613. // Calling REVERT() even if the impersonate failed is harmless
  3614. //
  3615. REVERT( );
  3616. }
  3617. #if SRVDBG_STATS
  3618. //
  3619. // Determine how long the IoCreateFile took.
  3620. //
  3621. timeDifference.QuadPart = currentTime.QuadPart - timeStamp.QuadPart;
  3622. //
  3623. // Update statistics, including server pool quota statistics if the
  3624. // open didn't succeed.
  3625. //
  3626. ExInterlockedAddLargeInteger(
  3627. &SrvDbgStatistics.TotalIoCreateFileTime,
  3628. timeDifference,
  3629. &GLOBAL_SPIN_LOCK(Statistics)
  3630. );
  3631. #endif
  3632. //
  3633. // Release the share root handle
  3634. //
  3635. if ( ARGUMENT_PRESENT( Share ) ) {
  3636. SrvReleaseShareRootHandle( Share );
  3637. }
  3638. if ( NT_SUCCESS(status) ) {
  3639. IF_DEBUG( CREATE ) {
  3640. KdPrint(( " ** %wZ, handle %p\n",
  3641. ObjectAttributes->ObjectName, *FileHandle ));
  3642. }
  3643. tempStatus = SrvVerifyDeviceStackSize(
  3644. *FileHandle,
  3645. TRUE,
  3646. &fileObject,
  3647. &deviceObject,
  3648. NULL
  3649. );
  3650. if ( !NT_SUCCESS( tempStatus )) {
  3651. INTERNAL_ERROR(
  3652. ERROR_LEVEL_EXPECTED,
  3653. "SrvIoCreateFile: Verify Device Stack Size failed: %X\n",
  3654. tempStatus,
  3655. NULL
  3656. );
  3657. SRVDBG_RELEASE_HANDLE( *FileHandle, "FIL", 50, 0 );
  3658. SrvNtClose( *FileHandle, FALSE );
  3659. status = tempStatus;
  3660. } else {
  3661. //
  3662. // Mark the orgin of this file as remote. This should
  3663. // never fail. If it does, it means it is already set.
  3664. // Check for this in a debug build, but ignore errors
  3665. // in the retail build.
  3666. //
  3667. #if DBG
  3668. tempStatus =
  3669. #endif
  3670. IoSetFileOrigin( fileObject,
  3671. TRUE );
  3672. ASSERT( tempStatus == STATUS_SUCCESS );
  3673. //
  3674. // Remove the reference we added in SrvVerifyDeviceStackSize().
  3675. //
  3676. ObDereferenceObject( fileObject );
  3677. }
  3678. }
  3679. if ( !NT_SUCCESS(status) ) {
  3680. goto error_exit;
  3681. }
  3682. IF_DEBUG(HANDLES) {
  3683. if ( NT_SUCCESS(status) ) {
  3684. PVOID caller, callersCaller;
  3685. RtlGetCallersAddress( &caller, &callersCaller );
  3686. KdPrint(( "opened handle %p for %wZ (%p %p)\n",
  3687. *FileHandle, ObjectAttributes->ObjectName,
  3688. caller, callersCaller ));
  3689. }
  3690. }
  3691. IF_DEBUG( CREATE ) {
  3692. KdPrint((" Status %X\n", status ));
  3693. }
  3694. return status;
  3695. error_exit:
  3696. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  3697. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= IO_FILE_OBJECT_PAGED_POOL_CHARGE );
  3698. InterlockedExchangeAdd(
  3699. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  3700. -IO_FILE_OBJECT_PAGED_POOL_CHARGE
  3701. );
  3702. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3703. }
  3704. error_exit1:
  3705. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  3706. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE );
  3707. InterlockedExchangeAdd(
  3708. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  3709. -IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  3710. );
  3711. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  3712. }
  3713. if ( status == STATUS_INSUFF_SERVER_RESOURCES ) {
  3714. requiredSize = ((eventToLog == EVENT_SRV_NONPAGED_POOL_LIMIT) ?
  3715. IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE :
  3716. IO_FILE_OBJECT_PAGED_POOL_CHARGE);
  3717. INTERNAL_ERROR(
  3718. ERROR_LEVEL_EXPECTED,
  3719. "SrvIoCreateFile: nonpaged pool limit reached, current = %ld, max = %ld\n",
  3720. newUsage - requiredSize,
  3721. SrvMaxNonPagedPoolUsage
  3722. );
  3723. SrvLogError(
  3724. SrvDeviceObject,
  3725. eventToLog,
  3726. STATUS_INSUFFICIENT_RESOURCES,
  3727. &requiredSize,
  3728. sizeof(ULONG),
  3729. NULL,
  3730. 0
  3731. );
  3732. } else {
  3733. //
  3734. // Finish up the access checking started above. If the open failed
  3735. // because the file didn't exist, and we turned off the create-if
  3736. // mode because the client doesn't have write access, change the
  3737. // status to STATUS_ACCESS_DENIED.
  3738. //
  3739. if ( dispositionModified && (status == STATUS_OBJECT_NAME_NOT_FOUND) ) {
  3740. status = STATUS_ACCESS_DENIED;
  3741. }
  3742. }
  3743. IF_DEBUG( CREATE ) {
  3744. KdPrint((" Status %X\n", status ));
  3745. }
  3746. return status;
  3747. } // SrvIoCreateFile
  3748. NTSTATUS
  3749. SrvNtClose (
  3750. IN HANDLE Handle,
  3751. IN BOOLEAN QuotaCharged
  3752. )
  3753. /*++
  3754. Routine Description:
  3755. Closes a handle, records statistics for number of handles closed,
  3756. total time spent closing handles.
  3757. Arguments:
  3758. Handle - the handle to close.
  3759. QuotaCharged - indicates whether the server's internal quota for
  3760. paged and nonpaged pool was charged for this open.
  3761. Return Value:
  3762. NTSTATUS - result of operation.
  3763. --*/
  3764. {
  3765. NTSTATUS status;
  3766. #if SRVDBG_STATS
  3767. LARGE_INTEGER timeStamp, currentTime;
  3768. LARGE_INTEGER timeDifference;
  3769. #endif
  3770. PEPROCESS process;
  3771. PAGED_CODE( );
  3772. #if SRVDBG_STATS
  3773. //
  3774. // SnapShot the system time.
  3775. //
  3776. KeQuerySystemTime( &timeStamp );
  3777. #endif
  3778. //
  3779. // Make sure we're in the server FSP.
  3780. //
  3781. process = IoGetCurrentProcess();
  3782. if ( process != SrvServerProcess ) {
  3783. //KdPrint(( "SRV: Closing handle %x in process %x\n", Handle, process ));
  3784. KeAttachProcess( SrvServerProcess );
  3785. }
  3786. IF_DEBUG( CREATE ) {
  3787. KdPrint(( "SrvNtClose handle %p\n", Handle ));
  3788. }
  3789. //
  3790. // Close the handle.
  3791. //
  3792. status = NtClose( Handle );
  3793. //
  3794. // Return to the original process.
  3795. //
  3796. if ( process != SrvServerProcess ) {
  3797. KeDetachProcess();
  3798. }
  3799. IF_DEBUG( ERRORS ) {
  3800. if ( !NT_SUCCESS( status ) ) {
  3801. KdPrint(( "SRV: NtClose failed: %x\n", status ));
  3802. DbgBreakPoint( );
  3803. }
  3804. }
  3805. ASSERT( NT_SUCCESS( status ) );
  3806. #if SRVDBG_STATS
  3807. //
  3808. // Get the time again.
  3809. //
  3810. KeQuerySystemTime( &currentTime );
  3811. //
  3812. // Determine how long the close took.
  3813. //
  3814. timeDifference.QuadPart = currentTime.QuadPart - timeStamp.QuadPart;
  3815. #endif
  3816. //
  3817. // Update the relevant statistics, including server quota statistics.
  3818. //
  3819. #if SRVDBG_STATS
  3820. SrvDbgStatistics.TotalNtCloseTime.QuadPart += timeDifference.QuadPart;
  3821. #endif
  3822. if ( QuotaCharged ) {
  3823. if( SrvMaxPagedPoolUsage != 0xFFFFFFFF ) {
  3824. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3825. InterlockedExchangeAdd(
  3826. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  3827. -(LONG)IO_FILE_OBJECT_PAGED_POOL_CHARGE
  3828. );
  3829. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  3830. }
  3831. if( SrvMaxNonPagedPoolUsage != 0xFFFFFFFF ) {
  3832. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  3833. InterlockedExchangeAdd(
  3834. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  3835. -(LONG)IO_FILE_OBJECT_NON_PAGED_POOL_CHARGE
  3836. );
  3837. ASSERT( (LONG)SrvStatistics.CurrentNonPagedPoolUsage >= 0 );
  3838. }
  3839. }
  3840. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalHandlesClosed );
  3841. IF_DEBUG(HANDLES) {
  3842. PVOID caller, callersCaller;
  3843. RtlGetCallersAddress( &caller, &callersCaller );
  3844. if ( NT_SUCCESS(status) ) {
  3845. KdPrint(( "closed handle %p (%p %p)\n",
  3846. Handle, caller, callersCaller ));
  3847. } else {
  3848. KdPrint(( "closed handle %p (%p %p) FAILED: %X\n",
  3849. Handle, caller, callersCaller, status ));
  3850. }
  3851. }
  3852. return STATUS_SUCCESS;
  3853. } // SrvNtClose
  3854. NTSTATUS
  3855. SrvVerifyDeviceStackSize (
  3856. IN HANDLE FileHandle,
  3857. IN BOOLEAN ReferenceFileObject,
  3858. OUT PFILE_OBJECT *FileObject,
  3859. OUT PDEVICE_OBJECT *DeviceObject,
  3860. OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
  3861. )
  3862. /*++
  3863. Routine Description:
  3864. Ths routine references the file object associated with the
  3865. file handle and checks whether our work item has sufficient
  3866. irp stack size to handle requests to this device or the device
  3867. associated with this file.
  3868. Arguments:
  3869. FileHandle - The handle to an open device or file
  3870. ReferenceFileObject - if TRUE, the file object is left referenced.
  3871. FileObject - The file object associated with the filehandle.
  3872. DeviceObject - the device object associated with the filehandle.
  3873. HandleInformation - if not NULL, returns information about the file handle.
  3874. Return Value:
  3875. Status of request.
  3876. --*/
  3877. {
  3878. NTSTATUS status;
  3879. PAGED_CODE( );
  3880. //
  3881. // Get a pointer to the file object, so that we can directly
  3882. // get the related device object that should contain a count
  3883. // of the irp stack size needed by that device.
  3884. //
  3885. status = ObReferenceObjectByHandle(
  3886. FileHandle,
  3887. 0,
  3888. NULL,
  3889. KernelMode,
  3890. (PVOID *)FileObject,
  3891. HandleInformation
  3892. );
  3893. if ( !NT_SUCCESS(status) ) {
  3894. SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
  3895. //
  3896. // This internal error bugchecks the system.
  3897. //
  3898. INTERNAL_ERROR(
  3899. ERROR_LEVEL_IMPOSSIBLE,
  3900. "SrvVerifyDeviceStackSize: unable to reference file handle 0x%lx",
  3901. FileHandle,
  3902. NULL
  3903. );
  3904. } else {
  3905. *DeviceObject = IoGetRelatedDeviceObject( *FileObject );
  3906. if ( (*DeviceObject)->StackSize > SrvReceiveIrpStackSize ) {
  3907. INTERNAL_ERROR(
  3908. ERROR_LEVEL_UNEXPECTED,
  3909. "SrvVerifyStackSize: WorkItem Irp StackSize too small. Need %d Allocated %d\n",
  3910. (*DeviceObject)->StackSize+1,
  3911. SrvReceiveIrpStackSize
  3912. );
  3913. SrvLogSimpleEvent( EVENT_SRV_IRP_STACK_SIZE, STATUS_SUCCESS );
  3914. ObDereferenceObject( *FileObject );
  3915. *FileObject = NULL;
  3916. status = STATUS_INSUFF_SERVER_RESOURCES;
  3917. } else if ( !ReferenceFileObject ) {
  3918. ObDereferenceObject( *FileObject );
  3919. *FileObject = NULL;
  3920. }
  3921. }
  3922. return status;
  3923. } // SrvVerifyDeviceStackSize
  3924. NTSTATUS
  3925. SrvImpersonate (
  3926. IN PWORK_CONTEXT WorkContext
  3927. )
  3928. /*++
  3929. Routine Description:
  3930. Impersonates the remote client specified in the Session pointer
  3931. of the work context block.
  3932. Arguments:
  3933. WorkContext - a work context block containing a valid pointer to
  3934. a session block.
  3935. Return Value:
  3936. status code of the attempt
  3937. --*/
  3938. {
  3939. NTSTATUS status;
  3940. PAGED_CODE( );
  3941. ASSERT( WorkContext->Session != NULL );
  3942. if( !IS_VALID_SECURITY_HANDLE (WorkContext->Session->UserHandle) ) {
  3943. return STATUS_ACCESS_DENIED;
  3944. }
  3945. status = ImpersonateSecurityContext(
  3946. &WorkContext->Session->UserHandle
  3947. );
  3948. if ( !NT_SUCCESS(status) ) {
  3949. INTERNAL_ERROR(
  3950. ERROR_LEVEL_UNEXPECTED,
  3951. "IMPERSONATE: NtSetInformationThread failed: %X",
  3952. status,
  3953. NULL
  3954. );
  3955. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_THREAD, status );
  3956. }
  3957. return status;
  3958. } // SrvImpersonate
  3959. VOID
  3960. SrvRevert (
  3961. VOID
  3962. )
  3963. /*++
  3964. Routine Description:
  3965. Reverts to the server FSP's default thread context.
  3966. Arguments:
  3967. None.
  3968. Return Value:
  3969. None.
  3970. --*/
  3971. {
  3972. NTSTATUS status;
  3973. PAGED_CODE( );
  3974. status = PsAssignImpersonationToken(PsGetCurrentThread(),NULL);
  3975. if ( !NT_SUCCESS(status) ) {
  3976. INTERNAL_ERROR(
  3977. ERROR_LEVEL_UNEXPECTED,
  3978. "REVERT: NtSetInformationThread failed: %X",
  3979. status,
  3980. NULL
  3981. );
  3982. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_THREAD, status );
  3983. }
  3984. return;
  3985. } // SrvRevert
  3986. #ifdef INCLUDE_SMB_IFMODIFIED
  3987. NTSTATUS
  3988. SrvSetLastWriteTime (
  3989. IN PRFCB Rfcb,
  3990. IN ULONG LastWriteTimeInSeconds,
  3991. IN ACCESS_MASK GrantedAccess,
  3992. IN BOOLEAN ForceChanges
  3993. )
  3994. #else
  3995. NTSTATUS
  3996. SrvSetLastWriteTime (
  3997. IN PRFCB Rfcb,
  3998. IN ULONG LastWriteTimeInSeconds,
  3999. IN ACCESS_MASK GrantedAccess
  4000. )
  4001. #endif
  4002. /*++
  4003. Routine Description:
  4004. Sets the last write time on a file if the specified handle has
  4005. sufficient access. This is used by the Close and Create SMBs to
  4006. ensure that file times on server files are consistent with
  4007. times on clients.
  4008. Arguments:
  4009. Rfcb - pointer to the rfcb block that is associated with the file
  4010. which we need to set the lastwrite time on.
  4011. LastWriteTimeInSeconds - the time, in seconds since 1970, to put
  4012. on the file. If it is 0 or -1, the last write time is not
  4013. changed.
  4014. GrantedAccess - an access mask specifying the access the specified
  4015. handle has. If it has insufficient access, the file time is
  4016. not changed.
  4017. ForceChanges - flag set to true if we want to send down the
  4018. SetFileInfo anyway even if the client specified a zero lastWriteTime.
  4019. Useful to force the filesystem to update the file control block now
  4020. rather than at the close.
  4021. Return Value:
  4022. NTSTATUS - result of operation.
  4023. --*/
  4024. {
  4025. NTSTATUS status;
  4026. FILE_BASIC_INFORMATION fileBasicInfo;
  4027. IO_STATUS_BLOCK ioStatusBlock;
  4028. PAGED_CODE( );
  4029. //
  4030. // If the client doesn't want to set the time, don't set it.
  4031. //
  4032. #ifdef INCLUDE_SMB_IFMODIFIED
  4033. if (ForceChanges && LastWriteTimeInSeconds == 0xFFFFFFFF) {
  4034. LastWriteTimeInSeconds = 0;
  4035. }
  4036. if ( Rfcb->ShareType != ShareTypeDisk ||
  4037. ( ( LastWriteTimeInSeconds == 0 ) && ! ForceChanges ) ||
  4038. ( LastWriteTimeInSeconds == 0xFFFFFFFF ) ) {
  4039. #else
  4040. if ( Rfcb->ShareType != ShareTypeDisk ||
  4041. LastWriteTimeInSeconds == 0 ||
  4042. LastWriteTimeInSeconds == 0xFFFFFFFF ) {
  4043. #endif
  4044. //
  4045. // If the file was written to, we won't cache the file. This is to
  4046. // ensure the file directory entry gets updated by the file system.
  4047. //
  4048. if ( Rfcb->WrittenTo ) {
  4049. Rfcb->IsCacheable = FALSE;
  4050. }
  4051. return STATUS_SUCCESS;
  4052. }
  4053. //
  4054. // Make sure that we have the correct access on the specified handle.
  4055. //
  4056. CHECK_FILE_INFORMATION_ACCESS(
  4057. GrantedAccess,
  4058. IRP_MJ_SET_INFORMATION,
  4059. FileBasicInformation,
  4060. &status
  4061. );
  4062. if ( !NT_SUCCESS(status) ) {
  4063. return status;
  4064. }
  4065. //
  4066. // Set to 0 the fields we don't want to change.
  4067. //
  4068. fileBasicInfo.CreationTime.QuadPart = 0;
  4069. fileBasicInfo.LastAccessTime.QuadPart = 0;
  4070. fileBasicInfo.ChangeTime.QuadPart = 0;
  4071. fileBasicInfo.FileAttributes = 0;
  4072. //
  4073. // Set up the last write time.
  4074. //
  4075. #ifdef INCLUDE_SMB_IFMODIFIED
  4076. if (LastWriteTimeInSeconds == 0) {
  4077. fileBasicInfo.LastWriteTime.QuadPart = 0;
  4078. } else {
  4079. #endif
  4080. RtlSecondsSince1970ToTime(
  4081. LastWriteTimeInSeconds,
  4082. &fileBasicInfo.LastWriteTime
  4083. );
  4084. ExLocalTimeToSystemTime(
  4085. &fileBasicInfo.LastWriteTime,
  4086. &fileBasicInfo.LastWriteTime
  4087. );
  4088. #ifdef INCLUDE_SMB_IFMODIFIED
  4089. }
  4090. #endif
  4091. //
  4092. // Set the time using the passed-in file handle.
  4093. //
  4094. status = NtSetInformationFile(
  4095. Rfcb->Lfcb->FileHandle,
  4096. &ioStatusBlock,
  4097. &fileBasicInfo,
  4098. sizeof(FILE_BASIC_INFORMATION),
  4099. FileBasicInformation
  4100. );
  4101. if ( !NT_SUCCESS(status) ) {
  4102. INTERNAL_ERROR(
  4103. ERROR_LEVEL_UNEXPECTED,
  4104. "SrvSetLastWriteTime: NtSetInformationFile returned %X",
  4105. status,
  4106. NULL
  4107. );
  4108. SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );
  4109. return status;
  4110. }
  4111. return STATUS_SUCCESS;
  4112. } // SrvSetLastWriteTime
  4113. NTSTATUS
  4114. SrvCheckShareFileAccess(
  4115. IN PWORK_CONTEXT WorkContext,
  4116. IN ACCESS_MASK FileDesiredAccess
  4117. )
  4118. /*++
  4119. Routine Description:
  4120. This routine checks the desired access against the permissions
  4121. set for this client.
  4122. Arguments:
  4123. WorkContext - pointer to the work context block that contains information
  4124. about the request.
  4125. FileDesiredAccess - the desired access.
  4126. Return Value:
  4127. Status of operation.
  4128. --*/
  4129. {
  4130. NTSTATUS status = STATUS_SUCCESS;
  4131. SECURITY_SUBJECT_CONTEXT subjectContext;
  4132. PSECURITY_DESCRIPTOR securityDescriptor;
  4133. ACCESS_MASK grantedAccess;
  4134. ACCESS_MASK mappedAccess = FileDesiredAccess;
  4135. PPRIVILEGE_SET privileges = NULL;
  4136. PAGED_CODE( );
  4137. ACQUIRE_LOCK_SHARED( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  4138. securityDescriptor = WorkContext->TreeConnect->Share->FileSecurityDescriptor;
  4139. if (securityDescriptor != NULL) {
  4140. status = IMPERSONATE( WorkContext );
  4141. if( NT_SUCCESS( status ) ) {
  4142. SeCaptureSubjectContext( &subjectContext );
  4143. RtlMapGenericMask( &mappedAccess, &SrvFileAccessMapping );
  4144. //
  4145. // SYNCHRONIZE does not make any sense for a share ACL
  4146. //
  4147. mappedAccess &= ~SYNCHRONIZE;
  4148. if ( !SeAccessCheck(
  4149. securityDescriptor,
  4150. &subjectContext,
  4151. FALSE, // Locked ?
  4152. mappedAccess,
  4153. 0, // PreviousGrantedAccess
  4154. &privileges,
  4155. &SrvFileAccessMapping,
  4156. UserMode,
  4157. &grantedAccess,
  4158. &status
  4159. ) ) {
  4160. IF_DEBUG(ERRORS) {
  4161. KdPrint((
  4162. "SrvCheckShareFileAccess: Status %x, Desired access %x, mappedAccess %x\n",
  4163. status,
  4164. FileDesiredAccess,
  4165. mappedAccess
  4166. ));
  4167. }
  4168. }
  4169. if ( privileges != NULL ) {
  4170. SeFreePrivileges( privileges );
  4171. }
  4172. SeReleaseSubjectContext( &subjectContext );
  4173. REVERT( );
  4174. }
  4175. }
  4176. RELEASE_LOCK( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  4177. return status;
  4178. } // SrvCheckShareFileAccess
  4179. VOID
  4180. SrvReleaseShareRootHandle (
  4181. IN PSHARE Share
  4182. )
  4183. /*++
  4184. Routine Description:
  4185. This routine releases the root handle for a given share if the
  4186. shared device is removable (floopy, or cdrom).
  4187. Arguments:
  4188. Share - The share for which the root directory handle is to be released.
  4189. Return Value:
  4190. None.
  4191. --*/
  4192. {
  4193. PAGED_CODE( );
  4194. if ( Share->Removable ) {
  4195. ASSERT( Share->CurrentRootHandleReferences > 0 );
  4196. ACQUIRE_LOCK( &SrvShareLock );
  4197. if ( --Share->CurrentRootHandleReferences == 0 ) {
  4198. ASSERT( Share->RootDirectoryHandle != NULL );
  4199. SRVDBG_RELEASE_HANDLE( Share->RootDirectoryHandle, "RTD", 51, Share );
  4200. SrvNtClose( Share->RootDirectoryHandle, FALSE );
  4201. Share->RootDirectoryHandle = NULL;
  4202. SrvDereferenceShare( Share );
  4203. }
  4204. RELEASE_LOCK( &SrvShareLock );
  4205. }
  4206. return;
  4207. } // SrvReleaseShareRootHandle
  4208. VOID
  4209. SrvUpdateVcQualityOfService (
  4210. IN PCONNECTION Connection,
  4211. IN PLARGE_INTEGER CurrentTime OPTIONAL
  4212. )
  4213. /*++
  4214. Routine Description:
  4215. Updates the connection quality of service information by
  4216. querying the underlying transport.
  4217. Arguments:
  4218. Connection - pointer to the connection whose qos we want to update.
  4219. CurrentTime - an optional pointer to a large interger containing the
  4220. current time.
  4221. Return Value:
  4222. None.
  4223. --*/
  4224. {
  4225. NTSTATUS status;
  4226. PTDI_CONNECTION_INFO connectionInfo;
  4227. LARGE_INTEGER currentTime;
  4228. LARGE_INTEGER throughput;
  4229. LARGE_INTEGER linkDelay;
  4230. PPAGED_CONNECTION pagedConnection = Connection->PagedConnection;
  4231. PAGED_CODE( );
  4232. //
  4233. // This routine is a no-op on connectionless transports.
  4234. //
  4235. if ( Connection->Endpoint->IsConnectionless ) {
  4236. Connection->EnableOplocks = FALSE;
  4237. Connection->EnableRawIo = FALSE;
  4238. return;
  4239. }
  4240. //
  4241. // Update the connection information
  4242. //
  4243. if ( ARGUMENT_PRESENT( CurrentTime ) ) {
  4244. currentTime = *CurrentTime;
  4245. } else {
  4246. KeQuerySystemTime( &currentTime );
  4247. }
  4248. //
  4249. // Check if connection info is still valid.
  4250. //
  4251. if ( pagedConnection->LinkInfoValidTime.QuadPart > currentTime.QuadPart ) {
  4252. return;
  4253. }
  4254. //
  4255. // We need to update the connection information.
  4256. //
  4257. connectionInfo = ALLOCATE_NONPAGED_POOL(
  4258. sizeof(TDI_CONNECTION_INFO),
  4259. BlockTypeDataBuffer
  4260. );
  4261. if ( connectionInfo == NULL ) {
  4262. goto exitquery;
  4263. }
  4264. //
  4265. // Issue a TdiQueryInformation to get the current connection info
  4266. // from the transport provider for this connection. This is a
  4267. // synchronous operation.
  4268. //
  4269. status = SrvIssueTdiQuery(
  4270. Connection->FileObject,
  4271. &Connection->DeviceObject,
  4272. (PUCHAR)connectionInfo,
  4273. sizeof(TDI_CONNECTION_INFO),
  4274. TDI_QUERY_CONNECTION_INFO
  4275. );
  4276. //
  4277. // If the request failed, log an event.
  4278. //
  4279. // *** We special-case STATUS_INVALID_CONNECTION because NBF completes
  4280. // our Accept IRP before it's actually ready to accept requests.
  4281. //
  4282. if ( !NT_SUCCESS(status) ) {
  4283. if ( status != STATUS_INVALID_CONNECTION &&
  4284. status != STATUS_CONNECTION_INVALID ) {
  4285. INTERNAL_ERROR(
  4286. ERROR_LEVEL_UNEXPECTED,
  4287. "SrvUpdateVcQualityOfService: SrvIssueTdiQuery failed: %X\n",
  4288. status,
  4289. NULL
  4290. );
  4291. SrvLogServiceFailure( SRV_SVC_NT_IOCTL_FILE, status );
  4292. }
  4293. DEALLOCATE_NONPAGED_POOL( connectionInfo );
  4294. goto exitquery;
  4295. }
  4296. //
  4297. // Set the time when this information becomes invalid.
  4298. //
  4299. currentTime.QuadPart += SrvLinkInfoValidTime.QuadPart;
  4300. //
  4301. // Get a positive delay. The TP returns a relative time which
  4302. // is negative.
  4303. //
  4304. linkDelay.QuadPart = -connectionInfo->Delay.QuadPart;
  4305. if ( linkDelay.QuadPart < 0 ) {
  4306. linkDelay.QuadPart = 0;
  4307. }
  4308. //
  4309. // Get the throughput
  4310. //
  4311. throughput = connectionInfo->Throughput;
  4312. //
  4313. // If connection is reliable, check and see if the delay and throughput
  4314. // are within our limits. If not, the vc is unreliable.
  4315. //
  4316. Connection->EnableOplocks =
  4317. (BOOLEAN) ( !connectionInfo->Unreliable &&
  4318. throughput.QuadPart >= SrvMinLinkThroughput.QuadPart );
  4319. DEALLOCATE_NONPAGED_POOL( connectionInfo );
  4320. //
  4321. // We need to check the delay for Raw I/O.
  4322. //
  4323. Connection->EnableRawIo =
  4324. (BOOLEAN) ( Connection->EnableOplocks &&
  4325. linkDelay.QuadPart <= SrvMaxLinkDelay.QuadPart );
  4326. //
  4327. // See if oplocks are always disabled for this connection. We do it
  4328. // here so that Connection->EnableRawIo can be computed correctly.
  4329. //
  4330. if ( Connection->OplocksAlwaysDisabled ) {
  4331. Connection->EnableOplocks = FALSE;
  4332. }
  4333. //
  4334. // Access "large" connection QOS fields using a lock, to
  4335. // ensure consistent values.
  4336. //
  4337. ACQUIRE_LOCK( &Connection->Lock );
  4338. pagedConnection->LinkInfoValidTime = currentTime;
  4339. pagedConnection->Delay = linkDelay;
  4340. pagedConnection->Throughput = throughput;
  4341. RELEASE_LOCK( &Connection->Lock );
  4342. return;
  4343. exitquery:
  4344. Connection->EnableOplocks = TRUE;
  4345. Connection->EnableRawIo = TRUE;
  4346. return;
  4347. } // SrvUpdateVcQualityOfService
  4348. BOOLEAN SRVFASTCALL
  4349. SrvValidateSmb (
  4350. IN PWORK_CONTEXT WorkContext
  4351. )
  4352. /*++
  4353. Routine Description:
  4354. This function validates an SMB header.
  4355. Arguments:
  4356. WorkContext - Pointer to a work context block. The RequestHeader
  4357. and RequestParameter fields must be valid.
  4358. Return Value:
  4359. TRUE - The SMB is valid
  4360. FALSE - The SMB is invalid
  4361. --*/
  4362. {
  4363. PSMB_HEADER smbHeader;
  4364. UCHAR wordCount = 0;
  4365. PSMB_USHORT byteCount = NULL;
  4366. ULONG availableSpaceForSmb = 0;
  4367. PAGED_CODE( );
  4368. smbHeader = WorkContext->RequestHeader;
  4369. //
  4370. // Did we get an entire SMB? We check here for an SMB that at least goes
  4371. // to the WordCount field.
  4372. //
  4373. if( WorkContext->RequestBuffer->DataLength < sizeof( SMB_HEADER ) + sizeof( UCHAR ) ) {
  4374. IF_DEBUG(SMB_ERRORS) {
  4375. KdPrint(( "SMB of %d bytes too short!\n", availableSpaceForSmb ));
  4376. }
  4377. IF_DEBUG( ERRORS ) {
  4378. KdPrint(("Closing connection %p -- msg too small\n", WorkContext->Connection ));
  4379. }
  4380. //
  4381. // This client has really misbehaved. Nuke it!
  4382. //
  4383. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4384. SrvCloseConnection( WorkContext->Connection, FALSE );
  4385. return FALSE;
  4386. }
  4387. //
  4388. // Does it start with 0xFF S M B ?
  4389. //
  4390. if ( SmbGetAlignedUlong( (PULONG)smbHeader->Protocol ) !=
  4391. SMB_HEADER_PROTOCOL ) {
  4392. IF_DEBUG(SMB_ERRORS) {
  4393. KdPrint(( "SMB does not start with SMB_HEADER_PROTOCOL\n" ));
  4394. }
  4395. IF_DEBUG( ERRORS ) {
  4396. KdPrint(("Closing connection %p -- no ffSMB\n", WorkContext->Connection ));
  4397. }
  4398. //
  4399. // This client has really misbehaved. Nuke it!
  4400. //
  4401. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4402. SrvCloseConnection( WorkContext->Connection, FALSE );
  4403. return FALSE;
  4404. }
  4405. #if 0
  4406. if ( smbHeader->Reserved != 0 ) {
  4407. IF_DEBUG(SMB_ERRORS) {
  4408. KdPrint(( "SMB Header->Reserved %x != 0!\n", smbHeader->Reserved ));
  4409. }
  4410. SrvLogInvalidSmb( WorkContext );
  4411. return FALSE;
  4412. }
  4413. //
  4414. // DOS LM2.1 sets SMB_FLAGS_SERVER_TO_REDIR on an oplock break
  4415. // response, so ignore that bit.
  4416. //
  4417. if ( (smbHeader->Flags &
  4418. ~(INCOMING_SMB_FLAGS | SMB_FLAGS_SERVER_TO_REDIR)) != 0 ) {
  4419. IF_DEBUG(SMB_ERRORS) {
  4420. KdPrint(( "SMB Header->Flags (%x) invalid\n", smbHeader->Flags ));
  4421. }
  4422. SrvLogInvalidSmb( WorkContext );
  4423. return FALSE;
  4424. }
  4425. if ( (SmbGetAlignedUshort( &smbHeader->Flags2 ) &
  4426. ~INCOMING_SMB_FLAGS2) != 0 ) {
  4427. KdPrint(( "ValidatesmbHeader: Flags2 = %lx, valid bits = %lx, "
  4428. "invalid bit(s) = %lx\n",
  4429. SmbGetAlignedUshort( &smbHeader->Flags2 ),
  4430. INCOMING_SMB_FLAGS2,
  4431. SmbGetAlignedUshort( &smbHeader->Flags2 ) &
  4432. ~INCOMING_SMB_FLAGS2 ));
  4433. SrvLogInvalidSmb( WorkContext );
  4434. return FALSE;
  4435. }
  4436. #endif
  4437. #if 0
  4438. if( (smbHeader->Command != SMB_COM_LOCKING_ANDX) &&
  4439. (smbHeader->Flags & SMB_FLAGS_SERVER_TO_REDIR) ) {
  4440. //
  4441. // A client has set the bit indicating that this is a server response
  4442. // packet. This could be an attempt by a client to sneak through a
  4443. // firewall -- because the firewall may be configured to allow incoming
  4444. // responses, but no incomming requests (thereby allowing internal clients
  4445. // to access Internet servers, but not allowing external clients to access
  4446. // internal servers). Reject this SMB.
  4447. //
  4448. //
  4449. SrvLogInvalidSmb( WorkContext );
  4450. return FALSE;
  4451. }
  4452. #endif
  4453. if( WorkContext->Connection->SmbDialect == SmbDialectIllegal &&
  4454. smbHeader->Command != SMB_COM_NEGOTIATE ) {
  4455. //
  4456. // Whoa -- the client sent us an SMB, but we haven't negotiated a dialect
  4457. // yet!
  4458. //
  4459. IF_DEBUG(SMB_ERRORS) {
  4460. KdPrint(( "SMB command %x w/o negotiate!\n", smbHeader->Command ));
  4461. }
  4462. IF_DEBUG( ERRORS ) {
  4463. KdPrint(("Closing connection %p -- no Negotiate\n", WorkContext->Connection ));
  4464. }
  4465. //
  4466. // This client has really misbehaved. Nuke it!
  4467. //
  4468. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4469. SrvCloseConnection( WorkContext->Connection, FALSE );
  4470. return FALSE;
  4471. }
  4472. //
  4473. // Get the WordCount and ByteCount values to make sure that there
  4474. // was enough information sent to satisfy the specifications.
  4475. //
  4476. wordCount = *((PUCHAR)WorkContext->RequestParameters);
  4477. byteCount = (PSMB_USHORT)( (PCHAR)WorkContext->RequestParameters +
  4478. sizeof(UCHAR) + (wordCount * sizeof(USHORT)) );
  4479. availableSpaceForSmb = WorkContext->RequestBuffer->DataLength -
  4480. PTR_DIFF( WorkContext->ResponseParameters,
  4481. WorkContext->RequestBuffer->Buffer );
  4482. //
  4483. // Verify all of the fixed valued SMB header fields. Variable
  4484. // valued fields (such as Tid) are verified as needed by the
  4485. // individual SMB handlers.
  4486. //
  4487. //
  4488. // Make sure that the valid word count was sent across. If the
  4489. // value in the table is -1, then the processing routine will
  4490. // verify the word count, and if the word count is -2 then this
  4491. // is an illegal command which will be caught later.
  4492. //
  4493. // We check whether the word count is negative first since
  4494. // critical smbs like read/write (andX/raw) have -1.
  4495. //
  4496. //
  4497. // Make sure that the ByteCount lies within the boundaries of
  4498. // the received SMB. Without this test, it would be possible,
  4499. // when at the end of a long AndX chain and WordCount is large,
  4500. // for the server to take an access violation when looking at
  4501. // ByteCount. The location for ByteCount must be at least two
  4502. // bytes short of the end of the buffer, as ByteCount is a
  4503. // USHORT (two bytes).
  4504. //
  4505. //
  4506. // The WordCount parameter is a byte that indicates the number of
  4507. // word parameters, and ByteCount is a word that indicated the
  4508. // number of following bytes. They do not account for their own
  4509. // sizes, so add sizeof(UCHAR) + sizeof(USHORT) to account for them.
  4510. //
  4511. if ( ((SrvSmbWordCount[WorkContext->NextCommand] < 0)
  4512. ||
  4513. ((CHAR)wordCount == SrvSmbWordCount[WorkContext->NextCommand]))
  4514. &&
  4515. ((PCHAR)byteCount <= (PCHAR)WorkContext->RequestBuffer->Buffer +
  4516. WorkContext->RequestBuffer->DataLength -
  4517. sizeof(USHORT))
  4518. &&
  4519. ((wordCount*sizeof(USHORT) + sizeof(UCHAR) + sizeof(USHORT) +
  4520. SmbGetUshort( byteCount )) <= availableSpaceForSmb) ) {
  4521. return(TRUE);
  4522. }
  4523. //
  4524. // If we have an NT style WriteAndX, we let the client exceed the negotiated
  4525. // buffer size. We do not need to check the WordCount, because we know that
  4526. // the SMB processor itself checks it.
  4527. //
  4528. if( WorkContext->LargeIndication ) {
  4529. if( WorkContext->NextCommand == SMB_COM_WRITE_ANDX ) {
  4530. return(TRUE);
  4531. } else {
  4532. IF_DEBUG(SMB_ERRORS) {
  4533. KdPrint(( "LargeIndication but not WRITE_AND_X (%x) received!\n",
  4534. WorkContext->NextCommand ));
  4535. }
  4536. IF_DEBUG( ERRORS ) {
  4537. KdPrint(("Closing connection %p -- msg too large\n", WorkContext->Connection ));
  4538. }
  4539. //
  4540. // This client has really misbehaved. Nuke it!
  4541. //
  4542. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4543. SrvCloseConnection( WorkContext->Connection, FALSE );
  4544. return( FALSE );
  4545. }
  4546. }
  4547. //
  4548. // Make sure that the valid word count was sent across. If the
  4549. // value in the table is -1, then the processing routine will
  4550. // verify the word count, and if the word count is -2 then this
  4551. // is an illegal command which will be caught later.
  4552. //
  4553. // We check whether the word count is negative first since
  4554. // critical smbs like read/write (andX/raw) have -1.
  4555. //
  4556. if ( (CHAR)wordCount != SrvSmbWordCount[WorkContext->NextCommand] ) {
  4557. //
  4558. // Living with sin. The DOS redir sends a word count of 9
  4559. // (instead of 8) on a Transaction secondary SMB. Pretend it
  4560. // sent the correct number.
  4561. //
  4562. if ( WorkContext->RequestHeader->Command ==
  4563. SMB_COM_TRANSACTION_SECONDARY &&
  4564. IS_DOS_DIALECT( WorkContext->Connection->SmbDialect) &&
  4565. wordCount == 9 ) {
  4566. wordCount = 8;
  4567. *((PUCHAR)WorkContext->RequestParameters) = 8;
  4568. byteCount = (PSMB_USHORT)( (PCHAR)WorkContext->RequestParameters +
  4569. sizeof(UCHAR) + (8 * sizeof(USHORT)) );
  4570. #ifdef INCLUDE_SMB_IFMODIFIED
  4571. } else if ( IS_POSTNT5_DIALECT( WorkContext->Connection->SmbDialect ) &&
  4572. ( (( WorkContext->RequestHeader->Command ==
  4573. SMB_COM_NT_CREATE_ANDX ) &&
  4574. (wordCount == SMB_REQ_EXTENDED_NT_CREATE_ANDX2_WORK_COUNT)) ||
  4575. (( WorkContext->RequestHeader->Command ==
  4576. SMB_COM_CLOSE ) && (wordCount == 5)) )) {
  4577. //
  4578. // Per SethuR's suggestion, some SMB commands will have
  4579. // multiple number of arguments rather than define new
  4580. // SMBs.
  4581. //
  4582. if (((PCHAR)byteCount <= (PCHAR)WorkContext->RequestBuffer->Buffer +
  4583. WorkContext->RequestBuffer->DataLength -
  4584. sizeof(USHORT))
  4585. &&
  4586. ((wordCount*sizeof(USHORT) + sizeof(UCHAR) + sizeof(USHORT) +
  4587. SmbGetUshort( byteCount )) <= availableSpaceForSmb) ) {
  4588. return(TRUE);
  4589. }
  4590. #endif
  4591. } else {
  4592. //
  4593. // Any other request with an incorrect word count is
  4594. // toast. Reject the request.
  4595. //
  4596. IF_DEBUG(SMB_ERRORS) {
  4597. KdPrint(( "SMB WordCount incorrect. WordCount=%ld, "
  4598. "should be %ld (command = 0x%lx)\n", wordCount,
  4599. SrvSmbWordCount[WorkContext->NextCommand],
  4600. WorkContext->NextCommand ));
  4601. KdPrint(( " SMB received from %z\n",
  4602. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4603. }
  4604. SrvLogInvalidSmb( WorkContext );
  4605. return FALSE;
  4606. }
  4607. }
  4608. //
  4609. // Make sure that the ByteCount lies within the boundaries of
  4610. // the received SMB. Without this test, it would be possible,
  4611. // when at the end of a long AndX chain and WordCount is large,
  4612. // for the server to take an access violation when looking at
  4613. // ByteCount. The location for ByteCount must be at least two
  4614. // bytes short of the end of the buffer, as ByteCount is a
  4615. // USHORT (two bytes).
  4616. //
  4617. if ( (PCHAR)byteCount > (PCHAR)WorkContext->RequestBuffer->Buffer +
  4618. WorkContext->RequestBuffer->DataLength -
  4619. sizeof(USHORT) ) {
  4620. IF_DEBUG(SMB_ERRORS) {
  4621. KdPrint(( "ByteCount address past end of sent SMB. "
  4622. "ByteCount address=0x%p, "
  4623. "End of buffer=0x%lx\n",
  4624. byteCount,
  4625. WorkContext->RequestBuffer->DataLength ));
  4626. KdPrint(( " SMB received from %z\n",
  4627. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4628. }
  4629. SrvLogInvalidSmb( WorkContext );
  4630. IF_DEBUG( ERRORS ) {
  4631. KdPrint(("Closing connection %p -- ByteCount too big\n", WorkContext->Connection ));
  4632. }
  4633. //
  4634. // This client has really misbehaved. Nuke it!
  4635. //
  4636. WorkContext->Connection->DisconnectReason = DisconnectBadSMBPacket;
  4637. SrvCloseConnection( WorkContext->Connection, FALSE );
  4638. } else {
  4639. //
  4640. // if this is an IOCTL smb with category 0x53, set byte count to zero.
  4641. // This is due to a DOS Lm2.0 and Lm2.1 bug which does not zero out
  4642. // the bcc causing the preceding check to fail.
  4643. //
  4644. if ( (WorkContext->RequestHeader->Command == SMB_COM_IOCTL) &&
  4645. (((PREQ_IOCTL) WorkContext->RequestParameters)->Category == 0x53)
  4646. ) {
  4647. SmbPutUshort( byteCount , 0 );
  4648. return(TRUE);
  4649. }
  4650. IF_DEBUG(SMB_ERRORS) {
  4651. KdPrint(( "SMB WordCount and/or ByteCount incorrect. "
  4652. "WordCount=%ld, ByteCount=%ld, Space=%ld\n",
  4653. wordCount, SmbGetUshort( byteCount ),
  4654. availableSpaceForSmb ));
  4655. KdPrint(( " SMB received from %z\n",
  4656. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  4657. }
  4658. SrvLogInvalidSmb( WorkContext );
  4659. }
  4660. return FALSE;
  4661. } // SrvValidateSmb
  4662. NTSTATUS
  4663. SrvWildcardRename(
  4664. IN PUNICODE_STRING FileSpec,
  4665. IN PUNICODE_STRING SourceString,
  4666. OUT PUNICODE_STRING TargetString
  4667. )
  4668. /*++
  4669. Routine Description:
  4670. This routine converts a filespec and a source filename into a
  4671. destination file name. This routine is used to support DOS-based
  4672. wildcard renames.
  4673. Arguments:
  4674. FileSpec - The wildcard specification describing the destination file.
  4675. SourceString - Pointer to a string that contains the source file name.
  4676. TargetString - Pointer to a string that will contain the destination name.
  4677. Return Value:
  4678. Status of operation.
  4679. --*/
  4680. {
  4681. PWCHAR currentFileSpec;
  4682. WCHAR delimit;
  4683. PWCHAR buffer;
  4684. PWCHAR source;
  4685. ULONG bufferSize;
  4686. ULONG sourceLeft;
  4687. ULONG i;
  4688. //
  4689. // This will store the number of bytes we have written to the
  4690. // target buffer so far.
  4691. //
  4692. ULONG resultLength = 0;
  4693. PAGED_CODE( );
  4694. //
  4695. // This points to the current character in the filespec.
  4696. //
  4697. currentFileSpec = FileSpec->Buffer;
  4698. //
  4699. // Initialize the pointer and the length of the source buffer.
  4700. //
  4701. source = SourceString->Buffer;
  4702. sourceLeft = SourceString->Length;
  4703. //
  4704. // Initialize the pointer and the length of the target buffer.
  4705. //
  4706. buffer = TargetString->Buffer;
  4707. bufferSize = TargetString->MaximumLength;
  4708. //
  4709. // Go throught each character in the filespec.
  4710. //
  4711. for ( i = 0; i < (ULONG)FileSpec->Length ; i += sizeof(WCHAR) ) {
  4712. if (resultLength < bufferSize) {
  4713. switch ( *currentFileSpec ) {
  4714. case L':':
  4715. case L'\\':
  4716. return STATUS_OBJECT_NAME_INVALID;
  4717. case L'*':
  4718. //
  4719. // Store the next character.
  4720. //
  4721. delimit = *(currentFileSpec+1);
  4722. //
  4723. // While we have not exceeded the buffer and
  4724. // we have not reached the end of the source string
  4725. // and the current source character is not equal to
  4726. // the delimeter, copy the source character to the
  4727. // target string.
  4728. //
  4729. while ( ( resultLength < bufferSize ) &&
  4730. ( sourceLeft > 0 ) &&
  4731. ( *source != delimit ) ) {
  4732. *(buffer++) = *(source++);
  4733. sourceLeft -= sizeof(WCHAR);
  4734. resultLength += sizeof(WCHAR);
  4735. }
  4736. break;
  4737. case L'?': //
  4738. case L'>': // should we even consider >, <, and "
  4739. case L'<': // I'll just put this here to be safe
  4740. //
  4741. // For each ? in the filespec, we copy one character
  4742. // from the source string.
  4743. //
  4744. if ( ( *source != L'.' ) && ( sourceLeft > 0 )) {
  4745. if (resultLength < bufferSize) {
  4746. *(buffer++) = *(source++);
  4747. sourceLeft -= sizeof(WCHAR);
  4748. resultLength += sizeof(WCHAR);
  4749. } else {
  4750. return(STATUS_BUFFER_OVERFLOW);
  4751. }
  4752. }
  4753. break;
  4754. case L'.':
  4755. case L'"':
  4756. //
  4757. // Discard all the characters from the source string up
  4758. // to . or the end of the string.
  4759. //
  4760. while ( (*source != L'.') && (sourceLeft > 0) ) {
  4761. source++;
  4762. sourceLeft -= sizeof(WCHAR);
  4763. }
  4764. *(buffer++) = L'.';
  4765. resultLength += sizeof(WCHAR);
  4766. if ( sourceLeft > 0 ) {
  4767. source++;
  4768. sourceLeft -= sizeof(WCHAR);
  4769. }
  4770. break;
  4771. default:
  4772. //
  4773. // Just copy one to one
  4774. //
  4775. if ( (*source != L'.') && (sourceLeft > 0)) {
  4776. source++;
  4777. sourceLeft -= sizeof(WCHAR);
  4778. }
  4779. if (resultLength < bufferSize) {
  4780. *(buffer++) = *currentFileSpec;
  4781. resultLength += sizeof(WCHAR);
  4782. } else {
  4783. return(STATUS_BUFFER_OVERFLOW);
  4784. }
  4785. break;
  4786. }
  4787. currentFileSpec++;
  4788. } else {
  4789. return(STATUS_BUFFER_OVERFLOW);
  4790. }
  4791. }
  4792. TargetString->Length = (USHORT)resultLength;
  4793. return( STATUS_SUCCESS );
  4794. } // SrvWildcardRename
  4795. VOID
  4796. DispatchToOrphanage(
  4797. IN PQUEUEABLE_BLOCK_HEADER Block
  4798. )
  4799. {
  4800. KIRQL oldIrql;
  4801. ASSERT( Block->BlockHeader.ReferenceCount == 1 );
  4802. ExInterlockedPushEntrySList(
  4803. &SrvBlockOrphanage,
  4804. &Block->SingleListEntry,
  4805. &GLOBAL_SPIN_LOCK(Fsd)
  4806. );
  4807. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  4808. InterlockedIncrement( &SrvResourceOrphanedBlocks );
  4809. SrvFsdQueueExWorkItem(
  4810. &SrvResourceThreadWorkItem,
  4811. &SrvResourceThreadRunning,
  4812. CriticalWorkQueue
  4813. );
  4814. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  4815. return;
  4816. } // DispatchToOrphanage
  4817. NTSTATUS
  4818. SrvIsAllowedOnAdminShare(
  4819. IN PWORK_CONTEXT WorkContext,
  4820. IN PSHARE Share
  4821. )
  4822. /*++
  4823. Routine Description:
  4824. This routine returns STATUS_SUCCESS if the client represented by
  4825. the WorkContext should be allowed to access the Share, if the share
  4826. is an Administrative Disk share.
  4827. Arguments:
  4828. WorkContext - the unit of work
  4829. Share - pointer to a share, possibly an administrative share
  4830. Return Value:
  4831. STATUS_SUCCESS if allowed. Error otherwise.
  4832. --*/
  4833. {
  4834. NTSTATUS status = STATUS_SUCCESS;
  4835. PAGED_CODE();
  4836. if( Share->SpecialShare && Share->ShareType == ShareTypeDisk ) {
  4837. SECURITY_SUBJECT_CONTEXT subjectContext;
  4838. ACCESS_MASK desiredAccess, grantedAccess;
  4839. status = IMPERSONATE( WorkContext );
  4840. if( NT_SUCCESS( status ) ) {
  4841. SeCaptureSubjectContext( &subjectContext );
  4842. if( !SeAccessCheck(
  4843. Share->SecurityDescriptor,
  4844. &subjectContext,
  4845. FALSE,
  4846. SRVSVC_SHARE_CONNECT,
  4847. 0L,
  4848. NULL,
  4849. &SrvShareConnectMapping,
  4850. UserMode,
  4851. &grantedAccess,
  4852. &status
  4853. ) ) {
  4854. //
  4855. // We have a non-administrative user trying to access a file
  4856. // through an administrative share. Can't allow that!
  4857. //
  4858. // Some clients want ACCESS_DENIED to be in the server class
  4859. // instead of the DOS class when it's due to share ACL
  4860. // restrictions. So we need to keep track of why we're
  4861. // returning ACCESS_DENIED.
  4862. //
  4863. WorkContext->ShareAclFailure = TRUE;
  4864. }
  4865. SeReleaseSubjectContext( &subjectContext );
  4866. REVERT();
  4867. }
  4868. }
  4869. return status;
  4870. }
  4871. NTSTATUS
  4872. SrvRetrieveMaximalAccessRightsForUser(
  4873. CtxtHandle *pUserHandle,
  4874. PSECURITY_DESCRIPTOR pSecurityDescriptor,
  4875. PGENERIC_MAPPING pMapping,
  4876. PACCESS_MASK pMaximalAccessRights)
  4877. /*++
  4878. Routine Description:
  4879. This routine retrieves the maximal access rights for this client
  4880. Arguments:
  4881. pUserHandle - the users security handle
  4882. pSecurityDescriptor - the security descriptor
  4883. pMapping - the mapping of access rights
  4884. pMaximalAccessRights - the computed rights
  4885. Return Value:
  4886. Status of operation.
  4887. Notes:
  4888. The srv macros IMPERSONATE is defined in terms of a WORK_CONTEXT. Since
  4889. we desire this routine should be used in all situations even when a
  4890. WORK_CONTEXT is not available the code in SrvImpersonate is duplicated
  4891. over here
  4892. --*/
  4893. {
  4894. NTSTATUS status;
  4895. PPRIVILEGE_SET privileges = NULL;
  4896. SECURITY_SUBJECT_CONTEXT subjectContext;
  4897. if( !IS_VALID_SECURITY_HANDLE (*pUserHandle) ) {
  4898. return STATUS_ACCESS_DENIED;
  4899. }
  4900. status = ImpersonateSecurityContext(
  4901. pUserHandle);
  4902. if( NT_SUCCESS( status ) ) {
  4903. SeCaptureSubjectContext( &subjectContext );
  4904. if (!SeAccessCheck(
  4905. pSecurityDescriptor,
  4906. &subjectContext,
  4907. FALSE, // Locked ?
  4908. MAXIMUM_ALLOWED,
  4909. 0, // PreviousGrantedAccess
  4910. &privileges,
  4911. pMapping,
  4912. UserMode,
  4913. pMaximalAccessRights,
  4914. &status
  4915. ) ) {
  4916. IF_DEBUG(ERRORS) {
  4917. KdPrint((
  4918. "SrvCheckShareFileAccess: Status %x, Desired access %x\n",
  4919. status,
  4920. MAXIMUM_ALLOWED
  4921. ));
  4922. }
  4923. }
  4924. if ( privileges != NULL ) {
  4925. SeFreePrivileges( privileges );
  4926. }
  4927. SeReleaseSubjectContext( &subjectContext );
  4928. REVERT();
  4929. if (status == STATUS_ACCESS_DENIED) {
  4930. *pMaximalAccessRights = 0;
  4931. status = STATUS_SUCCESS;
  4932. }
  4933. }
  4934. return status;
  4935. }
  4936. NTSTATUS
  4937. SrvRetrieveMaximalAccessRights(
  4938. IN OUT PWORK_CONTEXT WorkContext,
  4939. OUT PACCESS_MASK pMaximalAccessRights,
  4940. OUT PACCESS_MASK pGuestMaximalAccessRights)
  4941. /*++
  4942. Routine Description:
  4943. This routine retrieves the maximal access rights for this client as well
  4944. as a guest based upon the ACLs specified for the file
  4945. Arguments:
  4946. WorkContext - pointer to the work context block that contains information
  4947. about the request.
  4948. pMaximalAccessRights - the maximal access rights for this client.
  4949. pGuestMaximalAccessRights - the maximal access rights for a guest
  4950. Return Value:
  4951. Status of operation.
  4952. --*/
  4953. {
  4954. NTSTATUS status;
  4955. BOOLEAN SecurityBufferAllocated = FALSE;
  4956. PRFCB rfcb;
  4957. ULONG lengthNeeded;
  4958. LONG SecurityDescriptorBufferLength;
  4959. PSECURITY_DESCRIPTOR SecurityDescriptorBuffer;
  4960. GENERIC_MAPPING Mapping = {
  4961. FILE_GENERIC_READ,
  4962. FILE_GENERIC_WRITE,
  4963. FILE_GENERIC_EXECUTE,
  4964. FILE_ALL_ACCESS
  4965. };
  4966. rfcb = WorkContext->Rfcb;
  4967. SecurityDescriptorBufferLength = (WorkContext->RequestBuffer->DataLength -
  4968. sizeof(SMB_HEADER) -
  4969. - 4);
  4970. if (SecurityDescriptorBufferLength > 0) {
  4971. SecurityDescriptorBufferLength &= ~3;
  4972. } else {
  4973. SecurityDescriptorBufferLength = 0;
  4974. }
  4975. SecurityDescriptorBuffer = ((PCHAR)WorkContext->RequestBuffer->Buffer +
  4976. WorkContext->RequestBuffer->BufferLength -
  4977. SecurityDescriptorBufferLength);
  4978. status = NtQuerySecurityObject(
  4979. rfcb->Lfcb->FileHandle,
  4980. (DACL_SECURITY_INFORMATION |
  4981. SACL_SECURITY_INFORMATION |
  4982. GROUP_SECURITY_INFORMATION |
  4983. OWNER_SECURITY_INFORMATION),
  4984. SecurityDescriptorBuffer,
  4985. SecurityDescriptorBufferLength,
  4986. &lengthNeeded
  4987. );
  4988. if (status == STATUS_BUFFER_TOO_SMALL) {
  4989. SecurityDescriptorBuffer = ALLOCATE_HEAP(lengthNeeded,PagedPool);
  4990. if (SecurityDescriptorBuffer != NULL) {
  4991. SecurityBufferAllocated = TRUE;
  4992. SecurityDescriptorBufferLength = lengthNeeded;
  4993. status = NtQuerySecurityObject(
  4994. rfcb->Lfcb->FileHandle,
  4995. (DACL_SECURITY_INFORMATION |
  4996. SACL_SECURITY_INFORMATION |
  4997. GROUP_SECURITY_INFORMATION |
  4998. OWNER_SECURITY_INFORMATION),
  4999. SecurityDescriptorBuffer,
  5000. SecurityDescriptorBufferLength,
  5001. &lengthNeeded
  5002. );
  5003. } else {
  5004. status = STATUS_INSUFFICIENT_RESOURCES;
  5005. }
  5006. }
  5007. if (status == STATUS_SUCCESS) {
  5008. status = SrvRetrieveMaximalAccessRightsForUser(
  5009. &WorkContext->Session->UserHandle,
  5010. SecurityDescriptorBuffer,
  5011. &Mapping,
  5012. pMaximalAccessRights);
  5013. }
  5014. // Extract the GUEST access rights
  5015. if (status == STATUS_SUCCESS) {
  5016. status = SrvRetrieveMaximalAccessRightsForUser(
  5017. &SrvNullSessionToken,
  5018. SecurityDescriptorBuffer,
  5019. &Mapping,
  5020. pGuestMaximalAccessRights);
  5021. }
  5022. if (SecurityBufferAllocated) {
  5023. FREE_HEAP(SecurityDescriptorBuffer);
  5024. }
  5025. return status;
  5026. }
  5027. NTSTATUS
  5028. SrvRetrieveMaximalShareAccessRights(
  5029. IN PWORK_CONTEXT WorkContext,
  5030. OUT PACCESS_MASK pMaximalAccessRights,
  5031. OUT PACCESS_MASK pGuestMaximalAccessRights)
  5032. /*++
  5033. Routine Description:
  5034. This routine retrieves the maximal access rights for this client as well
  5035. as a guest based upon the ACLs specified for the share
  5036. Arguments:
  5037. WorkContext - pointer to the work context block that contains information
  5038. about the request.
  5039. pMaximalAccessRights - the maximal access rights for this client.
  5040. pGuestMaximalAccessRights - the maximal access rights for a guest
  5041. Return Value:
  5042. Status of operation.
  5043. --*/
  5044. {
  5045. NTSTATUS status = STATUS_SUCCESS;
  5046. PSECURITY_DESCRIPTOR securityDescriptor;
  5047. ACCESS_MASK grantedAccess;
  5048. ACCESS_MASK mappedAccess = MAXIMUM_ALLOWED;
  5049. PAGED_CODE( );
  5050. ACQUIRE_LOCK_SHARED( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  5051. securityDescriptor = WorkContext->TreeConnect->Share->FileSecurityDescriptor;
  5052. if (securityDescriptor != NULL) {
  5053. status = SrvRetrieveMaximalAccessRightsForUser(
  5054. &WorkContext->Session->UserHandle,
  5055. securityDescriptor,
  5056. &SrvFileAccessMapping,
  5057. pMaximalAccessRights);
  5058. if (NT_SUCCESS(status)) {
  5059. // Get the guest rights
  5060. status = SrvRetrieveMaximalAccessRightsForUser(
  5061. &SrvNullSessionToken,
  5062. securityDescriptor,
  5063. &SrvFileAccessMapping,
  5064. pGuestMaximalAccessRights);
  5065. }
  5066. } else {
  5067. // No Share Level ACL, Grant maximum access to both the current client
  5068. // as well as guest
  5069. *pMaximalAccessRights = 0x1ff;
  5070. *pGuestMaximalAccessRights = 0x1ff;
  5071. }
  5072. RELEASE_LOCK( WorkContext->TreeConnect->Share->SecurityDescriptorLock );
  5073. return status;
  5074. }
  5075. NTSTATUS
  5076. SrvUpdateMaximalAccessRightsInResponse(
  5077. IN OUT PWORK_CONTEXT WorkContext,
  5078. OUT PSMB_ULONG pMaximalAccessRightsInResponse,
  5079. OUT PSMB_ULONG pGuestMaximalAccessRightsInResponse
  5080. )
  5081. /*++
  5082. Routine Description:
  5083. This routine updates the maximal access rights fields in an extended
  5084. response. This is used to update these fields in various kinds of
  5085. OPEN requests
  5086. Arguments:
  5087. WorkContext - Supplies the address of a Work Context Block
  5088. describing the current request. See smbtypes.h for a more
  5089. complete description of the valid fields.
  5090. pMaximalAccessRightsInResponse - the maximal access rights field in
  5091. the response
  5092. pGuestMaximalAccessRightsInResponse - the guest maximal access rights field
  5093. in the response
  5094. Return Value:
  5095. STATUS_SUCCESS if successful, otherwise appropriate error code
  5096. --*/
  5097. {
  5098. NTSTATUS status;
  5099. ACCESS_MASK OwnerMaximalAccessRights = 0;
  5100. ACCESS_MASK GuestMaximalAccessRights = 0;
  5101. status = SrvRetrieveMaximalAccessRights(
  5102. WorkContext,
  5103. &OwnerMaximalAccessRights,
  5104. &GuestMaximalAccessRights);
  5105. if (status == STATUS_SUCCESS) {
  5106. SmbPutUlong(
  5107. pMaximalAccessRightsInResponse,
  5108. OwnerMaximalAccessRights
  5109. );
  5110. SmbPutUlong(
  5111. pGuestMaximalAccessRightsInResponse,
  5112. GuestMaximalAccessRights
  5113. );
  5114. }
  5115. return status;
  5116. }
  5117. NTSTATUS
  5118. SrvUpdateMaximalShareAccessRightsInResponse(
  5119. IN OUT PWORK_CONTEXT WorkContext,
  5120. OUT PSMB_ULONG pMaximalAccessRightsInResponse,
  5121. OUT PSMB_ULONG pGuestMaximalAccessRightsInResponse
  5122. )
  5123. /*++
  5124. Routine Description:
  5125. This routine updates the maximal access rights fields in an extended
  5126. response. This is used to update these fields in various kinds of
  5127. TREE_CONNECT requests
  5128. Arguments:
  5129. WorkContext - Supplies the address of a Work Context Block
  5130. describing the current request. See smbtypes.h for a more
  5131. complete description of the valid fields.
  5132. pMaximalAccessRightsInResponse - the maximal access rights field in
  5133. the response
  5134. pGuestMaximalAccessRightsInResponse - the guest maximal access rights field
  5135. in the response
  5136. Return Value:
  5137. STATUS_SUCCESS if successful, otherwise appropriate error code
  5138. --*/
  5139. {
  5140. NTSTATUS status;
  5141. ACCESS_MASK OwnerMaximalAccessRights = 0;
  5142. ACCESS_MASK GuestMaximalAccessRights = 0;
  5143. status = SrvRetrieveMaximalShareAccessRights(
  5144. WorkContext,
  5145. &OwnerMaximalAccessRights,
  5146. &GuestMaximalAccessRights);
  5147. if (status == STATUS_SUCCESS) {
  5148. SmbPutUlong(
  5149. pMaximalAccessRightsInResponse,
  5150. OwnerMaximalAccessRights
  5151. );
  5152. SmbPutUlong(
  5153. pGuestMaximalAccessRightsInResponse,
  5154. GuestMaximalAccessRights
  5155. );
  5156. }
  5157. return status;
  5158. }
  5159. VOID SRVFASTCALL
  5160. RestartConsumeSmbData(
  5161. IN OUT PWORK_CONTEXT WorkContext
  5162. )
  5163. /*++
  5164. Routine Description:
  5165. This is the restart routine for 'SrvConsumeSmbData'. We need to see if we
  5166. drained the current message from the transport. If we have, then we send
  5167. the response SMB to the client. If we have not, we keep going.
  5168. --*/
  5169. {
  5170. PIRP irp = WorkContext->Irp;
  5171. PIO_STACK_LOCATION irpSp;
  5172. PTDI_REQUEST_KERNEL_RECEIVE parameters;
  5173. ASSERT( WorkContext->LargeIndication );
  5174. //
  5175. // Check to see if we are done. If so, send the response to the client
  5176. //
  5177. if( irp->Cancel ||
  5178. NT_SUCCESS( irp->IoStatus.Status ) ||
  5179. irp->IoStatus.Status != STATUS_BUFFER_OVERFLOW ) {
  5180. RtlZeroMemory( WorkContext->ResponseHeader + 1, sizeof( SMB_PARAMS ) );
  5181. WorkContext->ResponseBuffer->DataLength = sizeof( SMB_HEADER ) + sizeof( SMB_PARAMS );
  5182. WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
  5183. //
  5184. // Send the data!
  5185. //
  5186. SRV_START_SEND_2(
  5187. WorkContext,
  5188. SrvFsdRestartSmbAtSendCompletion,
  5189. NULL,
  5190. NULL
  5191. );
  5192. return;
  5193. }
  5194. //
  5195. // Not done yet. Consume more data!
  5196. //
  5197. WorkContext->Connection->ReceivePending = FALSE;
  5198. irp->Tail.Overlay.OriginalFileObject = NULL;
  5199. irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
  5200. DEBUG irp->RequestorMode = KernelMode;
  5201. //
  5202. // Get a pointer to the next stack location. This one is used to
  5203. // hold the parameters for the device I/O control request.
  5204. //
  5205. irpSp = IoGetNextIrpStackLocation( irp );
  5206. //
  5207. // Set up the completion routine
  5208. //
  5209. IoSetCompletionRoutine(
  5210. irp,
  5211. SrvFsdIoCompletionRoutine,
  5212. WorkContext,
  5213. TRUE,
  5214. TRUE,
  5215. TRUE
  5216. );
  5217. WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
  5218. WorkContext->FspRestartRoutine = RestartConsumeSmbData;
  5219. irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  5220. irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
  5221. irpSp->FileObject = WorkContext->Connection->FileObject;
  5222. irpSp->DeviceObject = WorkContext->Connection->DeviceObject;
  5223. irpSp->Flags = 0;
  5224. parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
  5225. parameters->ReceiveLength = WorkContext->ResponseBuffer->BufferLength - sizeof( SMB_HEADER );
  5226. parameters->ReceiveFlags = 0;
  5227. //
  5228. // Set the buffer's partial mdl to point just after the header for this
  5229. // WriteAndX SMB. We need to preserve the header to make it easier to send
  5230. // back the response.
  5231. //
  5232. IoBuildPartialMdl(
  5233. WorkContext->RequestBuffer->Mdl,
  5234. WorkContext->RequestBuffer->PartialMdl,
  5235. WorkContext->ResponseHeader + 1,
  5236. parameters->ReceiveLength
  5237. );
  5238. irp->MdlAddress = WorkContext->RequestBuffer->PartialMdl;
  5239. irp->AssociatedIrp.SystemBuffer = NULL;
  5240. irp->Flags = (ULONG)IRP_BUFFERED_IO; // ???
  5241. (VOID)IoCallDriver( irpSp->DeviceObject, irp );
  5242. }
  5243. SMB_PROCESSOR_RETURN_TYPE
  5244. SrvConsumeSmbData(
  5245. IN OUT PWORK_CONTEXT WorkContext
  5246. )
  5247. /*++
  5248. Routine Description:
  5249. This routine handles the case where we have received a LargeIndication
  5250. from a client (i.e. received SMB exceeds the negotiated buffer size). Some
  5251. error has occurred prior to consuming the entire message. The SMB header is
  5252. already formatted for the response, but we need to consume the rest of the
  5253. incoming data and then send the response.
  5254. --*/
  5255. {
  5256. if( WorkContext->LargeIndication == FALSE ) {
  5257. return SmbStatusSendResponse;
  5258. }
  5259. IF_DEBUG( ERRORS ) {
  5260. KdPrint(("SRV: SrvConsumeSmbData, BytesAvailable = %u\n",
  5261. WorkContext->BytesAvailable ));
  5262. }
  5263. WorkContext->Irp->Cancel = FALSE;
  5264. WorkContext->Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
  5265. RestartConsumeSmbData( WorkContext );
  5266. return SmbStatusInProgress;
  5267. }
  5268. BOOLEAN
  5269. SrvIsDottedQuadAddress(
  5270. IN PUNICODE_STRING ServerName
  5271. )
  5272. /*++
  5273. Routine Description:
  5274. Return true if the ServerName appears to be a dotted quad address along the lines
  5275. of xxx.yyy.zzz.qqq
  5276. False otherwise
  5277. --*/
  5278. {
  5279. PWCHAR p, ep;
  5280. DWORD numberOfDots = 0;
  5281. DWORD numberOfDigits = 0;
  5282. PAGED_CODE();
  5283. //
  5284. // If the address is all digits and contains 3 dots, then we'll figure that it's
  5285. // a dotted quad address.
  5286. //
  5287. ep = &ServerName->Buffer[ ServerName->Length / sizeof( WCHAR ) ];
  5288. for( p = ServerName->Buffer; p < ep; p++ ) {
  5289. if( *p == L'.' ) {
  5290. if( ++numberOfDots > 3 || numberOfDigits == 0 ) {
  5291. return FALSE;
  5292. }
  5293. numberOfDigits = 0;
  5294. } else if( (*p < L'0' || *p > L'9') || ++numberOfDigits > 3 ) {
  5295. return FALSE;
  5296. }
  5297. }
  5298. return (numberOfDots == 3) && (numberOfDigits <= 3);
  5299. }