Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2161 lines
70 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. smbsrch.c
  5. Abstract:
  6. This module contains routines for processing the Search SMB.
  7. Author:
  8. David Treadwell (davidtr) 13-Feb-1990
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "smbsrch.tmh"
  13. #pragma hdrstop
  14. #define BugCheckFileId SRV_FILE_SMBSRCH
  15. #define VOLUME_BUFFER_SIZE \
  16. FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel) + \
  17. MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR)
  18. #ifdef ALLOC_PRAGMA
  19. #pragma alloc_text( PAGE, SrvSmbSearch )
  20. #endif
  21. SMB_PROCESSOR_RETURN_TYPE
  22. SrvSmbSearch (
  23. SMB_PROCESSOR_PARAMETERS
  24. )
  25. /*++
  26. Routine Description:
  27. This routine processes the various search SMBs, including the core
  28. Search and the LM 1.0 Find, Find Unique, and Find Close.
  29. Arguments:
  30. SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
  31. of the parameters to SMB processor routines.
  32. Return Value:
  33. SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
  34. --*/
  35. {
  36. PREQ_SEARCH request;
  37. PRESP_SEARCH response;
  38. NTSTATUS status = STATUS_SUCCESS;
  39. SMB_STATUS SmbStatus = SmbStatusInProgress;
  40. UNICODE_STRING fileName;
  41. PSRV_DIRECTORY_INFORMATION directoryInformation = NULL;
  42. CLONG availableSpace;
  43. CLONG totalBytesWritten;
  44. BOOLEAN calledQueryDirectory;
  45. BOOLEAN findFirst;
  46. BOOLEAN isUnicode;
  47. BOOLEAN filterLongNames;
  48. BOOLEAN isCoreSearch;
  49. PTABLE_ENTRY entry = NULL;
  50. SHORT sidIndex;
  51. SHORT sequence;
  52. PSMB_RESUME_KEY resumeKey = NULL;
  53. PCCHAR s;
  54. PSMB_DIRECTORY_INFORMATION smbDirInfo;
  55. USHORT smbFileAttributes;
  56. PSEARCH search = NULL;
  57. PDIRECTORY_CACHE dirCache, dc;
  58. USHORT count;
  59. USHORT maxCount;
  60. USHORT i;
  61. USHORT resumeKeyLength;
  62. UCHAR command;
  63. CCHAR j;
  64. CLONG nonPagedBufferSize;
  65. ULONG resumeFileIndex;
  66. WCHAR nameBuffer[8 + 1 + 3 + 1];
  67. OEM_STRING oemString;
  68. PTREE_CONNECT treeConnect;
  69. PSESSION session;
  70. PCONNECTION connection;
  71. PPAGED_CONNECTION pagedConnection;
  72. HANDLE RootDirectoryHandle;
  73. WCHAR unicodeResumeName[ sizeof( dirCache->UnicodeResumeName ) / sizeof( WCHAR ) ];
  74. USHORT unicodeResumeNameLength = 0;
  75. PAGED_CODE( );
  76. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  77. WorkContext->PreviousSMB = EVENT_TYPE_SMB_SEARCH;
  78. SrvWmiStartContext(WorkContext);
  79. connection = WorkContext->Connection;
  80. pagedConnection = connection->PagedConnection;
  81. //
  82. // HackHack: Check the flags2 field if dos client. Some dos clients
  83. // set flags2 to 0xffff.
  84. //
  85. isUnicode = SMB_IS_UNICODE( WorkContext );
  86. if ( isUnicode && IS_DOS_DIALECT(connection->SmbDialect) ) {
  87. WorkContext->RequestHeader->Flags2 = 0;
  88. isUnicode = FALSE;
  89. }
  90. filterLongNames =
  91. ((SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
  92. SMB_FLAGS2_KNOWS_LONG_NAMES) == 0) ||
  93. IS_DOS_DIALECT(connection->SmbDialect);
  94. IF_SMB_DEBUG(SEARCH1) {
  95. SrvPrint2( "Search request header at 0x%p, response header at 0x%p\n",
  96. WorkContext->RequestHeader, WorkContext->ResponseHeader );
  97. SrvPrint2( "Search request params at 0x%p, response params%p\n",
  98. WorkContext->RequestParameters,
  99. WorkContext->ResponseParameters );
  100. }
  101. request = (PREQ_SEARCH)WorkContext->RequestParameters;
  102. response = (PRESP_SEARCH)WorkContext->ResponseParameters;
  103. command = WorkContext->RequestHeader->Command;
  104. //
  105. // Set up a pointer in the SMB buffer where we will write
  106. // information about files. The +3 is to account for the
  107. // SMB_FORMAT_VARIABLE and the word that holds the data length.
  108. //
  109. smbDirInfo = (PSMB_DIRECTORY_INFORMATION)(response->Buffer + 3);
  110. fileName.Buffer = NULL;
  111. //
  112. // If a session block has not already been assigned to the current
  113. // work context , verify the UID. If verified, the address of the
  114. // session block corresponding to this user is stored in the
  115. // WorkContext block and the session block is referenced.
  116. //
  117. // Find tree connect corresponding to given TID if a tree connect
  118. // pointer has not already been put in the WorkContext block by an
  119. // AndX command.
  120. //
  121. status = SrvVerifyUidAndTid(
  122. WorkContext,
  123. &session,
  124. &treeConnect,
  125. ShareTypeDisk
  126. );
  127. if ( !NT_SUCCESS(status) ) {
  128. IF_DEBUG(SMB_ERRORS) {
  129. SrvPrint0( "SrvSmbSearch: Invalid UID or TID\n" );
  130. }
  131. SrvSetSmbError( WorkContext, status );
  132. SmbStatus = SmbStatusSendResponse;
  133. goto Cleanup;
  134. }
  135. isCoreSearch = (BOOLEAN)(command == SMB_COM_SEARCH);
  136. if( session->IsSessionExpired )
  137. {
  138. status = SESSION_EXPIRED_STATUS_CODE;
  139. SrvSetSmbError( WorkContext, status );
  140. SmbStatus = SmbStatusSendResponse;
  141. goto Cleanup;
  142. }
  143. //
  144. // Get necessary information from the request SMB before we start
  145. // overwriting it.
  146. //
  147. maxCount = SmbGetUshort( &request->MaxCount );
  148. //
  149. // If they aren't asking for any files, then they are confused!
  150. //
  151. if( maxCount == 0 ) {
  152. //
  153. // We would log this, but certain linux clients mistakenly do this
  154. // over and over and over...
  155. //
  156. status = STATUS_INVALID_SMB;
  157. goto error_exit;
  158. }
  159. //
  160. // If this is a core search, we don't want to get too many files,
  161. // as we have to cache information about them between requests.
  162. //
  163. //
  164. // A buffer of nonpaged pool is required by SrvQueryDirectoryFile.
  165. // We need to use the SMB buffer for found file names and
  166. // information, so allocate a buffer from nonpaged pool.
  167. //
  168. // If we don't need to return many files, we don't need to allocate
  169. // a large buffer. The buffer size is the configurable size or
  170. // enough to hold two more than the number of files we need to
  171. // return. We get space to hold two extra files in case some files
  172. // do not meet the search criteria (eg directories).
  173. //
  174. if ( isCoreSearch ) {
  175. maxCount = MIN( maxCount, (USHORT)MAX_DIRECTORY_CACHE_SIZE );
  176. nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
  177. } else {
  178. if ( maxCount > MAX_FILES_FOR_MED_SEARCH ) {
  179. nonPagedBufferSize = MAX_SEARCH_BUFFER_SIZE;
  180. } else if ( maxCount > MAX_FILES_FOR_MIN_SEARCH ) {
  181. nonPagedBufferSize = MED_SEARCH_BUFFER_SIZE;
  182. } else {
  183. nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
  184. }
  185. }
  186. //
  187. // The response to a search is never unicode, though the request may be.
  188. //
  189. if( isUnicode ) {
  190. USHORT flags2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 );
  191. flags2 &= ~SMB_FLAGS2_UNICODE;
  192. SmbPutAlignedUshort( &WorkContext->ResponseHeader->Flags2, flags2 );
  193. }
  194. //
  195. // If there was a resume key, verify the SID. If there was no
  196. // resume key, allocate a search block. The first two bytes after
  197. // the format token are the length of the resume key. If they are
  198. // both zero, then the request was a find first.
  199. //
  200. count = MIN( SmbGetUshort( &request->ByteCount ), (USHORT)(END_OF_REQUEST_SMB(WorkContext)-request->Buffer+1) );
  201. if( isUnicode ) {
  202. PWCHAR p;
  203. // We will be starting at a WCHAR offset, so we'll skip the first byte. Remove it.
  204. // Also, set count to an even length so we don't have any "half-char" style buffer overruns
  205. count = count & ~1;
  206. for( p = (PWCHAR)((PCCHAR)request->Buffer+1), i = 0;
  207. i < count && SmbGetUshort(p) != UNICODE_NULL;
  208. p++, i += sizeof(*p) );
  209. s = (PCCHAR)(p + 1); // skip past the null to the next char
  210. } else {
  211. for ( s = (PCCHAR)request->Buffer, i = 0;
  212. i < count && *s != (CCHAR)SMB_FORMAT_VARIABLE;
  213. s++, i += sizeof(*s) );
  214. }
  215. //
  216. // If there was no SMB_FORMAT_VARIABLE token in the buffer, fail.
  217. //
  218. if ( i == count || *s != (CCHAR)SMB_FORMAT_VARIABLE ) {
  219. IF_DEBUG(SMB_ERRORS) {
  220. SrvPrint0( "SrvSmbSearch: no SMB_FORMAT_VARIABLE token.\n" );
  221. }
  222. SrvLogInvalidSmb( WorkContext );
  223. status = STATUS_INVALID_SMB;
  224. goto error_exit;
  225. }
  226. resumeKeyLength = SmbGetUshort( (PSMB_USHORT)(s+1) );
  227. if ( resumeKeyLength == 0 ) {
  228. //
  229. // There was no resume key, so either a Find First or a Find
  230. // Unique was intended. If it was actually a Find Close, return
  231. // an error to the client.
  232. //
  233. if ( command == SMB_COM_FIND_CLOSE ) {
  234. IF_DEBUG(SMB_ERRORS) {
  235. SrvPrint0( "SrvSmbSearch: Find Close sent w/o resume key.\n" );
  236. }
  237. status = STATUS_INVALID_SMB;
  238. SrvLogInvalidSmb( WorkContext );
  239. goto error_exit;
  240. }
  241. IF_SMB_DEBUG(SEARCH2) SrvPrint0( "FIND FIRST\n" );
  242. findFirst = TRUE;
  243. calledQueryDirectory = FALSE;
  244. //
  245. // Initialize the string containing the path name. The +1 is to
  246. // account for the ASCII token in the Buffer field of the
  247. // request SMB.
  248. //
  249. status = SrvCanonicalizePathName(
  250. WorkContext,
  251. treeConnect->Share,
  252. NULL,
  253. (PVOID)(request->Buffer + 1),
  254. END_OF_REQUEST_SMB( WorkContext ),
  255. FALSE,
  256. isUnicode,
  257. &fileName
  258. );
  259. if( !NT_SUCCESS( status ) ) {
  260. goto error_exit;
  261. }
  262. //
  263. // If the volume attribute bit is set, just return the volume name.
  264. //
  265. if ( SmbGetUshort( &request->SearchAttributes )
  266. == SMB_FILE_ATTRIBUTE_VOLUME ) {
  267. //
  268. // Use NtQueryVolumeInformationFile to get the volume name.
  269. //
  270. IO_STATUS_BLOCK ioStatusBlock;
  271. PFILE_FS_VOLUME_INFORMATION volumeInformation;
  272. //
  273. // Allocate enough space to store the volume information structure
  274. // and MAXIMUM_FILENAME_LENGTH bytes for the volume label name.
  275. //
  276. volumeInformation = ALLOCATE_HEAP( VOLUME_BUFFER_SIZE, BlockTypeVolumeInformation );
  277. if ( volumeInformation == NULL ) {
  278. INTERNAL_ERROR(
  279. ERROR_LEVEL_EXPECTED,
  280. "SrvSmbSearch: Unable to allocate memory from server heap",
  281. NULL,
  282. NULL
  283. );
  284. status = STATUS_INSUFF_SERVER_RESOURCES;
  285. goto error_exit;
  286. }
  287. RtlZeroMemory( volumeInformation, VOLUME_BUFFER_SIZE );
  288. //
  289. // Get the Share root handle.
  290. //
  291. status = SrvGetShareRootHandle( treeConnect->Share );
  292. if ( !NT_SUCCESS(status) ) {
  293. IF_DEBUG(ERRORS) {
  294. SrvPrint1( "SrvSmbSearch: SrvGetShareRootHandle failed %x.\n",
  295. status );
  296. }
  297. FREE_HEAP( volumeInformation );
  298. goto error_exit;
  299. }
  300. //
  301. // Handle SnapShot case
  302. //
  303. status = SrvSnapGetRootHandle( WorkContext, &RootDirectoryHandle );
  304. if( !NT_SUCCESS(status) )
  305. {
  306. FREE_HEAP( volumeInformation );
  307. goto error_exit;
  308. }
  309. status = NtQueryVolumeInformationFile(
  310. RootDirectoryHandle,
  311. &ioStatusBlock,
  312. volumeInformation,
  313. VOLUME_BUFFER_SIZE,
  314. FileFsVolumeInformation
  315. );
  316. //
  317. // If the media was changed and we can come up with a new share root handle,
  318. // then we should retry the operation
  319. //
  320. if( SrvRetryDueToDismount( treeConnect->Share, status ) ) {
  321. status = SrvSnapGetRootHandle( WorkContext, &RootDirectoryHandle );
  322. if( !NT_SUCCESS(status) )
  323. {
  324. FREE_HEAP( volumeInformation );
  325. goto error_exit;
  326. }
  327. status = NtQueryVolumeInformationFile(
  328. RootDirectoryHandle,
  329. &ioStatusBlock,
  330. volumeInformation,
  331. VOLUME_BUFFER_SIZE,
  332. FileFsVolumeInformation
  333. );
  334. }
  335. //
  336. // Release the share root handle
  337. //
  338. SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
  339. if ( !NT_SUCCESS(status) ) {
  340. INTERNAL_ERROR(
  341. ERROR_LEVEL_UNEXPECTED,
  342. "SrvSmbSearch: NtQueryVolumeInformationFile returned %X",
  343. status,
  344. NULL
  345. );
  346. SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
  347. FREE_HEAP( volumeInformation );
  348. goto error_exit;
  349. }
  350. IF_SMB_DEBUG(SEARCH2) {
  351. SrvPrint2( "NtQueryVolumeInformationFile succeeded, name = %ws, "
  352. "length %ld\n", volumeInformation->VolumeLabel,
  353. volumeInformation->VolumeLabelLength );
  354. }
  355. //
  356. // Check if we have a volume label
  357. //
  358. if ( volumeInformation->VolumeLabelLength > 0 ) {
  359. //
  360. // Build the response SMB.
  361. //
  362. response->WordCount = 1;
  363. SmbPutUshort( &response->Count, 1 );
  364. SmbPutUshort(
  365. &response->ByteCount,
  366. 3 + sizeof(SMB_DIRECTORY_INFORMATION)
  367. );
  368. response->Buffer[0] = SMB_FORMAT_VARIABLE;
  369. SmbPutUshort(
  370. (PSMB_USHORT)(response->Buffer+1),
  371. sizeof(SMB_DIRECTORY_INFORMATION)
  372. );
  373. //
  374. // *** Is there anything that we must set in the resume key?
  375. //
  376. smbDirInfo->FileAttributes = SMB_FILE_ATTRIBUTE_VOLUME;
  377. SmbZeroTime( &smbDirInfo->LastWriteTime );
  378. SmbZeroDate( &smbDirInfo->LastWriteDate );
  379. SmbPutUlong( &smbDirInfo->FileSize, 0 );
  380. {
  381. UNICODE_STRING unicodeString;
  382. OEM_STRING innerOemString;
  383. //
  384. // Volume labels may be longer than 11 bytes, but we
  385. // truncate then to this length in order to make sure that
  386. // the label will fit into the 8.3+NULL -byte space in the
  387. // SMB_DIRECTORY_INFORMATION structure. Also, the LM 1.2
  388. // ring 3 and Pinball servers do this.
  389. //
  390. unicodeString.Length =
  391. (USHORT) MIN(
  392. volumeInformation->VolumeLabelLength,
  393. 11 * sizeof(WCHAR) );
  394. unicodeString.MaximumLength = 13;
  395. unicodeString.Buffer = volumeInformation->VolumeLabel;
  396. innerOemString.MaximumLength = 13;
  397. innerOemString.Buffer = (PCHAR)smbDirInfo->FileName;
  398. RtlUnicodeStringToOemString(
  399. &innerOemString,
  400. &unicodeString,
  401. FALSE
  402. );
  403. //
  404. // If the volume label is greater than 8 characters, it
  405. // needs to be turned into 8.3 format.
  406. //
  407. if( innerOemString.Length > 8 ) {
  408. //
  409. // Slide the last three characters one position to the
  410. // right and insert a '.' to formulate an 8.3 name
  411. //
  412. smbDirInfo->FileName[11] = smbDirInfo->FileName[10];
  413. smbDirInfo->FileName[10] = smbDirInfo->FileName[9];
  414. smbDirInfo->FileName[9] = smbDirInfo->FileName[8];
  415. smbDirInfo->FileName[8] = '.';
  416. innerOemString.Length++;
  417. }
  418. smbDirInfo->FileName[innerOemString.Length] = '\0';
  419. //
  420. // Blank pad space after the volume label.
  421. //
  422. for ( i = (USHORT)(innerOemString.Length + 1);
  423. i < 13;
  424. i++ ) {
  425. smbDirInfo->FileName[i] = ' ';
  426. }
  427. }
  428. //
  429. // Store the resume key in the response packet. DOS redirs
  430. // actually use this!
  431. //
  432. {
  433. UNICODE_STRING baseFileName;
  434. SrvGetBaseFileName( &fileName, &baseFileName );
  435. SrvUnicodeStringTo8dot3(
  436. &baseFileName,
  437. (PSZ)smbDirInfo->ResumeKey.FileName,
  438. FALSE
  439. );
  440. //
  441. // I set Sid = 1 because the next 5 bytes should
  442. // be nonzero and we don't really have a sid.
  443. //
  444. smbDirInfo->ResumeKey.Sid = 0x01;
  445. SmbPutUlong( &smbDirInfo->ResumeKey.FileIndex, 0);
  446. }
  447. } else {
  448. //
  449. // There is no volume label.
  450. //
  451. response->WordCount = 1;
  452. SmbPutUshort( &response->Count, 0 );
  453. SmbPutUshort( &response->ByteCount, 3 );
  454. response->Buffer[0] = SMB_FORMAT_VARIABLE;
  455. SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
  456. }
  457. WorkContext->ResponseParameters =
  458. NEXT_LOCATION(
  459. response,
  460. RESP_SEARCH,
  461. SmbGetUshort( &response->ByteCount )
  462. );
  463. FREE_HEAP( volumeInformation );
  464. if ( !isUnicode &&
  465. fileName.Buffer != NULL &&
  466. fileName.Buffer != nameBuffer ) {
  467. RtlFreeUnicodeString( &fileName );
  468. }
  469. SmbStatus = SmbStatusSendResponse;
  470. goto Cleanup;
  471. }
  472. //
  473. // If this is a core search without patterns, short circuit the
  474. // whole thing right here.
  475. //
  476. if( isCoreSearch && fileName.Length <= sizeof( nameBuffer ) &&
  477. ( fileName.Length == 0 ||
  478. !FsRtlDoesNameContainWildCards( &fileName )) ) {
  479. IO_STATUS_BLOCK ioStatus;
  480. OBJECT_ATTRIBUTES objectAttributes;
  481. ULONG attributes;
  482. ULONG inclusiveSearchAttributes, exclusiveSearchAttributes;
  483. USHORT searchAttributes;
  484. UNICODE_STRING baseFileName;
  485. BOOLEAN returnDirectories, returnDirectoriesOnly;
  486. FILE_NETWORK_OPEN_INFORMATION fileInformation;
  487. PSZ dirInfoName;
  488. SMB_DATE dosDate;
  489. SMB_TIME dosTime;
  490. UNICODE_STRING foundFileName;
  491. UNICODE_STRING ObjectNameString;
  492. PUNICODE_STRING filePathName;
  493. BOOLEAN FreePathName = FALSE;
  494. ObjectNameString.Buffer = fileName.Buffer;
  495. ObjectNameString.Length = fileName.Length;
  496. ObjectNameString.MaximumLength = fileName.Length;
  497. if( fileName.Length == 0 ) {
  498. //
  499. // Since we are opening the root of the share, set the attribute to
  500. // case insensitive, as this is how we opened the share when it was added
  501. //
  502. status = SrvSnapGetNameString( WorkContext, &filePathName, &FreePathName );
  503. if( !NT_SUCCESS(status) )
  504. {
  505. goto error_exit;
  506. }
  507. ObjectNameString = *filePathName;
  508. attributes = OBJ_CASE_INSENSITIVE;
  509. } else {
  510. attributes =
  511. (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
  512. WorkContext->Session->UsingUppercasePaths ) ?
  513. OBJ_CASE_INSENSITIVE : 0;
  514. }
  515. SrvInitializeObjectAttributes_U(
  516. &objectAttributes,
  517. &ObjectNameString,
  518. attributes,
  519. NULL,
  520. NULL
  521. );
  522. status = IMPERSONATE( WorkContext );
  523. if( NT_SUCCESS( status ) ) {
  524. status = SrvGetShareRootHandle( treeConnect->Share );
  525. if( NT_SUCCESS( status ) ) {
  526. //
  527. // The file name is always relative to the share root
  528. //
  529. status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
  530. if( !NT_SUCCESS(status) )
  531. {
  532. goto SnapError;
  533. }
  534. //
  535. // Get the attributes of the object
  536. //
  537. if( IoFastQueryNetworkAttributes(
  538. &objectAttributes,
  539. FILE_READ_ATTRIBUTES,
  540. 0,
  541. &ioStatus,
  542. &fileInformation
  543. ) == FALSE ) {
  544. SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
  545. ioStatus.Status = STATUS_OBJECT_PATH_NOT_FOUND;
  546. }
  547. status = ioStatus.Status;
  548. //
  549. // If the media was changed and we can come up with a new share root handle,
  550. // then we should retry the operation
  551. //
  552. if( SrvRetryDueToDismount( treeConnect->Share, status ) ) {
  553. status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
  554. if( !NT_SUCCESS(status) )
  555. {
  556. goto SnapError;
  557. }
  558. //
  559. // Get the attributes of the object
  560. //
  561. if( IoFastQueryNetworkAttributes(
  562. &objectAttributes,
  563. FILE_READ_ATTRIBUTES,
  564. 0,
  565. &ioStatus,
  566. &fileInformation
  567. ) == FALSE ) {
  568. SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
  569. ioStatus.Status = STATUS_OBJECT_PATH_NOT_FOUND;
  570. }
  571. }
  572. SrvReleaseShareRootHandle( treeConnect->Share );
  573. }
  574. SnapError:
  575. REVERT();
  576. }
  577. // Free up the string
  578. if( FreePathName )
  579. {
  580. FREE_HEAP( filePathName );
  581. filePathName = NULL;
  582. }
  583. if( !NT_SUCCESS( status ) ) {
  584. //
  585. // Do error mapping to keep the DOS clients happy
  586. //
  587. if ( status == STATUS_NO_SUCH_FILE ||
  588. status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  589. status = STATUS_NO_MORE_FILES;
  590. }
  591. goto error_exit;
  592. }
  593. searchAttributes = SmbGetUshort( &request->SearchAttributes );
  594. searchAttributes &= SMB_FILE_ATTRIBUTE_READONLY |
  595. SMB_FILE_ATTRIBUTE_HIDDEN |
  596. SMB_FILE_ATTRIBUTE_SYSTEM |
  597. SMB_FILE_ATTRIBUTE_VOLUME |
  598. SMB_FILE_ATTRIBUTE_DIRECTORY |
  599. SMB_FILE_ATTRIBUTE_ARCHIVE;
  600. //
  601. // The file or directory exists, now we need to see if it matches the
  602. // criteria given by the client
  603. //
  604. SRV_SMB_ATTRIBUTES_TO_NT(
  605. searchAttributes & 0xff,
  606. &returnDirectories,
  607. &inclusiveSearchAttributes
  608. );
  609. inclusiveSearchAttributes |= FILE_ATTRIBUTE_NORMAL |
  610. FILE_ATTRIBUTE_ARCHIVE |
  611. FILE_ATTRIBUTE_READONLY;
  612. SRV_SMB_ATTRIBUTES_TO_NT(
  613. searchAttributes >> 8,
  614. &returnDirectoriesOnly,
  615. &exclusiveSearchAttributes
  616. );
  617. exclusiveSearchAttributes &= ~FILE_ATTRIBUTE_NORMAL;
  618. //
  619. // See if the returned file meets our objectives
  620. //
  621. if(
  622. //
  623. // If we're only supposed to return directories, then we don't want it
  624. //
  625. returnDirectoriesOnly
  626. ||
  627. //
  628. // If there are bits set in FileAttributes that are
  629. // not set in inclusiveSearchAttributes, then we don't want it
  630. //
  631. ((fileInformation.FileAttributes << 24 ) |
  632. ( inclusiveSearchAttributes << 24 )) !=
  633. ( inclusiveSearchAttributes << 24 )
  634. ||
  635. //
  636. // If the file doesn't have attribute bits specified as exclusive
  637. // bits, we don't want it
  638. //
  639. ( ((fileInformation.FileAttributes << 24 ) |
  640. (exclusiveSearchAttributes << 24 )) !=
  641. (fileInformation.FileAttributes << 24) )
  642. ) {
  643. //
  644. // Just as though the file was never there!
  645. //
  646. status = STATUS_OBJECT_PATH_NOT_FOUND;
  647. goto error_exit;
  648. }
  649. //
  650. // We want this entry!
  651. // Fill in the response
  652. //
  653. //
  654. // Switch over to a private name buffer, to avoid overwriting info
  655. // in the SMB buffer.
  656. //
  657. RtlCopyMemory( nameBuffer, fileName.Buffer, fileName.Length );
  658. foundFileName.Buffer = nameBuffer;
  659. foundFileName.Length = fileName.Length;
  660. foundFileName.MaximumLength = fileName.MaximumLength;
  661. SrvGetBaseFileName( &foundFileName, &baseFileName );
  662. SrvUnicodeStringTo8dot3(
  663. &baseFileName,
  664. (PSZ)smbDirInfo->ResumeKey.FileName,
  665. TRUE
  666. );
  667. //
  668. // Resume Key doesn't matter, since the client will not come back. But
  669. // just in case it does, make sure we have a bum resume key
  670. //
  671. SET_RESUME_KEY_INDEX( (PSMB_RESUME_KEY)smbDirInfo, 0xff );
  672. SET_RESUME_KEY_SEQUENCE( (PSMB_RESUME_KEY)smbDirInfo, 0xff );
  673. SmbPutUlong( &((PSMB_RESUME_KEY)smbDirInfo)->FileIndex, 0 );
  674. SmbPutUlong( (PSMB_ULONG)&((PSMB_RESUME_KEY)smbDirInfo)->Consumer[0], 0 );
  675. //
  676. // Fill in the name (even though they knew what it was!)
  677. //
  678. oemString.Buffer = smbDirInfo->FileName;
  679. oemString.MaximumLength = sizeof( smbDirInfo->FileName );
  680. RtlUpcaseUnicodeStringToOemString( &oemString, &baseFileName, FALSE );
  681. //
  682. // Null terminate and blank-pad the name
  683. //
  684. oemString.Buffer[ oemString.Length ] = '\0';
  685. for( i=(USHORT)oemString.Length+1; i < sizeof( smbDirInfo->FileName); i++ ) {
  686. oemString.Buffer[i] = ' ';
  687. }
  688. SRV_NT_ATTRIBUTES_TO_SMB(
  689. fileInformation.FileAttributes,
  690. fileInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
  691. &smbFileAttributes
  692. );
  693. smbDirInfo->FileAttributes = (UCHAR)smbFileAttributes;
  694. SrvTimeToDosTime(
  695. &fileInformation.LastWriteTime,
  696. &dosDate,
  697. &dosTime
  698. );
  699. SmbPutDate( &smbDirInfo->LastWriteDate, dosDate );
  700. SmbPutTime( &smbDirInfo->LastWriteTime, dosTime );
  701. SmbPutUlong( &smbDirInfo->FileSize, fileInformation.EndOfFile.LowPart );
  702. totalBytesWritten = sizeof(SMB_DIRECTORY_INFORMATION);
  703. count = 1;
  704. goto done_core;
  705. }
  706. directoryInformation = ALLOCATE_NONPAGED_POOL(
  707. nonPagedBufferSize,
  708. BlockTypeDirectoryInfo
  709. );
  710. if ( directoryInformation == NULL ) {
  711. INTERNAL_ERROR(
  712. ERROR_LEVEL_EXPECTED,
  713. "SrvSmbSearch: unable to allocate nonpaged pool",
  714. NULL,
  715. NULL
  716. );
  717. status = STATUS_INSUFF_SERVER_RESOURCES;
  718. goto error_exit;
  719. }
  720. directoryInformation->DirectoryHandle = NULL;
  721. directoryInformation->DownlevelTimewarp = FALSE;
  722. IF_SMB_DEBUG(SEARCH2) {
  723. SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
  724. nonPagedBufferSize, directoryInformation );
  725. }
  726. //
  727. // Allocate a search block. The search block is used to retain
  728. // state information between search or find SMBs. The search
  729. // blocks for core and regular searches are slightly different,
  730. // hence the BOOLEAN parameter to SrvAllocateSearch.
  731. //
  732. //
  733. // If we've reached our max, start closing searches.
  734. //
  735. if ( SrvStatistics.CurrentNumberOfOpenSearches >= SrvMaxOpenSearches ) {
  736. SrvForceTimeoutSearches( connection );
  737. }
  738. SrvAllocateSearch(
  739. &search,
  740. &fileName,
  741. isCoreSearch
  742. );
  743. if ( search == NULL ) {
  744. IF_DEBUG(ERRORS) {
  745. SrvPrint0( "SrvSmbSearch: unable to allocate search block.\n" );
  746. }
  747. status = STATUS_INSUFF_SERVER_RESOURCES;
  748. goto error_exit;
  749. }
  750. search->Pid = SmbGetAlignedUshort(
  751. &WorkContext->RequestHeader->Pid
  752. );
  753. //
  754. // Set up referenced session and tree connect pointers and
  755. // increment the count of open files on the session. This
  756. // prevents an idle session with an open search from being
  757. // autodisconnected.
  758. //
  759. ACQUIRE_LOCK( &connection->Lock );
  760. if ( isCoreSearch ) {
  761. pagedConnection->CurrentNumberOfCoreSearches++;
  762. }
  763. search->Session = WorkContext->Session;
  764. SrvReferenceSession( WorkContext->Session );
  765. search->TreeConnect = WorkContext->TreeConnect;
  766. SrvReferenceTreeConnect( WorkContext->TreeConnect );
  767. //
  768. // If this is not a find unique, put the search block in the
  769. // search table. Otherwise, just set the sidIndex and sequence
  770. // variables to 0 to distinguish between a valid resumable
  771. // search block.
  772. //
  773. if ( command == SMB_COM_FIND_UNIQUE ) {
  774. WorkContext->Session->CurrentSearchOpenCount++;
  775. RELEASE_LOCK( &connection->Lock );
  776. sequence = sidIndex = -1;
  777. } else {
  778. NTSTATUS TableStatus;
  779. PTABLE_HEADER searchTable = &pagedConnection->SearchTable;
  780. //
  781. // If there are no free entries in the table, attempt to
  782. // grow the table. If we are unable to grow the table,
  783. // attempt to timeout a search block using the shorter
  784. // timeout period. If this fails, reject the request.
  785. //
  786. if ( searchTable->FirstFreeEntry == -1
  787. &&
  788. SrvGrowTable(
  789. searchTable,
  790. SrvInitialSearchTableSize,
  791. SrvMaxSearchTableSize,
  792. &TableStatus ) == FALSE
  793. &&
  794. SrvTimeoutSearches(
  795. NULL,
  796. connection,
  797. TRUE ) == 0
  798. ) {
  799. IF_DEBUG(ERRORS)
  800. SrvPrint0( "SrvSmbSearch: Connection searchTable full.\n" );
  801. RELEASE_LOCK( &connection->Lock );
  802. if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
  803. {
  804. SrvLogTableFullError( SRV_TABLE_SEARCH );
  805. status = STATUS_OS2_NO_MORE_SIDS;
  806. }
  807. else
  808. {
  809. status = TableStatus;
  810. }
  811. goto error_exit;
  812. } else if ( GET_BLOCK_STATE( session ) != BlockStateActive ) {
  813. //
  814. //
  815. // If the session is closing do not insert this search
  816. // on the search list, because the list may already
  817. // have been cleaned up.
  818. //
  819. RELEASE_LOCK( &connection->Lock );
  820. status = STATUS_SMB_BAD_UID;
  821. goto error_exit;
  822. } else if ( GET_BLOCK_STATE( treeConnect ) != BlockStateActive ) {
  823. //
  824. // Tree connect is closing. Don't insert the search block
  825. // so the tree connect can be cleaned up immediately.
  826. //
  827. RELEASE_LOCK( &connection->Lock );
  828. status = STATUS_SMB_BAD_TID;
  829. goto error_exit;
  830. }
  831. //
  832. // increment the count of open searches
  833. //
  834. WorkContext->Session->CurrentSearchOpenCount++;
  835. sidIndex = searchTable->FirstFreeEntry;
  836. //
  837. // A free SID was found. Remove it from the free list and set
  838. // its owner and sequence number.
  839. //
  840. entry = &searchTable->Table[sidIndex];
  841. searchTable->FirstFreeEntry = entry->NextFreeEntry;
  842. DEBUG entry->NextFreeEntry = -2;
  843. if ( searchTable->LastFreeEntry == sidIndex ) {
  844. searchTable->LastFreeEntry = -1;
  845. }
  846. INCREMENT_SID_SEQUENCE( entry->SequenceNumber );
  847. //
  848. // SID = sequence | sidIndex == 0 is illegal. If this is
  849. // the current value, increment the sequence.
  850. //
  851. if ( entry->SequenceNumber == 0 && sidIndex == 0 ) {
  852. INCREMENT_SID_SEQUENCE( entry->SequenceNumber );
  853. }
  854. sequence = entry->SequenceNumber;
  855. entry->Owner = search;
  856. RELEASE_LOCK( &connection->Lock );
  857. }
  858. //
  859. // Fill in other fields of the search block.
  860. //
  861. search->SearchAttributes =
  862. SmbGetUshort( &request->SearchAttributes );
  863. search->TableIndex = sidIndex;
  864. IF_SMB_DEBUG(SEARCH2) {
  865. SrvPrint3( "Allocated search block at 0x%p. Index = 0x%lx, sequence = 0x%lx\n", search, sidIndex, sequence );
  866. }
  867. } else {
  868. //
  869. // The resume key length was nonzero, so this should be a find
  870. // next. Check the resume key length to be safe.
  871. //
  872. USHORT resumeSequence;
  873. if ( resumeKeyLength != sizeof(SMB_RESUME_KEY) ) {
  874. IF_DEBUG(SMB_ERRORS) {
  875. SrvPrint2( "Resume key length was incorrect--was %ld instead "
  876. "of %ld\n", resumeKeyLength, sizeof(SMB_RESUME_KEY) );
  877. }
  878. SrvLogInvalidSmb( WorkContext );
  879. status = STATUS_INVALID_SMB;
  880. goto error_exit;
  881. }
  882. findFirst = FALSE;
  883. resumeKey = (PSMB_RESUME_KEY)(s + 3);
  884. //
  885. // Set up the sequence number and index. These are used for the
  886. // return resume keys.
  887. //
  888. sequence = SID_SEQUENCE( resumeKey );
  889. sidIndex = SID_INDEX( resumeKey );
  890. directoryInformation = ALLOCATE_NONPAGED_POOL(
  891. nonPagedBufferSize,
  892. BlockTypeDirectoryInfo
  893. );
  894. if ( directoryInformation == NULL ) {
  895. INTERNAL_ERROR(
  896. ERROR_LEVEL_EXPECTED,
  897. "SrvSmbSearch: unable to allocate nonpaged pool",
  898. NULL,
  899. NULL
  900. );
  901. status = STATUS_INSUFF_SERVER_RESOURCES;
  902. goto error_exit;
  903. }
  904. directoryInformation->DirectoryHandle = NULL;
  905. IF_SMB_DEBUG(SEARCH2) {
  906. SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
  907. nonPagedBufferSize, directoryInformation );
  908. }
  909. //
  910. // Verify the SID in the resume key. SrvVerifySid also fills in
  911. // fields of directoryInformation so it is ready to be used by
  912. // SrvQueryDirectoryFile.
  913. //
  914. search = SrvVerifySid(
  915. WorkContext,
  916. sidIndex,
  917. sequence,
  918. directoryInformation,
  919. nonPagedBufferSize
  920. );
  921. if ( search == NULL ) {
  922. if (0) IF_DEBUG(SMB_ERRORS) {
  923. SrvPrint2( "SrvSmbSearch: Invalid resume key (SID): index = "
  924. "%lx, seq. = %lx\n",
  925. sidIndex, sequence );
  926. }
  927. status = STATUS_INVALID_PARAMETER;
  928. goto error_exit;
  929. }
  930. //
  931. // If this is a core search, take the search block off its last-use
  932. // list. We will return it to the end of the list when we are
  933. // done processing this SMB.
  934. //
  935. if ( isCoreSearch ) {
  936. USHORT dirCacheIndex;
  937. ACQUIRE_LOCK( &connection->Lock );
  938. //
  939. // If the reference count on the search block is not 2,
  940. // somebody messed up and we could run into problems,
  941. // because the timeout code assumes that dereferencing a
  942. // search block will kill it. The reference count is 2--one
  943. // for our pointer, one for the active status.
  944. //
  945. ASSERT( search->BlockHeader.ReferenceCount == 2 );
  946. //
  947. // If the search block has already been taken off the LRU
  948. // list, then the client has attempted two simultaneous core
  949. // searches with the same search block. This is an error on
  950. // the part of the client.
  951. //
  952. if ( search->LastUseListEntry.Flink == NULL ) {
  953. RELEASE_LOCK( &connection->Lock );
  954. status = STATUS_INVALID_SMB;
  955. IF_DEBUG(SMB_ERRORS) {
  956. SrvPrint0( "SrvSmbSearch: Attempt to do two simultaneuos core searches on same search block.\n" );
  957. }
  958. SrvLogInvalidSmb( WorkContext );
  959. goto error_exit;
  960. }
  961. SrvRemoveEntryList(
  962. &pagedConnection->CoreSearchList,
  963. &search->LastUseListEntry
  964. );
  965. DECREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
  966. //
  967. // Set the entry pointer fields to NULL so that if another
  968. // core search comes in for this search block we will know
  969. // there is an error.
  970. //
  971. search->LastUseListEntry.Flink = NULL;
  972. search->LastUseListEntry.Blink = NULL;
  973. //
  974. // Get the information from the directory cache
  975. // corresponding to this file and put it into the resume key
  976. // so that SrvQueryDirectoryFile has the proper information
  977. // in the resume key. Core clients do not return the
  978. // correct file name in the resume key, and have special
  979. // requirements for the file index in the resume key.
  980. //
  981. resumeFileIndex = SmbGetUlong( &resumeKey->FileIndex );
  982. resumeSequence = (USHORT)((resumeFileIndex & 0xFFFF0000) >> 16);
  983. dirCacheIndex = (USHORT)(resumeFileIndex & (USHORT)0xFFFF);
  984. //
  985. // If the directory cache pointer in the search block
  986. // indicates that there is no directory cache, then we
  987. // returned no files last time, so return no files this
  988. // time. This is due to DOS weirdness.
  989. //
  990. if ( search->DirectoryCache == NULL ||
  991. dirCacheIndex >= search->NumberOfCachedFiles ) {
  992. IF_SMB_DEBUG(SEARCH2) {
  993. SrvPrint0( "Core request for rewind when no dircache exists.\n" );
  994. }
  995. //
  996. // Put the search block back on the LRU list if the
  997. // session and tree connect is still active.
  998. //
  999. if ((GET_BLOCK_STATE( session ) == BlockStateActive) &&
  1000. (GET_BLOCK_STATE( treeConnect ) == BlockStateActive)) {
  1001. KeQuerySystemTime( &search->LastUseTime );
  1002. SrvInsertTailList(
  1003. &pagedConnection->CoreSearchList,
  1004. &search->LastUseListEntry
  1005. );
  1006. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
  1007. RELEASE_LOCK( &connection->Lock );
  1008. } else {
  1009. RELEASE_LOCK( &connection->Lock );
  1010. //
  1011. // Not needed any more since session is closing.
  1012. //
  1013. SrvCloseSearch( search );
  1014. }
  1015. //
  1016. // Remove our pointer's reference.
  1017. //
  1018. SrvDereferenceSearch( search );
  1019. //
  1020. // Build the response SMB.
  1021. //
  1022. response->WordCount = 1;
  1023. SmbPutUshort( &response->Count, 0 );
  1024. SmbPutUshort( &response->ByteCount, 3 );
  1025. response->Buffer[0] = SMB_FORMAT_VARIABLE;
  1026. SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
  1027. WorkContext->ResponseParameters = NEXT_LOCATION(
  1028. response,
  1029. RESP_SEARCH,
  1030. 3
  1031. );
  1032. DEALLOCATE_NONPAGED_POOL( directoryInformation );
  1033. SmbStatus = SmbStatusSendResponse;
  1034. goto Cleanup;
  1035. }
  1036. dirCache = &search->DirectoryCache[dirCacheIndex];
  1037. IF_SMB_DEBUG(SEARCH2) {
  1038. SrvPrint3( "Accessing dircache, real file = %ws, index = 0x%lx, "
  1039. "cache index = %ld\n",
  1040. dirCache->UnicodeResumeName, dirCache->FileIndex,
  1041. dirCacheIndex );
  1042. }
  1043. SmbPutUlong( &resumeKey->FileIndex, dirCache->FileIndex );
  1044. unicodeResumeNameLength = dirCache->UnicodeResumeNameLength;
  1045. ASSERT( unicodeResumeNameLength <= sizeof( unicodeResumeName ) );
  1046. RtlCopyMemory( unicodeResumeName,
  1047. dirCache->UnicodeResumeName,
  1048. unicodeResumeNameLength );
  1049. //
  1050. // Free the directory cache--it is no longer needed.
  1051. //
  1052. FREE_HEAP( search->DirectoryCache );
  1053. search->DirectoryCache = NULL;
  1054. search->NumberOfCachedFiles = 0;
  1055. RELEASE_LOCK( &connection->Lock );
  1056. } else if ( command == SMB_COM_FIND_CLOSE ) {
  1057. //
  1058. // If this is a find close request, close the search block and
  1059. // dereference it. Close out the directory query, and send the
  1060. // response SMB.
  1061. //
  1062. IF_SMB_DEBUG(SEARCH2) {
  1063. SrvPrint1( "FIND CLOSE: Closing search block at 0x%p\n",
  1064. search );
  1065. }
  1066. SrvCloseQueryDirectory( directoryInformation );
  1067. search->DirectoryHandle = NULL;
  1068. SrvCloseSearch( search );
  1069. //
  1070. // Dereference the search block. SrvCloseSearch has already
  1071. // dereferenced it once, so it will be deallocated when we
  1072. // dereference it here.
  1073. //
  1074. SrvDereferenceSearch( search );
  1075. DEALLOCATE_NONPAGED_POOL( directoryInformation );
  1076. response->WordCount = 1;
  1077. SmbPutUshort( &response->ByteCount, 3 );
  1078. response->Buffer[0] = SMB_FORMAT_VARIABLE;
  1079. SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
  1080. WorkContext->ResponseParameters = NEXT_LOCATION(
  1081. response,
  1082. RESP_SEARCH,
  1083. 0
  1084. );
  1085. SmbStatus = SmbStatusSendResponse;
  1086. goto Cleanup;
  1087. }
  1088. IF_SMB_DEBUG(SEARCH2) {
  1089. SrvPrint1( "FIND NEXT: Resuming search with file %s\n",
  1090. resumeKey->FileName );
  1091. }
  1092. //
  1093. // Set the filename string so that SrvQueryDirectoryFile knows
  1094. // what search to resume on.
  1095. //
  1096. if( unicodeResumeNameLength != 0 ) {
  1097. fileName.Buffer = unicodeResumeName;
  1098. fileName.Length = fileName.MaximumLength = unicodeResumeNameLength;
  1099. } else {
  1100. fileName.Buffer = nameBuffer;
  1101. Srv8dot3ToUnicodeString(
  1102. (PSZ)resumeKey->FileName,
  1103. &fileName
  1104. );
  1105. }
  1106. //
  1107. // Set calledQueryDirectory to TRUE will indicate to
  1108. // SrvQueryDirectoryFile that it does not need to parse the
  1109. // input name for the directory in which the search occurs, nor
  1110. // does it need to open the directory.
  1111. //
  1112. calledQueryDirectory = TRUE;
  1113. //
  1114. // Get the resume file index in an aligned field for later use.
  1115. //
  1116. resumeFileIndex = SmbGetUlong( &resumeKey->FileIndex );
  1117. IF_SMB_DEBUG(SEARCH2) {
  1118. SrvPrint1( "Found search block at 0x%p\n", search );
  1119. }
  1120. }
  1121. IF_SMB_DEBUG(SEARCH2) {
  1122. SrvPrint2( "Sequence # = %ld, index = %ld\n", sequence, sidIndex );
  1123. }
  1124. //
  1125. // Find the amount of space we have available for writing file
  1126. // entries into. The total buffer size available (includes space
  1127. // for the SMB header and parameters) is the minimum of our buffer
  1128. // size and the client's buffer size. The available space is the
  1129. // total buffer space less the amount we will need for the SMB
  1130. // header and parameters.
  1131. //
  1132. IF_SMB_DEBUG(SEARCH2) {
  1133. SrvPrint4( "BL = %ld, MBS = %ld, r->B = 0x%p, RB->Buffer = 0x%p\n",
  1134. WorkContext->ResponseBuffer->BufferLength,
  1135. session->MaxBufferSize, (PSZ)response->Buffer,
  1136. (PSZ)WorkContext->ResponseBuffer->Buffer );
  1137. }
  1138. availableSpace =
  1139. MIN(
  1140. WorkContext->ResponseBuffer->BufferLength,
  1141. (CLONG)session->MaxBufferSize
  1142. ) -
  1143. PTR_DIFF(response->Buffer, WorkContext->ResponseBuffer->Buffer );
  1144. IF_SMB_DEBUG(SEARCH2) {
  1145. SrvPrint1( "Available buffer space: %ld\n", availableSpace );
  1146. }
  1147. //
  1148. // Simplify the search patterns if possible. This makes the filesystems more
  1149. // efficient, as they special case the '*' pattern
  1150. //
  1151. if ( (fileName.Length >= (12 * sizeof(WCHAR))) &&
  1152. (RtlEqualMemory(
  1153. &fileName.Buffer[fileName.Length/sizeof(WCHAR) - 12],
  1154. StrQuestionMarks,
  1155. 12 * sizeof(WCHAR)))) {
  1156. if( fileName.Length == (12 * sizeof( WCHAR )) ||
  1157. fileName.Buffer[ fileName.Length/sizeof(WCHAR) - 13 ] == L'\\' ) {
  1158. //
  1159. // The search name ends in ????????.???, replace it with *
  1160. //
  1161. fileName.Buffer[fileName.Length/sizeof(WCHAR)-12] = L'*';
  1162. fileName.Length -= 11 * sizeof(WCHAR);
  1163. }
  1164. } else if ((fileName.Length >= (3 * sizeof(WCHAR))) &&
  1165. (RtlEqualMemory(
  1166. &fileName.Buffer[fileName.Length/sizeof(WCHAR) - 3],
  1167. StrStarDotStar,
  1168. 3 * sizeof(WCHAR)))) {
  1169. if( fileName.Length == (3 * sizeof( WCHAR )) ||
  1170. fileName.Buffer[ fileName.Length/sizeof(WCHAR) - 4 ] == L'\\' ) {
  1171. //
  1172. // The search name ends in *.*, replace it with *
  1173. //
  1174. fileName.Length -= 2 * sizeof(WCHAR);
  1175. }
  1176. }
  1177. if( isCoreSearch ) {
  1178. dirCache = (PDIRECTORY_CACHE)ALLOCATE_HEAP(
  1179. maxCount * sizeof(DIRECTORY_CACHE),
  1180. BlockTypeDirCache
  1181. );
  1182. if( dirCache == NULL ) {
  1183. INTERNAL_ERROR(
  1184. ERROR_LEVEL_EXPECTED,
  1185. "SrvSmbSearch: Unable to allocate %d bytes from heap",
  1186. maxCount * sizeof(DIRECTORY_CACHE),
  1187. NULL
  1188. );
  1189. status = STATUS_INSUFF_SERVER_RESOURCES;
  1190. goto error_exit;
  1191. }
  1192. RtlZeroMemory( dirCache, maxCount * sizeof(DIRECTORY_CACHE) );
  1193. }
  1194. //
  1195. // Now we can start getting files. We do this until one of three
  1196. // conditions is met:
  1197. //
  1198. // 1) There are no more files to return.
  1199. // 2) We have obtained as many files as were requested.
  1200. // 3) The SMB buffer is full.
  1201. //
  1202. count = 0;
  1203. totalBytesWritten = 0;
  1204. dc = dirCache;
  1205. do {
  1206. PSZ dirInfoName;
  1207. UNICODE_STRING name;
  1208. PFILE_BOTH_DIR_INFORMATION bothDirInfo;
  1209. SMB_DATE dosDate;
  1210. SMB_TIME dosTime;
  1211. ULONG effectiveBufferSize;
  1212. //
  1213. // Since the file information returned by NtQueryDirectoryFile is
  1214. // about twice the size of the information we must return in the SMB
  1215. // (SMB_DIRECTORY_INFORMATION), use a buffer size equal to twice the
  1216. // available space if the available space is getting small. This
  1217. // optimization means that NtQueryDirectoryFile will return unused
  1218. // files less often. For example, if there is only space left in
  1219. // the SMB buffer for a single file entry, it does not make sense
  1220. // for NtQueryDirectoryFile to completely fill up the buffer--all it
  1221. // really needs to return is a single file.
  1222. //
  1223. effectiveBufferSize = MIN( nonPagedBufferSize, availableSpace * 2 );
  1224. //
  1225. // Make sure that there is at least enough room to hold a single
  1226. // file.
  1227. //
  1228. effectiveBufferSize = MAX( effectiveBufferSize, MIN_SEARCH_BUFFER_SIZE );
  1229. status = SrvQueryDirectoryFile(
  1230. WorkContext,
  1231. (BOOLEAN)!calledQueryDirectory,
  1232. TRUE, // filter long names
  1233. FALSE, // not for backup intent
  1234. FileBothDirectoryInformation,
  1235. 0,
  1236. &fileName,
  1237. (PULONG)( (findFirst || count != 0) ?
  1238. NULL : &resumeFileIndex ),
  1239. search->SearchAttributes,
  1240. directoryInformation,
  1241. effectiveBufferSize
  1242. );
  1243. calledQueryDirectory = TRUE;
  1244. if ( status == STATUS_NO_SUCH_FILE ) {
  1245. status = STATUS_NO_MORE_FILES;
  1246. } else if ( status == STATUS_OBJECT_NAME_NOT_FOUND ) {
  1247. status = STATUS_OBJECT_PATH_NOT_FOUND;
  1248. }
  1249. if ( status == STATUS_NO_MORE_FILES ) {
  1250. if ( findFirst && count == 0 ) {
  1251. //
  1252. // If no files matched on the find first, close out
  1253. // the search.
  1254. //
  1255. IF_SMB_DEBUG(SEARCH1) {
  1256. SrvPrint1( "SrvSmbSearch: no matching files (%wZ).\n",
  1257. &fileName );
  1258. }
  1259. if( isCoreSearch ) {
  1260. FREE_HEAP( dirCache );
  1261. }
  1262. goto error_exit;
  1263. }
  1264. break;
  1265. } else if ( !NT_SUCCESS(status) ) {
  1266. IF_DEBUG(ERRORS) {
  1267. SrvPrint1(
  1268. "SrvSmbSearch: SrvQueryDirectoryFile returned %X\n",
  1269. status
  1270. );
  1271. }
  1272. if( isCoreSearch ) {
  1273. FREE_HEAP( dirCache );
  1274. }
  1275. goto error_exit;
  1276. }
  1277. //
  1278. // Set the resumeKey pointer to NULL. If this is a find next, we
  1279. // have already resumed/rewound the search, so calls to
  1280. // NtQueryDirectoryFile during this search should continue where
  1281. // the last search left off.
  1282. //
  1283. resumeKey = NULL;
  1284. //
  1285. // If this is a Find command, then put the 8dot3 (no ".")
  1286. // representation of the file name into the resume key. If
  1287. // this is a search command, then put the 8dot3 representation of
  1288. // the search specification in the resume key.
  1289. //
  1290. bothDirInfo = (PFILE_BOTH_DIR_INFORMATION)
  1291. directoryInformation->CurrentEntry;
  1292. IF_SMB_DEBUG(SEARCH2) {
  1293. SrvPrint3( "SrvQueryDirectoryFile--name %ws, length = %ld, "
  1294. "status = %X\n",
  1295. bothDirInfo->FileName,
  1296. bothDirInfo->FileNameLength,
  1297. status );
  1298. SrvPrint1( "smbDirInfo = 0x%p\n", smbDirInfo );
  1299. }
  1300. //
  1301. // Use the FileName, unless it is not legal 8.3
  1302. //
  1303. name.Buffer = bothDirInfo->FileName;
  1304. name.Length = (SHORT)bothDirInfo->FileNameLength;
  1305. if( bothDirInfo->ShortNameLength != 0 ) {
  1306. if( !SrvIsLegalFatName( bothDirInfo->FileName,
  1307. bothDirInfo->FileNameLength ) ) {
  1308. //
  1309. // FileName is not legal 8.3, so switch to the
  1310. // ShortName
  1311. //
  1312. name.Buffer = bothDirInfo->ShortName;
  1313. name.Length = (SHORT)bothDirInfo->ShortNameLength;
  1314. }
  1315. }
  1316. name.MaximumLength = name.Length;
  1317. if ( isCoreSearch ) {
  1318. UNICODE_STRING baseFileName;
  1319. SrvGetBaseFileName( &search->SearchName, &baseFileName );
  1320. SrvUnicodeStringTo8dot3(
  1321. &baseFileName,
  1322. (PSZ)smbDirInfo->ResumeKey.FileName,
  1323. filterLongNames
  1324. );
  1325. //
  1326. // Save the unicode version of the 8.3 name in the dirCache
  1327. //
  1328. dc->UnicodeResumeNameLength = MIN(name.Length, 12*sizeof(WCHAR));
  1329. RtlCopyMemory( dc->UnicodeResumeName, name.Buffer, MIN(name.Length, 12*sizeof(WCHAR)) );
  1330. dc->FileIndex = bothDirInfo->FileIndex;
  1331. ++dc;
  1332. } else {
  1333. SrvUnicodeStringTo8dot3(
  1334. &name,
  1335. (PSZ)smbDirInfo->ResumeKey.FileName,
  1336. filterLongNames
  1337. );
  1338. }
  1339. //
  1340. // Generate the resume key for this file.
  1341. //
  1342. // *** This must be done AFTER setting the file name in the resume
  1343. // key, as setting the resume key name would overwrite some
  1344. // of the sequence bytes which are stored in the high bits
  1345. // of the file name bytes.
  1346. //
  1347. SET_RESUME_KEY_INDEX( (PSMB_RESUME_KEY)smbDirInfo, sidIndex );
  1348. SET_RESUME_KEY_SEQUENCE( (PSMB_RESUME_KEY)smbDirInfo, sequence );
  1349. //
  1350. // Put the file index in the resume key.
  1351. //
  1352. SmbPutUlong(
  1353. &((PSMB_RESUME_KEY)smbDirInfo)->FileIndex,
  1354. bothDirInfo->FileIndex
  1355. );
  1356. SmbPutUlong(
  1357. (PSMB_ULONG)&((PSMB_RESUME_KEY)smbDirInfo)->Consumer[0],
  1358. 0
  1359. );
  1360. //
  1361. // Load the file name into the SMB_DIRECTORY_INFORMATION structure.
  1362. //
  1363. dirInfoName = (PSZ)smbDirInfo->FileName;
  1364. oemString.Buffer = dirInfoName;
  1365. oemString.MaximumLength = (USHORT)RtlUnicodeStringToOemSize( &name );
  1366. if ( filterLongNames ) {
  1367. //
  1368. // If the client doesn't understand long names, upcase the file
  1369. // name. This is necessary for compatibility reasons. Note
  1370. // that the FAT file system returns upcased names anyway.
  1371. //
  1372. RtlUpcaseUnicodeStringToOemString( &oemString, &name, FALSE );
  1373. } else {
  1374. RtlUnicodeStringToOemString( &oemString, &name, FALSE );
  1375. }
  1376. //
  1377. // Blank-pad the end of the filename in order to be compatible with
  1378. // prior redirectors.
  1379. //
  1380. // !!! It is not certain whether this is required.
  1381. //
  1382. for ( i = (USHORT)(oemString.MaximumLength); i < 13; i++ ) {
  1383. dirInfoName[i] = ' ';
  1384. }
  1385. //
  1386. // Fill in other fields in the file entry.
  1387. //
  1388. SRV_NT_ATTRIBUTES_TO_SMB(
  1389. bothDirInfo->FileAttributes,
  1390. bothDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
  1391. &smbFileAttributes
  1392. );
  1393. smbDirInfo->FileAttributes = (UCHAR)smbFileAttributes;
  1394. SrvTimeToDosTime(
  1395. &bothDirInfo->LastWriteTime,
  1396. &dosDate,
  1397. &dosTime
  1398. );
  1399. SmbPutDate( &smbDirInfo->LastWriteDate, dosDate );
  1400. SmbPutTime( &smbDirInfo->LastWriteTime, dosTime );
  1401. //
  1402. // *** NT file sizes are LARGE_INTEGERs (64 bits), SMB file sizes
  1403. // are longwords (32 bits). We just return the low 32 bits
  1404. // of the NT file size, because that is all we can do.
  1405. //
  1406. SmbPutUlong( &smbDirInfo->FileSize, bothDirInfo->EndOfFile.LowPart );
  1407. //
  1408. // Find the space left in the SMB buffer.
  1409. //
  1410. availableSpace -= sizeof(SMB_DIRECTORY_INFORMATION);
  1411. totalBytesWritten += sizeof(SMB_DIRECTORY_INFORMATION);
  1412. //
  1413. // Set up the smbDirInfo pointer for the next file. There is
  1414. // no padding for alignment between files, so just increment
  1415. // the pointer.
  1416. //
  1417. smbDirInfo++;
  1418. count++;
  1419. } while ( ( availableSpace > sizeof(SMB_DIRECTORY_INFORMATION) ) &&
  1420. ( count < maxCount ) );
  1421. IF_SMB_DEBUG(SEARCH2) {
  1422. SrvPrint0( "Stopped putting entries in buffer. Reason:\n" );
  1423. if ( status == STATUS_NO_MORE_FILES ) {
  1424. SrvPrint0( "status = STATUS_NO_MORE_FILES\n" );
  1425. } else if ( count >= maxCount ) {
  1426. SrvPrint2( "count = %ld, maxCount = %ld\n", count, maxCount );
  1427. } else {
  1428. SrvPrint1( "Available space = %ld\n", availableSpace );
  1429. }
  1430. }
  1431. //
  1432. // Store information in the search block.
  1433. //
  1434. search->DirectoryHandle = directoryInformation->DirectoryHandle;
  1435. search->Wildcards = directoryInformation->Wildcards;
  1436. search->DownlevelTimewarp = directoryInformation->DownlevelTimewarp;
  1437. //
  1438. // If this was a core search, store information about the files that
  1439. // were returned in a directory cache. Also, modify the information
  1440. // in the SMB buffer, as it is slightly different for core searches.
  1441. //
  1442. if ( isCoreSearch ) {
  1443. if ( count == 0 ) {
  1444. FREE_HEAP( dirCache );
  1445. IF_SMB_DEBUG(SEARCH1) {
  1446. SrvPrint3( "SrvSmbSearch: prematurely closing search %p, index %lx sequence %lx\n",
  1447. search, sidIndex, sequence );
  1448. }
  1449. SrvCloseSearch( search );
  1450. goto done_core;
  1451. }
  1452. //
  1453. // Modify the CoreSequence field of the search block. This is
  1454. // done because core searches demand that the FileIndex field of
  1455. // the resume key always increase.
  1456. //
  1457. search->CoreSequence++;
  1458. //
  1459. // Set up the pointer to the file information now stored in the
  1460. // SMB buffer and save the location of the directory cache.
  1461. // Store the number of files in the directory cache.
  1462. //
  1463. smbDirInfo = (PSMB_DIRECTORY_INFORMATION)(response->Buffer + 3);
  1464. search->DirectoryCache = dirCache;
  1465. search->NumberOfCachedFiles = count;
  1466. //
  1467. // Loop through the files changing information about the files
  1468. // in the SMB buffer to conform to what the core client expects.
  1469. //
  1470. for ( i = 0; i < count; i++ ) {
  1471. SmbPutUlong(
  1472. &smbDirInfo->ResumeKey.FileIndex,
  1473. (search->CoreSequence << 16) + i
  1474. );
  1475. smbDirInfo++;
  1476. dirCache++;
  1477. }
  1478. //
  1479. // If this was a core search, put the search block back on the
  1480. // appropriate search block list. If no files were found for this
  1481. // SMB, put the search block on the complete list. Also, set the
  1482. // last use time field of the search block.
  1483. //
  1484. // If this is a find first to which we responded with
  1485. // one file AND either more than one file was requested or this
  1486. // is a unique search (no wildcards) AND there was space in the
  1487. // buffer for more, close out the search. This saves the memory
  1488. // associated with an open handle and frees up the search table
  1489. // entry. Also close the search if zero files are being returned.
  1490. //
  1491. // We can do this safely because we know that the client would
  1492. // not be able to do a rewind or resume with these conditions
  1493. // and get back anything other than NO_MORE_FILES, which is what
  1494. // we'll return if the client attempts to resume or rewind to an
  1495. // invalid SID.
  1496. //
  1497. if ( (count == 1
  1498. &&
  1499. findFirst
  1500. &&
  1501. ( maxCount > 1 || !search->Wildcards )
  1502. &&
  1503. availableSpace > sizeof(SMB_DIRECTORY_INFORMATION) ) ) {
  1504. IF_SMB_DEBUG(SEARCH1) {
  1505. SrvPrint3( "SrvSmbSearch: prematurely closing search %p, index %lx sequence %lx\n",
  1506. search, sidIndex, sequence );
  1507. }
  1508. SrvCloseSearch( search );
  1509. } else {
  1510. PLIST_ENTRY hashEntry;
  1511. //
  1512. // Put the search on the core search list.
  1513. //
  1514. ACQUIRE_LOCK( &connection->Lock );
  1515. if ( GET_BLOCK_STATE( session ) != BlockStateActive ) {
  1516. //
  1517. // The session is closing. Do not insert this search
  1518. // on the search list, because the list may already
  1519. // have been cleaned up.
  1520. //
  1521. RELEASE_LOCK( &connection->Lock );
  1522. status = STATUS_SMB_BAD_UID;
  1523. goto error_exit;
  1524. } else if ( GET_BLOCK_STATE( treeConnect ) != BlockStateActive ) {
  1525. //
  1526. // Tree connect is closing. Don't insert the search block
  1527. // so the tree connect can be cleaned up immediately.
  1528. //
  1529. RELEASE_LOCK( &connection->Lock );
  1530. status = STATUS_SMB_BAD_TID;
  1531. goto error_exit;
  1532. }
  1533. KeQuerySystemTime( &search->LastUseTime );
  1534. SrvInsertTailList(
  1535. &pagedConnection->CoreSearchList,
  1536. &search->LastUseListEntry
  1537. );
  1538. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
  1539. //
  1540. // Insert this into the hash table.
  1541. //
  1542. hashEntry = &search->HashTableEntry;
  1543. if ( hashEntry->Flink == NULL ) {
  1544. SrvAddToSearchHashTable(
  1545. pagedConnection,
  1546. search
  1547. );
  1548. } else {
  1549. PLIST_ENTRY listHead;
  1550. listHead = &pagedConnection->SearchHashTable[
  1551. search->HashTableIndex].ListHead;
  1552. if ( listHead->Flink != hashEntry ) {
  1553. //
  1554. // remove it and put it back on the front of the queue.
  1555. //
  1556. SrvRemoveEntryList(
  1557. listHead,
  1558. hashEntry
  1559. );
  1560. SrvInsertHeadList(
  1561. listHead,
  1562. hashEntry
  1563. );
  1564. }
  1565. }
  1566. RELEASE_LOCK( &connection->Lock );
  1567. //
  1568. // Make sure the reference count will be 2. 1 for out pointer,
  1569. // and one for the active status.
  1570. //
  1571. ASSERT( search->BlockHeader.ReferenceCount == 2 );
  1572. }
  1573. } else if ( command == SMB_COM_FIND_UNIQUE ) {
  1574. //
  1575. // If this was a find unique, get rid of the search block by
  1576. // closing the query directory and the search block.
  1577. //
  1578. search->DirectoryHandle = NULL;
  1579. SrvCloseQueryDirectory( directoryInformation );
  1580. SrvCloseSearch( search );
  1581. }
  1582. done_core:
  1583. //
  1584. // Set up the response SMB.
  1585. //
  1586. response->WordCount = 1;
  1587. SmbPutUshort( &response->Count, count );
  1588. SmbPutUshort( &response->ByteCount, (USHORT)(totalBytesWritten+3) );
  1589. response->Buffer[0] = SMB_FORMAT_VARIABLE;
  1590. SmbPutUshort(
  1591. (PSMB_USHORT)(response->Buffer+1),
  1592. (USHORT)totalBytesWritten
  1593. );
  1594. WorkContext->ResponseParameters = NEXT_LOCATION(
  1595. response,
  1596. RESP_SEARCH,
  1597. SmbGetUshort( &response->ByteCount )
  1598. );
  1599. //
  1600. // Remove our pointer's reference.
  1601. //
  1602. if( search ) {
  1603. search->InUse = FALSE;
  1604. SrvDereferenceSearch( search );
  1605. }
  1606. if ( !isUnicode &&
  1607. fileName.Buffer != NULL &&
  1608. fileName.Buffer != nameBuffer &&
  1609. fileName.Buffer != unicodeResumeName ) {
  1610. RtlFreeUnicodeString( &fileName );
  1611. }
  1612. if( directoryInformation ) {
  1613. DEALLOCATE_NONPAGED_POOL( directoryInformation );
  1614. }
  1615. SmbStatus = SmbStatusSendResponse;
  1616. goto Cleanup;
  1617. error_exit:
  1618. if ( search != NULL ) {
  1619. //
  1620. // If findFirst == TRUE, then we allocated a search block which
  1621. // we have to close.
  1622. // If findFirst == TRUE and calledQueryDirectory == TRUE, then
  1623. // we also opened the directory handle and need to close it.
  1624. // If findFirst == FALSE, then then we got an existing search
  1625. // block with an existing directory handle.
  1626. //
  1627. if ( findFirst) {
  1628. if ( calledQueryDirectory ) {
  1629. SrvCloseQueryDirectory( directoryInformation );
  1630. search->DirectoryHandle = NULL;
  1631. }
  1632. SrvCloseSearch( search );
  1633. }
  1634. search->InUse = FALSE;
  1635. //
  1636. // Remove our pointer's reference.
  1637. //
  1638. SrvDereferenceSearch( search );
  1639. }
  1640. //
  1641. // Deallocate the directory information block. We do not need
  1642. // to close the directoryhandle here since we should have already
  1643. // closed it (if we need to) in the preceding code.
  1644. //
  1645. if ( directoryInformation != NULL ) {
  1646. DEALLOCATE_NONPAGED_POOL( directoryInformation );
  1647. }
  1648. if ( !isUnicode &&
  1649. fileName.Buffer != NULL &&
  1650. fileName.Buffer != nameBuffer &&
  1651. fileName.Buffer != unicodeResumeName ) {
  1652. RtlFreeUnicodeString( &fileName );
  1653. }
  1654. if( status == STATUS_PATH_NOT_COVERED ) {
  1655. SrvSetSmbError( WorkContext, status );
  1656. } else {
  1657. SrvSetSmbError(
  1658. WorkContext,
  1659. isCoreSearch && (status != STATUS_OBJECT_PATH_NOT_FOUND) ?
  1660. STATUS_NO_MORE_FILES : status
  1661. );
  1662. }
  1663. SmbStatus = SmbStatusSendResponse;
  1664. Cleanup:
  1665. SrvWmiEndContext(WorkContext);
  1666. return SmbStatus;
  1667. } // SrvSmbSearch