test: namesup.c Source File

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