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.

1893 lines
55 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 isSystem = FALSE;
  274. BOOLEAN lastIndexFromServer = FALSE;
  275. PNW_DIRECTORY_INFO dirCache;
  276. PLIST_ENTRY entry;
  277. ULONG i;
  278. PAGED_CODE();
  279. //
  280. // Get the current stack location.
  281. //
  282. Irp = pIrpContext->pOriginalIrp;
  283. irpSp = IoGetCurrentIrpStackLocation( Irp );
  284. vcb = Icb->SuperType.Fcb->Vcb;
  285. DebugTrace(+1, Dbg, "NwQueryDirectory\n", 0 );
  286. DebugTrace( 0, Dbg, "Icb = %08lx\n", (ULONG_PTR)Icb);
  287. DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG_PTR)Irp->AssociatedIrp.SystemBuffer);
  288. DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
  289. DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG_PTR)irpSp->Parameters.QueryDirectory.FileName);
  290. DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
  291. DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
  292. DebugTrace( 0, Dbg, "RestartScan = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN));
  293. DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
  294. DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
  295. //
  296. // Make local copies of the input parameters.
  297. //
  298. systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
  299. restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN);
  300. indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
  301. returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
  302. if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
  303. fileIndexLow = 0;
  304. fileIndexHigh = 0;
  305. } else {
  306. //
  307. // Tell the gateway we do support resume from index so long
  308. // as the index returned is the same as the last file we
  309. // returned. Otherwise the SMB server does a brute force rewind
  310. // on each find next.
  311. //
  312. isSystem = TRUE;
  313. if( indexSpecified ) {
  314. fileIndexLow = irpSp->Parameters.QueryDirectory.FileIndex;
  315. } else {
  316. fileIndexLow = 0;
  317. }
  318. //
  319. // See if we can avoid a rewind.
  320. //
  321. if( fileIndexLow != 0 ) {
  322. if( fileIndexLow == Icb->SearchIndexLow ) {
  323. fileIndexHigh = Icb->SearchIndexHigh;
  324. canContinue = TRUE;
  325. } else {
  326. if( Icb->CacheHint ) {
  327. entry = Icb->CacheHint;
  328. searchRetry = TRUE;
  329. } else {
  330. entry = Icb->DirCache.Flink;
  331. searchRetry = FALSE;
  332. }
  333. do {
  334. if( entry == &(Icb->DirCache) ) {
  335. entry = Icb->DirCache.Flink;
  336. searchRetry = FALSE;
  337. }
  338. while( entry != &(Icb->DirCache) ) {
  339. dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  340. if( dirCache->FileIndexLow == fileIndexLow ) {
  341. canContinue = TRUE;
  342. useCache = TRUE;
  343. Icb->CacheHint = entry->Flink;
  344. searchRetry = FALSE;
  345. break;
  346. }
  347. entry = entry->Flink;
  348. }
  349. } while( searchRetry );
  350. }
  351. }
  352. }
  353. fileInformationClass =
  354. irpSp->Parameters.QueryDirectory.FileInformationClass;
  355. if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
  356. searchMask = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName;
  357. } else {
  358. searchMask.Length = 0;
  359. searchMask.Buffer = NULL;
  360. }
  361. buffer = Irp->UserBuffer;
  362. DebugTrace(0, Dbg, "Users Buffer -> %08lx\n", buffer);
  363. //
  364. // It is ok to attempt a reconnect if this request fails with a
  365. // connection error.
  366. //
  367. SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  368. //
  369. // Check if the ICB already has a query template attached. If it
  370. // does not already have one then we either use the string we are
  371. // given or we attach our own containing "*"
  372. //
  373. if ( Icb->NwQueryTemplate.Buffer == NULL ) {
  374. //
  375. // This is our first time calling query directory so we need
  376. // to either set the query template to the user specified string
  377. // or to "*.*".
  378. //
  379. if ( searchMask.Buffer == NULL ) {
  380. DebugTrace(0, Dbg, "Set template to *", 0);
  381. searchMask.Length = sizeof( star ) - sizeof(WCHAR);
  382. searchMask.Buffer = star;
  383. }
  384. DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG_PTR)&searchMask);
  385. //
  386. // Map the NT search names to NCP. Note that this must be
  387. // done after the Unicode to OEM translation.
  388. //
  389. searchRetry = FALSE;
  390. do {
  391. status = NtSearchMaskToNw(
  392. &searchMask,
  393. &Icb->NwQueryTemplate,
  394. Icb,
  395. searchRetry );
  396. if ( !NT_SUCCESS( status ) ) {
  397. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  398. return( status );
  399. }
  400. Icb->UQueryTemplate.Buffer = ALLOCATE_POOL( PagedPool, searchMask.Length );
  401. if (Icb->UQueryTemplate.Buffer == NULL ) {
  402. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES );
  403. return( STATUS_INSUFFICIENT_RESOURCES );
  404. }
  405. Icb->UQueryTemplate.MaximumLength = searchMask.Length;
  406. RtlCopyUnicodeString( &Icb->UQueryTemplate, &searchMask );
  407. //
  408. // Now send a Search Initialize NCP.
  409. //
  410. // Do a short search if the server doesn't support long names,
  411. // or this is a short-name non-wild card search
  412. //
  413. if ( !Icb->ShortNameSearch ) {
  414. status = ExchangeWithWait(
  415. pIrpContext,
  416. SynchronousResponseCallback,
  417. "Lbb-DbC",
  418. NCP_LFN_SEARCH_INITIATE,
  419. vcb->Specific.Disk.LongNameSpace,
  420. vcb->Specific.Disk.VolumeNumber,
  421. vcb->Specific.Disk.Handle,
  422. LFN_FLAG_SHORT_DIRECTORY,
  423. &Icb->SuperType.Fcb->RelativeFileName );
  424. if ( NT_SUCCESS( status ) ) {
  425. status = ParseResponse(
  426. pIrpContext,
  427. pIrpContext->rsp,
  428. pIrpContext->ResponseLength,
  429. "Nbee",
  430. &Icb->SearchVolume,
  431. &Icb->SearchIndexHigh,
  432. &Icb->SearchIndexLow );
  433. }
  434. } else {
  435. status = ExchangeWithWait(
  436. pIrpContext,
  437. SynchronousResponseCallback,
  438. "FbJ",
  439. NCP_SEARCH_INITIATE,
  440. vcb->Specific.Disk.Handle,
  441. &Icb->SuperType.Fcb->RelativeFileName );
  442. if ( NT_SUCCESS( status ) ) {
  443. status = ParseResponse(
  444. pIrpContext,
  445. pIrpContext->rsp,
  446. pIrpContext->ResponseLength,
  447. "Nbww-",
  448. &Icb->SearchVolume,
  449. &Icb->SearchHandle,
  450. &Icb->SearchIndexLow );
  451. }
  452. }
  453. //
  454. // If we couldn't find the search path, and we did a long
  455. // name search initiate, try again with a short name.
  456. //
  457. if ( status == STATUS_OBJECT_PATH_NOT_FOUND &&
  458. !Icb->ShortNameSearch ) {
  459. searchRetry = TRUE;
  460. if ( Icb->UQueryTemplate.Buffer != NULL ) {
  461. FREE_POOL( Icb->UQueryTemplate.Buffer );
  462. }
  463. RtlFreeOemString ( &Icb->NwQueryTemplate );
  464. } else {
  465. searchRetry = FALSE;
  466. }
  467. } while ( searchRetry );
  468. if ( !NT_SUCCESS( status ) ) {
  469. if (status == STATUS_UNSUCCESSFUL) {
  470. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_SUCH_FILE);
  471. return( STATUS_NO_SUCH_FILE );
  472. }
  473. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  474. return( status );
  475. }
  476. //
  477. // Since we are doing a search we will need to send an End Of Job
  478. // for this PID.
  479. //
  480. NwSetEndOfJobRequired(pIrpContext->pNpScb, Icb->Pid );
  481. fileIndexLow = Icb->SearchIndexLow;
  482. fileIndexHigh = Icb->SearchIndexHigh;
  483. //
  484. // We can't ask for both files and directories, so first ask for
  485. // files, then ask for directories.
  486. //
  487. SearchAttributes = NW_ATTRIBUTE_SYSTEM |
  488. NW_ATTRIBUTE_HIDDEN |
  489. NW_ATTRIBUTE_READ_ONLY;
  490. //
  491. // If there are no wildcards in the search mask, then setup to
  492. // not generate the . and .. entries.
  493. //
  494. if ( !FsRtlDoesNameContainWildCards( &Icb->UQueryTemplate ) ) {
  495. Icb->DotReturned = TRUE;
  496. Icb->DotDotReturned = TRUE;
  497. } else {
  498. Icb->DotReturned = FALSE;
  499. Icb->DotDotReturned = FALSE;
  500. }
  501. } else {
  502. //
  503. // Check if we were given an index to start with or if we need to
  504. // restart the scan or if we should use the index that was saved in
  505. // the ICB.
  506. //
  507. if (restartScan) {
  508. //
  509. // Make sure we don't use any cached directory info if this is
  510. // for a system account.
  511. //
  512. useCache = FALSE;
  513. if( isSystem ) {
  514. NwFreeDirCacheForIcb( Icb );
  515. }
  516. fileIndexLow = (ULONG)-1;
  517. fileIndexHigh = Icb->SearchIndexHigh;
  518. //
  519. // Send a Search Initialize NCP. The server often times out search
  520. // handles and if this one has been sitting at the end of the
  521. // directory then its likely we would get no files at all!
  522. //
  523. // Do a short search if the server doesn't support long names,
  524. // or this is a short-name non-wild card search
  525. //
  526. if ( !Icb->ShortNameSearch ) {
  527. status = ExchangeWithWait(
  528. pIrpContext,
  529. SynchronousResponseCallback,
  530. "Lbb-DbC",
  531. NCP_LFN_SEARCH_INITIATE,
  532. vcb->Specific.Disk.LongNameSpace,
  533. vcb->Specific.Disk.VolumeNumber,
  534. vcb->Specific.Disk.Handle,
  535. LFN_FLAG_SHORT_DIRECTORY,
  536. &Icb->SuperType.Fcb->RelativeFileName );
  537. if ( NT_SUCCESS( status ) ) {
  538. status = ParseResponse(
  539. pIrpContext,
  540. pIrpContext->rsp,
  541. pIrpContext->ResponseLength,
  542. "Nbee",
  543. &Icb->SearchVolume,
  544. &Icb->SearchIndexHigh,
  545. &Icb->SearchIndexLow );
  546. }
  547. } else {
  548. status = ExchangeWithWait(
  549. pIrpContext,
  550. SynchronousResponseCallback,
  551. "FbJ",
  552. NCP_SEARCH_INITIATE,
  553. vcb->Specific.Disk.Handle,
  554. &Icb->SuperType.Fcb->RelativeFileName );
  555. if ( NT_SUCCESS( status ) ) {
  556. status = ParseResponse(
  557. pIrpContext,
  558. pIrpContext->rsp,
  559. pIrpContext->ResponseLength,
  560. "Nbww-",
  561. &Icb->SearchVolume,
  562. &Icb->SearchHandle,
  563. &Icb->SearchIndexLow );
  564. }
  565. }
  566. Icb->ReturnedSomething = FALSE;
  567. //
  568. // We can't ask for both files and directories, so first ask for
  569. // files, then ask for directories.
  570. //
  571. SearchAttributes = NW_ATTRIBUTE_SYSTEM |
  572. NW_ATTRIBUTE_HIDDEN |
  573. NW_ATTRIBUTE_READ_ONLY;
  574. Icb->SearchAttributes = SearchAttributes;
  575. Icb->DotReturned = FALSE;
  576. Icb->DotDotReturned = FALSE;
  577. } else if ((!indexSpecified) ||
  578. (canContinue) ) {
  579. //
  580. // Continue from the one of the last filenames. If an index is specified then its
  581. // only allowed for the gateway (and other system services).
  582. //
  583. SearchAttributes = Icb->SearchAttributes;
  584. if( !indexSpecified ) {
  585. //
  586. // We need to check to see if this is the sytem or not. If it
  587. // is, we need to continue from the last entry returned which may
  588. // be from the cache.
  589. //
  590. if( isSystem && Icb->CacheHint ) {
  591. entry = Icb->CacheHint;
  592. dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  593. useCache = TRUE;
  594. } else {
  595. useCache = FALSE;
  596. fileIndexLow = Icb->SearchIndexLow;
  597. fileIndexHigh = Icb->SearchIndexHigh;
  598. }
  599. }
  600. if ( SearchAttributes == 0xFF && fileIndexLow == Icb->SearchIndexLow ) {
  601. //
  602. // This is a completed search.
  603. //
  604. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_MORE_FILES);
  605. return( STATUS_NO_MORE_FILES );
  606. }
  607. } else {
  608. //
  609. // SVR to avoid rescanning from end of dir all
  610. // the way through the directory again.
  611. //
  612. if ((Icb->SearchIndexLow == -1) &&
  613. (Icb->LastSearchIndexLow == fileIndexLow) &&
  614. (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart) &&
  615. (Icb->SearchAttributes == 0xFF )) {
  616. DebugTrace(-1, Dbg, "NwQueryDirectory SVR exit-> %08lx\n", STATUS_NO_MORE_FILES);
  617. return( STATUS_NO_MORE_FILES );
  618. }
  619. // SVR end
  620. //
  621. // Someone's trying to do a resume from key. The netware
  622. // server doesn't support this, so neither do we.
  623. //
  624. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NOT_IMPLEMENTED);
  625. return( STATUS_NOT_IMPLEMENTED );
  626. }
  627. }
  628. //
  629. // Now we are committed to completing the Irp, we do that in
  630. // the finally clause of the following try.
  631. //
  632. try {
  633. ULONG baseLength;
  634. ULONG lengthAdded;
  635. PNW_DIRECTORY_INFO nwDirInfo;
  636. ULONG FileNameLength;
  637. ULONG entriesToCreate;
  638. lastEntry = 0;
  639. nextEntry = 0;
  640. switch (fileInformationClass) {
  641. case FileDirectoryInformation:
  642. baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
  643. break;
  644. case FileFullDirectoryInformation:
  645. baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
  646. break;
  647. case FileNamesInformation:
  648. baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] );
  649. break;
  650. case FileBothDirectoryInformation:
  651. baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
  652. break;
  653. default:
  654. try_return( status = STATUS_INVALID_INFO_CLASS );
  655. }
  656. //
  657. // It is not ok to attempt a reconnect if this request fails with a
  658. // connection error, since our search handle would be invalid.
  659. //
  660. ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  661. //
  662. // See if we have a dir cache. If not, create one.
  663. //
  664. if( !Icb->DirCacheBuffer ) {
  665. if( isSystem ) {
  666. entriesToCreate = DirCacheEntries;
  667. } else {
  668. entriesToCreate = 1;
  669. }
  670. Icb->DirCacheBuffer = ALLOCATE_POOL ( PagedPool, (sizeof(NW_DIRECTORY_INFO) * entriesToCreate) );
  671. if( !Icb->DirCacheBuffer ) {
  672. try_return( status = STATUS_NO_MEMORY );
  673. }
  674. RtlZeroMemory( Icb->DirCacheBuffer, sizeof(NW_DIRECTORY_INFO) * entriesToCreate );
  675. dirCache = (PNW_DIRECTORY_INFO)Icb->DirCacheBuffer;
  676. for( i = 0; i < entriesToCreate; i++ ) {
  677. InsertTailList( &(Icb->DirCache), &(dirCache->ListEntry) );
  678. dirCache++;
  679. }
  680. }
  681. while ( TRUE ) {
  682. ULONG bytesToCopy;
  683. ULONG bytesRemainingInBuffer;
  684. DebugTrace(0, Dbg, "Top of Loop\n", 0);
  685. DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", fileIndexLow);
  686. DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
  687. DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
  688. if( useCache ) {
  689. //
  690. // We need to use the data out of the entry we found in the cache.
  691. // dirCache points to the entry that matches, and the request was not
  692. // for the last file we read, so the entry after dirCache is the one
  693. // we want.
  694. //
  695. DebugTrace(0, Dbg, "Using cache\n", 0);
  696. entry = dirCache->ListEntry.Flink;
  697. dirCache = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  698. nwDirInfo = dirCache;
  699. fileIndexLow = nwDirInfo->FileIndexLow;
  700. fileIndexHigh = nwDirInfo->FileIndexHigh;
  701. status = nwDirInfo->Status;
  702. //
  703. // Check to see if we should still keep using the cache or not.
  704. //
  705. if( entry->Flink == &(Icb->DirCache) ) {
  706. //
  707. // This is the last entry. We need to stop using the cache.
  708. //
  709. useCache = FALSE;
  710. Icb->CacheHint = NULL;
  711. } else {
  712. Icb->CacheHint = entry;
  713. }
  714. } else {
  715. //
  716. // Pull an entry from the dir cache.
  717. //
  718. entry = RemoveHeadList( &(Icb->DirCache) );
  719. nwDirInfo = CONTAINING_RECORD( entry, NW_DIRECTORY_INFO, ListEntry );
  720. nwDirInfo->FileName.Buffer = nwDirInfo->FileNameBuffer;
  721. nwDirInfo->FileName.MaximumLength = NW_MAX_FILENAME_SIZE;
  722. status = GetNextFile(
  723. pIrpContext,
  724. Icb,
  725. &fileIndexLow,
  726. &fileIndexHigh,
  727. SearchAttributes,
  728. nwDirInfo );
  729. //
  730. // Store the return and the file index number,
  731. // and then put this entry in the cache.
  732. //
  733. nwDirInfo->FileIndexLow = fileIndexLow;
  734. nwDirInfo->FileIndexHigh = fileIndexHigh;
  735. nwDirInfo->Status = status;
  736. InsertTailList( &(Icb->DirCache), &(nwDirInfo->ListEntry) );
  737. lastIndexFromServer = TRUE;
  738. // SVR to avoid rescanning from end of dir all
  739. if (fileIndexLow != -1) {
  740. Icb->LastSearchIndexLow = fileIndexLow;
  741. }
  742. // SVR end
  743. }
  744. if ( NT_SUCCESS( status ) ) {
  745. DebugTrace(0, Dbg, "DirFileName = %wZ\n", &nwDirInfo->FileName);
  746. DebugTrace(0, Dbg, "FileIndexLow = %08lx\n", fileIndexLow);
  747. FileNameLength = nwDirInfo->FileName.Length;
  748. bytesRemainingInBuffer = systemBufferLength - nextEntry;
  749. ASSERT( bytesRemainingInBuffer >= baseLength );
  750. if (IsTerminalServer() && (LONG)NW_MAX_FILENAME_SIZE < FileNameLength )
  751. try_return( status = STATUS_BUFFER_OVERFLOW );
  752. //
  753. // See how much of the name we will be able to copy into
  754. // the system buffer. This also dictates our return
  755. // value.
  756. //
  757. if ( baseLength + FileNameLength <= bytesRemainingInBuffer ) {
  758. bytesToCopy = FileNameLength;
  759. status = STATUS_SUCCESS;
  760. } else {
  761. if (IsTerminalServer()) {
  762. try_return( status = STATUS_BUFFER_OVERFLOW );
  763. }
  764. bytesToCopy = bytesRemainingInBuffer - baseLength;
  765. status = STATUS_BUFFER_OVERFLOW;
  766. }
  767. //
  768. // Note how much of buffer we are consuming and zero
  769. // the base part of the structure.
  770. //
  771. lengthAdded = baseLength + bytesToCopy;
  772. RtlZeroMemory( &buffer[nextEntry], baseLength );
  773. switch (fileInformationClass) {
  774. case FileBothDirectoryInformation:
  775. //
  776. // Fill in the short name, if this is a LFN volume.
  777. //
  778. DebugTrace(0, Dbg, "Getting directory both information\n", 0);
  779. if (!DisableAltFileName) {
  780. if ( nwDirInfo->DosDirectoryEntry != 0xFFFF &&
  781. !IsFatNameValid( &nwDirInfo->FileName ) ) {
  782. UNICODE_STRING ShortName;
  783. status = ExchangeWithWait (
  784. pIrpContext,
  785. SynchronousResponseCallback,
  786. "SbDb",
  787. NCP_DIR_FUNCTION, NCP_GET_SHORT_NAME,
  788. Icb->SearchVolume,
  789. nwDirInfo->DosDirectoryEntry,
  790. 0 );
  791. if ( NT_SUCCESS( status ) ) {
  792. dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
  793. //
  794. // Short name is in form 8.3 plus nul terminator.
  795. //
  796. ShortName.MaximumLength = 13 * sizeof(WCHAR) ;
  797. ShortName.Buffer = dirInfo->ShortName;
  798. status = ParseResponse(
  799. pIrpContext,
  800. pIrpContext->rsp,
  801. pIrpContext->ResponseLength,
  802. "N_P",
  803. 15,
  804. &ShortName );
  805. if ( NT_SUCCESS( status ) ) {
  806. dirInfo->ShortNameLength = (CCHAR)ShortName.Length;
  807. }
  808. }
  809. }
  810. }
  811. case FileFullDirectoryInformation:
  812. //
  813. // We don't use EaLength, so fill in nothing here.
  814. //
  815. DebugTrace(0, Dbg, "Getting directory full information\n", 0);
  816. case FileDirectoryInformation:
  817. DebugTrace(0, Dbg, "Getting directory information\n", 0);
  818. //
  819. // The eof indicates the number of instances and
  820. // allocation size is the maximum allowed
  821. //
  822. dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
  823. dirInfo->FileAttributes = nwDirInfo->Attributes;
  824. dirInfo->FileNameLength = bytesToCopy;
  825. dirInfo->EndOfFile.LowPart = nwDirInfo->FileSize;
  826. dirInfo->EndOfFile.HighPart = 0;
  827. dirInfo->AllocationSize = dirInfo->EndOfFile;
  828. dirInfo->CreationTime = NwDateTimeToNtTime( nwDirInfo->CreationDate, nwDirInfo->CreationTime );
  829. dirInfo->LastAccessTime = NwDateTimeToNtTime( nwDirInfo->LastAccessDate, 0 );
  830. dirInfo->LastWriteTime = NwDateTimeToNtTime( nwDirInfo->LastUpdateDate, nwDirInfo->LastUpdateTime );
  831. dirInfo->ChangeTime = dirInfo->LastWriteTime;
  832. if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
  833. dirInfo->FileIndex = 0;
  834. } else {
  835. dirInfo->FileIndex = fileIndexLow;
  836. }
  837. break;
  838. case FileNamesInformation:
  839. DebugTrace(0, Dbg, "Getting names information\n", 0);
  840. namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
  841. namesInfo->FileNameLength = FileNameLength;
  842. if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
  843. namesInfo->FileIndex = 0;
  844. } else {
  845. namesInfo->FileIndex = fileIndexLow;
  846. }
  847. break;
  848. default:
  849. KeBugCheck( RDR_FILE_SYSTEM );
  850. }
  851. // Mapping for Novell's handling of Euro char in file names
  852. {
  853. int i = 0;
  854. WCHAR * pCurrChar = nwDirInfo->FileName.Buffer;
  855. for (i = 0; i < (nwDirInfo->FileName.Length / 2); i++)
  856. {
  857. if (*(pCurrChar + i) == (WCHAR) 0x2560) // Its Novell's mapping of a Euro
  858. *(pCurrChar + i) = (WCHAR) 0x20AC; // set it to Euro
  859. }
  860. }
  861. RtlMoveMemory( &buffer[nextEntry + baseLength],
  862. nwDirInfo->FileName.Buffer,
  863. bytesToCopy );
  864. dump( Dbg, &buffer[nextEntry], lengthAdded);
  865. //
  866. // Setup the previous next entry offset.
  867. //
  868. *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
  869. totalBufferLength = nextEntry + lengthAdded;
  870. //
  871. // Set ourselves up for the next iteration
  872. //
  873. lastEntry = nextEntry;
  874. nextEntry += (ULONG)QuadAlign( lengthAdded );
  875. //
  876. // Check if the last entry didn't completely fit
  877. //
  878. if ( status == STATUS_BUFFER_OVERFLOW ) {
  879. try_return( NOTHING );
  880. }
  881. //
  882. // Check if we are only to return a single entry
  883. //
  884. if (returnSingleEntry) {
  885. try_return( status = STATUS_SUCCESS );
  886. }
  887. } else {
  888. //
  889. // The search response contained an error. If we have
  890. // not yet enumerated directories, do them now. Otherwise,
  891. // we are done searching for files.
  892. //
  893. if ( status == STATUS_UNSUCCESSFUL &&
  894. (!FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) || useCache) ) {
  895. SetFlag( SearchAttributes, NW_ATTRIBUTE_DIRECTORY );
  896. fileIndexLow = (ULONG)-1;
  897. continue;
  898. } else {
  899. //
  900. // Remember that this is a completed search and
  901. // quit the loop.
  902. //
  903. SearchAttributes = 0xFF;
  904. break;
  905. }
  906. }
  907. //
  908. // Here are the rules concerning filling up the buffer:
  909. //
  910. // 1. The Io system garentees that there will always be
  911. // enough room for at least one base record.
  912. //
  913. // 2. If the full first record (including file name) cannot
  914. // fit, as much of the name as possible is copied and
  915. // STATUS_BUFFER_OVERFLOW is returned.
  916. //
  917. // 3. If a subsequent record cannot completely fit into the
  918. // buffer, none of it (as in 0 bytes) is copied, and
  919. // STATUS_SUCCESS is returned. A subsequent query will
  920. // pick up with this record.
  921. //
  922. // Since we cannot rewind a search, we'll guess that the
  923. // next entry is a full length name. If it mightn't fix,
  924. // just bail and re the files we've got.
  925. //
  926. bytesRemainingInBuffer = systemBufferLength - nextEntry;
  927. if ( baseLength + NW_MAX_FILENAME_SIZE > bytesRemainingInBuffer ) {
  928. DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
  929. try_return( status = STATUS_SUCCESS );
  930. }
  931. } // while ( TRUE )
  932. try_exit: NOTHING;
  933. } finally {
  934. //
  935. // At this point we're finished searching for files.
  936. // If the NextEntry is zero then we haven't found anything so we
  937. // will return no more files or no such file.
  938. //
  939. if ( status == STATUS_NO_MORE_FILES ||
  940. status == STATUS_UNSUCCESSFUL ||
  941. status == STATUS_SUCCESS ) {
  942. if (nextEntry == 0) {
  943. if (Icb->ReturnedSomething) {
  944. status = STATUS_NO_MORE_FILES;
  945. } else {
  946. status = STATUS_NO_SUCH_FILE;
  947. }
  948. } else {
  949. Icb->ReturnedSomething = TRUE;
  950. status = STATUS_SUCCESS;
  951. }
  952. }
  953. //
  954. // Indicate how much of the system buffer we have used up.
  955. //
  956. Irp->IoStatus.Information = totalBufferLength;
  957. //
  958. // Remember the last file index, so that we can resume this
  959. // search.
  960. //
  961. //
  962. // Update the last search index read as long as it didn't come from cache.
  963. //
  964. if( lastIndexFromServer ) {
  965. Icb->SearchIndexLow = fileIndexLow;
  966. Icb->SearchIndexHigh = fileIndexHigh;
  967. }
  968. Icb->SearchAttributes = SearchAttributes;
  969. DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
  970. }
  971. return status;
  972. }
  973. NTSTATUS
  974. GetNextFile(
  975. PIRP_CONTEXT pIrpContext,
  976. PICB Icb,
  977. PULONG FileIndexLow,
  978. PULONG FileIndexHigh,
  979. UCHAR SearchAttributes,
  980. PNW_DIRECTORY_INFO DirInfo
  981. )
  982. /*++
  983. Routine Description:
  984. Get the next file in the directory being searched.
  985. Arguments:
  986. pIrpContext - Supplies the request being processed.
  987. Icb - A pointer to the ICB for the directory to query.
  988. FileIndexLow, FileIndexHigh - On entry, the the index of the
  989. previous directory entry. On exit, the index to the directory
  990. entry returned.
  991. SearchAttributes - Search attributes to use.
  992. DirInfo - Returns information for the directory entry found.
  993. Return Value:
  994. NTSTATUS - The result status.
  995. --*/
  996. {
  997. NTSTATUS status;
  998. PVCB vcb;
  999. static UNICODE_STRING DotFile = { 2, 2, L"." };
  1000. static UNICODE_STRING DotDotFile = { 4, 4, L".." };
  1001. PAGED_CODE();
  1002. DirInfo->DosDirectoryEntry = 0xFFFF;
  1003. if ( !Icb->DotReturned ) {
  1004. Icb->DotReturned = TRUE;
  1005. //
  1006. // Return '.' only if it we are not searching in the root directory
  1007. // and it matches the search pattern.
  1008. //
  1009. if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
  1010. FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotFile, TRUE, NULL ) ) {
  1011. RtlCopyUnicodeString( &DirInfo->FileName, &DotFile );
  1012. DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
  1013. DirInfo->FileSize = 0;
  1014. DirInfo->CreationDate = DEFAULT_DATE;
  1015. DirInfo->LastAccessDate = DEFAULT_DATE;
  1016. DirInfo->LastUpdateDate = DEFAULT_DATE;
  1017. DirInfo->LastUpdateTime = DEFAULT_TIME;
  1018. DirInfo->CreationTime = DEFAULT_TIME;
  1019. return( STATUS_SUCCESS );
  1020. }
  1021. }
  1022. if ( !Icb->DotDotReturned ) {
  1023. Icb->DotDotReturned = TRUE;
  1024. //
  1025. // Return '..' only if it we are not searching in the root directory
  1026. // and it matches the search pattern.
  1027. //
  1028. if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
  1029. FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotDotFile, TRUE, NULL ) ) {
  1030. RtlCopyUnicodeString( &DirInfo->FileName, &DotDotFile );
  1031. DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
  1032. DirInfo->FileSize = 0;
  1033. DirInfo->CreationDate = DEFAULT_DATE;
  1034. DirInfo->LastAccessDate = DEFAULT_DATE;
  1035. DirInfo->LastUpdateDate = DEFAULT_DATE;
  1036. DirInfo->LastUpdateTime = DEFAULT_TIME;
  1037. DirInfo->CreationTime = DEFAULT_TIME;
  1038. return( STATUS_SUCCESS );
  1039. }
  1040. }
  1041. vcb = Icb->SuperType.Fcb->Vcb;
  1042. if ( Icb->ShortNameSearch ) {
  1043. status = ExchangeWithWait(
  1044. pIrpContext,
  1045. SynchronousResponseCallback,
  1046. "Fbwwbp",
  1047. NCP_SEARCH_CONTINUE,
  1048. Icb->SearchVolume,
  1049. Icb->SearchHandle,
  1050. *(PUSHORT)FileIndexLow,
  1051. SearchAttributes,
  1052. Icb->NwQueryTemplate.Buffer
  1053. );
  1054. if ( !NT_SUCCESS( status )) {
  1055. return status;
  1056. }
  1057. *FileIndexLow = 0;
  1058. *FileIndexHigh = 0;
  1059. if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
  1060. status = ParseResponse(
  1061. pIrpContext,
  1062. pIrpContext->rsp,
  1063. pIrpContext->ResponseLength,
  1064. "Nw=Rb-ww",
  1065. FileIndexLow,
  1066. &DirInfo->FileName, 14,
  1067. &DirInfo->Attributes,
  1068. &DirInfo->CreationDate,
  1069. &DirInfo->CreationTime
  1070. );
  1071. #if 0
  1072. if ( DirInfo->CreationDate == 0 && DirInfo->CreationTime == 0 ) {
  1073. DirInfo->CreationDate = DEFAULT_DATE;
  1074. DirInfo->CreationTime = DEFAULT_TIME;
  1075. }
  1076. #endif
  1077. DirInfo->FileSize = 0;
  1078. DirInfo->LastAccessDate = DirInfo->CreationDate;
  1079. DirInfo->LastUpdateDate = DirInfo->CreationDate;
  1080. DirInfo->LastUpdateTime = DirInfo->CreationTime;
  1081. } else {
  1082. status = ParseResponse(
  1083. pIrpContext,
  1084. pIrpContext->rsp,
  1085. pIrpContext->ResponseLength,
  1086. "Nw=Rb-dwwww",
  1087. FileIndexLow,
  1088. &DirInfo->FileName, 14,
  1089. &DirInfo->Attributes,
  1090. &DirInfo->FileSize,
  1091. &DirInfo->CreationDate,
  1092. &DirInfo->LastAccessDate,
  1093. &DirInfo->LastUpdateDate,
  1094. &DirInfo->LastUpdateTime
  1095. );
  1096. DirInfo->CreationTime = DEFAULT_TIME;
  1097. }
  1098. } else {
  1099. status = ExchangeWithWait (
  1100. pIrpContext,
  1101. SynchronousResponseCallback,
  1102. "LbbWDbDDp",
  1103. NCP_LFN_SEARCH_CONTINUE,
  1104. vcb->Specific.Disk.LongNameSpace,
  1105. 0, // Data stream
  1106. SearchAttributes & SEARCH_ALL_DIRECTORIES,
  1107. LFN_FLAG_INFO_ATTRIBUTES |
  1108. LFN_FLAG_INFO_FILE_SIZE |
  1109. LFN_FLAG_INFO_MODIFY_TIME |
  1110. LFN_FLAG_INFO_CREATION_TIME |
  1111. LFN_FLAG_INFO_DIR_INFO |
  1112. LFN_FLAG_INFO_NAME,
  1113. vcb->Specific.Disk.VolumeNumber,
  1114. *FileIndexHigh,
  1115. *FileIndexLow,
  1116. Icb->NwQueryTemplate.Buffer );
  1117. if ( NT_SUCCESS( status ) ) {
  1118. status = ParseResponse(
  1119. pIrpContext,
  1120. pIrpContext->rsp,
  1121. pIrpContext->ResponseLength,
  1122. "N-ee_e_e_xx_xx_x_e_P",
  1123. FileIndexHigh,
  1124. FileIndexLow,
  1125. 5,
  1126. &DirInfo->Attributes,
  1127. 2,
  1128. &DirInfo->FileSize,
  1129. 6,
  1130. &DirInfo->CreationTime,
  1131. &DirInfo->CreationDate,
  1132. 4,
  1133. &DirInfo->LastUpdateTime,
  1134. &DirInfo->LastUpdateDate,
  1135. 4,
  1136. &DirInfo->LastAccessDate,
  1137. 14,
  1138. &DirInfo->DosDirectoryEntry,
  1139. 20,
  1140. &DirInfo->FileName );
  1141. }
  1142. if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
  1143. DirInfo->FileSize = 0;
  1144. }
  1145. }
  1146. if ( DirInfo->Attributes == 0 ) {
  1147. DirInfo->Attributes = FILE_ATTRIBUTE_NORMAL;
  1148. }
  1149. return status;
  1150. }
  1151. NTSTATUS
  1152. NtSearchMaskToNw(
  1153. IN PUNICODE_STRING UcSearchMask,
  1154. IN OUT POEM_STRING OemSearchMask,
  1155. IN PICB Icb,
  1156. IN BOOLEAN ShortNameSearch
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. This routine maps a netware path name to the correct netware format.
  1161. Arguments:
  1162. UcSearchMask - The search mask in NT format.
  1163. OemSearchMask - The search mask in Netware format.
  1164. Icb - The ICB of the directory in which we are searching.
  1165. ShortNameSearch - If TRUE, always do a short name search.
  1166. Return Value:
  1167. NTSTATUS - The result status.
  1168. --*/
  1169. {
  1170. USHORT i;
  1171. NTSTATUS status;
  1172. PAGED_CODE();
  1173. //
  1174. // Use a short name search if the volume does not support long names.
  1175. // or this is a short name ICB, and we are doing a short name, non
  1176. // wild-card search.
  1177. //
  1178. if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
  1179. ShortNameSearch ||
  1180. ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
  1181. !FsRtlDoesNameContainWildCards( UcSearchMask ) &&
  1182. IsFatNameValid( UcSearchMask ) ) ) {
  1183. Icb->ShortNameSearch = TRUE;
  1184. // Mapping for Novell's handling of Euro char in file names
  1185. {
  1186. int i = 0;
  1187. WCHAR * pCurrChar = UcSearchMask->Buffer;
  1188. for (i = 0; i < (UcSearchMask->Length / 2); i++)
  1189. {
  1190. if (*(pCurrChar + i) == (WCHAR) 0x20AC) // Its a Euro
  1191. *(pCurrChar + i) = (WCHAR) 0x2560; // set it to Novell's mapping for Euro
  1192. }
  1193. }
  1194. //
  1195. // Allocate space for and initialize the query templates.
  1196. //
  1197. status = RtlUpcaseUnicodeStringToOemString(
  1198. OemSearchMask,
  1199. UcSearchMask,
  1200. TRUE );
  1201. if ( !NT_SUCCESS( status ) ) {
  1202. return( status );
  1203. }
  1204. //
  1205. // Special case. Map '*.*' to '*'.
  1206. //
  1207. if ( OemSearchMask->Length == 3 &&
  1208. RtlCompareMemory( OemSearchMask->Buffer, "*.*", 3 ) == 3 ) {
  1209. OemSearchMask->Length = 1;
  1210. OemSearchMask->Buffer[1] = '\0';
  1211. } else {
  1212. for ( i = 0; i < OemSearchMask->Length ; i++ ) {
  1213. //
  1214. // In fact Novell server seems to convert all 0xBF, 0xAA, 0xAE
  1215. // even if they are DBCS lead or trail byte.
  1216. // We can't single out DBCS case in the conversion.
  1217. //
  1218. if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) {
  1219. if((UCHAR)(OemSearchMask->Buffer[i]) == 0xBF ) {
  1220. OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
  1221. }else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAE ) {
  1222. OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
  1223. }else if((UCHAR)(OemSearchMask->Buffer[i]) == 0xAA ) {
  1224. OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
  1225. }
  1226. i++;
  1227. if((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C ) {
  1228. //
  1229. // The trailbyte is 0x5C, replace it with 0x13
  1230. //
  1231. OemSearchMask->Buffer[i] = (UCHAR)( 0x13 );
  1232. }
  1233. //
  1234. // Continue to check other conversions for trailbyte.
  1235. //
  1236. }
  1237. // Single byte character that may need modification.
  1238. switch ( (UCHAR)(OemSearchMask->Buffer[i]) ) {
  1239. case ANSI_DOS_STAR:
  1240. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '*' );
  1241. break;
  1242. case ANSI_DOS_QM:
  1243. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '?' );
  1244. break;
  1245. case ANSI_DOS_DOT:
  1246. OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '.' );
  1247. break;
  1248. //
  1249. // Netware Japanese version The following character is
  1250. // replaced with another one if the string is for File
  1251. // Name only when sendding from Client to Server.
  1252. //
  1253. // SO U+0xFF7F SJIS+0xBF -> 0x10
  1254. // SMALL_YO U+0xFF6E SJIS+0xAE -> 0x11
  1255. // SMALL_E U+0xFF64 SJIS+0xAA -> 0x12
  1256. //
  1257. // The reason is unknown, Should ask Novell Japan.
  1258. //
  1259. // See Also exchange.c
  1260. case 0xBF: // ANSI_DOS_KATAKANA_SO:
  1261. if (Japan) {
  1262. OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
  1263. }
  1264. break;
  1265. case 0xAE: // ANSI_DOS_KATAKANA_SMALL_YO:
  1266. if (Japan) {
  1267. OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
  1268. }
  1269. break;
  1270. case 0xAA: // ANSI_DOS_KATAKANA_SMALL_E:
  1271. if (Japan) {
  1272. OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
  1273. }
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. } else {
  1279. USHORT size;
  1280. PCHAR buffer;
  1281. UNICODE_STRING src;
  1282. OEM_STRING dest;
  1283. Icb->ShortNameSearch = FALSE;
  1284. //
  1285. // Allocate space for and initialize the query templates.
  1286. // We allocate an extra byte to account for the null terminator.
  1287. //
  1288. #ifndef QFE_BUILD
  1289. buffer = ExAllocatePoolWithTag( PagedPool,
  1290. (UcSearchMask->Length) + 1,
  1291. 'scwn' );
  1292. #else
  1293. buffer = ExAllocatePool( PagedPool,
  1294. (UcSearchMask->Length) + 1 );
  1295. #endif
  1296. if ( buffer == NULL ) {
  1297. return( STATUS_INSUFFICIENT_RESOURCES );
  1298. }
  1299. OemSearchMask->Buffer = buffer;
  1300. //
  1301. // Special case. Map '????????.???' to '*'.
  1302. //
  1303. if ( UcSearchMask->Length == 24 &&
  1304. RtlCompareMemory( UcSearchMask->Buffer, L">>>>>>>>\">>>", 24 ) == 24 ) {
  1305. OemSearchMask->Length = 3;
  1306. OemSearchMask->Buffer[0] = (UCHAR)0xFF;
  1307. OemSearchMask->Buffer[1] = '*';
  1308. OemSearchMask->Buffer[2] = '\0';
  1309. return STATUS_SUCCESS;
  1310. }
  1311. //
  1312. // Now convert the string, character by character
  1313. //
  1314. src.Buffer = UcSearchMask->Buffer;
  1315. src.Length = 2;
  1316. dest.Buffer = buffer;
  1317. dest.MaximumLength = UcSearchMask->Length;
  1318. size = UcSearchMask->Length / 2;
  1319. for ( i = 0; i < size ; i++ ) {
  1320. switch ( *src.Buffer ) {
  1321. case L'*':
  1322. case L'?':
  1323. *dest.Buffer++ = LFN_META_CHARACTER;
  1324. *dest.Buffer++ = (UCHAR)*src.Buffer++;
  1325. break;
  1326. case L'.':
  1327. *dest.Buffer++ = (UCHAR)*src.Buffer++;
  1328. break;
  1329. case DOS_DOT:
  1330. *dest.Buffer++ = LFN_META_CHARACTER;
  1331. *dest.Buffer++ = (UCHAR)( 0x80 | '.' );
  1332. *src.Buffer++;
  1333. break;
  1334. case DOS_STAR:
  1335. *dest.Buffer++ = LFN_META_CHARACTER;
  1336. *dest.Buffer++ = (UCHAR)( 0x80 | '*' );
  1337. *src.Buffer++;
  1338. break;
  1339. case DOS_QM:
  1340. *dest.Buffer++ = LFN_META_CHARACTER;
  1341. *dest.Buffer++ = (UCHAR)( 0x80 | '?' );
  1342. *src.Buffer++;
  1343. break;
  1344. case 0x20AC: // Euro
  1345. *src.Buffer = (WCHAR)0x2560; // change it to Novell's mapping
  1346. // intentional fall-through to get it mapped to OEM
  1347. default:
  1348. RtlUnicodeStringToCountedOemString( &dest, &src, FALSE );
  1349. if( FsRtlIsLeadDbcsCharacter( dest.Buffer[0] ) ) {
  1350. dest.Buffer++;
  1351. }
  1352. dest.Buffer++;
  1353. src.Buffer++;
  1354. }
  1355. }
  1356. *dest.Buffer = '\0';
  1357. OemSearchMask->Length = (USHORT)( dest.Buffer - buffer );
  1358. }
  1359. return STATUS_SUCCESS;
  1360. }
  1361. #if 0
  1362. VOID
  1363. NwCancelFindNotify (
  1364. IN PDEVICE_OBJECT DeviceObject,
  1365. IN PIRP Irp
  1366. )
  1367. /*++
  1368. Routine Description:
  1369. This routine implements the cancel function for an find notify IRP.
  1370. Arguments:
  1371. DeviceObject - ignored
  1372. Irp - Supplies the Irp being cancelled.
  1373. Return Value:
  1374. None.
  1375. --*/
  1376. {
  1377. PLIST_ENTRY listEntry;
  1378. UNREFERENCED_PARAMETER( DeviceObject );
  1379. //
  1380. // We now need to void the cancel routine and release the io cancel
  1381. // spin-lock.
  1382. //
  1383. IoSetCancelRoutine( Irp, NULL );
  1384. IoReleaseCancelSpinLock( Irp->CancelIrql );
  1385. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  1386. for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
  1387. PIRP_CONTEXT IrpContext;
  1388. IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
  1389. if ( IrpContext->pOriginalIrp == Irp ) {
  1390. RemoveEntryList( &IrpContext->NextRequest );
  1391. NwCompleteRequest( IrpContext, STATUS_CANCELLED );
  1392. break;
  1393. }
  1394. }
  1395. NwReleaseRcb( &NwRcb );
  1396. }
  1397. #endif