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.

2729 lines
85 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. info.c
  5. Abstract:
  6. This module contains various routines for obtaining information such as
  7. times, dates, etc. that is to be returned by SMBs and for converting
  8. information that is given by request SMBs.
  9. Author:
  10. David Treadwell (davidtr) 30-Nov-1989
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "info.tmh"
  15. #pragma hdrstop
  16. #define BugCheckFileId SRV_FILE_INFO
  17. NTSTATUS
  18. BruteForceRewind(
  19. IN HANDLE DirectoryHandle,
  20. IN PVOID Buffer,
  21. IN ULONG BufferLength,
  22. IN PUNICODE_STRING FileName,
  23. IN FILE_INFORMATION_CLASS FileInformationClass,
  24. IN OUT PFILE_DIRECTORY_INFORMATION *CurrentEntry
  25. );
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text( PAGE, SrvCloseQueryDirectory )
  28. #pragma alloc_text( PAGE, SrvQueryInformationFile )
  29. #pragma alloc_text( PAGE, SrvQueryInformationFileAbbreviated )
  30. #pragma alloc_text( PAGE, SrvQueryNtInformationFile )
  31. #pragma alloc_text( PAGE, SrvQueryDirectoryFile )
  32. #pragma alloc_text( PAGE, BruteForceRewind )
  33. #pragma alloc_text( PAGE, SrvQueryEaFile )
  34. #pragma alloc_text( PAGE, SrvTimeToDosTime )
  35. #pragma alloc_text( PAGE, SrvDosTimeToTime )
  36. #pragma alloc_text( PAGE, SrvGetOs2TimeZone )
  37. #pragma alloc_text( PAGE, SrvQueryBasicAndStandardInformation )
  38. #pragma alloc_text( PAGE, SrvQueryNetworkOpenInformation )
  39. #endif
  40. VOID
  41. SrvCloseQueryDirectory (
  42. IN PSRV_DIRECTORY_INFORMATION DirectoryInformation
  43. )
  44. /*++
  45. Routine Description:
  46. This routine cleans up after a directory search was aborted before
  47. SrvQueryDirectoryFile is done. It closes the directory handle.
  48. Arguments:
  49. DirectoryInformation - pointer to the buffer that is being used for
  50. SrvQueryDirectoryFile.
  51. Return Value:
  52. None.
  53. --*/
  54. {
  55. PAGED_CODE( );
  56. //
  57. // Close the directory handle.
  58. //
  59. if ( DirectoryInformation->DirectoryHandle != NULL ) {
  60. SRVDBG_RELEASE_HANDLE( DirectoryInformation->DirectoryHandle, "DID", 8, DirectoryInformation );
  61. SrvNtClose( DirectoryInformation->DirectoryHandle, TRUE );
  62. }
  63. DirectoryInformation->DirectoryHandle = NULL;
  64. } // SrvCloseQueryDirectory
  65. NTSTATUS
  66. SrvQueryInformationFile (
  67. IN HANDLE FileHandle,
  68. IN PFILE_OBJECT FileObject OPTIONAL,
  69. OUT PSRV_FILE_INFORMATION SrvFileInformation,
  70. IN SHARE_TYPE ShareType,
  71. IN BOOLEAN QueryEaSize
  72. )
  73. /*++
  74. Routine Description:
  75. This routine makes calls to NtQueryInformationFile to get information
  76. about a file opened by the server.
  77. Arguments:
  78. FileHandle - a handle of the file to get information about.
  79. FileInformation - pointer to a structure in which to store the
  80. information.
  81. ShareType - The file type. It will be disk, comm, print, pipe
  82. or (-1) for don't care.
  83. QueryEaSize - Try if EA size info is requested.
  84. Return Value:
  85. A status indicating success or failure of the operation.
  86. --*/
  87. {
  88. SRV_NETWORK_OPEN_INFORMATION srvNetworkOpenInformation;
  89. FILE_PIPE_LOCAL_INFORMATION pipeLocalInformation;
  90. NTSTATUS status;
  91. PAGED_CODE( );
  92. //
  93. // Most query operations will fail on comm devices and print shares.
  94. // If this is a disk file, etc. do the queries. If it is a comm
  95. // device, fake it with defaults.
  96. //
  97. if ( ShareType != ShareTypePrint )
  98. {
  99. status = SrvQueryNetworkOpenInformation( FileHandle,
  100. FileObject,
  101. &srvNetworkOpenInformation,
  102. QueryEaSize
  103. );
  104. if ( !NT_SUCCESS(status) ) {
  105. INTERNAL_ERROR(
  106. ERROR_LEVEL_UNEXPECTED,
  107. "SrvQueryInformationFile: NtQueryInformationFile "
  108. " failed: %X",
  109. status,
  110. NULL
  111. );
  112. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  113. return status;
  114. }
  115. } else {
  116. //
  117. // Use defaults for comm and print shares.
  118. //
  119. RtlZeroMemory( &srvNetworkOpenInformation, sizeof( srvNetworkOpenInformation ) );
  120. }
  121. if ( ShareType == ShareTypePipe ) {
  122. FILE_PIPE_INFORMATION pipeInformation;
  123. IO_STATUS_BLOCK ioStatusBlock;
  124. USHORT pipeHandleState;
  125. status = NtQueryInformationFile(
  126. FileHandle,
  127. &ioStatusBlock,
  128. (PVOID)&pipeInformation,
  129. sizeof(pipeInformation),
  130. FilePipeInformation
  131. );
  132. if ( !NT_SUCCESS(status) ) {
  133. INTERNAL_ERROR(
  134. ERROR_LEVEL_UNEXPECTED,
  135. "SrvQueryInformationFile: NtQueryInformationFile "
  136. "(pipe information) failed: %X",
  137. status,
  138. NULL
  139. );
  140. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  141. return status;
  142. }
  143. status = NtQueryInformationFile(
  144. FileHandle,
  145. &ioStatusBlock,
  146. (PVOID)&pipeLocalInformation,
  147. sizeof(pipeLocalInformation),
  148. FilePipeLocalInformation
  149. );
  150. if ( !NT_SUCCESS(status) ) {
  151. INTERNAL_ERROR(
  152. ERROR_LEVEL_UNEXPECTED,
  153. "SrvQueryInformationFile: NtQueryInformationFile "
  154. "(pipe local information) failed: %X",
  155. status,
  156. NULL
  157. );
  158. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  159. return status;
  160. }
  161. //
  162. // Fill in the handle state information in SMB format
  163. //
  164. pipeHandleState = (USHORT)pipeInformation.CompletionMode
  165. << PIPE_COMPLETION_MODE_BITS;
  166. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeEnd
  167. << PIPE_PIPE_END_BITS;
  168. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeType
  169. << PIPE_PIPE_TYPE_BITS;
  170. pipeHandleState |= (USHORT)pipeInformation.ReadMode
  171. << PIPE_READ_MODE_BITS;
  172. pipeHandleState |= (USHORT)(pipeLocalInformation.MaximumInstances &
  173. 0xFF)
  174. << PIPE_MAXIMUM_INSTANCES_BITS;
  175. SrvFileInformation->HandleState = pipeHandleState;
  176. } else {
  177. SrvFileInformation->HandleState = 0;
  178. }
  179. //
  180. // Set up creation time fields.
  181. //
  182. {
  183. LARGE_INTEGER newTime;
  184. ExSystemTimeToLocalTime(
  185. &srvNetworkOpenInformation.LastWriteTime,
  186. &newTime
  187. );
  188. //
  189. // Make sure we round up to two seconds.
  190. //
  191. newTime.QuadPart += AlmostTwoSeconds;
  192. if ( !RtlTimeToSecondsSince1970(
  193. &newTime,
  194. &SrvFileInformation->LastWriteTimeInSeconds
  195. ) ) {
  196. SrvFileInformation->LastWriteTimeInSeconds = 0;
  197. } else {
  198. //
  199. // Mask off the low bit so we can be consistent with LastWriteTime.
  200. // (We need to round up to 2 seconds)
  201. //
  202. SrvFileInformation->LastWriteTimeInSeconds &= ~1;
  203. }
  204. }
  205. SrvTimeToDosTime(
  206. &srvNetworkOpenInformation.LastWriteTime,
  207. &SrvFileInformation->LastWriteDate,
  208. &SrvFileInformation->LastWriteTime
  209. );
  210. if( srvNetworkOpenInformation.CreationTime.QuadPart == srvNetworkOpenInformation.LastWriteTime.QuadPart ) {
  211. SrvFileInformation->CreationDate = SrvFileInformation->LastWriteDate;
  212. SrvFileInformation->CreationTime = SrvFileInformation->LastWriteTime;
  213. } else {
  214. SrvTimeToDosTime(
  215. &srvNetworkOpenInformation.CreationTime,
  216. &SrvFileInformation->CreationDate,
  217. &SrvFileInformation->CreationTime
  218. );
  219. }
  220. if( srvNetworkOpenInformation.LastAccessTime.QuadPart == srvNetworkOpenInformation.LastWriteTime.QuadPart ) {
  221. SrvFileInformation->LastAccessDate = SrvFileInformation->LastWriteDate;
  222. SrvFileInformation->LastAccessTime = SrvFileInformation->LastWriteTime;
  223. } else {
  224. SrvTimeToDosTime(
  225. &srvNetworkOpenInformation.LastAccessTime,
  226. &SrvFileInformation->LastAccessDate,
  227. &SrvFileInformation->LastAccessTime
  228. );
  229. }
  230. //
  231. // Set File Attributes field of structure.
  232. //
  233. SRV_NT_ATTRIBUTES_TO_SMB(
  234. srvNetworkOpenInformation.FileAttributes,
  235. srvNetworkOpenInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
  236. &SrvFileInformation->Attributes
  237. );
  238. //
  239. // Set up allocation and data sizes.
  240. //
  241. // *** Note the assumption that the high part of the 64-bit
  242. // allocation and EOF size is zero. If it's not (i.e., the file
  243. // is bigger than 4GB), then we're out of luck, because the SMB
  244. // protocol can't express that.
  245. //
  246. SrvFileInformation->AllocationSize.QuadPart =
  247. srvNetworkOpenInformation.AllocationSize.QuadPart;
  248. SrvFileInformation->DataSize.QuadPart =
  249. srvNetworkOpenInformation.EndOfFile.QuadPart;
  250. //
  251. // Set the file device type.
  252. //
  253. switch( ShareType ) {
  254. case ShareTypeDisk:
  255. SrvFileInformation->Type = FileTypeDisk;
  256. break;
  257. case ShareTypePipe:
  258. if (pipeLocalInformation.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) {
  259. SrvFileInformation->Type = FileTypeMessageModePipe;
  260. } else {
  261. SrvFileInformation->Type = FileTypeByteModePipe;
  262. }
  263. break;
  264. case ShareTypePrint:
  265. SrvFileInformation->Type = FileTypePrinter;
  266. break;
  267. default:
  268. SrvFileInformation->Type = FileTypeUnknown;
  269. }
  270. //
  271. // If the caller wants to know the length of the file's extended
  272. // attributes, obtain them now.
  273. //
  274. if ( QueryEaSize ) {
  275. //
  276. // If the file has no EAs, return an FEA size = 4 (that's what OS/2
  277. // does--it accounts for the size of the cbList field of an
  278. // FEALIST).
  279. //
  280. if ( srvNetworkOpenInformation.EaSize == 0 ) {
  281. SrvFileInformation->EaSize = 4;
  282. } else {
  283. SrvFileInformation->EaSize = srvNetworkOpenInformation.EaSize;
  284. }
  285. }
  286. return STATUS_SUCCESS;
  287. } // SrvQueryInformationFile
  288. NTSTATUS
  289. SrvQueryInformationFileAbbreviated(
  290. IN HANDLE FileHandle,
  291. IN PFILE_OBJECT FileObject OPTIONAL,
  292. OUT PSRV_FILE_INFORMATION_ABBREVIATED SrvFileInformation,
  293. IN BOOLEAN AdditionalInfo,
  294. IN SHARE_TYPE ShareType
  295. )
  296. /*++
  297. Routine Description:
  298. This routine makes calls to NtQueryInformationFile to get information
  299. about a file opened by the server.
  300. Arguments:
  301. FileHandle - a handle of the file to get information about.
  302. FileInformation - pointer to a structure in which to store the
  303. information.
  304. ShareType - The file type. It will be disk, comm, print, pipe
  305. or (-1) for don't care.
  306. QueryEaSize - Try if EA size info is requested.
  307. Return Value:
  308. A status indicating success or failure of the operation.
  309. --*/
  310. {
  311. IO_STATUS_BLOCK ioStatusBlock;
  312. SRV_NETWORK_OPEN_INFORMATION srvNetworkOpenInformation;
  313. NTSTATUS status;
  314. LARGE_INTEGER newTime;
  315. PAGED_CODE( );
  316. //
  317. // Most query operations will fail on comm devices and print shares.
  318. // If this is a disk file, etc. do the queries. If it is a comm
  319. // device, fake it with defaults.
  320. //
  321. if ( ShareType != ShareTypePrint ) {
  322. status = SrvQueryNetworkOpenInformation(
  323. FileHandle,
  324. FileObject,
  325. &srvNetworkOpenInformation,
  326. FALSE
  327. );
  328. if ( !NT_SUCCESS(status) ) {
  329. INTERNAL_ERROR(
  330. ERROR_LEVEL_UNEXPECTED,
  331. "SrvQueryInformationFile: NtQueryInformationFile "
  332. " failed: %X",
  333. status,
  334. NULL
  335. );
  336. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  337. return status;
  338. }
  339. } else {
  340. //
  341. // Use defaults for comm and print shares.
  342. //
  343. RtlZeroMemory( &srvNetworkOpenInformation, sizeof( srvNetworkOpenInformation ) );
  344. }
  345. //
  346. // Set up creation time fields.
  347. //
  348. ExSystemTimeToLocalTime(
  349. &srvNetworkOpenInformation.LastWriteTime,
  350. &newTime
  351. );
  352. //
  353. // Make sure we round up to two seconds.
  354. //
  355. newTime.QuadPart += AlmostTwoSeconds;
  356. if ( !RtlTimeToSecondsSince1970(
  357. &newTime,
  358. &SrvFileInformation->LastWriteTimeInSeconds
  359. ) ) {
  360. SrvFileInformation->LastWriteTimeInSeconds = 0;
  361. } else {
  362. //
  363. // Mask off the low bit so we can be consistent with LastWriteTime.
  364. // (We need to round up to 2 seconds)
  365. //
  366. SrvFileInformation->LastWriteTimeInSeconds &= ~1;
  367. }
  368. //
  369. // Set File Attributes field of structure.
  370. //
  371. SRV_NT_ATTRIBUTES_TO_SMB(
  372. srvNetworkOpenInformation.FileAttributes,
  373. srvNetworkOpenInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
  374. &SrvFileInformation->Attributes
  375. );
  376. SrvFileInformation->DataSize.QuadPart =
  377. srvNetworkOpenInformation.EndOfFile.QuadPart;
  378. //
  379. // Set the file device type.
  380. //
  381. switch( ShareType ) {
  382. case ShareTypeDisk: {
  383. SrvFileInformation->Type = FileTypeDisk;
  384. SrvFileInformation->HandleState = 0;
  385. if( AdditionalInfo ) {
  386. union {
  387. FILE_EA_INFORMATION eaInformation;
  388. FILE_STREAM_INFORMATION streamInformation;
  389. FILE_ATTRIBUTE_TAG_INFORMATION tagInformation;
  390. ULONG buffer[ (sizeof( FILE_STREAM_INFORMATION ) + 14) / sizeof(ULONG) ];
  391. } u;
  392. //
  393. // Find out if this file has EAs
  394. //
  395. status = NtQueryInformationFile(
  396. FileHandle,
  397. &ioStatusBlock,
  398. (PVOID)&u.eaInformation,
  399. sizeof( u.eaInformation ),
  400. FileEaInformation
  401. );
  402. if( !NT_SUCCESS( status ) || u.eaInformation.EaSize == 0 ) {
  403. SrvFileInformation->HandleState |= SMB_FSF_NO_EAS;
  404. }
  405. //
  406. // Find out if this file has substreams.
  407. //
  408. RtlZeroMemory( &u, sizeof(u) );
  409. status = NtQueryInformationFile(
  410. FileHandle,
  411. &ioStatusBlock,
  412. (PVOID)&u.streamInformation,
  413. sizeof( u.streamInformation ),
  414. FileStreamInformation
  415. );
  416. //
  417. // If the filesystem does not support this call, then there are no substreams. Or
  418. // If the filesystem supports the call but returned exactly no name or returned "::$DATA"
  419. // then there are no substreams.
  420. //
  421. if( status == STATUS_INVALID_PARAMETER ||
  422. status == STATUS_NOT_IMPLEMENTED ||
  423. (status == STATUS_SUCCESS &&
  424. (u.streamInformation.StreamNameLength == 0 ||
  425. (u.streamInformation.StreamNameLength == 14 ))
  426. )
  427. ) {
  428. SrvFileInformation->HandleState |= SMB_FSF_NO_SUBSTREAMS;
  429. }
  430. //
  431. // Find out if this file is a reparse point
  432. //
  433. status = NtQueryInformationFile(
  434. FileHandle,
  435. &ioStatusBlock,
  436. (PVOID)&u.tagInformation,
  437. sizeof( u.tagInformation ),
  438. FileAttributeTagInformation
  439. );
  440. if( !NT_SUCCESS( status ) ||
  441. u.tagInformation.ReparseTag == IO_REPARSE_TAG_RESERVED_ZERO ) {
  442. SrvFileInformation->HandleState |= SMB_FSF_NO_REPARSETAG;
  443. }
  444. }
  445. break;
  446. } case ShareTypePipe: {
  447. FILE_PIPE_INFORMATION pipeInformation;
  448. FILE_PIPE_LOCAL_INFORMATION pipeLocalInformation;
  449. USHORT pipeHandleState;
  450. status = NtQueryInformationFile(
  451. FileHandle,
  452. &ioStatusBlock,
  453. (PVOID)&pipeInformation,
  454. sizeof(pipeInformation),
  455. FilePipeInformation
  456. );
  457. if ( !NT_SUCCESS(status) ) {
  458. INTERNAL_ERROR(
  459. ERROR_LEVEL_UNEXPECTED,
  460. "SrvQueryInformationFile: NtQueryInformationFile "
  461. "(pipe information) failed: %X",
  462. status,
  463. NULL
  464. );
  465. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  466. return status;
  467. }
  468. status = NtQueryInformationFile(
  469. FileHandle,
  470. &ioStatusBlock,
  471. (PVOID)&pipeLocalInformation,
  472. sizeof(pipeLocalInformation),
  473. FilePipeLocalInformation
  474. );
  475. if ( !NT_SUCCESS(status) ) {
  476. INTERNAL_ERROR(
  477. ERROR_LEVEL_UNEXPECTED,
  478. "SrvQueryInformationFile: NtQueryInformationFile "
  479. "(pipe local information) failed: %X",
  480. status,
  481. NULL
  482. );
  483. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  484. return status;
  485. }
  486. //
  487. // Fill in the handle state information in SMB format
  488. //
  489. pipeHandleState = (USHORT)pipeInformation.CompletionMode
  490. << PIPE_COMPLETION_MODE_BITS;
  491. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeEnd
  492. << PIPE_PIPE_END_BITS;
  493. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeType
  494. << PIPE_PIPE_TYPE_BITS;
  495. pipeHandleState |= (USHORT)pipeInformation.ReadMode
  496. << PIPE_READ_MODE_BITS;
  497. pipeHandleState |= (USHORT)(pipeLocalInformation.MaximumInstances &
  498. 0xFF)
  499. << PIPE_MAXIMUM_INSTANCES_BITS;
  500. SrvFileInformation->HandleState = pipeHandleState;
  501. if (pipeLocalInformation.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) {
  502. SrvFileInformation->Type = FileTypeMessageModePipe;
  503. } else {
  504. SrvFileInformation->Type = FileTypeByteModePipe;
  505. }
  506. break;
  507. } case ShareTypePrint: {
  508. SrvFileInformation->Type = FileTypePrinter;
  509. break;
  510. } default:
  511. SrvFileInformation->Type = FileTypeUnknown;
  512. }
  513. return STATUS_SUCCESS;
  514. } // SrvQueryInformationFileAbbreviated
  515. NTSTATUS
  516. SrvQueryNtInformationFile (
  517. IN HANDLE FileHandle,
  518. IN PFILE_OBJECT FileObject OPTIONAL,
  519. IN SHARE_TYPE ShareType,
  520. IN BOOLEAN AdditionalInfo,
  521. IN OUT PSRV_NT_FILE_INFORMATION SrvFileInformation
  522. )
  523. /*++
  524. Routine Description:
  525. This routine makes calls to NtQueryInformationFile to get information
  526. about a file opened by the server.
  527. Arguments:
  528. FileHandle - a handle of the file to get information about.
  529. FileInformation - pointer to a structure in which to store the
  530. information.
  531. Return Value:
  532. A status indicating success or failure of the operation.
  533. --*/
  534. {
  535. IO_STATUS_BLOCK ioStatusBlock;
  536. FILE_PIPE_INFORMATION pipeInformation;
  537. FILE_PIPE_LOCAL_INFORMATION pipeLocalInformation;
  538. USHORT pipeHandleState;
  539. NTSTATUS status;
  540. PAGED_CODE( );
  541. status = SrvQueryNetworkOpenInformation( FileHandle,
  542. FileObject,
  543. &SrvFileInformation->NwOpenInfo,
  544. FALSE
  545. );
  546. if ( !NT_SUCCESS(status) ) {
  547. if ( ShareType != ShareTypePipe ) {
  548. INTERNAL_ERROR(
  549. ERROR_LEVEL_UNEXPECTED,
  550. "SrvQueryNtInformationFile: NtQueryInformationFile "
  551. " failed: %X",
  552. status,
  553. NULL
  554. );
  555. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  556. }
  557. return status;
  558. }
  559. if ( ShareType == ShareTypePipe ) {
  560. status = NtQueryInformationFile(
  561. FileHandle,
  562. &ioStatusBlock,
  563. (PVOID)&pipeInformation,
  564. sizeof(pipeInformation),
  565. FilePipeInformation
  566. );
  567. if ( !NT_SUCCESS(status) ) {
  568. INTERNAL_ERROR(
  569. ERROR_LEVEL_UNEXPECTED,
  570. "SrvNtQueryInformationFile: NtQueryInformationFile "
  571. "(pipe information) failed: %X",
  572. status,
  573. NULL
  574. );
  575. return status;
  576. }
  577. status = NtQueryInformationFile(
  578. FileHandle,
  579. &ioStatusBlock,
  580. (PVOID)&pipeLocalInformation,
  581. sizeof(pipeLocalInformation),
  582. FilePipeLocalInformation
  583. );
  584. if ( !NT_SUCCESS(status) ) {
  585. INTERNAL_ERROR(
  586. ERROR_LEVEL_UNEXPECTED,
  587. "SrvNtQueryInformationFile: NtQueryInformationFile "
  588. "(pipe local information) failed: %X",
  589. status,
  590. NULL
  591. );
  592. return status;
  593. }
  594. //
  595. // Fill in the handle state information in SMB format
  596. //
  597. pipeHandleState = (USHORT)pipeInformation.CompletionMode
  598. << PIPE_COMPLETION_MODE_BITS;
  599. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeEnd
  600. << PIPE_PIPE_END_BITS;
  601. pipeHandleState |= (USHORT)pipeLocalInformation.NamedPipeType
  602. << PIPE_PIPE_TYPE_BITS;
  603. pipeHandleState |= (USHORT)pipeInformation.ReadMode
  604. << PIPE_READ_MODE_BITS;
  605. pipeHandleState |= (USHORT)(pipeLocalInformation.MaximumInstances &
  606. 0xFF)
  607. << PIPE_MAXIMUM_INSTANCES_BITS;
  608. SrvFileInformation->HandleState = pipeHandleState;
  609. } else {
  610. SrvFileInformation->HandleState = 0;
  611. if( AdditionalInfo ) {
  612. //
  613. // the buffer is added to the end to ensure that we have enough space on the
  614. // stack to return a FILE_STREAM_INFORMATION buffer having the ::$DATA substream
  615. union {
  616. FILE_EA_INFORMATION eaInformation;
  617. FILE_STREAM_INFORMATION streamInformation;
  618. FILE_ATTRIBUTE_TAG_INFORMATION tagInformation;
  619. ULONG buffer[ (sizeof( FILE_STREAM_INFORMATION ) + 14) / sizeof(ULONG) ];
  620. } u;
  621. //
  622. // Find out if this file has EAs
  623. //
  624. status = NtQueryInformationFile(
  625. FileHandle,
  626. &ioStatusBlock,
  627. (PVOID)&u.eaInformation,
  628. sizeof( u.eaInformation ),
  629. FileEaInformation
  630. );
  631. if( !NT_SUCCESS( status ) || u.eaInformation.EaSize == 0 ) {
  632. SrvFileInformation->HandleState |= SMB_FSF_NO_EAS;
  633. }
  634. //
  635. // Find out if this file has substreams.
  636. //
  637. RtlZeroMemory( &u, sizeof(u) );
  638. status = NtQueryInformationFile(
  639. FileHandle,
  640. &ioStatusBlock,
  641. (PVOID)&u.streamInformation,
  642. sizeof( u ),
  643. FileStreamInformation
  644. );
  645. //
  646. // If the filesystem does not support this call, then there are no substreams. Or
  647. // If the filesystem supports the call but returned exactly no name or returned "::$DATA"
  648. // then there are no substreams.
  649. //
  650. if( status == STATUS_INVALID_PARAMETER ||
  651. status == STATUS_NOT_IMPLEMENTED ||
  652. (status == STATUS_SUCCESS &&
  653. (u.streamInformation.StreamNameLength == 0 ||
  654. (u.streamInformation.StreamNameLength == 14 ))
  655. )
  656. ) {
  657. SrvFileInformation->HandleState |= SMB_FSF_NO_SUBSTREAMS;
  658. }
  659. //
  660. // Find out if this file is a reparse point
  661. //
  662. status = NtQueryInformationFile(
  663. FileHandle,
  664. &ioStatusBlock,
  665. (PVOID)&u.tagInformation,
  666. sizeof( u.tagInformation ),
  667. FileAttributeTagInformation
  668. );
  669. if( !NT_SUCCESS( status ) ||
  670. u.tagInformation.ReparseTag == IO_REPARSE_TAG_RESERVED_ZERO ) {
  671. SrvFileInformation->HandleState |= SMB_FSF_NO_REPARSETAG;
  672. }
  673. }
  674. }
  675. //
  676. // Set the file device type.
  677. //
  678. switch( ShareType ) {
  679. case ShareTypeDisk:
  680. SrvFileInformation->Type = FileTypeDisk;
  681. break;
  682. case ShareTypePipe:
  683. if (pipeLocalInformation.NamedPipeType == FILE_PIPE_MESSAGE_TYPE) {
  684. SrvFileInformation->Type = FileTypeMessageModePipe;
  685. } else {
  686. SrvFileInformation->Type = FileTypeByteModePipe;
  687. }
  688. break;
  689. case ShareTypePrint:
  690. SrvFileInformation->Type = FileTypePrinter;
  691. break;
  692. default:
  693. SrvFileInformation->Type = FileTypeUnknown;
  694. }
  695. return STATUS_SUCCESS;
  696. } // SrvQueryNtInformationFile
  697. NTSTATUS
  698. SrvQueryDirectoryFile (
  699. IN PWORK_CONTEXT WorkContext,
  700. IN BOOLEAN IsFirstCall,
  701. IN BOOLEAN FilterLongNames,
  702. IN BOOLEAN FindWithBackupIntent,
  703. IN FILE_INFORMATION_CLASS FileInformationClass,
  704. IN ULONG SearchStorageType,
  705. IN PUNICODE_STRING FilePathName,
  706. IN PULONG ResumeFileIndex OPTIONAL,
  707. IN USHORT SmbSearchAttributes,
  708. IN PSRV_DIRECTORY_INFORMATION DirectoryInformation,
  709. IN CLONG BufferLength
  710. )
  711. /*++
  712. Routine Description:
  713. This routine acts as a wrapper for NT LanMan server access to
  714. NtQueryDirectoryFile. It allows server routines to obtain information
  715. about the files in a directory using the kind of information
  716. passed in an SMB. This localizes the code for this operation and
  717. simplifies the writing of SMB processing routines that use wildcards.
  718. The calling routine is responsible for setting up a quadword-aligned
  719. buffer in nonpaged pool that may be used by this routine. A pointer
  720. to the buffer and the buffer length are passed in as parameters.
  721. The buffer must be allocated from nonpaged pool because one of
  722. the things it is used for is as a buffer for NtQueryDirectoryFile,
  723. a buffered-IO request. The buffer is also used to hold information
  724. needed by this routine, such as a handle to the directory in which
  725. the search is being performed, a pointer to the
  726. FILE_DIRECTORY_INFORMATION structure that was last returned, and the
  727. basename (with wildcards) that we're using as a search key. Since
  728. all this information must remain valid across calls to this routine,
  729. the calling routine must ensure that the buffer remains intact until
  730. this routine returns an unsuccessful status or STATUS_NO_MORE_FILES,
  731. or SrvCloseQueryDirectory is called.
  732. SMB processing routines which do not need to make use of the Buffer
  733. field of the outgoing SMB may use this as a buffer for this routine,
  734. remembering to leave any pathname information in the buffer field of the
  735. incoming SMB intact by starting the buffer after the pathname. SMB
  736. processing routines that write into the Buffer field of the outgoing SMB,
  737. such as Search and Find, must allocate space for the buffer from nonpaged
  738. pool. The size of the buffer should be approximately 4k. Smaller
  739. buffers will work, but more slowly due to the need for more calls
  740. to NtQueryDirectoryFile. The minimum buffer size is equal to:
  741. sizeof(SRV_DIRECTORY_INFORMATION) +
  742. sizeof(SRV_QUERY_DIRECTORY_INFORMATION) +
  743. MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR) +
  744. sizeof(UNICODE_STRING) +
  745. MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR)
  746. This ensures that NtQueryDirectoryFile will be able to put at least
  747. one entry in the buffer.
  748. On the first call to this routine, it fills up its buffer with
  749. information from NtQueryDirectoryFile and passes back the name of
  750. a single file that conforms to the specified name and search
  751. attributes. On subsequent calls, the names stored in the buffer are
  752. used until there are no more files in the directory or another
  753. call to NtQueryDirectoryFile is needed to again fill the buffer.
  754. Whenever the caller is done with the search, it must call
  755. SrvCloseQueryDirectory. This is required even if this routine
  756. returns an error.
  757. Arguments:
  758. WorkContext - pointer to a work context block for the operation. The
  759. TreeConnect, Session, and RequestHeader fields are used, and the
  760. pointer is passed to the SMB error handling function if necessary.
  761. IsFirstCall - a boolean indicating whether this is the first time
  762. the calling routine is calling this function. If it is, then
  763. the directory for the search is opened and other setup
  764. operations take place.
  765. FilterLongNames - a boolean that is TRUE when non-FAT names should be
  766. filtered out (not returned). If FALSE, return all filenames,
  767. regardless of whether or not they could be FAT 8.3 names.
  768. FindWithBackupIntent - Whether the directory was opened by the use
  769. for backup intent.
  770. FileInformationClass - the type of file structures to return. This
  771. field can be one of FileDirectoryInformation,
  772. FileFullDirectoryInformation, FileOleDirectoryInformation, or
  773. FileBothDirectoryInformation.
  774. FilePathName - a pointer to a string describing the file path name
  775. to do directory searches on. This path is relative to the
  776. PathName specified in the share block. This parameter is only
  777. used on the first call to this routine; subsequent calls ignore it.
  778. ResumeFileIndex - an optional pointer to a file index which determines
  779. the file with which to restart the search. NULL if the search
  780. should be restarted from the last file returned.
  781. SmbSearchAttributes - the atttibutes, in SMB format, that files must
  782. have in order to be found. The search is inclusive, meaning that
  783. if several attributes are specified, files having those attributes
  784. will be found, in addition to normal files.
  785. DirectoryInformation - a pointer to the buffer to be used by this
  786. routine to do its work. This buffer must be quadword-aligned.
  787. BufferLength - the length of the buffer passed to this routine.
  788. Return Value:
  789. A status indicating success or failure of the operation, or
  790. STATUS_NO_MORE_FILES if the files in the directory that match the
  791. specified parameters have been exausted.
  792. --*/
  793. {
  794. NTSTATUS status;
  795. IO_STATUS_BLOCK ioStatusBlock;
  796. PFILE_DIRECTORY_INFORMATION *currentEntry;
  797. ULONG inclusiveSearchAttributes;
  798. ULONG exclusiveSearchAttributes;
  799. ULONG currentAttributes;
  800. BOOLEAN returnDirectories;
  801. BOOLEAN returnDirectoriesOnly;
  802. BOOLEAN calledQueryDirectory = FALSE;
  803. OBJECT_ATTRIBUTES objectAttributes;
  804. PUNICODE_STRING filePathName;
  805. BOOLEAN FreePathName = FALSE;
  806. UNICODE_STRING objectNameString;
  807. UNICODE_STRING baseFileName;
  808. PSHARE fileShare = NULL;
  809. PUNICODE_STRING resumeName = NULL;
  810. BOOLEAN resumeSearch;
  811. CLONG fileNameOffset;
  812. ULONG createOptions;
  813. PAGED_CODE( );
  814. ASSERT( ( FileInformationClass == FileDirectoryInformation ) ||
  815. ( FileInformationClass == FileFullDirectoryInformation ) ||
  816. ( FileInformationClass == FileBothDirectoryInformation ) ||
  817. ( FileInformationClass == FileIdFullDirectoryInformation ) ||
  818. ( FileInformationClass == FileIdBothDirectoryInformation ) );
  819. //
  820. // Set up the offsets to the fields in FILE_FULL_DIR_INFORMATION in
  821. // a different place than corresponding fields in
  822. // FILE_DIRECTORY_INFORMATION. These allow this routine to
  823. // efficiently use either structure.
  824. //
  825. {
  826. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, NextEntryOffset ) ==
  827. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, NextEntryOffset ) );
  828. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileIndex ) ==
  829. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileIndex ) );
  830. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, CreationTime ) ==
  831. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, CreationTime ) );
  832. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastAccessTime ) ==
  833. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, LastAccessTime ) );
  834. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastWriteTime ) ==
  835. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, LastWriteTime ) );
  836. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, ChangeTime ) ==
  837. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, ChangeTime ) );
  838. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, EndOfFile ) ==
  839. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, EndOfFile ) );
  840. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, AllocationSize ) ==
  841. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, AllocationSize ) );
  842. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileAttributes ) ==
  843. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileAttributes ) );
  844. C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileNameLength ) ==
  845. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileNameLength ) );
  846. }
  847. if ( FileInformationClass == FileFullDirectoryInformation ) {
  848. fileNameOffset =
  849. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
  850. } else if ( FileInformationClass == FileBothDirectoryInformation ) {
  851. fileNameOffset =
  852. FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
  853. } else if ( FileInformationClass == FileIdBothDirectoryInformation ) {
  854. fileNameOffset =
  855. FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, FileName[0] );
  856. } else if ( FileInformationClass == FileIdFullDirectoryInformation ) {
  857. fileNameOffset =
  858. FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, FileName[0] );
  859. } else {
  860. fileNameOffset =
  861. FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
  862. }
  863. //
  864. // This macro is used to actually get at the FileName field. Note
  865. // that it depends on a local variable.
  866. //
  867. #define FILE_NAME(a) (PWCH)( (PCHAR)(a) + fileNameOffset )
  868. //
  869. // If this is the first call to this routine, we must open the
  870. // correct directory, thereby obtaining a handle to it to pass to
  871. // NtQueryDirectoryFile. The calling routine stores the handle
  872. // to prevent problems if SrvQueryDirectoryFile is called more
  873. // than once simultaneously.
  874. //
  875. if ( IsFirstCall ) {
  876. BOOLEAN endsInDot;
  877. ULONG attributes;
  878. DirectoryInformation->DirectoryHandle = 0L;
  879. DirectoryInformation->ErrorOnFileOpen = FALSE;
  880. DirectoryInformation->OnlySingleEntries = FALSE;
  881. //
  882. // We must get the appropriate directory name in which to perform the
  883. // search. First, find the basename of the file from the FilePathName.
  884. //
  885. // Find out whether there are wildcards in the file name we are
  886. // searching for. This information will be used later to
  887. // know whether we should try to get more files if the buffer
  888. // is empty--if there were no wildcards and we have emptied the
  889. // buffer, then we know that we have already returned the one and
  890. // only file that could be found, so return STATUS_NO_MORE_FILES.
  891. //
  892. SrvGetBaseFileName( FilePathName, &baseFileName );
  893. DirectoryInformation->Wildcards =
  894. FsRtlDoesNameContainWildCards( &baseFileName );
  895. if ( DirectoryInformation->Wildcards &&
  896. (!IS_NT_DIALECT(WorkContext->Connection->SmbDialect) ) ) {
  897. //
  898. // Bogus code to workaround ~* problem
  899. //
  900. if ( baseFileName.Buffer[(baseFileName.Length>>1)-1] == (WCHAR)'.' ) {
  901. endsInDot = TRUE;
  902. baseFileName.Length -= sizeof( WCHAR );
  903. } else {
  904. endsInDot = FALSE;
  905. }
  906. //
  907. // Convert the file name to the new form expected by the file
  908. // systems. Special case *.* to * since it is so common. Otherwise
  909. // transmogrify the input name according to the following rules:
  910. //
  911. // - Change all ? to DOS_QM
  912. // - Change all . followed by ? or * to DOS_DOT
  913. // - Change all * followed by a . into DOS_STAR
  914. //
  915. // These transmogrifications are all done in place.
  916. //
  917. if ( (baseFileName.Length == 6) &&
  918. (RtlEqualMemory(baseFileName.Buffer, StrStarDotStar, 6) ) ) {
  919. baseFileName.Length = 2;
  920. } else {
  921. ULONG index;
  922. WCHAR *nameChar;
  923. for ( index = 0, nameChar = baseFileName.Buffer;
  924. index < baseFileName.Length/sizeof(WCHAR);
  925. index += 1, nameChar += 1) {
  926. if (index && (*nameChar == L'.') && (*(nameChar - 1) == L'*')) {
  927. *(nameChar - 1) = DOS_STAR;
  928. }
  929. if ((*nameChar == L'?') || (*nameChar == L'*')) {
  930. if (*nameChar == L'?') {
  931. *nameChar = DOS_QM;
  932. }
  933. if (index && *(nameChar-1) == L'.') {
  934. *(nameChar-1) = DOS_DOT;
  935. }
  936. }
  937. }
  938. if ( endsInDot && *(nameChar - 1) == L'*' ) {
  939. *(nameChar-1) = DOS_STAR;
  940. }
  941. }
  942. }
  943. //
  944. // Set up the object attributes structure for SrvIoCreateFile.
  945. //
  946. objectNameString.Buffer = FilePathName->Buffer;
  947. objectNameString.Length = SrvGetSubdirectoryLength( FilePathName );
  948. objectNameString.MaximumLength = objectNameString.Length;
  949. //
  950. // !!! If the object system supported relative opens with name
  951. // length = 0, this wouldn't be necessary. Take it out when
  952. // the object system is done.
  953. //
  954. if ( objectNameString.Length == 0 ) {
  955. //
  956. // Since we are opening the root directory, set the attribute
  957. // to case insensitive since this is how we opened the share
  958. // point when it was added.
  959. //
  960. PSHARE share = WorkContext->TreeConnect->Share;
  961. status = SrvSnapGetNameString( WorkContext, &filePathName, &FreePathName );
  962. if( !NT_SUCCESS(status) )
  963. {
  964. return status;
  965. }
  966. objectNameString = *filePathName;
  967. DirectoryInformation->Wildcards = TRUE;
  968. attributes = OBJ_CASE_INSENSITIVE;
  969. } else {
  970. fileShare = WorkContext->TreeConnect->Share;
  971. attributes =
  972. (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
  973. WorkContext->Session->UsingUppercasePaths) ?
  974. OBJ_CASE_INSENSITIVE : 0L;
  975. }
  976. SrvInitializeObjectAttributes_U(
  977. &objectAttributes,
  978. &objectNameString,
  979. attributes,
  980. NULL,
  981. NULL
  982. );
  983. IF_DEBUG(SEARCH) {
  984. SrvPrint1( "Opening directory name: %wZ\n", &objectNameString );
  985. }
  986. //
  987. // Attempt to open the directory, using the client's security
  988. // profile to check access. (We call SrvIoCreateFile, rather than
  989. // NtOpenFile, in order to get user-mode access checking.)
  990. //
  991. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
  992. INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
  993. //
  994. // There's no need to specify FILE_DIRECTORY_FILE; the file systems
  995. // will open whatever is there and reject later QueryDirectoryFile
  996. // when the object opened does not support enumeration.
  997. //
  998. createOptions = 0;
  999. if (FindWithBackupIntent) {
  1000. createOptions = FILE_OPEN_FOR_BACKUP_INTENT;
  1001. }
  1002. status = SrvIoCreateFile(
  1003. WorkContext,
  1004. &DirectoryInformation->DirectoryHandle,
  1005. FILE_LIST_DIRECTORY, // DesiredAccess
  1006. &objectAttributes,
  1007. &ioStatusBlock,
  1008. NULL, // AllocationSize
  1009. 0, // FileAttributes
  1010. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  1011. FILE_OPEN, // Disposition
  1012. createOptions, // CreateOptions
  1013. NULL, // EaBuffer
  1014. 0, // EaLength
  1015. CreateFileTypeNone, // File type
  1016. NULL, // ExtraCreateParameters
  1017. IO_FORCE_ACCESS_CHECK, // Options
  1018. fileShare
  1019. );
  1020. //
  1021. // If the user didn't have this permission, update the statistics
  1022. // database.
  1023. //
  1024. if ( status == STATUS_ACCESS_DENIED ) {
  1025. SrvStatistics.AccessPermissionErrors++;
  1026. }
  1027. // Free the path since we won't use it anymore
  1028. if( FreePathName )
  1029. {
  1030. FREE_HEAP( filePathName );
  1031. filePathName = NULL;
  1032. }
  1033. if ( !NT_SUCCESS(status) ) {
  1034. IF_DEBUG(ERRORS) {
  1035. SrvPrint2( "SrvQueryDirectoryFile: SrvIoCreateFile for dir %wZ "
  1036. "failed: %X\n",
  1037. &objectNameString, status );
  1038. }
  1039. DirectoryInformation->DirectoryHandle = NULL;
  1040. return status;
  1041. }
  1042. SRVDBG_CLAIM_HANDLE( DirectoryInformation->DirectoryHandle, "DID", 3, DirectoryInformation );
  1043. SrvStatistics.TotalFilesOpened++;
  1044. IF_DEBUG(SEARCH) {
  1045. SrvPrint1( "SrvIoCreateFile succeeded, handle = %p\n",
  1046. DirectoryInformation->DirectoryHandle );
  1047. }
  1048. //
  1049. // Set up the currentEntry pointer. This is a pointer to the
  1050. // location where the FILE_DIRECTORY_INFORMATION pointer is
  1051. // stored. It is not really necessary--
  1052. // DirectoryInformation->CurrentEntry could be substituted for
  1053. // every occurrance of *currentEntry. Using currentEntry makes
  1054. // the code more compact and simpler.
  1055. //
  1056. currentEntry = &(DirectoryInformation->CurrentEntry);
  1057. *currentEntry = NULL;
  1058. //
  1059. // Store the length of buffer space remaining--this is where IO
  1060. // request information will be stored.
  1061. //
  1062. DirectoryInformation->BufferLength = BufferLength -
  1063. sizeof(SRV_DIRECTORY_INFORMATION);
  1064. IF_DEBUG(SEARCH) {
  1065. SrvPrint3( "In BufferLength: %ld, sizeof(): %ld, ->BufferLength: "
  1066. "%ld\n", BufferLength,
  1067. sizeof(SRV_DIRECTORY_INFORMATION),
  1068. DirectoryInformation->BufferLength );
  1069. }
  1070. } else {
  1071. //
  1072. // This is not the first call to this routine, so just set up
  1073. // the currentEntry pointer and have it point to the next entry
  1074. // in the buffer. If there are no more entries in the buffer at
  1075. // this time (NextEntryOffset == 0), set the currentEntry
  1076. // pointer to NULL so that we will know to get more later.
  1077. //
  1078. currentEntry = &DirectoryInformation->CurrentEntry;
  1079. if ( *currentEntry != NULL ) {
  1080. if ( (*currentEntry)->NextEntryOffset == 0 ) {
  1081. *currentEntry = NULL;
  1082. } else {
  1083. *currentEntry = (PFILE_DIRECTORY_INFORMATION)
  1084. ( (PCHAR)*currentEntry + (*currentEntry)->NextEntryOffset );
  1085. }
  1086. }
  1087. }
  1088. //
  1089. // The lower byte of SmbSearchAttributes defines "inclusive"
  1090. // search attribute bits, meaning that if the bit is on on the
  1091. // file but not set in the request, the file should be skipped.
  1092. // For example, if HIDDEN is not specified in the request, then
  1093. // files with the HIDDEN bit turned on are not returned.
  1094. //
  1095. // The upper byte of SmbSearchAttributes, as an LM2.1 extension,
  1096. // defines "exclusive" search attributes, which means that a
  1097. // file must have the specified bits set in order to be returned.
  1098. // For example, if the READONLY bit is set in the request, only
  1099. // files with the READONLY bit turned on will be returned to the
  1100. // client.
  1101. //
  1102. // Convert the inclusive and exclusive search bits to NT format.
  1103. //
  1104. SRV_SMB_ATTRIBUTES_TO_NT(
  1105. (USHORT)(SmbSearchAttributes & 0xFF),
  1106. &returnDirectories,
  1107. &inclusiveSearchAttributes
  1108. );
  1109. SRV_SMB_ATTRIBUTES_TO_NT(
  1110. (USHORT)(SmbSearchAttributes >> 8),
  1111. &returnDirectoriesOnly,
  1112. &exclusiveSearchAttributes
  1113. );
  1114. //
  1115. // For the inclusive bits, files with the NORMAL, ARCHIVE, or READONLY
  1116. // bits set should be returned regardless of whether these bits
  1117. // were set in SmbSearchAttributes.
  1118. //
  1119. inclusiveSearchAttributes |= FILE_ATTRIBUTE_NORMAL |
  1120. FILE_ATTRIBUTE_ARCHIVE |
  1121. FILE_ATTRIBUTE_READONLY;
  1122. //
  1123. // For exclusive bits, the VOLUME bit is meaningless. It is also not
  1124. // necessary for a file to have the NORMAL bit on, since the NORMAL
  1125. // bit is not defined for the SMB protocol.
  1126. //
  1127. exclusiveSearchAttributes &=
  1128. ~(SMB_FILE_ATTRIBUTE_VOLUME | FILE_ATTRIBUTE_NORMAL);
  1129. //
  1130. // If a resume file index was passed in, this search is a resumption
  1131. // from that file and the name specified in FilePathName.
  1132. //
  1133. if ( ARGUMENT_PRESENT( ResumeFileIndex ) ) {
  1134. resumeSearch = TRUE;
  1135. resumeName = FilePathName;
  1136. IF_DEBUG(SEARCH) {
  1137. SrvPrint3( "Resuming search at file %wZ, length %ld, index %lx\n",
  1138. resumeName, resumeName->Length,
  1139. *ResumeFileIndex );
  1140. }
  1141. } else {
  1142. resumeSearch = FALSE;
  1143. }
  1144. //
  1145. // Now we need to find a file to return. We keep going until we find
  1146. // a file that meets all of our criteria, pointing to the next file
  1147. // if a file fails. We continue the loop under the following conditions:
  1148. //
  1149. // 1) If *currentEntry == NULL, then we haven't yet filled our buffer
  1150. // with entries, so get some entries.
  1151. //
  1152. // 2) If there are bits set in the FileAttributes field of the
  1153. // FILE_DIRECTORY_INFORMATION field that are not set in the
  1154. // searchAttributes variable, then the file does not meet the
  1155. // search requirements, and we need to continue looking.
  1156. //
  1157. // 3) If we are not searching for directories and the file is actually
  1158. // a directory, skip over it.
  1159. //
  1160. // 4) If we are filtering long (non-FAT) filenames AND this file name
  1161. // is not a legal FAT name AND we have no short name for this file,
  1162. // skip it.
  1163. //
  1164. // 5) If the file doesn't have attribute bits specified as exclusive
  1165. // bits, skip it.
  1166. //
  1167. // 6) If the file is not a directory and we're only supposed to return
  1168. // directories, skip it.
  1169. //
  1170. // When this loop is complete, *currentEntry will point to the
  1171. // FILE_DIRECTORY_INFORMATION structure corresponding to the file we
  1172. // will return. If no qualifying files are found, return
  1173. // STATUS_NO_MORE_FILES and close the directory.
  1174. //
  1175. if( *currentEntry != NULL ) {
  1176. SRV_NT_ATTRIBUTES_TO_SMB( (*currentEntry)->FileAttributes,0,&currentAttributes);
  1177. }
  1178. while ( ( *currentEntry == NULL ) // 1
  1179. ||
  1180. ( (currentAttributes | inclusiveSearchAttributes) != // 2
  1181. inclusiveSearchAttributes )
  1182. ||
  1183. ( !returnDirectories && // 3
  1184. (currentAttributes & FILE_ATTRIBUTE_DIRECTORY))
  1185. ||
  1186. // 4
  1187. ( FilterLongNames &&
  1188. !SrvIsLegalFatName( FILE_NAME( *currentEntry ),
  1189. (*currentEntry)->FileNameLength) &&
  1190. !( FileInformationClass == FileBothDirectoryInformation &&
  1191. ((PFILE_BOTH_DIR_INFORMATION)*currentEntry)->
  1192. ShortNameLength != 0) )
  1193. ||
  1194. // 5
  1195. ( (currentAttributes | exclusiveSearchAttributes) !=
  1196. currentAttributes )
  1197. ||
  1198. ( returnDirectoriesOnly && // 6
  1199. !(currentAttributes & FILE_ATTRIBUTE_DIRECTORY) )
  1200. ) {
  1201. IF_DEBUG(SEARCH) {
  1202. if ( *currentEntry != NULL) {
  1203. UNICODE_STRING name;
  1204. name.Length = (SHORT)(*currentEntry)->FileNameLength;
  1205. name.Buffer = FILE_NAME( *currentEntry );
  1206. SrvPrint4( "Skipped %wZ, FileAttr: %lx, ISA: %lx ESA: %lx ",
  1207. &name, (*currentEntry)->FileAttributes,
  1208. inclusiveSearchAttributes,
  1209. exclusiveSearchAttributes );
  1210. SrvPrint4( "NL=%ld D=%ld RD=%ld RDO=%ld ",
  1211. (*currentEntry)->FileNameLength,
  1212. (((*currentEntry)->FileAttributes &
  1213. FILE_ATTRIBUTE_DIRECTORY) != 0), returnDirectories,
  1214. returnDirectoriesOnly );
  1215. SrvPrint1( "FLN=%ld\n", FilterLongNames );
  1216. }
  1217. }
  1218. //
  1219. // We need to look for more files under the following conditions:
  1220. //
  1221. // o we have yet to fill the buffer with entries;
  1222. //
  1223. // o the NextEntryOffset is zero, indicating that the files in
  1224. // the buffer have been exausted.
  1225. //
  1226. if ( *currentEntry == NULL ||
  1227. (*currentEntry)->NextEntryOffset == 0 ) {
  1228. PUNICODE_STRING actualString;
  1229. BOOLEAN bruteForceRewind = FALSE;
  1230. //
  1231. // The buffer has no more valid entries in it. If no
  1232. // wildcards were specified in the file name to search on,
  1233. // then we have already returned the single file and we
  1234. // should just stop now. Otherwise, we go get more entries.
  1235. //
  1236. if ( !DirectoryInformation->Wildcards &&
  1237. ( !IsFirstCall || calledQueryDirectory ) ) {
  1238. if ( calledQueryDirectory ) {
  1239. return STATUS_NO_SUCH_FILE;
  1240. } else {
  1241. return STATUS_NO_MORE_FILES;
  1242. }
  1243. }
  1244. //
  1245. // Set up the file name that will be passed to
  1246. // SrvIssueQueryDirectoryRequest. If this is the first
  1247. // call, then pass the file spec given by the user. If this
  1248. // is a resume search and we haven't yet done a directory
  1249. // query, then use the resume file name and index.
  1250. // Otherwise, pass NULL for these and the file system will
  1251. // continue from where it left off after the last directory
  1252. // query.
  1253. //
  1254. if ( IsFirstCall &&
  1255. !calledQueryDirectory &&
  1256. baseFileName.Length != 0 ) {
  1257. actualString = &baseFileName;
  1258. } else if ( resumeSearch && !calledQueryDirectory ) {
  1259. actualString = resumeName;
  1260. } else {
  1261. actualString = NULL;
  1262. ResumeFileIndex = NULL;
  1263. }
  1264. IF_DEBUG(SEARCH) {
  1265. if ( actualString == NULL ) {
  1266. SrvPrint0( "**** CALLING NTQUERYDIRECTORYFILE, file = NULL, length: 0\n" );
  1267. } else {
  1268. SrvPrint2( "**** CALLING NTQUERYDIRECTORYFILE, file = %wZ, length: %ld\n",
  1269. actualString, actualString->Length );
  1270. }
  1271. SrvPrint0( "Reason: \n" );
  1272. if ( *currentEntry == NULL ) {
  1273. SrvPrint0( "*currentEntry == NULL\n" );
  1274. } else {
  1275. SrvPrint1( "(*currentEntry)->NextEntryOffset == %ld\n",
  1276. (*currentEntry)->NextEntryOffset );
  1277. }
  1278. }
  1279. //
  1280. // Do the directory query operation using a directly-built
  1281. // IRP. Doing this rather than calling NtQueryDirectoryFile
  1282. // eliminates a buffered I/O copy of the directory
  1283. // information and allows use of a kernel event object. If
  1284. // this is the first call to NtQueryDirectoryFile, pass it
  1285. // the search file name. If this is a rewind or resume of a
  1286. // prior search, pass the resume file name and index.
  1287. //
  1288. // The query is performed synchronously, which may be a
  1289. // detriment to performance. However, it may be the case
  1290. // that routines calling SrvQueryDirectoryFile want to
  1291. // exploit the asynchronous capabilities of the IO system,
  1292. // so keeping this routine synchronous significantly
  1293. // simplifies their job.
  1294. //
  1295. status = SrvIssueQueryDirectoryRequest(
  1296. DirectoryInformation->DirectoryHandle,
  1297. (PCHAR)DirectoryInformation->Buffer,
  1298. DirectoryInformation->BufferLength,
  1299. FileInformationClass,
  1300. actualString,
  1301. ResumeFileIndex,
  1302. FALSE,
  1303. DirectoryInformation->OnlySingleEntries
  1304. );
  1305. calledQueryDirectory = TRUE;
  1306. //
  1307. // If the file system cannot support the rewind request,
  1308. // do a brute force rewind (restart search at beginning
  1309. // of directory).
  1310. //
  1311. // This check is before the check for STATUS_NO_MORE_FILES
  1312. // in case there are no files after the resume file.
  1313. //
  1314. if ( status == STATUS_NOT_IMPLEMENTED ) {
  1315. IF_DEBUG(SEARCH) {
  1316. SrvPrint0( "Doing brute force rewind!!\n" );
  1317. }
  1318. bruteForceRewind = TRUE;
  1319. DirectoryInformation->OnlySingleEntries = TRUE;
  1320. status = BruteForceRewind(
  1321. DirectoryInformation->DirectoryHandle,
  1322. (PCHAR)DirectoryInformation->Buffer,
  1323. DirectoryInformation->BufferLength,
  1324. actualString,
  1325. FileInformationClass,
  1326. currentEntry
  1327. );
  1328. //
  1329. // If BruteForceRewind fails with STATUS_NOT_IMPLEMENTED, it
  1330. // means that the client requested a rewind from a
  1331. // non-existant file. The only time this happens in when
  1332. // an OS/2 is deleting many files in a directory. To cope
  1333. // simple rewind the search to the beginning of the
  1334. // directory.
  1335. //
  1336. if ( status == STATUS_NOT_IMPLEMENTED ) {
  1337. bruteForceRewind = FALSE;
  1338. DirectoryInformation->OnlySingleEntries = FALSE;
  1339. status = SrvIssueQueryDirectoryRequest(
  1340. DirectoryInformation->DirectoryHandle,
  1341. (PCHAR)DirectoryInformation->Buffer,
  1342. DirectoryInformation->BufferLength,
  1343. FileInformationClass,
  1344. actualString,
  1345. ResumeFileIndex,
  1346. TRUE,
  1347. FALSE
  1348. );
  1349. }
  1350. }
  1351. //
  1352. // If there are no more files to be gotten, then stop.
  1353. //
  1354. if ( status == STATUS_NO_MORE_FILES ) {
  1355. IF_DEBUG(SEARCH) {
  1356. SrvPrint0( "SrvQueryDirectoryFile: No more files.\n" );
  1357. }
  1358. return status;
  1359. }
  1360. if ( !NT_SUCCESS(status) ) {
  1361. IF_DEBUG(SEARCH) {
  1362. SrvPrint1( "SrvQueryDirectoryFile: NtQueryDirectoryFile "
  1363. "failed: %X.\n", status );
  1364. }
  1365. return status;
  1366. }
  1367. IF_DEBUG(SEARCH) {
  1368. SrvPrint1( "NtQueryDirectoryFile succeeded: %X\n", status );
  1369. }
  1370. //
  1371. // If there wasn't a brute force rewind, which would have
  1372. // set up the CurrentEntry pointer, Set up CurrentEntry
  1373. // pointer to point to the first entry in the buffer.
  1374. //
  1375. if ( !bruteForceRewind ) {
  1376. *currentEntry =
  1377. (PFILE_DIRECTORY_INFORMATION)DirectoryInformation->Buffer;
  1378. } else {
  1379. bruteForceRewind = FALSE;
  1380. }
  1381. IF_DEBUG(SEARCH) {
  1382. UNICODE_STRING name;
  1383. name.Length = (SHORT)(*currentEntry)->FileNameLength;
  1384. name.Buffer = FILE_NAME( *currentEntry );
  1385. SrvPrint2( "First file name is %wZ, length = %ld\n",
  1386. &name, (*currentEntry)->FileNameLength );
  1387. }
  1388. } else {
  1389. //
  1390. // The file described by the FILE_DIRECTORY_INFORMATION pointed
  1391. // to by *currentEntry does not meet our requirements, so
  1392. // point to the next file in the buffer.
  1393. //
  1394. *currentEntry = (PFILE_DIRECTORY_INFORMATION)( (PCHAR)*currentEntry
  1395. + (*currentEntry)->NextEntryOffset );
  1396. }
  1397. if( *currentEntry != NULL ) {
  1398. SRV_NT_ATTRIBUTES_TO_SMB( (*currentEntry)->FileAttributes,0,&currentAttributes);
  1399. }
  1400. }
  1401. return STATUS_SUCCESS;
  1402. } // SrvQueryDirectoryFile
  1403. STATIC
  1404. NTSTATUS
  1405. BruteForceRewind(
  1406. IN HANDLE DirectoryHandle,
  1407. IN PVOID Buffer,
  1408. IN ULONG BufferLength,
  1409. IN PUNICODE_STRING FileName,
  1410. IN FILE_INFORMATION_CLASS FileInformationClass,
  1411. IN OUT PFILE_DIRECTORY_INFORMATION *CurrentEntry
  1412. )
  1413. /*++
  1414. Routine Description:
  1415. This routine manually does a rewind rather than use the file system
  1416. to do it. It gets starts at the first entry in the directory
  1417. specified by DirectoryHandle and continues until it reaches the end
  1418. of the directory or a match. If a file is deleted between the
  1419. original search and the rewind, then this mechanism will fail.
  1420. This routine is intended to work in conjunction with
  1421. SrvQueryDirectoryFile.
  1422. Arguments:
  1423. DirectoryHandle - handle of directory to search.
  1424. Buffer - Space to hold results.
  1425. BufferLength - length of Buffer.
  1426. FileName - the rewind file name. The file *after* this one is returned.
  1427. FileInformationClass - one of FileDirectoryInformation,
  1428. FileBothDirInformation, or FileFullDirectoryInformation.
  1429. (The latter of the four if EA sizes are being requested.)
  1430. CurrentEntry - a pointer to receive a pointer to the file after
  1431. FileName in the directory.
  1432. Return Value:
  1433. NTSTATUS - result of operation.
  1434. --*/
  1435. {
  1436. NTSTATUS status;
  1437. UNICODE_STRING checkFileName;
  1438. BOOLEAN matchFound = FALSE;
  1439. BOOLEAN restartScan = TRUE;
  1440. ULONG fileNameOffset;
  1441. PAGED_CODE( );
  1442. checkFileName.Length = 0;
  1443. *CurrentEntry = NULL;
  1444. if ( FileInformationClass == FileFullDirectoryInformation ) {
  1445. fileNameOffset =
  1446. FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
  1447. } else if ( FileInformationClass == FileBothDirectoryInformation ) {
  1448. fileNameOffset =
  1449. FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
  1450. } else {
  1451. fileNameOffset =
  1452. FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
  1453. }
  1454. while ( TRUE ) {
  1455. if ( *CurrentEntry == NULL ) {
  1456. //
  1457. // Restart the directory search and get a buffer of files.
  1458. //
  1459. status = SrvIssueQueryDirectoryRequest(
  1460. DirectoryHandle,
  1461. Buffer,
  1462. BufferLength,
  1463. FileInformationClass,
  1464. NULL,
  1465. NULL,
  1466. restartScan,
  1467. TRUE
  1468. );
  1469. restartScan = FALSE;
  1470. if ( status == STATUS_NO_MORE_FILES ) {
  1471. if ( matchFound ) {
  1472. //
  1473. // The file matched the last one in the directory;
  1474. // there is no following file. Return
  1475. // STATUS_NO_MORE_FILES.
  1476. //
  1477. return status;
  1478. } else {
  1479. //
  1480. // The file was deleted between when the original search
  1481. // was done and this rewind. Return an error.
  1482. //
  1483. return STATUS_NOT_IMPLEMENTED;
  1484. }
  1485. }
  1486. if ( !NT_SUCCESS(status) ) {
  1487. return status;
  1488. }
  1489. //
  1490. // Set up the current entry pointer.
  1491. //
  1492. *CurrentEntry = Buffer;
  1493. }
  1494. //
  1495. // If the last file we looked at was the correct resume file,
  1496. // then we want to return this file.
  1497. //
  1498. if ( matchFound ) {
  1499. return STATUS_SUCCESS;
  1500. }
  1501. //
  1502. // Check to see if this file is the resume file.
  1503. //
  1504. checkFileName.Length = (SHORT)(*CurrentEntry)->FileNameLength;
  1505. checkFileName.Buffer = FILE_NAME( *CurrentEntry );
  1506. checkFileName.MaximumLength = checkFileName.Length;
  1507. if ( RtlCompareUnicodeString(
  1508. FileName,
  1509. &checkFileName,
  1510. TRUE
  1511. ) == 0 ) {
  1512. matchFound = TRUE;
  1513. } else if ( FileInformationClass == FileBothDirectoryInformation ) {
  1514. //
  1515. // Compare the short name.
  1516. //
  1517. checkFileName.Length = (SHORT)
  1518. ((PFILE_BOTH_DIR_INFORMATION)*CurrentEntry)->ShortNameLength;
  1519. checkFileName.Buffer =
  1520. ((PFILE_BOTH_DIR_INFORMATION)*CurrentEntry)->ShortName;
  1521. checkFileName.MaximumLength = checkFileName.Length;
  1522. if ( RtlCompareUnicodeString(
  1523. FileName,
  1524. &checkFileName,
  1525. TRUE
  1526. ) == 0 ) {
  1527. matchFound = TRUE;
  1528. }
  1529. }
  1530. IF_DEBUG(SEARCH) {
  1531. if ( matchFound ) {
  1532. SrvPrint2( "Matched: %wZ and %wZ\n", FileName, &checkFileName );
  1533. } else {
  1534. SrvPrint2( "No match: %wZ and %wZ\n", FileName, &checkFileName );
  1535. }
  1536. }
  1537. //
  1538. // Set up the current entry pointer for the next iteration.
  1539. //
  1540. if ( (*CurrentEntry)->NextEntryOffset == 0 ) {
  1541. *CurrentEntry = NULL;
  1542. } else {
  1543. *CurrentEntry =
  1544. (PFILE_DIRECTORY_INFORMATION)( (PCHAR)(*CurrentEntry) +
  1545. (*CurrentEntry)->NextEntryOffset );
  1546. }
  1547. }
  1548. } // BruteForceRewind
  1549. NTSTATUS
  1550. SrvQueryEaFile (
  1551. IN BOOLEAN IsFirstCall,
  1552. IN HANDLE FileHandle,
  1553. IN PFILE_GET_EA_INFORMATION EaList OPTIONAL,
  1554. IN ULONG EaListLength,
  1555. IN PSRV_EA_INFORMATION EaInformation,
  1556. IN CLONG BufferLength,
  1557. OUT PULONG EaErrorOffset
  1558. )
  1559. /*++
  1560. Routine Description:
  1561. This routine acts as a wrapper for NT LanMan server access to
  1562. NtQueryEaFile. It has basically the same interface as
  1563. SrvQueryDirectoryFile, allowing a routine to be written to deal
  1564. with a single EA at a time while also maintaining performance
  1565. by requesting a large number of EAs from the IO system at a
  1566. time.
  1567. The calling routine is responsible for setting up a quadword-aligned
  1568. buffer in nonpaged pool that may be used by this routine. A pointer
  1569. to the buffer and the buffer length are passed in as parameters.
  1570. The buffer must be allocated from nonpaged pool because one of
  1571. the things it is used for is as a buffer for NtQueryEaFile,
  1572. a buffered-IO request. The buffer is also used to hold information
  1573. needed by this routine, such as a pointer to the FILE_EA_INFORMATION
  1574. structure that was last returned. Since all this information must
  1575. remain valid across calls to this routine, the calling routine
  1576. must ensure that the buffer remains intact until this routine
  1577. returns an unsuccessful status or STATUS_NO_MORE_EAS.
  1578. Routines that make use of this routine should set up a buffer
  1579. large enough to hold at least a single EA. Since this can be
  1580. over 64k, it is a good idea to call NtQueryInformationFile to
  1581. get the EA size, then allocate a buffer of this size, unless
  1582. it is greater than the maximum size of an EA. In this case,
  1583. the maximum size of an EA should be allocated as the buffer.
  1584. On the first call to this routine, it fills up its buffer with
  1585. information from NtQueryEaFile and passes back a single EA. On
  1586. subsequent calls, the names stored in the buffer are used until
  1587. there are no more files in the directory or another call to
  1588. NtQueryEaFile is needed to again fill the buffer.
  1589. Arguments:
  1590. IsFirstCall - a boolean indicating whether this is the first time
  1591. the calling routine is calling this function. If it is, then
  1592. setup operations take place.
  1593. FileHandle - a handle to a file open with FILE_READ_EA.
  1594. EaList - an optional pointer to an NT-style get EA list. Only those
  1595. EAs listed in this structure are returned.
  1596. EaListLength - length in bytes of ths get EA list.
  1597. EaInformation - a pointer to the buffer to be used by this routine
  1598. to do its work. This buffer must be quadword-aligned.
  1599. BufferLength - the length of the buffer passed to this routine.
  1600. EaErrorOffset - the offset into EaList of an invalid EA, if any.
  1601. Return Value:
  1602. A status indicating success or failure of the operation, or
  1603. STATUS_NO_MORE_EAS if all the EAs have been returned.
  1604. --*/
  1605. {
  1606. NTSTATUS status;
  1607. PFILE_GET_EA_INFORMATION useEaList = NULL;
  1608. PFILE_FULL_EA_INFORMATION *currentEntry;
  1609. PAGED_CODE( );
  1610. //
  1611. // If this is the first call, do the necessary setup.
  1612. //
  1613. if ( IsFirstCall ) {
  1614. //
  1615. // Set up the currentEntry pointer. This is a pointer to the
  1616. // location where the FILE_EA_INFORMATION pointer is stored.
  1617. // It is not really necessary--EaInformation->CurrentEntry
  1618. // could be substituted for every occurrance of *currentEntry.
  1619. // Using currentEntry makes the code more compact and simpler.
  1620. //
  1621. currentEntry = &(EaInformation->CurrentEntry);
  1622. *currentEntry = NULL;
  1623. //
  1624. // Store the length of buffer space remaining--this is where IO
  1625. // request information will be stored.
  1626. //
  1627. EaInformation->BufferLength = BufferLength - sizeof(SRV_EA_INFORMATION);
  1628. EaInformation->GetEaListOffset = 0;
  1629. IF_DEBUG(SEARCH) {
  1630. SrvPrint3( "In BufferLength: %ld, sizeof(): %ld, ->BufferLength: "
  1631. "%ld\n", BufferLength, sizeof(SRV_EA_INFORMATION),
  1632. EaInformation->BufferLength );
  1633. }
  1634. } else {
  1635. //
  1636. // This is not the first call to this routine, so just set up
  1637. // the currentEntry pointer and have it point to the next entry
  1638. // in the buffer. If there are no more entries in the buffer at
  1639. // this time (NextEntryOffset == 0), set the currentEntry
  1640. // pointer to NULL so that we will know to get more later.
  1641. //
  1642. currentEntry = &EaInformation->CurrentEntry;
  1643. if ( *currentEntry != NULL ) {
  1644. if ( (*currentEntry)->NextEntryOffset == 0 ) {
  1645. *currentEntry = NULL;
  1646. } else {
  1647. *currentEntry = (PFILE_FULL_EA_INFORMATION)
  1648. ( (PCHAR)*currentEntry + (*currentEntry)->NextEntryOffset );
  1649. }
  1650. }
  1651. }
  1652. //
  1653. // If the buffer has no valid entries in it, get some.
  1654. //
  1655. if ( *currentEntry == NULL ) {
  1656. //
  1657. // If all the EAs in a get EA list were returned last time,
  1658. // return now.
  1659. //
  1660. if ( ARGUMENT_PRESENT(EaList) &&
  1661. EaInformation->GetEaListOffset == 0xFFFFFFFF ) {
  1662. return STATUS_NO_MORE_EAS;
  1663. }
  1664. //
  1665. // The buffer has no more valid entries in it, so get more.
  1666. //
  1667. IF_DEBUG(SEARCH) SrvPrint0( "**** CALLING NTQUERYEAFILE\n" );
  1668. //
  1669. // Set up the proper get EA list if one was specified on input.
  1670. //
  1671. if ( ARGUMENT_PRESENT(EaList) ) {
  1672. useEaList = (PFILE_GET_EA_INFORMATION)( (PCHAR)EaList +
  1673. EaInformation->GetEaListOffset );
  1674. EaListLength -= EaInformation->GetEaListOffset;
  1675. }
  1676. //
  1677. // Do the EA query operation using a directly-build IRP. Doing
  1678. // this rather than calling NtQueryEaFile eliminates a buffered I/O
  1679. // copy of the EAs and allows use of a kernel event object.
  1680. //
  1681. // The query is performed synchronously, which may be a
  1682. // detriment to performance. However, it may be the case that
  1683. // routines calling SrvQueryEaFile want to exploit the
  1684. // asynchronous capabilities of the IO system, so keeping this
  1685. // routine synchronous significantly simplifies their job.
  1686. //
  1687. status = SrvIssueQueryEaRequest(
  1688. FileHandle,
  1689. (PCHAR)EaInformation->Buffer,
  1690. EaInformation->BufferLength,
  1691. useEaList,
  1692. EaListLength,
  1693. IsFirstCall,
  1694. EaErrorOffset
  1695. );
  1696. //
  1697. // If there are no more EAs to be gotten, then stop.
  1698. //
  1699. if ( status == STATUS_NO_MORE_EAS ||
  1700. status == STATUS_NONEXISTENT_EA_ENTRY ||
  1701. status == STATUS_NO_EAS_ON_FILE ) {
  1702. IF_DEBUG(SEARCH) {
  1703. SrvPrint0( "SrvQueryEaFile: No more EAs (or file has no EAs).\n" );
  1704. }
  1705. return STATUS_NO_MORE_EAS;
  1706. }
  1707. if ( !NT_SUCCESS(status) ) {
  1708. return status;
  1709. }
  1710. IF_DEBUG(SEARCH) {
  1711. SrvPrint1( "NtQueryEaFile succeeded: %X\n", status );
  1712. }
  1713. //
  1714. // Set up the offset into the get EA list by counting how many
  1715. // full EAs were returned, then walking that far into the get
  1716. // EA list.
  1717. //
  1718. // If all the requested EAs were returned, set the offset to
  1719. // 0xFFFFFFFF so that we know to return STATUS_NO_MORE_EAS.
  1720. //
  1721. if ( ARGUMENT_PRESENT(EaList) ) {
  1722. CLONG numberOfGetEas;
  1723. CLONG numberOfFullEas;
  1724. numberOfGetEas = SrvGetNumberOfEasInList( useEaList );
  1725. numberOfFullEas = SrvGetNumberOfEasInList( EaInformation->Buffer );
  1726. ASSERT( numberOfGetEas >= numberOfFullEas );
  1727. if ( numberOfGetEas == numberOfFullEas ) {
  1728. EaInformation->GetEaListOffset = 0xFFFFFFFF;
  1729. } else {
  1730. CLONG i;
  1731. //
  1732. // Walk the get EA list until we have passed the number
  1733. // of EAs that were returned. This assumes that we got
  1734. // back at least one EA--if not even one EA would fit in
  1735. // the buffer, SrvIssueQueryEaRequest should have
  1736. // returned STATUS_BUFFER_OVERFLOW.
  1737. //
  1738. for ( i = 0; i < numberOfFullEas; i++ ) {
  1739. useEaList = (PFILE_GET_EA_INFORMATION)(
  1740. (PCHAR)useEaList +
  1741. useEaList->NextEntryOffset );
  1742. }
  1743. EaInformation->GetEaListOffset = (ULONG)((PCHAR)useEaList -
  1744. (PCHAR)EaList);
  1745. }
  1746. }
  1747. //
  1748. // Set up CurrentEntry pointer to point to the first entry in the
  1749. // buffer.
  1750. //
  1751. *currentEntry = (PFILE_FULL_EA_INFORMATION)EaInformation->Buffer;
  1752. IF_DEBUG(SEARCH) {
  1753. ANSI_STRING name;
  1754. name.Length = (*currentEntry)->EaNameLength;
  1755. name.Buffer = (*currentEntry)->EaName;
  1756. SrvPrint2( "First EA name is %z, length = %ld\n",
  1757. (PCSTRING)&name, (*currentEntry)->EaNameLength );
  1758. }
  1759. }
  1760. return STATUS_SUCCESS;
  1761. } // SrvQueryEaFile
  1762. VOID
  1763. SrvTimeToDosTime (
  1764. IN PLARGE_INTEGER SystemTime,
  1765. OUT PSMB_DATE DosDate,
  1766. OUT PSMB_TIME DosTime
  1767. )
  1768. /*++
  1769. Routine Description:
  1770. This function converts a time in NT format to the format used by
  1771. MS-DOS.
  1772. Arguments:
  1773. SystemTime - a pointer to an NT time to convert.
  1774. DosDate - a pointer to a location in which to store the date in DOS format.
  1775. DosTime - a pointer to a location in which to store the time in DOS format.
  1776. Return Value:
  1777. None.
  1778. --*/
  1779. {
  1780. TIME_FIELDS timeFields;
  1781. LARGE_INTEGER localTime;
  1782. PAGED_CODE( );
  1783. if ( SystemTime->QuadPart == 0 ) {
  1784. goto zerotime;
  1785. }
  1786. //
  1787. // Add almost two seconds to round up to the nearest double second.
  1788. // We need to do this to be compatible with the NT rdr and NT FAT
  1789. // filesystem.
  1790. //
  1791. SystemTime->QuadPart += AlmostTwoSeconds;
  1792. //
  1793. // Convert System time (UTC) to local NT time
  1794. //
  1795. ExSystemTimeToLocalTime( SystemTime, &localTime );
  1796. RtlTimeToTimeFields(
  1797. &localTime,
  1798. &timeFields
  1799. );
  1800. DosDate->Struct.Day = timeFields.Day;
  1801. DosDate->Struct.Month = timeFields.Month;
  1802. DosDate->Struct.Year = (SHORT)(timeFields.Year - 1980);
  1803. DosTime->Struct.TwoSeconds = (SHORT)(timeFields.Second / 2);
  1804. DosTime->Struct.Minutes = timeFields.Minute;
  1805. DosTime->Struct.Hours = timeFields.Hour;
  1806. return;
  1807. zerotime:
  1808. DosDate->Struct.Day = 0;
  1809. DosDate->Struct.Month = 0;
  1810. DosDate->Struct.Year = 0;
  1811. DosTime->Struct.TwoSeconds = 0;
  1812. DosTime->Struct.Minutes = 0;
  1813. DosTime->Struct.Hours = 0;
  1814. return;
  1815. } // SrvTimeToDosTime
  1816. VOID
  1817. SrvDosTimeToTime (
  1818. OUT PLARGE_INTEGER SystemTime,
  1819. IN SMB_DATE DosDate,
  1820. IN SMB_TIME DosTime
  1821. )
  1822. /*++
  1823. Routine Description:
  1824. This function converts a time in NT format to the format used by
  1825. MS-DOS.
  1826. Arguments:
  1827. Time - a pointer to a location in which to store the NT time.
  1828. DosDate - a pointer the date in DOS format.
  1829. DosDate - a pointer the date in DOS format.
  1830. Return Value:
  1831. None.
  1832. --*/
  1833. {
  1834. TIME_FIELDS timeFields;
  1835. LARGE_INTEGER localTime;
  1836. PAGED_CODE( );
  1837. timeFields.Day = DosDate.Struct.Day;
  1838. timeFields.Month = DosDate.Struct.Month;
  1839. timeFields.Year = (SHORT)(DosDate.Struct.Year + 1980);
  1840. timeFields.Milliseconds = 0;
  1841. timeFields.Second = (SHORT)(DosTime.Struct.TwoSeconds * 2);
  1842. timeFields.Minute = DosTime.Struct.Minutes;
  1843. timeFields.Hour = DosTime.Struct.Hours;
  1844. if ( !RtlTimeFieldsToTime( &timeFields, &localTime ) ) {
  1845. goto zerotime;
  1846. }
  1847. ExLocalTimeToSystemTime( &localTime, SystemTime );
  1848. return;
  1849. zerotime:
  1850. SystemTime->QuadPart = 0;
  1851. return;
  1852. } // SrvDosTimeToTime
  1853. USHORT
  1854. SrvGetOs2TimeZone(
  1855. IN PLARGE_INTEGER SystemTime
  1856. )
  1857. /*++
  1858. Routine Description:
  1859. This function gets the timezone bias.
  1860. Arguments:
  1861. SystemTime - The current UTC time expressed.
  1862. Return Value:
  1863. The time zone bias in minutes from GMT.
  1864. --*/
  1865. {
  1866. LARGE_INTEGER zeroTime;
  1867. LARGE_INTEGER timeZoneBias;
  1868. PAGED_CODE( );
  1869. zeroTime.QuadPart = 0;
  1870. //
  1871. // Specifying a zero local time will give you the time zone bias.
  1872. //
  1873. ExLocalTimeToSystemTime( &zeroTime, &timeZoneBias );
  1874. //
  1875. // Convert the bias unit from 100ns to minutes. The maximum value
  1876. // for the bias is 720 minutes so a USHORT is big enough to contain
  1877. // it.
  1878. //
  1879. return (SHORT)(timeZoneBias.QuadPart / (10*1000*1000*60));
  1880. } // SrvGetOs2TimeZone
  1881. NTSTATUS
  1882. SrvQueryBasicAndStandardInformation(
  1883. HANDLE FileHandle,
  1884. PFILE_OBJECT FileObject OPTIONAL,
  1885. PFILE_BASIC_INFORMATION FileBasicInfo,
  1886. PFILE_STANDARD_INFORMATION FileStandardInfo OPTIONAL
  1887. )
  1888. {
  1889. NTSTATUS status;
  1890. PFILE_OBJECT fileObject;
  1891. PDEVICE_OBJECT deviceObject;
  1892. PFAST_IO_DISPATCH fastIoDispatch;
  1893. PFAST_IO_QUERY_BASIC_INFO fastQueryBasicInfo;
  1894. PFAST_IO_QUERY_STANDARD_INFO fastQueryStandardInfo;
  1895. IO_STATUS_BLOCK ioStatus;
  1896. PAGED_CODE( );
  1897. ASSERT( FileBasicInfo != NULL );
  1898. //
  1899. // Get a pointer to the file object, so that we can directly
  1900. // access the fast IO routines, if they exists.
  1901. //
  1902. if ( !ARGUMENT_PRESENT( FileObject ) ) {
  1903. status = ObReferenceObjectByHandle(
  1904. FileHandle,
  1905. 0,
  1906. NULL,
  1907. KernelMode,
  1908. (PVOID *)&fileObject,
  1909. NULL
  1910. );
  1911. if ( !NT_SUCCESS(status) ) {
  1912. SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
  1913. //
  1914. // This internal error bugchecks the system.
  1915. //
  1916. INTERNAL_ERROR(
  1917. ERROR_LEVEL_IMPOSSIBLE,
  1918. "CompleteOpen: unable to reference file handle 0x%lx",
  1919. FileHandle,
  1920. NULL
  1921. );
  1922. return(status);
  1923. }
  1924. } else {
  1925. fileObject = FileObject;
  1926. }
  1927. deviceObject = IoGetRelatedDeviceObject( fileObject );
  1928. fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
  1929. if ( fastIoDispatch ) {
  1930. fastQueryBasicInfo = fastIoDispatch->FastIoQueryBasicInfo;
  1931. fastQueryStandardInfo = fastIoDispatch->FastIoQueryStandardInfo;
  1932. } else {
  1933. fastQueryBasicInfo = NULL;
  1934. fastQueryStandardInfo = NULL;
  1935. }
  1936. if ( fastQueryBasicInfo &&
  1937. fastQueryBasicInfo(
  1938. fileObject,
  1939. TRUE,
  1940. FileBasicInfo,
  1941. &ioStatus,
  1942. deviceObject
  1943. ) ) {
  1944. status = ioStatus.Status;
  1945. } else {
  1946. status = NtQueryInformationFile(
  1947. FileHandle,
  1948. &ioStatus,
  1949. (PVOID)FileBasicInfo,
  1950. sizeof(FILE_BASIC_INFORMATION),
  1951. FileBasicInformation
  1952. );
  1953. }
  1954. //
  1955. // If we're done if there was a failure, return
  1956. //
  1957. if ( ARGUMENT_PRESENT( FileStandardInfo ) && NT_SUCCESS(status) ) {
  1958. //
  1959. // Get the standard info
  1960. //
  1961. if ( fastQueryStandardInfo &&
  1962. fastQueryStandardInfo(
  1963. fileObject,
  1964. TRUE,
  1965. FileStandardInfo,
  1966. &ioStatus,
  1967. deviceObject
  1968. ) ) {
  1969. status = ioStatus.Status;
  1970. } else {
  1971. status = NtQueryInformationFile(
  1972. FileHandle,
  1973. &ioStatus,
  1974. (PVOID)FileStandardInfo,
  1975. sizeof(FILE_STANDARD_INFORMATION),
  1976. FileStandardInformation
  1977. );
  1978. }
  1979. }
  1980. if ( !ARGUMENT_PRESENT( FileObject ) ) {
  1981. ObDereferenceObject( fileObject );
  1982. }
  1983. return(status);
  1984. } // SrvQueryBasicAndStandardInformation
  1985. NTSTATUS
  1986. SrvQueryNetworkOpenInformation(
  1987. HANDLE FileHandle,
  1988. PFILE_OBJECT FileObject OPTIONAL,
  1989. PSRV_NETWORK_OPEN_INFORMATION SrvNetworkOpenInformation,
  1990. BOOLEAN QueryEaSize
  1991. )
  1992. {
  1993. NTSTATUS status;
  1994. PFILE_OBJECT fileObject;
  1995. PDEVICE_OBJECT deviceObject;
  1996. PFAST_IO_DISPATCH fastIoDispatch;
  1997. PFAST_IO_QUERY_NETWORK_OPEN_INFO fastQueryNetworkOpenInfo;
  1998. FILE_BASIC_INFORMATION FileBasicInfo;
  1999. FILE_STANDARD_INFORMATION FileStandardInfo;
  2000. IO_STATUS_BLOCK ioStatus;
  2001. PAGED_CODE( );
  2002. //
  2003. // Get a pointer to the file object, so that we can directly
  2004. // access the fast IO routines, if they exist.
  2005. //
  2006. if ( !ARGUMENT_PRESENT( FileObject ) ) {
  2007. status = ObReferenceObjectByHandle(
  2008. FileHandle,
  2009. 0,
  2010. NULL,
  2011. KernelMode,
  2012. (PVOID *)&fileObject,
  2013. NULL
  2014. );
  2015. if ( !NT_SUCCESS(status) ) {
  2016. SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
  2017. //
  2018. // This internal error bugchecks the system.
  2019. //
  2020. INTERNAL_ERROR(
  2021. ERROR_LEVEL_IMPOSSIBLE,
  2022. "CompleteOpen: unable to reference file handle 0x%lx",
  2023. FileHandle,
  2024. NULL
  2025. );
  2026. return(status);
  2027. }
  2028. } else {
  2029. fileObject = FileObject;
  2030. }
  2031. deviceObject = IoGetRelatedDeviceObject( fileObject );
  2032. fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
  2033. if( !QueryEaSize &&
  2034. fastIoDispatch &&
  2035. fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH,FastIoQueryNetworkOpenInfo)) {
  2036. fastQueryNetworkOpenInfo = fastIoDispatch->FastIoQueryNetworkOpenInfo;
  2037. if( fastQueryNetworkOpenInfo &&
  2038. fastQueryNetworkOpenInfo(
  2039. fileObject,
  2040. TRUE,
  2041. (PFILE_NETWORK_OPEN_INFORMATION)SrvNetworkOpenInformation,
  2042. &ioStatus,
  2043. deviceObject ) ) {
  2044. status = ioStatus.Status;
  2045. if ( !ARGUMENT_PRESENT( FileObject ) ) {
  2046. ObDereferenceObject( fileObject );
  2047. }
  2048. return status;
  2049. }
  2050. }
  2051. //
  2052. // The fast path didn't work. Do it the slow way
  2053. //
  2054. status = SrvQueryBasicAndStandardInformation(
  2055. FileHandle,
  2056. fileObject,
  2057. &FileBasicInfo,
  2058. &FileStandardInfo
  2059. );
  2060. if ( !ARGUMENT_PRESENT( FileObject ) ) {
  2061. ObDereferenceObject( fileObject );
  2062. }
  2063. if( !NT_SUCCESS( status ) ) {
  2064. return status;
  2065. }
  2066. SrvNetworkOpenInformation->CreationTime = FileBasicInfo.CreationTime;
  2067. SrvNetworkOpenInformation->LastAccessTime = FileBasicInfo.LastAccessTime;
  2068. SrvNetworkOpenInformation->LastWriteTime = FileBasicInfo.LastWriteTime;
  2069. SrvNetworkOpenInformation->ChangeTime = FileBasicInfo.ChangeTime;
  2070. SrvNetworkOpenInformation->AllocationSize = FileStandardInfo.AllocationSize;
  2071. SrvNetworkOpenInformation->EndOfFile = FileStandardInfo.EndOfFile;
  2072. SrvNetworkOpenInformation->FileAttributes = FileBasicInfo.FileAttributes;
  2073. if ( QueryEaSize ) {
  2074. FILE_EA_INFORMATION fileEaInformation;
  2075. status = NtQueryInformationFile(
  2076. FileHandle,
  2077. &ioStatus,
  2078. (PVOID)&fileEaInformation,
  2079. sizeof(FILE_EA_INFORMATION),
  2080. FileEaInformation
  2081. );
  2082. if ( !NT_SUCCESS(status) ) {
  2083. INTERNAL_ERROR(
  2084. ERROR_LEVEL_UNEXPECTED,
  2085. "SrvQueryInformationFile: NtQueryInformationFile "
  2086. "(EA information) failed: %X",
  2087. status,
  2088. NULL
  2089. );
  2090. SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
  2091. return status;
  2092. }
  2093. SrvNetworkOpenInformation->EaSize = fileEaInformation.EaSize;
  2094. }
  2095. return(status);
  2096. } // SrvQueryNetworkOpenInformation