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.

1046 lines
24 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. NameSup.c
  5. Abstract:
  6. This module implements the Cdfs Name support routines
  7. // @@BEGIN_DDKSPLIT
  8. Author:
  9. Brian Andrew [BrianAn] 01-July-1995
  10. Revision History:
  11. // @@END_DDKSPLIT
  12. --*/
  13. #include "CdProcs.h"
  14. //
  15. // The Bug check file id for this module
  16. //
  17. #define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP)
  18. #ifdef ALLOC_PRAGMA
  19. #pragma alloc_text(PAGE, CdConvertBigToLittleEndian)
  20. #pragma alloc_text(PAGE, CdConvertNameToCdName)
  21. #pragma alloc_text(PAGE, CdDissectName)
  22. #pragma alloc_text(PAGE, CdGenerate8dot3Name)
  23. #pragma alloc_text(PAGE, CdFullCompareNames)
  24. #pragma alloc_text(PAGE, CdIs8dot3Name)
  25. #pragma alloc_text(PAGE, CdIsNameInExpression)
  26. #pragma alloc_text(PAGE, CdShortNameDirentOffset)
  27. #pragma alloc_text(PAGE, CdUpcaseName)
  28. #endif
  29. VOID
  30. CdConvertNameToCdName (
  31. IN PIRP_CONTEXT IrpContext,
  32. IN OUT PCD_NAME CdName
  33. )
  34. /*++
  35. Routine Description:
  36. This routine is called to convert a string of bytes into a CdName.
  37. The full name is already in the CdName structure in the FileName field.
  38. We split this into the filename and version strings.
  39. Arguments:
  40. CdName - Pointer to CdName structure to update.
  41. Return Value:
  42. None.
  43. --*/
  44. {
  45. ULONG NameLength = 0;
  46. PWCHAR CurrentCharacter = CdName->FileName.Buffer;
  47. PAGED_CODE();
  48. //
  49. // Look for a separator character.
  50. //
  51. while ((NameLength < CdName->FileName.Length) &&
  52. (*CurrentCharacter != L';')) {
  53. CurrentCharacter += 1;
  54. NameLength += 2;
  55. }
  56. //
  57. // If there is at least one more character after a possible separator then it
  58. // and all following characters are part of the version string.
  59. //
  60. CdName->VersionString.Length = 0;
  61. if (NameLength + sizeof( WCHAR ) < CdName->FileName.Length) {
  62. CdName->VersionString.MaximumLength =
  63. CdName->VersionString.Length = (USHORT) (CdName->FileName.Length - NameLength - sizeof( WCHAR ));
  64. CdName->VersionString.Buffer = Add2Ptr( CdName->FileName.Buffer,
  65. NameLength + sizeof( WCHAR ),
  66. PWCHAR );
  67. }
  68. //
  69. // Now update the filename portion of the name.
  70. //
  71. CdName->FileName.Length = (USHORT) NameLength;
  72. return;
  73. }
  74. VOID
  75. CdConvertBigToLittleEndian (
  76. IN PIRP_CONTEXT IrpContext,
  77. IN PCHAR BigEndian,
  78. IN ULONG ByteCount,
  79. OUT PCHAR LittleEndian
  80. )
  81. /*++
  82. Routine Description:
  83. This routine is called to convert a unicode string in big endian to
  84. little endian. We start by copying all of the source bytes except
  85. the first. This will put the low order bytes in the correct position.
  86. We then copy each high order byte in its correct position.
  87. Arguments:
  88. BigEndian - Pointer to the string of big endian characters.
  89. ByteCount - Number of unicode characters in this string.
  90. LittleEndian - Pointer to array to store the little endian characters.
  91. Return Value:
  92. None.
  93. --*/
  94. {
  95. ULONG RemainingByteCount = ByteCount;
  96. PCHAR Source = BigEndian;
  97. PCHAR Destination = LittleEndian;
  98. PAGED_CODE();
  99. //
  100. // If the byte count isn't an even number then the disk is corrupt.
  101. //
  102. if (FlagOn( ByteCount, 1 )) {
  103. CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
  104. }
  105. //
  106. // Start by copy the low-order bytes into the correct position. Do
  107. // this by skipping the first byte in the BigEndian string.
  108. //
  109. RtlCopyMemory( Destination,
  110. Source + 1,
  111. RemainingByteCount - 1 );
  112. //
  113. // Now move the high-order bytes into position.
  114. //
  115. Destination += 1;
  116. while (RemainingByteCount != 0) {
  117. *Destination = *Source;
  118. Source += 2;
  119. Destination += 2;
  120. RemainingByteCount -= 2;
  121. }
  122. return;
  123. }
  124. VOID
  125. CdUpcaseName (
  126. IN PIRP_CONTEXT IrpContext,
  127. IN PCD_NAME Name,
  128. IN OUT PCD_NAME UpcaseName
  129. )
  130. /*++
  131. Routine Description:
  132. This routine is called to upcase a CdName structure. We will do both
  133. the filename and version strings.
  134. Arguments:
  135. Name - This is the mixed case version of the name.
  136. UpcaseName - This is the destination for the upcase operation.
  137. Return Value:
  138. None. This routine will raise all errors.
  139. --*/
  140. {
  141. NTSTATUS Status;
  142. PVOID NewBuffer;
  143. PAGED_CODE();
  144. //
  145. // If the name structures are different then initialize the different components.
  146. //
  147. if (Name != UpcaseName) {
  148. //
  149. // Initialize the version string portion of the name.
  150. //
  151. UpcaseName->VersionString.Length = 0;
  152. if (Name->VersionString.Length != 0) {
  153. UpcaseName->VersionString.MaximumLength =
  154. UpcaseName->VersionString.Length = Name->VersionString.Length;
  155. //
  156. // Initially set the buffer to point to where we need to insert
  157. // the separator.
  158. //
  159. UpcaseName->VersionString.Buffer = Add2Ptr( UpcaseName->FileName.Buffer,
  160. Name->FileName.Length,
  161. PWCHAR );
  162. //
  163. // We are currently pointing to the location to store the separator.
  164. // Store the ';' and then move to the next character to
  165. // copy the data.
  166. //
  167. *(UpcaseName->VersionString.Buffer) = L';';
  168. UpcaseName->VersionString.Buffer += 1;
  169. }
  170. }
  171. //
  172. // Upcase the string using the correct upcase routine.
  173. //
  174. Status = RtlUpcaseUnicodeString( &UpcaseName->FileName,
  175. &Name->FileName,
  176. FALSE );
  177. //
  178. // This should never fail.
  179. //
  180. ASSERT( Status == STATUS_SUCCESS );
  181. if (Name->VersionString.Length != 0) {
  182. Status = RtlUpcaseUnicodeString( &UpcaseName->VersionString,
  183. &Name->VersionString,
  184. FALSE );
  185. //
  186. // This should never fail.
  187. //
  188. ASSERT( Status == STATUS_SUCCESS );
  189. }
  190. return;
  191. }
  192. VOID
  193. CdDissectName (
  194. IN PIRP_CONTEXT IrpContext,
  195. IN OUT PUNICODE_STRING RemainingName,
  196. OUT PUNICODE_STRING FinalName
  197. )
  198. /*++
  199. Routine Description:
  200. This routine is called to strip off leading components of the name strings. We search
  201. for either the end of the string or separating characters. The input remaining
  202. name strings should have neither a trailing or leading backslash.
  203. Arguments:
  204. RemainingName - Remaining name.
  205. FinalName - Location to store next component of name.
  206. Return Value:
  207. None.
  208. --*/
  209. {
  210. ULONG NameLength;
  211. PWCHAR NextWchar;
  212. PAGED_CODE();
  213. //
  214. // Find the offset of the next component separators.
  215. //
  216. for (NameLength = 0, NextWchar = RemainingName->Buffer;
  217. (NameLength < RemainingName->Length) && (*NextWchar != L'\\');
  218. NameLength += sizeof( WCHAR) , NextWchar += 1);
  219. //
  220. // Adjust all the strings by this amount.
  221. //
  222. FinalName->Buffer = RemainingName->Buffer;
  223. FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength;
  224. //
  225. // If this is the last component then set the RemainingName lengths to zero.
  226. //
  227. if (NameLength == RemainingName->Length) {
  228. RemainingName->Length = 0;
  229. //
  230. // Otherwise we adjust the string by this amount plus the separating character.
  231. //
  232. } else {
  233. RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR ));
  234. RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR ));
  235. RemainingName->Buffer = Add2Ptr( RemainingName->Buffer,
  236. NameLength + sizeof( WCHAR ),
  237. PWCHAR );
  238. }
  239. return;
  240. }
  241. BOOLEAN
  242. CdIs8dot3Name (
  243. IN PIRP_CONTEXT IrpContext,
  244. IN UNICODE_STRING FileName
  245. )
  246. /*++
  247. Routine Description:
  248. This routine checks if the name follows the 8.3 name conventions. We check for
  249. the name length and whether the characters are valid.
  250. Arguments:
  251. FileName - String of bytes containing the name.
  252. Return Value:
  253. BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise.
  254. --*/
  255. {
  256. CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ];
  257. STRING DbcsName;
  258. PWCHAR NextWchar;
  259. ULONG Count;
  260. ULONG DotCount = 0;
  261. BOOLEAN LastCharDot = FALSE;
  262. PAGED_CODE();
  263. //
  264. // The length must be less than 24 bytes.
  265. //
  266. ASSERT( FileName.Length != 0 );
  267. if (FileName.Length > BYTE_COUNT_8_DOT_3) {
  268. return FALSE;
  269. }
  270. //
  271. // Walk though and check for a space character.
  272. //
  273. NextWchar = FileName.Buffer;
  274. Count = 0;
  275. do {
  276. //
  277. // No spaces allowed.
  278. //
  279. if (*NextWchar == L' ') { return FALSE; }
  280. if (*NextWchar == L'.') {
  281. //
  282. // Not an 8.3 name if more than 1 dot or more than 8 characters
  283. // remaining. (It is legal for the dot to be in the ninth
  284. // position)
  285. //
  286. if ((DotCount > 0) ||
  287. (Count > 8 * sizeof( WCHAR ))) {
  288. return FALSE;
  289. }
  290. DotCount += 1;
  291. LastCharDot = TRUE;
  292. } else {
  293. LastCharDot = FALSE;
  294. }
  295. Count += 2;
  296. NextWchar += 1;
  297. } while (Count < FileName.Length);
  298. //
  299. // Go ahead and truncate the dot if at the end.
  300. //
  301. if (LastCharDot) {
  302. FileName.Length -= sizeof( WCHAR );
  303. }
  304. //
  305. // Create an Oem name to use to check for a valid short name.
  306. //
  307. DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3;
  308. DbcsName.Buffer = DbcsNameBuffer;
  309. if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName,
  310. &FileName,
  311. FALSE ))) {
  312. return FALSE;
  313. }
  314. //
  315. // We have now initialized the Oem string. Call the FsRtl package to check for a
  316. // valid FAT name.
  317. //
  318. return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE );
  319. }
  320. VOID
  321. CdGenerate8dot3Name (
  322. IN PIRP_CONTEXT IrpContext,
  323. IN PUNICODE_STRING FileName,
  324. IN ULONG DirentOffset,
  325. OUT PWCHAR ShortFileName,
  326. OUT PUSHORT ShortByteCount
  327. )
  328. /*++
  329. Routine Description:
  330. This routine is called to generate a short name from the given long name. We will
  331. generate a short name from the given long name.
  332. We go through the following steps to make this conversion.
  333. 1 - Generate the generic short name. This will also be in unicode format.
  334. 2 - Build the string representation of the dirent offset.
  335. 3 - Build the biased short name string by combining the generic short name with
  336. the dirent offset string.
  337. 4 - Copy the final unicode string back to our caller's buffer.
  338. Arguments:
  339. FileName - String of bytes containing the name.
  340. DirentOffset - Offset in the directory for this filename. We incorporate the offset into
  341. the short name by dividing this by 32 and prepending a tilde character to the
  342. digit character. We then append this to the base of the generated short name.
  343. ShortFileName - Pointer to the buffer to store the short name into.
  344. ShortByteCount - Address to store the number of bytes in the short name.
  345. Return Value:
  346. None.
  347. --*/
  348. {
  349. UNICODE_STRING ShortName;
  350. UNICODE_STRING BiasedShortName;
  351. WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ];
  352. WCHAR BiasedShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ];
  353. GENERATE_NAME_CONTEXT NameContext;
  354. ULONG BiasedDirentOffset;
  355. ULONG MaximumBaseBytes;
  356. ULONG BaseNameOffset;
  357. PWCHAR NextWchar;
  358. WCHAR ThisWchar;
  359. USHORT Length;
  360. BOOLEAN FoundTilde = FALSE;
  361. OEM_STRING OemName;
  362. USHORT OemNameOffset = 0;
  363. BOOLEAN OverflowBuffer = FALSE;
  364. PAGED_CODE();
  365. //
  366. // Initialize the short string to use the input buffer.
  367. //
  368. ShortName.Buffer = ShortNameBuffer;
  369. ShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
  370. //
  371. // Initialize the name context.
  372. //
  373. RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT ));
  374. //
  375. // We now have the unicode name for the input string. Go ahead and generate
  376. // the short name.
  377. //
  378. RtlGenerate8dot3Name( FileName, TRUE, &NameContext, &ShortName );
  379. //
  380. // We now have the generic short name. We want incorporate the dirent offset
  381. // into the name in order to reduce the chance of name conflicts. We will use
  382. // a tilde character followed by a character representation of the dirent offset.
  383. // This will be the hexadecimal representation of the dirent offset in the directory.
  384. // It is actuall this offset divided by 32 since we don't need the full
  385. // granularity.
  386. //
  387. BiasedDirentOffset = DirentOffset >> SHORT_NAME_SHIFT;
  388. //
  389. // Point to a local buffer to store the offset string. We start
  390. // at the end of the buffer and work backwards.
  391. //
  392. NextWchar = Add2Ptr( BiasedShortNameBuffer,
  393. BYTE_COUNT_8_DOT_3,
  394. PWCHAR );
  395. BiasedShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
  396. //
  397. // Generate an OEM version of the string so that we can check for double
  398. // byte characters.
  399. //
  400. RtlUnicodeStringToOemString(&OemName, &ShortName, TRUE);
  401. Length = 0;
  402. //
  403. // Now add the characters for the dirent offset. We need to start
  404. // from the least significant digit and work backwards.
  405. //
  406. do {
  407. NextWchar -= 1;
  408. ThisWchar = (WCHAR) (BiasedDirentOffset & 0x0000000f);
  409. //
  410. // Store in the next character. Bias against either '0' or 'A'
  411. //
  412. if (ThisWchar <= 9) {
  413. *NextWchar = ThisWchar + L'0';
  414. } else {
  415. *NextWchar = ThisWchar + L'A' - 0xA;
  416. }
  417. Length += sizeof( WCHAR );
  418. //
  419. // Shift out the low 4 bits of the offset.
  420. //
  421. BiasedDirentOffset >>= 4;
  422. } while (BiasedDirentOffset != 0);
  423. //
  424. // Now store in the tilde character.
  425. //
  426. NextWchar -= 1;
  427. *NextWchar = L'~';
  428. Length += sizeof( WCHAR );
  429. //
  430. // Set the length of this string.
  431. //
  432. BiasedShortName.Length = Length;
  433. BiasedShortName.Buffer = NextWchar;
  434. //
  435. // Figure out the maximum number of characters we can copy of the base
  436. // name. We subract the number of characters in the dirent string from 8.
  437. // We will copy this many characters or stop when we reach a '.' character
  438. // or a '~' character in the name.
  439. //
  440. MaximumBaseBytes = 16 - Length;
  441. BaseNameOffset = 0;
  442. //
  443. // Keep copying from the base name until we hit a '.', '~' or the end of
  444. // the short name.
  445. //
  446. NextWchar = ShortFileName;
  447. Length = 0;
  448. while ((BaseNameOffset < ShortName.Length) &&
  449. (ShortName.Buffer[BaseNameOffset / 2] != L'.')) {
  450. //
  451. // Remember if we found a tilde character in the short name,
  452. // so we don't copy it or anything following it.
  453. //
  454. if (ShortName.Buffer[BaseNameOffset / 2] == L'~') {
  455. FoundTilde = TRUE;
  456. }
  457. //
  458. // We need to consider the DBCS code page, because Unicode characters
  459. // may use 2 bytes as DBCS characters.
  460. //
  461. if (FsRtlIsLeadDbcsCharacter(OemName.Buffer[OemNameOffset])) {
  462. OemNameOffset += 2;
  463. if ((OemNameOffset + (BiasedShortName.Length / sizeof(WCHAR))) > 8) {
  464. OverflowBuffer = TRUE;
  465. }
  466. }
  467. else {
  468. OemNameOffset++;
  469. }
  470. //
  471. // Only copy the bytes if we still have space for the dirent string.
  472. //
  473. if (!FoundTilde && !OverflowBuffer && (BaseNameOffset < MaximumBaseBytes)) {
  474. *NextWchar = ShortName.Buffer[BaseNameOffset / 2];
  475. Length += sizeof( WCHAR );
  476. NextWchar += 1;
  477. }
  478. BaseNameOffset += 2;
  479. }
  480. RtlFreeOemString(&OemName);
  481. //
  482. // Now copy the dirent string into the biased name buffer.
  483. //
  484. RtlCopyMemory( NextWchar,
  485. BiasedShortName.Buffer,
  486. BiasedShortName.Length );
  487. Length += BiasedShortName.Length;
  488. NextWchar += (BiasedShortName.Length / sizeof( WCHAR ));
  489. //
  490. // Now copy any remaining bytes over to the biased short name.
  491. //
  492. if (BaseNameOffset != ShortName.Length) {
  493. RtlCopyMemory( NextWchar,
  494. &ShortName.Buffer[BaseNameOffset / 2],
  495. ShortName.Length - BaseNameOffset );
  496. Length += (ShortName.Length - (USHORT) BaseNameOffset);
  497. }
  498. //
  499. // The final short name is stored in the user's buffer.
  500. //
  501. *ShortByteCount = Length;
  502. return;
  503. }
  504. BOOLEAN
  505. CdIsNameInExpression (
  506. IN PIRP_CONTEXT IrpContext,
  507. IN PCD_NAME CurrentName,
  508. IN PCD_NAME SearchExpression,
  509. IN ULONG WildcardFlags,
  510. IN BOOLEAN CheckVersion
  511. )
  512. /*++
  513. Routine Description:
  514. This routine will compare two CdName strings. We assume that if this
  515. is to be a case-insensitive search then they are already upcased.
  516. We compare the filename portions of the name and if they match we
  517. compare the version strings if requested.
  518. Arguments:
  519. CurrentName - Filename from the disk.
  520. SearchExpression - Filename expression to use for match.
  521. WildcardFlags - Flags field which indicates which parts of the
  522. search expression might have wildcards. These flags are the
  523. same as in the Ccb flags field.
  524. CheckVersion - Indicates whether we should check both the name and the
  525. version strings or just the name.
  526. Return Value:
  527. BOOLEAN - TRUE if the expressions match, FALSE otherwise.
  528. --*/
  529. {
  530. BOOLEAN Match = TRUE;
  531. PAGED_CODE();
  532. //
  533. // If there are wildcards in the expression then we call the
  534. // appropriate FsRtlRoutine.
  535. //
  536. if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD )) {
  537. Match = FsRtlIsNameInExpression( &SearchExpression->FileName,
  538. &CurrentName->FileName,
  539. FALSE,
  540. NULL );
  541. //
  542. // Otherwise do a direct memory comparison for the name string.
  543. //
  544. } else {
  545. if ((CurrentName->FileName.Length != SearchExpression->FileName.Length) ||
  546. (!RtlEqualMemory( CurrentName->FileName.Buffer,
  547. SearchExpression->FileName.Buffer,
  548. CurrentName->FileName.Length ))) {
  549. Match = FALSE;
  550. }
  551. }
  552. //
  553. // Check the version numbers if requested by the user and we have a
  554. // match on the name and the version number is present.
  555. //
  556. if (Match && CheckVersion && SearchExpression->VersionString.Length &&
  557. !FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL )) {
  558. //
  559. // If there are wildcards in the expression then call the
  560. // appropriate search expression.
  561. //
  562. if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD )) {
  563. Match = FsRtlIsNameInExpression( &SearchExpression->VersionString,
  564. &CurrentName->VersionString,
  565. FALSE,
  566. NULL );
  567. //
  568. // Otherwise do a direct memory comparison for the name string.
  569. //
  570. } else {
  571. if ((CurrentName->VersionString.Length != SearchExpression->VersionString.Length) ||
  572. (!RtlEqualMemory( CurrentName->VersionString.Buffer,
  573. SearchExpression->VersionString.Buffer,
  574. CurrentName->VersionString.Length ))) {
  575. Match = FALSE;
  576. }
  577. }
  578. }
  579. return Match;
  580. }
  581. ULONG
  582. CdShortNameDirentOffset (
  583. IN PIRP_CONTEXT IrpContext,
  584. IN PUNICODE_STRING Name
  585. )
  586. /*++
  587. Routine Description:
  588. This routine is called to examine a name to see if the dirent offset string is contained.
  589. This consists of a tilde character followed by the offset represented as a hexadecimal
  590. characters. We don't do any other checks to see if this is a short name. We
  591. catch that later outside this routine.
  592. Arguments:
  593. Name - This is the CdName to examine.
  594. Return Value:
  595. ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the
  596. convert the value to numeric form.
  597. --*/
  598. {
  599. ULONG ResultOffset = MAXULONG;
  600. ULONG RemainingByteCount = Name->Length;
  601. BOOLEAN FoundTilde = FALSE;
  602. PWCHAR NextWchar;
  603. PAGED_CODE();
  604. //
  605. // Walk through the name until we either reach the end of the name
  606. // or find a tilde character.
  607. //
  608. for (NextWchar = Name->Buffer;
  609. RemainingByteCount != 0;
  610. NextWchar += 1, RemainingByteCount -= sizeof( WCHAR )) {
  611. //
  612. // Check if this is a dot. Stop constructing any string if
  613. // we found a dot.
  614. //
  615. if (*NextWchar == L'.') {
  616. break;
  617. }
  618. //
  619. // If we already found a tilde then check this character as a
  620. // valid character. It must be a digit or A to F.
  621. //
  622. if (FoundTilde) {
  623. if ((*NextWchar < L'0') ||
  624. (*NextWchar > L'F') ||
  625. ((*NextWchar > L'9') && (*NextWchar < 'A'))) {
  626. ResultOffset = MAXULONG;
  627. break;
  628. }
  629. //
  630. // Shift the result by 4 bits and add in this new character.
  631. //
  632. ResultOffset <<= 4;
  633. if (*NextWchar < L'A') {
  634. ResultOffset += *NextWchar - L'0';
  635. } else {
  636. ResultOffset += (*NextWchar - L'A') + 10;
  637. }
  638. continue;
  639. }
  640. //
  641. // If this is a tilde then start building the dirent string.
  642. //
  643. if (*NextWchar == L'~') {
  644. FoundTilde = TRUE;
  645. ResultOffset = 0;
  646. }
  647. }
  648. return ResultOffset;
  649. }
  650. //
  651. // Local support routine
  652. //
  653. FSRTL_COMPARISON_RESULT
  654. CdFullCompareNames (
  655. IN PIRP_CONTEXT IrpContext,
  656. IN PUNICODE_STRING NameA,
  657. IN PUNICODE_STRING NameB
  658. )
  659. /*++
  660. Routine Description:
  661. This function compares two names as fast as possible. Note that since
  662. this comparison is case sensitive we can do a direct memory comparison.
  663. Arguments:
  664. NameA & NameB - The names to compare.
  665. Return Value:
  666. COMPARISON - returns
  667. LessThan if NameA < NameB lexicalgraphically,
  668. GreaterThan if NameA > NameB lexicalgraphically,
  669. EqualTo if NameA is equal to NameB
  670. --*/
  671. {
  672. SIZE_T i;
  673. ULONG MinLength = NameA->Length;
  674. FSRTL_COMPARISON_RESULT Result = LessThan;
  675. PAGED_CODE();
  676. //
  677. // Figure out the minimum of the two lengths
  678. //
  679. if (NameA->Length > NameB->Length) {
  680. MinLength = NameB->Length;
  681. Result = GreaterThan;
  682. } else if (NameA->Length == NameB->Length) {
  683. Result = EqualTo;
  684. }
  685. //
  686. // Loop through looking at all of the characters in both strings
  687. // testing for equalilty, less than, and greater than
  688. //
  689. i = RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength );
  690. if (i < MinLength) {
  691. //
  692. // We know the offset of the first character which is different.
  693. //
  694. return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ?
  695. LessThan :
  696. GreaterThan);
  697. }
  698. //
  699. // The names match up to the length of the shorter string.
  700. // The shorter string lexically appears first.
  701. //
  702. return Result;
  703. }