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.

871 lines
19 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. NameSup.c
  5. Abstract:
  6. This module implements the Rx Name support routines
  7. Author:
  8. Gary Kimura [GaryKi] & Tom Miller [TomM] 20-Feb-1990
  9. Revision History:
  10. --*/
  11. // ----------------------joejoe-----------found-------------#include "RxProcs.h"
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. //
  15. // The Bug check file id for this module
  16. //
  17. #define BugCheckFileId (RDBSS_BUG_CHECK_NAMESUP)
  18. #define Dbg (DEBUG_TRACE_NAMESUP)
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(PAGE, Rx8dot3ToString)
  21. #pragma alloc_text(PAGE, RxIsNameInExpression)
  22. #pragma alloc_text(PAGE, RxStringTo8dot3)
  23. #pragma alloc_text(PAGE, RxSetFullFileNameInFcb)
  24. #pragma alloc_text(PAGE, RxGetUnicodeNameFromFcb)
  25. #pragma alloc_text(PAGE, RxSelectNames)
  26. #pragma alloc_text(PAGE, RxEvaluateNameCase)
  27. #endif
  28. BOOLEAN
  29. RxIsNameInExpression (
  30. IN PRX_CONTEXT RxContext,
  31. IN OEM_STRING Expression,
  32. IN OEM_STRING Name
  33. )
  34. /*++
  35. Routine Description:
  36. This routine compare a name and an expression and tells the caller if
  37. the name is equal to or not equal to the expression. The input name
  38. cannot contain wildcards, while the expression may contain wildcards.
  39. Arguments:
  40. Expression - Supplies the input expression to check against
  41. The caller must have already upcased the Expression.
  42. Name - Supplies the input name to check for. The caller must have
  43. already upcased the name.
  44. Return Value:
  45. BOOLEAN - TRUE if Name is an element in the set of strings denoted
  46. by the input Expression and FALSE otherwise.
  47. --*/
  48. {
  49. //
  50. // Call the appropriate FsRtl routine do to the real work
  51. //
  52. return FsRtlIsDbcsInExpression( &Expression,
  53. &Name );
  54. UNREFERENCED_PARAMETER( RxContext );
  55. }
  56. VOID
  57. RxStringTo8dot3 (
  58. IN PRX_CONTEXT RxContext,
  59. IN OEM_STRING InputString,
  60. OUT PRDBSS8DOT3 Output8dot3
  61. )
  62. /*++
  63. Routine Description:
  64. Convert a string into rx 8.3 format. The string must not contain
  65. any wildcards.
  66. Arguments:
  67. InputString - Supplies the input string to convert
  68. Output8dot3 - Receives the converted string, the memory must be supplied
  69. by the caller.
  70. Return Value:
  71. None.
  72. --*/
  73. {
  74. ULONG i;
  75. ULONG j;
  76. DebugTrace(+1, Dbg, "RxStringTo8dot3\n", 0);
  77. DebugTrace( 0, Dbg, "InputString = %wZ\n", &InputString);
  78. ASSERT( InputString.Length <= 12 );
  79. //
  80. // Make the output name all blanks
  81. //
  82. for (i = 0; i < 11; i += 1) {
  83. (*Output8dot3)[i] = UCHAR_SP;
  84. }
  85. //
  86. // Copy over the first part of the file name. Stop when we get to
  87. // the end of the input string or a dot.
  88. //
  89. for (i = 0;
  90. (i < (ULONG)InputString.Length) && (InputString.Buffer[i] != '.');
  91. i += 1) {
  92. (*Output8dot3)[i] = InputString.Buffer[i];
  93. }
  94. //
  95. // Check if we need to process an extension
  96. //
  97. if (i < (ULONG)InputString.Length) {
  98. //
  99. // Make sure we have a dot and then skip over it.
  100. //
  101. ASSERT( (InputString.Length - i) <= 4 );
  102. ASSERT( InputString.Buffer[i] == '.' );
  103. i += 1;
  104. //
  105. // Copy over the extension. Stop when we get to the
  106. // end of the input string.
  107. //
  108. for (j = 8; (i < (ULONG)InputString.Length); j += 1, i += 1) {
  109. (*Output8dot3)[j] = InputString.Buffer[i];
  110. }
  111. }
  112. //
  113. // Before we return check if we should translate the first character
  114. // from 0xe5 to 0x5.
  115. //
  116. if ((*Output8dot3)[0] == 0xe5) {
  117. (*Output8dot3)[0] = RDBSS_DIRENT_REALLY_0E5;
  118. }
  119. DebugTrace(-1, Dbg, "RxStringTo8dot3 -> (VOID)\n", 0);
  120. UNREFERENCED_PARAMETER( RxContext );
  121. return;
  122. }
  123. VOID
  124. Rx8dot3ToString (
  125. IN PRX_CONTEXT RxContext,
  126. IN PDIRENT Dirent,
  127. IN BOOLEAN RestoreCase,
  128. OUT POEM_STRING OutputString
  129. )
  130. /*++
  131. Routine Description:
  132. Convert rx 8.3 format into a string. The 8.3 name must be well formed.
  133. Arguments:
  134. Dirent - Supplies the input 8.3 name to convert
  135. RestoreCase - If TRUE, then the magic reserved bits are used to restore
  136. the original case.
  137. OutputString - Receives the converted name, the memory must be supplied
  138. by the caller.
  139. Return Value:
  140. None
  141. --*/
  142. {
  143. ULONG i,j;
  144. DebugTrace(+1, Dbg, "Rx8dot3ToString\n", 0);
  145. //
  146. // Copy over the 8 part of the 8.3 name into the output buffer
  147. // and then make sure if the first character needs to be changed
  148. // from 0x05 to 0xe5. Then backup the index to the first non space
  149. // character searching backwards
  150. //
  151. RtlCopyMemory( &OutputString->Buffer[0], &Dirent->FileName[0], 8 );
  152. if (OutputString->Buffer[0] == RDBSS_DIRENT_REALLY_0E5) {
  153. OutputString->Buffer[0] = (CHAR)0xe5;
  154. }
  155. for (i = 7; (i >= 0) && (OutputString->Buffer[i] == UCHAR_SP); i -= 1) {
  156. NOTHING;
  157. }
  158. ASSERT( i >= 0 );
  159. //
  160. // Now if we are to restore case, look for A-Z
  161. //
  162. if (RxData.ChicagoMode &&
  163. RestoreCase &&
  164. FlagOn(Dirent->NtByte, RDBSS_DIRENT_NT_BYTE_8_LOWER_CASE)) {
  165. for (j = 0; j <= i; j += 1) {
  166. if ((OutputString->Buffer[j] >= 'A') &&
  167. (OutputString->Buffer[j] <= 'Z')) {
  168. OutputString->Buffer[j] += 'a' - 'A';
  169. }
  170. }
  171. }
  172. //
  173. // Now add the dot
  174. //
  175. i += 1;
  176. OutputString->Buffer[i] = '.';
  177. //
  178. // Copy over the extension into the output buffer and backup the
  179. // index to the first non space character searching backwards
  180. //
  181. i += 1;
  182. RtlCopyMemory( &OutputString->Buffer[i], &Dirent->FileName[8], 3 );
  183. j = i;
  184. for (i += 2; OutputString->Buffer[i] == UCHAR_SP; i -= 1) {
  185. NOTHING;
  186. }
  187. //
  188. // Now if the last character is a '.' then we don't have an extension
  189. // so backup before the dot.
  190. //
  191. if (OutputString->Buffer[i] == '.') {
  192. i -= 1;
  193. }
  194. //
  195. // Now if we are to restore case, look for A-Z
  196. //
  197. if (RxData.ChicagoMode &&
  198. RestoreCase &&
  199. FlagOn(Dirent->NtByte, RDBSS_DIRENT_NT_BYTE_3_LOWER_CASE)) {
  200. for (; j <= i; j += 1) {
  201. if ((OutputString->Buffer[j] >= 'A') &&
  202. (OutputString->Buffer[j] <= 'Z')) {
  203. OutputString->Buffer[j] += 'a' - 'A';
  204. }
  205. }
  206. }
  207. //
  208. // Set the output string length
  209. //
  210. OutputString->Length = (USHORT)(i + 1);
  211. //
  212. // And return to our caller
  213. //
  214. DebugTrace(-1, Dbg, "Rx8dot3ToString, OutputString = \"%wZ\" -> VOID\n", OutputString);
  215. UNREFERENCED_PARAMETER( RxContext );
  216. return;
  217. }
  218. VOID
  219. RxGetUnicodeNameFromFcb (
  220. IN PRX_CONTEXT RxContext,
  221. IN PFCB Fcb,
  222. IN OUT PUNICODE_STRING Lfn
  223. )
  224. /*++
  225. Routine Description:
  226. This routine will return the unicode name for a given Fcb. If the
  227. file has an LFN, it will return this. Otherwise it will return
  228. the UNICODE conversion of the Oem name, properly cased.
  229. Arguments:
  230. Fcb - Supplies the Fcb to query.
  231. Lfn - Supplies a string that already has enough storage for the
  232. full unicode name.
  233. Return Value:
  234. None
  235. --*/
  236. {
  237. PDIRENT Dirent;
  238. PBCB DirentBcb = NULL;
  239. ULONG DirentByteOffset;
  240. FOBX LocalFobx;
  241. //
  242. // We'll start by locating the dirent for the name.
  243. //
  244. ASSERT(FALSE);
  245. // RxStringTo8dot3( RxContext,
  246. // Fcb->ShortName.Name.Oem,
  247. // &LocalFobx.OemQueryTemplate.Constant );
  248. LocalFobx.Flags = 0;
  249. LocalFobx.UnicodeQueryTemplate.Length = 0;
  250. LocalFobx.ContainsWildCards = FALSE;
  251. RxLocateDirent( RxContext,
  252. Fcb->ParentDcb,
  253. &LocalFobx,
  254. Fcb->LfnOffsetWithinDirectory,
  255. &Dirent,
  256. &DirentBcb,
  257. &DirentByteOffset,
  258. Lfn );
  259. try {
  260. //
  261. // If we didn't find the Dirent, something is terribly wrong.
  262. //
  263. if ((DirentBcb == NULL) ||
  264. (DirentByteOffset != Fcb->DirentOffsetWithinDirectory)) {
  265. RxRaiseStatus( RxContext, RxStatus(FILE_INVALID) );
  266. }
  267. //
  268. // Check for the easy case.
  269. //
  270. if (Lfn->Length == 0) {
  271. RXSTATUS Status;
  272. OEM_STRING ShortName;
  273. UCHAR ShortNameBuffer[12];
  274. //
  275. // There is no LFN, so manufacture a UNICODE name.
  276. //
  277. ShortName.Length = 0;
  278. ShortName.MaximumLength = 12;
  279. ShortName.Buffer = ShortNameBuffer;
  280. Rx8dot3ToString( RxContext, Dirent, TRUE, &ShortName );
  281. //
  282. // OK, now convert this string to UNICODE
  283. //
  284. Status = RtlOemStringToCountedUnicodeString( Lfn,
  285. &ShortName,
  286. FALSE );
  287. ASSERT( Status == RxStatus(SUCCESS) );
  288. }
  289. } finally {
  290. RxUnpinBcb( RxContext, DirentBcb );
  291. }
  292. }
  293. VOID
  294. RxSetFullFileNameInFcb (
  295. IN PRX_CONTEXT RxContext,
  296. IN PFCB Fcb
  297. )
  298. /*++
  299. Routine Description:
  300. If the FullFileName field in the Fcb has not yet been filled in, we
  301. proceed to do so.
  302. Arguments:
  303. Fcb - Supplies the file.
  304. Return Value:
  305. None
  306. --*/
  307. {
  308. if (Fcb->FullFileName.Buffer == NULL) {
  309. UNICODE_STRING Lfn;
  310. PFCB TmpFcb = Fcb;
  311. PFCB StopFcb;
  312. PWCHAR TmpBuffer;
  313. ULONG PathLength = 0;
  314. //
  315. // We will assume we do this infrequently enough, that it's OK to
  316. // to a pool allocation here.
  317. //
  318. Lfn.Length = 0;
  319. Lfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
  320. Lfn.Buffer = FsRtlAllocatePool( PagedPool,
  321. MAX_LFN_CHARACTERS * sizeof(WCHAR) );
  322. try {
  323. //
  324. // First determine how big the name will be. If we find an
  325. // ancestor with a FullFileName, our work is easier.
  326. //
  327. while (TmpFcb != Fcb->Vcb->RootDcb) {
  328. if ((TmpFcb != Fcb) && (TmpFcb->FullFileName.Buffer != NULL)) {
  329. PathLength += TmpFcb->FullFileName.Length;
  330. Fcb->FullFileName.Buffer = FsRtlAllocatePool( PagedPool, PathLength );
  331. RtlCopyMemory( Fcb->FullFileName.Buffer,
  332. TmpFcb->FullFileName.Buffer,
  333. TmpFcb->FullFileName.Length );
  334. break;
  335. }
  336. PathLength += TmpFcb->FinalNameLength + sizeof(WCHAR);
  337. TmpFcb = TmpFcb->ParentDcb;
  338. }
  339. //
  340. // If FullFileName.Buffer is still NULL, allocate it.
  341. //
  342. if (Fcb->FullFileName.Buffer == NULL) {
  343. Fcb->FullFileName.Buffer = FsRtlAllocatePool( PagedPool, PathLength );
  344. }
  345. StopFcb = TmpFcb;
  346. TmpFcb = Fcb;
  347. TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
  348. Fcb->FullFileName.Length =
  349. Fcb->FullFileName.MaximumLength = (USHORT)PathLength;
  350. while (TmpFcb != StopFcb) {
  351. RxGetUnicodeNameFromFcb( RxContext,
  352. TmpFcb,
  353. &Lfn );
  354. TmpBuffer -= Lfn.Length / sizeof(WCHAR);
  355. RtlCopyMemory( TmpBuffer, Lfn.Buffer, Lfn.Length );
  356. TmpBuffer -= 1;
  357. *TmpBuffer = L'\\';
  358. TmpFcb = TmpFcb->ParentDcb;
  359. }
  360. } finally {
  361. if (AbnormalTermination()) {
  362. if (Fcb->FullFileName.Buffer) {
  363. ExFreePool( Fcb->FullFileName.Buffer );
  364. Fcb->FullFileName.Buffer = NULL;
  365. }
  366. }
  367. ExFreePool( Lfn.Buffer );
  368. }
  369. }
  370. }
  371. VOID
  372. RxUnicodeToUpcaseOem (
  373. IN PRX_CONTEXT RxContext,
  374. IN POEM_STRING OemString,
  375. IN PUNICODE_STRING UnicodeString
  376. )
  377. /*++
  378. Routine Description:
  379. This routine is our standard routine for trying to use stack space
  380. if possible when calling RtlUpcaseUnicodeStringToCountedOemString().
  381. If an unmappable character is encountered, we set the destination
  382. length to 0.
  383. Arguments:
  384. OemString - Specifies the destination string. Space is already assumed to
  385. be allocated. If there is not enough, then we allocate enough
  386. space.
  387. UnicodeString - Specifies the source string.
  388. Return Value:
  389. None.
  390. --*/
  391. {
  392. RXSTATUS Status;
  393. Status = RtlUpcaseUnicodeStringToCountedOemString( OemString,
  394. UnicodeString,
  395. FALSE );
  396. if (Status == RxStatus(BUFFER_OVERFLOW)) {
  397. OemString->Buffer = NULL;
  398. OemString->Length = 0;
  399. OemString->MaximumLength = 0;
  400. Status = RtlUpcaseUnicodeStringToCountedOemString( OemString,
  401. UnicodeString,
  402. TRUE );
  403. }
  404. if (!NT_SUCCESS(Status)) {
  405. if (Status == RxStatus(UNMAPPABLE_CHARACTER)) {
  406. OemString->Length = 0;
  407. } else {
  408. RxNormalizeAndRaiseStatus( RxContext, Status );
  409. }
  410. }
  411. return;
  412. }
  413. VOID
  414. RxSelectNames (
  415. IN PRX_CONTEXT RxContext,
  416. IN PDCB Parent,
  417. IN POEM_STRING OemName,
  418. IN PUNICODE_STRING UnicodeName,
  419. IN OUT POEM_STRING ShortName,
  420. IN OUT BOOLEAN *AllLowerComponent,
  421. IN OUT BOOLEAN *AllLowerExtension,
  422. IN OUT BOOLEAN *CreateLfn
  423. )
  424. /*++
  425. Routine Description:
  426. This routine takes the original UNICODE string that the user specified,
  427. and the upcased Oem equivolent. This routine then decides if the OemName
  428. is acceptable for dirent, or whether a short name has to be manufactured.
  429. Two values are returned to the caller. One tells the caller if the name
  430. happens to be all lower case < 0x80. In this special case we don't
  431. have to create an Lfn. Also we tell the caller if it must create an LFN.
  432. Arguments:
  433. OemName - Supplies the proposed short Oem name.
  434. ShortName - If this OemName is OK for storeage in a dirent it is copied to
  435. this string, otherwise this string is filled with a name that is OK.
  436. If OemName and ShortName are the same string, no copy is done.
  437. UnicodeName - Provides the original final name.
  438. AllLowerComponent - Returns whether this compoent was all lower case.
  439. AllLowerExtension - Returns wheather the extension was all lower case.
  440. CreateLfn - Tells the call if we must create an LFN for the UnicodeName.
  441. Return Value:
  442. None.
  443. --*/
  444. {
  445. BOOLEAN GenerateShortName;
  446. PAGED_CODE();
  447. //
  448. // First see if we must generate a short name.
  449. //
  450. if ((OemName->Length == 0) ||
  451. !RxIsNameValid( RxContext, *OemName, FALSE, FALSE, FALSE )) {
  452. WCHAR ShortNameBuffer[12];
  453. UNICODE_STRING ShortUnicodeName;
  454. GENERATE_NAME_CONTEXT Context;
  455. GenerateShortName = TRUE;
  456. //
  457. // Now generate a short name.
  458. //
  459. ShortUnicodeName.Length = 0;
  460. ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR);
  461. ShortUnicodeName.Buffer = ShortNameBuffer;
  462. RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) );
  463. while ( TRUE ) {
  464. PDIRENT Dirent;
  465. PBCB Bcb = NULL;
  466. ULONG ByteOffset;
  467. RXSTATUS Status;
  468. RtlGenerate8dot3Name( UnicodeName, TRUE, &Context, &ShortUnicodeName );
  469. //
  470. // We have a candidate, make sure it doesn't exist.
  471. //
  472. Status = RtlUnicodeStringToCountedOemString( ShortName,
  473. &ShortUnicodeName,
  474. FALSE );
  475. ASSERT( Status == RxStatus(SUCCESS) );
  476. RxLocateSimpleOemDirent( RxContext,
  477. Parent,
  478. ShortName,
  479. &Dirent,
  480. &Bcb,
  481. &ByteOffset );
  482. if (Bcb == NULL) {
  483. break;
  484. } else {
  485. RxUnpinBcb( RxContext, Bcb );
  486. }
  487. }
  488. } else {
  489. //
  490. // Only do this copy if the two string are indeed different.
  491. //
  492. if (ShortName != OemName) {
  493. ShortName->Length = OemName->Length;
  494. RtlCopyMemory( ShortName->Buffer, OemName->Buffer, OemName->Length );
  495. }
  496. GenerateShortName = FALSE;
  497. }
  498. //
  499. // Now see if the caller will have to use unicode string as an LFN
  500. //
  501. if (GenerateShortName) {
  502. *CreateLfn = TRUE;
  503. *AllLowerComponent = FALSE;
  504. *AllLowerExtension = FALSE;
  505. } else {
  506. RxEvaluateNameCase( RxContext,
  507. UnicodeName,
  508. AllLowerComponent,
  509. AllLowerExtension,
  510. CreateLfn );
  511. }
  512. return;
  513. }
  514. VOID
  515. RxEvaluateNameCase (
  516. IN PRX_CONTEXT RxContext,
  517. IN PUNICODE_STRING UnicodeName,
  518. IN OUT BOOLEAN *AllLowerComponent,
  519. IN OUT BOOLEAN *AllLowerExtension,
  520. IN OUT BOOLEAN *CreateLfn
  521. )
  522. /*++
  523. Routine Description:
  524. This routine takes a UNICODE string and sees if it is eligible for
  525. the special case optimization.
  526. Arguments:
  527. UnicodeName - Provides the original final name.
  528. AllLowerComponent - Returns whether this compoent was all lower case.
  529. AllLowerExtension - Returns wheather the extension was all lower case.
  530. CreateLfn - Tells the call if we must create an LFN for the UnicodeName.
  531. Return Value:
  532. None.
  533. --*/
  534. {
  535. ULONG i;
  536. UCHAR Uppers = 0;
  537. UCHAR Lowers = 0;
  538. BOOLEAN ExtensionPresent = FALSE;
  539. *CreateLfn = FALSE;
  540. for (i = 0; i < UnicodeName->Length / sizeof(WCHAR); i++) {
  541. WCHAR c;
  542. c = UnicodeName->Buffer[i];
  543. if ((c >= 'A') && (c <= 'Z')) {
  544. Uppers += 1;
  545. } else if ((c >= 'a') && (c <= 'z')) {
  546. Lowers += 1;
  547. } else if (c >= 0x0080) {
  548. break;
  549. }
  550. //
  551. // If we come to a period, figure out if the extension was
  552. // all one case.
  553. //
  554. if (c == L'.') {
  555. *CreateLfn = (Lowers != 0) && (Uppers != 0);
  556. *AllLowerComponent = !(*CreateLfn) && (Lowers != 0);
  557. ExtensionPresent = TRUE;
  558. //
  559. // Now reset the uppers and lowers count.
  560. //
  561. Uppers = Lowers = 0;
  562. }
  563. }
  564. //
  565. // Now check again for creating an LFN.
  566. //
  567. *CreateLfn = (*CreateLfn ||
  568. (i != UnicodeName->Length / sizeof(WCHAR)) ||
  569. ((Lowers != 0) && (Uppers != 0)));
  570. //
  571. // Now we know the final state of CreateLfn, update the two
  572. // "AllLower" booleans.
  573. //
  574. if (ExtensionPresent) {
  575. *AllLowerComponent = !(*CreateLfn) && *AllLowerComponent;
  576. *AllLowerExtension = !(*CreateLfn) && (Lowers != 0);
  577. } else {
  578. *AllLowerComponent = !(*CreateLfn) && (Lowers != 0);
  579. *AllLowerExtension = FALSE;
  580. }
  581. return;
  582. }
  583.