Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

namesup.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1991 Microsoft Corporation 00004 00005 Module Name: 00006 00007 NameSup.c 00008 00009 Abstract: 00010 00011 This module implements the Udfs Name support routines 00012 00013 Author: 00014 00015 Dan Lovinger [DanLo] 9-October-1996 00016 00017 Revision History: 00018 00019 --*/ 00020 00021 #include "UdfProcs.h" 00022 00023 // 00024 // The Bug check file id for this module 00025 // 00026 00027 #define BugCheckFileId (UDFS_BUG_CHECK_NAMESUP) 00028 00029 // 00030 // The local debug trace level 00031 // 00032 00033 #define Dbg (UDFS_DEBUG_LEVEL_NAMESUP) 00034 00035 // 00036 // Local constants 00037 // 00038 00039 static CONST CHAR UdfCrcChar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#_~-@"; 00040 00041 #ifdef ALLOC_PRAGMA 00042 #pragma alloc_text(PAGE, UdfCandidateShortName) 00043 #pragma alloc_text(PAGE, UdfCheckLegalCS0Dstring) 00044 #pragma alloc_text(PAGE, UdfConvertCS0DstringToUnicode) 00045 #pragma alloc_text(PAGE, UdfDissectName) 00046 #pragma alloc_text(PAGE, UdfFullCompareNames) 00047 #pragma alloc_text(PAGE, UdfGenerate8dot3Name) 00048 #pragma alloc_text(PAGE, UdfIs8dot3Name) 00049 #pragma alloc_text(PAGE, UdfIsNameInExpression) 00050 #pragma alloc_text(PAGE, UdfRenderNameToLegalUnicode) 00051 #endif 00052 00053 00054 INLINE 00055 ULONG 00056 NativeDosCharLength ( 00057 IN WCHAR Wchar 00058 ) 00059 00060 /*++ 00061 00062 Routine Description: 00063 00064 This routine is a translation layer for asking how big a given UNICODE 00065 character will be when converted to OEM. Aside from adding more material 00066 to the kernel export table, this is how ya do it. 00067 00068 Arguments: 00069 00070 Wchar - pointer to the character 00071 00072 Return Value: 00073 00074 Size in bytes. 00075 00076 --*/ 00077 00078 { 00079 NTSTATUS Status; 00080 CHAR OemBuf[2]; 00081 ULONG Length; 00082 00083 Status = RtlUpcaseUnicodeToOemN( OemBuf, 00084 sizeof(OemBuf), 00085 &Length, 00086 &Wchar, 00087 sizeof(WCHAR)); 00088 00089 ASSERT( NT_SUCCESS( Status )); 00090 00091 return Length; 00092 } 00093 00094 00095 VOID 00096 UdfDissectName ( 00097 IN PIRP_CONTEXT IrpContext, 00098 IN OUT PUNICODE_STRING RemainingName, 00099 OUT PUNICODE_STRING FinalName 00100 ) 00101 00102 /*++ 00103 00104 Routine Description: 00105 00106 This routine is called to strip off leading components of the name strings. We search 00107 for either the end of the string or separating characters. The input remaining 00108 name strings should have neither a trailing or leading backslash. 00109 00110 Arguments: 00111 00112 RemainingName - Remaining name. 00113 00114 FinalName - Location to store next component of name. 00115 00116 Return Value: 00117 00118 None. 00119 00120 --*/ 00121 00122 { 00123 ULONG NameLength; 00124 PWCHAR NextWchar; 00125 00126 PAGED_CODE(); 00127 00128 // 00129 // Check inputs. 00130 // 00131 00132 ASSERT_IRP_CONTEXT( IrpContext ); 00133 00134 // 00135 // Find the offset of the next component separators. 00136 // 00137 00138 for (NameLength = 0, NextWchar = RemainingName->Buffer; 00139 (NameLength < RemainingName->Length) && (*NextWchar != L'\\'); 00140 NameLength += sizeof( WCHAR) , NextWchar += 1); 00141 00142 // 00143 // Adjust all the strings by this amount. 00144 // 00145 00146 FinalName->Buffer = RemainingName->Buffer; 00147 00148 FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength; 00149 00150 // 00151 // If this is the last component then set the RemainingName lengths to zero. 00152 // 00153 00154 if (NameLength == RemainingName->Length) { 00155 00156 RemainingName->Length = 0; 00157 00158 // 00159 // Otherwise we adjust the string by this amount plus the separating character. 00160 // 00161 00162 } else { 00163 00164 RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR )); 00165 RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR )); 00166 RemainingName->Buffer = Add2Ptr( RemainingName->Buffer, 00167 NameLength + sizeof( WCHAR ), 00168 PWCHAR ); 00169 } 00170 00171 return; 00172 } 00173 00174 00175 BOOLEAN 00176 UdfIs8dot3Name ( 00177 IN PIRP_CONTEXT IrpContext, 00178 IN UNICODE_STRING FileName 00179 ) 00180 00181 /*++ 00182 00183 Routine Description: 00184 00185 This routine checks if the name follows the 8.3 name conventions. We check for 00186 the name length and whether the characters are valid. 00187 00188 Arguments: 00189 00190 FileName - String of bytes containing the name. 00191 00192 Return Value: 00193 00194 BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise. 00195 00196 --*/ 00197 00198 { 00199 CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ]; 00200 STRING DbcsName; 00201 00202 PWCHAR NextWchar; 00203 ULONG Count; 00204 00205 ULONG DotCount = 0; 00206 BOOLEAN LastCharDot = FALSE; 00207 00208 PAGED_CODE(); 00209 00210 // 00211 // Check inputs. 00212 // 00213 00214 ASSERT_IRP_CONTEXT( IrpContext ); 00215 00216 // 00217 // The length must be less than 24 bytes. 00218 // 00219 00220 ASSERT( FileName.Length != 0 ); 00221 if (FileName.Length > BYTE_COUNT_8_DOT_3) { 00222 00223 return FALSE; 00224 } 00225 00226 // 00227 // Walk though and check for a space character. 00228 // 00229 00230 NextWchar = FileName.Buffer; 00231 Count = 0; 00232 00233 do { 00234 00235 // 00236 // No spaces allowed. 00237 // 00238 00239 if (*NextWchar == L' ') { return FALSE; } 00240 00241 if (*NextWchar == L'.') { 00242 00243 // 00244 // Not an 8.3 name if more than 1 dot or more than 8 characters 00245 // remaining. (It is legal for the dot to be in the ninth 00246 // position) 00247 // 00248 00249 if ((DotCount > 0) || 00250 (Count > 8 * sizeof( WCHAR ))) { 00251 00252 return FALSE; 00253 } 00254 00255 DotCount += 1; 00256 LastCharDot = TRUE; 00257 00258 } else { 00259 00260 LastCharDot = FALSE; 00261 } 00262 00263 Count += 2; 00264 NextWchar += 1; 00265 00266 } while (Count < FileName.Length); 00267 00268 // 00269 // We can't have a period at the end of the name. 00270 // 00271 00272 if (LastCharDot) { 00273 00274 return FALSE; 00275 } 00276 00277 // 00278 // Create an Oem name to use to check for a valid short name. 00279 // 00280 00281 DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3; 00282 DbcsName.Buffer = DbcsNameBuffer; 00283 00284 if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName, 00285 &FileName, 00286 FALSE ))) { 00287 00288 return FALSE; 00289 } 00290 00291 // 00292 // We have now initialized the Oem string. Call the FsRtl package to check for a 00293 // valid FAT name. 00294 // 00295 00296 return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE ); 00297 } 00298 00299 00300 BOOLEAN 00301 UdfCandidateShortName ( 00302 IN PIRP_CONTEXT IrpContext, 00303 IN PUNICODE_STRING Name 00304 ) 00305 00306 /*++ 00307 00308 Routine Description: 00309 00310 This routine is called to determine if the input name could be a generated 00311 short name. 00312 00313 Arguments: 00314 00315 Name - Pointer to the name to stare at. 00316 00317 Return Value: 00318 00319 BOOLEAN True if it is possible that this is a shortname, False otherwise. 00320 00321 --*/ 00322 00323 { 00324 ULONG Index, SubIndex; 00325 BOOLEAN LooksShort = FALSE; 00326 00327 PAGED_CODE(); 00328 00329 // 00330 // Check inputs. 00331 // 00332 00333 ASSERT_IRP_CONTEXT( IrpContext ); 00334 00335 // 00336 // The length can't be larger than an 8.3 name and must be 00337 // at least as big as the uniqifier stamp. 00338 // 00339 00340 ASSERT( Name->Length != 0 ); 00341 00342 if (Name->Length > BYTE_COUNT_8_DOT_3 || 00343 Name->Length < DOS_CRC_LEN * sizeof(WCHAR)) { 00344 00345 return FALSE; 00346 } 00347 00348 // 00349 // Walk across the name looking for the uniquifier stamp. The stamp 00350 // is of the form #<hex><hex><hex> so if we can stop before the end 00351 // of the full name. 00352 // 00353 00354 for ( Index = 0; 00355 Index <= (Name->Length / sizeof(WCHAR)) - DOS_CRC_LEN; 00356 Index++ ) { 00357 00358 // 00359 // Is the current character the stamp UDF uses to offset the stamp? 00360 // 00361 00362 if (Name->Buffer[Index] == CRC_MARK) { 00363 00364 // 00365 // We may potentially have just a CRC at the end 00366 // of the name OR have a period following. If we 00367 // do, it is reasonable to think the name may be 00368 // a generated shorty. 00369 // 00370 // #123 (a very special case - orignal name was ".") 00371 // FOO#123 00372 // FOO#123.TXT 00373 // 00374 00375 if (Index == (Name->Length / sizeof(WCHAR)) - DOS_CRC_LEN || 00376 Name->Buffer[Index + DOS_CRC_LEN] == PERIOD) { 00377 00378 LooksShort = TRUE; 00379 break; 00380 } 00381 } 00382 } 00383 00384 return LooksShort; 00385 } 00386 00387 00388 VOID 00389 UdfGenerate8dot3Name ( 00390 IN PIRP_CONTEXT IrpContext, 00391 IN PUNICODE_STRING FileName, 00392 OUT PUNICODE_STRING ShortFileName 00393 ) 00394 00395 /*++ 00396 00397 Routine Description: 00398 00399 This routine is called to generate a short name from the given long name. We will 00400 generate a short name from the given long name. 00401 00402 The short form is to convert all runs of illegal characters to "_" and tack 00403 on a base41 representation of the CRC of the original name. The algorithm is 00404 nearly directly lifted from the UDF (2.01 proposed!) standard, so apologies for the 00405 style clash. 00406 00407 Arguments: 00408 00409 FileName - String of bytes containing the name. 00410 00411 ShortFileName - Pointer to the string to store the short name into. 00412 00413 Return Value: 00414 00415 None. 00416 00417 --*/ 00418 00419 { 00420 INT16 index; 00421 INT16 targetIndex; 00422 INT16 crcIndex; 00423 INT16 extLen; 00424 INT16 nameLen; 00425 INT16 charLen; 00426 INT16 overlayBytes; 00427 INT16 bytesLeft; 00428 UNICODE_CHAR current; 00429 BOOLEAN needsCRC; 00430 UNICODE_CHAR ext[DOS_EXT_LEN]; 00431 00432 // 00433 // So as to lift as directly as possible from the standard, chunk things around. 00434 // 00435 00436 PWCHAR dosName = ShortFileName->Buffer; 00437 PWCHAR udfName = FileName->Buffer; 00438 LONG udfNameLen = FileName->Length / sizeof(WCHAR); 00439 00440 needsCRC = FALSE; 00441 00442 /* Start at the end of the UDF file name and scan for a period */ 00443 /* ('.'). This will be where the DOS extension starts (if */ 00444 /* any). */ 00445 index = udfNameLen; 00446 while (index-- > 0) { 00447 if (udfName[index] == PERIOD) 00448 break; 00449 } 00450 00451 if (index < 0) { 00452 /* There name was scanned to the beginning of the buffer */ 00453 /* and no extension was found. */ 00454 extLen = 0; 00455 nameLen = udfNameLen; 00456 } 00457 else { 00458 /* A DOS extension was found, process it first. */ 00459 extLen = udfNameLen - index - 1; 00460 nameLen = index; 00461 targetIndex = 0; 00462 bytesLeft = DOS_EXT_LEN; 00463 00464 while (++index < udfNameLen && bytesLeft > 0) { 00465 /* Get the current character and convert it to upper */ 00466 /* case. */ 00467 current = UnicodeToUpper(udfName[index]); 00468 if (current == SPACE) { 00469 /* If a space is found, a CRC must be appended to */ 00470 /* the mangled file name. */ 00471 needsCRC = TRUE; 00472 } 00473 else { 00474 /* Determine if this is a valid file name char and */ 00475 /* calculate its corresponding BCS character byte */ 00476 /* length (zero if the char is not legal or */ 00477 /* undisplayable on this system). */ 00478 charLen = (IsFileNameCharLegal(current)) ? 00479 NativeDosCharLength(current) : 0; 00480 00481 /* If the char is larger than the available space */ 00482 /* in the buffer, pretend it is undisplayable. */ 00483 if (charLen > bytesLeft) 00484 charLen = 0; 00485 00486 if (charLen == 0) { 00487 /* Undisplayable or illegal characters are */ 00488 /* substituted with an underscore ("_"), and */ 00489 /* required a CRC code appended to the mangled */ 00490 /* file name. */ 00491 needsCRC = TRUE; 00492 charLen = 1; 00493 current = ILLEGAL_CHAR_MARK; 00494 00495 /* Skip over any following undiplayable or */ 00496 /* illegal chars. */ 00497 while (index + 1 < udfNameLen && 00498 (!IsFileNameCharLegal(udfName[index + 1]) || 00499 NativeDosCharLength(udfName[index + 1]) == 0)) 00500 index++; 00501 } 00502 00503 /* Assign the resulting char to the next index in */ 00504 /* the extension buffer and determine how many BCS */ 00505 /* bytes are left. */ 00506 ext[targetIndex++] = current; 00507 bytesLeft -= charLen; 00508 } 00509 } 00510 00511 /* Save the number of Unicode characters in the extension */ 00512 extLen = targetIndex; 00513 00514 /* If the extension was too large, or it was zero length */ 00515 /* (i.e. the name ended in a period), a CRC code must be */ 00516 /* appended to the mangled name. */ 00517 if (index < udfNameLen || extLen == 0) 00518 needsCRC = TRUE; 00519 } 00520 00521 /* Now process the actual file name. */ 00522 index = 0; 00523 targetIndex = 0; 00524 crcIndex = 0; 00525 overlayBytes = -1; 00526 bytesLeft = DOS_NAME_LEN; 00527 while (index < nameLen && bytesLeft > 0) { 00528 /* Get the current character and convert it to upper case. */ 00529 current = UnicodeToUpper(udfName[index]); 00530 if (current == SPACE || current == PERIOD) { 00531 /* Spaces and periods are just skipped, a CRC code */ 00532 /* must be added to the mangled file name. */ 00533 needsCRC = TRUE; 00534 } 00535 else { 00536 /* Determine if this is a valid file name char and */ 00537 /* calculate its corresponding BCS character byte */ 00538 /* length (zero if the char is not legal or */ 00539 /* undisplayable on this system). */ 00540 charLen = (IsFileNameCharLegal(current)) ? 00541 NativeDosCharLength(current) : 0; 00542 00543 /* If the char is larger than the available space in */ 00544 /* the buffer, pretend it is undisplayable. */ 00545 if (charLen > bytesLeft) 00546 charLen = 0; 00547 00548 if (charLen == 0) { 00549 /* Undisplayable or illegal characters are */ 00550 /* substituted with an underscore ("_"), and */ 00551 /* required a CRC code appended to the mangled */ 00552 /* file name. */ 00553 needsCRC = TRUE; 00554 charLen = 1; 00555 current = ILLEGAL_CHAR_MARK; 00556 00557 /* Skip over any following undiplayable or illegal */ 00558 /* chars. */ 00559 while (index + 1 < nameLen && 00560 (!IsFileNameCharLegal(udfName[index + 1]) || 00561 NativeDosCharLength(udfName[index + 1]) == 0)) 00562 index++; 00563 00564 /* Terminate loop if at the end of the file name. */ 00565 if (index >= nameLen) 00566 break; 00567 } 00568 00569 /* Assign the resulting char to the next index in the */ 00570 /* file name buffer and determine how many BCS bytes */ 00571 /* are left. */ 00572 dosName[targetIndex++] = current; 00573 bytesLeft -= charLen; 00574 00575 /* This figures out where the CRC code needs to start */ 00576 /* in the file name buffer. */ 00577 if (bytesLeft >= DOS_CRC_LEN) { 00578 /* If there is enough space left, just tack it */ 00579 /* onto the end. */ 00580 crcIndex = targetIndex; 00581 } 00582 else { 00583 /* If there is not enough space left, the CRC */ 00584 /* must overlay a character already in the file */ 00585 /* name buffer. Once this condition has been */ 00586 /* met, the value will not change. */ 00587 if (overlayBytes < 0) { 00588 /* Determine the index and save the length of */ 00589 /* the BCS character that is overlayed. It */ 00590 /* is possible that the CRC might overlay */ 00591 /* half of a two-byte BCS character depending */ 00592 /* upon how the character boundaries line up. */ 00593 overlayBytes = (bytesLeft + charLen > DOS_CRC_LEN) 00594 ? 1 : 0; 00595 crcIndex = targetIndex - 1; 00596 } 00597 } 00598 } 00599 00600 /* Advance to the next character. */ 00601 index++; 00602 } 00603 00604 /* If the scan did not reach the end of the file name, or the */ 00605 /* length of the file name is zero, a CRC code is needed. */ 00606 if (index < nameLen || index == 0) 00607 needsCRC = TRUE; 00608 00609 /* If the name has illegal characters or and extension, it */ 00610 /* is not a DOS device name. */ 00611 if (needsCRC == FALSE && extLen == 0) { 00612 /* If this is the name of a DOS device, a CRC code should */ 00613 /* be appended to the file name. */ 00614 if (IsDeviceName(udfName, udfNameLen)) 00615 needsCRC = TRUE; 00616 } 00617 00618 /* Append the CRC code to the file name, if needed. */ 00619 if (needsCRC) { 00620 /* Get the CRC value for the original Unicode string */ 00621 UINT16 udfCRCValue; 00622 UINT16 modulus; 00623 00624 // 00625 // In UDF 2.00, the sample code changed to take the CRC 00626 // from the UNICODE expansion of the CS0 as opposed to 00627 // the CS0 itself. In UDF 2.01, the wording of the spec 00628 // will actually match this. 00629 // 00630 // Additionally, the checksum changes to be byte-order 00631 // independent. 00632 // 00633 00634 udfCRCValue = UdfComputeCrc16Uni(udfName, udfNameLen); 00635 00636 /* Determine the character index where the CRC should */ 00637 /* begin. */ 00638 targetIndex = crcIndex; 00639 00640 /* If the character being overlayed is a two-byte BCS */ 00641 /* character, replace the first byte with an underscore. */ 00642 if (overlayBytes > 0) 00643 dosName[targetIndex++] = ILLEGAL_CHAR_MARK; 00644 00645 // 00646 // UDF 2.01 changes to a base 41 encoding. UDF 1.50 and 00647 // UDF 2.00 exchanged the # delimeter with the high 4bits 00648 // of the CRC. 00649 // 00650 00651 dosName[targetIndex++] = CRC_MARK; 00652 00653 dosName[targetIndex++] = 00654 UdfCrcChar[udfCRCValue / (41 * 41)]; 00655 udfCRCValue %= (41 * 41); 00656 00657 dosName[targetIndex++] = 00658 UdfCrcChar[udfCRCValue / 41]; 00659 udfCRCValue %= 41; 00660 00661 dosName[targetIndex++] = 00662 UdfCrcChar[udfCRCValue]; 00663 } 00664 00665 /* Append the extension, if any. */ 00666 if (extLen > 0) { 00667 /* Tack on a period and each successive byte in the */ 00668 /* extension buffer. */ 00669 dosName[targetIndex++] = PERIOD; 00670 for (index = 0; index < extLen; index++) 00671 dosName[targetIndex++] = ext[index]; 00672 } 00673 00674 ASSERT( (targetIndex * sizeof(WCHAR)) <= ShortFileName->MaximumLength ); 00675 00676 ShortFileName->Length = (USHORT) (targetIndex * sizeof(WCHAR)); 00677 00678 // 00679 // Now we upcase the whole name at once. 00680 // 00681 00682 UdfUpcaseName( IrpContext, 00683 ShortFileName, 00684 ShortFileName ); 00685 } 00686 00687 00688 VOID 00689 UdfConvertCS0DstringToUnicode ( 00690 IN PIRP_CONTEXT IrpContext, 00691 IN PUCHAR Dstring, 00692 IN UCHAR Length OPTIONAL, 00693 IN UCHAR FieldLength OPTIONAL, 00694 IN OUT PUNICODE_STRING Name 00695 ) 00696 00697 /*++ 00698 00699 Routine Description: 00700 00701 This routine will convert the CS0 input dstring (1/7.2.12) to Unicode. We assume that 00702 the length is sane. 00703 00704 This "compression" in CS0 is really just a special case hack for ASCII. 00705 00706 Arguments: 00707 00708 Dstring - the input dstring field 00709 00710 Length - length of the dstring. If unspecified, we assume that the characters come 00711 from a proper 1/7.2.12 dstring that specifies length in the last character of the 00712 field. 00713 00714 FieldLength - length of the dstring field. If unspecified, we assume that the characters 00715 come from an uncounted length of CS0 characters and that the Length parameter is 00716 specified. 00717 00718 Name - the output Unicode string 00719 00720 Return Value: 00721 00722 None. 00723 00724 --*/ 00725 00726 { 00727 ULONG CompressID; 00728 ULONG UnicodeIndex, ByteIndex; 00729 PWCHAR Unicode = Name->Buffer; 00730 00731 UCHAR NameLength; 00732 ULONG CopyNameLength; 00733 00734 PAGED_CODE(); 00735 00736 // 00737 // Check input. 00738 // 00739 00740 ASSERT_IRP_CONTEXT( IrpContext ); 00741 00742 CompressID = *Dstring; 00743 00744 // 00745 // If the length is unspecified, this is a real 1/7.2.12 dstring and the length is in 00746 // the last character of the field. 00747 // 00748 00749 ASSERT( Length || FieldLength ); 00750 00751 if (Length) { 00752 00753 NameLength = FieldLength = Length; 00754 00755 } else { 00756 00757 NameLength = *(Dstring + FieldLength - 1); 00758 } 00759 00760 // 00761 // If the caller specified a size, they should have made sure the buffer is big enough. 00762 // Otherwise, we will trim to fit. 00763 // 00764 00765 ASSERT( Length == 0 || Name->MaximumLength >= UdfCS0DstringUnicodeSize( IrpContext, Dstring, NameLength ) ); 00766 00767 // 00768 // Decide how many UNICODE bytes to "copy". 00769 // 00770 00771 CopyNameLength = Min( Name->MaximumLength, UdfCS0DstringUnicodeSize( IrpContext, Dstring, NameLength )); 00772 00773 // 00774 // Reset the name length and advance over the compression ID in the dstring. 00775 // 00776 00777 Name->Length = 0; 00778 Dstring++; 00779 00780 // 00781 // Loop through all the bytes. 00782 // 00783 00784 while (CopyNameLength > Name->Length) { 00785 00786 if (CompressID == 16) { 00787 00788 // 00789 // We're little endian, and this is the single place in the entire UDF/ISO standard 00790 // where they use big endian. 00791 // 00792 // Thank you. Thank you very much. 00793 // 00794 // Do an unaligned swapcopy of this 16bit value. 00795 // 00796 00797 SwapCopyUchar2( Unicode, Dstring ); 00798 Dstring += sizeof(WCHAR); 00799 00800 } else { 00801 00802 // 00803 // Drop the byte into the low bits. 00804 // 00805 00806 *Unicode = *Dstring; 00807 Dstring += sizeof(CHAR); 00808 } 00809 00810 Name->Length += sizeof(WCHAR); 00811 Unicode++; 00812 } 00813 00814 return; 00815 } 00816 00817 00818 BOOLEAN 00819 UdfCheckLegalCS0Dstring ( 00820 PIRP_CONTEXT IrpContext, 00821 PUCHAR Dstring, 00822 UCHAR Length OPTIONAL, 00823 UCHAR FieldLength OPTIONAL, 00824 BOOLEAN ReturnOnError 00825 ) 00826 00827 /*++ 00828 00829 Routine Description: 00830 00831 This routine inspects a CS0 Dstring for conformance. 00832 00833 Arguments: 00834 00835 Dstring - a dstring to check 00836 00837 Length - length of the dstring. If unspecified, we assume that the characters come 00838 from a proper 1/7.2.12 dstring that specifies length in the last character of the 00839 field. 00840 00841 FieldLength - length of the dstring field. If unspecified, we assume that the characters 00842 come from an uncounted length of CS0 characters and that the Length parameter is 00843 specified. 00844 00845 ReturnOnError - whether to return or raise on a discovered error 00846 00847 Return Value: 00848 00849 None. Raised status if corruption is found. 00850 00851 --*/ 00852 00853 { 00854 UCHAR NameLength; 00855 00856 // 00857 // Check input. 00858 // 00859 00860 ASSERT_IRP_CONTEXT( IrpContext ); 00861 00862 // 00863 // If the length is unspecified, this is a real 1/7.2.12 dstring and the length is in 00864 // the last character of the field. 00865 // 00866 00867 ASSERT( Length || FieldLength ); 00868 00869 if (Length) { 00870 00871 NameLength = FieldLength = Length; 00872 00873 } else { 00874 00875 NameLength = *(Dstring + FieldLength - 1); 00876 } 00877 00878 DebugTrace(( +1, Dbg, 00879 "UdfCheckLegalCS0Dstring, Dstring %08x Length %02x FieldLength %02x (NameLength %02x)\n", 00880 Dstring, 00881 Length, 00882 FieldLength, 00883 NameLength )); 00884 00885 // 00886 // The string must be "compressed" in 8bit or 16bit chunks. If it 00887 // is in 16bit chunks, we better have an integral number of them - 00888 // remember we have the compression ID, so the length will be odd. 00889 // 00890 00891 if ((NameLength <= 1 && 00892 DebugTrace(( 0, Dbg, 00893 "UdfCheckLegalCS0Dstring, NameLength is too small!\n" ))) || 00894 00895 (NameLength > FieldLength && 00896 DebugTrace(( 0, Dbg, 00897 "UdfCheckLegalCS0Dstring, NameLength is bigger than the field itself!\n" ))) || 00898 00899 ((*Dstring != 8 && *Dstring != 16) && 00900 DebugTrace(( 0, Dbg, 00901 "UdfCheckLegalCS0Dstring, claims encoding %02x, unknown! (not 0x8 or 0x10)\n", 00902 *Dstring ))) || 00903 00904 ((*Dstring == 16 && !FlagOn( NameLength, 1)) && 00905 DebugTrace(( 0, Dbg, 00906 "UdfCheckLegalCS0Dstring, NameLength not odd, encoding 0x10!\n" )))) { 00907 00908 if (ReturnOnError) { 00909 00910 DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> FALSE\n" )); 00911 00912 return FALSE; 00913 } 00914 00915 DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> raised status\n" )); 00916 00917 UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 00918 } 00919 00920 DebugTrace(( -1, Dbg, "UdfCheckLegalCS0Dstring -> TRUE\n" )); 00921 00922 return TRUE; 00923 } 00924 00925 00926 VOID 00927 UdfRenderNameToLegalUnicode ( 00928 IN PIRP_CONTEXT IrpContext, 00929 IN PUNICODE_STRING Name, 00930 IN PUNICODE_STRING RenderedName 00931 ) 00932 00933 /*++ 00934 00935 Routine Description: 00936 00937 This routine will take a Unicode string containing illegal characters and 00938 run it through the UDF standard algorithim to render it into a "legal" 00939 name. 00940 00941 The short form is to convert all runs of illegal characters to "_" and tack 00942 on a hex representation of the CRC of the original name. The algorithm is 00943 nearly directly lifted from the UDF (2.01 proposed!) standard, so apologies 00944 for the style clash. 00945 00946 Arguments: 00947 00948 Name - the actual name 00949 00950 RenderedName - the name rendered into legal characters 00951 00952 Return Value: 00953 00954 BOOLEAN - TRUE if the expressions match, FALSE otherwise. 00955 00956 --*/ 00957 00958 { 00959 INT16 index; 00960 INT16 targetIndex; 00961 INT16 crcIndex; 00962 INT16 extLen; 00963 INT16 nameLen; 00964 INT16 charLen; 00965 INT16 overlayBytes; 00966 INT16 bytesLeft; 00967 UNICODE_CHAR current; 00968 BOOLEAN needsCRC; 00969 BOOLEAN foundDot; 00970 UNICODE_CHAR ext[EXT_LEN]; 00971 00972 // 00973 // So as to lift as directly as possible from the standard, chunk things around. 00974 // 00975 00976 PWCHAR newName = RenderedName->Buffer; 00977 PWCHAR udfName = Name->Buffer; 00978 LONG udfNameLen = Name->Length / sizeof(WCHAR); 00979 00980 /* Remove trailing periods ('.') and spaces (' '), Windows */ 00981 /* does not like them. */ 00982 foundDot = FALSE; 00983 index = udfNameLen; 00984 while (index-- > 0) { 00985 if (udfName[index] == PERIOD) 00986 foundDot = TRUE; 00987 else if (udfName[index] != SPACE) 00988 break; 00989 } 00990 00991 /* If any trailing periods or spaces were found, a CRC code */ 00992 /* needs to be added to the resulting file name. */ 00993 nameLen = index + 1; 00994 if (nameLen < udfNameLen) 00995 needsCRC = TRUE; 00996 00997 needsCRC = FALSE; 00998 bytesLeft = MAX_LEN; 00999 extLen = 0; 01000 01001 if (needsCRC == FALSE || foundDot == FALSE) { 01002 /* Look for an extension in the file name. We do not */ 01003 /* need to look for one if there were any trailing periods */ 01004 /* removed. */ 01005 INT16 endIndex; 01006 INT16 prevCharLen = 1; 01007 INT16 extBytes = 0; 01008 01009 targetIndex = 0; 01010 index = nameLen; 01011 01012 /* Determine how many bytes we need to scan to find the */ 01013 /* extension delimiter. The extension has a maximum of */ 01014 /* five characters, but we do not want to scan past the */ 01015 /* beginning of the buffer. */ 01016 endIndex = (udfNameLen > EXT_LEN + 1) ? 01017 udfNameLen - EXT_LEN - 1 : 1; 01018 01019 /* Start at the end of the name and scan backward, looking */ 01020 /* for the extension delimiter ("."). */ 01021 while (index-- > endIndex) { 01022 /* Get the character to test. */ 01023 current = udfName[index]; 01024 01025 if (current == '.') { 01026 /* The extension delimiter was found, figure out */ 01027 /* how many characters the extension contains and */ 01028 /* the length of the resulting file name without */ 01029 /* the extension. */ 01030 extLen = nameLen - index - 1; 01031 nameLen = index; 01032 break; 01033 } 01034 01035 /* Determine the byte length of the current character */ 01036 /* when converted to native format. */ 01037 charLen = (IsFileNameCharLegal(current)) ? 01038 NativeCharLength(current) : 0; 01039 01040 if (charLen == 0) { 01041 /* If the character byte length is zero, it is */ 01042 /* illegal or unprintable, place an underscore */ 01043 /* ("_") in the extension buffer if the previous */ 01044 /* character tested was legal. Not that the */ 01045 /* characters placed in the extension buffer are */ 01046 /* in reverse order. */ 01047 if (prevCharLen != 0) { 01048 ext[targetIndex++] = ILLEGAL_CHAR_MARK; 01049 extBytes++; 01050 } 01051 } 01052 else { 01053 /* The current character is legal and printable, */ 01054 /* put it in the extension buffer. Note that the */ 01055 /* characters placed in the extension buffer are */ 01056 /* in reverse order. */ 01057 ext[targetIndex++] = current; 01058 extBytes += charLen; 01059 } 01060 01061 /* Save the byte length of the current character, so */ 01062 /* we can determine if it was a legal character during */ 01063 /* the next test. */ 01064 prevCharLen = charLen; 01065 } 01066 01067 /* If an extension was found, determine how many bytes */ 01068 /* remain in the file name buffer once we account for it. */ 01069 if (extLen > 0) 01070 bytesLeft -= extBytes + 1; 01071 } 01072 01073 index = 0; 01074 targetIndex = 0; 01075 crcIndex = 0; 01076 overlayBytes = -1; 01077 while (index < nameLen && bytesLeft > 0) { 01078 /* Get the current character and convert it to upper case. */ 01079 current = udfName[index]; 01080 01081 /* Determine if this is a valid file name char and */ 01082 /* calculate its corresponding native character byte */ 01083 /* length (zero if the char is not legal or undiplayable */ 01084 /* on this system). */ 01085 charLen = (IsFileNameCharLegal(current)) ? 01086 NativeCharLength(current) : 0; 01087 01088 /* If the char is larger than the available space in the */ 01089 /* buffer, pretend it is undisplayable. */ 01090 if (charLen > bytesLeft) 01091 charLen = 0; 01092 01093 if (charLen == 0) { 01094 /* Undisplayable or illegal characters are substituted */ 01095 /* with an underscore ("_"), and requires a CRC code */ 01096 /* appended to the mangled file name. */ 01097 needsCRC = TRUE; 01098 charLen = 1; 01099 current = '_'; 01100 01101 /* Skip over any following undiplayable or illegal */ 01102 /* chars. */ 01103 while (index + 1 < udfNameLen && 01104 (!IsFileNameCharLegal(udfName[index + 1]) || 01105 NativeCharLength(udfName[index + 1]) == 0)) 01106 index++; 01107 01108 /* Terminate loop if at the end of the file name. */ 01109 if (index >= udfNameLen) 01110 break; 01111 } 01112 01113 /* Assign the resulting char to the next index in the file */ 01114 /* name buffer and determine how many native bytes are */ 01115 /* left. */ 01116 newName[targetIndex++] = current; 01117 bytesLeft -= charLen; 01118 01119 /* This figures out where the CRC code needs to start in */ 01120 /* the file name buffer. */ 01121 if (bytesLeft >= CRC_LEN) { 01122 /* If there is enough space left, just tack it onto */ 01123 /* the end. */ 01124 crcIndex = targetIndex; 01125 } 01126 else { 01127 /* If there is not enough space left, the CRC must */ 01128 /* overlay a character already in the file name */ 01129 /* buffer. Once this condition has been met, the */ 01130 /* value will not change. */ 01131 if (overlayBytes < 0) { 01132 /* Determine the index and save the length of the */ 01133 /* native character that is overlayed. It is */ 01134 /* possible that the CRC might overlay half of a */ 01135 /* two-byte native character depending upon how */ 01136 /* the character boundaries line up. */ 01137 overlayBytes = (bytesLeft + charLen > CRC_LEN) 01138 ? 1 : 0; 01139 crcIndex = targetIndex - 1; 01140 } 01141 } 01142 01143 /* Advance to the next character. */ 01144 index++; 01145 } 01146 01147 /* If the scan did not reach the end of the file name, or the */ 01148 /* length of the file name is zero, a CRC code is needed. */ 01149 if (index < nameLen || index == 0) 01150 needsCRC = TRUE; 01151 01152 /* If the name has illegal characters or and extension, it */ 01153 /* is not a DOS device name. */ 01154 if (needsCRC == FALSE && extLen == 0) { 01155 /* If this is the name of a DOS device, a CRC code should */ 01156 /* be appended to the file name. */ 01157 if (IsDeviceName(udfName, udfNameLen)) 01158 needsCRC = TRUE; 01159 } 01160 01161 /* Append the CRC code to the file name, if needed. */ 01162 if (needsCRC) { 01163 /* Get the CRC value for the original Unicode string */ 01164 UINT16 udfCRCValue = UdfComputeCrc16Uni(udfName, udfNameLen); 01165 01166 /* Determine the character index where the CRC should */ 01167 /* begin. */ 01168 targetIndex = crcIndex; 01169 01170 /* If the character being overlayed is a two-byte native */ 01171 /* character, replace the first byte with an underscore. */ 01172 if (overlayBytes > 0) 01173 newName[targetIndex++] = ILLEGAL_CHAR_MARK; 01174 01175 /* Append the encoded CRC value with delimiter. */ 01176 newName[targetIndex++] = CRC_MARK; 01177 newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0xf000) >> 12]; 01178 newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x0f00) >> 8]; 01179 newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x00f0) >> 4]; 01180 newName[targetIndex++] = UdfCrcChar[(udfCRCValue & 0x000f)]; 01181 } 01182 01183 01184 /* If an extension was found, append it here. */ 01185 if (extLen > 0) { 01186 /* Add the period ('.') for the extension delimiter. */ 01187 newName[targetIndex++] = PERIOD; 01188 01189 /* Append the characters in the extension buffer. They */ 01190 /* were stored in reverse order, so we need to begin with */ 01191 /* the last character and work forward. */ 01192 while (extLen-- > 0) 01193 newName[targetIndex++] = ext[extLen]; 01194 } 01195 01196 ASSERT( (targetIndex * sizeof(WCHAR)) <= RenderedName->MaximumLength ); 01197 01198 RenderedName->Length = (USHORT) (targetIndex * sizeof(WCHAR)); 01199 } 01200 01201 01202 BOOLEAN 01203 UdfIsNameInExpression ( 01204 IN PIRP_CONTEXT IrpContext, 01205 IN PUNICODE_STRING CurrentName, 01206 IN PUNICODE_STRING SearchExpression, 01207 IN BOOLEAN Wild 01208 ) 01209 01210 /*++ 01211 01212 Routine Description: 01213 01214 This routine will compare two Unicode strings. We assume that if this 01215 is to be a case-insensitive search then they are already upcased. 01216 01217 Arguments: 01218 01219 CurrentName - Filename from the disk. 01220 01221 SearchExpression - Filename expression to use for match. 01222 01223 Wild - True if wildcards are present in SearchExpression. 01224 01225 Return Value: 01226 01227 BOOLEAN - TRUE if the expressions match, FALSE otherwise. 01228 01229 --*/ 01230 01231 { 01232 BOOLEAN Match = TRUE; 01233 01234 PAGED_CODE(); 01235 01236 // 01237 // Check inputs. 01238 // 01239 01240 ASSERT_IRP_CONTEXT( IrpContext ); 01241 01242 // 01243 // If there are wildcards in the expression then we call the 01244 // appropriate FsRtlRoutine. 01245 // 01246 01247 if (Wild) { 01248 01249 Match = FsRtlIsNameInExpression( SearchExpression, 01250 CurrentName, 01251 FALSE, 01252 NULL ); 01253 01254 // 01255 // Otherwise do a direct memory comparison for the name string. 01256 // 01257 01258 } else { 01259 01260 if ((CurrentName->Length != SearchExpression->Length) || 01261 (!RtlEqualMemory( CurrentName->Buffer, 01262 SearchExpression->Buffer, 01263 CurrentName->Length ))) { 01264 01265 Match = FALSE; 01266 } 01267 } 01268 01269 return Match; 01270 } 01271 01272 01273 FSRTL_COMPARISON_RESULT 01274 UdfFullCompareNames ( 01275 IN PIRP_CONTEXT IrpContext, 01276 IN PUNICODE_STRING NameA, 01277 IN PUNICODE_STRING NameB 01278 ) 01279 01280 /*++ 01281 01282 Routine Description: 01283 01284 This function compares two names as fast as possible. Note that since 01285 this comparison is case sensitive we can do a direct memory comparison. 01286 01287 Arguments: 01288 01289 NameA & NameB - The names to compare. 01290 01291 Return Value: 01292 01293 COMPARISON - returns 01294 01295 LessThan if NameA < NameB lexicalgraphically, 01296 GreaterThan if NameA > NameB lexicalgraphically, 01297 EqualTo if NameA is equal to NameB 01298 01299 --*/ 01300 01301 { 01302 ULONG i; 01303 ULONG MinLength = NameA->Length; 01304 FSRTL_COMPARISON_RESULT Result = LessThan; 01305 01306 PAGED_CODE(); 01307 01308 // 01309 // Check inputs. 01310 // 01311 01312 ASSERT_IRP_CONTEXT( IrpContext ); 01313 01314 // 01315 // Figure out the minimum of the two lengths 01316 // 01317 01318 if (NameA->Length > NameB->Length) { 01319 01320 MinLength = NameB->Length; 01321 Result = GreaterThan; 01322 01323 } else if (NameA->Length == NameB->Length) { 01324 01325 Result = EqualTo; 01326 } 01327 01328 // 01329 // Loop through looking at all of the characters in both strings 01330 // testing for equalilty, less than, and greater than 01331 // 01332 01333 i = (ULONG) RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); 01334 01335 if (i < MinLength) { 01336 01337 // 01338 // We know the offset of the first character which is different. 01339 // 01340 01341 return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ? 01342 LessThan : 01343 GreaterThan); 01344 } 01345 01346 // 01347 // The names match up to the length of the shorter string. 01348 // The shorter string lexically appears first. 01349 // 01350 01351 return Result; 01352 } 01353

Generated on Sat May 15 19:40:53 2004 for test by doxygen 1.3.7