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.

1356 lines
38 KiB

  1. /*++
  2. Copyright (c) 1991-2000 Microsoft Corporation
  3. Module Name:
  4. NameSup.c
  5. Abstract:
  6. This module implements the Udfs Name support routines
  7. // @@BEGIN_DDKSPLIT
  8. Author:
  9. Dan Lovinger [DanLo] 9-October-1996
  10. Revision History:
  11. // @@END_DDKSPLIT
  12. --*/
  13. #include "UdfProcs.h"
  14. //
  15. // The Bug check file id for this module
  16. //
  17. #define BugCheckFileId (UDFS_BUG_CHECK_NAMESUP)
  18. //
  19. // The local debug trace level
  20. //
  21. #define Dbg (UDFS_DEBUG_LEVEL_NAMESUP)
  22. //
  23. // Local constants
  24. //
  25. static CONST CHAR UdfCrcChar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#_~-@";
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text(PAGE, UdfCandidateShortName)
  28. #pragma alloc_text(PAGE, UdfCheckLegalCS0Dstring)
  29. #pragma alloc_text(PAGE, UdfConvertCS0DstringToUnicode)
  30. #pragma alloc_text(PAGE, UdfDissectName)
  31. #pragma alloc_text(PAGE, UdfFullCompareNames)
  32. #pragma alloc_text(PAGE, UdfGenerate8dot3Name)
  33. #pragma alloc_text(PAGE, UdfIs8dot3Name)
  34. #pragma alloc_text(PAGE, UdfIsNameInExpression)
  35. #pragma alloc_text(PAGE, UdfRenderNameToLegalUnicode)
  36. #endif
  37. INLINE
  38. ULONG
  39. NativeDosCharLength (
  40. IN WCHAR Wchar
  41. )
  42. /*++
  43. Routine Description:
  44. This routine is a translation layer for asking how big a given UNICODE
  45. character will be when converted to OEM. Aside from adding more material
  46. to the kernel export table, this is how ya do it.
  47. Arguments:
  48. Wchar - pointer to the character
  49. Return Value:
  50. Size in bytes.
  51. --*/
  52. {
  53. NTSTATUS Status;
  54. CHAR OemBuf[2];
  55. ULONG Length;
  56. Status = RtlUpcaseUnicodeToOemN( OemBuf,
  57. sizeof(OemBuf),
  58. &Length,
  59. &Wchar,
  60. sizeof(WCHAR));
  61. ASSERT( NT_SUCCESS( Status ));
  62. return Length;
  63. }
  64. VOID
  65. UdfDissectName (
  66. IN PIRP_CONTEXT IrpContext,
  67. IN OUT PUNICODE_STRING RemainingName,
  68. OUT PUNICODE_STRING FinalName
  69. )
  70. /*++
  71. Routine Description:
  72. This routine is called to strip off leading components of the name strings. We search
  73. for either the end of the string or separating characters. The input remaining
  74. name strings should have neither a trailing or leading backslash.
  75. Arguments:
  76. RemainingName - Remaining name.
  77. FinalName - Location to store next component of name.
  78. Return Value:
  79. None.
  80. --*/
  81. {
  82. ULONG NameLength;
  83. PWCHAR NextWchar;
  84. PAGED_CODE();
  85. //
  86. // Check inputs.
  87. //
  88. ASSERT_IRP_CONTEXT( IrpContext );
  89. //
  90. // Find the offset of the next component separators.
  91. //
  92. for (NameLength = 0, NextWchar = RemainingName->Buffer;
  93. (NameLength < RemainingName->Length) && (*NextWchar != L'\\');
  94. NameLength += sizeof( WCHAR) , NextWchar += 1);
  95. //
  96. // Adjust all the strings by this amount.
  97. //
  98. FinalName->Buffer = RemainingName->Buffer;
  99. FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength;
  100. //
  101. // If this is the last component then set the RemainingName lengths to zero.
  102. //
  103. if (NameLength == RemainingName->Length) {
  104. RemainingName->Length = 0;
  105. //
  106. // Otherwise we adjust the string by this amount plus the separating character.
  107. //
  108. } else {
  109. RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR ));
  110. RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR ));
  111. RemainingName->Buffer = Add2Ptr( RemainingName->Buffer,
  112. NameLength + sizeof( WCHAR ),
  113. PWCHAR );
  114. }
  115. return;
  116. }
  117. BOOLEAN
  118. UdfIs8dot3Name (
  119. IN PIRP_CONTEXT IrpContext,
  120. IN UNICODE_STRING FileName
  121. )
  122. /*++
  123. Routine Description:
  124. This routine checks if the name follows the 8.3 name conventions. We check for
  125. the name length and whether the characters are valid.
  126. Arguments:
  127. FileName - String of bytes containing the name.
  128. Return Value:
  129. BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise.
  130. --*/
  131. {
  132. CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ];
  133. STRING DbcsName;
  134. PWCHAR NextWchar;
  135. ULONG Count;
  136. ULONG DotCount = 0;
  137. BOOLEAN LastCharDot = FALSE;
  138. PAGED_CODE();
  139. //
  140. // Check inputs.
  141. //
  142. ASSERT_IRP_CONTEXT( IrpContext );
  143. //
  144. // The length must be less than 24 bytes.
  145. //
  146. ASSERT( FileName.Length != 0 );
  147. if (FileName.Length > BYTE_COUNT_8_DOT_3) {
  148. return FALSE;
  149. }
  150. //
  151. // Walk though and check for a space character.
  152. //
  153. NextWchar = FileName.Buffer;
  154. Count = 0;
  155. do {
  156. //
  157. // No spaces allowed.
  158. //
  159. if (*NextWchar == L' ') { return FALSE; }
  160. if (*NextWchar == L'.') {
  161. //
  162. // Not an 8.3 name if more than 1 dot or more than 8 characters
  163. // remaining. (It is legal for the dot to be in the ninth
  164. // position)
  165. //
  166. if ((DotCount > 0) ||
  167. (Count > 8 * sizeof( WCHAR ))) {
  168. return FALSE;
  169. }
  170. DotCount += 1;
  171. LastCharDot = TRUE;
  172. } else {
  173. LastCharDot = FALSE;
  174. }
  175. Count += 2;
  176. NextWchar += 1;
  177. } while (Count < FileName.Length);
  178. //
  179. // We can't have a period at the end of the name.
  180. //
  181. if (LastCharDot) {
  182. return FALSE;
  183. }
  184. //
  185. // Create an Oem name to use to check for a valid short name.
  186. //
  187. DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3;
  188. DbcsName.Buffer = DbcsNameBuffer;
  189. if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName,
  190. &FileName,
  191. FALSE ))) {
  192. return FALSE;
  193. }
  194. //
  195. // We have now initialized the Oem string. Call the FsRtl package to check for a
  196. // valid FAT name.
  197. //
  198. return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE );
  199. }
  200. BOOLEAN
  201. UdfCandidateShortName (
  202. IN PIRP_CONTEXT IrpContext,
  203. IN PUNICODE_STRING Name
  204. )
  205. /*++
  206. Routine Description:
  207. This routine is called to determine if the input name could be a generated
  208. short name.
  209. Arguments:
  210. Name - Pointer to the name to stare at.
  211. Return Value:
  212. BOOLEAN True if it is possible that this is a shortname, False otherwise.
  213. --*/
  214. {
  215. ULONG Index, SubIndex;
  216. BOOLEAN LooksShort = FALSE;
  217. PAGED_CODE();
  218. //
  219. // Check inputs.
  220. //
  221. ASSERT_IRP_CONTEXT( IrpContext );
  222. //
  223. // The length can't be larger than an 8.3 name and must be
  224. // at least as big as the uniqifier stamp.
  225. //
  226. ASSERT( Name->Length != 0 );
  227. if (Name->Length > BYTE_COUNT_8_DOT_3 ||
  228. Name->Length < DOS_CRC_LEN * sizeof(WCHAR)) {
  229. return FALSE;
  230. }
  231. //
  232. // Walk across the name looking for the uniquifier stamp. The stamp
  233. // is of the form #<hex><hex><hex> so if we can stop before the end
  234. // of the full name.
  235. //
  236. for ( Index = 0;
  237. Index <= (Name->Length / sizeof(WCHAR)) - DOS_CRC_LEN;
  238. Index++ ) {
  239. //
  240. // Is the current character the stamp UDF uses to offset the stamp?
  241. //
  242. if (Name->Buffer[Index] == CRC_MARK) {
  243. //
  244. // We may potentially have just a CRC at the end
  245. // of the name OR have a period following. If we
  246. // do, it is reasonable to think the name may be
  247. // a generated shorty.
  248. //
  249. // #123 (a very special case - orignal name was ".")
  250. // FOO#123
  251. // FOO#123.TXT
  252. //
  253. if (Index == (Name->Length / sizeof(WCHAR)) - DOS_CRC_LEN ||
  254. Name->Buffer[Index + DOS_CRC_LEN] == PERIOD) {
  255. LooksShort = TRUE;
  256. break;
  257. }
  258. }
  259. }
  260. return LooksShort;
  261. }
  262. VOID
  263. UdfGenerate8dot3Name (
  264. IN PIRP_CONTEXT IrpContext,
  265. IN PUNICODE_STRING FileName,
  266. OUT PUNICODE_STRING ShortFileName
  267. )
  268. /*++
  269. Routine Description:
  270. This routine is called to generate a short name from the given long name. We will
  271. generate a short name from the given long name.
  272. The short form is to convert all runs of illegal characters to "_" and tack
  273. on a base41 representation of the CRC of the original name. The algorithm is
  274. nearly directly lifted from the UDF (2.01 proposed!) standard, so apologies for the
  275. style clash.
  276. Arguments:
  277. FileName - String of bytes containing the name.
  278. ShortFileName - Pointer to the string to store the short name into.
  279. Return Value:
  280. None.
  281. --*/
  282. {
  283. INT16 index;
  284. INT16 targetIndex;
  285. INT16 crcIndex;
  286. INT16 extLen;
  287. INT16 nameLen;
  288. INT16 charLen;
  289. INT16 overlayBytes;
  290. INT16 bytesLeft;
  291. UNICODE_CHAR current;
  292. BOOLEAN needsCRC;
  293. UNICODE_CHAR ext[DOS_EXT_LEN];
  294. //
  295. // So as to lift as directly as possible from the standard, chunk things around.
  296. //
  297. PWCHAR dosName = ShortFileName->Buffer;
  298. PWCHAR udfName = FileName->Buffer;
  299. SHORT udfNameLen = FileName->Length / sizeof(WCHAR);
  300. needsCRC = FALSE;
  301. /* Start at the end of the UDF file name and scan for a period */
  302. /* ('.'). This will be where the DOS extension starts (if */
  303. /* any). */
  304. index = udfNameLen;
  305. while (index-- > 0) {
  306. if (udfName[index] == PERIOD)
  307. break;
  308. }
  309. if (index < 0) {
  310. /* There name was scanned to the beginning of the buffer */
  311. /* and no extension was found. */
  312. extLen = 0;
  313. nameLen = udfNameLen;
  314. }
  315. else {
  316. /* A DOS extension was found, process it first. */
  317. extLen = udfNameLen - index - 1;
  318. nameLen = index;
  319. targetIndex = 0;
  320. bytesLeft = DOS_EXT_LEN;
  321. while (++index < udfNameLen && bytesLeft > 0) {
  322. /* Get the current character and convert it to upper */
  323. /* case. */
  324. current = UnicodeToUpper(udfName[index]);
  325. if (current == SPACE) {
  326. /* If a space is found, a CRC must be appended to */
  327. /* the mangled file name. */
  328. needsCRC = TRUE;
  329. }
  330. else {
  331. /* Determine if this is a valid file name char and */
  332. /* calculate its corresponding BCS character byte */
  333. /* length (zero if the char is not legal or */
  334. /* undisplayable on this system). */
  335. charLen = (IsFileNameCharLegal(current)) ?
  336. (USHORT)NativeDosCharLength(current) : 0;
  337. /* If the char is larger than the available space */
  338. /* in the buffer, pretend it is undisplayable. */
  339. if (charLen > bytesLeft)
  340. charLen = 0;
  341. if (charLen == 0) {
  342. /* Undisplayable or illegal characters are */
  343. /* substituted with an underscore ("_"), and */
  344. /* required a CRC code appended to the mangled */
  345. /* file name. */
  346. needsCRC = TRUE;
  347. charLen = 1;
  348. current = ILLEGAL_CHAR_MARK;
  349. /* Skip over any following undiplayable or */
  350. /* illegal chars. */
  351. while (index + 1 < udfNameLen &&
  352. (!IsFileNameCharLegal(udfName[index + 1]) ||
  353. NativeDosCharLength(udfName[index + 1]) == 0))
  354. index++;
  355. }
  356. /* Assign the resulting char to the next index in */
  357. /* the extension buffer and determine how many BCS */
  358. /* bytes are left. */
  359. ext[targetIndex++] = current;
  360. bytesLeft -= charLen;
  361. }
  362. }
  363. /* Save the number of Unicode characters in the extension */
  364. extLen = targetIndex;
  365. /* If the extension was too large, or it was zero length */
  366. /* (i.e. the name ended in a period), a CRC code must be */
  367. /* appended to the mangled name. */
  368. if (index < udfNameLen || extLen == 0)
  369. needsCRC = TRUE;
  370. }
  371. /* Now process the actual file name. */
  372. index = 0;
  373. targetIndex = 0;
  374. crcIndex = 0;
  375. overlayBytes = -1;
  376. bytesLeft = DOS_NAME_LEN;
  377. while (index < nameLen && bytesLeft > 0) {
  378. /* Get the current character and convert it to upper case. */
  379. current = UnicodeToUpper(udfName[index]);
  380. if (current == SPACE || current == PERIOD) {
  381. /* Spaces and periods are just skipped, a CRC code */
  382. /* must be added to the mangled file name. */
  383. needsCRC = TRUE;
  384. }
  385. else {
  386. /* Determine if this is a valid file name char and */
  387. /* calculate its corresponding BCS character byte */
  388. /* length (zero if the char is not legal or */
  389. /* undisplayable on this system). */
  390. charLen = (IsFileNameCharLegal(current)) ?
  391. (USHORT)NativeDosCharLength(current) : 0;
  392. /* If the char is larger than the available space in */
  393. /* the buffer, pretend it is undisplayable. */
  394. if (charLen > bytesLeft)
  395. charLen = 0;
  396. if (charLen == 0) {
  397. /* Undisplayable or illegal characters are */
  398. /* substituted with an underscore ("_"), and */
  399. /* required a CRC code appended to the mangled */
  400. /* file name. */
  401. needsCRC = TRUE;
  402. charLen = 1;
  403. current = ILLEGAL_CHAR_MARK;
  404. /* Skip over any following undiplayable or illegal */
  405. /* chars. */
  406. while (index + 1 < nameLen &&
  407. (!IsFileNameCharLegal(udfName[index + 1]) ||
  408. NativeDosCharLength(udfName[index + 1]) == 0))
  409. index++;
  410. /* Terminate loop if at the end of the file name. */
  411. if (index >= nameLen)
  412. break;
  413. }
  414. /* Assign the resulting char to the next index in the */
  415. /* file name buffer and determine how many BCS bytes */
  416. /* are left. */
  417. dosName[targetIndex++] = current;
  418. bytesLeft -= charLen;
  419. /* This figures out where the CRC code needs to start */
  420. /* in the file name buffer. */
  421. if (bytesLeft >= DOS_CRC_LEN) {
  422. /* If there is enough space left, just tack it */
  423. /* onto the end. */
  424. crcIndex = targetIndex;
  425. }
  426. else {
  427. /* If there is not enough space left, the CRC */
  428. /* must overlay a character already in the file */
  429. /* name buffer. Once this condition has been */
  430. /* met, the value will not change. */
  431. if (overlayBytes < 0) {
  432. /* Determine the index and save the length of */
  433. /* the BCS character that is overlayed. It */
  434. /* is possible that the CRC might overlay */
  435. /* half of a two-byte BCS character depending */
  436. /* upon how the character boundaries line up. */
  437. overlayBytes = (bytesLeft + charLen > DOS_CRC_LEN)
  438. ? 1 : 0;
  439. crcIndex = targetIndex - 1;
  440. }
  441. }
  442. }
  443. /* Advance to the next character. */
  444. index++;
  445. }
  446. /* If the scan did not reach the end of the file name, or the */
  447. /* length of the file name is zero, a CRC code is needed. */
  448. if (index < nameLen || index == 0)
  449. needsCRC = TRUE;
  450. /* If the name has illegal characters or and extension, it */
  451. /* is not a DOS device name. */
  452. if (needsCRC == FALSE && extLen == 0) {
  453. /* If this is the name of a DOS device, a CRC code should */
  454. /* be appended to the file name. */
  455. if (IsDeviceName(udfName, udfNameLen))
  456. needsCRC = TRUE;
  457. }
  458. /* Append the CRC code to the file name, if needed. */
  459. if (needsCRC) {
  460. /* Get the CRC value for the original Unicode string */
  461. UINT16 udfCRCValue;
  462. //
  463. // In UDF 2.00, the sample code changed to take the CRC
  464. // from the UNICODE expansion of the CS0 as opposed to
  465. // the CS0 itself. In UDF 2.01, the wording of the spec
  466. // will actually match this.
  467. //
  468. // Additionally, the checksum changes to be byte-order
  469. // independent.
  470. //
  471. udfCRCValue = UdfComputeCrc16Uni(udfName, udfNameLen);
  472. /* Determine the character index where the CRC should */
  473. /* begin. */
  474. targetIndex = crcIndex;
  475. /* If the character being overlayed is a two-byte BCS */
  476. /* character, replace the first byte with an underscore. */
  477. if (overlayBytes > 0)
  478. dosName[targetIndex++] = ILLEGAL_CHAR_MARK;
  479. //
  480. // UDF 2.01 changes to a base 41 encoding. UDF 1.50 and
  481. // UDF 2.00 exchanged the # delimeter with the high 4bits
  482. // of the CRC.
  483. //
  484. dosName[targetIndex++] = CRC_MARK;
  485. dosName[targetIndex++] =
  486. UdfCrcChar[udfCRCValue / (41 * 41)];
  487. udfCRCValue %= (41 * 41);
  488. dosName[targetIndex++] =
  489. UdfCrcChar[udfCRCValue / 41];
  490. udfCRCValue %= 41;
  491. dosName[targetIndex++] =
  492. UdfCrcChar[udfCRCValue];
  493. }
  494. /* Append the extension, if any. */
  495. if (extLen > 0) {
  496. /* Tack on a period and each successive byte in the */
  497. /* extension buffer. */
  498. dosName[targetIndex++] = PERIOD;
  499. for (index = 0; index < extLen; index++)
  500. dosName[targetIndex++] = ext[index];
  501. }
  502. ASSERT( (targetIndex * sizeof(WCHAR)) <= ShortFileName->MaximumLength );
  503. ShortFileName->Length = (USHORT) (targetIndex * sizeof(WCHAR));
  504. //
  505. // Now we upcase the whole name at once.
  506. //
  507. UdfUpcaseName( IrpContext,
  508. ShortFileName,
  509. ShortFileName );
  510. }
  511. VOID
  512. UdfConvertCS0DstringToUnicode (
  513. IN PIRP_CONTEXT IrpContext,
  514. IN PUCHAR Dstring,
  515. IN UCHAR Length OPTIONAL,
  516. IN UCHAR FieldLength OPTIONAL,
  517. IN OUT PUNICODE_STRING Name
  518. )
  519. /*++
  520. Routine Description:
  521. This routine will convert the CS0 input dstring (1/7.2.12) to Unicode. We assume that
  522. the length is sane.
  523. This "compression" in CS0 is really just a special case hack for ASCII.
  524. Arguments:
  525. Dstring - the input dstring field
  526. Length - length of the dstring. If unspecified, we assume that the characters come
  527. from a proper 1/7.2.12 dstring that specifies length in the last character of the
  528. field.
  529. FieldLength - length of the dstring field. If unspecified, we assume that the characters
  530. come from an uncounted length of CS0 characters and that the Length parameter is
  531. specified.
  532. Name - the output Unicode string
  533. Return Value:
  534. None.
  535. --*/
  536. {
  537. ULONG CompressID;
  538. ULONG UnicodeIndex, ByteIndex;
  539. PWCHAR Unicode = Name->Buffer;
  540. UCHAR NameLength;
  541. ULONG CopyNameLength;
  542. PAGED_CODE();
  543. //
  544. // Check input.
  545. //
  546. ASSERT_IRP_CONTEXT( IrpContext );
  547. CompressID = *Dstring;
  548. //
  549. // If the length is unspecified, this is a real 1/7.2.12 dstring and the length is in
  550. // the last character of the field.
  551. //
  552. ASSERT( Length || FieldLength );
  553. if (Length) {
  554. NameLength = FieldLength = Length;
  555. } else {
  556. NameLength = *(Dstring + FieldLength - 1);
  557. }
  558. //
  559. // If the caller specified a size, they should have made sure the buffer is big enough.
  560. // Otherwise, we will trim to fit.
  561. //
  562. ASSERT( Length == 0 || Name->MaximumLength >= UdfCS0DstringUnicodeSize( IrpContext, Dstring, NameLength ) );
  563. //
  564. // Decide how many UNICODE bytes to "copy".
  565. //
  566. CopyNameLength = Min( Name->MaximumLength, UdfCS0DstringUnicodeSize( IrpContext, Dstring, NameLength ));
  567. //
  568. // Reset the name length and advance over the compression ID in the dstring.
  569. //
  570. Name->Length = 0;
  571. Dstring++;
  572. //
  573. // Loop through all the bytes.
  574. //
  575. while (CopyNameLength > Name->Length) {
  576. if (CompressID == 16) {
  577. //
  578. // We're little endian, and this is the single place in the entire UDF/ISO standard
  579. // where they use big endian.
  580. //
  581. // Thank you. Thank you very much.
  582. //
  583. // Do an unaligned swapcopy of this 16bit value.
  584. //
  585. SwapCopyUchar2( Unicode, Dstring );
  586. Dstring += sizeof(WCHAR);
  587. } else {
  588. //
  589. // Drop the byte into the low bits.
  590. //
  591. *Unicode = *Dstring;
  592. Dstring += sizeof(CHAR);
  593. }
  594. Name->Length += sizeof(WCHAR);
  595. Unicode++;
  596. }
  597. return;
  598. }
  599. BOOLEAN
  600. UdfCheckLegalCS0Dstring (
  601. PIRP_CONTEXT IrpContext,
  602. PUCHAR Dstring,
  603. UCHAR Length OPTIONAL,
  604. UCHAR FieldLength OPTIONAL,
  605. BOOLEAN ReturnOnError
  606. )
  607. /*++
  608. Routine Description:
  609. This routine inspects a CS0 Dstring for conformance.
  610. Arguments:
  611. Dstring - a dstring to check
  612. Length - length of the dstring. If unspecified, we assume that the characters come
  613. from a proper 1/7.2.12 dstring that specifies length in the last character of the
  614. field.
  615. FieldLength - length of the dstring field. If unspecified, we assume that the characters
  616. come from an uncounted length of CS0 characters and that the Length parameter is
  617. specified.
  618. ReturnOnError - whether to return or raise on a discovered error
  619. Return Value:
  620. None. Raised status if corruption is found.
  621. --*/
  622. {
  623. UCHAR NameLength;
  624. //
  625. // Check input.
  626. //
  627. ASSERT_IRP_CONTEXT( IrpContext );
  628. //
  629. // If the length is unspecified, this is a real 1/7.2.12 dstring and the length is in
  630. // the last character of the field.
  631. //
  632. ASSERT( Length || FieldLength );
  633. if (Length) {
  634. NameLength = FieldLength = Length;
  635. } else {
  636. NameLength = *(Dstring + FieldLength - 1);
  637. }
  638. DebugTrace(( +1, Dbg,
  639. "UdfCheckLegalCS0Dstring, Dstring %08x Length %02x FieldLength %02x (NameLength %02x)\n",
  640. Dstring,
  641. Length,
  642. FieldLength,
  643. NameLength ));
  644. //
  645. // The string must be "compressed" in 8bit or 16bit chunks. If it
  646. // is in 16bit chunks, we better have an integral number of them -
  647. // remember we have the compression ID, so the length will be odd.
  648. //
  649. if ((NameLength <= 1 &&
  650. DebugTrace(( 0, Dbg,
  651. "UdfCheckLegalCS0Dstring, NameLength is too small!\n" ))) ||
  652. (NameLength > FieldLength &&
  653. DebugTrace(( 0, Dbg,
  654. "UdfCheckLegalCS0Dstring, NameLength is bigger than the field itself!\n" ))) ||
  655. ((*Dstring != 8 && *Dstring != 16) &&
  656. DebugTrace(( 0, Dbg,
  657. "UdfCheckLegalCS0Dstring, claims encoding %02x, unknown! (not 0x8 or 0x10)\n",
  658. *Dstring ))) ||
  659. ((*Dstring == 16 && !FlagOn( NameLength, 1)) &&
  660. DebugTrace(( 0, Dbg,
  661. "UdfCheckLegalCS0Dstring, NameLength not odd, encoding 0x10!\n" )))) {
  662. if (ReturnOnError) {
  663. DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> FALSE\n" ));
  664. return FALSE;
  665. }
  666. DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> raised status\n" ));
  667. UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
  668. }
  669. DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> TRUE\n" ));
  670. return TRUE;
  671. }
  672. VOID
  673. UdfRenderNameToLegalUnicode (
  674. IN PIRP_CONTEXT IrpContext,
  675. IN PUNICODE_STRING Name,
  676. IN PUNICODE_STRING RenderedName
  677. )
  678. /*++
  679. Routine Description:
  680. This routine will take a Unicode string containing illegal characters and
  681. run it through the UDF standard algorithim to render it into a "legal"
  682. name.
  683. The short form is to convert all runs of illegal characters to "_" and tack
  684. on a hex representation of the CRC of the original name. The algorithm is
  685. nearly directly lifted from the UDF (2.01 proposed!) standard, so apologies
  686. for the style clash.
  687. Arguments:
  688. Name - the actual name
  689. RenderedName - the name rendered into legal characters
  690. Return Value:
  691. BOOLEAN - TRUE if the expressions match, FALSE otherwise.
  692. --*/
  693. {
  694. INT16 index;
  695. INT16 targetIndex;
  696. INT16 crcIndex;
  697. INT16 extLen;
  698. INT16 nameLen;
  699. INT16 charLen;
  700. INT16 overlayBytes;
  701. INT16 bytesLeft;
  702. UNICODE_CHAR current;
  703. BOOLEAN needsCRC;
  704. BOOLEAN foundDot;
  705. UNICODE_CHAR ext[EXT_LEN];
  706. //
  707. // So as to lift as directly as possible from the standard, chunk things around.
  708. //
  709. PWCHAR newName = RenderedName->Buffer;
  710. PWCHAR udfName = Name->Buffer;
  711. SHORT udfNameLen = Name->Length / sizeof(WCHAR);
  712. /* Remove trailing periods ('.') and spaces (' '), Windows */
  713. /* does not like them. */
  714. foundDot = FALSE;
  715. index = udfNameLen;
  716. while (index-- > 0) {
  717. if (udfName[index] == PERIOD)
  718. foundDot = TRUE;
  719. else if (udfName[index] != SPACE)
  720. break;
  721. }
  722. needsCRC = FALSE;
  723. bytesLeft = MAX_LEN;
  724. extLen = 0;
  725. /* If any trailing periods or spaces were found, a CRC code */
  726. /* needs to be added to the resulting file name. */
  727. nameLen = index + 1;
  728. if (nameLen < udfNameLen)
  729. needsCRC = TRUE;
  730. if (needsCRC == FALSE || foundDot == FALSE) {
  731. /* Look for an extension in the file name. We do not */
  732. /* need to look for one if there were any trailing periods */
  733. /* or spaces removed. */
  734. INT16 endIndex;
  735. INT16 prevCharLen = 1;
  736. INT16 extBytes = 0;
  737. targetIndex = 0;
  738. index = nameLen;
  739. /* Determine how many bytes we need to scan to find the */
  740. /* extension delimiter. The extension has a maximum of */
  741. /* five characters, but we do not want to scan past the */
  742. /* beginning of the buffer. */
  743. endIndex = (udfNameLen > EXT_LEN + 1) ?
  744. udfNameLen - EXT_LEN - 1 : 1;
  745. /* Start at the end of the name and scan backward, looking */
  746. /* for the extension delimiter ("."). */
  747. while (index-- > endIndex) {
  748. /* Get the character to test. */
  749. current = udfName[index];
  750. if (current == '.') {
  751. /* The extension delimiter was found, figure out */
  752. /* how many characters the extension contains and */
  753. /* the length of the resulting file name without */
  754. /* the extension. */
  755. extLen = nameLen - index - 1;
  756. nameLen = index;
  757. break;
  758. }
  759. /* Determine the byte length of the current character */
  760. /* when converted to native format. */
  761. charLen = (IsFileNameCharLegal(current)) ?
  762. NativeCharLength(current) : 0;
  763. if (charLen == 0) {
  764. /* If the character byte length is zero, it is */
  765. /* illegal or unprintable, place an underscore */
  766. /* ("_") in the extension buffer if the previous */
  767. /* character tested was legal. Not that the */
  768. /* characters placed in the extension buffer are */
  769. /* in reverse order. */
  770. if (prevCharLen != 0) {
  771. ext[targetIndex++] = ILLEGAL_CHAR_MARK;
  772. extBytes++;
  773. }
  774. }
  775. else {
  776. /* The current character is legal and printable, */
  777. /* put it in the extension buffer. Note that the */
  778. /* characters placed in the extension buffer are */
  779. /* in reverse order. */
  780. ext[targetIndex++] = current;
  781. extBytes += charLen;
  782. }
  783. /* Save the byte length of the current character, so */
  784. /* we can determine if it was a legal character during */
  785. /* the next test. */
  786. prevCharLen = charLen;
  787. }
  788. /* If an extension was found, determine how many bytes */
  789. /* remain in the file name buffer once we account for it. */
  790. if (extLen > 0)
  791. bytesLeft -= extBytes + 1;
  792. }
  793. index = 0;
  794. targetIndex = 0;
  795. crcIndex = 0;
  796. overlayBytes = -1;
  797. while (index < nameLen && bytesLeft > 0) {
  798. /* Get the current character and convert it to upper case. */
  799. current = udfName[index];
  800. /* Determine if this is a valid file name char and */
  801. /* calculate its corresponding native character byte */
  802. /* length (zero if the char is not legal or undiplayable */
  803. /* on this system). */
  804. charLen = (IsFileNameCharLegal(current)) ?
  805. NativeCharLength(current) : 0;
  806. /* If the char is larger than the available space in the */
  807. /* buffer, pretend it is undisplayable. */
  808. if (charLen > bytesLeft)
  809. charLen = 0;
  810. if (charLen == 0) {
  811. /* Undisplayable or illegal characters are substituted */
  812. /* with an underscore ("_"), and requires a CRC code */
  813. /* appended to the mangled file name. */
  814. needsCRC = TRUE;
  815. charLen = 1;
  816. current = '_';
  817. /* Skip over any following undiplayable or illegal */
  818. /* chars. */
  819. while (index + 1 < udfNameLen &&
  820. (!IsFileNameCharLegal(udfName[index + 1]) ||
  821. NativeCharLength(udfName[index + 1]) == 0))
  822. index++;
  823. /* Terminate loop if at the end of the file name. */
  824. if (index >= udfNameLen)
  825. break;
  826. }
  827. /* Assign the resulting char to the next index in the file */
  828. /* name buffer and determine how many native bytes are */
  829. /* left. */
  830. newName[targetIndex++] = current;
  831. bytesLeft -= charLen;
  832. /* This figures out where the CRC code needs to start in */
  833. /* the file name buffer. */
  834. if (bytesLeft >= CRC_LEN) {
  835. /* If there is enough space left, just tack it onto */
  836. /* the end. */
  837. crcIndex = targetIndex;
  838. }
  839. else {
  840. /* If there is not enough space left, the CRC must */
  841. /* overlay a character already in the file name */
  842. /* buffer. Once this condition has been met, the */
  843. /* value will not change. */
  844. if (overlayBytes < 0) {
  845. /* Determine the index and save the length of the */
  846. /* native character that is overlayed. It is */
  847. /* possible that the CRC might overlay half of a */
  848. /* two-byte native character depending upon how */
  849. /* the character boundaries line up. */
  850. overlayBytes = (bytesLeft + charLen > CRC_LEN)
  851. ? 1 : 0;
  852. crcIndex = targetIndex - 1;
  853. }
  854. }
  855. /* Advance to the next character. */
  856. index++;
  857. }
  858. /* If the scan did not reach the end of the file name, or the */
  859. /* length of the file name is zero, a CRC code is needed. */
  860. if (index < nameLen || index == 0)
  861. needsCRC = TRUE;
  862. /* If the name has illegal characters or and extension, it */
  863. /* is not a DOS device name. */
  864. if (needsCRC == FALSE && extLen == 0) {
  865. /* If this is the name of a DOS device, a CRC code should */
  866. /* be appended to the file name. */
  867. if (IsDeviceName(udfName, udfNameLen))
  868. needsCRC = TRUE;
  869. }
  870. /* Append the CRC code to the file name, if needed. */
  871. if (needsCRC) {
  872. /* Get the CRC value for the original Unicode string */
  873. UINT16 udfCRCValue = UdfComputeCrc16Uni(udfName, udfNameLen);
  874. /* Determine the character index where the CRC should */
  875. /* begin. */
  876. targetIndex = crcIndex;
  877. /* If the character being overlayed is a two-byte native */
  878. /* character, replace the first byte with an underscore. */
  879. if (overlayBytes > 0)
  880. newName[targetIndex++] = ILLEGAL_CHAR_MARK;
  881. /* Append the encoded CRC value with delimiter. */
  882. newName[targetIndex++] = CRC_MARK;
  883. newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0xf000) >> 12];
  884. newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x0f00) >> 8];
  885. newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x00f0) >> 4];
  886. newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x000f)];
  887. }
  888. /* If an extension was found, append it here. */
  889. if (extLen > 0) {
  890. /* Add the period ('.') for the extension delimiter. */
  891. newName[targetIndex++] = PERIOD;
  892. /* Append the characters in the extension buffer. They */
  893. /* were stored in reverse order, so we need to begin with */
  894. /* the last character and work forward. */
  895. while (extLen-- > 0)
  896. newName[targetIndex++] = ext[extLen];
  897. }
  898. ASSERT( (targetIndex * sizeof(WCHAR)) <= RenderedName->MaximumLength );
  899. RenderedName->Length = (USHORT) (targetIndex * sizeof(WCHAR));
  900. }
  901. BOOLEAN
  902. UdfIsNameInExpression (
  903. IN PIRP_CONTEXT IrpContext,
  904. IN PUNICODE_STRING CurrentName,
  905. IN PUNICODE_STRING SearchExpression,
  906. IN BOOLEAN Wild
  907. )
  908. /*++
  909. Routine Description:
  910. This routine will compare two Unicode strings. We assume that if this
  911. is to be a case-insensitive search then they are already upcased.
  912. Arguments:
  913. CurrentName - Filename from the disk.
  914. SearchExpression - Filename expression to use for match.
  915. Wild - True if wildcards are present in SearchExpression.
  916. Return Value:
  917. BOOLEAN - TRUE if the expressions match, FALSE otherwise.
  918. --*/
  919. {
  920. BOOLEAN Match = TRUE;
  921. PAGED_CODE();
  922. //
  923. // Check inputs.
  924. //
  925. ASSERT_IRP_CONTEXT( IrpContext );
  926. //
  927. // If there are wildcards in the expression then we call the
  928. // appropriate FsRtlRoutine.
  929. //
  930. if (Wild) {
  931. Match = FsRtlIsNameInExpression( SearchExpression,
  932. CurrentName,
  933. FALSE,
  934. NULL );
  935. //
  936. // Otherwise do a direct memory comparison for the name string.
  937. //
  938. } else {
  939. if ((CurrentName->Length != SearchExpression->Length) ||
  940. (!RtlEqualMemory( CurrentName->Buffer,
  941. SearchExpression->Buffer,
  942. CurrentName->Length ))) {
  943. Match = FALSE;
  944. }
  945. }
  946. return Match;
  947. }
  948. FSRTL_COMPARISON_RESULT
  949. UdfFullCompareNames (
  950. IN PIRP_CONTEXT IrpContext,
  951. IN PUNICODE_STRING NameA,
  952. IN PUNICODE_STRING NameB
  953. )
  954. /*++
  955. Routine Description:
  956. This function compares two names as fast as possible. Note that since
  957. this comparison is case sensitive we can do a direct memory comparison.
  958. Arguments:
  959. NameA & NameB - The names to compare.
  960. Return Value:
  961. COMPARISON - returns
  962. LessThan if NameA < NameB lexicalgraphically,
  963. GreaterThan if NameA > NameB lexicalgraphically,
  964. EqualTo if NameA is equal to NameB
  965. --*/
  966. {
  967. ULONG i;
  968. ULONG MinLength = NameA->Length;
  969. FSRTL_COMPARISON_RESULT Result = LessThan;
  970. PAGED_CODE();
  971. //
  972. // Check inputs.
  973. //
  974. ASSERT_IRP_CONTEXT( IrpContext );
  975. //
  976. // Figure out the minimum of the two lengths
  977. //
  978. if (NameA->Length > NameB->Length) {
  979. MinLength = NameB->Length;
  980. Result = GreaterThan;
  981. } else if (NameA->Length == NameB->Length) {
  982. Result = EqualTo;
  983. }
  984. //
  985. // Loop through looking at all of the characters in both strings
  986. // testing for equalilty, less than, and greater than
  987. //
  988. i = (ULONG) RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength );
  989. if (i < MinLength) {
  990. //
  991. // We know the offset of the first character which is different.
  992. //
  993. return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ?
  994. LessThan :
  995. GreaterThan);
  996. }
  997. //
  998. // The names match up to the length of the shorter string.
  999. // The shorter string lexically appears first.
  1000. //
  1001. return Result;
  1002. }