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.

1023 lines
27 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. NameSup.c
  5. Abstract:
  6. This module implements the Ntfs Name support routines
  7. Author:
  8. Gary Kimura [GaryKi] & Tom Miller [TomM] 20-Feb-1990
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. #define Dbg (DEBUG_TRACE_NAMESUP)
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text(PAGE, NtfsCollateNames)
  15. #pragma alloc_text(PAGE, NtfsIsFatNameValid)
  16. #pragma alloc_text(PAGE, NtfsIsFileNameValid)
  17. #pragma alloc_text(PAGE, NtfsParseName)
  18. #pragma alloc_text(PAGE, NtfsParsePath)
  19. #pragma alloc_text(PAGE, NtfsUpcaseName)
  20. #endif
  21. #define MAX_CHARS_IN_8_DOT_3 (12)
  22. PARSE_TERMINATION_REASON
  23. NtfsParsePath (
  24. IN UNICODE_STRING Path,
  25. IN BOOLEAN WildCardsPermissible,
  26. OUT PUNICODE_STRING FirstPart,
  27. OUT PNTFS_NAME_DESCRIPTOR Name,
  28. OUT PUNICODE_STRING RemainingPart
  29. )
  30. /*++
  31. Routine Description:
  32. This routine takes as input a path. Each component of the path is
  33. checked until either:
  34. - The end of the path has been reached, or
  35. - A well formed complex name is excountered, or
  36. - An illegal character is encountered, or
  37. - A complex name component is malformed
  38. At this point the return value is set to one of the three reasons
  39. above, and the arguments are set as follows:
  40. FirstPart: All the components up to one containing an illegal
  41. character or colon character. May be the whole path.
  42. Name: The "pieces" of a component containing an illegal
  43. character or colon character. This name is actually
  44. a struncture containing the four pieces of a name,
  45. "file name, attribute type, attribute name, version
  46. number." In the example below, they are shown
  47. separated by plus signs.
  48. RemainingPart: All the remaining components.
  49. A preceding or trailing backslash is ignored during processing and
  50. stripped in either FirstPart or RemainingPart. Following are some
  51. examples of this routine's actions.
  52. Path FirstPart Name Remaining
  53. ================ ========= ============ =========
  54. \nt\pri\os \nt\pri\os <empty>
  55. \nt\pri\os\ \nt\pri\os <empty>
  56. nt\pri\os \nt\pri\os <empty>
  57. \nt\pr"\os \nt pr" os
  58. \nt\pri\os:contr::3\ntfs \nt\pri os + contr + + 3 ntfs
  59. \nt\pri\os\circle:pict:circ \nt\pri\os circle + pict + circ <empty>
  60. Arguments:
  61. Path - This unicode string descibes the path to parse. Note that path
  62. here may only describe a single component.
  63. WildCardsPermissible - This parameter tells us if wild card characters
  64. should be considered legal.
  65. FirstPart - This unicode string will receive portion of the path, up to
  66. a component boundry, successfully parsed before the parse terminated.
  67. Note that store for this string comes from the Path parameter.
  68. Name - This is the name we were parsing when we reached our termination
  69. condition. It is a srtucture of strings that receive the file name,
  70. attribute type, attribute name, and version number respectively.
  71. It wil be filled in only to the extent that the parse succeeded. For
  72. example, in the case we encounter an illegal character in the
  73. attribute type field, only the file name field will be filled in.
  74. This may signal a special control file, and this possibility must be
  75. investigated by the file system.
  76. RemainingPart - This string will receive any portion of the path, starting
  77. at the first component boundry after the termination name, not parsed.
  78. It will often be an empty string.
  79. ReturnValue:
  80. An enumerated type with one of the following values:
  81. EndOfPathReached - The path was fully parsed. Only first part
  82. is filled in.
  83. NonSimpleName - A component of the path containing a legal,
  84. well formed non-simple name was encountered.
  85. IllegalCharacterInName - An illegal character was encountered. Parsing
  86. stops immediately.
  87. MalFormedName - A non-simple name did not conform to the
  88. correct format. This may be a result of too
  89. many fields, or a malformed version number.
  90. AttributeOnly - A component of the path containing a legal
  91. well formed non-simple name was encountered
  92. which does not have a file name.
  93. VersionNumberPresent - A component of the path containing a legal
  94. well formed non-simple name was encountered
  95. which contains a version number.
  96. --*/
  97. {
  98. UNICODE_STRING FirstName;
  99. BOOLEAN WellFormed;
  100. BOOLEAN MoreNamesInPath;
  101. BOOLEAN FirstIteration;
  102. BOOLEAN FoundIllegalCharacter;
  103. PARSE_TERMINATION_REASON TerminationReason;
  104. PAGED_CODE();
  105. //
  106. // Initialize some loacal variables and OUT parameters.
  107. //
  108. FirstIteration = TRUE;
  109. MoreNamesInPath = TRUE;
  110. //
  111. // Clear the fieldspresent flag in the name descriptor.
  112. //
  113. Name->FieldsPresent = 0;
  114. //
  115. // By default, set the returned first part to start at the beginning of
  116. // the input buffer and include a leading backslash.
  117. //
  118. FirstPart->Buffer = Path.Buffer;
  119. if (Path.Buffer[0] == L'\\') {
  120. FirstPart->Length = 2;
  121. FirstPart->MaximumLength = 2;
  122. } else {
  123. FirstPart->Length = 0;
  124. FirstPart->MaximumLength = 0;
  125. }
  126. //
  127. // Do the first check outside the loop in case we are given a backslash
  128. // by itself.
  129. //
  130. if (FirstPart->Length == Path.Length) {
  131. RemainingPart->Length = 0;
  132. RemainingPart->Buffer = &Path.Buffer[Path.Length >> 1];
  133. return EndOfPathReached;
  134. }
  135. //
  136. // Crack the path, checking each componant
  137. //
  138. while (MoreNamesInPath) {
  139. FsRtlDissectName( Path, &FirstName, RemainingPart );
  140. MoreNamesInPath = (BOOLEAN)(RemainingPart->Length != 0);
  141. //
  142. // If this is not the last name in the path, then attributes
  143. // and version numbers are not allowed. If this is the last
  144. // name then propagate the callers arguments.
  145. //
  146. WellFormed = NtfsParseName( FirstName,
  147. WildCardsPermissible,
  148. &FoundIllegalCharacter,
  149. Name );
  150. //
  151. // Check the cases when we will break out of this loop, ie. if the
  152. // the name was not well formed or it was non-simple.
  153. //
  154. if ( !WellFormed ||
  155. (Name->FieldsPresent != FILE_NAME_PRESENT_FLAG)
  156. //
  157. // TEMPCODE TRAILING_DOT
  158. //
  159. || (Name->FileName.Length != Name->FileName.MaximumLength)
  160. ) {
  161. break;
  162. }
  163. //
  164. // We will continue parsing this string, so consider the current
  165. // FirstName to be parsed and add it to FirstPart. Also reset
  166. // the Name->FieldsPresent variable.
  167. //
  168. if ( FirstIteration ) {
  169. FirstPart->Length += FirstName.Length;
  170. FirstIteration = FALSE;
  171. } else {
  172. FirstPart->Length += (sizeof(WCHAR) + FirstName.Length);
  173. }
  174. FirstPart->MaximumLength = FirstPart->Length;
  175. Path = *RemainingPart;
  176. }
  177. //
  178. // At this point FirstPart, Name, and RemainingPart should all be set
  179. // correctly. It remains, only to generate the correct return value.
  180. //
  181. if ( !WellFormed ) {
  182. if ( FoundIllegalCharacter ) {
  183. TerminationReason = IllegalCharacterInName;
  184. } else {
  185. TerminationReason = MalFormedName;
  186. }
  187. } else {
  188. if ( Name->FieldsPresent == FILE_NAME_PRESENT_FLAG ) {
  189. //
  190. // TEMPCODE TRAILING_DOT
  191. //
  192. if (Name->FileName.Length != Name->FileName.MaximumLength) {
  193. TerminationReason = NonSimpleName;
  194. } else {
  195. TerminationReason = EndOfPathReached;
  196. }
  197. } else if (FlagOn( Name->FieldsPresent, VERSION_NUMBER_PRESENT_FLAG )) {
  198. TerminationReason = VersionNumberPresent;
  199. } else if (!FlagOn( Name->FieldsPresent, FILE_NAME_PRESENT_FLAG )) {
  200. TerminationReason = AttributeOnly;
  201. } else {
  202. TerminationReason = NonSimpleName;
  203. }
  204. }
  205. return TerminationReason;
  206. }
  207. BOOLEAN
  208. NtfsParseName (
  209. IN const UNICODE_STRING Name,
  210. IN BOOLEAN WildCardsPermissible,
  211. OUT PBOOLEAN FoundIllegalCharacter,
  212. OUT PNTFS_NAME_DESCRIPTOR ParsedName
  213. )
  214. /*++
  215. Routine Description:
  216. This routine takes as input a single name component. It is processed into
  217. file name, attribute type, attribute name, and version number fields.
  218. If the name is well formed according to the following rules:
  219. A. An NTFS name may not contain any of the following characters:
  220. 0x0000-0x001F " / < > ? | *
  221. B. An Ntfs name can take any of the following forms:
  222. ::T
  223. :A
  224. :A:T
  225. N
  226. N:::V
  227. N::T
  228. N::T:V
  229. N:A
  230. N:A::V
  231. N:A:T
  232. N:A:T:V
  233. If a version number is present, there must be a file name.
  234. We specifically note the legal names without a filename
  235. component (AttributeOnly) and any name with a version number
  236. (VersionNumberPresent).
  237. Incidently, N corresponds to file name, T to attribute type, A to
  238. attribute name, and V to version number.
  239. TRUE is returned. If FALSE is returned, then the OUT parameter
  240. FoundIllegalCharacter will be set appropriatly. Note that the buffer
  241. space for ParsedName comes from Name.
  242. Arguments:
  243. Name - This is the single path element input name.
  244. WildCardsPermissible - This determines if wild cards characters should be
  245. considered legal
  246. FoundIllegalCharacter - This parameter will receive a TRUE if the the
  247. function returns FALSE because of encountering an illegal character.
  248. ParsedName - Recieves the pieces of the processed name. Note that the
  249. storage for all the string from the input Name.
  250. ReturnValue:
  251. TRUE if the Name is well formed, and FALSE otherwise.
  252. --*/
  253. {
  254. ULONG Index;
  255. ULONG NameLength;
  256. ULONG FieldCount;
  257. ULONG FieldIndexes[5];
  258. UCHAR ValidCharFlags = FSRTL_NTFS_LEGAL;
  259. PULONG Fields;
  260. BOOLEAN IsNameValid = TRUE;
  261. PAGED_CODE();
  262. //
  263. // Initialize some OUT parameters and local variables.
  264. //
  265. *FoundIllegalCharacter = FALSE;
  266. Fields = &ParsedName->FieldsPresent;
  267. *Fields = 0;
  268. FieldCount = 1;
  269. FieldIndexes[0] = 0xFFFFFFFF; // We add on to this later...
  270. //
  271. // For starters, zero length names are invalid.
  272. //
  273. NameLength = Name.Length / sizeof(WCHAR);
  274. if ( NameLength == 0 ) {
  275. return FALSE;
  276. }
  277. //
  278. // Now name must correspond to a legal single Ntfs Name.
  279. //
  280. for (Index = 0; Index < NameLength; Index += 1) {
  281. WCHAR Char;
  282. Char = Name.Buffer[Index];
  283. //
  284. // First check that file names are well formed in terms of colons.
  285. //
  286. if ( Char == L':' ) {
  287. //
  288. // A colon can't be the last character, and we can't have
  289. // more than three colons.
  290. //
  291. if ( (Index == NameLength - 1) ||
  292. (FieldCount >= 4) ) {
  293. IsNameValid = FALSE;
  294. break;
  295. }
  296. FieldIndexes[FieldCount] = Index;
  297. FieldCount += 1;
  298. ValidCharFlags = FSRTL_NTFS_STREAM_LEGAL;
  299. continue;
  300. }
  301. //
  302. // Now check for wild card characters if they weren't allowed,
  303. // and other illegal characters.
  304. //
  305. if ((Char <= 0xff) &&
  306. !FsRtlTestAnsiCharacter( Char, TRUE, WildCardsPermissible, ValidCharFlags )) {
  307. IsNameValid = FALSE;
  308. *FoundIllegalCharacter = TRUE;
  309. break;
  310. }
  311. }
  312. //
  313. // If we ran into a problem with one of the fields, don't try to load
  314. // up that field into the out parameter.
  315. //
  316. if ( !IsNameValid ) {
  317. FieldCount -= 1;
  318. //
  319. // Set the end of the last field to the current Index.
  320. //
  321. } else {
  322. FieldIndexes[FieldCount] = Index;
  323. }
  324. //
  325. // Now we load up the OUT parmeters
  326. //
  327. while ( FieldCount != 0 ) {
  328. ULONG StartIndex;
  329. ULONG EndIndex;
  330. USHORT Length;
  331. //
  332. // Add one here since this is actually the position of the colon.
  333. //
  334. StartIndex = FieldIndexes[FieldCount - 1] + 1;
  335. EndIndex = FieldIndexes[FieldCount];
  336. Length = (USHORT)((EndIndex - StartIndex) * sizeof(WCHAR));
  337. //
  338. // If this field is empty, skip it
  339. //
  340. if ( Length == 0 ) {
  341. FieldCount -= 1;
  342. continue;
  343. }
  344. //
  345. // Now depending of the field, extract the appropriate information.
  346. //
  347. if ( FieldCount == 1 ) {
  348. UNICODE_STRING TempName;
  349. TempName.Buffer = &Name.Buffer[StartIndex];
  350. TempName.Length = Length;
  351. TempName.MaximumLength = Length;
  352. //
  353. // If the resulting length is 0, forget this entry.
  354. //
  355. if (TempName.Length == 0) {
  356. FieldCount -= 1;
  357. continue;
  358. }
  359. SetFlag(*Fields, FILE_NAME_PRESENT_FLAG);
  360. ParsedName->FileName = TempName;
  361. } else if ( FieldCount == 2) {
  362. SetFlag(*Fields, ATTRIBUTE_NAME_PRESENT_FLAG);
  363. ParsedName->AttributeName.Buffer = &Name.Buffer[StartIndex];
  364. ParsedName->AttributeName.Length = Length;
  365. ParsedName->AttributeName.MaximumLength = Length;
  366. } else if ( FieldCount == 3) {
  367. SetFlag(*Fields, ATTRIBUTE_TYPE_PRESENT_FLAG);
  368. ParsedName->AttributeType.Buffer = &Name.Buffer[StartIndex];
  369. ParsedName->AttributeType.Length = Length;
  370. ParsedName->AttributeType.MaximumLength = Length;
  371. } else if ( FieldCount == 4) {
  372. ULONG VersionNumber;
  373. STRING VersionNumberA;
  374. UNICODE_STRING VersionNumberU;
  375. NTSTATUS Status;
  376. UCHAR *endp = NULL;
  377. VersionNumberU.Buffer = &Name.Buffer[StartIndex];
  378. VersionNumberU.Length = Length;
  379. VersionNumberU.MaximumLength = Length;
  380. //
  381. // Note that the resulting Ansi string is null terminated.
  382. //
  383. Status = RtlUnicodeStringToCountedOemString( &VersionNumberA,
  384. &VersionNumberU,
  385. TRUE );
  386. //
  387. // If something went wrong (most likely ran out of pool), raise.
  388. //
  389. if ( !NT_SUCCESS(Status) ) {
  390. ExRaiseStatus( Status );
  391. }
  392. VersionNumber = 0; //**** strtoul( VersionNumberA.Buffer, &endp, 0 );
  393. RtlFreeOemString( &VersionNumberA );
  394. if ( (VersionNumber == MAXULONG) || (endp != NULL) ) {
  395. IsNameValid = FALSE;
  396. } else {
  397. SetFlag( *Fields, VERSION_NUMBER_PRESENT_FLAG );
  398. ParsedName->VersionNumber = VersionNumber;
  399. }
  400. }
  401. FieldCount -= 1;
  402. }
  403. //
  404. // Check for special malformed cases.
  405. //
  406. if (FlagOn( *Fields, VERSION_NUMBER_PRESENT_FLAG )
  407. && !FlagOn( *Fields, FILE_NAME_PRESENT_FLAG )) {
  408. IsNameValid = FALSE;
  409. }
  410. return IsNameValid;
  411. }
  412. VOID
  413. NtfsUpcaseName (
  414. IN PWCH UpcaseTable,
  415. IN ULONG UpcaseTableSize,
  416. IN OUT PUNICODE_STRING Name
  417. )
  418. /*++
  419. Routine Description:
  420. This routine upcases a string.
  421. Arguments:
  422. UpcaseTable - Pointer to an array of Unicode upcased characters indexed by
  423. the Unicode character to be upcased.
  424. UpcaseTableSize - Size of the Upcase table in unicode characters
  425. Name - Supplies the string to upcase
  426. Return Value:
  427. None.
  428. --*/
  429. {
  430. ULONG i;
  431. ULONG Length;
  432. PAGED_CODE();
  433. DebugTrace( +1, Dbg, ("NtfsUpcaseName\n") );
  434. DebugTrace( 0, Dbg, ("Name = %Z\n", Name) );
  435. Length = Name->Length / sizeof(WCHAR);
  436. for (i=0; i < Length; i += 1) {
  437. if ((ULONG)Name->Buffer[i] < UpcaseTableSize) {
  438. Name->Buffer[i] = UpcaseTable[ (ULONG)Name->Buffer[i] ];
  439. }
  440. }
  441. DebugTrace( 0, Dbg, ("Upcased Name = %Z\n", Name) );
  442. DebugTrace( -1, Dbg, ("NtfsUpcaseName -> VOID\n") );
  443. return;
  444. }
  445. FSRTL_COMPARISON_RESULT
  446. NtfsCollateNames (
  447. IN PCWCH UpcaseTable,
  448. IN ULONG UpcaseTableSize,
  449. IN PCUNICODE_STRING Expression,
  450. IN PCUNICODE_STRING Name,
  451. IN FSRTL_COMPARISON_RESULT WildIs,
  452. IN BOOLEAN IgnoreCase
  453. )
  454. /*++
  455. Routine Description:
  456. This routine compares an expression with a name lexigraphically for
  457. LessThan, EqualTo, or GreaterThan. If the expression does not contain
  458. any wildcards, this procedure does a complete comparison. If the
  459. expression does contain wild cards, then the comparison is only done up
  460. to the first wildcard character. Name may not contain wild cards.
  461. The wildcard character compares as less then all other characters. So
  462. the wildcard name "*.*" will always compare less than all all strings.
  463. Arguments:
  464. UpcaseTable - Pointer to an array of Unicode upcased characters indexed by
  465. the Unicode character to be upcased.
  466. UpcaseTableSize - Size of the Upcase table in unicode characters
  467. Expression - Supplies the first name expression to compare, optionally with
  468. wild cards. Note that caller must have already upcased
  469. the name (this will make lookup faster).
  470. Name - Supplies the second name to compare - no wild cards allowed.
  471. The caller must have already upcased the name.
  472. WildIs - Determines what Result is returned if a wild card is encountered
  473. in the Expression String. For example, to find the start of
  474. an expression in the Btree, LessThan should be supplied; then
  475. GreaterThan should be supplied to find the end of the expression
  476. in the tree.
  477. IgnoreCase - TRUE if case should be ignored for the comparison
  478. Return Value:
  479. FSRTL_COMPARISON_RESULT - LessThan if Expression < Name
  480. EqualTo if Expression == Name
  481. GreaterThan if Expression > Name
  482. --*/
  483. {
  484. WCHAR ConstantChar;
  485. WCHAR ExpressionChar;
  486. ULONG i;
  487. ULONG Length;
  488. PAGED_CODE();
  489. DebugTrace( +1, Dbg, ("NtfsCollateNames\n") );
  490. DebugTrace( 0, Dbg, ("Expression = %Z\n", Expression) );
  491. DebugTrace( 0, Dbg, ("Name = %Z\n", Name) );
  492. DebugTrace( 0, Dbg, ("WildIs = %08lx\n", WildIs) );
  493. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  494. //
  495. // Calculate the length in wchars that we need to compare. This will
  496. // be the smallest length of the two strings.
  497. //
  498. if (Expression->Length < Name->Length) {
  499. Length = Expression->Length / sizeof(WCHAR);
  500. } else {
  501. Length = Name->Length / sizeof(WCHAR);
  502. }
  503. //
  504. // Now we'll just compare the elements in the names until we can decide
  505. // their lexicagrahical ordering, checking for wild cards in
  506. // LocalExpression (from Expression).
  507. //
  508. // If an upcase table was specified, the compare is done case insensitive.
  509. //
  510. for (i = 0; i < Length; i += 1) {
  511. ConstantChar = Name->Buffer[i];
  512. ExpressionChar = Expression->Buffer[i];
  513. if ( IgnoreCase ) {
  514. if (ConstantChar < UpcaseTableSize) {
  515. ConstantChar = UpcaseTable[(ULONG)ConstantChar];
  516. }
  517. if (ExpressionChar < UpcaseTableSize) {
  518. ExpressionChar = UpcaseTable[(ULONG)ExpressionChar];
  519. }
  520. }
  521. if ( FsRtlIsUnicodeCharacterWild(ExpressionChar) ) {
  522. DebugTrace( -1, Dbg, ("NtfsCollateNames -> %08lx (Wild)\n", WildIs) );
  523. return WildIs;
  524. }
  525. if ( ExpressionChar < ConstantChar ) {
  526. DebugTrace( -1, Dbg, ("NtfsCollateNames -> LessThan\n") );
  527. return LessThan;
  528. }
  529. if ( ExpressionChar > ConstantChar ) {
  530. DebugTrace( -1, Dbg, ("NtfsCollateNames -> GreaterThan\n") );
  531. return GreaterThan;
  532. }
  533. }
  534. //
  535. // We've gone through the entire short match and they're equal
  536. // so we need to now check which one is shorter, or, if
  537. // LocalExpression is longer, we need to see if the next character is
  538. // wild! (For example, an enumeration of "ABC*", must return
  539. // "ABC".
  540. //
  541. if (Expression->Length < Name->Length) {
  542. DebugTrace( -1, Dbg, ("NtfsCollateNames -> LessThan (length)\n") );
  543. return LessThan;
  544. }
  545. if (Expression->Length > Name->Length) {
  546. if (FsRtlIsUnicodeCharacterWild(Expression->Buffer[i])) {
  547. DebugTrace( -1, Dbg, ("NtfsCollateNames -> %08lx (trailing wild)\n", WildIs) );
  548. return WildIs;
  549. }
  550. DebugTrace( -1, Dbg, ("NtfsCollateNames -> GreaterThan (length)\n") );
  551. return GreaterThan;
  552. }
  553. DebugTrace( -1, Dbg, ("NtfsCollateNames -> EqualTo\n") );
  554. return EqualTo;
  555. }
  556. BOOLEAN
  557. NtfsIsFileNameValid (
  558. IN PUNICODE_STRING FileName,
  559. IN BOOLEAN WildCardsPermissible
  560. )
  561. /*++
  562. Routine Description:
  563. This routine checks if the specified file name is valid. Note that
  564. only the file name part of the name is allowed, ie. no colons are
  565. permitted.
  566. Arguments:
  567. FileName - Supplies the name to check.
  568. WildCardsPermissible - Tells us if wild card characters are ok.
  569. Return Value:
  570. BOOLEAN - TRUE if the name is valid, FALSE otherwise.
  571. --*/
  572. {
  573. ULONG Index;
  574. ULONG NameLength;
  575. BOOLEAN AllDots = TRUE;
  576. BOOLEAN IsNameValid = TRUE;
  577. PAGED_CODE();
  578. DebugTrace( +1, Dbg, ("NtfsIsFileNameValid\n") );
  579. DebugTrace( 0, Dbg, ("FileName = %Z\n", FileName) );
  580. DebugTrace( 0, Dbg, ("WildCardsPermissible = %s\n",
  581. WildCardsPermissible ? "TRUE" : "FALSE") );
  582. //
  583. // It better be a valid unicode string.
  584. //
  585. if ((FileName->Length == 0) || FlagOn( FileName->Length, 1 )) {
  586. IsNameValid = FALSE;
  587. } else {
  588. //
  589. // Check if corresponds to a legal single Ntfs Name.
  590. //
  591. NameLength = FileName->Length / sizeof(WCHAR);
  592. for (Index = 0; Index < NameLength; Index += 1) {
  593. WCHAR Char;
  594. Char = FileName->Buffer[Index];
  595. //
  596. // Check for wild card characters if they weren't allowed, and
  597. // check for the other illegal characters including the colon and
  598. // backslash characters since this can only be a single component.
  599. //
  600. if ( ((Char <= 0xff) &&
  601. !FsRtlIsAnsiCharacterLegalNtfs(Char, WildCardsPermissible)) ||
  602. (Char == L':') ||
  603. (Char == L'\\') ) {
  604. IsNameValid = FALSE;
  605. break;
  606. }
  607. //
  608. // Remember if this is not a '.' character.
  609. //
  610. if (Char != L'.') {
  611. AllDots = FALSE;
  612. }
  613. }
  614. //
  615. // The names '.' and '..' are also invalid.
  616. //
  617. if (AllDots
  618. && (NameLength == 1
  619. || NameLength == 2)) {
  620. IsNameValid = FALSE;
  621. }
  622. }
  623. DebugTrace( -1, Dbg, ("NtfsIsFileNameValid -> %s\n", IsNameValid ? "TRUE" : "FALSE") );
  624. return IsNameValid;
  625. }
  626. BOOLEAN
  627. NtfsIsFatNameValid (
  628. IN PUNICODE_STRING FileName,
  629. IN BOOLEAN WildCardsPermissible
  630. )
  631. /*++
  632. Routine Description:
  633. This routine checks if the specified file name is conformant to the
  634. Fat 8.3 file naming rules.
  635. Arguments:
  636. FileName - Supplies the name to check.
  637. WildCardsPermissible - Tells us if wild card characters are ok.
  638. Return Value:
  639. BOOLEAN - TRUE if the name is valid, FALSE otherwise.
  640. --*/
  641. {
  642. BOOLEAN Results;
  643. STRING DbcsName;
  644. USHORT i;
  645. CHAR Buffer[24];
  646. WCHAR wc;
  647. PAGED_CODE();
  648. //
  649. // If the name is more than 24 bytes then it can't be a valid Fat name.
  650. //
  651. if (FileName->Length > 24) {
  652. return FALSE;
  653. }
  654. //
  655. // We will do some extra checking ourselves because we really want to be
  656. // fairly restrictive of what an 8.3 name contains. That way
  657. // we will then generate an 8.3 name for some nomially valid 8.3
  658. // names (e.g., names that contain DBCS characters). The extra characters
  659. // we'll filter off are those characters less than and equal to the space
  660. // character and those beyond lowercase z.
  661. //
  662. if (FlagOn( NtfsData.Flags,NTFS_FLAGS_ALLOW_EXTENDED_CHAR )) {
  663. for (i = 0; i < FileName->Length / sizeof( WCHAR ); i += 1) {
  664. wc = FileName->Buffer[i];
  665. if ((wc <= 0x0020) || (wc == 0x007c)) { return FALSE; }
  666. }
  667. } else {
  668. for (i = 0; i < FileName->Length / sizeof( WCHAR ); i += 1) {
  669. wc = FileName->Buffer[i];
  670. if ((wc <= 0x0020) || (wc >= 0x007f) || (wc == 0x007c)) { return FALSE; }
  671. }
  672. }
  673. //
  674. // The characters match up okay so now build up the dbcs string to call
  675. // the fsrtl routine to check for legal 8.3 formation
  676. //
  677. Results = FALSE;
  678. DbcsName.MaximumLength = 24;
  679. DbcsName.Buffer = Buffer;
  680. if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, FALSE))) {
  681. if (FsRtlIsFatDbcsLegal( DbcsName, WildCardsPermissible, FALSE, FALSE )) {
  682. Results = TRUE;
  683. }
  684. }
  685. //
  686. // And return to our caller
  687. //
  688. return Results;
  689. }