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.

1778 lines
53 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. dir.c
  5. Abstract:
  6. This module implements the file directory routines for the
  7. Netware Redirector.
  8. Author:
  9. Manny Weiser (mannyw) 4-Mar-1993
  10. Revision History:
  11. --*/
  12. #include "procs.h"
  13. //
  14. // Local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_DIRCTRL)
  17. NTSTATUS
  18. NwCommonDirectoryControl (
  19. IN PIRP_CONTEXT pIrpContext
  20. );
  21. NTSTATUS
  22. NwQueryDirectory (
  23. IN PIRP_CONTEXT pIrpContext,
  24. IN PICB pIcb
  25. );
  26. NTSTATUS
  27. GetNextFile(
  28. PIRP_CONTEXT pIrpContext,
  29. PICB Icb,
  30. PULONG fileIndexLow,
  31. PULONG fileIndexHigh,
  32. UCHAR SearchAttributes,
  33. PNW_DIRECTORY_INFO NwDirInfo
  34. );
  35. NTSTATUS
  36. NtSearchMaskToNw(
  37. IN PUNICODE_STRING UcSearchMask,
  38. IN OUT POEM_STRING OemSearchMask,
  39. IN PICB Icb,
  40. IN BOOLEAN ShortNameSearch
  41. );
  42. #if 0
  43. VOID
  44. NwCancelFindNotify (
  45. IN PDEVICE_OBJECT DeviceObject,
  46. IN PIRP Irp
  47. );
  48. #endif
  49. #ifdef ALLOC_PRAGMA
  50. #pragma alloc_text( PAGE, NwFsdDirectoryControl )
  51. #pragma alloc_text( PAGE, NwQueryDirectory )
  52. #pragma alloc_text( PAGE, GetNextFile )
  53. #pragma alloc_text( PAGE, NtSearchMaskToNw )
  54. #ifndef QFE_BUILD
  55. #pragma alloc_text( PAGE1, NwCommonDirectoryControl )
  56. #endif
  57. #endif
  58. #if 0 // Not pageable
  59. // see ifndef QFE_BUILD above
  60. #endif
  61. NTSTATUS
  62. NwFsdDirectoryControl (
  63. IN PDEVICE_OBJECT DeviceObject,
  64. IN PIRP Irp
  65. )
  66. /*++
  67. Routine Description:
  68. This routine is the FSD routine that handles directory control
  69. functions (i.e., query and notify).
  70. Arguments:
  71. NwfsDeviceObject - Supplies the device object for the directory function.
  72. Irp - Supplies the IRP to process.
  73. Return Value:
  74. NTSTATUS - The result status.
  75. --*/
  76. {
  77. PIRP_CONTEXT pIrpContext = NULL;
  78. NTSTATUS status;
  79. BOOLEAN TopLevel;
  80. PAGED_CODE();
  81. DebugTrace(+1, Dbg, "NwFsdDirectoryControl\n", 0);
  82. //
  83. // Call the common directory control routine.
  84. //
  85. FsRtlEnterFileSystem();
  86. TopLevel = NwIsIrpTopLevel( Irp );
  87. try {
  88. pIrpContext = AllocateIrpContext( Irp );
  89. status = NwCommonDirectoryControl( pIrpContext );
  90. } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
  91. if ( pIrpContext == NULL ) {
  92. //
  93. // If we couldn't allocate an irp context, just complete
  94. // irp without any fanfare.
  95. //
  96. status = STATUS_INSUFFICIENT_RESOURCES;
  97. Irp->IoStatus.Status = status;
  98. Irp->IoStatus.Information = 0;
  99. IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
  100. } else {
  101. //
  102. // We had some trouble trying to perform the requested
  103. // operation, so we'll abort the I/O request with
  104. // the error status that we get back from the
  105. // execption code.
  106. //
  107. status = NwProcessException( pIrpContext, GetExceptionCode() );
  108. }
  109. }
  110. if ( pIrpContext ) {
  111. NwCompleteRequest( pIrpContext, status );
  112. }
  113. if ( TopLevel ) {
  114. NwSetTopLevelIrp( NULL );
  115. }
  116. FsRtlExitFileSystem();
  117. //
  118. // Return to the caller.
  119. //
  120. DebugTrace(-1, Dbg, "NwFsdDirectoryControl -> %08lx\n", status );
  121. return status;
  122. }
  123. NTSTATUS
  124. NwCommonDirectoryControl (
  125. IN PIRP_CONTEXT IrpContext
  126. )
  127. /*++
  128. Routine Description:
  129. This routine does the common code for directory control functions.
  130. Arguments:
  131. IrpContext - Supplies the request being processed.
  132. Return Value:
  133. NTSTATUS - The return status for the operation
  134. --*/
  135. {
  136. NTSTATUS status;
  137. PIRP Irp;
  138. PIO_STACK_LOCATION irpSp;
  139. NODE_TYPE_CODE nodeTypeCode;
  140. PICB icb;
  141. PDCB dcb;
  142. PVOID fsContext;
  143. //
  144. // Get the current stack location
  145. //
  146. Irp = IrpContext->pOriginalIrp;
  147. irpSp = IoGetCurrentIrpStackLocation( Irp );
  148. DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0);
  149. DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG_PTR)Irp);
  150. //
  151. // Decode the file object to figure out who we are. If the result
  152. // is not an ICB then its an illegal parameter.
  153. //
  154. if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
  155. &fsContext,
  156. (PVOID *)&icb )) != NW_NTC_ICB) {
  157. DebugTrace(0, Dbg, "Not a directory\n", 0);
  158. status = STATUS_INVALID_PARAMETER;
  159. DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
  160. return status;
  161. }
  162. dcb = (PDCB)icb->SuperType.Fcb;
  163. nodeTypeCode = dcb->NodeTypeCode;
  164. if ( nodeTypeCode != NW_NTC_DCB ) {
  165. DebugTrace(0, Dbg, "Not a directory\n", 0);
  166. status = STATUS_INVALID_PARAMETER;
  167. DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
  168. return status;
  169. }
  170. IrpContext->pScb = icb->SuperType.Fcb->Scb;
  171. IrpContext->pNpScb = IrpContext->pScb->pNpScb;
  172. IrpContext->Icb = icb;
  173. //
  174. // Acquire exclusive access to the DCB. Get to front of queue
  175. // first to avoid deadlock potential.
  176. //
  177. NwAppendToQueueAndWait( IrpContext );
  178. NwAcquireExclusiveFcb( dcb->NonPagedFcb, TRUE );
  179. try {
  180. NwVerifyIcb( icb );
  181. //
  182. // We know this is a directory control so we'll case on the
  183. // minor function, and call the appropriate work routines.
  184. //
  185. switch (irpSp->MinorFunction) {
  186. case IRP_MN_QUERY_DIRECTORY:
  187. status = NwQueryDirectory( IrpContext, icb );
  188. break;
  189. case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
  190. #if 0
  191. if ( !icb->FailedFindNotify ) {
  192. icb->FailedFindNotify = TRUE;
  193. #endif
  194. status = STATUS_NOT_SUPPORTED;
  195. #if 0
  196. } else {
  197. //
  198. // HACKHACK
  199. // Cover for process that keeps trying to use
  200. // find notify even though we don't support it.
  201. //
  202. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  203. IoAcquireCancelSpinLock( &Irp->CancelIrql );
  204. if ( Irp->Cancel ) {
  205. status = STATUS_CANCELLED;
  206. } else {
  207. InsertTailList( &FnList, &IrpContext->NextRequest );
  208. IoMarkIrpPending( Irp );
  209. IoSetCancelRoutine( Irp, NwCancelFindNotify );
  210. status = STATUS_PENDING;
  211. }
  212. IoReleaseCancelSpinLock( Irp->CancelIrql );
  213. NwReleaseRcb( &NwRcb );
  214. }
  215. #endif
  216. break;
  217. default:
  218. //
  219. // For all other minor function codes we say they're invalid
  220. // and complete the request.
  221. //
  222. DebugTrace(0, Dbg, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction);
  223. status = STATUS_INVALID_DEVICE_REQUEST;
  224. break;
  225. }
  226. } finally {
  227. NwDequeueIrpContext( IrpContext, FALSE );
  228. NwReleaseFcb( dcb->NonPagedFcb );
  229. DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status);
  230. }
  231. return status;
  232. }
  233. NTSTATUS
  234. NwQueryDirectory (
  235. IN PIRP_CONTEXT pIrpContext,
  236. IN PICB Icb
  237. )
  238. /*++
  239. Routine Description:
  240. This is the work routine for querying a directory.
  241. Arugments:
  242. IrpContext - Supplies the Irp context information.
  243. Icb - Pointer the ICB for the request.
  244. Return Value:
  245. NTSTATUS - The return status for the operation.
  246. --*/
  247. {
  248. NTSTATUS status = STATUS_SUCCESS;
  249. PIRP Irp;
  250. PIO_STACK_LOCATION irpSp;
  251. PUCHAR buffer;
  252. CLONG systemBufferLength;
  253. UNICODE_STRING searchMask;
  254. ULONG fileIndexLow;
  255. ULONG fileIndexHigh;
  256. FILE_INFORMATION_CLASS fileInformationClass;
  257. BOOLEAN restartScan;
  258. BOOLEAN returnSingleEntry;
  259. BOOLEAN indexSpecified;
  260. PVCB vcb;
  261. BOOLEAN ansiStringAllocated = FALSE;
  262. UCHAR SearchAttributes;
  263. BOOLEAN searchRetry;
  264. static WCHAR star[] = L"*";
  265. BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive
  266. ULONG lastEntry;
  267. ULONG nextEntry;
  268. ULONG totalBufferLength = 0;
  269. PFILE_BOTH_DIR_INFORMATION dirInfo;
  270. PFILE_NAMES_INFORMATION namesInfo;
  271. BOOLEAN canContinue = FALSE;
  272. BOOLEAN useCache = FALSE;
  273. BOOLEAN lastIndexFromServer = FALSE;
  274. PNW_DIRECTORY_INFO dirCache;
  275. PLIST_ENTRY entry;
  276. ULONG i;
  277. PAGED_CODE();
  278. //
  279. // Get the current stack location.
  280. //
  281. Irp = pIrpContext->pOriginalIrp;
  282. irpSp = IoGetCurrentIrpStackLocation( Irp );
  283. vcb = Icb->SuperType.Fcb->Vcb;
  284. DebugTrace(+1, Dbg, "NwQueryDirectory\n", 0 );
  285. DebugTrace( 0, Dbg, "Icb = %08lx\n", (ULONG_PTR)Icb);
  286. DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer);
  287. DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
  288. DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG_PTR)irpSp->Parameters.QueryDirectory.FileName);
  289. DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
  290. DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
  291. DebugTrace( 0, Dbg, "RestartScan = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN));
  292. DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
  293. DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
  294. //
  295. // Make local copies of the input parameters.
  296. //
  297. systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
  298. restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN);
  299. indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
  300. returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  301. fileIndexLow = 0;
  302. fileIndexHigh = 0;
  303. fileInformationClass =
  304. irpSp->Parameters.QueryDirectory.FileInformationClass;
  305. if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
  306. searchMask = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName;
  307. } else {
  308. searchMask.Length = 0;
  309. searchMask.Buffer = NULL;
  310. }
  311. buffer = Irp->UserBuffer;
  312. DebugTrace(0, Dbg, "Users Buffer -> %08lx\n", buffer);
  313. //
  314. // It is ok to attempt a reconnect if this request fails with a
  315. // connection error.
  316. //
  317. SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  318. //
  319. // Check if the ICB already has a query template attached. If it
  320. // does not already have one then we either use the string we are
  321. // given or we attach our own containing "*"
  322. //
  323. if ( Icb->NwQueryTemplate.Buffer == NULL ) {
  324. //
  325. // This is our first time calling query directory so we need
  326. // to either set the query template to the user specified string
  327. // or to "*.*".
  328. //
  329. if ( searchMask.Buffer == NULL ) {
  330. DebugTrace(0, Dbg, "Set template to *", 0);
  331. searchMask.Length = sizeof( star ) - sizeof(WCHAR);
  332. searchMask.Buffer = star;
  333. }
  334. DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG_PTR)&searchMask);
  335. //
  336. // Map the NT search names to NCP. Note that this must be
  337. // done after the Unicode to OEM translation.
  338. //
  339. searchRetry = FALSE;
  340. do {
  341. status = NtSearchMaskToNw(
  342. &searchMask,
  343. &Icb->NwQueryTemplate,
  344. Icb,
  345. searchRetry );
  346. if ( !NT_SUCCESS( status ) ) {
  347. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  348. return( status );
  349. }
  350. Icb->UQueryTemplate.Buffer = ALLOCATE_POOL( PagedPool, searchMask.Length );
  351. if (Icb->UQueryTemplate.Buffer == NULL ) {
  352. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES );
  353. return( STATUS_INSUFFICIENT_RESOURCES );
  354. }
  355. Icb->UQueryTemplate.MaximumLength = searchMask.Length;
  356. RtlCopyUnicodeString( &Icb->UQueryTemplate, &searchMask );
  357. //
  358. // Now send a Search Initialize NCP.
  359. //
  360. // Do a short search if the server doesn't support long names,
  361. // or this is a short-name non-wild card search
  362. //
  363. if ( !Icb->ShortNameSearch ) {
  364. status = ExchangeWithWait(
  365. pIrpContext,
  366. SynchronousResponseCallback,
  367. "Lbb-DbC",
  368. NCP_LFN_SEARCH_INITIATE,
  369. vcb->Specific.Disk.LongNameSpace,
  370. vcb->Specific.Disk.VolumeNumber,
  371. vcb->Specific.Disk.Handle,
  372. LFN_FLAG_SHORT_DIRECTORY,
  373. &Icb->SuperType.Fcb->RelativeFileName );
  374. if ( NT_SUCCESS( status ) ) {
  375. status = ParseResponse(
  376. pIrpContext,
  377. pIrpContext->rsp,
  378. pIrpContext->ResponseLength,
  379. "Nbee",
  380. &Icb->SearchVolume,
  381. &Icb->SearchIndexHigh,
  382. &Icb->SearchIndexLow );
  383. }
  384. } else {
  385. status = ExchangeWithWait(
  386. pIrpContext,
  387. SynchronousResponseCallback,
  388. "FbJ",
  389. NCP_SEARCH_INITIATE,
  390. vcb->Specific.Disk.Handle,
  391. &Icb->SuperType.Fcb->RelativeFileName );
  392. if ( NT_SUCCESS( status ) ) {
  393. status = ParseResponse(
  394. pIrpContext,
  395. pIrpContext->rsp,
  396. pIrpContext->ResponseLength,
  397. "Nbww-",
  398. &Icb->SearchVolume,
  399. &Icb->SearchHandle,
  400. &Icb->SearchIndexLow );
  401. }
  402. }
  403. //
  404. // If we couldn't find the search path, and we did a long
  405. // name search initiate, try again with a short name.
  406. //
  407. if ( status == STATUS_OBJECT_PATH_NOT_FOUND &&
  408. !Icb->ShortNameSearch ) {
  409. searchRetry = TRUE;
  410. if ( Icb->UQueryTemplate.Buffer != NULL ) {
  411. FREE_POOL( Icb->UQueryTemplate.Buffer );
  412. }
  413. RtlFreeOemString ( &Icb->NwQueryTemplate );
  414. } else {
  415. searchRetry = FALSE;
  416. }
  417. } while ( searchRetry );
  418. if ( !NT_SUCCESS( status ) ) {
  419. if (status == STATUS_UNSUCCESSFUL) {
  420. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_SUCH_FILE);
  421. return( STATUS_NO_SUCH_FILE );
  422. }
  423. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  424. return( status );
  425. }
  426. //
  427. // Since we are doing a search we will need to send an End Of Job
  428. // for this PID.
  429. //
  430. NwSetEndOfJobRequired(pIrpContext->pNpScb, Icb->Pid );
  431. fileIndexLow = Icb->SearchIndexLow;
  432. fileIndexHigh = Icb->SearchIndexHigh;
  433. //
  434. // We can't ask for both files and directories, so first ask for
  435. // files, then ask for directories.
  436. //
  437. SearchAttributes = NW_ATTRIBUTE_SYSTEM |
  438. NW_ATTRIBUTE_HIDDEN |
  439. NW_ATTRIBUTE_READ_ONLY;
  440. //
  441. // If there are no wildcards in the search mask, then setup to
  442. // not generate the . and .. entries.
  443. //
  444. if ( !FsRtlDoesNameContainWildCards( &Icb->UQueryTemplate ) ) {
  445. Icb->DotReturned = TRUE;
  446. Icb->DotDotReturned = TRUE;
  447. } else {
  448. Icb->DotReturned = FALSE;
  449. Icb->DotDotReturned = FALSE;
  450. }
  451. } else {
  452. //
  453. // Check if we were given an index to start with or if we need to
  454. // restart the scan or if we should use the index that was saved in
  455. // the ICB.
  456. //
  457. if (restartScan) {
  458. useCache = FALSE;
  459. fileIndexLow = (ULONG)-1;
  460. fileIndexHigh = Icb->SearchIndexHigh;
  461. //
  462. // Send a Search Initialize NCP. The server often times out search
  463. // handles and if this one has been sitting at the end of the
  464. // directory then its likely we would get no files at all!
  465. //
  466. // Do a short search if the server doesn't support long names,
  467. // or this is a short-name non-wild card search
  468. //
  469. if ( !Icb->ShortNameSearch ) {
  470. status = ExchangeWithWait(
  471. pIrpContext,
  472. SynchronousResponseCallback,
  473. "Lbb-DbC",
  474. NCP_LFN_SEARCH_INITIATE,
  475. vcb->Specific.Disk.LongNameSpace,
  476. vcb->Specific.Disk.VolumeNumber,
  477. vcb->Specific.Disk.Handle,
  478. LFN_FLAG_SHORT_DIRECTORY,
  479. &Icb->SuperType.Fcb->RelativeFileName );
  480. if ( NT_SUCCESS( status ) ) {
  481. status = ParseResponse(
  482. pIrpContext,
  483. pIrpContext->rsp,
  484. pIrpContext->ResponseLength,
  485. "Nbee",
  486. &Icb->SearchVolume,
  487. &Icb->SearchIndexHigh,
  488. &Icb->SearchIndexLow );
  489. }
  490. } else {
  491. status = ExchangeWithWait(
  492. pIrpContext,
  493. SynchronousResponseCallback,
  494. "FbJ",
  495. NCP_SEARCH_INITIATE,
  496. vcb->Specific.Disk.Handle,
  497. &Icb->SuperType.Fcb->RelativeFileName );
  498. if ( NT_SUCCESS( status ) ) {
  499. status = ParseResponse(
  500. pIrpContext,
  501. pIrpContext->rsp,
  502. pIrpContext->ResponseLength,
  503. "Nbww-",
  504. &Icb->SearchVolume,
  505. &Icb->SearchHandle,
  506. &Icb->SearchIndexLow );
  507. }
  508. }
  509. Icb->ReturnedSomething = FALSE;
  510. //
  511. // We can't ask for both files and directories, so first ask for
  512. // files, then ask for directories.
  513. //
  514. SearchAttributes = NW_ATTRIBUTE_SYSTEM |
  515. NW_ATTRIBUTE_HIDDEN |
  516. NW_ATTRIBUTE_READ_ONLY;
  517. Icb->SearchAttributes = SearchAttributes;
  518. Icb->DotReturned = FALSE;
  519. Icb->DotDotReturned = FALSE;
  520. } else if ((!indexSpecified) ||
  521. (canContinue) ) {
  522. //
  523. // Continue from the one of the last filenames.
  524. //
  525. SearchAttributes = Icb->SearchAttributes;
  526. if( !indexSpecified ) {
  527. useCache = FALSE;
  528. fileIndexLow = Icb->SearchIndexLow;
  529. fileIndexHigh = Icb->SearchIndexHigh;
  530. }
  531. if ( SearchAttributes == 0xFF && fileIndexLow == Icb->SearchIndexLow ) {
  532. //
  533. // This is a completed search.
  534. //
  535. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_MORE_FILES);
  536. return( STATUS_NO_MORE_FILES );
  537. }
  538. } else {
  539. //
  540. // Someone's trying to do a resume from key. The netware
  541. // server doesn't support this, so neither do we.
  542. //
  543. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NOT_IMPLEMENTED);
  544. return( STATUS_NOT_IMPLEMENTED );
  545. }
  546. }
  547. //
  548. // Now we are committed to completing the Irp, we do that in
  549. // the finally clause of the following try.
  550. //
  551. try {
  552. ULONG baseLength;
  553. ULONG lengthAdded;
  554. PNW_DIRECTORY_INFO nwDirInfo;
  555. ULONG FileNameLength;
  556. ULONG entriesToCreate;
  557. lastEntry = 0;
  558. nextEntry = 0;
  559. switch (fileInformationClass) {
  560. case FileDirectoryInformation:
  561. baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
  562. break;
  563. case FileFullDirectoryInformation:
  564. baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
  565. break;
  566. case FileNamesInformation:
  567. baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] );
  568. break;
  569. case FileBothDirectoryInformation:
  570. baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
  571. break;
  572. default:
  573. try_return( status = STATUS_INVALID_INFO_CLASS );
  574. }
  575. //
  576. // It is not ok to attempt a reconnect if this request fails with a
  577. // connection error, since our search handle would be invalid.
  578. //
  579. ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  580. //
  581. // See if we have a dir cache. If not, create one.
  582. //
  583. if( !Icb->DirCacheBuffer ) {
  584. entriesToCreate = 1;
  585. Icb->DirCacheBuffer = ALLOCATE_POOL ( PagedPool, (sizeof(NW_DIRECTORY_INFO) * entriesToCreate) );
  586. if( !Icb->DirCacheBuffer ) {
  587. try_return( status = STATUS_NO_MEMORY );
  588. }
  589. RtlZeroMemory( Icb->DirCacheBuffer, sizeof(NW_DIRECTORY_INFO) * entriesToCreate );
  590. dirCache = (PNW_DIRECTORY_INFO)Icb->DirCacheBuffer;
  591. for( i = 0; i < entriesToCreate; i++ ) {
  592. InsertTailList( &(Icb->DirCache), &(dirCache->ListEntry) );
  593. dirCache++;
  594. }
  595. }
  596. while ( TRUE ) {
  597. ULONG bytesToCopy;
  598. ULONG bytesRemainingInBuffer;
  599. DebugTrace(0, Dbg, "Top of Loop\n", 0);
  600. DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", fileIndexLow);
  601. DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
  602. DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
  603. if( useCache ) {
  604. //
  605. // We need to use the data out of the entry we found in the cache.
  606. // dirCache points to the entry that matches, and the request was not
  607. // for the last file we read, so the entry after dirCache is the one
  608. // we want.
  609. //
  610. DebugTrace(0, Dbg, "Using cache\n", 0);
  611. entry = dirCache->ListEntry.Flink;
  612. dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  613. nwDirInfo = dirCache;
  614. fileIndexLow = nwDirInfo->FileIndexLow;
  615. fileIndexHigh = nwDirInfo->FileIndexHigh;
  616. status = nwDirInfo->Status;
  617. //
  618. // Check to see if we should still keep using the cache or not.
  619. //
  620. if( entry->Flink == &(Icb->DirCache) ) {
  621. //
  622. // This is the last entry. We need to stop using the cache.
  623. //
  624. useCache = FALSE;
  625. Icb->CacheHint = NULL;
  626. } else {
  627. Icb->CacheHint = entry;
  628. }
  629. } else {
  630. //
  631. // Pull an entry from the dir cache.
  632. //
  633. entry = RemoveHeadList( &(Icb->DirCache) );
  634. nwDirInfo = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  635. nwDirInfo->FileName.Buffer = nwDirInfo->FileNameBuffer;
  636. nwDirInfo->FileName.MaximumLength = NW_MAX_FILENAME_SIZE;
  637. status = GetNextFile(
  638. pIrpContext,
  639. Icb,
  640. &fileIndexLow,
  641. &fileIndexHigh,
  642. SearchAttributes,
  643. nwDirInfo );
  644. //
  645. // Store the return and the file index number,
  646. // and then put this entry in the cache.
  647. //
  648. nwDirInfo->FileIndexLow = fileIndexLow;
  649. nwDirInfo->FileIndexHigh = fileIndexHigh;
  650. nwDirInfo->Status = status;
  651. InsertTailList( &(Icb->DirCache), &(nwDirInfo->ListEntry) );
  652. lastIndexFromServer = TRUE;
  653. // SVR to avoid rescanning from end of dir all
  654. if (fileIndexLow != -1) {
  655. Icb->LastSearchIndexLow = fileIndexLow;
  656. }
  657. // SVR end
  658. }
  659. if ( NT_SUCCESS( status ) ) {
  660. DebugTrace(0, Dbg, "DirFileName = %wZ\n", &nwDirInfo->FileName);
  661. DebugTrace(0, Dbg, "FileIndexLow = %08lx\n", fileIndexLow);
  662. FileNameLength = nwDirInfo->FileName.Length;
  663. bytesRemainingInBuffer = systemBufferLength - nextEntry;
  664. ASSERT( bytesRemainingInBuffer >= baseLength );
  665. if (IsTerminalServer() && (LONG)NW_MAX_FILENAME_SIZE < FileNameLength )
  666. try_return( status = STATUS_BUFFER_OVERFLOW );
  667. //
  668. // See how much of the name we will be able to copy into
  669. // the system buffer. This also dictates our return
  670. // value.
  671. //
  672. if ( baseLength + FileNameLength <= bytesRemainingInBuffer ) {
  673. bytesToCopy = FileNameLength;
  674. status = STATUS_SUCCESS;
  675. } else {
  676. if (IsTerminalServer()) {
  677. try_return( status = STATUS_BUFFER_OVERFLOW );
  678. }
  679. bytesToCopy = bytesRemainingInBuffer - baseLength;
  680. status = STATUS_BUFFER_OVERFLOW;
  681. }
  682. //
  683. // Note how much of buffer we are consuming and zero
  684. // the base part of the structure.
  685. //
  686. lengthAdded = baseLength + bytesToCopy;
  687. RtlZeroMemory( &buffer[nextEntry], baseLength );
  688. switch (fileInformationClass) {
  689. case FileBothDirectoryInformation:
  690. //
  691. // Fill in the short name, if this is a LFN volume.
  692. //
  693. DebugTrace(0, Dbg, "Getting directory both information\n", 0);
  694. if (!DisableAltFileName) {
  695. if ( nwDirInfo->DosDirectoryEntry != 0xFFFF &&
  696. !IsFatNameValid( &nwDirInfo->FileName ) ) {
  697. UNICODE_STRING ShortName;
  698. status = ExchangeWithWait (
  699. pIrpContext,
  700. SynchronousResponseCallback,
  701. "SbDb",
  702. NCP_DIR_FUNCTION, NCP_GET_SHORT_NAME,
  703. Icb->SearchVolume,
  704. nwDirInfo->DosDirectoryEntry,
  705. 0 );
  706. if ( NT_SUCCESS( status ) ) {
  707. dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
  708. //
  709. // Short name is in form 8.3 plus nul terminator.
  710. //
  711. ShortName.MaximumLength = 13 * sizeof(WCHAR) ;
  712. ShortName.Buffer = dirInfo->ShortName;
  713. status = ParseResponse(
  714. pIrpContext,
  715. pIrpContext->rsp,
  716. pIrpContext->ResponseLength,
  717. "N_P",
  718. 15,
  719. &ShortName );
  720. if ( NT_SUCCESS( status ) ) {
  721. dirInfo->ShortNameLength = (CCHAR)ShortName.Length;
  722. }
  723. }
  724. }
  725. }
  726. case FileFullDirectoryInformation:
  727. //
  728. // We don't use EaLength, so fill in nothing here.
  729. //
  730. DebugTrace(0, Dbg, "Getting directory full information\n", 0);
  731. case FileDirectoryInformation:
  732. DebugTrace(0, Dbg, "Getting directory information\n", 0);
  733. //
  734. // The eof indicates the number of instances and
  735. // allocation size is the maximum allowed
  736. //
  737. dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
  738. dirInfo->FileAttributes = nwDirInfo->Attributes;
  739. dirInfo->FileNameLength = bytesToCopy;
  740. dirInfo->EndOfFile.LowPart = nwDirInfo->FileSize;
  741. dirInfo->EndOfFile.HighPart = 0;
  742. dirInfo->AllocationSize = dirInfo->EndOfFile;
  743. dirInfo->CreationTime = NwDateTimeToNtTime( nwDirInfo->CreationDate, nwDirInfo->CreationTime );
  744. dirInfo->LastAccessTime = NwDateTimeToNtTime( nwDirInfo->LastAccessDate, 0 );
  745. dirInfo->LastWriteTime = NwDateTimeToNtTime( nwDirInfo->LastUpdateDate, nwDirInfo->LastUpdateTime );
  746. dirInfo->ChangeTime = dirInfo->LastWriteTime;
  747. dirInfo->FileIndex = 0;
  748. break;
  749. case FileNamesInformation:
  750. DebugTrace(0, Dbg, "Getting names information\n", 0);
  751. namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
  752. namesInfo->FileNameLength = FileNameLength;
  753. namesInfo->FileIndex = 0;
  754. break;
  755. default:
  756. KeBugCheck( RDR_FILE_SYSTEM );
  757. }
  758. // Mapping for Novell's handling of Euro char in file names
  759. {
  760. int index = 0;
  761. WCHAR * pCurrChar = nwDirInfo->FileName.Buffer;
  762. for (index = 0; index < (nwDirInfo->FileName.Length / 2); index++)
  763. {
  764. if (*(pCurrChar + index) == (WCHAR) 0x2560) // Its Novell's mapping of a Euro
  765. *(pCurrChar + index) = (WCHAR) 0x20AC; // set it to Euro
  766. }
  767. }
  768. RtlMoveMemory( &buffer[nextEntry + baseLength],
  769. nwDirInfo->FileName.Buffer,
  770. bytesToCopy );
  771. dump( Dbg, &buffer[nextEntry], lengthAdded);
  772. //
  773. // Setup the previous next entry offset.
  774. //
  775. *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
  776. totalBufferLength = nextEntry + lengthAdded;
  777. //
  778. // Set ourselves up for the next iteration
  779. //
  780. lastEntry = nextEntry;
  781. nextEntry += (ULONG)QuadAlign( lengthAdded );
  782. //
  783. // Check if the last entry didn't completely fit
  784. //
  785. if ( status == STATUS_BUFFER_OVERFLOW ) {
  786. try_return( NOTHING );
  787. }
  788. //
  789. // Check if we are only to return a single entry
  790. //
  791. if (returnSingleEntry) {
  792. try_return( status = STATUS_SUCCESS );
  793. }
  794. } else {
  795. //
  796. // The search response contained an error. If we have
  797. // not yet enumerated directories, do them now. Otherwise,
  798. // we are done searching for files.
  799. //
  800. if ( status == STATUS_UNSUCCESSFUL &&
  801. (!FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) || useCache) ) {
  802. SetFlag( SearchAttributes, NW_ATTRIBUTE_DIRECTORY );
  803. fileIndexLow = (ULONG)-1;
  804. continue;
  805. } else {
  806. //
  807. // Remember that this is a completed search and
  808. // quit the loop.
  809. //
  810. SearchAttributes = 0xFF;
  811. break;
  812. }
  813. }
  814. //
  815. // Here are the rules concerning filling up the buffer:
  816. //
  817. // 1. The Io system garentees that there will always be
  818. // enough room for at least one base record.
  819. //
  820. // 2. If the full first record (including file name) cannot
  821. // fit, as much of the name as possible is copied and
  822. // STATUS_BUFFER_OVERFLOW is returned.
  823. //
  824. // 3. If a subsequent record cannot completely fit into the
  825. // buffer, none of it (as in 0 bytes) is copied, and
  826. // STATUS_SUCCESS is returned. A subsequent query will
  827. // pick up with this record.
  828. //
  829. // Since we cannot rewind a search, we'll guess that the
  830. // next entry is a full length name. If it mightn't fix,
  831. // just bail and re the files we've got.
  832. //
  833. bytesRemainingInBuffer = systemBufferLength - nextEntry;
  834. if ( baseLength + NW_MAX_FILENAME_SIZE > bytesRemainingInBuffer ) {
  835. DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
  836. try_return( status = STATUS_SUCCESS );
  837. }
  838. } // while ( TRUE )
  839. try_exit: NOTHING;
  840. } finally {
  841. //
  842. // At this point we're finished searching for files.
  843. // If the NextEntry is zero then we haven't found anything so we
  844. // will return no more files or no such file.
  845. //
  846. if ( status == STATUS_NO_MORE_FILES ||
  847. status == STATUS_UNSUCCESSFUL ||
  848. status == STATUS_SUCCESS ) {
  849. if (nextEntry == 0) {
  850. if (Icb->ReturnedSomething) {
  851. status = STATUS_NO_MORE_FILES;
  852. } else {
  853. status = STATUS_NO_SUCH_FILE;
  854. }
  855. } else {
  856. Icb->ReturnedSomething = TRUE;
  857. status = STATUS_SUCCESS;
  858. }
  859. }
  860. //
  861. // Indicate how much of the system buffer we have used up.
  862. //
  863. Irp->IoStatus.Information = totalBufferLength;
  864. //
  865. // Remember the last file index, so that we can resume this
  866. // search.
  867. //
  868. //
  869. // Update the last search index read as long as it didn't come from cache.
  870. //
  871. if( lastIndexFromServer ) {
  872. Icb->SearchIndexLow = fileIndexLow;
  873. Icb->SearchIndexHigh = fileIndexHigh;
  874. }
  875. Icb->SearchAttributes = SearchAttributes;
  876. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  877. }
  878. return status;
  879. }
  880. NTSTATUS
  881. GetNextFile(
  882. PIRP_CONTEXT pIrpContext,
  883. PICB Icb,
  884. PULONG FileIndexLow,
  885. PULONG FileIndexHigh,
  886. UCHAR SearchAttributes,
  887. PNW_DIRECTORY_INFO DirInfo
  888. )
  889. /*++
  890. Routine Description:
  891. Get the next file in the directory being searched.
  892. Arguments:
  893. pIrpContext - Supplies the request being processed.
  894. Icb - A pointer to the ICB for the directory to query.
  895. FileIndexLow, FileIndexHigh - On entry, the the index of the
  896. previous directory entry. On exit, the index to the directory
  897. entry returned.
  898. SearchAttributes - Search attributes to use.
  899. DirInfo - Returns information for the directory entry found.
  900. Return Value:
  901. NTSTATUS - The result status.
  902. --*/
  903. {
  904. NTSTATUS status;
  905. PVCB vcb;
  906. static UNICODE_STRING DotFile = { 2, 2, L"." };
  907. static UNICODE_STRING DotDotFile = { 4, 4, L".." };
  908. PAGED_CODE();
  909. DirInfo->DosDirectoryEntry = 0xFFFF;
  910. if ( !Icb->DotReturned ) {
  911. Icb->DotReturned = TRUE;
  912. //
  913. // Return '.' only if it we are not searching in the root directory
  914. // and it matches the search pattern.
  915. //
  916. if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
  917. FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotFile, TRUE, NULL ) ) {
  918. RtlCopyUnicodeString( &DirInfo->FileName, &DotFile );
  919. DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
  920. DirInfo->FileSize = 0;
  921. DirInfo->CreationDate = DEFAULT_DATE;
  922. DirInfo->LastAccessDate = DEFAULT_DATE;
  923. DirInfo->LastUpdateDate = DEFAULT_DATE;
  924. DirInfo->LastUpdateTime = DEFAULT_TIME;
  925. DirInfo->CreationTime = DEFAULT_TIME;
  926. return( STATUS_SUCCESS );
  927. }
  928. }
  929. if ( !Icb->DotDotReturned ) {
  930. Icb->DotDotReturned = TRUE;
  931. //
  932. // Return '..' only if it we are not searching in the root directory
  933. // and it matches the search pattern.
  934. //
  935. if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
  936. FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotDotFile, TRUE, NULL ) ) {
  937. RtlCopyUnicodeString( &DirInfo->FileName, &DotDotFile );
  938. DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
  939. DirInfo->FileSize = 0;
  940. DirInfo->CreationDate = DEFAULT_DATE;
  941. DirInfo->LastAccessDate = DEFAULT_DATE;
  942. DirInfo->LastUpdateDate = DEFAULT_DATE;
  943. DirInfo->LastUpdateTime = DEFAULT_TIME;
  944. DirInfo->CreationTime = DEFAULT_TIME;
  945. return( STATUS_SUCCESS );
  946. }
  947. }
  948. vcb = Icb->SuperType.Fcb->Vcb;
  949. if ( Icb->ShortNameSearch ) {
  950. status = ExchangeWithWait(
  951. pIrpContext,
  952. SynchronousResponseCallback,
  953. "Fbwwbp",
  954. NCP_SEARCH_CONTINUE,
  955. Icb->SearchVolume,
  956. Icb->SearchHandle,
  957. *(PUSHORT)FileIndexLow,
  958. SearchAttributes,
  959. Icb->NwQueryTemplate.Buffer
  960. );
  961. if ( !NT_SUCCESS( status )) {
  962. return status;
  963. }
  964. *FileIndexLow = 0;
  965. *FileIndexHigh = 0;
  966. if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
  967. status = ParseResponse(
  968. pIrpContext,
  969. pIrpContext->rsp,
  970. pIrpContext->ResponseLength,
  971. "Nw=Rb-ww",
  972. FileIndexLow,
  973. &DirInfo->FileName, 14,
  974. &DirInfo->Attributes,
  975. &DirInfo->CreationDate,
  976. &DirInfo->CreationTime
  977. );
  978. #if 0
  979. if ( DirInfo->CreationDate == 0 && DirInfo->CreationTime == 0 ) {
  980. DirInfo->CreationDate = DEFAULT_DATE;
  981. DirInfo->CreationTime = DEFAULT_TIME;
  982. }
  983. #endif
  984. DirInfo->FileSize = 0;
  985. DirInfo->LastAccessDate = DirInfo->CreationDate;
  986. DirInfo->LastUpdateDate = DirInfo->CreationDate;
  987. DirInfo->LastUpdateTime = DirInfo->CreationTime;
  988. } else {
  989. status = ParseResponse(
  990. pIrpContext,
  991. pIrpContext->rsp,
  992. pIrpContext->ResponseLength,
  993. "Nw=Rb-dwwww",
  994. FileIndexLow,
  995. &DirInfo->FileName, 14,
  996. &DirInfo->Attributes,
  997. &DirInfo->FileSize,
  998. &DirInfo->CreationDate,
  999. &DirInfo->LastAccessDate,
  1000. &DirInfo->LastUpdateDate,
  1001. &DirInfo->LastUpdateTime
  1002. );
  1003. DirInfo->CreationTime = DEFAULT_TIME;
  1004. }
  1005. } else {
  1006. status = ExchangeWithWait (
  1007. pIrpContext,
  1008. SynchronousResponseCallback,
  1009. "LbbWDbDDp",
  1010. NCP_LFN_SEARCH_CONTINUE,
  1011. vcb->Specific.Disk.LongNameSpace,
  1012. 0, // Data stream
  1013. SearchAttributes & SEARCH_ALL_DIRECTORIES,
  1014. LFN_FLAG_INFO_ATTRIBUTES |
  1015. LFN_FLAG_INFO_FILE_SIZE |
  1016. LFN_FLAG_INFO_MODIFY_TIME |
  1017. LFN_FLAG_INFO_CREATION_TIME |
  1018. LFN_FLAG_INFO_DIR_INFO |
  1019. LFN_FLAG_INFO_NAME,
  1020. vcb->Specific.Disk.VolumeNumber,
  1021. *FileIndexHigh,
  1022. *FileIndexLow,
  1023. Icb->NwQueryTemplate.Buffer );
  1024. if ( NT_SUCCESS( status ) ) {
  1025. status = ParseResponse(
  1026. pIrpContext,
  1027. pIrpContext->rsp,
  1028. pIrpContext->ResponseLength,
  1029. "N-ee_e_e_xx_xx_x_e_P",
  1030. FileIndexHigh,
  1031. FileIndexLow,
  1032. 5,
  1033. &DirInfo->Attributes,
  1034. 2,
  1035. &DirInfo->FileSize,
  1036. 6,
  1037. &DirInfo->CreationTime,
  1038. &DirInfo->CreationDate,
  1039. 4,
  1040. &DirInfo->LastUpdateTime,
  1041. &DirInfo->LastUpdateDate,
  1042. 4,
  1043. &DirInfo->LastAccessDate,
  1044. 14,
  1045. &DirInfo->DosDirectoryEntry,
  1046. 20,
  1047. &DirInfo->FileName );
  1048. }
  1049. if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
  1050. DirInfo->FileSize = 0;
  1051. }
  1052. }
  1053. if ( DirInfo->Attributes == 0 ) {
  1054. DirInfo->Attributes = FILE_ATTRIBUTE_NORMAL;
  1055. }
  1056. return status;
  1057. }
  1058. NTSTATUS
  1059. NtSearchMaskToNw(
  1060. IN PUNICODE_STRING UcSearchMask,
  1061. IN OUT POEM_STRING OemSearchMask,
  1062. IN PICB Icb,
  1063. IN BOOLEAN ShortNameSearch
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. This routine maps a netware path name to the correct netware format.
  1068. Arguments:
  1069. UcSearchMask - The search mask in NT format.
  1070. OemSearchMask - The search mask in Netware format.
  1071. Icb - The ICB of the directory in which we are searching.
  1072. ShortNameSearch - If TRUE, always do a short name search.
  1073. Return Value:
  1074. NTSTATUS - The result status.
  1075. --*/
  1076. {
  1077. USHORT i;
  1078. NTSTATUS status;
  1079. PAGED_CODE();
  1080. //
  1081. // Use a short name search if the volume does not support long names.
  1082. // or this is a short name ICB, and we are doing a short name, non
  1083. // wild-card search.
  1084. //
  1085. if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
  1086. ShortNameSearch ||
  1087. ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
  1088. !FsRtlDoesNameContainWildCards( UcSearchMask ) &&
  1089. IsFatNameValid( UcSearchMask ) ) ) {
  1090. Icb->ShortNameSearch = TRUE;
  1091. // Mapping for Novell's handling of Euro char in file names
  1092. {
  1093. int index = 0;
  1094. WCHAR * pCurrChar = UcSearchMask->Buffer;
  1095. for (index = 0; index < (UcSearchMask->Length / 2); index++)
  1096. {
  1097. if (*(pCurrChar + index) == (WCHAR) 0x20AC) // Its a Euro
  1098. *(pCurrChar + index) = (WCHAR) 0x2560; // set it to Novell's mapping for Euro
  1099. }
  1100. }
  1101. //
  1102. // Allocate space for and initialize the query templates.
  1103. //
  1104. status = RtlUpcaseUnicodeStringToOemString(
  1105. OemSearchMask,
  1106. UcSearchMask,
  1107. TRUE );
  1108. if ( !NT_SUCCESS( status ) ) {
  1109. return( status );
  1110. }
  1111. //
  1112. // Special case. Map '*.*' to '*'.
  1113. //
  1114. if ( OemSearchMask->Length == 3 &&
  1115. RtlCompareMemory( OemSearchMask->Buffer, "*.*", 3 ) == 3 ) {
  1116. OemSearchMask->Length = 1;
  1117. OemSearchMask->Buffer[1] = '\0';
  1118. } else {
  1119. for ( i = 0; i < OemSearchMask->Length ; i++ ) {
  1120. //
  1121. // In fact Novell server seems to convert all 0xBF, 0xAA, 0xAE
  1122. // even if they are DBCS lead or trail byte.
  1123. // We can't single out DBCS case in the conversion.
  1124. //
  1125. if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) {
  1126. if((UCHAR)(OemSearchMask->Buffer[i]) == 0xBF ) {
  1127. OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
  1128. }else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAE ) {
  1129. OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
  1130. }else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAA ) {
  1131. OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
  1132. }
  1133. i++;
  1134. if((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C ) {
  1135. //
  1136. // The trailbyte is 0x5C, replace it with 0x13
  1137. //
  1138. OemSearchMask->Buffer[i] = (UCHAR)( 0x13 );
  1139. }
  1140. //
  1141. // Continue to check other conversions for trailbyte.
  1142. //
  1143. }
  1144. // Single byte character that may need modification.
  1145. switch ( (UCHAR)(OemSearchMask->Buffer[i]) ) {
  1146. case ANSI_DOS_STAR:
  1147. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '*' );
  1148. break;
  1149. case ANSI_DOS_QM:
  1150. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '?' );
  1151. break;
  1152. case ANSI_DOS_DOT:
  1153. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '.' );
  1154. break;
  1155. //
  1156. // Netware Japanese version The following character is
  1157. // replaced with another one if the string is for File
  1158. // Name only when sendding from Client to Server.
  1159. //
  1160. // SO U+0xFF7F SJIS+0xBF -> 0x10
  1161. // SMALL_YO U+0xFF6E SJIS+0xAE -> 0x11
  1162. // SMALL_E U+0xFF64 SJIS+0xAA -> 0x12
  1163. //
  1164. // The reason is unknown, Should ask Novell Japan.
  1165. //
  1166. // See Also exchange.c
  1167. case 0xBF: // ANSI_DOS_KATAKANA_SO:
  1168. if (Japan) {
  1169. OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
  1170. }
  1171. break;
  1172. case 0xAE: // ANSI_DOS_KATAKANA_SMALL_YO:
  1173. if (Japan) {
  1174. OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
  1175. }
  1176. break;
  1177. case 0xAA: // ANSI_DOS_KATAKANA_SMALL_E:
  1178. if (Japan) {
  1179. OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
  1180. }
  1181. break;
  1182. }
  1183. }
  1184. }
  1185. } else {
  1186. USHORT size;
  1187. PCHAR buffer;
  1188. UNICODE_STRING src;
  1189. OEM_STRING dest;
  1190. Icb->ShortNameSearch = FALSE;
  1191. //
  1192. // Allocate space for and initialize the query templates.
  1193. // We allocate an extra byte to account for the null terminator.
  1194. //
  1195. #ifndef QFE_BUILD
  1196. buffer = ExAllocatePoolWithTag( PagedPool,
  1197. (UcSearchMask->Length) + 1,
  1198. 'scwn' );
  1199. #else
  1200. buffer = ExAllocatePool( PagedPool,
  1201. (UcSearchMask->Length) + 1 );
  1202. #endif
  1203. if ( buffer == NULL ) {
  1204. return( STATUS_INSUFFICIENT_RESOURCES );
  1205. }
  1206. OemSearchMask->Buffer = buffer;
  1207. //
  1208. // Special case. Map '????????.???' to '*'.
  1209. //
  1210. if ( UcSearchMask->Length == 24 &&
  1211. RtlCompareMemory( UcSearchMask->Buffer, L">>>>>>>>\">>>", 24 ) == 24 ) {
  1212. OemSearchMask->Length = 3;
  1213. OemSearchMask->Buffer[0] = (UCHAR)0xFF;
  1214. OemSearchMask->Buffer[1] = '*';
  1215. OemSearchMask->Buffer[2] = '\0';
  1216. return STATUS_SUCCESS;
  1217. }
  1218. //
  1219. // Now convert the string, character by character
  1220. //
  1221. src.Buffer = UcSearchMask->Buffer;
  1222. src.Length = 2;
  1223. dest.Buffer = buffer;
  1224. dest.MaximumLength = UcSearchMask->Length;
  1225. size = UcSearchMask->Length / 2;
  1226. for ( i = 0; i < size ; i++ ) {
  1227. switch ( *src.Buffer ) {
  1228. case L'*':
  1229. case L'?':
  1230. *dest.Buffer++ = LFN_META_CHARACTER;
  1231. *dest.Buffer++ = (UCHAR)*src.Buffer++;
  1232. break;
  1233. case L'.':
  1234. *dest.Buffer++ = (UCHAR)*src.Buffer++;
  1235. break;
  1236. case DOS_DOT:
  1237. *dest.Buffer++ = LFN_META_CHARACTER;
  1238. *dest.Buffer++ = (UCHAR)( 0x80 | '.' );
  1239. src.Buffer++;
  1240. break;
  1241. case DOS_STAR:
  1242. *dest.Buffer++ = LFN_META_CHARACTER;
  1243. *dest.Buffer++ = (UCHAR)( 0x80 | '*' );
  1244. src.Buffer++;
  1245. break;
  1246. case DOS_QM:
  1247. *dest.Buffer++ = LFN_META_CHARACTER;
  1248. *dest.Buffer++ = (UCHAR)( 0x80 | '?' );
  1249. src.Buffer++;
  1250. break;
  1251. case 0x20AC: // Euro
  1252. *src.Buffer = (WCHAR)0x2560; // change it to Novell's mapping
  1253. // intentional fall-through to get it mapped to OEM
  1254. default:
  1255. RtlUnicodeStringToCountedOemString( &dest, &src, FALSE );
  1256. if( FsRtlIsLeadDbcsCharacter( dest.Buffer[0] ) ) {
  1257. dest.Buffer++;
  1258. }
  1259. dest.Buffer++;
  1260. src.Buffer++;
  1261. }
  1262. }
  1263. *dest.Buffer = '\0';
  1264. OemSearchMask->Length = (USHORT)( dest.Buffer - buffer );
  1265. }
  1266. return STATUS_SUCCESS;
  1267. }
  1268. #if 0
  1269. VOID
  1270. NwCancelFindNotify (
  1271. IN PDEVICE_OBJECT DeviceObject,
  1272. IN PIRP Irp
  1273. )
  1274. /*++
  1275. Routine Description:
  1276. This routine implements the cancel function for an find notify IRP.
  1277. Arguments:
  1278. DeviceObject - ignored
  1279. Irp - Supplies the Irp being cancelled.
  1280. Return Value:
  1281. None.
  1282. --*/
  1283. {
  1284. PLIST_ENTRY listEntry;
  1285. UNREFERENCED_PARAMETER( DeviceObject );
  1286. //
  1287. // We now need to void the cancel routine and release the io cancel
  1288. // spin-lock.
  1289. //
  1290. IoSetCancelRoutine( Irp, NULL );
  1291. IoReleaseCancelSpinLock( Irp->CancelIrql );
  1292. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  1293. for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
  1294. PIRP_CONTEXT IrpContext;
  1295. IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
  1296. if ( IrpContext->pOriginalIrp == Irp ) {
  1297. RemoveEntryList( &IrpContext->NextRequest );
  1298. NwCompleteRequest( IrpContext, STATUS_CANCELLED );
  1299. break;
  1300. }
  1301. }
  1302. NwReleaseRcb( &NwRcb );
  1303. }
  1304. #endif